2024-05-29 16:31:38 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
from hellmbot.env import ENV
|
|
|
|
from sqlite3 import connect as sqlite
|
|
|
|
from typing import Any, Iterable
|
|
|
|
from enum import Enum
|
|
|
|
|
|
|
|
|
|
|
|
##############################
|
|
|
|
# sqlite Database Management #
|
|
|
|
##############################
|
|
|
|
|
|
|
|
|
|
|
|
class DBColumnsTypes(Enum):
|
2024-05-29 17:20:00 +00:00
|
|
|
"""
|
|
|
|
Data types for sqlite table columns
|
|
|
|
"""
|
2024-05-29 16:31:38 +00:00
|
|
|
stroke = "TEXT"
|
|
|
|
integer_number = "INTEGER"
|
|
|
|
float_number = "FLOAT"
|
|
|
|
|
|
|
|
|
|
|
|
class DataBase(object):
|
2024-05-29 17:20:00 +00:00
|
|
|
"""
|
|
|
|
Describes the interaction with the database sqlite3
|
|
|
|
"""
|
2024-05-29 16:31:38 +00:00
|
|
|
DB_PATH = ENV.DB_PATH
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def __getcolumns(columns: dict[str: DBColumnsTypes]) -> str:
|
|
|
|
"""
|
|
|
|
Converts dictionary to a string for a sqlite query
|
|
|
|
|
|
|
|
:param columns: Dict["column_title": ColumnType]
|
|
|
|
:return: Part of sqlite query
|
|
|
|
"""
|
|
|
|
keys = tuple(columns.keys())
|
|
|
|
values = tuple(columns.values())
|
|
|
|
lst = zip(keys, values)
|
|
|
|
lst = [f"{column_title.lower()} {column_type.value}" for column_title, column_type in lst]
|
|
|
|
return ", ".join(lst)
|
|
|
|
|
2024-06-03 12:39:06 +00:00
|
|
|
def _execute(self, request: str, params: list[Any] = None) -> None:
|
2024-05-29 16:31:38 +00:00
|
|
|
"""
|
|
|
|
Executes sqlite query
|
|
|
|
|
|
|
|
:param request: Query text
|
|
|
|
:param params: Additional parameters for the request
|
|
|
|
"""
|
|
|
|
with sqlite(self.DB_PATH) as db:
|
|
|
|
cursor = db.cursor()
|
|
|
|
if params is None:
|
|
|
|
cursor.execute(request)
|
|
|
|
return
|
|
|
|
cursor.execute(request, params)
|
|
|
|
db.commit()
|
|
|
|
|
2024-06-03 12:39:06 +00:00
|
|
|
def _gentable(self, table: str, columns: dict[str: DBColumnsTypes]) -> None:
|
2024-05-29 16:31:38 +00:00
|
|
|
"""
|
|
|
|
Generates table in the database
|
|
|
|
|
|
|
|
:param table: Name of the table
|
|
|
|
:param columns: Columns in the table
|
|
|
|
"""
|
2024-05-29 17:20:00 +00:00
|
|
|
request = f"CREATE TABLE IF NOT EXISTS {table} (id INTEGER PRIMARY KEY, {self.__getcolumns(columns)})"
|
2024-06-03 12:39:06 +00:00
|
|
|
self._execute(request)
|
2024-05-29 16:31:38 +00:00
|
|
|
|
2024-06-03 12:39:06 +00:00
|
|
|
def _insert(self, table: str, items: dict[str: Any]) -> None:
|
2024-05-29 16:31:38 +00:00
|
|
|
"""
|
|
|
|
Inserts data into a field of a database table
|
|
|
|
|
|
|
|
:param table: Name of the table
|
|
|
|
:param items: Dict["column_title": value]
|
|
|
|
"""
|
|
|
|
columns = [title.lower() for title in tuple(items.keys())]
|
|
|
|
columns = ", ".join(columns)
|
|
|
|
values = list(items.values())
|
2024-05-29 17:20:00 +00:00
|
|
|
request = f"INSERT INTO {table} ({columns}) VALUES ({("?, " * len(values))[:-2]})"
|
2024-06-03 12:39:06 +00:00
|
|
|
self._execute(request, values)
|
2024-05-29 16:31:38 +00:00
|
|
|
|
2024-06-03 12:39:06 +00:00
|
|
|
def _get(self, table: str, column: list[str] = None, where: dict[str: Any] = None, order: str = None) -> list[Any]:
|
2024-05-29 16:31:38 +00:00
|
|
|
"""
|
|
|
|
Returns data from sqlite table
|
|
|
|
|
|
|
|
:param table: Name of the table
|
|
|
|
:param column: List of the columns titles
|
|
|
|
:param where: Dict['column_title': value]
|
|
|
|
:param order: sqlite query order syntax stroke
|
|
|
|
:return: List of values from table
|
|
|
|
"""
|
2024-05-29 17:20:00 +00:00
|
|
|
args = None
|
|
|
|
if column is None: # if no column is specified, all columns are used by default
|
2024-05-29 16:31:38 +00:00
|
|
|
column = "*"
|
|
|
|
else:
|
|
|
|
column = ", ".join(i.lower() for i in column)
|
|
|
|
request = [f"SELECT {column} FROM {table}"]
|
|
|
|
if where is not None:
|
2024-05-29 17:20:00 +00:00
|
|
|
args = list(where.values())
|
|
|
|
where = f"WHERE {", ".join(tuple(where.keys()))} = ({("?, " * len(args))[:-2]})"
|
2024-05-29 16:31:38 +00:00
|
|
|
request.append(where)
|
|
|
|
if order is not None:
|
|
|
|
order = f"ORDER BY {order}"
|
|
|
|
request.append(order)
|
|
|
|
request = [" ".join(request)]
|
2024-05-29 17:20:00 +00:00
|
|
|
if args is not None:
|
|
|
|
request.append(args)
|
2024-05-29 16:31:38 +00:00
|
|
|
with sqlite(self.DB_PATH) as db:
|
|
|
|
cursor = db.cursor()
|
|
|
|
cursor.execute(*request)
|
|
|
|
return cursor.fetchall()
|
|
|
|
|
2024-06-03 12:39:06 +00:00
|
|
|
def _delete(self, table: str, where: dict[str: Any]) -> None:
|
2024-05-29 16:31:38 +00:00
|
|
|
values = list(where.values())
|
|
|
|
request = f"DELETE FROM {table} WHERE {", ".join(tuple(where.keys()))} = ({("?, " * len(values))[:-2]})"
|
2024-06-03 12:39:06 +00:00
|
|
|
self._execute(request, values)
|
2024-05-29 16:31:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
class ServersDB(DataBase):
|
2024-05-29 17:20:00 +00:00
|
|
|
"""
|
|
|
|
Describes the server channel table in the database
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
- channels (tuple) - Tuple of server channel id's
|
|
|
|
"""
|
|
|
|
TABLE = "servers" # Table name constant
|
2024-05-29 16:31:38 +00:00
|
|
|
|
|
|
|
def __init__(self, server_id: int) -> None:
|
2024-05-29 17:20:00 +00:00
|
|
|
"""
|
|
|
|
Init Server DataBase object
|
|
|
|
|
|
|
|
:param server_id: id of discord server
|
|
|
|
"""
|
2024-06-03 12:39:06 +00:00
|
|
|
self._gentable(self.TABLE, {
|
2024-05-29 17:20:00 +00:00
|
|
|
"server_id": DBColumnsTypes.integer_number, # id of discord server
|
|
|
|
"channel_id": DBColumnsTypes.integer_number, # id of discord voice channel id
|
|
|
|
"loop": DBColumnsTypes.integer_number # Channel sequence number
|
2024-05-29 16:31:38 +00:00
|
|
|
})
|
|
|
|
self.server = server_id
|
|
|
|
|
|
|
|
def check_server_exists(self) -> bool:
|
|
|
|
"""
|
|
|
|
Checks the existence of the server in the database table
|
|
|
|
"""
|
2024-06-07 16:19:24 +00:00
|
|
|
lst = self._get(self.TABLE, ["server_id"], {"server_id": self.server})
|
2024-05-29 16:31:38 +00:00
|
|
|
if len(lst) > 0:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2024-06-03 12:39:06 +00:00
|
|
|
def add_channel(self, channel_id: int, loop_number: int) -> None:
|
2024-05-29 16:31:38 +00:00
|
|
|
"""
|
|
|
|
Adds channel to the database
|
|
|
|
|
|
|
|
:param channel_id: id of discord channel
|
2024-06-03 12:39:06 +00:00
|
|
|
:param loop_number: Channel sequence number in the group
|
2024-05-29 16:31:38 +00:00
|
|
|
"""
|
2024-06-03 12:39:06 +00:00
|
|
|
self._insert(self.TABLE, {
|
2024-05-29 16:31:38 +00:00
|
|
|
"server_id": self.server,
|
2024-06-03 12:39:06 +00:00
|
|
|
"channel_id": channel_id,
|
|
|
|
"loop": loop_number
|
2024-05-29 16:31:38 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
@property
|
2024-05-29 17:20:00 +00:00
|
|
|
def channels(self) -> tuple:
|
2024-05-29 16:31:38 +00:00
|
|
|
"""
|
|
|
|
Returns tuple of server channel id's
|
|
|
|
|
|
|
|
:return: tuple of server channel id's
|
|
|
|
"""
|
2024-06-03 12:39:06 +00:00
|
|
|
lst = self._get(self.TABLE, ["channel_id"], {"server_id": self.server}, "loop")
|
2024-05-29 16:31:38 +00:00
|
|
|
if len(lst) == 0:
|
|
|
|
raise IndexError("This server has no added channels")
|
|
|
|
return tuple(map(lambda element: element[0], lst))
|
|
|
|
|
|
|
|
def clear_channels(self) -> None:
|
|
|
|
"""
|
|
|
|
Deletes all server channels from the database
|
|
|
|
"""
|
2024-06-03 12:39:06 +00:00
|
|
|
self._delete(self.TABLE, {"server_id": self.server})
|
2024-05-29 16:31:38 +00:00
|
|
|
|
|
|
|
def __iter__(self) -> Iterable[int]:
|
|
|
|
"""
|
|
|
|
Returns iterable object of server channel id's
|
|
|
|
"""
|
2024-05-29 17:20:00 +00:00
|
|
|
return iter(self.channels)
|
2024-05-29 16:31:38 +00:00
|
|
|
|
|
|
|
def __bool__(self) -> bool:
|
|
|
|
"""
|
|
|
|
Checks the existence of the server in the database table
|
|
|
|
"""
|
|
|
|
return self.check_server_exists()
|