From 963757b0563cb03d4fc2e461fb1446a75bfc3685 Mon Sep 17 00:00:00 2001 From: Darks Date: Sat, 7 Nov 2020 00:26:51 +0100 Subject: [PATCH] First & dirty commit --- .gitignore | 1 + main.py | 58 +++++++++++++++++++++++++++++++++++++++++++++ sample.txt | 42 +++++++++++++++++++++++++++++++++ utils.py | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 170 insertions(+) create mode 100644 .gitignore create mode 100755 main.py create mode 100644 sample.txt create mode 100644 utils.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96403d3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/* diff --git a/main.py b/main.py new file mode 100755 index 0000000..6f5ca47 --- /dev/null +++ b/main.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +import sys +import time +from threading import Thread +# from queue import Queue + +from utils import create_socket, IRC + +server_name = 'localhost' +server_port = 5500 + +NICK = "GLaDOS" +PASSWORD = "abcdef123456" + +class Bot(object): + def __init__(self, irc, v5): + self.irc = IRC(irc) + self.v5_sock = create_socket("v5", v5) + self.channels = ["#general", "#glados"] + self.connected = False # Dirty lock + + def run(self): + # Start a thread for raw IRC messages + Thread(target=self.irc.raw_handle).start() + + # Authenticate to server + self.irc.connect(NICK, PASSWORD) + for c in self.channels: + self.irc.join(c) + + # Finally, handle v5 messages + while True: + data, addr = self.v5_sock.recvfrom(4096) + data = data.decode() + print(f"v5: Received <{data}>") + self.irc.msg(data, "#glados") + + def handle_irc(self): + self.connected = True + print("irc connected") + + while True: + msg = self.irc.get_msg() + print(f"IRC: {data}") + + if "Hello" in msg: + self.irc.msg("Hello :)", "#glados") + + +glados = Bot( + ('irc.planet-casio.com', 6697), + ('127.0.0.1', 5555) +) + +glados.run() + +#abcdef123456 diff --git a/sample.txt b/sample.txt new file mode 100644 index 0000000..0b1180c --- /dev/null +++ b/sample.txt @@ -0,0 +1,42 @@ +< :irc.planet-casio.com NOTICE * :*** Looking up your hostname... +< :irc.planet-casio.com NOTICE * :*** Found your hostname (cached) +> USER GLaDOS * * :GLaDOS +> NICK GLaDOS +< PING :5B61F700 +> PONG :5B61F700 + +< :irc.planet-casio.com NOTICE GLaDOS :Serveur réservé aux membres de Planète Casio. +< :irc.planet-casio.com NOTICE GLaDOS :Ce serveur nécessite de s'authentifier. Les identifiants sont ceux de votre compte Planète Casio (https://www.planet-casio.com). +< :irc.planet-casio.com NOTICE GLaDOS :Pour vous connecter, configurez le SASL dans votre client ou entrez la commande suivante : /AUTH : + +> AUTH GLaDOS:password +< :irc.planet-casio.com 900 GLaDOS GLaDOS!GLaDOS@87-231-180-235.rev.numericable.fr GLaDOS :You are now logged in as GLaDOS. +< :irc.planet-casio.com 001 GLaDOS :Welcome to the Planete Casio IRC Network GLaDOS!GLaDOS@87-231-180-235.rev.numericable.fr +< :irc.planet-casio.com 002 GLaDOS :Your host is irc.planet-casio.com, running version UnrealIRCd-5.0.7 +< :irc.planet-casio.com 003 GLaDOS :This server was created Sun Oct 11 2020 at 12:46:49 MSK +< :irc.planet-casio.com 004 GLaDOS irc.planet-casio.com UnrealIRCd-5.0.7 iowrsxzdHtIDZRqpWGTSB lvhopsmntikraqbeIHzMQNRTOVKDdGLPZSCcf +< :irc.planet-casio.com 005 GLaDOS AWAYLEN=307 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=beI,kLf,lH,psmntirzMQNRTOVKDdGPZSCc CHANNELLEN=32 CHANTYPES=# CLIENTTAGDENY=*,-draft/typing,-typing DEAF=d ELIST=MNUCT EXCEPTS EXTBAN=~,ptmTSOcarnqjf :are supported by this server +< :irc.planet-casio.com 005 GLaDOS HCN INVEX KICKLEN=307 KNOCK MAP MAXCHANNELS=100 MAXLIST=b:60,e:60,I:60 MAXNICKLEN=30 MINNICKLEN=0 MODES=12 NAMESX NETWORK=Planete-Casio :are supported by this server +< :irc.planet-casio.com 005 GLaDOS NICKLEN=30 PREFIX=(qaohv)~&@%+ QUITLEN=307 SAFELIST SILENCE=15 STATUSMSG=~&@%+ TARGMAX=DCCALLOW:,ISON:,JOIN:,KICK:4,KILL:,LIST:,NAMES:1,NOTICE:1,PART:,PRIVMSG:4,SAJOIN:,SAPART:,TAGMSG:1,USERHOST:,USERIP:,WATCH:,WHOIS:1,WHOWAS:1 TOPICLEN=360 UHNAMES USERIP WALLCHOPS WATCH=128 :are supported by this server +< :irc.planet-casio.com 005 GLaDOS WATCHOPTS=A WHOX :are supported by this server +< :irc.planet-casio.com 396 GLaDOS Clk-3B0FEC88.rev.numericable.fr :is now your displayed host +< :irc.planet-casio.com NOTICE GLaDOS :*** You are connected to irc.planet-casio.com with TLSv1.3-TLS_CHACHA20_POLY1305_SHA256 +< :irc.planet-casio.com 251 GLaDOS :There are 1 users and 10 invisible on 2 servers +< :irc.planet-casio.com 252 GLaDOS 7 :operator(s) online +< :irc.planet-casio.com 254 GLaDOS 1 :channels formed +< :irc.planet-casio.com 255 GLaDOS :I have 4 clients and 1 servers +< :irc.planet-casio.com 265 GLaDOS 4 8 :Current local users 4, max 8 +< :irc.planet-casio.com 266 GLaDOS 11 13 :Current global users 11, max 13 +< :irc.planet-casio.com 422 GLaDOS :MOTD File is missing +< :GLaDOS MODE GLaDOS :+iwxz +< :GLaDOS!GLaDOS@Clk-3B0FEC88.rev.numericable.fr JOIN :#general +< :irc.planet-casio.com 353 GLaDOS = #general :GLaDOS Darks Breizh eragon +< :irc.planet-casio.com 366 GLaDOS #general :End of /NAMES list. +< :NickServ MODE GLaDOS :+r + +> Hello +< :irc.planet-casio.com 421 GLaDOS Hello :Unknown command + +> join #general +> PRIVMSG #general :Hello +< :Darks!eldeberen@Clk-3B0FEC88.rev.numericable.fr PRIVMSG #general :Nice diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..a641abd --- /dev/null +++ b/utils.py @@ -0,0 +1,69 @@ +import socket, ssl +from queue import Queue + +def create_socket(type, target): + if type == "irc": + s = socket.create_connection(('irc.planet-casio.com', 6697)) + context = ssl.create_default_context() + s = context.wrap_socket(s, server_hostname='irc.planet-casio.com') + return s + elif type == "v5": + s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) + s.bind(target) + return s + +class IRC(object): + def __init__(self, target): + self.socket = create_socket("irc", target) + self.inbox = Queue() + + + # Public API + + def raw_handle(self): + """ Handle new messages and ping responses. Runs in its own thread""" + data = self.socket.recv(4096).decode() + for m in data.split('\r\n'): + if m.startswith("PING"): + self._send(m.replace("PING", "PONG")) + else: + self.inbox.put(m) + + def connect(self, nick, password): + """ Connect and authenticate to IRC server """ + self._send(f"NICK {nick}") + self._send(f"USER {nick} * * :{nick}") + + self._waitfor(lambda m: "NOTICE" in m and "/AUTH" in m) + self._send(f"auth {nick}:{password}") + self._waitfor(lambda m: f":{nick} MODE {nick} :+iwxz" in m) + + def join(self, channel): + """ Join a channel """ + self._send("JOIN :{channel}") + + def msg(self, msg, channel): + self._send(f"PRIVMSG {channel} :{msg}") + + def get_msg(self): + """ Get the next message on the inbox """ + return _waitfor(lambda m: "PRIVMSG" in m) + + # Private API + + def _send(self, raw): + """ Send raw data """ + self.socket.send(f"{raw}\r\n".encode()) + + def _recv(self): + """ Pop a new item from INBOX """ + print("Waiting for new item") + return self.inbox.get() + + def _waitfor(self, condition): + """ Wait for a message that matches the condition """ + msg = self._recv() + while not condition(msg): + print(f"Wait for: {msg}") + msg = self._recv() + return msg