initial commit
This commit is contained in:
commit
68b874d791
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
23
.idea/dataSources.xml
generated
Normal file
23
.idea/dataSources.xml
generated
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||||
|
<data-source source="LOCAL" name="telezab" uuid="ff9e489c-52ab-4c62-b43c-d17dfe7e9364">
|
||||||
|
<driver-ref>sqlite.xerial</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:sqlite:D:\Projects\Python\TeleZab2.0\db\telezab.db</jdbc-url>
|
||||||
|
<jdbc-additional-properties>
|
||||||
|
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
|
||||||
|
</jdbc-additional-properties>
|
||||||
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
|
<libraries>
|
||||||
|
<library>
|
||||||
|
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/xerial/sqlite-jdbc/3.45.1.0/sqlite-jdbc-3.45.1.0.jar</url>
|
||||||
|
</library>
|
||||||
|
<library>
|
||||||
|
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar</url>
|
||||||
|
</library>
|
||||||
|
</libraries>
|
||||||
|
</data-source>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
9
.idea/misc.xml
generated
Normal file
9
.idea/misc.xml
generated
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Black">
|
||||||
|
<option name="sdkName" value="Python 3.11 (TeleZab2.0)" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_22" project-jdk-name="Python 3.11 (TeleZab2.0)" project-jdk-type="Python SDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/TeleZab2.0.iml" filepath="$PROJECT_DIR$/TeleZab2.0.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
34
.idea/sqlDataSources.xml
generated
Normal file
34
.idea/sqlDataSources.xml
generated
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DdlMappings">
|
||||||
|
<mapping uuid="0b208055-bc8a-4f73-8d95-d9ef33a1d36c" name="telezab (DDL) Mapping">
|
||||||
|
<data-sources db="04f4618c-4b37-4c3f-af49-829b685b8abb" ddl="135f85b0-383a-4387-84ad-e842c5026d21" />
|
||||||
|
<scope>
|
||||||
|
<node negative="1">
|
||||||
|
<node kind="database" negative="1" />
|
||||||
|
<node kind="database" qname="@">
|
||||||
|
<node kind="schema" negative="1" />
|
||||||
|
</node>
|
||||||
|
<node kind="schema" qname="main" />
|
||||||
|
</node>
|
||||||
|
</scope>
|
||||||
|
</mapping>
|
||||||
|
</component>
|
||||||
|
<component name="SqlDataSourceStorage">
|
||||||
|
<option name="dataSources">
|
||||||
|
<list>
|
||||||
|
<State>
|
||||||
|
<option name="id" value="135f85b0-383a-4387-84ad-e842c5026d21" />
|
||||||
|
<option name="name" value="telezab (DDL)" />
|
||||||
|
<option name="dbmsName" value="SQLITE" />
|
||||||
|
<option name="urls">
|
||||||
|
<array>
|
||||||
|
<option value="file://$PROJECT_DIR$" />
|
||||||
|
</array>
|
||||||
|
</option>
|
||||||
|
<option name="outLayout" value="File per object by schema.groovy" />
|
||||||
|
</State>
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/sqldialects.xml
generated
Normal file
6
.idea/sqldialects.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="SqlDialectMappings">
|
||||||
|
<file url="file://$PROJECT_DIR$" dialect="SQLite" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
9
TeleZab2.0.iml
Normal file
9
TeleZab2.0.iml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.11 (TeleZab2.0)" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
3
config.py
Normal file
3
config.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import os
|
||||||
|
BOT_TOKEN = os.getenv("BOT_TOKEN")
|
||||||
|
WHITELIST_CHAT_IDS = [211595028]
|
||||||
27
main.py
Normal file
27
main.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from aiogram import Bot, F
|
||||||
|
from aiogram import Dispatcher
|
||||||
|
from aiogram import types
|
||||||
|
from aiogram.client.default import DefaultBotProperties
|
||||||
|
from aiogram.enums import ParseMode
|
||||||
|
from routers import router as main_router
|
||||||
|
from utils.db import init_db
|
||||||
|
|
||||||
|
import config
|
||||||
|
|
||||||
|
async def main ():
|
||||||
|
dp = Dispatcher()
|
||||||
|
dp.include_router(main_router)
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
bot = Bot(token=config.BOT_TOKEN,
|
||||||
|
default=DefaultBotProperties(
|
||||||
|
parse_mode=ParseMode.HTML
|
||||||
|
)
|
||||||
|
)
|
||||||
|
await init_db()
|
||||||
|
await dp.start_polling(bot)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
7
main/admins.sql
Normal file
7
main/admins.sql
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
create table admins
|
||||||
|
(
|
||||||
|
chat_id INTEGER
|
||||||
|
primary key,
|
||||||
|
username TEXT
|
||||||
|
);
|
||||||
|
|
||||||
10
main/events.sql
Normal file
10
main/events.sql
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
create table events
|
||||||
|
(
|
||||||
|
id INTEGER
|
||||||
|
primary key autoincrement,
|
||||||
|
hash TEXT
|
||||||
|
unique,
|
||||||
|
data TEXT,
|
||||||
|
delivered BOOLEAN
|
||||||
|
);
|
||||||
|
|
||||||
8
main/regions.sql
Normal file
8
main/regions.sql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
create table regions
|
||||||
|
(
|
||||||
|
region_id TEXT
|
||||||
|
primary key,
|
||||||
|
region_name TEXT,
|
||||||
|
active BOOLEAN default TRUE
|
||||||
|
);
|
||||||
|
|
||||||
10
main/subscriptions.sql
Normal file
10
main/subscriptions.sql
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
create table subscriptions
|
||||||
|
(
|
||||||
|
chat_id INTEGER,
|
||||||
|
region_id TEXT,
|
||||||
|
username TEXT,
|
||||||
|
active BOOLEAN default TRUE,
|
||||||
|
skip BOOLEAN default FALSE,
|
||||||
|
unique (chat_id, region_id)
|
||||||
|
);
|
||||||
|
|
||||||
10
main/user_events.sql
Normal file
10
main/user_events.sql
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
create table user_events
|
||||||
|
(
|
||||||
|
id INTEGER
|
||||||
|
primary key autoincrement,
|
||||||
|
chat_id INTEGER,
|
||||||
|
username TEXT,
|
||||||
|
action TEXT,
|
||||||
|
timestamp TEXT
|
||||||
|
);
|
||||||
|
|
||||||
8
main/whitelist.sql
Normal file
8
main/whitelist.sql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
create table whitelist
|
||||||
|
(
|
||||||
|
chat_id INTEGER
|
||||||
|
primary key,
|
||||||
|
username TEXT,
|
||||||
|
user_email TEXT
|
||||||
|
);
|
||||||
|
|
||||||
11
routers/__init__.py
Normal file
11
routers/__init__.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
__all__ = ("router",)
|
||||||
|
|
||||||
|
from aiogram import Router
|
||||||
|
from .commands import router as commands_router
|
||||||
|
|
||||||
|
router = Router(name=__name__)
|
||||||
|
|
||||||
|
router.include_routers(
|
||||||
|
commands_router,
|
||||||
|
)
|
||||||
|
|
||||||
18
routers/commands/__init__.py
Normal file
18
routers/commands/__init__.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
__all__ = ("router",)
|
||||||
|
|
||||||
|
from aiogram import Router
|
||||||
|
from .base_commands import router as base_commands_router
|
||||||
|
from .common_commands import router as common_commands_router
|
||||||
|
from .register_command import router as register_command_router
|
||||||
|
from .setting_commands import router as setting_commands
|
||||||
|
from .test_commands import router as test_commands_router
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
router.include_routers(base_commands_router,
|
||||||
|
register_command_router,
|
||||||
|
setting_commands,
|
||||||
|
test_commands_router)
|
||||||
|
|
||||||
|
router.include_router(common_commands_router)
|
||||||
|
|
||||||
41
routers/commands/base_commands.py
Normal file
41
routers/commands/base_commands.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
from aiogram import Router, types, F
|
||||||
|
from aiogram.filters import CommandStart, Command
|
||||||
|
from aiogram.types import KeyboardButton, ReplyKeyboardMarkup
|
||||||
|
from aiogram.utils import markdown
|
||||||
|
from config import WHITELIST_CHAT_IDS
|
||||||
|
|
||||||
|
router = Router(name=__name__)
|
||||||
|
|
||||||
|
async def is_whitelist(chat_id: int) -> bool:
|
||||||
|
return chat_id in WHITELIST_CHAT_IDS
|
||||||
|
|
||||||
|
@router.message(CommandStart())
|
||||||
|
async def handle_start(message: types.Message):
|
||||||
|
|
||||||
|
if await is_whitelist(message.chat.id):
|
||||||
|
button_settings = KeyboardButton(text="Настройки")
|
||||||
|
button_help = KeyboardButton(text="Помощь")
|
||||||
|
button_active_triggers = KeyboardButton(text="Активные тригеры")
|
||||||
|
button_row = [button_settings,button_help,button_active_triggers]
|
||||||
|
markup = ReplyKeyboardMarkup(keyboard=[button_row],resize_keyboard=True)
|
||||||
|
else:
|
||||||
|
button_register = KeyboardButton(text="Регистрация")
|
||||||
|
button_row = [button_register]
|
||||||
|
markup = ReplyKeyboardMarkup(keyboard=[button_row],resize_keyboard=True, one_time_keyboard=True)
|
||||||
|
await message.answer(text="Выберите действие:", reply_markup=markup)
|
||||||
|
|
||||||
|
|
||||||
|
@router.message(Command("help"))
|
||||||
|
async def handle_help(message: types.Message):
|
||||||
|
text = markdown.text("/start - Показать меню бота\n",
|
||||||
|
markdown.hbold("Настройки"),"- Перейти в режим настроек и управления подписками\n",
|
||||||
|
markdown.hbold("Активные тригеры")," - Получение активных проблем за последние 24 часа\n",
|
||||||
|
markdown.text(
|
||||||
|
markdown.hbold("Помощь - "),
|
||||||
|
markdown.hlink("Описание всех возможностей бота",
|
||||||
|
"https://confluence.is-mis.ru/pages/viewpage.action?pageId=460596141"),
|
||||||
|
sep=""),
|
||||||
|
sep="")
|
||||||
|
await message.answer(text=text)
|
||||||
|
|
||||||
|
|
||||||
13
routers/commands/common_commands.py
Normal file
13
routers/commands/common_commands.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
|
||||||
|
from aiogram import Router, types
|
||||||
|
|
||||||
|
router = Router(name=__name__)
|
||||||
|
|
||||||
|
@router.message()
|
||||||
|
async def unexpected_message(message: types.Message):
|
||||||
|
await message.answer(text="Неизвестная команда. попробуйте ещё раз")
|
||||||
|
|
||||||
|
@router.message()
|
||||||
|
async def cancel_button(message: types.Message):
|
||||||
|
pass
|
||||||
11
routers/commands/register_command.py
Normal file
11
routers/commands/register_command.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
from aiogram import Router, types, F
|
||||||
|
|
||||||
|
router = Router(name=__name__)
|
||||||
|
@router.message(F.text == "Регистрация")
|
||||||
|
async def handle_register(message: types.Message):
|
||||||
|
user_full_name = message.from_user.full_name
|
||||||
|
chat_id = message.chat.id
|
||||||
|
username = "@" + message.from_user.username
|
||||||
|
await message.answer(text=f"Привет, {user_full_name}!\n"
|
||||||
|
f"Твой Чат ID: {chat_id}\n"
|
||||||
|
f"Твоё имя пользователя: {username}\n")
|
||||||
75
routers/commands/setting_commands.py
Normal file
75
routers/commands/setting_commands.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
from aiogram import Router, types, F
|
||||||
|
from aiogram.fsm.context import FSMContext
|
||||||
|
from aiogram.fsm.state import StatesGroup, State
|
||||||
|
|
||||||
|
from utils.get_regions import get_sorted_regions
|
||||||
|
from utils.subscribe import handle_subscribe, handle_unsubscribe, get_user_subscriptions
|
||||||
|
from utils.show_settings_menu import show_settings_menu
|
||||||
|
|
||||||
|
from .base_commands import is_whitelist
|
||||||
|
|
||||||
|
router = Router(name=__name__)
|
||||||
|
|
||||||
|
@router.message(F.text == "Настройки")
|
||||||
|
async def handle_settings_menu(message: types.Message):
|
||||||
|
chat_id = message.chat.id
|
||||||
|
whitelist = await is_whitelist(chat_id)
|
||||||
|
if not whitelist:
|
||||||
|
text = "Вы неавторизованы для использования бота."
|
||||||
|
await message.answer(text=text)
|
||||||
|
return
|
||||||
|
|
||||||
|
# await SubscriptionState.waiting_for_action.set() # Устанавливаем состояние ожидания выбора действия
|
||||||
|
await message.answer("Выберите действие", reply_markup=show_settings_menu(chat_id))
|
||||||
|
|
||||||
|
@router.message(F.text == "Подписаться")
|
||||||
|
async def subscribe_command(message: types.Message,):
|
||||||
|
chat_id = message.chat.id
|
||||||
|
whitelist = await is_whitelist(chat_id)
|
||||||
|
if not whitelist:
|
||||||
|
text = "Вы неавторизованы для использования бота."
|
||||||
|
await message.answer(text=text)
|
||||||
|
# await state.clear() # Завершаем состояние, если пользователь не авторизован
|
||||||
|
return
|
||||||
|
|
||||||
|
# await SubscriptionState.choosing_regions.set() # Переходим в состояние ожидания ввода номеров регионов
|
||||||
|
|
||||||
|
region_list = await get_sorted_regions()
|
||||||
|
text = (f"Отправьте номер или номера регионов, на которые хотите подписаться (через запятую):\n"
|
||||||
|
f"{region_list}\n"
|
||||||
|
f"Напишите 'отмена' для отмены.")
|
||||||
|
await message.answer(text=text, reply_markup=types.ReplyKeyboardRemove())
|
||||||
|
|
||||||
|
|
||||||
|
@router.message(F.text == "Отписаться")
|
||||||
|
async def unsubscribe_command(message: types.Message):
|
||||||
|
chat_id = message.chat.id
|
||||||
|
if not await is_whitelist(chat_id):
|
||||||
|
text = "Вы неавторизованы для использования бота."
|
||||||
|
await message.answer(text=text)
|
||||||
|
# await state.clear() # Завершаем состояние, если пользователь не авторизован
|
||||||
|
return
|
||||||
|
|
||||||
|
await handle_unsubscribe(chat_id)
|
||||||
|
await message.answer("Вы успешно отписались от всех регионов.", reply_markup=show_settings_menu(chat_id))
|
||||||
|
# await state.clear() # Завершаем состояние после выполнения действия
|
||||||
|
|
||||||
|
|
||||||
|
@router.message(F.text == "Мои подписки")
|
||||||
|
async def my_subscriptions_command(message: types.Message):
|
||||||
|
chat_id = message.chat.id
|
||||||
|
if not await is_whitelist(chat_id):
|
||||||
|
text = "Вы неавторизованы для использования бота."
|
||||||
|
await message.answer(text=text)
|
||||||
|
# await state.clear() # Завершаем состояние, если пользователь не авторизован
|
||||||
|
return
|
||||||
|
|
||||||
|
# Логика для отображения подписок пользователя
|
||||||
|
subscriptions = await get_user_subscriptions(chat_id)
|
||||||
|
if subscriptions:
|
||||||
|
text = "Ваши подписки:\n" + "\n".join(f"{sub.region_id}: {sub.region_name}" for sub in subscriptions)
|
||||||
|
else:
|
||||||
|
text = "У вас нет активных подписок."
|
||||||
|
|
||||||
|
await message.answer(text=text, reply_markup=show_settings_menu(chat_id))
|
||||||
|
# await state.clear() # Завершаем состояние после выполнения действия
|
||||||
22
routers/commands/test_commands.py
Normal file
22
routers/commands/test_commands.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# handlers.py
|
||||||
|
|
||||||
|
from aiogram import types
|
||||||
|
from aiogram import Router, F
|
||||||
|
from sqlalchemy.future import select
|
||||||
|
from utils.db import AsyncSessionLocal
|
||||||
|
from utils.db.whitelist import Whitelist
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
@router.message(F.text == "Проверка функций" and F.from_user.id == 211595028)
|
||||||
|
async def check_access(message: types.Message):
|
||||||
|
chat_id = message.chat.id
|
||||||
|
async with AsyncSessionLocal() as session:
|
||||||
|
stmt = select(Whitelist).filter_by(chat_id=chat_id)
|
||||||
|
result = await session.execute(stmt)
|
||||||
|
user = result.scalars().first()
|
||||||
|
|
||||||
|
if user:
|
||||||
|
await message.answer(f"Ваш логин: {user.username}.\nВаш E-mail: {user.user_email}\nВаш чат ID: {user.chat_id}")
|
||||||
|
else:
|
||||||
|
await message.answer("Вы не в списке разрешенных.")
|
||||||
27
utils/db/__init__.py
Normal file
27
utils/db/__init__.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# utils/db/__init__.py
|
||||||
|
|
||||||
|
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
||||||
|
from sqlalchemy.orm import sessionmaker, declarative_base
|
||||||
|
|
||||||
|
# Создание асинхронного движка SQLAlchemy
|
||||||
|
DATABASE_URL = 'sqlite+aiosqlite:///db/telezab.db'
|
||||||
|
engine = create_async_engine(DATABASE_URL, echo=False)
|
||||||
|
AsyncSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine, class_=AsyncSession)
|
||||||
|
|
||||||
|
# Базовый класс для всех моделей
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
# Импорт моделей
|
||||||
|
from .whitelist import Whitelist
|
||||||
|
from .user_events import UserEvent
|
||||||
|
from .subscriptions import Subscription
|
||||||
|
from .regions import Region
|
||||||
|
from .events import Event
|
||||||
|
from .admins import Admin
|
||||||
|
|
||||||
|
# Создание всех таблиц
|
||||||
|
async def init_db():
|
||||||
|
async with engine.begin() as conn:
|
||||||
|
await conn.run_sync(Base.metadata.create_all)
|
||||||
|
print("INFO:SQLAlchemy:Database inited")
|
||||||
|
|
||||||
10
utils/db/admins.py
Normal file
10
utils/db/admins.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# utils/db/admins.py
|
||||||
|
|
||||||
|
from sqlalchemy import Column, Integer, String
|
||||||
|
from utils.db import Base
|
||||||
|
|
||||||
|
class Admin(Base):
|
||||||
|
__tablename__ = 'admins'
|
||||||
|
|
||||||
|
chat_id = Column(Integer, primary_key=True, unique=True, nullable=False)
|
||||||
|
username = Column(String, nullable=False)
|
||||||
12
utils/db/events.py
Normal file
12
utils/db/events.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# utils/db/events.py
|
||||||
|
|
||||||
|
from sqlalchemy import Column, Integer, String, Text, Boolean
|
||||||
|
from utils.db import Base
|
||||||
|
|
||||||
|
class Event(Base):
|
||||||
|
__tablename__ = 'events'
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
hash = Column(String, unique=True, nullable=False)
|
||||||
|
data = Column(Text, nullable=False)
|
||||||
|
delivered = Column(Boolean, default=False)
|
||||||
11
utils/db/regions.py
Normal file
11
utils/db/regions.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# utils/db/regions.py
|
||||||
|
|
||||||
|
from sqlalchemy import Column, String, Boolean
|
||||||
|
from utils.db import Base
|
||||||
|
|
||||||
|
class Region(Base):
|
||||||
|
__tablename__ = 'regions'
|
||||||
|
|
||||||
|
region_id = Column(String, primary_key=True, unique=True, nullable=False)
|
||||||
|
region_name = Column(String, nullable=False)
|
||||||
|
active = Column(Boolean, default=True)
|
||||||
22
utils/db/subscriptions.py
Normal file
22
utils/db/subscriptions.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# utils/db/subscriptions.py
|
||||||
|
|
||||||
|
from sqlalchemy import Column, Integer, String, Boolean, UniqueConstraint
|
||||||
|
from utils.db import Base
|
||||||
|
|
||||||
|
class Subscription(Base):
|
||||||
|
__tablename__ = 'subscriptions'
|
||||||
|
|
||||||
|
chat_id = Column(Integer, nullable=False)
|
||||||
|
region_id = Column(String, nullable=False)
|
||||||
|
username = Column(String, nullable=False)
|
||||||
|
active = Column(Boolean, default=True)
|
||||||
|
skip = Column(Boolean, default=False)
|
||||||
|
|
||||||
|
# Определение составного первичного ключа
|
||||||
|
__table_args__ = (
|
||||||
|
UniqueConstraint('chat_id', 'region_id', name='unique_chat_region'),
|
||||||
|
{'sqlite_autoincrement': True}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Определяем составной первичный ключ
|
||||||
|
primary_key = Column(Integer, primary_key=True, autoincrement=True) # Добавление этого столбца необходимо для уникального первичного ключа
|
||||||
13
utils/db/user_events.py
Normal file
13
utils/db/user_events.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# utils/db/user_events.py
|
||||||
|
|
||||||
|
from sqlalchemy import Column, Integer, String, Text
|
||||||
|
from utils.db import Base
|
||||||
|
|
||||||
|
class UserEvent(Base):
|
||||||
|
__tablename__ = 'user_events'
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
chat_id = Column(Integer, nullable=False)
|
||||||
|
username = Column(String, nullable=False)
|
||||||
|
action = Column(String, nullable=False)
|
||||||
|
timestamp = Column(Text, nullable=False)
|
||||||
9
utils/db/whitelist.py
Normal file
9
utils/db/whitelist.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from sqlalchemy import Column, Integer, String
|
||||||
|
from utils.db import Base
|
||||||
|
|
||||||
|
class Whitelist(Base):
|
||||||
|
__tablename__ = 'whitelist'
|
||||||
|
|
||||||
|
chat_id = Column(Integer, primary_key=True)
|
||||||
|
username = Column(String)
|
||||||
|
user_email = Column(String)
|
||||||
21
utils/get_regions.py
Normal file
21
utils/get_regions.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
from sqlalchemy import cast, Integer
|
||||||
|
from sqlalchemy.future import select
|
||||||
|
from utils.db import AsyncSessionLocal
|
||||||
|
from utils.db.regions import Region
|
||||||
|
|
||||||
|
async def get_sorted_regions():
|
||||||
|
"""
|
||||||
|
Получает отсортированный список активных регионов из базы данных.
|
||||||
|
|
||||||
|
:return: Список кортежей с идентификаторами и названиями регионов
|
||||||
|
"""
|
||||||
|
async with AsyncSessionLocal() as session:
|
||||||
|
async with session.begin():
|
||||||
|
# Формируем запрос для получения активных регионов
|
||||||
|
stmt = select(Region.region_id, Region.region_name).where(Region.active == True).order_by(cast(Region.region_id, Integer))
|
||||||
|
|
||||||
|
# Выполняем запрос и получаем результат
|
||||||
|
result = await session.execute(stmt)
|
||||||
|
regions = result.fetchall() # Получаем все записи как список кортежей
|
||||||
|
regions = "\n".join(f"{region_id}: {region_name}" for region_id, region_name in regions)
|
||||||
|
return regions
|
||||||
17
utils/permissions.py
Normal file
17
utils/permissions.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
from aiogram import types
|
||||||
|
|
||||||
|
from utils.db import AsyncSessionLocal, Whitelist
|
||||||
|
from sqlalchemy.future import select
|
||||||
|
|
||||||
|
async def is_whitelisted(message: types.message):
|
||||||
|
chat_id = message.chat.id
|
||||||
|
async with AsyncSessionLocal() as session:
|
||||||
|
stmt = select(Whitelist).filter_by(chat_id=chat_id)
|
||||||
|
result = await session.execute(stmt)
|
||||||
|
user = result.scalars().first()
|
||||||
|
if not user:
|
||||||
|
await message.answer("Вы не авторизованы для использования этого бота")
|
||||||
|
logging.info(f"Unauthorized access attempt by {chat_id}")
|
||||||
14
utils/show_settings_menu.py
Normal file
14
utils/show_settings_menu.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
from aiogram.types import KeyboardButton, ReplyKeyboardMarkup
|
||||||
|
|
||||||
|
|
||||||
|
def show_settings_menu(chat_id):
|
||||||
|
button_subscribe = KeyboardButton(text="Подписаться")
|
||||||
|
button_unsubscribe = KeyboardButton(text="Отписаться")
|
||||||
|
button_subscription = KeyboardButton(text="Мои подписки")
|
||||||
|
button_active_regions = KeyboardButton(text="Активные регионы")
|
||||||
|
button_cancel = KeyboardButton(text="Назад")
|
||||||
|
button_row_1 = [button_subscribe,button_unsubscribe,button_subscription]
|
||||||
|
button_row_2 = [button_active_regions]
|
||||||
|
button_row_3 = [button_cancel]
|
||||||
|
markup = ReplyKeyboardMarkup(keyboard=[button_row_1,button_row_2,button_row_3], resize_keyboard=True)
|
||||||
|
return markup
|
||||||
33
utils/subscribe.py
Normal file
33
utils/subscribe.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# utils/subscribe.py
|
||||||
|
from sqlalchemy import delete, select
|
||||||
|
from utils.db.regions import Region
|
||||||
|
from utils.db import AsyncSessionLocal, Subscription
|
||||||
|
|
||||||
|
|
||||||
|
async def handle_subscribe(user_id, region_ids):
|
||||||
|
async with AsyncSessionLocal() as session:
|
||||||
|
async with session.begin():
|
||||||
|
# Пример: удаляем старые подписки и добавляем новые
|
||||||
|
await session.execute(delete(Subscription).where(Subscription.user_id == user_id))
|
||||||
|
new_subscriptions = [Subscription(user_id=user_id, region_id=region_id) for region_id in region_ids]
|
||||||
|
session.add_all(new_subscriptions)
|
||||||
|
await session.commit()
|
||||||
|
|
||||||
|
async def handle_unsubscribe(user_id):
|
||||||
|
async with AsyncSessionLocal() as session:
|
||||||
|
async with session.begin():
|
||||||
|
# Удаление всех подписок пользователя
|
||||||
|
await session.execute(delete(Subscription).where(Subscription.user_id == user_id))
|
||||||
|
await session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
async def get_user_subscriptions(user_id):
|
||||||
|
async with AsyncSessionLocal() as session:
|
||||||
|
async with session.begin():
|
||||||
|
result = await session.execute(
|
||||||
|
select(Subscription.region_id, Region.region_name)
|
||||||
|
.join(Region, Subscription.region_id == Region.region_id)
|
||||||
|
.where(Subscription.user_id == user_id, Subscription.active == True)
|
||||||
|
)
|
||||||
|
subscriptions = result.fetchall()
|
||||||
|
return subscriptions
|
||||||
Loading…
x
Reference in New Issue
Block a user