Что такое Discord?

Надеюсь, вы знаете, что Discord — популярная социальная сеть для общения. Здесь можно встретить много интересных людей, вести увлекательные беседы на любые темы, вместе смотреть фильмы (когда-то я смотрела «Астрал» (кстати, лучший фильм ужасов, который я когда-либо смотрела, очень рекомендую) с парнем из Испании, с которой я познакомился в World Of Warcraft).

Зачем нам нужен бот в Discord?

Discord бот может быть использован для упрощения вашей жизни как владельца гильдии, например, этот бот может банить/разбанивать пользователей. Однако вы можете создать бота для проверки своих навыков работы с Golang, как это сделал я :3

С чего начать?

Во-первых, вам нужно настроить среду. Предполагаю, что вы уже установили go, так что на этом останавливаться не буду. Для создания бота вам необходимо включить режим разработчика в Discord. Этого можно добиться, нажав

Настройки пользователя → Дополнительно → Режим разработчика (в приложении Discord)

Как создать приложение Discord?

Чтобы создать дискорд-бота, вам нужно создать приложение для дискорда. Во-первых, перейдите на https://discord.com/developers/applications/ и войдите в систему. Затем нажмите Новое приложениеВведите имя приложенияСоздать. Вы также можете заполнить описание и добавить значок в только что созданное приложение. После успеха Вы должны увидеть такие вещи

Как создать бота

Нажмите на меню Бот, а затем на кнопку Добавить бота. После этого вам нужно перейти в раздел «Бот» и создать нового бота. Здесь вам нужно будет включить все намерения и предоставить вашему боту права администратора.

Теперь перейдите в меню OAuth2 и нажмите кнопку Копировать, чтобы получить информацию о идентификаторе клиента:

Ссылка для приглашения бота

