Komplett Discord Bot Guide
Del 5: Event System
📋 Innholdsfortegnelse
- Hva lærer vi i denne delen?
- Steg 1: Forstå Discord Events
- Steg 2: Event Handler System
- 2.1: Lag events mappestruktur
- 2.2: Flytt ready event
- 2.3: Flytt interactionCreate event
- 2.4: Oppdater index.js med Event Loader
- 👋 Steg 3: Member Join/Leave Events
- 💬 Steg 4: Message Events
- 🏠 Steg 5: Guild Events
- 🧪 Steg 6: Testing Event System
- 🎨 Steg 7: Avanserte Features
- ✅ Oppsummering
- 🔧 Neste Forbedringer
- 🚀 Neste Steg
Del 5: Event System
Nå skal vi lære å lage en bot som reagerer på alt som skjer i serveren! Events er hjertet i Discord bots - de lar boten "høre" når ting skjer og reagere deretter.
Hva lærer vi i denne delen?
- Forstå Discord Events - Hva er events og hvordan fungerer de?
- Event handler system - Organisere events i egne filer
- Member join/leave events - Velkommen og farvel meldinger
- Message events - Reagere på meldinger og spam protection
- Guild events - Når boten legges til/fjernes fra servere
- Logging system - Holde styr på alt som skjer
- Auto-role system - Gi roller automatisk til nye medlemmer
Steg 1: Forstå Discord Events
Hva er Events?
Events er ting som skjer i Discord som boten din kan "høre":
Person kommer inn → memberAdd event → Bot sender velkommen melding
Noen sender melding → messageCreate event → Bot sjekker spam/filter
Person forlater → memberRemove event → Bot sender farvel melding
Reaction legges til → messageReactionAdd event → Bot gir rolle
Viktige Events:
ready- Bot starter (bruker vi allerede)guildMemberAdd- Noen kommer inn i serverenguildMemberRemove- Noen forlater serverenmessageCreate- Ny melding sendesmessageDelete- Melding slettesguildCreate- Bot legges til i ny serverinteractionCreate- Slash command brukes (har vi allerede)
Steg 2: Event Handler System
La oss organisere events på samme måte som commands!
2.1: Lag events mappestruktur
events/
├── client/
│ ├── ready.js
│ └── interactionCreate.js
├── guild/
│ ├── guildMemberAdd.js
│ ├── guildMemberRemove.js
│ └── guildCreate.js
└── message/
├── messageCreate.js
└── messageDelete.js
2.2: Flytt ready event
Lag events/client/ready.js:
module.exports = {
name: 'ready',
once: true, // Dette eventet kjører bare en gang
execute(client) {
console.log('='.repeat(50));
console.log('BOT ER ONLINE!');
console.log('='.repeat(50));
console.log(`Bot navn: ${client.user.tag}`);
console.log(`Bot ID: ${client.user.id}`);
console.log(`Servere: ${client.guilds.cache.size}`);
console.log(`Totalt brukere: ${client.users.cache.size}`);
console.log('='.repeat(50));
// Sett bot aktivitet
client.user.setPresence({
activities: [{
name: `${client.guilds.cache.size} servere | /help for commands`,
type: 'WATCHING'
}],
status: 'online'
});
// Log hvilke servere boten er i
console.log('📋 Servere boten er i:');
client.guilds.cache.forEach(guild => {
console.log(` • ${guild.name} (${guild.memberCount} medlemmer)`);
});
},
};
2.3: Flytt interactionCreate event
Lag events/client/interactionCreate.js:
module.exports = {
name: 'interactionCreate',
async execute(interaction) {
if (!interaction.isChatInputCommand()) return;
const command = interaction.client.commands.get(interaction.commandName);
if (!command) {
console.error(`❌ Ingen command kalt ${interaction.commandName} funnet.`);
return;
}
try {
await command.execute(interaction);
console.log(`✅ ${interaction.user.tag} brukte /${interaction.commandName} i ${interaction.guild.name}`);
} catch (error) {
console.error('❌ Feil ved utføring av command:', error);
const errorMessage = 'Det oppstod en feil ved utføring av denne kommandoen!';
if (interaction.replied || interaction.deferred) {
await interaction.followUp({ content: errorMessage, ephemeral: true });
} else {
await interaction.reply({ content: errorMessage, ephemeral: true });
}
}
},
};
2.4: Oppdater index.js med Event Loader
Fjern de gamle event handlerne fra index.js og legg til dette:
// Last inn alle events
const eventsPath = path.join(__dirname, 'events');
const eventFolders = fs.readdirSync(eventsPath);
for (const folder of eventFolders) {
const folderPath = path.join(eventsPath, folder);
const eventFiles = fs.readdirSync(folderPath).filter(file => file.endsWith('.js'));
for (const file of eventFiles) {
const filePath = path.join(folderPath, file);
const event = require(filePath);
if (event.once) {
client.once(event.name, (...args) => event.execute(...args));
} else {
client.on(event.name, (...args) => event.execute(...args));
}
console.log(`✅ Lastet event: ${event.name}`);
}
}
👋 Steg 3: Member Join/Leave Events
3.1: Velkommen System
Lag events/guild/guildMemberAdd.js:
const { EmbedBuilder, AttachmentBuilder } = require('discord.js');
module.exports = {
name: 'guildMemberAdd',
async execute(member) {
console.log(`👋 ${member.user.tag} ble med i ${member.guild.name}`);
// Finn velkommen kanal (søk etter kanaler med "welcome", "velkommen", "general")
const welcomeChannel = member.guild.channels.cache.find(channel =>
channel.name.includes('welcome') ||
channel.name.includes('velkommen') ||
channel.name.includes('general')
) || member.guild.systemChannel;
if (!welcomeChannel) {
console.log('❌ Ingen velkommen kanal funnet');
return;
}
// Lag fancy velkommen embed
const welcomeEmbed = new EmbedBuilder()
.setColor(0x2ecc71) // Grønn farge
.setTitle('🎉 Velkommen til serveren!')
.setDescription(`Hei ${member}! Vi er glad for å ha deg her! 🎊`)
.setThumbnail(member.user.displayAvatarURL({ dynamic: true, size: 256 }))
.addFields(
{
name: '👤 Ny medlem',
value: `${member.user.tag}`,
inline: true
},
{
name: '📅 Konto opprettet',
value: `<t:${Math.floor(member.user.createdTimestamp / 1000)}:R>`,
inline: true
},
{
name: '🔢 Medlem nummer',
value: `${member.guild.memberCount}`,
inline: true
},
{
name: '📋 Kom i gang',
value: '• Les <#KANAL_ID> for regler\n• Bruk `/help` for bot commands\n• Ha det gøy! 🎮',
inline: false
}
)
.setImage('https://media.giphy.com/media/Nx0rz3jtxtEre/giphy.gif') // Velkommen GIF
.setFooter({
text: `${member.guild.name} • Medlem ${member.guild.memberCount}`,
iconURL: member.guild.iconURL()
})
.setTimestamp();
try {
await welcomeChannel.send({
content: `${member} 👋`, // Pinger den nye brukeren
embeds: [welcomeEmbed]
});
} catch (error) {
console.error('❌ Kunne ikke sende velkommen melding:', error);
}
// Auto-role (legger til standardrolle)
const autoRole = member.guild.roles.cache.find(role =>
role.name.toLowerCase() === 'medlem' ||
role.name.toLowerCase() === 'member'
);
if (autoRole) {
try {
await member.roles.add(autoRole);
console.log(`✅ Ga ${member.user.tag} rollen "${autoRole.name}"`);
} catch (error) {
console.error('❌ Kunne ikke gi auto-rolle:', error);
}
}
},
};
3.2: Farvel System
Lag events/guild/guildMemberRemove.js:
const { EmbedBuilder } = require('discord.js');
module.exports = {
name: 'guildMemberRemove',
async execute(member) {
console.log(`👋 ${member.user.tag} forlot ${member.guild.name}`);
// Finn log kanal eller bruk samme som velkommen
const logChannel = member.guild.channels.cache.find(channel =>
channel.name.includes('log') ||
channel.name.includes('goodbye') ||
channel.name.includes('farvel') ||
channel.name.includes('general')
) || member.guild.systemChannel;
if (!logChannel) return;
// Lag farvel embed
const goodbyeEmbed = new EmbedBuilder()
.setColor(0xe74c3c) // Rød farge
.setTitle('👋 Medlem forlot serveren')
.setDescription(`**${member.user.tag}** har forlatt serveren.`)
.setThumbnail(member.user.displayAvatarURL({ dynamic: true }))
.addFields(
{
name: '👤 Bruker',
value: `${member.user.tag}\n\`${member.user.id}\``,
inline: true
},
{
name: '📅 Var medlem i',
value: `<t:${Math.floor(member.joinedTimestamp / 1000)}:R>`,
inline: true
},
{
name: '🔢 Totalt medlemmer',
value: `${member.guild.memberCount}`,
inline: true
}
)
.setFooter({
text: member.guild.name,
iconURL: member.guild.iconURL()
})
.setTimestamp();
try {
await logChannel.send({ embeds: [goodbyeEmbed] });
} catch (error) {
console.error('❌ Kunne ikke sende farvel melding:', error);
}
},
};
💬 Steg 4: Message Events
4.1: Message Logging
Lag events/message/messageCreate.js:
module.exports = {
name: 'messageCreate',
async execute(message) {
// Ignorer bot meldinger
if (message.author.bot) return;
// Log alle meldinger (for debugging - kan fjernes senere)
console.log(`💬 ${message.author.tag} i #${message.channel.name}: ${message.content.substring(0, 50)}${message.content.length > 50 ? '...' : ''}`);
// Enkel spam protection
if (await isSpamMessage(message)) {
try {
await message.delete();
console.log(`🗑️ Slettet spam melding fra ${message.author.tag}`);
// Send warning (ephemeral via DM eller channel)
const warningChannel = message.channel;
await warningChannel.send({
content: `⚠️ ${message.author}, ikke spam takk!`,
allowedMentions: { users: [message.author.id] }
});
} catch (error) {
console.error('❌ Kunne ikke slette spam melding:', error);
}
}
// Auto-react på spesielle meldinger
if (message.content.toLowerCase().includes('takk')) {
message.react('❤️').catch(console.error);
}
if (message.content.toLowerCase().includes('bot')) {
message.react('🤖').catch(console.error);
}
},
};
// Enkel spam detection
async function isSpamMessage(message) {
const content = message.content.toLowerCase();
// Sjekk for caps spam (over 70% store bokstaver)
const capsPercentage = (content.match(/[A-Z]/g) || []).length / content.length;
if (capsPercentage > 0.7 && content.length > 10) return true;
// Sjekk for repeated karakterer
if (/(.)\1{4,}/.test(content)) return true;
// Sjekk for spam keywords
const spamKeywords = ['free nitro', 'discord.gift', 'click here', 'dm me'];
if (spamKeywords.some(keyword => content.includes(keyword))) return true;
return false;
}
4.2: Message Delete Logging
Lag events/message/messageDelete.js:
const { EmbedBuilder } = require('discord.js');
module.exports = {
name: 'messageDelete',
async execute(message) {
// Ignorer bot meldinger
if (message.author?.bot) return;
console.log(`🗑️ Melding slettet i #${message.channel.name} av ${message.author?.tag || 'Ukjent'}`);
// Finn log kanal
const logChannel = message.guild.channels.cache.find(channel =>
channel.name.includes('log') ||
channel.name.includes('deleted') ||
channel.name.includes('mod')
);
if (!logChannel) return;
// Lag delete log embed
const deleteEmbed = new EmbedBuilder()
.setColor(0xf39c12) // Orange farge
.setTitle('🗑️ Melding Slettet')
.addFields(
{
name: '👤 Forfatter',
value: message.author ? `${message.author.tag}\n\`${message.author.id}\`` : 'Ukjent',
inline: true
},
{
name: '📍 Kanal',
value: `${message.channel}\n\`${message.channel.id}\``,
inline: true
},
{
name: '🕐 Tidspunkt',
value: `<t:${Math.floor(Date.now() / 1000)}:F>`,
inline: true
}
)
.setTimestamp();
// Legg til meldings innhold hvis det finnes
if (message.content) {
deleteEmbed.addFields({
name: '💬 Innhold',
value: message.content.length > 1024 ?
message.content.substring(0, 1021) + '...' :
message.content,
inline: false
});
}
// Legg til vedlegg info
if (message.attachments.size > 0) {
const attachmentInfo = message.attachments.map(att => att.name).join(', ');
deleteEmbed.addFields({
name: '📎 Vedlegg',
value: attachmentInfo,
inline: false
});
}
try {
await logChannel.send({ embeds: [deleteEmbed] });
} catch (error) {
console.error('❌ Kunne ikke logge slettet melding:', error);
}
},
};
🏠 Steg 5: Guild Events
5.1: Bot Lagt til i Ny Server
Lag events/guild/guildCreate.js:
const { EmbedBuilder } = require('discord.js');
module.exports = {
name: 'guildCreate',
async execute(guild) {
console.log(`🎉 Lagt til i ny server: ${guild.name} (${guild.memberCount} medlemmer)`);
// Oppdater bot presence
guild.client.user.setPresence({
activities: [{
name: `${guild.client.guilds.cache.size} servere | /help for commands`,
type: 'WATCHING'
}],
status: 'online'
});
// Send velkommen melding til eieren
try {
const owner = await guild.fetchOwner();
const welcomeEmbed = new EmbedBuilder()
.setColor(0x2ecc71)
.setTitle('🎉 Takk for at du la til boten!')
.setDescription('Hei! Jeg er din nye Discord bot. Her er noen tips for å komme i gang:')
.addFields(
{
name: '⚡ Kom i gang',
value: '• Bruk `/help` for å se alle commands\n• Sett opp velkommen kanal (navngi den "welcome" eller "velkommen")\n• Lag en rolle som heter "medlem" for auto-role',
inline: false
},
{
name: '🛠️ Grunnleggende oppsett',
value: '• Gi boten "Manage Roles" tillatelse for auto-role\n• Gi "Manage Messages" for spam protection\n• Lag en #log kanal for aktivitets logging',
inline: false
},
{
name: '💡 Tips',
value: 'Jeg reagerer automatisk på meldinger og holder øye med serveren din. Bruk `/status` kommandoen (admin) for å endre min aktivitet!',
inline: false
}
)
.setThumbnail(guild.client.user.displayAvatarURL())
.setFooter({
text: 'Ha det gøy med boten din! 🚀',
iconURL: guild.iconURL()
})
.setTimestamp();
await owner.send({ embeds: [welcomeEmbed] });
} catch (error) {
console.log('❌ Kunne ikke sende velkommen DM til eier:', error.message);
// Prøv å sende i system channel i stedet
if (guild.systemChannel) {
try {
await guild.systemChannel.send({
content: `👋 Hei ${guild.name}! Bruk \`/help\` for å se alle mine commands!`
});
} catch (systemError) {
console.log('❌ Kunne ikke sende velkommen melding i system channel');
}
}
}
// Finn og hilse på i general channel
const generalChannel = guild.channels.cache.find(channel =>
channel.name.includes('general') ||
channel.name.includes('chat') ||
channel.name.includes('main')
);
if (generalChannel && generalChannel.permissionsFor(guild.members.me).has('SendMessages')) {
try {
await generalChannel.send('👋 Hei alle sammen! Jeg er ny her. Bruk `/help` for å se hva jeg kan gjøre! 🤖');
} catch (error) {
console.log('❌ Kunne ikke sende hilsen i general channel');
}
}
},
};
🧪 Steg 6: Testing Event System
6.1: Start Bot med Ny Event System
node index.js
Du skal se:
✅ Lastet event: ready
✅ Lastet event: interactionCreate
✅ Lastet event: guildMemberAdd
✅ Lastet event: guildMemberRemove
✅ Lastet event: messageCreate
✅ Lastet event: messageDelete
✅ Lastet event: guildCreate
6.2: Test Events
Velkommen/Farvel:
- Invite en venn til serveren (eller bruk alt account)
- Se at bot sender velkommen melding
- La personen forlate serveren
- Se farvel log melding
Message Events:
- Send normale meldinger - se logging i konsoll
- Send "takk" - bot skal reagere med ❤️
- Send "bot" - bot skal reagere med 🤖
- Test spam (send CAPS MELDING eller aaaaaaa)
- Slett en melding - sjekk log channel
🎨 Steg 7: Avanserte Features
7.1: XP System (enkelt)
Legg til i events/message/messageCreate.js:
// Legg til øverst i filen
const userXP = new Map(); // Midlertidig lagring - vi lager database senere
// Legg til i execute funksjonen
async function execute(message) {
if (message.author.bot) return;
// ... eksisterende kode ...
// XP system
const userId = message.author.id;
const currentXP = userXP.get(userId) || 0;
const xpGain = Math.floor(Math.random() * 10) + 1; // 1-10 XP per melding
userXP.set(userId, currentXP + xpGain);
// Sjekk for level up (hver 100 XP = 1 level)
const newLevel = Math.floor((currentXP + xpGain) / 100);
const oldLevel = Math.floor(currentXP / 100);
if (newLevel > oldLevel) {
message.react('🎉');
message.reply(`🎉 Gratulerer ${message.author}! Du nådde level ${newLevel}! 🎉`);
console.log(`🎉 ${message.author.tag} nådde level ${newLevel}`);
}
}
7.2: Reaction Role System
Lag events/message/messageReactionAdd.js:
module.exports = {
name: 'messageReactionAdd',
async execute(reaction, user) {
// Ignorer bot reactions
if (user.bot) return;
// Handle partial reactions
if (reaction.partial) {
try {
await reaction.fetch();
} catch (error) {
console.error('Could not fetch reaction:', error);
return;
}
}
// Reaction role system - sjekk for spesifikke meldinger/reactions
const guild = reaction.message.guild;
const member = guild.members.cache.get(user.id);
// Eksempel: 🎮 gir "Gamer" rolle
if (reaction.emoji.name === '🎮') {
const gamerRole = guild.roles.cache.find(role => role.name === 'Gamer');
if (gamerRole && !member.roles.cache.has(gamerRole.id)) {
try {
await member.roles.add(gamerRole);
console.log(`✅ Ga ${user.tag} Gamer rollen via reaction`);
} catch (error) {
console.error('❌ Kunne ikke gi rolle:', error);
}
}
}
// Eksempel: 🎵 gir "Music Lover" rolle
if (reaction.emoji.name === '🎵') {
const musicRole = guild.roles.cache.find(role => role.name === 'Music Lover');
if (musicRole && !member.roles.cache.has(musicRole.id)) {
try {
await member.roles.add(musicRole);
console.log(`✅ Ga ${user.tag} Music Lover rollen via reaction`);
} catch (error) {
console.error('❌ Kunne ikke gi rolle:', error);
}
}
}
},
};
✅ Oppsummering
Hva har vi lært:
- ✅ Organisere events i strukturerte filer
- ✅ Velkommen/farvel system med fancy embeds
- ✅ Auto-role assignment
- ✅ Message logging og spam protection
- ✅ Server event håndtering
- ✅ Reaction systems
- ✅ Enkel XP tracking
Event system gir oss:
- 🎉 Levende server - Bot reagerer på alt som skjer
- 📊 Logging - Hold styr på aktivitet
- 🛡️ Moderasjon - Automatisk spam protection
- 👋 Velkomst - Nye medlemmer føler seg velkommen
- 🎮 Interaksjon - Auto-reactions og rolle system
🔧 Neste Forbedringer
I neste del (Database System) flytter vi XP og annen data fra midlertidig lagring til permanent database. Da kan boten huske ting selv når den restarter!
🚀 Neste Steg
I del 6 skal vi lære om Database System med MySQL! Vi lærer:
- Sette opp MySQL database
- Lagre brukerdata permanent
- XP system som huskes
- Ban/kick historikk
- User profiles og statistikk
- Database sikkerhet
Klar for å gi boten permanent hukommelse?