aboutsummaryrefslogtreecommitdiff
path: root/xp/events.go
blob: 3ce4f064c55a878a4747a377720c757ab318ef6d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package xp

import (
	"context"
	"errors"
	"fmt"
	"github.com/anhgelus/gokord"
	"github.com/anhgelus/gokord/utils"
	"github.com/bwmarrin/discordgo"
	"github.com/redis/go-redis/v9"
	"slices"
	"strconv"
	"strings"
	"time"
)

const (
	ConnectedSince = "connected_since"
	NotConnected   = -1
	MaxTimeInVocal = 60 * 60 * 6
)

func OnMessage(s *discordgo.Session, m *discordgo.MessageCreate) {
	c := GetCopaing(m.Author.ID, m.GuildID)
	// add xp
	trimmed := utils.TrimMessage(strings.ToLower(m.Content))
	c.AddXP(s, XPMessage(uint(len(trimmed)), calcDiversity(trimmed)), func(_ uint, _ uint) {
		if err := s.MessageReactionAdd(m.ChannelID, m.Message.ID, "⬆"); err != nil {
			utils.SendAlert("xp/events.go - reaction add new level", "cannot add the reaction: "+err.Error())
		}
	})
}

func calcDiversity(msg string) uint {
	var chars []rune
	for _, c := range []rune(msg) {
		if !slices.Contains(chars, c) {
			chars = append(chars, c)
		}
	}
	return uint(len(chars))
}

func OnVoiceUpdate(s *discordgo.Session, e *discordgo.VoiceStateUpdate) {
	if e.Member.User.Bot {
		return
	}
	client, err := getRedisClient()
	if err != nil {
		utils.SendAlert("xp/events.go - Getting redis client", err.Error())
		return
	}
	if e.BeforeUpdate == nil {
		onConnection(s, e, client)
	} else {
		onDisconnect(s, e, client)
	}
}

func onConnection(_ *discordgo.Session, e *discordgo.VoiceStateUpdate, client *redis.Client) {
	u := gokord.UserBase{DiscordID: e.UserID, GuildID: e.GuildID}
	err := client.Set(
		context.Background(),
		fmt.Sprintf("%s:%s", u.GenKey(), ConnectedSince),
		strconv.FormatInt(time.Now().Unix(), 10),
		0,
	).Err()
	if err != nil {
		utils.SendAlert("xp/events.go - Setting connected_since", err.Error())
	}
}

func onDisconnect(s *discordgo.Session, e *discordgo.VoiceStateUpdate, client *redis.Client) {
	now := time.Now().Unix()
	u := gokord.UserBase{DiscordID: e.UserID, GuildID: e.GuildID}
	key := fmt.Sprintf("%s:%s", u.GenKey(), ConnectedSince)
	res := client.Get(context.Background(), key)
	// check validity of user (1)
	if errors.Is(res.Err(), redis.Nil) {
		utils.SendWarn(fmt.Sprintf(
			"User %s diconnect from a vocal but does not have a connected_since",
			e.Member.DisplayName(),
		))
		return
	}
	if res.Err() != nil {
		utils.SendAlert("xp/events.go - Getting connected_since", res.Err().Error())
		err := client.Set(context.Background(), key, strconv.Itoa(NotConnected), 0).Err()
		if err != nil {
			utils.SendAlert("xp/events.go - Set connected_since to not connected after get err", err.Error())
		}
		return
	}
	con, err := res.Int64()
	if err != nil {
		utils.SendAlert("xp/events.go - Converting result to int64", err.Error())
		return
	}
	// check validity of user (2)
	if con == NotConnected {
		utils.SendWarn(fmt.Sprintf(
			"User %s diconnect from a vocal but was registered as not connected",
			e.Member.DisplayName(),
		))
		return
	}
	err = client.Set(context.Background(), key, strconv.Itoa(NotConnected), 0).Err()
	if err != nil {
		utils.SendAlert("xp/events.go - Set connected_since to not connected", err.Error())
	}
	// add xp
	timeInVocal := now - con
	if timeInVocal < 0 {
		utils.SendAlert("xp/events.go - Calculating time spent in vocal", "the time is negative")
		return
	}
	if timeInVocal > MaxTimeInVocal {
		utils.SendWarn(fmt.Sprintf("User %s spent more than 6 hours in vocal", e.Member.DisplayName()))
		timeInVocal = MaxTimeInVocal
	}
	c := GetCopaing(u.DiscordID, u.GuildID)
	c.AddXP(s, XPVocal(uint(timeInVocal)), func(_ uint, _ uint) {
		//TODO: handle new level in vocal
	})
}