From 47d32b674a2b1fab977e11dbd1cd1b3d2a193c2b Mon Sep 17 00:00:00 2001 From: Evgeny Date: Sat, 12 Sep 2026 14:13:11 +0100 Subject: [PATCH] bots: add APIs (#7494) * docs: fix bot API surface - Fix missing space in APIShareMyAddress syntax expression which resulted in parse errors - Document missing `Connect` responses: `CRConnectionPlan`, `CRSentInvitationToContact`, `CRStartedConnectionToContact`, and `CRStartedConnectionToGroup` (relay channel links). - Document missing `CRUserProfileNoChange` responsed returned when `APISetProfileAddress` doesn't change the profile. - Expose remote control APIs to bots * website: improve sign-up forms (#7484) * website: improve sign-up forms * name --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> * rename field * generic --------- Co-authored-by: a1akris Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> --- bots/api/COMMANDS.md | 121 +++++++++++++++++- bots/api/EVENTS.md | 35 +++++ bots/api/TYPES.md | 94 ++++++++++++++ bots/src/API/Docs/Commands.hs | 16 ++- bots/src/API/Docs/Events.hs | 9 +- bots/src/API/Docs/Responses.hs | 10 +- bots/src/API/Docs/Types.hs | 13 ++ bots/src/API/TypeInfo.hs | 2 + .../types/typescript/src/commands.ts | 45 ++++++- .../types/typescript/src/events.ts | 16 +++ .../types/typescript/src/responses.ts | 45 +++++++ .../types/typescript/src/types.ts | 99 ++++++++++++++ .../src/simplex_chat/types/_commands.py | 42 +++++- .../src/simplex_chat/types/_events.py | 26 +++- .../src/simplex_chat/types/_responses.py | 37 +++++- .../src/simplex_chat/types/_types.py | 73 +++++++++++ src/Simplex/Chat/Controller.hs | 4 +- 17 files changed, 661 insertions(+), 26 deletions(-) diff --git a/bots/api/COMMANDS.md b/bots/api/COMMANDS.md index dd62b87585..42b3da2373 100644 --- a/bots/api/COMMANDS.md +++ b/bots/api/COMMANDS.md @@ -79,6 +79,10 @@ This file is generated automatically. - [StartChat](#startchat) - [APIStopChat](#apistopchat) +[Remote control commands](#remote-control-commands) +- [ConnectRemoteCtrl](#connectremotectrl) +- [VerifyRemoteCtrlSession](#verifyremotectrlsession) + --- @@ -231,6 +235,10 @@ UserProfileUpdated: User profile updated. - toProfile: [Profile](./TYPES.md#profile) - updateSummary: [UserProfileUpdateSummary](./TYPES.md#userprofileupdatesummary) +UserProfileNoChange: User profile was not changed. +- type: "userProfileNoChange" +- user: [User](./TYPES.md#user) + ChatCmdError: Command error (only used in WebSockets API). - type: "chatCmdError" - chatError: [ChatError](./TYPES.md#chaterror) @@ -505,15 +513,15 @@ Share user address card **Syntax**: ``` -/_share address +/_share address ``` ```javascript -'/_share address' + ChatRef.cmdString(toSendRef) // JavaScript +'/_share address ' + ChatRef.cmdString(toSendRef) // JavaScript ``` ```python -'/_share address' + ChatRef_cmd_string(toSendRef) # Python +'/_share address ' + ChatRef_cmd_string(toSendRef) # Python ``` **Response**: @@ -1609,6 +1617,33 @@ SentInvitation: Invitation sent to contact address. - connection: [PendingContactConnection](./TYPES.md#pendingcontactconnection) - customUserProfile: [Profile](./TYPES.md#profile)? +ConnectionPlan: Connection link information. +- type: "connectionPlan" +- user: [User](./TYPES.md#user) +- connLink: [CreatedConnLink](./TYPES.md#createdconnlink) +- planSimplexName: [SimplexNameInfo](./TYPES.md#simplexnameinfo)? +- otherSimplexName: [SimplexNameInfo](./TYPES.md#simplexnameinfo)? +- connectionPlan: [ConnectionPlan](./TYPES.md#connectionplan) + +SentInvitationToContact: Invitation sent to contact (when connecting via SimpleX name to a known contact address).. +- type: "sentInvitationToContact" +- user: [User](./TYPES.md#user) +- contact: [Contact](./TYPES.md#contact) +- customUserProfile: [Profile](./TYPES.md#profile)? + +StartedConnectionToContact: Connection to contact started (when connecting via prepared contact).. +- type: "startedConnectionToContact" +- user: [User](./TYPES.md#user) +- contact: [Contact](./TYPES.md#contact) +- customUserProfile: [Profile](./TYPES.md#profile)? + +StartedConnectionToGroup: Connection to channel started (when connecting via channel link).. +- type: "startedConnectionToGroup" +- user: [User](./TYPES.md#user) +- groupInfo: [GroupInfo](./TYPES.md#groupinfo) +- customUserProfile: [Profile](./TYPES.md#profile)? +- relayResults: [[RelayConnectionResult](./TYPES.md#relayconnectionresult)] + ChatCmdError: Command error (only used in WebSockets API). - type: "chatCmdError" - chatError: [ChatError](./TYPES.md#chaterror) @@ -2370,3 +2405,83 @@ ChatStopped: Chat stopped. - type: "chatStopped" --- + + +## Remote control commands + +Allows a bot to accept an incoming remote control session from a SimpleX Desktop client, giving the desktop live access to the bot's SimpleX instance. + + +### ConnectRemoteCtrl + +Connect to a remote controller using an OOB invitation link. + +*Network usage*: interactive. + +**Parameters**: +- remoteInvitation: string + +**Syntax**: + +``` +/crc +``` + +```javascript +'/crc ' + remoteInvitation // JavaScript +``` + +```python +'/crc ' + remoteInvitation # Python +``` + +**Responses**: + +RemoteCtrlConnecting: Remote controller is connecting.. +- type: "remoteCtrlConnecting" +- remoteCtrl_: [RemoteCtrlInfo](./TYPES.md#remotectrlinfo)? +- ctrlAppInfo: [CtrlAppInfo](./TYPES.md#ctrlappinfo) +- appVersion: string + +ChatCmdError: Command error (only used in WebSockets API). +- type: "chatCmdError" +- chatError: [ChatError](./TYPES.md#chaterror) + +--- + + +### VerifyRemoteCtrlSession + +Verify the remote controller session code to complete the connection. + +*Network usage*: no. + +**Parameters**: +- sessionCode: string + +**Syntax**: + +``` +/verify remote ctrl +``` + +```javascript +'/verify remote ctrl ' + sessionCode // JavaScript +``` + +```python +'/verify remote ctrl ' + sessionCode # Python +``` + +**Responses**: + +RemoteCtrlConnected: Remote controller session connected.. +- type: "remoteCtrlConnected" +- remoteCtrl: [RemoteCtrlInfo](./TYPES.md#remotectrlinfo) +- compression: bool + +ChatCmdError: Command error (only used in WebSockets API). +- type: "chatCmdError" +- chatError: [ChatError](./TYPES.md#chaterror) + +--- diff --git a/bots/api/EVENTS.md b/bots/api/EVENTS.md index d2744951cd..5416fe4c0e 100644 --- a/bots/api/EVENTS.md +++ b/bots/api/EVENTS.md @@ -72,6 +72,10 @@ This file is generated automatically. - [ServiceRequest](#servicerequest) - [ServiceReplySent](#servicereplysent) +[Remote control events](#remote-control-events) +- [RemoteCtrlSessionCode](#remotectrlsessioncode) +- [RemoteCtrlStopped](#remotectrlstopped) + [Error events](#error-events) - [MessageError](#messageerror) - [ChatError](#chaterror) @@ -793,6 +797,37 @@ Correlate `connectionId` with the connection ID from the response to [APISendSer --- +## Remote control events + +Bots that act as remote control hosts receive these events during the remote control session lifecycle. + + +### RemoteCtrlSessionCode + +Remote controller session code ready for verification. + +Use [VerifyRemoteCtrlSession](./COMMANDS.md#verifyremotectrlsession) to complete the connection. + +**Record type**: +- type: "remoteCtrlSessionCode" +- remoteCtrl_: [RemoteCtrlInfo](./TYPES.md#remotectrlinfo)? +- sessionCode: string + +--- + + +### RemoteCtrlStopped + +Remote controller session stopped. + +**Record type**: +- type: "remoteCtrlStopped" +- rcsState: [RemoteCtrlSessionState](./TYPES.md#remotectrlsessionstate) +- rcStopReason: [RemoteCtrlStopReason](./TYPES.md#remotectrlstopreason) + +--- + + ## Error events Bots may log these events for debugging. There will be many error events - this does NOT indicate a malfunction - e.g., they may happen because of bad network connectivity, or because messages may be delivered to deleted chats for a short period of time (they will be ignored). diff --git a/bots/api/TYPES.md b/bots/api/TYPES.md index 1ba260b3d3..6759ff9d35 100644 --- a/bots/api/TYPES.md +++ b/bots/api/TYPES.md @@ -10,6 +10,7 @@ This file is generated automatically. - [AgentCryptoError](#agentcryptoerror) - [AgentErrorType](#agenterrortype) - [AgentServiceError](#agentserviceerror) +- [AppVersionRange](#appversionrange) - [AutoAccept](#autoaccept) - [BadgeInfo](#badgeinfo) - [BadgeProof](#badgeproof) @@ -77,6 +78,7 @@ This file is generated automatically. - [CreatedConnLink](#createdconnlink) - [CryptoFile](#cryptofile) - [CryptoFileArgs](#cryptofileargs) +- [CtrlAppInfo](#ctrlappinfo) - [DroppedMsg](#droppedmsg) - [E2EInfo](#e2einfo) - [ErrorType](#errortype) @@ -170,8 +172,12 @@ This file is generated automatically. - [RcvGroupEvent](#rcvgroupevent) - [RcvMsgError](#rcvmsgerror) - [RelayCapabilities](#relaycapabilities) +- [RelayConnectionResult](#relayconnectionresult) - [RelayProfile](#relayprofile) - [RelayStatus](#relaystatus) +- [RemoteCtrlInfo](#remotectrlinfo) +- [RemoteCtrlSessionState](#remotectrlsessionstate) +- [RemoteCtrlStopReason](#remotectrlstopreason) - [ReportReason](#reportreason) - [RoleGroupPreference](#rolegrouppreference) - [SMPAgentError](#smpagenterror) @@ -386,6 +392,17 @@ BadSignature: - type: "badSignature" +--- + +## AppVersionRange + +Remote controller app version range (min and max as version strings). + +**Record type**: +- minVersion: string +- maxVersion: string + + --- ## AutoAccept @@ -1967,6 +1984,18 @@ connFullLink + ((' ' + connShortLink) if connShortLink is not None else '') # Py - fileNonce: string +--- + +## CtrlAppInfo + +Remote controller application info. + +**Record type**: +- appVersionRange: [AppVersionRange](#appversionrange) +- deviceName: string +- compression: bool + + --- ## DroppedMsg @@ -3558,6 +3587,15 @@ ParseError: - webDomain: string? +--- + +## RelayConnectionResult + +**Record type**: +- relayMember: [GroupMember](#groupmember) +- relayError: [ChatError](#chaterror)? + + --- ## RelayProfile @@ -3583,6 +3621,62 @@ ParseError: - "rejected" +--- + +## RemoteCtrlInfo + +**Record type**: +- remoteCtrlId: int64 +- ctrlDeviceName: string +- sessionState: [RemoteCtrlSessionState](#remotectrlsessionstate)? + + +--- + +## RemoteCtrlSessionState + +**Discriminated union type**: + +Starting: +- type: "starting" + +Searching: +- type: "searching" + +Connecting: +- type: "connecting" + +PendingConfirmation: +- type: "pendingConfirmation" +- sessionCode: string + +Connected: +- type: "connected" +- sessionCode: string + + +--- + +## RemoteCtrlStopReason + +**Discriminated union type**: + +DiscoveryFailed: +- type: "discoveryFailed" +- chatError: [ChatError](#chaterror) + +ConnectionFailed: +- type: "connectionFailed" +- chatError: [ChatError](#chaterror) + +SetupFailed: +- type: "setupFailed" +- chatError: [ChatError](#chaterror) + +Disconnected: +- type: "disconnected" + + --- ## ReportReason diff --git a/bots/src/API/Docs/Commands.hs b/bots/src/API/Docs/Commands.hs index d126ff1844..782d0d21cd 100644 --- a/bots/src/API/Docs/Commands.hs +++ b/bots/src/API/Docs/Commands.hs @@ -80,7 +80,7 @@ chatCommandsDocsData = [ ("APICreateMyAddress", ["server_"], "Create bot address.", ["CRUserContactLinkCreated", "CRChatCmdError"], [], Just UNInteractive, "/_address " <> Param "userId" <> OnOffParam "pq_ratchet" "pqRatchet" Nothing), ("APIDeleteMyAddress", [], "Delete bot address.", ["CRUserContactLinkDeleted", "CRChatCmdError"], [], Just UNBackground, "/_delete_address " <> Param "userId"), ("APIShowMyAddress", [], "Get bot address and settings.", ["CRUserContactLink", "CRChatCmdError"], [], Nothing, "/_show_address " <> Param "userId"), - ("APISetProfileAddress", [], "Add address to bot profile.", ["CRUserProfileUpdated", "CRChatCmdError"], [], Just UNInteractive, "/_profile_address " <> Param "userId" <> " " <> OnOff "enable"), + ("APISetProfileAddress", [], "Add address to bot profile.", ["CRUserProfileUpdated", "CRUserProfileNoChange", "CRChatCmdError"], [], Just UNInteractive, "/_profile_address " <> Param "userId" <> " " <> OnOff "enable"), ("APISetAddressSettings", [], "Set bot address settings.", ["CRUserContactLinkUpdated", "CRChatCmdError"], [], Just UNInteractive, "/_address_settings " <> Param "userId" <> OnOffParam "pq_ratchet" "pqRatchet" Nothing <> " " <> Json "settings") ] ), @@ -98,7 +98,7 @@ chatCommandsDocsData = ("APIDeleteChatItem", [], "Delete message.", ["CRChatItemsDeleted", "CRChatCmdError"], [], Just UNBackground, "/_delete item " <> Param "chatRef" <> " " <> Join ',' "chatItemIds" <> " " <> Param "deleteMode"), ("APIDeleteMemberChatItem", [], "Moderate message. Requires Moderator role (and higher than message author's).", ["CRChatItemsDeleted", "CRChatCmdError"], [], Just UNBackground, "/_delete member item #" <> Param "groupId" <> " " <> Join ',' "chatItemIds"), ("APIChatItemReaction", [], "Add/remove message reaction.", ["CRChatItemReaction", "CRChatCmdError"], [], Just UNBackground, "/_reaction " <> Param "chatRef" <> " " <> Param "chatItemId" <> " " <> OnOff "add" <> " " <> Json "reaction"), - ("APIShareMyAddress", [], "Share user address card", ["CRChatMsgContent"], [], Nothing, "/_share address" <> Param "toSendRef"), + ("APIShareMyAddress", [], "Share user address card", ["CRChatMsgContent"], [], Nothing, "/_share address " <> Param "toSendRef"), ("APIShareChatMsgContent", [], "Share channel address", ["CRChatMsgContent"], [], Nothing, "/_share chat content " <> Param "shareChatRef" <> " " <> Param "toSendRef") ] ), @@ -141,7 +141,7 @@ chatCommandsDocsData = -- `Maybe` in `connectTarget :: Maybe ConnectTarget` is used to signal parse failure to the runtime (the handler returns CEInvalidConnReq on Nothing); it is NOT API-level optionality. The parameter is required from callers. ("APIConnectPlan", [], "Determine SimpleX link type and if the bot is already connected via this link or name.", ["CRConnectionPlan", "CRChatCmdError"], [], Just UNInteractive, "/_connect plan " <> Param "userId" <> " " <> Param "connectTarget"), ("APIConnect", [], "Connect via prepared SimpleX link. The link can be 1-time invitation link, contact address or group link.", ["CRSentConfirmation", "CRContactAlreadyExists", "CRSentInvitation", "CRChatCmdError"], [], Just UNInteractive, "/_connect " <> Param "userId" <> Optional "" (" " <> Param "$0") "preparedLink_"), - ("Connect", [], "Connect via SimpleX link or name as string in the active user profile.", ["CRSentConfirmation", "CRContactAlreadyExists", "CRSentInvitation", "CRChatCmdError"], [], Just UNInteractive, "/connect" <> Optional "" (" " <> Param "$0") "connTarget_"), + ("Connect", [], "Connect via SimpleX link or name as string in the active user profile.", ["CRSentConfirmation", "CRContactAlreadyExists", "CRSentInvitation", "CRConnectionPlan", "CRSentInvitationToContact", "CRStartedConnectionToContact", "CRStartedConnectionToGroup", "CRChatCmdError"], [], Just UNInteractive, "/connect" <> Optional "" (" " <> Param "$0") "connTarget_"), ("APIAcceptContact", ["incognito"], "Accept contact request.", ["CRAcceptingContactRequest", "CRChatCmdError"], [], Just UNInteractive, "/_accept " <> Param "contactReqId"), ("APIRejectContact", [], "Reject contact request. The user who sent the request is **not notified**.", ["CRContactRequestRejected", "CRChatCmdError"], [], Nothing, "/_reject " <> Param "contactReqId") ] @@ -202,6 +202,12 @@ chatCommandsDocsData = [ ("StartChat", [], "Start chat controller.", ["CRChatStarted", "CRChatRunning"], [], Nothing, "/_start" <> OnOffParam "main" "mainApp" Nothing <> OnOffParam "snd_files" "enableSndFiles" (Just True) <> OnOffParam "service_requests" "serviceRequests" (Just False)), ("APIStopChat", [], "Stop chat controller.", ["CRChatStopped"], [], Nothing, "/_stop") ] + ), + ( "Remote control commands", + "Allows a bot to accept an incoming remote control session from a SimpleX Desktop client, giving the desktop live access to the bot's SimpleX instance.", + [ ("ConnectRemoteCtrl", [], "Connect to a remote controller using an OOB invitation link.", ["CRRemoteCtrlConnecting", "CRChatCmdError"], [], Just UNInteractive, "/crc " <> Param "remoteInvitation"), + ("VerifyRemoteCtrlSession", [], "Verify the remote controller session code to complete the connection.", ["CRRemoteCtrlConnected", "CRChatCmdError"], [], Nothing, "/verify remote ctrl " <> Param "sessionCode") + ] ) ] @@ -452,7 +458,6 @@ undocumentedCommands = "APIVerifyToken", "CheckChatRunning", "ConfirmRemoteCtrl", - "ConnectRemoteCtrl", "CustomChatCommand", "DebugEvent", "DebugLocks", @@ -499,6 +504,5 @@ undocumentedCommands = "SwitchRemoteHost", "TestChatRelay", "TestProtoServer", - "TestStorageEncryption", - "VerifyRemoteCtrlSession" + "TestStorageEncryption" ] diff --git a/bots/src/API/Docs/Events.hs b/bots/src/API/Docs/Events.hs index 1590ad54d5..05b3067181 100644 --- a/bots/src/API/Docs/Events.hs +++ b/bots/src/API/Docs/Events.hs @@ -152,6 +152,13 @@ chatEventsDocsData = ], [] ), + ( "Remote control events", + "Bots that act as remote control hosts receive these events during the remote control session lifecycle.", + [ ("CEvtRemoteCtrlSessionCode", "Remote controller session code ready for verification.\n\nUse [VerifyRemoteCtrlSession](./COMMANDS.md#verifyremotectrlsession) to complete the connection."), + ("CEvtRemoteCtrlStopped", "Remote controller session stopped.") + ], + [] + ), ( "Error events", "Bots may log these events for debugging. \ \There will be many error events - this does NOT indicate a malfunction - \ @@ -203,8 +210,6 @@ undocumentedEvents = "CEvtRcvFileProgressXFTP", "CEvtRcvStandaloneFileComplete", "CEvtRemoteCtrlFound", - "CEvtRemoteCtrlSessionCode", - "CEvtRemoteCtrlStopped", "CEvtRemoteHostConnected", "CEvtRemoteHostSessionCode", "CEvtRemoteHostStopped", diff --git a/bots/src/API/Docs/Responses.hs b/bots/src/API/Docs/Responses.hs index 76f1ddb76b..c87c46c012 100644 --- a/bots/src/API/Docs/Responses.hs +++ b/bots/src/API/Docs/Responses.hs @@ -88,11 +88,16 @@ chatResponsesDocsData = ("CRRcvFileAccepted", "File accepted to be received"), ("CRRcvFileAcceptedSndCancelled", "File accepted, but no longer sent"), ("CRRcvFileCancelled", "Cancelled receiving file"), + ("CRRemoteCtrlConnected", "Remote controller session connected."), + ("CRRemoteCtrlConnecting", "Remote controller is connecting."), ("CRSentConfirmation", "Confirmation sent to one-time invitation"), ("CRSentGroupInvitation", "Group invitation sent"), ("CRSentInvitation", "Invitation sent to contact address"), + ("CRSentInvitationToContact", "Invitation sent to contact (when connecting via SimpleX name to a known contact address)."), ("CRServiceReplyAccepted", "Service reply accepted for delivery. `connectionId` correlates the reply delivery event."), ("CRSndFileCancelled", "Cancelled sending file"), + ("CRStartedConnectionToContact", "Connection to contact started (when connecting via prepared contact)."), + ("CRStartedConnectionToGroup", "Connection to channel started (when connecting via channel link)."), ("CRUserAcceptedGroupSent", "User accepted group invitation"), ("CRUserContactLink", "User contact address"), ("CRUserContactLinkCreated", "User contact address created"), @@ -188,13 +193,10 @@ undocumentedResponses = "CRQueueInfo", "CRRcvStandaloneFileCreated", "CRReactionMembers", - "CRRemoteCtrlConnected", - "CRRemoteCtrlConnecting", "CRRemoteCtrlList", "CRRemoteFileStored", "CRRemoteHostList", "CRRemoteHostStarted", - "CRSentInvitationToContact", "CRServerOperatorConditions", "CRServerTestResult", "CRServiceResponse", @@ -202,8 +204,6 @@ undocumentedResponses = "CRSndStandaloneFileCreated", "CRSQLResult", "CRStandaloneFileInfo", - "CRStartedConnectionToContact", - "CRStartedConnectionToGroup", "CRTagsUpdated", "CRUsageConditions", "CRUserPrivacy", diff --git a/bots/src/API/Docs/Types.hs b/bots/src/API/Docs/Types.hs index 4567512b75..89fd4c263c 100644 --- a/bots/src/API/Docs/Types.hs +++ b/bots/src/API/Docs/Types.hs @@ -49,6 +49,8 @@ import Simplex.Messaging.Parsers (dropPrefix, fstToLower) import Simplex.Messaging.Protocol (BlockingInfo (..), BlockingReason (..), CommandError (..), ErrorType (..), NameErrorType (..), NetworkError (..), ProxyError (..)) import Simplex.Messaging.Protocol.Types (ClientNotice (..)) import Simplex.Messaging.Transport +import Simplex.Chat.Remote.AppVersion (AppVersion, AppVersionRange) +import Simplex.Chat.Remote.Types (CtrlAppInfo (..)) import Simplex.RemoteControl.Types import System.Console.ANSI.Types (Color (..)) @@ -211,6 +213,7 @@ chatTypesDocsData = (sti @AgentCryptoError, STUnion, "", ["RATCHET_EARLIER", "RATCHET_SKIPPED"], "", ""), -- TODO add fields to types (sti @AgentErrorType, STUnion, "", [], "", ""), (sti @AgentServiceError, STUnion, "ASE", [], "", ""), + (STI "AppVersionRange" [RecordTypeInfo "AppVersionRange" [FieldInfo "minVersion" (TIType (ST TString [])), FieldInfo "maxVersion" (TIType (ST TString []))]], STRecord, "", [], "", "Remote controller app version range (min and max as version strings)."), (sti @AutoAccept, STRecord, "", [], "", ""), (sti @BadgeProof, STRecord, "", [], "", ""), (sti @BlockingInfo, STRecord, "", [], "", ""), @@ -243,6 +246,7 @@ chatTypesDocsData = (sti @CIReactionCount, STRecord, "", [], "", ""), (sti @CITimed, STRecord, "", [], "", ""), (sti @ClientNotice, STRecord, "", [], "", ""), + (sti @CtrlAppInfo, STRecord, "", [], "", "Remote controller application info."), (sti @Color, STEnum, "", [], "", ""), (sti @CommandError, STUnion, "", [], "", ""), (sti @CommandErrorType, STUnion, "", [], "", ""), @@ -354,8 +358,12 @@ chatTypesDocsData = (sti @RcvGroupEvent, STUnion, "RGE", [], "", ""), (sti @RcvMsgError, STUnion, "RME", [], "", ""), (sti @RelayCapabilities, STRecord, "", [], "", ""), + (sti @RelayConnectionResult, STRecord, "", [], "", ""), (sti @RelayProfile, STRecord, "", [], "", ""), (sti @RelayStatus, STEnum, "RS", [], "", ""), + (sti @RemoteCtrlInfo, STRecord, "", [], "", ""), + (sti @RemoteCtrlSessionState, STUnion, "RCS", [], "", ""), + (sti @RemoteCtrlStopReason, STUnion, "RCSR", [], "", ""), (sti @ReportReason, STEnum' (dropPfxSfx "RR" ""), "", ["RRUnknown"], "", ""), (sti @RoleGroupPreference, STRecord, "", [], "", ""), (sti @SecurityCode, STRecord, "", [], "", ""), @@ -471,6 +479,7 @@ deriving instance Generic CIMentionMember deriving instance Generic CIReactionCount deriving instance Generic CITimed deriving instance Generic ClientNotice +deriving instance Generic CtrlAppInfo deriving instance Generic Color deriving instance Generic CommandError deriving instance Generic CommandErrorType @@ -589,8 +598,12 @@ deriving instance Generic RcvFileTransfer deriving instance Generic RcvGroupEvent deriving instance Generic RcvMsgError deriving instance Generic RelayCapabilities +deriving instance Generic RelayConnectionResult deriving instance Generic RelayProfile deriving instance Generic RelayStatus +deriving instance Generic RemoteCtrlInfo +deriving instance Generic RemoteCtrlSessionState +deriving instance Generic RemoteCtrlStopReason deriving instance Generic ReportReason deriving instance Generic SecurityCode deriving instance Generic SimplexDomain diff --git a/bots/src/API/TypeInfo.hs b/bots/src/API/TypeInfo.hs index e225659c4d..59bfe4e52e 100644 --- a/bots/src/API/TypeInfo.hs +++ b/bots/src/API/TypeInfo.hs @@ -200,6 +200,7 @@ toTypeInfo tr = "AgentInvId", "AgentRcvFileId", "AgentSndFileId", + "AppVersion", "BadgeMasterKey", "B64UrlByteString", "BBSProof", @@ -219,6 +220,7 @@ toTypeInfo tr = "ProofPresHeader", "PublicKey", "ProtocolServer", + "RCSignedInvitation", "SbKey", "SharedMsgId", "Signature", diff --git a/packages/simplex-chat-client/types/typescript/src/commands.ts b/packages/simplex-chat-client/types/typescript/src/commands.ts index 0dfd63ba88..dcc9ec767d 100644 --- a/packages/simplex-chat-client/types/typescript/src/commands.ts +++ b/packages/simplex-chat-client/types/typescript/src/commands.ts @@ -59,7 +59,7 @@ export interface APISetProfileAddress { } export namespace APISetProfileAddress { - export type Response = CR.UserProfileUpdated | CR.ChatCmdError + export type Response = CR.UserProfileUpdated | CR.UserProfileNoChange | CR.ChatCmdError export function cmdString(self: APISetProfileAddress): string { return '/_profile_address ' + self.userId + ' ' + (self.enable ? 'on' : 'off') @@ -178,7 +178,7 @@ export namespace APIShareMyAddress { export type Response = CR.ChatMsgContent export function cmdString(self: APIShareMyAddress): string { - return '/_share address' + T.ChatRef.cmdString(self.toSendRef) + return '/_share address ' + T.ChatRef.cmdString(self.toSendRef) } } @@ -582,7 +582,15 @@ export interface Connect { } export namespace Connect { - export type Response = CR.SentConfirmation | CR.ContactAlreadyExists | CR.SentInvitation | CR.ChatCmdError + export type Response = + | CR.SentConfirmation + | CR.ContactAlreadyExists + | CR.SentInvitation + | CR.ConnectionPlan + | CR.SentInvitationToContact + | CR.StartedConnectionToContact + | CR.StartedConnectionToGroup + | CR.ChatCmdError export function cmdString(self: Connect): string { return '/connect' + (self.connTarget_ ? ' ' + self.connTarget_ : '') @@ -897,3 +905,34 @@ export namespace APIStopChat { return '/_stop' } } + +// Remote control commands +// Allows a bot to accept an incoming remote control session from a SimpleX Desktop client, giving the desktop live access to the bot's SimpleX instance. + +// Connect to a remote controller using an OOB invitation link. +// Network usage: interactive. +export interface ConnectRemoteCtrl { + remoteInvitation: string +} + +export namespace ConnectRemoteCtrl { + export type Response = CR.RemoteCtrlConnecting | CR.ChatCmdError + + export function cmdString(self: ConnectRemoteCtrl): string { + return '/crc ' + self.remoteInvitation + } +} + +// Verify the remote controller session code to complete the connection. +// Network usage: no. +export interface VerifyRemoteCtrlSession { + sessionCode: string +} + +export namespace VerifyRemoteCtrlSession { + export type Response = CR.RemoteCtrlConnected | CR.ChatCmdError + + export function cmdString(self: VerifyRemoteCtrlSession): string { + return '/verify remote ctrl ' + self.sessionCode + } +} diff --git a/packages/simplex-chat-client/types/typescript/src/events.ts b/packages/simplex-chat-client/types/typescript/src/events.ts index 59e0c3274d..5b644ee41e 100644 --- a/packages/simplex-chat-client/types/typescript/src/events.ts +++ b/packages/simplex-chat-client/types/typescript/src/events.ts @@ -52,6 +52,8 @@ export type ChatEvent = | CEvt.SubscriptionStatus | CEvt.ServiceRequest | CEvt.ServiceReplySent + | CEvt.RemoteCtrlSessionCode + | CEvt.RemoteCtrlStopped | CEvt.MessageError | CEvt.ChatError | CEvt.ChatErrors @@ -106,6 +108,8 @@ export namespace CEvt { | "subscriptionStatus" | "serviceRequest" | "serviceReplySent" + | "remoteCtrlSessionCode" + | "remoteCtrlStopped" | "messageError" | "chatError" | "chatErrors" @@ -471,6 +475,18 @@ export namespace CEvt { connectionId: string } + export interface RemoteCtrlSessionCode extends Interface { + type: "remoteCtrlSessionCode" + remoteCtrl_?: T.RemoteCtrlInfo + sessionCode: string + } + + export interface RemoteCtrlStopped extends Interface { + type: "remoteCtrlStopped" + rcsState: T.RemoteCtrlSessionState + rcStopReason: T.RemoteCtrlStopReason + } + export interface MessageError extends Interface { type: "messageError" user: T.User diff --git a/packages/simplex-chat-client/types/typescript/src/responses.ts b/packages/simplex-chat-client/types/typescript/src/responses.ts index fd6050ef49..647427f0dc 100644 --- a/packages/simplex-chat-client/types/typescript/src/responses.ts +++ b/packages/simplex-chat-client/types/typescript/src/responses.ts @@ -47,11 +47,16 @@ export type ChatResponse = | CR.RcvFileAccepted | CR.RcvFileAcceptedSndCancelled | CR.RcvFileCancelled + | CR.RemoteCtrlConnected + | CR.RemoteCtrlConnecting | CR.SentConfirmation | CR.SentGroupInvitation | CR.SentInvitation + | CR.SentInvitationToContact | CR.ServiceReplyAccepted | CR.SndFileCancelled + | CR.StartedConnectionToContact + | CR.StartedConnectionToGroup | CR.UserAcceptedGroupSent | CR.UserContactLink | CR.UserContactLinkCreated @@ -108,11 +113,16 @@ export namespace CR { | "rcvFileAccepted" | "rcvFileAcceptedSndCancelled" | "rcvFileCancelled" + | "remoteCtrlConnected" + | "remoteCtrlConnecting" | "sentConfirmation" | "sentGroupInvitation" | "sentInvitation" + | "sentInvitationToContact" | "serviceReplyAccepted" | "sndFileCancelled" + | "startedConnectionToContact" + | "startedConnectionToGroup" | "userAcceptedGroupSent" | "userContactLink" | "userContactLinkCreated" @@ -407,6 +417,19 @@ export namespace CR { rcvFileTransfer: T.RcvFileTransfer } + export interface RemoteCtrlConnected extends Interface { + type: "remoteCtrlConnected" + remoteCtrl: T.RemoteCtrlInfo + compression: boolean + } + + export interface RemoteCtrlConnecting extends Interface { + type: "remoteCtrlConnecting" + remoteCtrl_?: T.RemoteCtrlInfo + ctrlAppInfo: T.CtrlAppInfo + appVersion: string + } + export interface SentConfirmation extends Interface { type: "sentConfirmation" user: T.User @@ -429,6 +452,13 @@ export namespace CR { customUserProfile?: T.Profile } + export interface SentInvitationToContact extends Interface { + type: "sentInvitationToContact" + user: T.User + contact: T.Contact + customUserProfile?: T.Profile + } + export interface ServiceReplyAccepted extends Interface { type: "serviceReplyAccepted" user: T.User @@ -443,6 +473,21 @@ export namespace CR { sndFileTransfers: T.SndFileTransfer[] } + export interface StartedConnectionToContact extends Interface { + type: "startedConnectionToContact" + user: T.User + contact: T.Contact + customUserProfile?: T.Profile + } + + export interface StartedConnectionToGroup extends Interface { + type: "startedConnectionToGroup" + user: T.User + groupInfo: T.GroupInfo + customUserProfile?: T.Profile + relayResults: T.RelayConnectionResult[] + } + export interface UserAcceptedGroupSent extends Interface { type: "userAcceptedGroupSent" user: T.User diff --git a/packages/simplex-chat-client/types/typescript/src/types.ts b/packages/simplex-chat-client/types/typescript/src/types.ts index eadfda7ba9..19f2d5dca3 100644 --- a/packages/simplex-chat-client/types/typescript/src/types.ts +++ b/packages/simplex-chat-client/types/typescript/src/types.ts @@ -223,6 +223,12 @@ export namespace AgentServiceError { type: "badSignature" } } +// Remote controller app version range (min and max as version strings). + +export interface AppVersionRange { + minVersion: string + maxVersion: string +} export interface AutoAccept { acceptIncognito: boolean @@ -2210,6 +2216,13 @@ export interface CryptoFileArgs { fileKey: string fileNonce: string } +// Remote controller application info. + +export interface CtrlAppInfo { + appVersionRange: AppVersionRange + deviceName: string + compression: boolean +} export interface DroppedMsg { brokerTs: string // ISO-8601 timestamp @@ -3950,6 +3963,11 @@ export interface RelayCapabilities { webDomain?: string } +export interface RelayConnectionResult { + relayMember: GroupMember + relayError?: ChatError +} + export interface RelayProfile { displayName: string fullName: string @@ -3967,6 +3985,87 @@ export enum RelayStatus { Rejected = "rejected", } +export interface RemoteCtrlInfo { + remoteCtrlId: number // int64 + ctrlDeviceName: string + sessionState?: RemoteCtrlSessionState +} + +export type RemoteCtrlSessionState = + | RemoteCtrlSessionState.Starting + | RemoteCtrlSessionState.Searching + | RemoteCtrlSessionState.Connecting + | RemoteCtrlSessionState.PendingConfirmation + | RemoteCtrlSessionState.Connected + +export namespace RemoteCtrlSessionState { + export type Tag = + | "starting" + | "searching" + | "connecting" + | "pendingConfirmation" + | "connected" + + interface Interface { + type: Tag + } + + export interface Starting extends Interface { + type: "starting" + } + + export interface Searching extends Interface { + type: "searching" + } + + export interface Connecting extends Interface { + type: "connecting" + } + + export interface PendingConfirmation extends Interface { + type: "pendingConfirmation" + sessionCode: string + } + + export interface Connected extends Interface { + type: "connected" + sessionCode: string + } +} + +export type RemoteCtrlStopReason = + | RemoteCtrlStopReason.DiscoveryFailed + | RemoteCtrlStopReason.ConnectionFailed + | RemoteCtrlStopReason.SetupFailed + | RemoteCtrlStopReason.Disconnected + +export namespace RemoteCtrlStopReason { + export type Tag = "discoveryFailed" | "connectionFailed" | "setupFailed" | "disconnected" + + interface Interface { + type: Tag + } + + export interface DiscoveryFailed extends Interface { + type: "discoveryFailed" + chatError: ChatError + } + + export interface ConnectionFailed extends Interface { + type: "connectionFailed" + chatError: ChatError + } + + export interface SetupFailed extends Interface { + type: "setupFailed" + chatError: ChatError + } + + export interface Disconnected extends Interface { + type: "disconnected" + } +} + export enum ReportReason { Spam = "spam", Content = "content", diff --git a/packages/simplex-chat-python/src/simplex_chat/types/_commands.py b/packages/simplex-chat-python/src/simplex_chat/types/_commands.py index 086b3cfd29..53544324fc 100644 --- a/packages/simplex-chat-python/src/simplex_chat/types/_commands.py +++ b/packages/simplex-chat-python/src/simplex_chat/types/_commands.py @@ -56,7 +56,7 @@ class APISetProfileAddress(TypedDict): def APISetProfileAddress_cmd_string(self: APISetProfileAddress) -> str: return '/_profile_address ' + str(self['userId']) + ' ' + ('on' if self['enable'] else 'off') -APISetProfileAddress_Response = CR.UserProfileUpdated | CR.ChatCmdError +APISetProfileAddress_Response = CR.UserProfileUpdated | CR.UserProfileNoChange | CR.ChatCmdError # Set bot address settings. @@ -156,7 +156,7 @@ class APIShareMyAddress(TypedDict): def APIShareMyAddress_cmd_string(self: APIShareMyAddress) -> str: - return '/_share address' + T.ChatRef_cmd_string(self['toSendRef']) + return '/_share address ' + T.ChatRef_cmd_string(self['toSendRef']) APIShareMyAddress_Response = CR.ChatMsgContent @@ -513,7 +513,16 @@ class Connect(TypedDict): def Connect_cmd_string(self: Connect) -> str: return '/connect' + ((' ' + self.get('connTarget_')) if self.get('connTarget_') is not None else '') -Connect_Response = CR.SentConfirmation | CR.ContactAlreadyExists | CR.SentInvitation | CR.ChatCmdError +Connect_Response = ( + CR.SentConfirmation + | CR.ContactAlreadyExists + | CR.SentInvitation + | CR.ConnectionPlan + | CR.SentInvitationToContact + | CR.StartedConnectionToContact + | CR.StartedConnectionToGroup + | CR.ChatCmdError +) # Accept contact request. @@ -787,3 +796,30 @@ def APIStopChat_cmd_string(self: APIStopChat) -> str: APIStopChat_Response = CR.ChatStopped + +# Remote control commands +# Allows a bot to accept an incoming remote control session from a SimpleX Desktop client, giving the desktop live access to the bot's SimpleX instance. + +# Connect to a remote controller using an OOB invitation link. +# Network usage: interactive. +class ConnectRemoteCtrl(TypedDict): + remoteInvitation: str + + +def ConnectRemoteCtrl_cmd_string(self: ConnectRemoteCtrl) -> str: + return '/crc ' + self['remoteInvitation'] + +ConnectRemoteCtrl_Response = CR.RemoteCtrlConnecting | CR.ChatCmdError + + +# Verify the remote controller session code to complete the connection. +# Network usage: no. +class VerifyRemoteCtrlSession(TypedDict): + sessionCode: str + + +def VerifyRemoteCtrlSession_cmd_string(self: VerifyRemoteCtrlSession) -> str: + return '/verify remote ctrl ' + self['sessionCode'] + +VerifyRemoteCtrlSession_Response = CR.RemoteCtrlConnected | CR.ChatCmdError + diff --git a/packages/simplex-chat-python/src/simplex_chat/types/_events.py b/packages/simplex-chat-python/src/simplex_chat/types/_events.py index 58ead11837..bd73637ef8 100644 --- a/packages/simplex-chat-python/src/simplex_chat/types/_events.py +++ b/packages/simplex-chat-python/src/simplex_chat/types/_events.py @@ -314,6 +314,16 @@ class ServiceReplySent(TypedDict): type: Literal["serviceReplySent"] connectionId: str +class RemoteCtrlSessionCode(TypedDict): + type: Literal["remoteCtrlSessionCode"] + remoteCtrl_: NotRequired["T.RemoteCtrlInfo"] + sessionCode: str + +class RemoteCtrlStopped(TypedDict): + type: Literal["remoteCtrlStopped"] + rcsState: "T.RemoteCtrlSessionState" + rcStopReason: "T.RemoteCtrlStopReason" + class MessageError(TypedDict): type: Literal["messageError"] user: "T.User" @@ -377,12 +387,14 @@ ChatEvent = ( | SubscriptionStatus | ServiceRequest | ServiceReplySent + | RemoteCtrlSessionCode + | RemoteCtrlStopped | MessageError | ChatError | ChatErrors ) -ChatEvent_Tag = Literal["contactConnected", "contactUpdated", "contactDeletedByContact", "receivedContactRequest", "newMemberContactReceivedInv", "contactSndReady", "newChatItems", "chatItemReaction", "chatItemsDeleted", "chatItemUpdated", "groupChatItemsDeleted", "chatItemsStatusesUpdated", "receivedGroupInvitation", "userJoinedGroup", "groupUpdated", "joinedGroupMember", "memberRole", "deletedMember", "leftMember", "deletedMemberUser", "groupDeleted", "connectedToGroupMember", "memberAcceptedByOther", "memberBlockedForAll", "groupMemberUpdated", "groupLinkDataUpdated", "groupRelayUpdated", "rcvFileDescrReady", "rcvFileComplete", "sndFileCompleteXFTP", "rcvFileStart", "rcvFileSndCancelled", "rcvFileAccepted", "rcvFileError", "rcvFileWarning", "sndFileError", "sndFileWarning", "acceptingContactRequest", "acceptingBusinessRequest", "contactConnecting", "businessLinkConnecting", "joinedGroupMemberConnecting", "groupLinkConnecting", "hostConnected", "hostDisconnected", "subscriptionStatus", "serviceRequest", "serviceReplySent", "messageError", "chatError", "chatErrors"] +ChatEvent_Tag = Literal["contactConnected", "contactUpdated", "contactDeletedByContact", "receivedContactRequest", "newMemberContactReceivedInv", "contactSndReady", "newChatItems", "chatItemReaction", "chatItemsDeleted", "chatItemUpdated", "groupChatItemsDeleted", "chatItemsStatusesUpdated", "receivedGroupInvitation", "userJoinedGroup", "groupUpdated", "joinedGroupMember", "memberRole", "deletedMember", "leftMember", "deletedMemberUser", "groupDeleted", "connectedToGroupMember", "memberAcceptedByOther", "memberBlockedForAll", "groupMemberUpdated", "groupLinkDataUpdated", "groupRelayUpdated", "rcvFileDescrReady", "rcvFileComplete", "sndFileCompleteXFTP", "rcvFileStart", "rcvFileSndCancelled", "rcvFileAccepted", "rcvFileError", "rcvFileWarning", "sndFileError", "sndFileWarning", "acceptingContactRequest", "acceptingBusinessRequest", "contactConnecting", "businessLinkConnecting", "joinedGroupMemberConnecting", "groupLinkConnecting", "hostConnected", "hostDisconnected", "subscriptionStatus", "serviceRequest", "serviceReplySent", "remoteCtrlSessionCode", "remoteCtrlStopped", "messageError", "chatError", "chatErrors"] class OnEventDecorator(Protocol): @@ -681,6 +693,18 @@ class OnEventDecorator(Protocol): Callable[["ServiceReplySent"], Awaitable[None]], ]: ... + @overload + def __call__(self, event: Literal["remoteCtrlSessionCode"], /) -> Callable[ + [Callable[["RemoteCtrlSessionCode"], Awaitable[None]]], + Callable[["RemoteCtrlSessionCode"], Awaitable[None]], + ]: ... + + @overload + def __call__(self, event: Literal["remoteCtrlStopped"], /) -> Callable[ + [Callable[["RemoteCtrlStopped"], Awaitable[None]]], + Callable[["RemoteCtrlStopped"], Awaitable[None]], + ]: ... + @overload def __call__(self, event: Literal["messageError"], /) -> Callable[ [Callable[["MessageError"], Awaitable[None]]], diff --git a/packages/simplex-chat-python/src/simplex_chat/types/_responses.py b/packages/simplex-chat-python/src/simplex_chat/types/_responses.py index 217fb3019d..e3885fc16a 100644 --- a/packages/simplex-chat-python/src/simplex_chat/types/_responses.py +++ b/packages/simplex-chat-python/src/simplex_chat/types/_responses.py @@ -240,6 +240,17 @@ class RcvFileCancelled(TypedDict): chatItem_: NotRequired["T.AChatItem"] rcvFileTransfer: "T.RcvFileTransfer" +class RemoteCtrlConnected(TypedDict): + type: Literal["remoteCtrlConnected"] + remoteCtrl: "T.RemoteCtrlInfo" + compression: bool + +class RemoteCtrlConnecting(TypedDict): + type: Literal["remoteCtrlConnecting"] + remoteCtrl_: NotRequired["T.RemoteCtrlInfo"] + ctrlAppInfo: "T.CtrlAppInfo" + appVersion: str + class SentConfirmation(TypedDict): type: Literal["sentConfirmation"] user: "T.User" @@ -259,6 +270,12 @@ class SentInvitation(TypedDict): connection: "T.PendingContactConnection" customUserProfile: NotRequired["T.Profile"] +class SentInvitationToContact(TypedDict): + type: Literal["sentInvitationToContact"] + user: "T.User" + contact: "T.Contact" + customUserProfile: NotRequired["T.Profile"] + class ServiceReplyAccepted(TypedDict): type: Literal["serviceReplyAccepted"] user: "T.User" @@ -271,6 +288,19 @@ class SndFileCancelled(TypedDict): fileTransferMeta: "T.FileTransferMeta" sndFileTransfers: list["T.SndFileTransfer"] +class StartedConnectionToContact(TypedDict): + type: Literal["startedConnectionToContact"] + user: "T.User" + contact: "T.Contact" + customUserProfile: NotRequired["T.Profile"] + +class StartedConnectionToGroup(TypedDict): + type: Literal["startedConnectionToGroup"] + user: "T.User" + groupInfo: "T.GroupInfo" + customUserProfile: NotRequired["T.Profile"] + relayResults: list["T.RelayConnectionResult"] + class UserAcceptedGroupSent(TypedDict): type: Literal["userAcceptedGroupSent"] user: "T.User" @@ -368,11 +398,16 @@ ChatResponse = ( | RcvFileAccepted | RcvFileAcceptedSndCancelled | RcvFileCancelled + | RemoteCtrlConnected + | RemoteCtrlConnecting | SentConfirmation | SentGroupInvitation | SentInvitation + | SentInvitationToContact | ServiceReplyAccepted | SndFileCancelled + | StartedConnectionToContact + | StartedConnectionToGroup | UserAcceptedGroupSent | UserContactLink | UserContactLinkCreated @@ -385,4 +420,4 @@ ChatResponse = ( | ApiChats ) -ChatResponse_Tag = Literal["acceptingContactRequest", "activeUser", "chatItemNotChanged", "chatItemReaction", "chatItemUpdated", "chatItemsDeleted", "chatMsgContent", "chatRunning", "chatStarted", "chatStopped", "cmdOk", "chatCmdError", "connectionPlan", "contactAlreadyExists", "contactConnectionDeleted", "contactDeleted", "contactPrefsUpdated", "contactRequestRejected", "contactsList", "groupDeletedUser", "groupLink", "groupLinkCreated", "groupLinkDeleted", "groupCreated", "publicGroupCreated", "publicGroupCreationFailed", "groupRelays", "groupRelaysAdded", "groupRelaysAddFailed", "relayGroupAllowed", "groupMembers", "groupUpdated", "groupsList", "groupDomainVerified", "invitation", "leftMemberUser", "memberAccepted", "membersBlockedForAllUser", "membersRoleUser", "newChatItems", "rcvFileAccepted", "rcvFileAcceptedSndCancelled", "rcvFileCancelled", "sentConfirmation", "sentGroupInvitation", "sentInvitation", "serviceReplyAccepted", "sndFileCancelled", "userAcceptedGroupSent", "userContactLink", "userContactLinkCreated", "userContactLinkDeleted", "userContactLinkUpdated", "userDeletedMembers", "userProfileUpdated", "userProfileNoChange", "usersList", "apiChats"] +ChatResponse_Tag = Literal["acceptingContactRequest", "activeUser", "chatItemNotChanged", "chatItemReaction", "chatItemUpdated", "chatItemsDeleted", "chatMsgContent", "chatRunning", "chatStarted", "chatStopped", "cmdOk", "chatCmdError", "connectionPlan", "contactAlreadyExists", "contactConnectionDeleted", "contactDeleted", "contactPrefsUpdated", "contactRequestRejected", "contactsList", "groupDeletedUser", "groupLink", "groupLinkCreated", "groupLinkDeleted", "groupCreated", "publicGroupCreated", "publicGroupCreationFailed", "groupRelays", "groupRelaysAdded", "groupRelaysAddFailed", "relayGroupAllowed", "groupMembers", "groupUpdated", "groupsList", "groupDomainVerified", "invitation", "leftMemberUser", "memberAccepted", "membersBlockedForAllUser", "membersRoleUser", "newChatItems", "rcvFileAccepted", "rcvFileAcceptedSndCancelled", "rcvFileCancelled", "remoteCtrlConnected", "remoteCtrlConnecting", "sentConfirmation", "sentGroupInvitation", "sentInvitation", "sentInvitationToContact", "serviceReplyAccepted", "sndFileCancelled", "startedConnectionToContact", "startedConnectionToGroup", "userAcceptedGroupSent", "userContactLink", "userContactLinkCreated", "userContactLinkDeleted", "userContactLinkUpdated", "userDeletedMembers", "userProfileUpdated", "userProfileNoChange", "usersList", "apiChats"] diff --git a/packages/simplex-chat-python/src/simplex_chat/types/_types.py b/packages/simplex-chat-python/src/simplex_chat/types/_types.py index 74ffd4536b..c6870abb37 100644 --- a/packages/simplex-chat-python/src/simplex_chat/types/_types.py +++ b/packages/simplex-chat-python/src/simplex_chat/types/_types.py @@ -165,6 +165,12 @@ AgentServiceError = ( AgentServiceError_Tag = Literal["rejected", "timeout", "noPendingRequest", "notDRAddress", "badSignature"] +# Remote controller app version range (min and max as version strings). + +class AppVersionRange(TypedDict): + minVersion: str + maxVersion: str + class AutoAccept(TypedDict): acceptIncognito: bool @@ -1560,6 +1566,13 @@ class CryptoFileArgs(TypedDict): fileKey: str fileNonce: str +# Remote controller application info. + +class CtrlAppInfo(TypedDict): + appVersionRange: "AppVersionRange" + deviceName: str + compression: bool + class DroppedMsg(TypedDict): brokerTs: str # ISO-8601 timestamp attempts: int # int @@ -2770,6 +2783,10 @@ RcvMsgError_Tag = Literal["dropped", "parseError"] class RelayCapabilities(TypedDict): webDomain: NotRequired[str] +class RelayConnectionResult(TypedDict): + relayMember: "GroupMember" + relayError: NotRequired["ChatError"] + class RelayProfile(TypedDict): displayName: str fullName: str @@ -2778,6 +2795,62 @@ class RelayProfile(TypedDict): RelayStatus = Literal["new", "invited", "accepted", "acknowledgedRoster", "active", "inactive", "rejected"] +class RemoteCtrlInfo(TypedDict): + remoteCtrlId: int # int64 + ctrlDeviceName: str + sessionState: NotRequired["RemoteCtrlSessionState"] + +class RemoteCtrlSessionState_starting(TypedDict): + type: Literal["starting"] + +class RemoteCtrlSessionState_searching(TypedDict): + type: Literal["searching"] + +class RemoteCtrlSessionState_connecting(TypedDict): + type: Literal["connecting"] + +class RemoteCtrlSessionState_pendingConfirmation(TypedDict): + type: Literal["pendingConfirmation"] + sessionCode: str + +class RemoteCtrlSessionState_connected(TypedDict): + type: Literal["connected"] + sessionCode: str + +RemoteCtrlSessionState = ( + RemoteCtrlSessionState_starting + | RemoteCtrlSessionState_searching + | RemoteCtrlSessionState_connecting + | RemoteCtrlSessionState_pendingConfirmation + | RemoteCtrlSessionState_connected +) + +RemoteCtrlSessionState_Tag = Literal["starting", "searching", "connecting", "pendingConfirmation", "connected"] + +class RemoteCtrlStopReason_discoveryFailed(TypedDict): + type: Literal["discoveryFailed"] + chatError: "ChatError" + +class RemoteCtrlStopReason_connectionFailed(TypedDict): + type: Literal["connectionFailed"] + chatError: "ChatError" + +class RemoteCtrlStopReason_setupFailed(TypedDict): + type: Literal["setupFailed"] + chatError: "ChatError" + +class RemoteCtrlStopReason_disconnected(TypedDict): + type: Literal["disconnected"] + +RemoteCtrlStopReason = ( + RemoteCtrlStopReason_discoveryFailed + | RemoteCtrlStopReason_connectionFailed + | RemoteCtrlStopReason_setupFailed + | RemoteCtrlStopReason_disconnected +) + +RemoteCtrlStopReason_Tag = Literal["discoveryFailed", "connectionFailed", "setupFailed", "disconnected"] + ReportReason = Literal["spam", "content", "community", "profile", "other"] class RoleGroupPreference(TypedDict): diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index f670af261c..fc3f33ad54 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -655,10 +655,10 @@ data ChatCommand | DeleteRemoteHost RemoteHostId -- Unregister remote host and remove its data | StoreRemoteFile {remoteHostId :: RemoteHostId, storeEncrypted :: Maybe Bool, localPath :: FilePath} | GetRemoteFile {remoteHostId :: RemoteHostId, file :: RemoteFile} - | ConnectRemoteCtrl RCSignedInvitation -- Connect new or existing controller via OOB data + | ConnectRemoteCtrl {remoteInvitation :: RCSignedInvitation} -- Connect new or existing controller via OOB data | FindKnownRemoteCtrl -- Start listening for announcements from all existing controllers | ConfirmRemoteCtrl RemoteCtrlId -- Confirm the connection with found controller - | VerifyRemoteCtrlSession Text -- Verify remote controller session + | VerifyRemoteCtrlSession {sessionCode :: Text} -- Verify remote controller session | ListRemoteCtrls | StopRemoteCtrl -- Stop listening for announcements or terminate an active session | DeleteRemoteCtrl RemoteCtrlId -- Remove all local data associated with a remote controller session