# originchats

> Write bots for originChats servers: answer commands and slash commands, react to events, post embeds and polls, and manage channels.

This is the Markdown version of https://osl-lang.org/packages/originchats. Every page of the OSL docs has one: add .md to its path. https://osl-lang.org/llms.txt lists them all.

```osl
import "std:originchats"
```

`std:originchats` is a bot framework for originChats servers. It handles the connection, the login, reconnecting and matching replies to requests, so a bot is a list of handlers. You react to messages and slash commands, and reply with text, embeds and polls.

A bot talks to a live server, so those examples compile but don't run here. Embeds and polls are plain data you can build and look at, so they run in the page.

## The smallest bot

`originchats.new(url, options)` makes a client. `command(prefix, handler)` answers messages that start with a word, and `run` connects and blocks:

```osl
import "std:originchats"

*originchats.Client client = originchats.new("wss://chats.example.com", {token: "your rotur token"})

client.command("!ping", def(*originchats.Message msg) (
  msg.reply("pong, " ++ msg.user()).unwrap()
))

client.run("your rotur token").unwrap()
```

Send `!ping` in a channel the bot can see and it answers. The handler receives a `Message`. `msg.user()` is who wrote it, `msg.content()` is the text with the prefix removed, and `msg.args()` splits it into words.

## Reading what was said

A command's text arrives without the prefix, so a command is a function of its arguments:

```osl
import "std:originchats"

*originchats.Client client = originchats.new("wss://chats.example.com", {token: "your rotur token"})

client.command("!add", def(*originchats.Message msg) (
  string[] parts = msg.args()
  if parts.len != 2 (
    msg.reply("usage: !add 1 2").unwrap()
    return
  )
  number total = parts[1].as<number> + parts[2].as<number>
  msg.reply("= " ++ total).unwrap()
))

client.run("your rotur token").unwrap()
```

Messages have more to look at: `isReply()`, `mentionsMe()`, `attachments()`, `embeds()` and `reactions()`. They can be acted on, too: `react("👍")`, `edit(text)`, `pin()` and `delete()`.

## Events

