mirror of
https://github.com/Balshgit/mosgortrans.git
synced 2025-09-11 13:00:40 +03:00
bot reworked to class
This commit is contained in:
parent
2304955212
commit
591d5ea01a
10
Makefile
10
Makefile
@ -4,8 +4,14 @@ PY_TARGET_FILES=
|
|||||||
PORT=8000
|
PORT=8000
|
||||||
|
|
||||||
.PHONY: app
|
.PHONY: app
|
||||||
app:
|
app-up:
|
||||||
poetry run python app/main.py
|
docker-compose up -d --build
|
||||||
|
|
||||||
|
app-down:
|
||||||
|
docker-compose down -v
|
||||||
|
|
||||||
|
app-clean:
|
||||||
|
docker-compose down -v && docker-clean run
|
||||||
|
|
||||||
# standard commands to run on every commit
|
# standard commands to run on every commit
|
||||||
format:
|
format:
|
||||||
|
@ -30,7 +30,7 @@ killall python
|
|||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- [ ] Добавить очередь сообщений
|
- [x] Добавить очередь сообщений
|
||||||
- [x] Исправить запуск локально
|
- [x] Исправить запуск локально
|
||||||
- [ ] Добавить тестов
|
- [ ] Добавить тестов
|
||||||
- [ ] Close connection
|
- [x] Close connection
|
||||||
|
@ -4,7 +4,7 @@ from dataclasses import dataclass
|
|||||||
from aiogram import Dispatcher
|
from aiogram import Dispatcher
|
||||||
from aiogram.utils.executor import start_polling
|
from aiogram.utils.executor import start_polling
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from app.core.bot import bot, dispatcher
|
from app.core.bot import TransportBot
|
||||||
from app.core.routes import Handler
|
from app.core.routes import Handler
|
||||||
from app.core.scheduler import BotScheduler, bot_scheduler
|
from app.core.scheduler import BotScheduler, bot_scheduler
|
||||||
from app.core.utils import logger
|
from app.core.utils import logger
|
||||||
@ -18,7 +18,7 @@ class Application:
|
|||||||
|
|
||||||
async def _on_startup(self, dp: Dispatcher) -> None:
|
async def _on_startup(self, dp: Dispatcher) -> None:
|
||||||
logger.info("Start bot with webhook")
|
logger.info("Start bot with webhook")
|
||||||
await bot.set_webhook(WEBHOOK_URL)
|
await TransportBot.bot.set_webhook(WEBHOOK_URL)
|
||||||
loop = asyncio.get_running_loop()
|
loop = asyncio.get_running_loop()
|
||||||
loop.create_task(self.handler.get_updates_from_queue())
|
loop.create_task(self.handler.get_updates_from_queue())
|
||||||
logger.info(
|
logger.info(
|
||||||
@ -32,9 +32,9 @@ class Application:
|
|||||||
logger.warning('Shutting down..')
|
logger.warning('Shutting down..')
|
||||||
|
|
||||||
# Remove webhook (not acceptable in some cases)
|
# Remove webhook (not acceptable in some cases)
|
||||||
await bot.delete_webhook()
|
await TransportBot.bot.delete_webhook()
|
||||||
|
|
||||||
session = await bot.get_session()
|
session = await TransportBot.bot.get_session()
|
||||||
if session and not session.closed:
|
if session and not session.closed:
|
||||||
await session.close()
|
await session.close()
|
||||||
await asyncio.sleep(0.2)
|
await asyncio.sleep(0.2)
|
||||||
@ -45,7 +45,7 @@ class Application:
|
|||||||
def bot_polling() -> None:
|
def bot_polling() -> None:
|
||||||
logger.info("Start bot in polling mode")
|
logger.info("Start bot in polling mode")
|
||||||
start_polling(
|
start_polling(
|
||||||
dispatcher=dispatcher,
|
dispatcher=TransportBot.dispatcher,
|
||||||
skip_updates=True,
|
skip_updates=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from aiogram import Bot, types
|
from aiogram import Bot, types
|
||||||
from aiogram.contrib.middlewares.logging import LoggingMiddleware
|
from aiogram.contrib.middlewares.logging import LoggingMiddleware
|
||||||
@ -7,15 +8,22 @@ from aiogram.utils.callback_data import CallbackData
|
|||||||
from app.core.parse_web import get_driver, parse_yandex_maps
|
from app.core.parse_web import get_driver, parse_yandex_maps
|
||||||
from app.settings import TELEGRAM_API_TOKEN
|
from app.settings import TELEGRAM_API_TOKEN
|
||||||
|
|
||||||
bot = Bot(token=TELEGRAM_API_TOKEN)
|
|
||||||
dispatcher = Dispatcher(bot)
|
|
||||||
dispatcher.middleware.setup(LoggingMiddleware())
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TransportBot:
|
||||||
|
bot: Bot = Bot(TELEGRAM_API_TOKEN)
|
||||||
|
dispatcher: Dispatcher = Dispatcher(bot)
|
||||||
|
dispatcher.middleware.setup(LoggingMiddleware())
|
||||||
|
|
||||||
stations_cb = CallbackData('station', 'direction')
|
stations_cb: CallbackData = CallbackData('station', 'direction')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@dispatcher.message_handler(commands=['chatid'])
|
||||||
|
async def chat_id(message: types.Message) -> types.Message:
|
||||||
|
return await TransportBot.bot.send_message(message.chat.id, message.chat.id)
|
||||||
|
|
||||||
def get_keyboard() -> types.InlineKeyboardMarkup:
|
@staticmethod
|
||||||
|
def get_keyboard() -> types.InlineKeyboardMarkup:
|
||||||
"""
|
"""
|
||||||
Generate keyboard with list of posts
|
Generate keyboard with list of posts
|
||||||
"""
|
"""
|
||||||
@ -23,19 +31,21 @@ def get_keyboard() -> types.InlineKeyboardMarkup:
|
|||||||
|
|
||||||
markup.row(
|
markup.row(
|
||||||
types.InlineKeyboardButton(
|
types.InlineKeyboardButton(
|
||||||
'Дом -> Офис', callback_data=stations_cb.new(direction='home->office')
|
'Дом -> Офис',
|
||||||
|
callback_data=TransportBot.stations_cb.new(direction='home->office'),
|
||||||
),
|
),
|
||||||
types.InlineKeyboardButton(
|
types.InlineKeyboardButton(
|
||||||
'Офис -> Дом', callback_data=stations_cb.new(direction='office->home')
|
'Офис -> Дом',
|
||||||
|
callback_data=TransportBot.stations_cb.new(direction='office->home'),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return markup
|
return markup
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
@dispatcher.callback_query_handler(stations_cb.filter(direction='home->office'))
|
@dispatcher.callback_query_handler(stations_cb.filter(direction='home->office'))
|
||||||
async def home_office(
|
async def home_office(
|
||||||
query: types.CallbackQuery, callback_data: dict[str, str]
|
query: types.CallbackQuery, callback_data: dict[str, str]
|
||||||
) -> types.Message:
|
) -> types.Message:
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
text = parse_yandex_maps(
|
text = parse_yandex_maps(
|
||||||
driver=driver,
|
driver=driver,
|
||||||
@ -43,15 +53,15 @@ async def home_office(
|
|||||||
message='Остановка Б. Академическая ул, д. 15',
|
message='Остановка Б. Академическая ул, д. 15',
|
||||||
)
|
)
|
||||||
|
|
||||||
return await bot.send_message(
|
return await TransportBot.bot.send_message(
|
||||||
query.message.chat.id, text, reply_markup=get_keyboard()
|
query.message.chat.id, text, reply_markup=TransportBot.get_keyboard()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
@dispatcher.callback_query_handler(stations_cb.filter(direction='office->home'))
|
@dispatcher.callback_query_handler(stations_cb.filter(direction='office->home'))
|
||||||
async def office_home(
|
async def office_home(
|
||||||
query: types.CallbackQuery, callback_data: dict[str, str]
|
query: types.CallbackQuery, callback_data: dict[str, str]
|
||||||
) -> types.Message:
|
) -> types.Message:
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
text = parse_yandex_maps(
|
text = parse_yandex_maps(
|
||||||
driver=driver,
|
driver=driver,
|
||||||
@ -59,24 +69,24 @@ async def office_home(
|
|||||||
message='Остановка Улица Алабяна',
|
message='Остановка Улица Алабяна',
|
||||||
)
|
)
|
||||||
|
|
||||||
return await bot.send_message(
|
return await TransportBot.bot.send_message(
|
||||||
query.message.chat.id, text, reply_markup=get_keyboard()
|
query.message.chat.id, text, reply_markup=TransportBot.get_keyboard()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
@dispatcher.message_handler(commands=['chatid'])
|
@dispatcher.message_handler()
|
||||||
async def chat_id(message: types.Message) -> types.Message:
|
async def echo(message: types.Message) -> types.Message:
|
||||||
return await bot.send_message(message.chat.id, message.chat.id)
|
return await TransportBot.bot.send_message(
|
||||||
|
message.chat.id,
|
||||||
|
'Выбери остановку',
|
||||||
@dispatcher.message_handler()
|
reply_markup=TransportBot.get_keyboard(),
|
||||||
async def echo(message: types.Message) -> types.Message:
|
|
||||||
return await bot.send_message(
|
|
||||||
message.chat.id, 'Выбери остановку', reply_markup=get_keyboard()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def morning_bus_mailing(chat_ids: list[int] | None) -> None:
|
||||||
|
if not chat_ids:
|
||||||
|
return None
|
||||||
|
|
||||||
async def morning_bus_mailing(chat_ids: list[int]) -> None:
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
text = parse_yandex_maps(
|
text = parse_yandex_maps(
|
||||||
driver=driver,
|
driver=driver,
|
||||||
@ -85,7 +95,7 @@ async def morning_bus_mailing(chat_ids: list[int]) -> None:
|
|||||||
)
|
)
|
||||||
await asyncio.gather(
|
await asyncio.gather(
|
||||||
*[
|
*[
|
||||||
bot.send_message(
|
TransportBot.bot.send_message(
|
||||||
chat_id=chat_id, text=text, parse_mode=types.ParseMode.HTML
|
chat_id=chat_id, text=text, parse_mode=types.ParseMode.HTML
|
||||||
)
|
)
|
||||||
for chat_id in chat_ids
|
for chat_id in chat_ids
|
||||||
|
@ -3,7 +3,7 @@ from http import HTTPStatus
|
|||||||
|
|
||||||
from aiogram.types import Update
|
from aiogram.types import Update
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from app.core.bot import dispatcher
|
from app.core.bot import TransportBot
|
||||||
|
|
||||||
|
|
||||||
class Handler:
|
class Handler:
|
||||||
@ -16,10 +16,7 @@ class Handler:
|
|||||||
|
|
||||||
async def put_updates_on_queue(self, request: web.Request) -> web.Response:
|
async def put_updates_on_queue(self, request: web.Request) -> web.Response:
|
||||||
"""
|
"""
|
||||||
Listen {WEBHOOK_PATH} and proxy post request to bot
|
Listen {WEBHOOK_PATH}/{TELEGRAM_WEB_TOKEN} path and proxy post request to bot
|
||||||
|
|
||||||
:param request:
|
|
||||||
:return:
|
|
||||||
"""
|
"""
|
||||||
data = await request.json()
|
data = await request.json()
|
||||||
tg_update = Update(**data)
|
tg_update = Update(**data)
|
||||||
@ -30,5 +27,5 @@ class Handler:
|
|||||||
async def get_updates_from_queue(self) -> None:
|
async def get_updates_from_queue(self) -> None:
|
||||||
while True:
|
while True:
|
||||||
update = await self.queue.get()
|
update = await self.queue.get()
|
||||||
await dispatcher.process_update(update)
|
await TransportBot.dispatcher.process_update(update)
|
||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(0.1)
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from app.core.bot import morning_bus_mailing
|
from app.core.bot import TransportBot
|
||||||
from app.core.utils import logger
|
from app.core.utils import logger
|
||||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||||
|
|
||||||
bot_cron_jobs = {
|
bot_cron_jobs = {
|
||||||
'morning_home->work_bus': [
|
'morning_home->work_bus': {
|
||||||
|
'job': TransportBot.morning_bus_mailing,
|
||||||
|
'cron': [
|
||||||
{
|
{
|
||||||
'trigger': 'cron',
|
'trigger': 'cron',
|
||||||
'day_of_week': 'mon-fri',
|
'day_of_week': 'mon-fri',
|
||||||
@ -27,38 +29,39 @@ bot_cron_jobs = {
|
|||||||
'minute': 9,
|
'minute': 9,
|
||||||
'second': 0,
|
'second': 0,
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
}
|
'func_kwargs': {
|
||||||
user_chat_ids = {
|
|
||||||
'chat_ids': [
|
'chat_ids': [
|
||||||
417070387, # me
|
417070387, # me
|
||||||
# 431571617, # Lenok
|
# 431571617, # Lenok
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class BotScheduler:
|
class BotScheduler:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
cron_jobs: dict[str, list[dict[str, Any]]],
|
cron_jobs: dict[str, dict[str, Any]],
|
||||||
chat_ids: dict[str, list[int]] | None = None,
|
|
||||||
):
|
):
|
||||||
self.cron_jobs = cron_jobs
|
self.cron_jobs = cron_jobs
|
||||||
self.chat_ids = chat_ids
|
|
||||||
self.scheduler = AsyncIOScheduler()
|
self.scheduler = AsyncIOScheduler()
|
||||||
|
|
||||||
def add_scheduler_jobs(self, jobs_name: str) -> None:
|
def add_scheduler_jobs(self, jobs_name: str) -> None:
|
||||||
cron_jobs = self.cron_jobs.get(jobs_name)
|
cron_jobs = self.cron_jobs.get(jobs_name)
|
||||||
if not cron_jobs:
|
if not cron_jobs:
|
||||||
return None
|
return None
|
||||||
for cron in cron_jobs:
|
for cron in cron_jobs['cron']:
|
||||||
self.scheduler.add_job(morning_bus_mailing, kwargs=user_chat_ids, **cron)
|
self.scheduler.add_job(
|
||||||
logger.info(f'Added scheduled job {cron}')
|
cron_jobs['job'], kwargs=cron_jobs.get('func_kwargs'), **cron
|
||||||
|
)
|
||||||
|
logger.info(f'Added scheduled job: {cron_jobs["job"].__name__} {cron}')
|
||||||
|
|
||||||
def start(self) -> None:
|
def start(self) -> None:
|
||||||
self.scheduler.start()
|
self.scheduler.start()
|
||||||
logger.info('Scheduler started')
|
logger.info('Scheduler started')
|
||||||
|
|
||||||
|
|
||||||
bot_scheduler = BotScheduler(cron_jobs=bot_cron_jobs, chat_ids=user_chat_ids)
|
bot_scheduler = BotScheduler(cron_jobs=bot_cron_jobs)
|
||||||
bot_scheduler.add_scheduler_jobs(jobs_name='morning_home->work_bus')
|
bot_scheduler.add_scheduler_jobs(jobs_name='morning_home->work_bus')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user