Чтобы протестировать нашего бота, нам нужно добавить его на ваш сервер. Для этого нам нужно сгенерировать ссылку-приглашение с идентификатором клиента, который вы скопировали (вместо ‹CLIENT-ID›:

https://discord.com/api/oauth2/authorize?client_id=<CLIENT-ID>&permissions=8&scope=bot

Выберите сервер Discord, на котором у вас есть права администратора, нажмите кнопку Продолжить, а затем нажмите кнопку Подтвердить.

Последнее, что нам нужно сделать перед созданием настоящего бота, это сохранить токен. Вернитесь в приложение Discord, нажмите «Бот» → «Сбросить токен» (или просто скопируйте).

Ууу, мы закончили первую часть. Пришло время кодировать!!

Откройте редактор кода (я буду использовать GoLand) и создайте файл main.go, каталог tmp и там файл utils.go (tmp/utils.go)
Создайте файл .env (здесь мы будем хранить наш токен, поэтому никто не может навредить вашему боту!), и создайте значение токена, которое вы скопировали, в переменную с именем DISCORD_TOKEN.
В этом проекте я использую библиотеку discordgo для взаимодействия с discord API. Godotenv для получения переменных из файла .env. Изображение/цвет для настройки MessageEmbed.
Дополнительно для работы нам потребуются следующие пакеты:

go get github.com/bwmarrin/discordgo v0.26.1
go get github.com/joho/godotenv v1.4.0
go get golang.org/x/image v0.3.0

После этого вам нужно создать каталог .utils и создать файлutilities.go. Вставьте следующий код в. Я разберусь с делами по ходу дела.

package main

import (
 // ds-bot is my name of project, instead, there will occur yours.
 "ds-bot/tmp"
 "encoding/json"
 "fmt"
 "github.com/bwmarrin/discordgo"
 "github.com/joho/godotenv"
 "image/color"
 "io/ioutil"
 "log"
 "net/http"
 "os"
 "os/signal"
 "strings"
 "syscall"
)

// You can call this bot's command by typing ${command}, you can change on 
// any symbol
const botPrefix = "$"

func main() {
   err := godotenv.Load(".env")
   if err != nil {
       log.Fatal(err)
   }
   // getting value of DISCORD_TOKEN
   Token := os.Getenv("DISCORD_TOKEN")
   // here we initializing our bot

   bot, err := discordgo.New("Bot " + Token)
   if err != nil {
       fmt.Println("Error creating a discord Session, ", err)
   }

   // Those handlers will come in handy later
   bot.AddHandler(BanUser)
   bot.AddHandler(help)
   bot.AddHandler(changeStatus)
   bot.AddHandler(searchGifs)
   bot.AddHandler(greetNewMember)
   bot.AddHandler(listRules)
   bot.AddHandler(UnbanUser)

   // this is a very important part, especially for our bot to be able 
   // to send messages in server and guild chats
   bot.Identify.Intents = discordgo.IntentsGuildMessages
   bot.Identify.Intents = discordgo.IntentsGuilds | discordgo.IntentsGuildMessages | discordgo.IntentsGuildMembers | discordgo.IntentsGuildPresences
   err = bot.Open()
   if err != nil {
       fmt.Println("Error opening Discord Session, ", err)
   }
   fmt.Println("Bot is currently running. CTRL-C to exit.")
 
   // this is needed to for closing bot
   sc := make(chan os.Signal, 1)
   signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
   <-sc
}

func changeStatus(s *discordgo.Session, event *discordgo.Event) {
 s.UpdateListeningStatus("Horde theme song")
}

Функция changeStatus изменяет статус нашего бота на «Слушаем музыкальную тему Орды». Вы можете выбрать между:
s.UpdateGameStatus(1, Game) --> Будет показано «В процессе игры»
s.UpdateStreamingStatus() Если не используется › 0, затем установите статус ожидания.
Если имя!=, то установите игру.
Если name!= и url!=, установите тип статуса на потоковую передачу с заданным URL-адресом.
в противном случае установите статус «Активно» и «Нет игры».
s.UpdateStatusComplex(), где вы можете добавить свой собственный статус.

func greetNewMember(s *discordgo.Session, mes *discordgo.MessageCreate) {
 if mes.Type == 7 {
    s.ChannelMessageSend(mes.ChannelID, `
    I'm glad to see you here, recruit!
    Here is some rules, you have to follow, to become a High Overload, Leader, or Warchief:
     Do NOT:
        Harass, bully, or threat marginalized or vulnerable groups of people,
        Engage in content manipulation (spamming, subscriber fraud, vote manipulation, or ban evasion),
        Post or threaten to post intimate or sexually explicit photos or videos of another person without their consent,
        Impersonate someone in a misleading way,
        Label the content and communities improperly (especially graphic content),
        Post suggestive or sexual content that involves minors,
        Post illegal content,
        Do anything that stops the normal use of this server,
        Transmit, distribute, or upload any viruses, worms, or other malware intended to interfere with this servers service,
        Use the platform to violate the law or infringe on intellectual and other property right,
        Engage in actions that could disrupt, disable, overburden, or impair this servers service,
        Attempt to gain access to another user's account,
        Access, search, or collect data from this server,
        Use the platform in any way that may be abusive or fraudulent 
  For The Horde! 
 `)
  s.ChannelMessageSend(mes.ChannelID, fmt.Sprint("https://media.giphy.com/media/xThtatRttFzLD9oEtG/giphy.gif"))
 }
}

Функция GreetNewMember возвращает мое пользовательское приветствие каждый раз, когда новый пользователь подключается к серверу!

func listRules(s *discordgo.Session, m *discordgo.MessageCreate) {
   if m.Author.Bot {
      return
   }

   args := strings.Split(strings.TrimPrefix(m.Content, botPrefix), " ")
   command := args[0]

    if command == "listrules" {
        s.ChannelMessageSend(m.ChannelID, rules)
    }
}

Функция ListRules отправляет в чат список правил, которые я объявил при старте программы.

func help(s *discordgo.Session, ms *discordgo.MessageCreate) {
 if ms.Author.Bot {
  return
 }
 if ms.Content == botPrefix+"help" {
  s.ChannelMessageSend(ms.ChannelID, `
  My name is Saurfang, and I am extremely useful bot!
      I have such commands: 
       $help           - List of commands, which this bot wields
       $ban {user} {reason} - Kick out user from server 
       $listrules          - Prints all rules, in this server
       $unban      - Unbans user(need to reply to message)
   `)
 }
}

Функция справки выводит все функции, которые может выполнять этот бот.

func BanUser(s *discordgo.Session, m *discordgo.MessageCreate) {
 if m.Author.Bot {
  return
 }
 args := strings.Split(strings.TrimPrefix(m.Content, botPrefix), " ")
 command := args[0]
 if len(args) > 1 {
    args = args[1:]
 } else {
    args = nil
 }
 if command == "ban" {
    if tmp.HasPerm(s, m.Author, m.ChannelID, discordgo.PermissionBanMembers) {
       // if len(args) < 2, it means that either user was not provided, or reason.
       if len(args) < 2 {
          _, _ = s.ChannelMessageSend(m.ChannelID, "You should provide reasons for banning: $ban {user} {reason}")
          return
       }
       u := tmp.FindUser(s, m.Mentions, args[0])
       if u == nil {
            // if user was not found, findUser returns nil
            _, _ = s.ChannelMessageSend(m.ChannelID, "That user is not in the server.")
            return
       }
       err := s.GuildBanCreateWithReason(m.GuildID, u.ID, strings.Join(args[1:], " "), 0)
       if err != nil {
            _, _ = s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("Error banning user: %s", err.Error()))
            return
       }
       title := "User successfully banned"
       desc := fmt.Sprintf("User %v has been banned. Do not repeat his mistakes.", u.Mention())

       _, _ = s.ChannelMessageSendEmbed(m.ChannelID, tmp.CreateEmbedMessage(title, desc).Return())
        } else {
           _, _ = s.ChannelMessageSend(m.ChannelID, "You have no permission")
        }
    }
}

