FSM History

State transition audit log stored as a ring-buffer in FSM storage. Useful for debugging, analytics, and undo/back navigation. Added in v0.5.0.

Recording Transitions

Call push_history() whenever you transition to a new state. Each entry stores the state name and a Unix timestamp.

from kurigram_addons import FSMContext, StatesGroup, State

class OrderFlow(StatesGroup):
    cart     = State()
    checkout = State()
    payment  = State()
    done     = State()

@router.on_command("start_order")
async def start(client, message, state: FSMContext):
    await state.set_state(OrderFlow.cart)
    await state.push_history("OrderFlow:cart")
    await message.reply("Cart ready.")

@router.on_callback_query(OrderFlow.checkout.filter())
async def go_checkout(client, query, state: FSMContext):
    await state.set_state(OrderFlow.checkout)
    await state.push_history(str(OrderFlow.checkout))
    await query.answer()

Reading History

@router.on_command("history")
async def show_history(client, message, state: FSMContext):
    entries = await state.get_history(limit=5)

    lines = []
    for e in entries:
        import datetime
        ts = datetime.datetime.fromtimestamp(e["at"]).strftime("%H:%M:%S")
        lines.append(f"{ts} → {e['state']}")

    await message.reply("\n".join(lines) or "No history.")

Entries are returned newest-last. The ring-buffer caps at 50 entries— older entries are dropped automatically.

Back Navigation

Use history to implement a "go back" button by reading the previous state from the log and transitioning back to it.

@router.on_callback_query(filters.regex(r"^back$"))
async def go_back(client, query, state: FSMContext):
    entries = await state.get_history(limit=2)
    if len(entries) < 2:
        await query.answer("Nothing to go back to.")
        return
    prev_state = entries[-2]["state"]   # entry before current
    await state.set_state(prev_state)
    await query.answer(f"Back to {prev_state}")

Clearing History

# Clear only history (state & data are unaffected)
await state.clear_history()

# Or clear everything at end of flow
await state.clear_state()
await state.clear_history()

API Reference

MethodDescription
await push_history(state_name, *, ttl=None)Append state_name + current timestamp to the ring-buffer
await get_history(limit=10)Return up to limit most recent entries as [{"state": str, "at": float}], newest last
await clear_history()Delete the entire history for this identifier

History is stored under a separate key (identifier + "__history__") so it never interferes with state or data.