package main import ( "context" "encoding/json" "log" "os" "os/signal" "strings" "syscall" "SimpleArmaAdmin/internal/crypto" "SimpleArmaAdmin/internal/nats" "github.com/nats-io/nats.go/jetstream" ) // DiscordBot represents the central provider-managed bot type DiscordBot struct { natsClient *nats.Client managedKeys map[string][]byte // communityID -> decrypted master key (in-memory vault) } // Event represents a game event from NATS type Event struct { Type string `json:"Type"` Content string `json:"Content"` Raw string `json:"Raw"` Timestamp string `json:"Timestamp"` } func main() { natsURL := os.Getenv("NATS_URL") if natsURL == "" { natsURL = "nats://localhost:4222" } // TODO: Load Discord token from environment // discordToken := os.Getenv("DISCORD_TOKEN") nc, err := nats.Connect(natsURL) if err != nil { log.Fatalf("Could not connect to NATS: %v", err) } defer nc.Close() bot := &DiscordBot{ natsClient: nc, managedKeys: make(map[string][]byte), } log.Println("Discord Bot starting (Managed Trust Mode)") // TODO: Load managed keys from database (encrypted with provider's key) // For now, use a hardcoded demo key bot.managedKeys["comm-123-abc"] = []byte("this-is-a-32-byte-master-key-xyz") ctx := context.Background() // Subscribe to NATS logs stream stream, err := nc.JS.Stream(ctx, "LOGS") if err != nil { log.Fatalf("Stream 'LOGS' not found: %v", err) } consumer, err := stream.CreateOrUpdateConsumer(ctx, jetstream.ConsumerConfig{ Durable: "discord_bot", AckPolicy: jetstream.AckExplicitPolicy, }) if err != nil { log.Fatalf("Failed to create consumer: %v", err) } iter, err := consumer.Messages() if err != nil { log.Fatal(err) } // Process messages go func() { for { msg, err := iter.Next() if err != nil { log.Printf("Iterator error: %v", err) return } // Extract community ID from subject (e.g., "logs.comm-123-abc.live") subjectParts := strings.Split(msg.Subject(), ".") if len(subjectParts) < 2 { msg.Ack() continue } communityID := subjectParts[1] // Check if we have a managed key for this community masterKey, exists := bot.managedKeys[communityID] if !exists { // No managed trust granted, skip msg.Ack() continue } // Decrypt the log in RAM (zero-knowledge breach: temporary only!) decrypted, err := crypto.Decrypt(msg.Data(), masterKey) if err != nil { log.Printf("Decryption failed for %s: %v", communityID, err) msg.Ack() continue } // Parse the event var event Event if err := json.Unmarshal(decrypted, &event); err != nil { log.Printf("Failed to parse event: %v", err) msg.Ack() continue } // Send to Discord bot.sendToDiscord(communityID, &event) msg.Ack() } }() // Wait for shutdown signal sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) <-sig log.Println("Discord Bot shutting down") } func (b *DiscordBot) sendToDiscord(communityID string, event *Event) { // TODO: Implement actual Discord webhook/bot message sending // For now, just log it log.Printf("[Discord] [%s] [%s] %s", communityID, event.Type, event.Content) // Example Discord embed structure: // { // "embeds": [{ // "title": "Player Join", // "description": "Mike1Delta connected to server", // "color": 3066993, // "timestamp": "2026-04-30T12:00:00Z" // }] // } }