Skip to main content

Event Hooks

Event hooks allow your script to respond to Discord events in real-time. Set a function to an event property to handle that event.
Scripts are written in JavaScript and executed by the Go client using Goja.

Message Events

discord.on_message

Fired when any message is received in Discord.

Event Context

The event receives a ctx object:
PropertyTypeDescription
ctx.messageMessageFull message object (id, content, timestamp, etc.)
ctx.guildGuildFull guild object (name, member_count, icon, etc.)
ctx.channelChannelFull channel object (name, type, topic, etc.)
ctx.authorAuthorFull author object (username, id, avatar, etc.)
ctx.contentstringMessage content (shortcut for ctx.message.content)
ctx.mentionsAuthor[]Mentioned users
ctx.attachmentsAttachment[]Message attachments
ctx.embedsEmbed[]Message embeds

Example

discord.on_message = function(ctx) {
    if (ctx.author.bot) return; // Ignore bots
    
    ethone.log("Message from " + ctx.author.username + " in #" + ctx.channel.name);
    
    if (!ctx.guild) {
        discord.send_message(ctx.channel.id, "Thanks for the DM!");
    }
    
    if (ctx.guild && ctx.guild.name === "My Server") {
        if (ctx.content.toLowerCase().includes("hello")) {
            discord.send_message(ctx.channel.id, "Hello in " + ctx.guild.name + "!");
        }
    }
};

discord.on_message_edit

Fired when a message is edited.

Event Context

PropertyTypeDescription
ctx.messageMessageFull message object with new content
ctx.guildGuildFull guild object
ctx.channelChannelFull channel object
ctx.authorAuthorFull author object
ctx.contentstringNew message content
ctx.edited_atstringTimestamp of the edit

Example

discord.on_message_edit = function(ctx) {
    ethone.log("Message edited by " + ctx.author.username);
    ethone.log("  In: #" + ctx.channel.name + " (" + ctx.guild.name + ")");
    ethone.log("  New content: " + ctx.content);
    
    var logChannel = "YOUR_LOG_CHANNEL_ID";
    discord.send_message(
        logChannel,
        ctx.author.username + " edited a message in #" + ctx.channel.name
    );
};

discord.on_message_delete

Fired when a message is deleted.

Event Context

PropertyTypeDescription
ctx.messageMessageFull message object (id, cached content if available)
ctx.guildGuildFull guild object
ctx.channelChannelFull channel object

Example

var deletedMessages = [];

discord.on_message_delete = function(ctx) {
    deletedMessages.push({
        id: ctx.message.id,
        channel: ctx.channel.name,
        guild: ctx.guild.name,
        timestamp: Date.now()
    });
    
    ethone.log("Message deleted in #" + ctx.channel.name);
    
    var logChannel = "YOUR_LOG_CHANNEL_ID";
    discord.send_message(
        logChannel,
        "Message deleted in #" + ctx.channel.name + " (" + ctx.guild.name + ")"
    );
};

Reaction Events

discord.on_reaction_add

Fired when a reaction is added to a message.

Event Context

PropertyTypeDescription
ctx.userAuthorFull user object who added the reaction
ctx.guildGuildFull guild object
ctx.channelChannelFull channel object
ctx.messageMessageFull message object that was reacted to
ctx.emojiEmojiEmoji information (id, name, animated)

Example

discord.on_reaction_add = function(ctx) {
    ethone.log("Reaction " + ctx.emoji.name + " by " + ctx.user.username);
    ethone.log("  In: #" + ctx.channel.name + " (" + ctx.guild.name + ")");
    
    if (ctx.emoji.name === "+1") {
        discord.send_message(
            ctx.channel.id,
            ctx.user.username + " liked message " + ctx.message.id
        );
    }
};

discord.on_reaction_remove

Fired when a reaction is removed from a message.

Event Context

Same structure as on_reaction_add - includes full user, guild, channel, message, and emoji objects.

Example

discord.on_reaction_remove = function(ctx) {
    ethone.log("Reaction removed: " + ctx.emoji.name + " by " + ctx.user.username);
    ethone.log("  From: #" + ctx.channel.name);
};

Channel Events

discord.on_channel_create

Fired when a channel is created.

Event Context

PropertyTypeDescription
ctx.channelChannelFull channel object (id, name, type, topic, nsfw, etc.)
ctx.guildGuildFull guild object

Example

