IRC.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. # Part of rabbitears See LICENSE for permissions
  2. # Copyright (C) 2022 Matt Arnold
  3. import socket
  4. import sys
  5. import irctokens
  6. import time
  7. import logging
  8. class IRCBadMessage(Exception):
  9. pass
  10. class IRCError(Exception):
  11. pass
  12. def printred(s):
  13. t = f"\033[1;31m {s} \033[0m\n"
  14. print(t)
  15. def parsemsg(s):
  16. """Breaks a message from an IRC server into its prefix, command, and arguments.
  17. """
  18. prefix = ''
  19. trailing = []
  20. if not s:
  21. raise IRCBadMessage("Empty line.")
  22. if s[0] == ':':
  23. prefix, s = s[1:].split(' ', 1)
  24. if s.find(' :') != -1:
  25. s, trailing = s.split(' :', 1)
  26. args = s.split()
  27. args.append(trailing)
  28. else:
  29. args = s.split()
  30. command = args.pop(0)
  31. return prefix, command, args
  32. LINEEND = '\r\n'
  33. class IRCBot:
  34. irc = None
  35. def __init__(self, sock, config=None, isBad=False):
  36. self.irc = sock
  37. self.connected = False
  38. self.config = config
  39. self.badircd = isBad
  40. def send_cmd(self, line):
  41. """Send an IRC Command, takes an IRC command string without the CRLF
  42. Returns encoded msg on success raises IRCError on failure """
  43. if not self.connected:
  44. raise IRCError("Not Connected")
  45. encmsg = bytes(line.format() + LINEEND, 'UTF-8' )
  46. expected = len(encmsg)
  47. if self.irc.send(encmsg) == expected:
  48. return str(encmsg)
  49. else:
  50. raise IRCError("Unexpected Send Length")
  51. def on_welcome(self, *args, **kwargs):
  52. authmsg = irctokens.build("NICKSERV", ['IDENTIFY', self.config['nickpass']])
  53. self.send_cmd(authmsg)
  54. joinmsg = irctokens.build("JOIN", [self.config['channel']])
  55. self.send_cmd(joinmsg)
  56. def send_privmsg(self, dst, msg):
  57. msg = irctokens.build("PRIVMSG",[dst, msg])
  58. self.send_cmd(msg)
  59. def send_quit(self, quitmsg):
  60. msg = irctokens.build("QUIT", [quitmsg])
  61. logging.debug(msg)
  62. self.send_cmd(msg)
  63. def send_action(self, action_msg, dst):
  64. pass
  65. def connect(self, server, port, channel, botnick, botnickpass):
  66. if self.config is None:
  67. self.config = {}
  68. self.config["hostname"] = server
  69. self.config["port"] = port
  70. self.config["nick"] = botnick
  71. self.config["channel"] = channel
  72. self.config["nickpass"] = botnickpass
  73. logging.debug("Connecting to: " + server)
  74. self.irc.connect((self.config["hostname"], self.config["port"]))
  75. self.connected = True
  76. # Perform user registration
  77. usermsg = irctokens.build("USER", [botnick, "0","*", "RabbitEars Bot"]).format()
  78. logging.debug(usermsg)
  79. self.send_cmd(usermsg)
  80. nickmsg = irctokens.build("NICK", [botnick])
  81. self.send_cmd(nickmsg)
  82. if self.badircd:
  83. time.sleep(5)
  84. authmsg = irctokens.build("NICKSERV", ['IDENTIFY', self.config['nickpass']])
  85. self.send_cmd(authmsg)
  86. time.sleep(5)
  87. self.on_welcome([self.config["hostname"]])
  88. def get_response(self):
  89. # Get the response
  90. resp = self.irc.recv(4096).decode("UTF-8")
  91. msg = parsemsg(resp)
  92. nwmsg = irctokens.tokenise(resp)
  93. logging.info(nwmsg.command)
  94. if nwmsg.command == "001":
  95. self.on_welcome(nwmsg.params)
  96. if nwmsg.command == "ERROR":
  97. raise IRCError(str(nwmsg.params[0]))
  98. if nwmsg.command == 'PING':
  99. logging.debug('Sending pong')
  100. pongmsg = irctokens.build("PONG", [nwmsg.params[0]])
  101. self.send_cmd(pongmsg)
  102. return msg