-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #35 from wneessen/feature/34_reputation-slashcommand
Feature/34 reputation slashcommand
- Loading branch information
Showing
21 changed files
with
434 additions
and
32 deletions.
There are no files selected for viewing
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
package bot | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"net/http" | ||
"regexp" | ||
"time" | ||
|
||
"github.com/wneessen/arrgo/crypto" | ||
"github.com/wneessen/arrgo/model" | ||
|
||
"github.com/bwmarrin/discordgo" | ||
) | ||
|
||
// SoTReputation represents first level of the JSON structure of the Sea of Thieves reputation | ||
// within a season API response | ||
type SoTReputation map[string]SoTFactionReputation | ||
|
||
// SoTFactionReputation represents second level of the JSON structure of the Sea of Thieves reputation | ||
// within a season API response | ||
type SoTFactionReputation struct { | ||
Name string | ||
Motto string `json:"Motto"` | ||
Rank string `json:"Rank"` | ||
Level int64 `json:"Level"` | ||
Experience int64 `json:"XP"` | ||
NextCompanyLevel SoTFactionNextLevel `json:"NextCompanyLevel"` | ||
TitlesTotal int64 `json:"TitlesTotal"` | ||
TitlesUnlocked int64 `json:"TitlesUnlocked"` | ||
EmblemsTotal int64 `json:"EmblemsTotal"` | ||
EmblemsUnlocked int64 `json:"EmblemsUnlocked"` | ||
ItemsTotal int64 `json:"ItemsTotal"` | ||
ItemsUnlocked int64 `json:"ItemsUnlocked"` | ||
} | ||
|
||
// SoTFactionNextLevel represents XP level information of the JSON structure of the Sea of Thieves reputation | ||
// within a season API response | ||
type SoTFactionNextLevel struct { | ||
Level int64 `json:"Level"` | ||
XPRequired int64 `json:"XpRequiredToAttain"` | ||
} | ||
|
||
// SlashCmdSoTReputation handles the /reputation slash command | ||
func (b *Bot) SlashCmdSoTReputation(s *discordgo.Session, i *discordgo.InteractionCreate) error { | ||
fo := i.ApplicationCommandData().Options | ||
if len(fo) <= 0 { | ||
return fmt.Errorf("no option given") | ||
} | ||
rc, ok := fo[0].Value.(string) | ||
if !ok { | ||
return fmt.Errorf("provided option value is not a string") | ||
} | ||
|
||
re, err := regexp.Compile(`^(?i:factiong|hunterscall|merchantalliance|bilgerats|athenasfortune|` + | ||
`goldhoarders|orderofsouls|reapersbones|factionb)$`) | ||
if err != nil { | ||
return err | ||
} | ||
faa := re.FindStringSubmatch(rc) | ||
if len(faa) != 1 { | ||
return fmt.Errorf("failed to parse value string") | ||
} | ||
fa := faa[0] | ||
_ = fa | ||
|
||
r, err := b.NewRequester(i.Interaction) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if err := b.StoreSoTUserReputation(r.User); err != nil { | ||
b.Log.Warn().Msgf("failed to store user reputation data to database") | ||
} | ||
ur, err := b.Model.UserReputation.GetByUserID(r.User.ID, fa) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
var ef []*discordgo.MessageEmbedField | ||
ef = append(ef, &discordgo.MessageEmbedField{ | ||
Name: "Motto", | ||
Value: ur.Motto, | ||
Inline: false, | ||
}) | ||
if ur.Rank != "" { | ||
ef = append(ef, &discordgo.MessageEmbedField{ | ||
Name: "Rank", | ||
Value: ur.Rank, | ||
Inline: false, | ||
}) | ||
} | ||
ef = append(ef, &discordgo.MessageEmbedField{ | ||
Name: "Level", | ||
Value: fmt.Sprintf("%s **%d**", IconGauge, ur.Level), | ||
Inline: true, | ||
}) | ||
ef = append(ef, &discordgo.MessageEmbedField{ | ||
Name: "XP in current level", | ||
Value: fmt.Sprintf("%s **%d/%d**", IconIncrease, ur.Experience, ur.ExperienceNextLevel), | ||
Inline: true, | ||
}) | ||
|
||
e := []*discordgo.MessageEmbed{ | ||
{ | ||
Title: fmt.Sprintf("Your user reputation with **%s**", dbEmissaryToName(ur.Emissary)), | ||
Thumbnail: &discordgo.MessageEmbedThumbnail{ | ||
URL: fmt.Sprintf("%s/factions/%s.png", AssetsBaseURL, fa), | ||
}, | ||
Type: discordgo.EmbedTypeRich, | ||
Fields: ef, | ||
}, | ||
} | ||
if _, err := s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{Embeds: &e}); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// SoTGetReputation returns the parsed API response from the Sea of Thieves reputation API | ||
func (b *Bot) SoTGetReputation(rq *Requester) (SoTReputation, error) { | ||
var re SoTReputation | ||
hc, err := NewHTTPClient() | ||
if err != nil { | ||
return re, fmt.Errorf(ErrFailedHTTPClient, err) | ||
} | ||
c, err := rq.GetSoTRATCookie() | ||
if err != nil { | ||
return re, err | ||
} | ||
r, err := hc.HTTPReq(APIURLSoTReputation, ReqMethodGet, nil) | ||
if err != nil { | ||
return re, err | ||
} | ||
r.SetSOTRequest(c) | ||
rd, ho, err := hc.Fetch(r) | ||
if err != nil { | ||
return re, err | ||
} | ||
if ho.StatusCode == http.StatusUnauthorized { | ||
return re, ErrSOTUnauth | ||
} | ||
if err := json.Unmarshal(rd, &re); err != nil { | ||
return re, err | ||
} | ||
return re, nil | ||
} | ||
|
||
// StoreSoTUserReputation will retrieve the latest user reputation from the API and store them in the DB | ||
func (b *Bot) StoreSoTUserReputation(u *model.User) error { | ||
r, err := NewRequesterFromUser(u, b.Model.User) | ||
if err != nil { | ||
b.Log.Warn().Msgf("failed to create new requester: %s", err) | ||
return err | ||
} | ||
ur, err := b.SoTGetReputation(r) | ||
if err != nil { | ||
switch { | ||
case errors.Is(err, ErrSOTUnauth): | ||
b.Log.Warn().Msgf("failed to fetch user reputation - RAT token is expired") | ||
return nil | ||
default: | ||
return fmt.Errorf("failed to fetch user reputation for user %s: %w", u.UserID, err) | ||
} | ||
} | ||
for k, rep := range ur { | ||
dur := &model.UserReputation{ | ||
UserID: u.ID, | ||
Emissary: k, | ||
Motto: rep.Motto, | ||
Rank: rep.Rank, | ||
Level: rep.Level, | ||
Experience: rep.Experience, | ||
NextLevel: rep.NextCompanyLevel.Level, | ||
ExperienceNextLevel: rep.NextCompanyLevel.XPRequired, | ||
TitlesTotal: rep.TitlesTotal, | ||
TitlesUnlocked: rep.TitlesUnlocked, | ||
EmblemsTotal: rep.EmblemsTotal, | ||
EmblemsUnlocked: rep.EmblemsUnlocked, | ||
ItemsTotal: rep.ItemsTotal, | ||
ItemsUnlocked: rep.ItemsUnlocked, | ||
} | ||
if err := b.Model.UserReputation.Insert(dur); err != nil { | ||
return fmt.Errorf("failed to store user reputation for user %q in DB: %w", u.UserID, err) | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// ScheduledEventUpdateUserReputation performs scheuled updates of the SoT user reputation for each user | ||
func (b *Bot) ScheduledEventUpdateUserReputation() error { | ||
ll := b.Log.With().Str("context", "bot.ScheduledEventUpdateUserReputation").Logger() | ||
ul, err := b.Model.User.GetUsers() | ||
if err != nil { | ||
return fmt.Errorf("failed to retrieve user list from DB: %w", err) | ||
} | ||
for _, u := range ul { | ||
if err := b.StoreSoTUserReputation(u); err != nil { | ||
ll.Error().Msgf("failed to store user reputation in DB: %s", err) | ||
continue | ||
} | ||
rd, err := crypto.RandDuration(10, "s") | ||
if err != nil { | ||
rd = time.Second * 10 | ||
} | ||
time.Sleep(rd) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
package bot | ||
|
||
const Version = "0.2.7" | ||
const Version = "0.2.8" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.