simplex-chat 7.1.0-beta.2 → 7.1.0-beta.4.2

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 (64) hide show
  1. package/README.md +4 -2
  2. package/binding.gyp +2 -0
  3. package/cpp/simplex.cc +265 -29
  4. package/cpp/simplex.h +2 -0
  5. package/dist/api.d.ts +9 -6
  6. package/dist/api.js +37 -19
  7. package/dist/api.js.map +1 -1
  8. package/dist/bot.d.ts +6 -2
  9. package/dist/bot.js +17 -34
  10. package/dist/bot.js.map +1 -1
  11. package/dist/core.d.ts +11 -7
  12. package/dist/core.js +5 -2
  13. package/dist/core.js.map +1 -1
  14. package/dist/simplex.d.ts +3 -2
  15. package/dist/util.js +1 -1
  16. package/dist/util.js.map +1 -1
  17. package/docs/Namespace.bot.md +1 -0
  18. package/docs/api.Class.ChatApi.md +122 -78
  19. package/docs/api.TypeAlias.EventSubscriberFunc.md +5 -1
  20. package/docs/bot.Function.run.md +1 -1
  21. package/docs/bot.Function.subscribeChatItems.md +27 -0
  22. package/docs/bot.Interface.BotConfig.md +13 -9
  23. package/docs/bot.Interface.BotOptions.md +10 -10
  24. package/docs/bot.TypeAlias.BotDbOpts.md +4 -0
  25. package/docs/core.Class.ChatAPIError.md +4 -4
  26. package/docs/core.Class.ChatInitError.md +4 -4
  27. package/docs/core.DBMigrationError.Interface.ErrorMigration.md +4 -4
  28. package/docs/core.DBMigrationError.Interface.ErrorNotADatabase.md +3 -3
  29. package/docs/core.DBMigrationError.Interface.ErrorSQL.md +4 -4
  30. package/docs/core.DBMigrationError.Interface.InvalidConfirmation.md +2 -2
  31. package/docs/core.DBMigrationError.Interface.InvalidQueueSize.md +25 -0
  32. package/docs/core.DBMigrationError.TypeAlias.Tag.md +2 -2
  33. package/docs/core.Enumeration.MigrationConfirmation.md +5 -5
  34. package/docs/core.Function.chatCloseStore.md +1 -1
  35. package/docs/core.Function.chatDecryptFile.md +1 -1
  36. package/docs/core.Function.chatEncryptFile.md +1 -1
  37. package/docs/core.Function.chatMigrateInit.md +8 -2
  38. package/docs/core.Function.chatReadFile.md +3 -3
  39. package/docs/core.Function.chatRecvMsgWait.md +1 -1
  40. package/docs/core.Function.chatSendCmd.md +1 -1
  41. package/docs/core.Function.chatWriteFile.md +2 -2
  42. package/docs/core.Interface.APIResult.md +3 -3
  43. package/docs/core.Interface.CryptoArgs.md +3 -3
  44. package/docs/core.Interface.UpMigration.md +3 -3
  45. package/docs/core.MTRError.Interface.MTREDifferent.md +3 -3
  46. package/docs/core.MTRError.Interface.MTRENoDown.md +10 -10
  47. package/docs/core.MTRError.TypeAlias.Tag.md +1 -1
  48. package/docs/core.MigrationError.Interface.MEDowngrade.md +3 -3
  49. package/docs/core.MigrationError.Interface.MEUpgrade.md +4 -4
  50. package/docs/core.MigrationError.Interface.MigrationError.md +3 -3
  51. package/docs/core.MigrationError.TypeAlias.Tag.md +1 -1
  52. package/docs/core.Namespace.DBMigrationError.md +1 -0
  53. package/docs/core.TypeAlias.DBMigrationError.md +2 -2
  54. package/docs/core.TypeAlias.MTRError.md +1 -1
  55. package/docs/core.TypeAlias.MigrationError.md +1 -1
  56. package/examples/squaring-bot-readme.js +2 -2
  57. package/examples/squaring-bot.ts +4 -4
  58. package/package.json +2 -2
  59. package/src/api.ts +54 -25
  60. package/src/bot.ts +43 -37
  61. package/src/core.ts +15 -7
  62. package/src/download-libs.js +28 -1
  63. package/src/simplex.d.ts +3 -2
  64. package/src/util.ts +3 -3
@@ -6,7 +6,7 @@
6
6
 
7
7
  # Interface: MTREDifferent
8
8
 
