Skip to content

Commit

Permalink
Merge pull request #117 from Studio-Yandex-Practicum/feature/admin_co…
Browse files Browse the repository at this point in the history
…mmands

перенос команд в админку. ( как вариант).
  • Loading branch information
NiKuma0 authored Dec 11, 2023
2 parents 634e8e7 + e3d36cc commit 56f2c2f
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 139 deletions.
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ exclude = (?x)(
| fill_db.py
| stubs/
)
disable_error_code=unused-ignore

[mypy-mmpy_bot.*]
ignore_missing_imports=True
Expand Down
96 changes: 92 additions & 4 deletions src/bot/plugins/admin.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,107 @@
import re
from functools import wraps
from typing import Any, Generic, Protocol, TypeVar

from apscheduler.schedulers.asyncio import AsyncIOScheduler
from dependency_injector.wiring import Provide, inject
from mmpy_bot import Message, Plugin, listen_to

from src.bot.services.admin import AdminService
from src.bot.services.matching import MatchingService
from src.bot.services.notify_service import NotifyService
from src.core.db.models import Admin
from src.depends import Container

ReturningT = TypeVar("ReturningT", covariant=True)

class BotAdmin(Plugin):
@listen_to("Admin", re.IGNORECASE)

class Handler(Protocol, Generic[ReturningT]):
async def __call__(
self_: Any, # type: ignore ## for pyright
self: Plugin,
message: Message,
*args: Any,
) -> ReturningT:
...


def is_admin(fn: Handler[ReturningT]) -> Handler[ReturningT | None]:
@wraps(fn)
@inject
async def admin(self, message: Message, admin_service: AdminService = Provide[Container.admin_service]) -> None:
async def wrapper(
self: Plugin,
message: Message,
*args: Any,
admin_service: AdminService = Provide[Container.admin_service],
) -> ReturningT | None:
admin_instance = Admin(username=message.sender_name, user_id=message.user_id)
if await admin_service.check_if_admin(message.user_id, admin_instance):
self.driver.reply_to(message, "Привет, админ!")
return await fn(self, message, *args)
else:
self.driver.reply_to(message, "Недостаточно прав!")
return None

return wrapper


class BotAdmin(Plugin):
@is_admin
@listen_to("notify_all_users", re.IGNORECASE)
@inject
async def test_notify_all_users(
self, message: Message, notify_service: NotifyService = Provide[Container.week_routine_service,]
) -> None:
"""Тестирование опроса по пятницам"""
await notify_service.notify_all_users(plugin=self)

@is_admin
@listen_to("monday_message", re.IGNORECASE)
@inject
async def test_monday_message(
self, message: Message, notify_service: NotifyService = Provide[Container.week_routine_service,]
) -> None:
"""Тестирование напоминания о встречах"""
await notify_service.meeting_notifications(plugin=self)

@is_admin
@listen_to("wednesday_message", re.IGNORECASE)
@inject
async def test_wednesday_message(
self, message: str, notify_service: NotifyService = Provide[Container.week_routine_service,]
) -> None:
"""Тестирование опроса по средам"""
await notify_service.match_review_notifications(plugin=self)

@is_admin
@listen_to("match", re.IGNORECASE)
@inject
async def test(
self, message: Message, matching_service: MatchingService = Provide[Container.matching_service]
) -> None:
"""Тестирование создания пар"""
try:
reply_text = await matching_service.run_matching()
self.driver.reply_to(message, reply_text)
except Exception as error:
self.driver.reply_to(message, str(error))

@is_admin
@listen_to("close", re.IGNORECASE)
@inject
async def test_closing_meetings(
self, message: Message, matching_service: MatchingService = Provide[Container.matching_service]
) -> None:
"""Тестирование закрытия встреч"""
try:
await matching_service.run_closing_meetings()
self.driver.reply_to(message, "Встречи закрыты")
except Exception as error:
self.driver.reply_to(message, str(error))

