@@ -14,6 +14,7 @@ import (
1414 "github.com/charmbracelet/wish"
1515 "github.com/charmbracelet/wish/bubbletea"
1616 "github.com/charmbracelet/wish/logging"
17+ "github.com/f-gillmann/wordle-ssh/internal/stats"
1718 "github.com/f-gillmann/wordle-ssh/internal/ui"
1819 "github.com/f-gillmann/wordle-ssh/internal/wordle"
1920)
@@ -22,13 +23,15 @@ const (
2223 defaultHost = "0.0.0.0"
2324 defaultPort = "23234"
2425 defaultHostKeyPath = ".ssh/id_ed25519"
26+ defaultDBPath = "./wordle-stats.db"
2527)
2628
2729// Config holds the server configuration
2830type Config struct {
2931 Host string
3032 Port string
3133 HostKeyPath string
34+ DBPath string
3235 Logger * log.Logger
3336 LogLevel log.Level
3437}
@@ -50,6 +53,11 @@ func LoadConfigFromEnv() Config {
5053 hostKeyPath = defaultHostKeyPath
5154 }
5255
56+ dbPath := os .Getenv ("WORDLE_SSH_DB_PATH" )
57+ if dbPath == "" {
58+ dbPath = defaultDBPath
59+ }
60+
5361 logLevel := os .Getenv ("WORDLE_SSH_LOG_LEVEL" )
5462 var level log.Level
5563
@@ -70,6 +78,7 @@ func LoadConfigFromEnv() Config {
7078 Host : host ,
7179 Port : port ,
7280 HostKeyPath : hostKeyPath ,
81+ DBPath : dbPath ,
7382 LogLevel : level ,
7483 }
7584}
@@ -80,6 +89,7 @@ type Server struct {
8089 wordleWord string
8190 wordleDate string
8291 wishServer * ssh.Server
92+ statsStore * stats.Store
8393}
8494
8595// New creates a new SSH server
@@ -96,20 +106,25 @@ func New(config Config) (*Server, error) {
96106 config .HostKeyPath = defaultHostKeyPath
97107 }
98108
109+ if config .DBPath == "" {
110+ config .DBPath = defaultDBPath
111+ }
112+
99113 if config .Logger == nil {
100- config .Logger = log .NewWithOptions (os .Stderr , log.Options {
101- ReportTimestamp : true ,
102- ReportCaller : config .LogLevel == log .DebugLevel ,
103- TimeFormat : "2006/01/02 15:04:05" ,
104- Prefix : "[wordle-ssh]" ,
105- Level : config .LogLevel ,
106- })
114+ return nil , fmt .Errorf ("logger must be provided in config" )
107115 }
108116
109117 s := & Server {
110118 config : config ,
111119 }
112120
121+ // Initialize stats store
122+ statsStore , err := stats .NewStore (config .DBPath , config .Logger )
123+ if err != nil {
124+ return nil , fmt .Errorf ("failed to initialize stats store: %w" , err )
125+ }
126+ s .statsStore = statsStore
127+
113128 // Fetch today's Wordle word
114129 if err := s .refreshWordleWord (); err != nil {
115130 return nil , fmt .Errorf ("failed to fetch wordle word: %w" , err )
@@ -153,15 +168,35 @@ func (s *Server) refreshWordleWord() error {
153168}
154169
155170// teaHandler creates a bubbletea program for each SSH session
156- func (s * Server ) teaHandler (ssh.Session ) (tea.Model , []tea.ProgramOption ) {
171+ func (s * Server ) teaHandler (sshSession ssh.Session ) (tea.Model , []tea.ProgramOption ) {
157172 // Refresh Wordle word if it's a new day
158173 if err := s .refreshWordleWord (); err != nil {
159174 s .config .Logger .Error ("Failed to refresh Wordle word" , "error" , err )
160175 return nil , nil
161176 }
162177
163- // Create the app model with the current word
164- m := ui .NewAppModel (s .wordleWord )
178+ // Get username from SSH session
179+ username := sshSession .User ()
180+ if username == "" {
181+ username = "anonymous"
182+ }
183+
184+ // Check if username is blacklisted
185+ isBlacklisted := stats .IsBlacklisted (username )
186+
187+ // Check if user has already played today (but not for blacklisted users)
188+ hasPlayed := false
189+ if ! isBlacklisted {
190+ var err error
191+ hasPlayed , err = s .statsStore .HasPlayedToday (username , s .wordleDate )
192+
193+ if err != nil {
194+ s .config .Logger .Error ("Failed to check if user played today" , "error" , err , "username" , username )
195+ }
196+ }
197+
198+ // Create the app model with the current word, stats store, and logger
199+ m := ui .NewAppModel (s .wordleWord , s .wordleDate , username , s .statsStore , hasPlayed , isBlacklisted , s .config .Logger )
165200
166201 return m , []tea.ProgramOption {tea .WithAltScreen ()}
167202}
@@ -171,7 +206,7 @@ func (s *Server) Start() error {
171206 done := make (chan os.Signal , 1 )
172207 signal .Notify (done , os .Interrupt , syscall .SIGINT , syscall .SIGTERM )
173208
174- s .config .Logger .Info ("Starting SSH server" , "host" , s .config .Host , "port" , s .config .Port )
209+ s .config .Logger .Info ("Starting SSH server" , "host" , s .config .Host , "port" , s .config .Port , "db" , s . config . DBPath )
175210
176211 go func () {
177212 if err := s .wishServer .ListenAndServe (); err != nil {
@@ -185,6 +220,11 @@ func (s *Server) Start() error {
185220 ctx , cancel := context .WithTimeout (context .Background (), 30 * time .Second )
186221 defer cancel ()
187222
223+ // Close stats store
224+ if err := s .statsStore .Close (); err != nil {
225+ s .config .Logger .Error ("Failed to close stats store" , "error" , err )
226+ }
227+
188228 if err := s .wishServer .Shutdown (ctx ); err != nil {
189229 return fmt .Errorf ("failed to shutdown server: %w" , err )
190230 }
0 commit comments