From 9f0ed3ac65cf78b128dfbe85b9768d9a5cf13c34 Mon Sep 17 00:00:00 2001 From: "Evgeny @ SimpleX Chat" <259188159+evgeny-simplex@users.noreply.github.com> Date: Fri, 25 Sep 2026 10:55:41 +0000 Subject: [PATCH] replies --- apps/simplex-calculator-bot/README.md | 2 +- apps/simplex-calculator-bot/calculator.test.ts | 14 +++++++++++++- apps/simplex-calculator-bot/calculatorBot.test.ts | 5 ++++- apps/simplex-calculator-bot/src/calculator.ts | 10 ++++++---- apps/simplex-calculator-bot/src/calculatorBot.ts | 10 +++++++--- 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/apps/simplex-calculator-bot/README.md b/apps/simplex-calculator-bot/README.md index 7075862b3f..05a1cc1d4c 100644 --- a/apps/simplex-calculator-bot/README.md +++ b/apps/simplex-calculator-bot/README.md @@ -8,7 +8,7 @@ Each user who connects via the bot's business address receives a calculator mess - `%` is computed as in Apple's calculator: `100 + 15 % =` equals 115. - Tap `C` to clear the number, and `C` again to clear the calculation. - Numbers are shown without exponent, up to 15 digits; larger results are shown as `Error`. -- You can also send numbers, expressions like `(2 + 3) × 4`, which are entered as one number, and keys like `+`, `=`, `add` or `c`. +- You can also send numbers and keys like `+`, `=`, `add` or `c`. Expressions like `(2 + 3) × 4` are entered as one number, and the bot replies to them with the result. - The bot deletes tapped keys and sends each calculation after `=`. - After 10 minutes without input, the bot turns the calculator off and discards the calculation; tap any key to turn it on. - The bot sends keys like `/+` to apps with chat protocol version 21 or later, and keys like `/add` to older apps. diff --git a/apps/simplex-calculator-bot/calculator.test.ts b/apps/simplex-calculator-bot/calculator.test.ts index d45bb98e83..54083d1f9f 100644 --- a/apps/simplex-calculator-bot/calculator.test.ts +++ b/apps/simplex-calculator-bot/calculator.test.ts @@ -6,7 +6,7 @@ type Input = {tap: string} | {text: string} function run(inputs: Input[]): {calc: Calc, logLines: string[]} { const logLines: string[] = [] const calc = inputs.reduce((current, input) => { - const [next, logLine] = "tap" in input ? press(current, input.tap as Key) : textInput(input.text)!(current) + const [next, logLine] = "tap" in input ? press(current, input.tap as Key) : textInput(input.text)!.update(current) if (logLine) logLines.push(logLine) return next }, initialCalc) @@ -142,6 +142,18 @@ describe("typed messages", () => { expect(type("10", "+", "25=").calc.display).toBe("35") }) + test("result only for expressions", () => { + const result = (text: string) => textInput(text)?.result + expect(result("(2 + 3) × 4")).toBe("20") + expect(result("(2 + 3) × 4 =")).toBe("20") + expect(result("15%")).toBe("0.15") + expect(result("1 / 0")).toBe("Error") + expect(result("25")).toBeUndefined() + expect(result("-5")).toBeUndefined() + expect(result("25=")).toBeUndefined() + expect(result("+")).toBeUndefined() + }) + test("overflow in typed numbers", () => { expect(type("1234567890123456").calc.display).toBe("Error") expect(type("123456789012345").calc.display).toBe("123456789012345") diff --git a/apps/simplex-calculator-bot/calculatorBot.test.ts b/apps/simplex-calculator-bot/calculatorBot.test.ts index aed83d45e7..10b927e6f2 100644 --- a/apps/simplex-calculator-bot/calculatorBot.test.ts +++ b/apps/simplex-calculator-bot/calculatorBot.test.ts @@ -31,6 +31,9 @@ const calculatorShows = (display: string): ItemCheck => ci => ci.chatItem.meta.i const hasText = (text: string): ItemCheck => ci => ci.chatItem.meta.itemText === text +const repliesTo = (quoted: string, text: string): ItemCheck => ({chatItem}) => + chatItem.meta.itemText === text && chatItem.quotedItem?.content.text === quoted + test("calculator in business chat", async () => { const dir = mkdtempSync(join(tmpdir(), "calculator-bot-")) const botDbOpts: bot.BotDbOpts = {type: "sqlite", filePrefix: join(dir, "bot")} @@ -54,7 +57,7 @@ test("calculator in business chat", async () => { for (const event of received) expect(await event).toBeDefined() } await send(["/2", "/+", "/2", "/="], hasText("2 + 2 = 4"), calculatorShows("4")) - await send(["2 × (3 + 4) - 1"], calculatorShows("13")) + await send(["2 × (3 + 4) - 1"], repliesTo("2 × (3 + 4) - 1", "= 13"), calculatorShows("13")) await send(["25", "+", "25", "="], hasText("25 + 25 = 50"), calculatorShows("50")) } finally { await alice.close() diff --git a/apps/simplex-calculator-bot/src/calculator.ts b/apps/simplex-calculator-bot/src/calculator.ts index f27fc840b5..a2b8f23763 100644 --- a/apps/simplex-calculator-bot/src/calculator.ts +++ b/apps/simplex-calculator-bot/src/calculator.ts @@ -157,17 +157,19 @@ function format(n: number): string { return trimmed === "-0" ? "0" : trimmed } -export function textInput(text: string): Update | undefined { +export function textInput(text: string): {update: Update, result?: string} | undefined { const input = text.replace(/\s/g, "") - const n = expressionValue(input.replace(/=$/, "")) + const entry = input.replace(/=$/, "") + const n = expressionValue(entry) if (n !== undefined) { - return calc => { + const update: Update = calc => { const entered = showNumber(calc, n) return input.endsWith("=") ? press(entered, "=") : [entered] } + return {update, result: Number.isNaN(Number(entry)) ? format(n) : undefined} } const key = keyNames.get(input.toLowerCase()) - return key ? calc => press(calc, key) : undefined + return key ? {update: calc => press(calc, key)} : undefined } const tokenPattern = /\d+(?:\.\d*)?|\.\d+|[-+−×x*÷\/%()]/g diff --git a/apps/simplex-calculator-bot/src/calculatorBot.ts b/apps/simplex-calculator-bot/src/calculatorBot.ts index 1f6acb0ca8..d54ca61dfb 100644 --- a/apps/simplex-calculator-bot/src/calculatorBot.ts +++ b/apps/simplex-calculator-bot/src/calculatorBot.ts @@ -68,9 +68,13 @@ const keyCommands = Object.fromEntries([...keyNames].map(([name, key]) => [name, async function onMessage(ci: T.AChatItem, content: T.MsgContent, chat: api.ChatApi): Promise { const sender = groupSender(ci) if (!sender || content.type !== "text") return - const update = textInput(content.text) - if (update) await updateCalculator(chat, sender, update) - else await chat.apiSendTextReply(ci, hint) + const input = textInput(content.text) + if (!input) { + await chat.apiSendTextReply(ci, hint) + return + } + if (input.result) await chat.apiSendTextReply(ci, `= ${input.result}`) + await updateCalculator(chat, sender, input.update) } export function runCalculatorBot(dbOpts: bot.BotDbOpts): Promise<[api.ChatApi, T.User, T.UserContactLink | undefined]> {