# RaxolGateway

Unified messaging gateway for Raxol: one supervised daemon connects many chat
platforms through a single adapter contract, with process-per-chat sessions,
unified session keying, and DM pairing authorization.

## Installation

```elixir
def deps do
  [{:raxol_gateway, "~> 0.2"}]
end
```

Start the daemon under your supervisor with the handler each chat session runs:

```elixir
children = [
  {Raxol.Gateway.Supervisor,
   handler: {MyApp.ChatHandler, []}, adapter: {MyApp.Adapter, conn}}
]
```

`MyApp.ChatHandler` implements `Raxol.Gateway.Handler`; `MyApp.Adapter`
implements `Raxol.Gateway.Adapter`, and `conn` is what its `connect/1` returned.

## Pieces

- `Raxol.Gateway.Route`: the routing tuple and the session key
  `agent:main:{platform}:{chat_type}:{chat_id}`.
- `Raxol.Gateway.Adapter`: the behaviour every platform implements
  (`connect`/`disconnect`/`platform`/`normalize_event`/`send_message`).
  `Raxol.Gateway.Adapter.InMemory` is a reference adapter for tests.
- `Raxol.Gateway.Handler`: the per-chat behaviour a session runs
  (`init/2`, `handle_event/2`, optional `terminate/2` on clean stops).
- `Raxol.Gateway.Handler.Lifecycle`: a full TEA app per chat under
  `environment: :gateway` (needs the optional `raxol` dependency; no driver,
  no plugin manager, unnamed processes, so one app module serves many chats).
- `Raxol.Gateway.Session`: one process per chat, with an idle timeout.
- `Raxol.Gateway.SessionRouter`: starts and routes to sessions, keyed by
  `Route.key/1`, with the idle-timeout, cooldown, and max-session limits.
- `Raxol.Gateway.Pairing`: DM pairing codes plus allowlists and the
  authorization check order.
- `Raxol.Gateway.Delivery`: the four outbound destinations (direct, home,
  cross-platform, explicit target string).
- `Raxol.Gateway.Pipeline.Transcribe`: feed-loop stage that turns a
  `%{media: %{kind: :voice, ...}}` event into `%{text: transcript}` before
  routing (injectable fetch/convert/recognize; ffmpeg + the optional
  `raxol_speech` Recognizer by default; failures drop the one event, loudly).
- `Raxol.Gateway.Supervisor`: the daemon that ties them together.

A session optionally records each inbound event and each successfully delivered
outbound reply to a `:log` (any `append(server, conversation_id, items)`, e.g.
`Conversation.Log`) keyed by a stable `conversation_id`. Adapter errors,
exceptions, and exits emit `[:raxol_gateway, :session, :delivery_failed]` and do
not create phantom outbound history. `SessionRouter.handoff/3` rebinds a
conversation to another platform's route, reusing that `conversation_id` so the
log resumes the same history.

## Platform adapters

- Telegram: `Raxol.Telegram.GatewayAdapter` + `Raxol.Telegram.UpdatePoller`
  (in the raxol_telegram package). The adapter compiles only when
  `raxol_gateway` is already compiled; after adding `raxol_gateway` to a
  project that has `raxol_telegram`, run `mix deps.compile raxol_telegram --force`.
- Discord: `Raxol.Gateway.Adapter.Discord` (REST sends, optional `req`) +
  `Raxol.Gateway.Adapter.Discord.GatewaySocket` (Gateway v10 WebSocket feed,
  optional `mint_web_socket`).
- Email: `Raxol.Gateway.Adapter.Email` (bidirectional via optional
  `gen_smtp`: outbound SMTP submission, inbound RFC822 parsing through
  `normalize_event/1`, with `Adapter.Email.Inbox` as the sink-agnostic poll
  feed and `Adapter.Email.ThreadStore` for reply threading; the mailbox
  transport itself, IMAP/POP/Gmail, is deployment-supplied).
