Guest User

Untitled

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