package clients import ( "bufio" "fmt" "log" "net" "net/textproto" "strings" "time" ) type TwitchBot struct { conn net.Conn reader *textproto.Reader username string oauth string clientID string channel string } func NewTwitchBot(username, oauth, clientID, channel string) *TwitchBot { return &TwitchBot{ username: username, oauth: oauth, clientID: clientID, channel: channel, } } func (bot *TwitchBot) Connect() error { var err error bot.conn, err = net.Dial("tcp", "irc.chat.twitch.tv:6667") if err != nil { return fmt.Errorf("failed to connect to Twitch IRC: %v", err) } bot.reader = textproto.NewReader(bufio.NewReader(bot.conn)) // Authenticate with IRC (Client ID not needed for IRC connection) fmt.Fprintf(bot.conn, "PASS %s\r\n", bot.oauth) fmt.Fprintf(bot.conn, "NICK %s\r\n", bot.username) fmt.Fprintf(bot.conn, "CAP REQ :twitch.tv/membership\r\n") fmt.Fprintf(bot.conn, "CAP REQ :twitch.tv/tags\r\n") fmt.Fprintf(bot.conn, "CAP REQ :twitch.tv/commands\r\n") // Join channel fmt.Fprintf(bot.conn, "JOIN #%s\r\n", bot.channel) log.Printf("Connected to Twitch IRC and joined #%s", bot.channel) log.Printf("Client ID ready for Helix API calls: %s", bot.clientID[:8]+"...") return nil } func (bot *TwitchBot) SendMessage(message string) error { _, err := fmt.Fprintf(bot.conn, "PRIVMSG #%s :%s\r\n", bot.channel, message) if err != nil { return fmt.Errorf("failed to send message: %v", err) } log.Printf("Sent message: %s", message) return nil } // GetClientID returns the client ID for potential Helix API usage func (bot *TwitchBot) GetClientID() string { return bot.clientID } // GetOAuthToken returns the OAuth token (without oauth: prefix) for Helix API usage func (bot *TwitchBot) GetOAuthToken() string { // Remove oauth: prefix for API calls return strings.TrimPrefix(bot.oauth, "oauth:") } func (bot *TwitchBot) HandleMessages() { for { line, err := bot.reader.ReadLine() if err != nil { log.Printf("Error reading from connection: %v", err) break } // Handle PING messages to keep connection alive if strings.HasPrefix(line, "PING") { pong := strings.Replace(line, "PING", "PONG", 1) fmt.Fprintf(bot.conn, "%s\r\n", pong) continue } // Log incoming messages (optional - remove if you don't want to see chat) if strings.Contains(line, "PRIVMSG") { // Parse username from message for cleaner logging if idx := strings.Index(line, "!"); idx != -1 { username := line[1:idx] if msgIdx := strings.Index(line, " :"); msgIdx != -1 { message := line[msgIdx+2:] log.Printf("[%s]: %s", username, message) } } } } } func (bot *TwitchBot) StartFishTimer() { // Send initial !fish message after a short delay time.Sleep(2 * time.Second) if err := bot.SendMessage("!fish"); err != nil { log.Printf("Error sending initial !fish: %v", err) } // Set up timer for every 5 minutes ticker := time.NewTicker(5 * time.Minute) defer ticker.Stop() for range ticker.C { if err := bot.SendMessage("!fish"); err != nil { log.Printf("Error sending !fish: %v", err) } } } func (bot *TwitchBot) Close() { if bot.conn != nil { log.Println("Closing connection to Twitch IRC") bot.conn.Close() } }