From f778d3f71cc2d6344f88f71635aeef91c443d039 Mon Sep 17 00:00:00 2001 From: BarsTiger Date: Mon, 13 Feb 2023 19:51:25 +0200 Subject: [PATCH] Skeleton of project - database loading --- .gitignore | 6 +++ .replit | 37 +++++++++++++++ bot/__init__.py | 1 + bot/common.py | 8 ++++ bot/config.py | 13 ++++++ bot/db/__init__.py | 2 + bot/db/db.py | 12 +++++ bot/db/db_model.py | 32 +++++++++++++ bot/db/meta.py | 72 +++++++++++++++++++++++++++++ bot/db/pull_db.py | 49 ++++++++++++++++++++ bot/handlers/__init__.py | 1 + bot/handlers/help/__init__.py | 1 + bot/handlers/help/help.py | 23 +++++++++ bot/handlers/help/help_strings.py | 2 + bot/handlers/initialize/__init__.py | 1 + bot/handlers/initialize/pull_db.py | 7 +++ bot/handlers/register.py | 11 +++++ bot/modules/__init__.py | 0 bot/utils/__init__.py | 0 bot/utils/commands.py | 20 ++++++++ main.py | 23 +++++++++ replit.nix | 20 ++++++++ requirements.txt | 7 +++ 23 files changed, 348 insertions(+) create mode 100644 .gitignore create mode 100644 .replit create mode 100644 bot/__init__.py create mode 100644 bot/common.py create mode 100644 bot/config.py create mode 100644 bot/db/__init__.py create mode 100644 bot/db/db.py create mode 100644 bot/db/db_model.py create mode 100644 bot/db/meta.py create mode 100644 bot/db/pull_db.py create mode 100644 bot/handlers/__init__.py create mode 100644 bot/handlers/help/__init__.py create mode 100644 bot/handlers/help/help.py create mode 100644 bot/handlers/help/help_strings.py create mode 100644 bot/handlers/initialize/__init__.py create mode 100644 bot/handlers/initialize/pull_db.py create mode 100644 bot/handlers/register.py create mode 100644 bot/modules/__init__.py create mode 100644 bot/utils/__init__.py create mode 100644 bot/utils/commands.py create mode 100644 main.py create mode 100644 replit.nix create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d7e5899 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/.idea/ +/tests/ +.env +/db +/dbb +/dbmeta \ No newline at end of file diff --git a/.replit b/.replit new file mode 100644 index 0000000..1c8491f --- /dev/null +++ b/.replit @@ -0,0 +1,37 @@ +run = "python3 main.py" +language = "python3" +entrypoint = "main.py" + +hidden = ["**/__pycache__", "**/.mypy_cache", "**/*.pyc"] + +[nix] +channel = "stable-22_11" + +[interpreter] + [interpreter.command] + args = [ + "stderred", + "--", + "prybar-python310", + "-q", + "--ps1", + "\u0001\u001b[33m\u0002\u0001\u001b[00m\u0002 ", + "-i", + ] + env = { LD_LIBRARY_PATH = "$PYTHON_LD_LIBRARY_PATH" } + +[env] +VIRTUAL_ENV = "/home/runner/${REPL_SLUG}/venv" +PATH = "${VIRTUAL_ENV}/bin" +PYTHONPATH = "${VIRTUAL_ENV}/lib/python3.10/site-packages" + +[gitHubImport] +requiredFiles = [".replit", "replit.nix", ".config", "venv"] + +[languages] + +[languages.python3] +pattern = "**/*.py" + +[languages.python3.languageServer] +start = "pylsp" diff --git a/bot/__init__.py b/bot/__init__.py new file mode 100644 index 0000000..2ae2839 --- /dev/null +++ b/bot/__init__.py @@ -0,0 +1 @@ +pass diff --git a/bot/common.py b/bot/common.py new file mode 100644 index 0000000..2d402e5 --- /dev/null +++ b/bot/common.py @@ -0,0 +1,8 @@ +from aiogram import Bot, Dispatcher +from bot.config import TOKEN +from aiogram.contrib.fsm_storage.memory import MemoryStorage + + +bot = Bot(token=TOKEN) +storage = MemoryStorage() +dp = Dispatcher(bot, storage=storage) diff --git a/bot/config.py b/bot/config.py new file mode 100644 index 0000000..af3d94e --- /dev/null +++ b/bot/config.py @@ -0,0 +1,13 @@ +import os +from dotenv import load_dotenv + +load_dotenv() + +TOKEN = os.getenv('TOKEN') +ADMIN = os.getenv('ADMIN') +DB_CHAT = os.getenv('DB_CHAT') +_DB_PATH = os.getenv('DB_PATH') +DB = _DB_PATH + '/db' +DBMETA = _DB_PATH + '/dbmeta' + +BARS_APP_ID = 'TELE-DIFFUSION-BOT' diff --git a/bot/db/__init__.py b/bot/db/__init__.py new file mode 100644 index 0000000..f7a640d --- /dev/null +++ b/bot/db/__init__.py @@ -0,0 +1,2 @@ +from .db import db +from .db_model import DBTables diff --git a/bot/db/db.py b/bot/db/db.py new file mode 100644 index 0000000..837aba8 --- /dev/null +++ b/bot/db/db.py @@ -0,0 +1,12 @@ +import os.path +from bot.config import DB +from .db_model import DBDict + +if not os.path.isfile(DB): + open('sync', 'w') + + +db = { + 'config': DBDict(DB, autocommit=True, tablename='config'), + 'cooldown': DBDict(DB, autocommit=True, tablename='cooldown') +} diff --git a/bot/db/db_model.py b/bot/db/db_model.py new file mode 100644 index 0000000..ae32ef4 --- /dev/null +++ b/bot/db/db_model.py @@ -0,0 +1,32 @@ +from sqlitedict import SqliteDict +from bot.common import bot +from bot.config import DB_CHAT, DB +from aiogram.types import InputFile, InputMediaDocument +from aiogram.utils.exceptions import MessageToEditNotFound +import time +from .meta import DBMeta + + +class DBTables: + tables = ['config', 'cooldown'] + config = "config" + cooldown = "cooldown" + + +class DBDict(SqliteDict): + async def write(self): + try: + DBMeta()[DBMeta.update_time] = time.time_ns() + await bot.edit_message_media(media=InputMediaDocument(InputFile(DB)), + chat_id=DB_CHAT, message_id=DBMeta()[DBMeta.message_id]) + await bot.edit_message_caption( + caption=DBMeta(), chat_id=DB_CHAT, message_id=DBMeta()[DBMeta.message_id] + ) + except MessageToEditNotFound: + DBMeta()[DBMeta.update_time] = time.time_ns() + self['db_message_id'] = (await bot.send_document(chat_id=DB_CHAT, document=InputFile(DB), + disable_notification=True)).message_id + DBMeta()[DBMeta.message_id] = self['db_message_id'] + await bot.edit_message_caption( + caption=DBMeta(), chat_id=DB_CHAT, message_id=self.get('db_message_id') + ) diff --git a/bot/db/meta.py b/bot/db/meta.py new file mode 100644 index 0000000..4888429 --- /dev/null +++ b/bot/db/meta.py @@ -0,0 +1,72 @@ +import os.path +from bot.config import DBMETA, BARS_APP_ID, DB_CHAT +from bot.common import bot +from aiogram.utils.exceptions import MessageToForwardNotFound + + +class DBMeta: + app_id = "app_id" + message_id = "message_id" + update_time = "update_time" + + def __init__(self): + if not os.path.isfile(DBMETA): + open(DBMETA, 'w').write(f'{BARS_APP_ID}|None|0') + + def __getitem__(self, item): + try: + return open(DBMETA).read().split('|')[{ + "app_id": 0, + "message_id": 1, + "update_time": 2 + }.get(item)] + except TypeError: + return None + + def __setitem__(self, key, value): + meta = open(DBMETA).read().split('|') + meta[{ + "app_id": 0, + "message_id": 1, + "update_time": 2 + }[key]] = value + open(DBMETA, 'w').write('|'.join(str(x) for x in meta)) + + def __str__(self): + return open(DBMETA).read() + + +class CloudMeta: + app_id = "app_id" + message_id = "message_id" + update_time = "update_time" + + @staticmethod + async def get(item): + try: + try: + if not DBMeta()[DBMeta.update_time] or not bot.cloudmeta_message_text: + raise AttributeError + except AttributeError: + try: + message = await bot.forward_message(DB_CHAT, DB_CHAT, DBMeta()[DBMeta.message_id]) + + bot.cloudmeta_message_text = message.caption + + await message.delete() + except MessageToForwardNotFound: + print('Cannot get CloudMeta - writing DBDict') + from .db_model import DBDict + await DBDict().write() + message = await bot.forward_message(DB_CHAT, DB_CHAT, DBMeta()[DBMeta.message_id]) + bot.cloudmeta_message_text = message.caption + await message.delete() + + cloudmeta = bot.cloudmeta_message_text.split('|') + return cloudmeta[{ + "app_id": 0, + "message_id": 1, + "update_time": 2 + }.get(item)] + except TypeError: + return None diff --git a/bot/db/pull_db.py b/bot/db/pull_db.py new file mode 100644 index 0000000..065efce --- /dev/null +++ b/bot/db/pull_db.py @@ -0,0 +1,49 @@ +import os +from .meta import DBMeta, CloudMeta +from bot.common import bot +from bot.config import DB, DB_CHAT +from .db_model import DBTables +from sqlitedict import SqliteDict + + +async def pull(): + if DBMeta()[DBMeta.message_id] == 'None': + from .db import db + print('No dbmeta file') + if msg_id := db[DBTables.config].get('db_message_id'): + print('Found message id in in-db config') + DBMeta()[DBMeta.message_id] = msg_id + await db[DBTables.config].write() + + if not os.path.isfile('sync'): + try: + if not bot.cloudmeta_message_text: + print('No cloudmeta initialized') + raise AttributeError + else: + return + except AttributeError: + if int(DBMeta()[DBMeta.update_time]) >= int(await CloudMeta.get(CloudMeta.update_time)): + print('First database pulling for this instance - DB is up-to-date') + return + else: + print('Database file is new. Trying to download cloud data') + os.remove('sync') + + print('DB is not up-to-date') + + message = await bot.forward_message(DB_CHAT, DB_CHAT, DBMeta()[DBMeta.message_id]) + + await message.delete() + + await message.document.download(destination_file=DB + 'b') + + from .db import db + for table in DBTables.tables: + db[table].clear() + new_table = SqliteDict(DB + 'b', tablename=table) + for key in new_table.keys(): + db[table][key] = new_table[key] + new_table.close() + + print('Loaded database from cloud') diff --git a/bot/handlers/__init__.py b/bot/handlers/__init__.py new file mode 100644 index 0000000..2ae2839 --- /dev/null +++ b/bot/handlers/__init__.py @@ -0,0 +1 @@ +pass diff --git a/bot/handlers/help/__init__.py b/bot/handlers/help/__init__.py new file mode 100644 index 0000000..2ae2839 --- /dev/null +++ b/bot/handlers/help/__init__.py @@ -0,0 +1 @@ +pass diff --git a/bot/handlers/help/help.py b/bot/handlers/help/help.py new file mode 100644 index 0000000..aa184f4 --- /dev/null +++ b/bot/handlers/help/help.py @@ -0,0 +1,23 @@ +from aiogram import types +from bot.common import dp +from .help_strings import help_data + + +@dp.message_handler(commands='help') +async def help_command(message: types.Message): + if message.get_args() == "": + await message.reply( + "\n".join( + list( + map( + (lambda x: f"/{x} - {help_data.get(x) if help_data.get(x) else f'No info for {x}'}"), + help_data.keys() + ) + ) + ) + ) + else: + if help_data.get(message.get_args()): + await message.reply(help_data.get(message.get_args())) + else: + await message.reply(f"No info for {message.get_args()}") diff --git a/bot/handlers/help/help_strings.py b/bot/handlers/help/help_strings.py new file mode 100644 index 0000000..fd7195f --- /dev/null +++ b/bot/handlers/help/help_strings.py @@ -0,0 +1,2 @@ +help_data = { +} diff --git a/bot/handlers/initialize/__init__.py b/bot/handlers/initialize/__init__.py new file mode 100644 index 0000000..fc80254 --- /dev/null +++ b/bot/handlers/initialize/__init__.py @@ -0,0 +1 @@ +pass \ No newline at end of file diff --git a/bot/handlers/initialize/pull_db.py b/bot/handlers/initialize/pull_db.py new file mode 100644 index 0000000..ab0a26d --- /dev/null +++ b/bot/handlers/initialize/pull_db.py @@ -0,0 +1,7 @@ +from bot.common import dp +from bot.db.pull_db import pull + + +@dp.message_handler() +async def pull_db_if_new(_): + await pull() diff --git a/bot/handlers/register.py b/bot/handlers/register.py new file mode 100644 index 0000000..7e29201 --- /dev/null +++ b/bot/handlers/register.py @@ -0,0 +1,11 @@ +from rich import print + + +def import_handlers(): + import bot.handlers.help.help + assert bot.handlers.help.help + + import bot.handlers.initialize.pull_db + assert bot.handlers.initialize.pull_db + + print('[gray]All handlers imported[/]') diff --git a/bot/modules/__init__.py b/bot/modules/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bot/utils/__init__.py b/bot/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bot/utils/commands.py b/bot/utils/commands.py new file mode 100644 index 0000000..4be4c89 --- /dev/null +++ b/bot/utils/commands.py @@ -0,0 +1,20 @@ +from rich import print +from aiogram.types import BotCommand +from bot.common import bot + + +async def set_commands(): + from bot.handlers.help.help_strings import help_data + + await bot.set_my_commands( + commands=list( + map( + (lambda x: BotCommand( + command='/' + x, + description=help_data.get(x) if help_data.get(x) else f'No info for {x}' + ) + ), help_data.keys()) + ) + [BotCommand(command='/help', description='Get commands list or info by command')] + ) + + print('[gray]Commands registered[/]') diff --git a/main.py b/main.py new file mode 100644 index 0000000..e9812cc --- /dev/null +++ b/main.py @@ -0,0 +1,23 @@ +from bot.config import BARS_APP_ID +from rich import print + + +async def main(): + print(BARS_APP_ID) + import bot.handlers.register + from bot.common import dp + from bot.utils.commands import set_commands + + bot.handlers.register.import_handlers() + await set_commands() + print('[green]Bot will start now[/]') + await dp.skip_updates() + await dp.start_polling() + + +if __name__ == '__main__': + import asyncio + try: + asyncio.run(main()) + except (KeyboardInterrupt, SystemExit, RuntimeError): + print('[red]Bot stopped[/]') diff --git a/replit.nix b/replit.nix new file mode 100644 index 0000000..70c75d3 --- /dev/null +++ b/replit.nix @@ -0,0 +1,20 @@ +{ pkgs }: { + deps = [ + pkgs.strace + pkgs.python310Full + pkgs.replitPackages.prybar-python310 + pkgs.replitPackages.stderred + ]; + env = { + PYTHON_LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [ + pkgs.stdenv.cc.cc.lib + pkgs.zlib + pkgs.glib + pkgs.xorg.libX11 + ]; + PYTHONBIN = "${pkgs.python310Full}/bin/python3.10"; + LANG = "en_US.UTF-8"; + STDERREDBIN = "${pkgs.replitPackages.stderred}/bin/stderred"; + PRYBAR_PYTHON_BIN = "${pkgs.replitPackages.prybar-python310}/bin/prybar-python310"; + }; +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..644d592 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +wheel +aiogram +python-dotenv +rich +aiohttp +validators +sqlitedict \ No newline at end of file