Events
React to what happens on the server.
ctx.events.on(...) subscribes to server events. Use it for automation, logging, and anything that should happen without a user asking.
import type { PluginContext } from "@sharkord/plugin-sdk";
const onLoad = (ctx: PluginContext) => {
ctx.events.on("message:created", async (payload) => {
ctx.logger.debug("message created", payload.messageId, payload.channelId);
});
};Events are notifications about things that already happened. A handler cannot change or stop them: for that you want hooks, which run before the write.
on returns an unsubscribe function, and ctx.events.off(event, handler) does the same if you kept the handler around. Neither is needed for cleanup on unload: every subscription is removed when the plugin unloads.
Users
| Event | Payload | When |
|---|---|---|
user:joined | { userId, username } | Connected to the server |
user:left | { userId, username } | Disconnected |
user:created | { userId, username } | The account was created |
user:updated | { userId, username } | The profile changed |
user:deleted | { userId } | |
user:banned | { userId, reason?, actorUserId? } | actorUserId is absent for a plugin |
user:unbanned | { userId, actorUserId? } | |
user:kicked | { userId, reason?, actorUserId? } | |
user:joined_voice | { userId, channelId } | |
user:left_voice | { userId, channelId } |
Messages
| Event | Payload |
|---|---|
message:created | { messageId, channelId, userId, pluginId, content, textContent } |
message:updated | { messageId, channelId, userId, editedBy, pluginId, content, textContent } |
message:deleted | { messageId, channelId } |
message:pinned | { messageId, channelId, userId? } |
message:unpinned | { messageId, channelId, userId? } |
reaction:added | { messageId, channelId, userId?, pluginId?, emoji } |
reaction:removed | { messageId, channelId, userId?, pluginId?, emoji } |
content is the sanitized HTML body; textContent is the plain text of it. userId and pluginId are null when the message came from the other kind of author, so a message sent by a plugin has a pluginId and no userId. On message:updated, editedBy is the user who made the edit, which is not always the author.
Messages and reactions your own plugin creates also raise these events. Check pluginId before reacting, or you can build yourself a loop.
Channels, Categories and Roles
| Event | Payload |
|---|---|
channel:created | { channelId, name, type, categoryId } |
channel:updated | { channelId, name, type, categoryId } |
channel:deleted | { channelId, name } |
category:created | { categoryId, name } |
category:updated | { categoryId, name } |
category:deleted | { categoryId, name } |
role:created | { roleId, name } |
role:updated | { roleId, name } |
role:deleted | { roleId, name } |
role:assigned | { userId, roleId } |
role:removed | { userId, roleId } |
Voice
| Event | Payload |
|---|---|
voice:runtime_initialized | { channelId } |
voice:runtime_closed | { channelId } |
voice:producer_added | { channelId, userId, kind, producerId } |
voice:producer_removed | { channelId, userId, kind, producerId } |
A voice channel's runtime is created when the first user joins and closed when the last one leaves. These are the events to wait for before touching the Voice API: there is no router to get before the runtime exists.
A producer is added when a user starts sending audio, video or a screen, and removed however that ends: stopped, disconnected, or the channel closing. kind is a StreamKind. These are what a recorder listens to, but they only fire from the moment you subscribe, so read ctx.voice.getProducers as well or you miss everyone who was already talking.
Settings
| Event | Payload |
|---|---|
setting:set | { key, value } |
Raised when one of your own plugin's settings is written, whether by an admin in the interface or by your own set. You never see other plugins' settings. See Settings.
Handler Rules
- Handlers time out after 10 seconds. Kick off longer work and return.
- A handler that throws or times out is logged to the server log; the other handlers still run.
- Handlers run for events triggered by anyone, including other plugins.
- Emission is fire and forget. Nothing waits for your handler, so a slow one delays nothing, and returning early does not skip the operation.