Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
unixpickle
GitHub Repository: unixpickle/kahoot-hack
Path: blob/master/kahoot-rand/main.go
10110 views
1
package main
2
3
import (
4
"errors"
5
"fmt"
6
"io/ioutil"
7
"math/rand"
8
"os"
9
"os/signal"
10
"strconv"
11
"strings"
12
"sync"
13
"sync/atomic"
14
"syscall"
15
"time"
16
17
"github.com/unixpickle/kahoot-hack/kahoot"
18
)
19
20
var wg sync.WaitGroup
21
22
const ConnectionDelay = time.Millisecond * 100
23
24
var StatisticsChan = make(chan int, 0)
25
var AnswerCount uint32
26
27
func main() {
28
if len(os.Args) != 3 && len(os.Args) != 4 {
29
fmt.Fprintln(os.Stderr, "Usage: rand <game pin> <nickname prefix> <count>")
30
fmt.Fprintln(os.Stderr, " rand <game pin> <name_list.txt>")
31
os.Exit(1)
32
}
33
34
gamePin := os.Args[1]
35
36
nicknames, err := readNicknames()
37
if err != nil {
38
fmt.Fprintln(os.Stderr, err)
39
os.Exit(1)
40
}
41
42
botCount := 0
43
for nickname := range nicknames {
44
wg.Add(1)
45
go launchConnection(gamePin, nickname)
46
time.Sleep(ConnectionDelay)
47
botCount++
48
}
49
50
fmt.Println("Entered with", botCount, "bots.")
51
fmt.Println("Terminate this program to stop the automatons...")
52
53
go printStatistics(botCount)
54
55
wg.Wait()
56
}
57
58
func launchConnection(gamePin string, nickname string) {
59
defer wg.Done()
60
61
conn, err := kahoot.NewConn(gamePin)
62
if err != nil {
63
fmt.Fprintln(os.Stderr, "failed to connect:", err)
64
os.Exit(1)
65
}
66
67
closed := make(chan bool, 1)
68
closed <- false
69
go func() {
70
sigChan := make(chan os.Signal, 1)
71
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
72
<-sigChan
73
<-closed
74
closed <- true
75
conn.GracefulClose()
76
}()
77
78
if err := conn.Login(nickname); err != nil {
79
fmt.Fprintln(os.Stderr, "failed to login:", err)
80
os.Exit(1)
81
}
82
83
quiz := kahoot.NewQuiz(conn)
84
85
for {
86
action, err := quiz.Receive()
87
if err != nil {
88
if <-closed {
89
return
90
} else {
91
fmt.Fprintln(os.Stderr, "Could not receive question:", err)
92
os.Exit(1)
93
}
94
}
95
if action.Type == kahoot.QuestionAnswers {
96
atomic.StoreUint32(&AnswerCount, uint32(action.NumAnswers))
97
answer := rand.Intn(action.NumAnswers)
98
quiz.Send(action.AnswerMap[answer])
99
StatisticsChan <- answer
100
}
101
}
102
}
103
104
func printStatistics(clientCount int) {
105
qid := 1
106
for {
107
stats := map[int]int{}
108
for i := 0; i < clientCount; i++ {
109
stats[<-StatisticsChan]++
110
}
111
112
answerCount := int(atomic.LoadUint32(&AnswerCount))
113
114
fmt.Println("-----STATISTICS-----")
115
fmt.Println("Question ID:", qid)
116
for i := 0; i < answerCount; i++ {
117
fmt.Println("Answer "+strconv.Itoa(i)+":", stats[i])
118
}
119
qid++
120
}
121
}
122
123
func readNicknames() (<-chan string, error) {
124
if len(os.Args) == 3 {
125
contents, err := ioutil.ReadFile(os.Args[2])
126
if err != nil {
127
return nil, err
128
}
129
nameLines := strings.Split(string(contents), "\n")
130
res := make(chan string, len(nameLines))
131
for _, line := range nameLines {
132
nickname := strings.TrimSpace(line)
133
if len(nickname) != 0 {
134
res <- nickname
135
}
136
}
137
close(res)
138
return res, nil
139
}
140
141
count, err := strconv.Atoi(os.Args[3])
142
if err != nil {
143
return nil, errors.New("invalid count: " + os.Args[3])
144
}
145
146
baseName := os.Args[2]
147
res := make(chan string)
148
go func() {
149
for i := 0; i < count; i++ {
150
res <- baseName + strconv.Itoa(i+1)
151
}
152
close(res)
153
}()
154
return res, nil
155
}
156
157