Advertisement
Guest User

Untitled

a guest
Aug 2nd, 2016
133
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 50.31 KB | None | 0 0
  1.  
  2.  
  3. ################################################################
  4.  
  5. # File: ch.py
  6.  
  7. # Title: Chatango Library
  8.  
  9. # Author: Lumirayz <lumirayz@gmail.com>
  10.  
  11. # Version: 1.3
  12.  
  13. # Description:
  14.  
  15. # An event-based library for connecting to one or multiple Chatango rooms, has
  16.  
  17. # support for several things including: messaging, message font,
  18.  
  19. # name color, deleting, banning, recent history, 2 userlist modes,
  20.  
  21. # flagging, avoiding flood bans, detecting flags, message bg in pm's, idling in pm's.
  22.  
  23. ################################################################
  24.  
  25.  
  26.  
  27. ################################################################
  28.  
  29. # License
  30.  
  31. ################################################################
  32.  
  33. # Copyright 2011 Lumirayz
  34.  
  35. # This program is distributed under the terms of the GNU GPL.
  36.  
  37.  
  38.  
  39. ################################################################
  40.  
  41. # Imports
  42.  
  43. ################################################################
  44.  
  45. import socket
  46.  
  47. import threading
  48.  
  49. import time
  50.  
  51. import random
  52.  
  53. import re
  54.  
  55. import sys
  56.  
  57. import select
  58.  
  59. import os
  60.  
  61. ################################################################
  62.  
  63. # Python 2 compatibility
  64.  
  65. ################################################################
  66.  
  67.  
  68. if sys.version_info[0] < 3:
  69.  
  70. class urllib:
  71.  
  72. parse = __import__("urllib")
  73.  
  74. request = __import__("urllib2")
  75.  
  76. input = raw_input
  77.  
  78. else:
  79.  
  80. import urllib.request
  81.  
  82. import urllib.parse
  83.  
  84.  
  85.  
  86. ################################################################
  87.  
  88. # Constants
  89.  
  90. ################################################################
  91.  
  92. Userlist_Recent = 0
  93.  
  94. Userlist_All = 1
  95.  
  96.  
  97.  
  98. BigMessage_Multiple = 0
  99.  
  100. BigMessage_Cut = 1
  101.  
  102.  
  103.  
  104. ################################################################
  105.  
  106. # Tagserver stuff
  107.  
  108. ################################################################
  109.  
  110. specials = {'mitvcanal': 56, 'magicc666': 67, 'livenfree': 18, 'eplsiite': 56, 'soccerjumbo2': 21, 'bguk': 67, 'animachat20': 34, 'cricket365live': 21, 'pokemonepisodeorg': 22, 'sport24lt': 56, 'mywowpinoy': 5, 'phnoytalk': 21, 'futboldirectochat': 67, 'flowhot-chat-online': 12, 'watchanimeonn': 26, 'cricvid-hitcric-': 51, 'bobproctor': 56, 'fullsportshd2': 18, 'chia-anime': 12, 'narutochatt': 52, 'animeproxer': 55, 'ttvsports': 56, 'leeplarp': 27, 'portalsports': 18, 'stream2watch3': 56, 'proudlypinoychat': 51, 'ver-anime': 34, 'iluvpinas': 53, 'vipstand': 21, 'eafangames': 56, 'worldfootballusch2': 18, 'soccerjumbo': 21, 'myfoxdfw': 67, 'animelinkz': 20, 'rgsmotrisport': 51, 'bateriafina-8': 8, 'as-chatroom': 10, 'dbzepisodeorg': 12, 'watch-dragonball': 19, 'narutowire': 10, 'tvanimefreak': 54}
  111.  
  112. tsweights = [['5', 75], ['6', 75], ['7', 75], ['8', 75], ['16', 75], ['17', 75], ['18', 75], ['9', 95], ['11', 95], ['12', 95], ['13', 95], ['14', 95], ['15', 95], ['19', 110], ['23', 110], ['24', 110], ['25', 110], ['26', 110], ['28', 104], ['29', 104], ['30', 104], ['31', 104], ['32', 104], ['33', 104], ['35', 101], ['36', 101], ['37', 101], ['38', 101], ['39', 101], ['40', 101], ['41', 101], ['42', 101], ['43', 101], ['44', 101], ['45', 101], ['46', 101], ['47', 101], ['48', 101], ['49', 101], ['50', 101], ['52', 110], ['53', 110], ['55', 110], ['57', 110], ['58', 110], ['59', 110], ['60', 110], ['61', 110], ['62', 110], ['63', 110], ['64', 110], ['65', 110], ['66', 110], ['68', 95], ['71', 116], ['72', 116], ['73', 116], ['74', 116], ['75', 116], ['76', 116], ['77', 116], ['78', 116], ['79', 116], ['80', 116], ['81', 116], ['82', 116], ['83', 116], ['84', 116]]
  113.  
  114.  
  115.  
  116. def getServer(group):
  117.  
  118. """
  119.  
  120. Get the server host for a certain room.
  121.  
  122.  
  123.  
  124. @type group: str
  125.  
  126. @param group: room name
  127.  
  128.  
  129.  
  130. @rtype: str
  131.  
  132. @return: the server's hostname
  133.  
  134. """
  135.  
  136. try:
  137.  
  138. sn = specials[group]
  139.  
  140. except KeyError:
  141.  
  142. group = group.replace("_", "q")
  143.  
  144. group = group.replace("-", "q")
  145.  
  146. fnv = float(int(group[0:min(5, len(group))], 36))
  147.  
  148. lnv = group[6: (6 + min(3, len(group) - 5))]
  149.  
  150. if(lnv):
  151.  
  152. lnv = float(int(lnv, 36))
  153.  
  154. if(lnv <= 1000):
  155.  
  156. lnv = 1000
  157.  
  158. else:
  159.  
  160. lnv = 1000
  161.  
  162. num = (fnv % lnv) / lnv
  163.  
  164. maxnum = sum(map(lambda x: x[1], tsweights))
  165.  
  166. cumfreq = 0
  167.  
  168. sn = 0
  169.  
  170. for wgt in tsweights:
  171.  
  172. cumfreq += float(wgt[1]) / maxnum
  173.  
  174. if(num <= cumfreq):
  175.  
  176. sn = int(wgt[0])
  177.  
  178. break
  179.  
  180. return "s" + str(sn) + ".chatango.com"
  181.  
  182.  
  183.  
  184. ################################################################
  185.  
  186. # Uid
  187.  
  188. ################################################################
  189.  
  190. def genUid():
  191.  
  192. return str(random.randrange(10 ** 15, 10 ** 16))
  193.  
  194.  
  195.  
  196. ################################################################
  197.  
  198. # Message stuff
  199.  
  200. ################################################################
  201.  
  202. def clean_message(msg):
  203.  
  204. """
  205.  
  206. Clean a message and return the message, n tag and f tag.
  207.  
  208.  
  209.  
  210. @type msg: str
  211.  
  212. @param msg: the message
  213.  
  214.  
  215.  
  216. @rtype: str, str, str
  217.  
  218. @returns: cleaned message, n tag contents, f tag contents
  219.  
  220. """
  221.  
  222. n = re.search("<n(.*?)/>", msg)
  223.  
  224. if n: n = n.group(1)
  225.  
  226. f = re.search("<f(.*?)>", msg)
  227.  
  228. if f: f = f.group(1)
  229.  
  230. msg = re.sub("<n.*?/>", "", msg)
  231.  
  232. msg = re.sub("<f.*?>", "", msg)
  233.  
  234. msg = strip_html(msg)
  235.  
  236. msg = msg.replace("&lt;", "<")
  237.  
  238. msg = msg.replace("&gt;", ">")
  239.  
  240. msg = msg.replace("&quot;", "\"")
  241.  
  242. msg = msg.replace("&apos;", "'")
  243.  
  244. msg = msg.replace("&amp;", "&")
  245.  
  246. return msg, n, f
  247.  
  248.  
  249.  
  250. def strip_html(msg):
  251.  
  252. """Strip HTML."""
  253.  
  254. li = msg.split("<")
  255.  
  256. if len(li) == 1:
  257.  
  258. return li[0]
  259.  
  260. else:
  261.  
  262. ret = list()
  263.  
  264. for data in li:
  265.  
  266. data = data.split(">", 1)
  267.  
  268. if len(data) == 1:
  269.  
  270. ret.append(data[0])
  271.  
  272. elif len(data) == 2:
  273.  
  274. ret.append(data[1])
  275.  
  276. return "".join(ret)
  277.  
  278.  
  279.  
  280. def parseNameColor(n):
  281.  
  282. """This just returns its argument, should return the name color."""
  283.  
  284. #probably is already the name
  285.  
  286. return n
  287.  
  288.  
  289.  
  290. def parseFont(f):
  291.  
  292. """Parses the contents of a f tag and returns color, face and size."""
  293.  
  294. #' xSZCOL="FONT"'
  295.  
  296. try: #TODO: remove quick hack
  297.  
  298. sizecolor, fontface = f.split("=", 1)
  299.  
  300. sizecolor = sizecolor.strip()
  301.  
  302. size = int(sizecolor[1:3])
  303.  
  304. col = sizecolor[3:6]
  305.  
  306. if col == "": col = None
  307.  
  308. face = f.split("\"", 2)[1]
  309.  
  310. return col, face, size
  311.  
  312. except:
  313.  
  314. return None, None, None
  315.  
  316.  
  317.  
  318. ################################################################
  319.  
  320. # Anon id
  321.  
  322. ################################################################
  323.  
  324.  
  325.  
  326. # i don't know what the fuck got into my head while i wrote this, so
  327.  
  328. # TODO: FIX DIS SHIT
  329.  
  330. def getAnonId(n, ssid):
  331.  
  332. """Gets the anon's id."""
  333.  
  334. if n == None: n = "5504"
  335.  
  336. try:
  337.  
  338. return "".join(list(
  339.  
  340. map(lambda x: str(x[0] + x[1])[-1], list(zip(
  341.  
  342. list(map(lambda x: int(x), n)),
  343.  
  344. list(map(lambda x: int(x), ssid[4:]))
  345.  
  346. )))
  347.  
  348. ))
  349.  
  350. except ValueError:
  351.  
  352. return "NNNN"
  353.  
  354.  
  355.  
  356. ################################################################
  357.  
  358. # PM Auth
  359.  
  360. ################################################################
  361.  
  362. auth_re = re.compile(r"auth\.chatango\.com ?= ?([^;]*)", re.IGNORECASE)
  363.  
  364.  
  365.  
  366. def _getAuth(name, password):
  367.  
  368. """
  369.  
  370. Request an auid using name and password.
  371.  
  372.  
  373.  
  374. @type name: str
  375.  
  376. @param name: name
  377.  
  378. @type password: str
  379.  
  380. @param password: password
  381.  
  382.  
  383.  
  384. @rtype: str
  385.  
  386. @return: auid
  387.  
  388. """
  389.  
  390. data = urllib.parse.urlencode({
  391.  
  392. "user_id": name,
  393.  
  394. "password": password,
  395.  
  396. "storecookie": "on",
  397.  
  398. "checkerrors": "yes"
  399.  
  400. }).encode()
  401.  
  402. try:
  403.  
  404. resp = urllib.request.urlopen("http://chatango.com/login", data)
  405.  
  406. headers = resp.headers
  407.  
  408. except Exception:
  409.  
  410. return None
  411.  
  412. for header, value in headers.items():
  413.  
  414. if header.lower() == "set-cookie":
  415.  
  416. m = auth_re.search(value)
  417.  
  418. if m:
  419.  
  420. auth = m.group(1)
  421.  
  422. if auth == "":
  423.  
  424. return None
  425.  
  426. return auth
  427.  
  428. return None
  429.  
  430.  
  431.  
  432. ################################################################
  433.  
  434. # PM class
  435.  
  436. ################################################################
  437.  
  438. class PM(object):
  439.  
  440. """Manages a connection with Chatango PM."""
  441.  
  442. ####
  443.  
  444. # Init
  445.  
  446. ####
  447.  
  448. def __init__(self, mgr):
  449.  
  450. self._connected = False
  451.  
  452. self._mgr = mgr
  453.  
  454. self._idle = False
  455.  
  456. self.idle = 1
  457.  
  458. self._auid = None
  459.  
  460. self._blocklist = set()
  461.  
  462. self._contacts = set()
  463.  
  464. self._wlock = False
  465.  
  466. self._premium = False
  467.  
  468. self._firstCommand = True
  469.  
  470. self._wbuf = b""
  471.  
  472. self._wlockbuf = b""
  473.  
  474. self._rbuf = b""
  475.  
  476. self._pingTask = None
  477.  
  478. self._connect()
  479.  
  480.  
  481.  
  482. ####
  483.  
  484. # Connections
  485.  
  486. ####
  487.  
  488. def _connect(self, idle = False):
  489.  
  490. self._wbuf = b""
  491.  
  492. self._sock = socket.socket()
  493.  
  494. self._sock.connect((self._mgr._PMHost, self._mgr._PMPort))
  495.  
  496. self._sock.setblocking(False)
  497.  
  498. self._firstCommand = True
  499.  
  500. if not self._auth(): return
  501.  
  502. self._pingTask = self.mgr.setInterval(self._mgr._pingDelay, self.ping)
  503.  
  504. self._connected = True
  505.  
  506. if idle: self._idle = True
  507.  
  508.  
  509.  
  510. def _auth(self):
  511.  
  512. self._auid = _getAuth(self._mgr.name, self._mgr.password)
  513.  
  514. if self._auid == None:
  515.  
  516. self._sock.close()
  517.  
  518. self._callEvent("onLoginFail")
  519.  
  520. self._sock = None
  521.  
  522. return False
  523.  
  524. self._sendCommand("tlogin", self._auid, "2")
  525.  
  526. self._setWriteLock(True)
  527.  
  528. return True
  529.  
  530.  
  531.  
  532. def disconnect(self):
  533.  
  534. self._disconnect()
  535.  
  536. self._callEvent("onPMDisconnect")
  537.  
  538.  
  539.  
  540. def _disconnect(self):
  541.  
  542. self._connected = False
  543.  
  544. self._sock.close()
  545.  
  546. self._sock = None
  547.  
  548.  
  549.  
  550. ####
  551.  
  552. # Feed
  553.  
  554. ####
  555.  
  556. def _feed(self, data):
  557.  
  558. """
  559.  
  560. Feed data to the connection.
  561.  
  562.  
  563.  
  564. @type data: bytes
  565.  
  566. @param data: data to be fed
  567.  
  568. """
  569.  
  570. self._rbuf += data
  571.  
  572. while self._rbuf.find(b"\x00") != -1:
  573.  
  574. data = self._rbuf.split(b"\x00")
  575.  
  576. for food in data[:-1]:
  577.  
  578. self._process(food.decode("latin-1").rstrip("\r\n")) #numnumz ;3
  579.  
  580. self._rbuf = data[-1]
  581.  
  582.  
  583.  
  584. def _process(self, data):
  585.  
  586. """
  587.  
  588. Process a command string.
  589.  
  590.  
  591.  
  592. @type data: str
  593.  
  594. @param data: the command string
  595.  
  596. """
  597.  
  598. self._callEvent("onRaw", data)
  599.  
  600. data = data.split(":")
  601.  
  602. cmd, args = data[0], data[1:]
  603.  
  604. func = "rcmd_" + cmd
  605.  
  606. if hasattr(self, func):
  607.  
  608. getattr(self, func)(args)
  609.  
  610.  
  611.  
  612. ####
  613.  
  614. # Properties
  615.  
  616. ####
  617.  
  618. def getManager(self): return self._mgr
  619.  
  620. def getContacts(self): return self._contacts
  621.  
  622. def getBlocklist(self): return self._blocklist
  623.  
  624.  
  625.  
  626. mgr = property(getManager)
  627.  
  628. contacts = property(getContacts)
  629.  
  630. blocklist = property(getBlocklist)
  631.  
  632.  
  633.  
  634. ####
  635.  
  636. # Received Commands
  637.  
  638. ####
  639.  
  640. def rcmd_OK(self, args):
  641.  
  642. self._setWriteLock(False)
  643.  
  644. self._sendCommand("wl")
  645.  
  646. self._sendCommand("getblock")
  647.  
  648. self._sendCommand("getpremium", "1")
  649.  
  650. if self._idle:
  651.  
  652. self.setIdle()
  653.  
  654. self._callEvent("onPMConnect")
  655.  
  656.  
  657.  
  658. def rcmd_wl(self, args):
  659.  
  660. self._contacts = set()
  661.  
  662. for i in range(len(args) // 4):
  663.  
  664. name, last_on, is_on, idle = args[i * 4: i * 4 + 4]
  665.  
  666. user = User(name)
  667.  
  668. self._contacts.add(user)
  669.  
  670. self._callEvent("onPMContactlistReceive")
  671.  
  672.  
  673.  
  674. def rcmd_block_list(self, args):
  675.  
  676. self._blocklist = set()
  677.  
  678. for name in args:
  679.  
  680. if name == "": continue
  681.  
  682. self._blocklist.add(User(name))
  683.  
  684.  
  685.  
  686. def rcmd_DENIED(self, args):
  687.  
  688. self._disconnect()
  689.  
  690. self._callEvent("onLoginFail")
  691.  
  692.  
  693.  
  694. def rcmd_msg(self, args):
  695.  
  696. user = User(args[0])
  697.  
  698. body = strip_html(":".join(args[5:]))
  699.  
  700. self._callEvent("onPMMessage", user, body)
  701.  
  702.  
  703.  
  704. def rcmd_msgoff(self, args):
  705.  
  706. user = User(args[0])
  707.  
  708. body = strip_html(":".join(args[5:]))
  709.  
  710. self._callEvent("onPMOfflineMessage", user, body)
  711.  
  712.  
  713.  
  714. def rcmd_wlonline(self, args):
  715.  
  716. self._callEvent("onPMContactOnline", User(args[0]))
  717.  
  718.  
  719.  
  720. def rcmd_wloffline(self, args):
  721.  
  722. self._callEvent("onPMContactOffline", User(args[0]))
  723.  
  724.  
  725.  
  726. def rcmd_premium(self, args):
  727.  
  728. if float(args[1]) > time.time():
  729.  
  730. self._premium = True
  731.  
  732. if self._mgr.user._mbg: self.setBgMode(1)
  733.  
  734. if self._mgr.user._mrec: self.setRecordingMode(1)
  735.  
  736. else:
  737.  
  738. self._premium = False
  739.  
  740.  
  741.  
  742. def rcmd_kickingoff(self, args):
  743.  
  744. self.disconnect()
  745.  
  746.  
  747.  
  748. ####
  749.  
  750. # Commands
  751.  
  752. ####
  753.  
  754. def ping(self):
  755.  
  756. self._sendCommand("")
  757.  
  758. self._callEvent("onPMPing")
  759.  
  760. if self._idle:
  761.  
  762. if self.idle != 0:
  763.  
  764. self.setIdle()
  765.  
  766. self.idle = 0
  767.  
  768.  
  769.  
  770. def message(self, user, msg):
  771.  
  772. self.setActive()
  773.  
  774. if self._idle:
  775.  
  776. self.idle = 1
  777.  
  778. msg = msg.replace("<b>", "<B>").replace("<u>", "<U>").replace("<i>", "<I>").replace("</b>", "</B>").replace("</u>", "</U>").replace("</i>", "</I>")
  779.  
  780. self._sendCommand("msg", user.name, "<n%s/><m v=\"1\"><g xs0=\"0\"><g x%ss%s=\"6\">%s</g></g></m>" % (self._mgr.user.nameColor.lower(), self._mgr.user.fontSize, self._mgr.user.fontColor.lower(), msg))
  781.  
  782.  
  783.  
  784. def test(self, user):
  785.  
  786. self._sendCommand("connect", user.name)
  787.  
  788.  
  789.  
  790. def addContact(self, user):
  791.  
  792. if user not in self._contacts:
  793.  
  794. self._sendCommand("wladd", user.name)
  795.  
  796. self._contacts.add(user)
  797.  
  798. self._callEvent("onPMContactAdd", user)
  799.  
  800.  
  801.  
  802. def removeContact(self, user):
  803.  
  804. if user in self._contacts:
  805.  
  806. self._sendCommand("wldelete", user.name)
  807.  
  808. self._contacts.remove(user)
  809.  
  810. self._callEvent("onPMContactRemove", user)
  811.  
  812.  
  813.  
  814. def block(self, user):
  815.  
  816. if user not in self._blocklist:
  817.  
  818. self._sendCommand("block", user.name, user.name, "S")
  819.  
  820. self._block.add(user)
  821.  
  822. self._callEvent("onPMBlock", user)
  823.  
  824.  
  825.  
  826. def unblock(self, user):
  827.  
  828. if user in self._blocklist:
  829.  
  830. self._sendCommand("unblock", user.name)
  831.  
  832. self._block.remove(user)
  833.  
  834. self._callEvent("onPMUnblock", user)
  835.  
  836.  
  837.  
  838. def setBgMode(self, mode):
  839.  
  840. self._sendCommand("msgbg", str(mode))
  841.  
  842.  
  843.  
  844. def setRecordingMode(self, mode):
  845.  
  846. self._sendCommand("msgmedia", str(mode))
  847.  
  848.  
  849.  
  850. def setIdle(self):
  851.  
  852. self._sendCommand("idle:0")
  853.  
  854.  
  855.  
  856. def setActive(self):
  857.  
  858. self._sendCommand("idle:1")
  859.  
  860. ####
  861.  
  862. # Util
  863.  
  864. ####
  865.  
  866. def _callEvent(self, evt, *args, **kw):
  867.  
  868. getattr(self.mgr, evt)(self, *args, **kw)
  869.  
  870. self.mgr.onEventCalled(self, evt, *args, **kw)
  871.  
  872.  
  873.  
  874. def _write(self, data):
  875.  
  876. if self._wlock:
  877.  
  878. self._wlockbuf += data
  879.  
  880. else:
  881.  
  882. self.mgr._write(self, data)
  883.  
  884.  
  885.  
  886. def _setWriteLock(self, lock):
  887.  
  888. self._wlock = lock
  889.  
  890. if self._wlock == False:
  891.  
  892. self._write(self._wlockbuf)
  893.  
  894. self._wlockbuf = b""
  895.  
  896.  
  897.  
  898. def _sendCommand(self, *args):
  899.  
  900. """
  901.  
  902. Send a command.
  903.  
  904.  
  905.  
  906. @type args: [str, str, ...]
  907.  
  908. @param args: command and list of arguments
  909.  
  910. """
  911.  
  912. if self._firstCommand:
  913.  
  914. terminator = b"\x00"
  915.  
  916. self._firstCommand = False
  917.  
  918. else:
  919.  
  920. terminator = b"\r\n\x00"
  921.  
  922. self._write(":".join(args).encode() + terminator)
  923.  
  924.  
  925.  
  926. ################################################################
  927.  
  928. # Room class
  929.  
  930. ################################################################
  931.  
  932. class Room(object):
  933.  
  934. """Manages a connection with a Chatango room."""
  935.  
  936. ####
  937.  
  938. # Init
  939.  
  940. ####
  941.  
  942. def __init__(self, room, uid = None, server = None, port = None, mgr = None):
  943.  
  944. # Basic stuff
  945.  
  946. self._name = room
  947.  
  948. self._server = server or getServer(room)
  949.  
  950. self._port = port or 443
  951.  
  952. self._mgr = mgr
  953.  
  954.  
  955.  
  956. # Under the hood
  957.  
  958. self._connected = False
  959.  
  960. self._reconnecting = False
  961.  
  962. self._uid = uid or genUid()
  963.  
  964. self._rbuf = b""
  965.  
  966. self._wbuf = b""
  967.  
  968. self._wlockbuf = b""
  969.  
  970. self._owner = None
  971.  
  972. self._mods = set()
  973.  
  974. self._mqueue = dict()
  975.  
  976. self._history = list()
  977.  
  978. self._userlist = list()
  979.  
  980. self._firstCommand = True
  981.  
  982. self._connectAmmount = 0
  983.  
  984. self._premium = False
  985.  
  986. self._userCount = 0
  987.  
  988. self._pingTask = None
  989.  
  990. self._users = dict()
  991.  
  992. self._msgs = dict()
  993.  
  994. self._wlock = False
  995.  
  996. self._silent = False
  997.  
  998. self._banlist = list()
  999.  
  1000.  
  1001.  
  1002. # Inited vars
  1003.  
  1004. if self._mgr: self._connect()
  1005.  
  1006.  
  1007.  
  1008. ####
  1009.  
  1010. # User and Message management
  1011.  
  1012. ####
  1013.  
  1014. def getMessage(self, mid):
  1015.  
  1016. return self._msgs.get(mid)
  1017.  
  1018.  
  1019.  
  1020. def createMessage(self, msgid, **kw):
  1021.  
  1022. if msgid not in self._msgs:
  1023.  
  1024. msg = Message(msgid = msgid, **kw)
  1025.  
  1026. self._msgs[msgid] = msg
  1027.  
  1028. else:
  1029.  
  1030. msg = self._msgs[msgid]
  1031.  
  1032. return msg
  1033.  
  1034.  
  1035.  
  1036. ####
  1037.  
  1038. # Connect/disconnect
  1039.  
  1040. ####
  1041.  
  1042. def _connect(self):
  1043.  
  1044. """Connect to the server."""
  1045.  
  1046. self._sock = socket.socket()
  1047.  
  1048. self._sock.connect((self._server, self._port))
  1049.  
  1050. self._sock.setblocking(False)
  1051.  
  1052. self._firstCommand = True
  1053.  
  1054. self._wbuf = b""
  1055.  
  1056. self._auth()
  1057.  
  1058. self._pingTask = self.mgr.setInterval(self.mgr._pingDelay, self.ping)
  1059.  
  1060. if not self._reconnecting: self.connected = True
  1061.  
  1062.  
  1063.  
  1064. def reconnect(self):
  1065.  
  1066. """Reconnect."""
  1067.  
  1068. self._reconnect()
  1069.  
  1070.  
  1071.  
  1072. def _reconnect(self):
  1073.  
  1074. """Reconnect."""
  1075.  
  1076. self._reconnecting = True
  1077.  
  1078. if self.connected:
  1079.  
  1080. self._disconnect()
  1081.  
  1082. self._uid = genUid()
  1083.  
  1084. self._connect()
  1085.  
  1086. self._reconnecting = False
  1087.  
  1088.  
  1089.  
  1090. def disconnect(self):
  1091.  
  1092. """Disconnect."""
  1093.  
  1094. self._disconnect()
  1095.  
  1096. self._callEvent("onDisconnect")
  1097.  
  1098.  
  1099.  
  1100. def _disconnect(self):
  1101.  
  1102. """Disconnect from the server."""
  1103.  
  1104. if not self._reconnecting: self.connected = False
  1105.  
  1106. for user in self._userlist:
  1107.  
  1108. user.clearSessionIds(self)
  1109.  
  1110. self._userlist = list()
  1111.  
  1112. self._pingTask.cancel()
  1113.  
  1114. self._sock.close()
  1115.  
  1116. if not self._reconnecting: del self.mgr._rooms[self.name]
  1117.  
  1118.  
  1119.  
  1120. def _auth(self):
  1121.  
  1122. """Authenticate."""
  1123.  
  1124. self._sendCommand("bauth", self.name, self._uid, self.mgr.name, self.mgr.password)
  1125.  
  1126. self._setWriteLock(True)
  1127.  
  1128.  
  1129.  
  1130. ####
  1131.  
  1132. # Properties
  1133.  
  1134. ####
  1135.  
  1136. def getName(self): return self._name
  1137.  
  1138. def getManager(self): return self._mgr
  1139.  
  1140. def getUserlist(self, mode = None, unique = None, memory = None):
  1141.  
  1142. ul = None
  1143.  
  1144. if mode == None: mode = self.mgr._userlistMode
  1145.  
  1146. if unique == None: unique = self.mgr._userlistUnique
  1147.  
  1148. if memory == None: memory = self.mgr._userlistMemory
  1149.  
  1150. if mode == Userlist_Recent:
  1151.  
  1152. ul = map(lambda x: x.user, self._history[-memory:])
  1153.  
  1154. elif mode == Userlist_All:
  1155.  
  1156. ul = self._userlist
  1157.  
  1158. if unique:
  1159.  
  1160. return list(set(ul))
  1161.  
  1162. else:
  1163.  
  1164. return ul
  1165.  
  1166. def getUserNames(self):
  1167.  
  1168. ul = self.userlist
  1169.  
  1170. return list(map(lambda x: x.name, ul))
  1171.  
  1172. def getUser(self): return self.mgr.user
  1173.  
  1174. def getOwner(self): return self._owner
  1175.  
  1176. def getOwnerName(self): return self._owner.name
  1177.  
  1178. def getMods(self):
  1179.  
  1180. newset = set()
  1181.  
  1182. for mod in self._mods:
  1183.  
  1184. newset.add(mod)
  1185.  
  1186. return newset
  1187.  
  1188. def getModNames(self):
  1189.  
  1190. mods = self.getMods()
  1191.  
  1192. return [x.name for x in mods]
  1193.  
  1194. def getUserCount(self): return self._userCount
  1195.  
  1196. def getSilent(self): return self._silent
  1197.  
  1198. def setSilent(self, val): self._silent = val
  1199.  
  1200. def getBanlist(self): return [record[2] for record in self._banlist]
  1201.  
  1202.  
  1203.  
  1204. name = property(getName)
  1205.  
  1206. mgr = property(getManager)
  1207.  
  1208. userlist = property(getUserlist)
  1209.  
  1210. usernames = property(getUserNames)
  1211.  
  1212. user = property(getUser)
  1213.  
  1214. owner = property(getOwner)
  1215.  
  1216. ownername = property(getOwnerName)
  1217.  
  1218. mods = property(getMods)
  1219.  
  1220. modnames = property(getModNames)
  1221.  
  1222. usercount = property(getUserCount)
  1223.  
  1224. silent = property(getSilent, setSilent)
  1225.  
  1226. banlist = property(getBanlist)
  1227.  
  1228.  
  1229.  
  1230. ####
  1231.  
  1232. # Feed/process
  1233.  
  1234. ####
  1235.  
  1236. def _feed(self, data):
  1237.  
  1238. """
  1239.  
  1240. Feed data to the connection.
  1241.  
  1242.  
  1243.  
  1244. @type data: bytes
  1245.  
  1246. @param data: data to be fed
  1247.  
  1248. """
  1249.  
  1250. self._rbuf += data
  1251.  
  1252. while self._rbuf.find(b"\x00") != -1:
  1253.  
  1254. data = self._rbuf.split(b"\x00")
  1255.  
  1256. for food in data[:-1]:
  1257. try:
  1258. self._process(food.decode("latin-1").rstrip("\r\n")) #numnumz ;3
  1259. except:
  1260. pass
  1261. self._rbuf = data[-1]
  1262.  
  1263.  
  1264.  
  1265. def _process(self, data):
  1266.  
  1267. """
  1268.  
  1269. Process a command string.
  1270.  
  1271.  
  1272.  
  1273. @type data: str
  1274.  
  1275. @param data: the command string
  1276.  
  1277. """
  1278.  
  1279. self._callEvent("onRaw", data)
  1280.  
  1281. data = data.split(":")
  1282.  
  1283. cmd, args = data[0], data[1:]
  1284.  
  1285. func = "rcmd_" + cmd
  1286.  
  1287. if hasattr(self, func):
  1288.  
  1289. getattr(self, func)(args)
  1290.  
  1291.  
  1292.  
  1293. ####
  1294.  
  1295. # Received Commands
  1296.  
  1297. ####
  1298.  
  1299. def rcmd_ok(self, args):
  1300.  
  1301. if args[2] != "M": #unsuccesful login
  1302.  
  1303. self._callEvent("onLoginFail")
  1304.  
  1305. self.disconnect()
  1306.  
  1307. self._owner = User(args[0])
  1308.  
  1309. self._uid = args[1]
  1310.  
  1311. self._aid = args[1][4:8]
  1312.  
  1313. self._mods = set(map(lambda x: User(x), args[6].split(";")))
  1314.  
  1315. self._i_log = list()
  1316.  
  1317.  
  1318.  
  1319. def rcmd_denied(self, args):
  1320.  
  1321. self._disconnect()
  1322.  
  1323. self._callEvent("onConnectFail")
  1324.  
  1325.  
  1326.  
  1327. def rcmd_inited(self, args):
  1328.  
  1329. self._sendCommand("g_participants", "start")
  1330.  
  1331. self._sendCommand("getpremium", "1")
  1332.  
  1333. self.requestBanlist()
  1334.  
  1335. if self._connectAmmount == 0:
  1336.  
  1337. self._callEvent("onConnect")
  1338.  
  1339. for msg in reversed(self._i_log):
  1340.  
  1341. user = msg.user
  1342.  
  1343. self._callEvent("onHistoryMessage", user, msg)
  1344.  
  1345. self._addHistory(msg)
  1346.  
  1347. del self._i_log
  1348.  
  1349. else:
  1350.  
  1351. self._callEvent("onReconnect")
  1352.  
  1353. self._connectAmmount += 1
  1354.  
  1355. self._setWriteLock(False)
  1356.  
  1357.  
  1358.  
  1359. def rcmd_premium(self, args):
  1360.  
  1361. if float(args[1]) > time.time():
  1362.  
  1363. self._premium = True
  1364.  
  1365. if self.user._mbg: self.setBgMode(1)
  1366.  
  1367. if self.user._mrec: self.setRecordingMode(1)
  1368.  
  1369. else:
  1370.  
  1371. self._premium = False
  1372.  
  1373.  
  1374.  
  1375. def rcmd_mods(self, args):
  1376.  
  1377. modnames = args
  1378.  
  1379. mods = set(map(lambda x: User(x), modnames))
  1380.  
  1381. premods = self._mods
  1382.  
  1383. for user in mods - premods: #modded
  1384.  
  1385. self._mods.add(user)
  1386.  
  1387. self._callEvent("onModAdd", user)
  1388.  
  1389. for user in premods - mods: #demodded
  1390.  
  1391. self._mods.remove(user)
  1392.  
  1393. self._callEvent("onModRemove", user)
  1394.  
  1395. self._callEvent("onModChange")
  1396.  
  1397.  
  1398.  
  1399. def rcmd_b(self, args):
  1400.  
  1401. mtime = float(args[0])
  1402.  
  1403. puid = args[3]
  1404.  
  1405. ip = args[6]
  1406.  
  1407. name = args[1]
  1408.  
  1409. rawmsg = ":".join(args[9:])
  1410.  
  1411. msg, n, f = clean_message(rawmsg)
  1412.  
  1413. if name == "":
  1414.  
  1415. nameColor = None
  1416.  
  1417. name = "#" + args[2]
  1418.  
  1419. if name == "#":
  1420.  
  1421. name = "!anon" + getAnonId(n, puid)
  1422.  
  1423. else:
  1424.  
  1425. if n: nameColor = parseNameColor(n)
  1426.  
  1427. else: nameColor = None
  1428.  
  1429. i = args[5]
  1430.  
  1431. unid = args[4]
  1432.  
  1433. #Create an anonymous message and queue it because msgid is unknown.
  1434.  
  1435. if f: fontColor, fontFace, fontSize = parseFont(f)
  1436.  
  1437. else: fontColor, fontFace, fontSize = None, None, None
  1438.  
  1439. msg = Message(
  1440.  
  1441. time = mtime,
  1442.  
  1443. user = User(name),
  1444.  
  1445. body = msg,
  1446.  
  1447. raw = rawmsg,
  1448.  
  1449. uid = puid,
  1450.  
  1451. ip = ip,
  1452.  
  1453. nameColor = nameColor,
  1454.  
  1455. fontColor = fontColor,
  1456.  
  1457. fontFace = fontFace,
  1458.  
  1459. fontSize = fontSize,
  1460.  
  1461. unid = unid,
  1462.  
  1463. room = self
  1464.  
  1465. )
  1466.  
  1467. self._mqueue[i] = msg
  1468.  
  1469.  
  1470.  
  1471. def rcmd_u(self, args):
  1472.  
  1473. msg = self._mqueue[args[0]]
  1474.  
  1475. if msg.user != self.user:
  1476.  
  1477. msg.user._fontColor = msg.fontColor
  1478.  
  1479. msg.user._fontFace = msg.fontFace
  1480.  
  1481. msg.user._fontSize = msg.fontSize
  1482.  
  1483. msg.user._nameColor = msg.nameColor
  1484.  
  1485. del self._mqueue[args[0]]
  1486.  
  1487. msg.attach(self, args[1])
  1488.  
  1489. self._addHistory(msg)
  1490.  
  1491. self._callEvent("onMessage", msg.user, msg)
  1492.  
  1493.  
  1494.  
  1495. def rcmd_i(self, args):
  1496.  
  1497. mtime = float(args[0])
  1498.  
  1499. puid = args[3]
  1500.  
  1501. ip = args[6]
  1502.  
  1503. if ip == "": ip = None
  1504.  
  1505. name = args[1]
  1506.  
  1507. rawmsg = ":".join(args[8:])
  1508.  
  1509. msg, n, f = clean_message(rawmsg)
  1510.  
  1511. msgid = args[5]
  1512.  
  1513. if name == "":
  1514.  
  1515. nameColor = None
  1516.  
  1517. name = "#" + args[2]
  1518.  
  1519. if name == "#":
  1520.  
  1521. name = "!anon" + getAnonId(n, puid)
  1522.  
  1523. else:
  1524.  
  1525. if n: nameColor = parseNameColor(n)
  1526.  
  1527. else: nameColor = None
  1528.  
  1529. if f: fontColor, fontFace, fontSize = parseFont(f)
  1530.  
  1531. else: fontColor, fontFace, fontSize = None, None, None
  1532.  
  1533. msg = self.createMessage(
  1534.  
  1535. msgid = msgid,
  1536.  
  1537. time = mtime,
  1538.  
  1539. user = User(name),
  1540.  
  1541. body = msg,
  1542.  
  1543. raw = rawmsg,
  1544.  
  1545. ip = args[6],
  1546.  
  1547. unid = args[4],
  1548.  
  1549. nameColor = nameColor,
  1550.  
  1551. fontColor = fontColor,
  1552.  
  1553. fontFace = fontFace,
  1554.  
  1555. fontSize = fontSize,
  1556.  
  1557. room = self
  1558.  
  1559. )
  1560.  
  1561. if msg.user != self.user:
  1562.  
  1563. msg.user._fontColor = msg.fontColor
  1564.  
  1565. msg.user._fontFace = msg.fontFace
  1566.  
  1567. msg.user._fontSize = msg.fontSize
  1568.  
  1569. msg.user._nameColor = msg.nameColor
  1570.  
  1571. self._i_log.append(msg)
  1572.  
  1573.  
  1574.  
  1575. def rcmd_g_participants(self, args):
  1576.  
  1577. args = ":".join(args)
  1578.  
  1579. args = args.split(";")
  1580.  
  1581. for data in args:
  1582.  
  1583. data = data.split(":")
  1584.  
  1585. name = data[3].lower()
  1586.  
  1587. if name == "none": continue
  1588.  
  1589. user = User(
  1590.  
  1591. name = name,
  1592.  
  1593. room = self
  1594.  
  1595. )
  1596.  
  1597. user.addSessionId(self, data[0])
  1598.  
  1599. self._userlist.append(user)
  1600.  
  1601.  
  1602.  
  1603. def rcmd_participant(self, args):
  1604.  
  1605. if args[0] == "0": #leave
  1606.  
  1607. name = args[3].lower()
  1608.  
  1609. if name == "none": return
  1610.  
  1611. user = User(name)
  1612.  
  1613. user.removeSessionId(self, args[1])
  1614.  
  1615. self._userlist.remove(user)
  1616.  
  1617. if user not in self._userlist or not self.mgr._userlistEventUnique:
  1618.  
  1619. self._callEvent("onLeave", user)
  1620.  
  1621. else: #join
  1622.  
  1623. name = args[3].lower()
  1624.  
  1625. if name == "none": return
  1626.  
  1627. user = User(
  1628.  
  1629. name = name,
  1630.  
  1631. room = self
  1632.  
  1633. )
  1634.  
  1635. user.addSessionId(self, args[1])
  1636.  
  1637. if user not in self._userlist: doEvent = True
  1638.  
  1639. else: doEvent = False
  1640.  
  1641. self._userlist.append(user)
  1642.  
  1643. if doEvent or not self.mgr._userlistEventUnique:
  1644.  
  1645. self._callEvent("onJoin", user)
  1646.  
  1647.  
  1648.  
  1649. def rcmd_show_fw(self, args):
  1650.  
  1651. self._callEvent("onFloodWarning")
  1652.  
  1653.  
  1654.  
  1655. def rcmd_show_tb(self, args):
  1656.  
  1657. self._callEvent("onFloodBan")
  1658.  
  1659.  
  1660.  
  1661. def rcmd_tb(self, args):
  1662.  
  1663. self._callEvent("onFloodBanRepeat")
  1664.  
  1665.  
  1666.  
  1667. def rcmd_delete(self, args):
  1668.  
  1669. msg = self.getMessage(args[0])
  1670.  
  1671. if msg:
  1672.  
  1673. if msg in self._history:
  1674.  
  1675. self._history.remove(msg)
  1676.  
  1677. self._callEvent("onMessageDelete", msg.user, msg)
  1678.  
  1679. msg.detach()
  1680.  
  1681.  
  1682.  
  1683. def rcmd_deleteall(self, args):
  1684.  
  1685. for msgid in args:
  1686.  
  1687. self.rcmd_delete([msgid])
  1688.  
  1689.  
  1690.  
  1691. def rcmd_n(self, args):
  1692.  
  1693. self._userCount = int(args[0], 16)
  1694.  
  1695. self._callEvent("onUserCountChange")
  1696.  
  1697.  
  1698. def rcmd_blocklist(self, args):
  1699.  
  1700. self._banlist = list()
  1701.  
  1702. sections = ":".join(args).split(";")
  1703.  
  1704. for section in sections:
  1705.  
  1706. params = section.split(":")
  1707.  
  1708. if len(params) != 5: continue
  1709.  
  1710. if params[2] == "": continue
  1711.  
  1712. self._banlist.append((
  1713.  
  1714. params[0], #unid
  1715.  
  1716. params[1], #ip
  1717.  
  1718. User(params[2]), #target
  1719.  
  1720. float(params[3]), #time
  1721.  
  1722. User(params[4]) #src
  1723.  
  1724. ))
  1725.  
  1726. self._callEvent("onBanlistUpdate")
  1727.  
  1728.  
  1729.  
  1730. def rcmd_blocked(self, args):
  1731.  
  1732. if args[2] == "": return
  1733.  
  1734. target = User(args[2])
  1735.  
  1736. user = User(args[3])
  1737.  
  1738. self._banlist.append((args[0], args[1], target, float(args[4]), user))
  1739.  
  1740. self._callEvent("onBan", user, target)
  1741.  
  1742. self.requestBanlist()
  1743.  
  1744.  
  1745.  
  1746. def rcmd_unblocked(self, args):
  1747.  
  1748. if args[2] == "": return
  1749.  
  1750. target = User(args[2])
  1751.  
  1752. user = User(args[3])
  1753.  
  1754. self._callEvent("onUnban", user, target)
  1755.  
  1756. self.requestBanlist()
  1757.  
  1758.  
  1759.  
  1760. ####
  1761.  
  1762. # Commands
  1763.  
  1764. ####
  1765.  
  1766. def ping(self):
  1767.  
  1768. """Send a ping."""
  1769.  
  1770. self._sendCommand("")
  1771.  
  1772. self._callEvent("onPing")
  1773.  
  1774.  
  1775.  
  1776. def rawMessage(self, msg):
  1777.  
  1778. """
  1779.  
  1780. Send a message without n and f tags.
  1781.  
  1782.  
  1783.  
  1784. @type msg: str
  1785.  
  1786. @param msg: message
  1787.  
  1788. """
  1789.  
  1790. if not self._silent:
  1791.  
  1792. self._sendCommand("bmsg:tl2r", msg)
  1793.  
  1794.  
  1795.  
  1796. def message(self, msg, html = False):
  1797.  
  1798. """
  1799.  
  1800. Send a message.
  1801.  
  1802.  
  1803.  
  1804. @type msg: str
  1805.  
  1806. @param msg: message
  1807.  
  1808. """
  1809.  
  1810. if not html:
  1811.  
  1812. msg = msg.replace("<", "&lt;").replace(">", "&gt;")
  1813.  
  1814. if len(msg) > self.mgr._maxLength:
  1815.  
  1816. if self.mgr._tooBigMessage == BigMessage_Cut:
  1817.  
  1818. self.message(msg[:self.mgr._maxLength], html = html)
  1819.  
  1820. elif self.mgr._tooBigMessage == BigMessage_Multiple:
  1821.  
  1822. while len(msg) > 0:
  1823.  
  1824. sect = msg[:self.mgr._maxLength]
  1825.  
  1826. msg = msg[self.mgr._maxLength:]
  1827.  
  1828. self.message(sect, html = html)
  1829.  
  1830. return
  1831.  
  1832. msg = "<n" + self.user.nameColor + "/>" + msg
  1833.  
  1834. msg = "<f x%0.2i%s=\"%s\">" %(self.user.fontSize, self.user.fontColor, self.user.fontFace) + msg
  1835.  
  1836. self.rawMessage(msg)
  1837.  
  1838.  
  1839.  
  1840. def setBgMode(self, mode):
  1841.  
  1842. self._sendCommand("msgbg", str(mode))
  1843.  
  1844.  
  1845.  
  1846. def setRecordingMode(self, mode):
  1847.  
  1848. self._sendCommand("msgmedia", str(mode))
  1849.  
  1850.  
  1851. def addMod(self, user):
  1852. if self.getLevel(User(self.currentname)) == 2:
  1853. self._sendCommand("addmod", user.name)
  1854. def removeMod(self, user):
  1855. if self.getLevel(User(self.currentname)) == 2:
  1856. self._sendCommand("removemod", user.name)
  1857.  
  1858.  
  1859.  
  1860. def flag(self, message):
  1861.  
  1862. """
  1863.  
  1864. Flag a message.
  1865.  
  1866.  
  1867.  
  1868. @type message: Message
  1869.  
  1870. @param message: message to flag
  1871.  
  1872. """
  1873.  
  1874. self._sendCommand("g_flag", message.msgid)
  1875.  
  1876.  
  1877.  
  1878. def flagUser(self, user):
  1879.  
  1880. """
  1881.  
  1882. Flag a user.
  1883.  
  1884.  
  1885.  
  1886. @type user: User
  1887.  
  1888. @param user: user to flag
  1889.  
  1890.  
  1891.  
  1892. @rtype: bool
  1893.  
  1894. @return: whether a message to flag was found
  1895.  
  1896. """
  1897.  
  1898. msg = self.getLastMessage(user)
  1899.  
  1900. if msg:
  1901.  
  1902. self.flag(msg)
  1903.  
  1904. return True
  1905.  
  1906. return False
  1907.  
  1908.  
  1909.  
  1910. def delete(self, message):
  1911. if self.getLevel(self.user) > 0:
  1912. msg = self.getLastMessage(user)
  1913. if msg:
  1914. self._sendCommand("delmsg", msg.msgid)
  1915. return True
  1916. return False
  1917.  
  1918.  
  1919. def clearUser(self, user):
  1920.  
  1921. if self.getLevel(self.user) > 0:
  1922. msg = self.getLastMessage(user)
  1923. unid = None
  1924. if msg:
  1925. unid = msg.unid
  1926. if unid:
  1927. if user.name[0] in ["!","#"]:
  1928. self._sendCommand("delallmsg", unid, msg.ip, "")
  1929. else:
  1930. self._sendCommand("delallmsg", unid, msg.ip, user.name)
  1931. return True
  1932. return False
  1933.  
  1934.  
  1935. def clearall(self):
  1936. """Clear all messages. (Owner only)""" ##<---BULLSHIT! :P
  1937. if self.getLevel(self.user) > 0:
  1938. if self.user == self._owner:
  1939. self._sendCommand("clearall")
  1940. else:
  1941. mArray = self._msgs.values()
  1942. for user in list(set([x.user for x in mArray])):
  1943. msg = self.getLastMessage(user)
  1944. if msg and hasattr(msg, 'unid'):
  1945. self.clearUser(user)
  1946. return True
  1947. return False
  1948.  
  1949.  
  1950.  
  1951. def ban(self, user):
  1952. msg = self.getLastMessage(user)
  1953. if msg:
  1954. unid = msg.unid
  1955. if unid:
  1956. if user.name[0] in ['!','#']:
  1957. self._sendCommand("block", unid, msg.ip, "")
  1958. self._sendCommand("delallmsg", unid, msg.ip, "")
  1959. else:
  1960. self._sendCommand("block", unid, msg.ip, user.name)
  1961. self._sendCommand("delallmsg", unid, msg.ip, user.name)
  1962. return True
  1963. return False
  1964.  
  1965. def requestBanlist(self):
  1966. """Request an updated banlist."""
  1967. self._sendCommand("blocklist", "block", "", "next", "500")
  1968. def requestUnbanlist(self):
  1969. """Request an updated unbanlist."""
  1970. self._sendCommand("blocklist", "unblock", "", "next", "500")
  1971.  
  1972.  
  1973.  
  1974. def requestBanlist(self):
  1975.  
  1976. """Request an updated banlist."""
  1977.  
  1978. self._sendCommand("blocklist", "block", "", "next", "500")
  1979.  
  1980.  
  1981.  
  1982. def rawUnban(self, name, ip, unid):
  1983.  
  1984. """
  1985.  
  1986. Execute the unblock command using specified arguments.
  1987.  
  1988. (For advanced usage)
  1989.  
  1990.  
  1991.  
  1992. @type name: str
  1993.  
  1994. @param name: name
  1995.  
  1996. @type ip: str
  1997.  
  1998. @param ip: ip address
  1999.  
  2000. @type unid: str
  2001.  
  2002. @param unid: unid
  2003.  
  2004. """
  2005.  
  2006. self._sendCommand("removeblock", unid, ip, name)
  2007.  
  2008.  
  2009.  
  2010. def unban(self, user):
  2011.  
  2012. """
  2013.  
  2014. Unban a user. (Moderator only)
  2015.  
  2016.  
  2017.  
  2018. @type user: User
  2019.  
  2020. @param user: user to unban
  2021.  
  2022.  
  2023.  
  2024. @rtype: bool
  2025.  
  2026. @return: whether it succeeded
  2027.  
  2028. """
  2029.  
  2030. rec = self._getBanRecord(user)
  2031.  
  2032. if rec:
  2033.  
  2034. self.rawUnban(rec[2].name, rec[1], rec[0])
  2035.  
  2036. return True
  2037.  
  2038. else:
  2039.  
  2040. return False
  2041.  
  2042.  
  2043.  
  2044. ####
  2045.  
  2046. # Util
  2047.  
  2048. ####
  2049.  
  2050. def _getBanRecord(self, user):
  2051.  
  2052. for record in self._banlist:
  2053.  
  2054. if record[2] == user:
  2055.  
  2056. return record
  2057.  
  2058. return None
  2059.  
  2060.  
  2061.  
  2062. def _callEvent(self, evt, *args, **kw):
  2063.  
  2064. getattr(self.mgr, evt)(self, *args, **kw)
  2065.  
  2066. self.mgr.onEventCalled(self, evt, *args, **kw)
  2067.  
  2068.  
  2069.  
  2070. def _write(self, data):
  2071.  
  2072. if self._wlock:
  2073.  
  2074. self._wlockbuf += data
  2075.  
  2076. else:
  2077.  
  2078. self.mgr._write(self, data)
  2079.  
  2080.  
  2081.  
  2082. def _setWriteLock(self, lock):
  2083.  
  2084. self._wlock = lock
  2085.  
  2086. if self._wlock == False:
  2087.  
  2088. self._write(self._wlockbuf)
  2089.  
  2090. self._wlockbuf = b""
  2091.  
  2092.  
  2093.  
  2094. def _sendCommand(self, *args):
  2095.  
  2096. """
  2097.  
  2098. Send a command.
  2099.  
  2100.  
  2101.  
  2102. @type args: [str, str, ...]
  2103.  
  2104. @param args: command and list of arguments
  2105.  
  2106. """
  2107.  
  2108. if self._firstCommand:
  2109.  
  2110. terminator = b"\x00"
  2111.  
  2112. self._firstCommand = False
  2113.  
  2114. else:
  2115.  
  2116. terminator = b"\r\n\x00"
  2117.  
  2118. self._write(":".join(args).encode() + terminator)
  2119.  
  2120.  
  2121.  
  2122. def getLevel(self, user):
  2123.  
  2124. if user == self._owner: return 2
  2125.  
  2126. if user.name in self.modnames: return 1
  2127.  
  2128. return 0
  2129.  
  2130.  
  2131.  
  2132. def getLastMessage(self, user = None):
  2133.  
  2134. if user:
  2135.  
  2136. try:
  2137.  
  2138. i = 1
  2139.  
  2140. while True:
  2141.  
  2142. msg = self._history[-i]
  2143.  
  2144. if msg.user == user:
  2145.  
  2146. return msg
  2147.  
  2148. i += 1
  2149.  
  2150. except IndexError:
  2151.  
  2152. return None
  2153.  
  2154. else:
  2155.  
  2156. try:
  2157.  
  2158. return self._history[-1]
  2159.  
  2160. except IndexError:
  2161.  
  2162. return None
  2163.  
  2164. return None
  2165.  
  2166.  
  2167.  
  2168. def findUser(self, name):
  2169.  
  2170. name = name.lower()
  2171.  
  2172. ul = self.getUserlist()
  2173.  
  2174. udi = dict(zip([u.name for u in ul], ul))
  2175.  
  2176. cname = None
  2177.  
  2178. for n in udi.keys():
  2179.  
  2180. if n.find(name) != -1:
  2181.  
  2182. if cname: return None #ambigious!!
  2183.  
  2184. cname = n
  2185.  
  2186. if cname: return udi[cname]
  2187.  
  2188. else: return None
  2189.  
  2190.  
  2191.  
  2192. ####
  2193.  
  2194. # History
  2195.  
  2196. ####
  2197.  
  2198. def _addHistory(self, msg):
  2199.  
  2200. """
  2201.  
  2202. Add a message to history.
  2203.  
  2204.  
  2205.  
  2206. @type msg: Message
  2207.  
  2208. @param msg: message
  2209.  
  2210. """
  2211.  
  2212. self._history.append(msg)
  2213.  
  2214. if len(self._history) > self.mgr._maxHistoryLength:
  2215.  
  2216. rest, self._history = self._history[:-self.mgr._maxHistoryLength], self._history[-self.mgr._maxHistoryLength:]
  2217.  
  2218. for msg in rest: msg.detach()
  2219.  
  2220.  
  2221.  
  2222. ################################################################
  2223.  
  2224. # RoomManager class
  2225.  
  2226. ################################################################
  2227.  
  2228. class RoomManager(object):
  2229.  
  2230. """Class that manages multiple connections."""
  2231.  
  2232. ####
  2233.  
  2234. # Config
  2235.  
  2236. ####
  2237.  
  2238. _Room = Room
  2239.  
  2240. _PM = PM
  2241.  
  2242. _PMHost = "c1.chatango.com"
  2243.  
  2244. _PMPort = 5222
  2245.  
  2246. _TimerResolution = 0.2 #at least x times per second
  2247.  
  2248. _pingDelay = 20
  2249.  
  2250. _userlistMode = Userlist_Recent
  2251.  
  2252. _userlistUnique = True
  2253.  
  2254. _userlistMemory = 50
  2255.  
  2256. _userlistEventUnique = False
  2257.  
  2258. _tooBigMessage = BigMessage_Multiple
  2259.  
  2260. _maxLength = 1800
  2261.  
  2262. _maxHistoryLength = 150
  2263.  
  2264.  
  2265.  
  2266. ####
  2267.  
  2268. # Init
  2269.  
  2270. ####
  2271.  
  2272. def __init__(self, name = None, password = None, pm = True):
  2273.  
  2274. self._name = name
  2275.  
  2276. self._password = password
  2277.  
  2278. self._running = False
  2279.  
  2280. self._tasks = set()
  2281.  
  2282. self._rooms = dict()
  2283.  
  2284. if pm:
  2285.  
  2286. self._pm = self._PM(mgr = self)
  2287.  
  2288. else:
  2289.  
  2290. self._pm = None
  2291.  
  2292.  
  2293.  
  2294. ####
  2295.  
  2296. # Join/leave
  2297.  
  2298. ####
  2299.  
  2300. def joinRoom(self, room):
  2301.  
  2302. """
  2303.  
  2304. Join a room or return None if already joined.
  2305.  
  2306.  
  2307.  
  2308. @type room: str
  2309.  
  2310. @param room: room to join
  2311.  
  2312.  
  2313.  
  2314. @rtype: Room or None
  2315.  
  2316. @return: the room or nothing
  2317.  
  2318. """
  2319.  
  2320. room = room.lower()
  2321.  
  2322. if room not in self._rooms:
  2323.  
  2324. con = self._Room(room, mgr = self)
  2325.  
  2326. self._rooms[room] = con
  2327.  
  2328. return con
  2329.  
  2330. else:
  2331.  
  2332. return None
  2333.  
  2334.  
  2335.  
  2336. def leaveRoom(self, room):
  2337.  
  2338. """
  2339.  
  2340. Leave a room.
  2341.  
  2342.  
  2343.  
  2344. @type room: str
  2345.  
  2346. @param room: room to leave
  2347.  
  2348. """
  2349.  
  2350. room = room.lower()
  2351.  
  2352. if room in self._rooms:
  2353.  
  2354. con = self._rooms[room]
  2355.  
  2356. con.disconnect()
  2357.  
  2358.  
  2359.  
  2360. def getRoom(self, room):
  2361.  
  2362. """
  2363.  
  2364. Get room with a name, or None if not connected to this room.
  2365.  
  2366.  
  2367.  
  2368. @type room: str
  2369.  
  2370. @param room: room
  2371.  
  2372.  
  2373.  
  2374. @rtype: Room
  2375.  
  2376. @return: the room
  2377.  
  2378. """
  2379.  
  2380. room = room
  2381.  
  2382. if room in self._rooms:
  2383.  
  2384. return self._rooms[room]
  2385.  
  2386. else:
  2387.  
  2388. return None
  2389.  
  2390.  
  2391.  
  2392. ####
  2393.  
  2394. # Properties
  2395.  
  2396. ####
  2397.  
  2398. def getUser(self): return User(self._name)
  2399.  
  2400. def getName(self): return self._name
  2401.  
  2402. def getPassword(self): return self._password
  2403.  
  2404. def getRooms(self): return set(self._rooms.values())
  2405.  
  2406. def getRoomNames(self): return set(self._rooms.keys())
  2407.  
  2408. def getPM(self): return self._pm
  2409.  
  2410.  
  2411.  
  2412. user = property(getUser)
  2413.  
  2414. name = property(getName)
  2415.  
  2416. password = property(getPassword)
  2417.  
  2418. rooms = property(getRooms)
  2419.  
  2420. roomnames = property(getRoomNames)
  2421.  
  2422. pm = property(getPM)
  2423.  
  2424.  
  2425.  
  2426. ####
  2427.  
  2428. # Virtual methods
  2429.  
  2430. ####
  2431.  
  2432. def onInit(self):
  2433.  
  2434. """Called on init."""
  2435.  
  2436. pass
  2437.  
  2438.  
  2439.  
  2440. def onConnect(self, room):
  2441.  
  2442. """
  2443.  
  2444. Called when connected to the room.
  2445.  
  2446.  
  2447.  
  2448. @type room: Room
  2449.  
  2450. @param room: room where the event occured
  2451.  
  2452. """
  2453.  
  2454. pass
  2455.  
  2456.  
  2457.  
  2458. def onReconnect(self, room):
  2459.  
  2460. """
  2461.  
  2462. Called when reconnected to the room.
  2463.  
  2464.  
  2465.  
  2466. @type room: Room
  2467.  
  2468. @param room: room where the event occured
  2469.  
  2470. """
  2471.  
  2472. pass
  2473.  
  2474.  
  2475.  
  2476. def onConnectFail(self, room):
  2477.  
  2478. """
  2479.  
  2480. Called when the connection failed.
  2481.  
  2482.  
  2483.  
  2484. @type room: Room
  2485.  
  2486. @param room: room where the event occured
  2487.  
  2488. """
  2489.  
  2490. pass
  2491.  
  2492.  
  2493.  
  2494. def onDisconnect(self, room):
  2495.  
  2496. """
  2497.  
  2498. Called when the client gets disconnected.
  2499.  
  2500.  
  2501.  
  2502. @type room: Room
  2503.  
  2504. @param room: room where the event occured
  2505.  
  2506. """
  2507.  
  2508. pass
  2509.  
  2510.  
  2511.  
  2512. def onLoginFail(self, room):
  2513.  
  2514. """
  2515.  
  2516. Called on login failure, disconnects after.
  2517.  
  2518.  
  2519.  
  2520. @type room: Room
  2521.  
  2522. @param room: room where the event occured
  2523.  
  2524. """
  2525.  
  2526. pass
  2527.  
  2528.  
  2529.  
  2530. def onFloodBan(self, room):
  2531.  
  2532. """
  2533.  
  2534. Called when either flood banned or flagged.
  2535.  
  2536.  
  2537.  
  2538. @type room: Room
  2539.  
  2540. @param room: room where the event occured
  2541.  
  2542. """
  2543.  
  2544. pass
  2545.  
  2546.  
  2547.  
  2548. def onFloodBanRepeat(self, room):
  2549.  
  2550. """
  2551.  
  2552. Called when trying to send something when floodbanned.
  2553.  
  2554.  
  2555.  
  2556. @type room: Room
  2557.  
  2558. @param room: room where the event occured
  2559.  
  2560. """
  2561.  
  2562. pass
  2563.  
  2564.  
  2565.  
  2566. def onFloodWarning(self, room):
  2567.  
  2568. """
  2569.  
  2570. Called when an overflow warning gets received.
  2571.  
  2572.  
  2573.  
  2574. @type room: Room
  2575.  
  2576. @param room: room where the event occured
  2577.  
  2578. """
  2579.  
  2580. pass
  2581.  
  2582.  
  2583.  
  2584. def onMessageDelete(self, room, user, message):
  2585.  
  2586. """
  2587.  
  2588. Called when a message gets deleted.
  2589.  
  2590.  
  2591.  
  2592. @type room: Room
  2593.  
  2594. @param room: room where the event occured
  2595.  
  2596. @type user: User
  2597.  
  2598. @param user: owner of deleted message
  2599.  
  2600. @type message: Message
  2601.  
  2602. @param message: message that got deleted
  2603.  
  2604. """
  2605.  
  2606. pass
  2607.  
  2608.  
  2609.  
  2610. def onModChange(self, room):
  2611.  
  2612. """
  2613.  
  2614. Called when the moderator list changes.
  2615.  
  2616.  
  2617.  
  2618. @type room: Room
  2619.  
  2620. @param room: room where the event occured
  2621.  
  2622. """
  2623.  
  2624. pass
  2625.  
  2626.  
  2627.  
  2628. def onModAdd(self, room, user):
  2629.  
  2630. """
  2631.  
  2632. Called when a moderator gets added.
  2633.  
  2634.  
  2635.  
  2636. @type room: Room
  2637.  
  2638. @param room: room where the event occured
  2639.  
  2640. """
  2641.  
  2642. pass
  2643.  
  2644.  
  2645.  
  2646. def onModRemove(self, room, user):
  2647.  
  2648. """
  2649.  
  2650. Called when a moderator gets removed.
  2651.  
  2652.  
  2653.  
  2654. @type room: Room
  2655.  
  2656. @param room: room where the event occured
  2657.  
  2658. """
  2659.  
  2660. pass
  2661.  
  2662.  
  2663.  
  2664. def onMessage(self, room, user, message):
  2665.  
  2666. """
  2667.  
  2668. Called when a message gets received.
  2669.  
  2670.  
  2671.  
  2672. @type room: Room
  2673.  
  2674. @param room: room where the event occured
  2675.  
  2676. @type user: User
  2677.  
  2678. @param user: owner of message
  2679.  
  2680. @type message: Message
  2681.  
  2682. @param message: received message
  2683.  
  2684. """
  2685.  
  2686. pass
  2687.  
  2688.  
  2689.  
  2690. def onHistoryMessage(self, room, user, message):
  2691.  
  2692. """
  2693.  
  2694. Called when a message gets received from history.
  2695.  
  2696.  
  2697.  
  2698. @type room: Room
  2699.  
  2700. @param room: room where the event occured
  2701.  
  2702. @type user: User
  2703.  
  2704. @param user: owner of message
  2705.  
  2706. @type message: Message
  2707.  
  2708. @param message: the message that got added
  2709.  
  2710. """
  2711.  
  2712. pass
  2713.  
  2714.  
  2715.  
  2716. def onJoin(self, room, user):
  2717.  
  2718. """
  2719.  
  2720. Called when a user joins. Anonymous users get ignored here.
  2721.  
  2722.  
  2723.  
  2724. @type room: Room
  2725.  
  2726. @param room: room where the event occured
  2727.  
  2728. @type user: User
  2729.  
  2730. @param user: the user that has joined
  2731.  
  2732. """
  2733.  
  2734. pass
  2735.  
  2736.  
  2737.  
  2738. def onLeave(self, room, user):
  2739.  
  2740. """
  2741.  
  2742. Called when a user leaves. Anonymous users get ignored here.
  2743.  
  2744.  
  2745.  
  2746. @type room: Room
  2747.  
  2748. @param room: room where the event occured
  2749.  
  2750. @type user: User
  2751.  
  2752. @param user: the user that has left
  2753.  
  2754. """
  2755.  
  2756. pass
  2757.  
  2758.  
  2759.  
  2760. def onRaw(self, room, raw):
  2761.  
  2762. """
  2763.  
  2764. Called before any command parsing occurs.
  2765.  
  2766.  
  2767.  
  2768. @type room: Room
  2769.  
  2770. @param room: room where the event occured
  2771.  
  2772. @type raw: str
  2773.  
  2774. @param raw: raw command data
  2775.  
  2776. """
  2777.  
  2778. pass
  2779.  
  2780.  
  2781.  
  2782. def onPing(self, room):
  2783.  
  2784. """
  2785.  
  2786. Called when a ping gets sent.
  2787.  
  2788.  
  2789.  
  2790. @type room: Room
  2791.  
  2792. @param room: room where the event occured
  2793.  
  2794. """
  2795.  
  2796. pass
  2797.  
  2798.  
  2799.  
  2800. def onUserCountChange(self, room):
  2801.  
  2802. """
  2803.  
  2804. Called when the user count changes.
  2805.  
  2806.  
  2807.  
  2808. @type room: Room
  2809.  
  2810. @param room: room where the event occured
  2811.  
  2812. """
  2813.  
  2814. pass
  2815.  
  2816.  
  2817.  
  2818. def onBan(self, room, user, target):
  2819.  
  2820. """
  2821.  
  2822. Called when a user gets banned.
  2823.  
  2824.  
  2825.  
  2826. @type room: Room
  2827.  
  2828. @param room: room where the event occured
  2829.  
  2830. @type user: User
  2831.  
  2832. @param user: user that banned someone
  2833.  
  2834. @type target: User
  2835.  
  2836. @param target: user that got banned
  2837.  
  2838. """
  2839.  
  2840. pass
  2841.  
  2842.  
  2843.  
  2844. def onUnban(self, room, user, target):
  2845.  
  2846. """
  2847.  
  2848. Called when a user gets unbanned.
  2849.  
  2850.  
  2851.  
  2852. @type room: Room
  2853.  
  2854. @param room: room where the event occured
  2855.  
  2856. @type user: User
  2857.  
  2858. @param user: user that unbanned someone
  2859.  
  2860. @type target: User
  2861.  
  2862. @param target: user that got unbanned
  2863.  
  2864. """
  2865.  
  2866. pass
  2867.  
  2868.  
  2869.  
  2870. def onBanlistUpdate(self, room):
  2871.  
  2872. """
  2873.  
  2874. Called when a banlist gets updated.
  2875.  
  2876.  
  2877.  
  2878. @type room: Room
  2879.  
  2880. @param room: room where the event occured
  2881.  
  2882. """
  2883.  
  2884. pass
  2885.  
  2886.  
  2887.  
  2888. def onPMConnect(self, pm):
  2889.  
  2890. pass
  2891.  
  2892.  
  2893.  
  2894. def onPMDisconnect(self, pm):
  2895.  
  2896. pass
  2897.  
  2898.  
  2899.  
  2900. def onPMPing(self, pm):
  2901.  
  2902. pass
  2903.  
  2904.  
  2905.  
  2906. def onPMMessage(self, pm, user, body):
  2907.  
  2908. pass
  2909.  
  2910.  
  2911.  
  2912. def onPMOfflineMessage(self, pm, user, body):
  2913.  
  2914. pass
  2915.  
  2916.  
  2917.  
  2918. def onPMContactlistReceive(self, pm):
  2919.  
  2920. pass
  2921.  
  2922.  
  2923.  
  2924. def onPMBlocklistReceive(self, pm):
  2925.  
  2926. pass
  2927.  
  2928.  
  2929.  
  2930. def onPMContactAdd(self, pm, user):
  2931.  
  2932. pass
  2933.  
  2934.  
  2935.  
  2936. def onPMContactRemove(self, pm, user):
  2937.  
  2938. pass
  2939.  
  2940.  
  2941.  
  2942. def onPMBlock(self, pm, user):
  2943.  
  2944. pass
  2945.  
  2946.  
  2947.  
  2948. def onPMUnblock(self, pm, user):
  2949.  
  2950. pass
  2951.  
  2952.  
  2953.  
  2954. def onPMContactOnline(self, pm, user):
  2955.  
  2956. pass
  2957.  
  2958.  
  2959.  
  2960. def onPMContactOffline(self, pm, user):
  2961.  
  2962. pass
  2963.  
  2964.  
  2965.  
  2966. def onEventCalled(self, room, evt, *args, **kw):
  2967.  
  2968. """
  2969.  
  2970. Called on every room-based event.
  2971.  
  2972.  
  2973.  
  2974. @type room: Room
  2975.  
  2976. @param room: room where the event occured
  2977.  
  2978. @type evt: str
  2979.  
  2980. @param evt: the event
  2981.  
  2982. """
  2983.  
  2984. pass
  2985.  
  2986.  
  2987.  
  2988. ####
  2989.  
  2990. # Deferring
  2991.  
  2992. ####
  2993. def deferToThread(self, callback, func, *args, **kw):
  2994. """
  2995. Defer a function to a thread and callback the return value.
  2996. @type callback: function
  2997. @param callback: function to call on completion
  2998. @type cbargs: tuple or list
  2999. @param cbargs: arguments to get supplied to the callback
  3000. @type func: function
  3001. @param func: function to call
  3002. """
  3003. def f(func, callback, *args, **kw):
  3004. ret = func(*args, **kw)
  3005. self.setTimeout(0, callback, ret)
  3006. threading._start_new_thread(f, (func, callback) + args, kw)
  3007. ####
  3008. # Scheduling
  3009. ####
  3010. class _Task(object):
  3011. def cancel(self):
  3012. """Sugar for removeTask."""
  3013. self.mgr.removeTask(self)
  3014. def _tick(self):
  3015. now = time.time()
  3016. for task in set(self._tasks):
  3017. if task.target <= now:
  3018. task.func(*task.args, **task.kw)
  3019. if task.isInterval:
  3020. task.target = now + task.timeout
  3021. else:
  3022. self._tasks.remove(task)
  3023. def setTimeout(self, timeout, func, *args, **kw):
  3024. """
  3025. Call a function after at least timeout seconds with specified arguments.
  3026. @type timeout: int
  3027. @param timeout: timeout
  3028. @type func: function
  3029. @param func: function to call
  3030. @rtype: _Task
  3031. @return: object representing the task
  3032. """
  3033. task = self._Task()
  3034. task.mgr = self
  3035. task.target = time.time() + timeout
  3036. task.timeout = timeout
  3037. task.func = func
  3038. task.isInterval = False
  3039. task.args = args
  3040. task.kw = kw
  3041. self._tasks.add(task)
  3042. return task
  3043. def setInterval(self, timeout, func, *args, **kw):
  3044. """
  3045. Call a function at least every timeout seconds with specified arguments.
  3046. @type timeout: int
  3047. @param timeout: timeout
  3048. @type func: function
  3049. @param func: function to call
  3050. @rtype: _Task
  3051. @return: object representing the task
  3052. """
  3053. task = self._Task()
  3054. task.mgr = self
  3055. task.target = time.time() + timeout
  3056. task.timeout = timeout
  3057. task.func = func
  3058. task.isInterval = True
  3059. task.args = args
  3060. task.kw = kw
  3061. self._tasks.add(task)
  3062. return task
  3063. def removeTask(self, task):
  3064. """
  3065. Cancel a task.
  3066. @type task: _Task
  3067. @param task: task to cancel
  3068. """
  3069. self._tasks.remove(task)
  3070. ####
  3071. # Util
  3072. ####
  3073. def _write(self, room, data):
  3074. room._wbuf += data
  3075. def getConnections(self):
  3076. li = list(self._rooms.values())
  3077. if self._pm:
  3078. li.append(self._pm)
  3079. return [c for c in li if c._sock != None]
  3080. ####
  3081. # Main
  3082. ####
  3083. def main(self):
  3084. self.onInit()
  3085. self._running = True
  3086. while self._running:
  3087. conns = self.getConnections()
  3088. socks = [x._sock for x in conns]
  3089. wsocks = [x._sock for x in conns if x._wbuf != b""]
  3090. rd, wr, sp = select.select(socks, wsocks, [], self._TimerResolution)
  3091. for sock in rd:
  3092. con = [c for c in conns if c._sock == sock][0]
  3093. try:
  3094. data = sock.recv(1024)
  3095. if(len(data) > 0):
  3096. con._feed(data)
  3097. else:
  3098. con.disconnect()
  3099. except socket.error:
  3100. pass
  3101. for sock in wr:
  3102. con = [c for c in conns if c._sock == sock][0]
  3103. try:
  3104. size = sock.send(con._wbuf)
  3105. con._wbuf = con._wbuf[size:]
  3106. except socket.error:
  3107. pass
  3108. self._tick()
  3109. @classmethod
  3110. def easy_start(cl, rooms = None, name = None, password = None, pm = True):
  3111. """
  3112. Prompts the user for missing info, then starts.
  3113. @type rooms: list
  3114. @param room: rooms to join
  3115. @type name: str
  3116. @param name: name to join as ("" = None, None = unspecified)
  3117. @type password: str
  3118. @param password: password to join with ("" = None, None = unspecified)
  3119. """
  3120. if not rooms: rooms = str(input("Room names separated by semicolons: ")).split(";")
  3121. if len(rooms) == 1 and rooms[0] == "": rooms = []
  3122. if not name: name = str(input("User name: "))
  3123. if name == "": name = None
  3124. if not password: password = str(input("User password: "))
  3125. if password == "": password = None
  3126. self = cl(name, password, pm = pm)
  3127. for room in rooms:
  3128. self.joinRoom(room)
  3129. self.main()
  3130. def stop(self):
  3131. for conn in list(self._rooms.values()):
  3132. conn.disconnect()
  3133. self._running = False
  3134. ####
  3135. # Commands
  3136. ####
  3137. def enableBg(self):
  3138. """Enable background if available."""
  3139. self.user._mbg = True
  3140. for room in self.rooms:
  3141. room.setBgMode(1)
  3142. def disableBg(self):
  3143. """Disable background."""
  3144. self.user._mbg = False
  3145. for room in self.rooms:
  3146. room.setBgMode(0)
  3147. def enableRecording(self):
  3148. """Enable recording if available."""
  3149. self.user._mrec = True
  3150. for room in self.rooms:
  3151. room.setRecordingMode(1)
  3152. def disableRecording(self):
  3153. """Disable recording."""
  3154. self.user._mrec = False
  3155. for room in self.rooms:
  3156. room.setRecordingMode(0)
  3157. def setNameColor(self, color3x):
  3158. """
  3159. Set name color.
  3160. @type color3x: str
  3161. @param color3x: a 3-char RGB hex code for the color
  3162. """
  3163. self.user._nameColor = color3x
  3164. def setFontColor(self, color3x):
  3165. """
  3166. Set font color.
  3167. @type color3x: str
  3168. @param color3x: a 3-char RGB hex code for the color
  3169. """
  3170. self.user._fontColor = color3x
  3171. def setFontFace(self, face):
  3172. """
  3173. Set font face/family.
  3174. @type face: str
  3175. @param face: the font face
  3176. """
  3177. self.user._fontFace = face
  3178. def setFontSize(self, size):
  3179. """
  3180. Set font size.
  3181. @type size: int
  3182. @param size: the font size (limited: 9 to 22)
  3183. """
  3184. if size < 9: size = 9
  3185. if size > 22: size = 22
  3186. self.user._fontSize = size
  3187. ################################################################
  3188. # User class (well, yeah, i lied, it's actually _User)
  3189. ################################################################
  3190. _users = dict()
  3191. def User(name, *args, **kw):
  3192. name = name.lower()
  3193. user = _users.get(name)
  3194. if not user:
  3195. user = _User(name = name, *args, **kw)
  3196. _users[name] = user
  3197. return user
  3198. class _User(object):
  3199. """Class that represents a user."""
  3200. ####
  3201. # Init
  3202. ####
  3203. def __init__(self, name, **kw):
  3204. self._name = name.lower()
  3205. self._sids = dict()
  3206. self._msgs = list()
  3207. self._nameColor = "000"
  3208. self._fontSize = 12
  3209. self._fontFace = "0"
  3210. self._fontColor = "000"
  3211. self._mbg = False
  3212. self._mrec = False
  3213. for attr, val in kw.items():
  3214. if val == None: continue
  3215. setattr(self, "_" + attr, val)
  3216. ####
  3217. # Properties
  3218. ####
  3219. def getName(self): return self._name
  3220. def getSessionIds(self, room = None):
  3221. if room:
  3222. return self._sids.get(room, set())
  3223. else:
  3224. return set.union(*self._sids.values())
  3225. def getRooms(self): return self._sids.keys()
  3226. def getRoomNames(self): return [room.name for room in self.getRooms()]
  3227. def getFontColor(self): return self._fontColor
  3228. def getFontFace(self): return self._fontFace
  3229. def getFontSize(self): return self._fontSize
  3230. def getNameColor(self): return self._nameColor
  3231. name = property(getName)
  3232. sessionids = property(getSessionIds)
  3233. rooms = property(getRooms)
  3234. roomnames = property(getRoomNames)
  3235. fontColor = property(getFontColor)
  3236. fontFace = property(getFontFace)
  3237. fontSize = property(getFontSize)
  3238. nameColor = property(getNameColor)
  3239. ####
  3240. # Util
  3241. ####
  3242. def addSessionId(self, room, sid):
  3243. if room not in self._sids:
  3244. self._sids[room] = set()
  3245. self._sids[room].add(sid)
  3246. def removeSessionId(self, room, sid):
  3247. try:
  3248. self._sids[room].remove(sid)
  3249. if len(self._sids[room]) == 0:
  3250. del self._sids[room]
  3251. except KeyError:
  3252. pass
  3253. def clearSessionIds(self, room):
  3254. try:
  3255. del self._sids[room]
  3256. except KeyError:
  3257. pass
  3258. def hasSessionId(self, room, sid):
  3259. try:
  3260. if sid in self._sids[room]:
  3261. return True
  3262. else:
  3263. return False
  3264. except KeyError:
  3265. return False
  3266. ####
  3267. # Repr
  3268. ####
  3269. def __repr__(self):
  3270. return "<User: %s>" %(self.name)
  3271. ################################################################
  3272. # Message class
  3273. ################################################################
  3274. class Message(object):
  3275. """Class that represents a message."""
  3276. ####
  3277. # Attach/detach
  3278. ####
  3279. def attach(self, room, msgid):
  3280. """
  3281. Attach the Message to a message id.
  3282. @type msgid: str
  3283. @param msgid: message id
  3284. """
  3285. if self._msgid == None:
  3286. self._room = room
  3287. self._msgid = msgid
  3288. self._room._msgs[msgid] = self
  3289. def detach(self):
  3290. """Detach the Message."""
  3291. if self._msgid != None and self._msgid in self._room._msgs:
  3292. del self._room._msgs[self._msgid]
  3293. self._msgid = None
  3294. ####
  3295. # Init
  3296. ####
  3297. def __init__(self, **kw):
  3298. self._msgid = None
  3299. self._time = None
  3300. self._user = None
  3301. self._body = None
  3302. self._room = None
  3303. self._raw = ""
  3304. self._ip = None
  3305. self._unid = ""
  3306. self._nameColor = "000"
  3307. self._fontSize = 12
  3308. self._fontFace = "0"
  3309. self._fontColor = "000"
  3310. for attr, val in kw.items():
  3311. if val == None: continue
  3312. setattr(self, "_" + attr, val)
  3313. ####
  3314. # Properties
  3315. ####
  3316. def getId(self): return self._msgid
  3317. def getTime(self): return self._time
  3318. def getUser(self): return self._user
  3319. def getBody(self): return self._body
  3320. def getUid(self): return self._uid
  3321. def getIP(self): return self._ip
  3322. def getFontColor(self): return self._fontColor
  3323. def getFontFace(self): return self._fontFace
  3324. def getFontSize(self): return self._fontSize
  3325. def getNameColor(self): return self._nameColor
  3326. def getRoom(self): return self._room
  3327. def getRaw(self): return self._raw
  3328. def getUnid(self): return self._unid
  3329. msgid = property(getId)
  3330. time = property(getTime)
  3331. user = property(getUser)
  3332. body = property(getBody)
  3333. uid = property(getUid)
  3334. room = property(getRoom)
  3335. ip = property(getIP)
  3336. fontColor = property(getFontColor)
  3337. fontFace = property(getFontFace)
  3338. fontSize = property(getFontSize)
  3339. raw = property(getRaw)
  3340. nameColor = property(getNameColor)
  3341. unid = property(getUnid)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement