SHOW:
|
|
- or go back to the newest paste.
| 1 | import rtmp_protocol | |
| 2 | import httplib | |
| 3 | from xml.dom.minidom import parseString | |
| 4 | import thread | |
| 5 | import time | |
| 6 | import random | |
| 7 | import socket | |
| 8 | ||
| 9 | # Tinychat Library by MegaLoler | |
| 10 | ||
| 11 | # Changes: | |
| 12 | # Added youtube and soundcloud control commands | |
| 13 | # Incorporated some console commands for when running the script directly | |
| 14 | ||
| 15 | # Brief Todo: | |
| 16 | # Unban command | |
| 17 | # Fix bug where all messages received in rooms the same name as your username appear to be PMs | |
| 18 | # Ability to tell whether users are moderators or not | |
| 19 | # Figure out more things to add :P | |
| 20 | ||
| 21 | DEBUG = False | |
| 22 | ||
| 23 | def httpRequest(server, resource, body="", headers={}, method="GET"):
| |
| 24 | connection = httplib.HTTPConnection(server) | |
| 25 | connection.request(method, resource, body, headers) | |
| 26 | response = connection.getresponse() | |
| 27 | headers = response.getheaders() | |
| 28 | data = response.read() | |
| 29 | connection.close() | |
| 30 | return (headers, data) | |
| 31 | ||
| 32 | class TinychatMessage(): | |
| 33 | def __init__(self, msg, nick, user=None, recipient=None, color=None, pm=False): | |
| 34 | self.msg = msg | |
| 35 | self.nick = nick | |
| 36 | self.user = user | |
| 37 | self.recipient = recipient | |
| 38 | self.color = color | |
| 39 | self.pm = pm | |
| 40 | ||
| 41 | def printFormatted(self): | |
| 42 | if self.pm: | |
| 43 | pm = "(PM) " | |
| 44 | else: | |
| 45 | pm = "" | |
| 46 | print(pm + self.recipient + ": " + self.nick + ": " + self.msg) | |
| 47 | ||
| 48 | class TinychatUser(): | |
| 49 | def __init__(self, nick, id=None, color=None, lastMsg=None): | |
| 50 | self.nick = nick | |
| 51 | self.id = id | |
| 52 | self.color = color | |
| 53 | self.lastMsg = lastMsg | |
| 54 | ||
| 55 | TINYCHAT_COLORS = ["#7db257", "#a78901", "#9d5bb5", "#5c1a7a", "#c53332", "#821615", "#a08f23", "#487d21", "#c356a3", "#1d82eb", "#919104", "#a990", "#b9807f", "#7bb224", "#1965b6", "#32a5d9"] | |
| 56 | ||
| 57 | class TinychatRoom(): | |
| 58 | # Manages a single room connection | |
| 59 | def __init__(self, room, nick=None, passwd=None): | |
| 60 | self.room = room | |
| 61 | self.tcUrl = self._getTcUrl() | |
| 62 | parts = self.tcUrl.split("/")
| |
| 63 | server = parts[2].split(":")
| |
| 64 | self.server = server[0] | |
| 65 | self.port = int(server[1]) | |
| 66 | self.app = parts[3] | |
| 67 | self.url = "http://tinychat.com/" + room | |
| 68 | self.connected = False | |
| 69 | self.queue = [] | |
| 70 | self.color = TINYCHAT_COLORS[random.randint(0, len(TINYCHAT_COLORS) - 1)] | |
| 71 | self.nick = nick | |
| 72 | self.passwd = passwd | |
| 73 | if self.nick == "": self.nick = None | |
| 74 | if self.passwd == "": self.passwd = None | |
| 75 | self.topic = None | |
| 76 | self.users = {}
| |
| 77 | self.echo = __name__ == "__main__" | |
| 78 | self.stack = False | |
| 79 | ||
| 80 | def connect(self): | |
| 81 | if not self.connected: | |
| 82 | self.connection = rtmp_protocol.RtmpClient(self.server, self.port, self.tcUrl, self.url, '', self.app) | |
| 83 | if self.nick: | |
| 84 | n = self.nick.lower() | |
| 85 | else: | |
| 86 | n = "" | |
| 87 | self.connection.connect([self.room, self._getAutoOp(), u'show', u'tinychat', n]) | |
| 88 | self.connected = True | |
| 89 | self._listen() | |
| 90 | ||
| 91 | def disconnect(self): | |
| 92 | if self.connected: | |
| 93 | self.connected = False | |
| 94 | try: | |
| 95 | self.connection.socket.shutdown(socket.SHUT_RDWR) | |
| 96 | except: | |
| 97 | pass | |
| 98 | self.onDisconnect() | |
| 99 | ||
| 100 | def poll(self): | |
| 101 | q = self.queue | |
| 102 | self.queue = [] | |
| 103 | return q | |
| 104 | ||
| 105 | # Commands | |
| 106 | def say(self, msg): | |
| 107 | if len(msg) > 152: return | |
| 108 | self._sendCommand("privmsg", [u"" + self._encodeMessage(msg), u"" + self.color + ",en"])
| |
| 109 | ||
| 110 | def pm(self, msg, recipient): | |
| 111 | if len(msg) > 152: return | |
| 112 | self._sendCommand("privmsg", [u"" + self._encodeMessage("/msg " + recipient + " " + msg), u"" + self.color + ",en", u"n" + self._getUser(recipient).id + "-" + recipient])
| |
| 113 | ||
| 114 | def setNick(self, nick=None): | |
| 115 | if not nick: nick = self.nick | |
| 116 | self.nick = nick | |
| 117 | self._sendCommand("nick", [u"" + nick])
| |
| 118 | ||
| 119 | def cycleColor(self): | |
| 120 | try: | |
| 121 | i = TINYCHAT_COLORS.index(self.color) | |
| 122 | except: | |
| 123 | i = TINYCHAT_COLORS[random.randint(0, len(TINYCHAT_COLORS) - 1)] | |
| 124 | i = (i + 1) % len(TINYCHAT_COLORS) | |
| 125 | self.color = TINYCHAT_COLORS[i] | |
| 126 | ||
| 127 | def ban(self, nick): | |
| 128 | self._sendCommand("kick", [u"" + nick, self._getUser(nick).id])
| |
| 129 | ||
| 130 | def playYoutube(self, video): | |
| 131 | self.say("/mbs youTube " + str(video) + " 0")
| |
| 132 | ||
| 133 | def stopYoutube(self): | |
| 134 | self.say("/mbc youTube")
| |
| 135 | ||
| 136 | def playSoundcloud(self, track): | |
| 137 | self.say("/mbs soundCloud " + str(track) + " 0")
| |
| 138 | ||
| 139 | def stopSoundcloud(self): | |
| 140 | self.say("/mbc soundCloud")
| |
| 141 | ||
| 142 | # Events | |
| 143 | def onMessage(self, user, message): | |
| 144 | if self.echo: message.printFormatted() | |
| 145 | ||
| 146 | def onPM(self, user, message): | |
| 147 | if self.echo: message.printFormatted() | |
| 148 | ||
| 149 | def onQuit(self, user): | |
| 150 | if self.echo: print(self.room + ": " + user.nick + " left the room.") | |
| 151 | ||
| 152 | def onBan(self, user): | |
| 153 | if self.echo: print(self.room + ": " + user.nick + " was banned.") | |
| 154 | ||
| 155 | def onJoin(self, user): | |
| 156 | if self.echo: print(self.room + ": " + user.nick + " entered the room.") | |
| 157 | ||
| 158 | def onRegister(self, user): | |
| 159 | if self.echo: print("You have connected to " + self.room + ".")
| |
| 160 | ||
| 161 | def onNickChange(self, new, old, user): | |
| 162 | if self.echo: print(self.room + ": " + old + " changed nickname to " + new + ".") | |
| 163 | ||
| 164 | def onTopic(self, topic): | |
| 165 | if self.echo: print(self.room + ": Topic set to \"" + topic + "\".") | |
| 166 | ||
| 167 | def onUserList(self, users): | |
| 168 | pass | |
| 169 | ||
| 170 | def onDisconnect(self): | |
| 171 | if self.echo: print("You have disconnected from " + self.room + ".")
| |
| 172 | ||
| 173 | # Helper methods | |
| 174 | def _listen(self): | |
| 175 | while self.connected: | |
| 176 | try: | |
| 177 | msg = self.connection.reader.next() | |
| 178 | if DEBUG: print("SERVER: " + str(msg))
| |
| 179 | if msg['msg'] == rtmp_protocol.DataTypes.COMMAND: | |
| 180 | pars = msg['command'] | |
| 181 | cmd = pars[0].encode("ascii", "ignore").lower()
| |
| 182 | if len(pars) > 3: | |
| 183 | pars = pars[3:] | |
| 184 | else: | |
| 185 | pars = [] | |
| 186 | for i in range(len(pars)): | |
| 187 | if type(pars[i]) == str: pars[i] = pars[i].encode("ascii", "ignore")
| |
| 188 | if cmd == "privmsg": | |
| 189 | recipient = pars[0] | |
| 190 | message = pars[1] | |
| 191 | color = pars[2].lower().split(",")[0]
| |
| 192 | nick = pars[3] | |
| 193 | if recipient[0] == "#": | |
| 194 | recipient = "^".join(recipient.split("^")[1:])
| |
| 195 | else: | |
| 196 | recipient = "-".join(recipient.split("-")[1:])
| |
| 197 | user = self._getUser(nick) | |
| 198 | message = TinychatMessage(self._decodeMessage(message), nick, user, recipient, color) | |
| 199 | user.lastMsg = message | |
| 200 | user.color = color | |
| 201 | if self.stack: self.queue.append(message) | |
| 202 | if recipient.lower() == self.nick.lower(): | |
| 203 | message.pm = True | |
| 204 | if message.msg.startswith("/msg ") and len(message.msg.split(" ")) >= 2: message.msg = " ".join(message.msg.split(" ")[2:])
| |
| 205 | self.onPM(user, message) | |
| 206 | else: | |
| 207 | self.onMessage(user, message) | |
| 208 | elif cmd == "registered": | |
| 209 | if self.nick: self.setNick() | |
| 210 | user = self._getUser(pars[0]) | |
| 211 | user.id = pars[1] | |
| 212 | self.onRegister(user) | |
| 213 | elif cmd == "topic": | |
| 214 | self.topic = pars[0] | |
| 215 | self.onTopic(self.topic) | |
| 216 | elif cmd == "nick": | |
| 217 | user = self._getUser(pars[0]) | |
| 218 | old = user.nick | |
| 219 | user.nick = pars[1] | |
| 220 | if old.lower() in self.users.keys(): | |
| 221 | del self.users[old.lower()] | |
| 222 | self.users[user.nick.lower()] = user | |
| 223 | self.onNickChange(user.nick, old, user) | |
| 224 | elif cmd == "quit": | |
| 225 | user = self.users[pars[0].lower()] | |
| 226 | del self.users[pars[0].lower()] | |
| 227 | self.onQuit(user) | |
| 228 | elif cmd == "kick": | |
| 229 | self.onBan(user = self.users[pars[1].lower()]) | |
| 230 | elif cmd == "join": | |
| 231 | user = self._getUser(pars[1]) | |
| 232 | user.id = pars[0] | |
| 233 | self.onJoin(user) | |
| 234 | elif cmd == "joins": | |
| 235 | for i in range((len(pars) - 1) / 2): | |
| 236 | user = self._getUser(pars[i*2 + 2]) | |
| 237 | user.id = pars[i*2 + 1] | |
| 238 | elif cmd == "joinsdone": | |
| 239 | self.onUserList(self.users) | |
| 240 | except: | |
| 241 | self.disconnect() | |
| 242 | ||
| 243 | def _getUser(self, nick): | |
| 244 | if not nick.lower() in self.users.keys(): self.users[nick.lower()] = TinychatUser(nick) | |
| 245 | return self.users[nick.lower()] | |
| 246 | ||
| 247 | def _decodeMessage(self, msg): | |
| 248 | chars = msg.split(",")
| |
| 249 | msg = "" | |
| 250 | for i in chars: | |
| 251 | msg += chr(int(i)) | |
| 252 | return msg | |
| 253 | ||
| 254 | def _encodeMessage(self, msg): | |
| 255 | msg2 = [] | |
| 256 | for i in msg: | |
| 257 | msg2.append(str(ord(i))) | |
| 258 | return ",".join(msg2) | |
| 259 | ||
| 260 | def _sendCommand(self, cmd, pars=[]): | |
| 261 | msg = {"msg": rtmp_protocol.DataTypes.COMMAND, "command": [u"" + cmd, 0, None] + pars}
| |
| 262 | if DEBUG: print("CLIENT: " + str(msg))
| |
| 263 | self.connection.writer.write(msg) | |
| 264 | self.connection.writer.flush() | |
| 265 | ||
| 266 | def _getTcUrl(self): | |
| 267 | return parseString(httpRequest("tinychat.com", "/api/find.room/" + self.room)[1]).getElementsByTagName("response")[0].getAttribute("rtmp")
| |
| 268 | ||
| 269 | def _getAutoOp(self): | |
| 270 | if self.nick and self.passwd: | |
| 271 | headers = httpRequest("tinychat.com", "/login", "form_sent=1&referer=&next=&username=" + self.nick + "&password=" + self.passwd + "&passwordfake=Password&remember=1", {"Content-Type": "application/x-www-form-urlencoded"}, "POST")[0]
| |
| 272 | - | while room.connected: room.say(raw_input()) |
| 272 | + | |
| 273 | for header in headers: | |
| 274 | if header[0].lower() == "set-cookie": | |
| 275 | parts = header[1].split(", ")
| |
| 276 | for i in range(len(parts)): | |
| 277 | part = parts[i] | |
| 278 | part2 = part.split(";")[0]
| |
| 279 | if "=" in part2: | |
| 280 | if "," in part2: | |
| 281 | part2 = part2.split(", ")[1]
| |
| 282 | cookies.append(part2) | |
| 283 | break | |
| 284 | if len(cookies) > 0: | |
| 285 | html = httpRequest("tinychat.com", "/" + self.room, "", {"Content-Type": "application/x-www-form-urlencoded", "Cookie": "; ".join(cookies)})[1]
| |
| 286 | if ", autoop: \"" in html: | |
| 287 | return html.split(", autoop: \"")[1].split("\"")[0]
| |
| 288 | return u"none" | |
| 289 | ||
| 290 | if __name__ == "__main__": | |
| 291 | room = TinychatRoom(raw_input("Enter room name: "), raw_input("Enter username (optional): "), raw_input("Enter password (optional): "))
| |
| 292 | thread.start_new_thread(room.connect, ()) | |
| 293 | while not room.connected: time.sleep(1) | |
| 294 | while room.connected: | |
| 295 | msg = raw_input() | |
| 296 | if len(msg) > 0: | |
| 297 | if msg[0] == "/": | |
| 298 | msg = msg[1:] | |
| 299 | if len(msg) > 0: | |
| 300 | parts = msg.split(" ")
| |
| 301 | if len(parts) == 1: | |
| 302 | cmd = parts[0] | |
| 303 | pars = [] | |
| 304 | par = "" | |
| 305 | else: | |
| 306 | cmd = parts[0] | |
| 307 | pars = parts[1:] | |
| 308 | par = " ".join(parts[1:]) | |
| 309 | if cmd.lower() == "say": | |
| 310 | room.say(par) | |
| 311 | elif cmd.lower() == "pm": | |
| 312 | if len(pars) > 1: | |
| 313 | room.pm(" ".join(pars[1:]), pars[0])
| |
| 314 | else: | |
| 315 | print("Please supply the recipient's nick as well as the message to send")
| |
| 316 | elif cmd.lower() == "nick": | |
| 317 | room.setNick(par) | |
| 318 | elif cmd.lower() == "color": | |
| 319 | room.cycleColor() | |
| 320 | elif cmd.lower() == "ban": | |
| 321 | room.ban(par) | |
| 322 | elif cmd.lower() == "quit": | |
| 323 | room.disconnect() | |
| 324 | elif cmd.lower() == "playyoutube": | |
| 325 | room.playYoutube(par) | |
| 326 | elif cmd.lower() == "stopyoutube": | |
| 327 | room.stopYoutube() | |
| 328 | elif cmd.lower() == "playsoundcloud": | |
| 329 | room.playSoundcloud(par) | |
| 330 | elif cmd.lower() == "stopsoundcloud": | |
| 331 | room.stopSoundcloud() | |
| 332 | else: | |
| 333 | room.say(msg) |