@is_admin
@listen_to("stop_jobs", re.IGNORECASE)
@inject
def cancel_jobs(self, message: Message, scheduler: AsyncIOScheduler = Provide[Container.scheduler,]) -> None:
"""Остановка всех jobs"""
scheduler.shutdown()
self.driver.reply_to(message, "All jobs cancelled.")
30 changes: 2 additions & 28 deletions src/bot/plugins/matching.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,5 @@
import re

from dependency_injector.wiring import Provide, inject
from mmpy_bot import Message, Plugin, listen_to

from src.bot.services.matching import MatchingService
from src.depends import Container
from mmpy_bot import Plugin


class Matching(Plugin):
@listen_to("/match", re.IGNORECASE)
@inject
async def test(
self, message: Message, matching_service: MatchingService = Provide[Container.matching_service]
) -> None:
try:
await matching_service.run_matching()
self.driver.reply_to(message, "Создание пар завершено")
except Exception as error:
self.driver.reply_to(message, str(error))

@listen_to("/close", re.IGNORECASE)
@inject
async def test_closing_meetings(
self, message: Message, matching_service: MatchingService = Provide[Container.matching_service]
) -> None:
try:
await matching_service.run_closing_meetings()
self.driver.reply_to(message, "Встречи закрыты")
except Exception as error:
self.driver.reply_to(message, str(error))
pass
84 changes: 3 additions & 81 deletions src/bot/plugins/week_routine.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import re
from typing import Any

from apscheduler.schedulers.asyncio import AsyncIOScheduler
from dependency_injector.wiring import Provide, inject
from mmpy_bot import ActionEvent, Plugin, listen_to, listen_webhook
from mmpy_bot.wrappers import Message
from mmpy_bot import ActionEvent, Plugin, listen_webhook

from src.bot.schemas import Actions, Attachment, Context, Integration
from src.bot.services.matching import MatchingService
from src.bot.services.notify_service import NotifyService
from src.core.db.models import MatchReviewAnswerEnum
from src.depends import Container
from src.endpoints import Endpoints

MONDAY_TIME_SENDING_MESSAGE = 10
DAY_OF_WEEK_MONDAY = "mon"
Expand All @@ -24,60 +20,19 @@


class WeekRoutine(Plugin):
@inject
def direct_friday_message(self, endpoints: Endpoints = Provide[Container.endpoints]) -> Attachment:
action_yes = Actions(
id="yes",
name="Да",
type="button",
integration=Integration(url=endpoints.add_to_meeting, context=Context(action="yes")),
)

action_no = Actions(
id="No",
name="Нет",
type="button",
integration=Integration(url=endpoints.not_meeting, context=Context(action="no")),
)

every_friday_message = Attachment(
text="Хочешь ли принять участие в random coffee на следующей неделе?", actions=[action_yes, action_no]
)
return every_friday_message

@listen_to("/notify_all_users", re.IGNORECASE)
@inject
async def test_notify_all_users(
self, message: Message, notify_service: NotifyService = Provide[Container.week_routine_service,]
) -> None:
attachments = self.direct_friday_message()
await notify_service.notify_all_users(
plugin=self, attachments=attachments, title="Еженедельный пятничный опрос"
)

@listen_to("/monday_message", re.IGNORECASE)
@inject
async def test_monday_message(
self, message: Message, notify_service: NotifyService = Provide[Container.week_routine_service,]
) -> None:
await notify_service.meeting_notifications(plugin=self)

@inject
def on_start(
self,
notify_service: NotifyService = Provide[Container.week_routine_service,],
matching_service: MatchingService = Provide[Container.matching_service],
scheduler: AsyncIOScheduler = Provide[Container.scheduler],
) -> None:
friday_attachments = self.direct_friday_message()
wednesday_attachments = self.direct_wednesday_message()

