discord bot cleanup

This commit is contained in:
wish
2022-08-17 23:29:16 +10:00
parent 97c57762f0
commit 14d3b37435
6 changed files with 54 additions and 388 deletions

View File

@@ -28,10 +28,7 @@
"discord": { "discord": {
"enabled": false, "enabled": false,
"bottoken": "", "bottoken": "",
"realtimeChannelID": "", "realtimeChannelID": ""
"serverId": "",
"devRoles": [],
"devMode": false
}, },
"database": { "database": {
"host": "localhost", "host": "localhost",

View File

@@ -65,7 +65,7 @@ func main() {
var discordBot *discordbot.DiscordBot = nil var discordBot *discordbot.DiscordBot = nil
if erupeConfig.Discord.Enabled { if erupeConfig.Discord.Enabled {
bot, err := discordbot.NewDiscordBot(discordbot.DiscordBotOptions{ bot, err := discordbot.NewDiscordBot(discordbot.Options{
Logger: logger, Logger: logger,
Config: erupeConfig, Config: erupeConfig,
}) })
@@ -82,6 +82,7 @@ func main() {
} }
discordBot = bot discordBot = bot
logger.Info("Discord bot is enabled")
} else { } else {
logger.Info("Discord bot is disabled") logger.Info("Discord bot is disabled")
} }

View File

@@ -2,87 +2,45 @@ package channelserver
import ( import (
"fmt" "fmt"
"github.com/bwmarrin/discordgo"
"sort" "sort"
"strings" "strings"
"unicode"
"github.com/bwmarrin/discordgo"
) )
type Character struct { type Player struct {
ID uint32 `db:"id"` CharName string
IsFemale bool `db:"is_female"` QuestID int
IsNewCharacter bool `db:"is_new_character"`
Name string `db:"name"`
UnkDescString string `db:"unk_desc_string"`
HRP uint16 `db:"hrp"`
GR uint16 `db:"gr"`
WeaponType uint16 `db:"weapon_type"`
LastLogin uint32 `db:"last_login"`
} }
var weapons = []string{ func getPlayerSlice(s *Server) []Player {
"<:gs:970861408227049477>", var p []Player
"<:hbg:970861408281563206>", var questIndex int
"<:hm:970861408239628308>",
"<:lc:970861408298352660>",
"<:sns:970861408319315988>",
"<:lbg:970861408327725137>",
"<:ds:970861408277368883>",
"<:ls:970861408319311872>",
"<:hh:970861408222863400>",
"<:gl:970861408327720980>",
"<:bw:970861408294174780>",
"<:tf:970861408424177744>",
"<:sw:970861408340283472>",
"<:ms:970861408411594842>",
}
func (s *Server) getCharacterForUser(uid int) (*Character, error) { for _, channel := range s.Channels {
character := Character{} for _, stage := range channel.stages {
err := s.db.Get(&character, "SELECT id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login FROM characters WHERE id = $1", uid) if len(stage.clients) == 0 {
if err != nil { continue
return nil, err }
questID := 0
if stage.isQuest() {
questIndex++
questID = questIndex
}
for client := range stage.clients {
p = append(p, Player{
CharName: client.Name,
QuestID: questID,
})
}
}
} }
return p
return &character, nil
} }
func CountChars(s *Server) string { func getCharacterList(s *Server) string {
count := 0
for _, stage := range s.stages {
count += len(stage.clients)
}
message := fmt.Sprintf("Server [%s]: %d players;", s.name, count)
return message
}
type ListPlayer struct {
CharName string
InQuest bool
WeaponEmoji string
QuestEmoji string
StageName string
}
func (p *ListPlayer) toString(length int) string {
status := ""
if p.InQuest {
status = "(in quest " + p.QuestEmoji + ")"
} else {
status = p.StageName
}
missingSpace := length - len(p.CharName)
whitespaces := strings.Repeat(" ", missingSpace+5)
return fmt.Sprintf("%s %s %s %s", p.WeaponEmoji, p.CharName, whitespaces, status)
}
func getPlayerList(s *Server) ([]ListPlayer, int) {
list := []ListPlayer{}
questEmojis := []string{ questEmojis := []string{
":person_in_lotus_position:",
":white_circle:", ":white_circle:",
":red_circle:", ":red_circle:",
":blue_circle:", ":blue_circle:",
@@ -91,255 +49,41 @@ func getPlayerList(s *Server) ([]ListPlayer, int) {
":purple_circle:", ":purple_circle:",
":yellow_circle:", ":yellow_circle:",
":orange_circle:", ":orange_circle:",
":black_circle:",
} }
bigNameLen := 0 playerSlice := getPlayerSlice(s)
for _, stage := range s.stages { sort.SliceStable(playerSlice, func(i, j int) bool {
if len(stage.clients) == 0 { return playerSlice[i].QuestID < playerSlice[j].QuestID
continue
}
questEmoji := ":black_circle:"
if len(questEmojis) > 0 {
questEmoji = questEmojis[len(questEmojis)-1]
questEmojis = questEmojis[:len(questEmojis)-1]
}
isQuest := stage.isQuest()
for client := range stage.clients {
char, err := s.getCharacterForUser(int(client.charID))
if err == nil {
if len(char.Name) > bigNameLen {
bigNameLen = len(char.Name)
}
list = append(list, ListPlayer{
CharName: char.Name,
InQuest: isQuest,
QuestEmoji: questEmoji,
WeaponEmoji: weapons[char.WeaponType],
StageName: stage.GetName(),
})
}
}
}
return list, bigNameLen
}
func PlayerList(s *Server) string {
list := ""
count := 0
listPlayers, bigNameLen := getPlayerList(s)
sort.SliceStable(listPlayers, func(i, j int) bool {
return listPlayers[i].CharName < listPlayers[j].CharName
}) })
for _, lp := range listPlayers { message := fmt.Sprintf("===== Online: %d =====\n", len(playerSlice))
list += lp.toString(bigNameLen) + "\n" for _, player := range playerSlice {
count++ message += fmt.Sprintf("%s %s", questEmojis[player.QuestID], player.CharName)
} }
message := fmt.Sprintf("<:SnS:822963937360347148> Frontier Hunters Online: [%s ] <:switcha:822963906401533992> \n============== Total %d =============\n", s.name, count)
message += list
return message return message
} }
func debug(s *Server) string {
list := ""
for _, stage := range s.stages {
if !stage.isQuest() && len(stage.objects) == 0 {
continue
}
list += fmt.Sprintf(" -> Stage: %s StageId: %s\n", stage.GetName(), stage.id)
isQuest := "false"
hasDeparted := "false"
if stage.isQuest() {
isQuest = "true"
}
list += fmt.Sprintf(" '-> isQuest: %s\n", isQuest)
if stage.isQuest() {
if len(stage.clients) > 0 {
hasDeparted = "true"
}
list += fmt.Sprintf(" '-> isDeparted: %s\n", hasDeparted)
list += fmt.Sprintf(" '-> reserveSlots (%d/%d)\n", len(stage.reservedClientSlots), stage.maxPlayers)
for charid, _ := range stage.reservedClientSlots {
char, err := s.getCharacterForUser(int(charid))
if err == nil {
list += fmt.Sprintf(" '-> %s\n", char.Name)
}
}
}
list += " '-> objects: \n"
for _, obj := range stage.objects {
objInfo := fmt.Sprintf("X,Y,Z: %f %f %f", obj.x, obj.y, obj.z)
list += fmt.Sprintf(" '-> ObjectId: %d - %s\n", obj.id, objInfo)
}
}
message := fmt.Sprintf("Objects in Server: [%s ]\n", s.name)
message += list
return message
}
func questlist(s *Server) string {
list := ""
for _, stage := range s.stages {
if !stage.isQuest() {
continue
}
hasDeparted := ""
if len(stage.clients) > 0 {
hasDeparted = " - departed"
}
list += fmt.Sprintf(" '-> StageId: %s (%d/%d) %s - %s\n", stage.id, len(stage.reservedClientSlots), stage.maxPlayers, hasDeparted, stage.createdAt)
for charid, _ := range stage.reservedClientSlots {
char, err := s.getCharacterForUser(int(charid))
if err == nil {
list += fmt.Sprintf(" '-> %s\n", char.Name)
}
}
}
message := fmt.Sprintf("Quests in Server: [%s ]\n", s.name)
message += list
return message
}
func removeStageById(s *Server, stageId string) string {
if s.stages[stageId] != nil {
delete(s.stages, stageId)
return "Stage deleted!"
}
return "Stage not found!"
}
func cleanStr(str string) string {
return strings.ToLower(strings.Trim(str, " "))
}
func getCharInfo(server *Server, charName string) string {
var s *Stage
var c *Session
for _, stage := range server.stages {
for client := range stage.clients {
if client.Name == "" {
continue
}
if cleanStr(client.Name) == cleanStr(charName) {
s = stage
c = client
}
}
}
if s == nil {
return "Character not found"
}
objInfo := ""
obj := server.FindObjectByChar(c.charID)
// server.logger.Info("Found object: %+v", zap.Object("obj", obj))
if obj != nil {
objInfo = fmt.Sprintf("X,Y,Z: %f %f %f", obj.x, obj.y, obj.z)
}
return fmt.Sprintf("Character: %s\nStage: %s\nStageId: %s\n%s", c.Name, s.GetName(), s.id, objInfo)
}
func (s *Server) isDiscordAdmin(ds *discordgo.Session, m *discordgo.MessageCreate) bool {
for _, role := range m.Member.Roles {
for _, id := range s.erupeConfig.Discord.DevRoles {
if id == role {
return true
}
}
}
return false
}
// onDiscordMessage handles receiving messages from discord and forwarding them ingame. // onDiscordMessage handles receiving messages from discord and forwarding them ingame.
func (s *Server) onDiscordMessage(ds *discordgo.Session, m *discordgo.MessageCreate) { func (s *Server) onDiscordMessage(ds *discordgo.Session, m *discordgo.MessageCreate) {
// Ignore messages from our bot, or ones that are not in the correct channel. // Ignore messages from our bot, or ones that are not in the correct channel.
if m.Author.ID == ds.State.User.ID || !s.enable { if m.Author.Bot || m.ChannelID != s.erupeConfig.Discord.RealtimeChannelID {
return return
} }
// Ignore other channels in devMode paddedName := strings.TrimSpace(strings.Map(func(r rune) rune {
if s.erupeConfig.Discord.DevMode && m.ChannelID != s.erupeConfig.Discord.RealtimeChannelID { if r > unicode.MaxASCII {
return return -1
}
args := strings.Split(m.Content, " ")
commandName := args[0]
// Move to slash commadns
if commandName == "!players" {
ds.ChannelMessageSend(m.ChannelID, PlayerList(s))
return
}
if commandName == "-char" {
if len(args) < 2 {
ds.ChannelMessageSend(m.ChannelID, "Usage: !char <char name>")
return
} }
return r
}, m.Author.Username))
charName := strings.Join(args[1:], " ") for i := 0; i < 10-len(m.Author.Username); i++ {
ds.ChannelMessageSend(m.ChannelID, getCharInfo(s, charName)) paddedName += " "
return
} }
if commandName == "!debug" && s.isDiscordAdmin(ds, m) { message := fmt.Sprintf("[D] %s > %s", paddedName, m.Content)
ds.ChannelMessageSend(m.ChannelID, debug(s)) s.BroadcastChatMessage(s.discordBot.NormalizeDiscordMessage(message))
return
}
if commandName == "!questlist" && s.isDiscordAdmin(ds, m) {
ds.ChannelMessageSend(m.ChannelID, questlist(s))
return
}
if commandName == "!remove-stage" && s.isDiscordAdmin(ds, m) {
if len(args) < 2 {
ds.ChannelMessageSend(m.ChannelID, "Usage: !remove-stage <stage id>")
return
}
stageId := strings.Join(args[1:], " ")
ds.ChannelMessageSend(m.ChannelID, removeStageById(s, stageId))
return
}
if m.ChannelID == s.erupeConfig.Discord.RealtimeChannelID {
message := fmt.Sprintf("[DISCORD] %s: %s", m.Author.Username, m.Content)
s.BroadcastChatMessage(s.discordBot.NormalizeDiscordMessage(message))
}
} }

