aioappsrv/appservice/db.py
git-bruh 4713a00016
cleanup
* Use websocket events to sync usernames/avatars instead of periodic syncing.

* Use caches for fetched room_ids, room state (for usernames and avatars). Also switch to using a single cache with locks.

* Don't store full message objects in cache, just store the relation of matrix event IDs to discord message IDs and vice-versa. Content can be fetched from the server instead.

* Don't rely on websocket events for mentioning Discord users, mentions are now done by mentioning the dummy matrix user. The ID to be mentioned is extracted from the MXID instead.

* General clean-ups.
2021-05-13 15:51:58 +05:30

123 lines
3.3 KiB
Python

import os
import sqlite3
import threading
from typing import List
class DataBase:
def __init__(self, db_file) -> None:
self.create(db_file)
# The database is accessed via multiple threads.
self.lock = threading.Lock()
def create(self, db_file) -> None:
"""
Create a database with the relevant tables if it doesn't already exist.
"""
exists = os.path.exists(db_file)
self.conn = sqlite3.connect(db_file, check_same_thread=False)
self.conn.row_factory = self.dict_factory
self.cur = self.conn.cursor()
if exists:
return
self.cur.execute(
"CREATE TABLE bridge(room_id TEXT PRIMARY KEY, channel_id TEXT);"
)
self.cur.execute(
"CREATE TABLE users(mxid TEXT PRIMARY KEY, "
"avatar_url TEXT, username TEXT);"
)
self.conn.commit()
def dict_factory(self, cursor, row):
"""
https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection.row_factory
"""
d = {}
for idx, col in enumerate(cursor.description):
d[col[0]] = row[idx]
return d
def add_room(self, room_id: str, channel_id: str) -> None:
"""
Add a bridged room to the database.
"""
with self.lock:
self.cur.execute(
"INSERT INTO bridge (room_id, channel_id) VALUES (?, ?)",
[room_id, channel_id],
)
self.conn.commit()
def add_user(self, mxid: str) -> None:
with self.lock:
self.cur.execute("INSERT INTO users (mxid) VALUES (?)", [mxid])
self.conn.commit()
def add_avatar(self, avatar_url: str, mxid: str) -> None:
with self.lock:
self.cur.execute(
"UPDATE users SET avatar_url = (?) WHERE mxid = (?)",
[avatar_url, mxid],
)
self.conn.commit()
def add_username(self, username: str, mxid: str) -> None:
with self.lock:
self.cur.execute(
"UPDATE users SET username = (?) WHERE mxid = (?)",
[username, mxid],
)
self.conn.commit()
def get_channel(self, room_id: str) -> str:
"""
Get the corresponding channel ID for a given room ID.
"""
with self.lock:
self.cur.execute(
"SELECT channel_id FROM bridge WHERE room_id = ?", [room_id]
)
room = self.cur.fetchone()
# Return an empty string if the channel is not bridged.
return "" if not room else room["channel_id"]
def list_channels(self) -> List[str]:
"""
Get a list of all the bridged channels.
"""
with self.lock:
self.cur.execute("SELECT channel_id FROM bridge")
channels = self.cur.fetchall()
return [channel["channel_id"] for channel in channels]
def fetch_user(self, mxid: str) -> dict:
"""
Fetch the profile for a bridged user.
"""
with self.lock:
self.cur.execute("SELECT * FROM users")
users = self.cur.fetchall()
user: dict = next(
iter([user for user in users if user["mxid"] == mxid]), {}
)
return user