-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconnection.go
176 lines (148 loc) · 3.98 KB
/
connection.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package quiz
import (
"encoding/json"
"fmt"
"math/rand"
"time"
"github.com/gorilla/websocket"
"github.com/kesuaheli/twitchgo"
)
// Connection represents a connection to a logged in player
type Connection struct {
Twitch *twitchgo.Session
WS *websocket.Conn
lastResponse time.Time
started time.Time
Game *Game
userID string
}
type wsVoteMessage struct {
Type string `json:"type"`
Username string `json:"username"`
}
// AllConnections is a map of a user id to connection for all currently active connections
var AllConnections = make(map[string]*Connection)
// New creates a new quiz connection for a new player. The user id is used to identify this client
// again for later requests.
//
// If there is already a connection with this user id New returns nil
func New(userID string) (c *Connection) {
if _, found := GetConnection(userID); found {
return nil
}
c = &Connection{
userID: userID,
started: time.Now(),
}
AllConnections[userID] = c
return c
}
// Used to re-obtain a connection, by passing in the user id. It also returns whether the
// connection was found.
func GetConnection(userID string) (*Connection, bool) {
c, ok := AllConnections[userID]
return c, ok
}
// SetLastResponse saves the current timestamp which can be reobtained as [time.Duration] by
// [c.GetLastResponse].
func (c *Connection) SetLastResponse() {
c.lastResponse = time.Now()
}
// GetLastResponse returns the [time.Duration] since the last timestamp saved by
// [c.SetLastResponse].
func (c *Connection) GetLastResponse() time.Duration {
return time.Since(c.lastResponse)
}
// Close is a graceful termination of the quiz connection to a player
func (c *Connection) Close() {
delete(AllConnections, c.userID)
if c.Twitch != nil {
c.LeaveTwitchChannel()
c.Twitch.Close()
c.Twitch = nil
}
if c.WS != nil {
c.WS.Close()
c.WS = nil
}
if c.Game != nil && c.Game.RoundTimer != nil {
c.Game.RoundTimer.Stop()
}
}
func (c *Connection) OnTwitchChannelMessage(t *twitchgo.Session, source *twitchgo.IRCUser, msg, msgID string, tags twitchgo.IRCMessageTags) {
if c.WS == nil || c.Game == nil {
return
}
vote := MsgToVote(msg, c.Game)
if vote == 0 {
// ignoring non-valid votes
return
}
if _, ok := c.Game.voteHistory[source.Nickname]; ok {
// ignore users who already voted
return
}
v := wsVoteMessage{
Type: "CHAT_VOTE",
Username: source.Nickname,
}
if tags.IsBroadcaster() {
if c.Game.StreamerVote != 0 {
// ignore streamer vote when already voted
return
}
c.Game.StreamerVote = vote
v.Type = "STREAMER_VOTE"
} else {
c.Game.voteHistory[source.Nickname] = true
c.Game.ChatVoteCount[vote-1]++
}
err := c.Twitch.DeleteMessage("", msgID)
if err != nil {
log.Printf("Failed to delete message: %v", err)
}
err = c.WS.WriteJSON(v)
if err != nil {
log.Printf("Error writing chat vote to websocket: %v", err)
}
}
func (c *Connection) NewGame(data []byte) error {
var gameData struct {
Categories map[string]int `json:"categories"`
RoundDuration int `json:"round_duration"`
}
err := json.Unmarshal(data, &gameData)
if err != nil {
return fmt.Errorf("create game: %v", err)
}
if gameData.RoundDuration <= 0 {
return fmt.Errorf("create game: round_duration must not be negative, got %ds", gameData.RoundDuration)
}
var rounds []*Round
for name, n := range gameData.Categories {
cat := Categories.GetCategoryByName(name)
if cat.ID == "" {
return fmt.Errorf("create game: unknown category '%s'", name)
}
rounds = append(rounds, cat.GetRounds(n)...)
}
max := len(rounds)
if max == 0 {
return fmt.Errorf("create game: too few question")
}
rand.Shuffle(max, func(i, j int) {
rounds[i], rounds[j] = rounds[j], rounds[i]
})
for i, r := range rounds {
r.Current = i + 1
r.Max = max
}
c.Game = &Game{
connection: c,
Rounds: rounds,
RoundDuration: time.Duration(gameData.RoundDuration) * time.Second,
Summary: &GameSummary{},
voteHistory: make(map[string]bool),
}
return nil
}