Del 4 av 10 Moderat

Komplett Discord Bot Guide

Del 4: Slash Commands System

Fremgang: 4/10 (40%)

Del 4: Slash Commands System

Nå skal vi lære å lage moderne Slash Commands! Dette er den nye og profesjonelle måten å lage Discord bot kommandoer på. Slash commands ser mye bedre ut og er enklere for brukere å bruke.

Hva lærer vi i denne delen?

  1. Forstå Slash Commands - Hvorfor de er bedre enn vanlige commands
  2. Lage vår første slash command - /ping kommando
  3. Command registration - Få Discord til å kjenne til commandsene
  4. Command handler system - Organisere commands i egne filer
  5. Parameters og options - Lage commands som tar input
  6. Feilhåndtering - Håndtere når ting går galt

Steg 1: Forstå Slash Commands

Hva er forskjellen?

Gamle commands (som vi laget i del 3):

Bruker skriver: !ping
Bot svarer: Pong!

Nye slash commands:

Bruker skriver: /ping
Discord viser: Autocompletion, hjelpetekst, parametere
Bot svarer: Pong! (med fancy embed)

Fordeler med Slash Commands:

  • Autocompletion - Discord foreslår commands mens du skriver
  • Hjelpetekst - Brukere ser hva commandet gjør
  • Parametere - Tydelige input felter
  • Permissions - Lettere å kontrollere hvem som kan bruke hva
  • Profesjonelt - Ser ut som offisielle Discord funksjoner

Steg 2: Lage Vår Første Slash Command

2.1: Lag commands mappe struktur

I VS Code, lag disse filene i commands/ mappen:

commands/
├── utility/
│   └── ping.js
└── general/
    └── userinfo.js

2.2: Lage ping.js

Åpne commands/utility/ping.js og skriv:

javascript
const { SlashCommandBuilder } = require('discord.js');

module.exports = {
    data: new SlashCommandBuilder()
        .setName('ping')
        .setDescription('Sjekk bot responstid og status'),
    
    async execute(interaction) {
        // Kalkuler responstid
        const sent = await interaction.reply({ 
            content: 'Regner ut ping...', 
            fetchReply: true 
        });
        
        const ping = sent.createdTimestamp - interaction.createdTimestamp;
        const apiPing = Math.round(interaction.client.ws.ping);
        
        // Oppdater melding med riktig info
        await interaction.editReply({
            content: `🏓 **Pong!**
⏱️ **Responstid:** ${ping}ms
🌐 **API Ping:** ${apiPing}ms
📡 **Status:** ${ping < 100 ? '🟢 Utmerket' : ping < 200 ? '🟡 Bra' : '🔴 Treg'}`
        });
    },
};

2.3: Lage userinfo.js

Åpne commands/general/userinfo.js:

javascript
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');

module.exports = {
    data: new SlashCommandBuilder()
        .setName('userinfo')
        .setDescription('Vis informasjon om en bruker')
        .addUserOption(option =>
            option
                .setName('bruker')
                .setDescription('Brukeren du vil se info om')
                .setRequired(false)
        ),
    
    async execute(interaction) {
        // Hvis ingen bruker er spesifisert, bruk den som kjører kommandoen
        const target = interaction.options.getUser('bruker') || interaction.user;
        const member = interaction.guild.members.cache.get(target.id);
        
        // Lag fancy embed
        const embed = new EmbedBuilder()
            .setColor(0x3498db)
            .setTitle(`👤 Bruker Informasjon`)
            .setThumbnail(target.displayAvatarURL({ dynamic: true, size: 256 }))
            .addFields(
                { name: '📛 Navn', value: target.username, inline: true },
                { name: '🏷️ Tag', value: `#${target.discriminator}`, inline: true },
                { name: '🆔 ID', value: target.id, inline: true },
                { name: '📅 Konto Opprettet', value: `<t:${Math.floor(target.createdTimestamp / 1000)}:F>`, inline: false },
                { name: '🚪 Ble Med Server', value: `<t:${Math.floor(member.joinedTimestamp / 1000)}:F>`, inline: false }
            )
            .setFooter({ 
                text: `Forespurt av ${interaction.user.username}`,
                iconURL: interaction.user.displayAvatarURL()
            })
            .setTimestamp();
        
        // Legg til roller hvis brukeren har noen
        if (member.roles.cache.size > 1) {
            const roles = member.roles.cache
                .filter(role => role.id !== interaction.guild.id)
                .map(role => role.toString())
                .slice(0, 10) // Max 10 roller
                .join(', ');
            
            embed.addFields({
                name: '🎭 Roller',
                value: roles || 'Ingen roller',
                inline: false
            });
        }
        
        await interaction.reply({ embeds: [embed] });
    },
};

⚙️ Steg 3: Command Handler System

Nå må vi lage et system som laster inn alle commands automatisk.

3.1: Oppdater index.js

Legg til dette øverst i index.js (under andre imports):

javascript
const fs = require('fs');
const path = require('path');

Legg til dette etter client.commands = new Collection();:

