This repository has been archived on 2022-10-02. You can view files and clone it, but cannot push or open issues or pull requests.
dcbot-old/discord/discord.go

312 lines
8.1 KiB
Go

package discord
import (
"context"
"github.com/pkg/errors"
"strings"
"github.com/sirupsen/logrus"
"github.com/tribalwarshelp/dcbot/message"
"github.com/tribalwarshelp/golang-sdk/sdk"
"github.com/tribalwarshelp/dcbot/group"
"github.com/tribalwarshelp/dcbot/model"
"github.com/tribalwarshelp/dcbot/observation"
"github.com/tribalwarshelp/dcbot/server"
"github.com/bwmarrin/discordgo"
)
var log = logrus.WithField("package", "discord")
type SessionConfig struct {
Token string
CommandPrefix string
Status string
ServerRepository server.Repository
GroupRepository group.Repository
ObservationRepository observation.Repository
API *sdk.SDK
}
type Session struct {
dg *discordgo.Session
cfg SessionConfig
handlers commandHandlers
}
func New(cfg SessionConfig) (*Session, error) {
var err error
s := &Session{
cfg: cfg,
}
s.dg, err = discordgo.New("Bot " + cfg.Token)
if err != nil {
return nil, err
}
if err := s.init(); err != nil {
return nil, err
}
return s, nil
}
func (s *Session) init() error {
s.handlers = commandHandlers{
&commandHandler{
cmd: HelpCommand.WithPrefix(s.cfg.CommandPrefix),
fn: s.handleHelpCommand,
},
&commandHandler{
cmd: AuthorCommand.WithPrefix(s.cfg.CommandPrefix),
fn: s.handleAuthorCommand,
},
&commandHandler{
cmd: TribeCommand.WithPrefix(s.cfg.CommandPrefix),
fn: s.handleTribeCommand,
},
&commandHandler{
cmd: ChangeLanguageCommand.WithPrefix(s.cfg.CommandPrefix),
fn: s.handleChangeLanguageCommand,
requireAdmPermissions: true,
},
&commandHandler{
cmd: AddGroupCommand.WithPrefix(s.cfg.CommandPrefix),
fn: s.handleAddGroupCommand,
requireAdmPermissions: true,
},
&commandHandler{
cmd: DeleteGroupCommand.WithPrefix(s.cfg.CommandPrefix),
fn: s.handleDeleteGroupCommand,
requireAdmPermissions: true,
},
&commandHandler{
cmd: GroupsCommand.WithPrefix(s.cfg.CommandPrefix),
fn: s.handleGroupsCommand,
requireAdmPermissions: true,
},
&commandHandler{
cmd: ObserveCommand.WithPrefix(s.cfg.CommandPrefix),
fn: s.handleObserveCommand,
requireAdmPermissions: true,
},
&commandHandler{
cmd: DeleteObservationCommand.WithPrefix(s.cfg.CommandPrefix),
fn: s.handleDeleteObservationCommand,
requireAdmPermissions: true,
},
&commandHandler{
cmd: ObservationsCommand.WithPrefix(s.cfg.CommandPrefix),
fn: s.handleObservationsCommand,
requireAdmPermissions: true,
},
&commandHandler{
cmd: ConqueredVillagesCommand.WithPrefix(s.cfg.CommandPrefix),
fn: s.handleConqueredVillagesCommand,
requireAdmPermissions: true,
},
&commandHandler{
cmd: DisableConqueredVillagesCommand.WithPrefix(s.cfg.CommandPrefix),
fn: s.handleDisableConqueredVillagesCommand,
requireAdmPermissions: true,
},
&commandHandler{
cmd: LostVillagesCommand.WithPrefix(s.cfg.CommandPrefix),
fn: s.handleLostVillagesCommand,
requireAdmPermissions: true,
},
&commandHandler{
cmd: DisableLostVillagesCommand.WithPrefix(s.cfg.CommandPrefix),
fn: s.handleDisableLostVillagesCommand,
requireAdmPermissions: true,
},
&commandHandler{
cmd: ShowEnnobledBarbariansCommand.WithPrefix(s.cfg.CommandPrefix),
fn: s.handleShowEnnobledBarbariansCommand,
requireAdmPermissions: true,
},
&commandHandler{
cmd: ShowInternalsCommand.WithPrefix(s.cfg.CommandPrefix),
fn: s.handleShowInternalsCommand,
requireAdmPermissions: true,
},
&commandHandler{
cmd: CoordsTranslationCommand.WithPrefix(s.cfg.CommandPrefix),
fn: s.handleCoordsTranslationCommand,
requireAdmPermissions: true,
},
&commandHandler{
cmd: DisableCoordsTranslationCommand.WithPrefix(s.cfg.CommandPrefix),
fn: s.handleDisableCoordsTranslationCommand,
requireAdmPermissions: true,
},
}
s.dg.AddHandler(s.handleNewMessage)
err := s.dg.Open()
if err != nil {
return errors.Wrap(err, "error opening ws connection")
}
if err := s.UpdateStatus(s.cfg.Status); err != nil {
return err
}
return nil
}
func (s *Session) Close() error {
return s.dg.Close()
}
func (s *Session) SendMessage(channelID, message string) error {
_, err := s.dg.ChannelMessageSend(channelID, message)
return err
}
func (s *Session) SendEmbed(channelID string, message *discordgo.MessageEmbed) error {
fields := message.Fields
baseNumberOfCharacters := len(message.Description) + len(message.Title)
if message.Author != nil {
baseNumberOfCharacters += len(message.Author.Name)
}
if message.Footer != nil {
baseNumberOfCharacters += len(message.Footer.Text)
}
var splittedFields [][]*discordgo.MessageEmbedField
characters := baseNumberOfCharacters
fromIndex := 0
fieldsLen := len(fields)
for index, field := range fields {
fNameLen := len(field.Name)
fValLen := len(field.Value)
if characters+fNameLen+fValLen > EmbedSizeLimit || index == fieldsLen-1 {
splittedFields = append(splittedFields, fields[fromIndex:index+1])
fromIndex = index
characters = baseNumberOfCharacters
}
characters += fNameLen
characters += fValLen
}
for _, fields := range splittedFields {
fieldsLen := len(fields)
for i := 0; i < fieldsLen; i += EmbedLimitField {
end := i + EmbedLimitField
if end > fieldsLen {
end = fieldsLen
}
message.Fields = fields[i:end]
if _, err := s.dg.ChannelMessageSendEmbed(channelID, message); err != nil {
return err
}
}
}
return nil
}
func (s *Session) UpdateStatus(status string) error {
if err := s.dg.UpdateStatus(0, status); err != nil {
return err
}
return nil
}
func (s *Session) IsGuildMember(guildID string) (bool, error) {
_, err := s.dg.State.Guild(guildID)
if err != nil {
if _, err = s.dg.Guild(guildID); err != nil {
return false, err
}
}
return true, nil
}
func (s *Session) handleNewMessage(_ *discordgo.Session, m *discordgo.MessageCreate) {
if m.Author.ID == s.dg.State.User.ID || m.Author.Bot {
return
}
parts := strings.Split(m.Content, " ")
args := parts[1:]
svr := &model.Server{
ID: m.GuildID,
Lang: message.GetDefaultLanguage().String(),
}
if svr.ID != "" {
if err := s.cfg.ServerRepository.Store(context.Background(), svr); err != nil {
return
}
}
ctx := &commandCtx{
server: svr,
localizer: message.NewLocalizer(svr.Lang),
}
cmd := Command(parts[0])
h := s.handlers.find(cmd)
if h != nil {
if h.requireAdmPermissions {
if m.GuildID == "" {
return
}
has, err := s.memberHasPermission(m.GuildID, m.Author.ID, discordgo.PermissionAdministrator)
if err != nil || !has {
return
}
}
log.
WithFields(logrus.Fields{
"serverID": svr.ID,
"lang": svr.Lang,
"command": cmd,
"args": args,
"authorID": m.Author.ID,
"authorUsername": m.Author.Username,
}).
Info("handleNewMessage: Executing command...")
h.fn(ctx, m, args...)
return
}
s.translateCoords(ctx, m)
}
func (s *Session) memberHasPermission(guildID string, userID string, permission int) (bool, error) {
member, err := s.dg.State.Member(guildID, userID)
if err != nil {
if member, err = s.dg.GuildMember(guildID, userID); err != nil {
return false, err
}
}
// check if user is a guild owner
guild, err := s.dg.State.Guild(guildID)
if err != nil {
if guild, err = s.dg.Guild(guildID); err != nil {
return false, err
}
}
if guild.OwnerID == userID {
return true, nil
}
// Iterate through the role IDs stored in member.Roles
// to check permissions
for _, roleID := range member.Roles {
role, err := s.dg.State.Role(guildID, roleID)
if err != nil {
return false, err
}
if role.Permissions&permission != 0 {
return true, nil
}
}
return false, nil
}