titlebot.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import socket
  2. socket.setdefaulttimeout(4)
  3. from queue import Queue
  4. import re
  5. import web
  6. from urllib.error import HTTPError
  7. from hack import async, restart_program, Signal
  8. import libirc
  9. from time import sleep
  10. from config import (HOST, PORT, NICK, IDENT,
  11. REALNAME, CHANNELS, ADMINS,
  12. IGNORED_URLS)
  13. class IRCHandler(object):
  14. message_recived = Signal()
  15. error_raised = Signal()
  16. def __init__(self, irc_connection):
  17. self.__connection = irc_connection
  18. self.__running = True
  19. self.__message_pool = Queue()
  20. def __mainloop(self):
  21. while self.__running:
  22. text = self.__connection.recvline(block=True)
  23. message = irc.parse(line=text)
  24. if message and message['msg'] and message['cmd'] == "PRIVMSG":
  25. self.__last_message = message
  26. self.message_recived.emit(message)
  27. def mainloop(self):
  28. while self.__running:
  29. try:
  30. self.__say()
  31. self.__mainloop()
  32. except socket.error as e:
  33. self.quit("Network error")
  34. restart_program()
  35. except Exception as e:
  36. self.complain(self.__last_message['dest'], e)
  37. def say(self, nick, text):
  38. self.__message_pool.put([nick, text])
  39. def _say(self, nick, text):
  40. self.__connection.say(nick, text)
  41. @async
  42. def __say(self):
  43. while self.__running:
  44. sleep(0.5)
  45. nick, text = self.__message_pool.get(block=True)
  46. self._say(nick, text)
  47. def complain(self, nick, text):
  48. nick = str(nick)
  49. text = str(text)
  50. try:
  51. self.say(nick, "哎呀,%s 好像出了点问题: " % (NICK) + text)
  52. except Exception:
  53. pass
  54. def complain_network(self, nick, text):
  55. nick = str(nick)
  56. text = str(text)
  57. try:
  58. self.say(nick, "哎呀,网络好像出了点问题: " + text)
  59. except Exception:
  60. pass
  61. def quit(self, reason="Exit"):
  62. self.__running = False
  63. self.__connection.quit(reason)
  64. class MessageHandler(object):
  65. def __init__(self, irc_handler):
  66. self.__handler = irc_handler
  67. self.__handler.message_recived.connect(self.message_handler)
  68. @async
  69. def message_handler(self, msg):
  70. if msg['dest'] == NICK:
  71. self.react_command(msg)
  72. else:
  73. self.react_message(msg)
  74. def react_command(self, msg):
  75. if msg['nick'] in ADMINS or (not ADMINS):
  76. if msg['msg'] == "Get out of this channel!":
  77. self.__handler.quit("%s asked to leave" % msg['nick'])
  78. elif msg['msg'] == "Restart!":
  79. self.__handler.quit("%s asked to restart" % msg['nick'])
  80. restart_program()
  81. else:
  82. irc.say(msg['nick'], "Unknown Command, 233333...")
  83. else:
  84. irc.say(msg['nick'], "Permission Denied")
  85. def react_message(self, msg):
  86. for word in msg['msg'].split():
  87. self.say_title(msg['dest'], word)
  88. @async
  89. def say_title(self, channel, text):
  90. url = web.pickup_url(text)
  91. if not url:
  92. return
  93. for badurl in IGNORED_URLS:
  94. if re.match(badurl, url):
  95. return
  96. errors = 0
  97. while True:
  98. try:
  99. web_info = web.web_res_info(url)
  100. if web_info['type'] == "text/html":
  101. self.say_webpage_title(channel, web_info)
  102. else:
  103. self.say_resource_info(channel, web_info)
  104. break
  105. except (RuntimeError, HTTPError) as e:
  106. if errors < 3:
  107. errors += 1
  108. continue
  109. else:
  110. self.__handler.complain_network(channel, e)
  111. break
  112. except Exception as e:
  113. self.__handler.complain(channel, e)
  114. break
  115. def say_webpage_title(self, channel, web_info):
  116. if web_info['title']:
  117. self.__handler.say(channel, "⇪标题: %s" % web_info['title'])
  118. elif web_info['title'] is None:
  119. self.__handler.say(channel, "⇪无标题网页")
  120. elif not (web_info['title'].strip()):
  121. self.__handler.say(channel, "⇪标题: (空)")
  122. def say_resource_info(self, channel, web_info):
  123. if web_info['title']:
  124. assert web_info['size']
  125. assert web_info['type']
  126. self.__handler.say(channel, "⇪标题: %s, 文件类型: %s, 文件大小: %s 字节\r\n" % (web_info["title"], web_info['type'], web_info['size']))
  127. elif web_info['size']:
  128. assert web_info['type']
  129. self.__handler.say(channel, "⇪文件类型: %s, 文件大小: %s 字节\r\n" % (web_info['type'], web_info['size']))
  130. elif web_info['type']:
  131. self.__handler.say(channel, "⇪文件类型: %s\r\n" % (web_info['type']))
  132. irc = libirc.IRCConnection()
  133. irc.connect((HOST, PORT), use_ssl=True)
  134. irc.setnick(NICK)
  135. irc.setuser(IDENT, REALNAME)
  136. for channel in CHANNELS:
  137. irc.join(channel)
  138. irc_handler = IRCHandler(irc)
  139. message_handler = MessageHandler(irc_handler)
  140. irc_handler.mainloop()