Files
darkfi/bin/darkirc/script/bots/meetbot/meetbot.py
2024-10-21 17:30:44 +03:00

288 lines
9.7 KiB
Python
Executable File

#!/usr/bin/env python3
import asyncio
import logging
import pickle
from time import time
from meetbot_cfg import config
# Initialized channels from the configuration
CHANS = {}
# Pickle DB
PICKLE_DB = "meetbot.pickle"
async def main(debug=False):
global CHANS
loglevel = logging.DEBUG if debug else logging.INFO
logfmt = "%(asctime)s [%(levelname)s]\t%(message)s"
logging.basicConfig(format=logfmt,
level=loglevel,
datefmt="%Y-%m-%d %H:%M:%S")
try:
with open(PICKLE_DB, "rb") as pickle_fd:
CHANS = pickle.load(pickle_fd)
logging.info("Loaded pickle database")
except:
logging.info("Did not find existing pickle database")
host = config["host"]
port = config["port"]
nick = config["nick"]
try:
logging.info("Connecting to %s/%d", host, port)
reader, writer = await asyncio.open_connection(host, port)
logging.debug("--> %s/%d: CAP LS 302", host, port)
msg = "CAP LS 302\r\n"
writer.write(msg.encode("utf-8"))
await writer.drain()
logging.debug("--> %s/%d: NICK %s", host, port, nick)
msg = f"NICK {nick}\r\n"
writer.write(msg.encode("utf-8"))
await writer.drain()
logging.debug("--> %s/%d: USER %s * 0 %s", host, port, nick, nick)
msg = f"USER {nick} * 0 :{nick}\r\n"
writer.write(msg.encode("utf-8"))
await writer.drain()
msg = await reader.readline()
msg = msg.decode("utf-8")
logging.debug("<-- %s/%d: %s", host, port, msg)
logging.debug("--> %s/%d: CAP REQ :no-history", host, port)
msg = "CAP REQ :no-history\r\n"
writer.write(msg.encode("utf-8"))
await writer.drain()
logging.debug("--> %s/%d: CAP REQ :no-autojoin", host, port)
msg = "CAP REQ :no-autojoin\r\n"
writer.write(msg.encode("utf-8"))
await writer.drain()
msg = await reader.readline()
msg = msg.decode("utf-8")
logging.debug("<-- %s/%d: %s", host, port, msg)
logging.debug("--> %s/%d: CAP END", host, port)
msg = "CAP END\r\n"
writer.write(msg.encode("utf-8"))
await writer.drain()
msg = await reader.readline()
msg = msg.decode("utf-8")
logging.debug("<-- %s/%d: %s", host, port, msg)
for chan in config["channels"]:
chan = chan["name"]
logging.debug("--> %s/%d: JOIN %s", host, port, chan)
msg = f"JOIN {chan}\r\n"
writer.write(msg.encode("utf-8"))
await writer.drain()
channels = [chan["name"] for chan in config["channels"]]
logging.info("%s/%d: Listening to channels: %s", host, port, channels)
elapsed = 0
while True:
msg = await reader.readline()
msg = msg.decode("utf-8")
if not msg:
continue
split_msg = msg.split(" ")
command = split_msg[1]
chan = split_msg[2]
nick_c = split_msg[0][1:].rsplit("!", 1)[0]
logging.debug("<-- %s/%d: %s", host, port, msg.rstrip())
if command == "PRIVMSG":
msg_title = msg.split(" ")[3][1:].rstrip()
if not msg_title:
continue
if msg_title == "!start":
logging.info("%s: Got !start", chan)
if not CHANS.get(chan):
CHANS[chan] = {}
CHANS[chan]["topics"] = {}
topics = CHANS[chan]["topics"]
reply = f"PRIVMSG {chan} :Meeting started\r\n"
writer.write(reply.encode("utf-8"))
await writer.drain()
if len(topics) == 0:
reply = f"PRIVMSG {chan} :No topics\r\n"
writer.write(reply.encode("utf-8"))
await writer.drain()
continue
reply = f"PRIVMSG {chan} :Topics:\r\n"
writer.write(reply.encode("utf-8"))
await writer.drain()
for i, topic in enumerate(topics):
reply = f"PRIVMSG {chan} :{i+1}. {topic}\r\n"
writer.write(reply.encode("utf-8"))
await writer.drain()
cur_topic = topics.pop(0)
reply = f"PRIVMSG {chan} :Current topic: {cur_topic}\r\n"
CHANS[chan]["topics"] = topics
writer.write(reply.encode("utf-8"))
await writer.drain()
elapsed = time()
continue
if msg_title == "!end":
logging.info("%s: Got !end", chan)
reply = f"PRIVMSG {chan} :Elapsed time: {round((time() - elapsed)/60, 1)} min\r\n"
elapsed = 0
writer.write(reply.encode("utf-8"))
await writer.drain()
reply = f"PRIVMSG {chan} :Meeting ended\r\n"
writer.write(reply.encode("utf-8"))
await writer.drain()
continue
if msg_title == "!topic":
logging.info("%s: Got !topic", chan)
topic = msg.split(" ", 4)
if len(topic) != 5:
continue
topic = topic[4].rstrip() + f" (by {nick_c})"
if topic == "":
continue
if not CHANS.get(chan):
CHANS[chan] = {}
CHANS[chan]["topics"] = {}
topics = CHANS[chan]["topics"]
if topic not in topics:
topics.append(topic)
CHANS[chan]["topics"] = topics
reply = f"PRIVMSG {chan} :Added topic: {topic}\r\n"
else:
reply = f"PRIVMSG {chan} :Topic already in list\r\n"
writer.write(reply.encode("utf-8"))
await writer.drain()
continue
if msg_title == "!deltopic":
logging.info("%s: Got !deltopic", chan)
if not CHANS.get(chan):
CHANS[chan] = {}
CHANS[chan]["topics"] = {}
topics = CHANS[chan]["topics"]
if len(topics) == 0:
reply = f"PRIVMSG {chan} :No topics\r\n"
writer.write(reply.encode("utf-8"))
await writer.drain()
continue
try:
topic = msg.split(" ", 4)
topic = int(topic[4].rstrip())
del topics[topic-1]
CHANS[chan]["topics"] = topics
reply = f"PRIVMSG {chan} :Removed topic {topic}\r\n"
writer.write(reply.encode("utf-8"))
await writer.drain()
except:
reply = f"PRIVMSG {chan} :Topic not found\r\n"
writer.write(reply.encode("utf-8"))
await writer.drain()
continue
if msg_title == "!list":
logging.info("%s: Got !list", chan)
if not CHANS.get(chan):
CHANS[chan] = {}
CHANS[chan]["topics"] = []
topics = CHANS[chan]["topics"]
if len(topics) == 0:
reply = f"PRIVMSG {chan} :No topics\r\n"
else:
reply = f"PRIVMSG {chan} :Topics:\r\n"
writer.write(reply.encode("utf-8"))
await writer.drain()
for i, topic in enumerate(topics):
reply = f"PRIVMSG {chan} :{i+1}. {topic}\r\n"
writer.write(reply.encode("utf-8"))
await writer.drain()
continue
if msg_title == "!next":
logging.info("%s: Got !next", chan)
if not CHANS.get(chan):
CHANS[chan] = {}
CHANS[chan]["topics"] = []
topics = CHANS[chan]["topics"]
reply = f"PRIVMSG {chan} :Elapsed time: {round((time() - elapsed)/60, 1)}, min\r\n"
writer.write(reply.encode("utf-8"))
await writer.drain()
elapsed = time()
if len(topics) == 0:
reply = f"PRIVMSG {chan} :No further topics\r\n"
else:
cur_topic = topics.pop(0)
CHANS[chan]["topics"] = topics
reply = f"PRIVMSG {chan} :Current topic: {cur_topic}\r\n"
writer.write(reply.encode("utf-8"))
await writer.drain()
continue
except KeyboardInterrupt:
return
except ConnectionRefusedError:
logging.error("%s/%d: Connection refused", host, port)
return
finally:
reply = "QUIT\r\n"
writer.write(reply.encode("utf-8"))
await writer.drain()
writer.close()
await writer.wait_closed()
if __name__ == "__main__":
from sys import argv
debug = "-v" in argv
try:
asyncio.run(main(debug=debug))
except KeyboardInterrupt:
print("\rCaught ^C, saving pickle and exiting")
with open(PICKLE_DB, "wb") as fdesc:
pickle.dump(CHANS, fdesc, protocol=pickle.HIGHEST_PROTOCOL)