A Python robot that edits Wikipedia and interacts with people over IRC https://en.wikipedia.org/wiki/User:EarwigBot
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

92 lines
2.9 KiB

  1. # -*- coding: utf-8 -*-
  2. """
  3. EarwigBot's IRC Watcher Component
  4. The IRC watcher runs on a wiki recent-changes server and listens for edits.
  5. Users cannot interact with this part of the bot. When an event occurs, we run
  6. it through rules.py's process() function, which can result in wiki bot tasks
  7. being started (located in tasks/) or messages being sent to channels on the IRC
  8. frontend.
  9. """
  10. import config
  11. from classes import Connection, RC, BrokenSocketException
  12. import rules
  13. frontend_conn = None
  14. def get_connection():
  15. """Return a new Connection() instance with connection information.
  16. Don't actually connect yet.
  17. """
  18. cf = config.irc["watcher"]
  19. connection = Connection(cf["host"], cf["port"], cf["nick"], cf["ident"],
  20. cf["realname"])
  21. return connection
  22. def main(connection, f_conn=None):
  23. """Main loop for the Watcher IRC Bot component.
  24. get_connection() should have already been called and the connection should
  25. have been started with connection.connect(). Accept the frontend connection
  26. as well as an optional parameter in order to send messages directly to
  27. frontend IRC channels.
  28. """
  29. global frontend_conn
  30. frontend_conn = f_conn
  31. read_buffer = str()
  32. while 1:
  33. try:
  34. read_buffer = read_buffer + connection.get()
  35. except BrokenSocketException:
  36. return
  37. lines = read_buffer.split("\n")
  38. read_buffer = lines.pop()
  39. for line in lines:
  40. _process_message(connection, line)
  41. def _process_message(connection, line):
  42. """Process a single message from IRC."""
  43. line = line.strip().split()
  44. if line[1] == "PRIVMSG":
  45. chan = line[2]
  46. # Ignore messages originating from channels not in our list, to prevent
  47. # someone PMing us false data:
  48. if chan not in config.irc["watcher"]["channels"]:
  49. return
  50. msg = ' '.join(line[3:])[1:]
  51. rc = RC(msg) # new RC object to store this event's data
  52. rc.parse() # parse a message into pagenames, usernames, etc.
  53. process_rc(rc) # report to frontend channels or start tasks
  54. # If we are pinged, pong back to the server:
  55. elif line[0] == "PING":
  56. msg = " ".join(("PONG", line[1]))
  57. connection.send(msg)
  58. # When we've finished starting up, join all watcher channels:
  59. elif line[1] == "376":
  60. for chan in config.irc["watcher"]["channels"]:
  61. connection.join(chan)
  62. def process_rc(rc):
  63. """Process a recent change event from IRC (or, an RC object).
  64. The actual processing is configurable, so we don't have that hard-coded
  65. here. We simply call rules's process() function and expect a list of
  66. channels back, which we report the event data to.
  67. """
  68. chans = rules.process(rc)
  69. if chans and frontend_conn:
  70. pretty = rc.prettify()
  71. for chan in chans:
  72. frontend_conn.say(chan, pretty)