Advertisement
Guest User

Untitled

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