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.

114 line
3.8 KiB

  1. # -*- coding: utf-8 -*-
  2. """
  3. EarwigBot's IRC Frontend Component
  4. The IRC frontend runs on a normal IRC server and expects users to interact with
  5. it and give it commands. Commands are stored as "command classes", subclasses
  6. of BaseCommand in irc/base_command.py. All command classes are automatically
  7. imported by irc/command_handler.py if they are in irc/commands.
  8. """
  9. import re
  10. import config
  11. import commands
  12. from classes import Connection, Data, BrokenSocketException
  13. __all__ = ["get_connection", "startup", "main"]
  14. connection = None
  15. sender_regex = re.compile(":(.*?)!(.*?)@(.*?)\Z")
  16. def get_connection():
  17. """Return a new Connection() instance with information about our server
  18. connection, but don't actually connect yet."""
  19. cf = config.irc["frontend"]
  20. connection = Connection(cf["host"], cf["port"], cf["nick"], cf["ident"],
  21. cf["realname"])
  22. return connection
  23. def startup(conn):
  24. """Accept a single arg, a Connection() object, and set our global variable
  25. 'connection' to it. Load all command classes in irc/commands with
  26. command_handler, and then establish a connection with the IRC server."""
  27. global connection
  28. connection = conn
  29. commands.load(connection)
  30. connection.connect()
  31. def main():
  32. """Main loop for the frontend component.
  33. get_connection() and startup() should have already been called before this.
  34. """
  35. read_buffer = str()
  36. while 1:
  37. try:
  38. read_buffer = read_buffer + connection.get()
  39. except BrokenSocketException:
  40. print "Socket has broken on front-end; restarting bot..."
  41. return
  42. lines = read_buffer.split("\n")
  43. read_buffer = lines.pop()
  44. for line in lines:
  45. _process_message(line)
  46. def _process_message(line):
  47. """Process a single message from IRC."""
  48. line = line.strip().split()
  49. data = Data(line) # new Data instance to store info about this line
  50. if line[1] == "JOIN":
  51. data.nick, data.ident, data.host = sender_regex.findall(line[0])[0]
  52. data.chan = line[2][1:]
  53. # Check for 'join' hooks in our commands:
  54. commands.check("join", data)
  55. elif line[1] == "PRIVMSG":
  56. data.nick, data.ident, data.host = sender_regex.findall(line[0])[0]
  57. data.msg = ' '.join(line[3:])[1:]
  58. data.chan = line[2]
  59. if data.chan == config.irc["frontend"]["nick"]:
  60. # This is a privmsg to us, so set 'chan' as the nick of the, sender
  61. # then check for private-only command hooks:
  62. data.chan = data.nick
  63. commands.check("msg_private", data)
  64. else:
  65. # Check for public-only command hooks:
  66. commands.check("msg_public", data)
  67. # Check for command hooks that apply to all messages:
  68. commands.check("msg", data)
  69. # Hardcode the !restart command (we can't restart from within an
  70. # ordinary command):
  71. if data.msg in ["!restart", ".restart"]:
  72. if data.host in config.irc["permissions"]["owners"]:
  73. print "Restarting bot per owner request..."
  74. return
  75. # If we are pinged, pong back to the server:
  76. if line[0] == "PING":
  77. msg = " ".join(("PONG", line[1]))
  78. connection.send(msg)
  79. # On successful connection to the server:
  80. if line[1] == "376":
  81. # If we're supposed to auth to NickServ, do that:
  82. try:
  83. username = config.irc["frontend"]["nickservUsername"]
  84. password = config.irc["frontend"]["nickservPassword"]
  85. except KeyError:
  86. pass
  87. else:
  88. msg = " ".join(("IDENTIFY", username, password))
  89. connection.say("NickServ", msg)
  90. # Join all of our startup channels:
  91. for chan in config.irc["frontend"]["channels"]:
  92. connection.join(chan)