├── 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 |
--------------------------------------------------------------------------------