> For the complete documentation index, see [llms.txt](https://docs.iynxdev.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.iynxdev.com/addon-api/getting-started/internal-api/custom-events.md).

# Custom Events

This page explains how to run Athena custom events for your AthenaBot plugin.

***

## 1. Setting up the event file

1. Create a new event file in `/plugins/<plugin_name>/src/events/`.
2. Name the file with a unique event listener name, such as `myLevelUpListener.js`.
3. Set `event.discord` to the Athena custom event name you want to listen to, such as `athena:levelUp`.

```js
const event = require('../../../../main/discord/core/events/event.js');

module.exports = class myLevelUpListener extends event {
  constructor(heart) {
    super(heart, { name: 'myLevelUpListener', event: { discord: 'athena:levelUp', bypassManager: false, dm: false, bypassRestrictions: true, permissionLevel: null } });
  }

  async execute(userId, newLevel) {
    try {
      this.heart.core.console.log(this.heart.core.console.type.log, `${userId} reached level ${newLevel}`);
    }
    catch (err) {
      this.heart.core.console.log(this.heart.core.console.type.error, `An issue occurred while executing event ${this.getName()}`);
      new this.heart.core.error.interface(this.heart, err);
    }
  }
};
```

### Parameters

* **name:** Unique identifier for your listener file.
* **discord:** The Athena custom event name.
* **bypassManager:** Keep `false` unless you fully understand the side effects.
* **dm:** Keep `false` for Athena custom events.
* **bypassRestrictions:** Keep `true`.
* **permissionLevel:** Keep `null` for Athena custom events.

> The important value is `event.discord`. It must exactly match the emitted event name.

***

## 2. Running the event

Athena events are not fired by Discord.js. You run them by calling Athena’s `eventManager.emitSafe` from your feature logic.

```js
const didEmit = await this.heart.manager.discord.eventManager.emitSafe('athena:levelUp', userId, newLevel);
if (!didEmit) {
  this.heart.core.console.log(this.heart.core.console.type.warn, 'Event levelUp emitted but no listener was registered');
}
```

If you emit:

```js
emitSafe('athena:levelUp', userId, newLevel)
```

Your listener should be:

```js
async execute(userId, newLevel) {
  // ...
}
```

***

## 3. Additional information

* Event names are case-sensitive.
* Do not rename `athena:*` events unless all emitters and listeners are updated together.
* IDs are Discord snowflakes unless the event signature states otherwise.
* Object payloads should be treated as internal APIs that may evolve.

***

## 4. Athena custom event catalog

> Signatures are shown as `EventName(arg1, arg2, ...)`.

### Core

* `athena:error(errorInterface)`

### Economy

* `athena:bankUpgrade(userId, bankLevel)`
* `athena:shopPurchase(userId, itemId, cost)`
* `athena:jobUpgrade(userId, jobKey, jobLevel)`
* `athena:lootboxRollPurchase(userId, purchasedRolls)`
* `athena:lootboxOpen(userId, rewardType, rewardAmount)`
* `athena:questClaim(userId, period, finishedQuestCount, rewards)`
* `athena:rankUpgrade(userId, newRank)`

### Fun

* `athena:levelUp(userId, level)`
* `athena:countingGameSuccess(userId, counterValue)`
* `athena:countingGameFail(userId, evaluatedInput, expectedCounter)`
* `athena:hangmanEnd(userId, won, streak, winStreakCurrent)`
* `athena:higherLowerEnd(userId, streak, highscore)`
* `athena:starboardSend(userId, messageId, reactionCount)`
* `athena:starboardReaction(userId, messageId, reactionCount)`

### Giveaway

* `athena:giveawayEnter(userId, giveawayMessageId, entryCount)`
* `athena:giveawayLeave(userId, giveawayMessageId, entryCount)`

### Join to create

* `athena:tempVoiceCreate(voiceChannelId, ownerUserId, sourceChannelId)`
* `athena:tempVoiceDelete(voiceChannelId, ownerUserId)`
* `athena:tempVoiceOwnerChange(voiceChannelId, previousOwnerUserId, newOwnerUserId)`
* `athena:tempVoiceOwnerSet(voiceChannelId, executorUserId, newOwnerUserId)`
* `athena:tempVoiceName(voiceChannelId, executorUserId, newName)`
* `athena:tempVoiceLimit(voiceChannelId, executorUserId, newLimit)`
* `athena:tempVoiceVisibility(voiceChannelId, executorUserId, visibility)`
* `athena:tempVoiceKick(voiceChannelId, executorUserId, targetUserId)`
* `athena:tempVoiceBlacklist(voiceChannelId, executorUserId, targetUserId)`
* `athena:tempVoiceUnblacklist(voiceChannelId, executorUserId, targetUserId)`

### Management

* `athena:applySavedRoles(userId, roleCount)`
* `athena:autoRole(userId, roleCount)`
* `athena:saveRoles(userId, roleCount)`
* `athena:pollVote(userId, pollId, optionId)`
* `athena:pollVoteRevoke(userId, pollId, optionId)`
* `athena:suggestionCreate(userId, suggestionNumber)`
* `athena:suggestionStatus(staffUserId, suggestionId, status)`
* `athena:suggestionReview(staffUserId, suggestionId, approved)`

### Moderation

* `athena:blacklist(type, targetUserId, executorUserId, punishmentId)`
* `athena:unblacklist(type, targetUserId, executorUserId, punishmentId)`
* `athena:ban(targetUserId, executorUserId, punishmentId, manual)`
* `athena:unban(targetUserId, executorUserId, punishmentId, manual)`
* `athena:kick(targetUserId, executorUserId, punishmentId, manual)`
* `athena:mute(targetUserId, executorUserId, punishmentId, manual)`
* `athena:unmute(targetUserId, executorUserId, punishmentId, manual)`
* `athena:warn(targetUserId, executorUserId, punishmentId)`
* `athena:unwarn(targetUserId, executorUserId, punishmentId)`
* `athena:strike(targetUserId, executorUserId, punishmentId, strikeAmount)`
* `athena:automodAction(userId, ruleName, warning)`

### Music

* `athena:nodeConnect(nodeName)`
* `athena:nodeDisconnect(nodeName, disconnectCount)`
* `athena:nodeError(nodeName, errorMessageOrNull)`
* `athena:playerCreate(guildIdOrNull, defaultVolume)`
* `athena:trackStart(guildId, requesterUserId, title)`
* `athena:trackEnd(guildId, titleOrNull, remainingQueueLength)`
* `athena:queueEnd(guildId, textChannelId)`

### Security

* `athena:autoVerify(userId)`
* `athena:verifyFail(userId, remainingAttempts)`
* `athena:verifySuccess(userId)`

### Social

* `athena:twitchLive(username, messageId, viewerCount)`
* `athena:twitchOffline(username)`
* `athena:youtubeNotification(authorName, videoId, discordChannelId)`

### Staff management

* `athena:verifyStaffRoles()`
* `athena:verifyStaffRolesFail(invalidRoleIds)`
* `athena:strikelistView(viewerUserId, targetMemberId, punishmentId, page)`
* `athena:taskClaim(userId, taskId)`
* `athena:taskEditOpen(userId, taskId, type)`
* `athena:taskEdit(userId, taskId, type)`

### Tickets

* `athena:ticketCreate(ticket)`
* `athena:ticketAdd(ticket, addedUserId)`
* `athena:ticketRemove(ticket, removedUserId)`
* `athena:ticketRename(ticket, newName)`
* `athena:ticketClaim(ticket, claimerUserId)`
* `athena:ticketUnclaim(ticket, unclaimerUserId)`
* `athena:ticketClose(ticket)`
* `athena:ticketLower(ticket, level)`
* `athena:ticketElevate(ticket, level)`
* `athena:applicationStart(userId, applicationIndex)`
* `athena:applicationSubmit(userId, applicationIndex, answersCount, submitted)`
* `athena:applicationCancel(userId, applicationIndex)`
* `athena:applicationChannelCreate(application)`
* `athena:applicationChatAllow(applicantUserId, staffUserId, appChannelId)`
* `athena:applicationChatDeny(applicantUserId, staffUserId, appChannelId)`
* `athena:applicationHistoryView(staffUserId, applicantUserId, appChannelId)`
* `athena:applicationAccept(application, roleName, staffUserId)`
* `athena:applicationDeny(application, staffUserId, reason, timeoutMs)`
* `athena:applicationAutoClose(application)`
