@shhhum/simplex-chat 0.0.0-stage → 7.1.0-beta.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +129 -2
  3. package/dist/api.d.ts +439 -0
  4. package/dist/api.js +911 -0
  5. package/dist/api.js.map +1 -0
  6. package/dist/bot.d.ts +34 -0
  7. package/dist/bot.js +194 -0
  8. package/dist/bot.js.map +1 -0
  9. package/dist/cli.d.ts +4 -0
  10. package/dist/cli.js +46 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/core.d.ts +138 -0
  13. package/dist/core.js +144 -0
  14. package/dist/core.js.map +1 -0
  15. package/dist/index.d.ts +19 -0
  16. package/dist/index.js +23 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/libs.d.ts +12 -0
  19. package/dist/libs.js +210 -0
  20. package/dist/libs.js.map +1 -0
  21. package/dist/release.d.ts +5 -0
  22. package/dist/release.js +20 -0
  23. package/dist/release.js.map +1 -0
  24. package/dist/simplex.d.ts +12 -0
  25. package/dist/simplex.js +22 -0
  26. package/dist/util.d.ts +15 -0
  27. package/dist/util.js +91 -0
  28. package/dist/util.js.map +1 -0
  29. package/docs/Namespace.api.md +33 -0
  30. package/docs/Namespace.bot.md +24 -0
  31. package/docs/Namespace.core.md +50 -0
  32. package/docs/Namespace.util.md +25 -0
  33. package/docs/README.md +12 -0
  34. package/docs/api.Class.ChatApi.md +1851 -0
  35. package/docs/api.Class.ChatCommandError.md +205 -0
  36. package/docs/api.Enumeration.ConnReqType.md +27 -0
  37. package/docs/api.Interface.BotAddressSettings.md +60 -0
  38. package/docs/api.TypeAlias.DbConfig.md +62 -0
  39. package/docs/api.TypeAlias.EventSubscriberFunc.md +31 -0
  40. package/docs/api.TypeAlias.EventSubscribers.md +11 -0
  41. package/docs/api.Variable.defaultBotAddressSettings.md +11 -0
  42. package/docs/bot.Function.run.md +21 -0
  43. package/docs/bot.Function.subscribeChatItems.md +27 -0
  44. package/docs/bot.Interface.BotConfig.md +87 -0
  45. package/docs/bot.Interface.BotOptions.md +81 -0
  46. package/docs/bot.TypeAlias.BotDbOpts.md +21 -0
  47. package/docs/core.Class.ChatAPIError.md +205 -0
  48. package/docs/core.Class.ChatInitError.md +205 -0
  49. package/docs/core.DBMigrationError.Interface.ErrorMigration.md +41 -0
  50. package/docs/core.DBMigrationError.Interface.ErrorNotADatabase.md +33 -0
  51. package/docs/core.DBMigrationError.Interface.ErrorSQL.md +41 -0
  52. package/docs/core.DBMigrationError.Interface.InvalidConfirmation.md +25 -0
  53. package/docs/core.DBMigrationError.Interface.InvalidQueueSize.md +25 -0
  54. package/docs/core.DBMigrationError.TypeAlias.Tag.md +11 -0
  55. package/docs/core.Enumeration.MigrationConfirmation.md +43 -0
  56. package/docs/core.Function.chatCloseStore.md +23 -0
  57. package/docs/core.Function.chatDecryptFile.md +31 -0
  58. package/docs/core.Function.chatEncryptFile.md +31 -0
  59. package/docs/core.Function.chatMigrateInit.md +37 -0
  60. package/docs/core.Function.chatReadFile.md +27 -0
  61. package/docs/core.Function.chatRecvMsgWait.md +27 -0
  62. package/docs/core.Function.chatSendCmd.md +27 -0
  63. package/docs/core.Function.chatWriteFile.md +31 -0
  64. package/docs/core.Function.loadLibrary.md +24 -0
  65. package/docs/core.Interface.APIResult.md +31 -0
  66. package/docs/core.Interface.CryptoArgs.md +27 -0
  67. package/docs/core.Interface.UpMigration.md +25 -0
  68. package/docs/core.MTRError.Interface.MTREDifferent.md +33 -0
  69. package/docs/core.MTRError.Interface.MTRENoDown.md +33 -0
  70. package/docs/core.MTRError.TypeAlias.Tag.md +11 -0
  71. package/docs/core.MigrationError.Interface.MEDowngrade.md +33 -0
  72. package/docs/core.MigrationError.Interface.MEUpgrade.md +33 -0
  73. package/docs/core.MigrationError.Interface.MigrationError.md +33 -0
  74. package/docs/core.MigrationError.TypeAlias.Tag.md +11 -0
  75. package/docs/core.Namespace.DBMigrationError.md +19 -0
  76. package/docs/core.Namespace.MTRError.md +16 -0
  77. package/docs/core.Namespace.MigrationError.md +17 -0
  78. package/docs/core.TypeAlias.Backend.md +11 -0
  79. package/docs/core.TypeAlias.DBMigrationError.md +11 -0
  80. package/docs/core.TypeAlias.MTRError.md +11 -0
  81. package/docs/core.TypeAlias.MigrationError.md +11 -0
  82. package/docs/util.Function.botAddressSettings.md +21 -0
  83. package/docs/util.Function.chatInfoName.md +21 -0
  84. package/docs/util.Function.chatInfoRef.md +21 -0
  85. package/docs/util.Function.ciBotCommand.md +21 -0
  86. package/docs/util.Function.ciContentText.md +21 -0
  87. package/docs/util.Function.contactAddressStr.md +21 -0
  88. package/docs/util.Function.fromLocalProfile.md +21 -0
  89. package/docs/util.Function.reactionText.md +21 -0
  90. package/docs/util.Function.senderName.md +25 -0
  91. package/docs/util.Interface.BotCommand.md +25 -0
  92. package/examples/squaring-bot-readme.js +17 -0
  93. package/examples/squaring-bot.ts +42 -0
  94. package/package.json +55 -4
  95. package/src/api.ts +997 -0
  96. package/src/bot.ts +221 -0
  97. package/src/cli.ts +42 -0
  98. package/src/core.ts +240 -0
  99. package/src/index.ts +22 -0
  100. package/src/libs.ts +202 -0
  101. package/src/release.ts +19 -0
  102. package/src/simplex.d.ts +12 -0
  103. package/src/simplex.js +22 -0
  104. package/src/util.ts +92 -0
  105. package/tsconfig.json +23 -0
