// Command discord-go is a Go port of discord-rare-monitor: it consumes the // tracker's /ws/live firehose (subscribed to rare+chat), classifies rares // common/great, posts embeds to Discord, and relays allegiance chat. // // SAFETY: it runs in dry-run (log only, no Discord posts) by default. Going live // requires BOTH a bot token AND DRY_RUN=0 — so it can never accidentally // double-post to the production channels during the parallel run. For a parallel // test, set a TEST token + TEST channel IDs + DRY_RUN=0. package main import ( "context" "fmt" "log/slog" "os" "os/signal" "sort" "syscall" "github.com/bwmarrin/discordgo" ) func main() { // `discord-go dump-rares` prints the common-rares set (for parity diffing // against the Python regex). No network, no token. if len(os.Args) > 1 && os.Args[1] == "dump-rares" { names := make([]string, 0, len(commonRares)) for n := range commonRares { names = append(names, n) } sort.Strings(names) for _, n := range names { fmt.Println(n) } return } logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})) slog.SetDefault(logger) token := os.Getenv("DISCORD_RARE_BOT_TOKEN") // Dry-run unless a token is present AND DRY_RUN is explicitly "0". dryRun := token == "" || os.Getenv("DRY_RUN") != "0" wsURL := envOr("DERETH_TRACKER_WS_URL", "ws://dereth-tracker:8765/ws/live") monitorChar := envOr("MONITOR_CHARACTER", "Dunking Rares") ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() var out poster if dryRun { reason := "no DISCORD_RARE_BOT_TOKEN" if token != "" { reason = "DRY_RUN != 0" } logger.Info("starting in DRY-RUN — classifying but NOT posting to Discord", "reason", reason, "ws", wsURL, "monitor", monitorChar) out = &logPoster{log: logger} } else { dg, err := discordgo.New("Bot " + token) if err != nil { logger.Error("discord session init failed", "err", err) os.Exit(1) } // REST-only: we send by channel ID, so no gateway Open()/intents needed. logger.Info("starting LIVE — posting to Discord", "ws", wsURL, "monitor", monitorChar) out = &discordPoster{ dg: dg, common: envOr("COMMON_RARE_CHANNEL_ID", "1355328792184226014"), great: envOr("GREAT_RARE_CHANNEL_ID", "1353676584334131211"), aclog: envOr("ACLOG_CHANNEL_ID", "1349649482786275328"), sawato: envOr("SAWATOLIFE_CHANNEL_ID", "1387323032271327423"), iconsDir: envOr("ICONS_DIR", "icons"), log: logger, } } b := &bot{wsURL: wsURL, monitorChar: monitorChar, out: out, log: logger} go b.run(ctx) <-ctx.Done() logger.Info("shutdown signal received") } func envOr(key, def string) string { if v := os.Getenv(key); v != "" { return v } return def }