Advertisement
Guest User

Untitled

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