marchat-bot

September 1, 2026 ยท View on GitHub

Standalone Go SDK for writing WebSocket bots against a marchat server. It is not a marchat plugin and does not import the marchat module or plugin/sdk. Wire structs are vendored in marchatbot/wire.go. The only runtime dependency is github.com/gorilla/websocket.

Protocol pin: marchat v1.3.6 (commit 97ea0c9), PROTOCOL.md and shared/types.go. Unknown JSON fields are ignored by encoding/json. Re-check vendored types if the protocol changes.

Handshake and Origin

The first WebSocket message is a handshake (username, optional admin / admin_key). gorilla's DefaultDialer sends no Origin header; marchat allows an empty Origin.

Admin: true requires the username on the server admin allowlist and a matching admin_key. Usernames: max 32 characters, [A-Za-z0-9_.-], not starting with : or ..

Handshake policy failures close with RFC 6455 1008 (duplicate username, ban, allowlist, invalid username, not an admin). Handshake JSON parse failures close with 1002. Invalid admin key also sends JSON auth_failed before close 1008. Run treats 1008, 1002, and auth_failed as fatal (ErrHandshakeRejected / ErrAuthFailed) and does not reconnect. Network errors still reconnect.

Server :kick disconnects the target and applies a 24 hour temp ban. Reconnect then fails handshake with close 1008.

Channels and send

Every connection is placed in general after handshake. Optional Config.Channel (when not general) sends join_channel after handshake.

The server stamps outbound channel from hub membership and sender from the authenticated session. SendText has no channel argument; call JoinChannel first. Channel() after JoinChannel / LeaveChannel is optimistic local state only.

History replay

After a successful handshake the server sends up to 50 recent visible messages, then a userlist. Those messages use the same text / dm / ... types as live traffic.

Until the first userlist on that connection, the SDK does not run Command handlers and does not fire OnText / OnDM / OnFile / OnReaction / OnEdit / OnDelete. OnRaw still fires. OnSystem may fire (join notices are live, not only replay). The replay window resets on every new connection.

E2E

The SDK parses the encrypted flag but does not encrypt or decrypt. On an E2E server, content is opaque.

Rate limit

The client spaces writes at 15 messages / 5s. The server limit is 20 / 5s, then a 10s cooldown.

Tests and examples

go test ./...

Examples (echobot, moderator, probe, apitest) are manual programs against a running marchat server. They are not CI.

Flags / env where used: MARCHAT_SERVER (default ws://localhost:8080/ws), MARCHAT_USERNAME, MARCHAT_CHANNEL, MARCHAT_ADMIN_KEY.

License

MIT. See LICENSE.