v1.6.0: max_bot fixes — feature keys, flush→commit, test-run, categories, broadcast page, proxy error handling, deploy scripts
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user