This commit is contained in:
djimbo
2022-08-13 09:18:51 +03:00
commit 562d7f1acf
34 changed files with 1236 additions and 0 deletions
+189
View File
@@ -0,0 +1,189 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# IPython Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# dotenv
.env
# virtualenv
venv/
ENV/
# Spyder project settings
.spyderproject
# Rope project settings
.ropeproject
### VirtualEnv template
# Virtualenv
# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
[Bb]in
[Ii]nclude
[Ll]ib
[Ll]ib64
[Ll]ocal
[Ss]cripts
pyvenv.cfg
.venv
pip-selfcheck.json
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
auto-import.
.idea/artifacts
.idea/compiler.xml
.idea/jarRepositories.xml
.idea/modules.xml
.idea/*.iml
.idea/modules
*.iml
*.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
# idea folder, uncomment if you don't need it
.idea
# Users exceptions
/other/
/tgbot/data/database.db
/tgbot/data/logs.log
settings.ini
+72
View File
@@ -0,0 +1,72 @@
# - *- coding: utf- 8 - *-
import asyncio
import os
import sys
import colorama
from aiogram import Bot, Dispatcher
from tgbot.config import BOT_TOKEN, scheduler, get_admins
from tgbot.middlewares import register_all_middlwares
from tgbot.routers import register_all_routers
from tgbot.services.api_session import RequestsSession
from tgbot.services.api_sqlite import create_dbx
from tgbot.utils.misc.bot_commands import set_commands
from tgbot.utils.misc.bot_logging import bot_logger
from tgbot.utils.misc_functions import autobackup, startup_notify
colorama.init()
# Запуск шедулеров
async def scheduler_start(bot):
scheduler.add_job(autobackup, "cron", hour=00, args=(bot,)) # Автобэкап в 12:00
scheduler.add_job(autobackup, "cron", hour=12, args=(bot,)) # Автобэкап в 00:00
# Запуск бота и функций
async def main():
scheduler.start() # Запуск Шедулера
dp = Dispatcher() # Создание Диспетчера
rSession = RequestsSession() # Асинхронные Запросы
bot = Bot(token=BOT_TOKEN, parse_mode="HTML") # Образ Бота
register_all_middlwares(dp) # Регистрация всех мидлварей
register_all_routers(dp) # Регистрация всех роутеров
try:
await set_commands(bot)
await startup_notify(bot)
await scheduler_start(bot)
bot_logger.warning("Bot was started")
print(colorama.Fore.LIGHTYELLOW_EX + "~~~~~ Bot was started ~~~~~")
print(colorama.Fore.LIGHTBLUE_EX + "~~~~~ TG developer: @djimbox ~~~~~")
print(colorama.Fore.RESET)
if len(get_admins()) == 0: print("***** ENTER ADMIN ID IN settings.ini *****")
await bot.delete_webhook()
await bot.get_updates(offset=-1)
await dp.start_polling(bot, allowed_updates=dp.resolve_used_update_types(), rSession=rSession)
finally:
await rSession.close()
await bot.session.close()
if __name__ == "__main__":
create_dbx()
try:
# Исправление "RuntimeError: Event loop is closed" для Windows
# if sys.version_info[0] == 3 and sys.version_info[1] >= 8 and sys.platform.startswith("win"):
# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
asyncio.run(main())
except (KeyboardInterrupt, SystemExit):
bot_logger.warning("Bot was stopped")
finally:
if sys.platform.startswith("win"):
os.system("cls")
else:
os.system("clear")
+5
View File
@@ -0,0 +1,5 @@
colorlog
aiogram>=3.0.0b2
aiohttp
typing
APScheduler==3.8.0
+1
View File
@@ -0,0 +1 @@
+36
View File
@@ -0,0 +1,36 @@
# - *- coding: utf- 8 - *-
import configparser
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from tgbot.utils.const_functions import clear_list
scheduler = AsyncIOScheduler()
read_config = configparser.ConfigParser()
read_config.read("settings.ini")
BOT_TOKEN = read_config["settings"]["token"].strip()
PATH_DATABASE = "tgbot/data/database.db" # Путь к БД
PATH_LOGS = "tgbot/data/logs.log" # Путь к Логам
# Получение администраторов бота
def get_admins():
read_admins = configparser.ConfigParser()
read_admins.read("settings.ini")
admins = read_admins["settings"]["admin_id"].strip()
admins = admins.replace(" ", "")
if "," in admins:
admins = admins.split(",")
else:
if len(admins) >= 1:
admins = [admins]
else:
admins = []
admins = list(map(int, clear_list(admins)))
return admins
View File
+38
View File
@@ -0,0 +1,38 @@
# - *- coding: utf- 8 - *-
import configparser
from apscheduler.schedulers.asyncio import AsyncIOScheduler
scheduler = AsyncIOScheduler()
read_config = configparser.ConfigParser()
read_config.read("settings.ini")
BOT_TOKEN = read_config['settings']['token'].strip() # Токен бота
PATH_DATABASE = "tgbot/data/database.db" # Путь к БД
PATH_LOGS = "tgbot/data/logs.log" # Путь к Логам
# Получение администраторов бота
def get_admins():
read_admins = configparser.ConfigParser()
read_admins.read('settings.ini')
admins = read_admins['settings']['admin_id'].strip()
admins = admins.replace(' ', '')
if ',' in admins:
admins = admins.split(',')
else:
if len(admins) >= 1:
admins = [admins]
else:
admins = []
while "" in admins: admins.remove("")
while " " in admins: admins.remove(" ")
while "," in admins: admins.remove(",")
while "\r" in admins: admins.remove("\r")
admins = list(map(int, admins))
return admins
View File
+22
View File
@@ -0,0 +1,22 @@
# - *- coding: utf- 8 - *-
from aiogram.utils.keyboard import InlineKeyboardBuilder
from tgbot.data.config import get_admins
from tgbot.utils.const_functions import ikb
# Кнопки инлайн меню
def menu_finl(user_id):
keyboard = InlineKeyboardBuilder(
).row(
ikb("User 1", data="..."),
ikb("User 2", data="...")
)
if user_id in get_admins():
keyboard.row(
ikb("Admin 1", data="..."),
ikb("Admin 2", data="...")
)
return keyboard.as_markup()
+22
View File
@@ -0,0 +1,22 @@
# - *- coding: utf- 8 - *-
from aiogram.utils.keyboard import InlineKeyboardBuilder
from tgbot.utils.const_functions import ikb
# Тестовые админ инлайн кнопки
admin_inl = InlineKeyboardBuilder(
).row(
ikb("Admin Inline 1", data="..."),
ikb("Admin Inline 2", data="..."),
).row(
ikb("Admin Inline 3", data="..."),
).as_markup()
# Тестовые юзер инлайн кнопки
user_inl = InlineKeyboardBuilder(
).row(
ikb("User Inline 1", data="..."),
ikb("User Inline 2", data="..."),
).row(
ikb("User Inline 3", data="..."),
).as_markup()
+20
View File
@@ -0,0 +1,20 @@
# - *- coding: utf- 8 - *-
from aiogram.utils.keyboard import ReplyKeyboardBuilder
from tgbot.data.config import get_admins
from tgbot.utils.const_functions import rkb
# Кнопки главного меню
def menu_frep(user_id):
keyboard = ReplyKeyboardBuilder(
).row(
rkb("User Inline"), rkb("User Reply"),
)
if user_id in get_admins():
keyboard.row(
rkb("Admin Inline"), rkb("Admin Reply"),
)
return keyboard.as_markup(resize_keyboard=True)
+22
View File
@@ -0,0 +1,22 @@
# - *- coding: utf- 8 - *-
from aiogram.utils.keyboard import ReplyKeyboardBuilder
from tgbot.utils.const_functions import rkb
# Тестовые админ реплай кнопки
admin_rep = ReplyKeyboardBuilder(
).row(
rkb("Admin Reply 1"),
rkb("Admin Reply 2"),
).row(
rkb("⬅ Главное меню"),
).as_markup(resize_keyboard=True)
# Тестовые юзер реплай кнопки
user_rep = ReplyKeyboardBuilder(
).row(
rkb("User Reply 1"),
rkb("User Reply 2"),
).row(
rkb("⬅ Главное меню"),
).as_markup(resize_keyboard=True)
+12
View File
@@ -0,0 +1,12 @@
# - *- coding: utf- 8 - *-
from aiogram import Dispatcher
from tgbot.middlewares.exists_user import ExistsUserMiddleware
from tgbot.middlewares.throttling import ThrottlingMiddleware
def register_all_middlwares(dp: Dispatcher):
dp.callback_query.outer_middleware(ExistsUserMiddleware())
dp.message.outer_middleware(ExistsUserMiddleware())
dp.message.middleware(ThrottlingMiddleware())
+48
View File
@@ -0,0 +1,48 @@
# - *- coding: utf- 8 - *-
from aiogram import BaseMiddleware
from tgbot.services.api_sqlite import get_userx, add_userx, update_userx
from tgbot.utils.const_functions import clear_html
# Проверка юзера в БД и его добавление
class ExistsUserMiddleware(BaseMiddleware):
async def __call__(self, handler, event, data):
this_user = data.get("event_from_user")
if not this_user.is_bot:
get_user = get_userx(user_id=this_user.id)
user_id = this_user.id
user_login = this_user.username
user_name = clear_html(this_user.first_name)
user_fullname = clear_html(this_user.first_name)
user_surname = clear_html(this_user.last_name)
user_language = this_user.language_code
if user_language != "ru": user_language = "en"
if user_login is None: user_login = ""
if user_name is None: user_name = ""
if user_fullname is None: user_fullname = ""
if user_surname is None: user_surname = ""
if len(user_surname) >= 1: user_fullname += f" {user_surname}"
if get_user is None:
add_userx(user_id, user_login.lower(), user_name, user_surname, user_fullname)
else:
if user_name != get_user['user_name']:
update_userx(get_user['user_id'], user_name=user_name)
if user_surname != get_user['user_surname']:
update_userx(get_user['user_id'], user_surname=user_surname)
if user_fullname != get_user['user_fullname']:
update_userx(get_user['user_id'], user_fullname=user_fullname)
if user_login.lower() != get_user['user_login']:
update_userx(get_user['user_id'], user_login=user_login.lower())
data['my_user'] = get_userx(user_id=user_id)
return await handler(event, data)
+59
View File
@@ -0,0 +1,59 @@
# - *- coding: utf- 8 - *-
import time
from typing import Any, Awaitable, Callable, Dict
from aiogram import BaseMiddleware
from aiogram.dispatcher.event.handler import HandlerObject
from aiogram.types import Message
from tgbot.services.api_sqlite import get_userx
# Антиспам
class ThrottlingMiddleware(BaseMiddleware):
def __init__(self, default_rate: int = 0.6) -> None:
self.default_rate = default_rate
self.now_rate = default_rate
self.last_throttled = int(time.time())
self.count_throttled = 0
async def __call__(self, handler: Callable[[Message, Dict[str, Any]], Awaitable[Any]], event: Message, data):
real_handler: HandlerObject = data['handler']
skip_pass = True
this_user = data.get("event_from_user")
if not this_user.is_bot:
data['get_user'] = get_userx(user_id=this_user.id)
if real_handler.flags.get("skip_pass") is not None:
skip_pass = real_handler.flags.get("skip_pass")
if skip_pass:
if int(time.time()) - self.last_throttled >= self.now_rate:
self.last_throttled = int(time.time())
self.now_rate = self.default_rate
self.count_throttled = 0
return await handler(event, data)
else:
if self.count_throttled == 0:
self.count_throttled += 1
self.now_rate = self.default_rate * 2
return await handler(event, data)
elif self.count_throttled == 1:
self.count_throttled += 1
self.now_rate = self.default_rate * 2
await event.reply("<b>❗ Пожалуйста, не спамьте.</b>")
else:
self.now_rate = 3
if self.count_throttled == 2:
self.count_throttled = 3
await event.reply("<b>❗ Бот не будет отвечать до прекращения спама.</b>")
self.last_throttled = int(time.time())
else:
return await handler(event, data)
+30
View File
@@ -0,0 +1,30 @@
# - *- coding: utf- 8 - *-
from aiogram import Dispatcher, F
from tgbot.routers import main_errors, main_missed, main_start
from tgbot.routers.admin import admin_menu
from tgbot.routers.user import user_menu
from tgbot.utils.misc.bot_filters import IsAdmin
# Регистрация роутеров
def register_all_routers(dp: Dispatcher):
# Подключение фильтров
main_errors.router.message.filter(F.chat.type == "private")
main_start.router.message.filter(F.chat.type == "private")
main_missed.router.message.filter(F.chat.type == "private")
user_menu.router.message.filter(F.chat.type == "private")
admin_menu.router.message.filter(F.chat.type == "private", IsAdmin())
# Инициализация обязательных роутеров
dp.include_router(main_errors.router)
dp.include_router(main_start.router)
# Подключение хендлеров (юзеров и админов)
dp.include_router(user_menu.router) # Юзер хендлер
dp.include_router(admin_menu.router) # Админ хендлер
# Инициализация обязательных роутеров
dp.include_router(main_missed.router)
View File
+46
View File
@@ -0,0 +1,46 @@
# - *- coding: utf- 8 - *-
from aiogram import Router, Bot
from aiogram.types import FSInputFile, Message
from tgbot.config import PATH_DATABASE, PATH_LOGS
from tgbot.data.config import PATH_DATABASE, PATH_LOGS
from tgbot.keyboards.inline_misc import admin_inl
from tgbot.keyboards.reply_misc import admin_rep
from tgbot.utils.const_functions import get_date
from tgbot.utils.misc.bot_models import FSM, RS
router = Router()
# Кнопка - Admin Inline
@router.message(text="Admin Inline")
async def admin_button_inline(message: Message, bot: Bot, state: FSM, rSession: RS, my_user):
await state.clear()
await message.answer("Click Button - Admin Inline", reply_markup=admin_inl)
# Кнопка - Admin Reply
@router.message(text="Admin Reply")
async def admin_button_reply(message: Message, bot: Bot, state: FSM, rSession: RS, my_user):
await state.clear()
await message.answer("Click Button - Admin Reply", reply_markup=admin_rep)
# Получение Базы Данных
@router.message(commands=['db', 'database'])
async def admin_database(message: Message, bot: Bot, state: FSM, rSession: RS, my_user):
await state.clear()
await message.answer_document(FSInputFile(PATH_DATABASE),
caption=f"<b>📦 BACKUP</b>\n"
f"<code>🕰 {get_date()}</code>")
# Получение логов
@router.message(commands=['log', 'logs'])
async def admin_log(message: Message, bot: Bot, state: FSM, rSession: RS, my_user):
await state.clear()
await message.answer_document(FSInputFile(PATH_LOGS), caption=f"<code>🕰 {get_date()}</code>")
+23
View File
@@ -0,0 +1,23 @@
# - *- coding: utf- 8 - *-
from aiogram import Router, Bot
from aiogram.exceptions import TelegramAPIError
from aiogram.types import Update
from tgbot.utils.misc.bot_logging import bot_logger
from tgbot.utils.misc_functions import send_admins
router = Router()
# Обработка ошибок
@router.errors()
async def processing_errors(update: Update, exception: TelegramAPIError, bot: Bot):
print(f"-Exception | {exception}")
await send_admins(bot, f"<b>❌ Ошибка\n\n"
f"<b>Exception: <code>{exception}</code>\n\n"
f"Update: <code>{update.dict()}</code></b>")
bot_logger.exception(
f"Exception: {exception}\n"
f"Update: {update.dict()}"
)
+40
View File
@@ -0,0 +1,40 @@
# - *- coding: utf- 8 - *-
from aiogram import types, Router
from aiogram.types import CallbackQuery
from tgbot.keyboards.reply_main import menu_frep
from tgbot.utils.misc.bot_models import FSM
router = Router()
# Колбэк с удалением сообщения
@router.callback_query(text="close_this")
async def processing_callback_remove(call: CallbackQuery, state: FSM):
await call.message.delete()
# Колбэк с обработкой кнопки
@router.callback_query(text="...")
async def processing_callback_answer(call: CallbackQuery, state: FSM):
await call.answer(cache_time=20)
# Колбэк с обработкой удаления сообщений потерявших стейт
@router.callback_query()
async def processing_callback_missed(call: CallbackQuery, state: FSM):
try:
await call.message.delete()
except:
pass
await call.message.answer("<b>❌ Данные не были найдены из-за перезапуска скрипта.\n"
"♻ Выполните действие заново.</b>",
reply_markup=menu_frep(call.from_user.id))
# Обработка всех неизвестных команд
@router.message()
async def processing_message_missed(message: types.Message, state: FSM):
await message.answer("♦ Неизвестная команда.\n"
"▶ Введите /start")
+19
View File
@@ -0,0 +1,19 @@
# - *- coding: utf- 8 - *-
from aiogram import Router, Bot
from aiogram.types import Message
from tgbot.keyboards.reply_main import menu_frep
from tgbot.utils.misc.bot_models import FSM, RS
router = Router()
# Открытие главного меню
@router.message(text_startswith=["⬅ Главное меню", "/start"])
async def main_start(message: Message, bot: Bot, state: FSM, rSession: RS, my_user):
await state.clear()
await message.answer("🔸 Бот готов к использованию.\n"
"🔸 Если не появились вспомогательные кнопки\n"
"▶ Введите /start",
reply_markup=menu_frep(message.from_user.id))
View File
+34
View File
@@ -0,0 +1,34 @@
# - *- coding: utf- 8 - *-
from aiogram import Router, Bot
from aiogram.types import Message
from tgbot.keyboards.inline_main import menu_finl
from tgbot.keyboards.inline_misc import user_inl
from tgbot.keyboards.reply_misc import user_rep
from tgbot.utils.misc.bot_models import FSM, RS
router = Router()
# Кнопка - User Inline
@router.message(text="User Inline")
async def user_button_inline(message: Message, bot: Bot, state: FSM, rSession: RS, my_user):
await state.clear()
await message.answer("Click Button - User Inline", reply_markup=user_inl)
# Кнопка - User Reply
@router.message(text="User Reply")
async def user_button_reply(message: Message, bot: Bot, state: FSM, rSession: RS, my_user):
await state.clear()
await message.answer("Click Button - User Reply", reply_markup=user_rep)
# Команда - /inline
@router.message(commands="inline")
async def user_command_inline(message: Message, bot: Bot, state: FSM, rSession: RS, my_user):
await state.clear()
await message.answer("Click command - /inline", reply_markup=menu_finl(message.from_user.id))
+1
View File
@@ -0,0 +1 @@
+25
View File
@@ -0,0 +1,25 @@
# - *- coding: utf- 8 - *-
from typing import Optional
import aiohttp
# Асинхронная сессия для запросов
class RequestsSession:
def __init__(self) -> None:
self._session: Optional[aiohttp.ClientSession] = None
# Получение сессии
async def get_session(self) -> aiohttp.ClientSession:
if self._session is None:
new_session = aiohttp.ClientSession()
self._session = new_session
return self._session
# Закрытие сессии
async def close(self) -> None:
if self._session is None:
return None
await self._session.close()
+125
View File
@@ -0,0 +1,125 @@
# - *- coding: utf- 8 - *-
import sqlite3
from tgbot.config import PATH_DATABASE
from tgbot.utils.const_functions import get_unix, get_date
# Преобразование БД словаря
def dict_factory(cursor, row):
this_dict = {}
for idx, col in enumerate(cursor.description):
this_dict[col[0]] = row[idx]
return this_dict
####################################################################################################
##################################### ФОРМАТИРОВАНИЕ ЗАПРОСОВ ######################################
# Форматирование запроса без аргументов
def update_format_with_args(sql, parameters: dict):
if "XXX" not in sql:
sql += " XXX "
values = ", ".join([
f"{item} = ?" for item in parameters
])
sql = sql.replace("XXX", values)
return sql, list(parameters.values())
# Форматирование запроса с аргументами
def update_format_args(sql, parameters: dict):
sql = f"{sql} WHERE "
sql += " AND ".join([
f"{item} = ?" for item in parameters
])
return sql, list(parameters.values())
####################################### ЗАПРОСЫ К БАЗЕ ДАННЫХ ######################################
####################################################################################################
# Добавление пользователя
def add_userx(user_id, user_login, user_name, user_surname, user_fullname):
with sqlite3.connect(PATH_DATABASE) as con:
con.row_factory = dict_factory
con.execute("INSERT INTO storage_users "
"(user_id, user_login, user_name, user_surname, user_fullname, user_date, user_unix) "
"VALUES (?, ?, ?, ?, ?, ?, ?)",
[user_id, user_login, user_name, user_surname, user_fullname, get_date(), get_unix()])
con.commit()
# Получение пользователя
def get_userx(**kwargs):
with sqlite3.connect(PATH_DATABASE) as con:
con.row_factory = dict_factory
sql = "SELECT * FROM storage_users"
sql, parameters = update_format_args(sql, kwargs)
return con.execute(sql, parameters).fetchone()
# Получение пользователей
def get_usersx(**kwargs):
with sqlite3.connect(PATH_DATABASE) as con:
con.row_factory = dict_factory
sql = "SELECT * FROM storage_users"
sql, parameters = update_format_args(sql, kwargs)
return con.execute(sql, parameters).fetchall()
# Получение всех пользователей
def get_all_usersx():
with sqlite3.connect(PATH_DATABASE) as con:
con.row_factory = dict_factory
sql = "SELECT * FROM storage_users"
return con.execute(sql).fetchall()
# Редактирование пользователя
def update_userx(user_id, **kwargs):
with sqlite3.connect(PATH_DATABASE) as con:
con.row_factory = dict_factory
sql = f"UPDATE storage_users SET"
sql, parameters = update_format_with_args(sql, kwargs)
parameters.append(user_id)
con.execute(sql + "WHERE user_id = ?", parameters)
con.commit()
# Удаление пользователя
def delete_userx(**kwargs):
with sqlite3.connect(PATH_DATABASE) as con:
con.row_factory = dict_factory
sql = "DELETE FROM storage_users"
sql, parameters = update_format_args(sql, kwargs)
con.execute(sql, parameters)
con.commit()
######################################## СОЗДАНИЕ БАЗЫ ДАННЫХ ######################################
# Создание всех таблиц для Базы Данных
def create_dbx():
with sqlite3.connect(PATH_DATABASE) as con:
con.row_factory = dict_factory
# Таблица с хранением пользователей
if len(con.execute("PRAGMA table_info(storage_users)").fetchall()) == 8:
print("DB was found(1/1)")
else:
con.execute("CREATE TABLE storage_users("
"increment INTEGER PRIMARY KEY AUTOINCREMENT, "
"user_id INTEGER,"
"user_login TEXT,"
"user_name TEXT,"
"user_surname TEXT,"
"user_fullname TEXT,"
"user_date TIMESTAMP,"
"user_unix INTEGER)")
print("DB was not found(1/1) | Creating...")
con.commit()
+1
View File
@@ -0,0 +1 @@
+235
View File
@@ -0,0 +1,235 @@
# - *- coding: utf- 8 - *-
import random
import time
from datetime import datetime
from aiogram import Bot, types
from aiogram.types import InlineKeyboardButton, KeyboardButton
######################################## AIOGRAM ########################################
# Генерация реплай кнопки
from tgbot.config import get_admins
def rkb(text):
return KeyboardButton(text=text)
# Генерация инлайн кнопки
def ikb(text, data=None, url=None, switch=None):
if data is not None:
return InlineKeyboardButton(text=text, callback_data=data)
elif url is not None:
return InlineKeyboardButton(text=text, url=url)
else:
return InlineKeyboardButton(text=text, switch_inline_query=switch)
# Отправка сообщения всем админам
async def send_admins(bot: Bot, message, markup=None, not_me=0):
for admin in get_admins():
try:
if str(admin) != str(not_me):
await bot.send_message(admin, message, reply_markup=markup, disable_web_page_preview=True)
except:
pass
# Умная отправка сообщений
async def smart_send(bot: Bot, message: types.Message, user_id, text_add=None, reply=None):
if message.text is not None:
get_text = message.text
elif message.caption is not None:
get_text = message.caption
else:
get_text = ""
if text_add is not None:
get_text = text_add.format(get_text)
if message.photo is not None:
await bot.send_photo(user_id, message.photo[-1].file_id, caption=get_text, reply_markup=reply)
elif message.video is not None:
await bot.send_video(user_id, message.video.file_id, caption=get_text, reply_markup=reply)
elif message.document is not None:
await bot.send_document(user_id, message.document.file_id, caption=get_text, reply_markup=reply)
elif message.audio is not None:
await bot.send_audio(user_id, message.audio.file_id, caption=get_text, reply_markup=reply)
elif message.voice is not None:
await bot.send_voice(user_id, message.voice.file_id, caption=get_text, reply_markup=reply)
elif message.animation is not None:
await bot.send_animation(user_id, message.animation.file_id, reply_markup=reply)
elif message.sticker is not None:
await bot.send_sticker(user_id, message.sticker.file_id, reply_markup=reply)
elif message.dice is not None:
await bot.send_dice(user_id, message.dice.emoji, reply_markup=reply)
elif message.location is not None:
await bot.send_location(user_id, latitude=message.location.latitude, longitude=message.location.longitude,
reply_markup=reply)
else:
await bot.send_message(user_id, get_text)
######################################## ПРОЧЕЕ ########################################
# Очистка HTML тэгов
def clear_html(get_text: str):
if get_text is not None:
if "<" in get_text: get_text = get_text.replace("<", "*")
if ">" in get_text: get_text = get_text.replace(">", "*")
return get_text
# Очистка пробелов в списке
def clear_list(get_list: list):
while "" in get_list: get_list.remove("")
while " " in get_list: get_list.remove(" ")
while "," in get_list: get_list.remove(",")
while "\r" in get_list: get_list.remove("\r")
return get_list
# Разбив списка на несколько частей
def split_messages(get_list: list, count: int):
return [get_list[i:i + count] for i in range(0, len(get_list), count)]
# Получение даты
def get_date():
this_date = datetime.today().replace(microsecond=0)
this_date = this_date.strftime("%d.%m.%Y %H:%M:%S")
return this_date
# Получение юникс даты
def get_unix():
return int(time.time())
# Генерация пароля
def generate_password(len_password: int):
passwd = list("1234567890abcdefghigklmnopqrstuvyxwzABCDEFGHIGKLMNOPQRSTUVYXWZ")
random.shuffle(passwd)
random_chars = "".join([random.choice(passwd) for x in range(len_password)])
return random_chars
# Корректный вывод времени
def convert_times(get_time, get_type="day"):
get_time = int(get_time)
if get_type == "second":
get_list = ["секунда", "секунды", "секунд"]
elif get_type == "minute":
get_list = ["минута", "минуты", "минут"]
elif get_type == "hour":
get_list = ["час", "часа", "часов"]
elif get_type == "day":
get_list = ["день", "дня", "дней"]
elif get_type == "month":
get_list = ["месяц", "месяца", "месяцев"]
else:
get_list = ["год", "года", "лет"]
if get_time % 10 == 1 and get_time % 100 != 11:
count = 0
elif 2 <= get_time % 10 <= 4 and (get_time % 100 < 10 or get_time % 100 >= 20):
count = 1
else:
count = 2
return f"{get_time} {get_list[count]}"
######################################## ЧИСЛА ########################################
# Конвертация числа в вещественное
def to_float(get_number, remains=2):
if "," in str(get_number):
get_number = str(get_number).replace(",", ".")
if "." in get_number:
get_last = get_number.split(".")
if get_last[1].endswith("0"):
while True:
if get_number.endswith("0"):
get_number = get_number[:-1]
else:
break
get_number = round(float(get_number), remains)
if str(float(get_number)).split(".")[1] == "0":
get_number = int(get_number)
else:
get_number = float(get_number)
return get_number
# Конвертация числа в целочисленное
def to_int(get_number):
if "," in get_number:
get_number = get_number.replace(",", ".")
get_number = int(round(float(get_number)))
return get_number
# Проверка числа на вещественное
def is_number(get_number):
if get_number.isdigit():
return False
else:
if "," in str(get_number): get_number = str(get_number).replace(",", ".")
try:
float(get_number)
return False
except ValueError:
return True
# Преобразование длинных вещественных чисел в читаемый вид
def show_floats(amount):
return f"{float(amount):.{len(str(amount).split('.')[1])}f}"
# Форматирование чисел в читаемый вид
def format_rate(rate, around=False):
rate = str(round(float(rate), 2))
if "," in rate:
rate = float(rate.replace(",", "."))
len_rate = str(int(float(rate)))
if len(len_rate) == 3:
get_rate = str(len_rate)
elif len(len_rate) == 4:
get_rate = f"{len_rate[0]} {len_rate[1:]}"
elif len(len_rate) == 5:
get_rate = f"{len_rate[0:2]} {len_rate[2:]}"
elif len(len_rate) == 6:
get_rate = f"{len_rate[0:3]} {len_rate[3:]}"
elif len(len_rate) == 7:
get_rate = f"{len_rate[0:1]} {len_rate[1:4]} {len_rate[4:]}"
elif len(len_rate) == 8:
get_rate = f"{len_rate[0:2]} {len_rate[2:5]} {len_rate[5:]}"
elif len(len_rate) == 9:
get_rate = f"{len_rate[0:3]} {len_rate[3:6]} {len_rate[6:]}"
elif len(len_rate) == 10:
get_rate = f"{len_rate[0:1]} {len_rate[1:4]} {len_rate[4:7]} {len_rate[7:]}"
else:
get_rate = 0
if not around:
if "." in rate:
dot_amount = rate.find(".")
get_rate += f"{rate[dot_amount:]}"
return get_rate
+1
View File
@@ -0,0 +1 @@
+30
View File
@@ -0,0 +1,30 @@
# - *- coding: utf- 8 - *-
from aiogram import Bot
from aiogram.types import BotCommand, BotCommandScopeChat, BotCommandScopeDefault
from tgbot.config import get_admins
# Команды для юзеров
user_commands = [
BotCommand(command="start", description="♻ Перезапустить бота"),
BotCommand(command="inline", description="🌀 Получить Inline клавиатуру"),
]
# Команды для админов
admin_commands = [
BotCommand(command="start", description="♻ Перезапустить бота"),
BotCommand(command="inline", description="🌀 Получить Inline клавиатуру"),
BotCommand(command="log", description="🖨 Получить Логи"),
BotCommand(command="db", description="📦 Получить Базу Данных"),
]
# Установка команд
async def set_commands(bot: Bot):
await bot.set_my_commands(user_commands, scope=BotCommandScopeDefault())
for admin in get_admins():
try:
await bot.set_my_commands(admin_commands, scope=BotCommandScopeChat(chat_id=admin))
except:
pass
+14
View File
@@ -0,0 +1,14 @@
# - *- coding: utf- 8 - *-
from aiogram.dispatcher.filters import BaseFilter
from aiogram.types import Message
from tgbot.config import get_admins
# Проверка на админа
class IsAdmin(BaseFilter):
async def __call__(self, message: Message):
if message.from_user.id in get_admins():
return True
else:
return False
+32
View File
@@ -0,0 +1,32 @@
# - *- coding: utf- 8 - *-
import logging as bot_logger
import colorlog
from tgbot.config import PATH_LOGS
# Формат логгирования
log_formatter_file = bot_logger.Formatter("%(levelname)s | %(asctime)s | %(filename)s:%(lineno)d | %(message)s")
log_formatter_console = colorlog.ColoredFormatter(
"%(purple)s%(levelname)s %(blue)s|%(purple)s %(asctime)s %(blue)s|%(purple)s %(filename)s:%(lineno)d %(blue)s|%(purple)s %(message)s%(red)s",
datefmt="%d-%m-%Y %H:%M:%S",
)
# Логгирование в файл logs.log
file_handler = bot_logger.FileHandler(PATH_LOGS, "w", "utf-8")
file_handler.setFormatter(log_formatter_file)
file_handler.setLevel(bot_logger.INFO)
# Логгирование в консоль
console_handler = bot_logger.StreamHandler()
console_handler.setFormatter(log_formatter_console)
console_handler.setLevel(bot_logger.CRITICAL)
# Подключение настроек логгирования
bot_logger.basicConfig(
format="%(levelname)s | %(asctime)s | %(filename)s:%(lineno)d | %(message)s",
handlers=[
file_handler,
console_handler
]
)
+10
View File
@@ -0,0 +1,10 @@
# - *- coding: utf- 8 - *-
from textwrap import dedent
from aiogram.dispatcher.fsm.context import FSMContext
from tgbot.services.api_session import RequestsSession
ded = dedent
FSM = FSMContext
RS = RequestsSession
+24
View File
@@ -0,0 +1,24 @@
# - *- coding: utf- 8 - *-
from aiogram import Bot
from aiogram.types import FSInputFile
from tgbot.config import get_admins, PATH_DATABASE
from tgbot.utils.const_functions import get_date, send_admins
# Выполнение функции после запуска бота (рассылка админам о запуске бота)
async def startup_notify(bot: Bot):
if len(get_admins()) >= 1:
await send_admins(bot, "<b>✅ Бот был запущен</b>")
# Автоматические бэкапы БД
async def autobackup(bot: Bot):
for admin in get_admins():
try:
await bot.send_document(admin,
FSInputFile(PATH_DATABASE),
caption=f"<b>📦 AUTOBACKUP</b>\n"
f"<code>🕰 {get_date()}</code>")
except:
pass