package bot import ( "bytes" "context" "log" "matrix-bot/game" "matrix-bot/wordleimg" "matrix-bot/words" "os" "strconv" "strings" "maunium.net/go/mautrix" "maunium.net/go/mautrix/event" ) var dict *words.Dictionary type roomUserPair struct { room string user string } var wordleGames = make(map[roomUserPair]game.Wordle) var userSettings = make(map[string]map[string]string) var defaultSettings = map[string]string{ "wordletheme": "dark", } func Load() { var err error wordListPath := os.Getenv("MATRIX_WORDLIST") if wordListPath == "" { wordListPath = "sowpods.csv" } dict, err = words.New(wordListPath) if err != nil { log.Fatal(err) } } func HandleCommand(body string, sender string, roomID string, ctx context.Context, client *mautrix.Client, evt *event.Event, replyTo *event.RelatesTo) []interface{} { pair := roomUserPair{roomID, sender} part := strings.Fields(strings.ToLower(body)) _, e := userSettings[sender] if !e { userSettings[sender] = make(map[string]string) for key, value := range defaultSettings { userSettings[sender][key] = value } } us := userSettings[sender] if equals(part[0], "!settings", "!setts", "!stns", "!?") { if len(part) < 2 { } else if equals(part[1], "set", "add", "s", "a") { if len(part) == 4 { userSettings[sender][part[2]] = part[3] us = userSettings[sender] } } else if equals(part[1], "delete", "del", "remove", "rem", "d", "r") { if len(part) == 3 { delete(userSettings[sender], part[2]) } } b := strings.Builder{} b.WriteString("Your settings: ") for k, s := range us { b.WriteString("\n" + k + " : " + s) } return jia(txt(b.String(), replyTo)) } else if equals(part[0], "!wordle", "!wrdl", "!w") { wg, wgEx := wordleGames[pair] wordleState := func() event.MessageEventContent { state := wg.GetState() if setting(us, "wordletheme") == "ascii" { return txt(state, nil) } else { theme, e := wordleimg.Themes[setting(us, "wordletheme")] if !e { theme = wordleimg.Themes["dark"] } imgData, w, h, err := wordleimg.RenderHistoryImage(state, theme, len(wg.GetSecret()), 6) if err != nil { log.Println("Image render error:", err) } return img(imgData, w, h, state, ctx, client) } } if len(part) < 2 { part = append(part, "daily") } if equals(part[1], "daily", "day", "d") { if wgEx { return jia(txt("Wordle:\nAlready exist", replyTo)) } word := dict.DailyWord(5) wordleGames[pair] = game.NewWordle(word, 6) return jia(txt("Wordle:\nDaily started ("+roomID+" "+sender+")\nLength: "+ strconv.Itoa(len(word))+"\n[CAN BE PLAYED ONCE A DAY, DO NOT END!]", replyTo)) } else if equals(part[1], "normal", "play", "p") { if wgEx { return jia(txt("Wordle:\nAlready exist", replyTo)) } length := 5 if len(part) > 2 { if l, err := strconv.Atoi(part[2]); err == nil { length = l } } word, err := dict.RandomWord(length) if err != nil { return jia(txt("Wordle:\nNo words of that length", replyTo)) } wordleGames[pair] = game.NewWordle(word, 6) return jia(txt("Wordle:\nInitilized ("+roomID+" "+sender+")\nLength: "+strconv.Itoa(length), replyTo)) } else if equals(part[1], "guess", "gus", "g") && len(part) > 2 { if !wgEx { return jia(txt("Wordle:\nNot found (use !wordle play)", replyTo)) } else if !wg.ValidLength(part[2]) { return jia(txt("Wordle:\nWord guess length is bad", replyTo)) } else if !dict.Contains(part[2]) { return jia(txt("Wordle:\nSorry but idk that word", replyTo)) } sig := wg.Guess(part[2]) if sig == 0 { return jia(txt("Wordle:", replyTo), wordleState()) } delete(wordleGames, pair) switch sig { case -1: return jia(txt("Wordle:\nEnded in lost, word was "+wg.GetSecret(), replyTo), wordleState()) case 1: return jia(txt("Wordle:\nYou guessed right!", replyTo), wordleState()) } } else if equals(part[1], "status", "stat", "s") { if !wgEx { return jia(txt("Wordle:\nNone found", replyTo)) } return jia(txt("Wordle:", replyTo), wordleState()) } else if equals(part[1], "forfit", "end", "e") { if !wgEx { return jia(txt("Wordle:\nNone to end", replyTo)) } delete(wordleGames, pair) return jia(txt("Wordle:\nEnded", replyTo)) } } return nil } func jia(i ...interface{}) []interface{} { return i } func txt(body string, relates *event.RelatesTo) event.MessageEventContent { return event.MessageEventContent{ MsgType: event.MsgText, Body: body, RelatesTo: relates, } } func img(data []byte, width int, height int, body string, ctx context.Context, client *mautrix.Client) event.MessageEventContent { uploadResp, err := client.UploadMedia( ctx, mautrix.ReqUploadMedia{ FileName: "temp.png", Content: bytes.NewReader(data), ContentType: "image/png", }, ) if err != nil { log.Println("Upload error:", err) return event.MessageEventContent{} } return event.MessageEventContent{ MsgType: event.MsgImage, Body: body, URL: uploadResp.ContentURI.CUString(), Info: &event.FileInfo{ MimeType: "image/png", Size: len(data), Height: height, Width: width, }, } } func setting(us map[string]string, key string) string { if v, e := us[key]; e { return v } return "" } func equals(body string, prefixes ...string) bool { for _, prefix := range prefixes { if strings.Compare(body, prefix) == 0 { return true } } return false }