mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 16:42:41 +00:00
Add space groups, fix HLTB scraper, fix quote wall E2EE, improve tarot prompts
Space groups: automatic room grouping via membership overlap percentage. Rooms with sufficient shared members are grouped together so leaderboards, trivia scores, and stats aggregate across the community. Uses strict clique-based algorithm (every room must meet threshold with every other room in group). Configurable via SPACE_GROUP_THRESHOLD env var. Persisted to SQLite, recomputed hourly and on startup. HLTB scraper: rewrote for new howlongtobeat.com API (two-step token auth via /api/finder/init + /api/finder endpoint). Quote wall: fix E2EE decrypt flow for reply-to-save (ParseRaw before Decrypt, skip ParseRaw after Decrypt returns pre-parsed event). Fix subcommand matching for delete/search. Remove broken star-reaction handler and old quote handler from user.go. Other fixes: - Strip Matrix reply fallback before command detection (main.go) - Increase Ollama context window to 8192 - Improve tarot spread prompts (~4x longer, narrative arc) - Add "cards are props" instruction to tarot LLM prompt - Fix movies.go CommandDef name mismatch - Add Community category to !help - Remove daily horoscope broadcast cron - Update README with all changes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -329,11 +330,73 @@ func (p *FunPlugin) handleHLTB(ctx MessageContext) error {
|
||||
return p.SendMessage(ctx.RoomID, result)
|
||||
}
|
||||
|
||||
func scrapeHLTB(gameName string) (string, error) {
|
||||
payload := fmt.Sprintf(`{"searchType":"games","searchTerms":["%s"],"searchPage":1,"size":1,"searchOptions":{"games":{"userId":0,"platform":"","sortCategory":"popular","rangeCategory":"main","rangeTime":{"min":null,"max":null},"gameplay":{"perspective":"","flow":"","genre":"","subGenre":""},"rangeYear":{"min":"","max":""},"modifier":""},"users":{"sortCategory":"postcount"},"lists":{"sortCategory":"follows"},"filter":"","sort":0,"randomizer":0}}`,
|
||||
strings.ReplaceAll(gameName, `"`, `\"`))
|
||||
// hltbFetchToken gets a short-lived auth token from the HLTB finder init endpoint.
|
||||
func hltbFetchToken() (string, error) {
|
||||
client := &http.Client{Timeout: 5 * time.Second}
|
||||
url := fmt.Sprintf("https://howlongtobeat.com/api/finder/init?t=%d", time.Now().UnixMilli())
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36")
|
||||
req.Header.Set("Referer", "https://howlongtobeat.com")
|
||||
req.Header.Set("Origin", "https://howlongtobeat.com")
|
||||
|
||||
req, err := http.NewRequest("POST", "https://howlongtobeat.com/api/search", strings.NewReader(payload))
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if result.Token == "" {
|
||||
return "", fmt.Errorf("empty token from HLTB")
|
||||
}
|
||||
return result.Token, nil
|
||||
}
|
||||
|
||||
func scrapeHLTB(gameName string) (string, error) {
|
||||
token, err := hltbFetchToken()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("hltb token: %w", err)
|
||||
}
|
||||
|
||||
payload := map[string]interface{}{
|
||||
"searchType": "games",
|
||||
"searchTerms": []string{gameName},
|
||||
"searchPage": 1,
|
||||
"size": 1,
|
||||
"searchOptions": map[string]interface{}{
|
||||
"games": map[string]interface{}{
|
||||
"userId": 0,
|
||||
"platform": "",
|
||||
"sortCategory": "popular",
|
||||
"rangeCategory": "main",
|
||||
"rangeTime": map[string]interface{}{"min": nil, "max": nil},
|
||||
"gameplay": map[string]interface{}{"perspective": "", "flow": "", "genre": "", "subGenre": "", "difficulty": ""},
|
||||
"rangeYear": map[string]interface{}{"min": "", "max": ""},
|
||||
"modifier": "",
|
||||
},
|
||||
"users": map[string]interface{}{"sortCategory": "postcount"},
|
||||
"lists": map[string]interface{}{"sortCategory": "follows"},
|
||||
"filter": "",
|
||||
"sort": 0,
|
||||
"randomizer": 0,
|
||||
},
|
||||
"useCache": true,
|
||||
}
|
||||
|
||||
data, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", "https://howlongtobeat.com/api/finder", bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -342,6 +405,7 @@ func scrapeHLTB(gameName string) (string, error) {
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36")
|
||||
req.Header.Set("Referer", "https://howlongtobeat.com")
|
||||
req.Header.Set("Origin", "https://howlongtobeat.com")
|
||||
req.Header.Set("x-auth-token", token)
|
||||
|
||||
client := &http.Client{Timeout: 15 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
@@ -355,14 +419,18 @@ func scrapeHLTB(gameName string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return "", fmt.Errorf("hltb HTTP %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var result struct {
|
||||
Data []struct {
|
||||
GameName string `json:"game_name"`
|
||||
CompMain float64 `json:"comp_main"`
|
||||
CompPlus float64 `json:"comp_plus"`
|
||||
CompAll float64 `json:"comp_100"`
|
||||
ProfileDev string `json:"profile_dev"`
|
||||
ReleaseWorld int `json:"release_world"`
|
||||
GameName string `json:"game_name"`
|
||||
CompMain float64 `json:"comp_main"`
|
||||
CompPlus float64 `json:"comp_plus"`
|
||||
CompAll float64 `json:"comp_100"`
|
||||
ProfileDev string `json:"profile_dev"`
|
||||
ReleaseWorld int `json:"release_world"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
@@ -388,7 +456,7 @@ func scrapeHLTB(gameName string) (string, error) {
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
sb.WriteString(fmt.Sprintf("🎮 **%s**\n", game.GameName))
|
||||
sb.WriteString(fmt.Sprintf("\U0001f3ae **%s**\n", game.GameName))
|
||||
sb.WriteString(fmt.Sprintf(" Main Story: %s\n", formatTime(game.CompMain)))
|
||||
sb.WriteString(fmt.Sprintf(" Main + Extras: %s\n", formatTime(game.CompPlus)))
|
||||
sb.WriteString(fmt.Sprintf(" Completionist: %s\n", formatTime(game.CompAll)))
|
||||
|
||||
Reference in New Issue
Block a user