javascript
// Last inn alle commands
const commandsPath = path.join(__dirname, 'commands');
const commandFolders = fs.readdirSync(commandsPath);

for (const folder of commandFolders) {
    const folderPath = path.join(commandsPath, folder);
    const commandFiles = fs.readdirSync(folderPath).filter(file => file.endsWith('.js'));
    
    for (const file of commandFiles) {
        const filePath = path.join(folderPath, file);
        const command = require(filePath);
        
        if ('data' in command && 'execute' in command) {
            client.commands.set(command.data.name, command);
            console.log(`✅ Lastet command: ${command.data.name}`);
        } else {
            console.log(`⚠️  Command ${file} mangler 'data' eller 'execute' egenskaper.`);
        }
    }
}

3.2: Håndter Slash Command Interactions

Legg til dette event handlerne i index.js:

javascript
// Håndter slash command interactions
client.on('interactionCreate', async interaction => {
    if (!interaction.isChatInputCommand()) return;
    
    const command = 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}`);
    } 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 });
        }
    }
});

📡 Steg 4: Command Registration

Discord må vite hvilke slash commands som finnes. Vi lager et eget script for dette.

4.1: Lag deploy-commands.js

Lag en ny fil deploy-commands.js i hovedmappen:

javascript
require('dotenv').config();
const { REST, Routes } = require('discord.js');
const fs = require('fs');
const path = require('path');

// Samle alle commands
const commands = [];
const commandsPath = path.join(__dirname, 'commands');
const commandFolders = fs.readdirSync(commandsPath);

for (const folder of commandFolders) {
    const folderPath = path.join(commandsPath, folder);
    const commandFiles = fs.readdirSync(folderPath).filter(file => file.endsWith('.js'));
    
    for (const file of commandFiles) {
        const filePath = path.join(folderPath, file);
        const command = require(filePath);
        
        if ('data' in command && 'execute' in command) {
            commands.push(command.data.toJSON());
            console.log(`✅ Lagt til command: ${command.data.name}`);
        }
    }
}

// Deploy commands til Discord
const rest = new REST().setToken(process.env.BOT_TOKEN);

(async () => {
    try {
        console.log(`🚀 Starter registrering av ${commands.length} slash commands...`);
        
        // Registrer commands globalt (tar 1 time å oppdatere)
        // For raskere testing, bruk guild-spesifikke commands
        const data = await rest.put(
            Routes.applicationCommands(process.env.CLIENT_ID),
            { body: commands }
        );
        
        console.log(`✅ Registrert ${data.length} slash commands globalt!`);
        console.log('⏰ NB: Globale commands kan ta opptil 1 time å vises i Discord.');
        
    } catch (error) {
        console.error('❌ Feil ved registrering av commands:', error);
    }
})();

4.2: Kjør Command Registration

I terminalen:

bash
node deploy-commands.js

Du skal se:

✅ Lagt til command: ping
✅ Lagt til command: userinfo  
🚀 Starter registrering av 2 slash commands...
✅ Registrert 2 slash commands globalt!

🧪 Steg 5: Test Slash Commands

5.1: Start Boten

bash
node index.js

5.2: Test i Discord

I Discord serveren din:

  1. Type / - Du skal se dine commands i listen
  2. Prøv /ping - Skal vise responstid
  3. Prøv /userinfo - Skal vise din brukerinfo
  4. Prøv /userinfo @noen - Skal vise info om den brukeren

⚡ Steg 6: Guild Commands (Raskere Testing)

For raskere testing, kan du registrere commands bare for din server:

6.1: Lag deploy-guild-commands.js

javascript
require('dotenv').config();
const { REST, Routes } = require('discord.js');
const fs = require('fs');
const path = require('path');

// SETT INN DIN SERVER ID HER
const GUILD_ID = 'DIN_SERVER_ID_HER';

// ... samme kode som deploy-commands.js, men bytt ut:
const data = await rest.put(
    Routes.applicationGuildCommands(process.env.CLIENT_ID, GUILD_ID),
    { body: commands }
);

Hvor finner du server ID?

  1. I Discord, høyreklikk på server navnet
  2. Klikk "Copy Server ID"
  3. Lim inn som GUILD_ID

Kjør:

bash
node deploy-guild-commands.js

Guild commands oppdateres umiddelbart! 🚀

🎨 Steg 7: Avanserte Command Features

7.1: Command med Flere Options

Lag commands/utility/echo.js:

javascript
const { SlashCommandBuilder } = require('discord.js');

module.exports = {
    data: new SlashCommandBuilder()
        .setName('echo')
        .setDescription('Få boten til å gjenta det du sier')
        .addStringOption(option =>
            option.setName('melding')
                .setDescription('Meldingen du vil at boten skal gjenta')
                .setRequired(true)
                .setMaxLength(1000)
        )
        .addBooleanOption(option =>
            option.setName('hemmelig')
                .setDescription('Skal meldingen være hemmelig? (bare du ser den)')
                .setRequired(false)
        ),
        
    async execute(interaction) {
        const message = interaction.options.getString('melding');
        const isSecret = interaction.options.getBoolean('hemmelig') || false;
        
        await interaction.reply({
            content: `🔊 **Echo:** ${message}`,
            ephemeral: isSecret
        });
    },
};

7.2: Command med Choices

Lag commands/general/status.js:

javascript
const { SlashCommandBuilder } = require('discord.js');

module.exports = {
    data: new SlashCommandBuilder()
        .setName('status')
        .setDescription('Endre bot status')
        .addStringOption(option =>
            option.setName('aktivitet')
                .setDescription('Hva skal boten "gjøre"?')
                .setRequired(true)
                .addChoices(
                    { name: '🎮 Spille et spill', value: 'PLAYING' },
                    { name: '🎵 Høre på musikk', value: 'LISTENING' },
                    { name: '📺 Se på noe', value: 'WATCHING' },
                    { name: '🏃 Konkurrere', value: 'COMPETING' }
                )
        )
        .addStringOption(option =>
            option.setName('tekst')
                .setDescription('Hva skal det stå?')
                .setRequired(true)
                .setMaxLength(50)
        ),
        
    async execute(interaction) {
        const activityType = interaction.options.getString('aktivitet');
        const activityText = interaction.options.getString('tekst');
        
        // Bare admin/eier kan endre status
        if (!interaction.member.permissions.has('Administrator')) {
            return interaction.reply({
                content: '❌ Du må være administrator for å bruke denne kommandoen!',
                ephemeral: true
            });
        }
        
        // Endre bot aktivitet
        interaction.client.user.setPresence({
            activities: [{ 
                name: activityText,
                type: activityType 
            }],
            status: 'online'
        });
        
        await interaction.reply({
            content: `✅ Bot status endret til: **${activityType.toLowerCase()} ${activityText}**`
        });
    },
};

📊 Steg 8: Command Kategorier og Hjelp

8.1: Lag help command

Lag commands/general/help.js:

javascript
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');

module.exports = {
    data: new SlashCommandBuilder()
        .setName('help')
        .setDescription('Vis alle tilgjengelige commands'),
        
    async execute(interaction) {
        const { commands } = interaction.client;
        
        // Grupper commands etter kategori
        const categories = {};
        
        commands.forEach(command => {
            // Finn kategori fra fil path  
            const category = command.data.name.includes('ping') || command.data.name.includes('echo') 
                ? 'Verktøy' : 'Generelt';
                
            if (!categories[category]) categories[category] = [];
            categories[category].push(command);
        });
        
        const embed = new EmbedBuilder()
            .setColor(0x3498db)
            .setTitle('📋 Bot Commands')
            .setDescription('Her er alle tilgjengelige commands:')
            .setThumbnail(interaction.client.user.displayAvatarURL())
            .setTimestamp();
            
        // Legg til hver kategori
        for (const [category, cmds] of Object.entries(categories)) {
            const commandList = cmds.map(cmd => 
                `\`/${cmd.data.name}\` - ${cmd.data.description}`
            ).join('\n');
            
            embed.addFields({
                name: `📂 ${category}`,
                value: commandList,
                inline: false
            });
        }
        
        embed.setFooter({
            text: `Totalt ${commands.size} commands tilgjengelig`,
            iconURL: interaction.user.displayAvatarURL()
        });
        
        await interaction.reply({ embeds: [embed] });
    },
};