discord.on_channel_create = function(ctx) {
    ethone.log("=== New Channel ===");
    ethone.log("Name: #" + ctx.channel.name);
    ethone.log("Type: " + ctx.channel.type);
    ethone.log("Guild: " + ctx.guild.name);
    
    if (ctx.channel.nsfw) {
        ethone.log("This is an NSFW channel");
    }
    
    var announceChannel = "YOUR_ANNOUNCE_CHANNEL_ID";
    discord.send_message(
        announceChannel,
        "New channel created: #" + ctx.channel.name
    );
};

discord.on_channel_update

Fired when a channel is updated (name, topic, permissions, etc.).

Event Context

Same structure as on_channel_create - includes full channel and guild objects.

Example

discord.on_channel_update = function(ctx) {
    ethone.log("Channel updated: #" + ctx.channel.name);
    ethone.log("  In: " + ctx.guild.name);
    
    if (ctx.channel.topic) {
        ethone.log("  Topic: " + ctx.channel.topic);
    }
};

discord.on_channel_delete

Fired when a channel is deleted.

Event Context

PropertyTypeDescription
ctx.channelChannelFull channel object (id, name, type)
ctx.guildGuildFull guild object

Example

discord.on_channel_delete = function(ctx) {
    ethone.log("Channel deleted: #" + ctx.channel.name);
    ethone.log("  From: " + ctx.guild.name);
    
    var logChannel = "YOUR_LOG_CHANNEL_ID";
    discord.send_message(
        logChannel,
        "Channel #" + ctx.channel.name + " was deleted"
    );
};

Member Events

discord.on_guild_member_add

Fired when a user joins a server/guild.

Event Context

PropertyTypeDescription
ctx.userAuthorFull user object (id, username, avatar, bot, etc.)
ctx.guildGuildFull guild object
ctx.joined_atstringISO timestamp of when they joined
ctx.nickstring | undefinedTheir server nickname (if set)
ctx.rolesstring[]Array of role IDs assigned

Example

discord.on_guild_member_add = function(ctx) {
    ethone.log("=== New Member ===");
    ethone.log("User: " + ctx.user.username);
    ethone.log("Guild: " + ctx.guild.name);
    ethone.log("Joined at: " + ctx.joined_at);
    
    var welcomeChannelId = "YOUR_CHANNEL_ID";
    discord.send_message(
        welcomeChannelId,
        "Welcome " + ctx.user.username + " to " + ctx.guild.name + "!"
    );
};

discord.on_guild_member_remove

Fired when a member leaves or is kicked from a server.

Event Context

PropertyTypeDescription
ctx.userAuthorFull user object
ctx.guildGuildFull guild object

Example

discord.on_guild_member_remove = function(ctx) {
    ethone.log("Member left: " + ctx.user.username);
    ethone.log("  From: " + ctx.guild.name);
    
    var logChannelId = "YOUR_LOG_CHANNEL_ID";
    discord.send_message(
        logChannelId,
        ctx.user.username + " has left " + ctx.guild.name
    );
};

discord.on_guild_member_update

Fired when a member is updated (roles changed, nickname changed, etc.).

Event Context

PropertyTypeDescription
ctx.userAuthorFull user object
ctx.guildGuildFull guild object
ctx.nickstring | undefinedTheir server nickname
ctx.rolesstring[]Array of role IDs they now have
ctx.joined_atstringWhen they originally joined
ctx.pendingbooleanWhether they’re pending verification

Example

var previousRoles = {};

discord.on_guild_member_update = function(ctx) {
    var userId = ctx.user.id;
    var currentRoles = ctx.roles;
    
    ethone.log("Member updated: " + ctx.user.username);
    ethone.log("  In: " + ctx.guild.name);
    
    if (ctx.nick) {
        ethone.log("  Nickname: " + ctx.nick);
    }
    
    ethone.log("  Current roles: " + currentRoles.length);
    
    if (previousRoles[userId]) {
        var added = currentRoles.filter(function(r) {
            return previousRoles[userId].indexOf(r) === -1;
        });
        var removed = previousRoles[userId].filter(function(r) {
            return currentRoles.indexOf(r) === -1;
        });
        
        if (added.length > 0) {
            ethone.log("  Roles added: " + added.join(", "));
        }
        if (removed.length > 0) {
            ethone.log("  Roles removed: " + removed.join(", "));
        }
    }
    
    previousRoles[userId] = currentRoles;
};

Role Events

discord.on_guild_role_create

