Events
Listen to gateway events with client.on. Handlers receive hydrated structures, camelCase DTOs, or wire payloads depending on the event.
Basic Usage
Use client.on(Events.X, handler) to subscribe. Handlers receive event-specific payloads typed by ClientEvents. client.events is an optional chainable alias.
import { Client, Events } from '@fluxerjs/core';
const client = new Client();
client.on(Events.Ready, () => {
console.log('Bot is ready!');
});
client.on(Events.MessageCreate, async (message) => {
console.log(message.content);
});
await client.login(process.env.FLUXER_BOT_TOKEN);After Ready, client.uptime is milliseconds since that moment (null before). client.ws.ping is the last gateway heartbeat RTT in milliseconds (-1 until the first ACK; average when sharded).
Payload shapes
After the major rewrite, ClientEvents payloads fall into three groups:
- Hydrated structures -
Message,PartialMessage,Guild,Channel,GuildMember,Role,Invite,User,GuildBan,MessageReaction,GuildEmoji(via DTOs). Prefer these for bot logic. - CamelCase DTOs - normalized objects from
EventPayloads.ts(and a few inline camelCase shapes). UsechannelId/guildId, not snake_case wire fields. - Wire gateway shapes: still
Gateway*DispatchDatafrom@fluxerjs/types(snake_case). Voice events stay wire on purpose so@fluxerjs/voicecan consume them unchanged.
Nested message and invite fields are camelCase: message.call.endedAt, message.messageSnapshots, invite snapshots (parentId, creatorId, createdAt).
*Update handlers that receive old/new structures may see the same instance for both args when the cache patches in place (member, channel, guild). Message updates clone from cache; uncached edits emit a PartialMessage instead of a fake Message.
PartialMessage is a class with .partial (true) and .fetch(). It also has resolveChannel() (same as Message). edit() / reply() are not on partials. Message.partial is false.
Hydrate an update with Message.resolve(msg):
client.on(Events.MessageUpdate, async (_old, msg) => {
const message = await Message.resolve(msg);
console.log(message.content);
});
client.on(Events.MessageDelete, async (message) => {
const channel = await message.resolveChannel();
await channel.send(`${message.authorId} deleted a message`);
});GuildMemberRemove may be a PartialGuildMember (user + guild only). Check member.partial before joinedAt or roles.
Common Events
client.on(Events.Ready, () => {});
client.on(Events.MessageCreate, async (message) => {});
client.on(Events.MessageUpdate, (oldMessage, newMessage) => {
if (newMessage.partial) return; // uncached edit: ids/content only
});
client.on(Events.MessageDelete, (message) => {}); // PartialMessage (not Message)
client.on(Events.MessageReactionAdd, (payload) => {});
client.on(Events.MessageReactionRemove, (payload) => {});
client.on(Events.GuildCreate, (guild) => {});
client.on(Events.GuildUnavailable, (guild) => {});
client.on(Events.GuildAvailable, (guild) => {});
client.on(Events.GuildUpdate, (oldGuild, newGuild) => {});
client.on(Events.GuildDelete, (guild) => {});
client.on(Events.ChannelCreate, (channel) => {});
client.on(Events.ChannelUpdate, (oldChannel, newChannel) => {});
client.on(Events.ChannelDelete, (channel) => {});
client.on(Events.GuildMemberAdd, (member) => {});
client.on(Events.GuildMemberUpdate, (oldMember, newMember) => {}); // oldMember may be null
client.on(Events.GuildMemberRemove, (member) => {
console.log(`${member.user.username} left ${member.guild.name}`);
if (!member.partial) console.log(`joined ${member.joinedAt}`);
});
client.on(Events.GuildRoleCreate, (role) => {});
client.on(Events.GuildRoleUpdate, ({ role, oldRole }) => {});
client.on(Events.GuildRoleDelete, ({ roleId, guildId, role }) => {});
client.on(Events.GuildBanAdd, (ban) => {});
client.on(Events.GuildBanRemove, (ban) => {});
client.on(Events.InviteCreate, (invite) => {});
client.on(Events.InviteDelete, (payload) => {
console.log(payload.code, payload.channelId, payload.guildId);
});
client.on(Events.UserUpdate, (user) => {});
// Voice - wire Gateway* shapes (intentional)
client.on(Events.VoiceStateUpdate, (data) => {});
client.on(Events.VoiceServerUpdate, (data) => {});GuildUnavailable means the gateway temporarily lost access to a guild. The guild remains in client.guilds with guild.available === false, and repeated unavailable dispatches do not re-emit the event. When it returns, the same Guild instance is refreshed, available becomes true, and GuildAvailable fires. GuildDelete is reserved for permanent removal.
Reaction Events
MessageReactionAdd / MessageReactionRemove emit a single camelCase DTO (MessageReactionPayload). message / channel are cached or null. Reply with payload.message when it is there; otherwise reaction.fetchMessage().
import { Client, Events } from '@fluxerjs/core';
const client = new Client();
client.on(Events.MessageReactionAdd, async ({ reaction, message, emoji, userId, messageId }) => {
const emojiStr = emoji.id ? `<:${emoji.name}:${emoji.id}>` : emoji.name;
console.log(`User ${userId} reacted with ${emojiStr} on message ${messageId}`);
const msg = message ?? (await reaction.fetchMessage());
if (emoji.name === '👍') await msg.react('✅');
});
client.on(Events.MessageReactionRemove, ({ userId, emoji, messageId }) => {
console.log(`User ${userId} removed ${emoji.name} from message ${messageId}`);
});
client.on(Events.MessageReactionRemoveAll, (data) => {
console.log(`All reactions cleared from message ${data.messageId}`);
});
client.on(Events.MessageReactionRemoveEmoji, (data) => {
console.log(`All ${data.emoji.name} reactions removed from message ${data.messageId}`);
});
await client.login(process.env.FLUXER_BOT_TOKEN);Error Handling
client.on(Events.Error, (err) => {
console.error('Client error:', err);
});
client.on(Events.Debug, (message) => {
console.debug(message);
});Client Events Reference
Events exposed on Client via Events / ClientEvents. Only these names are supported - raw gateway dispatches that are not listed here are not emitted as public client events.
| Category | Events |
|---|---|
| Connection | Ready, Resumed, Error, Debug |
| Messages | MessageCreate, MessageUpdate, MessageDelete, MessageDeleteBulk |
| Reactions | MessageReactionAdd, MessageReactionAddMany, MessageReactionRemove, MessageReactionRemoveAll, MessageReactionRemoveEmoji |
| Guild | GuildCreate, GuildAvailable, GuildUnavailable, GuildUpdate, GuildDelete, GuildCountsUpdate, GuildEmojisUpdate, GuildStickersUpdate, GuildAuditLogEntryCreate |
| Members | GuildMemberAdd, GuildMemberUpdate, GuildMemberRemove, GuildMembersChunk |
| Roles | GuildRoleCreate, GuildRoleUpdate, GuildRoleDelete |
| Moderation | GuildBanAdd, GuildBanRemove |
| Channels | ChannelCreate, ChannelUpdate, ChannelDelete, ChannelPinsUpdate, ChannelMemberCountsUpdate, ChannelRecipientAdd, ChannelRecipientRemove |
| Invites | InviteCreate, InviteDelete |
| Users / presence | UserUpdate, UserConnectionsUpdate, WebAuthnCredentialsUpdate, PresenceUpdate, PresenceUpdateBulk, TypingStart |
| Webhooks | WebhooksUpdate |
| Voice | VoiceStateUpdate, VoiceStateAck, VoiceServerUpdate, EntranceSoundPlay, VoiceStatesSync |
Event Payload Reference
Handler argument types from ClientEvents. Structure names are classes from @fluxerjs/core. DTOs are camelCase. Wire rows use Gateway* types from @fluxerjs/types (snake_case fields).
Events | Handler args |
|---|---|
Ready | (none) |
Resumed | (none) |
Error | Error |
Debug | string |
MessageCreate | Message |
MessageUpdate | Message | null, Message | PartialMessage (narrow with newMessage.partial) |
MessageDelete | PartialMessage (.partial, .fetch(), .resolveChannel()) |
MessageDeleteBulk | MessageDeleteBulkPayload - { ids, channelId, guildId, channel, messages } |
MessageReactionAdd | MessageReactionPayload - { reaction, user, message, channel, member, messageId, channelId, emoji, userId } |
MessageReactionAddMany | MessageReactionAddManyPayload |
MessageReactionRemove | MessageReactionPayload |
MessageReactionRemoveAll | MessageReactionRemoveAllPayload |
MessageReactionRemoveEmoji | MessageReactionRemoveEmojiPayload |
GuildCreate | Guild |
GuildAvailable | Guild |
GuildUnavailable | Guild |
GuildUpdate | Guild, Guild |
GuildDelete | Guild |
GuildMemberAdd | GuildMember |
GuildMemberUpdate | GuildMember | null, GuildMember (old is a clone, or null if uncached) |
GuildMemberRemove | GuildMember | PartialGuildMember (narrow with member.partial) |
GuildMembersChunk | GuildMembersChunkPayload |
GuildCountsUpdate | GuildCountsUpdatePayload |
ChannelMemberCountsUpdate | ChannelMemberCountsUpdatePayload |
GuildAuditLogEntryCreate | AuditLogEntryPayload |
GuildBanAdd | GuildBan |
GuildBanRemove | GuildBan |
GuildEmojisUpdate | GuildEmojisUpdatePayload - { guildId, emojis } |
GuildStickersUpdate | GuildStickersUpdatePayload - { guildId, stickers } |
GuildRoleCreate | Role |
GuildRoleUpdate | GuildRoleUpdatePayload - { role, oldRole } |
GuildRoleDelete | GuildRoleDeletePayload - { roleId, guildId, role } |
ChannelCreate | Channel |
ChannelUpdate | Channel, Channel |
ChannelDelete | Channel |
ChannelPinsUpdate | ChannelPinsUpdatePayload |
ChannelRecipientAdd | ChannelRecipientPayload |
ChannelRecipientRemove | ChannelRecipientPayload |
InviteCreate | Invite |
InviteDelete | InviteDeletePayload |
TypingStart | TypingStartPayload |
UserUpdate | User |
UserConnectionsUpdate | GatewayUserConnectionsUpdateDispatchData (wire) |
WebAuthnCredentialsUpdate | GatewayWebAuthnCredentialsUpdateDispatchData (wire) |
PresenceUpdate | PresenceUpdatePayload |
PresenceUpdateBulk | PresenceUpdateBulkPayload |
WebhooksUpdate | WebhooksUpdatePayload |
VoiceStateUpdate | GatewayVoiceStateUpdateDispatchData (wire) |
VoiceStateAck | GatewayVoiceStateAckDispatchData (wire) |
VoiceServerUpdate | GatewayVoiceServerUpdateDispatchData (wire) |
EntranceSoundPlay | GatewayEntranceSoundPlayDispatchData (wire) |
VoiceStatesSync | GatewayVoiceStatesSyncData (wire) |
CamelCase DTOs
Exported from @fluxerjs/core (defined in EventPayloads.ts). Voice events remain wire-shaped on purpose.
| Payload | Notes |
|---|---|
MessageDeleteBulkPayload | ids, channelId, guildId, channel, messages (PartialMessage[]) |
InviteDeletePayload | code, guildId, channelId |
TypingStartPayload | channelId, guildId, userId, timestamp |
GuildEmojisUpdatePayload / GuildStickersUpdatePayload | cached structures |
MessageReactionRemoveAllPayload / RemoveEmoji / AddMany | camelCase plus cached message / channel |
ChannelPinsUpdatePayload | channelId, guildId, lastPinTimestamp |
ChannelRecipientPayload | channelId, user |
PresenceUpdatePayload / PresenceUpdateBulkPayload | camelCase |
PresenceActivity | nested activity on presence payloads |
WebhooksUpdatePayload | channelId, guildId |
GuildMembersChunkPayload | includes GuildMember[] |
GuildCountsUpdatePayload / ChannelMemberCountsUpdatePayload | camelCase counts |
AuditLogEntryPayload / AuditLogChange | camelCase audit entry |
GuildRoleUpdatePayload | { role, oldRole } |
GuildRoleDeletePayload | { roleId, guildId, role } |
MessageReactionAddManyEntry | nested entry on add-many batches |
Ask for live counts with client.requestGuildCounts({ guildIds }). That updates guild.memberCount / guild.onlineCount when GuildCountsUpdate arrives. client.requestChannelMemberCounts({ guildId }) is event-only: listen for ChannelMemberCountsUpdate. There is no channel.memberCount field.
See also: Sending Messages, Channels, Collectors, Where do I...?.