package/src/libs.ts ADDED
@@ -0,0 +1,202 @@
1
+ import * as crypto from "crypto"
2
+ import * as fs from "fs"
3
+ import * as http from "http"
4
+ import * as https from "https"
5
+ import * as os from "os"
6
+ import * as path from "path"
7
+ import {pipeline} from "stream/promises"
8
+ import extract = require("extract-zip")
9
+ import type {Backend} from "./core"
10
+ import {ADDON_RELEASE, LIBS_RELEASE, SHA256} from "./release"
11
+
12
+ const GITHUB_REPO = "simplex-chat/simplex-chat-libs"
13
+ const REQUEST_TIMEOUT_MS = 60_000
14
+ export const MAX_REDIRECTS = 5
15
+ const PLATFORMS: {[platform: string]: {name: string, lib: string} | undefined} = {
16
+ linux: {name: "linux", lib: "libsimplex.so"},
17
+ darwin: {name: "macos", lib: "libsimplex.dylib"},
18
+ win32: {name: "windows", lib: "libsimplex.dll"},
19
+ }
20
+ const ARCHS: {[arch: string]: string | undefined} = {x64: "x86_64", arm64: "aarch64"}
21
+ const SUPPORTED = ["linux-x86_64", "linux-aarch64", "macos-x86_64", "macos-aarch64", "windows-x86_64"]
22
+
23
+ function unsupported(platform: string, arch: string): Error {
24
+ return new Error(`Unsupported platform: ${platform}/${arch}; supported: ${SUPPORTED.join(", ")}`)
25
+ }
26
+
27
+ export function platformTag(platform: string, arch: string): string {
28
+ const tag = `${PLATFORMS[platform]?.name}-${ARCHS[arch]}`
29
+ if (!SUPPORTED.includes(tag)) throw unsupported(platform, arch)
30
+ return tag
31
+ }
32
+
33
+ function libName(platform: string = process.platform, arch: string = process.arch): string {
34
+ const lib = PLATFORMS[platform]?.lib
35
+ if (!lib) throw unsupported(platform, arch)
36
+ return lib
37
+ }
38
+
39
+ export function cacheRoot(platform: string, env: NodeJS.ProcessEnv, home: string = os.homedir()): string {
40
+ if (platform === "darwin") return path.join(home, "Library", "Caches", "simplex-chat")
41
+ if (platform === "win32") {
42
+ if (!env.LOCALAPPDATA) throw new Error("LOCALAPPDATA is not set")
43
+ return path.join(env.LOCALAPPDATA, "simplex-chat")
44
+ }
45
+ return path.join(env.XDG_CACHE_HOME || path.join(home, ".cache"), "simplex-chat")
46
+ }
47
+
48
+ export function libsAsset(backend: Backend, tag: string): string {
49
+ const suffix = backend === "postgres" ? "-postgres" : ""
50
+ return `simplex-chat-libs-${tag}${suffix}.zip`
51
+ }
52
+
53
+ export function addonAsset(tag: string): string {
54
+ return `simplex-chat-nodejs-${tag}.node`
55
+ }
56
+
57
+ export function releaseUrl(release: string, asset: string): string {
58
+ return `https://github.com/${GITHUB_REPO}/releases/download/${release}/${asset}`
59
+ }
60
+
61
+ function pinnedSha256(asset: string): string {
62
+ const sha256 = SHA256[asset]
63
+ if (!sha256) throw new Error(`no SHA-256 is pinned for ${asset}`)
64
+ return sha256
65
+ }
66
+
67
+ export function libPath(dir: string): string {
68
+ return path.join(dir, libName())
69
+ }
70
+
71
+ export async function resolveLibsDir(
72
+ backend: Backend,
73
+ env: NodeJS.ProcessEnv = process.env,
74
+ platform: string = process.platform,
75
+ arch: string = process.arch
76
+ ): Promise<string> {
77
+ const lib = libName(platform, arch)
78
+ if (env.SIMPLEX_LIBS_DIR) {
79
+ const dir = path.resolve(env.SIMPLEX_LIBS_DIR)
80
+ if (!fs.existsSync(path.join(dir, lib))) throw new Error(`SIMPLEX_LIBS_DIR has no ${lib}: ${dir}`)
81
+ return dir
82
+ }
83
+ const tag = platformTag(platform, arch)
84
+ if (backend === "postgres" && tag !== "linux-x86_64") {
85
+ throw new Error(`postgres backend is only supported on linux-x86_64; current platform is ${tag}`)
86
+ }
87
+ const target = path.resolve(cacheRoot(platform, env), LIBS_RELEASE, backend)
88
+ if (!fs.existsSync(path.join(target, lib))) {
89
+ const asset = libsAsset(backend, tag)
90
+ await installLibs(releaseUrl(LIBS_RELEASE, asset), pinnedSha256(asset), target, lib)
91
+ }
92
+ return target
93
+ }
94
+
95
+ export async function resolveAddonPath(
96
+ env: NodeJS.ProcessEnv = process.env,
97
+ platform: string = process.platform,
98
+ arch: string = process.arch
99
+ ): Promise<string> {
100
+ if (env.SIMPLEX_ADDON_PATH) {
101
+ const file = path.resolve(env.SIMPLEX_ADDON_PATH)
102
+ if (!fs.existsSync(file)) throw new Error(`SIMPLEX_ADDON_PATH does not exist: ${file}`)
103
+ return file
104
+ }
105
+ if (!ADDON_RELEASE) throw new Error("no prebuilt Node.js add-on is pinned in this version; set SIMPLEX_ADDON_PATH")
106
+ const asset = addonAsset(platformTag(platform, arch))
107
+ const target = path.resolve(cacheRoot(platform, env), ADDON_RELEASE, asset)
108
+ if (!fs.existsSync(target)) await installAddon(releaseUrl(ADDON_RELEASE, asset), pinnedSha256(asset), target)
109
+ return target
110
+ }
111
+
112
+ export function installLibs(url: string, sha256: string, target: string, lib: string, timeoutMs: number = REQUEST_TIMEOUT_MS): Promise<void> {
113
+ return inTempDir(target, async tmp => {
114
+ console.error(`Downloading libsimplex from ${url} ...`)
115
+ const zipPath = path.join(tmp, "libs.zip")
116
+ await download(url, zipPath, timeoutMs)
117
+ await verifySha256(zipPath, sha256, url)
118
+ await extract(zipPath, {dir: tmp})
119
+ const extracted = path.join(tmp, "libs")
120
+ if (!fs.existsSync(path.join(extracted, lib))) throw new Error(`libs/${lib} missing from ${url}`)
121
+ await renameInto(extracted, target, path.join(target, lib))
122
+ })
123
+ }
124
+
125
+ export function installAddon(url: string, sha256: string, target: string, timeoutMs: number = REQUEST_TIMEOUT_MS): Promise<void> {
126
+ return inTempDir(target, async tmp => {
127
+ console.error(`Downloading the Node.js add-on from ${url} ...`)
128
+ const file = path.join(tmp, path.basename(target))
129
+ await download(url, file, timeoutMs)
130
+ await verifySha256(file, sha256, url)
131
+ await renameInto(file, target, target)
132
+ })
133
+ }
134
+
135
+ async function verifySha256(file: string, expected: string, url: string): Promise<void> {
136
+ const hash = crypto.createHash("sha256")
137
+ for await (const chunk of fs.createReadStream(file)) hash.update(chunk)
138
+ const actual = hash.digest("hex")
139
+ if (actual !== expected) throw new Error(`SHA-256 of ${url} is ${actual}, expected ${expected}`)
140
+ }
141
+
142
+ // The temp dir is next to the target, so the final rename stays on one filesystem.
143
+ async function inTempDir(target: string, install: (tmp: string) => Promise<void>): Promise<void> {
144
+ const parent = path.dirname(target)
145
+ await fs.promises.mkdir(parent, {recursive: true})
146
+ const tmp = await fs.promises.mkdtemp(path.join(parent, ".download-"))
147
+ try {
148
+ await install(tmp)
149
+ } finally {
150
+ await fs.promises.rm(tmp, {recursive: true, force: true})
151
+ }
152
+ }
153
+
154
+ async function renameInto(from: string, target: string, installed: string): Promise<void> {
155
+ try {
156
+ await fs.promises.rename(from, target)
157
+ } catch (e) {
158
+ // Another process installed the same version first; its files are identical.
159
+ // Windows reports renaming onto an existing directory or a loaded file as EPERM.
160
+ const code = (e as NodeJS.ErrnoException).code
161
+ const lost = (code === "EEXIST" || code === "ENOTEMPTY" || code === "EPERM") && fs.existsSync(target)
162
+ if (!lost) throw e
163
+ if (!fs.existsSync(installed)) {
164
+ throw new Error(`another process partially populated ${target} but ${path.basename(installed)} is missing; remove it manually and retry`)
165
+ }
166
+ }
167
+ }
168
+
169
+ function download(url: string, dest: string, timeoutMs: number, redirects = 0): Promise<void> {
170
+ return new Promise((resolve, reject) => {
171
+ const get = url.startsWith("https:") ? https.get : http.get
172
+ // Once a response arrives, request errors are ignored: a download settles via pipeline, which
173
+ // waits for the file to close, so tmp cleanup cannot fail with EBUSY on Windows.
174
+ let res: http.IncomingMessage | undefined
175
+ const req = get(url, {headers: {"User-Agent": "simplex-chat-nodejs"}, timeout: timeoutMs}, response => {
176
+ res = response
177
+ const status = response.statusCode ?? 0
178
+ const location = response.headers.location
179
+ if (status >= 300 && status < 400 && location) {
180
+ response.resume()
181
+ if (redirects >= MAX_REDIRECTS) return reject(new Error(`too many redirects downloading ${url}`))
182
+ let next: URL
183
+ try {
184
+ next = new URL(location, url)
185
+ } catch (e) {
186
+ return reject(e)
187
+ }
188
+ if (next.protocol !== new URL(url).protocol) return reject(new Error(`redirect from ${url} to ${next} changes protocol`))
189
+ download(next.toString(), dest, timeoutMs, redirects + 1).then(resolve, reject)
190
+ return
191
+ }
192
+ if (status !== 200) {
193
+ response.resume()
194
+ return reject(new Error(`HTTP ${status} downloading ${url}`))
195
+ }
196
+ pipeline(response, fs.createWriteStream(dest)).then(resolve, reject)
197
+ })
198
+ const abort = (err: Error) => (res ? res.destroy(err) : req.destroy(err))
199
+ req.on("timeout", () => abort(new Error(`timeout downloading ${url}`)))
200
+ req.on("error", e => { if (!res) reject(e) })
201
+ })
202
+ }
package/src/release.ts ADDED
@@ -0,0 +1,19 @@
1
+ // Written by scripts/ci/packages_release.py from the simplex-chat-libs releases.
2
+
3
+ export const LIBS_RELEASE = "core-7.1.0.11"
4
+
5
+ export const ADDON_RELEASE: string | undefined = "nodejs-addon-308c0960c707"
6
+
7
+ export const SHA256: {readonly [asset: string]: string} = {
8
+ "simplex-chat-libs-linux-aarch64.zip": "d66aa1b30b9fa20225dad8ef75a9c605961e1c0adde2ba0283e2fcac4e15d260",
9
+ "simplex-chat-libs-linux-x86_64-postgres.zip": "925b3166f969b12082ff8ee659e5f2a9933fe40120deb80c9aa3002a5ba6c803",
10
+ "simplex-chat-libs-linux-x86_64.zip": "dabad0824e8813ea4a084fbf37e1ba4cf6dcea9cd8ccadc521fec200f246208a",
11
+ "simplex-chat-libs-macos-aarch64.zip": "aa74608b149fff7508a47eaac4284dcc6d189c6ed8b7c18428df9158bc176a61",
12
+ "simplex-chat-libs-macos-x86_64.zip": "a0e21457e1077be7f71b46c1c27452945b2cee2b6c50efb0004412ce5b7042ec",
13
+ "simplex-chat-libs-windows-x86_64.zip": "c01dad574daa101a1cbfd31ddbf2b3d0d986a4a42f97d789d2f878b2050b2b4b",
14
+ "simplex-chat-nodejs-linux-aarch64.node": "3636a82c56c48229e0326fa03d6ceb5c16dd3afbcc1e86337783dc53a7328d67",
15
+ "simplex-chat-nodejs-linux-x86_64.node": "55f3479b03fdb57591179ed730da744f1aa362e7529df55221b60028415746e9",
16
+ "simplex-chat-nodejs-macos-aarch64.node": "17da0ed991bcc60f3a0329ba4ce8af902a02cb3dfd7ab7b84718449cc857c857",
17
+ "simplex-chat-nodejs-macos-x86_64.node": "6218210db6caf520ecb7b5066babfce1e51ad2082f7ba6043aa26510418c1aea",
18
+ "simplex-chat-nodejs-windows-x86_64.node": "03aaee6f19b216c660f492e064a02a2820c41cd4b58b2a7b7561ac97b982f90d",
19
+ }
@@ -0,0 +1,12 @@
1
+ // These functions are defined in CPP add-on ../cpp/simplex.cc
2
+
3
+ export function load(addonPath: string, libPath: string): void
4
+ export function chat_migrate_init(dbPath: string, dbKey: string, confirm: string): Promise<[bigint, string]>
5
+ export function chat_migrate_init_queue(dbPath: string, dbKey: string, confirm: string, queueSize: number): Promise<[bigint, string]>
6
+ export function chat_close_store(ctrl: bigint): Promise<string>
7
+ export function chat_send_cmd(ctrl: bigint, cmd: string): Promise<string>
8
+ export function chat_recv_msg_wait(ctrl: bigint, wait: number): Promise<string>
9
+ export function chat_write_file(ctrl: bigint, path: string, buffer: ArrayBuffer | Uint8Array): Promise<string>
10
+ export function chat_read_file(path: string, key: string, nonce: string): Promise<Buffer>
11
+ export function chat_encrypt_file(ctrl: bigint, fromPath: string, toPath: string): Promise<string>
12
+ export function chat_decrypt_file(fromPath: string, key: string, nonce: string, toPath: string): Promise<string>
package/src/simplex.js ADDED
@@ -0,0 +1,22 @@
1
+ // The add-on is downloaded from the libsimplex release, so it is loaded at runtime by core.loadLibrary.
2
+ const NOT_LOADED = "libsimplex is not loaded, call core.loadLibrary(backend) first"
3
+ const BINDINGS = ["chat_migrate_init", "chat_migrate_init_queue", "chat_close_store", "chat_send_cmd", "chat_recv_msg_wait",
4
+ "chat_write_file", "chat_read_file", "chat_encrypt_file", "chat_decrypt_file"]
5
+
6
+ let addon
7
+
8
+ exports.load = (addonPath, libPath) => {
9
+ if (!addon) {
10
+ const addonModule = {exports: {}}
11
+ process.dlopen(addonModule, addonPath)
12
+ addon = addonModule.exports
13
+ }
14
+ addon.load(libPath)
15
+ }
16
+
17
+ for (const name of BINDINGS) {
18
+ exports[name] = (...args) => {
19
+ if (!addon) throw new Error(NOT_LOADED)
20
+ return addon[name](...args)
21
+ }
22
+ }
package/src/util.ts ADDED
@@ -0,0 +1,92 @@
1
+ import {T} from "@simplex-chat/types"
2
+ import {BotAddressSettings} from "./api"
3
+
4
+ export function chatInfoRef(cInfo: T.ChatInfo): T.ChatRef | undefined {
5
+ switch (cInfo.type) {
6
+ case T.ChatType.Direct: return {chatType: T.ChatType.Direct, chatId: cInfo.contact.contactId}
7
+ case T.ChatType.Group: {
8
+ const chatScope: T.GroupChatScope | undefined =
9
+ cInfo.groupChatScope?.type == "memberSupport"
10
+ ? {type: "memberSupport", groupMemberId_: cInfo.groupChatScope.groupMember_?.groupMemberId}
11
+ : undefined
12
+ return {chatType: T.ChatType.Group, chatId: cInfo.groupInfo.groupId, chatScope}
13
+ }
14
+ default: return undefined
15
+ }
16
+ }
17
+
18
+ export function chatInfoName(cInfo: T.ChatInfo): string {
19
+ switch (cInfo.type) {
20
+ case "direct": return `@${cInfo.contact.profile.displayName}`
21
+ case "group": {
22
+ const scope = cInfo.groupChatScope
23
+ const scopeName = scope?.type === "memberSupport"
24
+ ? `(support${scope.groupMember_ ? ` ${scope.groupMember_.memberProfile.displayName}` : ""})`
25
+ : ""
26
+ return `#${cInfo.groupInfo.groupProfile.displayName}${scopeName}`
27
+ }
28
+ case "local": return "private notes"
29
+ case "contactRequest": return `request from @${cInfo.contactRequest.profile.displayName}`
30
+ case "contactConnection": {
31
+ const alias = cInfo.contactConnection.localAlias
32
+ return `pending connection${alias ? ` (@${alias})` : ""}`
33
+ }
34
+ }
35
+ }
36
+
37
+ export function senderName(cInfo: T.ChatInfo, chatDir: T.CIDirection) {
38
+ const sender = chatDir.type === "groupRcv"
39
+ ? ` @${chatDir.groupMember.memberProfile.displayName}`
40
+ : ""
41
+ return chatInfoName(cInfo) + sender
42
+ }
43
+
44
+ export function contactAddressStr(link: T.CreatedConnLink): string {
45
+ return link.connShortLink || link.connFullLink
46
+ }
47
+
48
+ export function botAddressSettings({addressSettings}: T.UserContactLink): BotAddressSettings {
49
+ return {
50
+ autoAccept: addressSettings.autoAccept ? true : false,
51
+ welcomeMessage: addressSettings.autoReply,
52
+ businessAddress: addressSettings.businessAddress
53
+ }
54
+ }
55
+
56
+ export function fromLocalProfile({displayName, fullName, shortDescr, image, contactLink, preferences, peerType, contactDomain}: T.LocalProfile): T.Profile {
57
+ const profile = {displayName, fullName, shortDescr, image, contactLink, preferences, peerType, contactDomain: contactDomain && {domain: contactDomain.domain}}
58
+ for (const key in profile) {
59
+ if (typeof (profile as any)[key] === "undefined") delete (profile as any)[key]
60
+ }
61
+ return profile
62
+ }
63
+
64
+ export function ciContentText({content}: T.ChatItem): string | undefined {
65
+ switch (content.type) {
66
+ case "sndMsgContent": return content.msgContent.text;
67
+ case "rcvMsgContent": return content.msgContent.text;
68
+ default: return undefined;
69
+ }
70
+ }
71
+
72
+ export interface BotCommand {
73
+ keyword: string
74
+ params: string
75
+ }
76
+
77
+ // returns command (without /) and trimmed parameters
78
+ export function ciBotCommand(chatItem: T.ChatItem): BotCommand | undefined {
79
+ const msg = ciContentText(chatItem)?.trim()
80
+ if (msg) {
81
+ const r = msg.match(/^\/([^\s]+)([\s\S]*)/)
82
+ if (r && r.length >= 3) {
83
+ return {keyword: r[1], params: r[2].trim()}
84
+ }
85
+ }
86
+ return undefined
87
+ }
88
+
89
+ export function reactionText(reaction: T.ACIReaction): string {
90
+ const r = reaction.chatReaction
91
+ return r.reaction.type === "emoji" ? r.reaction.emoji : r.reaction.tag
92
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "include": ["src"],
3
+ "compilerOptions": {
4
+ "declaration": true,
5
+ "forceConsistentCasingInFileNames": true,
6
+ "lib": ["ES2018"],
7
+ "module": "CommonJS",
8
+ "moduleResolution": "Node",
9
+ "noFallthroughCasesInSwitch": true,
10
+ "noImplicitAny": true,
11
+ "noImplicitReturns": true,
12
+ "noImplicitThis": true,
13
+ "noUnusedLocals": true,
14
+ "noUnusedParameters": true,
15
+ "noEmitOnError": true,
16
+ "outDir": "dist",
17
+ "sourceMap": true,
18
+ "strict": true,
19
+ "strictNullChecks": true,
20
+ "target": "ES2018",
21
+ "types": ["node"]
22
+ }
23
+ }