Files
FernandoJVideira 035e383db2 feat: implement server logging functionality
- Added a logging service to manage server event logs including message edits, deletions, member joins/leaves, and moderation actions.
- Introduced commands to configure logging settings, including setting the log channel and enabling/disabling logging.
- Updated the README to document the new logging features and commands.
- Enhanced moderation commands to log actions taken on members.
2026-03-18 13:18:12 +00:00

164 lines
4.3 KiB
Go

package events
import (
"context"
"fmt"
"log"
"strconv"
"strings"
"time"
"velox-bot/internal/db/services"
"github.com/bwmarrin/discordgo"
)
// HandleBotAddedOnGuild applies safe defaults when this bot is added to a guild
// and DMs setup instructions to the inviter (or guild owner fallback).
func HandleBotAddedOnGuild(s *discordgo.Session, m *discordgo.GuildMemberAdd, svc *services.Services) {
if s == nil || m == nil || m.Member == nil || m.User == nil || svc == nil {
return
}
if s.State == nil || s.State.User == nil {
return
}
if m.User.ID != s.State.User.ID {
return
}
guildID64, err := strconv.ParseInt(m.GuildID, 10, 64)
if err != nil {
return
}
// Treat an existing logsettings row as "already onboarded once".
alreadyOnboarded := false
if svc.LogSettings != nil {
exists, err := svc.LogSettings.HasSettings(context.Background(), guildID64)
if err == nil {
alreadyOnboarded = exists
}
}
if alreadyOnboarded {
return
}
// Explicitly disable toggleable systems by default.
if svc.LevelSettings != nil {
if err := svc.LevelSettings.SetLevelSystemEnabled(context.Background(), guildID64, false); err != nil {
log.Printf("onboarding: failed to set default level state for guild %s: %v", m.GuildID, err)
}
}
if svc.LogSettings != nil {
if err := svc.LogSettings.SetEnabled(context.Background(), guildID64, false); err != nil {
log.Printf("onboarding: failed to set default log state for guild %s: %v", m.GuildID, err)
}
}
recipientID := findRecentBotAdderID(s, m.GuildID)
if recipientID == "" {
recipientID = guildOwnerID(s, m.GuildID)
}
if recipientID == "" {
log.Printf("onboarding: unable to resolve DM recipient for guild %s", m.GuildID)
return
}
dmCh, err := s.UserChannelCreate(recipientID)
if err != nil {
log.Printf("onboarding: failed creating DM for user %s: %v", recipientID, err)
return
}
guildName := m.GuildID
if g, err := s.State.Guild(m.GuildID); err == nil && g != nil && strings.TrimSpace(g.Name) != "" {
guildName = g.Name
} else if g, err := s.Guild(m.GuildID); err == nil && g != nil && strings.TrimSpace(g.Name) != "" {
guildName = g.Name
}
embed := &discordgo.MessageEmbed{
Title: "Thanks for adding Velox Bot!",
Color: 0x5865F2,
Description: fmt.Sprintf(
"Server: **%s**\n\nFor safety, toggleable systems start **disabled by default**.\nUse these commands in your server to enable features:",
guildName,
),
Fields: []*discordgo.MessageEmbedField{
{
Name: "Level System",
Value: "`/slvl toggle`",
Inline: false,
},
{
Name: "Logging System",
Value: "" +
"`/config setlogchannel channel:#channel`\n" +
"`/config enablelogging`",
Inline: false,
},
{
Name: "Welcome System",
Value: "" +
"`/config setwelcomechannel channel:#channel`\n" +
"`/config setwelcomemessage message:\"...\"`\n" +
"`/config setwelcomedm message:\"...\"`",
Inline: false,
},
},
Footer: &discordgo.MessageEmbedFooter{
Text: "Tip: run /help in any server channel for the full command reference.",
},
Timestamp: time.Now().Format(time.RFC3339),
}
if _, err := s.ChannelMessageSendEmbed(dmCh.ID, embed); err != nil {
log.Printf("onboarding: failed sending setup DM to user %s: %v", recipientID, err)
}
}
func findRecentBotAdderID(s *discordgo.Session, guildID string) string {
if s == nil || s.State == nil || s.State.User == nil {
return ""
}
logs, err := s.GuildAuditLog(guildID, "", "", int(discordgo.AuditLogActionBotAdd), 10)
if err != nil || logs == nil {
return ""
}
botID := s.State.User.ID
now := time.Now().UTC()
for _, entry := range logs.AuditLogEntries {
if entry == nil || entry.TargetID != botID || entry.ActionType == nil || *entry.ActionType != discordgo.AuditLogActionBotAdd {
continue
}
ts, err := discordgo.SnowflakeTimestamp(entry.ID)
if err != nil {
continue
}
if now.Sub(ts) > 10*time.Minute {
continue
}
if entry.UserID != "" {
return entry.UserID
}
}
return ""
}
func guildOwnerID(s *discordgo.Session, guildID string) string {
if s == nil || guildID == "" {
return ""
}
if s.State != nil {
if g, err := s.State.Guild(guildID); err == nil && g != nil && g.OwnerID != "" {
return g.OwnerID
}
}
if g, err := s.Guild(guildID); err == nil && g != nil {
return g.OwnerID
}
return ""
}