@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/bot.ts ADDED
@@ -0,0 +1,221 @@
1
+ import {T} from "@simplex-chat/types"
2
+ import * as api from "./api"
3
+ import * as core from "./core"
4
+ import * as util from "./util"
5
+ import equal = require("fast-deep-equal")
6
+
7
+ export type BotDbOpts = api.DbConfig & {
8
+ confirmMigrations?: core.MigrationConfirmation
9
+ queueSize?: number
10
+ }
11
+
12
+ export interface BotOptions {
13
+ createAddress?: boolean
14
+ updateAddress?: boolean
15
+ updateProfile?: boolean
16
+ addressSettings?: api.BotAddressSettings
17
+ allowFiles?: boolean
18
+ commands?: T.ChatBotCommand[] // commands to show in client UI
19
+ useBotProfile?: boolean // create profile not marked as a bot, with default preferences
20
+ logContacts?: boolean
21
+ logNetwork?: boolean
22
+ }
23
+
24
+ const defaultOpts: Required<BotOptions> = {
25
+ createAddress: true,
26
+ updateAddress: true,
27
+ updateProfile: true,
28
+ addressSettings: api.defaultBotAddressSettings,
29
+ allowFiles: false,
30
+ commands: [],
31
+ useBotProfile: true,
32
+ logContacts: true,
33
+ logNetwork: false
34
+ }
35
+
36
+ export interface BotConfig {
37
+ profile: T.Profile,
38
+ simplexDomain?: string | null,
39
+ dbOpts: BotDbOpts,
40
+ options: BotOptions,
41
+ onMessage?: (chatItem: T.AChatItem, content: T.MsgContent, chat: api.ChatApi) => void | Promise<void>,
42
+ // command handlers can be different from commands to be shown in client UI
43
+ onCommands?: {[K in string]?: ((chatItem: T.AChatItem, command: util.BotCommand, chat: api.ChatApi) => void | Promise<void>)},
44
+ // If you use `onMessage` and to subscribe "newChatItems" event, exclude content messages from processing
45
+ // If you use `onCommands` and to subscribe "newChatItems" event, exclude commands from processing
46
+ events?: api.EventSubscribers
47
+ }
48
+
49
+ export async function run({profile, simplexDomain, dbOpts, options = defaultOpts, onMessage, onCommands = {}, events = {}}: BotConfig): Promise<[api.ChatApi, T.User, T.UserContactLink | undefined]> {
50
+ const bot = await api.ChatApi.init(dbOpts, dbOpts.confirmMigrations || core.MigrationConfirmation.YesUp, dbOpts.queueSize)
51
+ const opts = fullOptions(options)
52
+ if (onMessage || Object.keys(onCommands).length > 0) subscribeChatItems(bot, onMessage, onCommands)
53
+ if (Object.keys(events).length > 0) bot.on(events)
54
+ subscribeLogEvents(bot, opts)
55
+ const botProfile = mkBotProfile(profile, opts)
56
+ const user = await createBotUser(bot, botProfile)
57
+ await bot.startChat()
58
+ const address = await createOrUpdateAddress(bot, user, opts)
59
+ if (address) {
60
+ const addressLink = util.contactAddressStr(address.connLinkContact)
61
+ console.log(`Bot address: ${addressLink}`)
62
+ if (opts.useBotProfile) botProfile.contactLink = addressLink
63
+ }
64
+ const namedUser = await updateBotSimplexDomain(bot, user, simplexDomain, opts)
65
+ await updateBotUserProfile(bot, namedUser, botProfile, opts)
66
+ return [bot, user, address]
67
+ }
68
+
69
+ function fullOptions(options: BotOptions): Required<BotOptions> {
70
+ const opts = {
71
+ createAddress: options.createAddress ?? defaultOpts.createAddress,
72
+ updateAddress: options.updateAddress ?? defaultOpts.updateAddress,
73
+ updateProfile: options.updateProfile ?? defaultOpts.updateProfile,
74
+ addressSettings: options.addressSettings ?? defaultOpts.addressSettings,
75
+ allowFiles: options.allowFiles ?? defaultOpts.allowFiles,
76
+ commands: options.commands ?? defaultOpts.commands,
77
+ useBotProfile: options.useBotProfile ?? defaultOpts.useBotProfile,
78
+ logContacts: options.logContacts ?? defaultOpts.logContacts,
79
+ logNetwork: options.logNetwork ?? defaultOpts.logNetwork
80
+ }
81
+ const welcomeMessage = opts.addressSettings.welcomeMessage ?? defaultOpts.addressSettings.welcomeMessage
82
+ opts.addressSettings = {
83
+ autoAccept: opts.addressSettings.autoAccept ?? defaultOpts.addressSettings.autoAccept,
84
+ welcomeMessage: typeof welcomeMessage === "string" ? {type: "text", text: welcomeMessage} : welcomeMessage,
85
+ businessAddress: opts.addressSettings.businessAddress ?? defaultOpts.addressSettings.businessAddress
86
+ }
87
+ return opts
88
+ }
89
+
90
+ function mkBotProfile(profile: T.Profile, opts: Required<BotOptions>): T.Profile {
91
+ if (opts.useBotProfile) {
92
+ const prefs = profile.preferences || {}
93
+ if (prefs.files || prefs.calls || prefs.voice || prefs.commands) {
94
+ console.log("Option useBotProfile is enabled and profile preferences used for files, calls, voice or commands, exiting")
95
+ process.exit()
96
+ }
97
+ prefs.files = {allow: opts.allowFiles ? T.FeatureAllowed.Yes : T.FeatureAllowed.No}
98
+ prefs.calls = {allow: T.FeatureAllowed.No}
99
+ prefs.voice = {allow: T.FeatureAllowed.No}
100
+ prefs.commands = opts.commands
101
+ profile.preferences = prefs
102
+ profile.peerType = T.ChatPeerType.Bot
103
+ } else if (opts.commands.length > 0) {
104
+ console.log("Option useBotProfile is disabled and commands are passed, exiting")
105
+ process.exit()
106
+ }
107
+ return profile
108
+ }
109
+
110
+ export function subscribeChatItems(
111
+ bot: api.ChatApi,
112
+ onMessage: ((chatItem: T.AChatItem, content: T.MsgContent, chat: api.ChatApi) => void | Promise<void>) | undefined,
113
+ commands: {[K in string]?: ((chatItem: T.AChatItem, command: util.BotCommand, chat: api.ChatApi) => void | Promise<void>)}
114
+ ) {
115
+ bot.on("newChatItems", async ({chatItems}) => {
116
+ for (const ci of chatItems) {
117
+ const content = ci.chatItem.content
118
+ if (content.type !== "rcvMsgContent") continue
119
+ const cmd = util.ciBotCommand(ci.chatItem)
120
+ const cmdFunc = cmd && (commands[cmd.keyword] || commands[""])
121
+ try {
122
+ if (cmd && cmdFunc) await cmdFunc(ci, cmd, bot)
123
+ else if (onMessage) await onMessage(ci, content.msgContent, bot)
124
+ } catch (e) {
125
+ console.log(cmd && cmdFunc ? `${cmd.keyword} command processing error` : "message processing error", e)
126
+ }
127
+ }
128
+ })
129
+ }
130
+
131
+ function subscribeLogEvents(bot: api.ChatApi, opts: Required<BotOptions>) {
132
+ if (opts.logContacts) {
133
+ bot.on({
134
+ "contactConnected": ({contact}) => console.log(`${contact.profile.displayName} connected`),
135
+ "contactDeletedByContact": ({contact}) => console.log(`${contact.profile.displayName} deleted connection with bot`)
136
+ })
137
+ }
138
+ if (opts.logNetwork) {
139
+ bot.on({
140
+ "hostConnected": ({transportHost}) => console.log(`connected server ${transportHost}`),
141
+ "hostDisconnected": ({transportHost}) => console.log(`diconnected server ${transportHost}`),
142
+ "subscriptionStatus": ({subscriptionStatus, connections}) => console.log(`${connections.length} subscription(s) ${subscriptionStatus.type}`)
143
+ })
144
+ }
145
+ }
146
+
147
+ async function createBotUser(bot: api.ChatApi, profile: T.Profile): Promise<T.User> {
148
+ let user = await bot.apiGetActiveUser()
149
+ if (!user) {
150
+ console.log("No active user in database, creating...")
151
+ user = await bot.apiCreateActiveUser(profile)
152
+ }
153
+ console.log("Bot user: ", user.profile.displayName)
154
+ return user
155
+ }
156
+
157
+ async function createOrUpdateAddress(bot: api.ChatApi, user: T.User, opts: Required<BotOptions>): Promise<T.UserContactLink | undefined> {
158
+ const {userId} = user
159
+ let address = await bot.apiGetUserAddress(userId)
160
+ if (!address) {
161
+ if (opts.createAddress) {
162
+ console.log("Bot has no address, creating...")
163
+ await bot.apiCreateUserAddress(userId)
164
+ address = await bot.apiGetUserAddress(userId)
165
+ if (!address) {
166
+ console.log("Failed reading created user address, exiting")
167
+ process.exit()
168
+ }
169
+ } else {
170
+ console.log("Warning: bot has no address")
171
+ return
172
+ }
173
+ }
174
+
175
+ const addressSettings = opts.addressSettings || defaultOpts.addressSettings
176
+ if (!equal(util.botAddressSettings(address), addressSettings)) {
177
+ if (opts.updateAddress) {
178
+ console.log("Bot address settings changed, updating...")
179
+ await bot.apiSetAddressSettings(userId, addressSettings)
180
+ } else {
181
+ console.log("Bot address settings changed")
182
+ }
183
+ }
184
+
185
+ return address
186
+ }
187
+
188
+ async function updateBotSimplexDomain(bot: api.ChatApi, user: T.User, simplexDomain: string | null | undefined, opts: Required<BotOptions>): Promise<T.User> {
189
+ if (simplexDomain === undefined) return user
190
+ const domain = simplexDomain === null ? undefined : simplexDomain.toLowerCase()
191
+ if (user.profile.contactDomain?.domain === domain) return user
192
+ if (!opts.updateAddress) {
193
+ console.log("Bot SimpleX name changed")
194
+ return user
195
+ }
196
+ console.log("Bot SimpleX name changed, updating...")
197
+ try {
198
+ return await bot.apiSetUserDomain(user.userId, domain)
199
+ } catch (e) {
200
+ console.log("Error updating bot SimpleX name", e)
201
+ return user
202
+ }
203
+ }
204
+
205
+ async function updateBotUserProfile(bot: api.ChatApi, user: T.User, profile: T.Profile, opts: Required<BotOptions>): Promise<void> {
206
+ const {userId} = user
207
+ const {contactDomain, ...currentProfile} = util.fromLocalProfile(user.profile)
208
+ if (!equal(currentProfile, profile)) {
209
+ if (opts.updateProfile) {
210
+ console.log("Bot profile changed, updating...")
211
+ const summary = await bot.apiUpdateProfile(userId, {...profile, contactDomain})
212
+ console.log(
213
+ summary
214
+ ? `Bot profile updated: ${summary.updateSuccesses} updated contact(s), ${summary.updateFailures} failed contact update(s).`
215
+ : "Unexpected: profile did not change!"
216
+ )
217
+ } else {
218
+ console.log("Bot profile changed")
219
+ }
220
+ }
221
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ import {parseArgs} from "util"
3
+ import type {Backend} from "./core"
4
+ import {resolveAddonPath, resolveLibsDir} from "./libs"
5
+
6
+ const USAGE = "usage: simplex-chat install [--backend sqlite|postgres]"
7
+ const EXIT_INSTALL_FAILED = 1
8
+ const EXIT_USAGE = 2
9
+
10
+ export function parseInstallArgs(argv: string[]): Backend {
11
+ const {positionals, values: {backend = "sqlite"}} = parseArgs({args: argv, allowPositionals: true, options: {backend: {type: "string"}}})
12
+ if (positionals.length !== 1 || positionals[0] !== "install") throw new Error("expected command: install")
13
+ if (backend !== "sqlite" && backend !== "postgres") throw new Error(`invalid backend: ${backend}`)
14
+ return backend
15
+ }
16
+
17
+ export async function main(argv: string[]): Promise<number> {
18
+ if (argv.includes("-h") || argv.includes("--help")) {
19
+ console.log(USAGE)
20
+ return 0
21
+ }
22
+ let backend: Backend
23
+ try {
24
+ backend = parseInstallArgs(argv)
25
+ } catch (e) {
26
+ console.error(`${(e as Error).message}\n${USAGE}`)
27
+ return EXIT_USAGE
28
+ }
29
+ try {
30
+ const [dir, addon] = await Promise.all([resolveLibsDir(backend), resolveAddonPath()])
31
+ console.log(`libsimplex installed at: ${dir}`)
32
+ console.log(`Node.js add-on installed at: ${addon}`)
33
+ return 0
34
+ } catch (e) {
35
+ console.error(`install failed: ${(e as Error).message}`)
36
+ return EXIT_INSTALL_FAILED
37
+ }
38
+ }
39
+
40
+ if (require.main === module) {
41
+ main(process.argv.slice(2)).then(code => { process.exitCode = code })
42
+ }
package/src/core.ts ADDED
@@ -0,0 +1,240 @@
1
+ import {ChatEvent, ChatResponse, T} from "@simplex-chat/types"
2
+ import * as libs from "./libs"
3
+ import * as simplex from "./simplex"
4
+
5
+ export type Backend = "sqlite" | "postgres"
6
+
7
+ let loading: {backend: Backend, promise: Promise<void>} | undefined
8
+
9
+ /**
10
+ * Resolve (downloading on first use) and load libsimplex for the backend.
11
+ * One libsimplex per process: the Haskell runtime is initialized once, and another backend is rejected.
12
+ */
13
+ export function loadLibrary(backend: Backend): Promise<void> {
14
+ if (loading) {
15
+ if (loading.backend === backend) return loading.promise
16
+ return Promise.reject(new Error(`libsimplex already loaded with backend=${loading.backend}; cannot switch to ${backend} in the same process`))
17
+ }
18
+ const promise = Promise.all([libs.resolveLibsDir(backend), libs.resolveAddonPath()])
19
+ .then(([dir, addon]) => simplex.load(addon, libs.libPath(dir)))
20
+ loading = {backend, promise}
21
+ // a failed download or load can be retried
22
+ promise.catch(() => { loading = undefined })
23
+ return promise
24
+ }
25
+
26
+ /**
27
+ * Initialize chat controller
28
+ * @param {number} [queueSize] - Size of internal queues, the core default is used when omitted.
29
+ */
30
+ export async function chatMigrateInit(dbPath: string, dbKey: string, confirm: MigrationConfirmation, queueSize?: number): Promise<bigint> {
31
+ const [ctrl, res] = queueSize === undefined
32
+ ? await simplex.chat_migrate_init(dbPath, dbKey, confirm)
33
+ : await simplex.chat_migrate_init_queue(dbPath, dbKey, confirm, queueSize)
34
+ const json = JSON.parse(res)
35
+ if (json.type === 'ok') return ctrl
36
+ throw new ChatInitError("Database or migration error (see dbMigrationError property)", json as DBMigrationError)
37
+ }
38
+
39
+ /**
40
+ * Close chat store
41
+ */
42
+ export async function chatCloseStore(ctrl: bigint): Promise<void> {
43
+ const res = await simplex.chat_close_store(ctrl)
44
+ if (res !== "") throw new Error(res)
45
+ }
46
+
47
+ /**
48
+ * Send chat command as string
49
+ */
50
+ export async function chatSendCmd(ctrl: bigint, cmd: string): Promise<ChatResponse> {
51
+ const res = await simplex.chat_send_cmd(ctrl, cmd)
52
+ const json = JSON.parse(res) as APIResult<ChatResponse>
53
+ // console.log(cmd.slice(0, 16), json.result?.type || json.error)
54
+ if (typeof json.result === 'object') return json.result
55
+ if (typeof json.error === 'object') throw new ChatAPIError("Chat command error (see chatError property)", json.error as T.ChatError)
56
+ throw new ChatAPIError("Invalid chat command result")
57
+ }
58
+
59
+ /**
60
+ * Receive chat event
61
+ */
62
+ export async function chatRecvMsgWait(ctrl: bigint, wait: number): Promise<ChatEvent | undefined> {
63
+ const res = await simplex.chat_recv_msg_wait(ctrl, wait)
64
+ if (res === "") return undefined
65
+ const json = JSON.parse(res) as APIResult<ChatEvent>
66
+ // if (json.result) console.log("event", json.result.type)
67
+ if (typeof json.result === 'object') return json.result
68
+ if (typeof json.error === 'object') throw new ChatAPIError("Chat event error (see chatError property)", json.error as T.ChatError)
69
+ throw new ChatAPIError("Invalid chat event")
70
+ }
71
+
72
+ /**
73
+ * Write buffer to encrypted file
74
+ */
75
+ export async function chatWriteFile(ctrl: bigint, path: string, buffer: ArrayBuffer | Uint8Array): Promise<CryptoArgs> {
76
+ const res = await simplex.chat_write_file(ctrl, path, buffer)
77
+ return cryptoArgsResult(res)
78
+ }
79
+
80
+ /**
81
+ * Read buffer from encrypted file
82
+ */
83
+ export async function chatReadFile(path: string, {fileKey, fileNonce}: CryptoArgs): Promise<Buffer> {
84
+ return await simplex.chat_read_file(path, fileKey, fileNonce)
85
+ }
86
+
87
+ /**
88
+ * Encrypt file
89
+ */
90
+ export async function chatEncryptFile(ctrl: bigint, fromPath: string, toPath: string): Promise<CryptoArgs> {
91
+ const res = await simplex.chat_encrypt_file(ctrl, fromPath, toPath)
92
+ return cryptoArgsResult(res)
93
+ }
94
+
95
+ /**
96
+ * Decrypt file
97
+ */
98
+ export async function chatDecryptFile(fromPath: string, {fileKey, fileNonce}: CryptoArgs, toPath: string): Promise<void> {
99
+ const res = await simplex.chat_decrypt_file(fromPath, fileKey, fileNonce, toPath)
100
+ if (res !== "") throw new Error(res)
101
+ }
102
+
103
+ function cryptoArgsResult(res: string): CryptoArgs {
104
+ const json = JSON.parse(res)
105
+ switch (json.type) {
106
+ case "result": return json.cryptoArgs as CryptoArgs
107
+ case "error": throw Error(json.writeError)
108
+ default: throw Error("unexpected chat_write_file result: " + res)
109
+ }
110
+ }
111
+
112
+ export interface APIResult<R> {
113
+ result?: R
114
+ error?: T.ChatError
115
+ }
116
+
117
+ export class ChatAPIError extends Error {
118
+ constructor(public message: string, public chatError: T.ChatError | undefined = undefined) {
119
+ super(message)
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Migration confirmation mode
125
+ */
126
+ export enum MigrationConfirmation {
127
+ YesUp = "yesUp",
128
+ YesUpDown = "yesUpDown",
129
+ Console = "console",
130
+ Error = "error"
131
+ }
132
+
133
+ /**
134
+ * File encryption key and nonce
135
+ */
136
+ export interface CryptoArgs {
137
+ fileKey: string
138
+ fileNonce: string
139
+ }
140
+
141
+ export class ChatInitError extends Error {
142
+ constructor(public message: string, public dbMigrationError: DBMigrationError) {
143
+ super(message)
144
+ }
145
+ }
146
+
147
+ export type DBMigrationError =
148
+ | DBMigrationError.InvalidConfirmation
149
+ | DBMigrationError.InvalidQueueSize
150
+ | DBMigrationError.ErrorNotADatabase // invalid/corrupt database file or incorrect encryption key
151
+ | DBMigrationError.ErrorMigration
152
+ | DBMigrationError.ErrorSQL
153
+
154
+ export namespace DBMigrationError {
155
+ export type Tag = "invalidConfirmation" | "invalidQueueSize" | "errorNotADatabase" | "errorMigration" | "errorSQL"
156
+
157
+ interface Interface {
158
+ type: Tag
159
+ }
160
+
161
+ export interface InvalidConfirmation extends Interface {
162
+ type: "invalidConfirmation"
163
+ }
164
+
165
+ export interface InvalidQueueSize extends Interface {
166
+ type: "invalidQueueSize"
167
+ }
168
+
169
+ export interface ErrorNotADatabase extends Interface {
170
+ type: "errorNotADatabase"
171
+ dbFile: string
172
+ }
173
+
174
+ export interface ErrorMigration extends Interface {
175
+ type: "errorMigration"
176
+ dbFile: string
177
+ migrationError: MigrationError
178
+ }
179
+
180
+ export interface ErrorSQL extends Interface {
181
+ type: "errorSQL"
182
+ dbFile: string
183
+ migrationSQLError: string
184
+ }
185
+ }
186
+
187
+ export type MigrationError =
188
+ | MigrationError.MEUpgrade
189
+ | MigrationError.MEDowngrade
190
+ | MigrationError.MigrationError
191
+
192
+ export namespace MigrationError {
193
+ export type Tag = "upgrade" | "downgrade" | "migrationError"
194
+
195
+ interface Interface {
196
+ type: Tag
197
+ }
198
+
199
+ export interface MEUpgrade extends Interface {
200
+ type: "upgrade"
201
+ upMigrations: UpMigration[]
202
+ }
203
+
204
+ export interface MEDowngrade extends Interface {
205
+ type: "downgrade"
206
+ downMigrations: string[]
207
+ }
208
+
209
+ export interface MigrationError extends Interface {
210
+ type: "migrationError"
211
+ mtrError: MTRError
212
+ }
213
+ }
214
+
215
+ export interface UpMigration {
216
+ upName: string
217
+ withDown: boolean
218
+ }
219
+
220
+ export type MTRError =
221
+ | MTRError.MTRENoDown
222
+ | MTRError.MTREDifferent
223
+
224
+ export namespace MTRError {
225
+ export type Tag = "noDown" | "different"
226
+
227
+ interface Interface {
228
+ type: Tag
229
+ }
230
+
231
+ export interface MTRENoDown extends Interface {
232
+ type: "noDown"
233
+ dbMigrations: string[]
234
+ }
235
+
236
+ export interface MTREDifferent extends Interface {
237
+ type: "different"
238
+ downMigrations: string[]
239
+ }
240
+ }
package/src/index.ts ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * A simple declarative API to run a chat-bot with a single function call.
3
+ * It automates creating and updating of the bot profile, address and bot commands shown in the app UI.
4
+ */
5
+ export * as bot from "./bot"
6
+
7
+ /**
8
+ * An API to send chat commands and receive chat events to/from chat core.
9
+ * You need to use it in bot event handlers, and for any other use cases.
10
+ */
11
+ export * as api from "./api"
12
+
13
+ /**
14
+ * A low level API to the core library - the same that is used in desktop clients.
15
+ * You are unlikely to ever need to use this module directly.
16
+ */
17
+ export * as core from "./core"
18
+
19
+ /**
20
+ * Useful functions for chat events and types.
21
+ */
22
+ export * as util from "./util"