✅ Testing Alt Sammen

8.1: Registrer Nye Commands

bash
node deploy-commands.js

8.2: Start Bot

bash
node index.js

8.3: Test Alle Commands

I Discord:

  • /ping - Sjekk responstid
  • /userinfo - Din info
  • /userinfo @noen - Andres info
  • /echo melding:Hei verden! - Echo command
  • /echo melding:Hemmelig hemmelig:True - Hemmelig echo
  • /help - Se alle commands
  • /status aktivitet:🎮 Spille et spill tekst:Minecraft - Endre bot status (krever admin)

🛠️ Feilsøking

Commands vises ikke i Discord

  • Vent 1 time for globale commands
  • Bruk guild commands for raskere testing
  • Sjekk at CLIENT_ID er riktig

"Interaction failed" i Discord

  • Sjekk terminalen for feilmeldinger
  • Sjekk at alle commands har data og execute
  • Sjekk at bot har riktige permissions

Bot svarer ikke på commands

  • Sjekk at interactionCreate event er lagt til
  • Sjekk at bot har applications.commands scope

🎉 Gratulerer!

Du har nå laget et komplett slash command system! Dette er grunnlaget for alle moderne Discord bots.

Hva har du lært:

  • ✅ Lage slash commands med options
  • ✅ Command registration system
  • ✅ Professional command struktur
  • ✅ Embeds og fancy meldinger
  • ✅ Permissions og sikkerhet
  • ✅ Feilhåndtering

🚀 Neste Steg

I del 5 skal vi lære om Event System! Vi lærer:

  • Håndtere når folk kommer inn/forlater serveren
  • Lage velkommen meldinger med embeds
  • Logging system for all server aktivitet
  • Role assignment ved server join
  • Message filtering og automod

Klar for å lage en bot som reagerer på alt som skjer?