scheduler.add_job(
notify_service.notify_all_users,
"cron",
day_of_week=DAY_OF_WEEK_FRIDAY,
hour=FRIDAY_TIME_SENDING_MESSAGE,
kwargs=dict(plugin=self, attachments=friday_attachments, title="Еженедельный пятничный опрос"),
kwargs=dict(plugin=self, title="Еженедельный пятничный опрос"),
)
scheduler.add_job(
matching_service.run_matching,
Expand All @@ -97,16 +52,10 @@ def on_start(
"cron",
day_of_week=DAY_OF_WEEK_WEDNESDAY,
hour=WEDNESDAY_TIME_SENDING_MESSAGE,
kwargs=dict(plugin=self, attachments=wednesday_attachments),
kwargs=dict(plugin=self),
)
scheduler.start()

@listen_to("/stop_jobs", re.IGNORECASE)
@inject
def cancel_jobs(self, message: Message, scheduler: AsyncIOScheduler = Provide[Container.scheduler,]) -> None:
scheduler.shutdown()
self.driver.reply_to(message, "All jobs cancelled.")

@inject
async def _change_user_status(
self, user_id: str, notify_service: NotifyService = Provide[Container.week_routine_service,]
Expand Down Expand Up @@ -135,25 +84,6 @@ async def no(self, event: ActionEvent) -> None:
},
)

@inject
def direct_wednesday_message(self, endpoints: Endpoints = Provide[Container.endpoints]) -> Attachment:
action_yes = Actions(
id="yes",
name="Да",
type="button",
integration=Integration(url=endpoints.answer_yes, context=Context(action="yes")),
)

action_no = Actions(
id="No",
name="Нет",
type="button",
integration=Integration(url=endpoints.answer_no, context=Context(action="no")),
)

every_wednesday_message = Attachment(text="Удалось ли вам встретиться?", actions=[action_yes, action_no])
return every_wednesday_message

@listen_webhook("match_review_answer_yes")
async def answer_yes(
self,
Expand Down Expand Up @@ -204,11 +134,3 @@ async def _get_pair_nickname(
self, user_id: str, matching_service: MatchingService = Provide[Container.matching_service,]
) -> Any:
return await matching_service.get_match_pair_nickname(user_id)

@listen_to("/wednesday_message", re.IGNORECASE)
@inject
async def test_wednesday_message(
self, message: str, notify_service: NotifyService = Provide[Container.week_routine_service,]
) -> None:
attachments = self.direct_wednesday_message()
await notify_service.match_review_notifications(plugin=self, attachments=attachments)
13 changes: 9 additions & 4 deletions src/bot/services/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@ def __init__(self, admin_repository: AdminRepository, admin_username: str) -> No
self._admin_repository = admin_repository
self._admin_username = admin_username

async def add_admin_if_in_settings(self, instance: Admin) -> None:
async def add_admin_if_in_settings(self, instance: Admin) -> bool | None:
if instance.username == self._admin_username:
await self._admin_repository.create(instance)
return True
else:
return False

async def check_if_admin(self, user_id: int, instance: Admin) -> bool | None:
await self.add_admin_if_in_settings(instance)
if self._admin_repository.get_by_user_id(user_id) is not None:
# await self.add_admin_if_in_settings(instance)
if await self._admin_repository.get_by_user_id(user_id) is not None:
return True
elif await self.add_admin_if_in_settings(instance):
return True
else:
return None
return False
6 changes: 3 additions & 3 deletions src/bot/services/matching.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,19 @@ def __init__(self, user_repository: UserRepository, match_repository: UsersMatch
self._user_repository = user_repository
self._match_repository = match_repository

async def run_matching(self) -> list[UsersMatch] | None:
async def run_matching(self) -> str:
"""Запускает создание метчей."""
matches: list[UsersMatch] = []
while user_one := await self._user_repository.get_free_user():
if not (user_two := await self._user_repository.get_suitable_pair(user_one)):
log.info(f"Невозможно создать пару для пользователя с id {user_one.id}.")
return None
return f"Невозможно создать пару для пользователя {user_one.username}."
matches.append(match := await self._match_repository.make_match_for_user(user_one, user_two))
for user in (user_one, user_two):
user.status = StatusEnum.IN_MEETING
user.matches.append(match)
await self._user_repository.update(user.id, user)
return matches
return "Создание пар завершено"

async def run_closing_meetings(self) -> Sequence[UsersMatch]:
"""Запускает закрытие встреч."""
Expand Down
Loading

0 comments on commit 56f2c2f

Please sign in to comment.