Fired when a role is created.

Event Context

PropertyTypeDescription
ctx.guildGuildFull guild object
ctx.roleRoleFull role object (id, name, color, permissions, etc.)

Example

discord.on_guild_role_create = function(ctx) {
    ethone.log("=== New Role Created ===");
    ethone.log("Name: " + ctx.role.name);
    ethone.log("Guild: " + ctx.guild.name);
    ethone.log("Color: " + ctx.role.color);
    ethone.log("Position: " + ctx.role.position);
    ethone.log("Mentionable: " + ctx.role.mentionable);
};

discord.on_guild_role_update

Fired when a role is updated.

Event Context

Same structure as on_guild_role_create - includes full guild and role objects.

Example

discord.on_guild_role_update = function(ctx) {
    ethone.log("Role updated: " + ctx.role.name);
    ethone.log("  In: " + ctx.guild.name);
    ethone.log("  Color: " + ctx.role.color);
    ethone.log("  Position: " + ctx.role.position);
};

discord.on_guild_role_delete

Fired when a role is deleted.

Event Context

PropertyTypeDescription
ctx.guildGuildFull guild object
ctx.role_idstringThe deleted role ID

Example

discord.on_guild_role_delete = function(ctx) {
    ethone.log("Role deleted!");
    ethone.log("  Role ID: " + ctx.role_id);
    ethone.log("  Guild: " + ctx.guild.name);
};

Activity Events

discord.on_typing_start

Fired when a user starts typing in a channel.

Event Context

PropertyTypeDescription
ctx.userAuthorFull user object
ctx.channelChannelFull channel object
ctx.guildGuildFull guild object
ctx.timestampnumberUnix timestamp

Example

discord.on_typing_start = function(ctx) {
    ethone.log(ctx.user.username + " is typing in #" + ctx.channel.name);
};
This event fires frequently. Use sparingly.

discord.on_presence_update

Fired when a user’s presence/status changes (online, offline, idle, etc.).

Event Context

PropertyTypeDescription
ctx.guildGuildFull guild object
ctx.userAuthorFull user object
ctx.statusstringStatus: "online", "offline", "idle", "dnd"
ctx.activitiesActivity[]Array of activities (optional)

Activities Array (if present)

PropertyTypeDescription
namestringActivity name
typenumberActivity type (0=playing, 1=streaming, 2=listening, 3=watching, 4=custom, 5=competing)
urlstring | undefinedStream URL (required for streaming type)
statestring | undefinedActivity state text
detailsstring | undefinedActivity details text
application_idstring | undefinedApplication ID for the activity
timestampsobject | undefinedUnix timestamps (start, end)
assetsobject | undefinedActivity assets (large_image, large_text, small_image, small_text)
partyobject | undefinedParty information (id, size)
emojiobject | undefinedEmoji for custom status (name, id, animated)
buttonsstring[] | undefinedButton labels (max 2)
created_atnumber | undefinedUnix timestamp of when activity was added

Example

discord.on_presence_update = function(ctx) {
    ethone.log("Presence update for: " + ctx.user.username);
    ethone.log("  In: " + ctx.guild.name);
    ethone.log("  Status: " + ctx.status);
    
    if (ctx.activities && ctx.activities.length > 0) {
        for (var i = 0; i < ctx.activities.length; i++) {
            var activity = ctx.activities[i];
            ethone.log("  Activity: " + activity.name);
            
            if (activity.type === 0) {
                ethone.log("  Playing: " + activity.name);
                if (activity.details) ethone.log("    Details: " + activity.details);
                if (activity.state) ethone.log("    State: " + activity.state);
            } else if (activity.type === 1) {
                ethone.log("  Streaming: " + activity.name);
                if (activity.url) ethone.log("    URL: " + activity.url);
            } else if (activity.type === 2) {
                ethone.log("  Listening to: " + activity.name);
            } else if (activity.type === 3) {
                ethone.log("  Watching: " + activity.name);
            } else if (activity.type === 4 && activity.emoji) {
                ethone.log("  Custom status: " + activity.emoji.name + " " + activity.state);
            } else if (activity.type === 5) {
                ethone.log("  Competing in: " + activity.name);
            }
            
            if (activity.timestamps && activity.timestamps.start) {
                ethone.log("    Started at: " + activity.timestamps.start);
            }
        }
    }
};
This event fires frequently (every status change). Use sparingly to avoid performance issues.