Intro

在数字时代,聊天机器人在互动、娱乐和工作场景中扮演着越来越重要的角色。
Discord,作为一款广受欢迎的社交平台,不仅仅是玩游戏的好地方,还是互相交流、分享兴趣爱好的社群网络。 而在这个丰富多彩的生态系统中,编写一个 Discord 机器人,不仅能够为服务器增添趣味和实用性,还能让我们更好地理解编程和自动化的魅力。
在这篇博文中,我使用 DiscordGo 构建了一个简单的 Ping Pong Bot,详细记录了从 Bot 的创建到代码的编写和测试整个过程。 我将其分为如下三个部分:
  1. 准备阶段
    • 创建 Bot App
    • 创建 Discord Server
    • 将 Bot 添加到 Server
  2. 编写代码
    • 初始化项目并安装环境
    • 编写业务代码
  3. 测试 Bot
在准备阶段,我创建了一个 Discord Bot App 和一个测试用的 Discord Server,然后将 Bot 添加到 Server 中。
在编写代码阶段,我使用 DiscordGo 编写了一个简单的 Ping Pong Bot,如果用户在频道中输入 ping,bot 将在频道中回复 Pong。
在测试阶段,我在频道中测试了 Bot 的功能。

Create a New Discord App

创建一个新的 Bot App,是我们要做的第一件事。
  1. 打开 Discord Developer Portal

  2. 点击头像旁边的 New Application

  3. 输入应用名称,点击 Create

Create a New App
Create a New App

Configure Ping Pong Bot

为了 Bot 能正常的收发消息,我们需要对其进行一些必要的配置,在 sidebar 中点击 Bot,这里列出了 bot 的相关配置
这里我们需要完成以下两项配置
  1. disable PUBLIC BOT : 关闭公共 bot,我们的 bot 是私有的
  2. enable MESSAGE CONTENT INTENT,为了能正常收到消息,将此配置打开
Configure Bot
Configure Bot

Add Your Own Server

接着,我们打开 Discord 客户端,添加一个服务器,这个服务器会用来测试 Ping Pong Bot 是否正常工作
在 sidebar 中点击 + 号,填入自己的信息,完成创建即可
Create Server
Create Server
在服务器创建完毕之后,我们需要把机器人邀请到服务器中
回到 Discord Developer Portal,在 sidebar 中点击 OAuth2 -> URL Generator
在这个界面中,我们需要配置 Bot 的权限
  1. SCOPES:
  • Bot
  1. BOT PERMISSIONS:
  • Send Messages
  • Read Message History
  • Read Messages / View Channels
Generate Invite Link
Generate Invite Link
选择完毕之后, URL 会自动在下方的输入框中,打开这个 URL, 点击 Authorize 按钮,即可将我们的机器人添加到服务器中
Invite Bot
Invite Bot
进入我们的服务器,即可看到 Bot 已经成功被添加到服务器中 🎉
Test App Server
Test App Server

Get Token

至此,我们创建好了我们的 bot 和测试服务器,并且已经把 bot 添加到了服务器中。
在正式开始编码之前,我们先获取一下 Bot 的 token 在 Discord Developer Portal 点击 Bot -> Reset Token,之后会显示出新的 token,复制下来,稍后将其写入代码,用于验证身份
Get Token
Get Token

Coding

OK,完成了所有准备工作,接下来我们开始编码,让我们的机器人能正常工作
首先创建一个 Go 项目,执行如下命令,安装依赖
go get github.com/bwmarrin/discordgo
首先编写 Bot 结构体
NewBot 方法中创建 discord session,在 Start 方法中将 session 打开
package discord

import (
    "github.com/bwmarrin/discordgo"
    "strings"
)

type Bot struct {
    session *discordgo.Session
    token   string
}

func NewBot(token string) *Bot {
    bot := &Bot{
        token:   token,
        session: createSession(token),
    }
    return bot
}

func (bot *Bot) Start() {
    session := bot.session
    if err := session.Open(); err != nil {
        panic(err)
    }
    defer session.Close()
    fmt.Println("Bot running...")
    bot.holdOn()
}

func (bot *Bot) holdOn() {
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    <- c
}

func createSession(token string) *discordgo.Session {
    session, err := discordgo.New(token)
    if err != nil {
        panic(err)
    }
    return session
}
为了避免程序直接关闭,我们使用 holdOn 方法阻塞程序,在 holdOn 方法中我创建了一个 channel,让程序等待一个 os.Interrupt 信号再继续向下执行
上述代码完成了 Bot 的启动流程,现在我们向其中添加处理消息的函数(EventHandler)
func (bot *Bot) Start() {
    session := bot.session
    session.AddHandler(handler) // 添加 EventHandler
    if err := session.Open(); err != nil {
        panic(err)
    }
    defer session.Close()
    fmt.Println("Bot running...")
    bot.holdOn()
}
我们在 Start 方法中添加一行 session.AddHandler(handler)
这会把消息处理函数添加到 session 中,当有消息发送过来时,session 会调用 handler 函数处理消息。
接着,我们在 handler 函数中编写具体的 ping pong 逻辑
handler 函数接受两个参数
  1. *discordgo.Session:bot 实例的 session 属性
  2. *discordgo.MessageCreate:传递了消息的相关信息,包括消息内容、发送者、提及等信息。
DiscordGo 会自动地根据 handler 的参数类型判断我们要监听的事件类型,比如这里我们传入的是 *discordgo.MessageCreate,那么 bot 就会监听 MessageCreate 事件。

Discord Gateway Doc 中列出了所有可以监听的事件。

除此之外,不要忘记 bot 只需回复他感兴趣的消息,因此我们判断一下哪些消息需要回复。 这里我们把 bot 自己的消息忽略,并且只回复提及自己的消息,我们将具体的逻辑写入 shouldReply 方法中,并在 handler 方法中调用进行判断。
package discord

import (
    "strings"
    "github.com/bwmarrin/discordgo"
)

func handler(session *discordgo.Session, message *discordgo.MessageCreate) {
	if shouldReply(session.State.User.ID, message) {
		switch {
		case strings.Contains(message.Content, "ping"):
			_, err := session.ChannelMessageSend(message.ChannelID, "pong")
			if err != nil {
				println(err.Error())
			}
		case strings.Contains(message.Content, "pong"):
			_, err := session.ChannelMessageSend(message.ChannelID, "ping")
			if err != nil {
				println(err.Error())
			}
		}
	}
}

// shouldReply 判断是否需要回复
func shouldReply(botId string, message *discordgo.MessageCreate) bool {
	// ignore messages from the bot itself
	if message.Author.ID == botId {
		return false
	}
	// ignore messages that don't mention the bot
	for _, mentionedUser := range message.Mentions {
		if mentionedUser.ID == botId {
			return true
		}
	}
	return false
}
OK,目前,我们已经把 EventHandler 添加到了 bot 中,现在让我们编写 main.go,启动我们的 bot
把 bot 的 token 拷贝过来用于验证身份,然后启动程序即可
package main

import "dev/hylas/discord-bot/discord"

const token = "${your-token}"

func main() {
    bot := discord.NewBot("Bot " + token)
    bot.Start()
}

Test Bot

打开 Discord 客户端,进入我们创建的服务器
输入 @Ping Pong Bot ping@Ping Pong Bot pong
Test Bot
Test Bot
可以看到,bot 已经成功获取到我们的消息了,并且回复正确!

References

  1. Discord Go
  2. Discord Developer Doc

Author:  Hylas    Publication Date:  8/12/2023

Licensed under CC BY-NC 3.0