Add poll chat command with reaction voting

This commit is contained in:
Fisto
2026-09-11 15:59:08 +02:00
parent 8e8c15e8bc
commit ace0bb18ff
16 changed files with 291 additions and 9 deletions
+16
View File
@@ -37,6 +37,7 @@ mention the puppet first.
| `whitelist list` | Show the owner and the trusted users with their tiers. |
| `whitelist add @user [user\|trusted\|admin]` | Add one or more users. The default tier is `user`. |
| `whitelist remove @user` | Remove one or more users from the trust list. |
| `poll <question> \| <opt 1> \| <opt 2> [\| opt 3 [\| opt 4]]` | Start a reaction poll. |
Notes:
@@ -45,6 +46,21 @@ Notes:
- `whitelist` commands need the admin tier.
- The reply follows the response policy.
## Polls
The `poll` command posts a message and adds one number emoji per option. Discord users vote by
reacting. The poll closes after `pollSeconds` (default 120) and the daemon posts the result.
- Options: 2 to 4. Each option is at most 80 characters.
- Emojis: `1️⃣`, `2️⃣`, `3️⃣`, `4️⃣`.
- One vote per user. A new vote replaces the old vote.
- Removing the reaction removes the vote.
- The puppet's own reactions do not count.
- Votes do not need a trust tier. Friends can vote.
- The result message goes to the poll channel.
Example: `@puppet poll Lunch? | Pizza | Sushi`
## Response policy
The policy decides where a reply goes. Sensitive replies go by DM.
+5 -1
View File
@@ -58,9 +58,10 @@ Event line:
| `events.subscribe` | none | `{ "subscribed": true }`. The socket becomes an event subscriber. |
| `state.get` | none | The current user, the guilds, and the channels. Cache-only. |
| `voice.get` | optional `userId`, optional `guildId` | The voice channel and its members, or `channelId: null` |
| `message.send` | `channelId`, `content` | `{ channelId, sent: true }` |
| `message.send` | `channelId`, `content` | `{ channelId, messageId, sent: true }` |
| `message.read` | `channelId`, `limit` | `{ cached, messages }`. Cache-only. |
| `message.fetch` | `channelId`, `limit`, optional `before` or `after` | `{ fetched, messages }`. Fetches from Discord. |
| `reaction.add` | `channelId`, `messageId`, `emoji` | `{ added: true }` |
| `dm.send` | `userId`, `content` | `{ userId, channelId, sent: true }` |
## Validation rules
@@ -78,6 +79,8 @@ The core validates all input. The daemon cannot bypass these rules.
- `before` and `after` must be snowflakes. They are mutually exclusive. `message.fetch` uses them
to walk history.
- `voice.get` accepts snowflakes only. It reads the client cache. It never joins a call.
- `reaction.add` accepts a short emoji string without line breaks. It counts as an outbound action
for the rate limit.
- A request line must not be larger than 64 KiB.
## Core rate limit
@@ -104,6 +107,7 @@ The core rate limit is a safety net. It does not replace the daemon pacing.
| `CHANNEL_NOT_FOUND` | The channel is not in the client cache. |
| `NOT_SENDABLE` | The channel type cannot receive messages. |
| `SEND_FAILED` | Discord rejected the message. |
| `REACTION_FAILED` | Discord rejected the reaction. |
| `DM_UNAVAILABLE` | The client cannot create a DM channel. |
| `NOT_SUPPORTED` | The client does not offer the internal action. |
| `RATE_LIMITED` | The core rate limit rejected the action. |