Skeleton of project - database loading

This commit is contained in:
BarsTiger
2023-02-13 19:51:25 +02:00
commit f778d3f71c
23 changed files with 348 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
/.idea/
/tests/
.env
/db
/dbb
/dbmeta

37
.replit Normal file
View File

@@ -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"

1
bot/__init__.py Normal file
View File

@@ -0,0 +1 @@
pass

8
bot/common.py Normal file
View File

@@ -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)

13
bot/config.py Normal file
View File

@@ -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'

2
bot/db/__init__.py Normal file
View File

@@ -0,0 +1,2 @@
from .db import db
from .db_model import DBTables

12
bot/db/db.py Normal file
View File

@@ -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')
}

32
bot/db/db_model.py Normal file
View File

@@ -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')
)

72
bot/db/meta.py Normal file
View File

@@ -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

49
bot/db/pull_db.py Normal file
View File

@@ -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')

1
bot/handlers/__init__.py Normal file
View File

@@ -0,0 +1 @@
pass

View File

@@ -0,0 +1 @@
pass

23
bot/handlers/help/help.py Normal file
View File

@@ -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()}")

View File

@@ -0,0 +1,2 @@
help_data = {
}

View File

@@ -0,0 +1 @@
pass

View File

@@ -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()

11
bot/handlers/register.py Normal file
View File

@@ -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[/]')

0
bot/modules/__init__.py Normal file
View File

0
bot/utils/__init__.py Normal file
View File

20
bot/utils/commands.py Normal file
View File

@@ -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[/]')

23
main.py Normal file
View File

@@ -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[/]')

20
replit.nix Normal file
View File

@@ -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";
};
}

7
requirements.txt Normal file
View File

@@ -0,0 +1,7 @@
wheel
aiogram
python-dotenv
rich
aiohttp
validators
sqlitedict