210 lines
6.5 KiB
Python
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()
|