Skeleton of project - database loading
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
/.idea/
|
||||||
|
/tests/
|
||||||
|
.env
|
||||||
|
/db
|
||||||
|
/dbb
|
||||||
|
/dbmeta
|
||||||
37
.replit
Normal file
37
.replit
Normal 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
1
bot/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pass
|
||||||
8
bot/common.py
Normal file
8
bot/common.py
Normal 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
13
bot/config.py
Normal 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
2
bot/db/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from .db import db
|
||||||
|
from .db_model import DBTables
|
||||||
12
bot/db/db.py
Normal file
12
bot/db/db.py
Normal 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
32
bot/db/db_model.py
Normal 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
72
bot/db/meta.py
Normal 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
49
bot/db/pull_db.py
Normal 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
1
bot/handlers/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pass
|
||||||
1
bot/handlers/help/__init__.py
Normal file
1
bot/handlers/help/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pass
|
||||||
23
bot/handlers/help/help.py
Normal file
23
bot/handlers/help/help.py
Normal 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()}")
|
||||||
2
bot/handlers/help/help_strings.py
Normal file
2
bot/handlers/help/help_strings.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
help_data = {
|
||||||
|
}
|
||||||
1
bot/handlers/initialize/__init__.py
Normal file
1
bot/handlers/initialize/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pass
|
||||||
7
bot/handlers/initialize/pull_db.py
Normal file
7
bot/handlers/initialize/pull_db.py
Normal 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
11
bot/handlers/register.py
Normal 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
0
bot/modules/__init__.py
Normal file
0
bot/utils/__init__.py
Normal file
0
bot/utils/__init__.py
Normal file
20
bot/utils/commands.py
Normal file
20
bot/utils/commands.py
Normal 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
23
main.py
Normal 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
20
replit.nix
Normal 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
7
requirements.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
wheel
|
||||||
|
aiogram
|
||||||
|
python-dotenv
|
||||||
|
rich
|
||||||
|
aiohttp
|
||||||
|
validators
|
||||||
|
sqlitedict
|
||||||
Reference in New Issue
Block a user