Advertisement
Guest User

Untitled

a guest
Jan 18th, 2017
128
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 63.73 KB | None | 0 0
  1. ################################################################
  2. # File: ch.py
  3. # Title: Chatango Library
  4. # Original Author: Lumirayz/Lumz <lumirayz@gmail.com>
  5. # Current Maintainers and Contributors:
  6. # Nullspeaker <import codecs;codecs.encode('aunzzbaq129@tznvy.pbz','rot_13')>
  7. # asl97 <asl97@outlook.com>
  8. # pystub
  9. # dani87
  10. # domzy
  11. # kamijoutouma
  12. # piks
  13. # Version: 1.3.8
  14. # Description:
  15. # An event-based library for connecting to one or multiple Chatango rooms, has
  16. # support for several things including: messaging, message font,
  17. # name color, deleting, banning, recent history, 2 userlist modes,
  18. # flagging, avoiding flood bans, detecting flags.
  19. # Contact Info:
  20. # Any question, comment, or suggestion should be directed to the current
  21. # maintainers and contributors, located at:
  22. # https://github.com/Nullspeaker/ch.py
  23. # Where a more satisfactory response to all bug reports (which can be made on the
  24. # issues page) and other statements can be garnered. For things not specific or
  25. # in direct reference to this library, 'ch.py', a direct response can be filed
  26. # to the individual persons listed above as 'Current Maintainers and Contributors.'
  27. ################################################################
  28.  
  29. ################################################################
  30. # License
  31. ################################################################
  32. # Copyright 2011 Lumirayz
  33. # This program is distributed under the terms of the GNU GPL.
  34.  
  35. ################################################################
  36. # Imports
  37. ################################################################
  38. import socket
  39. import threading
  40. import time
  41. import random
  42. import re
  43. import sys
  44. import select
  45.  
  46. ################################################################
  47. # Debug stuff
  48. ################################################################
  49. debug = False
  50.  
  51. ################################################################
  52. # Python 2 compatibility
  53. ################################################################
  54. if sys.version_info[0] < 3:
  55. class urllib:
  56. parse = __import__("urllib")
  57. request = __import__("urllib2")
  58. input = raw_input
  59. import codecs
  60. import Queue as queue
  61. else:
  62. import queue
  63. import urllib.request
  64. import urllib.parse
  65.  
  66. ################################################################
  67. # Constants
  68. ################################################################
  69. Userlist_Recent = 0
  70. Userlist_All = 1
  71.  
  72. BigMessage_Multiple = 0
  73. BigMessage_Cut = 1
  74.  
  75. # minimum of 1 thread needed
  76. Number_of_Threads = 1
  77. ################################################################
  78. # Struct class
  79. ################################################################
  80. class Struct:
  81. def __init__(self, **entries):
  82. self.__dict__.update(entries)
  83.  
  84. ################################################################
  85. # Tagserver stuff
  86. ################################################################
  87. specials = {'mitvcanal': 56, 'animeultimacom': 34, 'cricket365live': 21, 'pokemonepisodeorg': 22, 'animelinkz': 20, 'sport24lt': 56, 'narutowire': 10, 'watchanimeonn': 22, 'cricvid-hitcric-': 51, 'narutochatt': 70, 'leeplarp': 27, 'stream2watch3': 56, 'ttvsports': 56, 'ver-anime': 8, 'vipstand': 21, 'eafangames': 56, 'soccerjumbo': 21, 'myfoxdfw': 67, 'kiiiikiii': 21, 'de-livechat': 5, 'rgsmotrisport': 51, 'dbzepisodeorg': 10, 'watch-dragonball': 8, 'peliculas-flv': 69, 'tvanimefreak': 54, 'tvtvanimefreak': 54}
  88. 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]]
  89.  
  90. def getServer(group):
  91. """
  92. Get the server host for a certain room.
  93.  
  94. @type group: str
  95. @param group: room name
  96.  
  97. @rtype: str
  98. @return: the server's hostname
  99. """
  100. try:
  101. sn = specials[group]
  102. except KeyError:
  103. group = group.replace("_", "q")
  104. group = group.replace("-", "q")
  105. fnv = float(int(group[0:min(5, len(group))], 36))
  106. lnv = group[6: (6 + min(3, len(group) - 5))]
  107. if(lnv):
  108. lnv = float(int(lnv, 36))
  109. lnv = max(lnv,1000)
  110. else:
  111. lnv = 1000
  112. num = (fnv % lnv) / lnv
  113. maxnum = sum(map(lambda x: x[1], tsweights))
  114. cumfreq = 0
  115. sn = 0
  116. for wgt in tsweights:
  117. cumfreq += float(wgt[1]) / maxnum
  118. if(num <= cumfreq):
  119. sn = int(wgt[0])
  120. break
  121. return "s" + str(sn) + ".chatango.com"
  122.  
  123. ################################################################
  124. # Uid
  125. ################################################################
  126. def _genUid():
  127. """
  128. generate a uid
  129. """
  130. return str(random.randrange(10 ** 15, 10 ** 16))
  131.  
  132. ################################################################
  133. # Message stuff
  134. ################################################################
  135. def _clean_message(msg):
  136. """
  137. Clean a message and return the message, n tag and f tag.
  138.  
  139. @type msg: str
  140. @param msg: the message
  141.  
  142. @rtype: str, str, str
  143. @returns: cleaned message, n tag contents, f tag contents
  144. """
  145. n = re.search("<n(.*?)/>", msg)
  146. if n: n = n.group(1)
  147. f = re.search("<f(.*?)>", msg)
  148. if f: f = f.group(1)
  149. msg = re.sub("<n.*?/>", "", msg)
  150. msg = re.sub("<f.*?>", "", msg)
  151. msg = _strip_html(msg)
  152. msg = msg.replace("&lt;", "<")
  153. msg = msg.replace("&gt;", ">")
  154. msg = msg.replace("&quot;", "\"")
  155. msg = msg.replace("&apos;", "'")
  156. msg = msg.replace("&amp;", "&")
  157. return msg, n, f
  158.  
  159. def _strip_html(msg):
  160. """Strip HTML."""
  161. li = msg.split("<")
  162. if len(li) == 1:
  163. return li[0]
  164. else:
  165. ret = list()
  166. for data in li:
  167. data = data.split(">", 1)
  168. if len(data) == 1:
  169. ret.append(data[0])
  170. elif len(data) == 2:
  171. ret.append(data[1])
  172. return "".join(ret)
  173.  
  174. def _parseNameColor(n):
  175. """This just returns its argument, should return the name color."""
  176. #probably is already the name
  177. return n
  178.  
  179. def _parseFont(f):
  180. """Parses the contents of a f tag and returns color, face and size."""
  181. #' xSZCOL="FONT"'
  182. try: #TODO: remove quick hack
  183. sizecolor, fontface = f.split("=", 1)
  184. sizecolor = sizecolor.strip()
  185. size = int(sizecolor[1:3])
  186. col = sizecolor[3:6]
  187. if col == "": col = None
  188. face = f.split("\"", 2)[1]
  189. return col, face, size
  190. except:
  191. return None, None, None
  192.  
  193. ################################################################
  194. # Anon id
  195. ################################################################
  196. def _getAnonId(n, ssid):
  197. """Gets the anon's id."""
  198. if n == None: n = "5504"
  199. try:
  200. return "".join(list(
  201. map(lambda x: str(x[0] + x[1])[-1], list(zip(
  202. list(map(lambda x: int(x), n)),
  203. list(map(lambda x: int(x), ssid[4:]))
  204. )))
  205. ))
  206. except ValueError:
  207. return "NNNN"
  208.  
  209. ################################################################
  210. # ANON PM class
  211. ################################################################
  212.  
  213. class _ANON_PM_OBJECT:
  214. """Manages connection with Chatango anon PM."""
  215. def __init__(self, mgr, name):
  216. self._connected = False
  217. self._mgr = mgr
  218. self._wlock = False
  219. self._firstCommand = True
  220. self._wbuf = b""
  221. self._wlockbuf = b""
  222. self._rbuf = b""
  223. self._pingTask = None
  224. self._name = name
  225.  
  226. def _auth(self):
  227. self._sendCommand("mhs","mini","unknown","%s" % (self._name))
  228. self._setWriteLock(True)
  229. return True
  230.  
  231. def disconnect(self):
  232. """Disconnect the bot from PM"""
  233. self._disconnect()
  234. self._callEvent("onAnonPMDisconnect", User(self._name))
  235.  
  236. def _disconnect(self):
  237. self._connected = False
  238. self._sock.close()
  239. self._sock = None
  240.  
  241. def ping(self):
  242. """send a ping"""
  243. self._sendCommand("")
  244. self._callEvent("onPMPing")
  245.  
  246. def message(self, user, msg):
  247. """send a pm to a user"""
  248. if msg!=None:
  249. self._sendCommand("msg", user.name, msg)
  250.  
  251. ####
  252. # Feed
  253. ####
  254. def _feed(self, data):
  255. """
  256. Feed data to the connection.
  257.  
  258. @type data: bytes
  259. @param data: data to be fed
  260. """
  261. self._rbuf += data
  262. while self._rbuf.find(b"\x00") != -1:
  263. data = self._rbuf.split(b"\x00")
  264. for food in data[:-1]:
  265. self._process(food.decode(errors="replace").rstrip("\r\n"))
  266. self._rbuf = data[-1]
  267.  
  268. def _process(self, data):
  269. """
  270. Process a command string.
  271.  
  272. @type data: str
  273. @param data: the command string
  274. """
  275. self._callEvent("onRaw", data)
  276. data = data.split(":")
  277. cmd, args = data[0], data[1:]
  278. func = "_rcmd_" + cmd
  279. if hasattr(self, func):
  280. getattr(self, func)(args)
  281. else:
  282. if debug:
  283. print("unknown data: "+str(data))
  284.  
  285. def _getManager(self): return self._mgr
  286.  
  287. mgr = property(_getManager)
  288.  
  289. ####
  290. # Received Commands
  291. ####
  292.  
  293. def _rcmd_mhs(self, args):
  294. """
  295. note to future maintainers
  296.  
  297. args[1] is ether "online" or "offline"
  298. """
  299. self._connected = True
  300. self._setWriteLock(False)
  301.  
  302. def _rcmd_msg(self, args):
  303. user = User(args[0])
  304. body = _strip_html(":".join(args[5:]))
  305. self._callEvent("onPMMessage", user, body)
  306.  
  307. ####
  308. # Util
  309. ####
  310. def _callEvent(self, evt, *args, **kw):
  311. getattr(self.mgr, evt)(self, *args, **kw)
  312. self.mgr.onEventCalled(self, evt, *args, **kw)
  313.  
  314. def _write(self, data):
  315. if self._wlock:
  316. self._wlockbuf += data
  317. else:
  318. self.mgr._write(self, data)
  319.  
  320. def _setWriteLock(self, lock):
  321. self._wlock = lock
  322. if self._wlock == False:
  323. self._write(self._wlockbuf)
  324. self._wlockbuf = b""
  325.  
  326. def _sendCommand(self, *args):
  327. """
  328. Send a command.
  329.  
  330. @type args: [str, str, ...]
  331. @param args: command and list of arguments
  332. """
  333. if self._firstCommand:
  334. terminator = b"\x00"
  335. self._firstCommand = False
  336. else:
  337. terminator = b"\r\n\x00"
  338. self._write(":".join(args).encode() + terminator)
  339.  
  340. class ANON_PM:
  341. """Comparable wrapper for anon Chatango PM"""
  342. ####
  343. # Init
  344. ####
  345. def __init__(self, mgr):
  346. self._mgr = mgr
  347. self._wlock = False
  348. self._firstCommand = True
  349. self._persons = dict()
  350. self._wlockbuf = b""
  351. self._pingTask = None
  352.  
  353. ####
  354. # Connections
  355. ####
  356. def _connect(self,name):
  357. self._persons[name] = _ANON_PM_OBJECT(self._mgr,name)
  358. sock = socket.socket()
  359. sock.connect((self._mgr._anonPMHost, self._mgr._PMPort))
  360. sock.setblocking(False)
  361. self._persons[name]._sock = sock
  362. if not self._persons[name]._auth(): return
  363. self._persons[name]._pingTask = self._mgr.setInterval(self._mgr._pingDelay, self._persons[name].ping)
  364. self._persons[name]._connected = True
  365.  
  366. def message(self, user, msg):
  367. """send a pm to a user"""
  368. if not user.name in self._persons:
  369. self._connect(user.name)
  370. self._persons[user.name].message(user,msg)
  371.  
  372. def getConnections(self):
  373. return list(self._persons.values())
  374.  
  375. ################################################################
  376. # PM class
  377. ################################################################
  378. class PM:
  379. """Manages a connection with Chatango PM."""
  380. ####
  381. # Init
  382. ####
  383. def __init__(self, mgr):
  384. self._auth_re = re.compile(r"auth\.chatango\.com ?= ?([^;]*)", re.IGNORECASE)
  385. self._connected = False
  386. self._mgr = mgr
  387. self._auid = None
  388. self._blocklist = set()
  389. self._contacts = set()
  390. self._status = dict()
  391. self._wlock = False
  392. self._firstCommand = True
  393. self._wbuf = b""
  394. self._wlockbuf = b""
  395. self._rbuf = b""
  396. self._pingTask = None
  397. self._connect()
  398.  
  399. ####
  400. # Connections
  401. ####
  402. def _connect(self):
  403. self._wbuf = b""
  404. self._sock = socket.socket()
  405. self._sock.connect((self._mgr._PMHost, self._mgr._PMPort))
  406. self._sock.setblocking(False)
  407. self._firstCommand = True
  408. if not self._auth(): return
  409. self._pingTask = self.mgr.setInterval(self._mgr._pingDelay, self.ping)
  410. self._connected = True
  411.  
  412.  
  413. def _getAuth(self, name, password):
  414. """
  415. Request an auid using name and password.
  416.  
  417. @type name: str
  418. @param name: name
  419. @type password: str
  420. @param password: password
  421.  
  422. @rtype: str
  423. @return: auid
  424. """
  425. data = urllib.parse.urlencode({
  426. "user_id": name,
  427. "password": password,
  428. "storecookie": "on",
  429. "checkerrors": "yes"
  430. }).encode()
  431. try:
  432. resp = urllib.request.urlopen("http://chatango.com/login", data)
  433. headers = resp.headers
  434. except Exception:
  435. return None
  436. for header, value in headers.items():
  437. if header.lower() == "set-cookie":
  438. m = self._auth_re.search(value)
  439. if m:
  440. auth = m.group(1)
  441. if auth == "":
  442. return None
  443. return auth
  444. return None
  445.  
  446. def _auth(self):
  447. self._auid = self._getAuth(self._mgr.name, self._mgr.password)
  448. if self._auid == None:
  449. self._sock.close()
  450. self._callEvent("onLoginFail")
  451. self._sock = None
  452. return False
  453. self._sendCommand("tlogin", self._auid, "2")
  454. self._setWriteLock(True)
  455. return True
  456.  
  457. def disconnect(self):
  458. """Disconnect the bot from PM"""
  459. self._disconnect()
  460. self._callEvent("onPMDisconnect")
  461.  
  462. def _disconnect(self):
  463. self._connected = False
  464. self._sock.close()
  465. self._sock = None
  466.  
  467. ####
  468. # Feed
  469. ####
  470. def _feed(self, data):
  471. """
  472. Feed data to the connection.
  473.  
  474. @type data: bytes
  475. @param data: data to be fed
  476. """
  477. self._rbuf += data
  478. while self._rbuf.find(b"\x00") != -1:
  479. data = self._rbuf.split(b"\x00")
  480. for food in data[:-1]:
  481. self._process(food.decode(errors="replace").rstrip("\r\n"))
  482. self._rbuf = data[-1]
  483.  
  484. def _process(self, data):
  485. """
  486. Process a command string.
  487.  
  488. @type data: str
  489. @param data: the command string
  490. """
  491. self._callEvent("onRaw", data)
  492. data = data.split(":")
  493. cmd, args = data[0], data[1:]
  494. func = "_rcmd_" + cmd
  495. if hasattr(self, func):
  496. getattr(self, func)(args)
  497. else:
  498. if debug:
  499. print("unknown data: "+str(data))
  500.  
  501. ####
  502. # Properties
  503. ####
  504. def _getManager(self): return self._mgr
  505. def _getContacts(self): return self._contacts
  506. def _getBlocklist(self): return self._blocklist
  507.  
  508. mgr = property(_getManager)
  509. contacts = property(_getContacts)
  510. blocklist = property(_getBlocklist)
  511.  
  512. ####
  513. # Received Commands
  514. ####
  515. def _rcmd_OK(self, args):
  516. self._setWriteLock(False)
  517. self._sendCommand("wl")
  518. self._sendCommand("getblock")
  519. self._callEvent("onPMConnect")
  520.  
  521. def _rcmd_wl(self, args):
  522. self._contacts = set()
  523. for i in range(len(args) // 4):
  524. name, last_on, is_on, idle = args[i * 4: i * 4 + 4]
  525. user = User(name)
  526. if last_on=="None":pass#in case chatango gives a "None" as data argument
  527. elif not is_on == "on": self._status[user] = [int(last_on), False, 0]
  528. elif idle == '0': self._status[user] = [int(last_on), True, 0]
  529. else: self._status[user] = [int(last_on), True, time.time() - int(idle) * 60]
  530. self._contacts.add(user)
  531. self._callEvent("onPMContactlistReceive")
  532.  
  533. def _rcmd_block_list(self, args):
  534. self._blocklist = set()
  535. for name in args:
  536. if name == "": continue
  537. self._blocklist.add(User(name))
  538.  
  539. def _rcmd_idleupdate(self, args):
  540. user = User(args[0])
  541. try: last_on, is_on, idle = self._status[user]
  542. except:pass
  543. if args[1] == '1':
  544. self._status[user] = [last_on, is_on, 0]
  545. else:
  546. try: self._status[user] = [last_on, is_on, time.time()]
  547. except:pass
  548.  
  549. def _rcmd_track(self, args):
  550. user = User(args[0])
  551. if user in self._status:
  552. last_on = self._status[user][0]
  553. else:
  554. last_on = 0
  555. if args[1] == '0':
  556. idle = 0
  557. else:
  558. idle = time.time() - int(args[1]) * 60
  559. if args[2] == "online":
  560. is_on = True
  561. else:
  562. is_on = False
  563. self._status[user] = [last_on, is_on, idle]
  564.  
  565. def _rcmd_DENIED(self, args):
  566. self._disconnect()
  567. self._callEvent("onLoginFail")
  568.  
  569. def _rcmd_msg(self, args):
  570. user = User(args[0])
  571. body = _strip_html(":".join(args[5:]))
  572. self._callEvent("onPMMessage", user, body)
  573.  
  574. def _rcmd_msgoff(self, args):
  575. user = User(args[0])
  576. body = _strip_html(":".join(args[5:]))
  577. self._callEvent("onPMOfflineMessage", user, body)
  578.  
  579. def _rcmd_wlonline(self, args):
  580. user = User(args[0])
  581. last_on = float(args[1])
  582. self._status[user] = [last_on,True,last_on]
  583. self._callEvent("onPMContactOnline", user)
  584.  
  585. def _rcmd_wloffline(self, args):
  586. user = User(args[0])
  587. last_on = float(args[1])
  588. self._status[user] = [last_on,False,0]
  589. self._callEvent("onPMContactOffline", user)
  590.  
  591. def _rcmd_kickingoff(self, args):
  592. self.disconnect()
  593.  
  594. def _rcmd_toofast(self, args):
  595. self.disconnect()
  596.  
  597. def _rcmd_unblocked(self, user):
  598. """call when successfully unblocked"""
  599. if user in self._blocklist:
  600. self._blocklist.remove(user)
  601. self._callEvent("onPMUnblock", user)
  602.  
  603.  
  604. ####
  605. # Commands
  606. ####
  607. def ping(self):
  608. """send a ping"""
  609. self._sendCommand("")
  610. self._callEvent("onPMPing")
  611.  
  612. def message(self, user, msg):
  613. """send a pm to a user"""
  614. if msg!=None:
  615. self._sendCommand("msg", user.name, msg)
  616.  
  617. def addContact(self, user):
  618. """add contact"""
  619. if user not in self._contacts:
  620. self._sendCommand("wladd", user.name)
  621. self._contacts.add(user)
  622. self._callEvent("onPMContactAdd", user)
  623.  
  624. def removeContact(self, user):
  625. """remove contact"""
  626. if user in self._contacts:
  627. self._sendCommand("wldelete", user.name)
  628. self._contacts.remove(user)
  629. self._callEvent("onPMContactRemove", user)
  630.  
  631. def block(self, user):
  632. """block a person"""
  633. if user not in self._blocklist:
  634. self._sendCommand("block", user.name, user.name, "S")
  635. self._blocklist.add(user)
  636. self._callEvent("onPMBlock", user)
  637.  
  638. def unblock(self, user):
  639. """unblock a person"""
  640. if user in self._blocklist:
  641. self._sendCommand("unblock", user.name)
  642.  
  643. def track(self, user):
  644. """get and store status of person for future use"""
  645. self._sendCommand("track", user.name)
  646.  
  647. def checkOnline(self, user):
  648. """return True if online, False if offline, None if unknown"""
  649. if user in self._status:
  650. return self._status[user][1]
  651. else:
  652. return None
  653.  
  654. def getIdle(self, user):
  655. """return last active time, time.time() if isn't idle, 0 if offline, None if unknown"""
  656. if not user in self._status: return None
  657. if not self._status[user][1]: return 0
  658. if not self._status[user][2]: return time.time()
  659. else: return self._status[user][2]
  660.  
  661. ####
  662. # Util
  663. ####
  664. def _callEvent(self, evt, *args, **kw):
  665. getattr(self.mgr, evt)(self, *args, **kw)
  666. self.mgr.onEventCalled(self, evt, *args, **kw)
  667.  
  668. def _write(self, data):
  669. if self._wlock:
  670. self._wlockbuf += data
  671. else:
  672. self.mgr._write(self, data)
  673.  
  674. def _setWriteLock(self, lock):
  675. self._wlock = lock
  676. if self._wlock == False:
  677. self._write(self._wlockbuf)
  678. self._wlockbuf = b""
  679.  
  680. def _sendCommand(self, *args):
  681. """
  682. Send a command.
  683.  
  684. @type args: [str, str, ...]
  685. @param args: command and list of arguments
  686. """
  687. if self._firstCommand:
  688. terminator = b"\x00"
  689. self._firstCommand = False
  690. else:
  691. terminator = b"\r\n\x00"
  692. self._write(":".join(args).encode() + terminator)
  693.  
  694. def getConnections(self):
  695. return [self]
  696.  
  697. ################################################################
  698. # Room class
  699. ################################################################
  700. class Room:
  701. """Manages a connection with a Chatango room."""
  702. ####
  703. # Init
  704. ####
  705. def __init__(self, room, uid = None, server = None, port = None, mgr = None):
  706. """init, don't overwrite"""
  707. # Basic stuff
  708. self._name = room
  709. self._server = server or getServer(room)
  710. self._port = port or 443
  711. self._mgr = mgr
  712.  
  713. # Under the hood
  714. self._connected = False
  715. self._reconnecting = False
  716. self._uid = uid or _genUid()
  717. self._rbuf = b""
  718. self._wbuf = b""
  719. self._wlockbuf = b""
  720. self._owner = None
  721. self._mods = set()
  722. self._mqueue = dict()
  723. self._history = list()
  724. self._userlist = list()
  725. self._firstCommand = True
  726. self._connectAmmount = 0
  727. self._premium = False
  728. self._userCount = 0
  729. self._pingTask = None
  730. self._botname = None
  731. self._currentname = None
  732. self._users = dict()
  733. self._msgs = dict()
  734. self._wlock = False
  735. self._silent = False
  736. self._banlist = dict()
  737. self._unbanlist = dict()
  738.  
  739. # Inited vars
  740. if self._mgr: self._connect()
  741.  
  742. ####
  743. # Connect/disconnect
  744. ####
  745. def _connect(self):
  746. """Connect to the server."""
  747. self._sock = socket.socket()
  748. self._sock.connect((self._server, self._port))
  749. self._sock.setblocking(False)
  750. self._firstCommand = True
  751. self._wbuf = b""
  752. self._auth()
  753. self._pingTask = self.mgr.setInterval(self.mgr._pingDelay, self.ping)
  754. if not self._reconnecting: self.connected = True
  755.  
  756. def reconnect(self):
  757. """Reconnect."""
  758. self._reconnect()
  759.  
  760. def _reconnect(self):
  761. """Reconnect."""
  762. self._reconnecting = True
  763. if self.connected:
  764. self._disconnect()
  765. self._uid = _genUid()
  766. self._connect()
  767. self._reconnecting = False
  768.  
  769. def disconnect(self):
  770. """Disconnect."""
  771. self._disconnect()
  772. self._callEvent("onDisconnect")
  773.  
  774. def _disconnect(self):
  775. """Disconnect from the server."""
  776. if not self._reconnecting: self.connected = False
  777. for user in self._userlist:
  778. user.clearSessionIds(self)
  779. self._userlist = list()
  780. self._pingTask.cancel()
  781. self._sock.close()
  782. if not self._reconnecting: del self.mgr._rooms[self.name]
  783.  
  784. def _auth(self):
  785. """Authenticate."""
  786. # login as name with password
  787. if self.mgr.name and self.mgr.password:
  788. self._sendCommand("bauth", self.name, self._uid, self.mgr.name, self.mgr.password)
  789. self._currentname = self.mgr.name
  790. # login as anon
  791. else:
  792. self._sendCommand("bauth", self.name)
  793.  
  794. self._setWriteLock(True)
  795.  
  796. ####
  797. # Properties
  798. ####
  799. def _getName(self): return self._name
  800. def _getBotName(self):
  801. if self.mgr.name and self.mgr.password:
  802. return self.mgr.name
  803. elif self.mgr.name and self.mgr.password == None:
  804. return "#" + self.mgr.name
  805. elif self.mgr.name == None:
  806. return self._botname
  807. def _getCurrentname(self): return self._currentname
  808. def _getManager(self): return self._mgr
  809. def _getUserlist(self, mode = None, unique = None, memory = None):
  810. ul = None
  811. if mode == None: mode = self.mgr._userlistMode
  812. if unique == None: unique = self.mgr._userlistUnique
  813. if memory == None: memory = self.mgr._userlistMemory
  814. if mode == Userlist_Recent:
  815. ul = map(lambda x: x.user, self._history[-memory:])
  816. elif mode == Userlist_All:
  817. ul = self._userlist
  818. if unique:
  819. return list(set(ul))
  820. else:
  821. return ul
  822. def _getUserNames(self):
  823. ul = self.userlist
  824. return list(map(lambda x: x.name, ul))
  825. def _getUser(self): return self.mgr.user
  826. def _getOwner(self): return self._owner
  827. def _getOwnerName(self): return self._owner.name
  828. def _getMods(self):
  829. newset = set()
  830. for mod in self._mods:
  831. newset.add(mod)
  832. return newset
  833. def _getModNames(self):
  834. mods = self._getMods()
  835. return [x.name for x in mods]
  836. def _getUserCount(self): return self._userCount
  837. def _getSilent(self): return self._silent
  838. def _setSilent(self, val): self._silent = val
  839. def _getBanlist(self): return list(self._banlist.keys())
  840. def _getUnBanlist(self): return [[record["target"], record["src"]] for record in self._unbanlist.values()]
  841.  
  842. name = property(_getName)
  843. botname = property(_getBotName)
  844. currentname = property(_getCurrentname)
  845. mgr = property(_getManager)
  846. userlist = property(_getUserlist)
  847. usernames = property(_getUserNames)
  848. user = property(_getUser)
  849. owner = property(_getOwner)
  850. ownername = property(_getOwnerName)
  851. mods = property(_getMods)
  852. modnames = property(_getModNames)
  853. usercount = property(_getUserCount)
  854. silent = property(_getSilent, _setSilent)
  855. banlist = property(_getBanlist)
  856. unbanlist = property(_getUnBanlist)
  857.  
  858. ####
  859. # Feed/process
  860. ####
  861. def _feed(self, data):
  862. """
  863. Feed data to the connection.
  864.  
  865. @type data: bytes
  866. @param data: data to be fed
  867. """
  868. self._rbuf += data
  869. while self._rbuf.find(b"\x00") != -1:
  870. data = self._rbuf.split(b"\x00")
  871. for food in data[:-1]:
  872. self._process(food.decode(errors="replace").rstrip("\r\n"))
  873. self._rbuf = data[-1]
  874.  
  875. def _process(self, data):
  876. """
  877. Process a command string.
  878.  
  879. @type data: str
  880. @param data: the command string
  881. """
  882. self._callEvent("onRaw", data)
  883. data = data.split(":")
  884. cmd, args = data[0], data[1:]
  885. func = "_rcmd_" + cmd
  886. if hasattr(self, func):
  887. getattr(self, func)(args)
  888. else:
  889. if debug:
  890. print("unknown data: "+str(data))
  891.  
  892. ####
  893. # Received Commands
  894. ####
  895. def _rcmd_ok(self, args):
  896. # if no name, join room as anon and no password
  897. if args[2] == "N" and self.mgr.password == None and self.mgr.name == None:
  898. n = args[4].rsplit('.', 1)[0]
  899. n = n[-4:]
  900. aid = args[1][0:8]
  901. pid = "!anon" + _getAnonId(n, aid)
  902. self._botname = pid
  903. self._currentname = pid
  904. self.user._nameColor = n
  905. # if got name, join room as name and no password
  906. elif args[2] == "N" and self.mgr.password == None:
  907. self._sendCommand("blogin", self.mgr.name)
  908. self._currentname = self.mgr.name
  909. # if got password but fail to login
  910. elif args[2] != "M": #unsuccesful login
  911. self._callEvent("onLoginFail")
  912. self.disconnect()
  913. self._owner = User(args[0])
  914. self._uid = args[1]
  915. self._aid = args[1][4:8]
  916. self._mods = set(map(lambda x: User(x.split(",")[0]), args[6].split(";")))
  917. self._i_log = list()
  918.  
  919. def _rcmd_denied(self, args):
  920. self._disconnect()
  921. self._callEvent("onConnectFail")
  922.  
  923. def _rcmd_inited(self, args):
  924. self._sendCommand("g_participants", "start")
  925. self._sendCommand("getpremium", "1")
  926. self.requestBanlist()
  927. self.requestUnBanlist()
  928. if self._connectAmmount == 0:
  929. self._callEvent("onConnect")
  930. for msg in reversed(self._i_log):
  931. user = msg.user
  932. self._callEvent("onHistoryMessage", user, msg)
  933. self._addHistory(msg)
  934. del self._i_log
  935. else:
  936. self._callEvent("onReconnect")
  937. self._connectAmmount += 1
  938. self._setWriteLock(False)
  939.  
  940. def _rcmd_premium(self, args):
  941. if float(args[1]) > time.time():
  942. self._premium = True
  943. if self.user._mbg: self.setBgMode(1)
  944. if self.user._mrec: self.setRecordingMode(1)
  945. else:
  946. self._premium = False
  947.  
  948. def _rcmd_mods(self, args):
  949. modnames = args
  950. mods = set(map(lambda x: User(x.split(",")[0]), modnames))
  951. premods = self._mods
  952. for user in mods - premods: #modded
  953. self._mods.add(user)
  954. self._callEvent("onModAdd", user)
  955. for user in premods - mods: #demodded
  956. self._mods.remove(user)
  957. self._callEvent("onModRemove", user)
  958. self._callEvent("onModChange")
  959.  
  960. def _rcmd_b(self, args):
  961. mtime = float(args[0])
  962. puid = args[3]
  963. ip = args[6]
  964. name = args[1]
  965. rawmsg = ":".join(args[9:])
  966. msg, n, f = _clean_message(rawmsg)
  967. if name == "":
  968. nameColor = None
  969. name = "#" + args[2]
  970. if name == "#":
  971. name = "!anon" + _getAnonId(n, puid)
  972. else:
  973. if n: nameColor = _parseNameColor(n)
  974. else: nameColor = None
  975. i = args[5]
  976. unid = args[4]
  977. user = User(name)
  978. if puid:
  979. user.updatePuid(puid)
  980. #Create an anonymous message and queue it because msgid is unknown.
  981. if f: fontColor, fontFace, fontSize = _parseFont(f)
  982. else: fontColor, fontFace, fontSize = None, None, None
  983. msg = Message(
  984. time = mtime,
  985. user = user,
  986. body = msg,
  987. raw = rawmsg,
  988. ip = ip,
  989. nameColor = nameColor,
  990. fontColor = fontColor,
  991. fontFace = fontFace,
  992. fontSize = fontSize,
  993. unid = unid,
  994. room = self
  995. )
  996. self._mqueue[i] = msg
  997.  
  998. def _rcmd_u(self, args):
  999. temp = Struct(**self._mqueue)
  1000. if hasattr(temp, args[0]):
  1001. msg = getattr(temp, args[0])
  1002. if msg.user != self.user:
  1003. msg.user._fontColor = msg.fontColor
  1004. msg.user._fontFace = msg.fontFace
  1005. msg.user._fontSize = msg.fontSize
  1006. msg.user._nameColor = msg.nameColor
  1007. del self._mqueue[args[0]]
  1008. msg.attach(self, args[1])
  1009. self._addHistory(msg)
  1010. self._callEvent("onMessage", msg.user, msg)
  1011.  
  1012. def _rcmd_i(self, args):
  1013. mtime = float(args[0])
  1014. puid = args[3]
  1015. ip = args[6]
  1016. name = args[1]
  1017. rawmsg = ":".join(args[9:])
  1018. msg, n, f = _clean_message(rawmsg)
  1019. if name == "":
  1020. nameColor = None
  1021. name = "#" + args[2]
  1022. if name == "#":
  1023. name = "!anon" + _getAnonId(n, puid)
  1024. else:
  1025. if n: nameColor = _parseNameColor(n)
  1026. else: nameColor = None
  1027. i = args[5]
  1028. unid = args[4]
  1029. user = User(name)
  1030. if puid:
  1031. user.updatePuid(puid)
  1032. #Create an anonymous message and queue it because msgid is unknown.
  1033. if f: fontColor, fontFace, fontSize = _parseFont(f)
  1034. else: fontColor, fontFace, fontSize = None, None, None
  1035. msg = Message(
  1036. time = mtime,
  1037. user = user,
  1038. body = msg,
  1039. raw = rawmsg,
  1040. ip = ip,
  1041. nameColor = nameColor,
  1042. fontColor = fontColor,
  1043. fontFace = fontFace,
  1044. fontSize = fontSize,
  1045. unid = unid,
  1046. room = self
  1047. )
  1048. self._i_log.append(msg)
  1049.  
  1050. def _rcmd_g_participants(self, args):
  1051. args = ":".join(args)
  1052. args = args.split(";")
  1053. for data in args:
  1054. data = data.split(":")
  1055. name = data[3].lower()
  1056. if name == "none": continue
  1057. user = User(
  1058. name = name,
  1059. room = self
  1060. )
  1061. user.addSessionId(self, data[0])
  1062. self._userlist.append(user)
  1063.  
  1064. def _rcmd_participant(self, args):
  1065. name = args[3].lower()
  1066. if name == "none": return
  1067. user = User(name)
  1068. puid = args[2]
  1069. if puid:
  1070. user.updatePuid(puid)
  1071.  
  1072. if args[0] == "0": #leave
  1073. user.removeSessionId(self, args[1])
  1074. try:last_on, is_on, idle = self._status[user]
  1075. except:pass
  1076. if user not in self._userlist or not self.mgr._userlistEventUnique:
  1077. self._callEvent("onLeave", user)
  1078. else: #join
  1079. user.addSessionId(self, args[1])
  1080. if user not in self._userlist: doEvent = True
  1081. else: doEvent = False
  1082. self._userlist.append(user)
  1083. if doEvent or not self.mgr._userlistEventUnique:
  1084. self._callEvent("onJoin", user)
  1085.  
  1086. def _rcmd_show_fw(self, args):
  1087. self._callEvent("onFloodWarning")
  1088.  
  1089. def _rcmd_show_tb(self, args):
  1090. self._callEvent("onFloodBan")
  1091.  
  1092. def _rcmd_tb(self, args):
  1093. self._callEvent("onFloodBanRepeat")
  1094.  
  1095. def _rcmd_delete(self, args):
  1096. msg = self._msgs.get(args[0])
  1097. if msg:
  1098. if msg in self._history:
  1099. self._history.remove(msg)
  1100. self._callEvent("onMessageDelete", msg.user, msg)
  1101. msg.detach()
  1102.  
  1103. def _rcmd_deleteall(self, args):
  1104. for msgid in args:
  1105. self._rcmd_delete([msgid])
  1106.  
  1107. def _rcmd_n(self, args):
  1108. self._userCount = int(args[0], 16)
  1109. self._callEvent("onUserCountChange")
  1110.  
  1111. def _rcmd_blocklist(self, args):
  1112. self._banlist = dict()
  1113. sections = ":".join(args).split(";")
  1114. for section in sections:
  1115. params = section.split(":")
  1116. if len(params) != 5: continue
  1117. if params[2] == "": continue
  1118. user = User(params[2])
  1119. self._banlist[user] = {
  1120. "unid":params[0],
  1121. "ip":params[1],
  1122. "target":user,
  1123. "time":float(params[3]),
  1124. "src":User(params[4])
  1125. }
  1126. self._callEvent("onBanlistUpdate")
  1127.  
  1128. def _rcmd_unblocklist(self, args):
  1129. self._unbanlist = dict()
  1130. sections = ":".join(args).split(";")
  1131. for section in sections:
  1132. params = section.split(":")
  1133. if len(params) != 5: continue
  1134. if params[2] == "": continue
  1135. user = User(params[2])
  1136. self._unbanlist[user] = {
  1137. "unid":params[0],
  1138. "ip":params[1],
  1139. "target":user,
  1140. "time":float(params[3]),
  1141. "src":User(params[4])
  1142. }
  1143. self._callEvent("onUnBanlistUpdate")
  1144.  
  1145. def _rcmd_blocked(self, args):
  1146. if args[2] == "": return
  1147. target = User(args[2])
  1148. user = User(args[3])
  1149. self._banlist[target] = {"unid":args[0], "ip":args[1], "target":target, "time":float(args[4]), "src":user}
  1150. self._callEvent("onBan", user, target)
  1151.  
  1152. def _rcmd_unblocked(self, args):
  1153. if args[2] == "": return
  1154. target = User(args[2])
  1155. user=User(args[3])
  1156. del self._banlist[target]
  1157. self._unbanlist[user] = {"unid":args[0], "ip":args[1], "target":target, "time":float(args[4]), "src":user}
  1158. self._callEvent("onUnban", user, target)
  1159.  
  1160. ####
  1161. # Commands
  1162. ####
  1163. def login(self, NAME, PASS = None):
  1164. """login as a user or set a name in room"""
  1165. if PASS:
  1166. self._sendCommand("blogin", NAME, PASS)
  1167. else:
  1168. self._sendCommand("blogin", NAME)
  1169. self._currentname = NAME
  1170.  
  1171. def logout(self):
  1172. """logout of user in a room"""
  1173. self._sendCommand("blogout")
  1174. self._currentname = self._botname
  1175.  
  1176. def ping(self):
  1177. """Send a ping."""
  1178. self._sendCommand("")
  1179. self._callEvent("onPing")
  1180.  
  1181. def rawMessage(self, msg):
  1182. """
  1183. Send a message without n and f tags.
  1184.  
  1185. @type msg: str
  1186. @param msg: message
  1187. """
  1188. if not self._silent:
  1189. self._sendCommand("bmsg:tl2r", msg)
  1190.  
  1191. def message(self, msg, html = False):
  1192. """
  1193. Send a message. (Use "\n" for new line)
  1194.  
  1195. @type msg: str
  1196. @param msg: message
  1197. """
  1198. if msg==None:
  1199. return
  1200. msg = msg.rstrip()
  1201. if not html:
  1202. msg = msg.replace("<", "&lt;").replace(">", "&gt;")
  1203. if len(msg) > self.mgr._maxLength:
  1204. if self.mgr._tooBigMessage == BigMessage_Cut:
  1205. self.message(msg[:self.mgr._maxLength], html = html)
  1206. elif self.mgr._tooBigMessage == BigMessage_Multiple:
  1207. while len(msg) > 0:
  1208. sect = msg[:self.mgr._maxLength]
  1209. msg = msg[self.mgr._maxLength:]
  1210. self.message(sect, html = html)
  1211. return
  1212. msg = "<n" + self.user.nameColor + "/>" + msg
  1213. if self._currentname != None and not self._currentname.startswith("!anon"):
  1214. font_properties = "<f x%0.2i%s=\"%s\">" %(self.user.fontSize, self.user.fontColor, self.user.fontFace)
  1215. if "\n" in msg:
  1216. msg.replace("\n", "</f></p><p>%s" %(font_properties))
  1217. msg = font_properties + msg
  1218. msg.replace("~","&#126;")
  1219. self.rawMessage(msg)
  1220.  
  1221. def setBgMode(self, mode):
  1222. """turn on/off bg"""
  1223. self._sendCommand("msgbg", str(mode))
  1224.  
  1225. def setRecordingMode(self, mode):
  1226. """turn on/off rcecording"""
  1227. self._sendCommand("msgmedia", str(mode))
  1228.  
  1229. def addMod(self, user):
  1230. """
  1231. Add a moderator.
  1232.  
  1233. @type user: User
  1234. @param user: User to mod.
  1235. """
  1236. if self.getLevel(User(self.currentname)) == 2:
  1237. self._sendCommand("addmod", user.name)
  1238.  
  1239. def removeMod(self, user):
  1240. """
  1241. Remove a moderator.
  1242.  
  1243. @type user: User
  1244. @param user: User to demod.
  1245. """
  1246. if self.getLevel(User(self.currentname)) == 2:
  1247. self._sendCommand("removemod", user.name)
  1248.  
  1249. def flag(self, message):
  1250. """
  1251. Flag a message.
  1252.  
  1253. @type message: Message
  1254. @param message: message to flag
  1255. """
  1256. self._sendCommand("g_flag", message.msgid)
  1257.  
  1258. def flagUser(self, user):
  1259. """
  1260. Flag a user.
  1261.  
  1262. @type user: User
  1263. @param user: user to flag
  1264.  
  1265. @rtype: bool
  1266. @return: whether a message to flag was found
  1267. """
  1268. msg = self.getLastMessage(user)
  1269. if msg:
  1270. self.flag(msg)
  1271. return True
  1272. return False
  1273.  
  1274. def deleteMessage(self, message):
  1275. """
  1276. Delete a message. (Moderator only)
  1277.  
  1278. @type message: Message
  1279. @param message: message to delete
  1280. """
  1281. if self.getLevel(self.user) > 0:
  1282. self._sendCommand("delmsg", message.msgid)
  1283.  
  1284. def deleteUser(self, user):
  1285. """
  1286. Delete a message. (Moderator only)
  1287.  
  1288. @type message: User
  1289. @param message: delete user's last message
  1290. """
  1291. if self.getLevel(self.user) > 0:
  1292. msg = self.getLastMessage(user)
  1293. if msg:
  1294. self._sendCommand("delmsg", msg.msgid)
  1295. return True
  1296. return False
  1297.  
  1298. def delete(self, user):
  1299. """
  1300. compatibility wrapper for deleteUser
  1301. """
  1302. print("[obsolete] the delete function is obsolete, please use deleteUser")
  1303. return self.deleteUser(user)
  1304.  
  1305. def rawClearUser(self, unid, ip, user):
  1306. self._sendCommand("delallmsg", unid, ip, user)
  1307. def clearUser(self, user):
  1308. """
  1309. Clear all of a user's messages. (Moderator only)
  1310.  
  1311. @type user: User
  1312. @param user: user to delete messages of
  1313.  
  1314. @rtype: bool
  1315. @return: whether a message to delete was found
  1316. """
  1317. if self.getLevel(self.user) > 0:
  1318. msg = self.getLastMessage(user)
  1319. if msg:
  1320. if msg.user.name[0] in ["!","#"]:self.rawClearUser(msg.unid, msg.ip,"")
  1321. else:self.rawClearUser(msg.unid,msg.ip,msg.user.name)
  1322. return True
  1323. return False
  1324.  
  1325. def clearall(self):
  1326. """Clear all messages. (Owner only)"""
  1327. if self.getLevel(self.user) == 2:
  1328. self._sendCommand("clearall")
  1329.  
  1330. def rawBan(self, name, ip, unid):
  1331. """
  1332. Execute the block command using specified arguments.
  1333. (For advanced usage)
  1334.  
  1335. @type name: str
  1336. @param name: name
  1337. @type ip: str
  1338. @param ip: ip address
  1339. @type unid: str
  1340. @param unid: unid
  1341. """
  1342. self._sendCommand("block", unid, ip, name)
  1343.  
  1344. def ban(self, msg):
  1345. """
  1346. Ban a message's sender. (Moderator only)
  1347.  
  1348. @type message: Message
  1349. @param message: message to ban sender of
  1350. """
  1351. if self.getLevel(self.user) > 0:
  1352. self.rawBan(msg.user.name, msg.ip, msg.unid)
  1353.  
  1354. def banUser(self, user):
  1355. """
  1356. Ban a user. (Moderator only)
  1357.  
  1358. @type user: User
  1359. @param user: user to ban
  1360.  
  1361. @rtype: bool
  1362. @return: whether a message to ban the user was found
  1363. """
  1364. msg = self.getLastMessage(user)
  1365. if msg:
  1366. self.ban(msg)
  1367. return True
  1368. return False
  1369.  
  1370. def requestBanlist(self):
  1371. """Request an updated banlist."""
  1372. self._sendCommand("blocklist", "block", "", "next", "500")
  1373.  
  1374. def requestUnBanlist(self):
  1375. """Request an updated banlist."""
  1376. self._sendCommand("blocklist", "unblock", "", "next", "500")
  1377.  
  1378. def rawUnban(self, name, ip, unid):
  1379. """
  1380. Execute the unblock command using specified arguments.
  1381. (For advanced usage)
  1382.  
  1383. @type name: str
  1384. @param name: name
  1385. @type ip: str
  1386. @param ip: ip address
  1387. @type unid: str
  1388. @param unid: unid
  1389. """
  1390. self._sendCommand("removeblock", unid, ip, name)
  1391.  
  1392. def unban(self, user):
  1393. """
  1394. Unban a user. (Moderator only)
  1395.  
  1396. @type user: User
  1397. @param user: user to unban
  1398.  
  1399. @rtype: bool
  1400. @return: whether it succeeded
  1401. """
  1402. rec = self._getBanRecord(user)
  1403. if rec:
  1404. self.rawUnban(rec["target"].name, rec["ip"], rec["unid"])
  1405. return True
  1406. else:
  1407. return False
  1408.  
  1409. ####
  1410. # Util
  1411. ####
  1412. def _getBanRecord(self, user):
  1413. if user in self._banlist:
  1414. return self._banlist[user]
  1415. return None
  1416.  
  1417. def _callEvent(self, evt, *args, **kw):
  1418. getattr(self.mgr, evt)(self, *args, **kw)
  1419. self.mgr.onEventCalled(self, evt, *args, **kw)
  1420.  
  1421. def _write(self, data):
  1422. if self._wlock:
  1423. self._wlockbuf += data
  1424. else:
  1425. self.mgr._write(self, data)
  1426.  
  1427. def _setWriteLock(self, lock):
  1428. self._wlock = lock
  1429. if self._wlock == False:
  1430. self._write(self._wlockbuf)
  1431. self._wlockbuf = b""
  1432.  
  1433. def _sendCommand(self, *args):
  1434. """
  1435. Send a command.
  1436.  
  1437. @type args: [str, str, ...]
  1438. @param args: command and list of arguments
  1439. """
  1440. if self._firstCommand:
  1441. terminator = b"\x00"
  1442. self._firstCommand = False
  1443. else:
  1444. terminator = b"\r\n\x00"
  1445. self._write(":".join(args).encode() + terminator)
  1446.  
  1447. def getLevel(self, user):
  1448. """get the level of user in a room"""
  1449. if user == self._owner: return 2
  1450. if user.name in self.modnames: return 1
  1451. return 0
  1452.  
  1453. def getLastMessage(self, user = None):
  1454. """get last message said by user in a room"""
  1455. if user:
  1456. try:
  1457. i = 1
  1458. while True:
  1459. msg = self._history[-i]
  1460. if msg.user == user:
  1461. return msg
  1462. i += 1
  1463. except IndexError:
  1464. return None
  1465. else:
  1466. try:
  1467. return self._history[-1]
  1468. except IndexError:
  1469. return None
  1470. return None
  1471.  
  1472. def findUser(self, name):
  1473. """check if user is in the room
  1474.  
  1475. return User(name) if name in room else None"""
  1476. name = name.lower()
  1477. ul = self._getUserlist()
  1478. udi = dict(zip([u.name for u in ul], ul))
  1479. cname = None
  1480. for n in udi.keys():
  1481. if name in n:
  1482. if cname: return None #ambiguous!!
  1483. cname = n
  1484. if cname: return udi[cname]
  1485. else: return None
  1486.  
  1487. ####
  1488. # History
  1489. ####
  1490. def _addHistory(self, msg):
  1491. """
  1492. Add a message to history.
  1493.  
  1494. @type msg: Message
  1495. @param msg: message
  1496. """
  1497. self._history.append(msg)
  1498. if len(self._history) > self.mgr._maxHistoryLength:
  1499. rest, self._history = self._history[:-self.mgr._maxHistoryLength], self._history[-self.mgr._maxHistoryLength:]
  1500. for msg in rest: msg.detach()
  1501.  
  1502. ################################################################
  1503. # RoomManager class
  1504. ################################################################
  1505. class RoomManager:
  1506. """Class that manages multiple connections."""
  1507. ####
  1508. # Config
  1509. ####
  1510. _Room = Room
  1511. _PM = PM
  1512. _ANON_PM = ANON_PM
  1513. _anonPMHost = "b1.chatango.com"
  1514. _PMHost = "c1.chatango.com"
  1515. _PMPort = 5222
  1516. _TimerResolution = 0.2 #at least x times per second
  1517. _pingDelay = 20
  1518. _userlistMode = Userlist_Recent
  1519. _userlistUnique = True
  1520. _userlistMemory = 50
  1521. _userlistEventUnique = False
  1522. _tooBigMessage = BigMessage_Multiple
  1523. _maxLength = 1800
  1524. _maxHistoryLength = 150
  1525.  
  1526. ####
  1527. # Init
  1528. ####
  1529. def __init__(self, name = None, password = None, pm = True):
  1530. self._name = name
  1531. self._password = password
  1532. self._running = False
  1533. self._tasks = set()
  1534. self._rooms = dict()
  1535. self._rooms_queue = queue.Queue()
  1536. self._rooms_lock = threading.Lock()
  1537. if pm:
  1538. if self._password:
  1539. self._pm = self._PM(mgr = self)
  1540. else:
  1541. self._pm = self._ANON_PM(mgr = self)
  1542. else:
  1543. self._pm = None
  1544.  
  1545. def _joinThread(self):
  1546. while True:
  1547. room = self._rooms_queue.get()
  1548. with self._rooms_lock:
  1549. con = self._Room(room, mgr = self)
  1550. self._rooms[room] = con
  1551.  
  1552. ####
  1553. # Join/leave
  1554. ####
  1555. def joinRoom(self, room):
  1556. """
  1557. Join a room or return None if already joined.
  1558.  
  1559. @type room: str
  1560. @param room: room to join
  1561.  
  1562. @rtype: Room or None
  1563. @return: True or nothing
  1564. """
  1565. room = room.lower()
  1566. if room not in self._rooms:
  1567. self._rooms_queue.put(room)
  1568. return True
  1569. else:
  1570. return None
  1571.  
  1572. def leaveRoom(self, room):
  1573. """
  1574. Leave a room.
  1575.  
  1576. @type room: str
  1577. @param room: room to leave
  1578. """
  1579. room = room.lower()
  1580. if room in self._rooms:
  1581. with self._rooms_lock:
  1582. con = self._rooms[room]
  1583. con.disconnect()
  1584.  
  1585. def getRoom(self, room):
  1586. """
  1587. Get room with a name, or None if not connected to this room.
  1588.  
  1589. @type room: str
  1590. @param room: room
  1591.  
  1592. @rtype: Room
  1593. @return: the room
  1594. """
  1595. room = room.lower()
  1596. if room in self._rooms:
  1597. return self._rooms[room]
  1598. else:
  1599. return None
  1600.  
  1601. ####
  1602. # Properties
  1603. ####
  1604. def _getUser(self): return User(self._name)
  1605. def _getName(self): return self._name
  1606. def _getPassword(self): return self._password
  1607. def _getRooms(self): return set(self._rooms.values())
  1608. def _getRoomNames(self): return set(self._rooms.keys())
  1609. def _getPM(self): return self._pm
  1610.  
  1611. user = property(_getUser)
  1612. name = property(_getName)
  1613. password = property(_getPassword)
  1614. rooms = property(_getRooms)
  1615. roomnames = property(_getRoomNames)
  1616. pm = property(_getPM)
  1617.  
  1618. ####
  1619. # Virtual methods
  1620. ####
  1621. def onInit(self):
  1622. """Called on init."""
  1623. pass
  1624.  
  1625. def safePrint(self, text):
  1626. """Use this to safely print text with unicode"""
  1627. while True:
  1628. try:
  1629. print(text)
  1630. break
  1631. except UnicodeEncodeError as ex:
  1632. text = (text[0:ex.start]+'(unicode)'+text[ex.end:])
  1633.  
  1634. def onConnect(self, room):
  1635. """
  1636. Called when connected to the room.
  1637.  
  1638. @type room: Room
  1639. @param room: room where the event occured
  1640. """
  1641. pass
  1642.  
  1643. def onReconnect(self, room):
  1644. """
  1645. Called when reconnected to the room.
  1646.  
  1647. @type room: Room
  1648. @param room: room where the event occured
  1649. """
  1650. pass
  1651.  
  1652. def onConnectFail(self, room):
  1653. """
  1654. Called when the connection failed.
  1655.  
  1656. @type room: Room
  1657. @param room: room where the event occured
  1658. """
  1659. pass
  1660.  
  1661. def onDisconnect(self, room):
  1662. """
  1663. Called when the client gets disconnected.
  1664.  
  1665. @type room: Room
  1666. @param room: room where the event occured
  1667. """
  1668. pass
  1669.  
  1670. def onLoginFail(self, room):
  1671. """
  1672. Called on login failure, disconnects after.
  1673.  
  1674. @type room: Room
  1675. @param room: room where the event occured
  1676. """
  1677. pass
  1678.  
  1679. def onFloodBan(self, room):
  1680. """
  1681. Called when either flood banned or flagged.
  1682.  
  1683. @type room: Room
  1684. @param room: room where the event occured
  1685. """
  1686. pass
  1687.  
  1688. def onFloodBanRepeat(self, room):
  1689. """
  1690. Called when trying to send something when floodbanned.
  1691.  
  1692. @type room: Room
  1693. @param room: room where the event occured
  1694. """
  1695. pass
  1696.  
  1697. def onFloodWarning(self, room):
  1698. """
  1699. Called when an overflow warning gets received.
  1700.  
  1701. @type room: Room
  1702. @param room: room where the event occured
  1703. """
  1704. pass
  1705.  
  1706. def onMessageDelete(self, room, user, message):
  1707. """
  1708. Called when a message gets deleted.
  1709.  
  1710. @type room: Room
  1711. @param room: room where the event occured
  1712. @type user: User
  1713. @param user: owner of deleted message
  1714. @type message: Message
  1715. @param message: message that got deleted
  1716. """
  1717. pass
  1718.  
  1719. def onModChange(self, room):
  1720. """
  1721. Called when the moderator list changes.
  1722.  
  1723. @type room: Room
  1724. @param room: room where the event occured
  1725. """
  1726. pass
  1727.  
  1728. def onModAdd(self, room, user):
  1729. """
  1730. Called when a moderator gets added.
  1731.  
  1732. @type room: Room
  1733. @param room: room where the event occured
  1734. """
  1735. pass
  1736.  
  1737. def onModRemove(self, room, user):
  1738. """
  1739. Called when a moderator gets removed.
  1740.  
  1741. @type room: Room
  1742. @param room: room where the event occured
  1743. """
  1744. pass
  1745.  
  1746. def onMessage(self, room, user, message):
  1747. """
  1748. Called when a message gets received.
  1749.  
  1750. @type room: Room
  1751. @param room: room where the event occured
  1752. @type user: User
  1753. @param user: owner of message
  1754. @type message: Message
  1755. @param message: received message
  1756. """
  1757. pass
  1758.  
  1759. def onHistoryMessage(self, room, user, message):
  1760. """
  1761. Called when a message gets received from history.
  1762.  
  1763. @type room: Room
  1764. @param room: room where the event occured
  1765. @type user: User
  1766. @param user: owner of message
  1767. @type message: Message
  1768. @param message: the message that got added
  1769. """
  1770. pass
  1771.  
  1772. def onJoin(self, room, user):
  1773. """
  1774. Called when a user joins. Anonymous users get ignored here.
  1775.  
  1776. @type room: Room
  1777. @param room: room where the event occured
  1778. @type user: User
  1779. @param user: the user that has joined
  1780. """
  1781. pass
  1782.  
  1783. def onLeave(self, room, user):
  1784. """
  1785. Called when a user leaves. Anonymous users get ignored here.
  1786.  
  1787. @type room: Room
  1788. @param room: room where the event occured
  1789. @type user: User
  1790. @param user: the user that has left
  1791. """
  1792. pass
  1793.  
  1794. def onRaw(self, room, raw):
  1795. """
  1796. Called before any command parsing occurs.
  1797.  
  1798. @type room: Room
  1799. @param room: room where the event occured
  1800. @type raw: str
  1801. @param raw: raw command data
  1802. """
  1803. pass
  1804.  
  1805. def onPing(self, room):
  1806. """
  1807. Called when a ping gets sent.
  1808.  
  1809. @type room: Room
  1810. @param room: room where the event occured
  1811. """
  1812. pass
  1813.  
  1814. def onUserCountChange(self, room):
  1815. """
  1816. Called when the user count changes.
  1817.  
  1818. @type room: Room
  1819. @param room: room where the event occured
  1820. """
  1821. pass
  1822.  
  1823. def onBan(self, room, user, target):
  1824. """
  1825. Called when a user gets banned.
  1826.  
  1827. @type room: Room
  1828. @param room: room where the event occured
  1829. @type user: User
  1830. @param user: user that banned someone
  1831. @type target: User
  1832. @param target: user that got banned
  1833. """
  1834. pass
  1835.  
  1836. def onUnban(self, room, user, target):
  1837. """
  1838. Called when a user gets unbanned.
  1839.  
  1840. @type room: Room
  1841. @param room: room where the event occured
  1842. @type user: User
  1843. @param user: user that unbanned someone
  1844. @type target: User
  1845. @param target: user that got unbanned
  1846. """
  1847. pass
  1848.  
  1849. def onBanlistUpdate(self, room):
  1850. """
  1851. Called when a banlist gets updated.
  1852.  
  1853. @type room: Room
  1854. @param room: room where the event occured
  1855. """
  1856. pass
  1857.  
  1858. def onUnBanlistUpdate(self, room):
  1859. """
  1860. Called when a unbanlist gets updated.
  1861.  
  1862. @type room: Room
  1863. @param room: room where the event occured
  1864. """
  1865. pass
  1866.  
  1867. def onPMConnect(self, pm):
  1868. """
  1869. Called when connected to the pm
  1870.  
  1871. @type pm: PM
  1872. @param pm: the pm
  1873. """
  1874. pass
  1875.  
  1876. def onAnonPMDisconnect(self, pm, user):
  1877. """
  1878. Called when disconnected from the pm
  1879.  
  1880. @type pm: PM
  1881. @param pm: the pm
  1882. """
  1883. pass
  1884.  
  1885. def onPMDisconnect(self, pm):
  1886. """
  1887. Called when disconnected from the pm
  1888.  
  1889. @type pm: PM
  1890. @param pm: the pm
  1891. """
  1892. pass
  1893.  
  1894. def onPMPing(self, pm):
  1895. """
  1896. Called when sending a ping to the pm
  1897.  
  1898. @type pm: PM
  1899. @param pm: the pm
  1900. """
  1901. pass
  1902.  
  1903. def onPMMessage(self, pm, user, body):
  1904. """
  1905. Called when a message is received
  1906.  
  1907. @type pm: PM
  1908. @param pm: the pm
  1909. @type user: User
  1910. @param user: owner of message
  1911. @type message: Message
  1912. @param message: received message
  1913. """
  1914. pass
  1915.  
  1916. def onPMOfflineMessage(self, pm, user, body):
  1917. """
  1918. Called when connected if a message is received while offline
  1919.  
  1920. @type pm: PM
  1921. @param pm: the pm
  1922. @type user: User
  1923. @param user: owner of message
  1924. @type message: Message
  1925. @param message: received message
  1926. """
  1927. pass
  1928.  
  1929. def onPMContactlistReceive(self, pm):
  1930. """
  1931. Called when the contact list is received
  1932.  
  1933. @type pm: PM
  1934. @param pm: the pm
  1935. """
  1936. pass
  1937.  
  1938. def onPMBlocklistReceive(self, pm):
  1939. """
  1940. Called when the block list is received
  1941.  
  1942. @type pm: PM
  1943. @param pm: the pm
  1944. """
  1945. pass
  1946.  
  1947. def onPMContactAdd(self, pm, user):
  1948. """
  1949. Called when the contact added message is received
  1950.  
  1951. @type pm: PM
  1952. @param pm: the pm
  1953. @type user: User
  1954. @param user: the user that gotten added
  1955. """
  1956. pass
  1957.  
  1958. def onPMContactRemove(self, pm, user):
  1959. """
  1960. Called when the contact remove message is received
  1961.  
  1962. @type pm: PM
  1963. @param pm: the pm
  1964. @type user: User
  1965. @param user: the user that gotten remove
  1966. """
  1967. pass
  1968.  
  1969. def onPMBlock(self, pm, user):
  1970. """
  1971. Called when successfully block a user
  1972.  
  1973. @type pm: PM
  1974. @param pm: the pm
  1975. @type user: User
  1976. @param user: the user that gotten block
  1977. """
  1978. pass
  1979.  
  1980. def onPMUnblock(self, pm, user):
  1981. """
  1982. Called when successfully unblock a user
  1983.  
  1984. @type pm: PM
  1985. @param pm: the pm
  1986. @type user: User
  1987. @param user: the user that gotten unblock
  1988. """
  1989. pass
  1990.  
  1991. def onPMContactOnline(self, pm, user):
  1992. """
  1993. Called when a user from the contact come online
  1994.  
  1995. @type pm: PM
  1996. @param pm: the pm
  1997. @type user: User
  1998. @param user: the user that came online
  1999. """
  2000. pass
  2001.  
  2002. def onPMContactOffline(self, pm, user):
  2003. """
  2004. Called when a user from the contact go offline
  2005.  
  2006. @type pm: PM
  2007. @param pm: the pm
  2008. @type user: User
  2009. @param user: the user that went offline
  2010. """
  2011. pass
  2012.  
  2013. def onEventCalled(self, room, evt, *args, **kw):
  2014. """
  2015. Called on every room-based event.
  2016.  
  2017. @type room: Room
  2018. @param room: room where the event occured
  2019. @type evt: str
  2020. @param evt: the event
  2021. """
  2022. pass
  2023.  
  2024. ####
  2025. # Deferring
  2026. ####
  2027. def deferToThread(self, callback, func, *args, **kw):
  2028. """
  2029. Defer a function to a thread and callback the return value.
  2030.  
  2031. @type callback: function
  2032. @param callback: function to call on completion
  2033. @type cbargs: tuple or list
  2034. @param cbargs: arguments to get supplied to the callback
  2035. @type func: function
  2036. @param func: function to call
  2037. """
  2038. def f(func, callback, *args, **kw):
  2039. ret = func(*args, **kw)
  2040. self.setTimeout(0, callback, ret)
  2041. threading._start_new_thread(f, (func, callback) + args, kw)
  2042.  
  2043. ####
  2044. # Scheduling
  2045. ####
  2046. class _Task:
  2047. def cancel(self):
  2048. """Sugar for removeTask."""
  2049. self.mgr.removeTask(self)
  2050.  
  2051. def _tick(self):
  2052. now = time.time()
  2053. for task in set(self._tasks):
  2054. if task.target <= now:
  2055. task.func(*task.args, **task.kw)
  2056. if task.isInterval:
  2057. task.target = now + task.timeout
  2058. else:
  2059. self._tasks.remove(task)
  2060.  
  2061. def setTimeout(self, timeout, func, *args, **kw):
  2062. """
  2063. Call a function after at least timeout seconds with specified arguments.
  2064.  
  2065. @type timeout: int
  2066. @param timeout: timeout
  2067. @type func: function
  2068. @param func: function to call
  2069.  
  2070. @rtype: _Task
  2071. @return: object representing the task
  2072. """
  2073. task = self._Task()
  2074. task.mgr = self
  2075. task.target = time.time() + timeout
  2076. task.timeout = timeout
  2077. task.func = func
  2078. task.isInterval = False
  2079. task.args = args
  2080. task.kw = kw
  2081. self._tasks.add(task)
  2082. return task
  2083.  
  2084. def setInterval(self, timeout, func, *args, **kw):
  2085. """
  2086. Call a function at least every timeout seconds with specified arguments.
  2087.  
  2088. @type timeout: int
  2089. @param timeout: timeout
  2090. @type func: function
  2091. @param func: function to call
  2092.  
  2093. @rtype: _Task
  2094. @return: object representing the task
  2095. """
  2096. task = self._Task()
  2097. task.mgr = self
  2098. task.target = time.time() + timeout
  2099. task.timeout = timeout
  2100. task.func = func
  2101. task.isInterval = True
  2102. task.args = args
  2103. task.kw = kw
  2104. self._tasks.add(task)
  2105. return task
  2106.  
  2107. def removeTask(self, task):
  2108. """
  2109. Cancel a task.
  2110.  
  2111. @type task: _Task
  2112. @param task: task to cancel
  2113. """
  2114. self._tasks.remove(task)
  2115.  
  2116. ####
  2117. # Util
  2118. ####
  2119. def _write(self, room, data):
  2120. room._wbuf += data
  2121.  
  2122. def getConnections(self):
  2123. li = list(self._rooms.values())
  2124. if self._pm:
  2125. li.extend(self._pm.getConnections())
  2126. return [c for c in li if c._sock != None]
  2127.  
  2128. ####
  2129. # Main
  2130. ####
  2131. def main(self):
  2132. self.onInit()
  2133. self._running = True
  2134. for l in range(0,Number_of_Threads):
  2135. t = threading.Thread(target=self._joinThread)
  2136. t.daemon = True
  2137. t.start()
  2138. while self._running:
  2139. conns = self.getConnections()
  2140. socks = [x._sock for x in conns]
  2141. wsocks = [x._sock for x in conns if x._wbuf != b""]
  2142. rd, wr, sp = select.select(socks, wsocks, [], self._TimerResolution)
  2143. for sock in rd:
  2144. con = [c for c in conns if c._sock == sock][0]
  2145. try:
  2146. data = sock.recv(1024)
  2147. if(len(data) > 0):
  2148. con._feed(data)
  2149. else:
  2150. con.disconnect()
  2151. except socket.error:
  2152. pass
  2153. for sock in wr:
  2154. con = [c for c in conns if c._sock == sock][0]
  2155. try:
  2156. size = sock.send(con._wbuf)
  2157. con._wbuf = con._wbuf[size:]
  2158. except socket.error:
  2159. pass
  2160. self._tick()
  2161.  
  2162. @classmethod
  2163. def easy_start(cl, rooms = None, name = None, password = None, pm = True):
  2164. """
  2165. Prompts the user for missing info, then starts.
  2166.  
  2167. @type rooms: list
  2168. @param room: rooms to join
  2169. @type name: str
  2170. @param name: name to join as ("" = None, None = unspecified)
  2171. @type password: str
  2172. @param password: password to join with ("" = None, None = unspecified)
  2173. """
  2174. if not rooms: rooms = str(input("Room names separated by semicolons: ")).split(";")
  2175. if len(rooms) == 1 and rooms[0] == "": rooms = []
  2176. if not name: name = str(input("User name: "))
  2177. if name == "": name = None
  2178. if not password: password = str(input("User password: "))
  2179. if password == "": password = None
  2180. self = cl(name, password, pm = pm)
  2181. for room in rooms:
  2182. self.joinRoom(room)
  2183. self.main()
  2184.  
  2185. def stop(self):
  2186. for conn in list(self._rooms.values()):
  2187. conn.disconnect()
  2188. self._running = False
  2189.  
  2190. ####
  2191. # Commands
  2192. ####
  2193. def enableBg(self):
  2194. """Enable background if available."""
  2195. self.user._mbg = True
  2196. for room in self.rooms:
  2197. room.setBgMode(1)
  2198.  
  2199. def disableBg(self):
  2200. """Disable background."""
  2201. self.user._mbg = False
  2202. for room in self.rooms:
  2203. room.setBgMode(0)
  2204.  
  2205. def enableRecording(self):
  2206. """Enable recording if available."""
  2207. self.user._mrec = True
  2208. for room in self.rooms:
  2209. room.setRecordingMode(1)
  2210.  
  2211. def disableRecording(self):
  2212. """Disable recording."""
  2213. self.user._mrec = False
  2214. for room in self.rooms:
  2215. room.setRecordingMode(0)
  2216.  
  2217. def setNameColor(self, color3x):
  2218. """
  2219. Set name color.
  2220.  
  2221. @type color3x: str
  2222. @param color3x: a 3-char RGB hex code for the color
  2223. """
  2224. self.user._nameColor = color3x
  2225.  
  2226. def setFontColor(self, color3x):
  2227. """
  2228. Set font color.
  2229.  
  2230. @type color3x: str
  2231. @param color3x: a 3-char RGB hex code for the color
  2232. """
  2233. self.user._fontColor = color3x
  2234.  
  2235. def setFontFace(self, face):
  2236. """
  2237. Set font face/family.
  2238.  
  2239. @type face: str
  2240. @param face: the font face
  2241. """
  2242. self.user._fontFace = face
  2243.  
  2244. def setFontSize(self, size):
  2245. """
  2246. Set font size.
  2247.  
  2248. @type size: int
  2249. @param size: the font size (limited: 9 to 22)
  2250. """
  2251. if size < 9: size = 9
  2252. if size > 22: size = 22
  2253. self.user._fontSize = size
  2254.  
  2255. ################################################################
  2256. # User class (well, yeah, I lied, it's actually _User)
  2257. ################################################################
  2258. _users = dict()
  2259. def User(name, *args, **kw):
  2260. if name == None: name = ""
  2261. name = name.lower()
  2262. user = _users.get(name)
  2263. if not user:
  2264. user = _User(name = name, *args, **kw)
  2265. _users[name] = user
  2266. return user
  2267.  
  2268. class _User:
  2269. """Class that represents a user."""
  2270. ####
  2271. # Init
  2272. ####
  2273. def __init__(self, name, **kw):
  2274. self._name = name.lower()
  2275. self._puid = ""
  2276. self._sids = dict()
  2277. self._msgs = list()
  2278. self._nameColor = "000"
  2279. self._fontSize = 12
  2280. self._fontFace = "0"
  2281. self._fontColor = "000"
  2282. self._mbg = False
  2283. self._mrec = False
  2284. for attr, val in kw.items():
  2285. if val == None: continue
  2286. setattr(self, "_" + attr, val)
  2287.  
  2288. ####
  2289. # Properties
  2290. ####
  2291. def _getName(self): return self._name
  2292. def _getPuid(self): return self._puid
  2293. def _getSessionIds(self, room = None):
  2294. if room:
  2295. return self._sids.get(room, set())
  2296. else:
  2297. return set.union(*self._sids.values())
  2298. def _getRooms(self): return self._sids.keys()
  2299. def _getRoomNames(self): return [room.name for room in self._getRooms()]
  2300. def _getFontColor(self): return self._fontColor
  2301. def _getFontFace(self): return self._fontFace
  2302. def _getFontSize(self): return self._fontSize
  2303. def _getNameColor(self): return self._nameColor
  2304.  
  2305. name = property(_getName)
  2306. puid = property(_getPuid)
  2307. sessionids = property(_getSessionIds)
  2308. rooms = property(_getRooms)
  2309. roomnames = property(_getRoomNames)
  2310. fontColor = property(_getFontColor)
  2311. fontFace = property(_getFontFace)
  2312. fontSize = property(_getFontSize)
  2313. nameColor = property(_getNameColor)
  2314.  
  2315. ####
  2316. # Util
  2317. ####
  2318. def addSessionId(self, room, sid):
  2319. if room not in self._sids:
  2320. self._sids[room] = set()
  2321. self._sids[room].add(sid)
  2322.  
  2323. def removeSessionId(self, room, sid):
  2324. try:
  2325. self._sids[room].remove(sid)
  2326. if len(self._sids[room]) == 0:
  2327. del self._sids[room]
  2328. except KeyError:
  2329. pass
  2330.  
  2331. def clearSessionIds(self, room):
  2332. try:
  2333. del self._sids[room]
  2334. except KeyError:
  2335. pass
  2336.  
  2337. def hasSessionId(self, room, sid):
  2338. try:
  2339. if sid in self._sids[room]:
  2340. return True
  2341. else:
  2342. return False
  2343. except KeyError:
  2344. return False
  2345.  
  2346. def updatePuid(self, puid):
  2347. self._puid = puid
  2348.  
  2349. ####
  2350. # Repr
  2351. ####
  2352. def __repr__(self):
  2353. return "<User: %s>" %(self.name)
  2354.  
  2355. ################################################################
  2356. # Message class
  2357. ################################################################
  2358. class Message:
  2359. """Class that represents a message."""
  2360. ####
  2361. # Attach/detach
  2362. ####
  2363. def attach(self, room, msgid):
  2364. """
  2365. Attach the Message to a message id.
  2366.  
  2367. @type msgid: str
  2368. @param msgid: message id
  2369. """
  2370. if self._msgid == None:
  2371. self._room = room
  2372. self._msgid = msgid
  2373. self._room._msgs[msgid] = self
  2374.  
  2375. def detach(self):
  2376. """Detach the Message."""
  2377. if self._msgid != None and self._msgid in self._room._msgs:
  2378. del self._room._msgs[self._msgid]
  2379. self._msgid = None
  2380.  
  2381. def delete(self):
  2382. self._room.deleteMessage(self)
  2383.  
  2384. ####
  2385. # Init
  2386. ####
  2387. def __init__(self, **kw):
  2388. """init, don't overwrite"""
  2389. self._msgid = None
  2390. self._time = None
  2391. self._user = None
  2392. self._body = None
  2393. self._room = None
  2394. self._raw = ""
  2395. self._ip = None
  2396. self._unid = ""
  2397. self._nameColor = "000"
  2398. self._fontSize = 12
  2399. self._fontFace = "0"
  2400. self._fontColor = "000"
  2401. for attr, val in kw.items():
  2402. if val == None: continue
  2403. setattr(self, "_" + attr, val)
  2404.  
  2405. ####
  2406. # Properties
  2407. ####
  2408. def _getId(self): return self._msgid
  2409. def _getTime(self): return self._time
  2410. def _getUser(self): return self._user
  2411. def _getBody(self): return self._body
  2412. def _getIP(self): return self._ip
  2413. def _getFontColor(self): return self._fontColor
  2414. def _getFontFace(self): return self._fontFace
  2415. def _getFontSize(self): return self._fontSize
  2416. def _getNameColor(self): return self._nameColor
  2417. def _getRoom(self): return self._room
  2418. def _getRaw(self): return self._raw
  2419. def _getUnid(self): return self._unid
  2420.  
  2421. msgid = property(_getId)
  2422. time = property(_getTime)
  2423. user = property(_getUser)
  2424. body = property(_getBody)
  2425. room = property(_getRoom)
  2426. ip = property(_getIP)
  2427. fontColor = property(_getFontColor)
  2428. fontFace = property(_getFontFace)
  2429. fontSize = property(_getFontSize)
  2430. raw = property(_getRaw)
  2431. nameColor = property(_getNameColor)
  2432. unid = property(_getUnid)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement