tmain.go - scoreboard - Interactive scoreboard for CTF-like games
(HTM) git clone git://git.z3bra.org/scoreboard.git
(DIR) Log
(DIR) Files
(DIR) Refs
---
tmain.go (5149B)
---
1 // Copyright 2016 The Tcell Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use file except in compliance with the License.
5 // You may obtain a copy of the license at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package main
16
17 import (
18 "errors"
19 "flag"
20 "fmt"
21 "os"
22 "strings"
23 "time"
24 "database/sql"
25 "github.com/gdamore/tcell/v2"
26 "github.com/rivo/tview"
27
28 _ "modernc.org/sqlite"
29 )
30
31 const (
32 BOARD_WIDTH int = 28
33 BOARD_HEIGHT int = 15
34 HTML string = "scores.html"
35 TMPL string = "template.html"
36 DB string = "score.db"
37 TOKEN_REMINDER string = `%s, use the token below to submit your flags.
38 Save it carefully, do not share it.
39
40 🔑%s
41 `
42 FLAG_SUBMITTED string = `[::-]
43 You found a flag
44 worth [::b]%d[::-] points!
45
46
47 %s
48 `
49 )
50
51 type Flag struct {
52 id int
53 value string
54 badge string
55 score int
56 }
57
58 type Application struct {
59 flag Flag
60 flag_ref []Flag
61 db *sql.DB
62 app *tview.Application
63 html string
64 tmpl string
65 pages *tview.Pages
66 frame *tview.Frame
67 board *tview.Flex
68 player *Player
69 }
70
71 var scoreboard Application
72
73 func usage() {
74 fmt.Println("ssh -t score@5r.wtf [FLAG]")
75 os.Exit(0)
76 }
77
78
79 func checkflag(ref []Flag, flag string) (Flag, error) {
80 for _, f := range ref {
81 if strings.ToUpper(flag) == f.value {
82 return f, nil
83 }
84 }
85 return Flag{}, errors.New("Unknown flag")
86 }
87
88 func pageBoard() tview.Primitive {
89 scoreboard.SetupFrame()
90 scoreboard.DrawBoard()
91
92 // center frame on screen, counting borders + header + footer
93 return center(BOARD_WIDTH + 2, BOARD_HEIGHT + 7, scoreboard.frame)
94 }
95
96 func main() {
97 var err error
98 var reminder bool = false
99
100 html := flag.String("o", HTML, "Output HTML file")
101 tmpl := flag.String("t", HTML, "Template HTML file")
102 db := flag.String("d", DB, "Database file")
103
104 flag.Parse()
105
106 // Override default borders
107 tview.Borders.HorizontalFocus = tview.BoxDrawingsLightHorizontal
108 tview.Borders.VerticalFocus = tview.BoxDrawingsLightVertical
109 tview.Borders.TopLeftFocus = tview.BoxDrawingsLightDownAndRight
110 tview.Borders.TopRightFocus = tview.BoxDrawingsLightDownAndLeft
111 tview.Borders.BottomLeftFocus = tview.BoxDrawingsLightUpAndRight
112 tview.Borders.BottomRightFocus = tview.BoxDrawingsLightUpAndLeft
113
114 // Set default colors
115 tview.Styles.PrimitiveBackgroundColor = tcell.ColorDefault
116 tview.Styles.BorderColor = tcell.ColorDefault
117 tview.Styles.TitleColor = tcell.ColorDefault
118 tview.Styles.GraphicsColor = tcell.ColorDefault
119 tview.Styles.PrimaryTextColor = tcell.ColorDefault
120
121 scoreboard.db, err = db_init(*db)
122 if err != nil {
123 panic(err)
124 }
125 defer scoreboard.db.Close()
126
127 scoreboard.flag_ref, err = db_get_flags(scoreboard.db)
128
129 scoreboard.flag = Flag{}
130 scoreboard.html = *html
131 scoreboard.tmpl = *tmpl
132 scoreboard.app = tview.NewApplication()
133 scoreboard.pages = tview.NewPages()
134 scoreboard.board = tview.NewFlex()
135 scoreboard.player = &Player{
136 db: scoreboard.db,
137 id: -1,
138 token: "",
139 name: "",
140 flags: make([]Flag,0),
141 score: 0,
142 ts: time.Now().Unix(),
143 }
144
145 scoreboard.pages.SetBackgroundColor(tcell.ColorDefault)
146
147 scoreboard.pages.AddPage("board", pageBoard(), true, false)
148
149 args := flag.Args()
150
151 if len(args) > 1 {
152 usage()
153 }
154 if len(args) == 0 {
155 scoreboard.pages.SwitchToPage("board")
156 scoreboard.DrawBoard()
157 }
158
159 if len(args) == 1 {
160 switch args[0] {
161 case "help":
162 usage()
163 case "refresh":
164 scoreboard.GenerateHTML()
165 os.Exit(0)
166
167 case "register":
168 rank, _ := db_count_players(scoreboard.db)
169 scoreboard.NewPlayer(rank + 1)
170 scoreboard.pages.SwitchToPage("board")
171 reminder = true
172
173 /* anything not a command is treated as a flag */
174 default:
175 scoreboard.flag, err = checkflag(scoreboard.flag_ref, args[0])
176 if err != nil {
177 fmt.Println("Incorrect flag")
178 return
179 }
180
181 page := scoreboard.Token(func () {
182 err = scoreboard.player.Submit(scoreboard.flag)
183 if err != nil {
184 scoreboard.Fatal(err)
185 return
186 }
187 scoreboard.HighlightBoard(scoreboard.player.Rank() + 1)
188 scoreboard.pages.RemovePage("token")
189 scoreboard.GenerateHTML()
190 collection := strings.Replace(scoreboard.player.BadgeStr(),
191 scoreboard.flag.badge,
192 fmt.Sprintf("[::l]%s[::-]", scoreboard.flag.badge), -1)
193 scoreboard.Popup("CONGRATULATIONS",
194 fmt.Sprintf(FLAG_SUBMITTED,
195 scoreboard.flag.score, collection))
196 })
197
198 scoreboard.pages.AddAndSwitchToPage("token", page, true)
199 }
200 }
201
202 /* Run application */
203 if err := scoreboard.app.SetRoot(scoreboard.pages, true).EnableMouse(true).Run(); err != nil {
204 fmt.Println(err)
205 os.Exit(1)
206 }
207
208 /* Print a token reminder on exit in case one has been generated or provided */
209 if reminder && scoreboard.player.token != "" {
210 fmt.Printf(TOKEN_REMINDER, scoreboard.player.name, scoreboard.player.token)
211 }
212 }