Files
twitch-fish-bot/internal/clients/bot.go
Kevin Thompson c7b9e661f5 first commit
2025-06-13 19:43:16 -05:00

128 lines
3.2 KiB
Go

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()
}
}