Guest User

Untitled

a guest
Jul 19th, 2018
92
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.12 KB | None | 0 0
  1. # -*- coding: utf-8 -*-
  2.  
  3. #TODO: implement commands: oper, mode, topic, names, list, invite
  4. #TODO: '' '' : kick, who, whois, whowas, kill, away
  5. #TODO: '' '' : rehash, restart, wallops,
  6.  
  7. import logging
  8. from asynchat import async_chat
  9. from datetime import datetime
  10. from re import compile as re_compile
  11. from socket import AF_INET, SOCK_STREAM
  12.  
  13. LOGFORMAT = '[%(name)s] %(levelname)s - %(message)s'
  14. CTCP_RE = re_compile(r'^\x01(?P<type>[A-Z]+)(?: (?P<data>.+))?\x01$')
  15.  
  16.  
  17. class IRCClient(async_chat):
  18. """
  19. Asynchronous IRC client.
  20. """
  21. terminator = '\r\n'
  22. ctcp_version = None
  23.  
  24. def __init__(self, host, port, nickname, username,
  25. channels=None,
  26. loglevel=logging.INFO,
  27. logformat=LOGFORMAT):
  28. async_chat.__init__(self)
  29. logging.basicConfig(format=logformat)
  30. self.host = host
  31. self.port = port
  32. self.nickname = nickname
  33. self.username = username
  34. self.channels = [] if (channels is None) else channels
  35. self.received_data = ''
  36. self.logger = logging.getLogger('%(host)s:%(port)d' % {'host': host, 'port': port})
  37. self.logger.setLevel(loglevel)
  38. self.logger.info('connecting to host...')
  39. self.create_socket(AF_INET, SOCK_STREAM)
  40. self.connect((host, port))
  41.  
  42. def _connection_made(self):
  43. self.logger.info('connection successful')
  44. for channel in self.channels:
  45. self.send_data('JOIN %(chan)s' % {'chan': channel})
  46. self.on_connection()
  47.  
  48. def split_netmask(self, netmask):
  49. nick = netmask.split('!')[0].lstrip(':')
  50. user = netmask.split('@')[0].split('!')[1]
  51. host = netmask.split('@')[1]
  52. return (nick, user, host)
  53.  
  54. def send_data(self, data):
  55. self.logger.debug('SENT DATA: %(data)s' % {'data': repr(data)})
  56. data += '\r\n'
  57. self.push(data)
  58.  
  59. def handle_connect(self):
  60. self.send_data('NICK :%(nick)s' % {'nick': self.nickname})
  61. self.send_data('USER %(nick)s %(nick)s %(nick)s :%(user)s' % {
  62. 'nick': self.nickname,
  63. 'user': self.username,
  64. })
  65.  
  66. def handle_data(self, data):
  67. self.logger.debug(str(data))
  68. token = data.split()
  69. src = token[0]
  70.  
  71. if 'PRIVMSG' in token:
  72. dst = token[2]
  73. message = ' '.join(token[3:]).lstrip(':')
  74. ctcp = CTCP_RE.match(message)
  75. if ctcp is not None:
  76. ctcp = ctcp.groupdict()
  77. self.on_ctcp(src, dst, ctcp)
  78. return
  79. self.on_privmsg(src, dst, message)
  80.  
  81. elif 'NOTICE' in token:
  82. dst = token[2]
  83. message = ' '.join(token[3:]).lstrip(':')
  84. self.on_notice(src, dst, message)
  85.  
  86. elif 'JOIN' in token:
  87. dst = token[2]
  88. self.on_join(src, dst)
  89.  
  90. elif 'PART' in token:
  91. dst = token[2]
  92. message = ' '.join(token[3:]).lstrip(':') if (len(token) > 2) else None
  93. self.on_part(src, dst, message)
  94.  
  95. elif 'PING' in token:
  96. self.on_ping(token[1])
  97.  
  98. elif 376 in token:
  99. self._connection_made()
  100.  
  101. else:
  102. self.on_unknown_data(data)
  103.  
  104. def found_terminator(self):
  105. self.handle_data(self.received_data)
  106. self.received_data = ''
  107.  
  108. def collect_incoming_data(self, data):
  109. self.received_data += data
  110.  
  111. ##########################
  112. ## IRC COMMANDS METHODS ##
  113. ##########################
  114.  
  115. def set_nick(self, new_nick):
  116. self.nickname = new_nick
  117. self.send_data('NICK %(nick)s' % {'nick': new_nick})
  118.  
  119. def privmsg(self, dst, msg, color=None):
  120. self.send_data('PRIVMSG %(dst)s :%(msg)s' % {
  121. 'dst': dst,
  122. 'msg': msg,
  123. })
  124.  
  125. def notice(self, dst, msg):
  126. self.send_data('NOTICE %(dst)s :%(msg)s' % {
  127. 'dst': dst,
  128. 'msg': msg,
  129. })
  130.  
  131. def join(self, *dst):
  132. for channel in dst:
  133. self.send_data('JOIN %(dst)s' % {'dst': channel})
  134.  
  135. def part(self, dst, msg=None):
  136. data = 'PART %(dst)s' % {'dst': dst}
  137. if msg is not None:
  138. data += ' :%(msg)s' % {'msg': msg}
  139. self.send_data(data)
  140.  
  141. def quit(self, msg=None):
  142. data = 'QUIT'
  143. if msg is not None:
  144. data += ' :%(msg)s' % {'msg': msg}
  145. self.send_data(data)
  146.  
  147. def on_ping(self, ping_id):
  148. self.send_data('PONG %(id)s' % {'id': ping_id})
  149.  
  150. def on_ctcp(self, src, dst, ctcp):
  151. if (ctcp['type'] == 'PING') and (ctcp['data'] is not None):
  152. self.on_ctcp_ping(src, ctcp)
  153. elif ctcp['type'] == 'TIME':
  154. self.on_ctcp_time(src)
  155. elif ctcp['type'] == 'VERSION':
  156. self.on_ctcp_version(src)
  157. elif ctcp['type'] == 'FINGER':
  158. self.on_ctcp_finger(src)
  159. elif ctcp['type'] == 'ACTION':
  160. self.on_action(src, dst, ctcp['data'])
  161.  
  162. #####################################################
  163. ## EVENTS HANDLERS TO (RE-)IMPLEMENT IN A SUBCLASS ##
  164. #####################################################
  165.  
  166. def on_unknown_data(self, data):
  167. pass
  168.  
  169. def on_connection(self):
  170. pass
  171.  
  172. def on_ctcp_ping(self, src, ctcp):
  173. nick, _, _ = self.split_netmask(src)
  174. self.notice(nick, '\x01PING %(timestamp)s\x01' % {'timestamp': ctcp['data']})
  175.  
  176. def on_ctcp_time(self, src):
  177. nick, _, _ = self.split_netmask(src)
  178. message = '%s UTC' % datetime.utcnow()
  179. self.notice(nick, '\x01TIME :%(time)s' % {'time': message})
  180.  
  181. def on_ctcp_version(self, src):
  182. pass
  183.  
  184. def on_ctcp_finger(self, src):
  185. pass
  186.  
  187. def on_action(self, src, dst, msg):
  188. pass
  189.  
  190. def on_privmsg(self, src, dst, msg):
  191. pass
  192.  
  193. def on_notice(self, src, dst, msg):
  194. pass
  195.  
  196. def on_join(self, src, dst):
  197. pass
  198.  
  199. def on_part(self, src, dst, msg):
  200. pass
  201.  
  202. def on_quit(self, src, msg):
  203. pass
  204.  
  205. if __name__ == '__main__':
  206. from asyncore import loop
  207. host, port = 'irc.plain-text.info', 6667
  208. nick = username = 'fatbotslim'
  209. channels = ['#testbot']
  210. loglevel = logging.DEBUG
  211. client = IRCClient(host, port, nick, username, channels, loglevel)
  212. loop()
Add Comment
Please, Sign In to add comment