Rename /ping to /info

Related to #17

Renames the `/ping` command to `/info` and adds the following
information to the Discord response

* Uptime of the server
* Total number of claims currently held
* Total number of unique players holding claims
absences
William Perron 2 years ago committed by GitHub
parent fb25f21472
commit c454b92d41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -76,8 +76,8 @@ func main() {
commands := []*discordgo.ApplicationCommand{ commands := []*discordgo.ApplicationCommand{
{ {
Name: "ping", Name: "info",
Description: "Ping Themis", Description: "Server Information",
Type: discordgo.ChatApplicationCommand, Type: discordgo.ChatApplicationCommand,
}, },
{ {
@ -151,11 +151,39 @@ func main() {
}, },
} }
handlers := map[string]Handler{ handlers := map[string]Handler{
"ping": func(s *discordgo.Session, i *discordgo.InteractionCreate) { "info": func(s *discordgo.Session, i *discordgo.InteractionCreate) {
uptime, err := themis.Uptime()
if err != nil {
log.Error().Err(err).Msg("failed to get server uptime")
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: "Oops, something went wrong! :(",
},
})
if err != nil {
log.Error().Err(err).Msg("failed to respond to interaction")
}
}
claimCount, uniquePlayers, err := store.CountClaims(ctx)
if err != nil {
log.Error().Err(err).Msg("failed to count claims")
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource, Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{ Data: &discordgo.InteractionResponseData{
Content: "Pong", Content: "Oops, something went wrong! :(",
},
})
if err != nil {
log.Error().Err(err).Msg("failed to respond to interaction")
}
}
err = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: fmt.Sprintf("Server has been up for %s, has %d claims from %d unique players", uptime, claimCount, uniquePlayers),
}, },
}) })
if err != nil { if err != nil {

@ -237,6 +237,21 @@ func (s *Store) DeleteClaim(ctx context.Context, ID int, userId string) error {
return nil return nil
} }
func (s *Store) CountClaims(ctx context.Context) (total, uniquePlayers int, err error) {
stmt, err := s.db.PrepareContext(ctx, "SELECT COUNT(1), COUNT(DISTINCT(userid)) FROM claims")
if err != nil {
return 0, 0, fmt.Errorf("failed to prepare query: %w", err)
}
res := stmt.QueryRowContext(ctx)
if err := res.Scan(&total, &uniquePlayers); err != nil {
return 0, 0, fmt.Errorf("failed to scan result: %w", err)
}
return total, uniquePlayers, nil
}
func (s *Store) Flush(ctx context.Context) error { func (s *Store) Flush(ctx context.Context) error {
_, err := s.db.ExecContext(ctx, "DELETE FROM claims;") _, err := s.db.ExecContext(ctx, "DELETE FROM claims;")
if err != nil { if err != nil {

@ -185,6 +185,22 @@ func TestDescribeClaim(t *testing.T) {
assert.ErrorIs(t, err, ErrNoSuchClaim) assert.ErrorIs(t, err, ErrNoSuchClaim)
} }
func TestCountClaims(t *testing.T) {
store, err := NewStore(fmt.Sprintf(TEST_CONN_STRING_PATTERN, "TestFlush"))
assert.NoError(t, err)
store.Claim(context.TODO(), "000000000000000001", "foo", "Genoa", CLAIM_TYPE_TRADE)
store.Claim(context.TODO(), "000000000000000001", "foo", "Valencia", CLAIM_TYPE_TRADE)
store.Claim(context.TODO(), "000000000000000001", "foo", "Italy", CLAIM_TYPE_REGION)
store.Claim(context.TODO(), "000000000000000001", "foo", "Iberia", CLAIM_TYPE_REGION)
store.Claim(context.TODO(), "000000000000000001", "foo", "Ragusa", CLAIM_TYPE_TRADE)
total, uniquePlayers, err := store.CountClaims(context.TODO())
assert.NoError(t, err)
assert.Condition(t, func() bool { return total > 0 })
assert.Condition(t, func() bool { return uniquePlayers > 0 })
}
func TestFlush(t *testing.T) { func TestFlush(t *testing.T) {
store, err := NewStore(fmt.Sprintf(TEST_CONN_STRING_PATTERN, "TestFlush")) store, err := NewStore(fmt.Sprintf(TEST_CONN_STRING_PATTERN, "TestFlush"))
assert.NoError(t, err) assert.NoError(t, err)

@ -0,0 +1,26 @@
package themis
import (
"bytes"
"fmt"
"os"
"strconv"
"time"
)
// Uptime returns the time elapsed since the start of the current process ID.
func Uptime() (time.Duration, error) {
raw, err := os.ReadFile("/proc/uptime")
if err != nil {
return 0, fmt.Errorf("failed to read uptime from OS: %w", err)
}
i := bytes.IndexRune(raw, ' ')
up, err := strconv.ParseFloat(string(raw[:i]), 64)
if err != nil {
return 0, fmt.Errorf("failed to parse uptime from OS: %w", err)
}
return time.Duration(int(up*1000) * int(time.Millisecond)), nil
}

@ -0,0 +1,14 @@
package themis
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestUptime(t *testing.T) {
uptime, err := Uptime()
assert.NoError(t, err)
assert.Greater(t, uptime, 100*time.Millisecond)
}
Loading…
Cancel
Save