mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-23 06:22:43 +00:00
core, libs: configurable queue size, library fixes (#7542)
* core: add chat_migrate_init_queue FFI export * bots: fix BadgeServiceErrorCode API type * nodejs: pass required command fields * nodejs: fix migration error types * nodejs: install libsimplex from SIMPLEX_LIBS_DIR * nodejs: add queue size option * nodejs: regenerate docs * python: add queue size option * bots: pass incognito in APIConnect * nodejs: accept documented success responses * python: accept documented success responses * nodejs: dispatch each bot message once * nodejs: fix startChat events loop lifecycle * nodejs, python: parse multi-line bot commands * nodejs: fix file buffer handling in addon * nodejs: keep events loop when chat stop fails * python: fix send_and_wait race, load lib off loop * python: make queue size export optional * nodejs: receive events on a dedicated thread * nodejs: release haskell thread after receive * python: receive on a dedicated thread per chat * python: test receive shutdown order * nodejs: one receive thread per chat controller * nodejs: harden receiver shutdown and tests * nodejs: stop chat before closing store * python: stop chat before closing store * nodejs, python: harden close regression and retry * python: free results with ucrt on windows * nodejs: enable c++ exceptions on mac and windows
This commit is contained in:
78 files changed
+1670
-571
No files matched your search
@@ -106,13 +106,15 @@ export class ChatApi {
|
||||
* Initializes the ChatApi.
|
||||
* @param {DbConfig} db - Database configuration (sqlite or postgres).
|
||||
* @param {core.MigrationConfirmation} [confirm=core.MigrationConfirmation.YesUp] - Migration confirmation mode.
|
||||
* @param {number} [queueSize] - Size of internal queues, the core default is used when omitted.
|
||||
*/
|
||||
static async init(
|
||||
db: DbConfig,
|
||||
confirm = core.MigrationConfirmation.YesUp
|
||||
confirm = core.MigrationConfirmation.YesUp,
|
||||
queueSize?: number
|
||||
): Promise<ChatApi> {
|
||||
const [path, key] = dbConfigToMigrateArgs(db)
|
||||
const ctrl = await core.chatMigrateInit(path, key, confirm)
|
||||
const ctrl = await core.chatMigrateInit(path, key, confirm, queueSize)
|
||||
return new ChatApi(ctrl)
|
||||
}
|
||||
|
||||
@@ -120,39 +122,52 @@ export class ChatApi {
|
||||
* Start chat controller. Must be called with the existing user profile.
|
||||
*/
|
||||
async startChat(): Promise<void> {
|
||||
if (this.eventsLoop) throw new Error("chat already started")
|
||||
const ctrl = this.ctrl
|
||||
this.receiveEvents = true
|
||||
this.eventsLoop = this.runEventsLoop()
|
||||
const r = await this.sendChatCmd(CC.StartChat.cmdString({mainApp: true, enableSndFiles: true}))
|
||||
let r: ChatResponse
|
||||
try {
|
||||
r = await core.chatSendCmd(ctrl, CC.StartChat.cmdString({mainApp: true, enableSndFiles: true, serviceRequests: false}))
|
||||
} catch (e) {
|
||||
await this.stopEventsLoop()
|
||||
throw e
|
||||
}
|
||||
if (r.type !== "chatStarted" && r.type !== "chatRunning") {
|
||||
await this.stopEventsLoop()
|
||||
throw new ChatCommandError("error starting chat", r)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stop chat controller.
|
||||
* Must be called before closing the database.
|
||||
* `close` calls it before closing the database.
|
||||
* Usually doesn't need to be called in chat bots.
|
||||
*/
|
||||
async stopChat(): Promise<void> {
|
||||
const r = await this.sendChatCmd("/_stop")
|
||||
if (r.type !== "chatStopped") throw new ChatCommandError("error starting chat", r)
|
||||
this.receiveEvents = false
|
||||
if (this.eventsLoop) await this.eventsLoop
|
||||
this.eventsLoop = undefined
|
||||
if (r.type !== "chatStopped") throw new ChatCommandError("error stopping chat", r)
|
||||
await this.stopEventsLoop()
|
||||
}
|
||||
|
||||
/**
|
||||
* Close chat database.
|
||||
* Stop chat controller and close chat database.
|
||||
* The database is not closed if stopping fails.
|
||||
* Usually doesn't need to be called in chat bots.
|
||||
*/
|
||||
async close(): Promise<void> {
|
||||
this.receiveEvents = false
|
||||
if (this.eventsLoop) await this.eventsLoop
|
||||
this.eventsLoop = undefined
|
||||
// a running controller keeps using the database connections that closing frees
|
||||
await this.stopChat()
|
||||
await core.chatCloseStore(this.ctrl)
|
||||
this.ctrl_ = undefined
|
||||
}
|
||||
|
||||
|
||||
private async stopEventsLoop(): Promise<void> {
|
||||
this.receiveEvents = false
|
||||
if (this.eventsLoop) await this.eventsLoop
|
||||
this.eventsLoop = undefined
|
||||
}
|
||||
|
||||
private async runEventsLoop(): Promise<void> {
|
||||
while (this.receiveEvents) {
|
||||
try {
|
||||
@@ -335,7 +350,7 @@ export class ChatApi {
|
||||
return await core.chatSendCmd(this.ctrl, cmd)
|
||||
}
|
||||
|
||||
async recvChatEvent(wait: number = 5_000_000): Promise<ChatEvent | undefined> {
|
||||
async recvChatEvent(wait: number = 500_000): Promise<ChatEvent | undefined> {
|
||||
return await core.chatRecvMsgWait(this.ctrl, wait)
|
||||
}
|
||||
|
||||
@@ -386,8 +401,10 @@ export class ChatApi {
|
||||
switch (r.type) {
|
||||
case "userProfileUpdated":
|
||||
return r.updateSummary
|
||||
case "userProfileNoChange":
|
||||
return {updateSuccesses: 0, updateFailures: 0, changedContacts: []}
|
||||
default:
|
||||
throw new ChatCommandError("error loading user address", r)
|
||||
throw new ChatCommandError("error setting profile address", r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,7 +477,7 @@ export class ChatApi {
|
||||
updatedMessage: {msgContent, mentions: {}},
|
||||
})
|
||||
)
|
||||
if (r.type === "chatItemUpdated") return r.chatItem.chatItem
|
||||
if (r.type === "chatItemUpdated" || r.type === "chatItemNotChanged") return r.chatItem.chatItem
|
||||
throw new ChatCommandError("error updating chat item", r)
|
||||
}
|
||||
|
||||
@@ -499,10 +516,10 @@ export class ChatApi {
|
||||
chatItemId: number,
|
||||
add: boolean,
|
||||
reaction: T.MsgReaction
|
||||
) {
|
||||
): Promise<T.ACIReaction> {
|
||||
const r = await this.sendChatCmd(CC.APIChatItemReaction.cmdString({chatRef: {chatType, chatId}, chatItemId, add, reaction}))
|
||||
if (r.type === "chatItemsDeleted") return r.chatItemDeletions
|
||||
throw new ChatCommandError("error setting item reaction", r)
|
||||
if (r.type === "chatItemReaction") return r.reaction
|
||||
throw new ChatCommandError("error setting item reaction", r)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -512,6 +529,7 @@ export class ChatApi {
|
||||
async apiReceiveFile(fileId: number): Promise<T.AChatItem> {
|
||||
const r = await this.sendChatCmd(CC.ReceiveFile.cmdString({fileId, userApprovedRelays: true}))
|
||||
if (r.type === "rcvFileAccepted") return r.chatItem
|
||||
if (r.type === "rcvFileAcceptedSndCancelled") throw new ChatCommandError("file cancelled by sender", r)
|
||||
throw new ChatCommandError("error receiving file", r)
|
||||
}
|
||||
|
||||
@@ -698,7 +716,7 @@ export class ChatApi {
|
||||
* Connect via prepared SimpleX link. The link can be 1-time invitation link, contact address or group link
|
||||
* Network usage: interactive.
|
||||
*/
|
||||
async apiConnect(userId: number, incognito: boolean, preparedLink?: T.CreatedConnLink): Promise<ConnReqType> {
|
||||
async apiConnect(userId: number, incognito: boolean, preparedLink: T.CreatedConnLink): Promise<ConnReqType> {
|
||||
const r = await this.sendChatCmd(CC.APIConnect.cmdString({userId, incognito, preparedLink_: preparedLink}))
|
||||
return this.handleConnectResult(r)
|
||||
}
|
||||
@@ -740,7 +758,7 @@ export class ChatApi {
|
||||
* Network usage: no.
|
||||
*/
|
||||
async apiRejectContactRequest(contactReqId: number): Promise<void> {
|
||||
const r = await this.sendChatCmd(CC.APIRejectContact.cmdString({contactReqId}))
|
||||
const r = await this.sendChatCmd(CC.APIRejectContact.cmdString({contactReqId, notify: false}))
|
||||
if (r.type === "contactRequestRejected") return
|
||||
throw new ChatCommandError("error rejecting contact request", r)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user