Fix duplicate leaderboard entries, add /version command, fix jail DNS

- db/db.go: Add write-time username sync in AddLog to prevent duplicate

  leaderboard entries when users change display names. Revert correlated

  subqueries back to GROUP BY user_id, username (simpler approach).

- db/db.go: Early return in onMessageCreate if bot already reacted (prevents

  duplicate emoji reactions on Discord reconnection).

- bot/bot.go: Add /version slash command with build version injection.

- main.go: Add version variable with ldflags support.

- Makefile: Add dns-fix, test, vet, build-native, pg-*, boot targets.

  Prepend test+vet to deploy pipeline. Add version ldflags to build.

- db/migrations/002_fix_usernames.sql: One-time SQL to backfill old usernames.

- scripts/fix-jail-dns.sh: Script to update jail resolv.conf from 8.8.8.8

  to reachable nameservers (1.1.1.1, 9.9.9.9, 172.16.0.1).

Signed-off-by: Blake Ridgway <blake@blakeridgway.com>
This commit is contained in:
Blake Ridgway
2026-05-28 14:07:21 -05:00
parent 020a4139b3
commit 90d58c7f2d
8 changed files with 170 additions and 11 deletions

View File

@@ -25,19 +25,20 @@ type Bot struct {
session *discordgo.Session
db *db.DB
guildID string
version string
topicMu sync.Mutex
topicTimer *time.Timer
}
func New(token string, database *db.DB, guildID string) (*Bot, error) {
func New(token string, database *db.DB, guildID, version string) (*Bot, error) {
s, err := discordgo.New("Bot " + token)
if err != nil {
return nil, fmt.Errorf("create session: %w", err)
}
s.Identify.Intents = discordgo.IntentsGuilds | discordgo.IntentsGuildMessages | discordgo.IntentsMessageContent
b := &Bot{session: s, db: database, guildID: guildID}
b := &Bot{session: s, db: database, guildID: guildID, version: version}
s.AddHandler(b.onMessageCreate)
s.AddHandler(b.onMessageDelete)
s.AddHandler(b.onInteraction)
@@ -115,6 +116,9 @@ func (b *Bot) RegisterCommands() error {
{Name: "weeklyreport", Description: "Show distances logged this week"},
{Name: "monthlyreport", Description: "Show distances logged this month"},
// ── Utility ──────────────────────────────────────────────────────────
{Name: "version", Description: "Show the bot's build version"},
// ── Admin ────────────────────────────────────────────────────────────
{Name: "addkm", Description: "[Admin] Manually add or subtract KM for a user",
Options: []*discordgo.ApplicationCommandOption{
@@ -230,6 +234,11 @@ func (b *Bot) onMessageCreate(s *discordgo.Session, m *discordgo.MessageCreate)
if m.Author == nil || m.Author.Bot {
return
}
// Skip messages the bot has already reacted to.
// This prevents duplicate reactions from Discord reconnection event replays.
if hasCheckmark(m.Message) {
return
}
ctx := context.Background()
channelID, ok, err := b.db.GetSetting(ctx, m.GuildID, settingChannel)
@@ -302,12 +311,21 @@ func (b *Bot) onInteraction(s *discordgo.Session, i *discordgo.InteractionCreate
case "compare": b.handleCompare(ctx, s, i)
case "weeklyreport": b.handleWeeklyReport(ctx, s, i)
case "monthlyreport": b.handleMonthlyReport(ctx, s, i)
case "version": b.handleVersion(ctx, s, i)
case "addkm": b.handleAddKM(ctx, s, i)
case "removelog": b.handleRemoveLog(ctx, s, i)
case "audit": b.handleAudit(ctx, s, i)
}
}
// ── Version ───────────────────────────────────────────────────────────────────
func (b *Bot) handleVersion(ctx context.Context, s *discordgo.Session, i *discordgo.InteractionCreate) {
respond(s, i, fmt.Sprintf("Cycling Bot version **%s**", b.version))
}
// ── Topic update ──────────────────────────────────────────────────────────────
func (b *Bot) scheduleTopicUpdate(guildID, channelID string) {