mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-09-27 17:58:47 +00:00
simplify
This commit is contained in:
@@ -1,48 +1,29 @@
|
||||
# SimpleX Calculator
|
||||
|
||||
A pocket calculator in a chat, built with the [SimpleX Chat Node.js library](../../packages/simplex-chat-nodejs/).
|
||||
A calculator bot built with the [SimpleX Chat Node.js library](../../packages/simplex-chat-nodejs/).
|
||||
|
||||
Each user who connects via the bot's business address gets their own chat with a calculator message. The keys are bot commands: tapping a key sends it to the bot, and the bot replaces the calculator message with the updated one.
|
||||
Each user who connects via the bot's business address receives a calculator message with keys as commands. The bot replaces this message after each input.
|
||||
|
||||
## How it works
|
||||
|
||||
- Keys are applied left to right, without operator priority: `2 + 3 × 4 =` gives 20.
|
||||
- `%` follows Apple's calculator: `100 + 15 %` shows 15, and `=` gives 115; `50 × 10 %` shows 0.1, and `=` gives 5.
|
||||
- `C` clears the current number; the second `C` clears the whole calculation.
|
||||
- The display shows up to 15 digits, without exponent; larger results show `Error`.
|
||||
- After `=`, the bot sends the calculation as a message, e.g. `2 + 2 = 4`.
|
||||
- Tapped keys are deleted from the chat; the calculation messages stay as a log.
|
||||
- Users can also send messages instead of tapping keys:
|
||||
- a number or an expression, e.g. `12 × 3 + 4`, is computed and entered as the current number, so `25`, `/add`, `25`, `=` gives 50.
|
||||
- a key, e.g. `+`, `=`, `x`, `*`, `/`, `add` or `c`, works as the tapped key.
|
||||
- The calculator turns off after 10 minutes without use: the number is removed from the display, the keys stay. Any key, number or `/calc` turns it on.
|
||||
- Apps that support commands made of symbols (chat protocol version 21) get keys like `/+`; older apps get keys like `/add`.
|
||||
|
||||
## Install & build
|
||||
|
||||
```bash
|
||||
cd apps/simplex-calculator-bot
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
The bot requires a `simplex-chat` library version that passes the chat API to event handlers (7.1.0-beta.4.1 or later). To run against the in-tree library:
|
||||
|
||||
```bash
|
||||
# In packages/simplex-chat-nodejs
|
||||
npm link
|
||||
|
||||
# In apps/simplex-calculator-bot
|
||||
npm link simplex-chat
|
||||
```
|
||||
- Keys are applied left to right, so `2 + 3 × 4 =` equals 20.
|
||||
- `%` 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 `12 × 3 + 4`, which are entered as one number, and keys like `+`, `=`, `add` or `c`.
|
||||
- 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.
|
||||
|
||||
## Run
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run build
|
||||
npm start
|
||||
```
|
||||
|
||||
The bot prints its address on start. The database is stored in `./data`.
|
||||
The bot prints its address and keeps its data in `./data`.
|
||||
|
||||
To use the library from this repository, build it and run `npm install --no-save ../../packages/simplex-chat-nodejs` instead of `npm install`.
|
||||
|
||||
## Test
|
||||
|
||||
@@ -50,8 +31,4 @@ The bot prints its address on start. The database is stored in `./data`.
|
||||
npm test
|
||||
```
|
||||
|
||||
The end-to-end test runs a local SMP server with the TLS certificates from `tests/fixtures/tls`. On Linux, the test downloads `smp-server` from [simplexmq releases](https://github.com/simplex-chat/simplexmq/releases) to `node_modules/.cache` on the first run. To use another build, or on other systems, pass its path in `SMP_SERVER` variable:
|
||||
|
||||
```bash
|
||||
SMP_SERVER=/path/to/smp-server npm test
|
||||
```
|
||||
A local SMP server is started for the end-to-end test. On Linux, `smp-server` is downloaded from [simplexmq releases](https://github.com/simplex-chat/simplexmq/releases); to use another build, or on other systems, set `SMP_SERVER` to the path of `smp-server`.
|
||||
|
||||
@@ -134,6 +134,8 @@ describe("typed messages", () => {
|
||||
})
|
||||
|
||||
describe("calculator text", () => {
|
||||
const nbsp = String.fromCharCode(0xa0)
|
||||
|
||||
test("symbol keys", () => {
|
||||
expect(calculatorText(initialCalc, true)).toBe([
|
||||
"*0*",
|
||||
@@ -147,12 +149,12 @@ describe("calculator text", () => {
|
||||
|
||||
test("switched off: keys without number", () => {
|
||||
const [displayLine, ...rows] = calculatorText(undefined, true).split("\n")
|
||||
expect(displayLine).toBe("* *")
|
||||
expect(displayLine).toBe(`*${nbsp}*`)
|
||||
expect(rows).toEqual(calculatorText(initialCalc, true).split("\n").slice(1))
|
||||
})
|
||||
|
||||
test("word keys padded to equal width", () => {
|
||||
const pad = (n: number) => `\`${" ".repeat(n)}\``
|
||||
const pad = (n: number) => `\`${nbsp.repeat(n)}\``
|
||||
const [displayLine, firstRow] = calculatorText(initialCalc, false).split("\n")
|
||||
expect(displayLine).toBe("*0*")
|
||||
expect(firstRow).toBe(`/C ${pad(4)}/neg ${pad(2)}/pct ${pad(2)}/div ${pad(2)}`)
|
||||
|
||||
@@ -2,7 +2,7 @@ import {afterAll, beforeAll, test, expect} from "vitest"
|
||||
import {mkdtempSync, rmSync} from "fs"
|
||||
import {tmpdir} from "os"
|
||||
import {join} from "path"
|
||||
import {CEvt, T} from "@simplex-chat/types"
|
||||
import {T} from "@simplex-chat/types"
|
||||
import {api, bot, util} from "simplex-chat"
|
||||
import {runCalculatorBot} from "./src/calculatorBot.js"
|
||||
import {SmpServer, startSmpServer} from "./test/smpServer.js"
|
||||
@@ -25,12 +25,11 @@ async function prepareBotDatabase(dbOpts: bot.BotDbOpts): Promise<void> {
|
||||
await chat.close()
|
||||
}
|
||||
|
||||
const isCalculator = (display: string) => (ci: T.AChatItem) => ci.chatItem.meta.itemText.startsWith(`*${display}*\n`)
|
||||
type ItemCheck = (ci: T.AChatItem) => boolean
|
||||
|
||||
const calculatorShows = (display: string) => ({chatItems}: CEvt.NewChatItems) => chatItems.some(isCalculator(display))
|
||||
const calculatorShows = (display: string): ItemCheck => ci => ci.chatItem.meta.itemText.startsWith(`*${display}*\n`)
|
||||
|
||||
const hasText = (text: string) => ({chatItems}: CEvt.NewChatItems) =>
|
||||
chatItems.some(ci => ci.chatItem.meta.itemText === text)
|
||||
const hasText = (text: string): ItemCheck => ci => ci.chatItem.meta.itemText === text
|
||||
|
||||
test("calculator in business chat", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "calculator-bot-"))
|
||||
@@ -41,29 +40,22 @@ test("calculator in business chat", async () => {
|
||||
const aliceUser = await alice.apiCreateActiveUser({displayName: "alice", fullName: ""})
|
||||
await alice.startChat()
|
||||
await useSmpServer(alice)
|
||||
const receives = (check: ItemCheck) => alice.wait("newChatItems", ({chatItems}) => chatItems.some(check), 30000)
|
||||
try {
|
||||
const [_plan, link] = await alice.apiConnectPlan(aliceUser.userId, util.contactAddressStr(address!.connLinkContact))
|
||||
const firstCalculator = alice.wait("newChatItems", calculatorShows("0"), 30000)
|
||||
const firstCalculator = receives(calculatorShows("0"))
|
||||
await alice.apiConnect(aliceUser.userId, false, link)
|
||||
const calculatorItem = (await firstCalculator)?.chatItems.find(isCalculator("0"))
|
||||
const calculatorItem = (await firstCalculator)?.chatItems.find(calculatorShows("0"))
|
||||
expect(calculatorItem?.chatInfo.type).toBe(T.ChatType.Group)
|
||||
const groupId = (calculatorItem!.chatInfo as T.ChatInfo.Group).groupInfo.groupId
|
||||
|
||||
const logLine = alice.wait("newChatItems", hasText("2 + 2 = 4"), 30000)
|
||||
const four = alice.wait("newChatItems", calculatorShows("4"), 30000)
|
||||
for (const key of ["/2", "/+", "/2", "/="]) await alice.apiSendTextMessage([T.ChatType.Group, groupId], key)
|
||||
expect(await logLine).toBeDefined()
|
||||
expect(await four).toBeDefined()
|
||||
|
||||
const forty = alice.wait("newChatItems", calculatorShows("40"), 30000)
|
||||
await alice.apiSendTextMessage([T.ChatType.Group, groupId], "12 × 3 + 4")
|
||||
expect(await forty).toBeDefined()
|
||||
|
||||
const typedLogLine = alice.wait("newChatItems", hasText("25 + 25 = 50"), 30000)
|
||||
const fifty = alice.wait("newChatItems", calculatorShows("50"), 30000)
|
||||
for (const text of ["25", "+", "25", "="]) await alice.apiSendTextMessage([T.ChatType.Group, groupId], text)
|
||||
expect(await typedLogLine).toBeDefined()
|
||||
expect(await fifty).toBeDefined()
|
||||
const send = async (texts: string[], ...checks: ItemCheck[]) => {
|
||||
const received = checks.map(receives)
|
||||
for (const text of texts) await alice.apiSendTextMessage([T.ChatType.Group, groupId], text)
|
||||
for (const event of received) expect(await event).toBeDefined()
|
||||
}
|
||||
await send(["/2", "/+", "/2", "/="], hasText("2 + 2 = 4"), calculatorShows("4"))
|
||||
await send(["12 × 3 + 4"], calculatorShows("40"))
|
||||
await send(["25", "+", "25", "="], hasText("25 + 25 = 50"), calculatorShows("50"))
|
||||
} finally {
|
||||
await alice.close()
|
||||
await calculator.close()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export type Digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
|
||||
export type Operator = "+" | "-" | "×" | "÷"
|
||||
type Digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
|
||||
type Operator = "+" | "-" | "×" | "÷"
|
||||
export type Key = Digit | Operator | "C" | "±" | "%" | "√" | "." | "="
|
||||
export type Update = (calc: Calc) => [Calc, string?]
|
||||
|
||||
@@ -13,7 +13,7 @@ export interface Calc {
|
||||
|
||||
export const initialCalc: Calc = {display: "0", operand: "0", terms: [], mode: "typing"}
|
||||
|
||||
export const keypad: Key[][] = [
|
||||
const keypad: Key[][] = [
|
||||
["C", "±", "%", "÷"],
|
||||
["7", "8", "9", "×"],
|
||||
["4", "5", "6", "-"],
|
||||
@@ -33,7 +33,7 @@ const keyWords: Partial<Record<Key, string>> = {
|
||||
"=": "eq",
|
||||
}
|
||||
|
||||
export function keyWord(key: Key): string {
|
||||
function keyWord(key: Key): string {
|
||||
return keyWords[key] ?? key
|
||||
}
|
||||
|
||||
@@ -49,18 +49,23 @@ const errorDisplay = "Error"
|
||||
|
||||
export function press(calc: Calc, key: Key): [Calc, string?] {
|
||||
switch (key) {
|
||||
case "C": return [calc.display === "0" || calc.display === errorDisplay ? initialCalc : {...calc, display: "0", operand: "0", mode: "typing"}]
|
||||
case "C": return [clear(calc)]
|
||||
case "=": return equals(calc)
|
||||
case "+": case "-": case "×": case "÷": return [operator(calc, key)]
|
||||
case "%": return [result(calc, percent(calc), `${calc.operand}%`)]
|
||||
case "√": return [result(calc, Math.sqrt(value(calc)), `√${calc.operand}`)]
|
||||
case "%": return [showNumber(calc, percent(calc), `${calc.operand}%`)]
|
||||
case "√": return [showNumber(calc, Math.sqrt(value(calc)), `√${calc.operand}`)]
|
||||
case "±": return [negate(calc)]
|
||||
default: return [enter(calc, key)]
|
||||
}
|
||||
}
|
||||
|
||||
function clear(calc: Calc): Calc {
|
||||
if (calc.display === "0" || calc.display === errorDisplay) return initialCalc
|
||||
return {...calc, display: "0", operand: "0", mode: "typing"}
|
||||
}
|
||||
|
||||
function enter(calc: Calc, key: Digit | "."): Calc {
|
||||
const display = calc.mode === "typing" ? append(calc.display, key) : key === "." ? "0." : key
|
||||
const display = append(calc.mode === "typing" ? calc.display : "0", key)
|
||||
return {...calc, display, operand: display, mode: "typing"}
|
||||
}
|
||||
|
||||
@@ -71,7 +76,7 @@ function append(display: string, key: Digit | "."): string {
|
||||
}
|
||||
|
||||
function negate(calc: Calc): Calc {
|
||||
if (calc.mode !== "typing") return enterNumber(calc, -value(calc))
|
||||
if (calc.mode !== "typing") return showNumber(calc, -value(calc))
|
||||
const display = calc.display.startsWith("-") ? calc.display.slice(1) : `-${calc.display}`
|
||||
return {...calc, display, operand: display}
|
||||
}
|
||||
@@ -82,12 +87,9 @@ function percent(calc: Calc): number {
|
||||
return pending && (pending.op === "+" || pending.op === "-") ? pending.acc * b / 100 : b / 100
|
||||
}
|
||||
|
||||
function enterNumber(calc: Calc, n: number): Calc {
|
||||
return result(calc, n, format(n))
|
||||
}
|
||||
|
||||
function result(calc: Calc, n: number, operand: string): Calc {
|
||||
return {...calc, display: format(n), operand, mode: "result"}
|
||||
function showNumber(calc: Calc, n: number, operand?: string): Calc {
|
||||
const display = format(n)
|
||||
return {...calc, display, operand: operand ?? display, mode: "result"}
|
||||
}
|
||||
|
||||
function operator(calc: Calc, op: Operator): Calc {
|
||||
@@ -132,7 +134,7 @@ export function textInput(text: string): Update | undefined {
|
||||
const n = expressionValue(input.replace(/=$/, ""))
|
||||
if (n !== undefined) {
|
||||
return calc => {
|
||||
const entered = enterNumber(calc, n)
|
||||
const entered = showNumber(calc, n)
|
||||
return input.endsWith("=") ? press(entered, "=") : [entered]
|
||||
}
|
||||
}
|
||||
@@ -150,13 +152,13 @@ function expressionValue(expression: string): number | undefined {
|
||||
if (!valid) return undefined
|
||||
const calc = terms.reduce((current, [, op, sign, number, percentSign]) => {
|
||||
const withOperator = op ? press(current, keyAliases[op] ?? op as Key)[0] : current
|
||||
const entered = enterNumber(withOperator, sign ? -Number(number) : Number(number))
|
||||
const entered = showNumber(withOperator, sign ? -Number(number) : Number(number))
|
||||
return percentSign ? press(entered, "%")[0] : entered
|
||||
}, initialCalc)
|
||||
return value(press(calc, "=")[0])
|
||||
}
|
||||
|
||||
const nbsp = " "
|
||||
const nbsp = String.fromCharCode(0xa0)
|
||||
const wordWidth = Math.max(...keypad.flat().map(key => keyWord(key).length))
|
||||
|
||||
export function calculatorText(calc: Calc | undefined, symbolKeys: boolean): string {
|
||||
|
||||
@@ -6,13 +6,14 @@ import {calculatorIcon} from "./icon.js"
|
||||
const anyTextCommandsVersion = 21
|
||||
const idleMinutes = 10
|
||||
|
||||
const welcomeMessage = `Tap the keys, or send numbers, keys like + or =, and expressions like 12 × 3 + 4.\nKeys are applied left to right, as on a pocket calculator.\nThe calculator turns off after ${idleMinutes} minutes, any key turns it on.`
|
||||
const welcomeMessage = `Tap the keys or send numbers, keys like + and =, or expressions like 12 × 3 + 4.
|
||||
Keys are applied left to right, as on a pocket calculator.
|
||||
The calculator turns off after ${idleMinutes} minutes; any key turns it on.`
|
||||
const hint = "Send a number, a key like + or =, or an expression like 12 × 3 + 4."
|
||||
|
||||
interface Session {
|
||||
calc?: Calc
|
||||
itemId: number
|
||||
symbolKeys: boolean
|
||||
timer?: NodeJS.Timeout
|
||||
}
|
||||
|
||||
@@ -29,17 +30,13 @@ function groupSender({chatInfo, chatItem}: T.AChatItem): Sender | undefined {
|
||||
: undefined
|
||||
}
|
||||
|
||||
function currentCalc(groupId: number): Calc {
|
||||
return sessions.get(groupId)?.calc ?? initialCalc
|
||||
}
|
||||
|
||||
async function showCalculator(chat: api.ChatApi, groupId: number, member: T.GroupMember, calc: Calc): Promise<void> {
|
||||
async function showCalculator(chat: api.ChatApi, {groupId, member}: Sender, calc: Calc): Promise<void> {
|
||||
const symbolKeys = member.memberChatVRange.maxVersion >= anyTextCommandsVersion
|
||||
const [sent] = await chat.apiSendTextMessage([T.ChatType.Group, groupId], calculatorText(calc, symbolKeys))
|
||||
const itemId = sent.chatItem.meta.itemId
|
||||
const previous = sessions.get(groupId)
|
||||
const timer = setTimeout(() => turnOff(chat, groupId, itemId, symbolKeys), idleMinutes * 60_000)
|
||||
sessions.set(groupId, {calc, itemId, symbolKeys, timer})
|
||||
sessions.set(groupId, {calc, itemId, timer})
|
||||
if (previous) {
|
||||
clearTimeout(previous.timer)
|
||||
await chat.apiDeleteChatItems(T.ChatType.Group, groupId, [previous.itemId], T.CIDeleteMode.Broadcast)
|
||||
@@ -47,15 +44,15 @@ async function showCalculator(chat: api.ChatApi, groupId: number, member: T.Grou
|
||||
}
|
||||
|
||||
function turnOff(chat: api.ChatApi, groupId: number, itemId: number, symbolKeys: boolean): void {
|
||||
sessions.set(groupId, {itemId, symbolKeys})
|
||||
sessions.set(groupId, {itemId})
|
||||
chat.apiUpdateChatItem(T.ChatType.Group, groupId, itemId, {type: "text", text: calculatorText(undefined, symbolKeys)}, false)
|
||||
.catch(e => console.log("error turning calculator off", e))
|
||||
}
|
||||
|
||||
async function updateCalculator(chat: api.ChatApi, {groupId, member}: Sender, update: Update): Promise<void> {
|
||||
const [calc, logLine] = update(currentCalc(groupId))
|
||||
if (logLine) await chat.apiSendTextMessage([T.ChatType.Group, groupId], logLine)
|
||||
await showCalculator(chat, groupId, member, calc)
|
||||
async function updateCalculator(chat: api.ChatApi, sender: Sender, update: Update): Promise<void> {
|
||||
const [calc, logLine] = update(sessions.get(sender.groupId)?.calc ?? initialCalc)
|
||||
if (logLine) await chat.apiSendTextMessage([T.ChatType.Group, sender.groupId], logLine)
|
||||
await showCalculator(chat, sender, calc)
|
||||
}
|
||||
|
||||
function tapCommand(update: Update) {
|
||||
@@ -92,7 +89,7 @@ export function runCalculatorBot(dbOpts: bot.BotDbOpts): Promise<[api.ChatApi, T
|
||||
"": async (ci, _command, chat) => { await chat.apiSendTextReply(ci, hint) },
|
||||
},
|
||||
events: {
|
||||
joinedGroupMember: ({groupInfo, member}, chat) => showCalculator(chat, groupInfo.groupId, member, initialCalc),
|
||||
joinedGroupMember: ({groupInfo, member}, chat) => showCalculator(chat, {groupId: groupInfo.groupId, member}, initialCalc),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -28,20 +28,22 @@ export async function startSmpServer(): Promise<SmpServer> {
|
||||
env: {...process.env, SMP_SERVER_CFG_PATH: configDir, SMP_SERVER_LOG_PATH: join(dir, "logs")},
|
||||
stdio: ["ignore", "ignore", "inherit"],
|
||||
})
|
||||
let spawnError: Error | undefined
|
||||
server.on("error", e => { spawnError = e })
|
||||
await waitForServer(server, port, () => spawnError)
|
||||
return {
|
||||
address: `smp://${fingerprint}@localhost:${port}`,
|
||||
stop: async () => {
|
||||
if (server.exitCode === null) {
|
||||
const exited = once(server, "exit")
|
||||
server.kill()
|
||||
await exited
|
||||
}
|
||||
rmSync(dir, {recursive: true, force: true})
|
||||
},
|
||||
const stop = async () => {
|
||||
if (server.exitCode === null) {
|
||||
const exited = once(server, "exit")
|
||||
server.kill()
|
||||
await exited
|
||||
}
|
||||
rmSync(dir, {recursive: true, force: true})
|
||||
}
|
||||
try {
|
||||
await once(server, "spawn")
|
||||
await waitForServer(server, port)
|
||||
} catch (e) {
|
||||
await stop()
|
||||
throw e
|
||||
}
|
||||
return {address: `smp://${fingerprint}@localhost:${port}`, stop}
|
||||
}
|
||||
|
||||
async function smpServerExecutable(): Promise<string> {
|
||||
@@ -54,7 +56,7 @@ async function smpServerExecutable(): Promise<string> {
|
||||
async function downloadSmpServer(path: string): Promise<void> {
|
||||
const arch = releaseArch[process.arch]
|
||||
if (process.platform !== "linux" || !arch) {
|
||||
throw new Error("smp-server release binaries are only available for Linux, set SMP_SERVER to the smp-server executable")
|
||||
throw new Error(`smp-server release binaries are not available for ${process.platform} ${process.arch}, set SMP_SERVER to the smp-server path`)
|
||||
}
|
||||
const url = `https://github.com/simplex-chat/simplexmq/releases/download/${smpServerRelease}/smp-server-ubuntu-22_04-${arch}`
|
||||
const response = await fetch(url)
|
||||
@@ -80,12 +82,10 @@ function freePort(): Promise<number> {
|
||||
})
|
||||
}
|
||||
|
||||
async function waitForServer(server: ChildProcess, port: number, spawnError: () => Error | undefined): Promise<void> {
|
||||
async function waitForServer(server: ChildProcess, port: number): Promise<void> {
|
||||
const deadline = Date.now() + 15_000
|
||||
while (!(await canConnect(port))) {
|
||||
if (server.pid === undefined || server.exitCode !== null || Date.now() > deadline) {
|
||||
throw new Error(`smp-server did not start on port ${port}: ${spawnError()?.message ?? "no error"}`)
|
||||
}
|
||||
if (server.exitCode !== null || Date.now() > deadline) throw new Error(`smp-server did not start on port ${port}`)
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ There is an example with more options in [./examples/squaring-bot.ts](./examples
|
||||
|
||||
You can run it with: `npx ts-node ./examples/squaring-bot.ts`
|
||||
|
||||
A complete bot using business chats, command keys and message updates: [SimpleX Calculator](../../apps/simplex-calculator-bot/).
|
||||
A larger example, with a business address and commands as keys: [SimpleX Calculator](../../apps/simplex-calculator-bot/).
|
||||
|
||||
## PostgreSQL backend
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
# Type Alias: EventSubscriberFunc\<K\>
|
||||
|
||||
> **EventSubscriberFunc**\<`K`\> = (`event`) => `void` \| `Promise`\<`void`\>
|
||||
> **EventSubscriberFunc**\<`K`\> = (`event`, `chat`) => `void` \| `Promise`\<`void`\>
|
||||
|
||||
Defined in: [src/api.ts:50](../src/api.ts#L50)
|
||||
|
||||
@@ -22,6 +22,10 @@ Defined in: [src/api.ts:50](../src/api.ts#L50)
|
||||
|
||||
`ChatEvent` & `object`
|
||||
|
||||
### chat
|
||||
|
||||
[`ChatApi`](api.Class.ChatApi.md)
|
||||
|
||||
## Returns
|
||||
|
||||
`void` \| `Promise`\<`void`\>
|
||||
|
||||
@@ -18,7 +18,7 @@ Defined in: [src/bot.ts:108](../src/bot.ts#L108)
|
||||
|
||||
### onMessage
|
||||
|
||||
((`chatItem`, `content`) => `void` \| `Promise`\<`void`\>) \| `undefined`
|
||||
((`chatItem`, `content`, `chat`) => `void` \| `Promise`\<`void`\>) \| `undefined`
|
||||
|
||||
### commands
|
||||
|
||||
|
||||
@@ -34,13 +34,13 @@ Defined in: [src/bot.ts:42](../src/bot.ts#L42)
|
||||
|
||||
#### Index Signature
|
||||
|
||||
\[`key`: `string`\]: ((`chatItem`, `command`) => `void` \| `Promise`\<`void`\>) \| `undefined`
|
||||
\[`key`: `string`\]: ((`chatItem`, `command`, `chat`) => `void` \| `Promise`\<`void`\>) \| `undefined`
|
||||
|
||||
***
|
||||
|
||||
### onMessage?
|
||||
|
||||
> `optional` **onMessage?**: (`chatItem`, `content`) => `void` \| `Promise`\<`void`\>
|
||||
> `optional` **onMessage?**: (`chatItem`, `content`, `chat`) => `void` \| `Promise`\<`void`\>
|
||||
|
||||
Defined in: [src/bot.ts:40](../src/bot.ts#L40)
|
||||
|
||||
@@ -54,6 +54,10 @@ Defined in: [src/bot.ts:40](../src/bot.ts#L40)
|
||||
|
||||
`MsgContent`
|
||||
|
||||
##### chat
|
||||
|
||||
[`ChatApi`](api.Class.ChatApi.md)
|
||||
|
||||
#### Returns
|
||||
|
||||
`void` \| `Promise`\<`void`\>
|
||||
|
||||
@@ -407,10 +407,7 @@ textWithCommands = describe "text with commands" do
|
||||
"send /'filter 1'." <==> "send " <> command "filter 1" "/'filter 1'" <> "."
|
||||
"send /'filter 1.'!" <==> "send " <> command "filter 1." "/'filter 1.'" <> "!"
|
||||
"send /he?lp" <==> "send " <> command "he?lp" "/he?lp"
|
||||
"/+" <==> command "+" "/+"
|
||||
"/-" <==> command "-" "/-"
|
||||
"/." <==> command "." "/."
|
||||
"/√ /÷" <==> command "√" "/√" <> " " <> command "÷" "/÷"
|
||||
"send /+." <==> "send " <> command "+" "/+" <> "."
|
||||
"/'+'" <==> command "+" "/'+'"
|
||||
it "calculator keys" do
|
||||
|
||||
Reference in New Issue
Block a user