├── data ├── __init__.py └── config.py ├── handlers ├── groups │ └── __init__.py ├── channels │ └── __init__.py ├── errors │ ├── __init__.py │ └── error_handler.py ├── .DS_Store ├── users │ ├── __init__.py │ ├── echo.py │ ├── help.py │ └── start.py └── __init__.py ├── utils ├── db_api │ └── __init__.py ├── misc │ ├── __init__.py │ ├── logging.py │ └── throttling.py ├── __init__.py ├── set_bot_commands.py └── notify_admins.py ├── states ├── __init__.py └── state.py ├── keyboards ├── default │ ├── __init__.py │ └── button.py ├── inline │ ├── __init__.py │ └── inline_buttons.py └── __init__.py ├── README.md ├── .DS_Store ├── .env.dist ├── filters └── __init__.py ├── middlewares ├── __init__.py └── throttling.py ├── Pipfile ├── loader.py ├── app.py ├── requirements.txt ├── .github └── workflows │ └── python-package.yml ├── .gitignore ├── back_up.py ├── database_saver.py └── Pipfile.lock /data/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /handlers/groups/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /utils/db_api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /handlers/channels/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /states/__init__.py: -------------------------------------------------------------------------------- 1 | from . import state -------------------------------------------------------------------------------- /keyboards/default/__init__.py: -------------------------------------------------------------------------------- 1 | from . import button -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mars_Request 2 | # With Diyas and Behruz 3 | -------------------------------------------------------------------------------- /keyboards/inline/__init__.py: -------------------------------------------------------------------------------- 1 | from . import inline_buttons -------------------------------------------------------------------------------- /handlers/errors/__init__.py: -------------------------------------------------------------------------------- 1 | from . import error_handler 2 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d1yas/Mars-Request/HEAD/.DS_Store -------------------------------------------------------------------------------- /keyboards/__init__.py: -------------------------------------------------------------------------------- 1 | from . import default 2 | from . import inline 3 | -------------------------------------------------------------------------------- /utils/misc/__init__.py: -------------------------------------------------------------------------------- 1 | from .throttling import rate_limit 2 | from . import logging 3 | -------------------------------------------------------------------------------- /handlers/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d1yas/Mars-Request/HEAD/handlers/.DS_Store -------------------------------------------------------------------------------- /handlers/users/__init__.py: -------------------------------------------------------------------------------- 1 | from . import help 2 | from . import start 3 | from . import echo -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | from . import db_api 2 | from . import misc 3 | from .notify_admins import on_startup_notify 4 | -------------------------------------------------------------------------------- /handlers/__init__.py: -------------------------------------------------------------------------------- 1 | from . import errors 2 | from . import users 3 | from . import groups 4 | from . import channels 5 | -------------------------------------------------------------------------------- /.env.dist: -------------------------------------------------------------------------------- 1 | # YANGI .env FAYL YARATING VA 2 | # QUYIDAGI MA'LUMOTLARNI YOZING: 3 | 4 | ADMINS=12345678,12345677,12345676 5 | BOT_TOKEN=123452345243:Asdfasdfasf 6 | ip=localhost 7 | ADMIN_ID=12345678 8 | GROUP_ID=-1234567899test 9 | -------------------------------------------------------------------------------- /filters/__init__.py: -------------------------------------------------------------------------------- 1 | from aiogram import Dispatcher 2 | 3 | from loader import dp 4 | # from .is_admin import AdminFilter 5 | 6 | 7 | if __name__ == "filters": 8 | #dp.filters_factory.bind(is_admin) 9 | pass 10 | -------------------------------------------------------------------------------- /middlewares/__init__.py: -------------------------------------------------------------------------------- 1 | from aiogram import Dispatcher 2 | 3 | from loader import dp 4 | from .throttling import ThrottlingMiddleware 5 | 6 | 7 | if __name__ == "middlewares": 8 | dp.middleware.setup(ThrottlingMiddleware()) 9 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | aiogram = "~=2.14" 8 | environs = "~=8.0.0" 9 | 10 | [dev-packages] 11 | 12 | [requires] 13 | python_version = "3.9" 14 | -------------------------------------------------------------------------------- /data/config.py: -------------------------------------------------------------------------------- 1 | from environs import Env 2 | 3 | env = Env() 4 | env.read_env() 5 | 6 | BOT_TOKEN = env.str("BOT_TOKEN") 7 | ADMINS = env.list("ADMINS") 8 | IP = env.str("ip") 9 | ADMIN_ID = env.str("ADMIN_ID") 10 | GROUP_ID = env.str("GROUP_ID") -------------------------------------------------------------------------------- /handlers/users/echo.py: -------------------------------------------------------------------------------- 1 | # from aiogram import types 2 | # 3 | # from loader import dp 4 | # 5 | # 6 | # # Echo bot 7 | # @dp.message_handler(state=None) 8 | # async def bot_echo(message: types.Message): 9 | # await message.answer(message.text) 10 | -------------------------------------------------------------------------------- /loader.py: -------------------------------------------------------------------------------- 1 | from aiogram import Bot, Dispatcher, types 2 | from aiogram.contrib.fsm_storage.memory import MemoryStorage 3 | 4 | from data import config 5 | 6 | bot = Bot(token=config.BOT_TOKEN, parse_mode=types.ParseMode.HTML) 7 | storage = MemoryStorage() 8 | dp = Dispatcher(bot, storage=storage) 9 | -------------------------------------------------------------------------------- /utils/set_bot_commands.py: -------------------------------------------------------------------------------- 1 | from aiogram import types 2 | 3 | 4 | async def set_default_commands(dp): 5 | await dp.bot.set_my_commands( 6 | [ 7 | types.BotCommand("start", "Botni ishga tushurish"), 8 | types.BotCommand("help", "Yordam"), 9 | ] 10 | ) 11 | -------------------------------------------------------------------------------- /utils/misc/logging.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logging.basicConfig(format=u'%(filename)s [LINE:%(lineno)d] #%(levelname)-8s [%(asctime)s] %(message)s', 4 | level=logging.INFO, 5 | # level=logging.DEBUG, # Можно заменить на другой уровень логгирования. 6 | ) 7 | -------------------------------------------------------------------------------- /states/state.py: -------------------------------------------------------------------------------- 1 | from aiogram.dispatcher.filters.state import State, StatesGroup 2 | 3 | 4 | class Xonachalar(StatesGroup): 5 | ism_xonacha = State() 6 | vaqt_xonacha = State() 7 | guruxlar_xonacha = State() 8 | filial_xonacha = State() 9 | sabab_xonacha = State() 10 | check_admin = State() 11 | 12 | 13 | -------------------------------------------------------------------------------- /keyboards/default/button.py: -------------------------------------------------------------------------------- 1 | from aiogram.types import ReplyKeyboardMarkup, KeyboardButton 2 | 3 | birinchi_button = ReplyKeyboardMarkup( 4 | keyboard=[ 5 | [ 6 | KeyboardButton("🤝 Ruxsat so`rash") 7 | ], 8 | [ 9 | KeyboardButton("💼️ Bot haqida️️️️️") 10 | ] 11 | ], 12 | resize_keyboard=True 13 | ) -------------------------------------------------------------------------------- /utils/notify_admins.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from aiogram import Dispatcher 4 | 5 | from data.config import ADMINS 6 | 7 | 8 | async def on_startup_notify(dp: Dispatcher): 9 | for admin in ADMINS: 10 | try: 11 | await dp.bot.send_message(admin, "Bot ishga tushdi") 12 | 13 | except Exception as err: 14 | logging.exception(err) 15 | -------------------------------------------------------------------------------- /handlers/users/help.py: -------------------------------------------------------------------------------- 1 | from aiogram import types 2 | from aiogram.dispatcher.filters.builtin import CommandHelp 3 | 4 | from loader import dp 5 | 6 | 7 | @dp.message_handler(CommandHelp()) 8 | async def bot_help(message: types.Message): 9 | text = ("Buyruqlar: ", 10 | "/start - Botni ishga tushirish", 11 | "/help - Yordam") 12 | 13 | await message.answer("\n".join(text)) -------------------------------------------------------------------------------- /utils/misc/throttling.py: -------------------------------------------------------------------------------- 1 | def rate_limit(limit: int, key=None): 2 | """ 3 | Decorator for configuring rate limit and key in different functions. 4 | 5 | :param limit: 6 | :param key: 7 | :return: 8 | """ 9 | 10 | def decorator(func): 11 | setattr(func, 'throttling_rate_limit', limit) 12 | if key: 13 | setattr(func, 'throttling_key', key) 14 | return func 15 | 16 | return decorator 17 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from aiogram import executor 2 | 3 | from loader import dp 4 | import middlewares, filters, handlers 5 | from utils.notify_admins import on_startup_notify 6 | from utils.set_bot_commands import set_default_commands 7 | 8 | 9 | async def on_startup(dispatcher): 10 | await set_default_commands(dispatcher) 11 | 12 | await on_startup_notify(dispatcher) 13 | 14 | 15 | if __name__ == '__main__': 16 | executor.start_polling(dp, on_startup=on_startup) 17 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiogram==2.25.1 2 | aiohttp==3.8.6 3 | aiosignal==1.3.2 4 | anyio==4.8.0 5 | async-timeout==4.0.3 6 | attrs==25.1.0 7 | Babel==2.9.1 8 | certifi==2025.1.31 9 | charset-normalizer==3.4.1 10 | environs==8.0.0 11 | et_xmlfile==2.0.0 12 | exceptiongroup==1.2.2 13 | frozenlist==1.5.0 14 | h11==0.14.0 15 | httpcore==1.0.7 16 | httpx==0.28.1 17 | idna==3.10 18 | magic-filter==1.0.12 19 | marshmallow==3.26.1 20 | multidict==6.1.0 21 | numpy==2.0.2 22 | openpyxl==3.1.5 23 | packaging==24.2 24 | pandas==2.2.3 25 | propcache==0.2.1 26 | python-dateutil==2.9.0.post0 27 | python-dotenv==1.0.1 28 | python-telegram-bot==21.10 29 | pytz==2025.1 30 | six==1.17.0 31 | sniffio==1.3.1 32 | typing_extensions==4.12.2 33 | tzdata==2025.1 34 | yarl==1.18.3 35 | -------------------------------------------------------------------------------- /keyboards/inline/inline_buttons.py: -------------------------------------------------------------------------------- 1 | from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton 2 | 3 | tasdiqlash_buttons = InlineKeyboardMarkup( 4 | inline_keyboard=[ 5 | [ 6 | InlineKeyboardButton(text="✔️ Tasdiqlash", callback_data="tasdiqlash"), 7 | InlineKeyboardButton(text="✖️ Rad etish", callback_data="rad_etish") 8 | ] 9 | ], resize_keyboard=True 10 | ) 11 | 12 | 13 | 14 | 15 | filial_buttons = InlineKeyboardMarkup( 16 | inline_keyboard=[ 17 | [ 18 | InlineKeyboardButton(text="Yunusobod ⭕️", callback_data="Yunusobod"), 19 | InlineKeyboardButton(text="Tinchlik ⭕️", callback_data="Tinchlik"), 20 | InlineKeyboardButton(text="Chilonzor ️⭕️", callback_data="Chilonzor") 21 | ], 22 | [ 23 | InlineKeyboardButton(text="Sergeli ⭕️", callback_data="Sergeli"), 24 | InlineKeyboardButton(text="Maksim Gorki ⭕️", callback_data="Maksim_Gorki"), 25 | InlineKeyboardButton(text="Oybek ⭕️", callback_data="Oybek") 26 | ], 27 | [ 28 | InlineKeyboardButton(text="Minor ⭕️", callback_data="Minor") 29 | ] 30 | ], resize_keyboard=True 31 | ) 32 | 33 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: Python package 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | python-version: ["3.9", "3.10", "3.11"] 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v3 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | python -m pip install flake8 pytest 31 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 32 | - name: Lint with flake8 33 | run: | 34 | # stop the build if there are Python syntax errors or undefined names 35 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 36 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 37 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 38 | - name: Test with pytest 39 | run: | 40 | pytest 41 | -------------------------------------------------------------------------------- /middlewares/throttling.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | from aiogram import types, Dispatcher 4 | from aiogram.dispatcher import DEFAULT_RATE_LIMIT 5 | from aiogram.dispatcher.handler import CancelHandler, current_handler 6 | from aiogram.dispatcher.middlewares import BaseMiddleware 7 | from aiogram.utils.exceptions import Throttled 8 | 9 | 10 | class ThrottlingMiddleware(BaseMiddleware): 11 | """ 12 | Simple middleware 13 | """ 14 | 15 | def __init__(self, limit=DEFAULT_RATE_LIMIT, key_prefix='antiflood_'): 16 | self.rate_limit = limit 17 | self.prefix = key_prefix 18 | super(ThrottlingMiddleware, self).__init__() 19 | 20 | async def on_process_message(self, message: types.Message, data: dict): 21 | handler = current_handler.get() 22 | dispatcher = Dispatcher.get_current() 23 | if handler: 24 | limit = getattr(handler, "throttling_rate_limit", self.rate_limit) 25 | key = getattr(handler, "throttling_key", f"{self.prefix}_{handler.__name__}") 26 | else: 27 | limit = self.rate_limit 28 | key = f"{self.prefix}_message" 29 | try: 30 | await dispatcher.throttle(key, rate=limit) 31 | except Throttled as t: 32 | await self.message_throttled(message, t) 33 | raise CancelHandler() 34 | 35 | async def message_throttled(self, message: types.Message, throttled: Throttled): 36 | if throttled.exceeded_count <= 2: 37 | await message.reply("Too many requests!") 38 | -------------------------------------------------------------------------------- /handlers/errors/error_handler.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from aiogram.utils.exceptions import (Unauthorized, InvalidQueryID, TelegramAPIError, 3 | CantDemoteChatCreator, MessageNotModified, MessageToDeleteNotFound, 4 | MessageTextIsEmpty, RetryAfter, 5 | CantParseEntities, MessageCantBeDeleted) 6 | 7 | 8 | from loader import dp 9 | 10 | 11 | @dp.errors_handler() 12 | async def errors_handler(update, exception): 13 | """ 14 | Exceptions handler. Catches all exceptions within task factory tasks. 15 | :param dispatcher: 16 | :param update: 17 | :param exception: 18 | :return: stdout logging 19 | """ 20 | 21 | if isinstance(exception, CantDemoteChatCreator): 22 | logging.exception("Can't demote chat creator") 23 | return True 24 | 25 | if isinstance(exception, MessageNotModified): 26 | logging.exception('Message is not modified') 27 | return True 28 | if isinstance(exception, MessageCantBeDeleted): 29 | logging.exception('Message cant be deleted') 30 | return True 31 | 32 | if isinstance(exception, MessageToDeleteNotFound): 33 | logging.exception('Message to delete not found') 34 | return True 35 | 36 | if isinstance(exception, MessageTextIsEmpty): 37 | logging.exception('MessageTextIsEmpty') 38 | return True 39 | 40 | if isinstance(exception, Unauthorized): 41 | logging.exception(f'Unauthorized: {exception}') 42 | return True 43 | 44 | if isinstance(exception, InvalidQueryID): 45 | logging.exception(f'InvalidQueryID: {exception} \nUpdate: {update}') 46 | return True 47 | 48 | if isinstance(exception, TelegramAPIError): 49 | logging.exception(f'TelegramAPIError: {exception} \nUpdate: {update}') 50 | return True 51 | if isinstance(exception, RetryAfter): 52 | logging.exception(f'RetryAfter: {exception} \nUpdate: {update}') 53 | return True 54 | if isinstance(exception, CantParseEntities): 55 | logging.exception(f'CantParseEntities: {exception} \nUpdate: {update}') 56 | return True 57 | 58 | logging.exception(f'Update: {update} \n{exception}') 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | user_data.db 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | pip-wheel-metadata/ 25 | share/python-wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | MANIFEST 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .nox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | *.py,cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | .python-version 87 | 88 | # pipenv 89 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 90 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 91 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 92 | # install all needed dependencies. 93 | #Pipfile.lock 94 | 95 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 96 | __pypackages__/ 97 | 98 | # Celery stuff 99 | celerybeat-schedule 100 | celerybeat.pid 101 | 102 | # SageMath parsed files 103 | *.sage.py 104 | 105 | # Environments 106 | .venv 107 | */.env 108 | env/ 109 | venv/ 110 | ENV/ 111 | env.bak/ 112 | venv.bak/ 113 | 114 | # Spyder project settings 115 | .spyderproject 116 | .spyproject 117 | 118 | # Rope project settings 119 | .ropeproject 120 | 121 | # mkdocs documentation 122 | /site 123 | 124 | # mypy 125 | .mypy_cache/ 126 | .dmypy.json 127 | dmypy.json 128 | 129 | # Pyre type checker 130 | .pyre/ 131 | .idea/* 132 | .env -------------------------------------------------------------------------------- /back_up.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sqlite3 3 | import pandas as pd 4 | import os 5 | import datetime 6 | import asyncio 7 | from aiogram import Bot, Dispatcher, types 8 | from aiogram.utils.executor import start_polling 9 | 10 | logging.basicConfig(level=logging.INFO) 11 | 12 | # Telegram ma'lumotlari 13 | TOKEN = "YOUR_TOKEN_HERE" 14 | ADMIN_CHAT_ID = "YOUR_ADMIN_ID" # Adminga yuborish uchun chat ID 15 | 16 | # SQLite fayl manzili 17 | DB_PATH = "user_data.db" 18 | EXPORT_DIR = "exports" 19 | 20 | bot = Bot(token=TOKEN) 21 | dp = Dispatcher(bot) 22 | 23 | # Har oy 1-sanada ishga tushirish uchun tekshirish 24 | def is_first_day_of_month(): 25 | return datetime.datetime.now().day == 1 26 | 27 | def export_db_to_excel(): 28 | if not os.path.exists(EXPORT_DIR): 29 | os.makedirs(EXPORT_DIR) 30 | 31 | conn = sqlite3.connect(DB_PATH) 32 | cursor = conn.cursor() 33 | 34 | cursor.execute("SELECT name FROM sqlite_master WHERE type='table';") 35 | tables = cursor.fetchall() 36 | 37 | file_name = f"{EXPORT_DIR}/database_backup_{datetime.datetime.now().strftime('%Y-%m-%d')}.xlsx" 38 | 39 | with pd.ExcelWriter(file_name, engine='openpyxl') as writer: 40 | for table in tables: 41 | table_name = table[0] 42 | df = pd.read_sql_query(f"SELECT * FROM {table_name}", conn) 43 | df.to_excel(writer, sheet_name=table_name, index=False) 44 | 45 | conn.close() 46 | return file_name 47 | 48 | # Adminga faylni yuborish 49 | async def send_file_to_admin(): 50 | if is_first_day_of_month(): 51 | file_path = export_db_to_excel() 52 | with open(file_path, "rb") as file: 53 | await bot.send_document(chat_id=ADMIN_CHAT_ID, document=file, caption="Oylik SQLite backup") 54 | print("Backup yaratildi va adminga yuborildi.") 55 | else: 56 | print("Bugun birinchi kun emas.") 57 | 58 | # Bot buyruqlari 59 | @dp.message_handler(commands=["backup"]) 60 | async def manual_backup(message: types.Message): 61 | """Qo'lda /backup buyrug'ini yuborgan adminga xabar yuboradi.""" 62 | if str(message.chat.id) == ADMIN_CHAT_ID: 63 | file_path = export_db_to_excel() 64 | with open(file_path, "rb") as file: 65 | await bot.send_document(chat_id=ADMIN_CHAT_ID, document=file, caption="Manually requested SQLite backup") 66 | await message.reply("✅ Backup yaratildi va yuborildi!") 67 | else: 68 | await message.reply("❌ Sizda ruxsat yo'q!") 69 | 70 | # Har oy avtomatik yuborish uchun task 71 | async def scheduler(): 72 | while True: 73 | now = datetime.datetime.now() 74 | if now.hour == 0 and now.minute == 0: 75 | await send_file_to_admin() 76 | await asyncio.sleep(60) 77 | 78 | async def on_startup(dp): 79 | asyncio.create_task(scheduler()) 80 | 81 | if __name__ == "__main__": 82 | start_polling(dp, on_startup=on_startup) 83 | -------------------------------------------------------------------------------- /database_saver.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | from datetime import datetime 3 | 4 | DB_NAME = "user_data.db" 5 | 6 | def create_table(): 7 | conn = sqlite3.connect(DB_NAME) 8 | cursor = conn.cursor() 9 | 10 | cursor.execute(''' 11 | CREATE TABLE IF NOT EXISTS sorov_table ( 12 | id INTEGER PRIMARY KEY AUTOINCREMENT, 13 | name TEXT NULL, 14 | time TEXT NULL, 15 | guruxlar TEXT NULL, 16 | status TEXT NULL DEFAULT "Javob kutilmoqda", 17 | filial TEXT NULL, 18 | user_id INTEGER NOT NULL, 19 | sabab TEXT NULL, 20 | data TEXT NULL 21 | ) 22 | ''') 23 | 24 | cursor.execute(''' 25 | CREATE TABLE IF NOT EXISTS history_sorov ( 26 | id INTEGER PRIMARY KEY AUTOINCREMENT, 27 | name TEXT NULL, 28 | time TEXT NULL, 29 | guruxlar TEXT NULL, 30 | status TEXT NULL, 31 | filial TEXT NULL, 32 | user_id INTEGER NOT NULL, 33 | sabab TEXT NULL, 34 | data TEXT NULL 35 | ) 36 | ''') 37 | 38 | conn.commit() 39 | conn.close() 40 | 41 | create_table() 42 | 43 | def save_request_sorov_table(user_id, name, time, guruxlar, filial, sabab): 44 | conn = sqlite3.connect(DB_NAME) 45 | cursor = conn.cursor() 46 | data = datetime.now().strftime("%Y-%m-%d") 47 | cursor.execute(''' 48 | INSERT INTO sorov_table (user_id, name, time, guruxlar, filial, sabab, data) 49 | VALUES (?, ?, ?, ?, ?, ?, ?) 50 | ''', (user_id, name, time, guruxlar, filial, sabab, data)) 51 | conn.commit() 52 | last_id = cursor.lastrowid 53 | conn.close() 54 | return last_id 55 | 56 | 57 | def update_status(request_id, new_status): 58 | conn = sqlite3.connect(DB_NAME) 59 | cursor = conn.cursor() 60 | cursor.execute(''' 61 | UPDATE sorov_table 62 | SET status = ? 63 | WHERE id = ? 64 | ''', (new_status, request_id)) 65 | conn.commit() 66 | conn.close() 67 | 68 | def save_request_to_history(request_id): 69 | conn = sqlite3.connect(DB_NAME) 70 | cursor = conn.cursor() 71 | cursor.execute(''' 72 | SELECT user_id, name, time, guruxlar, status, filial, sabab, data 73 | FROM sorov_table 74 | WHERE id = ? 75 | ''', (request_id,)) 76 | request = cursor.fetchone() 77 | 78 | if request: 79 | user_id, name, time, guruxlar, status, filial, sabab, data = request 80 | cursor.execute(''' 81 | INSERT INTO history_sorov (user_id, name, time, guruxlar, status, filial, sabab, data) 82 | VALUES (?, ?, ?, ?, ?, ?, ?, ?) 83 | ''', (user_id, name, time, guruxlar, status, filial, sabab, data)) 84 | cursor.execute('DELETE FROM sorov_table WHERE id = ?', (request_id,)) 85 | 86 | conn.commit() 87 | conn.close() 88 | 89 | def get_user_data(request_id): 90 | conn = sqlite3.connect(DB_NAME) 91 | cursor = conn.cursor() 92 | cursor.execute(''' 93 | SELECT name, time, guruxlar, filial, sabab, data, id, status 94 | FROM sorov_table 95 | WHERE id = ? 96 | ''', (request_id,)) 97 | result = cursor.fetchone() 98 | conn.close() 99 | 100 | if result: 101 | name, time, guruxlar, filial, sabab, data, id, status = result 102 | return { 103 | "name": name, 104 | "time": time, 105 | "guruxlar": guruxlar, 106 | "filial": filial, 107 | "sabab": sabab, 108 | "data": data, 109 | "id": id, 110 | "status": status 111 | } 112 | return None 113 | 114 | def get_all_pending_requests(): 115 | conn = sqlite3.connect(DB_NAME) 116 | cursor = conn.cursor() 117 | cursor.execute(''' 118 | SELECT id, user_id, name, time, guruxlar, filial, sabab, data 119 | FROM sorov_table 120 | WHERE status = "Javob kutilmoqda" 121 | ''') 122 | results = cursor.fetchall() 123 | conn.close() 124 | 125 | pending_requests = [] 126 | for result in results: 127 | id, user_id, name, time, guruxlar, filial, sabab, data = result 128 | pending_requests.append({ 129 | "id": id, 130 | "user_id": user_id, 131 | "name": name, 132 | "time": time, 133 | "guruxlar": guruxlar, 134 | "filial": filial, 135 | "sabab": sabab, 136 | "data": data 137 | }) 138 | 139 | return pending_requests -------------------------------------------------------------------------------- /handlers/users/start.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import os 3 | import sqlite3 4 | import pandas as pd 5 | import datetime 6 | from aiogram import types 7 | from data.config import ADMIN_ID, GROUP_ID 8 | from aiogram.dispatcher import FSMContext 9 | from loader import dp, bot 10 | from aiogram.types import ReplyKeyboardRemove, InlineKeyboardMarkup, InlineKeyboardButton 11 | from keyboards.default.button import birinchi_button 12 | from states.state import Xonachalar 13 | from database_saver import save_request_sorov_table, update_status, save_request_to_history, get_user_data, get_all_pending_requests 14 | from keyboards.inline.inline_buttons import filial_buttons 15 | 16 | 17 | DB_PATH = "user_data.db" 18 | EXPORT_DIR = "exports" 19 | 20 | 21 | async def check_group(): 22 | chat = await bot.get_chat(GROUP_ID) 23 | print(chat) 24 | 25 | # check_group() 26 | 27 | @dp.message_handler(commands='start', state='*') 28 | async def send_welcome(message: types.Message, state: FSMContext): 29 | # Reset any ongoing form entry state 30 | current_state = await state.get_state() 31 | if current_state is not None: 32 | await state.finish() 33 | 34 | await message.answer("Assalomu Aleykum MARS IT SCHOOL ning botiga xush kelibsiz", 35 | reply_markup=birinchi_button) 36 | 37 | @dp.message_handler(text="🤝 Ruxsat so`rash") 38 | async def ruxsat_sorash(message: types.Message): 39 | await message.reply(""""Ism va familyangizni kiriting ⬇️ 40 | 41 | ✅ Bekbayev Sirojiddin 42 | ❌ Siroj""", reply_markup=ReplyKeyboardRemove()) 43 | await Xonachalar.ism_xonacha.set() 44 | 45 | @dp.message_handler(content_types=types.ContentType.TEXT, state=Xonachalar.ism_xonacha) 46 | async def vaqt(message: types.Message, state: FSMContext): 47 | await state.update_data(name=message.text) 48 | await message.answer(""""Javob so'rash sanasini to'liq kiriting ⬇️ 49 | 50 | ✅ 1.02.2025-8.02.2025 51 | ❌ 1 fev dan 8 fev gacha""", parse_mode="HTML") 52 | await Xonachalar.vaqt_xonacha.set() 53 | 54 | @dp.message_handler(content_types=types.ContentType.TEXT, state=Xonachalar.vaqt_xonacha) 55 | async def guruxlar(message: types.Message, state: FSMContext): 56 | await state.update_data(time=message.text) 57 | await message.reply("""Guruhingizni kiriting ⬇️ 58 | 59 | ✅ BG-1375 60 | ❌ bg1375""") 61 | await Xonachalar.guruxlar_xonacha.set() 62 | 63 | 64 | @dp.message_handler(content_types=types.ContentType.TEXT, state=Xonachalar.guruxlar_xonacha) 65 | async def filial(message: types.Message, state: FSMContext): 66 | await state.update_data(guruxlar=message.text) 67 | await message.reply("Filialdan birini tanlang", reply_markup=filial_buttons, parse_mode="HTML") 68 | await Xonachalar.filial_xonacha.set() 69 | 70 | 71 | @dp.callback_query_handler(state=Xonachalar.filial_xonacha) 72 | async def sabab(callback_query: types.CallbackQuery, state: FSMContext): 73 | filial = callback_query.data # callback data orqali filialni olish 74 | await state.update_data(filial=filial) 75 | 76 | await callback_query.answer() # Bu javobni yuboradi 77 | await bot.send_message(callback_query.from_user.id, "Javob so'rash sababini to'liq kiriting ⬇️") 78 | await Xonachalar.sabab_xonacha.set() 79 | 80 | 81 | @dp.message_handler(content_types=types.ContentType.TEXT, state=Xonachalar.sabab_xonacha) 82 | async def submit_request(message: types.Message, state: FSMContext): 83 | user_data = await state.get_data() 84 | name = user_data.get("name") 85 | time = user_data.get("time") 86 | guruxlar = user_data.get("guruxlar") 87 | filial = user_data.get("filial") 88 | sabab = message.text 89 | user_id = message.from_user.id 90 | 91 | # Ma'lumotlarni bazaga saqlash 92 | request_id = save_request_sorov_table(user_id, name, time, guruxlar, filial, sabab) 93 | await message.answer("☑️ Sizning arizangiz qabul qilindi") 94 | 95 | # Ruxsat so'rovini adminlarga yuborish 96 | await send_request_to_admin(user_id, name, time, guruxlar, filial, sabab, request_id) 97 | 98 | await state.finish() 99 | 100 | async def send_request_to_admin(user_id, name, time, guruxlar, filial, sabab, request_id): 101 | data = datetime.datetime.now().strftime("%Y-%m-%d") 102 | message_for_admin = f""" 103 | 🆔 Telegram ID: {user_id} 104 | 👤 Ism: {name} 105 | ⏳ Vaqt: {time} 106 | 👥 Guruhlar: {guruxlar} 107 | 📍 Filial: {filial} 108 | ❓ Sabab: {sabab} 109 | 📅 Ariza Sanasi: {data} 110 | 📌 So'rov ID: {request_id} 111 | """ 112 | 113 | # Inline tugmalarni yaratish 114 | tasdiqlash_buttons = InlineKeyboardMarkup() 115 | tasdiqlash_buttons.add(InlineKeyboardButton("✔️ Tasdiqlash", callback_data=f"approve_{request_id}")) 116 | tasdiqlash_buttons.add(InlineKeyboardButton("❌ Rad etish", callback_data=f"reject_{request_id}")) 117 | 118 | # Xabarni adminlarga yuborish 119 | await dp.bot.send_message(ADMIN_ID, message_for_admin, reply_markup=tasdiqlash_buttons) 120 | 121 | @dp.callback_query_handler(lambda c: c.data.startswith('approve_') or c.data.startswith('reject_')) 122 | async def process_callback_approval(callback_query: types.CallbackQuery): 123 | request_id = int(callback_query.data.split('_')[1]) 124 | status_text = "" 125 | 126 | if callback_query.data.startswith('approve_'): 127 | status_text = "Ruxsat berildi" 128 | else: 129 | status_text = "Ruxsat rad etildi" 130 | 131 | # Foydalanuvchi ma'lumotlarini bazadan olish 132 | user_data = get_user_data(request_id) 133 | if user_data: 134 | message_for_group = f""" 135 | {status_text} 136 | Ruxsat so'rov holati: 137 | 🆔 Telegram ID: {user_data['id']} 138 | 👤 Ism: {user_data['name']} 139 | ⏳ Vaqt: {user_data['time']} 140 | 👥 Guruhlar: {user_data['guruxlar']} 141 | 📍 Filial: {user_data['filial']} 142 | ❓ Sabab: {user_data['sabab']} 143 | 📅 Ariza Sanasi: {user_data['data']} 144 | Status: {status_text} 145 | """ 146 | if status_text == 'Ruxsat berildi': 147 | await bot.send_message(GROUP_ID, message_for_group, parse_mode="HTML") 148 | else: 149 | pass 150 | # Holatni sorov_table da yangilash 151 | update_status(request_id, status_text) 152 | 153 | # Extract user_id from database 154 | conn = sqlite3.connect(DB_PATH) 155 | cursor = conn.cursor() 156 | cursor.execute("SELECT user_id FROM sorov_table WHERE id = ?", (request_id,)) 157 | result = cursor.fetchone() 158 | conn.close() 159 | 160 | if result: 161 | user_id = result[0] 162 | await dp.bot.send_message(user_id, status_text) 163 | 164 | # Save to history 165 | save_request_to_history(request_id) 166 | 167 | # Edit the original message to remove the buttons 168 | try: 169 | # Get the original message text 170 | original_message_text = callback_query.message.text 171 | # Edit the message to remove buttons, and add status 172 | await callback_query.message.edit_text(f"{original_message_text}\n\nStatus: {status_text}", parse_mode="HTML") 173 | except Exception as e: 174 | print(f"Error editing message: {e}") 175 | 176 | # Inform the admin about the action 177 | await callback_query.answer(f"So'rov {status_text}") 178 | 179 | # Add command to show all pending requests 180 | @dp.message_handler(commands=["waits"]) 181 | async def show_pending_requests(message: types.Message): 182 | """Show all pending requests to admin""" 183 | if message.chat.id == int(ADMIN_ID): 184 | pending_requests = get_all_pending_requests() 185 | if not pending_requests: 186 | await message.reply("Javob kutilayotgan so'rovlar mavjud emas.") 187 | return 188 | 189 | for request in pending_requests: 190 | request_message = f""" 191 | 🆔 Telegram ID: {request['user_id']} 192 | 👤 Ism: {request['name']} 193 | ⏳ Vaqt: {request['time']} 194 | 👥 Guruhlar: {request['guruxlar']} 195 | 📍 Filial: {request['filial']} 196 | ❓ Sabab: {request['sabab']} 197 | 📅 Ariza Sanasi: {request['data']} 198 | 📌 So'rov ID: {request['id']} 199 | """ 200 | tasdiqlash_buttons = InlineKeyboardMarkup() 201 | tasdiqlash_buttons.add(InlineKeyboardButton("✔️ Tasdiqlash", callback_data=f"approve_{request['id']}")) 202 | tasdiqlash_buttons.add(InlineKeyboardButton("❌ Rad etish", callback_data=f"reject_{request['id']}")) 203 | 204 | await message.answer(request_message, reply_markup=tasdiqlash_buttons) 205 | else: 206 | await message.reply("❌ Sizda ruxsat yo'q!") 207 | 208 | def is_first_day_of_month(): 209 | return datetime.datetime.now().day == 1 210 | 211 | def export_db_to_excel(): 212 | if not os.path.exists(EXPORT_DIR): 213 | os.makedirs(EXPORT_DIR) 214 | 215 | conn = sqlite3.connect(DB_PATH) 216 | cursor = conn.cursor() 217 | 218 | table_name = "history_sorov" 219 | df = pd.read_sql_query(f"SELECT * FROM {table_name}", conn) 220 | 221 | file_name = f"{EXPORT_DIR}/history_sorov_backup_{datetime.datetime.now().strftime('%Y-%m-%d')}.xlsx" 222 | 223 | # Excel faylga yozish 224 | with pd.ExcelWriter(file_name, engine='openpyxl') as writer: 225 | df.to_excel(writer, sheet_name=table_name, index=False) 226 | 227 | conn.close() 228 | return file_name 229 | 230 | # Adminga faylni yuborish 231 | async def send_file_to_admin(): 232 | if is_first_day_of_month(): 233 | file_path = export_db_to_excel() 234 | with open(file_path, "rb") as file: 235 | await bot.send_document(chat_id=ADMIN_ID, document=file, caption="Oylik SQLite backup") 236 | print("Backup yaratildi va adminga yuborildi.") 237 | else: 238 | print("Bugun birinchi kun emas.") 239 | 240 | # Bot buyruqlari 241 | @dp.message_handler(commands=["backup"]) 242 | async def manual_backup(message: types.Message): 243 | """Qo'lda /backup buyrug'ini yuborgan adminga xabar yuboradi.""" 244 | if str(message.chat.id) == ADMIN_ID: 245 | file_path = export_db_to_excel() 246 | with open(file_path, "rb") as file: 247 | await bot.send_document(chat_id=ADMIN_ID, document=file, caption="Manually requested SQLite backup") 248 | await message.reply("✅ Backup yaratildi va yuborildi!") 249 | else: 250 | await message.reply("❌ Sizda ruxsat yo'q!") 251 | 252 | # Har oy avtomatik yuborish uchun task 253 | async def scheduler(): 254 | while True: 255 | now = datetime.datetime.now() 256 | if now.hour == 0 and now.minute == 0: # 00:00 da ishga tushadi 257 | await send_file_to_admin() 258 | await asyncio.sleep(60) # Har daqiqa tekshiradi 259 | 260 | # Botni ishga tushirish 261 | async def on_startup(dp): 262 | asyncio.create_task(scheduler()) 263 | await check_group() -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "728dafd69d2427e0cfd3cfb8b4d38e9a7bdad1cf1566a0d1694ecbf8bb7044b2" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.9" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "aiogram": { 20 | "hashes": [ 21 | "sha256:2f3259fcf7598a166aace022768a77be8ab732971f54e08a3b828a896cfe2bad", 22 | "sha256:8ce2886c6c74a35f7a75fb6422af53914aa9c1b7d7032de8d58a3f411349700d" 23 | ], 24 | "index": "pypi", 25 | "version": "==2.14.3" 26 | }, 27 | "aiohttp": { 28 | "hashes": [ 29 | "sha256:02f46fc0e3c5ac58b80d4d56eb0a7c7d97fcef69ace9326289fb9f1955e65cfe", 30 | "sha256:0563c1b3826945eecd62186f3f5c7d31abb7391fedc893b7e2b26303b5a9f3fe", 31 | "sha256:114b281e4d68302a324dd33abb04778e8557d88947875cbf4e842c2c01a030c5", 32 | "sha256:14762875b22d0055f05d12abc7f7d61d5fd4fe4642ce1a249abdf8c700bf1fd8", 33 | "sha256:15492a6368d985b76a2a5fdd2166cddfea5d24e69eefed4630cbaae5c81d89bd", 34 | "sha256:17c073de315745a1510393a96e680d20af8e67e324f70b42accbd4cb3315c9fb", 35 | "sha256:209b4a8ee987eccc91e2bd3ac36adee0e53a5970b8ac52c273f7f8fd4872c94c", 36 | "sha256:230a8f7e24298dea47659251abc0fd8b3c4e38a664c59d4b89cca7f6c09c9e87", 37 | "sha256:2e19413bf84934d651344783c9f5e22dee452e251cfd220ebadbed2d9931dbf0", 38 | "sha256:393f389841e8f2dfc86f774ad22f00923fdee66d238af89b70ea314c4aefd290", 39 | "sha256:3cf75f7cdc2397ed4442594b935a11ed5569961333d49b7539ea741be2cc79d5", 40 | "sha256:3d78619672183be860b96ed96f533046ec97ca067fd46ac1f6a09cd9b7484287", 41 | "sha256:40eced07f07a9e60e825554a31f923e8d3997cfc7fb31dbc1328c70826e04cde", 42 | "sha256:493d3299ebe5f5a7c66b9819eacdcfbbaaf1a8e84911ddffcdc48888497afecf", 43 | "sha256:4b302b45040890cea949ad092479e01ba25911a15e648429c7c5aae9650c67a8", 44 | "sha256:515dfef7f869a0feb2afee66b957cc7bbe9ad0cdee45aec7fdc623f4ecd4fb16", 45 | "sha256:547da6cacac20666422d4882cfcd51298d45f7ccb60a04ec27424d2f36ba3eaf", 46 | "sha256:5df68496d19f849921f05f14f31bd6ef53ad4b00245da3195048c69934521809", 47 | "sha256:64322071e046020e8797117b3658b9c2f80e3267daec409b350b6a7a05041213", 48 | "sha256:7615dab56bb07bff74bc865307aeb89a8bfd9941d2ef9d817b9436da3a0ea54f", 49 | "sha256:79ebfc238612123a713a457d92afb4096e2148be17df6c50fb9bf7a81c2f8013", 50 | "sha256:7b18b97cf8ee5452fa5f4e3af95d01d84d86d32c5e2bfa260cf041749d66360b", 51 | "sha256:932bb1ea39a54e9ea27fc9232163059a0b8855256f4052e776357ad9add6f1c9", 52 | "sha256:a00bb73540af068ca7390e636c01cbc4f644961896fa9363154ff43fd37af2f5", 53 | "sha256:a5ca29ee66f8343ed336816c553e82d6cade48a3ad702b9ffa6125d187e2dedb", 54 | "sha256:af9aa9ef5ba1fd5b8c948bb11f44891968ab30356d65fd0cc6707d989cd521df", 55 | "sha256:bb437315738aa441251214dad17428cafda9cdc9729499f1d6001748e1d432f4", 56 | "sha256:bdb230b4943891321e06fc7def63c7aace16095be7d9cf3b1e01be2f10fba439", 57 | "sha256:c6e9dcb4cb338d91a73f178d866d051efe7c62a7166653a91e7d9fb18274058f", 58 | "sha256:cffe3ab27871bc3ea47df5d8f7013945712c46a3cc5a95b6bee15887f1675c22", 59 | "sha256:d012ad7911653a906425d8473a1465caa9f8dea7fcf07b6d870397b774ea7c0f", 60 | "sha256:d9e13b33afd39ddeb377eff2c1c4f00544e191e1d1dee5b6c51ddee8ea6f0cf5", 61 | "sha256:e4b2b334e68b18ac9817d828ba44d8fcb391f6acb398bcc5062b14b2cbeac970", 62 | "sha256:e54962802d4b8b18b6207d4a927032826af39395a3bd9196a5af43fc4e60b009", 63 | "sha256:f705e12750171c0ab4ef2a3c76b9a4024a62c4103e3a55dd6f99265b9bc6fcfc", 64 | "sha256:f881853d2643a29e643609da57b96d5f9c9b93f62429dcc1cbb413c7d07f0e1a", 65 | "sha256:fe60131d21b31fd1a14bd43e6bb88256f69dfc3188b3a89d736d6c71ed43ec95" 66 | ], 67 | "markers": "python_version >= '3.6'", 68 | "version": "==3.7.4.post0" 69 | }, 70 | "async-timeout": { 71 | "hashes": [ 72 | "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", 73 | "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3" 74 | ], 75 | "markers": "python_full_version >= '3.5.3'", 76 | "version": "==3.0.1" 77 | }, 78 | "attrs": { 79 | "hashes": [ 80 | "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1", 81 | "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb" 82 | ], 83 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", 84 | "version": "==21.2.0" 85 | }, 86 | "babel": { 87 | "hashes": [ 88 | "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9", 89 | "sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0" 90 | ], 91 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 92 | "version": "==2.9.1" 93 | }, 94 | "certifi": { 95 | "hashes": [ 96 | "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee", 97 | "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8" 98 | ], 99 | "version": "==2021.5.30" 100 | }, 101 | "chardet": { 102 | "hashes": [ 103 | "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa", 104 | "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5" 105 | ], 106 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", 107 | "version": "==4.0.0" 108 | }, 109 | "environs": { 110 | "hashes": [ 111 | "sha256:a98005aab7613b6fe7a1af7192a5163f72a52d3348d3918e6c7a2a32e4012779", 112 | "sha256:bf3fd6bc54fcfd7f512ddcb80a7781f0ced2b0c83dd123d619e9468ecdaaf537" 113 | ], 114 | "index": "pypi", 115 | "version": "==8.0.0" 116 | }, 117 | "idna": { 118 | "hashes": [ 119 | "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a", 120 | "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3" 121 | ], 122 | "markers": "python_version >= '3.5'", 123 | "version": "==3.2" 124 | }, 125 | "marshmallow": { 126 | "hashes": [ 127 | "sha256:c67929438fd73a2be92128caa0325b1b5ed8b626d91a094d2f7f2771bf1f1c0e", 128 | "sha256:dd4724335d3c2b870b641ffe4a2f8728a1380cd2e7e2312756715ffeaa82b842" 129 | ], 130 | "markers": "python_version >= '3.5'", 131 | "version": "==3.13.0" 132 | }, 133 | "multidict": { 134 | "hashes": [ 135 | "sha256:018132dbd8688c7a69ad89c4a3f39ea2f9f33302ebe567a879da8f4ca73f0d0a", 136 | "sha256:051012ccee979b2b06be928a6150d237aec75dd6bf2d1eeeb190baf2b05abc93", 137 | "sha256:05c20b68e512166fddba59a918773ba002fdd77800cad9f55b59790030bab632", 138 | "sha256:07b42215124aedecc6083f1ce6b7e5ec5b50047afa701f3442054373a6deb656", 139 | "sha256:0e3c84e6c67eba89c2dbcee08504ba8644ab4284863452450520dad8f1e89b79", 140 | "sha256:0e929169f9c090dae0646a011c8b058e5e5fb391466016b39d21745b48817fd7", 141 | "sha256:1ab820665e67373de5802acae069a6a05567ae234ddb129f31d290fc3d1aa56d", 142 | "sha256:25b4e5f22d3a37ddf3effc0710ba692cfc792c2b9edfb9c05aefe823256e84d5", 143 | "sha256:2e68965192c4ea61fff1b81c14ff712fc7dc15d2bd120602e4a3494ea6584224", 144 | "sha256:2f1a132f1c88724674271d636e6b7351477c27722f2ed789f719f9e3545a3d26", 145 | "sha256:37e5438e1c78931df5d3c0c78ae049092877e5e9c02dd1ff5abb9cf27a5914ea", 146 | "sha256:3a041b76d13706b7fff23b9fc83117c7b8fe8d5fe9e6be45eee72b9baa75f348", 147 | "sha256:3a4f32116f8f72ecf2a29dabfb27b23ab7cdc0ba807e8459e59a93a9be9506f6", 148 | "sha256:46c73e09ad374a6d876c599f2328161bcd95e280f84d2060cf57991dec5cfe76", 149 | "sha256:46dd362c2f045095c920162e9307de5ffd0a1bfbba0a6e990b344366f55a30c1", 150 | "sha256:4b186eb7d6ae7c06eb4392411189469e6a820da81447f46c0072a41c748ab73f", 151 | "sha256:54fd1e83a184e19c598d5e70ba508196fd0bbdd676ce159feb412a4a6664f952", 152 | "sha256:585fd452dd7782130d112f7ddf3473ffdd521414674c33876187e101b588738a", 153 | "sha256:5cf3443199b83ed9e955f511b5b241fd3ae004e3cb81c58ec10f4fe47c7dce37", 154 | "sha256:6a4d5ce640e37b0efcc8441caeea8f43a06addace2335bd11151bc02d2ee31f9", 155 | "sha256:7df80d07818b385f3129180369079bd6934cf70469f99daaebfac89dca288359", 156 | "sha256:806068d4f86cb06af37cd65821554f98240a19ce646d3cd24e1c33587f313eb8", 157 | "sha256:830f57206cc96ed0ccf68304141fec9481a096c4d2e2831f311bde1c404401da", 158 | "sha256:929006d3c2d923788ba153ad0de8ed2e5ed39fdbe8e7be21e2f22ed06c6783d3", 159 | "sha256:9436dc58c123f07b230383083855593550c4d301d2532045a17ccf6eca505f6d", 160 | "sha256:9dd6e9b1a913d096ac95d0399bd737e00f2af1e1594a787e00f7975778c8b2bf", 161 | "sha256:ace010325c787c378afd7f7c1ac66b26313b3344628652eacd149bdd23c68841", 162 | "sha256:b47a43177a5e65b771b80db71e7be76c0ba23cc8aa73eeeb089ed5219cdbe27d", 163 | "sha256:b797515be8743b771aa868f83563f789bbd4b236659ba52243b735d80b29ed93", 164 | "sha256:b7993704f1a4b204e71debe6095150d43b2ee6150fa4f44d6d966ec356a8d61f", 165 | "sha256:d5c65bdf4484872c4af3150aeebe101ba560dcfb34488d9a8ff8dbcd21079647", 166 | "sha256:d81eddcb12d608cc08081fa88d046c78afb1bf8107e6feab5d43503fea74a635", 167 | "sha256:dc862056f76443a0db4509116c5cd480fe1b6a2d45512a653f9a855cc0517456", 168 | "sha256:ecc771ab628ea281517e24fd2c52e8f31c41e66652d07599ad8818abaad38cda", 169 | "sha256:f200755768dc19c6f4e2b672421e0ebb3dd54c38d5a4f262b872d8cfcc9e93b5", 170 | "sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281", 171 | "sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80" 172 | ], 173 | "markers": "python_version >= '3.6'", 174 | "version": "==5.1.0" 175 | }, 176 | "python-dotenv": { 177 | "hashes": [ 178 | "sha256:aae25dc1ebe97c420f50b81fb0e5c949659af713f31fdb63c749ca68748f34b1", 179 | "sha256:f521bc2ac9a8e03c736f62911605c5d83970021e3fa95b37d769e2bbbe9b6172" 180 | ], 181 | "markers": "python_version >= '3.5'", 182 | "version": "==0.19.0" 183 | }, 184 | "pytz": { 185 | "hashes": [ 186 | "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da", 187 | "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798" 188 | ], 189 | "version": "==2021.1" 190 | }, 191 | "typing-extensions": { 192 | "hashes": [ 193 | "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497", 194 | "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342", 195 | "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84" 196 | ], 197 | "version": "==3.10.0.0" 198 | }, 199 | "yarl": { 200 | "hashes": [ 201 | "sha256:00d7ad91b6583602eb9c1d085a2cf281ada267e9a197e8b7cae487dadbfa293e", 202 | "sha256:0355a701b3998dcd832d0dc47cc5dedf3874f966ac7f870e0f3a6788d802d434", 203 | "sha256:15263c3b0b47968c1d90daa89f21fcc889bb4b1aac5555580d74565de6836366", 204 | "sha256:2ce4c621d21326a4a5500c25031e102af589edb50c09b321049e388b3934eec3", 205 | "sha256:31ede6e8c4329fb81c86706ba8f6bf661a924b53ba191b27aa5fcee5714d18ec", 206 | "sha256:324ba3d3c6fee56e2e0b0d09bf5c73824b9f08234339d2b788af65e60040c959", 207 | "sha256:329412812ecfc94a57cd37c9d547579510a9e83c516bc069470db5f75684629e", 208 | "sha256:4736eaee5626db8d9cda9eb5282028cc834e2aeb194e0d8b50217d707e98bb5c", 209 | "sha256:4953fb0b4fdb7e08b2f3b3be80a00d28c5c8a2056bb066169de00e6501b986b6", 210 | "sha256:4c5bcfc3ed226bf6419f7a33982fb4b8ec2e45785a0561eb99274ebbf09fdd6a", 211 | "sha256:547f7665ad50fa8563150ed079f8e805e63dd85def6674c97efd78eed6c224a6", 212 | "sha256:5b883e458058f8d6099e4420f0cc2567989032b5f34b271c0827de9f1079a424", 213 | "sha256:63f90b20ca654b3ecc7a8d62c03ffa46999595f0167d6450fa8383bab252987e", 214 | "sha256:68dc568889b1c13f1e4745c96b931cc94fdd0defe92a72c2b8ce01091b22e35f", 215 | "sha256:69ee97c71fee1f63d04c945f56d5d726483c4762845400a6795a3b75d56b6c50", 216 | "sha256:6d6283d8e0631b617edf0fd726353cb76630b83a089a40933043894e7f6721e2", 217 | "sha256:72a660bdd24497e3e84f5519e57a9ee9220b6f3ac4d45056961bf22838ce20cc", 218 | "sha256:73494d5b71099ae8cb8754f1df131c11d433b387efab7b51849e7e1e851f07a4", 219 | "sha256:7356644cbed76119d0b6bd32ffba704d30d747e0c217109d7979a7bc36c4d970", 220 | "sha256:8a9066529240171b68893d60dca86a763eae2139dd42f42106b03cf4b426bf10", 221 | "sha256:8aa3decd5e0e852dc68335abf5478a518b41bf2ab2f330fe44916399efedfae0", 222 | "sha256:97b5bdc450d63c3ba30a127d018b866ea94e65655efaf889ebeabc20f7d12406", 223 | "sha256:9ede61b0854e267fd565e7527e2f2eb3ef8858b301319be0604177690e1a3896", 224 | "sha256:b2e9a456c121e26d13c29251f8267541bd75e6a1ccf9e859179701c36a078643", 225 | "sha256:b5dfc9a40c198334f4f3f55880ecf910adebdcb2a0b9a9c23c9345faa9185721", 226 | "sha256:bafb450deef6861815ed579c7a6113a879a6ef58aed4c3a4be54400ae8871478", 227 | "sha256:c49ff66d479d38ab863c50f7bb27dee97c6627c5fe60697de15529da9c3de724", 228 | "sha256:ce3beb46a72d9f2190f9e1027886bfc513702d748047b548b05dab7dfb584d2e", 229 | "sha256:d26608cf178efb8faa5ff0f2d2e77c208f471c5a3709e577a7b3fd0445703ac8", 230 | "sha256:d597767fcd2c3dc49d6eea360c458b65643d1e4dbed91361cf5e36e53c1f8c96", 231 | "sha256:d5c32c82990e4ac4d8150fd7652b972216b204de4e83a122546dce571c1bdf25", 232 | "sha256:d8d07d102f17b68966e2de0e07bfd6e139c7c02ef06d3a0f8d2f0f055e13bb76", 233 | "sha256:e46fba844f4895b36f4c398c5af062a9808d1f26b2999c58909517384d5deda2", 234 | "sha256:e6b5460dc5ad42ad2b36cca524491dfcaffbfd9c8df50508bddc354e787b8dc2", 235 | "sha256:f040bcc6725c821a4c0665f3aa96a4d0805a7aaf2caf266d256b8ed71b9f041c", 236 | "sha256:f0b059678fd549c66b89bed03efcabb009075bd131c248ecdf087bdb6faba24a", 237 | "sha256:fcbb48a93e8699eae920f8d92f7160c03567b421bc17362a9ffbbd706a816f71" 238 | ], 239 | "markers": "python_version >= '3.6'", 240 | "version": "==1.6.3" 241 | } 242 | }, 243 | "develop": {} 244 | } 245 | --------------------------------------------------------------------------------