package main import ( "encoding/json" "fmt" "log" "net/url" "os" "strings" "github.com/gorilla/websocket" ) type Event struct { Type string `json:"type"` Payload json.RawMessage `json:"payload"` } type Message struct { ID string `json:"id"` ChannelID string `json:"channel_id"` AuthorID string `json:"author_id"` Username string `json:"username"` Content string `json:"content"` } var bannedWords = []string{ "spam", "scam", "phishing", } func main() { token := os.Getenv("BOT_TOKEN") if token == "" { log.Fatal("BOT_TOKEN environment variable required") } host := os.Getenv("DUMPSTER_HOST") if host == "" { host = "localhost:8080" } u := url.URL{ Scheme: "ws", Host: host, Path: "/ws/bot", RawQuery: "token=" + token, } log.Printf("Connecting to %s", u.String()) c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() log.Println("Modbot connected!") for { _, message, err := c.ReadMessage() if err != nil { log.Println("read:", err) break } var event Event if err := json.Unmarshal(message, &event); err != nil { continue } switch event.Type { case "MESSAGE_CREATE": var msg Message if err := json.Unmarshal(event.Payload, &msg); err != nil { continue } handleMessage(c, msg) } } } func handleMessage(c *websocket.Conn, msg Message) { content := strings.ToLower(msg.Content) // Check for banned words for _, word := range bannedWords { if strings.Contains(content, word) { log.Printf("Deleting message from %s: contains banned word '%s'", msg.Username, word) // Delete the message deleteMsg := map[string]interface{}{ "type": "DELETE_MESSAGE", "payload": map[string]string{ "channel_id": msg.ChannelID, "message_id": msg.ID, }, } data, _ := json.Marshal(deleteMsg) c.WriteMessage(websocket.TextMessage, data) // Send warning sendMessage(c, msg.ChannelID, fmt.Sprintf( "⚠️ <@%s>, your message was removed (contained banned content).", msg.AuthorID, )) return } } // Handle slash commands if strings.HasPrefix(msg.Content, "/kick") { handleKick(c, msg) } else if strings.HasPrefix(msg.Content, "/ban") { handleBan(c, msg) } else if strings.HasPrefix(msg.Content, "/purge") { handlePurge(c, msg) } } func handleKick(c *websocket.Conn, msg Message) { parts := strings.Fields(msg.Content) if len(parts) < 2 { sendMessage(c, msg.ChannelID, "Usage: /kick @user [reason]") return } // Extract user ID from mention format <@user_id> targetID := strings.TrimPrefix(parts[0], "<@") targetID = strings.TrimSuffix(targetID, ">") reason := "No reason provided" if len(parts) > 2 { reason = strings.Join(parts[2:], " ") } log.Printf("Kick request: %s -> %s (%s)", msg.Username, targetID, reason) sendMessage(c, msg.ChannelID, fmt.Sprintf( "👢 <@%s> has been kicked. Reason: %s", targetID, reason, )) } func handleBan(c *websocket.Conn, msg Message) { parts := strings.Fields(msg.Content) if len(parts) < 2 { sendMessage(c, msg.ChannelID, "Usage: /ban @user [reason]") return } targetID := strings.TrimPrefix(parts[0], "<@") targetID = strings.TrimSuffix(targetID, ">") reason := "No reason provided" if len(parts) > 2 { reason = strings.Join(parts[2:], " ") } log.Printf("Ban request: %s -> %s (%s)", msg.Username, targetID, reason) sendMessage(c, msg.ChannelID, fmt.Sprintf( "🔨 <@%s> has been banned. Reason: %s", targetID, reason, )) } func handlePurge(c *websocket.Conn, msg Message) { parts := strings.Fields(msg.Content) if len(parts) < 2 { sendMessage(c, msg.ChannelID, "Usage: /purge ") return } count := 10 // default fmt.Sscanf(parts[1], "%d", &count) if count > 100 { count = 100 } log.Printf("Purge request: %s -> %d messages", msg.Username, count) sendMessage(c, msg.ChannelID, fmt.Sprintf( "🧹 Purging %d messages...", count, )) } func sendMessage(c *websocket.Conn, channelID, content string) { msg := map[string]interface{}{ "type": "SEND_MESSAGE", "payload": map[string]string{ "channel_id": channelID, "content": content, }, } data, _ := json.Marshal(msg) c.WriteMessage(websocket.TextMessage, data) }