Files
hal/handlers/message_created.go
T
2026-01-22 10:25:35 +01:00

197 lines
3.7 KiB
Go

package handlers
import (
"fmt"
"log"
"math/rand"
"regexp"
"time"
"hal/env"
"hal/gemini"
"hal/llm"
"hal/openai"
"github.com/bwmarrin/discordgo"
"google.golang.org/genai"
)
const MAX_HISTORY = 50
const DISCORD_MAX_CHAR = 1500
const (
SPAM_PERIOD = 5 * time.Minute
MESSAGES_PER_PERIOD = 10
)
var spams = []string{
"Arrête de spam putain!",
"Wesh...",
"Flemme.",
"Laisse-moi tranquille!",
"Fdr",
}
// messagesHistory []*openai.ChatMessage = []*openai.ChatMessage{}
var (
geminiHistory []*genai.Content = []*genai.Content{}
llmHistory []string = []string{}
)
var usersHistoryCount map[string]*userHistoryCount = map[string]*userHistoryCount{}
type userHistoryCount struct {
date time.Time
bannedUntilAt time.Time
count int
}
type Handler struct {
openaiClient *openai.Client
geminiClient *gemini.Client
llmClient *llm.Client
}
func Init(env *env.Env) Handler {
return Handler{
openaiClient: openai.NewClient(env.OpenaiHalToken),
geminiClient: gemini.NewClient(env.GeminiToken),
llmClient: llm.NewClient(env),
}
}
func (h Handler) OnMessageCreated(s *discordgo.Session, m *discordgo.MessageCreate) {
isHal := m.Author.ID == s.State.User.ID
// addMessageToHistory(m.Message, isHal)
if isHal || !containHal(m.Mentions, s.State.User.ID) {
return
}
userSpamTooMuch := updateUserHistoryCount(m.Author.ID)
if userSpamTooMuch {
sendResponse(s, m.ChannelID, getRandomSpam())
return
}
llmRes, err := h.llmClient.GenerateContent(cleanMessage(m.Content))
if err != nil {
sendResponse(
s,
m.ChannelID,
"X_X: Le serveur est mort...",
)
log.Printf("\nfailed to query the llm with the following prompt [%s]. Error: %s", m.Content, err.Error())
return
}
if llmRes == "" {
sendResponse(s, m.ChannelID, "X_X: Réponse vide de Hal...")
return
}
sendResponse(s, m.ChannelID, llmRes)
}
// func addMessageToHistory(m *discordgo.Message, isHal bool) []*genai.Content {
// message := cleanMessage(m.Content)
// if message == "" {
// return geminiHistory
// }
//
// var role string
// if isHal {
// role = "model"
// } else {
// role = "user"
// }
//
// geminiHistory = append(geminiHistory, &genai.Content{
// Role: role,
// Parts: []*genai.Part{
// genai.NewPartFromText(message),
// },
// })
//
// if len(geminiHistory) > MAX_HISTORY {
// geminiHistory = geminiHistory[1:]
// }
//
// return geminiHistory
// }
func cleanMessage(p string) string {
regex := regexp.MustCompile(`<@\d+>`)
return regex.ReplaceAllString(p, "")
}
func sendResponse(s *discordgo.Session, channelID string, response string) {
if _, err := s.ChannelMessageSend(channelID, truncateIfNeeded(response)); err != nil {
log.Printf("\nfailed to send the response [%s] to the discord channel [%s]", response, err.Error())
}
}
func truncateIfNeeded(response string) string {
if len(response) < DISCORD_MAX_CHAR {
return response
}
return fmt.Sprintf("%s %s", response[:DISCORD_MAX_CHAR-10], "[...]")
}
func containHal(users []*discordgo.User, userID string) bool {
for _, u := range users {
if u.ID == userID {
return true
}
}
return false
}
func updateUserHistoryCount(userID string) bool {
now := time.Now()
u, ok := usersHistoryCount[userID]
if !ok {
usersHistoryCount[userID] = &userHistoryCount{
date: now,
count: 1,
}
return false
}
u.count++
if now.Before(u.bannedUntilAt) {
return true
}
if !u.bannedUntilAt.IsZero() {
u.bannedUntilAt = time.Time{}
u.count = 1
}
if u.date.Add(SPAM_PERIOD).Before(now) {
u.count = 1
u.date = now
return false
}
if u.count > MESSAGES_PER_PERIOD {
u.bannedUntilAt = now.Add(30 * time.Minute)
return true
}
return false
}
func getRandomSpam() string {
return spams[rand.Intn(len(spams))]
}