mirror of
https://github.com/tiennm99/tiennm99bot.git
synced 2026-10-13 02:14:55 +00:00
/wordledaily sends a BotFather game card whose page plays one shared word per day, changing at 07:00 ICT. The server checks guesses, saves each player's board after every guess and keeps NYT-style stats. A win is reported with setGameScore, and in a group one live results message shows everyone's colour grids. /wordledaily_subscribe and /wordledaily_unsubscribe opt a chat or topic in to the 07:00 push: yesterday's recap, opening with the answer and the group's win streak, then today's card. The HTML5 game plumbing that noitu used is now shared in util/htmlgame, and the wordle word list and scoring live in wordle/wordlist.
50 lines
1.1 KiB
Go
50 lines
1.1 KiB
Go
package htmlgame
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Limiter allows at most Per requests per key within any Window (a sliding
|
|
// window). It is in-memory: a restart forgets it, which only resets the
|
|
// window. Keys with no request in the last Window are pruned as it runs.
|
|
type Limiter struct {
|
|
Per int
|
|
Window time.Duration
|
|
|
|
mu sync.Mutex
|
|
hits map[int64][]time.Time
|
|
pruned time.Time
|
|
}
|
|
|
|
// Allow records a request for key at now and reports whether it is within
|
|
// the limit. A refused request is not recorded.
|
|
func (l *Limiter) Allow(key int64, now time.Time) bool {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
if l.hits == nil {
|
|
l.hits = map[int64][]time.Time{}
|
|
}
|
|
cutoff := now.Add(-l.Window)
|
|
if now.Sub(l.pruned) >= l.Window {
|
|
for k, ts := range l.hits {
|
|
if len(ts) == 0 || !ts[len(ts)-1].After(cutoff) {
|
|
delete(l.hits, k)
|
|
}
|
|
}
|
|
l.pruned = now
|
|
}
|
|
recent := l.hits[key]
|
|
i := 0
|
|
for i < len(recent) && !recent[i].After(cutoff) {
|
|
i++
|
|
}
|
|
recent = recent[i:]
|
|
if len(recent) >= l.Per {
|
|
l.hits[key] = recent
|
|
return false
|
|
}
|
|
l.hits[key] = append(recent, now)
|
|
return true
|
|
}
|