Время для более сложных вещей! Здесь мы проверяем, предоставляет ли пользователь дополнительные параметры, такие как UserToBan и Reason для бана.

args := strings.Split(strings.TrimPrefix(m.Content, botPrefix), " ")
command := args[0]
if len(args) > 1 {
    args = args[1:]
} else {
    args = nil
}

Разделяем команду и аргументы, на всякий случай($ban @Sylvana Windrunner Reason, вывод будет на скриншоте ниже). Переменная команды хранит строку «запретить», а срез аргументов хранит причину @Sylvana Windrunner. P.S. @Sylvana Windrunner — это имя пользователя, поэтому оно хранится как 1 элемент.

    if tmp.HasPerm(s, m.Author, m.ChannelID, discordgo.PermissionBanMembers)
    u := tmp.FindUser(s, m.Mentions, args[0])

Эти функции я подробно описал ниже.

     err := s.GuildBanCreateWithReason(m.GuildID, u.ID, strings.Join(args[1:], " "), 0)
     if err != nil {
          _, _ = s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("Error banning user: %s", err.Error()))
          return
     }
     title := "User successfully banned"
     desc := fmt.Sprintf("User %v has been banned. Do not repeat his mistakes.", u.Mention())
      _, _ = s.ChannelMessageSendEmbed(m.ChannelID, tmp.CreateEmbedMessage(title, desc).Return())

Вот самая интересная часть. Первая строка, мы создаем бан пользователя, и если что-то пошло не так, отправляем сообщение с ошибкой. Если процедура прошла успешно, отправьте сообщение, как на скриншоте ниже.

Эта часть кода проверяет, было ли сообщение введено ботом (и если нет, то продолжает), и разделяет аргументы (аргументы — это слова после команда, например: $ban {пользователь} {причина} → где бан — это команда (без учета botPrefix), пользователь и причина — это аргументы

func UnbanUser(s *discordgo.Session, m *discordgo.MessageCreate) {
 if m.Author.Bot {
  return
 }
 args := strings.Split(strings.TrimPrefix(m.Content, botPrefix), " ")
 command := args[0]
 if len(args) <= 2 {
  args = args[1:]
 } else {
  args = nil
 }

 
 if command == "unban" {
    if tmp.HasPerm(s, m.Author, m.ChannelID, discordgo.PermissionBanMembers) {
       var u *discordgo.User
       if m.ReferencedMessage != nil {
            u = m.ReferencedMessage.Author
        } else {
            s.ChannelMessageSend(m.ChannelID, "You need to reply to message.")
            return
        }

        if u == nil {
            _, _ = s.ChannelMessageSend(m.ChannelID, "That user was never in the server.")
            return
         }
         err := s.GuildBanDelete(m.GuildID, u.ID)
         if err != nil {
              _, _ = s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("Error unbanning user: %s", err.Error()))
              return
         }
         title := "User successfully unbanned"
         desc := fmt.Sprintf("User %v has been unbanned! Welcome back!", u.Mention())

         _, _ = s.ChannelMessageSendEmbed(m.ChannelID, tmp.CreateEmbedMessage(title, desc).Return())
      } else {
           _, _ = s.ChannelMessageSend(m.ChannelID, "You have no permission")
       }
   }
}

Утилиты.go

