irc.go
 1  package main
 2  
 3  import (
 4  	"fmt"
 5  	"log"
 6  	"net/url"
 7  	"strings"
 8  	"time"
 9  
10  	"github.com/gorilla/websocket"
11  )
12  
13  func ConnectTwitchChat(app App, channelName string, chats chan<- ChatMessage, interrupt chan struct{}, terminate chan<- struct{}) {
14  	u := url.URL{Scheme: "ws", Host: TWITCH_IRC_HOST}
15  
16  	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
17  	if err != nil {
18  		log.Printf("%24s | IRC error: %s\n", channelName, err)
19  		interrupt <- struct{}{}
20  		return
21  	}
22  	inactive := time.NewTimer(5 * time.Minute)
23  	timeLimit := time.NewTimer(60 * time.Minute)
24  
25  	// validate token, if invalid use refresh token and update app in memory, rewrite env file
26  	ok := HelixValidateToken(app)
27  
28  	if !ok {
29  		newTokens := HelixRefreshToken(app)
30  		app.Token = newTokens.AccessToken
31  		app.RefreshToken = newTokens.AccessToken
32  	}
33  
34  	c.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("PASS oauth:%s", app.Token)))
35  	c.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("NICK %s", app.User)))
36  	c.WriteMessage(websocket.TextMessage, []byte("CAP REQ :twitch.tv/tags"))
37  	c.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("JOIN #%s", channelName)))
38  
39  	go func() {
40  		for {
41  			msgtype, message, err := c.ReadMessage()
42  			if err != nil {
43  				log.Printf("%24s | IRC error (%d): %s\n", channelName, msgtype, err)
44  				interrupt <- struct{}{}
45  				return
46  			}
47  
48  			if strings.Contains(string(message), "PING") {
49  				c.WriteMessage(websocket.TextMessage, []byte("PONG :tmi.twitch.tv"))
50  			}
51  
52  			if strings.Contains(string(message), "PRIVMSG") {
53  				parsed, err := ParseMessage(string(message))
54  				if err != nil {
55  					log.Println(err)
56  					interrupt <- struct{}{}
57  					return
58  				}
59  				if (ChatMessage{}) != parsed {
60  					chats <- parsed
61  					inactive.Reset(5 * time.Minute)
62  				}
63  			}
64  		}
65  	}()
66  
67  	select {
68  	case <-inactive.C:
69  		log.Printf("%24s | INACTIVE disconnecting from chat...\n", channelName)
70  		c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
71  		terminate <- struct{}{}
72  		return
73  	case <-timeLimit.C:
74  		log.Printf("%24s | TIME LIMIT disconnecting from chat...\n", channelName)
75  		c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
76  		terminate <- struct{}{}
77  		return
78  	case <-interrupt:
79  		log.Printf("%24s | INTERRUPT disconnecting from chat...\n", channelName)
80  		c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
81  		terminate <- struct{}{}
82  		return
83  	}
84  }