diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | IRC.py | 77 | ||||
-rw-r--r-- | client.py | 31 | ||||
-rw-r--r-- | config.json.example | 7 |
4 files changed, 84 insertions, 34 deletions
diff --git a/.gitignore b/.gitignore index de2d5e0..6d979ed 100644 --- a/.gitignore +++ b/.gitignore @@ -150,3 +150,6 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ + +# ignore config file +config.json diff --git a/IRC.py b/IRC.py index 018756b..a67e65d 100644 --- a/IRC.py +++ b/IRC.py @@ -2,9 +2,16 @@ # Copyright (C) 2022 Matt Arnold import socket import sys +import irctokens import time class IRCBadMessage(Exception): pass +class IRCError(Exception): + pass + +def printred(s): + t = f"\033[1;31m {s} \033[0m\n" + print(t) def parsemsg(s): """Breaks a message from an IRC server into its prefix, command, and arguments. @@ -24,48 +31,76 @@ def parsemsg(s): command = args.pop(0) return prefix, command, args + LINEEND = '\r\n' class IRCBot: irc = None - def __init__(self, sock): + def __init__(self, sock, config=None): self.irc = sock + self.connected = False + self.config = config + + def send_cmd(self, line): + """Send an IRC Command, takes an IRC command string without the CRLF + Returns encoded msg on success raises IRCError on failure """ + if not self.connected: + raise IRCError("Not Connected") + encmsg = bytes(line.format() + LINEEND, 'UTF-8' ) + expected = len(encmsg) + if self.irc.send(encmsg) == expected: + return str(encmsg) + else: + raise IRCError("Unexpected Send Length") + def on_welcome(self, *args, **kwargs): + authmsg = irctokens.build("NICKSERV", ['IDENTIFY', self.config['nickpass']]) + self.send_cmd(authmsg) + joinmsg = irctokens.build("JOIN", [self.config['channel']]) + self.send_cmd(joinmsg) + + def send_privmsg(self, dst, msg): - self.irc.send(bytes("PRIVMSG " + dst + " :" + msg + LINEEND, "UTF-8")) + msg = irctokens.build("PRIVMSG",[dst, msg]) + self.send_cmd(msg) def send_quit(self, quitmsg): - msg = f'QUIT :{quitmsg}' + LINEEND + msg = irctokens.build("QUIT", [quitmsg]) print(msg) - self.irc.send(msg.encode('utf-8')) + self.send_cmd(msg) - def send_action(self, action_msg): + def send_action(self, action_msg, dst): pass - def connect(self, server, port, channel, botnick, botpass, botnickpass): + def connect(self, server, port, channel, botnick, botnickpass): + if self.config is None: + self.config = {} + self.config["hostname"] = server + self.config["port"] = port + self.config["nick"] = botnick + self.config["channel"] = channel + self.config["nickpass"] = botnickpass print("Connecting to: " + server) - self.irc.connect((server, port)) - - # Perform user authentication - self.irc.send(bytes("USER " + botnick + " " + botnick + - " " + botnick + " :python" + LINEEND, "UTF-8")) - self.irc.send(bytes("NICK " + botnick + LINEEND, "UTF-8")) - self.irc.send(bytes("NICKSERV IDENTIFY " + - botnickpass + " " + LINEEND, "UTF-8")) - time.sleep(5) - - # join the channel - self.irc.send(bytes("JOIN " + channel + LINEEND, "UTF-8")) + self.irc.connect((self.config["hostname"], self.config["port"])) + self.connected = True + # Perform user registration + usermsg = irctokens.build("USER", [botnick, "0","*", "RabbitEars Bot"]).format() + print(usermsg) + self.send_cmd(usermsg) + nickmsg = irctokens.build("NICK", [botnick]) + self.send_cmd(nickmsg) def get_response(self): - time.sleep(1) # Get the response resp = self.irc.recv(4096).decode("UTF-8") msg = parsemsg(resp) - - if msg[1] == 'PING': + nwmsg = irctokens.tokenise(resp) + + if nwmsg.command == "001": + self.on_welcome(nwmsg.params) + if nwmsg.command == 'PING': print('Sending pong') self.irc.send( bytes('PONG ' + LINEEND, "UTF-8")) diff --git a/client.py b/client.py index 112707c..bbd0649 100644 --- a/client.py +++ b/client.py @@ -1,19 +1,22 @@ -# +# Part of rabbitears See LICENSE for permissions +# Copyright (C) 2022 Matt Arnold from IRC import * import os import random import ssl import socket import sys +import irctokens +import json LINEEND = '\r\n' # IRC Config -hostname = "irc.spartalinux.xyz" # Provide a valid server IP/Hostname -port = 6697 -channel = "#botdev" -botnick = "botley" -botnickpass = "a.password" -botpass = "unused" +config = None +with open('config.json') as f: + jld = f.read() + config = json.loads(jld) + +botpass = "unused.factor.this.out" # Need to pass the IRCBot class a socket the reason it doesn't do this itself is # so you can set up TLS or not as you need it @@ -22,14 +25,16 @@ oursock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) context = ssl.SSLContext() context.check_hostname = False context.verify_mode = ssl.CERT_NONE -oursock = context.wrap_socket(oursock, server_hostname=hostname) +oursock = context.wrap_socket(oursock, server_hostname=config['hostname']) irc = IRCBot(oursock) -irc.connect(hostname, port, channel, botnick, botpass, botnickpass) +irc.connect(config['hostname'], + config['port'], + config['channel'], + config['nick'], + config['nickpass']) def generate_response(person, message): - print(person, message) msg = message.strip(LINEEND) - irc.send_privmsg(channel, str(type(person)) + ' ' + str(type(message))) if 'cool.person' in person and msg.lower() == "hello botley": return "Greetings Master" elif msg.lower() == "hello": @@ -42,10 +47,10 @@ while True: text = irc.get_response() print(text[0],text[1],text[2]) - if text[1] == 'PRIVMSG' and text[2][0] == channel: + if text[1] == 'PRIVMSG' and text[2][0] == config['channel']: r = generate_response(text[0],text[2][1]) if r is not None: - irc.send_privmsg(channel,r) + irc.send_privmsg(config['channel'],r) except KeyboardInterrupt: irc.send_quit("Ctrl-C Pressed") msg = oursock.recv(4096) diff --git a/config.json.example b/config.json.example new file mode 100644 index 0000000..42d81a3 --- /dev/null +++ b/config.json.example @@ -0,0 +1,7 @@ +{ + "hostname": "irc.spartalinux.xyz", + "port": 6697, + "nick": "bad_bot", + "channel": "#botdev", + "nickpass": "a.password" +} |