Add basic thread handling
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
107
src/handlers.py
Normal file
107
src/handlers.py
Normal file
@@ -0,0 +1,107 @@
|
||||
import fbchat
|
||||
from util.logger import logger
|
||||
from util.database import database
|
||||
from util.session import session
|
||||
|
||||
|
||||
def activate_thread(thread: fbchat.Thread):
|
||||
thread_db = database.threads.update_one(
|
||||
{"_id": thread.id}, {"$setOnInsert": {
|
||||
"type": "group" if isinstance(thread, fbchat.Group) else "user" if isinstance(thread, fbchat.User) else "other",
|
||||
"messages": []
|
||||
}}, upsert=True)
|
||||
|
||||
database.threads.create_index(
|
||||
"messages.created_at", expireAfterSeconds=900)
|
||||
|
||||
thread.send_text("> Admina activated in thread")
|
||||
return thread_db
|
||||
|
||||
|
||||
def deactivate_thread(thread: fbchat.Thread):
|
||||
thread.send_text("> Admina deactivated in thread")
|
||||
return database.threads.delete_one({"_id": thread.id})
|
||||
|
||||
|
||||
COMMANDS = {
|
||||
"ping": {
|
||||
"description": "Pong!",
|
||||
"usage": "!ping",
|
||||
"admin_only": False,
|
||||
"handler": lambda event: event.thread.send_text("Pong!"),
|
||||
},
|
||||
"activate": {
|
||||
"description": "Activate a thread",
|
||||
"usage": "!activate",
|
||||
"admin_only": False,
|
||||
"handler": lambda event: activate_thread(event.thread),
|
||||
},
|
||||
"deactivate": {
|
||||
"description": "Deactivate a thread",
|
||||
"usage": "!deactivate",
|
||||
"admin_only": True,
|
||||
"handler": lambda event: deactivate_thread(event.thread),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def handle_message(_, event: fbchat.MessageEvent):
|
||||
if not event.message.text:
|
||||
return
|
||||
|
||||
if not database.threads.find_one({"_id": event.thread.id}):
|
||||
if not event.message.text.lower().startswith("!activate"):
|
||||
return
|
||||
|
||||
return COMMANDS["activate"]["handler"](event)
|
||||
|
||||
database.threads.update_one(
|
||||
{"_id": event.thread.id}, {"$push": {
|
||||
"messages": {
|
||||
"id": event.message.id,
|
||||
"role":
|
||||
"assistant" if event.message.author == session.user.id and event.message.text.startswith("#>")
|
||||
else "user" if "@admina" in event.message.text.lower()
|
||||
else None,
|
||||
"author": event.message.author,
|
||||
"created_at": event.message.created_at.timestamp(),
|
||||
"text": event.message.text,
|
||||
"attachments": [{
|
||||
"url": attachment.url,
|
||||
"original_url": attachment.original_url,
|
||||
"title": attachment.title,
|
||||
"description": attachment.description,
|
||||
"source": attachment.source,
|
||||
"image": attachment.image.url if attachment.image else None,
|
||||
"original_image_url": attachment.original_image_url,
|
||||
} for attachment in event.message.attachments]
|
||||
}
|
||||
}})
|
||||
|
||||
logger.info(
|
||||
f"Received message from {event.message.author} in {event.thread.id}"
|
||||
)
|
||||
|
||||
if event.message.text.startswith("!"):
|
||||
command = event.message.text[1:].split(" ")
|
||||
|
||||
if command[0].lower() not in COMMANDS:
|
||||
return
|
||||
|
||||
if COMMANDS[command[0].lower()]["admin_only"]:
|
||||
if not isinstance(event.thread, fbchat.Group):
|
||||
return event.thread.send_text("> This command can only be used in groups")
|
||||
|
||||
if session.user.id not in event.thread.admins:
|
||||
return event.thread.send_text("> Account running the bot must be an admin to use this command")
|
||||
|
||||
if event.message.author not in event.thread.admins:
|
||||
return event.thread.send_text("> You must be an admin to use this command")
|
||||
|
||||
return COMMANDS[command[0]]["handler"](event)
|
||||
|
||||
if "@everyone" in event.message.text.lower():
|
||||
return event.thread.send_text(">TODO: @everyone")
|
||||
|
||||
if "@admina" in event.message.text.lower():
|
||||
return event.thread.send_text(">TODO: @admina")
|
36
src/main.py
36
src/main.py
@@ -1,6 +1,7 @@
|
||||
import fbchat
|
||||
from message import handle_message
|
||||
from handlers import handle_message
|
||||
from util.logger import logger
|
||||
from util.session import session
|
||||
|
||||
import atexit
|
||||
from blinker import Signal
|
||||
@@ -8,43 +9,16 @@ from threading import Thread
|
||||
from os import environ
|
||||
|
||||
|
||||
def on_event(sender, session, event):
|
||||
def on_event(sender, event):
|
||||
match sender:
|
||||
case fbchat.MessageEvent:
|
||||
Thread(target=handle_message, args=(
|
||||
sender, session, event)).start()
|
||||
sender, event)).start()
|
||||
case _:
|
||||
logger.debug("Ignoring event: %s", event)
|
||||
|
||||
|
||||
def main():
|
||||
email = environ.get("FB_EMAIL")
|
||||
password = environ.get("FB_PASSWORD")
|
||||
cookie_path = environ.get("FB_COOKIE_PATH") or "session.json"
|
||||
|
||||
if not email or not password:
|
||||
raise ValueError("FB_EMAIL and FB_PASSWORD must be set")
|
||||
|
||||
try:
|
||||
logger.info("Loading cookies from %s", cookie_path)
|
||||
session = fbchat.Session.from_file(cookie_path)
|
||||
except (FileNotFoundError, fbchat.FacebookError):
|
||||
logger.warning("Failed to load cookies from %s", cookie_path)
|
||||
logger.info("Logging in with email and password")
|
||||
session = fbchat.Session.login(
|
||||
email, password, on_2fa_callback=lambda: input("Input 2FA code: ")
|
||||
)
|
||||
session.to_file(cookie_path)
|
||||
logger.info("Saved cookies to %s", cookie_path)
|
||||
|
||||
finally:
|
||||
if session.is_logged_in():
|
||||
logger.info("Logged in as %s", session.user.id)
|
||||
else:
|
||||
raise ValueError("Failed to log in")
|
||||
|
||||
atexit.register(lambda: session.to_file(cookie_path))
|
||||
|
||||
listener = fbchat.Listener(
|
||||
session=session, chat_on=False, foreground=False)
|
||||
|
||||
@@ -52,7 +26,7 @@ def main():
|
||||
events.connect(on_event)
|
||||
|
||||
for event in listener.listen():
|
||||
events.send(type(event), session=session, event=event)
|
||||
events.send(type(event), event=event)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@@ -1,8 +0,0 @@
|
||||
import fbchat
|
||||
from util.logger import logger
|
||||
from util.database import database
|
||||
from util.session import session
|
||||
|
||||
|
||||
def handle_message(_, event: fbchat.MessageEvent):
|
||||
logger.info("Received message from %s: %s", event.author.id, event.text)
|
@@ -12,4 +12,4 @@ if not MONGO_HOST or not MONGO_PORT or not MONGO_USERNAME or not MONGO_PASSWORD
|
||||
"MONGO_HOST, MONGO_PORT, MONGO_USERNAME, MONGO_PASSWORD and MONGO_DATABASE must be set")
|
||||
|
||||
database = pymongo.MongoClient(MONGO_HOST, int(
|
||||
MONGO_PORT), username=MONGO_USERNAME, password=MONGO_PASSWORD, authSource=MONGO_DATABASE)[MONGO_DATABASE].client
|
||||
MONGO_PORT), username=MONGO_USERNAME, password=MONGO_PASSWORD, authSource=MONGO_DATABASE)[MONGO_DATABASE]
|
||||
|
@@ -16,7 +16,7 @@ logger = logging.getLogger()
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
formatter = logging.Formatter(
|
||||
'%(asctime)s:%(levelname)s:%(name)s:%(module)s:%(funcName)s:%(lineno)s:%(message)s')
|
||||
'%(asctime)s:%(levelname)s:%(module)s:%(funcName)s:%(lineno)s:%(message)s')
|
||||
|
||||
stream_handler = logging.StreamHandler(sys.stdout)
|
||||
stream_handler.setLevel(logging.INFO)
|
||||
|
Reference in New Issue
Block a user