Events let you react to things that happen on Discord — members joining, messages being sent, reactions added, and so on. Each file in src/events/ handles one Discord event type.
Name the file after the Discord.js event you want to handle:
src/events/
guildMemberAdd.ts → fires when a member joins
messageCreate.ts → fires on every message
guildCreate.ts → fires when the bot joins a server
interactionCreate.ts → (usually not needed — Popii handles this)
Popii detects which intents are needed based on the filenames in your events directory. You don't need to declare intents manually.
// src/events/guildMemberAdd.ts
import { event } from "popii";
export default event(async (pop, member) => {
const channel = member.guild.systemChannel;
if (channel) {
await channel.send(`Welcome, ${member}! 👋`);
}
});
The first argument is always the Popii EventPop context (client, localization helpers, cache, etc.). Remaining arguments are the raw Discord.js event arguments — fully typed based on the event name.
import { event } from "popii";
export default event(async (pop, guild) => {
pop.log.info(`Joined ${guild.name} (${guild.memberCount} members)`);
pop.client.discord.guilds.cache.get(guild.id); // full discord.js access
});
Create multiple files that handle the same event by using subfolders or a naming suffix:
src/events/
messageCreate/
spam-filter.ts
xp-reward.ts
automod.ts
All files are loaded and run for the same event.
// src/events/clientReady.ts
import { event } from "popii";
export default event(async (pop) => {
pop.log.info(`Logged in as ${pop.client.discord.user?.tag}`);
});
The EventPop context gives you the same helpers available in commands:
import { event } from "popii";
export default event(async (pop, message) => {
if (message.author.bot) return;
// Caching
const count = await pop.cache(`msg_count:${message.author.id}`, 60_000, async () => {
return 0;
});
// i18n
const greeting = pop.translate("welcome_message");
// Cooler (shared KV store)
await pop.cooler.set(`last_seen:${message.author.id}`, Date.now(), 86_400_000);
});
Popii reads your event filenames and automatically enables the required intents. For example:
| Event file | Intent added |
|---|---|
guildMemberAdd.ts |
GuildMembers |
presenceUpdate.ts |
GuildPresences |
messageReactionAdd.ts |
GuildMessageReactions |
voiceStateUpdate.ts |
GuildVoiceStates |
You can override this by setting intents manually in popiiClient().