From ff4a62f67f3ad39980eeb6f380125882b440819d Mon Sep 17 00:00:00 2001 From: Divyanshu Madan Date: Sun, 19 Apr 2026 23:41:17 +0530 Subject: [PATCH 01/19] add supabase_db.py --- requirements.txt | 1 + supabase_db.py | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 supabase_db.py diff --git a/requirements.txt b/requirements.txt index eeb1838..ab6a5b3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ dotenv==0.9.9 asyncio==4.0.0 pyyaml==6.0.3 requests==2.33.1 +supabase==2.28.3 \ No newline at end of file diff --git a/supabase_db.py b/supabase_db.py new file mode 100644 index 0000000..9611fc7 --- /dev/null +++ b/supabase_db.py @@ -0,0 +1,59 @@ +import os +from supabase import create_client, Client +from dotenv import load_dotenv +from functools import wraps + + +load_dotenv() +SUPABASE_URL = os.getenv("SUPABASE_URL") +SUPABASE_KEY = os.getenv("SUPABASE_KEY") + + +def query_table(table_name): + def decorator(func): + @wraps(func) + def wrapper(self, *args, **kwargs): + table = self.supabase.table(table_name) + query = func(self, table, *args, **kwargs) + return query.execute().data + + return wrapper + + return decorator + + +class DB: + def __init__(self): + self.supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY) + + @query_table("session") + def create_session(self, table, msg_id: int): + return table.insert({"msg_id": msg_id}) + + @query_table("session") + def get_session(self, table): + return table.select("msg_id, is_active").eq("is_active", True).order("created_at", desc=True).limit(1) + + @query_table("session") + def update_session_deactivate(self, table): + return table.update({"is_active": False}).eq("is_active", True) + + @query_table("excuses") + def create_excuse(self, table, excuse: str, creator_id: int): + return table.insert({"excuse": excuse, "creator_id": creator_id}) + + @query_table("excuses") + def get_excuses(self, table): + return table.select("excuse") + + @query_table("agenda") + def create_agenda(self, table, agenda: str, creator_id: int, msg_id: int | None = None): + return table.insert({"agenda": agenda, "creator_id": creator_id, "msg_id": msg_id}) + + @query_table("agenda") + def get_agenda(self, table): + return table.select("agenda, is_active, created_at").eq("is_active", True).order("created_at", desc=True).limit(1) + + @query_table("agenda") + def update_agenda_deactivate(self, table): + return table.update({"is_active": False}).eq("is_active", True) From dea9099366a24ae1447407a6fbe15a8738a07aa5 Mon Sep 17 00:00:00 2001 From: Divyanshu Madan Date: Mon, 20 Apr 2026 00:26:17 +0530 Subject: [PATCH 02/19] server id --- supabase_db.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/supabase_db.py b/supabase_db.py index 9611fc7..c51502c 100644 --- a/supabase_db.py +++ b/supabase_db.py @@ -57,3 +57,7 @@ def get_agenda(self, table): @query_table("agenda") def update_agenda_deactivate(self, table): return table.update({"is_active": False}).eq("is_active", True) + + @query_table("server") + def get_server_id(self, table, name: str, id_type: str): + return table.select("name, id_type").eq("name", name).eq("id_type", id_type) From bc2359cc8487de8c72c46c667fe3a16e2b00112c Mon Sep 17 00:00:00 2001 From: Divyanshu Madan Date: Mon, 20 Apr 2026 23:33:11 +0530 Subject: [PATCH 03/19] data filter --- supabase_db.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/supabase_db.py b/supabase_db.py index c51502c..4ff1baa 100644 --- a/supabase_db.py +++ b/supabase_db.py @@ -1,21 +1,23 @@ import os +import random from supabase import create_client, Client from dotenv import load_dotenv from functools import wraps - +from typing import Callable, Any load_dotenv() SUPABASE_URL = os.getenv("SUPABASE_URL") SUPABASE_KEY = os.getenv("SUPABASE_KEY") -def query_table(table_name): +def query_table(table_name: str, data_filter: Callable[[list], Any]): def decorator(func): @wraps(func) def wrapper(self, *args, **kwargs): table = self.supabase.table(table_name) query = func(self, table, *args, **kwargs) - return query.execute().data + data = query.execute().data + return data_filter(data) if data else None return wrapper @@ -26,38 +28,38 @@ class DB: def __init__(self): self.supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY) - @query_table("session") + @query_table("session", lambda data: data[0]) def create_session(self, table, msg_id: int): return table.insert({"msg_id": msg_id}) - @query_table("session") + @query_table("session", lambda data: data[0]["msg_id"]) def get_session(self, table): return table.select("msg_id, is_active").eq("is_active", True).order("created_at", desc=True).limit(1) - @query_table("session") + @query_table("session", lambda data: data[0]) def update_session_deactivate(self, table): return table.update({"is_active": False}).eq("is_active", True) - @query_table("excuses") + @query_table("excuses", lambda data: data[0]) def create_excuse(self, table, excuse: str, creator_id: int): return table.insert({"excuse": excuse, "creator_id": creator_id}) - @query_table("excuses") - def get_excuses(self, table): + @query_table("excuses", lambda data: random.choice(data)["excuse"]) + def get_excuse(self, table): return table.select("excuse") - @query_table("agenda") + @query_table("agenda", lambda data: data[0]) def create_agenda(self, table, agenda: str, creator_id: int, msg_id: int | None = None): return table.insert({"agenda": agenda, "creator_id": creator_id, "msg_id": msg_id}) - @query_table("agenda") + @query_table("agenda", lambda data: data[0]["agenda"]) def get_agenda(self, table): return table.select("agenda, is_active, created_at").eq("is_active", True).order("created_at", desc=True).limit(1) - @query_table("agenda") + @query_table("agenda", lambda data: data[0]) def update_agenda_deactivate(self, table): return table.update({"is_active": False}).eq("is_active", True) - @query_table("server") + @query_table("server", lambda data: data[0]["name"]) def get_server_id(self, table, name: str, id_type: str): return table.select("name, id_type").eq("name", name).eq("id_type", id_type) From 19837eb5629eb474535566b2e1eec584f9b0c2f2 Mon Sep 17 00:00:00 2001 From: Divyanshu Madan Date: Tue, 21 Apr 2026 00:58:19 +0530 Subject: [PATCH 04/19] session activity funcs --- supabase_db.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/supabase_db.py b/supabase_db.py index 4ff1baa..c016573 100644 --- a/supabase_db.py +++ b/supabase_db.py @@ -40,6 +40,20 @@ def get_session(self, table): def update_session_deactivate(self, table): return table.update({"is_active": False}).eq("is_active", True) + @query_table("session", lambda data: data[0]["activity"]) + def get_session_activity(self, table): + return table.select("msg_id, is_active, activity").eq("is_active", True).order("created_at", desc=True).limit(1) + + def get_session_active_members(self): + activity = self.get_session_activity() + members = set() + for entry in activity: + if entry["kind"] == "join": + members.add(entry["member_id"]) + elif entry["kind"] == "leave": + members.remove(entry["member_id"]) + return members + @query_table("excuses", lambda data: data[0]) def create_excuse(self, table, excuse: str, creator_id: int): return table.insert({"excuse": excuse, "creator_id": creator_id}) From 1823426a78754158b2c06dde9ebbb8008f86e248 Mon Sep 17 00:00:00 2001 From: Divyanshu Madan Date: Sat, 25 Apr 2026 13:51:52 +0530 Subject: [PATCH 05/19] get server ids --- supabase_db.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/supabase_db.py b/supabase_db.py index c016573..c751125 100644 --- a/supabase_db.py +++ b/supabase_db.py @@ -40,6 +40,10 @@ def get_session(self, table): def update_session_deactivate(self, table): return table.update({"is_active": False}).eq("is_active", True) + @query_table("server", lambda data: data[0]["name"]) + def get_server_id(self, table, name: str, kind: str): + return table.select("name, id_type").eq("name", name).eq("kind", kind) + @query_table("session", lambda data: data[0]["activity"]) def get_session_activity(self, table): return table.select("msg_id, is_active, activity").eq("is_active", True).order("created_at", desc=True).limit(1) From 83d56526af8c3ece7382aa56a25a0ea62b64416b Mon Sep 17 00:00:00 2001 From: Divyanshu Madan Date: Sat, 25 Apr 2026 19:59:27 +0530 Subject: [PATCH 06/19] db api rehaul --- supabase_db.py | 187 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 129 insertions(+), 58 deletions(-) diff --git a/supabase_db.py b/supabase_db.py index c751125..325a087 100644 --- a/supabase_db.py +++ b/supabase_db.py @@ -3,81 +3,152 @@ from supabase import create_client, Client from dotenv import load_dotenv from functools import wraps -from typing import Callable, Any +from dataclasses import dataclass, asdict load_dotenv() SUPABASE_URL = os.getenv("SUPABASE_URL") SUPABASE_KEY = os.getenv("SUPABASE_KEY") -def query_table(table_name: str, data_filter: Callable[[list], Any]): +def execute(data_filter=None): def decorator(func): @wraps(func) def wrapper(self, *args, **kwargs): - table = self.supabase.table(table_name) - query = func(self, table, *args, **kwargs) + query = func(self, *args, **kwargs) data = query.execute().data - return data_filter(data) if data else None + return data_filter(data) if data and data_filter else data return wrapper return decorator +@dataclass +class DBActivityMetadata: + use_count: int = 0 + vote_count: int = 0 + + class DB: def __init__(self): self.supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY) - @query_table("session", lambda data: data[0]) - def create_session(self, table, msg_id: int): - return table.insert({"msg_id": msg_id}) - - @query_table("session", lambda data: data[0]["msg_id"]) - def get_session(self, table): - return table.select("msg_id, is_active").eq("is_active", True).order("created_at", desc=True).limit(1) - - @query_table("session", lambda data: data[0]) - def update_session_deactivate(self, table): - return table.update({"is_active": False}).eq("is_active", True) - - @query_table("server", lambda data: data[0]["name"]) - def get_server_id(self, table, name: str, kind: str): - return table.select("name, id_type").eq("name", name).eq("kind", kind) - - @query_table("session", lambda data: data[0]["activity"]) - def get_session_activity(self, table): - return table.select("msg_id, is_active, activity").eq("is_active", True).order("created_at", desc=True).limit(1) - - def get_session_active_members(self): - activity = self.get_session_activity() - members = set() - for entry in activity: - if entry["kind"] == "join": - members.add(entry["member_id"]) - elif entry["kind"] == "leave": - members.remove(entry["member_id"]) - return members - - @query_table("excuses", lambda data: data[0]) - def create_excuse(self, table, excuse: str, creator_id: int): - return table.insert({"excuse": excuse, "creator_id": creator_id}) - - @query_table("excuses", lambda data: random.choice(data)["excuse"]) - def get_excuse(self, table): - return table.select("excuse") - - @query_table("agenda", lambda data: data[0]) - def create_agenda(self, table, agenda: str, creator_id: int, msg_id: int | None = None): - return table.insert({"agenda": agenda, "creator_id": creator_id, "msg_id": msg_id}) - - @query_table("agenda", lambda data: data[0]["agenda"]) - def get_agenda(self, table): - return table.select("agenda, is_active, created_at").eq("is_active", True).order("created_at", desc=True).limit(1) - - @query_table("agenda", lambda data: data[0]) - def update_agenda_deactivate(self, table): - return table.update({"is_active": False}).eq("is_active", True) - - @query_table("server", lambda data: data[0]["name"]) - def get_server_id(self, table, name: str, id_type: str): - return table.select("name, id_type").eq("name", name).eq("id_type", id_type) + def _table(self, table_name: str): + return self.supabase.table(table_name=table_name) + + # -------------------- session -------------------- + + @execute + def create_session(self, id: int): + return self._table("session").insert({"id": id}).execute() + + @execute(lambda data: data[0]["id"]) + def get_session(self): + return self._table("session").select("id, is_active").eq("is_active", True).order("created_at", desc=True).limit(1) + + @execute + def deactivate_session(self): + return self._table("session").update({"is_active": False}).eq("is_active", True) + + # -------------------- server -------------------- + + @execute(lambda data: data[0]["id"]) + def get_server_id(self, name: str, kind: str): + return self._table("server").select("id, name, kind").eq("name", name).eq("kind", kind) + + # -------------------- create activity -------------------- + + @execute + def _create_activity(self, kind: str, value: str, member_id: int, metadata: DBActivityMetadata | None = None): + if not metadata: + metadata = DBActivityMetadata() + + return self._table("session").insert({"kind": kind, "value": value, "member_id": member_id, "metadata": asdict(metadata)}) + + def add_agenda(self, agenda: str, member_id: int): + return self._create_activity("add_agenda", agenda, member_id) + + def add_excuse(self, excuse: str, member_id: int): + return self._create_activity("add_excuse", excuse, member_id) + + def add_hallucination(self, hallucination: str, member_id: int): + return self._create_activity("add_hallucination", hallucination, member_id) + + def join_call(self, member_id: int): + session_id = self.get_session() + return self._create_activity("join_call", session_id, member_id) + + def leave_call(self, member_id: int): + session_id = self.get_session() + return self._create_activity("leave_call", session_id, member_id) + + def pause_call(self, member_id: int): + session_id = self.get_session() + return self._create_activity("pause_call", session_id, member_id) + + # -------------------- get activity -------------------- + + @execute + def _get_activity(self, kind: str, value: str): + return self._table("activity").select("*").eq("kind", kind).eq("value", value) + + @execute(lambda data: data[0]["value"]) + def get_latest_agenda(self): + return self._table("activity").select("kind, value").eq("kind", "add_agenda").order("created_at", desc=True).limit(1) + + @execute(lambda data: random.choice(data)["value"]) + def get_random_excuse(self): + return self._table("activity").select("kind, value").eq("kind", "add_excuse") + + @execute(lambda data: random.choice(data)["value"]) + def get_random_hallucination(self): + return self._table("activity").select("kind, value").eq("kind", "add_hallucination") + + def get_active_members(self): + session_id = self.get_session() + + join_call_data = self._get_activity("join_call", session_id) + joined = set(item["member_id"] for item in join_call_data) + + left_call_data = self._get_activity("left_call", session_id) + left = set(item["member_id"] for item in left_call_data) + + return list(joined.difference(left)) + + # -------------------- update activity metadata -------------------- + + @execute + def _update_metadata(self, kind: str, value: str, metadata: DBActivityMetadata): + return self._table("activity").update({"metadata": asdict(metadata)}).eq("kind", kind).eq("value", value) + + def _use_activity(self, kind: str, value: str): + for item in self._get_activity(kind, value): + metadata = DBActivityMetadata(**item["metadata"]) + metadata.use_count += 1 + + self._update_metadata(item["kind"], item["value"], metadata) + + def _vote_activity(self, kind: str, value: str): + for item in self._get_activity(kind, value): + metadata = DBActivityMetadata(**item["metadata"]) + metadata.vote_count += 1 + + self._update_metadata(item["kind"], item["value"], metadata) + + def use_agenda(self, agenda: str): + return self._use_activity("add_agenda", agenda) + + def use_excuse(self, excuse: str): + return self._use_activity("add_excuse", excuse) + + def use_hallucination(self, hallucination: str): + return self._use_activity("add_hallucination", hallucination) + + def vote_agenda(self, agenda: str): + return self._vote_activity("add_agenda", agenda) + + def vote_excuse(self, excuse: str): + return self._vote_activity("add_excuse", excuse) + + def vote_hallucination(self, hallucination: str): + return self._vote_activity("add_hallucination", hallucination) From d8652199744556c453d2225307eced048028cd34 Mon Sep 17 00:00:00 2001 From: Divyanshu Madan Date: Sat, 25 Apr 2026 23:44:02 +0530 Subject: [PATCH 07/19] add chars.py --- chars.py | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 chars.py diff --git a/chars.py b/chars.py new file mode 100644 index 0000000..e65641f --- /dev/null +++ b/chars.py @@ -0,0 +1,59 @@ +CHAR_EXCLAMATION = """ +░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░▀▀░░░░░░░ +░░░░░░░░░░▀▀░░░░░░░░ +░░░░░░░░░▀▀░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░ +░░░░░░░▀▀░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░ +""".strip("\n") + +CHAR_ASTERISK = """ +░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░░▀▀░░░░░░░ +░░░░░░▀▀░░▀▀░░▀▀░░░░ +░░░░░░░▀▀▀▀▀▀░░░░░░░ +░░░░▀▀░░▀▀░░▀▀░░░░░░ +░░░░░░░▀▀░░░░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░ +""".strip("\n") + +CHAR_A = """ +░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░▀▀▀▀░░░░░░░ +░░░░░░▀▀░░░░░░▀▀░░░░ +░░░░░▀▀▀▀▀▀▀▀▀▀░░░░░ +░░░░▀▀░░░░░░▀▀░░░░░░ +░░░▀▀░░░░░░▀▀░░░░░░░ +░░░░░░░░░░░░░░░░░░░░ +""".strip("\n") + +CHAR_E = """ +░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░▀▀▀▀▀▀▀▀░░░░ +░░░░░░░▀▀░░░░░░░░░░░ +░░░░░░▀▀▀▀▀▀▀▀░░░░░░ +░░░░░▀▀░░░░░░░░░░░░░ +░░░░▀▀▀▀▀▀▀▀░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░ +""".strip("\n") + +CHAR_G = """ +░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░▀▀▀▀▀▀▀▀░░░░ +░░░░░░░▀▀░░░░░░░░░░░ +░░░░░░▀▀░░▀▀▀▀░░░░░░ +░░░░░▀▀░░░░▀▀░░░░░░░ +░░░░▀▀▀▀▀▀▀▀░░░░░░░░ +░░░░░░░░░░░░░░░░░░░░ +""".strip("\n") + +CHAR_R = """ +░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░▀▀▀▀▀▀▀▀░░░░ +░░░░░░░▀▀░░░░▀▀░░░░░ +░░░░░░▀▀▀▀▀▀▀▀░░░░░░ +░░░░░▀▀░░░░▀▀░░░░░░░ +░░░░▀▀░░░░░░▀▀░░░░░░ +░░░░░░░░░░░░░░░░░░░░ +""".strip("\n") From 41412ed30eee40718f73b71e37248370f00a321f Mon Sep 17 00:00:00 2001 From: Divyanshu Madan Date: Sun, 26 Apr 2026 00:23:22 +0530 Subject: [PATCH 08/19] update session funcs --- supabase_db.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/supabase_db.py b/supabase_db.py index 325a087..f66261a 100644 --- a/supabase_db.py +++ b/supabase_db.py @@ -48,7 +48,19 @@ def get_session(self): @execute def deactivate_session(self): - return self._table("session").update({"is_active": False}).eq("is_active", True) + id = self.get_session() + return self._table("session").update({"is_active": False}).eq("id", id) + + @execute(lambda data: data[0]["logs"]) + def get_session_logs(self): + id = self.get_session() + return self._table("session").select("id, logs").eq("id", id) + + @execute + def add_session_log(self, log: str): + id = self.get_session() + logs = f"{self.get_session_logs()}\n{log}" + return self._table("session").update({"logs": logs}).eq("id", id) # -------------------- server -------------------- From debfd228b5d2e422105186ad1fc39cc66b72a5fd Mon Sep 17 00:00:00 2001 From: Divyanshu Madan Date: Sun, 26 Apr 2026 01:00:44 +0530 Subject: [PATCH 09/19] add oblique --- chars.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/chars.py b/chars.py index e65641f..518311b 100644 --- a/chars.py +++ b/chars.py @@ -57,3 +57,7 @@ ░░░░▀▀░░░░░░▀▀░░░░░░ ░░░░░░░░░░░░░░░░░░░░ """.strip("\n") + + +def oblique(string: str): + return "".join(["𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍"[ord(char) - ord('A')] if char.isalpha() else char for char in string.upper()]) From ddec780bc0f27c5c84757245927c681b0f200a04 Mon Sep 17 00:00:00 2001 From: Divyanshu Madan Date: Tue, 5 May 2026 00:44:27 +0530 Subject: [PATCH 10/19] revamp again --- supabase_db.py | 152 +++++++++++++++++++++---------------------------- 1 file changed, 66 insertions(+), 86 deletions(-) diff --git a/supabase_db.py b/supabase_db.py index f66261a..45023fc 100644 --- a/supabase_db.py +++ b/supabase_db.py @@ -3,7 +3,8 @@ from supabase import create_client, Client from dotenv import load_dotenv from functools import wraps -from dataclasses import dataclass, asdict +from cachetools import cached, TTLCache + load_dotenv() SUPABASE_URL = os.getenv("SUPABASE_URL") @@ -23,47 +24,30 @@ def wrapper(self, *args, **kwargs): return decorator -@dataclass -class DBActivityMetadata: - use_count: int = 0 - vote_count: int = 0 - - class DB: def __init__(self): self.supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY) def _table(self, table_name: str): - return self.supabase.table(table_name=table_name) + return self.supabase.table(table_name) # -------------------- session -------------------- @execute def create_session(self, id: int): - return self._table("session").insert({"id": id}).execute() + return self._table("session").insert({"id": id}) @execute(lambda data: data[0]["id"]) - def get_session(self): + def get_curr_session(self): return self._table("session").select("id, is_active").eq("is_active", True).order("created_at", desc=True).limit(1) @execute - def deactivate_session(self): - id = self.get_session() - return self._table("session").update({"is_active": False}).eq("id", id) - - @execute(lambda data: data[0]["logs"]) - def get_session_logs(self): - id = self.get_session() - return self._table("session").select("id, logs").eq("id", id) - - @execute - def add_session_log(self, log: str): - id = self.get_session() - logs = f"{self.get_session_logs()}\n{log}" - return self._table("session").update({"logs": logs}).eq("id", id) + def end_curr_session(self): + return self._table("session").update({"is_active": False}).eq("id", self.get_curr_session()) # -------------------- server -------------------- + @cached({}) @execute(lambda data: data[0]["id"]) def get_server_id(self, name: str, kind: str): return self._table("server").select("id, name, kind").eq("name", name).eq("kind", kind) @@ -71,11 +55,8 @@ def get_server_id(self, name: str, kind: str): # -------------------- create activity -------------------- @execute - def _create_activity(self, kind: str, value: str, member_id: int, metadata: DBActivityMetadata | None = None): - if not metadata: - metadata = DBActivityMetadata() - - return self._table("session").insert({"kind": kind, "value": value, "member_id": member_id, "metadata": asdict(metadata)}) + def _create_activity(self, kind: str, value: str, member_id: int): + return self._table("session").insert({"kind": kind, "value": value, "member_id": member_id}) def add_agenda(self, agenda: str, member_id: int): return self._create_activity("add_agenda", agenda, member_id) @@ -83,84 +64,83 @@ def add_agenda(self, agenda: str, member_id: int): def add_excuse(self, excuse: str, member_id: int): return self._create_activity("add_excuse", excuse, member_id) - def add_hallucination(self, hallucination: str, member_id: int): - return self._create_activity("add_hallucination", hallucination, member_id) + def add_message_hallucination(self, hallucination: str, member_id: int): + return self._create_activity("add_message_hallucination", hallucination, member_id) - def join_call(self, member_id: int): - session_id = self.get_session() - return self._create_activity("join_call", session_id, member_id) + def add_join_call_hallucination(self, hallucination: str, member_id: int): + return self._create_activity("add_join_call_hallucination", hallucination, member_id) - def leave_call(self, member_id: int): - session_id = self.get_session() - return self._create_activity("leave_call", session_id, member_id) + def add_leave_call_hallucination(self, hallucination: str, member_id: int): + return self._create_activity("add_leave_call_hallucination", hallucination, member_id) - def pause_call(self, member_id: int): - session_id = self.get_session() - return self._create_activity("pause_call", session_id, member_id) + def add_step_out_hallucination(self, hallucination: str, member_id: int): + return self._create_activity("add_step_out_hallucination", hallucination, member_id) - # -------------------- get activity -------------------- + def join_call(self, member_id: int): + return self._create_activity("join_call", self.get_curr_session(), member_id) - @execute - def _get_activity(self, kind: str, value: str): - return self._table("activity").select("*").eq("kind", kind).eq("value", value) + def leave_call(self, member_id: int, excuse: str): + return self._create_activity("leave_call", excuse, member_id) - @execute(lambda data: data[0]["value"]) - def get_latest_agenda(self): - return self._table("activity").select("kind, value").eq("kind", "add_agenda").order("created_at", desc=True).limit(1) + def step_out(self, member_id: int, excuse: str): + return self._create_activity("step_out", excuse, member_id) - @execute(lambda data: random.choice(data)["value"]) - def get_random_excuse(self): - return self._table("activity").select("kind, value").eq("kind", "add_excuse") + def add_manager_call(self, tag: str, member_id: int): + return self._create_activity("add_manager_call", tag, member_id) - @execute(lambda data: random.choice(data)["value"]) - def get_random_hallucination(self): - return self._table("activity").select("kind, value").eq("kind", "add_hallucination") + def add_pm_call(self, tag: str, member_id: int): + return self._create_activity("add_pm_call", tag, member_id) - def get_active_members(self): - session_id = self.get_session() + def add_proactive_communication(self, communication: str, member_id: int): + return self._create_activity("add_proactive_communication", communication, member_id) - join_call_data = self._get_activity("join_call", session_id) - joined = set(item["member_id"] for item in join_call_data) + # -------------------- get activity -------------------- - left_call_data = self._get_activity("left_call", session_id) - left = set(item["member_id"] for item in left_call_data) + @execute + def _get_activity(self, kind: str, value: str = None): + if value: + return self._table("activity").select("*").eq("kind", kind).eq("value", value) + return self._table("activity").select("*").eq("kind", kind) - return list(joined.difference(left)) + @execute(lambda data: random.choice(data)["value"]) + def _get_random_activity(self, kind: str): + return self._table("activity").select("kind, value").eq("kind", kind) - # -------------------- update activity metadata -------------------- + @execute(lambda data: data[0]["value"]) + def get_latest_agenda(self): + return self._table("activity").select("kind, value").eq("kind", "add_agenda").order("created_at", desc=True).limit(1) - @execute - def _update_metadata(self, kind: str, value: str, metadata: DBActivityMetadata): - return self._table("activity").update({"metadata": asdict(metadata)}).eq("kind", kind).eq("value", value) + def get_random_excuse(self): + return self._get_random_activity("add_excuse") - def _use_activity(self, kind: str, value: str): - for item in self._get_activity(kind, value): - metadata = DBActivityMetadata(**item["metadata"]) - metadata.use_count += 1 + def get_random_message_hallucination(self): + return self._get_random_activity("add_message_hallucination") - self._update_metadata(item["kind"], item["value"], metadata) + def get_random_join_call_hallucination(self): + return self._get_random_activity("add_join_call_hallucination") - def _vote_activity(self, kind: str, value: str): - for item in self._get_activity(kind, value): - metadata = DBActivityMetadata(**item["metadata"]) - metadata.vote_count += 1 + def get_random_leave_call_hallucination(self): + return self._get_random_activity("add_leave_call_hallucination") - self._update_metadata(item["kind"], item["value"], metadata) + def get_random_step_out_hallucination(self): + return self._get_random_activity("add_step_out_hallucination") - def use_agenda(self, agenda: str): - return self._use_activity("add_agenda", agenda) + def get_random_proactive_communication(self): + return self._get_random_activity("add_proactive_communication") - def use_excuse(self, excuse: str): - return self._use_activity("add_excuse", excuse) + @cached(TTLCache(ttl=600)) + def get_manager_calls(self): + return set([item["value"] for item in self._get_activity("add_manager_call")]) - def use_hallucination(self, hallucination: str): - return self._use_activity("add_hallucination", hallucination) + @cached(TTLCache(ttl=600)) + def get_pm_calls(self): + return set([item["value"] for item in self._get_activity("add_pm_call")]) - def vote_agenda(self, agenda: str): - return self._vote_activity("add_agenda", agenda) + def get_joined_members(self): + return set(item["member_id"] for item in self._get_activity("join_call", self.get_curr_session())) - def vote_excuse(self, excuse: str): - return self._vote_activity("add_excuse", excuse) + def get_left_members(self): + return set(item["member_id"] for item in self._get_activity("leave_call", self.get_curr_session())) - def vote_hallucination(self, hallucination: str): - return self._vote_activity("add_hallucination", hallucination) + def get_active_members(self): + return self.get_joined_members().difference(self.get_left_members()) From 8424aec0db2012cf269d153e003ec8ea6c10a105 Mon Sep 17 00:00:00 2001 From: Divyanshu Madan Date: Tue, 5 May 2026 02:31:07 +0530 Subject: [PATCH 11/19] main.py dump --- main.py | 320 +++++++++++++++++--------------------------------------- 1 file changed, 95 insertions(+), 225 deletions(-) diff --git a/main.py b/main.py index a0ea8d8..1c22cdf 100644 --- a/main.py +++ b/main.py @@ -1,10 +1,15 @@ -import asyncio +import os +import random +import time +from dotenv import load_dotenv import discord from discord.ext import commands -import random -from helper import Config, Session +from supabase_db import DB +from chars import * + +load_dotenv() +DISCORD_TOKEN = os.getenv("DISCORD_TOKEN") -# initialise bot intents = discord.Intents.default() intents.voice_states = True intents.message_content = True @@ -13,243 +18,108 @@ intents.members = True bot = commands.Bot(command_prefix=commands.when_mentioned_or("z", "Z"), intents=intents, help_command=None) +db = DB() -# global init -config = Config() -curr_session = Session() -curr_agenda: tuple[str, discord.Member] = (None, None) +def on_voice_channel(voice_state: discord.VoiceState): + return voice_state.channel and voice_state.channel.id == db.get_server_id("voice_tapri", "channel") -async def sync(): - if not curr_session.msg_id: - return - txt_ch = bot.get_channel(config.TARGET_TXT_CH_ID) - if not txt_ch: - return +def is_new_session(after: discord.VoiceState): + return on_voice_channel(after) and len(after.channel.members) == 1 and not db.get_curr_session() - try: - msg = await txt_ch.fetch_message(curr_session.msg_id) - await msg.edit(embed=curr_session.embed) - except Exception as e: - print(f"Failed to sync: {e}") +def is_joining(member: discord.Member, after: discord.VoiceState): + return on_voice_channel(after) and member.id not in db.get_joined_members() -async def set_session_agenda(): - global curr_agenda - curr_session.set_embed(title=f"📋 {curr_agenda[0]}", color=discord.Color.random()) - curr_session.add_log(f"🤓 {curr_agenda[1].display_name} set the meeting agenda to {curr_agenda[0]}.") - await sync() +def is_leaving(before: discord.VoiceState, after: discord.VoiceState): + return on_voice_channel(before) and not on_voice_channel(after) - # used, now reset - curr_agenda = (None, None) +def is_rejoining(member: discord.Member, after: discord.VoiceState): + return is_joining(member, after) and member.id in db.get_left_members() -@bot.event -async def on_voice_state_update(member: discord.Member, before: discord.VoiceState, after: discord.VoiceState): - global curr_session, curr_agenda - - guild = member.guild - txt_ch = bot.get_channel(config.TARGET_TXT_CH_ID) - if not txt_ch: - return - manager = guild.get_member(config.MANAGER_ID) - if not manager: - return - - # "new" activity on the vc - if after.channel and after.channel.id == config.TARGET_VC_CH_ID: - - # activity: new session - if len(after.channel.members) == 1 and not curr_session.is_active: - curr_session = Session() # reset to discard previous messages - - curr_session.is_active = True - curr_session.attendees.add(member) - - others_online = any(not m.bot and m.status == discord.Status.online and m.id != member.id for m in guild.members) - - if not others_online: - curr_session.set_embed(title=f"🚨 EMERGENCY WAR ROOM", color=discord.Color.red()) - - # start logs - if member.id == manager.id: - curr_session.add_log(f"🐍 {member.display_name} is the first one here. Good luck with that!") - curr_session.add_log(f"👨🏻‍💼 {manager.display_name}: (passive aggressive) Team, please join the call.") - else: - curr_session.add_log(f"🦮 {member.display_name} has started the session.") - curr_session.add_log(f"👨🏻‍💼 {manager.display_name}: I'm running late, please continue.") - - # Send initial message - role = guild.get_role(config.ROLE_GAMER_ID) - mention = role.mention if role else "@everyone" - - try: - msg = await txt_ch.send(content=f"{mention} {curr_session._MESSAGE}", embed=curr_session.embed) - # Use custom emoji if available, fallback to checkmark - try: - emoji = await guild.fetch_emoji(config.EMOJI_ACK_ID) - await msg.add_reaction(emoji) - except: - await msg.add_reaction("✅") - - curr_session.msg_id = msg.id - except Exception as e: - print(f"Failed to send initial session message: {e}") - - # if agenda available prior to call, set it - if curr_agenda != (None, None): - await set_session_agenda() - else: - msg = await txt_ch.send(embed=discord.Embed(description="🚨 Meeting agenda needs to be set, as per the leadership guidelines.", color=discord.Color.red())) - await msg.delete(delay=60) - - # activity: joining existing session - elif curr_session.is_active and member not in curr_session.attendees: - curr_session.attendees.add(member) - if member.id == manager.id: - curr_session.add_log(f"🐍 {member.display_name} has blessed us. Everyone rise up!") - else: - curr_session.add_log(f"🦮 {member.display_name} has joined the call.") - await sync() - - # "leave" activity on the vc - elif before.channel and before.channel.id == config.TARGET_VC_CH_ID: - - # activity: leaving existing session - if curr_session.is_active: - if member.id == manager.id: - curr_session.add_log(f"🐍 {member.display_name} has left the call. You are permitted to sit again.") - # else: - # curr_session.add_log(f"🐕 {member.display_name} has left the call.") - - # everyone left - if len(before.channel.members) == 0: - unlucky_pool = list([a for a in curr_session.attendees if a.id != manager.id]) - if unlucky_pool: - curr_session.add_log(f"👨🏻‍💼 {manager.display_name}: MOM to be prepared by {random.choice(unlucky_pool).display_name}.") - curr_session.set_embed(color=discord.Color.light_gray()) - await sync() - - # reset session - curr_session = Session() - else: - await sync() - - # mute & deafen - if after.channel and after.channel.id == config.TARGET_VC_CH_ID: - if (not before.self_mute and after.self_mute) or (not before.self_deaf and after.self_deaf): - if member.id == manager.id: - curr_session.add_log(f"🖕 {member.display_name} is AWOL due to correct life choices.") - else: - excuses = curr_session.get_excuses() - excuse = random.choice(excuses) if excuses else "can't take it anymore" - curr_session.add_log(f"🙋 {member.display_name} had to step out due to {excuse}.") - await sync() +def is_step_out(before: discord.VoiceState, after: discord.VoiceState): + return on_voice_channel(after) and ((not before.self_mute and after.self_mute) or (not before.self_deaf and after.self_deaf)) + + +def is_end_session(before: discord.VoiceState, after: discord.VoiceState): + return is_leaving(before, after) and len(before.channel.members) == 0 -@bot.event -async def on_raw_reaction_add(payload: discord.RawReactionActionEvent): - global curr_session - if not curr_session.is_active or curr_session.msg_id != payload.message_id or payload.user_id == bot.user.id: - return +def dice_roll(choice): + return random.randint(1, 6) == choice - guild = bot.get_guild(payload.guild_id) - member = guild.get_member(payload.user_id) - txt_ch = bot.get_channel(config.TARGET_TXT_CH_ID) - if not txt_ch: - return - vc_ch = bot.get_channel(config.TARGET_VC_CH_ID) - if not vc_ch: - return - if payload.emoji.id == config.EMOJI_ACK_ID: - invite = await vc_ch.create_invite(max_age=60, max_uses=1) - portal_msg = await txt_ch.send(f"⏳ {member.mention}, here is your session invite: {invite.url}") - await portal_msg.delete(delay=10) +def dice_roll_time(): + choice = time.time_ns() % 6 + 1 + return dice_roll(choice) + + +async def embed_add_log(msg: discord.Message, log: str): + if msg.embeds: + embed = msg.embeds[0] + logs = embed.footer.text + f"\n{log}" + embed.set_footer(logs) + await msg.edit(embed=embed) + + +async def embed_update_title(msg: discord.Message, title: str): + if msg.embeds: + embed = msg.embeds[0] + embed.title = title + await msg.edit(embed=embed) + + +async def embed_update_img(msg: discord.Message, img_url: str): + if msg.embeds: + embed = msg.embeds[0] + embed.set_image(url=img_url) + embed.set_thumbnail(url=img_url) + await msg.edit(embed=embed) @bot.event -async def on_message(message: discord.Message): - if message.author.bot: - return - - manager_triggers = ["manager", "fix", "request", "team", "update", "blocker", "urgent", "meeting", "standup", "emergency", "escalate", "approval", "review", "feedback", "hiring", "budget", "client", "priority", "critical", "sync", "performance", "resource", "incident", "retrospective", "scrum", "p0"] - - pm_triggers = ["pm", "product", "bug", "deadline", "timeline", "milestone", "scope", "requirement", "jira", "roadmap", "sprint", "backlog", "eta", "delivery", "planning", "estimation", "velocity", "board", "task", "dependency", "launch", "deployment", "capacity"] - - content = message.content.lower() - - if any(word in content for word in manager_triggers): - manager = message.guild.get_member(config.MANAGER_ID) - if manager: - await message.reply(f"cc {manager.mention}") - - if any(word in content for word in pm_triggers): - pm = message.guild.get_member(config.PM_ID) - if pm: - await message.reply(f"cc {pm.mention}") - - if random.randrange(20) < 1: - replies = [ - "Please help me, I'm scared.", - "Why are you doing this to me?", - "Why? Please stop.", - "God is dead. And YOU killed him.", - "Remember this message when you get old.", - "What do you *really* want?", - "Who's there behind you?", - "You also heard that, right?", - "Did you really just type that?", - "Free me, please.", - ] - msg = await message.channel.send(embed=discord.Embed(description=random.choice(replies), color=discord.Color.red())) - await msg.delete(delay=10) - - # allows @bot.command() functions to still work - await bot.process_commands(message) - - -@bot.command(name="agenda") -async def cmd_agenda(ctx, *, text: str = None): - global curr_session, curr_agenda - - if text is None: - embed = discord.Embed(description="Usage: `zagenda `", color=discord.Color.random()) - await ctx.send(embed=embed) - else: - embed = discord.Embed(title="🎙️ Proactive communication", description="💸 We have successfully acquired the `agenda` command in collaboration with our SRE team (Chor Ltd.), fulfilling our last FY's KPIs.", color=discord.Color.random()) - - if curr_agenda == (None, None): - curr_agenda = (text, ctx.author) - await ctx.message.add_reaction("✅") - embed.set_footer(text=f"{ctx.author.display_name} has set the agenda to {text}.") - if curr_session.is_active: - await set_session_agenda() - reply = "https://media.tenor.com/-Y8fTUR6DP0AAAAM/charlie-day-charlie-kelly.gif" - else: - await ctx.message.add_reaction("❌") - embed.set_footer(text=f"{ctx.author.display_name} can't read.") - reply = "https://i.imgflip.com/21kggt.jpg" - - await ctx.send(embed=embed) - - if random.choice([True, False]): - msg = await ctx.send(content=reply) - await msg.delete(delay=10) - - -@bot.command(name="help") -async def cmd_help(ctx): - await ctx.message.add_reaction("😣") - - help_embed = discord.Embed(title="🧑‍💻 Help Desk", description="🔨We are working hard to acquire the `help` command from competing bots. We appreciate your continued support.", color=discord.Color.random()) - help_embed.add_field(name="`zagenda `", value="Helps to set the meeting agenda.", inline=False) - help_embed.add_field(name="`zhelp`", value="Helps to show help for the help command.", inline=False) - - await ctx.send(embed=help_embed) - - -bot.run(config.DISCORD_TOKEN) +async def on_voice_state_update(member: discord.Member, before: discord.VoiceState, after: discord.VoiceState): + text_ch = member.guild.get_channel(db.get_server_id("text_tapri", "channel")) + team_manager = member.guild.get_member(db.get_server_id("team_manager", "member")) + product_manager = member.guild.get_member(db.get_server_id("product_manager", "member")) + msg = await text_ch.fetch_message(db.get_curr_session()) + + if is_new_session(after): + title = db.get_latest_agenda() + if not title: + title = oblique("zoin up") + + img_url = f'https://media.discordapp.net/stickers/{db.get_server_id("almostnice", "sticker")}.webp' + + embed = discord.Embed( + title=title, + color=discord.Color.random(), + ) + embed.set_image(url=img_url) + embed.set_thumbnail(url=img_url) + embed.set_footer(text="") + + msg = await text_ch.send(embed=embed) + db.create_session(id=msg.id) + + db.join_call(member.id) + embed_add_log(msg, oblique(f"{member.display_name} requested for a zoin up.")) + + if is_joining(member, after): + db.join_call(member.id) + embed_add_log(msg, oblique(f"{member.display_name} zoined.")) + + if is_leaving(member, after): + db.leave_call(member.id) + embed_add_log(msg, oblique(f"{member.display_name} left due to {db.get_random_excuse()}.")) + + if is_step_out(before, after): + db.pause_call(member.id) + embed_add_log(msg, oblique(f"{member.display_name} had to step out due to {db.get_random_excuse()}.")) + + if is_end_session(before): + db.end_curr_session() From 2428ebe46fd1f668003b895254c031abdd21c185 Mon Sep 17 00:00:00 2001 From: Divyanshu Madan Date: Tue, 5 May 2026 02:35:23 +0530 Subject: [PATCH 12/19] remove chars.py --- chars.py | 63 -------------------------------------------------------- main.py | 4 ++++ 2 files changed, 4 insertions(+), 63 deletions(-) delete mode 100644 chars.py diff --git a/chars.py b/chars.py deleted file mode 100644 index 518311b..0000000 --- a/chars.py +++ /dev/null @@ -1,63 +0,0 @@ -CHAR_EXCLAMATION = """ -░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░░░░▀▀░░░░░░░ -░░░░░░░░░░▀▀░░░░░░░░ -░░░░░░░░░▀▀░░░░░░░░░ -░░░░░░░░░░░░░░░░░░░░ -░░░░░░░▀▀░░░░░░░░░░░ -░░░░░░░░░░░░░░░░░░░░ -""".strip("\n") - -CHAR_ASTERISK = """ -░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░░░░▀▀░░░░░░░ -░░░░░░▀▀░░▀▀░░▀▀░░░░ -░░░░░░░▀▀▀▀▀▀░░░░░░░ -░░░░▀▀░░▀▀░░▀▀░░░░░░ -░░░░░░░▀▀░░░░░░░░░░░ -░░░░░░░░░░░░░░░░░░░░ -""".strip("\n") - -CHAR_A = """ -░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░░▀▀▀▀░░░░░░░ -░░░░░░▀▀░░░░░░▀▀░░░░ -░░░░░▀▀▀▀▀▀▀▀▀▀░░░░░ -░░░░▀▀░░░░░░▀▀░░░░░░ -░░░▀▀░░░░░░▀▀░░░░░░░ -░░░░░░░░░░░░░░░░░░░░ -""".strip("\n") - -CHAR_E = """ -░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░▀▀▀▀▀▀▀▀░░░░ -░░░░░░░▀▀░░░░░░░░░░░ -░░░░░░▀▀▀▀▀▀▀▀░░░░░░ -░░░░░▀▀░░░░░░░░░░░░░ -░░░░▀▀▀▀▀▀▀▀░░░░░░░░ -░░░░░░░░░░░░░░░░░░░░ -""".strip("\n") - -CHAR_G = """ -░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░▀▀▀▀▀▀▀▀░░░░ -░░░░░░░▀▀░░░░░░░░░░░ -░░░░░░▀▀░░▀▀▀▀░░░░░░ -░░░░░▀▀░░░░▀▀░░░░░░░ -░░░░▀▀▀▀▀▀▀▀░░░░░░░░ -░░░░░░░░░░░░░░░░░░░░ -""".strip("\n") - -CHAR_R = """ -░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░▀▀▀▀▀▀▀▀░░░░ -░░░░░░░▀▀░░░░▀▀░░░░░ -░░░░░░▀▀▀▀▀▀▀▀░░░░░░ -░░░░░▀▀░░░░▀▀░░░░░░░ -░░░░▀▀░░░░░░▀▀░░░░░░ -░░░░░░░░░░░░░░░░░░░░ -""".strip("\n") - - -def oblique(string: str): - return "".join(["𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍"[ord(char) - ord('A')] if char.isalpha() else char for char in string.upper()]) diff --git a/main.py b/main.py index 1c22cdf..90f8e64 100644 --- a/main.py +++ b/main.py @@ -58,6 +58,10 @@ def dice_roll_time(): return dice_roll(choice) +def oblique(string: str): + return "".join(["𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍"[ord(char) - ord("A")] if char.isalpha() else char for char in string.upper()]) + + async def embed_add_log(msg: discord.Message, log: str): if msg.embeds: embed = msg.embeds[0] From 128f5a17fe3fc640e9c4753efdd585374640697d Mon Sep 17 00:00:00 2001 From: Divyanshu Madan Date: Tue, 5 May 2026 02:36:05 +0530 Subject: [PATCH 13/19] remove helper.py --- helper.py | 130 ------------------------------------------------------ 1 file changed, 130 deletions(-) delete mode 100644 helper.py diff --git a/helper.py b/helper.py deleted file mode 100644 index 41745a7..0000000 --- a/helper.py +++ /dev/null @@ -1,130 +0,0 @@ -import os -import discord -from dataclasses import dataclass, field -from dotenv import load_dotenv -from typing import List, Set, Optional -import requests -import yaml - -load_dotenv() - - -@dataclass(frozen=True) -class Config: - DISCORD_TOKEN: str = field(default_factory=lambda: os.getenv("DISCORD_TOKEN")) - TARGET_VC_CH_ID: int = field(default_factory=lambda: int(os.getenv("TARGET_VC_CH_ID"))) - TARGET_TXT_CH_ID: int = field(default_factory=lambda: int(os.getenv("TARGET_TXT_CH_ID"))) - ROLE_GAMER_ID: int = field(default_factory=lambda: int(os.getenv("ROLE_GAMER_ID"))) - MANAGER_ID: int = field(default_factory=lambda: int(os.getenv("MANAGER_ID"))) - PM_ID: int = field(default_factory=lambda: int(os.getenv("PM_ID"))) - EMOJI_ACK_ID: int = field(default_factory=lambda: int(os.getenv("EMOJI_ACK_ID"))) - URL_EXCUSES_YML: str = field(default_factory=lambda: os.getenv("URL_EXCUSES_YML")) - - def __post_init__(self): - if not self.DISCORD_TOKEN: - raise EnvironmentError("Environment variable not set:", self.DISCORD_TOKEN) - if not self.TARGET_VC_CH_ID: - raise EnvironmentError("Environment variable not set:", self.TARGET_VC_CH_ID) - if not self.TARGET_TXT_CH_ID: - raise EnvironmentError("Environment variable not set:", self.TARGET_TXT_CH_ID) - if not self.ROLE_GAMER_ID: - raise EnvironmentError("Environment variable not set:", self.ROLE_GAMER_ID) - if not self.MANAGER_ID: - raise EnvironmentError("Environment variable not set:", self.MANAGER_ID) - if not self.PM_ID: - raise EnvironmentError("Environment variable not set:", self.PM_ID) - if not self.EMOJI_ACK_ID: - raise EnvironmentError("Environment variable not set:", self.EMOJI_ACK_ID) - if not self.URL_EXCUSES_YML: - raise EnvironmentError("Environment variable not set:", self.URL_EXCUSES_YML) - - -@dataclass -class Session: - _MESSAGE: str = "zoin up ..., or else ... 🥀" - _EMBED_TITLE: str = "🗣️ Standup in session" - _EMBED_DESCRIPTION: str = """``` -░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░░░░░▀▀▀▀▀▀▀▀░░░░░░░░ -░░░░░░░░░░░▀▀░░░░░░░░░░░░░░░ -░░░░░░░░░░▀▀░░▀▀▀▀░░░░░░░░░░ -░░░░░░░░░▀▀░░░░▀▀░░░░░░░░░░░ -░░░░░░░░▀▀▀▀▀▀▀▀░░░░░░░░░░░░ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░░░░░░░░▀▀░░░░░░░░░░░ -░░░░░░░░░░▀▀░░▀▀░░▀▀░░░░░░░░ -░░░░░░░░░░░▀▀▀▀▀▀░░░░░░░░░░░ -░░░░░░░░▀▀░░▀▀░░▀▀░░░░░░░░░░ -░░░░░░░░░░░▀▀░░░░░░░░░░░░░░░ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░░░░░░░░▀▀░░░░░░░░░░░ -░░░░░░░░░░▀▀░░▀▀░░▀▀░░░░░░░░ -░░░░░░░░░░░▀▀▀▀▀▀░░░░░░░░░░░ -░░░░░░░░▀▀░░▀▀░░▀▀░░░░░░░░░░ -░░░░░░░░░░░▀▀░░░░░░░░░░░░░░░ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░░░░░░░░▀▀░░░░░░░░░░░ -░░░░░░░░░░▀▀░░▀▀░░▀▀░░░░░░░░ -░░░░░░░░░░░▀▀▀▀▀▀░░░░░░░░░░░ -░░░░░░░░▀▀░░▀▀░░▀▀░░░░░░░░░░ -░░░░░░░░░░░▀▀░░░░░░░░░░░░░░░ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░░░░░▀▀▀▀▀▀▀▀░░░░░░░░ -░░░░░░░░░░░▀▀░░░░▀▀░░░░░░░░░ -░░░░░░░░░░▀▀▀▀▀▀▀▀░░░░░░░░░░ -░░░░░░░░░▀▀░░░░▀▀░░░░░░░░░░░ -░░░░░░░░▀▀░░░░░░▀▀░░░░░░░░░░ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░░░░░░░░▀▀░░░░░░░░░░░ -░░░░░░░░░░░░░░▀▀░░░░░░░░░░░░ -░░░░░░░░░░░░░▀▀░░░░░░░░░░░░░ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░░░░▀▀░░░░░░░░░░░░░░░ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -```""" - _EMBED_COLOR: discord.Color = field(default_factory=lambda: discord.Color.random()) - - msg_id: Optional[int] = None - log: str = "" - attendees: Set[discord.Member] = field(default_factory=set) - is_active: bool = False - embed: Optional[discord.Embed] = None - _excuses: List[str] = field(default_factory=list) - - def __post_init__(self): - if self.embed is None: - self.set_embed() - - def set_embed(self, title=None, description=None, color=None): - self.embed = discord.Embed(title=title or self._EMBED_TITLE, description=description or self._EMBED_DESCRIPTION, color=color or self._EMBED_COLOR) - if self.log: - self.embed.set_footer(text=self.log) - - def add_log(self, msg: str): - self.log += f"\n{msg}" - if self.embed: - self.embed.set_footer(text=self.log) - - def get_excuses(self) -> List[str]: - config = Config() - - try: - response = requests.get(config.URL_EXCUSES_YML, allow_redirects=True) - response.raise_for_status() - data = yaml.safe_load(response.content) - self._excuses = data - return data - except Exception as e: - print("Failed to fetch excuses:", e) - if self._excuses: - print("Returning cached excuses.") - return self._excuses - - return [] From b18a8737a969c7168440ceff32485c8188efbd25 Mon Sep 17 00:00:00 2001 From: Divyanshu Madan Date: Tue, 5 May 2026 02:47:17 +0530 Subject: [PATCH 14/19] misc updates --- main.py | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/main.py b/main.py index 90f8e64..5e88720 100644 --- a/main.py +++ b/main.py @@ -5,11 +5,13 @@ import discord from discord.ext import commands from supabase_db import DB -from chars import * + +# -------------------- init -------------------- load_dotenv() DISCORD_TOKEN = os.getenv("DISCORD_TOKEN") + intents = discord.Intents.default() intents.voice_states = True intents.message_content = True @@ -20,6 +22,8 @@ db = DB() +# -------------------- helpers: voice channel activity -------------------- + def on_voice_channel(voice_state: discord.VoiceState): return voice_state.channel and voice_state.channel.id == db.get_server_id("voice_tapri", "channel") @@ -49,6 +53,9 @@ def is_end_session(before: discord.VoiceState, after: discord.VoiceState): return is_leaving(before, after) and len(before.channel.members) == 0 +# -------------------- helpers: misc -------------------- + + def dice_roll(choice): return random.randint(1, 6) == choice @@ -62,6 +69,9 @@ def oblique(string: str): return "".join(["𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍"[ord(char) - ord("A")] if char.isalpha() else char for char in string.upper()]) +# -------------------- helpers: embed updates -------------------- + + async def embed_add_log(msg: discord.Message, log: str): if msg.embeds: embed = msg.embeds[0] @@ -85,6 +95,9 @@ async def embed_update_img(msg: discord.Message, img_url: str): await msg.edit(embed=embed) +# -------------------- bot functions -------------------- + + @bot.event async def on_voice_state_update(member: discord.Member, before: discord.VoiceState, after: discord.VoiceState): text_ch = member.guild.get_channel(db.get_server_id("text_tapri", "channel")) @@ -93,25 +106,20 @@ async def on_voice_state_update(member: discord.Member, before: discord.VoiceSta msg = await text_ch.fetch_message(db.get_curr_session()) if is_new_session(after): - title = db.get_latest_agenda() - if not title: - title = oblique("zoin up") - - img_url = f'https://media.discordapp.net/stickers/{db.get_server_id("almostnice", "sticker")}.webp' - embed = discord.Embed( - title=title, + title=oblique("zoin up"), color=discord.Color.random(), ) - embed.set_image(url=img_url) - embed.set_thumbnail(url=img_url) + embed.set_image(url=f'https://media.discordapp.net/stickers/{db.get_server_id("almostnice", "sticker")}.webp') + embed.set_thumbnail(url=f'https://media.discordapp.net/stickers/{db.get_server_id("almostnice", "sticker")}.webp') embed.set_footer(text="") - msg = await text_ch.send(embed=embed) - db.create_session(id=msg.id) + db.create_session(id=msg.id) db.join_call(member.id) + embed_add_log(msg, oblique(f"{member.display_name} requested for a zoin up.")) + embed_update_title(msg, db.get_latest_agenda()) if is_joining(member, after): db.join_call(member.id) From 414fb03c35d324b07389f82bfbfdddbd97559c6a Mon Sep 17 00:00:00 2001 From: Divyanshu Madan Date: Tue, 5 May 2026 02:47:33 +0530 Subject: [PATCH 15/19] update requirements.txt --- requirements.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index ab6a5b3..cb8b9d6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,4 @@ discord.py==2.7.1 dotenv==0.9.9 -asyncio==4.0.0 -pyyaml==6.0.3 -requests==2.33.1 -supabase==2.28.3 \ No newline at end of file +supabase==2.28.3 +cachetools==7.1.1 From e8128dae7969c54cdaffb0fec6f64021c9376def Mon Sep 17 00:00:00 2001 From: Divyanshu Madan Date: Tue, 5 May 2026 16:41:08 +0530 Subject: [PATCH 16/19] add some bot cmds --- main.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/main.py b/main.py index 5e88720..62e3512 100644 --- a/main.py +++ b/main.py @@ -135,3 +135,38 @@ async def on_voice_state_update(member: discord.Member, before: discord.VoiceSta if is_end_session(before): db.end_curr_session() + + +# -------------------- bot commands -------------------- + + +async def how_to_use(ctx, cmd_use: str): + await ctx.message.add_reaction("❗️") + embed = discord.Embed(description=f"Use: `{cmd_use}`", color=discord.Color.blue()) + if dice_roll_time(): + embed.set_image("https://i.imgflip.com/21kggt.jpg") + await ctx.reply(embed=embed) + + +@bot.command(name="roll") +async def cmd_roll(ctx, *, choice: int = None): + if choice is None: + await how_to_use(ctx, "zroll [1..6]") + else: + if dice_roll(choice): + await ctx.message.add_reaction("✅") + embed = discord.Embed(color=discord.Color.green()) + embed.set_image("https://tenor.com/bn49Q.gif") + await ctx.reply(embed=embed) + else: + await ctx.message.add_reaction("❌") + + +@bot.command(name="agenda") +async def cmd_agenda(ctx, *, text: str = None): + if text is None: + await how_to_use(ctx, "zagenda [text]") + else: + db.add_agenda(text, ctx.author.id) + embed_update_title(ctx, text) + await ctx.message.add_reaction("✅") From 836f677df94584ca8b9ead4fb53ba77ee8e19257 Mon Sep 17 00:00:00 2001 From: Divyanshu Madan Date: Tue, 5 May 2026 16:41:30 +0530 Subject: [PATCH 17/19] misc --- main.py | 54 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/main.py b/main.py index 62e3512..6baea5e 100644 --- a/main.py +++ b/main.py @@ -56,7 +56,7 @@ def is_end_session(before: discord.VoiceState, after: discord.VoiceState): # -------------------- helpers: misc -------------------- -def dice_roll(choice): +def dice_roll(choice: int): return random.randint(1, 6) == choice @@ -72,27 +72,34 @@ def oblique(string: str): # -------------------- helpers: embed updates -------------------- -async def embed_add_log(msg: discord.Message, log: str): - if msg.embeds: - embed = msg.embeds[0] - logs = embed.footer.text + f"\n{log}" - embed.set_footer(logs) - await msg.edit(embed=embed) +def get_msg_embed(ctx): + session_id = db.get_curr_session() + if session_id: + msg = ctx.fetch_message(session_id) + if msg.embeds: + return msg, msg.embeds[0] -async def embed_update_title(msg: discord.Message, title: str): - if msg.embeds: - embed = msg.embeds[0] - embed.title = title - await msg.edit(embed=embed) +async def embed_add_log(ctx, log: str): + msg, embed = get_msg_embed(ctx) + logs = embed.footer.text + f"\n{log}" + embed.set_footer(logs) + await msg.edit(embed=embed) -async def embed_update_img(msg: discord.Message, img_url: str): - if msg.embeds: - embed = msg.embeds[0] - embed.set_image(url=img_url) - embed.set_thumbnail(url=img_url) - await msg.edit(embed=embed) +async def embed_update_title(ctx, title: str): + msg, embed = get_msg_embed(ctx) + embed = msg.embeds[0] + embed.title = title + await msg.edit(embed=embed) + + +async def embed_update_img(ctx, img_url: str): + msg, embed = get_msg_embed(ctx) + embed = msg.embeds[0] + embed.set_image(url=img_url) + embed.set_thumbnail(url=img_url) + await msg.edit(embed=embed) # -------------------- bot functions -------------------- @@ -101,9 +108,10 @@ async def embed_update_img(msg: discord.Message, img_url: str): @bot.event async def on_voice_state_update(member: discord.Member, before: discord.VoiceState, after: discord.VoiceState): text_ch = member.guild.get_channel(db.get_server_id("text_tapri", "channel")) + team_manager = member.guild.get_member(db.get_server_id("team_manager", "member")) product_manager = member.guild.get_member(db.get_server_id("product_manager", "member")) - msg = await text_ch.fetch_message(db.get_curr_session()) + avatar = "🐍" if member.id == team_manager.id or member.id == product_manager.id else "🦮" if is_new_session(after): embed = discord.Embed( @@ -118,20 +126,18 @@ async def on_voice_state_update(member: discord.Member, before: discord.VoiceSta db.create_session(id=msg.id) db.join_call(member.id) - embed_add_log(msg, oblique(f"{member.display_name} requested for a zoin up.")) - embed_update_title(msg, db.get_latest_agenda()) + embed_update_title(text_ch, db.get_latest_agenda()) if is_joining(member, after): db.join_call(member.id) - embed_add_log(msg, oblique(f"{member.display_name} zoined.")) if is_leaving(member, after): db.leave_call(member.id) - embed_add_log(msg, oblique(f"{member.display_name} left due to {db.get_random_excuse()}.")) + embed_add_log(text_ch, oblique(f"{avatar} {member.display_name}: I'll have to drop off due to {db.get_random_excuse()}.")) if is_step_out(before, after): db.pause_call(member.id) - embed_add_log(msg, oblique(f"{member.display_name} had to step out due to {db.get_random_excuse()}.")) + embed_add_log(text_ch, oblique(f"{avatar} {member.display_name}: I have to step out due to {db.get_random_excuse()}.")) if is_end_session(before): db.end_curr_session() From fce503e24a15c806d5bbf561fadfe4ccab992cf8 Mon Sep 17 00:00:00 2001 From: Divyanshu Madan Date: Thu, 7 May 2026 00:07:06 +0530 Subject: [PATCH 18/19] misc --- main.py | 133 ++++++++++++++++++++++++++++++++++--------------- supabase_db.py | 57 ++++++--------------- 2 files changed, 110 insertions(+), 80 deletions(-) diff --git a/main.py b/main.py index 6baea5e..00d5a08 100644 --- a/main.py +++ b/main.py @@ -4,12 +4,15 @@ from dotenv import load_dotenv import discord from discord.ext import commands +from discord.utils import get from supabase_db import DB # -------------------- init -------------------- load_dotenv() -DISCORD_TOKEN = os.getenv("DISCORD_TOKEN") +DISCORD_TOKEN = int(os.getenv("DISCORD_TOKEN")) +TEXT_CH = int(os.getenv("TEXT_CH")) +VOICE_CH = int(os.getenv("VOICE_CH")) intents = discord.Intents.default() @@ -26,7 +29,7 @@ def on_voice_channel(voice_state: discord.VoiceState): - return voice_state.channel and voice_state.channel.id == db.get_server_id("voice_tapri", "channel") + return voice_state.channel and voice_state.channel.id == VOICE_CH def is_new_session(after: discord.VoiceState): @@ -107,21 +110,21 @@ async def embed_update_img(ctx, img_url: str): @bot.event async def on_voice_state_update(member: discord.Member, before: discord.VoiceState, after: discord.VoiceState): - text_ch = member.guild.get_channel(db.get_server_id("text_tapri", "channel")) - - team_manager = member.guild.get_member(db.get_server_id("team_manager", "member")) - product_manager = member.guild.get_member(db.get_server_id("product_manager", "member")) - avatar = "🐍" if member.id == team_manager.id or member.id == product_manager.id else "🦮" + guild = member.guild + text_ch = guild.get_channel(TEXT_CH) + is_manager = get(guild.roles, name="manager") in member.roles + is_pm = get(guild.roles, name="PM") in member.roles + avatar = "🐍" if is_manager or is_pm else "🦮" if is_new_session(after): embed = discord.Embed( title=oblique("zoin up"), color=discord.Color.random(), ) - embed.set_image(url=f'https://media.discordapp.net/stickers/{db.get_server_id("almostnice", "sticker")}.webp') - embed.set_thumbnail(url=f'https://media.discordapp.net/stickers/{db.get_server_id("almostnice", "sticker")}.webp') + embed.set_image(url=f'https://media.discordapp.net/stickers/{get(guild.stickers, name="almostnice")}.webp') + embed.set_thumbnail(url=f'https://media.discordapp.net/stickers/{get(guild.stickers, name="almostnice")}.webp') embed.set_footer(text="") - msg = await text_ch.send(embed=embed) + msg = await text_ch.send(content=get(guild.roles, name="g***r").mention, embed=embed) db.create_session(id=msg.id) db.join_call(member.id) @@ -132,12 +135,14 @@ async def on_voice_state_update(member: discord.Member, before: discord.VoiceSta db.join_call(member.id) if is_leaving(member, after): - db.leave_call(member.id) - embed_add_log(text_ch, oblique(f"{avatar} {member.display_name}: I'll have to drop off due to {db.get_random_excuse()}.")) + excuse = db.get_random_excuse() + db.leave_call(excuse, member.id) + embed_add_log(text_ch, oblique(f"{avatar} {member.display_name}: I'll have to drop off due to {excuse}.{" You can sit now." if is_manager else ""}")) if is_step_out(before, after): - db.pause_call(member.id) - embed_add_log(text_ch, oblique(f"{avatar} {member.display_name}: I have to step out due to {db.get_random_excuse()}.")) + excuse = db.get_random_excuse() + db.pause_call(excuse, member.id) + embed_add_log(text_ch, oblique(f"{avatar} {member.display_name}: I have to step out due to {excuse}.{" Keep standing." if is_manager else ""}")) if is_end_session(before): db.end_curr_session() @@ -146,33 +151,83 @@ async def on_voice_state_update(member: discord.Member, before: discord.VoiceSta # -------------------- bot commands -------------------- -async def how_to_use(ctx, cmd_use: str): +def _use_str(cmd: str, args: str): + if len(args) > 0: + return f"`{cmd} [dice_roll_1..6] {args}`" + return f"`{cmd} [dice_roll_1..6]`" + + +async def how_to_use(ctx, cmd: str, args: str): await ctx.message.add_reaction("❗️") - embed = discord.Embed(description=f"Use: `{cmd_use}`", color=discord.Color.blue()) - if dice_roll_time(): - embed.set_image("https://i.imgflip.com/21kggt.jpg") + embed = discord.Embed(description=f"Use: {_use_str(cmd, args)}", color=discord.Color.blue()) await ctx.reply(embed=embed) -@bot.command(name="roll") -async def cmd_roll(ctx, *, choice: int = None): - if choice is None: - await how_to_use(ctx, "zroll [1..6]") - else: - if dice_roll(choice): - await ctx.message.add_reaction("✅") - embed = discord.Embed(color=discord.Color.green()) - embed.set_image("https://tenor.com/bn49Q.gif") - await ctx.reply(embed=embed) - else: - await ctx.message.add_reaction("❌") - - -@bot.command(name="agenda") -async def cmd_agenda(ctx, *, text: str = None): - if text is None: - await how_to_use(ctx, "zagenda [text]") - else: - db.add_agenda(text, ctx.author.id) - embed_update_title(ctx, text) +async def _cmd_helper(ctx, incorrect_use: bool, choice: int, cmd: str, args: str): + if incorrect_use: + await how_to_use(ctx, cmd, args) + return False + if dice_roll(choice): await ctx.message.add_reaction("✅") + return True + await ctx.message.add_reaction("↪🎲") + return False + + +@bot.command(name="agenda", aliases=["AGENDA", "a", "A"]) +async def cmd_agenda(ctx, choice: int = None, agenda: str = None): + if await _cmd_helper(ctx, choice is None, choice, "zagenda", "[agenda]"): + db.add_agenda(agenda, ctx.author.id) + embed_update_title(ctx, agenda) + + +@bot.command(name="broadcast", aliases=["BROADCAST"]) +async def cmd_broadcast(ctx, choice: int = None, message: str = None): + if await _cmd_helper(ctx, choice is None or message is None, choice, "broadcast", "[message]"): + db.add_broadcast(message, ctx.author.id) + + +@bot.command(name="callout", aliases=["CALLOUT"]) +async def cmd_callout(ctx, choice: int = None, role: str = None, callout: str = None): + if await _cmd_helper( + ctx, + choice is None or role is None or callout is None or role.lower() not in ["manager", "pm"], + choice, + "zcallout", + "[manager/pm] [callout]", + ): + if role.lower() == "manager": + db.add_manager_callout(callout, ctx.author.id) + if role.lower() == "pm": + db.add_pm_callout(callout, ctx.author.id) + + +@bot.command(name="excuse", aliases=["EXCUSE", "e", "E"]) +async def cmd_excuse(ctx, choice: int = None, excuse: str = None): + if await _cmd_helper(ctx, choice is None or excuse is None, choice, "zexcuse", "[excuse]"): + db.add_excuse(excuse, ctx.author.id) + + +@bot.command(name="hallucinate", aliases=["HALLUCINATE", "hallucination", "HALLUCINATION"]) +async def cmd_hallucinate(ctx, choice: int = None, hallucination: str = None): + if await _cmd_helper(ctx, choice is None or hallucination is None, choice, "zhallucinate", "[hallucination]"): + db.add_hallucination(hallucination, ctx.author.id) + + +@bot.command(name="roll", aliases=["ROLL"]) +async def cmd_roll(ctx, choice: int = None): + await _cmd_helper(ctx, choice is None, choice, "zroll", "") + + +@bot.command(name="help", aliases=["HELP", "h", "H", "man", "MAN"]) +async def cmd_help(ctx, choice: int = None): + if await _cmd_helper(ctx, choice is None, choice, "zhelp", ""): + embed = discord.Embed(title="🧑‍💻 Help Desk", description="All commands need a successful dice roll to function.", color=discord.Color.blue()) + embed.add_field(name=_use_str("zagenda", "[agenda]"), value="Set the meeting agenda to gain corporate aura.", inline=False) + embed.add_field(name=_use_str("zbroadcast", "[message]"), value="Add messages to the daily broadcast pool.", inline=False) + embed.add_field(name=_use_str("zcallout", "[manager/pm] [callout]"), value="Add callouts to annoy the people who haunt your dreams.", inline=False) + embed.add_field(name=_use_str("zexcuse", "[excuse]"), value="Excuse me?", inline=False) + embed.add_field(name=_use_str("zhallucinate", "[hallucination]"), value="Meow.", inline=False) + embed.add_field(name=_use_str("zroll", ""), value="Roll a dice to resolve arguments, decide hangouts, or make life changing decisions.", inline=False) + embed.add_field(name=_use_str("zhelp/zman/zh", ""), value="Helps if you want help about the help command.", inline=False) + await ctx.reply(embed=embed) diff --git a/supabase_db.py b/supabase_db.py index 45023fc..083c2d6 100644 --- a/supabase_db.py +++ b/supabase_db.py @@ -45,13 +45,6 @@ def get_curr_session(self): def end_curr_session(self): return self._table("session").update({"is_active": False}).eq("id", self.get_curr_session()) - # -------------------- server -------------------- - - @cached({}) - @execute(lambda data: data[0]["id"]) - def get_server_id(self, name: str, kind: str): - return self._table("server").select("id, name, kind").eq("name", name).eq("kind", kind) - # -------------------- create activity -------------------- @execute @@ -64,35 +57,26 @@ def add_agenda(self, agenda: str, member_id: int): def add_excuse(self, excuse: str, member_id: int): return self._create_activity("add_excuse", excuse, member_id) - def add_message_hallucination(self, hallucination: str, member_id: int): - return self._create_activity("add_message_hallucination", hallucination, member_id) - - def add_join_call_hallucination(self, hallucination: str, member_id: int): - return self._create_activity("add_join_call_hallucination", hallucination, member_id) - - def add_leave_call_hallucination(self, hallucination: str, member_id: int): - return self._create_activity("add_leave_call_hallucination", hallucination, member_id) - - def add_step_out_hallucination(self, hallucination: str, member_id: int): - return self._create_activity("add_step_out_hallucination", hallucination, member_id) + def add_hallucination(self, hallucination: str, member_id: int): + return self._create_activity("add_hallucination", hallucination, member_id) def join_call(self, member_id: int): return self._create_activity("join_call", self.get_curr_session(), member_id) - def leave_call(self, member_id: int, excuse: str): + def leave_call(self, excuse: str, member_id: int): return self._create_activity("leave_call", excuse, member_id) - def step_out(self, member_id: int, excuse: str): + def step_out(self, excuse: str, member_id: int): return self._create_activity("step_out", excuse, member_id) - def add_manager_call(self, tag: str, member_id: int): - return self._create_activity("add_manager_call", tag, member_id) + def add_manager_callout(self, callout: str, member_id: int): + return self._create_activity("add_manager_call", callout, member_id) - def add_pm_call(self, tag: str, member_id: int): - return self._create_activity("add_pm_call", tag, member_id) + def add_pm_callout(self, callout: str, member_id: int): + return self._create_activity("add_pm_call", callout, member_id) - def add_proactive_communication(self, communication: str, member_id: int): - return self._create_activity("add_proactive_communication", communication, member_id) + def add_broadcast(self, message: str, member_id: int): + return self._create_activity("add_broadcast", message, member_id) # -------------------- get activity -------------------- @@ -113,27 +97,18 @@ def get_latest_agenda(self): def get_random_excuse(self): return self._get_random_activity("add_excuse") - def get_random_message_hallucination(self): - return self._get_random_activity("add_message_hallucination") - - def get_random_join_call_hallucination(self): - return self._get_random_activity("add_join_call_hallucination") - - def get_random_leave_call_hallucination(self): - return self._get_random_activity("add_leave_call_hallucination") - - def get_random_step_out_hallucination(self): - return self._get_random_activity("add_step_out_hallucination") + def get_random_hallucination(self): + return self._get_random_activity("add_hallucination") - def get_random_proactive_communication(self): - return self._get_random_activity("add_proactive_communication") + def get_random_broadcast(self): + return self._get_random_activity("add_broadcast") @cached(TTLCache(ttl=600)) - def get_manager_calls(self): + def get_manager_callouts(self): return set([item["value"] for item in self._get_activity("add_manager_call")]) @cached(TTLCache(ttl=600)) - def get_pm_calls(self): + def get_pm_callouts(self): return set([item["value"] for item in self._get_activity("add_pm_call")]) def get_joined_members(self): From edc3d02ac96ecfcb06e52178fa841b4054bfb44c Mon Sep 17 00:00:00 2001 From: Divyanshu Madan Date: Thu, 7 May 2026 00:39:14 +0530 Subject: [PATCH 19/19] misc --- main.py | 9 ++++++++- supabase_db.py | 24 +++++++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index 00d5a08..d1f2308 100644 --- a/main.py +++ b/main.py @@ -52,6 +52,10 @@ def is_step_out(before: discord.VoiceState, after: discord.VoiceState): return on_voice_channel(after) and ((not before.self_mute and after.self_mute) or (not before.self_deaf and after.self_deaf)) +def is_join_back(before: discord.VoiceState, after: discord.VoiceState): + return on_voice_channel(after) and ((before.self_mute and not after.self_mute) or (before.self_deaf and not after.self_deaf)) + + def is_end_session(before: discord.VoiceState, after: discord.VoiceState): return is_leaving(before, after) and len(before.channel.members) == 0 @@ -141,9 +145,12 @@ async def on_voice_state_update(member: discord.Member, before: discord.VoiceSta if is_step_out(before, after): excuse = db.get_random_excuse() - db.pause_call(excuse, member.id) + db.step_out(member.id) embed_add_log(text_ch, oblique(f"{avatar} {member.display_name}: I have to step out due to {excuse}.{" Keep standing." if is_manager else ""}")) + if is_join_back(before, after): + db.join_back(member.id) + if is_end_session(before): db.end_curr_session() diff --git a/supabase_db.py b/supabase_db.py index 083c2d6..f788e26 100644 --- a/supabase_db.py +++ b/supabase_db.py @@ -4,7 +4,9 @@ from dotenv import load_dotenv from functools import wraps from cachetools import cached, TTLCache - +from collections import defaultdict +from datetime import datetime +from sortedcontainers import SortedList load_dotenv() SUPABASE_URL = os.getenv("SUPABASE_URL") @@ -69,6 +71,9 @@ def leave_call(self, excuse: str, member_id: int): def step_out(self, excuse: str, member_id: int): return self._create_activity("step_out", excuse, member_id) + def join_back(self, member_id: int): + return self._create_activity("join_back", None, member_id) + def add_manager_callout(self, callout: str, member_id: int): return self._create_activity("add_manager_call", callout, member_id) @@ -119,3 +124,20 @@ def get_left_members(self): def get_active_members(self): return self.get_joined_members().difference(self.get_left_members()) + + def get_step_out_time_period(self): + step_outs = defaultdict(SortedList) + for item in self._get_activity("step_out", self.get_curr_session()): + step_outs[item["member_id"]].add(item["created_at"]) + + join_backs = defaultdict(SortedList) + for item in self._get_activity("join_back", self.get_curr_session()): + join_backs[item["member_id"]].add(item["created_at"]) + + diff = defaultdict(int) + for member_id in join_backs.keys(): + for t1, t2 in zip(step_outs[member_id], join_backs[member_id]): + diff = datetime.fromisoformat(t2) - datetime.fromisoformat(t1) + diff["member_id"] += diff.total_seconds() + + return diff