tRearrange pages to reuse token box - scoreboard - Interactive scoreboard for CTF-like games
(HTM) git clone git://git.z3bra.org/scoreboard.git
(DIR) Log
(DIR) Files
(DIR) Refs
---
(DIR) commit 4ccfc4d4d29f20d328168684842cf8ea1ac31581
(DIR) parent 37c53b0f0c56f05121f7fd0a3626eb278efa5f76
(HTM) Author: Willy Goiffon <contact@z3bra.org>
Date: Fri, 27 Sep 2024 00:01:25 +0200
Rearrange pages to reuse token box
Diffstat:
M main.go | 81 ++++++++++---------------------
M player.go | 30 +++++++++++++++++++++++++++++-
M playerbox.go | 19 +++++++++----------
M ui.go | 46 +++++++++++++++++++++++++++++++
4 files changed, 109 insertions(+), 67 deletions(-)
---
(DIR) diff --git a/main.go b/main.go
t@@ -18,7 +18,6 @@ import (
"flag"
"fmt"
"os"
- "regexp"
"strings"
"time"
"database/sql"
t@@ -33,7 +32,7 @@ const (
BOARD_HEIGHT int = 15
HTML string = "score.html"
DB string = "score.db"
- TOKEN_REMINDER string = `%s, use the token below to submit your next flag.
+ TOKEN_REMINDER string = `%s, use the token below to submit your flags.
Save it carefully, do not share it.
🔑%s
t@@ -76,57 +75,6 @@ func flagid(hash string) int {
return -1
}
-func pageToken() tview.Primitive {
- input := tview.NewInputField().
- SetLabel("TOKEN ").
- SetPlaceholder("").
- SetFieldStyle(tcell.StyleDefault.Reverse(true)).
- SetFieldWidth(30)
-
- input.SetAcceptanceFunc(func(text string, ch rune) bool {
- if len(text) > 24 {
- return false
- }
-
- // tokens are base32 strings
- matched, err := regexp.Match(`^[A-Z2-7]+$`, []byte(text))
- if err != nil {
- panic(err)
- }
- return matched
- })
- input.SetDoneFunc(func(key tcell.Key) {
- if key != tcell.KeyEnter {
- return
- }
-
- token := input.GetText()
-
- if len(token) != 24 {
- scoreboard.Popup("ERROR", "Invalid token format")
- return
- }
- err := scoreboard.player.FromToken(token)
- if err != nil {
- scoreboard.Fatal(err)
- return
- }
-
- scoreboard.player.token = token;
-
- err = scoreboard.player.Submit(scoreboard.flag)
- if err != nil {
- scoreboard.Fatal(err)
- return
- }
- scoreboard.HighlightBoard(scoreboard.player.ScoreRank() + 1)
- scoreboard.pages.SwitchToPage("board")
- scoreboard.GenerateHTML()
- })
-
- return center(40, 1, input)
-}
-
func pageBoard() tview.Primitive {
scoreboard.SetupFrame()
scoreboard.DrawBoard()
t@@ -137,6 +85,7 @@ func pageBoard() tview.Primitive {
func main() {
var err error
+ var reminder bool = false
html := flag.String("o", HTML, "Output HTML file")
db := flag.String("d", DB, "Database file")
t@@ -175,7 +124,6 @@ func main() {
scoreboard.pages.SetBackgroundColor(tcell.ColorDefault)
- scoreboard.pages.AddPage("token", pageToken(), true, false)
scoreboard.pages.AddPage("board", pageBoard(), true, false)
args := flag.Args()
t@@ -204,6 +152,14 @@ func main() {
rank, _ := db_count_players(scoreboard.db)
scoreboard.NewPlayer(rank + 1)
scoreboard.pages.SwitchToPage("board")
+ reminder = true
+
+ case "badges":
+ badgepage := scoreboard.Token(func () {
+ scoreboard.app.Stop()
+ fmt.Printf("Collection: %d/%d\n\n%s", scoreboard.player.flag, len(scoreboard.flag_ref), scoreboard.player.BadgeStr())
+ })
+ scoreboard.pages.AddAndSwitchToPage("badge", badgepage, true)
/* anything not a command is treated as a flag */
default:
t@@ -212,7 +168,20 @@ func main() {
fmt.Println("Incorrect flag")
return
}
- scoreboard.pages.SwitchToPage("token")
+
+ submitpage := scoreboard.Token(func () {
+ err = scoreboard.player.Submit(scoreboard.flag)
+ if err != nil {
+ scoreboard.Fatal(err)
+ return
+ }
+ scoreboard.HighlightBoard(scoreboard.player.ScoreRank() + 1)
+ scoreboard.pages.RemovePage("submit")
+ scoreboard.pages.ShowPage("board")
+ scoreboard.GenerateHTML()
+ })
+
+ scoreboard.pages.AddAndSwitchToPage("submit", submitpage, true)
}
}
t@@ -223,7 +192,7 @@ func main() {
}
/* Print a token reminder on exit in case one has been generated or provided */
- if scoreboard.player.token != "" {
+ if reminder {
fmt.Printf(TOKEN_REMINDER, scoreboard.player.name, scoreboard.player.token)
}
}
(DIR) diff --git a/player.go b/player.go
t@@ -20,6 +20,7 @@ import (
"encoding/base32"
"errors"
"fmt"
+ "strings"
"time"
"golang.org/x/crypto/scrypt"
"github.com/dustin/go-humanize"
t@@ -146,6 +147,33 @@ func (p *Player) FlagStr() string {
return fmt.Sprintf("%2d/%d", p.flag, len(scoreboard.flag_ref))
}
+func (p *Player) BadgeStr() string {
+ var badges strings.Builder
+
+ query := `SELECT
+ flag.badge, flag.value, flag.score
+ FROM flag
+ INNER JOIN score ON score.flag = flag.value
+ WHERE score.name = ?;`
+
+ rows, err := p.db.Query(query, p.name)
+ if err != nil {
+ return ""
+ }
+
+ for rows.Next() {
+ var b, v string
+ var s int
+ err := rows.Scan(&b, &v, &s)
+ if err != nil {
+ return ""
+ }
+ badges.WriteString(fmt.Sprintf("%s %s (%d pts)\n", b, v, s))
+ }
+
+ return badges.String();
+}
+
func (p *Player) RankStr() string {
return humanize.Ordinal(p.ScoreRank() + 1)
}
t@@ -221,7 +249,7 @@ func (p *Player) FromToken(token string) error {
return err
}
hash := base32.StdEncoding.EncodeToString(dk)
- query := `SELECT name,flag,score,ts FROM score WHERE hash = ?`
+ query := `SELECT name,flag,score,ts FROM user WHERE hash = ?`
row := p.db.QueryRow(query, hash)
err = row.Scan(&p.name, &p.flag, &p.score, &p.ts)
(DIR) diff --git a/playerbox.go b/playerbox.go
t@@ -16,19 +16,18 @@ type PlayerBox struct {
}
const (
- TOKEN_WELCOME string = `%s, your progression has
-been saved. To update it,
-you will need this token:
+ TOKEN_WELCOME string = `
+%s, your registration is
+now complete. To update
+your progression, you
+will need this token:
🔑%s
-Save it.
-Do not share it.
-Tokens are single-use.
-A new token is generated
-and displayed each time
-you submit a flag.
+
+Save it carefully.
+Do not share it.
`
)
t@@ -125,7 +124,7 @@ func PlayerBoxGrid(p *Player, rank int) *tview.Grid {
playerbox.score = gridcell(fmt.Sprintf("%5d ", p.score))
grid := tview.NewGrid().
- SetColumns(4,4,6,7).
+ SetColumns(5,4,6,7).
SetGap(0, 2).
SetRows(1).
AddItem(gridcell(rankstr), 0, 0, 1, 1, 0, 0, false).
(DIR) diff --git a/ui.go b/ui.go
t@@ -3,6 +3,7 @@ package main
import (
"fmt"
"math"
+ "regexp"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
"github.com/dustin/go-humanize"
t@@ -171,3 +172,48 @@ func (a *Application) Fatal(err error) {
a.pages.AddAndSwitchToPage("popup", p, true)
}
+
+func (a *Application) Token(callback func()) tview.Primitive {
+ input := tview.NewInputField().
+ SetLabel("TOKEN ").
+ SetPlaceholder("").
+ SetFieldStyle(tcell.StyleDefault.Reverse(true)).
+ SetFieldWidth(30)
+
+ input.SetAcceptanceFunc(func(text string, ch rune) bool {
+ if len(text) > 24 {
+ return false
+ }
+
+ // tokens are base32 strings
+ matched, err := regexp.Match(`^[A-Z2-7]+$`, []byte(text))
+ if err != nil {
+ panic(err)
+ }
+ return matched
+ })
+ input.SetDoneFunc(func(key tcell.Key) {
+ if key != tcell.KeyEnter {
+ return
+ }
+
+ token := input.GetText()
+
+ if len(token) != 24 {
+ scoreboard.Popup("ERROR", "Invalid token format")
+ return
+ }
+ err := scoreboard.player.FromToken(token)
+ if err != nil {
+ scoreboard.Fatal(err)
+ return
+ }
+
+ scoreboard.player.token = token;
+ scoreboard.player.Fetch()
+ scoreboard.pages.RemovePage("token");
+ callback()
+ })
+
+ return center(40, 1, input)
+}