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.

javascript
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:

  1. Hydrated structures - Message, PartialMessage, Guild, Channel, GuildMember, Role, Invite, User, GuildBan, MessageReaction, GuildEmoji (via DTOs). Prefer these for bot logic.
  2. CamelCase DTOs - normalized objects from EventPayloads.ts (and a few inline camelCase shapes). Use channelId / guildId, not snake_case wire fields.
  3. Wire gateway shapes: still Gateway*DispatchData from @fluxerjs/types (snake_case). Voice events stay wire on purpose so @fluxerjs/voice can 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):

javascript
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

javascript
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().

javascript
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

javascript
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.

CategoryEvents
ConnectionReady, Resumed, Error, Debug
MessagesMessageCreate, MessageUpdate, MessageDelete, MessageDeleteBulk
ReactionsMessageReactionAdd, MessageReactionAddMany, MessageReactionRemove, MessageReactionRemoveAll, MessageReactionRemoveEmoji
GuildGuildCreate, GuildAvailable, GuildUnavailable, GuildUpdate, GuildDelete, GuildCountsUpdate, GuildEmojisUpdate, GuildStickersUpdate, GuildAuditLogEntryCreate
MembersGuildMemberAdd, GuildMemberUpdate, GuildMemberRemove, GuildMembersChunk
RolesGuildRoleCreate, GuildRoleUpdate, GuildRoleDelete
ModerationGuildBanAdd, GuildBanRemove
ChannelsChannelCreate, ChannelUpdate, ChannelDelete, ChannelPinsUpdate, ChannelMemberCountsUpdate, ChannelRecipientAdd, ChannelRecipientRemove
InvitesInviteCreate, InviteDelete
Users / presenceUserUpdate, UserConnectionsUpdate, WebAuthnCredentialsUpdate, PresenceUpdate, PresenceUpdateBulk, TypingStart
WebhooksWebhooksUpdate
VoiceVoiceStateUpdate, 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).

EventsHandler args
Ready(none)
Resumed(none)
ErrorError
Debugstring
MessageCreateMessage
MessageUpdateMessage | null, Message | PartialMessage (narrow with newMessage.partial)
MessageDeletePartialMessage (.partial, .fetch(), .resolveChannel())
MessageDeleteBulkMessageDeleteBulkPayload - { ids, channelId, guildId, channel, messages }
MessageReactionAddMessageReactionPayload - { reaction, user, message, channel, member, messageId, channelId, emoji, userId }
MessageReactionAddManyMessageReactionAddManyPayload
MessageReactionRemoveMessageReactionPayload
MessageReactionRemoveAllMessageReactionRemoveAllPayload
MessageReactionRemoveEmojiMessageReactionRemoveEmojiPayload
GuildCreateGuild
GuildAvailableGuild
GuildUnavailableGuild
GuildUpdateGuild, Guild
GuildDeleteGuild
GuildMemberAddGuildMember
GuildMemberUpdateGuildMember | null, GuildMember (old is a clone, or null if uncached)
GuildMemberRemoveGuildMember | PartialGuildMember (narrow with member.partial)
GuildMembersChunkGuildMembersChunkPayload
GuildCountsUpdateGuildCountsUpdatePayload
ChannelMemberCountsUpdateChannelMemberCountsUpdatePayload
GuildAuditLogEntryCreateAuditLogEntryPayload
GuildBanAddGuildBan
GuildBanRemoveGuildBan
GuildEmojisUpdateGuildEmojisUpdatePayload - { guildId, emojis }
GuildStickersUpdateGuildStickersUpdatePayload - { guildId, stickers }
GuildRoleCreateRole
GuildRoleUpdateGuildRoleUpdatePayload - { role, oldRole }
GuildRoleDeleteGuildRoleDeletePayload - { roleId, guildId, role }
ChannelCreateChannel
ChannelUpdateChannel, Channel
ChannelDeleteChannel
ChannelPinsUpdateChannelPinsUpdatePayload
ChannelRecipientAddChannelRecipientPayload
ChannelRecipientRemoveChannelRecipientPayload
InviteCreateInvite
InviteDeleteInviteDeletePayload
TypingStartTypingStartPayload
UserUpdateUser
UserConnectionsUpdateGatewayUserConnectionsUpdateDispatchData (wire)
WebAuthnCredentialsUpdateGatewayWebAuthnCredentialsUpdateDispatchData (wire)
PresenceUpdatePresenceUpdatePayload
PresenceUpdateBulkPresenceUpdateBulkPayload
WebhooksUpdateWebhooksUpdatePayload
VoiceStateUpdateGatewayVoiceStateUpdateDispatchData (wire)
VoiceStateAckGatewayVoiceStateAckDispatchData (wire)
VoiceServerUpdateGatewayVoiceServerUpdateDispatchData (wire)
EntranceSoundPlayGatewayEntranceSoundPlayDispatchData (wire)
VoiceStatesSyncGatewayVoiceStatesSyncData (wire)

CamelCase DTOs

Exported from @fluxerjs/core (defined in EventPayloads.ts). Voice events remain wire-shaped on purpose.

PayloadNotes
MessageDeleteBulkPayloadids, channelId, guildId, channel, messages (PartialMessage[])
InviteDeletePayloadcode, guildId, channelId
TypingStartPayloadchannelId, guildId, userId, timestamp
GuildEmojisUpdatePayload / GuildStickersUpdatePayloadcached structures
MessageReactionRemoveAllPayload / RemoveEmoji / AddManycamelCase plus cached message / channel
ChannelPinsUpdatePayloadchannelId, guildId, lastPinTimestamp
ChannelRecipientPayloadchannelId, user
PresenceUpdatePayload / PresenceUpdateBulkPayloadcamelCase
PresenceActivitynested activity on presence payloads
WebhooksUpdatePayloadchannelId, guildId
GuildMembersChunkPayloadincludes GuildMember[]
GuildCountsUpdatePayload / ChannelMemberCountsUpdatePayloadcamelCase counts
AuditLogEntryPayload / AuditLogChangecamelCase audit entry
GuildRoleUpdatePayload{ role, oldRole }
GuildRoleDeletePayload{ roleId, guildId, role }
MessageReactionAddManyEntrynested 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...?.

Questions?Join the Fluxer community for help with the SDK.Join Fluxer