View File

@@ -17,21 +17,6 @@ import (
type StageIdType = string type StageIdType = string
const (
// GlobalStage is the stage that is used for all users.
MezeportaStageId StageIdType = "sl1Ns200p0a0u0"
GuildHallLv1StageId StageIdType = "sl1Ns202p0a0u0"
GuildHallLv2StageId StageIdType = "sl1Ns203p0a0u0"
GuildHallLv3StageId StageIdType = "sl1Ns204p0a0u0"
PugiFarmStageId StageIdType = "sl1Ns205p0a0u0"
RastaBarStageId StageIdType = "sl1Ns211p0a0u0"
PalloneCaravanStageId StageIdType = "sl1Ns260p0a0u0"
GookFarmStageId StageIdType = "sl1Ns265p0a0u0"
DivaFountainStageId StageIdType = "sl2Ns379p0a0u0"
DivaHallStageId StageIdType = "sl1Ns445p0a0u0"
MezFesStageId StageIdType = "sl1Ns462p0a0u0"
)
// Config struct allows configuring the server. // Config struct allows configuring the server.
type Config struct { type Config struct {
ID uint16 ID uint16
@@ -80,8 +65,7 @@ type Server struct {
// Discord chat integration // Discord chat integration
discordBot *discordbot.DiscordBot discordBot *discordbot.DiscordBot
name string name string
enable bool
raviente *Raviente raviente *Raviente
} }
@@ -157,7 +141,6 @@ func NewServer(config *Config) *Server {
semaphoreIndex: 7, semaphoreIndex: 7,
discordBot: config.DiscordBot, discordBot: config.DiscordBot,
name: config.Name, name: config.Name,
enable: config.Enable,
raviente: NewRaviente(), raviente: NewRaviente(),
} }
@@ -367,7 +350,7 @@ func (s *Server) BroadcastRaviente(ip uint32, port uint16, stage []byte, _type u
func (s *Server) DiscordChannelSend(charName string, content string) { func (s *Server) DiscordChannelSend(charName string, content string) {
if s.erupeConfig.Discord.Enabled && s.discordBot != nil { if s.erupeConfig.Discord.Enabled && s.discordBot != nil {
message := fmt.Sprintf("**%s** : %s", charName, content) message := fmt.Sprintf("**%s**: %s", charName, content)
s.discordBot.RealtimeChannelSend(message) s.discordBot.RealtimeChannelSend(message)
} }
} }

View File

@@ -98,35 +98,6 @@ func (s *Stage) isQuest() bool {
return len(s.reservedClientSlots) > 0 return len(s.reservedClientSlots) > 0
} }
func (s *Stage) GetName() string {
switch s.id {
case MezeportaStageId:
return "Mezeporta"
case GuildHallLv1StageId:
return "Guild Hall Lv1"
case GuildHallLv2StageId:
return "Guild Hall Lv2"
case GuildHallLv3StageId:
return "Guild Hall Lv3"
case PugiFarmStageId:
return "Pugi Farm"
case RastaBarStageId:
return "Rasta Bar"
case PalloneCaravanStageId:
return "Pallone Caravan"
case GookFarmStageId:
return "Gook Farm"
case DivaFountainStageId:
return "Diva Fountain"
case DivaHallStageId:
return "Diva Hall"
case MezFesStageId:
return "Mez Fes"
default:
return ""
}
}
func (s *Stage) NextObjectID() uint32 { func (s *Stage) NextObjectID() uint32 {
s.objectIndex = s.objectIndex + 1 s.objectIndex = s.objectIndex + 1
// Objects beyond 127 do not duplicate correctly // Objects beyond 127 do not duplicate correctly

View File

@@ -1,11 +1,10 @@
package discordbot package discordbot
import ( import (
"regexp"
"erupe-ce/config" "erupe-ce/config"
"github.com/bwmarrin/discordgo" "github.com/bwmarrin/discordgo"
"go.uber.org/zap" "go.uber.org/zap"
"regexp"
) )
type DiscordBot struct { type DiscordBot struct {
@@ -16,12 +15,12 @@ type DiscordBot struct {
RealtimeChannel *discordgo.Channel RealtimeChannel *discordgo.Channel
} }
type DiscordBotOptions struct { type Options struct {
Config *config.Config Config *config.Config
Logger *zap.Logger Logger *zap.Logger
} }
func NewDiscordBot(options DiscordBotOptions) (discordBot *DiscordBot, err error) { func NewDiscordBot(options Options) (discordBot *DiscordBot, err error) {
session, err := discordgo.New("Bot " + options.Config.Discord.BotToken) session, err := discordgo.New("Bot " + options.Config.Discord.BotToken)
if err != nil { if err != nil {
@@ -29,13 +28,6 @@ func NewDiscordBot(options DiscordBotOptions) (discordBot *DiscordBot, err error
return nil, err return nil, err
} }
mainGuild, err := session.Guild(options.Config.Discord.ServerID)
if err != nil {
options.Logger.Fatal("Discord failed to get main guild", zap.Error(err))
return nil, err
}
realtimeChannel, err := session.Channel(options.Config.Discord.RealtimeChannelID) realtimeChannel, err := session.Channel(options.Config.Discord.RealtimeChannelID)
if err != nil { if err != nil {
@@ -47,7 +39,6 @@ func NewDiscordBot(options DiscordBotOptions) (discordBot *DiscordBot, err error
config: options.Config, config: options.Config,
logger: options.Logger, logger: options.Logger,
Session: session, Session: session,
MainGuild: mainGuild,
RealtimeChannel: realtimeChannel, RealtimeChannel: realtimeChannel,
} }
@@ -60,21 +51,10 @@ func (bot *DiscordBot) Start() (err error) {
return return
} }
func (bot *DiscordBot) FindRoleByID(id string) *discordgo.Role {
for _, role := range bot.MainGuild.Roles {
if role.ID == id {
return role
}
}
return nil
}
// Replace all mentions to real name from the message. // Replace all mentions to real name from the message.
func (bot *DiscordBot) NormalizeDiscordMessage(message string) string { func (bot *DiscordBot) NormalizeDiscordMessage(message string) string {
userRegex := regexp.MustCompile(`<@!?(\d{17,19})>`) userRegex := regexp.MustCompile(`<@!?(\d{17,19})>`)
emojiRegex := regexp.MustCompile(`(?:<a?)?:(\w+):(?:\d{18}>)?`) emojiRegex := regexp.MustCompile(`(?:<a?)?:(\w+):(?:\d{18}>)?`)
roleRegex := regexp.MustCompile(`<@&(\d{17,19})>`)
result := ReplaceTextAll(message, userRegex, func(userId string) string { result := ReplaceTextAll(message, userRegex, func(userId string) string {
user, err := bot.Session.User(userId) user, err := bot.Session.User(userId)
@@ -90,17 +70,7 @@ func (bot *DiscordBot) NormalizeDiscordMessage(message string) string {
return ":" + emojiName + ":" return ":" + emojiName + ":"
}) })
result = ReplaceTextAll(result, roleRegex, func(roleId string) string { return result
role := bot.FindRoleByID(roleId)
if role != nil {
return "@!" + role.Name
}
return "@!unknown"
})
return string(result)
} }
func (bot *DiscordBot) RealtimeChannelSend(message string) (err error) { func (bot *DiscordBot) RealtimeChannelSend(message string) (err error) {