diff --git a/src/modules/wordle/handlers.js b/src/modules/wordle/handlers.js index 1455b92..aa6d6cf 100644 --- a/src/modules/wordle/handlers.js +++ b/src/modules/wordle/handlers.js @@ -24,10 +24,6 @@ import wordsData from "./words-data.js"; const words = wordsData; const wordSet = makeWordSet(words); -// All replies use HTML parse mode so renderGuess/renderBoard
blocks
-// render as a monospace code box; emoji markers then align with letter columns.
-const HTML = { parse_mode: "HTML" };
-
/**
* @param {import("grammy").Context} ctx
* @returns {number|null}
@@ -90,8 +86,8 @@ export async function handleWordle(ctx, db) {
? `๐ Solved in ${game.guesses.length}/${MAX_GUESSES}. /wordle_new for another.`
: game.giveup
? `๐ณ๏ธ Gave up. Answer was ${game.target.toUpperCase()}. /wordle_new for another.`
- : `Guess ${game.guesses.length}/${MAX_GUESSES}. Use /wordle <word>.`;
- return ctx.reply(`${header}\n\n${renderBoard(game.guesses)}`, HTML);
+ : `Guess ${game.guesses.length}/${MAX_GUESSES}. Use \`/wordle \`.`;
+ return ctx.reply(`${header}\n\n${renderBoard(game.guesses)}`);
}
if (isFinished(game)) {
@@ -109,22 +105,20 @@ export async function handleWordle(ctx, db) {
if (won) game.solved = true;
await saveGame(db, subject, game);
- const reply = renderGuess(results);
+ const reply = renderGuess(validated.word, results);
if (won) {
const s = await recordResult(db, subject, true);
return ctx.reply(
`${reply}\n\n๐ Solved in ${game.guesses.length}/${MAX_GUESSES}! Streak: ${s.streak}. /wordle_new for another.`,
- HTML,
);
}
if (game.guesses.length >= MAX_GUESSES) {
await recordResult(db, subject, false);
return ctx.reply(
`${reply}\n\nโ Out of guesses. Answer was ${game.target.toUpperCase()}. /wordle_new to retry.`,
- HTML,
);
}
- return ctx.reply(`${reply}\n\nGuess ${game.guesses.length}/${MAX_GUESSES}.`, HTML);
+ return ctx.reply(`${reply}\n\nGuess ${game.guesses.length}/${MAX_GUESSES}.`);
}
/**
@@ -143,10 +137,7 @@ export async function handleNew(ctx, db) {
}
await startFreshGame(db, subject);
- return ctx.reply(
- `${prelude}๐ New round started. Use /wordle <word> to guess.`,
- HTML,
- );
+ return ctx.reply(`${prelude}๐ New round started. Use \`/wordle \` to guess.`);
}
/**
diff --git a/src/modules/wordle/render.js b/src/modules/wordle/render.js
index a35b1e6..2ff9d77 100644
--- a/src/modules/wordle/render.js
+++ b/src/modules/wordle/render.js
@@ -1,57 +1,38 @@
/**
- * @file Render wordle comparison results as a Telegram monospace grid.
+ * @file Render wordle comparison results for Telegram.
*
- * Each guess renders as two lines โ colored markers over the upper-case
- * letters โ wrapped in an HTML block so Telegram renders it in
- * monospace and the emoji column widths line up with the letter columns.
+ * Uses the NYT Wordle share format โ guess word on one line, colored marker
+ * row below โ so there's no cross-client column-alignment dependency:
+ *
+ * CRANE
+ * ๐ฉ๐จโฌ๐ฉ๐ฉ
*
* ๐ฉ correct ยท ๐จ partial ยท โฌ wrong
*
- * Output strings are intended to be sent with `parse_mode: "HTML"`. All
- * game content is validated `[a-z]` so no HTML escaping is needed inside
- * the grid; only callers embedding user-controlled text around the grid
- * need to escape it.
+ * Output is plain text; no HTML parse mode required.
*/
const MARKER = { correct: "๐ฉ", partial: "๐จ", wrong: "โฌ" };
-// Alignment under Telegram : the color markers are emoji; to guarantee
-// the letter row is the same width, render letters as emoji too. Unicode
-// block "Enclosed Alphanumeric Supplement" has emoji-class capitals at
-// U+1F170..FF189 (๐
ฐ๐
ฑ๐
ฒ..๐ โ "Negative Squared Latin Capital Letter").
-// Because both rows draw from the emoji font, column widths match 1-to-1
-// on every client, regardless of the monospace font shipped with it.
-const EMOJI_A = 0x1f170;
-const ASCII_A = 0x41;
-
-function toEmojiLetter(ch) {
- const code = ch.toUpperCase().charCodeAt(0);
- if (code >= ASCII_A && code <= 0x5a) {
- return String.fromCodePoint(EMOJI_A + (code - ASCII_A));
- }
- return ch;
-}
-
-function rowPair(results) {
+function rowPair({ word, results }) {
const markers = results.map((r) => MARKER[r.result] ?? "โฌ").join("");
- const letters = results.map((r) => toEmojiLetter(r.letter)).join("");
- return `${markers}\n${letters}`;
+ return `${word.toUpperCase()}\n${markers}`;
}
/**
- * Render a single guess row as an HTML block.
+ * Render a single guess (word over colors).
+ * @param {string} word โ the submitted guess (lowercase a-z)
* @param {ReturnType} results
*/
-export function renderGuess(results) {
- return `${rowPair(results)}`;
+export function renderGuess(word, results) {
+ return rowPair({ word, results });
}
/**
- * Render the full board (all prior guesses, blank-line separated) as a
- * single HTML block, so all rows share one monospace code box.
+ * Render the full board (all prior guesses, blank-line separated).
* @param {Array<{word:string, results: any[]}>} guesses
*/
export function renderBoard(guesses) {
- if (guesses.length === 0) return "No guesses yet. Reply with /wordle <word>.";
- return `${guesses.map((g) => rowPair(g.results)).join("\n\n")}`;
+ if (guesses.length === 0) return "No guesses yet. Reply with `/wordle `.";
+ return guesses.map((g) => rowPair(g)).join("\n\n");
}