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 | # Credits for code base to MegaLOLer | |
| 10 | # Edits by Nola | |
| 11 | ||
| 12 | # Add Cauth token - API - http://tinychat.com/api/captcha/check.php?room=tinychat^[roomname]&guest_id=[user_id] | |
| 13 | # Fix Bug - If Using Admin account - all messages come in as PMs | |
| 14 | # Fix Youtube/Sound Cloud API Calls | |
| 15 | # Unban command - API - http://tinychat.com/ajax/user/clearbans | |
| 16 | # Ability to tell whether users are moderators or not - Prase JOINS | |
| 17 | # Add Auto Bans Based on Usernames | |
| 18 | # Add Trivia/Poker Bot based on IRC Bots | |
| 19 | # Other things | |
| 20 | ||
| 21 | DEBUG = True | |
| 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 = ["#a78901"] | |
| 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 | self.userID = None | |
| 80 | ||
| 81 | def connect(self): | |
| 82 | if not self.connected: | |
| 83 | self.connection = rtmp_protocol.RtmpClient(self.server, self.port, self.tcUrl, self.url, '', self.app) | |
| 84 | if self.nick: | |
| 85 | n = self.nick.lower() | |
| 86 | else: | |
| 87 | n = "" | |
| 88 | self.connection.connect([self.room, self._getAutoOp(), u'show', u'tinychat', n]) | |
| 89 | self.connected = True | |
| 90 | self._listen() | |
| 91 | ||
| 92 | def disconnect(self): | |
| 93 | if self.connected: | |
| 94 | self.connected = False | |
| 95 | try: | |
| 96 | self.connection.socket.shutdown(socket.SHUT_RDWR) | |
| 97 | except: | |
| 98 | pass | |
| 99 | self.onDisconnect() | |
| 100 | ||
| 101 | def poll(self): | |
| 102 | q = self.queue | |
| 103 | self.queue = [] | |
| 104 | return q | |
| 105 | ||
| 106 | # Commands | |
| 107 | def cauth(self): | |
| 108 | self._sendCauth(self._getTcCauth()) | |
| 109 | ||
| 110 | def say(self, msg): | |
| 111 | if len(msg) > 152: return | |
| 112 | self._sendCommand("privmsg", [u"" + self._encodeMessage(msg), u"" + self.color + ",en"])
| |
| 113 | ||
| 114 | def pm(self, msg, recipient): | |
| 115 | if len(msg) > 152: return | |
| 116 | self._sendCommand("privmsg", [u"" + self._encodeMessage("/msg " + recipient + " " + msg), u"" + self.color + ",en", u"n" + self._getUser(recipient).id + "-" + recipient])
| |
| 117 | ||
| 118 | def setNick(self, nick=None): | |
| 119 | if not nick: nick = self.nick | |
| 120 | self.nick = nick | |
| 121 | self._sendCommand("nick", [u"" + nick])
| |
| 122 | ||
| 123 | def cycleColor(self): | |
| 124 | try: | |
| 125 | i = TINYCHAT_COLORS.index(self.color) | |
| 126 | except: | |
| 127 | i = TINYCHAT_COLORS[random.randint(0, len(TINYCHAT_COLORS) - 1)] | |
| 128 | i = (i + 1) % len(TINYCHAT_COLORS) | |
| 129 | self.color = TINYCHAT_COLORS[i] | |
| 130 | ||
| 131 | def ban(self, nick): | |
| 132 | self._sendCommand("kick", [u"" + nick, self._getUser(nick).id])
| |
| 133 | ||
| 134 | def playYoutube(self, video): | |
| 135 | self.say("/mbs youTube " + str(video) + " 0")
| |
| 136 | ||
| 137 | def stopYoutube(self): | |
| 138 | self.say("/mbc youTube")
| |
| 139 | ||
| 140 | def playSoundcloud(self, track): | |
| 141 | self.say("/mbs soundCloud " + str(track) + " 0")
| |
| 142 | ||
| 143 | def stopSoundcloud(self): | |
| 144 | self.say("/mbc soundCloud")
| |
| 145 | ||
| 146 | # Events | |
| 147 | def onMessage(self, user, message): | |
| 148 | if self.echo: message.printFormatted() | |
| 149 | ||
| 150 | def onPM(self, user, message): | |
| 151 | if self.echo: message.printFormatted() | |
| 152 | ||
| 153 | def onQuit(self, user): | |
| 154 | if self.echo: print(self.room + ": " + user.nick + " left the room.") | |
| 155 | ||
| 156 | def onBan(self, user): | |
| 157 | if self.echo: print(self.room + ": " + user.nick + " was banned.") | |
| 158 | ||
| 159 | def onJoin(self, user): | |
| 160 | if self.echo: print(self.room + ": " + user.nick + " entered the room.") | |
| 161 | ||
| 162 | def onRegister(self, user): | |
| 163 | if self.echo: print("You have connected to " + self.room + ".")
| |
| 164 | ||
| 165 | def onNickChange(self, new, old, user): | |
| 166 | if self.echo: print(self.room + ": " + old + " changed nickname to " + new + ".") | |
| 167 | ||
| 168 | def onTopic(self, topic): | |
| 169 | if self.echo: print(self.room + ": Topic set to \"" + topic + "\".") | |
| 170 | ||
| 171 | def onUserList(self, users): | |
| 172 | pass | |
| 173 | ||
| 174 | def onDisconnect(self): | |
| 175 | if self.echo: print("You have disconnected from " + self.room + ".")
| |
| 176 | ||
| 177 | # Helper methods | |
| 178 | def _listen(self): | |
| 179 | while self.connected: | |
| 180 | try: | |
| 181 | msg = self.connection.reader.next() | |
| 182 | if DEBUG: print("SERVER: " + str(msg))
| |
| 183 | if msg['msg'] == rtmp_protocol.DataTypes.COMMAND: | |
| 184 | pars = msg['command'] | |
| 185 | cmd = pars[0].encode("ascii", "ignore").lower()
| |
| 186 | if len(pars) > 3: | |
| 187 | pars = pars[3:] | |
| 188 | else: | |
| 189 | pars = [] | |
| 190 | for i in range(len(pars)): | |
| 191 | if type(pars[i]) == str: pars[i] = pars[i].encode("ascii", "ignore")
| |
| 192 | if cmd == "privmsg": | |
| 193 | recipient = pars[0] | |
| 194 | message = pars[1] | |
| 195 | color = pars[2].lower().split(",")[0]
| |
| 196 | nick = pars[3] | |
| 197 | if recipient[0] == "#": | |
| 198 | recipient = "^".join(recipient.split("^")[1:])
| |
| 199 | else: | |
| 200 | recipient = "-".join(recipient.split("-")[1:])
| |
| 201 | user = self._getUser(nick) | |
| 202 | message = TinychatMessage(self._decodeMessage(message), nick, user, recipient, color) | |
| 203 | user.lastMsg = message | |
| 204 | user.color = color | |
| 205 | if self.stack: self.queue.append(message) | |
| 206 | if recipient.lower() == self.nick.lower(): | |
| 207 | message.pm = True | |
| 208 | if message.msg.startswith("/msg ") and len(message.msg.split(" ")) >= 2: message.msg = " ".join(message.msg.split(" ")[2:])
| |
| 209 | self.onPM(user, message) | |
| 210 | else: | |
| 211 | self.onMessage(user, message) | |
| 212 | elif cmd == "registered": | |
| 213 | if self.nick: self.setNick() | |
| 214 | user = self._getUser(pars[0]) | |
| 215 | user.id = pars[1] | |
| 216 | self.onRegister(user) | |
| 217 | elif cmd == "topic": | |
| 218 | self.topic = pars[0] | |
| 219 | self.onTopic(self.topic) | |
| 220 | elif cmd == "nick": | |
| 221 | user = self._getUser(pars[0]) | |
| 222 | old = user.nick | |
| 223 | user.nick = pars[1] | |
| 224 | if old.lower() in self.users.keys(): | |
| 225 | del self.users[old.lower()] | |
| 226 | self.users[user.nick.lower()] = user | |
| 227 | if user.nick == self.nick: | |
| 228 | self.userID = user.id | |
| 229 | self.cauth(); | |
| 230 | self.onNickChange(user.nick, old, user) | |
| 231 | elif cmd == "quit": | |
| 232 | user = self.users[pars[0].lower()] | |
| 233 | del self.users[pars[0].lower()] | |
| 234 | self.onQuit(user) | |
| 235 | elif cmd == "kick": | |
| 236 | self.onBan(user = self.users[pars[1].lower()]) | |
| 237 | elif cmd == "join": | |
| 238 | user = self._getUser(pars[1]) | |
| 239 | user.id = pars[0] | |
| 240 | self.onJoin(user) | |
| 241 | elif cmd == "joins": | |
| 242 | for i in range((len(pars) - 1) / 2): | |
| 243 | user = self._getUser(pars[i*2 + 2]) | |
| 244 | user.id = pars[i*2 + 1] | |
| 245 | #self.cauth(); | |
| 246 | elif cmd == "joinsdone": | |
| 247 | self.onUserList(self.users) | |
| 248 | except: | |
| 249 | self.disconnect() | |
| 250 | ||
| 251 | def _getUser(self, nick): | |
| 252 | if not nick.lower() in self.users.keys(): self.users[nick.lower()] = TinychatUser(nick) | |
| 253 | return self.users[nick.lower()] | |
| 254 | ||
| 255 | def _decodeMessage(self, msg): | |
| 256 | chars = msg.split(",")
| |
| 257 | msg = "" | |
| 258 | for i in chars: | |
| 259 | msg += chr(int(i)) | |
| 260 | return msg | |
| 261 | ||
| 262 | def _encodeMessage(self, msg): | |
| 263 | msg2 = [] | |
| 264 | for i in msg: | |
| 265 | msg2.append(str(ord(i))) | |
| 266 | return ",".join(msg2) | |
| 267 | ||
| 268 | def _sendCommand(self, cmd, pars=[]): | |
| 269 | msg = {"msg": rtmp_protocol.DataTypes.COMMAND, "command": [u"" + cmd, 0, None,] + pars}
| |
| 270 | if DEBUG: print("CLIENT: " + str(msg))
| |
| 271 | self.connection.writer.write(msg) | |
| 272 | self.connection.writer.flush() | |
| 273 | ||
| 274 | def _sendCauth(self, cauth): | |
| 275 | self._sendCommand("cauth", [cauth])
| |
| 276 | ||
| 277 | def _getTcUrl(self): | |
| 278 | return parseString(httpRequest("apl.tinychat.com", "/api/find.room/" + self.room + "?site=tinychat&url=tinychat.com")[1]).getElementsByTagName("response")[0].getAttribute("rtmp")
| |
| 279 | ||
| 280 | def _getTcCauth(self): | |
| 281 | return self._uglyStripCauthKey(httpRequest("tinychat.com", "/api/captcha/check.php?room=tinychat^" + self.room + "&guest_id=" + self.userID)[1])
| |
| 282 | ||
| 283 | def _uglyStripCauthKey(self, json): | |
| 284 | cauth = json.split('"key":"')[1].split('"')[0]
| |
| 285 | print("CAUTH KEY: " + cauth)
| |
| 286 | return cauth | |
| 287 | ||
| 288 | def _getAutoOp(self): | |
| 289 | if self.nick and self.passwd: | |
| 290 | 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]
| |
| 291 | cookies = [] | |
| 292 | for header in headers: | |
| 293 | if header[0].lower() == "set-cookie": | |
| 294 | parts = header[1].split(", ")
| |
| 295 | for i in range(len(parts)): | |
| 296 | part = parts[i] | |
| 297 | part2 = part.split(";")[0]
| |
| 298 | if "=" in part2: | |
| 299 | if "," in part2: | |
| 300 | part2 = part2.split(", ")[1]
| |
| 301 | cookies.append(part2) | |
| 302 | break | |
| 303 | if len(cookies) > 0: | |
| 304 | html = httpRequest("tinychat.com", "/" + self.room, "", {"Content-Type": "application/x-www-form-urlencoded", "Cookie": "; ".join(cookies)})[1]
| |
| 305 | if ", autoop: \"" in html: | |
| 306 | return html.split(", autoop: \"")[1].split("\"")[0]
| |
| 307 | return u"none" | |
| 308 | ||
| 309 | if __name__ == "__main__": | |
| 310 | room = TinychatRoom(raw_input("Enter room name: "), raw_input("Enter username (optional): "), raw_input("Enter password (optional): "))
| |
| 311 | thread.start_new_thread(room.connect, ()) | |
| 312 | while not room.connected: time.sleep(1) | |
| 313 | while room.connected: | |
| 314 | msg = raw_input() | |
| 315 | if len(msg) > 0: | |
| 316 | if msg[0] == "/": | |
| 317 | msg = msg[1:] | |
| 318 | if len(msg) > 0: | |
| 319 | parts = msg.split(" ")
| |
| 320 | if len(parts) == 1: | |
| 321 | cmd = parts[0] | |
| 322 | pars = [] | |
| 323 | par = "" | |
| 324 | else: | |
| 325 | cmd = parts[0] | |
| 326 | pars = parts[1:] | |
| 327 | par = " ".join(parts[1:]) | |
| 328 | if cmd.lower() == "say": | |
| 329 | room.say(par) | |
| 330 | elif cmd.lower() == "pm": | |
| 331 | if len(pars) > 1: | |
| 332 | room.pm(" ".join(pars[1:]), pars[0])
| |
| 333 | else: | |
| 334 | print("Please supply the recipient's nick as well as the message to send")
| |
| 335 | elif cmd.lower() == "nick": | |
| 336 | room.setNick(par) | |
| 337 | elif cmd.lower() == "color": | |
| 338 | room.cycleColor() | |
| 339 | elif cmd.lower() == "ban": | |
| 340 | room.ban(par) | |
| 341 | elif cmd.lower() == "quit": | |
| 342 | room.disconnect() | |
| 343 | elif cmd.lower() == "playyoutube": | |
| 344 | room.playYoutube(par) | |
| 345 | elif cmd.lower() == "stopyoutube": | |
| 346 | room.stopYoutube() | |
| 347 | elif cmd.lower() == "playsoundcloud": | |
| 348 | room.playSoundcloud(par) | |
| 349 | elif cmd.lower() == "stopsoundcloud": | |
| 350 | room.stopSoundcloud() | |
| 351 | elif cmd.lower() == "cauth": | |
| 352 | room.cauth() | |
| 353 | else: | |
| 354 | room.say(msg) |