Наиболее важной функцией в следующем фрагменте кода является HasPerm. Функция HasPerm проверяет, дал ли пользователь разрешения (в нашем случае мы
проверяем, есть ли у пользователя разрешение на бан). Переменная ColorCyan — это цвет для нашего нижнего колонтитула (см. фото после кода), вы можете изменить его на любой, который вам покажется красивым. colorToInt — вспомогательная функция, которая переводит цвет из RGBA в RGB.

var (
   ColorCyan = colornames.Cyan
)

func colorToInt(color color.RGBA) int {
   return 256*256*int(color.R) + 256*int(color.G) + int(color.B)
}


func HasPerm(session *discordgo.Session, user *discordgo.User, channelID string, perm int64) bool {
   perms, err := session.State.UserChannelPermissions(user.ID, channelID)
   if err != nil {
      _, _ = session.ChannelMessageSend(channelID, fmt.Sprintf("Failed to retrieve perms: %s", err.Error()))
      return false
   }
   return perms&perm != 0
}

FindUser также является вспомогательной функцией, которая немного ускоряет нашу программу и возвращает пользователя.

func FindUser(session *discordgo.Session, mentions []*discordgo.User, arg string) *discordgo.User {
   if len(mentions) > 0 {
      return mentions[0]
   }
   user, _ := session.User(arg)
   return user
}
func CreateEmbedMessage(title string, description string) *EmbMessage {
   return NewMessageEmbed().SetTitle(title).SetDescription(description).SetFooter().SetFooterText("Bot Saurfang ").SetTimestamp(time.Now()).SetColor(colornames.Cyan)
}

type EmbMessage struct {
   em *discordgo.MessageEmbed
}

Поскольку мы не можем добавлять методы к типу *discordgo.MessageEmbed, я решил создать свой собственный, с помощью которого мы можем вызывать такие функции, как SetTimestamp или SetFooterText. С ним легче взаимодействовать, и гораздо меньше неудобств.

SetTitle изменяет заголовок встроенного сообщения. В приведенном выше случае это пользователь успешно заблокирован.

SetDescription изменяет описание сообщения. В приведенном выше случае это Пользователь @Sylvana Windrunner забанен по [причине].

Функция SetColor изменяет цвет во встраиваемом сообщении (слева). В настоящее время он установлен как Голубой, но вы можете изменить его в любое время :3

SetTimeStamp возвращает метку времени в формате Sun Jan _1 00:00:00 2023

func (e *EmbMessage) SetTitle(t string) *EmbMessage {
   e.em.Title = t
   return e
}

func (e *EmbMessage) SetDescription(d string) *EmbMessage {
   e.em.Description = d
   return e
}

func (e *EmbMessage) SetColor(c color.RGBA) *EmbMessage {
   e.em.Color = colorToInt(c)
   return e
}

func (e *EmbMessage) SetTimestamp(t time.Time) *EmbMessage {
   e.em.Timestamp = t.Format(time.RFC3339)
   return e
}

Нижний колонтитул — это дополнительная функция во встраиваемом сообщении, которую я использовал в качестве подписи и метки времени.

SetFooter инициализирует нижний колонтитул

SetFooterText используется для настройки содержимого нижнего колонтитула. В приведенном выше случае это «Бот Саурфанг» и отметка времени.

type Footer struct {
   EmbMessage
}

func (f *Footer) SetFooterText(n string) *Footer {
   f.em.Footer.Text = n
   return f
}

func (e *EmbMessage) SetFooter() *Footer {
   e.em.Footer = &discordgo.MessageEmbedFooter{}
   return &Footer{*e}
}

NewMessageEmbed возвращает пустой EmbMessage, с которым мы можем работать. Возврат вызывается в конце, когда мы настроили заголовок, описание и другие компоненты.

func NewMessageEmbed() *EmbMessage {
   em := &discordgo.MessageEmbed{
      URL:         "",
      Type:        "",
      Title:       "",
      Description: "",
      Timestamp:   "",
      Color:       0,
      Footer:      nil,
      Image:       nil,
      Thumbnail:   nil,
      Video:       nil,
      Provider:    nil,
      Author:      nil,
      Fields:      nil,
   }
   return &EmbMessage{em: em}
}


func (e *EmbMessage) Return() *discordgo.MessageEmbed {
   return e.em
}

Полный исходный код можно посмотреть здесь(не забудьте добавить звездочку :3 ):
werniq/my_discord_bot (github.com)
Если у вас есть вопросы, вы можете задать их мне в телеграм: < br /> https://telegram.me/usioa
Email: [email protected]
Discord: Rama(V)#5065