diff --git a/assets/allegiance/guardians.png b/assets/allegiance/guardians.png
new file mode 100644
index 0000000..128c05f
Binary files /dev/null and b/assets/allegiance/guardians.png differ
diff --git a/assets/allegiance/guardians.svg b/assets/allegiance/guardians.svg
deleted file mode 100644
index 0841307..0000000
--- a/assets/allegiance/guardians.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/assets/allegiance/servants.png b/assets/allegiance/servants.png
new file mode 100644
index 0000000..a3d13be
Binary files /dev/null and b/assets/allegiance/servants.png differ
diff --git a/assets/allegiance/servants.svg b/assets/allegiance/servants.svg
deleted file mode 100644
index 141e440..0000000
--- a/assets/allegiance/servants.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/bot/bot.go b/bot/bot.go
index 93c05b7..538cdd0 100644
--- a/bot/bot.go
+++ b/bot/bot.go
@@ -18,6 +18,7 @@ import (
// List of Sea of Thieves API endpoints
const (
APIURLSoTAchievements = "https://www.seaofthieves.com/api/profilev2/achievements"
+ APIURLSoTAllegiance = "https://www.seaofthieves.com/api/profilev2"
APIURLSoTSeasons = "https://www.seaofthieves.com/api/profilev2/seasons-progress"
APIURLSoTUserBalance = "https://www.seaofthieves.com/api/profilev2/balance"
APIURLSoTUserOverview = "https://www.seaofthieves.com/api/profilev2/overview"
@@ -31,6 +32,7 @@ const (
ErrFailedHTTPClient = "failed to generate new HTTP client: %s"
ErrFailedRetrieveUserStatsDB = "failed retrieve user status from DB: %s"
ErrFailedGuildLookupDB = "failed to look up guild in database: %s"
+ ErrFailedStringConvert = "failed to convert string to int: %s"
)
// Bot represents the bot instance
diff --git a/bot/sc_handler_sot_allegiance.go b/bot/sc_handler_sot_allegiance.go
new file mode 100644
index 0000000..a91ad45
--- /dev/null
+++ b/bot/sc_handler_sot_allegiance.go
@@ -0,0 +1,174 @@
+package bot
+
+import (
+ "encoding/json"
+ "fmt"
+ "regexp"
+ "strconv"
+ "strings"
+
+ "github.com/bwmarrin/discordgo"
+ "golang.org/x/text/language"
+ "golang.org/x/text/message"
+)
+
+// SoTAllegianceJSON is the nested struct from the Sea of Thieves event hub response
+type SoTAllegianceJSON struct {
+ Stats []struct {
+ Name string `json:"name"`
+ Value string `json:"value"`
+ } `json:"stats"`
+}
+
+// SoTAllegiance is the struct that represents the parsed data from the API endpoint
+type SoTAllegiance struct {
+ Allegiance string
+ ShipsSunk int64
+ MaxStreak int64
+ TotalGold int64
+}
+
+// SlashCmdSoTAllegiance handles the /allegiance slash command
+func (b *Bot) SlashCmdSoTAllegiance(s *discordgo.Session, i *discordgo.InteractionCreate) error {
+ eo := i.ApplicationCommandData().Options
+ if len(eo) <= 0 {
+ return fmt.Errorf("no option given")
+ }
+ rc, ok := eo[0].Value.(string)
+ if !ok {
+ return fmt.Errorf("provided option value is not a string")
+ }
+
+ re, err := regexp.Compile(`^(?i:guardians|servants)$`)
+ if err != nil {
+ return err
+ }
+ ala := re.FindStringSubmatch(rc)
+ if len(ala) != 1 {
+ return fmt.Errorf("failed to parse value string")
+ }
+ al := ala[0]
+
+ r, err := b.NewRequester(i.Interaction)
+ if err != nil {
+ return err
+ }
+
+ a, err := b.SoTGetAllegiance(r, al)
+ if err != nil {
+ return err
+ }
+
+ p := message.NewPrinter(language.German)
+ var ef []*discordgo.MessageEmbedField
+ ef = append(ef, &discordgo.MessageEmbedField{
+ Name: "Ships Sunk",
+ Value: fmt.Sprintf("%s **%d** Total", IconShip, a.ShipsSunk),
+ Inline: true,
+ })
+ ef = append(ef, &discordgo.MessageEmbedField{
+ Name: "Highest Streak",
+ Value: fmt.Sprintf("%s **%d** Ships", IconGauge, a.MaxStreak),
+ Inline: true,
+ })
+ ef = append(ef, &discordgo.MessageEmbedField{
+ Name: "Highest Hourglass Value",
+ Value: fmt.Sprintf("%s **%s** Gold", IconGold, p.Sprintf("%d", a.TotalGold)),
+ Inline: true,
+ })
+
+ e := []*discordgo.MessageEmbed{
+ {
+ Title: fmt.Sprintf("Your current allegiance values for the **%s**:", a.Allegiance),
+ Thumbnail: &discordgo.MessageEmbedThumbnail{
+ URL: fmt.Sprintf("%s/allegiance/%s.png", AssetsBaseURL, al),
+ },
+ Type: discordgo.EmbedTypeRich,
+ Fields: ef,
+ },
+ }
+ if _, err := s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{Embeds: &e}); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// SoTGetAllegiance returns the parsed API response from the Sea of Thieves allegiance API
+func (b *Bot) SoTGetAllegiance(rq *Requester, at string) (SoTAllegiance, error) {
+ var a SoTAllegiance
+ var al SoTAllegianceJSON
+ hc, err := NewHTTPClient()
+ if err != nil {
+ return a, fmt.Errorf(ErrFailedHTTPClient, err)
+ }
+ c, err := rq.GetSoTRATCookie()
+ if err != nil {
+ return a, err
+ }
+
+ urlfmt := "%s/%s"
+ var url string
+ switch strings.ToLower(at) {
+ case "guardians":
+ url = fmt.Sprintf(urlfmt, APIURLSoTAllegiance, "piratelord")
+ case "servants":
+ url = fmt.Sprintf(urlfmt, APIURLSoTAllegiance, "flameheart")
+ default:
+ return a, fmt.Errorf("unknown allegiance given")
+ }
+
+ r, err := hc.HTTPReq(url, ReqMethodGet, nil)
+ if err != nil {
+ return a, err
+ }
+ r.SetSOTRequest(c)
+ rd, _, err := hc.Fetch(r)
+ if err != nil {
+ return a, err
+ }
+
+ if err := json.Unmarshal(rd, &al); err != nil {
+ return a, err
+ }
+
+ switch strings.ToLower(at) {
+ case "guardians":
+ for _, d := range al.Stats {
+ v, err := strconv.ParseInt(d.Value, 10, 64)
+ if err != nil {
+ return a, fmt.Errorf(ErrFailedStringConvert, d.Value)
+ }
+ switch d.Name {
+ case "FactionG_Ships_Sunk":
+ a.ShipsSunk = v
+ case "PirateLord_MaxStreak":
+ a.MaxStreak = v
+ case "FactionG_SandsOfFate_TotalGold":
+ a.TotalGold = v
+ }
+ a.Allegiance = "Guardians of Fortune"
+ }
+ case "servants":
+ for _, d := range al.Stats {
+ if d.Value == "" {
+ d.Value = "0"
+ }
+ v, err := strconv.ParseInt(d.Value, 10, 64)
+ if err != nil {
+ return a, fmt.Errorf(ErrFailedStringConvert, d.Value)
+ }
+ switch d.Name {
+ case "FactionB_Ships_Sunk":
+ a.ShipsSunk = v
+ case "Flameheart_MaxStreak":
+ a.MaxStreak = v
+ case "FactionB_SandsOfFate_TotalGold":
+ a.TotalGold = v
+ }
+ }
+ a.Allegiance = "Servants of the Flame"
+ }
+
+ return a, nil
+}
diff --git a/bot/slashcmd.go b/bot/slashcmd.go
index 2bfb705..a0fb312 100644
--- a/bot/slashcmd.go
+++ b/bot/slashcmd.go
@@ -181,6 +181,47 @@ func (b *Bot) getSlashCommands() []*discordgo.ApplicationCommand {
},
},
},
+
+ /*
+ // reputation provides the current emissary reputation value in the different factions
+ {
+ Name: "reputation",
+ Description: "Returns your current reputation value in the different emissary factions",
+ Options: []*discordgo.ApplicationCommandOption{
+ {
+ Type: discordgo.ApplicationCommandOptionString,
+ Name: "emissary-faction",
+ Description: "Name of the emissary faction",
+ Required: true,
+ Choices: []*discordgo.ApplicationCommandOptionChoice{
+ {Name: "Athena's Fortune", Value: "athena"},
+ {Name: "Gold Hoarder", Value: "hoarder"},
+ {Name: "Merchant Alliance", Value: "merchant"},
+ {Name: "Order of Souls", Value: "order"},
+ {Name: "Reaper's Bone", Value: "reaper"},
+ },
+ },
+ },
+ },
+ */
+
+ // allegiance provides the current allegiance values in the different factions
+ {
+ Name: "allegiance",
+ Description: "Returns your current allegiance values in the different allegiance factions",
+ Options: []*discordgo.ApplicationCommandOption{
+ {
+ Type: discordgo.ApplicationCommandOptionString,
+ Name: "allegiance-faction",
+ Description: "Name of the allegiance faction",
+ Required: true,
+ Choices: []*discordgo.ApplicationCommandOptionChoice{
+ {Name: "Servants of the Flame", Value: "servants"},
+ {Name: "Guardians of Fortune", Value: "guardians"},
+ },
+ },
+ },
+ },
}
}
@@ -322,6 +363,7 @@ func (b *Bot) SlashCommandHandler(s *discordgo.Session, i *discordgo.Interaction
"compare": b.SlashCmdSoTCompare,
"dailydeeds": b.SlashCmdSoTDailyDeeds,
"ledger": b.SlashCmdSoTLedger,
+ "allegiance": b.SlashCmdSoTAllegiance,
}
// Define list of slash commands that should use ephemeral messages
diff --git a/bot/version.go b/bot/version.go
index 1ae3c27..0a67788 100644
--- a/bot/version.go
+++ b/bot/version.go
@@ -1,3 +1,3 @@
package bot
-const Version = "0.2.4"
+const Version = "0.2.5"