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()