PyTS3.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. #!/usr/bin/python2.5
  2. # -*- coding: utf-8 -*-
  3. # Copyright (c) 2009 Christoph Heer (Christoph.Heer@googlemail.com)
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a
  6. # copy of this software and associated documentation files (the \"Software\"),
  7. # to deal in the Software without restriction, including without limitation
  8. # the rights to use, copy, modify, merge, publish, distribute, sublicense,
  9. # and/or sell copies of the Software, and to permit persons to whom the
  10. # Software is furnished to do so, subject to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included in
  13. # all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  18. # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20. # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  21. # DEALINGS IN THE SOFTWARE.
  22. import telnetlib
  23. import re
  24. import thread
  25. import time
  26. class TS3Error(Exception):
  27. def __init__(self, code, msg):
  28. self.code = code
  29. self.msg = msg
  30. def __str__(self):
  31. return "ID %s (%s)" % (self.code, self.msg)
  32. class ServerQuery():
  33. TSRegex = re.compile(r"(\w+)=(.*?)(\s|$|\|)")
  34. def __init__(self, ip='127.0.0.1', query=10011):
  35. """
  36. This class contains functions to connecting a TS3 Query Port and send
  37. command.
  38. @param ip: IP adress of the TS3 Server
  39. @type ip: str
  40. @param query: Query Port of the TS3 Server. Default 10011
  41. @type query: int
  42. """
  43. self.IP = ip
  44. self.Query = int(query)
  45. self.Timeout = 5.0
  46. def connect(self):
  47. """
  48. Open a link to the Teamspeak 3 query port
  49. @return: A tulpe with a error code. Example: ('error', 0, 'ok')
  50. """
  51. try:
  52. self.telnet = telnetlib.Telnet(self.IP, self.Query)
  53. except telnetlib.socket.error:
  54. raise TS3Error(10, 'Can not open a link on the port or IP')
  55. output = self.telnet.read_until('TS3', self.Timeout)
  56. if output.endswith('TS3') == False:
  57. raise TS3Error(20, 'This is not a Teamspeak 3 Server')
  58. else:
  59. return True
  60. def disconnect(self):
  61. """
  62. Close the link to the Teamspeak 3 query port
  63. @return: ('error', 0, 'ok')
  64. """
  65. self.telnet.write('quit \n')
  66. self.telnet.close()
  67. return True
  68. def escaping2string(self, string):
  69. """
  70. Convert the escaping string form the TS3 Query to a human string.
  71. @param string: A string form the TS3 Query with ecaping.
  72. @type string: str
  73. @return: A human string with out escaping.
  74. """
  75. string = str(string)
  76. string = string.replace('\/', '/')
  77. string = string.replace('\s', ' ')
  78. string = string.replace('\p', '|')
  79. string = string.replace('\n', '')
  80. string = string.replace('\r', '')
  81. try:
  82. string = int(string)
  83. return string
  84. except ValueError:
  85. ustring = unicode(string, "utf-8")
  86. return ustring
  87. def string2escaping(self, string):
  88. """
  89. Convert a human string to a TS3 Query Escaping String.
  90. @param string: A normal/human string.
  91. @type string: str
  92. @return: A string with escaping of TS3 Query.
  93. """
  94. if type(string) == type(int()):
  95. string = str(string)
  96. else:
  97. string = string.encode("utf-8")
  98. string = string.replace('/', '\\/')
  99. string = string.replace(' ', '\\s')
  100. string = string.replace('|', '\\p')
  101. return string
  102. def command(self, cmd, parameter={}, option=[]):
  103. """
  104. Send a command with paramters and options to the TS3 Query.
  105. @param cmd: The command who wants to send.
  106. @type cmd: str
  107. @param parameter: A dict with paramters and value.
  108. Example: sid=2 --> {'sid':'2'}
  109. @type cmd: dict
  110. @param option: A list with options. Example: –uid --> ['uid']
  111. @type option: list
  112. @return: The answer of the server as tulpe with error code and message.
  113. """
  114. telnetCMD = cmd
  115. for key in parameter:
  116. telnetCMD += " %s=%s" % (key, self.string2escaping(parameter[key]))
  117. for i in option:
  118. telnetCMD += " -%s" % (i)
  119. telnetCMD += '\n'
  120. self.telnet.write(telnetCMD)
  121. telnetResponse = self.telnet.read_until("msg=ok", self.Timeout)
  122. telnetResponse = telnetResponse.split(r'error id=')
  123. notParsedCMDStatus = "id=" + telnetResponse[1]
  124. notParsedInfo = telnetResponse[0].split('|')
  125. if (cmd.endswith("list") == True) or (len(notParsedInfo) > 1):
  126. returnInfo = []
  127. for notParsedInfoLine in notParsedInfo:
  128. ParsedInfo = self.TSRegex.findall(notParsedInfoLine)
  129. ParsedInfoDict = {}
  130. for ParsedInfoKey in ParsedInfo:
  131. ParsedInfoDict[ParsedInfoKey[0]] = self.escaping2string(
  132. ParsedInfoKey[1])
  133. returnInfo.append(ParsedInfoDict)
  134. else:
  135. returnInfo = {}
  136. ParsedInfo = self.TSRegex.findall(notParsedInfo[0])
  137. for ParsedInfoKey in ParsedInfo:
  138. returnInfo[ParsedInfoKey[0]] = self.escaping2string(
  139. ParsedInfoKey[1])
  140. ReturnCMDStatus = {}
  141. ParsedCMDStatus = self.TSRegex.findall(notParsedCMDStatus)
  142. for ParsedCMDStatusLine in ParsedCMDStatus:
  143. ReturnCMDStatus[ParsedCMDStatusLine[0]] = self.escaping2string(
  144. ParsedCMDStatusLine[1])
  145. if ReturnCMDStatus['id'] != 0:
  146. raise TS3Error(ReturnCMDStatus['id'], ReturnCMDStatus['msg'])
  147. return returnInfo
  148. class ServerNotification(ServerQuery):
  149. def __init__(self, ip='127.0.0.1', query=10011):
  150. """
  151. This class contains functions to work with the
  152. ServerNotification of TS3.
  153. @param ip: IP adress of the TS3 Server
  154. @type ip: str
  155. @param query: Query Port of the TS3 Server. Default 10011
  156. @type query: int
  157. """
  158. self.IP = ip
  159. self.Query = int(query)
  160. self.Timeout = 5.0
  161. self.LastCommand = 0
  162. self.Lock = thread.allocate_lock()
  163. self.RegistedNotifys = []
  164. self.RegistedEvents = []
  165. thread.start_new_thread(self.worker, ())
  166. def worker(self):
  167. while True:
  168. self.Lock.acquire()
  169. RegistedNotifys = self.RegistedNotifys
  170. LastCommand = self.LastCommand
  171. self.Lock.release()
  172. if len(RegistedNotifys) == 0:
  173. continue
  174. if LastCommand < time.time() - 180:
  175. self.command('version')
  176. self.Lock.acquire()
  177. self.LastCommand = time.time()
  178. self.Lock.release()
  179. telnetResponse = self.telnet.read_until("\n", 0.1)
  180. if telnetResponse.startswith('notify'):
  181. notifyName = telnetResponse.split(' ')[0]
  182. ParsedInfo = self.TSRegex.findall(telnetResponse)
  183. notifyData = {}
  184. for ParsedInfoKey in ParsedInfo:
  185. notifyData[ParsedInfoKey[0]] = self.escaping2string(
  186. ParsedInfoKey[1])
  187. for RegistedNotify in RegistedNotifys:
  188. if RegistedNotify['notify'] == notifyName:
  189. RegistedNotify['func'](notifyName, notifyData)
  190. time.sleep(0.2)
  191. def registerNotify(self, notify, func):
  192. notify2func = {'notify': notify, 'func': func}
  193. self.Lock.acquire()
  194. self.RegistedNotifys.append(notify2func)
  195. self.LastCommand = time.time()
  196. self.Lock.release()
  197. def unregisterNotify(self, notify, func):
  198. notify2func = {'notify': notify, 'func': func}
  199. self.Lock.acquire()
  200. self.RegistedNotifys.remove(notify2func)
  201. self.LastCommand = time.time()
  202. self.Lock.release()
  203. def registerEvent(self, eventName, parameter={}, option=[]):
  204. parameter['event'] = eventName
  205. self.RegistedEvents.append(eventName)
  206. self.command('servernotifyregister', parameter, option)
  207. self.Lock.acquire()
  208. self.LastCommand = time.time()
  209. self.Lock.release()
  210. def unregisterEvent(self):
  211. self.command('servernotifyunregister')