mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-09 22:57:59 -05:00
meetbot: Better debugging, and persistent channel data (including topics).
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
|
*.pickle
|
||||||
*.sage.py
|
*.sage.py
|
||||||
*.zk.bin
|
*.zk.bin
|
||||||
*_circuit_layout.png
|
*_circuit_layout.png
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
import pickle
|
||||||
|
|
||||||
from base58 import b58decode
|
from base58 import b58decode
|
||||||
from nacl.public import PrivateKey, Box
|
from nacl.public import PrivateKey, Box
|
||||||
@@ -10,131 +11,171 @@ from meetbot_cfg import config
|
|||||||
# Initialized channels from the configuration
|
# Initialized channels from the configuration
|
||||||
CHANS = {}
|
CHANS = {}
|
||||||
|
|
||||||
|
# Pickle DB
|
||||||
|
PICKLE_DB = "meetbot.pickle"
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: while this is nice to support, it would perhaps be better to do it
|
||||||
|
# all over the same connection rather than opening a socket for each channel.
|
||||||
async def channel_listen(host, port, nick, chan):
|
async def channel_listen(host, port, nick, chan):
|
||||||
global CHANS
|
logging.info("%s: Connecting to %s:%s", chan, host, port)
|
||||||
|
|
||||||
logging.info(f"Connecting to {host}:{port}")
|
|
||||||
reader, writer = await asyncio.open_connection(host, port)
|
reader, writer = await asyncio.open_connection(host, port)
|
||||||
|
|
||||||
logging.info(f"{host}:{port} Send CAP msg")
|
logging.debug("%s: Send CAP msg", chan)
|
||||||
cap_msg = "CAP REQ : no-history\r\n"
|
msg = "CAP REQ : no-history\r\n"
|
||||||
writer.write(cap_msg.encode("utf-8"))
|
writer.write(msg.encode("utf-8"))
|
||||||
|
|
||||||
logging.info(f"{host}:{port} Send NICK msg")
|
logging.debug("%s: Send NICK msg", chan)
|
||||||
nick_msg = f"NICK {nick}\r\n"
|
msg = f"NICK {nick}\r\n"
|
||||||
writer.write(nick_msg.encode("utf-8"))
|
writer.write(msg.encode("utf-8"))
|
||||||
|
|
||||||
logging.info(f"{host}:{port} Send CAP END msg")
|
logging.debug("%s: Send CAP END msg", chan)
|
||||||
cap_end_msg = "CAP END\r\n"
|
msg = "CAP END\r\n"
|
||||||
writer.write(cap_end_msg.encode("utf-8"))
|
writer.write(msg.encode("utf-8"))
|
||||||
|
|
||||||
logging.info(f"{host}:{port} Send JOIN msg for {chan}")
|
logging.debug("%s: Send JOIN msg", chan)
|
||||||
join_msg = f"JOIN {chan}\r\n"
|
msg = f"JOIN {chan}\r\n"
|
||||||
writer.write(join_msg.encode("utf-8"))
|
writer.write(msg.encode("utf-8"))
|
||||||
|
|
||||||
logging.info(f"{host}:{port} Listening to channel: {chan}")
|
logging.info("%s: Listening to channel", chan)
|
||||||
while True:
|
while True:
|
||||||
msg = await reader.read(1024)
|
msg = await reader.readline()
|
||||||
msg = msg.decode("utf8")
|
msg = msg.decode("utf8")
|
||||||
if not msg:
|
if not msg:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
command = msg.split(" ")[1]
|
command = msg.split(" ")[1]
|
||||||
|
logging.debug("%s: Recv: %s", chan, msg.rstrip())
|
||||||
|
|
||||||
if command == "PRIVMSG":
|
if command == "PRIVMSG":
|
||||||
msg_title = msg.split(" ")[3][1:].rstrip()
|
msg_title = msg.split(" ")[3][1:].rstrip()
|
||||||
if not msg_title:
|
if not msg_title:
|
||||||
logging.info("Got empty PRIVMSG, ignoring")
|
logging.info("%s: Recv empty PRIVMSG, ignoring", chan)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if msg_title == "!start":
|
if msg_title == "!start":
|
||||||
|
logging.info("%s: Got !start", chan)
|
||||||
topics = CHANS[chan]["topics"]
|
topics = CHANS[chan]["topics"]
|
||||||
reply = f"PRIVMSG {chan} :Meeting started\r\n"
|
reply = f"PRIVMSG {chan} :Meeting started"
|
||||||
writer.write(reply.encode("utf-8"))
|
logging.info("%s: Send: %s", chan, reply)
|
||||||
|
writer.write((reply + "\r\n").encode("utf-8"))
|
||||||
await writer.drain()
|
await writer.drain()
|
||||||
|
|
||||||
reply = f"PRIVMSG {chan} :Topics:\r\n"
|
if len(topics) == 0:
|
||||||
writer.write(reply.encode("utf-8"))
|
reply = f"PRIVMSG {chan} :No topics"
|
||||||
|
logging.info("%s: Send: %s", chan, reply)
|
||||||
|
writer.write((reply + "\r\n").encode("utf-8"))
|
||||||
|
await writer.drain()
|
||||||
|
continue
|
||||||
|
|
||||||
|
reply = f"PRIVMSG {chan} :Topics:"
|
||||||
|
logging.info("%s: Send: %s", chan, reply)
|
||||||
|
writer.write((reply + "\r\n").encode("utf-8"))
|
||||||
await writer.drain()
|
await writer.drain()
|
||||||
|
|
||||||
for i, topic in enumerate(topics):
|
for i, topic in enumerate(topics):
|
||||||
reply = f"PRIVMSG {chan} :1. {topic}\r\n"
|
reply = f"PRIVMSG {chan} :{i+1}. {topic}"
|
||||||
writer.write(reply.encode("utf-8"))
|
logging.info("%s: Send: %s", chan, reply)
|
||||||
|
writer.write((reply + "\r\n").encode("utf-8"))
|
||||||
await writer.drain()
|
await writer.drain()
|
||||||
|
|
||||||
if len(topics) > 0:
|
cur_topic = topics.pop(0)
|
||||||
cur_topic = topics.pop(0)
|
reply = f"PRIVMSG {chan} :Current topic: {cur_topic}\r\n"
|
||||||
reply = f"PRIVMSG {chan} :Current topic: {cur_topic}\r\n"
|
|
||||||
else:
|
|
||||||
reply = f"PRIVMSG {chan} :No further topics\r\n"
|
|
||||||
|
|
||||||
CHANS[chan]["topics"] = topics
|
CHANS[chan]["topics"] = topics
|
||||||
|
|
||||||
writer.write(reply.encode("utf-8"))
|
writer.write(reply.encode("utf-8"))
|
||||||
await writer.drain()
|
await writer.drain()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if msg_title == "!end":
|
if msg_title == "!end":
|
||||||
reply = f"PRIVMSG {chan} :Meeting ended\r\n"
|
logging.info("%s: Got !end", chan)
|
||||||
writer.write(reply.encode("utf-8"))
|
reply = f"PRIVMSG {chan} :Meeting ended"
|
||||||
|
logging.info("%s: Send: %s", chan, reply)
|
||||||
|
writer.write((reply + "\r\n").encode("utf-8"))
|
||||||
await writer.drain()
|
await writer.drain()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if msg_title == "!topic":
|
if msg_title == "!topic":
|
||||||
|
logging.info("%s: Got !topic", chan)
|
||||||
topic = msg.split(" ", 4)
|
topic = msg.split(" ", 4)
|
||||||
|
|
||||||
if len(topic) != 5:
|
if len(topic) != 5:
|
||||||
|
logging.debug("%s: Topic msg len not 5, skipping", chan)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
topic = topic[4].rstrip()
|
topic = topic[4].rstrip()
|
||||||
|
|
||||||
if topic == "":
|
if topic == "":
|
||||||
|
logging.debug("%s: Topic message empty, skipping", chan)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
topics = CHANS[chan]["topics"]
|
topics = CHANS[chan]["topics"]
|
||||||
topics.append(topic)
|
topics.append(topic)
|
||||||
CHANS[chan]["topics"] = topics
|
CHANS[chan]["topics"] = topics
|
||||||
reply = f"PRIVMSG {chan} :Added topic: {topic}\r\n"
|
logging.debug("%s: Appended topic to channel topics", chan)
|
||||||
writer.write(reply.encode("utf-8"))
|
|
||||||
|
reply = f"PRIVMSG {chan} :Added topic: {topic}"
|
||||||
|
logging.info("%s: Send: %s", chan, reply)
|
||||||
|
writer.write((reply + "\r\n").encode("utf-8"))
|
||||||
await writer.drain()
|
await writer.drain()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if msg_title == "!list":
|
if msg_title == "!list":
|
||||||
|
logging.info("%s: Got !list", chan)
|
||||||
topics = CHANS[chan]["topics"]
|
topics = CHANS[chan]["topics"]
|
||||||
if len(topics) == 0:
|
if len(topics) == 0:
|
||||||
reply = f"PRIVMSG {chan} :No set topics\r\n"
|
reply = f"PRIVMSG {chan} :No topics"
|
||||||
else:
|
else:
|
||||||
reply = f"PRIVMSG {chan} :Topics:\r\n"
|
reply = f"PRIVMSG {chan} :Topics:"
|
||||||
writer.write(reply.encode("utf-8"))
|
|
||||||
|
logging.info("%s: Send: %s", chan, reply)
|
||||||
|
writer.write((reply + "\r\n").encode("utf-8"))
|
||||||
await writer.drain()
|
await writer.drain()
|
||||||
|
|
||||||
for i, topic in enumerate(topics):
|
for i, topic in enumerate(topics):
|
||||||
reply = f"PRIVMSG {chan} :1. {topic}\r\n"
|
reply = f"PRIVMSG {chan} :{i+1}. {topic}"
|
||||||
writer.write(reply.encode("utf-8"))
|
logging.info("%s: Send: %s", chan, reply)
|
||||||
|
writer.write((reply + "\r\n").encode("utf-8"))
|
||||||
await writer.drain()
|
await writer.drain()
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if msg_title == "!next":
|
if msg_title == "!next":
|
||||||
|
logging.info("%s: Got !next", chan)
|
||||||
topics = CHANS[chan]["topics"]
|
topics = CHANS[chan]["topics"]
|
||||||
if len(topics) == 0:
|
if len(topics) == 0:
|
||||||
reply = f"PRIVMSG {chan} :No further topics\r\n"
|
reply = f"PRIVMSG {chan} :No further topics"
|
||||||
else:
|
else:
|
||||||
cur_topic = topics.pop(0)
|
cur_topic = topics.pop(0)
|
||||||
CHANS[chan]["topics"] = topics
|
CHANS[chan]["topics"] = topics
|
||||||
reply = f"PRIVMSG {chan} :Current topic: {cur_topic}\r\n"
|
reply = f"PRIVMSG {chan} :Current topic: {cur_topic}"
|
||||||
|
|
||||||
writer.write(reply.encode("utf-8"))
|
logging.info("%s: Send: %s", chan, reply)
|
||||||
|
writer.write((reply + "\r\n").encode("utf-8"))
|
||||||
await writer.drain()
|
await writer.drain()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main(debug=False):
|
||||||
format = "%(asctime)s: %(message)s"
|
global CHANS
|
||||||
logging.basicConfig(format=format, level=logging.INFO, datefmt="%H:%M:%S")
|
|
||||||
|
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 pickle database")
|
||||||
|
|
||||||
for i in config["channels"]:
|
for i in config["channels"]:
|
||||||
name = i["name"]
|
name = i["name"]
|
||||||
logging.info(f"Found config for channel {name}")
|
logging.info("Found config for channel %s", name)
|
||||||
|
|
||||||
# TODO: This will be useful when ircd has a CAP that tells it to
|
# TODO: This will be useful when ircd has a CAP that tells it to
|
||||||
# give **all** messages to the connected client, no matter if ircd
|
# give **all** messages to the connected client, no matter if ircd
|
||||||
@@ -143,20 +184,25 @@ async def main():
|
|||||||
# they can rather only be held by this bot. In turn this means the bot
|
# they can rather only be held by this bot. In turn this means the bot
|
||||||
# can be deployed with any ircd.
|
# can be deployed with any ircd.
|
||||||
if i["secret"]:
|
if i["secret"]:
|
||||||
logging.info(f"Instantiating NaCl box for {name}")
|
logging.info("Instantiating NaCl box for %s", name)
|
||||||
sk = b58decode(i["secret"].encode("utf-8"))
|
secret = b58decode(i["secret"].encode("utf-8"))
|
||||||
sk = PrivateKey(sk)
|
secret = PrivateKey(secret)
|
||||||
pk = sk.public_key
|
public = secret.public_key
|
||||||
box = Box(sk, pk)
|
box = Box(secret, public)
|
||||||
else:
|
else:
|
||||||
box = None
|
box = None
|
||||||
|
|
||||||
CHANS[name] = {}
|
if not CHANS.get(name):
|
||||||
|
CHANS[name] = {}
|
||||||
|
|
||||||
|
if not CHANS[name].get("topics"):
|
||||||
|
CHANS[name]["topics"] = []
|
||||||
|
|
||||||
CHANS[name]["box"] = box
|
CHANS[name]["box"] = box
|
||||||
CHANS[name]["topics"] = []
|
|
||||||
|
|
||||||
coroutines = []
|
coroutines = []
|
||||||
for i in CHANS.keys():
|
for i in CHANS.keys():
|
||||||
|
logging.debug("Creating async task for %s", i)
|
||||||
task = asyncio.create_task(
|
task = asyncio.create_task(
|
||||||
channel_listen(config["host"], config["port"], config["nick"], i))
|
channel_listen(config["host"], config["port"], config["nick"], i))
|
||||||
coroutines.append(task)
|
coroutines.append(task)
|
||||||
@@ -164,4 +210,14 @@ async def main():
|
|||||||
await asyncio.gather(*coroutines)
|
await asyncio.gather(*coroutines)
|
||||||
|
|
||||||
|
|
||||||
asyncio.run(main())
|
if __name__ == "__main__":
|
||||||
|
from sys import argv
|
||||||
|
DBG = bool(len(argv) == 2 and argv[1] == "-v")
|
||||||
|
|
||||||
|
try:
|
||||||
|
asyncio.run(main(debug=DBG))
|
||||||
|
except:
|
||||||
|
print("\rCaught ^C, saving pickle and exiting")
|
||||||
|
|
||||||
|
with open(PICKLE_DB, "wb") as fdesc:
|
||||||
|
pickle.dump(CHANS, fdesc, protocol=pickle.HIGHEST_PROTOCOL)
|
||||||
|
|||||||
Reference in New Issue
Block a user