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 <alkrivitskiy@pm.me>
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
This commit is contained in:
Evgeny
2026-09-12 14:13:11 +01:00
committed by GitHub
co-authored by Evgeny @ SimpleX Chat a1akris
parent 9631e0b1b9
commit 47d32b674a
17 changed files with 661 additions and 26 deletions
@@ -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
@@ -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]]],
@@ -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"]
@@ -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):