diff --git a/config.json b/config.json index 29af1fb8d..67087d28c 100644 --- a/config.json +++ b/config.json @@ -28,10 +28,7 @@ "discord": { "enabled": false, "bottoken": "", - "realtimeChannelID": "", - "serverId": "", - "devRoles": [], - "devMode": false + "realtimeChannelID": "" }, "database": { "host": "localhost", diff --git a/main.go b/main.go index c3c0162e0..2bc598a06 100644 --- a/main.go +++ b/main.go @@ -65,7 +65,7 @@ func main() { var discordBot *discordbot.DiscordBot = nil if erupeConfig.Discord.Enabled { - bot, err := discordbot.NewDiscordBot(discordbot.DiscordBotOptions{ + bot, err := discordbot.NewDiscordBot(discordbot.Options{ Logger: logger, Config: erupeConfig, }) @@ -82,6 +82,7 @@ func main() { } discordBot = bot + logger.Info("Discord bot is enabled") } else { logger.Info("Discord bot is disabled") } diff --git a/server/channelserver/handlers_discord.go b/server/channelserver/handlers_discord.go index 38619e079..f9290908b 100644 --- a/server/channelserver/handlers_discord.go +++ b/server/channelserver/handlers_discord.go @@ -2,87 +2,45 @@ package channelserver import ( "fmt" + "github.com/bwmarrin/discordgo" "sort" "strings" - - "github.com/bwmarrin/discordgo" + "unicode" ) -type Character struct { - ID uint32 `db:"id"` - IsFemale bool `db:"is_female"` - 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"` +type Player struct { + CharName string + QuestID int } -var weapons = []string{ - "<:gs:970861408227049477>", - "<:hbg:970861408281563206>", - "<: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 getPlayerSlice(s *Server) []Player { + var p []Player + var questIndex int -func (s *Server) getCharacterForUser(uid int) (*Character, error) { - character := Character{} - 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 err != nil { - return nil, err + for _, channel := range s.Channels { + for _, stage := range channel.stages { + if len(stage.clients) == 0 { + continue + } + questID := 0 + if stage.isQuest() { + questIndex++ + questID = questIndex + } + for client := range stage.clients { + p = append(p, Player{ + CharName: client.Name, + QuestID: questID, + }) + } + } } - - return &character, nil + return p } -func CountChars(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{} +func getCharacterList(s *Server) string { questEmojis := []string{ + ":person_in_lotus_position:", ":white_circle:", ":red_circle:", ":blue_circle:", @@ -91,255 +49,41 @@ func getPlayerList(s *Server) ([]ListPlayer, int) { ":purple_circle:", ":yellow_circle:", ":orange_circle:", + ":black_circle:", } - bigNameLen := 0 + playerSlice := getPlayerSlice(s) - for _, stage := range s.stages { - if len(stage.clients) == 0 { - 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 + sort.SliceStable(playerSlice, func(i, j int) bool { + return playerSlice[i].QuestID < playerSlice[j].QuestID }) - for _, lp := range listPlayers { - list += lp.toString(bigNameLen) + "\n" - count++ + message := fmt.Sprintf("===== Online: %d =====\n", len(playerSlice)) + for _, player := range playerSlice { + 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 } -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. func (s *Server) onDiscordMessage(ds *discordgo.Session, m *discordgo.MessageCreate) { // 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 } - // Ignore other channels in devMode - if s.erupeConfig.Discord.DevMode && m.ChannelID != s.erupeConfig.Discord.RealtimeChannelID { - return - } - - 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 ") - return + paddedName := strings.TrimSpace(strings.Map(func(r rune) rune { + if r > unicode.MaxASCII { + return -1 } + return r + }, m.Author.Username)) - charName := strings.Join(args[1:], " ") - ds.ChannelMessageSend(m.ChannelID, getCharInfo(s, charName)) - return + for i := 0; i < 10-len(m.Author.Username); i++ { + paddedName += " " } - if commandName == "!debug" && s.isDiscordAdmin(ds, m) { - ds.ChannelMessageSend(m.ChannelID, debug(s)) - 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 ") - 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)) - } + message := fmt.Sprintf("[D] %s > %s", paddedName, m.Content) + s.BroadcastChatMessage(s.discordBot.NormalizeDiscordMessage(message)) } diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index ca9063baf..8702d056a 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -17,21 +17,6 @@ import ( 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. type Config struct { ID uint16 @@ -80,8 +65,7 @@ type Server struct { // Discord chat integration discordBot *discordbot.DiscordBot - name string - enable bool + name string raviente *Raviente } @@ -157,7 +141,6 @@ func NewServer(config *Config) *Server { semaphoreIndex: 7, discordBot: config.DiscordBot, name: config.Name, - enable: config.Enable, 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) { 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) } } diff --git a/server/channelserver/sys_stage.go b/server/channelserver/sys_stage.go index 9a968bf3f..827b2d6be 100644 --- a/server/channelserver/sys_stage.go +++ b/server/channelserver/sys_stage.go @@ -98,35 +98,6 @@ func (s *Stage) isQuest() bool { 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 { s.objectIndex = s.objectIndex + 1 // Objects beyond 127 do not duplicate correctly diff --git a/server/discordbot/discord_bot.go b/server/discordbot/discord_bot.go index df817d50a..fc5c41ce8 100644 --- a/server/discordbot/discord_bot.go +++ b/server/discordbot/discord_bot.go @@ -1,11 +1,10 @@ package discordbot import ( - "regexp" - "erupe-ce/config" "github.com/bwmarrin/discordgo" "go.uber.org/zap" + "regexp" ) type DiscordBot struct { @@ -16,12 +15,12 @@ type DiscordBot struct { RealtimeChannel *discordgo.Channel } -type DiscordBotOptions struct { +type Options struct { Config *config.Config 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) if err != nil { @@ -29,13 +28,6 @@ func NewDiscordBot(options DiscordBotOptions) (discordBot *DiscordBot, err error 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) if err != nil { @@ -47,7 +39,6 @@ func NewDiscordBot(options DiscordBotOptions) (discordBot *DiscordBot, err error config: options.Config, logger: options.Logger, Session: session, - MainGuild: mainGuild, RealtimeChannel: realtimeChannel, } @@ -60,21 +51,10 @@ func (bot *DiscordBot) Start() (err error) { 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. func (bot *DiscordBot) NormalizeDiscordMessage(message string) string { userRegex := regexp.MustCompile(`<@!?(\d{17,19})>`) emojiRegex := regexp.MustCompile(`(?:)?`) - roleRegex := regexp.MustCompile(`<@&(\d{17,19})>`) result := ReplaceTextAll(message, userRegex, func(userId string) string { user, err := bot.Session.User(userId) @@ -90,17 +70,7 @@ func (bot *DiscordBot) NormalizeDiscordMessage(message string) string { return ":" + emojiName + ":" }) - result = ReplaceTextAll(result, roleRegex, func(roleId string) string { - role := bot.FindRoleByID(roleId) - - if role != nil { - return "@!" + role.Name - } - - return "@!unknown" - }) - - return string(result) + return result } func (bot *DiscordBot) RealtimeChannelSend(message string) (err error) {