Compare commits
1 Commits
master
...
c9ff57deba
Author | SHA1 | Date | |
---|---|---|---|
c9ff57deba |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -155,8 +155,6 @@ cython_debug/
|
|||||||
# Custom
|
# Custom
|
||||||
config.json
|
config.json
|
||||||
*.session
|
*.session
|
||||||
*.session-wal
|
|
||||||
*.session-shm
|
|
||||||
*.session-journal
|
*.session-journal
|
||||||
|
|
||||||
venv
|
venv
|
||||||
|
11
.renovaterc
11
.renovaterc
@@ -2,16 +2,5 @@
|
|||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
"extends": [
|
"extends": [
|
||||||
"config:base"
|
"config:base"
|
||||||
],
|
|
||||||
"packageRules": [
|
|
||||||
{
|
|
||||||
"matchUpdateTypes": [
|
|
||||||
"minor",
|
|
||||||
"patch",
|
|
||||||
"pin",
|
|
||||||
"digest"
|
|
||||||
],
|
|
||||||
"automerge": true
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
@@ -1,28 +0,0 @@
|
|||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
from pyrogram.types import CallbackQuery
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class CallbackLanguage:
|
|
||||||
language: str
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_callback(cls, callback: CallbackQuery):
|
|
||||||
"""Parse callback query and extract language data from it.
|
|
||||||
|
|
||||||
### Args:
|
|
||||||
* callback (`CallbackQuery`): Callback query got from user interaction.
|
|
||||||
|
|
||||||
### Raises:
|
|
||||||
* `ValueError`: Raised when callback provided is not a language one.
|
|
||||||
|
|
||||||
### Returns:
|
|
||||||
* `CallbackLanguage`: Parsed callback query.
|
|
||||||
"""
|
|
||||||
action, language = str(callback.data).split(":")
|
|
||||||
|
|
||||||
if action.lower() != "language":
|
|
||||||
raise ValueError("Callback provided is not a language callback")
|
|
||||||
|
|
||||||
return cls(language)
|
|
@@ -1,27 +1,40 @@
|
|||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
from libbot.pyrogram.classes import PyroClient as LibPyroClient
|
from libbot.pyrogram.classes import PyroClient
|
||||||
from pyrogram.types import User
|
from pyrogram.types import User
|
||||||
|
|
||||||
# PyroClient uses MongoDB implementation of PyroUser
|
from classes.pyrouser import PyroUser
|
||||||
# but you can also select SQLite one below
|
from modules.database import col_users
|
||||||
# from classes.pyrouser_sqlite import PyroUser
|
|
||||||
from classes.pyrouser_mongo import PyroUser
|
|
||||||
|
|
||||||
|
|
||||||
class PyroClient(LibPyroClient):
|
class PyroClient(PyroClient):
|
||||||
async def find_user(self, user: Union[int, User]) -> PyroUser:
|
async def find_user(self, user: Union[int, User]) -> PyroUser:
|
||||||
"""Find User by its ID or User object.
|
"""Find User by it's ID or User object
|
||||||
|
|
||||||
### Args:
|
### Args:
|
||||||
* user (`Union[int, User]`): ID or User object to extract ID from.
|
* user (`Union[int, User]`): ID or User object to extract ID from
|
||||||
|
|
||||||
### Returns:
|
### Returns:
|
||||||
* `PyroUser`: User in database representation.
|
* `PyroUser`: PyroUser object
|
||||||
"""
|
"""
|
||||||
|
if (
|
||||||
|
col_users.find_one({"id": user.id if isinstance(user, User) else user})
|
||||||
|
is None
|
||||||
|
):
|
||||||
|
col_users.insert_one(
|
||||||
|
{
|
||||||
|
"id": user.id if isinstance(user, User) else user,
|
||||||
|
"locale": user.language_code if isinstance(user, User) else None,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
db_record = col_users.find_one(
|
||||||
await PyroUser.find(user)
|
{"id": user.id if isinstance(user, User) else user}
|
||||||
if isinstance(user, int)
|
|
||||||
else await PyroUser.find(user.id, locale=user.language_code)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if db_record is None:
|
||||||
|
raise TypeError(
|
||||||
|
f"User with ID {user.id if isinstance(user, User) else user} was not found in the database"
|
||||||
|
)
|
||||||
|
|
||||||
|
return PyroUser(**db_record)
|
||||||
|
23
classes/pyrouser.py
Normal file
23
classes/pyrouser.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
from typing import Union
|
||||||
|
|
||||||
|
from attrs import define
|
||||||
|
from bson import ObjectId
|
||||||
|
|
||||||
|
from modules.database import col_users
|
||||||
|
|
||||||
|
|
||||||
|
@define
|
||||||
|
class PyroUser:
|
||||||
|
"""Dataclass of DB entry of a user"""
|
||||||
|
|
||||||
|
_id: ObjectId
|
||||||
|
id: int
|
||||||
|
locale: Union[str, None]
|
||||||
|
|
||||||
|
async def update_locale(self, locale: str) -> None:
|
||||||
|
"""Change user's locale stored in the database
|
||||||
|
|
||||||
|
### Args:
|
||||||
|
* locale (`str`): New locale to be set
|
||||||
|
"""
|
||||||
|
col_users.update_one({"_id": self._id}, {"$set": {"locale": locale}})
|
@@ -1,55 +0,0 @@
|
|||||||
from dataclasses import dataclass
|
|
||||||
from logging import Logger
|
|
||||||
from typing import Union
|
|
||||||
|
|
||||||
from bson import ObjectId
|
|
||||||
|
|
||||||
from modules.database_mongo import col_users
|
|
||||||
from modules.logging_utils import get_logger
|
|
||||||
|
|
||||||
logger: Logger = get_logger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class PyroUser:
|
|
||||||
"""Dataclass of DB entry of a user"""
|
|
||||||
|
|
||||||
__slots__ = ("_id", "id", "locale")
|
|
||||||
|
|
||||||
_id: ObjectId
|
|
||||||
id: int
|
|
||||||
locale: Union[str, None]
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def find(cls, id: int, locale: Union[str, None] = None):
|
|
||||||
"""Find user in database and create new record if user does not exist.
|
|
||||||
|
|
||||||
### Args:
|
|
||||||
* id (`int`): User's Telegram ID
|
|
||||||
* locale (`Union[str, None]`, *optional*): User's locale. Defaults to `None`.
|
|
||||||
|
|
||||||
### Raises:
|
|
||||||
* `RuntimeError`: Raised when user entry after insertion could not be found.
|
|
||||||
|
|
||||||
### Returns:
|
|
||||||
* `PyroUser`: User with its database data.
|
|
||||||
"""
|
|
||||||
db_entry = await col_users.find_one({"id": id})
|
|
||||||
|
|
||||||
if db_entry is None:
|
|
||||||
inserted = await col_users.insert_one({"id": id, "locale": locale})
|
|
||||||
db_entry = await col_users.find_one({"_id": inserted.inserted_id})
|
|
||||||
|
|
||||||
if db_entry is None:
|
|
||||||
raise RuntimeError("Could not find inserted user entry.")
|
|
||||||
|
|
||||||
return cls(**db_entry)
|
|
||||||
|
|
||||||
async def update_locale(self, locale: Union[str, None]) -> None:
|
|
||||||
"""Change user's locale stored in the database.
|
|
||||||
|
|
||||||
### Args:
|
|
||||||
* locale (`Union[str, None]`): New locale to be set.
|
|
||||||
"""
|
|
||||||
logger.debug("%s's locale has been set to %s", self.id, locale)
|
|
||||||
await col_users.update_one({"_id": self._id}, {"$set": {"locale": locale}})
|
|
@@ -1,57 +0,0 @@
|
|||||||
import logging
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from typing import Union
|
|
||||||
|
|
||||||
from modules.database_sqlite import cursor
|
|
||||||
from modules.logging_utils import get_logger
|
|
||||||
|
|
||||||
logger: logging.Logger = get_logger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class PyroUser:
|
|
||||||
"""Dataclass of DB entry of a user"""
|
|
||||||
|
|
||||||
__slots__ = ("id", "locale")
|
|
||||||
|
|
||||||
id: int
|
|
||||||
locale: Union[str, None]
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def find(cls, id: int, locale: Union[str, None] = None):
|
|
||||||
"""Find user in database and create new record if user does not exist.
|
|
||||||
|
|
||||||
### Args:
|
|
||||||
* id (`int`): User's Telegram ID
|
|
||||||
* locale (`Union[str, None]`, *optional*): User's locale. Defaults to `None`.
|
|
||||||
|
|
||||||
### Raises:
|
|
||||||
* `RuntimeError`: Raised when user entry after insertion could not be found.
|
|
||||||
|
|
||||||
### Returns:
|
|
||||||
* `PyroUser`: User with its database data.
|
|
||||||
"""
|
|
||||||
db_entry = cursor.execute("SELECT id, locale FROM users WHERE id = ?", (id,)).fetchone()
|
|
||||||
|
|
||||||
if db_entry is None:
|
|
||||||
cursor.execute("INSERT INTO users VALUES (?, ?)", (id, locale))
|
|
||||||
cursor.connection.commit()
|
|
||||||
db_entry = cursor.execute("SELECT id, locale FROM users WHERE id = ?", (id,)).fetchone()
|
|
||||||
|
|
||||||
if db_entry is None:
|
|
||||||
raise RuntimeError("Could not find inserted user entry.")
|
|
||||||
|
|
||||||
return cls(*db_entry)
|
|
||||||
|
|
||||||
async def update_locale(self, locale: Union[str, None]) -> None:
|
|
||||||
"""Change user's locale stored in the database.
|
|
||||||
|
|
||||||
### Args:
|
|
||||||
* locale (`Union[str, None]`): New locale to be set.
|
|
||||||
"""
|
|
||||||
logger.debug("%s's locale has been set to %s", self.id, locale)
|
|
||||||
cursor.execute(
|
|
||||||
"UPDATE users SET locale = ? WHERE id = ?",
|
|
||||||
(locale, self.id),
|
|
||||||
)
|
|
||||||
cursor.connection.commit()
|
|
@@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"start": {
|
|
||||||
"scopes": [
|
|
||||||
{
|
|
||||||
"name": "BotCommandScopeDefault"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "BotCommandScopeChat",
|
|
||||||
"chat_id": "owner"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"shutdown": {
|
|
||||||
"scopes": [
|
|
||||||
{
|
|
||||||
"name": "BotCommandScopeChat",
|
|
||||||
"chat_id": "owner"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"remove_commands": {
|
|
||||||
"scopes": [
|
|
||||||
{
|
|
||||||
"name": "BotCommandScopeChat",
|
|
||||||
"chat_id": "owner"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"debug": false,
|
|
||||||
"locale": "en",
|
"locale": "en",
|
||||||
"bot": {
|
"bot": {
|
||||||
"owner": 0,
|
"owner": 0,
|
||||||
@@ -20,5 +19,15 @@
|
|||||||
"reports": {
|
"reports": {
|
||||||
"chat_id": "owner"
|
"chat_id": "owner"
|
||||||
},
|
},
|
||||||
"disabled_plugins": []
|
"disabled_plugins": [],
|
||||||
|
"commands": {
|
||||||
|
"start": {
|
||||||
|
"scopes": [
|
||||||
|
{
|
||||||
|
"name": "BotCommandScopeChat",
|
||||||
|
"chat_id": "owner"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -1,24 +1,5 @@
|
|||||||
{
|
{
|
||||||
"metadata": {
|
|
||||||
"flag": "🇬🇧",
|
|
||||||
"name": "English",
|
|
||||||
"codes": [
|
|
||||||
"en",
|
|
||||||
"en-US",
|
|
||||||
"en-GB"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"commands": {
|
"commands": {
|
||||||
"start": "Start using the bot",
|
"start": "Start using the bot"
|
||||||
"shutdown": "Turn off the bot",
|
|
||||||
"language": "Change bot's language",
|
|
||||||
"remove_commands": "Unregister all commands"
|
|
||||||
},
|
|
||||||
"messages": {
|
|
||||||
"start": "Welcome! I'm your bot!",
|
|
||||||
"locale_choice": "Alright. Please choose the language using keyboard below."
|
|
||||||
},
|
|
||||||
"callbacks": {
|
|
||||||
"locale_set": "Your language now is: {locale}"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,23 +1,5 @@
|
|||||||
{
|
{
|
||||||
"metadata": {
|
|
||||||
"flag": "🇺🇦",
|
|
||||||
"name": "Українська",
|
|
||||||
"codes": [
|
|
||||||
"uk",
|
|
||||||
"uk-UA"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"commands": {
|
"commands": {
|
||||||
"start": "Почати користуватись ботом",
|
"start": "Почати користуватись ботом"
|
||||||
"shutdown": "Вимкнути бота",
|
|
||||||
"language": "Змінити мову бота",
|
|
||||||
"remove_commands": "Видалити всі команди"
|
|
||||||
},
|
|
||||||
"messages": {
|
|
||||||
"start": "Привіт! Я твій бот!",
|
|
||||||
"locale_choice": "Гаразд. Будь ласка, оберіть мову за допомогою клавіатури нижче."
|
|
||||||
},
|
|
||||||
"callbacks": {
|
|
||||||
"locale_set": "Встановлено мову: {locale}"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
46
main.py
46
main.py
@@ -1,20 +1,9 @@
|
|||||||
import contextlib
|
import contextlib
|
||||||
import logging.config
|
import logging
|
||||||
from argparse import ArgumentParser
|
from os import getpid
|
||||||
from logging import Logger
|
|
||||||
from os import getpid, makedirs
|
|
||||||
from pathlib import Path
|
|
||||||
from sys import exit
|
|
||||||
|
|
||||||
from libbot.utils import json_read
|
from libbot.pyrogram.classes import PyroClient
|
||||||
|
|
||||||
from classes.pyroclient import PyroClient
|
|
||||||
from modules.logging_utils import get_logger, get_logging_config
|
|
||||||
|
|
||||||
# Main uses MongoDB implementation of DB,
|
|
||||||
# but you can also select SQLite one below
|
|
||||||
# from modules.migrator_sqlite import migrate_database
|
|
||||||
from modules.migrator_mongo import migrate_database
|
|
||||||
from modules.scheduler import scheduler
|
from modules.scheduler import scheduler
|
||||||
|
|
||||||
# Uncomment this and the line below client declaration
|
# Uncomment this and the line below client declaration
|
||||||
@@ -24,21 +13,13 @@ from modules.scheduler import scheduler
|
|||||||
# from convopyro import Conversation
|
# from convopyro import Conversation
|
||||||
|
|
||||||
|
|
||||||
makedirs(Path("logs/"), exist_ok=True)
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
logging.config.dictConfig(get_logging_config())
|
format="%(name)s.%(funcName)s | %(levelname)s | %(message)s",
|
||||||
|
datefmt="[%X]",
|
||||||
logger: Logger = get_logger(__name__)
|
|
||||||
|
|
||||||
parser = ArgumentParser(
|
|
||||||
prog="__name__",
|
|
||||||
description="__description__",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Remove if no database is being used
|
logger = logging.getLogger(__name__)
|
||||||
parser.add_argument("--migrate", action="store_true")
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
with contextlib.suppress(ImportError):
|
with contextlib.suppress(ImportError):
|
||||||
import uvloop
|
import uvloop
|
||||||
@@ -47,22 +28,13 @@ with contextlib.suppress(ImportError):
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# Remove if no database is being used
|
client = PyroClient(scheduler=scheduler)
|
||||||
if args.migrate:
|
|
||||||
migrate_database()
|
|
||||||
logger.info("Migration finished. Exiting...")
|
|
||||||
exit()
|
|
||||||
|
|
||||||
client = PyroClient(scheduler=scheduler, commands_source=json_read(Path("commands.json")))
|
|
||||||
# Conversation(client)
|
# Conversation(client)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
client.run()
|
client.run()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
logger.warning("Forcefully shutting down with PID %s...", getpid())
|
logger.warning("Forcefully shutting down with PID %s...", getpid())
|
||||||
except Exception as exc:
|
|
||||||
logger.error("An unexpected exception has occurred: %s", exc, exc_info=exc)
|
|
||||||
exit(1)
|
|
||||||
finally:
|
finally:
|
||||||
if client.scheduler is not None:
|
if client.scheduler is not None:
|
||||||
client.scheduler.shutdown()
|
client.scheduler.shutdown()
|
||||||
|
32
modules/database.py
Normal file
32
modules/database.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
"""Module that provides all database columns"""
|
||||||
|
|
||||||
|
from pymongo import MongoClient
|
||||||
|
from ujson import loads
|
||||||
|
|
||||||
|
with open("config.json", "r", encoding="utf-8") as f:
|
||||||
|
db_config = loads(f.read())["database"]
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
if db_config["user"] is not None and db_config["password"] is not None:
|
||||||
|
con_string = "mongodb://{0}:{1}@{2}:{3}/{4}".format(
|
||||||
|
db_config["user"],
|
||||||
|
db_config["password"],
|
||||||
|
db_config["host"],
|
||||||
|
db_config["port"],
|
||||||
|
db_config["name"],
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
con_string = "mongodb://{0}:{1}/{2}".format(
|
||||||
|
db_config["host"], db_config["port"], db_config["name"]
|
||||||
|
)
|
||||||
|
|
||||||
|
db_client = MongoClient(con_string)
|
||||||
|
db = db_client.get_database(name=db_config["name"])
|
||||||
|
|
||||||
|
collections = db.list_collection_names()
|
||||||
|
|
||||||
|
for collection in ["users"]:
|
||||||
|
if collection not in collections:
|
||||||
|
db.create_collection(collection)
|
||||||
|
|
||||||
|
col_users = db.get_collection("users")
|
@@ -1,24 +0,0 @@
|
|||||||
"""Module that provides all database collections"""
|
|
||||||
|
|
||||||
from typing import Any, Mapping
|
|
||||||
|
|
||||||
from async_pymongo import AsyncClient, AsyncCollection, AsyncDatabase
|
|
||||||
from libbot.utils import config_get
|
|
||||||
|
|
||||||
db_config: Mapping[str, Any] = config_get("database")
|
|
||||||
|
|
||||||
if db_config["user"] is not None and db_config["password"] is not None:
|
|
||||||
con_string = "mongodb://{0}:{1}@{2}:{3}/{4}".format(
|
|
||||||
db_config["user"],
|
|
||||||
db_config["password"],
|
|
||||||
db_config["host"],
|
|
||||||
db_config["port"],
|
|
||||||
db_config["name"],
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
con_string = "mongodb://{0}:{1}/{2}".format(db_config["host"], db_config["port"], db_config["name"])
|
|
||||||
|
|
||||||
db_client = AsyncClient(con_string)
|
|
||||||
db: AsyncDatabase = db_client.get_database(name=db_config["name"])
|
|
||||||
|
|
||||||
col_users: AsyncCollection = db.get_collection("users")
|
|
@@ -1,11 +0,0 @@
|
|||||||
"""Module that provides database access"""
|
|
||||||
|
|
||||||
import sqlite3
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from libbot.utils import config_get
|
|
||||||
|
|
||||||
db: sqlite3.Connection = sqlite3.connect(Path(config_get("database")))
|
|
||||||
cursor: sqlite3.Cursor = db.cursor()
|
|
||||||
|
|
||||||
cursor.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER, card TEXT, locale TEXT)")
|
|
@@ -1,35 +0,0 @@
|
|||||||
import logging
|
|
||||||
from logging import Logger
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Any, Dict
|
|
||||||
|
|
||||||
from libbot.utils import config_get
|
|
||||||
|
|
||||||
|
|
||||||
def get_logging_config() -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
"version": 1,
|
|
||||||
"disable_existing_loggers": False,
|
|
||||||
"handlers": {
|
|
||||||
"file": {
|
|
||||||
"class": "logging.handlers.RotatingFileHandler",
|
|
||||||
"filename": str(Path("logs/latest.log")),
|
|
||||||
"maxBytes": 500000,
|
|
||||||
"backupCount": 10,
|
|
||||||
"formatter": "simple",
|
|
||||||
},
|
|
||||||
"console": {"class": "logging.StreamHandler", "formatter": "systemd"},
|
|
||||||
},
|
|
||||||
"formatters": {
|
|
||||||
"simple": {"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"},
|
|
||||||
"systemd": {"format": "%(name)s - %(levelname)s - %(message)s"},
|
|
||||||
},
|
|
||||||
"root": {
|
|
||||||
"level": "DEBUG" if config_get("debug") else "INFO",
|
|
||||||
"handlers": ["file", "console"],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def get_logger(name: str) -> Logger:
|
|
||||||
return logging.getLogger(name)
|
|
@@ -1,22 +0,0 @@
|
|||||||
from typing import Any, Mapping
|
|
||||||
|
|
||||||
from libbot.utils import config_get
|
|
||||||
from mongodb_migrations.cli import MigrationManager
|
|
||||||
from mongodb_migrations.config import Configuration
|
|
||||||
|
|
||||||
|
|
||||||
def migrate_database() -> None:
|
|
||||||
"""Apply migrations from folder `migrations/` to the database"""
|
|
||||||
db_config: Mapping[str, Any] = config_get("database")
|
|
||||||
|
|
||||||
manager_config = Configuration(
|
|
||||||
{
|
|
||||||
"mongo_host": db_config["host"],
|
|
||||||
"mongo_port": db_config["port"],
|
|
||||||
"mongo_database": db_config["name"],
|
|
||||||
"mongo_username": db_config["user"],
|
|
||||||
"mongo_password": db_config["password"],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
manager = MigrationManager(manager_config)
|
|
||||||
manager.run()
|
|
@@ -1,24 +0,0 @@
|
|||||||
from os import rename
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Mapping
|
|
||||||
|
|
||||||
from libbot.utils import json_read
|
|
||||||
|
|
||||||
from modules.database_sqlite import cursor
|
|
||||||
|
|
||||||
|
|
||||||
def migrate_database() -> None:
|
|
||||||
"""Apply migrations from old JSON database to SQLite"""
|
|
||||||
if not Path("data/database.json").exists():
|
|
||||||
return
|
|
||||||
|
|
||||||
db_old: Mapping[str, Mapping[str, str]] = json_read(Path("data/database.json"))
|
|
||||||
|
|
||||||
for user, keys in db_old.items():
|
|
||||||
user_locale = None if "locale" not in keys else keys["locale"]
|
|
||||||
user_card = None if "card" not in keys else keys["card"]
|
|
||||||
|
|
||||||
cursor.execute("INSERT INTO users VALUES (?, ?)", (int(user), user_card, user_locale))
|
|
||||||
|
|
||||||
cursor.connection.commit()
|
|
||||||
rename(Path("data/database.json"), Path("data/database.migrated.json"))
|
|
@@ -1,9 +1,10 @@
|
|||||||
from pyrogram import filters
|
from pyrogram import filters
|
||||||
|
from pyrogram.client import Client
|
||||||
from pyrogram.types import CallbackQuery
|
from pyrogram.types import CallbackQuery
|
||||||
|
|
||||||
from classes.pyroclient import PyroClient
|
from classes.pyroclient import PyroClient
|
||||||
|
|
||||||
|
|
||||||
@PyroClient.on_callback_query(filters.regex("nothing")) # type: ignore
|
@Client.on_callback_query(filters.regex("nothing")) # type: ignore
|
||||||
async def callback_nothing(app: PyroClient, callback: CallbackQuery):
|
async def callback_nothing(app: PyroClient, clb: CallbackQuery):
|
||||||
await callback.answer(text=app._("nothing", "callbacks", locale=callback.from_user.language_code))
|
await clb.answer(text="Nothing here...")
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
from pyrogram import filters
|
from pyrogram import filters
|
||||||
|
from pyrogram.client import Client
|
||||||
from pyrogram.types import Message
|
from pyrogram.types import Message
|
||||||
|
|
||||||
from classes.pyroclient import PyroClient
|
from classes.pyroclient import PyroClient
|
||||||
|
|
||||||
|
|
||||||
@PyroClient.on_message(
|
@Client.on_message(
|
||||||
~filters.scheduled & filters.private & filters.command(["start"], prefixes=["/"]) # type: ignore
|
~filters.scheduled & filters.private & filters.command(["start"], prefixes=["/"]) # type: ignore
|
||||||
)
|
)
|
||||||
async def command_start(app: PyroClient, message: Message):
|
async def command_start(app: PyroClient, msg: Message):
|
||||||
await message.reply_text(app._("start", "messages", locale=message.from_user.language_code))
|
await msg.reply_text("Welcome! I'm your bot!")
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
from pyrogram import filters
|
from pyrogram import filters
|
||||||
|
from pyrogram.client import Client
|
||||||
from pyrogram.types import Message
|
from pyrogram.types import Message
|
||||||
|
|
||||||
from classes.pyroclient import PyroClient
|
from classes.pyroclient import PyroClient
|
||||||
|
|
||||||
|
|
||||||
@PyroClient.on_message(
|
@Client.on_message(
|
||||||
~filters.scheduled & filters.private & filters.command(["remove_commands"], prefixes=["/"]) # type: ignore
|
~filters.scheduled & filters.private & filters.command(["remove_commands"], prefixes=["/"]) # type: ignore
|
||||||
)
|
)
|
||||||
async def command_remove_commands(app: PyroClient, message: Message):
|
async def command_remove_commands(app: PyroClient, msg: Message):
|
||||||
await message.reply_text("Okay.")
|
await msg.reply_text("Okay.")
|
||||||
await app.remove_commands(command_sets=await app.collect_commands())
|
await app.remove_commands(command_sets=await app.collect_commands())
|
||||||
|
@@ -1,16 +0,0 @@
|
|||||||
import asyncio
|
|
||||||
|
|
||||||
from pyrogram import filters
|
|
||||||
from pyrogram.types import Message
|
|
||||||
|
|
||||||
from classes.pyroclient import PyroClient
|
|
||||||
|
|
||||||
|
|
||||||
@PyroClient.on_message(
|
|
||||||
~filters.scheduled
|
|
||||||
& filters.private
|
|
||||||
& filters.command(["shutdown", "reboot", "restart"], prefixes=["/"]) # type: ignore
|
|
||||||
)
|
|
||||||
async def command_shutdown(app: PyroClient, msg: Message):
|
|
||||||
if msg.from_user.id == app.owner:
|
|
||||||
asyncio.get_event_loop().create_task(app.stop())
|
|
@@ -1,9 +1,10 @@
|
|||||||
from pyrogram import filters
|
from pyrogram import filters
|
||||||
|
from pyrogram.client import Client
|
||||||
from pyrogram.types import Message
|
from pyrogram.types import Message
|
||||||
|
|
||||||
from classes.pyroclient import PyroClient
|
from classes.pyroclient import PyroClient
|
||||||
|
|
||||||
|
|
||||||
@PyroClient.on_message(filters.text & filters.private) # type: ignore
|
@Client.on_message(filters.text & filters.private) # type: ignore
|
||||||
async def handler_echo(app: PyroClient, message: Message):
|
async def handler_echo(app: PyroClient, message: Message):
|
||||||
await message.reply(message.text[::-1])
|
await message.reply(message.text[::-1])
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
from pyrogram.client import Client
|
||||||
from pyrogram.types import (
|
from pyrogram.types import (
|
||||||
InlineQuery,
|
InlineQuery,
|
||||||
InlineQueryResultArticle,
|
InlineQueryResultArticle,
|
||||||
@@ -7,7 +8,7 @@ from pyrogram.types import (
|
|||||||
from classes.pyroclient import PyroClient
|
from classes.pyroclient import PyroClient
|
||||||
|
|
||||||
|
|
||||||
@PyroClient.on_inline_query() # type: ignore
|
@Client.on_inline_query() # type: ignore
|
||||||
async def inline(app: PyroClient, inline_query: InlineQuery):
|
async def inline(app: PyroClient, inline_query: InlineQuery):
|
||||||
await inline_query.answer(
|
await inline_query.answer(
|
||||||
results=[
|
results=[
|
||||||
|
@@ -1,24 +1,23 @@
|
|||||||
from typing import List
|
|
||||||
|
|
||||||
from pykeyboard import InlineButton, InlineKeyboard
|
from pykeyboard import InlineButton, InlineKeyboard
|
||||||
from pyrogram import filters
|
from pyrogram import filters
|
||||||
|
from pyrogram.client import Client
|
||||||
from pyrogram.types import CallbackQuery, Message
|
from pyrogram.types import CallbackQuery, Message
|
||||||
|
|
||||||
from classes.callbacks import CallbackLanguage
|
|
||||||
from classes.pyroclient import PyroClient
|
from classes.pyroclient import PyroClient
|
||||||
|
|
||||||
|
|
||||||
@PyroClient.on_message(
|
@Client.on_message(
|
||||||
~filters.scheduled & filters.private & filters.command(["language"], prefixes=["/"]) # type: ignore
|
~filters.scheduled & filters.private & filters.command(["language"], prefixes=["/"]) # type: ignore
|
||||||
)
|
)
|
||||||
async def command_language(app: PyroClient, message: Message):
|
async def command_language(app: PyroClient, message: Message):
|
||||||
user = await app.find_user(message.from_user)
|
user = await app.find_user(message.from_user)
|
||||||
|
|
||||||
keyboard = InlineKeyboard(row_width=2)
|
keyboard = InlineKeyboard(row_width=2)
|
||||||
buttons: List[InlineButton] = []
|
buttons = []
|
||||||
|
|
||||||
for locale, data in app.in_every_locale("metadata").items():
|
for locale, data in app.in_every_locale("metadata").items():
|
||||||
buttons.append(InlineButton(f"{data['flag']} {data['name']}", f"language:{locale}"))
|
buttons.append(
|
||||||
|
InlineButton(f"{data['flag']} {data['name']}", f"language:{locale}")
|
||||||
|
)
|
||||||
|
|
||||||
keyboard.add(*buttons)
|
keyboard.add(*buttons)
|
||||||
|
|
||||||
@@ -28,16 +27,16 @@ async def command_language(app: PyroClient, message: Message):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@PyroClient.on_callback_query(filters.regex(r"language:[\s\S]*")) # type: ignore
|
@Client.on_callback_query(filters.regex(r"language:[\s\S]*")) # type: ignore
|
||||||
async def callback_language(app: PyroClient, callback: CallbackQuery):
|
async def callback_language(app: PyroClient, callback: CallbackQuery):
|
||||||
user = await app.find_user(callback.from_user)
|
user = await app.find_user(callback.from_user)
|
||||||
parsed = CallbackLanguage.from_callback(callback)
|
language = str(callback.data).split(":")[1]
|
||||||
|
|
||||||
await user.update_locale(parsed.language)
|
await user.update_locale(language)
|
||||||
|
|
||||||
await callback.answer(
|
await callback.answer(
|
||||||
app._("locale_set", "callbacks", locale=parsed.language).format(
|
app._("locale_set", "callbacks", locale=language).format(
|
||||||
locale=app._("name", "metadata", locale=parsed.language)
|
locale=app._("name", "metadata", locale=language)
|
||||||
),
|
),
|
||||||
show_alert=True,
|
show_alert=True,
|
||||||
)
|
)
|
||||||
|
@@ -1,25 +0,0 @@
|
|||||||
[project]
|
|
||||||
name = "PyrogramBotBase"
|
|
||||||
authors = [{ name = "Profitroll" }]
|
|
||||||
readme = "README.md"
|
|
||||||
requires-python = ">=3.11"
|
|
||||||
|
|
||||||
[tool.black]
|
|
||||||
line-length = 108
|
|
||||||
target-version = ["py311", "py312", "py313"]
|
|
||||||
|
|
||||||
[tool.isort]
|
|
||||||
profile = "black"
|
|
||||||
|
|
||||||
[tool.mypy]
|
|
||||||
namespace_packages = true
|
|
||||||
install_types = true
|
|
||||||
strict = true
|
|
||||||
show_error_codes = true
|
|
||||||
|
|
||||||
[tool.pylint]
|
|
||||||
disable = ["line-too-long"]
|
|
||||||
|
|
||||||
[tool.pylint.main]
|
|
||||||
extension-pkg-whitelist = ["ujson"]
|
|
||||||
py-version = 3.11
|
|
@@ -1,10 +1,13 @@
|
|||||||
apscheduler~=3.11.0
|
aiofiles~=23.2.1
|
||||||
async_pymongo==0.1.9
|
apscheduler~=3.10.1
|
||||||
|
attrs~=23.1.0
|
||||||
|
black~=23.7.0
|
||||||
convopyro==0.5
|
convopyro==0.5
|
||||||
libbot[speed,pyrogram]==4.1.0
|
pykeyboard==0.1.5
|
||||||
tgcrypto-pyrofork==1.2.7
|
pymongo==4.4.1
|
||||||
uvloop==0.21.0
|
pyrogram==2.0.106
|
||||||
# If uses MongoDB:
|
tgcrypto==1.2.5
|
||||||
mongodb-migrations==1.3.1
|
ujson==5.8.0
|
||||||
|
uvloop==0.17.0
|
||||||
--extra-index-url https://git.end-play.xyz/api/packages/profitroll/pypi/simple
|
--extra-index-url https://git.end-play.xyz/api/packages/profitroll/pypi/simple
|
||||||
pykeyboard==0.1.7
|
libbot[speed,pyrogram]==2.0.0
|
@@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
"$jsonSchema": {
|
|
||||||
"required": [
|
|
||||||
"id",
|
|
||||||
"locale"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"user": {
|
|
||||||
"bsonType": [
|
|
||||||
"int",
|
|
||||||
"long"
|
|
||||||
],
|
|
||||||
"description": "Telegram ID of user"
|
|
||||||
},
|
|
||||||
"locale": {
|
|
||||||
"bsonType": [
|
|
||||||
"string",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"description": "Preferred messages language according to user's preference"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user