Repos / gorts / 48a2252248
commit 48a2252248c3acf30e8b0af000438bd9e5e9c24d
Author: Nhân <hi@imnhan.com>
Date: Tue Jun 20 23:40:06 2023 +0700
add start.gg tab; experiment with long running task
diff --git a/main.go b/main.go
index bc583ac..ce0e2e7 100644
--- a/main.go
+++ b/main.go
@@ -15,6 +15,7 @@
"runtime"
"strconv"
"strings"
+ "time"
"go.imnhan.com/gorts/players"
"go.imnhan.com/gorts/startgg"
@@ -24,6 +25,7 @@
const WebDir = "web"
const ScoreboardFile = WebDir + "/state.json"
const PlayersFile = "players.csv"
+const StartggFile = "creds-start.gg"
//go:embed tcl/main.tcl
var mainTcl string
@@ -88,11 +90,16 @@ func startGUI() {
allplayers := players.FromFile(PlayersFile)
scoreboard := initScoreboard()
b64icon := base64.StdEncoding.EncodeToString(gortsPngIcon)
+ startggInputs := startgg.LoadInputs(StartggFile)
fmt.Fprintf(
stdin,
- "initialize %s %s {%s}\n",
- b64icon, WebPort, strings.Join(startgg.CountryCodes, " "),
+ "initialize %s %s {%s} %s %s\n",
+ b64icon,
+ WebPort,
+ strings.Join(startgg.CountryCodes, " "),
+ startggInputs.Token,
+ startggInputs.Slug,
)
scanner := bufio.NewScanner(stdout)
@@ -162,6 +169,14 @@ func startGUI() {
}
}
respond("end")
+
+ case "fetchplayers":
+ startggInputs.Token = next()
+ startggInputs.Slug = next()
+ time.Sleep(3 * time.Second)
+ respond("fetchplayers__resp")
+ respond("All done.")
+ startggInputs.Write(StartggFile)
}
}
diff --git a/startgg/startgg.go b/startgg/startgg.go
index d1e94d4..c62cc74 100644
--- a/startgg/startgg.go
+++ b/startgg/startgg.go
@@ -1,12 +1,14 @@
package startgg
import (
+ "bufio"
"bytes"
_ "embed"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
+ "os"
"go.imnhan.com/gorts/players"
)
@@ -18,7 +20,36 @@ type GraphQL struct {
Variables struct{} `json:"variables"`
}
-func FetchPlayers(token string, tourneySlug string) ([]players.Player, error) {
+type Inputs struct {
+ Token string
+ Slug string
+}
+
+func LoadInputs(filepath string) Inputs {
+ var result Inputs
+ file, err := os.Open(filepath)
+ if err != nil {
+ return result
+ }
+ defer file.Close()
+
+ s := bufio.NewScanner(file)
+ s.Scan()
+ result.Token = s.Text()
+ s.Scan()
+ result.Slug = s.Text()
+ return result
+}
+
+func (c *Inputs) Write(filepath string) {
+ blob := []byte(fmt.Sprintf("%s\n%s\n", c.Token, c.Slug))
+ err := ioutil.WriteFile(filepath, blob, 0644)
+ if err != nil {
+ panic(err)
+ }
+}
+
+func FetchPlayers(i Inputs) ([]players.Player, error) {
query := `
{
tournament(slug: "%s") {
@@ -46,7 +77,7 @@ func FetchPlayers(token string, tourneySlug string) ([]players.Player, error) {
}
`
body, err := json.Marshal(GraphQL{
- Query: fmt.Sprintf(query, tourneySlug, 1),
+ Query: fmt.Sprintf(query, i.Slug, 1),
Variables: struct{}{},
})
if err != nil {
@@ -57,9 +88,9 @@ func FetchPlayers(token string, tourneySlug string) ([]players.Player, error) {
if err != nil {
panic(err)
}
- req.Header.Add("User-Agent", "GORTS/0.2")
+ req.Header.Add("User-Agent", "GORTS/0.4")
req.Header.Add("Content-Type", "application/json")
- req.Header.Add("Authorization", "Bearer "+token)
+ req.Header.Add("Authorization", "Bearer "+i.Token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
diff --git a/tcl/main.tcl b/tcl/main.tcl
index 6f886e1..875e7b8 100644
--- a/tcl/main.tcl
+++ b/tcl/main.tcl
@@ -43,106 +43,137 @@ foreach key [array names scoreboard] {
}
array set var_to_widget {
- description .c.description.entry
- subtitle .c.subtitle.entry
- p1name .c.players.p1name
- p1country .c.players.p1country
- p1score .c.players.p1score
- p1team .c.players.p1team
- p2name .c.players.p2name
- p2country .c.players.p2country
- p2score .c.players.p2score
- p2team .c.players.p2team
+ description .n.m.description.entry
+ subtitle .n.m.subtitle.entry
+ p1name .n.m.players.p1name
+ p1country .n.m.players.p1country
+ p1score .n.m.players.p1score
+ p1team .n.m.players.p1team
+ p2name .n.m.players.p2name
+ p2country .n.m.players.p2country
+ p2score .n.m.players.p2score
+ p2team .n.m.players.p2team
}
-# GUI
+array set startgg {
+ token ""
+ slug ""
+ msg ""
+}
+
+# GUI has 2 tabs: Main (.n.m) and start.gg (.n.s)
+
+ttk::notebook .n
+ttk::frame .n.m -padding 5
+ttk::frame .n.s -padding 5
+.n add .n.m -text Main
+.n add .n.s -text start.gg
+grid .n -column 0 -row 0 -sticky NESW -padx 3 -pady 3
-ttk::frame .c -padding 5
-ttk::frame .c.description
-ttk::label .c.description.lbl -text "Title"
-ttk::entry .c.description.entry -textvariable scoreboard(description)
-ttk::frame .c.subtitle
-ttk::label .c.subtitle.lbl -text "Subtitle"
-ttk::entry .c.subtitle.entry -textvariable scoreboard(subtitle)
-ttk::frame .c.players
-ttk::label .c.players.p1lbl -text "Player 1"
-ttk::combobox .c.players.p1name -textvariable scoreboard(p1name) -width 35
-ttk::combobox .c.players.p1country -textvariable scoreboard(p1country) -width 5
-ttk::spinbox .c.players.p1score -textvariable scoreboard(p1score) -from 0 -to 999 -width 4
-ttk::button .c.players.p1win -text "▲ Win" -width 6 -command {incr scoreboard(p1score)}
-ttk::label .c.players.p1teamlbl -text "Team 1"
-ttk::combobox .c.players.p1team -textvariable scoreboard(p1team)
-ttk::separator .c.players.separator -orient horizontal
-ttk::label .c.players.p2lbl -text "Player 2"
-ttk::combobox .c.players.p2name -textvariable scoreboard(p2name) -width 35
-ttk::combobox .c.players.p2country -textvariable scoreboard(p2country) -width 5
-ttk::spinbox .c.players.p2score -textvariable scoreboard(p2score) -from 0 -to 999 -width 4
-ttk::button .c.players.p2win -text "▲ Win" -width 6 -command {incr scoreboard(p2score)}
-ttk::label .c.players.p2teamlbl -text "Team 2"
-ttk::combobox .c.players.p2team -textvariable scoreboard(p2team)
-ttk::frame .c.buttons
-ttk::button .c.buttons.apply -text "▶ Apply" -command applyscoreboard
-ttk::button .c.buttons.discard -text "✖ Discard" -command discardscoreboard
-ttk::button .c.buttons.reset -text "↶ Reset scores" -command {
+# Main tab:
+
+ttk::frame .n.m.description
+ttk::label .n.m.description.lbl -text "Title"
+ttk::entry .n.m.description.entry -textvariable scoreboard(description)
+ttk::frame .n.m.subtitle
+ttk::label .n.m.subtitle.lbl -text "Subtitle"
+ttk::entry .n.m.subtitle.entry -textvariable scoreboard(subtitle)
+ttk::frame .n.m.players
+ttk::label .n.m.players.p1lbl -text "Player 1"
+ttk::combobox .n.m.players.p1name -textvariable scoreboard(p1name) -width 35
+ttk::combobox .n.m.players.p1country -textvariable scoreboard(p1country) -width 5
+ttk::spinbox .n.m.players.p1score -textvariable scoreboard(p1score) -from 0 -to 999 -width 4
+ttk::button .n.m.players.p1win -text "▲ Win" -width 6 -command {incr scoreboard(p1score)}
+ttk::label .n.m.players.p1teamlbl -text "Team 1"
+ttk::combobox .n.m.players.p1team -textvariable scoreboard(p1team)
+ttk::separator .n.m.players.separator -orient horizontal
+ttk::label .n.m.players.p2lbl -text "Player 2"
+ttk::combobox .n.m.players.p2name -textvariable scoreboard(p2name) -width 35
+ttk::combobox .n.m.players.p2country -textvariable scoreboard(p2country) -width 5
+ttk::spinbox .n.m.players.p2score -textvariable scoreboard(p2score) -from 0 -to 999 -width 4
+ttk::button .n.m.players.p2win -text "▲ Win" -width 6 -command {incr scoreboard(p2score)}
+ttk::label .n.m.players.p2teamlbl -text "Team 2"
+ttk::combobox .n.m.players.p2team -textvariable scoreboard(p2team)
+ttk::frame .n.m.buttons
+ttk::button .n.m.buttons.apply -text "▶ Apply" -command applyscoreboard
+ttk::button .n.m.buttons.discard -text "✖ Discard" -command discardscoreboard
+ttk::button .n.m.buttons.reset -text "↶ Reset scores" -command {
set scoreboard(p1score) 0
set scoreboard(p2score) 0
}
-ttk::button .c.buttons.swap -text "⇄ Swap players" -command {
+ttk::button .n.m.buttons.swap -text "⇄ Swap players" -command {
foreach key {name country score team} {
set tmp $scoreboard(p1$key)
set scoreboard(p1$key) $scoreboard(p2$key)
set scoreboard(p2$key) $tmp
}
}
-ttk::label .c.status -textvariable mainstatus
+ttk::label .n.m.status -textvariable mainstatus
+grid .n.m.description -row 0 -column 0 -sticky NESW -pady {0 5}
+grid .n.m.description.lbl -row 0 -column 0 -padx {0 5}
+grid .n.m.description.entry -row 0 -column 1 -sticky EW
+grid columnconfigure .n.m.description 1 -weight 1
+grid .n.m.subtitle -row 1 -column 0 -sticky NESW -pady {0 5}
+grid .n.m.subtitle.lbl -row 0 -column 0 -padx {0 5}
+grid .n.m.subtitle.entry -row 0 -column 1 -sticky EW
+grid columnconfigure .n.m.subtitle 1 -weight 1
+grid .n.m.players -row 2 -column 0
+grid .n.m.players.p1lbl -row 0 -column 0
+grid .n.m.players.p1name -row 0 -column 1
+grid .n.m.players.p1country -row 0 -column 2
+grid .n.m.players.p1score -row 0 -column 3
+grid .n.m.players.p1win -row 0 -column 4 -padx {5 0} -rowspan 2 -sticky NS
+grid .n.m.players.p1teamlbl -row 1 -column 0
+grid .n.m.players.p1team -row 1 -column 1 -columnspan 3 -sticky EW
+grid .n.m.players.separator -row 2 -column 0 -columnspan 5 -pady 10 -sticky EW
+grid .n.m.players.p2lbl -row 3 -column 0
+grid .n.m.players.p2name -row 3 -column 1
+grid .n.m.players.p2country -row 3 -column 2
+grid .n.m.players.p2score -row 3 -column 3
+grid .n.m.players.p2win -row 3 -column 4 -padx {5 0} -rowspan 2 -sticky NS
+grid .n.m.players.p2teamlbl -row 4 -column 0
+grid .n.m.players.p2team -row 4 -column 1 -columnspan 3 -sticky EW
+grid .n.m.buttons -row 3 -column 0 -sticky W -pady {10 0}
+grid .n.m.buttons.apply -row 0 -column 0
+grid .n.m.buttons.discard -row 0 -column 1
+grid .n.m.buttons.reset -row 0 -column 2
+grid .n.m.buttons.swap -row 0 -column 3
+grid .n.m.status -row 4 -column 0 -columnspan 5 -pady {10 0} -sticky EW
+grid columnconfigure .n.m.players 2 -pad 5
+grid columnconfigure .n.m.buttons 1 -pad 15
+grid columnconfigure .n.m.buttons 3 -pad 15
+grid rowconfigure .n.m.players 1 -pad 5
+grid rowconfigure .n.m.players 3 -pad 5
+
+# start.gg tab:
-grid .c -row 0 -column 0 -sticky NESW
-grid .c.description -row 0 -column 0 -sticky NESW -pady {0 5}
-grid .c.description.lbl -row 0 -column 0 -padx {0 5}
-grid .c.description.entry -row 0 -column 1 -sticky EW
-grid columnconfigure .c.description 1 -weight 1
-grid .c.subtitle -row 1 -column 0 -sticky NESW -pady {0 5}
-grid .c.subtitle.lbl -row 0 -column 0 -padx {0 5}
-grid .c.subtitle.entry -row 0 -column 1 -sticky EW
-grid columnconfigure .c.subtitle 1 -weight 1
-grid .c.players -row 2 -column 0
-grid .c.players.p1lbl -row 0 -column 0
-grid .c.players.p1name -row 0 -column 1
-grid .c.players.p1country -row 0 -column 2
-grid .c.players.p1score -row 0 -column 3
-grid .c.players.p1win -row 0 -column 4 -padx {5 0} -rowspan 2 -sticky NS
-grid .c.players.p1teamlbl -row 1 -column 0
-grid .c.players.p1team -row 1 -column 1 -columnspan 3 -sticky EW
-grid .c.players.separator -row 2 -column 0 -columnspan 5 -pady 10 -sticky EW
-grid .c.players.p2lbl -row 3 -column 0
-grid .c.players.p2name -row 3 -column 1
-grid .c.players.p2country -row 3 -column 2
-grid .c.players.p2score -row 3 -column 3
-grid .c.players.p2win -row 3 -column 4 -padx {5 0} -rowspan 2 -sticky NS
-grid .c.players.p2teamlbl -row 4 -column 0
-grid .c.players.p2team -row 4 -column 1 -columnspan 3 -sticky EW
-grid .c.buttons -row 3 -column 0 -sticky W -pady {10 0}
-grid .c.buttons.apply -row 0 -column 0
-grid .c.buttons.discard -row 0 -column 1
-grid .c.buttons.reset -row 0 -column 2
-grid .c.buttons.swap -row 0 -column 3
-grid .c.status -row 4 -column 0 -columnspan 5 -pady {10 0} -sticky EW
+.n select .n.s; # for debug only
+ttk::label .n.s.tokenlbl -text "Personal token: "
+ttk::entry .n.s.token -show * -textvariable startgg(token)
+ttk::label .n.s.tournamentlbl -text "Tournament slug: "
+ttk::entry .n.s.tournamentslug -textvariable startgg(slug)
+ttk::button .n.s.fetch -text "Fetch players" -command fetchplayers
+ttk::label .n.s.msg -textvariable startgg(msg)
-grid columnconfigure .c.players 2 -pad 5
-grid columnconfigure .c.buttons 1 -pad 15
-grid columnconfigure .c.buttons 3 -pad 15
-grid rowconfigure .c.players 1 -pad 5
-grid rowconfigure .c.players 3 -pad 5
+grid .n.s.tokenlbl -row 0 -column 0 -sticky W
+grid .n.s.token -row 0 -column 1 -sticky EW
+grid .n.s.tournamentlbl -row 1 -column 0 -sticky W
+grid .n.s.tournamentslug -row 1 -column 1 -sticky EW
+grid .n.s.fetch -row 2 -column 1 -stick W
+grid .n.s.msg -row 3 -column 1 -stick W
+grid columnconfigure .n.s 1 -weight 1
+grid rowconfigure .n.s 1 -pad 5
# The following procs constitute a very simple line-based IPC system where Tcl
# client talks to Go server via stdin/stdout.
-proc initialize {b64icon webport countrycodes} {
+proc initialize {b64icon webport countrycodes startgg_token startgg_slug} {
seticon $b64icon
set ::mainstatus "Point your OBS browser source to http://localhost:${webport}"
- .c.players.p1country configure -values $countrycodes
- .c.players.p2country configure -values $countrycodes
-
+ .n.m.players.p1country configure -values $countrycodes
+ .n.m.players.p2country configure -values $countrycodes
+ set ::startgg(token) $startgg_token
+ set ::startgg(slug) $startgg_slug
readscoreboard
setupdiffcheck
readplayernames
@@ -194,8 +225,8 @@ proc readplayernames {} {
lappend playernames $line
set line [gets stdin]
}
- .c.players.p1name configure -values $playernames
- .c.players.p2name configure -values $playernames
+ .n.m.players.p1name configure -values $playernames
+ .n.m.players.p2name configure -values $playernames
}
proc setupplayersuggestion {} {
@@ -204,7 +235,7 @@ proc setupplayersuggestion {} {
return
}
set newvalue $::scoreboard($key)
- set widget .c.players.$key
+ set widget .n.m.players.$key
set matches [searchplayers $newvalue]
$widget configure -values $matches
}
@@ -223,6 +254,23 @@ proc searchplayers {query} {
return $playernames
}
+proc fetchplayers {} {
+ .n.s.fetch configure -state disabled
+ .n.s.token configure -state disabled
+ .n.s.tournamentslug configure -state disabled
+ set ::startgg(msg) "Fetching..."
+ puts fetchplayers
+ puts $::startgg(token)
+ puts $::startgg(slug)
+}
+
+proc fetchplayers__resp {} {
+ set ::startgg(msg) [gets stdin]
+ .n.s.fetch configure -state normal
+ .n.s.token configure -state normal
+ .n.s.tournamentslug configure -state normal
+}
+
proc discardscoreboard {} {
foreach key [array names ::scoreboard] {
set ::scoreboard($key) $::applied_scoreboard($key)