From 55809a9a3939609b51f85f001519ff6fc25b5f5c Mon Sep 17 00:00:00 2001 From: Kumi Date: Thu, 25 May 2023 07:28:28 +0000 Subject: [PATCH] Make it an importable module Abandon DuckDB in favor of sqlite3 --- .gitignore | 3 +- .vscode/settings.json | 3 + README.md | 57 +- classes/__init__.py | 1 - classes/store.py | 637 ------------------ config.dist.ini | 30 +- database.db-journal | Bin 0 -> 4616 bytes gptbot.service | 2 +- migrations/migration_2.py | 138 ---- pyproject.toml | 69 ++ __init__.py => src/gptbot/__init__.py | 0 gptbot.py => src/gptbot/__main__.py | 7 +- {assets => src/gptbot/assets}/logo.png | Bin .../gptbot/callbacks}/__init__.py | 0 {callbacks => src/gptbot/callbacks}/invite.py | 0 {callbacks => src/gptbot/callbacks}/join.py | 4 +- .../gptbot/callbacks}/message.py | 0 .../gptbot/callbacks}/roommember.py | 0 {callbacks => src/gptbot/callbacks}/sync.py | 0 {callbacks => src/gptbot/callbacks}/test.py | 0 .../gptbot/callbacks}/test_response.py | 0 src/gptbot/classes/__init__.py | 0 {classes => src/gptbot/classes}/bot.py | 44 +- {classes => src/gptbot/classes}/dict.py | 0 {classes => src/gptbot/classes}/logging.py | 0 {classes => src/gptbot/classes}/openai.py | 0 .../gptbot/classes}/trackingmore.py | 0 .../gptbot/classes}/wolframalpha.py | 0 {commands => src/gptbot/commands}/__init__.py | 2 +- {commands => src/gptbot/commands}/botinfo.py | 0 .../gptbot/commands}/calculate.py | 0 {commands => src/gptbot/commands}/chat.py | 0 {commands => src/gptbot/commands}/classify.py | 0 {commands => src/gptbot/commands}/coin.py | 0 {commands => src/gptbot/commands}/custom.py | 0 {commands => src/gptbot/commands}/dice.py | 0 {commands => src/gptbot/commands}/help.py | 0 .../gptbot/commands}/ignoreolder.py | 0 {commands => src/gptbot/commands}/imagine.py | 0 {commands => src/gptbot/commands}/newroom.py | 3 +- {commands => src/gptbot/commands}/parcel.py | 0 {commands => src/gptbot/commands}/privacy.py | 0 .../gptbot/commands}/roomsettings.py | 8 +- {commands => src/gptbot/commands}/space.py | 16 +- {commands => src/gptbot/commands}/stats.py | 4 +- .../gptbot/commands}/systemmessage.py | 4 +- {commands => src/gptbot/commands}/unknown.py | 0 .../gptbot/migrations}/__init__.py | 0 .../gptbot/migrations}/migration_1.py | 3 +- src/gptbot/migrations/migration_2.py | 7 + .../gptbot/migrations}/migration_3.py | 5 +- .../gptbot/migrations}/migration_4.py | 3 +- .../gptbot/migrations}/migration_5.py | 3 +- .../gptbot/migrations}/migration_6.py | 3 +- .../gptbot/migrations}/migration_7.py | 3 +- .../gptbot/migrations}/migration_8.py | 3 +- 56 files changed, 234 insertions(+), 828 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 classes/__init__.py delete mode 100644 classes/store.py create mode 100644 database.db-journal delete mode 100644 migrations/migration_2.py create mode 100644 pyproject.toml rename __init__.py => src/gptbot/__init__.py (100%) rename gptbot.py => src/gptbot/__main__.py (81%) rename {assets => src/gptbot/assets}/logo.png (100%) rename {callbacks => src/gptbot/callbacks}/__init__.py (100%) rename {callbacks => src/gptbot/callbacks}/invite.py (100%) rename {callbacks => src/gptbot/callbacks}/join.py (87%) rename {callbacks => src/gptbot/callbacks}/message.py (100%) rename {callbacks => src/gptbot/callbacks}/roommember.py (100%) rename {callbacks => src/gptbot/callbacks}/sync.py (100%) rename {callbacks => src/gptbot/callbacks}/test.py (100%) rename {callbacks => src/gptbot/callbacks}/test_response.py (100%) create mode 100644 src/gptbot/classes/__init__.py rename {classes => src/gptbot/classes}/bot.py (95%) rename {classes => src/gptbot/classes}/dict.py (100%) rename {classes => src/gptbot/classes}/logging.py (100%) rename {classes => src/gptbot/classes}/openai.py (100%) rename {classes => src/gptbot/classes}/trackingmore.py (100%) rename {classes => src/gptbot/classes}/wolframalpha.py (100%) rename {commands => src/gptbot/commands}/__init__.py (87%) rename {commands => src/gptbot/commands}/botinfo.py (100%) rename {commands => src/gptbot/commands}/calculate.py (100%) rename {commands => src/gptbot/commands}/chat.py (100%) rename {commands => src/gptbot/commands}/classify.py (100%) rename {commands => src/gptbot/commands}/coin.py (100%) rename {commands => src/gptbot/commands}/custom.py (100%) rename {commands => src/gptbot/commands}/dice.py (100%) rename {commands => src/gptbot/commands}/help.py (100%) rename {commands => src/gptbot/commands}/ignoreolder.py (100%) rename {commands => src/gptbot/commands}/imagine.py (100%) rename {commands => src/gptbot/commands}/newroom.py (96%) rename {commands => src/gptbot/commands}/parcel.py (100%) rename {commands => src/gptbot/commands}/privacy.py (100%) rename {commands => src/gptbot/commands}/roomsettings.py (94%) rename {commands => src/gptbot/commands}/space.py (92%) rename {commands => src/gptbot/commands}/stats.py (88%) rename {commands => src/gptbot/commands}/systemmessage.py (91%) rename {commands => src/gptbot/commands}/unknown.py (100%) rename {migrations => src/gptbot/migrations}/__init__.py (100%) rename {migrations => src/gptbot/migrations}/migration_1.py (88%) create mode 100644 src/gptbot/migrations/migration_2.py rename {migrations => src/gptbot/migrations}/migration_3.py (79%) rename {migrations => src/gptbot/migrations}/migration_4.py (79%) rename {migrations => src/gptbot/migrations}/migration_5.py (94%) rename {migrations => src/gptbot/migrations}/migration_6.py (89%) rename {migrations => src/gptbot/migrations}/migration_7.py (84%) rename {migrations => src/gptbot/migrations}/migration_8.py (83%) diff --git a/.gitignore b/.gitignore index 58a72dd..c4f8f6d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ config.ini venv/ *.pyc -__pycache__/ \ No newline at end of file +__pycache__/ +*.bak \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..de288e1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.formatting.provider": "black" +} \ No newline at end of file diff --git a/README.md b/README.md index 1127c99..427265f 100644 --- a/README.md +++ b/README.md @@ -26,12 +26,61 @@ probably add more in the future, so the name is a bit misleading. ## Installation -Simply clone this repository and install the requirements. +To run the bot, you will need Python 3.10 or newer. -### Requirements +The bot has been tested with Python 3.11 on Arch, but should work with any +current version, and should not require any special dependencies or operating +system features. -- Python 3.10 or later -- Requirements from `requirements.txt` (install with `pip install -r requirements.txt` in a venv) +### Production + +The easiest way to install the bot is to use pip to install it directly from +[its Git repository](https://kumig.it/kumitterer/matrix-gptbot/): + +```shell +# If desired, activate a venv first + +python -m venv venv +. venv/bin/activate + +# Install the bot + +pip install git+https://kumig.it/kumitterer/matrix-gptbot.git +``` + +This will install the bot from the main branch and all required dependencies. +A release to PyPI is planned, but not yet available. + +### Development + +Clone the repository and install the requirements to a virtual environment. + +```shell +# Clone the repository + +git clone https://kumig.it/kumitterer/matrix-gptbot.git +cd matrix-gptbot + +# If desired, activate a venv first + +python -m venv venv +. venv/bin/activate + +# Install the requirements + +pip install -Ur requirements.txt + +# Install the bot in editable mode + +pip install -e . + +# Go to the bot directory and start working + +cd src/gptbot +``` + +Of course, you can also fork the repository on [GitHub](https://github.com/kumitterer/matrix-gptbot/) +and work on your own copy. ### Configuration diff --git a/classes/__init__.py b/classes/__init__.py deleted file mode 100644 index 0e22a56..0000000 --- a/classes/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .store import DuckDBStore \ No newline at end of file diff --git a/classes/store.py b/classes/store.py deleted file mode 100644 index e999abf..0000000 --- a/classes/store.py +++ /dev/null @@ -1,637 +0,0 @@ -import duckdb - -from nio.store.database import MatrixStore, DeviceTrustState, OlmDevice, TrustState, InboundGroupSession, SessionStore, OlmSessions, GroupSessionStore, OutgoingKeyRequest, DeviceStore, Session -from nio.crypto import OlmAccount, OlmDevice - -from random import SystemRandom -from collections import defaultdict -from typing import Dict, List, Optional, Tuple - -from .dict import AttrDict - -import json - - -class DuckDBStore(MatrixStore): - @property - def account_id(self): - id = self._get_account()[0] if self._get_account() else None - - if id is None: - id = SystemRandom().randint(0, 2**16) - - return id - - def __init__(self, user_id, device_id, duckdb_conn): - self.conn = duckdb_conn - self.user_id = user_id - self.device_id = device_id - - def _get_account(self): - cursor = self.conn.cursor() - cursor.execute( - "SELECT * FROM accounts WHERE user_id = ? AND device_id = ?", - (self.user_id, self.device_id), - ) - account = cursor.fetchone() - cursor.close() - return account - - def _get_device(self, device): - acc = self._get_account() - - if not acc: - return None - - cursor = self.conn.cursor() - cursor.execute( - "SELECT * FROM device_keys WHERE user_id = ? AND device_id = ? AND account_id = ?", - (device.user_id, device.id, acc[0]), - ) - device_entry = cursor.fetchone() - cursor.close() - - return device_entry - - # Implementing methods with DuckDB equivalents - def verify_device(self, device): - if self.is_device_verified(device): - return False - - d = self._get_device(device) - assert d - - cursor = self.conn.cursor() - cursor.execute( - "INSERT OR REPLACE INTO device_trust_state (device_id, state) VALUES (?, ?)", - (d[0], TrustState.verified), - ) - self.conn.commit() - cursor.close() - - device.trust_state = TrustState.verified - - return True - - def unverify_device(self, device): - if not self.is_device_verified(device): - return False - - d = self._get_device(device) - assert d - - cursor = self.conn.cursor() - cursor.execute( - "INSERT OR REPLACE INTO device_trust_state (device_id, state) VALUES (?, ?)", - (d[0], TrustState.unset), - ) - self.conn.commit() - cursor.close() - - device.trust_state = TrustState.unset - - return True - - def is_device_verified(self, device): - d = self._get_device(device) - - if not d: - return False - - cursor = self.conn.cursor() - cursor.execute( - "SELECT state FROM device_trust_state WHERE device_id = ?", (d[0],) - ) - trust_state = cursor.fetchone() - cursor.close() - - if not trust_state: - return False - - return trust_state[0] == TrustState.verified - - def blacklist_device(self, device): - if self.is_device_blacklisted(device): - return False - - d = self._get_device(device) - assert d - - cursor = self.conn.cursor() - cursor.execute( - "INSERT OR REPLACE INTO device_trust_state (device_id, state) VALUES (?, ?)", - (d[0], TrustState.blacklisted), - ) - self.conn.commit() - cursor.close() - - device.trust_state = TrustState.blacklisted - - return True - - def unblacklist_device(self, device): - if not self.is_device_blacklisted(device): - return False - - d = self._get_device(device) - assert d - - cursor = self.conn.cursor() - cursor.execute( - "INSERT OR REPLACE INTO device_trust_state (device_id, state) VALUES (?, ?)", - (d[0], TrustState.unset), - ) - self.conn.commit() - cursor.close() - - device.trust_state = TrustState.unset - - return True - - def is_device_blacklisted(self, device): - d = self._get_device(device) - - if not d: - return False - - cursor = self.conn.cursor() - cursor.execute( - "SELECT state FROM device_trust_state WHERE device_id = ?", (d[0],) - ) - trust_state = cursor.fetchone() - cursor.close() - - if not trust_state: - return False - - return trust_state[0] == TrustState.blacklisted - - def ignore_device(self, device): - if self.is_device_ignored(device): - return False - - d = self._get_device(device) - assert d - - cursor = self.conn.cursor() - cursor.execute( - "INSERT OR REPLACE INTO device_trust_state (device_id, state) VALUES (?, ?)", - (d[0], int(TrustState.ignored.value)), - ) - self.conn.commit() - cursor.close() - - return True - - def ignore_devices(self, devices): - for device in devices: - self.ignore_device(device) - - def unignore_device(self, device): - if not self.is_device_ignored(device): - return False - - d = self._get_device(device) - assert d - - cursor = self.conn.cursor() - cursor.execute( - "INSERT OR REPLACE INTO device_trust_state (device_id, state) VALUES (?, ?)", - (d[0], TrustState.unset), - ) - self.conn.commit() - cursor.close() - - device.trust_state = TrustState.unset - - return True - - def is_device_ignored(self, device): - d = self._get_device(device) - - if not d: - return False - - cursor = self.conn.cursor() - cursor.execute( - "SELECT state FROM device_trust_state WHERE device_id = ?", (d[0],) - ) - trust_state = cursor.fetchone() - cursor.close() - - if not trust_state: - return False - - return trust_state[0] == TrustState.ignored - - def load_device_keys(self): - """Load all the device keys from the database. - - Returns DeviceStore containing the OlmDevices with the device keys. - """ - store = DeviceStore() - account = self.account_id - - if not account: - return store - - with self.conn.cursor() as cur: - cur.execute( - "SELECT * FROM device_keys WHERE account_id = ?", - (account,) - ) - device_keys = cur.fetchall() - - for d in device_keys: - cur.execute( - "SELECT * FROM keys WHERE device_id = ?", - (d[0],) - ) - keys = cur.fetchall() - key_dict = {k[0]: k[1] for k in keys} - - store.add( - OlmDevice( - d[2], - d[0], - key_dict, - display_name=d[3], - deleted=d[4], - ) - ) - - return store - - def save_device_keys(self, device_keys): - """Save the provided device keys to the database.""" - account = self.account_id - assert account - rows = [] - - for user_id, devices_dict in device_keys.items(): - for device_id, device in devices_dict.items(): - rows.append( - { - "account_id": account, - "user_id": user_id, - "device_id": device_id, - "display_name": device.display_name, - "deleted": device.deleted, - } - ) - - if not rows: - return - - with self.conn.cursor() as cur: - for idx in range(0, len(rows), 100): - data = rows[idx: idx + 100] - cur.executemany( - "INSERT OR IGNORE INTO device_keys (account_id, user_id, device_id, display_name, deleted) VALUES (?, ?, ?, ?, ?)", - [(r["account_id"], r["user_id"], r["device_id"], - r["display_name"], r["deleted"]) for r in data] - ) - - for user_id, devices_dict in device_keys.items(): - for device_id, device in devices_dict.items(): - cur.execute( - "UPDATE device_keys SET deleted = ? WHERE device_id = ?", - (device.deleted, device_id) - ) - - for key_type, key in device.keys.items(): - cur.execute(""" - INSERT INTO keys (key_type, key, device_id) VALUES (?, ?, ?) - ON CONFLICT (key_type, device_id) DO UPDATE SET key = ? - """, - (key_type, key, device_id, key) - ) - self.conn.commit() - - def save_group_sessions(self, sessions): - with self.conn.cursor() as cur: - for session in sessions: - cur.execute(""" - INSERT OR REPLACE INTO inbound_group_sessions ( - session_id, sender_key, signing_key, room_id, pickle, account_id - ) VALUES (?, ?, ?, ?, ?, ?) - """, ( - session.id, - session.sender_key, - session.signing_key, - session.room_id, - session.pickle, - self.account_id - )) - - self.conn.commit() - - def save_olm_sessions(self, sessions): - with self.conn.cursor() as cur: - for session in sessions: - cur.execute(""" - INSERT OR REPLACE INTO olm_sessions ( - session_id, sender_key, pickle, account_id - ) VALUES (?, ?, ?, ?) - """, ( - session.id, - session.sender_key, - session.pickle, - self.account_id - )) - - self.conn.commit() - - def save_outbound_group_sessions(self, sessions): - with self.conn.cursor() as cur: - for session in sessions: - cur.execute(""" - INSERT OR REPLACE INTO outbound_group_sessions ( - room_id, session_id, pickle, account_id - ) VALUES (?, ?, ?, ?) - """, ( - session.room_id, - session.id, - session.pickle, - self.account_id - )) - - self.conn.commit() - - def save_account(self, account: OlmAccount): - with self.conn.cursor() as cur: - cur.execute(""" - INSERT OR REPLACE INTO accounts ( - id, user_id, device_id, shared_account, pickle - ) VALUES (?, ?, ?, ?, ?) - """, ( - self.account_id, - self.user_id, - self.device_id, - account.shared, - account.pickle(self.pickle_key), - )) - - self.conn.commit() - - def load_sessions(self): - session_store = SessionStore() - - with self.conn.cursor() as cur: - cur.execute(""" - SELECT - os.sender_key, os.session, os.creation_time - FROM - olm_sessions os - INNER JOIN - accounts a ON os.account_id = a.id - WHERE - a.id = ? - """, (self.account_id,)) - - for row in cur.fetchall(): - sender_key, session_pickle, creation_time = row - session = Session.from_pickle( - session_pickle, creation_time, self.pickle_key) - session_store.add(sender_key, session) - - return session_store - - def load_inbound_group_sessions(self): - # type: () -> GroupSessionStore - """Load all Olm sessions from the database. - - Returns: - ``GroupSessionStore`` object, containing all the loaded sessions. - - """ - store = GroupSessionStore() - - account = self.account_id - - if not account: - return store - - with self.conn.cursor() as cursor: - cursor.execute( - "SELECT * FROM inbound_group_sessions WHERE account_id = ?", ( - account,) - ) - - for row in cursor.fetchall(): - cursor.execute( - "SELECT sender_key FROM forwarded_chains WHERE session_id = ?", - (row[1],), - ) - chains = cursor.fetchall() - - session = InboundGroupSession.from_pickle( - row[2].encode(), - row[3], - row[4], - row[5], - self.pickle_key, - [ - chain[0] - for chain in chains - ], - ) - store.add(session) - - return store - - def load_outgoing_key_requests(self): - # type: () -> dict - """Load all outgoing key requests from the database. - - Returns: - ``OutgoingKeyRequestStore`` object, containing all the loaded key requests. - """ - account = self.account_id - - if not account: - return store - - with self.conn.cursor() as cur: - cur.execute( - "SELECT * FROM outgoing_key_requests WHERE account_id = ?", - (account,) - ) - rows = cur.fetchall() - - return { - row[1]: OutgoingKeyRequest.from_response(AttrDict({ - "id": row[0], - "account_id": row[1], - "request_id": row[2], - "session_id": row[3], - "room_id": row[4], - "algorithm": row[5], - })) for row in rows - } - - def load_encrypted_rooms(self): - """Load the set of encrypted rooms for this account. - - Returns: - ``Set`` containing room ids of encrypted rooms. - """ - account = self.account_id - - if not account: - return set() - - with self.conn.cursor() as cur: - cur.execute( - "SELECT room_id FROM encrypted_rooms WHERE account_id = ?", - (account,) - ) - rows = cur.fetchall() - - return {row[0] for row in rows} - - def save_sync_token(self, token): - """Save the given token""" - account = self.account_id - assert account - - with self.conn.cursor() as cur: - cur.execute( - "INSERT OR REPLACE INTO sync_tokens (account_id, token) VALUES (?, ?)", - (account, token) - ) - self.conn.commit() - - def save_encrypted_rooms(self, rooms): - """Save the set of room ids for this account.""" - account = self.account_id - assert account - - data = [(room_id, account) for room_id in rooms] - - with self.conn.cursor() as cur: - for idx in range(0, len(data), 400): - rows = data[idx: idx + 400] - cur.executemany( - "INSERT OR IGNORE INTO encrypted_rooms (room_id, account_id) VALUES (?, ?)", - rows - ) - self.conn.commit() - - def save_session(self, sender_key, session): - """Save the provided Olm session to the database. - - Args: - sender_key (str): The curve key that owns the Olm session. - session (Session): The Olm session that will be pickled and - saved in the database. - """ - account = self.account_id - assert account - - pickled_session = session.pickle(self.pickle_key) - - with self.conn.cursor() as cur: - - cur.execute( - "INSERT OR REPLACE INTO olm_sessions (account_id, sender_key, session, session_id, creation_time, last_usage_date) VALUES (?, ?, ?, ?, ?, ?)", - (account, sender_key, pickled_session, session.id, - session.creation_time, session.use_time) - ) - self.conn.commit() - - def save_inbound_group_session(self, session): - """Save the provided Megolm inbound group session to the database. - - Args: - session (InboundGroupSession): The session to save. - """ - account = self.account_id - assert account - - with self.conn.cursor() as cur: - - # Insert a new session or update the existing one - query = """ - INSERT INTO inbound_group_sessions (account_id, sender_key, fp_key, room_id, session) - VALUES (?, ?, ?, ?, ?) - ON CONFLICT (account_id, sender_key, fp_key, room_id) - DO UPDATE SET session = excluded.session - """ - cur.execute(query, (account, session.sender_key, - session.ed25519, session.room_id, session.pickle(self.pickle_key))) - - # Delete existing forwarded chains for the session - delete_query = """ - DELETE FROM forwarded_chains WHERE session_id = (SELECT id FROM inbound_group_sessions WHERE account_id = ? AND sender_key = ? AND fp_key = ? AND room_id = ?) - """ - cur.execute( - delete_query, (account, session.sender_key, session.ed25519, session.room_id)) - - # Insert new forwarded chains for the session - insert_query = """ - INSERT INTO forwarded_chains (session_id, sender_key) - VALUES ((SELECT id FROM inbound_group_sessions WHERE account_id = ? AND sender_key = ? AND fp_key = ? AND room_id = ?), ?) - """ - - for chain in session.forwarding_chain: - cur.execute( - insert_query, (account, session.sender_key, session.ed25519, session.room_id, chain)) - - def add_outgoing_key_request(self, key_request): - """Add a new outgoing key request to the database. - - Args: - key_request (OutgoingKeyRequest): The key request to add. - """ - - account_id = self.account_id - with self.conn.cursor() as cursor: - cursor.execute( - """ - SELECT MAX(id) FROM outgoing_key_requests - """ - ) - row = cursor.fetchone() - request_id = row[0] + 1 if row[0] else 1 - - cursor.execute( - """ - INSERT INTO outgoing_key_requests (id, account_id, request_id, session_id, room_id, algorithm) - VALUES (?, ?, ?, ?, ?, ?) - ON CONFLICT (account_id, request_id) DO NOTHING - """, - ( - request_id, - account_id, - key_request.request_id, - key_request.session_id, - key_request.room_id, - key_request.algorithm, - ) - ) - - def load_account(self): - # type: () -> Optional[OlmAccount] - """Load the Olm account from the database. - - Returns: - ``OlmAccount`` object, or ``None`` if it wasn't found for the - current device_id. - - """ - cursor = self.conn.cursor() - query = """ - SELECT pickle, shared_account - FROM accounts - WHERE device_id = ?; - """ - cursor.execute(query, (self.device_id,)) - - result = cursor.fetchone() - - if not result: - return None - - account_pickle, shared = result - return OlmAccount.from_pickle(account_pickle.encode(), self.pickle_key, shared) diff --git a/config.dist.ini b/config.dist.ini index e3b79bc..759a0d2 100644 --- a/config.dist.ini +++ b/config.dist.ini @@ -106,15 +106,35 @@ Operator = Contact details not set [Database] -# Settings for the DuckDB database. -# If not defined, the bot will not be able to remember anything, and will not support encryption -# N.B.: Encryption doesn't work as it is supposed to anyway. - +# Path of the main database +# Used to "remember" settings, etc. +# Path = database.db +# Path of the Crypto Store - required to support encrypted rooms +# (not tested/supported yet) +# +CryptoStore = store.db + [TrackingMore] # API key for TrackingMore # If not defined, the bot will not be able to provide parcel tracking # -# APIKey = abcde-fghij-klmnop \ No newline at end of file +# APIKey = abcde-fghij-klmnop + +[Replicate] + +# API key for replicate.com +# Can be used to run lots of different AI models +# If not defined, the features that depend on it are not available +# +# APIKey = r8_alotoflettersandnumbershere + +[HuggingFace] + +# API key for Hugging Face +# Can be used to run lots of different AI models +# If not defined, the features that depend on it are not available +# +# APIKey = __________________________ \ No newline at end of file diff --git a/database.db-journal b/database.db-journal new file mode 100644 index 0000000000000000000000000000000000000000..14aa3683fecf9ca2f8b66396e103a39fd3a07ec7 GIT binary patch literal 4616 zcmeI$u?YYn3 2 else None @@ -16,7 +18,7 @@ async def command_roomsettings(room: MatrixRoom, event: RoomMessageText, bot): if value: bot.logger.log("Adding system message...") - with bot.database.cursor() as cur: + with closing(bot.database.cursor()) as cur: cur.execute( """INSERT INTO room_settings (room_id, setting, value) VALUES (?, ?, ?) ON CONFLICT (room_id, setting) DO UPDATE SET value = ?;""", @@ -40,7 +42,7 @@ async def command_roomsettings(room: MatrixRoom, event: RoomMessageText, bot): bot.logger.log(f"Setting {setting} status for {room.room_id} to {value}...") - with bot.database.cursor() as cur: + with closing(bot.database.cursor()) as cur: cur.execute( """INSERT INTO room_settings (room_id, setting, value) VALUES (?, ?, ?) ON CONFLICT (room_id, setting) DO UPDATE SET value = ?;""", @@ -55,7 +57,7 @@ async def command_roomsettings(room: MatrixRoom, event: RoomMessageText, bot): bot.logger.log(f"Retrieving {setting} status for {room.room_id}...") - with bot.database.cursor() as cur: + with closing(bot.database.cursor()) as cur: cur.execute( """SELECT value FROM room_settings WHERE room_id = ? AND setting = ?;""", (room.room_id, setting) diff --git a/commands/space.py b/src/gptbot/commands/space.py similarity index 92% rename from commands/space.py rename to src/gptbot/commands/space.py index 075eb9a..fca09a0 100644 --- a/commands/space.py +++ b/src/gptbot/commands/space.py @@ -2,6 +2,8 @@ from nio.events.room_events import RoomMessageText from nio.rooms import MatrixRoom from nio.responses import RoomInviteError +from contextlib import closing + async def command_space(room: MatrixRoom, event: RoomMessageText, bot): if len(event.body.split()) == 3: @@ -10,7 +12,7 @@ async def command_space(room: MatrixRoom, event: RoomMessageText, bot): if request.lower() == "enable": bot.logger.log("Enabling space...") - with bot.database.cursor() as cursor: + with closing(bot.database.cursor()) as cursor: cursor.execute( "SELECT space_id FROM user_spaces WHERE user_id = ? AND active = TRUE", (event.sender,)) space = cursor.fetchone() @@ -25,7 +27,7 @@ async def command_space(room: MatrixRoom, event: RoomMessageText, bot): "url": bot.logo_uri }, "") - with bot.database.cursor() as cursor: + with closing(bot.database.cursor()) as cursor: cursor.execute( "INSERT INTO user_spaces (space_id, user_id) VALUES (?, ?)", (space, event.sender)) @@ -48,7 +50,7 @@ async def command_space(room: MatrixRoom, event: RoomMessageText, bot): elif request.lower() == "disable": bot.logger.log("Disabling space...") - with bot.database.cursor() as cursor: + with closing(bot.database.cursor()) as cursor: cursor.execute( "SELECT space_id FROM user_spaces WHERE user_id = ? AND active = TRUE", (event.sender,)) space = cursor.fetchone()[0] @@ -58,7 +60,7 @@ async def command_space(room: MatrixRoom, event: RoomMessageText, bot): await bot.send_message(room, "You don't have a space enabled.", True) return - with bot.database.cursor() as cursor: + with closing(bot.database.cursor()) as cursor: cursor.execute( "UPDATE user_spaces SET active = FALSE WHERE user_id = ?", (event.sender,)) @@ -69,7 +71,7 @@ async def command_space(room: MatrixRoom, event: RoomMessageText, bot): if request.lower() == "update": bot.logger.log("Updating space...") - with bot.database.cursor() as cursor: + with closing(bot.database.cursor()) as cursor: cursor.execute( "SELECT space_id FROM user_spaces WHERE user_id = ? AND active = TRUE", (event.sender,)) space = cursor.fetchone()[0] @@ -103,7 +105,7 @@ async def command_space(room: MatrixRoom, event: RoomMessageText, bot): if request.lower() == "invite": bot.logger.log("Inviting user to space...") - with bot.database.cursor() as cursor: + with closing(bot.database.cursor()) as cursor: cursor.execute( "SELECT space_id FROM user_spaces WHERE user_id = ?", (event.sender,)) space = cursor.fetchone()[0] @@ -126,7 +128,7 @@ async def command_space(room: MatrixRoom, event: RoomMessageText, bot): await bot.send_message(room, "Invited you to the space.", True) return - with bot.database.cursor() as cursor: + with closing(bot.database.cursor()) as cursor: cursor.execute( "SELECT active FROM user_spaces WHERE user_id = ?", (event.sender,)) status = cursor.fetchone() diff --git a/commands/stats.py b/src/gptbot/commands/stats.py similarity index 88% rename from commands/stats.py rename to src/gptbot/commands/stats.py index ed543a8..4022dc3 100644 --- a/commands/stats.py +++ b/src/gptbot/commands/stats.py @@ -1,6 +1,8 @@ from nio.events.room_events import RoomMessageText from nio.rooms import MatrixRoom +from contextlib import closing + async def command_stats(room: MatrixRoom, event: RoomMessageText, bot): bot.logger.log("Showing stats...") @@ -10,7 +12,7 @@ async def command_stats(room: MatrixRoom, event: RoomMessageText, bot): bot.send_message(room, "Sorry, I'm not connected to a database, so I don't have any statistics on your usage.", True) return - with bot.database.cursor() as cursor: + with closing(bot.database.cursor()) as cursor: cursor.execute( "SELECT SUM(tokens) FROM token_usage WHERE room_id = ?", (room.room_id,)) total_tokens = cursor.fetchone()[0] or 0 diff --git a/commands/systemmessage.py b/src/gptbot/commands/systemmessage.py similarity index 91% rename from commands/systemmessage.py rename to src/gptbot/commands/systemmessage.py index 5309ff2..0d8dbe7 100644 --- a/commands/systemmessage.py +++ b/src/gptbot/commands/systemmessage.py @@ -1,6 +1,8 @@ from nio.events.room_events import RoomMessageText from nio.rooms import MatrixRoom +from contextlib import closing + async def command_systemmessage(room: MatrixRoom, event: RoomMessageText, bot): system_message = " ".join(event.body.split()[2:]) @@ -8,7 +10,7 @@ async def command_systemmessage(room: MatrixRoom, event: RoomMessageText, bot): if system_message: bot.logger.log("Adding system message...") - with bot.database.cursor() as cur: + with closing(bot.database.cursor()) as cur: cur.execute( """ INSERT INTO room_settings (room_id, setting, value) VALUES (?, ?, ?) diff --git a/commands/unknown.py b/src/gptbot/commands/unknown.py similarity index 100% rename from commands/unknown.py rename to src/gptbot/commands/unknown.py diff --git a/migrations/__init__.py b/src/gptbot/migrations/__init__.py similarity index 100% rename from migrations/__init__.py rename to src/gptbot/migrations/__init__.py diff --git a/migrations/migration_1.py b/src/gptbot/migrations/migration_1.py similarity index 88% rename from migrations/migration_1.py rename to src/gptbot/migrations/migration_1.py index 7571bca..aa0d527 100644 --- a/migrations/migration_1.py +++ b/src/gptbot/migrations/migration_1.py @@ -1,9 +1,10 @@ # Initial migration, token usage logging from datetime import datetime +from contextlib import closing def migration(conn): - with conn.cursor() as cursor: + with closing(conn.cursor()) as cursor: cursor.execute( """ CREATE TABLE IF NOT EXISTS token_usage ( diff --git a/src/gptbot/migrations/migration_2.py b/src/gptbot/migrations/migration_2.py new file mode 100644 index 0000000..63011f6 --- /dev/null +++ b/src/gptbot/migrations/migration_2.py @@ -0,0 +1,7 @@ +# Migration for Matrix Store - No longer used + +from datetime import datetime +from contextlib import closing + +def migration(conn): + pass \ No newline at end of file diff --git a/migrations/migration_3.py b/src/gptbot/migrations/migration_3.py similarity index 79% rename from migrations/migration_3.py rename to src/gptbot/migrations/migration_3.py index b38deaf..8cf47ec 100644 --- a/migrations/migration_3.py +++ b/src/gptbot/migrations/migration_3.py @@ -1,9 +1,10 @@ # Migration for custom system messages from datetime import datetime +from contextlib import closing def migration(conn): - with conn.cursor() as cursor: + with closing(conn.cursor()) as cursor: cursor.execute( """ CREATE TABLE IF NOT EXISTS system_messages ( @@ -11,7 +12,7 @@ def migration(conn): message_id TEXT NOT NULL, user_id TEXT NOT NULL, body TEXT NOT NULL, - timestamp BIGINT NOT NULL, + timestamp BIGINT NOT NULL ) """ ) diff --git a/migrations/migration_4.py b/src/gptbot/migrations/migration_4.py similarity index 79% rename from migrations/migration_4.py rename to src/gptbot/migrations/migration_4.py index b2bea5d..2d20d41 100644 --- a/migrations/migration_4.py +++ b/src/gptbot/migrations/migration_4.py @@ -1,9 +1,10 @@ # Migration to add API column to token usage table from datetime import datetime +from contextlib import closing def migration(conn): - with conn.cursor() as cursor: + with closing(conn.cursor()) as cursor: cursor.execute( """ ALTER TABLE token_usage ADD COLUMN api TEXT DEFAULT 'openai' diff --git a/migrations/migration_5.py b/src/gptbot/migrations/migration_5.py similarity index 94% rename from migrations/migration_5.py rename to src/gptbot/migrations/migration_5.py index 9b4eeb8..2aebdd4 100644 --- a/migrations/migration_5.py +++ b/src/gptbot/migrations/migration_5.py @@ -1,9 +1,10 @@ # Migration to add room settings table from datetime import datetime +from contextlib import closing def migration(conn): - with conn.cursor() as cursor: + with closing(conn.cursor()) as cursor: cursor.execute( """ CREATE TABLE IF NOT EXISTS room_settings ( diff --git a/migrations/migration_6.py b/src/gptbot/migrations/migration_6.py similarity index 89% rename from migrations/migration_6.py rename to src/gptbot/migrations/migration_6.py index 791f167..fdd437c 100644 --- a/migrations/migration_6.py +++ b/src/gptbot/migrations/migration_6.py @@ -1,9 +1,10 @@ # Migration to drop primary key constraint from token_usage table from datetime import datetime +from contextlib import closing def migration(conn): - with conn.cursor() as cursor: + with closing(conn.cursor()) as cursor: cursor.execute( """ CREATE TABLE token_usage_temp ( diff --git a/migrations/migration_7.py b/src/gptbot/migrations/migration_7.py similarity index 84% rename from migrations/migration_7.py rename to src/gptbot/migrations/migration_7.py index 56d30df..15ee6de 100644 --- a/migrations/migration_7.py +++ b/src/gptbot/migrations/migration_7.py @@ -1,9 +1,10 @@ # Migration to add user_spaces table from datetime import datetime +from contextlib import closing def migration(conn): - with conn.cursor() as cursor: + with closing(conn.cursor()) as cursor: cursor.execute( """ CREATE TABLE user_spaces ( diff --git a/migrations/migration_8.py b/src/gptbot/migrations/migration_8.py similarity index 83% rename from migrations/migration_8.py rename to src/gptbot/migrations/migration_8.py index 86308fe..0c68a07 100644 --- a/migrations/migration_8.py +++ b/src/gptbot/migrations/migration_8.py @@ -1,9 +1,10 @@ # Migration to add settings table from datetime import datetime +from contextlib import closing def migration(conn): - with conn.cursor() as cursor: + with closing(conn.cursor()) as cursor: cursor.execute( """ CREATE TABLE IF NOT EXISTS settings (