Files
site_aegisone/max_bot/app/fsm.py
T

210 lines
6.5 KiB
Python

from enum import Enum
class BotState(str, Enum):
IDLE = "idle"
NAME_REQUEST = "name_request"
CONSENT_REQUEST = "consent_request"
CONTACT_REQUEST = "contact_request"
INQUIRY_REQUEST = "inquiry_request"
CATEGORY_SELECT = "category_select"
CONFIRMATION = "confirmation"
HANDOFF_REQUEST = "handoff_request"
FEATURE_RISK_SCORE = "feature_risk_score"
FEATURE_SLA_STATUS = "feature_sla_status"
FEATURE_PHOTO_REPORT = "feature_photo_report"
FEATURE_APPOINTMENT = "feature_appointment"
FEATURE_QUALITY_RATE = "feature_quality_rate"
FEATURE_COMMERCIAL = "feature_commercial"
FEATURE_MY_OBJECTS = "feature_my_objects"
FEATURE_REMINDERS = "feature_reminders"
SLA_TICKET_CREATE = "sla_ticket_create"
KB_SEARCH = "kb_search"
FEATURE_NOTIFICATIONS = "feature_notifications"
FEATURE_BROADCAST = "feature_broadcast"
AWAITING_RATING = "awaiting_rating"
WAITING_1C_RESPONSE = "waiting_1c_response"
MAIN_MENU = "main_menu"
HANDOFF_CONFIRMATION = "handoff_confirmation"
OUT_OF_HOURS = "out_of_hours"
STATE_TRANSITIONS = {
BotState.IDLE: {
"start": BotState.NAME_REQUEST,
"new_inquiry": BotState.NAME_REQUEST,
"handoff": BotState.HANDOFF_REQUEST,
"main_menu": BotState.MAIN_MENU,
"cancel": BotState.IDLE,
},
BotState.NAME_REQUEST: {
"name_provided": BotState.CONSENT_REQUEST,
"cancel": BotState.IDLE,
},
BotState.CONSENT_REQUEST: {
"consent_given": BotState.CONTACT_REQUEST,
"consent_refused": BotState.IDLE,
"cancel": BotState.IDLE,
},
BotState.CONTACT_REQUEST: {
"contact_provided": BotState.INQUIRY_REQUEST,
"cancel": BotState.IDLE,
},
BotState.INQUIRY_REQUEST: {
"inquiry_provided": BotState.CATEGORY_SELECT,
"cancel": BotState.IDLE,
},
BotState.CATEGORY_SELECT: {
"category_selected": BotState.CONFIRMATION,
"cancel": BotState.IDLE,
},
BotState.CONFIRMATION: {
"done": BotState.IDLE,
"edit_name": BotState.NAME_REQUEST,
"edit_contact": BotState.CONTACT_REQUEST,
"edit_inquiry": BotState.INQUIRY_REQUEST,
},
BotState.HANDOFF_REQUEST: {
"confirmed": BotState.HANDOFF_CONFIRMATION,
"cancel": BotState.MAIN_MENU,
},
BotState.HANDOFF_CONFIRMATION: {
"done": BotState.IDLE,
"cancel": BotState.MAIN_MENU,
},
BotState.OUT_OF_HOURS: {
"leave_message": BotState.INQUIRY_REQUEST,
"callback": BotState.NAME_REQUEST,
"cancel": BotState.IDLE,
"main_menu": BotState.MAIN_MENU,
},
BotState.MAIN_MENU: {
"new_inquiry": BotState.NAME_REQUEST,
"feature_risk_score": BotState.FEATURE_RISK_SCORE,
"feature_sla_status": BotState.FEATURE_SLA_STATUS,
"feature_photo_report": BotState.FEATURE_PHOTO_REPORT,
"feature_appointment": BotState.FEATURE_APPOINTMENT,
"feature_quality_rate": BotState.FEATURE_QUALITY_RATE,
"feature_commercial": BotState.FEATURE_COMMERCIAL,
"feature_my_objects": BotState.FEATURE_MY_OBJECTS,
"feature_reminders": BotState.FEATURE_REMINDERS,
"feature_notifications": BotState.FEATURE_NOTIFICATIONS,
"feature_broadcast": BotState.FEATURE_BROADCAST,
"sla_ticket_create": BotState.SLA_TICKET_CREATE,
"handoff": BotState.HANDOFF_REQUEST,
"kb_search": BotState.KB_SEARCH,
"cancel": BotState.IDLE,
},
BotState.FEATURE_RISK_SCORE: {
"done": BotState.MAIN_MENU,
"cancel": BotState.MAIN_MENU,
},
BotState.FEATURE_SLA_STATUS: {
"done": BotState.MAIN_MENU,
"cancel": BotState.MAIN_MENU,
},
BotState.FEATURE_PHOTO_REPORT: {
"done": BotState.MAIN_MENU,
"photo_provided": BotState.MAIN_MENU,
"cancel": BotState.MAIN_MENU,
},
BotState.FEATURE_APPOINTMENT: {
"done": BotState.MAIN_MENU,
"date_provided": BotState.MAIN_MENU,
"cancel": BotState.MAIN_MENU,
},
BotState.FEATURE_QUALITY_RATE: {
"done": BotState.MAIN_MENU,
"rating_provided": BotState.MAIN_MENU,
"cancel": BotState.MAIN_MENU,
},
BotState.FEATURE_COMMERCIAL: {
"done": BotState.MAIN_MENU,
"cancel": BotState.MAIN_MENU,
},
BotState.FEATURE_MY_OBJECTS: {
"done": BotState.MAIN_MENU,
"cancel": BotState.MAIN_MENU,
},
BotState.FEATURE_REMINDERS: {
"done": BotState.MAIN_MENU,
"cancel": BotState.MAIN_MENU,
},
BotState.FEATURE_NOTIFICATIONS: {
"done": BotState.MAIN_MENU,
"cancel": BotState.MAIN_MENU,
},
BotState.FEATURE_BROADCAST: {
"done": BotState.MAIN_MENU,
"cancel": BotState.MAIN_MENU,
},
BotState.SLA_TICKET_CREATE: {
"done": BotState.MAIN_MENU,
"cancel": BotState.MAIN_MENU,
},
BotState.KB_SEARCH: {
"found": BotState.MAIN_MENU,
"not_found": BotState.INQUIRY_REQUEST,
"cancel": BotState.MAIN_MENU,
},
BotState.AWAITING_RATING: {
"rating_provided": BotState.IDLE,
"skip": BotState.IDLE,
},
BotState.WAITING_1C_RESPONSE: {
"received": BotState.MAIN_MENU,
"timeout": BotState.IDLE,
"cancel": BotState.MAIN_MENU,
},
}
class FSM:
_states: dict = {}
_tmp: dict = {}
@classmethod
def get_state(cls, user_id: int) -> BotState:
return cls._states.get(user_id, BotState.IDLE)
@classmethod
def set_state(cls, user_id: int, state: BotState):
cls._states[user_id] = state
@classmethod
def reset(cls, user_id: int):
cls._states.pop(user_id, None)
cls._tmp.pop(user_id, None)
@classmethod
def set_temp(cls, user_id: int, data: dict):
cls._tmp[user_id] = data
@classmethod
def get_temp(cls, user_id: int) -> dict:
return cls._tmp.get(user_id, {})
@classmethod
def clear_temp(cls, user_id: int):
cls._tmp.pop(user_id, None)
@classmethod
def can_transition(cls, user_id: int, event: str) -> bool:
current = cls.get_state(user_id)
return event in STATE_TRANSITIONS.get(current, {})
@classmethod
def transition(cls, user_id: int, event: str) -> BotState:
current = cls.get_state(user_id)
transitions = STATE_TRANSITIONS.get(current, {})
if event not in transitions:
return current
new_state = transitions[event]
cls.set_state(user_id, new_state)
return new_state
@classmethod
def clear_all(cls):
cls._states.clear()
cls._tmp.clear()