Packages

Elixir library for building Telegram bots with macro-based API

Current section

Files

Jump to
telegram_ex lib fsm.ex
Raw

lib/fsm.ex

defmodule TelegramEx.FSM do
@moduledoc """
Per-user FSM backed by Pockets (ETS). Stores `{state, data}` keyed by chat ID.
FSM.set_state(bot_name, chat_id, :waiting_name)
FSM.set_state(bot_name, chat_id, :waiting_name, %{step: 1})
FSM.get_state(bot_name, chat_id) # => {:waiting_name, %{step: 1}}
"""
@type chat_id :: TelegramEx.Types.chat_id()
@spec init(atom()) :: :ok | {:error, term()}
def init(name) do
case Pockets.new(name) do
{:ok, _} -> :ok
{:error, reason} -> {:error, reason}
end
end
@spec reset_state(atom(), chat_id()) :: atom() | {:error, term()}
def reset_state(name, id) do
Pockets.delete(name, id)
end
@spec get_state(atom(), chat_id()) :: {term(), term()}
def get_state(name, id) do
Pockets.get(name, id, {nil, nil})
end
@spec set_state(atom(), chat_id(), atom()) :: atom() | {:error, term()}
def set_state(name, id, state) do
{_, data} = get_state(name, id)
Pockets.put(name, id, {state, data})
end
@spec set_state(atom(), chat_id(), atom(), term()) :: atom() | {:error, term()}
def set_state(name, id, state, data) do
Pockets.put(name, id, {state, data})
end
defmacro defstate(state, do: block) do
Macro.prewalk(block, fn
{:def, def_meta, [func_header | rest]} ->
{name, meta, args} = func_header
new_args =
case args do
[msg_arg, {:=, eq_meta, [{:%{}, map_meta, pairs}, binding]}] ->
[msg_arg, {:=, eq_meta, [{:%{}, map_meta, [{:state, state} | pairs]}, binding]}]
[msg_arg, {var_name, var_meta, var_ctx}] ->
binding = {var_name, var_meta, var_ctx}
[msg_arg, {:=, [], [{:%{}, [], [{:state, state}]}, binding]}]
_ ->
args
end
{:def, def_meta, [{name, meta, new_args} | rest]}
other ->
other
end)
end
end