from typing import Dict, Tuple, Type, Union
from telegram import Update
from telegram.ext import CallbackContext
from ...bot.contextual import Contextual
from ...dialogue.dialogue import Dialogue, Polemic
from ...dialogue.replicas.base import (
NoNeedReactionReplica,
Replica,
StopReplica,
)
from ...functions import (
get_chat_id,
get_from_id,
)
from ...functions.chat_id import (
ChatIdWasNotFetched,
)
from ...functions.from_id import (
FromIdWasNotFetched,
)
from ...logger.logger_interface import LoggerInterface
from .exceptions.access_deny import (
ReplicaClassAccessDeny as ReplicaClassAccessDeny,
)
class _DiscussionInterface:
def get_dialogues(self) -> Dict[int, Polemic]: ...
def get_logger(self) -> LoggerInterface: ...
[docs]
class ReplicasHandler:
[docs]
async def access_control_list(
self,
user_id: int,
replica_class: Type[Replica],
) -> bool:
_, _ = user_id, replica_class
"""Check user access to `Replica`-class within `handle()`."""
return True
[docs]
async def middleware(
self,
update: Update,
context: CallbackContext,
) -> Union[Tuple[Update, CallbackContext], None]:
"""Middleware work within `handle()`. If it returns None, `handle()` will not work."""
return update, context
[docs]
async def when_there_are_no_any_dialog(
self,
update: Update,
context: CallbackContext,
) -> Union[Tuple[Update, CallbackContext], None]:
"""Do something if `Telegram-bot` does not have any dialogue with initiator (often - the `User`)."""
return update, context
[docs]
async def handle(
self,
update: Update,
context: CallbackContext,
):
"""Catch all events, which are not commands calls and buttons clicks.
:raises ChatIdWasNotFetched: If User was not detected, `handle()` try find other event initiator. If event initiator not found `ChatIdWasNotFetched` will be raised.
"""
if (
await self.middleware(
update,
context,
)
is None
):
return
discussion: _DiscussionInterface = Contextual[_DiscussionInterface](context)()
try:
from_id = get_from_id(update, context)
if not from_id:
discussion.get_logger().warning("from_id is None")
return
if from_id in discussion.get_dialogues():
await self.get_replica_from_dialogue(
update,
context,
)
else:
await self.when_there_are_no_any_dialog(
update,
context,
)
except FromIdWasNotFetched as _:
discussion.get_logger().warning("from_id does not exist")
try:
chat_id = get_chat_id(update, context)
if not chat_id:
discussion.get_logger().warning("chat_id is None")
return
if chat_id in discussion.get_dialogues():
await self.get_replica_from_dialogue(
update,
context,
)
else:
await self.when_there_are_no_any_dialog(
update,
context,
)
except ChatIdWasNotFetched as chat_id_exc:
discussion.get_logger().warning("chat_id does not exist")
raise chat_id_exc
[docs]
async def get_replica_from_dialogue(
self,
update: Update,
context: CallbackContext,
):
from_id = get_from_id(update, context)
if not from_id:
return
discussion: _DiscussionInterface = Contextual[_DiscussionInterface](context)()
while True:
try:
replica: Replica = discussion.get_dialogues()[from_id].send(update)
if isinstance(replica, StopReplica):
self.stop_dialogue(
context=context,
with_user_id=from_id,
)
if await self.access_control_list(
from_id,
replica.__class__,
):
await replica.as_reply(update, context)
else:
raise ReplicaClassAccessDeny(from_id, replica.__class__)
if isinstance(replica, StopReplica):
break
if isinstance(replica, NoNeedReactionReplica):
continue
break
except KeyError as e:
discussion.get_logger().error(e)
raise e
except Exception as e:
discussion.get_logger().error(e)
self.stop_dialogue(
context=context,
with_user_id=from_id,
)
raise e
[docs]
async def start_dialogue(
self,
update: Update,
context: CallbackContext,
with_user_id: int,
dialogue: Dialogue,
):
discussion: _DiscussionInterface = Contextual[_DiscussionInterface](context)()
if not with_user_id:
with_user_id = get_from_id(update, context)
self.stop_dialogue(context, with_user_id)
discussion.get_dialogues()[with_user_id] = dialogue._replicas_flow()
_ = next(discussion.get_dialogues()[with_user_id])
await self.get_replica_from_dialogue(update, context)
[docs]
def stop_dialogue(
self,
context: CallbackContext,
with_user_id: int,
):
discussion: _DiscussionInterface = Contextual[_DiscussionInterface](context)()
if with_user_id in discussion.get_dialogues():
del discussion.get_dialogues()[with_user_id]