9
- Defined in: [src/core.ts:206](../src/core.ts#L206)
9
+ Defined in: [src/core.ts:214](../src/core.ts#L214)
10
10
 
11
11
  ## Extends
12
12
 
@@ -18,7 +18,7 @@ Defined in: [src/core.ts:206](../src/core.ts#L206)
18
18
 
19
19
  > **downMigrations**: `string`[]
20
20
 
21
- Defined in: [src/core.ts:208](../src/core.ts#L208)
21
+ Defined in: [src/core.ts:216](../src/core.ts#L216)
22
22
 
23
23
  ***
24
24
 
@@ -26,7 +26,7 @@ Defined in: [src/core.ts:208](../src/core.ts#L208)
26
26
 
27
27
  > **type**: `"different"`
28
28
 
29
- Defined in: [src/core.ts:207](../src/core.ts#L207)
29
+ Defined in: [src/core.ts:215](../src/core.ts#L215)
30
30
 
31
31
  #### Overrides
32
32
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  # Interface: MTRENoDown
8
8
 
9
- Defined in: [src/core.ts:201](../src/core.ts#L201)
9
+ Defined in: [src/core.ts:209](../src/core.ts#L209)
10
10
 
11
11
  ## Extends
12
12
 
@@ -14,20 +14,20 @@ Defined in: [src/core.ts:201](../src/core.ts#L201)
14
14
 
15
15
  ## Properties
16
16
 
17
- ### type
17
+ ### dbMigrations
18
18
 
19
- > **type**: `"noDown"`
19
+ > **dbMigrations**: `string`[]
20
20
 
21
- Defined in: [src/core.ts:202](../src/core.ts#L202)
21
+ Defined in: [src/core.ts:211](../src/core.ts#L211)
22
22
 
23
- #### Overrides
23
+ ***
24
24
 
25
- `Interface.type`
25
+ ### type
26
26
 
27
- ***
27
+ > **type**: `"noDown"`
28
28
 
29
- ### upMigrations
29
+ Defined in: [src/core.ts:210](../src/core.ts#L210)
30
30
 
31
- > **upMigrations**: [`UpMigration`](core.Interface.UpMigration.md)
31
+ #### Overrides
32
32
 
33
- Defined in: [src/core.ts:203](../src/core.ts#L203)
33
+ `Interface.type`
@@ -8,4 +8,4 @@
8
8
 
9
9
  > **Tag** = `"noDown"` \| `"different"`
10
10
 
11
- Defined in: [src/core.ts:195](../src/core.ts#L195)
11
+ Defined in: [src/core.ts:203](../src/core.ts#L203)
@@ -6,7 +6,7 @@
6
6
 
7
7
  # Interface: MEDowngrade
8
8
 
9
- Defined in: [src/core.ts:174](../src/core.ts#L174)
9
+ Defined in: [src/core.ts:182](../src/core.ts#L182)
10
10
 
11
11
  ## Extends
12
12
 
@@ -18,7 +18,7 @@ Defined in: [src/core.ts:174](../src/core.ts#L174)
18
18
 
19
19
  > **downMigrations**: `string`[]
20
20
 
21
- Defined in: [src/core.ts:176](../src/core.ts#L176)
21
+ Defined in: [src/core.ts:184](../src/core.ts#L184)
22
22
 
23
23
  ***
24
24
 
@@ -26,7 +26,7 @@ Defined in: [src/core.ts:176](../src/core.ts#L176)
26
26
 
27
27
  > **type**: `"downgrade"`
28
28
 
29
- Defined in: [src/core.ts:175](../src/core.ts#L175)
29
+ Defined in: [src/core.ts:183](../src/core.ts#L183)
30
30
 
31
31
  #### Overrides
32
32
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  # Interface: MEUpgrade
8
8
 
9
- Defined in: [src/core.ts:169](../src/core.ts#L169)
9
+ Defined in: [src/core.ts:177](../src/core.ts#L177)
10
10
 
11
11
  ## Extends
12
12
 
@@ -18,7 +18,7 @@ Defined in: [src/core.ts:169](../src/core.ts#L169)
18
18
 
19
19
  > **type**: `"upgrade"`
20
20
 
21
- Defined in: [src/core.ts:170](../src/core.ts#L170)
21
+ Defined in: [src/core.ts:178](../src/core.ts#L178)
22
22
 
23
23
  #### Overrides
24
24
 
@@ -28,6 +28,6 @@ Defined in: [src/core.ts:170](../src/core.ts#L170)
28
28
 
29
29
  ### upMigrations
30
30
 
31
- > **upMigrations**: [`UpMigration`](core.Interface.UpMigration.md)
31
+ > **upMigrations**: [`UpMigration`](core.Interface.UpMigration.md)[]
32
32
 
33
- Defined in: [src/core.ts:171](../src/core.ts#L171)
33
+ Defined in: [src/core.ts:179](../src/core.ts#L179)
@@ -6,7 +6,7 @@
6
6
 
7
7
  # Interface: MigrationError
8
8
 
9
- Defined in: [src/core.ts:179](../src/core.ts#L179)
9
+ Defined in: [src/core.ts:187](../src/core.ts#L187)
10
10
 
11
11
  ## Extends
12
12
 
@@ -18,7 +18,7 @@ Defined in: [src/core.ts:179](../src/core.ts#L179)
18
18
 
19
19
  > **mtrError**: [`MTRError`](core.TypeAlias.MTRError.md)
20
20
 
21
- Defined in: [src/core.ts:181](../src/core.ts#L181)
21
+ Defined in: [src/core.ts:189](../src/core.ts#L189)
22
22
 
23
23
  ***
24
24
 
@@ -26,7 +26,7 @@ Defined in: [src/core.ts:181](../src/core.ts#L181)
26
26
 
27
27
  > **type**: `"migrationError"`
28
28
 
29
- Defined in: [src/core.ts:180](../src/core.ts#L180)
29
+ Defined in: [src/core.ts:188](../src/core.ts#L188)
30
30
 
31
31
  #### Overrides
32
32
 
@@ -8,4 +8,4 @@
8
8
 
9
9
  > **Tag** = `"upgrade"` \| `"downgrade"` \| `"migrationError"`
10
10
 
11
- Defined in: [src/core.ts:163](../src/core.ts#L163)
11
+ Defined in: [src/core.ts:171](../src/core.ts#L171)
@@ -12,6 +12,7 @@
12
12
  - [ErrorNotADatabase](core.DBMigrationError.Interface.ErrorNotADatabase.md)
13
13
  - [ErrorSQL](core.DBMigrationError.Interface.ErrorSQL.md)
14
14
  - [InvalidConfirmation](core.DBMigrationError.Interface.InvalidConfirmation.md)
15
+ - [InvalidQueueSize](core.DBMigrationError.Interface.InvalidQueueSize.md)
15
16
 
16
17
  ## Type Aliases
17
18
 
@@ -6,6 +6,6 @@
6
6
 
7
7
  # Type Alias: DBMigrationError
8
8
 
9
- > **DBMigrationError** = [`InvalidConfirmation`](core.DBMigrationError.Interface.InvalidConfirmation.md) \| [`ErrorNotADatabase`](core.DBMigrationError.Interface.ErrorNotADatabase.md) \| [`ErrorMigration`](core.DBMigrationError.Interface.ErrorMigration.md) \| [`ErrorSQL`](core.DBMigrationError.Interface.ErrorSQL.md)
9
+ > **DBMigrationError** = [`InvalidConfirmation`](core.DBMigrationError.Interface.InvalidConfirmation.md) \| [`InvalidQueueSize`](core.DBMigrationError.Interface.InvalidQueueSize.md) \| [`ErrorNotADatabase`](core.DBMigrationError.Interface.ErrorNotADatabase.md) \| [`ErrorMigration`](core.DBMigrationError.Interface.ErrorMigration.md) \| [`ErrorSQL`](core.DBMigrationError.Interface.ErrorSQL.md)
10
10
 
11
- Defined in: [src/core.ts:122](../src/core.ts#L122)
11
+ Defined in: [src/core.ts:125](../src/core.ts#L125)
@@ -8,4 +8,4 @@
8
8
 
9
9
  > **MTRError** = [`MTRENoDown`](core.MTRError.Interface.MTRENoDown.md) \| [`MTREDifferent`](core.MTRError.Interface.MTREDifferent.md)
10
10
 
11
- Defined in: [src/core.ts:190](../src/core.ts#L190)
11
+ Defined in: [src/core.ts:198](../src/core.ts#L198)
@@ -8,4 +8,4 @@
8
8
 
9
9
  > **MigrationError** = [`MEUpgrade`](core.MigrationError.Interface.MEUpgrade.md) \| [`MEDowngrade`](core.MigrationError.Interface.MEDowngrade.md) \| [`MigrationError`](core.MigrationError.Interface.MigrationError.md)
10
10
 
11
- Defined in: [src/core.ts:157](../src/core.ts#L157)
11
+ Defined in: [src/core.ts:165](../src/core.ts#L165)
@@ -1,12 +1,12 @@
1
1
  (async () => {
2
2
  const {bot} = await import("../dist/index.js")
3
- const [chat, _user, _address] = await bot.run({
3
+ await bot.run({
4
4
  profile: {displayName: "Squaring bot example", fullName: ""},
5
5
  dbOpts: {type: "sqlite", filePrefix: "./squaring_bot"},
6
6
  options: {
7
7
  addressSettings: {welcomeMessage: "Send a number, I will square it."},
8
8
  },
9
- onMessage: async (ci, content) => {
9
+ onMessage: async (ci, content, chat) => {
10
10
  const n = +content.text
11
11
  const reply = typeof n === "number" && !isNaN(n)
12
12
  ? `${n} * ${n} = ${n * n}`
@@ -3,7 +3,7 @@ import {bot, util} from "../dist"
3
3
 
4
4
  (async () => {
5
5
  const welcomeMessage = "Hello! I am a simple squaring bot.\n\nIf you send me a number, I will calculate its square."
6
- const [chat, _user, _address] = await bot.run({
6
+ await bot.run({
7
7
  profile: {displayName: "Squaring bot example", fullName: ""},
8
8
  dbOpts: {type: "sqlite", filePrefix: "./squaring_bot"},
9
9
  options: {
@@ -15,7 +15,7 @@ import {bot, util} from "../dist"
15
15
  logContacts: true,
16
16
  logNetwork: false
17
17
  },
18
- onMessage: async (ci, content) => {
18
+ onMessage: async (ci, content, chat) => {
19
19
  const n = +content.text
20
20
  const reply = typeof n === "number" && !isNaN(n)
21
21
  ? `${n} * ${n} = ${n * n}`
@@ -23,11 +23,11 @@ import {bot, util} from "../dist"
23
23
  await chat.apiSendTextReply(ci, reply)
24
24
  },
25
25
  onCommands: { // command handlers can be different from commands to be shown in client UI
26
- "help": async (ci: T.AChatItem, _cmd: util.BotCommand) => {
26
+ "help": async (ci: T.AChatItem, _cmd: util.BotCommand, chat) => {
27
27
  await chat.apiSendTextMessage(ci.chatInfo, welcomeMessage)
28
28
  },
29
29
  // fallback handler that will be called for all other commands
30
- "": async (ci: T.AChatItem, _cmd: util.BotCommand) => {
30
+ "": async (ci: T.AChatItem, _cmd: util.BotCommand, chat) => {
31
31
  await chat.apiSendTextReply(ci, "This command is not supported")
32
32
  }
33
33
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simplex-chat",
3
- "version": "7.1.0-beta.2",
3
+ "version": "7.1.0-beta.4.2",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [
@@ -24,7 +24,7 @@
24
24
  "docs": "typedoc"
25
25
  },
26
26
  "dependencies": {
27
- "@simplex-chat/types": "^0.11.2",
27
+ "@simplex-chat/types": "^0.11.4",
28
28
  "extract-zip": "^2.0.1",
29
29
  "fast-deep-equal": "^3.1.3",
30
30
  "node-addon-api": "^8.5.0"
package/src/api.ts CHANGED
@@ -47,7 +47,7 @@ export const defaultBotAddressSettings: BotAddressSettings = {
47
47
  businessAddress: false
48
48
  }
49
49
 
50
- export type EventSubscriberFunc<K extends CEvt.Tag> = (event: ChatEvent & {type: K}) => void | Promise<void>
50
+ export type EventSubscriberFunc<K extends CEvt.Tag> = (event: ChatEvent & {type: K}, chat: ChatApi) => void | Promise<void>
51
51
 
52
52
  export type EventSubscribers = {[K in CEvt.Tag]?: EventSubscriberFunc<K>}
53
53
 
@@ -106,13 +106,15 @@ export class ChatApi {
106
106
  * Initializes the ChatApi.
107
107
  * @param {DbConfig} db - Database configuration (sqlite or postgres).
108
108
  * @param {core.MigrationConfirmation} [confirm=core.MigrationConfirmation.YesUp] - Migration confirmation mode.
109
+ * @param {number} [queueSize] - Size of internal queues, the core default is used when omitted.
109
110
  */
110
111
  static async init(
111
112
  db: DbConfig,
112
- confirm = core.MigrationConfirmation.YesUp
113
+ confirm = core.MigrationConfirmation.YesUp,
114
+ queueSize?: number
113
115
  ): Promise<ChatApi> {
114
116
  const [path, key] = dbConfigToMigrateArgs(db)
115
- const ctrl = await core.chatMigrateInit(path, key, confirm)
117
+ const ctrl = await core.chatMigrateInit(path, key, confirm, queueSize)
116
118
  return new ChatApi(ctrl)
117
119
  }
118
120
 
@@ -120,39 +122,52 @@ export class ChatApi {
120
122
  * Start chat controller. Must be called with the existing user profile.
121
123
  */
122
124
  async startChat(): Promise<void> {
125
+ if (this.eventsLoop) throw new Error("chat already started")
126
+ const ctrl = this.ctrl
123
127
  this.receiveEvents = true
124
128
  this.eventsLoop = this.runEventsLoop()
125
- const r = await this.sendChatCmd(CC.StartChat.cmdString({mainApp: true, enableSndFiles: true, serviceRequests: false}))
129
+ let r: ChatResponse
130
+ try {
131
+ r = await core.chatSendCmd(ctrl, CC.StartChat.cmdString({mainApp: true, enableSndFiles: true, serviceRequests: false}))
132
+ } catch (e) {
133
+ await this.stopEventsLoop()
134
+ throw e
135
+ }
126
136
  if (r.type !== "chatStarted" && r.type !== "chatRunning") {
137
+ await this.stopEventsLoop()
127
138
  throw new ChatCommandError("error starting chat", r)
128
139
  }
129
140
  }
130
-
141
+
131
142
  /**
132
143
  * Stop chat controller.
133
- * Must be called before closing the database.
144
+ * `close` calls it before closing the database.
134
145
  * Usually doesn't need to be called in chat bots.
135
146
  */
136
147
  async stopChat(): Promise<void> {
137
148
  const r = await this.sendChatCmd("/_stop")
138
- if (r.type !== "chatStopped") throw new ChatCommandError("error starting chat", r)
139
- this.receiveEvents = false
140
- if (this.eventsLoop) await this.eventsLoop
141
- this.eventsLoop = undefined
149
+ if (r.type !== "chatStopped") throw new ChatCommandError("error stopping chat", r)
150
+ await this.stopEventsLoop()
142
151
  }
143
152
 
144
153
  /**
145
- * Close chat database.
154
+ * Stop chat controller and close chat database.
155
+ * The database is not closed if stopping fails.
146
156
  * Usually doesn't need to be called in chat bots.
147
157
  */
148
158
  async close(): Promise<void> {
149
- this.receiveEvents = false
150
- if (this.eventsLoop) await this.eventsLoop
151
- this.eventsLoop = undefined
159
+ // a running controller keeps using the database connections that closing frees
160
+ await this.stopChat()
152
161
  await core.chatCloseStore(this.ctrl)
153
162
  this.ctrl_ = undefined
154
163
  }
155
-
164
+
165
+ private async stopEventsLoop(): Promise<void> {
166
+ this.receiveEvents = false
167
+ if (this.eventsLoop) await this.eventsLoop
168
+ this.eventsLoop = undefined
169
+ }
170
+
156
171
  private async runEventsLoop(): Promise<void> {
157
172
  while (this.receiveEvents) {
158
173
  try {
@@ -162,7 +177,7 @@ export class ChatApi {
162
177
  if (subs) {
163
178
  for (const {subscriber, once} of [...subs]) {
164
179
  try {
165
- const p = (subscriber as EventSubscriberFunc<typeof event.type>)(event)
180
+ const p = (subscriber as EventSubscriberFunc<typeof event.type>)(event, this)
166
181
  if (p instanceof Promise) await p
167
182
  } catch(e) {
168
183
  console.log(`${event.type} event processing error`, e)
@@ -172,7 +187,7 @@ export class ChatApi {
172
187
  }
173
188
  for (const r of [...this.receivers]) {
174
189
  try {
175
- const p = r(event)
190
+ const p = r(event, this)
176
191
  if (p instanceof Promise) await p
177
192
  } catch(e) {
178
193
  console.log(`${event.type} event processing error`, e)
@@ -335,7 +350,7 @@ export class ChatApi {
335
350
  return await core.chatSendCmd(this.ctrl, cmd)
336
351
  }
337
352
 
338
- async recvChatEvent(wait: number = 5_000_000): Promise<ChatEvent | undefined> {
353
+ async recvChatEvent(wait: number = 500_000): Promise<ChatEvent | undefined> {
339
354
  return await core.chatRecvMsgWait(this.ctrl, wait)
340
355
  }
341
356
 
@@ -386,8 +401,10 @@ export class ChatApi {
386
401
  switch (r.type) {
387
402
  case "userProfileUpdated":
388
403
  return r.updateSummary
404
+ case "userProfileNoChange":
405
+ return {updateSuccesses: 0, updateFailures: 0, changedContacts: []}
389
406
  default:
390
- throw new ChatCommandError("error loading user address", r)
407
+ throw new ChatCommandError("error setting profile address", r)
391
408
  }
392
409
  }
393
410
 
@@ -405,7 +422,18 @@ export class ChatApi {
405
422
  const r = await this.sendChatCmd(CC.APISetAddressSettings.cmdString({userId, settings}))
406
423
  if (r.type !== "userContactLinkUpdated") {
407
424
  throw new ChatCommandError("error changing user contact address settings", r)
408
- }
425
+ }
426
+ }
427
+
428
+ async apiSetUserDomain(userId: number, simplexDomain?: string): Promise<T.User> {
429
+ const r = await this.sendChatCmd(CC.APISetUserDomain.cmdString({userId, simplexDomain}))
430
+ switch (r.type) {
431
+ case "userProfileUpdated":
432
+ case "userProfileNoChange":
433
+ return r.user
434
+ default:
435
+ throw new ChatCommandError("error setting SimpleX name", r)
436
+ }
409
437
  }
410
438
 
411
439
  /**
@@ -460,7 +488,7 @@ export class ChatApi {
460
488
  updatedMessage: {msgContent, mentions: {}},
461
489
  })
462
490
  )
463
- if (r.type === "chatItemUpdated") return r.chatItem.chatItem
491
+ if (r.type === "chatItemUpdated" || r.type === "chatItemNotChanged") return r.chatItem.chatItem
464
492
  throw new ChatCommandError("error updating chat item", r)
465
493
  }
466
494
 
@@ -499,10 +527,10 @@ export class ChatApi {
499
527
  chatItemId: number,
500
528
  add: boolean,
501
529
  reaction: T.MsgReaction
502
- ) {
530
+ ): Promise<T.ACIReaction> {
503
531
  const r = await this.sendChatCmd(CC.APIChatItemReaction.cmdString({chatRef: {chatType, chatId}, chatItemId, add, reaction}))
504
- if (r.type === "chatItemsDeleted") return r.chatItemDeletions
505
- throw new ChatCommandError("error setting item reaction", r)
532
+ if (r.type === "chatItemReaction") return r.reaction
533
+ throw new ChatCommandError("error setting item reaction", r)
506
534
  }
507
535
 
508
536
  /**
@@ -512,6 +540,7 @@ export class ChatApi {
512
540
  async apiReceiveFile(fileId: number): Promise<T.AChatItem> {
513
541
  const r = await this.sendChatCmd(CC.ReceiveFile.cmdString({fileId, userApprovedRelays: true}))
514
542
  if (r.type === "rcvFileAccepted") return r.chatItem
543
+ if (r.type === "rcvFileAcceptedSndCancelled") throw new ChatCommandError("file cancelled by sender", r)
515
544
  throw new ChatCommandError("error receiving file", r)
516
545
  }
517
546
 
@@ -698,7 +727,7 @@ export class ChatApi {
698
727
  * Connect via prepared SimpleX link. The link can be 1-time invitation link, contact address or group link
699
728
  * Network usage: interactive.
700
729
  */
701
- async apiConnect(userId: number, incognito: boolean, preparedLink?: T.CreatedConnLink): Promise<ConnReqType> {
730
+ async apiConnect(userId: number, incognito: boolean, preparedLink: T.CreatedConnLink): Promise<ConnReqType> {
702
731
  const r = await this.sendChatCmd(CC.APIConnect.cmdString({userId, incognito, preparedLink_: preparedLink}))
703
732
  return this.handleConnectResult(r)
704
733
  }
package/src/bot.ts CHANGED
@@ -6,6 +6,7 @@ import equal = require("fast-deep-equal")
6
6
 
7
7
  export type BotDbOpts = api.DbConfig & {
8
8
  confirmMigrations?: core.MigrationConfirmation
9
+ queueSize?: number
9
10
  }
10
11
 
11
12
  export interface BotOptions {
@@ -34,21 +35,21 @@ const defaultOpts: Required<BotOptions> = {
34
35
 
35
36
  export interface BotConfig {
36
37
  profile: T.Profile,
38
+ simplexName?: string,
37
39
  dbOpts: BotDbOpts,
38
40
  options: BotOptions,
39
- onMessage?: (chatItem: T.AChatItem, content: T.MsgContent) => void | Promise<void>,
41
+ onMessage?: (chatItem: T.AChatItem, content: T.MsgContent, chat: api.ChatApi) => void | Promise<void>,
40
42
  // command handlers can be different from commands to be shown in client UI
41
- onCommands?: {[K in string]?: ((chatItem: T.AChatItem, command: util.BotCommand) => void | Promise<void>)},
43
+ onCommands?: {[K in string]?: ((chatItem: T.AChatItem, command: util.BotCommand, chat: api.ChatApi) => void | Promise<void>)},
42
44
  // If you use `onMessage` and to subscribe "newChatItems" event, exclude content messages from processing
43
45
  // If you use `onCommands` and to subscribe "newChatItems" event, exclude commands from processing
44
46
  events?: api.EventSubscribers
45
47
  }
46
48
 
47
- export async function run({profile, dbOpts, options = defaultOpts, onMessage, onCommands = {}, events = {}}: BotConfig): Promise<[api.ChatApi, T.User, T.UserContactLink | undefined]> {
48
- const bot = await api.ChatApi.init(dbOpts, dbOpts.confirmMigrations || core.MigrationConfirmation.YesUp)
49
+ export async function run({profile, simplexName, 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)
49
51
  const opts = fullOptions(options)
50
- if (onMessage) subscribeMessages(bot, onMessage)
51
- if (Object.keys(onCommands).length > 0) subscribeCommands(bot, onCommands)
52
+ if (onMessage || Object.keys(onCommands).length > 0) subscribeChatItems(bot, onMessage, onCommands)
52
53
  if (Object.keys(events).length > 0) bot.on(events)
53
54
  subscribeLogEvents(bot, opts)
54
55
  const botProfile = mkBotProfile(profile, opts)
@@ -60,7 +61,8 @@ export async function run({profile, dbOpts, options = defaultOpts, onMessage, on
60
61
  console.log(`Bot address: ${addressLink}`)
61
62
  if (opts.useBotProfile) botProfile.contactLink = addressLink
62
63
  }
63
- await updateBotUserProfile(bot, user, botProfile, opts)
64
+ const namedUser = await updateBotSimplexName(bot, user, simplexName, opts)
65
+ await updateBotUserProfile(bot, namedUser, botProfile, opts)
64
66
  return [bot, user, address]
65
67
  }
66
68
 
@@ -105,38 +107,25 @@ function mkBotProfile(profile: T.Profile, opts: Required<BotOptions>): T.Profile
105
107
  return profile
106
108
  }
107
109
 
108
- function subscribeMessages(bot: api.ChatApi, onMessage: (chatItem: T.AChatItem, content: T.MsgContent) => void | Promise<void>) {
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
+ ) {
109
115
  bot.on("newChatItems", async ({chatItems}) => {
110
116
  for (const ci of chatItems) {
111
- if (ci.chatItem.content.type === "rcvMsgContent") {
112
- try {
113
- const p = onMessage(ci, ci.chatItem.content.msgContent)
114
- if (p instanceof Promise) await p
115
- } catch (e) {
116
- console.log("message processing error", e)
117
- }
118
- }
119
- }
120
- })
121
- }
122
-
123
- function subscribeCommands(bot: api.ChatApi, commands: {[K in string]?: ((chatItem: T.AChatItem, command: util.BotCommand) => void | Promise<void>)}) {
124
- bot.on("newChatItems", async (evt) => {
125
- for (const ci of evt.chatItems) {
117
+ const content = ci.chatItem.content
118
+ if (content.type !== "rcvMsgContent") continue
126
119
  const cmd = util.ciBotCommand(ci.chatItem)
127
- if (cmd) {
128
- const cmdFunc = commands[cmd.keyword] || commands[""]
129
- if (cmdFunc) {
130
- try {
131
- const p = cmdFunc(ci, cmd)
132
- if (p instanceof Promise) await p
133
- } catch(e) {
134
- console.log(`${cmd} command processing error`, e)
135
- }
136
- }
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)
137
126
  }
138
127
  }
139
- })
128
+ })
140
129
  }
141
130
 
142
131
  function subscribeLogEvents(bot: api.ChatApi, opts: Required<BotOptions>) {
@@ -193,15 +182,32 @@ async function createOrUpdateAddress(bot: api.ChatApi, user: T.User, opts: Requi
193
182
  }
194
183
  }
195
184
 
196
- return address
185
+ return address
186
+ }
187
+
188
+ async function updateBotSimplexName(bot: api.ChatApi, user: T.User, simplexName: string | undefined, opts: Required<BotOptions>): Promise<T.User> {
189
+ const name = simplexName?.toLowerCase()
190
+ if (user.profile.contactDomain?.domain === name) return user
191
+ if (!opts.updateAddress) {
192
+ console.log("Bot SimpleX name changed")
193
+ return user
194
+ }
195
+ console.log("Bot SimpleX name changed, updating...")
196
+ try {
197
+ return await bot.apiSetUserDomain(user.userId, name)
198
+ } catch (e) {
199
+ console.log("Error updating bot SimpleX name", e)
200
+ return user
201
+ }
197
202
  }
198
203
 
199
204
  async function updateBotUserProfile(bot: api.ChatApi, user: T.User, profile: T.Profile, opts: Required<BotOptions>): Promise<void> {
200
205
  const {userId} = user
201
- if (!equal(util.fromLocalProfile(user.profile), profile)) {
206
+ const {contactDomain, ...currentProfile} = util.fromLocalProfile(user.profile)
207
+ if (!equal(currentProfile, profile)) {
202
208
  if (opts.updateProfile) {
203
209
  console.log("Bot profile changed, updating...")
204
- const summary = await bot.apiUpdateProfile(userId, profile)
210
+ const summary = await bot.apiUpdateProfile(userId, {...profile, contactDomain})
205
211
  console.log(
206
212
  summary
207
213
  ? `Bot profile updated: ${summary.updateSuccesses} updated contact(s), ${summary.updateFailures} failed contact update(s).`
package/src/core.ts CHANGED
@@ -3,9 +3,12 @@ import * as simplex from "./simplex"
3
3
 
4
4
  /**
5
5
  * Initialize chat controller
6
+ * @param {number} [queueSize] - Size of internal queues, the core default is used when omitted.
6
7
  */
7
- export async function chatMigrateInit(dbPath: string, dbKey: string, confirm: MigrationConfirmation): Promise<bigint> {
8
- const [ctrl, res] = await simplex.chat_migrate_init(dbPath, dbKey, confirm)
8
+ export async function chatMigrateInit(dbPath: string, dbKey: string, confirm: MigrationConfirmation, queueSize?: number): Promise<bigint> {
9
+ const [ctrl, res] = queueSize === undefined
10
+ ? await simplex.chat_migrate_init(dbPath, dbKey, confirm)
11
+ : await simplex.chat_migrate_init_queue(dbPath, dbKey, confirm, queueSize)
9
12
  const json = JSON.parse(res)
10
13
  if (json.type === 'ok') return ctrl
11
14
  throw new ChatInitError("Database or migration error (see dbMigrationError property)", json as DBMigrationError)
@@ -47,7 +50,7 @@ export async function chatRecvMsgWait(ctrl: bigint, wait: number): Promise<ChatE
47
50
  /**
48
51
  * Write buffer to encrypted file
49
52
  */
50
- export async function chatWriteFile(ctrl: bigint, path: string, buffer: ArrayBuffer): Promise<CryptoArgs> {
53
+ export async function chatWriteFile(ctrl: bigint, path: string, buffer: ArrayBuffer | Uint8Array): Promise<CryptoArgs> {
51
54
  const res = await simplex.chat_write_file(ctrl, path, buffer)
52
55
  return cryptoArgsResult(res)
53
56
  }
@@ -55,7 +58,7 @@ export async function chatWriteFile(ctrl: bigint, path: string, buffer: ArrayBuf
55
58
  /**
56
59
  * Read buffer from encrypted file
57
60
  */
58
- export async function chatReadFile(path: string, {fileKey, fileNonce}: CryptoArgs): Promise<ArrayBuffer> {
61
+ export async function chatReadFile(path: string, {fileKey, fileNonce}: CryptoArgs): Promise<Buffer> {
59
62
  return await simplex.chat_read_file(path, fileKey, fileNonce)
60
63
  }
61
64
 
@@ -121,12 +124,13 @@ export class ChatInitError extends Error {
121
124
 
122
125
  export type DBMigrationError =
123
126
  | DBMigrationError.InvalidConfirmation
127
+ | DBMigrationError.InvalidQueueSize
124
128
  | DBMigrationError.ErrorNotADatabase // invalid/corrupt database file or incorrect encryption key
125
129
  | DBMigrationError.ErrorMigration
126
130
  | DBMigrationError.ErrorSQL
127
131
 
128
132
  export namespace DBMigrationError {
129
- export type Tag = "invalidConfirmation" | "errorNotADatabase" | "errorMigration" | "errorSQL"
133
+ export type Tag = "invalidConfirmation" | "invalidQueueSize" | "errorNotADatabase" | "errorMigration" | "errorSQL"
130
134
 
131
135
  interface Interface {
132
136
  type: Tag
@@ -136,6 +140,10 @@ export namespace DBMigrationError {
136
140
  type: "invalidConfirmation"
137
141
  }
138
142
 
143
+ export interface InvalidQueueSize extends Interface {
144
+ type: "invalidQueueSize"
145
+ }
146
+
139
147
  export interface ErrorNotADatabase extends Interface {
140
148
  type: "errorNotADatabase"
141
149
  dbFile: string
@@ -168,7 +176,7 @@ export namespace MigrationError {
168
176
 
169
177
  export interface MEUpgrade extends Interface {
170
178
  type: "upgrade"
171
- upMigrations: UpMigration
179
+ upMigrations: UpMigration[]
172
180
  }
173
181
 
174
182
  export interface MEDowngrade extends Interface {
@@ -200,7 +208,7 @@ export namespace MTRError {
200
208
 
201
209
  export interface MTRENoDown extends Interface {
202
210
  type: "noDown"
203
- upMigrations: UpMigration
211
+ dbMigrations: string[]
204
212
  }
205
213
 
206
214
  export interface MTREDifferent extends Interface {