`client.on(event, handler)` handles anything the server sends. `"ready"` runs after every connect, `"message"` for each new message no command took (the bot's own are skipped), and a server event name such as `"reaction_add"` or `"user_join"` gets the raw packet. Handlers run on their own threads, and a throw is printed instead of stopping the bot:

```osl
import "std:originchats"

*originchats.Client client = originchats.new("wss://chats.example.com", {token: "your rotur token"})

client.on("ready", def(any c, any packet) ( log "connected as " ++ client.username() ))

client.on("message", def(*originchats.Message msg) (
  if msg.mentionsMe() (
    msg.react("👀").unwrap()
  )
))

client.run("your rotur token").unwrap()
```

**Quiz.** You register `client.command("!ping", ...)` and `client.on("message", ...)`. A user sends `!ping`. Which handlers run?

- [ ] Both.
- [x] Only the command.
- [ ] Only the `"message"` handler.

Answer: The first matching command wins, and the message event doesn't fire for a message a command took.

## Slash commands

A slash command has a name, a description and typed inputs, which the server shows in its menu. Build one with `slashCommand(name)`, add inputs, then give it a handler with `fn`:

```osl
import "std:originchats"
import "std:random"

*originchats.Client client = originchats.new("wss://chats.example.com", {token: "your rotur token"})

client.slashCommand("roll")
  .description("Roll a die")
  .addInteger("sides", {description: "How many sides", min: 2})
  .fn(def(*originchats.Slash call) (
    int sides = 6
    if call.has("sides") ( sides = call.argNum("sides").as<int> )
    call.respond("You rolled " ++ random.int(1, sides)).unwrap()
  ))

client.run("your rotur token").unwrap()
```

`addString`, `addBoolean`, `addNumber`, `addUser` and `addChoices` make other inputs, `ephemeral(true)` makes the answer visible only to the caller, and `whitelistRoles` limits who may run it. In the handler, `call.argStr`, `call.argNum`, `call.argBool` and `call.arg` read the inputs, and `call.invoker()` says who ran it.

## Channels

`client.channel(name)` is a handle for a channel. Nothing is sent until you use it:

```osl
import "std:originchats"

*originchats.Client client = originchats.new("wss://chats.example.com", {token: "your rotur token"})
client.start().unwrap()

auto general = client.channel("general")
general.send("Deploy finished").unwrap()

for item in general.messages(5).unwrap() (
  auto msg = item.<originchats.Message>
  log msg.user() ++ ": " ++ msg.content()
)
client.stop()
```

The list a read returns holds untyped items, so narrow each one with `item.<originchats.Message>`. `start()` connects and waits until the server is ready, which suits a script that does a job and exits. `run()` is for a bot that stays. Channels can also make threads, pin messages, search and manage webhooks.

## Embeds

An embed is a card attached to a message: a title, text, fields, a footer, an image. `originchats.newEmbed(options)` starts one, each method adds to it and returns it, and `build()` gives the data a server receives. That is plain data, so you can look at it:

```osl
import "std:originchats"

auto embed = originchats.newEmbed({
  title: "Build finished",
  description: "All checks passed",
  color: "#22aa55"
})
  .addField("Branch", "main", {inline: true})
  .addField("Time", "42s", {inline: true})
  .withFooter("CI")
  .build()

log embed.title
log embed.fields.len
log embed
```

Output:

```text
Build finished
2
{"color":2271829,"description":"All checks passed","fields":[{"inline":true,"name":"Branch","value":"main"},{"inline":true,"name":"Time","value":"42s"}],"footer":{"text":"CI"},"title":"Build finished","type":"rich"}
```

Change the colour or add a field and run it again. To post it, pass it to a send: `channel.send("", {embeds: [embed]})`.

## Polls

`originchats.newPoll(question, choices, options)` builds a poll embed. Choices are strings or `{text, emoji}`, and `multiselect` lets people pick several:

```osl
import "std:originchats"

auto poll = originchats.newPoll(
  "Lunch?",
  ["Pizza", {text: "Sushi", emoji: "🍣"}],
  {multiselect: true}
).build()
log poll
```

Output:

```text
{"poll":{"allow_multiselect":true,"options":[{"text":"Pizza"},{"emoji":"🍣","text":"Sushi"}],"question":"Lunch?"},"type":"poll"}
```

After it is posted, `client.vote(pollID, options)`, `client.poll(pollID)` and `client.endPoll(pollID)` work with it.

### Exercise: A status card

Build an embed titled `Status` with the description `All good` and one field named `Uptime` with the value `99.9%`. Print its `fields` length.

Starting code:

```osl
import "std:originchats"

auto embed = originchats.newEmbed({title: "Status"}).build()
log embed.title
```

It should print:

```text
1
```

Hint 1: Add `description` to the options, chain `.addField("Uptime", "99.9%")` before `.build()`.

A solution:

```osl
import "std:originchats"

auto embed = originchats.newEmbed({title: "Status", description: "All good"})
  .addField("Uptime", "99.9%")
  .build()
log embed.fields.len
```

## Server management

A bot with the right permissions can manage the server: `createChannel`, `deleteChannel`, `createRole`, `setRoles`, `kickUser`, `banUser`, `muteUser`, `setNickname` and `uploadFile`. Reads such as `users()`, `usersOnline()`, `roles()` and `channels()` return results too. Check `client.supports(command)` before using something an older server may lack.

## API reference

## API reference

Every member of `std:originchats`, generated from the source of OSL.

### Functions

#### `originchats.embed(any ...args) *originchats.Embed`

**Deprecated.** Deprecated: use originchats.newEmbed({title, description}) instead.

embed starts a rich embed: embed(title?, description?).

#### `originchats.new(string url, object? options) *originchats.Client`

new builds a bot client for url; start() or run() connects it. Options:
token, username, password, register, serverPassword, invite, clientName,
timeout (milliseconds, default 10000), ignoreSelf, signing and roturURL.
Requests fail with StateError, TimeoutError, PermissionError,
NotFoundError, ValueError or RuntimeError.

#### `originchats.newEmbed(object? options) *originchats.Embed`

newEmbed starts a rich embed. Options: title, description, url (strings),
color ("#rrggbb" or {r, g, b}). Chain the embed's methods to fill it in.

#### `originchats.newPoll(string question, array choices, object? options) *originchats.Embed`

newPoll makes a poll embed asking question. choices are strings or
{text, emoji} objects. Options: multiselect (boolean).

#### `originchats.poll(string question, array choices, any ...multiselect) *originchats.Embed`

**Deprecated.** Deprecated: use originchats.newPoll(question, choices, {multiselect}) instead.

poll makes a poll embed: poll(question, options, multiselect?).

### Constants

- `string originchats.boolean`: Boolean is the "bool" input type for slashCommand.addInput.

- `string originchats.choice`: Choice is the "enum" input type for slashCommand.addInput.

- `string originchats.integer`: Integer is the "int" input type for slashCommand.addInput.

- `string originchats.number`: Number is the "float" input type for slashCommand.addInput.

- `string originchats.string`: String is the "str" input type for slashCommand.addInput.

- `string originchats.username`: Username is the "user" input type for slashCommand.addInput.

### `*originchats.Client`

#### `client.attachments(int offset, int limit) result<array, error>`

attachments returns up to limit of the bot's uploaded attachments,
skipping the first offset.

#### `client.ban(string username) result<unit, error>`

ban bans username.

#### `client.call(object payload, object? options) result<any, error>`

call sends a raw command object (with a cmd key, in the server's format)
and returns the server's answer, for commands the client has no method
for. Options: wait (boolean, default true; false sends without waiting
and returns null). Fails like any request.

#### `client.channel(string name) *originchats.Channel`

channel is a handle for the channel name. Nothing is sent until you use it.

#### `client.channels() result<array, error>`

channels returns the server's channels.

#### `client.clientName(string name) *originchats.Client`

**Deprecated.** Deprecated: use originchats.new(url, {clientName}) instead.

clientName sets the reported client name and returns the client.

#### `client.command(string prefix, any handler)`

command runs handler(message) for messages starting with prefix (as a
whole word), with the prefix stripped from message.content(). The first
matching command wins and the message event doesn't fire for it.

#### `client.connect(any ...token) boolean`

**Deprecated.** Deprecated: use client.start() instead, which waits for the login and fails with a typed error.

connect opens the websocket and returns whether that worked; the login
finishes in the background and "ready" handlers run once it is done.

#### `client.connected() boolean`

**Deprecated.** Deprecated: use client.isConnected() instead.

connected reports whether the websocket is open.

#### `client.createChannel(string name, object? options) result<unit, error>`

createChannel creates a text channel called name. fields are extra
channel fields in the server's format, such as {type: "voice"} or
{description: "..."}.

#### `client.createRole(object role) result<unit, error>`

createRole creates a role from its fields (name, color, permissions, ...
in the server's format).

#### `client.delete(string channel, string id) boolean`

**Deprecated.** Deprecated: use (channel.get(id)).delete() instead.

delete deletes a message by channel and id.

#### `client.deleteChannel(string name) result<unit, error>`

deleteChannel deletes the channel name.

#### `client.deleteRole(string name) result<unit, error>`

deleteRole deletes the role name.

#### `client.deleteWebhook(string id) result<unit, error>`

deleteWebhook deletes the webhook id.

#### `client.details() any`

details is the whole handshake object (server, limits, capabilities,
permissions, ...), or null before the handshake.

#### `client.dispatch(string raw)`

**Deprecated.** Deprecated: an internal hook; servers deliver packets themselves.

dispatch handles one raw packet as if the server had sent it.

#### `client.edit(string channel, string id, string content) boolean`

**Deprecated.** Deprecated: use (channel.get(id)).edit(content) instead.

edit edits a message by channel and id.

#### `client.emojis() result<object, error>`

emojis returns the server's custom emojis.

#### `client.endPoll(string pollID) result<unit, error>`

endPoll closes the poll pollID.

#### `client.get(string key) any`

get returns the value set under key, or null.

#### `client.httpURL() result<string, error>`

httpURL is the server's HTTP origin, derived from the websocket address.
Fails with ParseError when the address isn't a URL.

#### `client.ignoreSelf(boolean v) *originchats.Client`

**Deprecated.** Deprecated: use originchats.new(url, {ignoreSelf}) instead.

ignoreSelf sets whether the bot's own messages are skipped and returns the client.

#### `client.invite(string code) *originchats.Client`

**Deprecated.** Deprecated: use originchats.new(url, {invite}) instead.

invite sets the invite code and returns the client.

#### `client.isConnected() boolean`

isConnected reports whether the websocket is open (it may still be logging in).

#### `client.isReady() boolean`

isReady reports whether the bot is logged in and ready.

#### `client.isSigning() boolean`

isSigning reports whether outgoing messages are signed: signing is on, the
server supports it and the bot's rotur key was loaded.

#### `client.kick(string username, any ...reason) boolean`

**Deprecated.** Deprecated: use client.kickUser(username, {reason}) instead.

kick disconnects a user: kick(username, reason?).

#### `client.kickUser(string username, object? options) result<unit, error>`

kickUser disconnects username. Options: reason (string).

#### `client.latency() result<number, error>`

latency measures the round trip to the server in ms.

#### `client.limits() any`

limits is the server's limits object from the handshake, or null before it.

#### `client.login(string username, string password) *originchats.Client`

**Deprecated.** Deprecated: use originchats.new(url, {username, password}) instead.

login logs in with a local account and returns the client.

#### `client.me() any`

me is the bot's user object from the server, or null before it is ready.

#### `client.message(string channel, string id) object`

**Deprecated.** Deprecated: use client.channel(name).get(id) instead.

message returns one message of a channel as an object, or {}.

#### `client.messages(string channel, number limit) array`

**Deprecated.** Deprecated: use client.channel(name).messages(limit) instead.

messages returns recent messages of a channel.

#### `client.mute(string username, number seconds, any ...reason) boolean`

**Deprecated.** Deprecated: use client.muteUser(username, duration, {reason}) instead, with the duration in ms.

mute times a user out: mute(username, seconds, reason?).

#### `client.muteUser(string username, number duration, object? options) result<unit, error>`

muteUser times username out for duration ms (counted in whole seconds).
Options: reason (string).

#### `client.now() number`

now is the current time on the server clock, in unix ms.

#### `client.off(string event, any handler)`

off removes a handler added with on(event, handler).

#### `client.ok(object payload) boolean`

**Deprecated.** Deprecated: use client.call(payload) instead, which fails with a typed error.

ok sends a raw command and reports whether the server accepted it.

#### `client.on(string event, any handler)`

on adds handler for event. "ready" runs as handler(client, packet) after
every (re)connect, "disconnect" as handler(client) when a ready
connection drops, "message" as handler(message) for each new message no
prefix command took (the bot's own are skipped unless ignoreSelf is
false), and any server command name ("message_new", "message_edit",
"reaction_add", "typing", "user_join", "thread_create", "poll_vote",
"error", ...) or "*" for every packet as handler(client, packet). Handlers
run on background threads; a throw is printed and the bot keeps running.

#### `client.onDisconnect(any handler)`

**Deprecated.** Deprecated: use client.on("disconnect", handler) instead.

onDisconnect runs handler(client) when a ready connection drops.

#### `client.onError(any handler)`

**Deprecated.** Deprecated: use client.on("error", handler) instead.

onError runs handler(client, packet) for server errors.

#### `client.onMessageDelete(any handler)`

**Deprecated.** Deprecated: use client.on("message_delete", handler) instead.

onMessageDelete runs handler(client, packet) for deletions.

#### `client.onMessageEdit(any handler)`

**Deprecated.** Deprecated: use client.on("message_edit", handler) instead.

onMessageEdit runs handler(client, packet) for edits.

#### `client.onMessageNew(any handler)`

**Deprecated.** Deprecated: use client.on("message", handler) instead.

onMessageNew runs handler(message) for new messages.

#### `client.onMessagePin(any handler)`

**Deprecated.** Deprecated: use client.on("message_pin", handler) instead.

onMessagePin runs handler(client, packet) for pins.

#### `client.onMessageUnpin(any handler)`

**Deprecated.** Deprecated: use client.on("message_unpin", handler) instead.

onMessageUnpin runs handler(client, packet) for unpins.

#### `client.onMessagesDelete(any handler)`

**Deprecated.** Deprecated: use client.on("messages_delete", handler) instead.

onMessagesDelete runs handler(client, packet) for bulk deletions.

#### `client.onPollEnd(any handler)`

**Deprecated.** Deprecated: use client.on("poll_end", handler) instead.

onPollEnd runs handler(client, packet) when a poll ends.

#### `client.onPollVote(any handler)`

**Deprecated.** Deprecated: use client.on("poll_vote", handler) instead.

onPollVote runs handler(client, packet) for poll votes.

#### `client.onRateLimit(any handler)`

**Deprecated.** Deprecated: use client.on("rate_limit", handler) instead.

onRateLimit runs handler(client, packet) when rate limited.

#### `client.onReactionAdd(any handler)`

**Deprecated.** Deprecated: use client.on("reaction_add", handler) instead.

onReactionAdd runs handler(client, packet) for added reactions.

#### `client.onReactionRemove(any handler)`

**Deprecated.** Deprecated: use client.on("reaction_remove", handler) instead.

onReactionRemove runs handler(client, packet) for removed reactions.

#### `client.onReady(any handler)`

**Deprecated.** Deprecated: use client.on("ready", handler) instead.

onReady runs handler(client) when the bot is ready.

#### `client.onStatus(any handler)`

**Deprecated.** Deprecated: use client.on("status_get", handler) and client.on("user_status", handler) instead.

onStatus runs handler(client, packet) for status packets.

#### `client.onThreadCreate(any handler)`

**Deprecated.** Deprecated: use client.on("thread_create", handler) instead.

onThreadCreate runs handler(client, packet) for new threads.

#### `client.onThreadDelete(any handler)`

**Deprecated.** Deprecated: use client.on("thread_delete", handler) instead.

onThreadDelete runs handler(client, packet) for deleted threads.

#### `client.onThreadUpdate(any handler)`

**Deprecated.** Deprecated: use client.on("thread_update", handler) instead.

onThreadUpdate runs handler(client, packet) for thread changes.

#### `client.onTyping(any handler)`

**Deprecated.** Deprecated: use client.on("typing", handler) instead.

onTyping runs handler(client, packet) for typing indicators.

#### `client.onUnreads(any handler)`

**Deprecated.** Deprecated: use client.on("unreads_update", handler) instead.

onUnreads runs handler(client, packet) for unread updates.

#### `client.onUserConnect(any handler)`

**Deprecated.** Deprecated: use client.on("user_connect", handler) instead.

onUserConnect runs handler(client, packet) when a user connects.

#### `client.onUserDisconnect(any handler)`

**Deprecated.** Deprecated: use client.on("user_disconnect", handler) instead.

onUserDisconnect runs handler(client, packet) when a user disconnects.

#### `client.onUserJoin(any handler)`

**Deprecated.** Deprecated: use client.on("user_join", handler) instead.

onUserJoin runs handler(client, packet) when a user joins.

#### `client.onUserLeave(any handler)`

**Deprecated.** Deprecated: use client.on("user_leave", handler) instead.

onUserLeave runs handler(client, packet) when a user leaves.

#### `client.onUserUpdate(any handler)`

**Deprecated.** Deprecated: use client.on("user_update", handler) instead.

onUserUpdate runs handler(client, packet) when a user changes.

#### `client.password(string pw) *originchats.Client`

**Deprecated.** Deprecated: use originchats.new(url, {serverPassword}) instead.

password sets the server password and returns the client.

#### `client.pin(string channel, string id) boolean`

**Deprecated.** Deprecated: use message.pin() instead.

pin pins a message by channel and id.

#### `client.pings(any ...limit) array`

**Deprecated.** Deprecated: use client.recentPings({limit}) instead.

pings returns recent mentions: pings(limit?).

#### `client.poll(string pollID) result<object, error>`

poll returns the poll pollID with its votes.

#### `client.react(string channel, string id, string emoji) boolean`

**Deprecated.** Deprecated: use message.react(emoji) instead.

react reacts to a message by channel and id.

#### `client.ready() boolean`

**Deprecated.** Deprecated: use client.isReady() instead.

ready reports whether the bot is ready.

#### `client.recentPings(object? options) result<array, error>`

recentPings returns recent messages that mention the bot. Options: limit (number).

#### `client.register(string username, string password) *originchats.Client`

**Deprecated.** Deprecated: use originchats.new(url, {username, password, register: true}) instead.

register creates a local bot account on first connect and returns the client.

#### `client.request(object payload) object`

**Deprecated.** Deprecated: use client.call(payload) instead, which fails with a typed error.

request sends a raw command and returns the reply, or an object with an
error key.

#### `client.roles() result<object, error>`

roles returns an object of role name to role.

#### `client.roturApi(string base) *originchats.Client`

**Deprecated.** Deprecated: use originchats.new(url, {roturURL}) instead.

roturApi sets the rotur API origin and returns the client.

#### `client.run(any ...token) result<unit, error>`

run starts the client and blocks until stop(), retrying a failed start
with backoff (1 s up to 30 s, noted on stderr); later drops reconnect by
themselves. Passing the token here is the older form of new(url,
{token}). Fails with PermissionError when the server refuses the login,
which retrying can't fix.

#### `client.search(string channel, string query, object? options) object`

**Deprecated.** Deprecated: use client.channel(name).search(query, filters?) instead.

search searches a channel.

#### `client.send(string channel, string content) *originchats.Message`

**Deprecated.** Deprecated: use client.channel(name).send(content) instead.

send posts content to a channel.

#### `client.sendCmd(object payload)`

**Deprecated.** Deprecated: use client.call(payload, {wait: false}) instead.

sendCmd sends a raw command without waiting.

#### `client.sendEmbed(string channel, string content, *originchats.Embed embed) *originchats.Message`

**Deprecated.** Deprecated: use client.channel(name).send(content, {embeds: [embed]}) instead.

sendEmbed posts content with an embed to a channel.

#### `client.sendRaw(object payload) *originchats.Message`

**Deprecated.** Deprecated: use channel.send(content, options) instead.

sendRaw posts a raw message payload.

#### `client.sendThread(string threadID, string content) *originchats.Message`

**Deprecated.** Deprecated: use client.thread(threadID).send(content) instead.

sendThread posts content to a thread.

#### `client.server() any`

server is the server's info object from the handshake ({name, icon, ...}),
or null before the handshake.

#### `client.serverTime() number`

**Deprecated.** Deprecated: use client.now() instead, which is in unix ms.

serverTime is the server clock in seconds.

#### `client.serverUrl() string`

**Deprecated.** Deprecated: use client.url() instead.

serverUrl is the websocket address.

#### `client.set(string key, any value)`

set stores value under key on the client, for sharing state between handlers.

#### `client.setNickname(string username, string nickname) result<unit, error>`

setNickname sets the nickname of username.

#### `client.setRoles(string username, array roles) result<unit, error>`

setRoles replaces the role names username has.

#### `client.setSigning(boolean enabled)`

setSigning turns message signing on or off while the bot runs.

#### `client.setStatus(string status, any ...text) boolean`

**Deprecated.** Deprecated: use client.updateStatus(status, {text}) instead.

setStatus sets the status: setStatus(status, text?).

#### `client.signing(boolean enabled) *originchats.Client`

**Deprecated.** Deprecated: use client.setSigning(enabled) or originchats.new(url, {signing}) instead.

signing turns signing on or off and returns the client.

#### `client.signingKey() object`

**Deprecated.** Deprecated: use client.signingKeyID() instead, which never exposes the private key.

signingKey returns the rotur key record used for signing, private key included, or {}.

#### `client.signingKeyID() string?`

signingKeyID is the id of the rotur key the bot signs with, or null when
it isn't signing.

#### `client.signingURL() string`

signingURL is the server URL bound into signatures (the handshake's
signing_url, else url()).

#### `client.slash(object schema, any handler)`

slash registers a slash command from a raw schema ({name, description,
options, ...} in the server's format) with handler(call). Registering a
name again replaces it. Throws ValueError when schema has no name.

#### `client.slashCommand(string name) *originchats.SlashCommand`

slashCommand starts describing the slash command name (a leading "/" is
dropped). Chain description and add* calls, then register it with fn(handler).

#### `client.start() result<unit, error>`

start opens the connection, logs in and waits until the server says the
bot is ready; dropped connections reconnect by themselves afterwards.
Already started is a no-op. Fails with NetworkError when the server can't
be reached, PermissionError when it refuses the login, TimeoutError when
it doesn't answer within the timeout, and StateError when stop()
interrupts it.

#### `client.stats() result<object, error>`

stats returns the server's statistics as the server reports them.

#### `client.status(string username) result<object, error>`

status returns the status object of username.

#### `client.stickers() result<object, error>`

stickers returns the server's stickers.

#### `client.stop()`

stop closes the connection, ends run() and fails waiting requests with
StateError. start() or run() can start the client again. Stopping twice
is a no-op.

#### `client.supports(string command) boolean`

supports reports whether the server advertised the capability command.

#### `client.thread(string threadID) *originchats.Channel`

thread is a handle for the thread threadID. Nothing is sent until you use it.

#### `client.timeout(number seconds) *originchats.Client`

**Deprecated.** Deprecated: use originchats.new(url, {timeout}) instead, with the timeout in ms.

timeout sets the request timeout in seconds (0.001 to 300) and returns the client.

#### `client.typing(string channel)`

**Deprecated.** Deprecated: use client.channel(name).typing() instead.

typing shows typing in a channel.

#### `client.unban(string username) result<unit, error>`

unban lifts the ban on username.

#### `client.unpin(string channel, string id) boolean`

**Deprecated.** Deprecated: use message.unpin() instead.

unpin unpins a message by channel and id.

#### `client.unreact(string channel, string id, string emoji) boolean`

**Deprecated.** Deprecated: use message.unreact(emoji) instead.

unreact removes a reaction by channel and id.

#### `client.unreads() result<object, error>`

unreads returns an object of channel to unread state.

#### `client.updateChannel(string name, object updates) result<unit, error>`

updateChannel changes fields of the channel name (in the server's format).

#### `client.updateRole(object role) result<unit, error>`

updateRole updates the role role.name with the other fields.

#### `client.updateStatus(string status, object? options) result<unit, error>`

updateStatus sets the bot's status ("online", "idle", "dnd" or
"offline"). Options: text (string shown with it).

#### `client.upload(string name, string data, string mimeType) object`

**Deprecated.** Deprecated: use client.uploadBytes(name, data, {mimeType}) or client.uploadFile(path) instead.

upload stores text data as a file and returns the attachment object, or
{} after printing why it failed.

#### `client.uploadBytes(string name, byte[] data, object? options) result<object, error>`

uploadBytes stores data on the server as a file called name and returns
the attachment object to pass to send({attachments}). Options: mimeType
(default guessed from name's extension). Fails with NetworkError when the
server can't be reached, the server's error kinds (by HTTP status) when it
refuses the file, and the rotur errors when the bot's token can't be
validated.

#### `client.uploadFile(string path, object? options) result<object, error>`

uploadFile uploads the file at path; see uploadBytes. Options: name
(default the file's name), mimeType (default guessed from the
extension). Fails with NotFoundError when the file doesn't exist.

#### `client.url() string`

url is the websocket address the client connects to.

#### `client.userID() string?`

userID is the bot's user id, or null before it is ready.

#### `client.userRoles(string username) result<array, error>`

userRoles returns the role names username has.

#### `client.username() string?`

username is the bot's username, or null before it is ready.

#### `client.users() result<array, error>`

users returns every member.

#### `client.usersBanned() result<array, error>`

usersBanned returns the banned users.

#### `client.usersOnline() result<array, error>`

usersOnline returns the members who are online.

#### `client.vote(string pollID, any ...optionIDs) result<unit, error>`

vote votes in the poll pollID for one or more option ids (strings).

### `*originchats.Channel`

#### `channel.ack(any ...messageID) boolean`

**Deprecated.** Deprecated: use channel.markRead({messageID}) instead.

ack marks the channel read: ack(messageID?).

#### `channel.archive(boolean archived) result<unit, error>`

archive archives (true) or unarchives (false) the thread. Throws
StateError on a channel handle.

#### `channel.client() *originchats.Client`

client is the client this handle belongs to.

#### `channel.createThread(string name) result<*originchats.Channel, error>`

createThread starts a thread called name in the channel and returns it.

#### `channel.createWebhook(string name) result<object, error>`

createWebhook creates a webhook called name in the channel and returns it
(with its token).

#### `channel.delete(string id) boolean`

**Deprecated.** Deprecated: use (channel.get(id)).delete() instead.

delete deletes the message id.

#### `channel.edit(string id, string content) boolean`

**Deprecated.** Deprecated: use (channel.get(id)).edit(content) instead.

edit edits the message id.

#### `channel.editWith(string id, object payload) boolean`

**Deprecated.** Deprecated: use (channel.get(id)).edit(content, options) instead.

editWith edits the message id with a raw payload.

#### `channel.get(string id) result<*originchats.Message, error>`

get fetches the message id as a handle. Fails with NotFoundError when it
doesn't exist.

#### `channel.info() result<object, error>`

info returns the channel's or thread's info object.

#### `channel.isThread() boolean`

isThread reports whether the handle is a thread.

#### `channel.join() result<unit, error>`

join joins the thread. Throws StateError on a channel handle.

#### `channel.leave() result<unit, error>`

leave leaves the thread. Throws StateError on a channel handle.

#### `channel.listThreads(object? options) result<array, error>`

listThreads returns the channel's threads. Options: limit (number).

#### `channel.lock(boolean locked) result<unit, error>`

lock locks (true) or unlocks (false) the thread. Throws StateError on a
channel handle.

#### `channel.markRead(object? options) result<unit, error>`

markRead marks the channel read. Options: messageID (string, read up to it).

#### `channel.message(string id) object`

**Deprecated.** Deprecated: use channel.get(id) instead.

message returns the message id as an object, or {}.

#### `channel.messages(int limit) result<array, error>`

messages returns up to limit of the newest messages as objects, oldest first.

#### `channel.messagesAround(string id, int above, int below) result<array, error>`

messagesAround returns the message id with up to above older and below
newer messages.

#### `channel.messagesBatch(array ids) result<array, error>`

messagesBatch returns the messages with these ids.

#### `channel.messagesBefore(string id, int limit) result<array, error>`

messagesBefore returns up to limit messages older than the message id.

#### `channel.name() string`

name is the channel name ("" for a thread handle made with client.thread).

#### `channel.pin(string id) boolean`

**Deprecated.** Deprecated: use message.pin() instead.

pin pins the message id.

#### `channel.pinThread() result<unit, error>`

pinThread pins the thread in its channel. Throws StateError on a channel handle.

#### `channel.pinned() result<array, error>`

pinned returns the pinned messages.

#### `channel.react(string id, string emoji) boolean`

**Deprecated.** Deprecated: use message.react(emoji) instead.

react reacts to the message id.

#### `channel.remove() result<unit, error>`

remove deletes the thread. Throws StateError on a channel handle.

#### `channel.rename(string name) result<unit, error>`

rename renames the thread. Throws StateError on a channel handle.

#### `channel.replies(string id) array`

**Deprecated.** Deprecated: use message.replies() instead.

replies returns the replies to the message id, or [].

#### `channel.search(string query, object? options) result<object, error>`

search searches messages for query and returns {results, total,
has_more}. filters is passed to the server as-is (such as {pinned: true}).

#### `channel.send(string content, object? options) result<*originchats.Message, error>`

send posts content (text, may be "") and returns the sent message.
Options: embeds (array of embeds or objects), attachments (array of
objects from client.upload), alias ({name, avatar, color}: post under
another name), replyTo (message id), ping (boolean, default true: notify
the replied-to author). Signed when the client is signing.

#### `channel.sendAs(string name, string content, any ...extra) *originchats.Message`

**Deprecated.** Deprecated: use channel.send(content, {alias: {name, avatar, color}}) instead.

sendAs posts under another name: sendAs(name, content, avatar?, color?).

#### `channel.sendAttachment(string content, object attachment) *originchats.Message`

**Deprecated.** Deprecated: use channel.send(content, {attachments: [attachment]}) instead.

sendAttachment posts content with an attachment.

#### `channel.sendEmbed(string content, *originchats.Embed embed) *originchats.Message`

**Deprecated.** Deprecated: use channel.send(content, {embeds: [embed]}) instead.

sendEmbed posts content with an embed.

#### `channel.sendWith(object payload) *originchats.Message`

**Deprecated.** Deprecated: use channel.send(content, options) instead.

sendWith posts a raw payload here.

#### `channel.threadID() string?`

threadID is the thread id, or null for a channel.

#### `channel.threads(any ...limit) array`

**Deprecated.** Deprecated: use channel.listThreads({limit}) instead.

threads lists the channel's threads: threads(limit?).

#### `channel.typing() result<unit, error>`

typing shows the bot as typing here for a few seconds.

#### `channel.unpin(string id) boolean`

**Deprecated.** Deprecated: use message.unpin() instead.

unpin unpins the message id.

#### `channel.unpinThread() result<unit, error>`

unpinThread unpins the thread. Throws StateError on a channel handle.

#### `channel.unreact(string id, string emoji) boolean`

**Deprecated.** Deprecated: use message.unreact(emoji) instead.

unreact removes a reaction from the message id.

#### `channel.webhooks() result<array, error>`

webhooks returns the channel's webhooks.

### `*originchats.Message`

#### `message.alias() any`

alias is the {name, avatar, usercolor} the message was posted under, or null.

#### `message.arg(int index) string?`

arg is the index-th word of content (1 for the first), or null when there
are fewer words.

#### `message.args() array`

args splits content on whitespace, handy inside prefix commands.

#### `message.attachments() array`

attachments lists the message's attachment objects.

#### `message.authorID() string`

authorID is the author's rotur user id ("" when the server didn't send one).

#### `message.channel() string`

channel is the name of the channel the message is in.

#### `message.client() *originchats.Client`

client is the client that received or sent the message.

#### `message.content() string`

content is the message text. Inside a prefix command it is the text after
the prefix.

#### `message.data() object`

data is the message object as the server sent it.

#### `message.delete() result<unit, error>`

delete deletes the message.

#### `message.edit(string content, object? options) result<unit, error>`

edit replaces the message's text. Options: embeds, attachments (as for
channel.send). Only the bot's own messages can be edited.

#### `message.editWith(object payload) boolean`

**Deprecated.** Deprecated: use message.edit(content, options) instead.

editWith edits the message with a raw payload.

#### `message.embeds() array`

embeds lists the message's embed objects.

#### `message.id() string`

id is the message id.

#### `message.interaction() any`

interaction is the slash command call the message answers, or null.

#### `message.isAutomated() boolean`

isAutomated reports whether a webhook or slash command posted the message.

#### `message.isEdited() boolean`

isEdited reports whether the message was edited.

#### `message.isEphemeral() boolean`

isEphemeral reports whether only the bot can see the message.

#### `message.isPinned() boolean`

isPinned reports whether the message is pinned.

#### `message.isReply() boolean`

isReply reports whether the message replies to another.

#### `message.isSigned() boolean`

isSigned reports whether the message carries a rotur signature (verify
checks it).

#### `message.isThread() boolean`

isThread reports whether the message is in a thread.

#### `message.mentions(string name) boolean`

mentions reports whether the message pings name or contains @name
(ignoring case).

#### `message.mentionsMe() boolean`

mentionsMe reports whether the message mentions the bot.

#### `message.pin() result<unit, error>`

pin pins the message.

#### `message.pings() object`

pings is the message's mentions as {users, roles, replies}.

#### `message.poll() any`

poll is the poll in the message's embeds ({question, options, ...}), or null.

#### `message.raw() object`

raw is the whole server packet the message arrived in.

#### `message.react(string emoji) result<unit, error>`

react adds the reaction emoji.

#### `message.reactions() object`

reactions is an object of emoji to the usernames who reacted.

#### `message.replies() result<array, error>`

replies returns the replies to the message as objects.

#### `message.reply(string content, object? options) result<*originchats.Message, error>`

reply replies to the message in its channel or thread and returns the
reply. Options: ping (boolean, default true: notify the author), embeds,
attachments, alias (as for channel.send).

#### `message.replyEmbed(string content, *originchats.Embed embed) *originchats.Message`

**Deprecated.** Deprecated: use message.reply(content, {embeds: [embed]}) instead.

replyEmbed replies with content and an embed.

#### `message.replyNoPing(string content) *originchats.Message`

**Deprecated.** Deprecated: use message.reply(content, {ping: false}) instead.

replyNoPing replies without notifying the author.

#### `message.replyTo() any`

replyTo is the replied-to message summary ({id, user, ...}), or null.

#### `message.replyWith(object payload) *originchats.Message`

**Deprecated.** Deprecated: use message.reply(content, options) instead.

replyWith replies with a raw payload.

#### `message.send(string content) *originchats.Message`

**Deprecated.** Deprecated: use message.target().send(content) instead.

send posts content in the message's channel.

#### `message.sendEmbed(string content, *originchats.Embed embed) *originchats.Message`

**Deprecated.** Deprecated: use message.target().send(content, {embeds: [embed]}) instead.

sendEmbed posts content with an embed in the message's channel.

#### `message.sendWith(object payload) *originchats.Message`

**Deprecated.** Deprecated: use message.target().send(content, options) instead.

sendWith posts a raw payload in the message's channel.

#### `message.sentAt() number`

sentAt is when the message was sent, in unix ms.

#### `message.target() *originchats.Channel`

target is a handle for the channel or thread the message is in.

#### `message.threadID() string?`

threadID is the thread the message is in, or null outside threads.

#### `message.timestamp() number`

**Deprecated.** Deprecated: use message.sentAt() instead, which is in unix ms.

timestamp is when the message was sent, in unix seconds.

#### `message.typing()`

**Deprecated.** Deprecated: use message.target().typing() instead.

typing shows typing in the message's channel.

#### `message.unpin() result<unit, error>`

unpin unpins the message.

#### `message.unreact(string emoji) result<unit, error>`

unreact removes the bot's reaction emoji.

#### `message.user() string`

user is the author's username.

#### `message.verify() result<boolean, error>`

verify reports whether the message's rotur signature is valid for its
author. Unsigned messages, signatures more than 5 minutes off the message
time and unknown keys are false. Fails only when the author's key can't
be fetched (NetworkError and the like).

#### `message.verifyEmbeds() result<boolean, error>`

verifyEmbeds reports whether the message has embeds and every one carries
a valid author signature. Fails like verify.

#### `message.webhook() any`

webhook is the webhook that posted the message, or null.

### `*originchats.Slash`

#### `slash.arg(string name) any`

arg is the argument called name, or null when it wasn't given.

#### `slash.argBool(string name) boolean`

argBool is the argument called name as a boolean, false when it wasn't
given (see has).

#### `slash.argNum(string name) number`

argNum is the argument called name as a number, 0 when it wasn't given
(see has).

#### `slash.argStr(string name) string`

argStr is the argument called name as text, "" when it wasn't given (see has).

#### `slash.args() object`

args is an object of every argument given, by input name.

#### `slash.channel() string`

channel is the channel name the command was called in.

#### `slash.client() *originchats.Client`

client is the client the command was called on.

#### `slash.command() string`

command is the command name, without "/".

#### `slash.has(string name) boolean`

has reports whether the argument called name was given.

#### `slash.id() string`

id is the call id.

#### `slash.invoker() string`

invoker is the caller's user id.

#### `slash.isEphemeral() boolean`

isEphemeral reports whether only the caller sees the response.

#### `slash.isSigned() boolean`

isSigned reports whether the call carries the caller's rotur signature.

#### `slash.raw() object`

raw is the whole slash_call packet.

#### `slash.respond(string text) result<unit, error>`

respond answers the call with text (signed when the client is signing).
Fails with StateError when the client isn't connected.

#### `slash.send(string content) *originchats.Message`

**Deprecated.** Deprecated: use call.target().send(content) instead.

send posts content in the command's channel.

#### `slash.sendEmbed(string content, *originchats.Embed embed) *originchats.Message`

**Deprecated.** Deprecated: use call.target().send(content, {embeds: [embed]}) instead.

sendEmbed posts content with an embed in the command's channel.

#### `slash.target() *originchats.Channel`

target is a handle for the channel or thread the command was called in.

#### `slash.threadID() string?`

threadID is the thread the command was called in, or null.

#### `slash.user() string`

user is the caller's username (the id when the server sent no username).

#### `slash.verify() result<boolean, error>`

verify reports whether the call's rotur signature is valid for the
caller. Unsigned calls and unknown keys are false. Fails only when the
caller's key can't be fetched.

### `*originchats.SlashCommand`

#### `slashCommand.addBoolean(string name, object? options) *originchats.SlashCommand`

addBoolean adds a true/false input; it mutates the command and returns
it. Options as for addString.

#### `slashCommand.addChoice(string name, array choices, any ...extra) *originchats.SlashCommand`

**Deprecated.** Deprecated: use slashCommand.addChoices(name, choices, {description, required}) instead.

addChoice adds an enum input in place: addChoice(name, choices, description?, required?).

#### `slashCommand.addChoices(string name, array choices, object? options) *originchats.SlashCommand`

addChoices adds an input limited to choices (strings); it mutates the
command and returns it. Options as for addString.

#### `slashCommand.addInput(any kind, string name, any ...extra) *originchats.SlashCommand`

**Deprecated.** Deprecated: use slashCommand.addString, addInteger, addNumber, addBoolean or addUser instead.

addInput adds an input of kind ("str", "int", "float", "bool", "user" or
"enum") in place: addInput(kind, name, description?, required?).

#### `slashCommand.addInteger(string name, object? options) *originchats.SlashCommand`

addInteger adds a whole-number input; it mutates the command and returns
it. Options as for addString.

#### `slashCommand.addNumber(string name, object? options) *originchats.SlashCommand`

addNumber adds a number input; it mutates the command and returns it.
Options as for addString.

#### `slashCommand.addOption(object option) *originchats.SlashCommand`

addOption adds a raw option object in the server's format; it mutates
the command and returns it.

#### `slashCommand.addString(string name, object? options) *originchats.SlashCommand`

addString adds a text input called name; it mutates the command and
returns it. Options: description (string, default name), required
(boolean, default true).

#### `slashCommand.addUser(string name, object? options) *originchats.SlashCommand`

addUser adds a user input; it mutates the command and returns it.
Options as for addString.

#### `slashCommand.blacklistRoles(array roles) *originchats.SlashCommand`

blacklistRoles stops members with one of roles using the command; it
mutates the command and returns it.

#### `slashCommand.description(string text) *originchats.SlashCommand`

description sets the command's description; it mutates the command and
returns it.

#### `slashCommand.ephemeral(boolean on) *originchats.SlashCommand`

ephemeral makes responses visible only to the caller (on true); it
mutates the command and returns it.

#### `slashCommand.fn(any handler)`

fn registers the command with handler(call); see client.slash.

#### `slashCommand.whitelistRoles(array roles) *originchats.SlashCommand`

whitelistRoles lets only members with one of roles use the command; it
mutates the command and returns it.

### `*originchats.Embed`

#### `embed.addField(string name, string value, object? options) *originchats.Embed`

addField adds a name/value field; it mutates the embed and returns it.
Options: inline (boolean, show next to other inline fields).

#### `embed.asPoll(string question, array choices, object? options) *originchats.Embed`

asPoll turns the embed into a poll asking question; it mutates the embed
and returns it. choices are strings or {text, emoji} objects. Options:
multiselect (boolean).

#### `embed.audio(string link) *originchats.Embed`

audio sets audio from link; it mutates the embed and returns it.

#### `embed.author(string name, any ...extra) *originchats.Embed`

**Deprecated.** Deprecated: use embed.withAuthor(name, {url, iconURL}) instead.

author sets the author line in place: author(name, url?, iconURL?).

#### `embed.build() object`

build returns the embed as the object the server receives.

#### `embed.color(any value) *originchats.Embed`

color sets the accent color value: "#rrggbb", {r, g, b} or a 0xRRGGBB
number; it mutates the embed and returns it. Throws ValueError for a
malformed color.

#### `embed.description(string text) *originchats.Embed`

description sets the description; it mutates the embed and returns it.

#### `embed.field(string name, string value, any ...inline) *originchats.Embed`

**Deprecated.** Deprecated: use embed.addField(name, value, {inline}) instead.

field adds a field in place: field(name, value, inline?).

#### `embed.footer(string text, any ...iconURL) *originchats.Embed`

**Deprecated.** Deprecated: use embed.withFooter(text, {iconURL}) instead.

footer sets the footer in place: footer(text, iconURL?).

#### `embed.image(string link) *originchats.Embed`

image sets a large image from link; it mutates the embed and returns it.

#### `embed.markdown(boolean on) *originchats.Embed`

markdown turns markdown rendering on (true) or off; it mutates the embed
and returns it.

#### `embed.poll(string question, array choices, any ...multiselect) *originchats.Embed`

**Deprecated.** Deprecated: use embed.asPoll(question, choices, {multiselect}) instead.

poll turns the embed into a poll in place: poll(question, options, multiselect?).

#### `embed.thumbnail(string link) *originchats.Embed`

thumbnail sets a small image from link; it mutates the embed and returns
it.

#### `embed.timestamp(string value) *originchats.Embed`

timestamp shows value, an ISO 8601 time; it mutates the embed and
returns it.

#### `embed.title(string text) *originchats.Embed`

title sets the title; it mutates the embed and returns it.

#### `embed.url(string link) *originchats.Embed`

url makes the title link to link; it mutates the embed and returns it.

#### `embed.video(string link) *originchats.Embed`

video sets a video from link; it mutates the embed and returns it.

#### `embed.withAuthor(string name, object? options) *originchats.Embed`

withAuthor sets the author line; it mutates the embed and returns it.
Options: url (string), iconURL (string).

#### `embed.withFooter(string text, object? options) *originchats.Embed`

withFooter sets the footer line; it mutates the embed and returns it.
Options: iconURL (string).
