diff --git a/src/modules/wordle/handlers.js b/src/modules/wordle/handlers.js index 8512456..1455b92 100644 --- a/src/modules/wordle/handlers.js +++ b/src/modules/wordle/handlers.js @@ -24,6 +24,10 @@ 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}
@@ -86,8 +90,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 \`.`;
- return ctx.reply(`${header}\n\n${renderBoard(game.guesses)}`);
+ : `Guess ${game.guesses.length}/${MAX_GUESSES}. Use /wordle <word>.`;
+ return ctx.reply(`${header}\n\n${renderBoard(game.guesses)}`, HTML);
}
if (isFinished(game)) {
@@ -110,15 +114,17 @@ export async function handleWordle(ctx, db) {
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}.`);
+ return ctx.reply(`${reply}\n\nGuess ${game.guesses.length}/${MAX_GUESSES}.`, HTML);
}
/**
@@ -137,7 +143,10 @@ export async function handleNew(ctx, db) {
}
await startFreshGame(db, subject);
- return ctx.reply(`${prelude}๐ New round started. Use \`/wordle \` to guess.`);
+ return ctx.reply(
+ `${prelude}๐ New round started. Use /wordle <word> to guess.`,
+ HTML,
+ );
}
/**
diff --git a/src/modules/wordle/render.js b/src/modules/wordle/render.js
index 4125f62..3678fbf 100644
--- a/src/modules/wordle/render.js
+++ b/src/modules/wordle/render.js
@@ -1,28 +1,40 @@
/**
- * @file Render wordle comparison results as a Telegram-friendly grid.
+ * @file Render wordle comparison results as a Telegram monospace grid.
+ *
+ * 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.
+ *
* ๐ฉ correct ยท ๐จ partial ยท โฌ wrong
*
- * Each guess shows two lines: the colored markers and the upper-case letters
- * underneath so the player can read the word at a glance.
+ * 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.
*/
const MARKER = { correct: "๐ฉ", partial: "๐จ", wrong: "โฌ" };
-/**
- * Render a single guess row (two lines: markers + letters).
- * @param {ReturnType} results
- */
-export function renderGuess(results) {
+function rowPair(results) {
const markers = results.map((r) => MARKER[r.result] ?? "โฌ").join("");
const letters = results.map((r) => ` ${r.letter.toUpperCase()} `).join("");
return `${markers}\n${letters}`;
}
/**
- * Render all prior guesses stacked.
+ * Render a single guess row as an HTML block.
+ * @param {ReturnType} results
+ */
+export function renderGuess(results) {
+ return `${rowPair(results)}`;
+}
+
+/**
+ * Render the full board (all prior guesses, blank-line separated) as a
+ * single HTML block, so all rows share one monospace code box.
* @param {Array<{word:string, results: any[]}>} guesses
*/
export function renderBoard(guesses) {
- if (guesses.length === 0) return "No guesses yet. Reply with `/wordle `.";
- return guesses.map((g) => renderGuess(g.results)).join("\n\n");
+ if (guesses.length === 0) return "No guesses yet. Reply with /wordle <word>.";
+ return `${guesses.map((g) => rowPair(g.results)).join("\n\n")}`;
}