This commit is contained in:
Evgeny Poberezkin committed 2026-06-27 23:29:53 +01:00
1 parent 8bcabcb912
commit 09b4c7b151
14 files changed
+82 -118

No files matched your search

-40
View File
@@ -58,7 +58,6 @@ This file is generated automatically.
- [APISetGroupCustomData](#apisetgroupcustomdata)
- [APISetContactCustomData](#apisetcontactcustomdata)
- [APISetUserAutoAcceptMemberContacts](#apisetuserautoacceptmembercontacts)
- [APIVerifySimplexName](#apiverifysimplexname)
[User profile commands](#user-profile-commands)
- [ShowActiveUser](#showactiveuser)
@@ -1860,45 +1859,6 @@ ChatCmdError: Command error (only used in WebSockets API).
---
### APIVerifySimplexName
Verify a contact's or group's claimed SimpleX name by RSLV-resolving the claim and comparing the resolved link to the peer's stored connection link. Returns `CRSimplexNameVerified` with a boolean `verified` (a match also writes the verification timestamp); resolver / agent failures are reported as `CRChatCmdError`.
*Network usage*: interactive.
**Parameters**:
- chatRef: [ChatRef](./TYPES.md#chatref)
**Syntax**:
```
/_verify simplex name <str(chatRef)>
```
```javascript
'/_verify simplex name ' + ChatRef.cmdString(chatRef) // JavaScript
```
```python
'/_verify simplex name ' + ChatRef_cmd_string(chatRef) # Python
```
**Responses**:
SimplexNameVerified: Result of SimpleX name verification (`verified`: whether the RSLV-resolved link matches the peer's stored link).
- type: "simplexNameVerified"
- user: [User](./TYPES.md#user)
- chatRef: [ChatRef](./TYPES.md#chatref)
- simplexName: [SimplexNameInfo](./TYPES.md#simplexnameinfo)
- verified: bool
ChatCmdError: Command error (only used in WebSockets API).
- type: "chatCmdError"
- chatError: [ChatError](./TYPES.md#chaterror)
---
## User profile commands
Most bots don't need to use these commands, as bot profile can be configured manually via CLI or desktop client. These commands can be used by bots that need to manage multiple user profiles (e.g., the profiles of support agents).
+17 -6
View File
@@ -177,6 +177,7 @@ This file is generated automatically.
- [SimplePreference](#simplepreference)
- [SimplexLinkType](#simplexlinktype)
- [SimplexNameDomain](#simplexnamedomain)
- [SimplexNameError](#simplexnameerror)
- [SimplexNameInfo](#simplexnameinfo)
- [SimplexNameType](#simplexnametype)
- [SimplexTLD](#simplextld)
@@ -1110,13 +1111,10 @@ ChatStoreChanged:
InvalidConnReq:
- type: "invalidConnReq"
SimplexNameNotFound:
- type: "simplexNameNotFound"
- simplexName: [SimplexNameInfo](#simplexnameinfo)
SimplexNameUnprepared:
- type: "simplexNameUnprepared"
SimplexName:
- type: "simplexName"
- simplexName: [SimplexNameInfo](#simplexnameinfo)
- simplexNameError: [SimplexNameError](#simplexnameerror)
UnsupportedConnReq:
- type: "unsupportedConnReq"
@@ -3621,6 +3619,19 @@ A_QUEUE:
- subDomain: [string]
---
## SimplexNameError
**Discriminated union type**:
NoValidLink:
- type: "noValidLink"
UnknownName:
- type: "unknownName"
---
## SimplexNameInfo
+3
View File
@@ -35,6 +35,7 @@ import Simplex.Chat.Store.Shared
import Simplex.Chat.Operators
import Simplex.Messaging.Agent.Store.Entity (DBStored (..))
import Simplex.Chat.Badges
import Simplex.Chat.Names
import Simplex.Chat.Types
import Simplex.Chat.Types.Preferences
import Simplex.Chat.Types.Shared
@@ -358,6 +359,7 @@ chatTypesDocsData =
(sti @SimplePreference, STRecord, "", [], "", ""),
(sti @SimplexLinkType, STEnum, "XL", [], "", ""),
(sti @SimplexNameDomain, STRecord, "", [], "", ""),
(sti @SimplexNameError, STUnion, "SNE", [], "", ""),
(sti @SimplexNameInfo, STRecord, "", [], "", ""),
(sti @SimplexNameType, STEnum, "NT", [], "", ""),
(sti @SimplexTLD, STEnum, "TLD", [], "", ""),
@@ -586,6 +588,7 @@ deriving instance Generic ReportReason
deriving instance Generic SecurityCode
deriving instance Generic SimplexLinkType
deriving instance Generic SimplexNameDomain
deriving instance Generic SimplexNameError
deriving instance Generic SimplexNameInfo
deriving instance Generic SimplexNameType
deriving instance Generic SimplexTLD
@@ -681,20 +681,6 @@ export namespace APISetUserAutoAcceptMemberContacts {
}
}
// Verify a contact's or group's claimed SimpleX name by RSLV-resolving the claim and comparing the resolved link to the peer's stored connection link. Returns `CRSimplexNameVerified` with a boolean `verified` (a match also writes the verification timestamp); resolver / agent failures are reported as `CRChatCmdError`.
// Network usage: interactive.
export interface APIVerifySimplexName {
chatRef: T.ChatRef
}
export namespace APIVerifySimplexName {
export type Response = CR.SimplexNameVerified | CR.ChatCmdError
export function cmdString(self: APIVerifySimplexName): string {
return '/_verify simplex name ' + T.ChatRef.cmdString(self.chatRef)
}
}
// User profile commands
// Most bots don't need to use these commands, as bot profile can be configured manually via CLI or desktop client. These commands can be used by bots that need to manage multiple user profiles (e.g., the profiles of support agents).
@@ -48,7 +48,6 @@ export type ChatResponse =
| CR.SentConfirmation
| CR.SentGroupInvitation
| CR.SentInvitation
| CR.SimplexNameVerified
| CR.SndFileCancelled
| CR.UserAcceptedGroupSent
| CR.UserContactLink
@@ -107,7 +106,6 @@ export namespace CR {
| "sentConfirmation"
| "sentGroupInvitation"
| "sentInvitation"
| "simplexNameVerified"
| "sndFileCancelled"
| "userAcceptedGroupSent"
| "userContactLink"
@@ -409,14 +407,6 @@ export namespace CR {
customUserProfile?: T.Profile
}
export interface SimplexNameVerified extends Interface {
type: "simplexNameVerified"
user: T.User
chatRef: T.ChatRef
simplexName: T.SimplexNameInfo
verified: boolean
}
export interface SndFileCancelled extends Interface {
type: "sndFileCancelled"
user: T.User
@@ -1042,8 +1042,7 @@ export type ChatErrorType =
| ChatErrorType.ChatNotStopped
| ChatErrorType.ChatStoreChanged
| ChatErrorType.InvalidConnReq
| ChatErrorType.SimplexNameNotFound
| ChatErrorType.SimplexNameUnprepared
| ChatErrorType.SimplexName
| ChatErrorType.UnsupportedConnReq
| ChatErrorType.ConnReqMessageProhibited
| ChatErrorType.ContactNotReady
@@ -1121,8 +1120,7 @@ export namespace ChatErrorType {
| "chatNotStopped"
| "chatStoreChanged"
| "invalidConnReq"
| "simplexNameNotFound"
| "simplexNameUnprepared"
| "simplexName"
| "unsupportedConnReq"
| "connReqMessageProhibited"
| "contactNotReady"
@@ -1277,14 +1275,10 @@ export namespace ChatErrorType {
type: "invalidConnReq"
}
export interface SimplexNameNotFound extends Interface {
type: "simplexNameNotFound"
simplexName: SimplexNameInfo
}
export interface SimplexNameUnprepared extends Interface {
type: "simplexNameUnprepared"
export interface SimplexName extends Interface {
type: "simplexName"
simplexName: SimplexNameInfo
simplexNameError: SimplexNameError
}
export interface UnsupportedConnReq extends Interface {
@@ -3996,6 +3990,24 @@ export interface SimplexNameDomain {
subDomain: string[]
}
export type SimplexNameError = SimplexNameError.NoValidLink | SimplexNameError.UnknownName
export namespace SimplexNameError {
export type Tag = "noValidLink" | "unknownName"
interface Interface {
type: Tag
}
export interface NoValidLink extends Interface {
type: "noValidLink"
}
export interface UnknownName extends Interface {
type: "unknownName"
}
}
export interface SimplexNameInfo {
nameType: SimplexNameType
nameDomain: SimplexNameDomain
@@ -596,18 +596,6 @@ def APISetUserAutoAcceptMemberContacts_cmd_string(self: APISetUserAutoAcceptMemb
APISetUserAutoAcceptMemberContacts_Response = CR.CmdOk | CR.ChatCmdError
# Verify a contact's or group's claimed SimpleX name by RSLV-resolving the claim and comparing the resolved link to the peer's stored connection link. Returns `CRSimplexNameVerified` with a boolean `verified` (a match also writes the verification timestamp); resolver / agent failures are reported as `CRChatCmdError`.
# Network usage: interactive.
class APIVerifySimplexName(TypedDict):
chatRef: "T.ChatRef"
def APIVerifySimplexName_cmd_string(self: APIVerifySimplexName) -> str:
return '/_verify simplex name ' + T.ChatRef_cmd_string(self['chatRef'])
APIVerifySimplexName_Response = CR.SimplexNameVerified | CR.ChatCmdError
# User profile commands
# Most bots don't need to use these commands, as bot profile can be configured manually via CLI or desktop client. These commands can be used by bots that need to manage multiple user profiles (e.g., the profiles of support agents).
@@ -245,13 +245,6 @@ class SentInvitation(TypedDict):
connection: "T.PendingContactConnection"
customUserProfile: NotRequired["T.Profile"]
class SimplexNameVerified(TypedDict):
type: Literal["simplexNameVerified"]
user: "T.User"
chatRef: "T.ChatRef"
simplexName: "T.SimplexNameInfo"
verified: bool
class SndFileCancelled(TypedDict):
type: Literal["sndFileCancelled"]
user: "T.User"
@@ -357,7 +350,6 @@ ChatResponse = (
| SentConfirmation
| SentGroupInvitation
| SentInvitation
| SimplexNameVerified
| SndFileCancelled
| UserAcceptedGroupSent
| UserContactLink
@@ -371,4 +363,4 @@ ChatResponse = (
| ApiChats
)
ChatResponse_Tag = Literal["acceptingContactRequest", "activeUser", "chatItemNotChanged", "chatItemReaction", "chatItemUpdated", "chatItemsDeleted", "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", "invitation", "leftMemberUser", "memberAccepted", "membersBlockedForAllUser", "membersRoleUser", "newChatItems", "rcvFileAccepted", "rcvFileAcceptedSndCancelled", "rcvFileCancelled", "sentConfirmation", "sentGroupInvitation", "sentInvitation", "simplexNameVerified", "sndFileCancelled", "userAcceptedGroupSent", "userContactLink", "userContactLinkCreated", "userContactLinkDeleted", "userContactLinkUpdated", "userDeletedMembers", "userProfileUpdated", "userProfileNoChange", "usersList", "apiChats"]
ChatResponse_Tag = Literal["acceptingContactRequest", "activeUser", "chatItemNotChanged", "chatItemReaction", "chatItemUpdated", "chatItemsDeleted", "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", "invitation", "leftMemberUser", "memberAccepted", "membersBlockedForAllUser", "membersRoleUser", "newChatItems", "rcvFileAccepted", "rcvFileAcceptedSndCancelled", "rcvFileCancelled", "sentConfirmation", "sentGroupInvitation", "sentInvitation", "sndFileCancelled", "userAcceptedGroupSent", "userContactLink", "userContactLinkCreated", "userContactLinkDeleted", "userContactLinkUpdated", "userDeletedMembers", "userProfileUpdated", "userProfileNoChange", "usersList", "apiChats"]
@@ -788,13 +788,10 @@ class ChatErrorType_chatStoreChanged(TypedDict):
class ChatErrorType_invalidConnReq(TypedDict):
type: Literal["invalidConnReq"]
class ChatErrorType_simplexNameNotFound(TypedDict):
type: Literal["simplexNameNotFound"]
simplexName: "SimplexNameInfo"
class ChatErrorType_simplexNameUnprepared(TypedDict):
type: Literal["simplexNameUnprepared"]
class ChatErrorType_simplexName(TypedDict):
type: Literal["simplexName"]
simplexName: "SimplexNameInfo"
simplexNameError: "SimplexNameError"
class ChatErrorType_unsupportedConnReq(TypedDict):
type: Literal["unsupportedConnReq"]
@@ -1026,8 +1023,7 @@ ChatErrorType = (
| ChatErrorType_chatNotStopped
| ChatErrorType_chatStoreChanged
| ChatErrorType_invalidConnReq
| ChatErrorType_simplexNameNotFound
| ChatErrorType_simplexNameUnprepared
| ChatErrorType_simplexName
| ChatErrorType_unsupportedConnReq
| ChatErrorType_connReqMessageProhibited
| ChatErrorType_contactNotReady
@@ -1084,7 +1080,7 @@ ChatErrorType = (
| ChatErrorType_exception
)
ChatErrorType_Tag = Literal["noActiveUser", "noConnectionUser", "noSndFileUser", "noRcvFileUser", "userUnknown", "userExists", "chatRelayExists", "differentActiveUser", "cantDeleteActiveUser", "cantDeleteLastUser", "cantHideLastUser", "hiddenUserAlwaysMuted", "emptyUserPassword", "userAlreadyHidden", "userNotHidden", "invalidDisplayName", "chatNotStarted", "chatNotStopped", "chatStoreChanged", "invalidConnReq", "simplexNameNotFound", "simplexNameUnprepared", "unsupportedConnReq", "connReqMessageProhibited", "contactNotReady", "contactNotActive", "contactDisabled", "connectionDisabled", "groupUserRole", "groupMemberInitialRole", "contactIncognitoCantInvite", "groupIncognitoCantInvite", "groupContactRole", "groupDuplicateMember", "groupDuplicateMemberId", "groupNotJoined", "groupMemberNotActive", "cantBlockMemberForSelf", "groupMemberUserRemoved", "groupMemberNotFound", "groupCantResendInvitation", "groupInternal", "fileNotFound", "fileSize", "fileAlreadyReceiving", "fileCancelled", "fileCancel", "fileAlreadyExists", "fileWrite", "fileSend", "fileRcvChunk", "fileInternal", "fileImageType", "fileImageSize", "fileNotReceived", "fileNotApproved", "fallbackToSMPProhibited", "inlineFileProhibited", "invalidForward", "invalidChatItemUpdate", "invalidChatItemDelete", "hasCurrentCall", "noCurrentCall", "callContact", "directMessagesProhibited", "agentVersion", "agentNoSubResult", "commandError", "agentCommandError", "invalidFileDescription", "connectionIncognitoChangeProhibited", "connectionUserChangeProhibited", "peerChatVRangeIncompatible", "relayTestError", "internalError", "exception"]
ChatErrorType_Tag = Literal["noActiveUser", "noConnectionUser", "noSndFileUser", "noRcvFileUser", "userUnknown", "userExists", "chatRelayExists", "differentActiveUser", "cantDeleteActiveUser", "cantDeleteLastUser", "cantHideLastUser", "hiddenUserAlwaysMuted", "emptyUserPassword", "userAlreadyHidden", "userNotHidden", "invalidDisplayName", "chatNotStarted", "chatNotStopped", "chatStoreChanged", "invalidConnReq", "simplexName", "unsupportedConnReq", "connReqMessageProhibited", "contactNotReady", "contactNotActive", "contactDisabled", "connectionDisabled", "groupUserRole", "groupMemberInitialRole", "contactIncognitoCantInvite", "groupIncognitoCantInvite", "groupContactRole", "groupDuplicateMember", "groupDuplicateMemberId", "groupNotJoined", "groupMemberNotActive", "cantBlockMemberForSelf", "groupMemberUserRemoved", "groupMemberNotFound", "groupCantResendInvitation", "groupInternal", "fileNotFound", "fileSize", "fileAlreadyReceiving", "fileCancelled", "fileCancel", "fileAlreadyExists", "fileWrite", "fileSend", "fileRcvChunk", "fileInternal", "fileImageType", "fileImageSize", "fileNotReceived", "fileNotApproved", "fallbackToSMPProhibited", "inlineFileProhibited", "invalidForward", "invalidChatItemUpdate", "invalidChatItemDelete", "hasCurrentCall", "noCurrentCall", "callContact", "directMessagesProhibited", "agentVersion", "agentNoSubResult", "commandError", "agentCommandError", "invalidFileDescription", "connectionIncognitoChangeProhibited", "connectionUserChangeProhibited", "peerChatVRangeIncompatible", "relayTestError", "internalError", "exception"]
ChatFeature = Literal["timedMessages", "fullDelete", "reactions", "voice", "files", "calls", "sessions"]
@@ -2790,6 +2786,16 @@ class SimplexNameDomain(TypedDict):
domain: str
subDomain: list[str]
class SimplexNameError_noValidLink(TypedDict):
type: Literal["noValidLink"]
class SimplexNameError_unknownName(TypedDict):
type: Literal["unknownName"]
SimplexNameError = SimplexNameError_noValidLink | SimplexNameError_unknownName
SimplexNameError_Tag = Literal["noValidLink", "unknownName"]
class SimplexNameInfo(TypedDict):
nameType: "SimplexNameType"
nameDomain: "SimplexNameDomain"
+1 -1
View File
@@ -1431,7 +1431,7 @@ data ChatErrorType
| CEChatNotStopped
| CEChatStoreChanged
| CEInvalidConnReq
| CESimplexNameError {simplexName :: SimplexNameInfo, simplexNameError :: SimplexNameError}
| CESimplexName {simplexName :: SimplexNameInfo, simplexNameError :: SimplexNameError}
| CEUnsupportedConnReq
| CEInvalidChatMessage {connection :: Connection, msgMeta :: Maybe MsgMetaJSON, messageData :: Text, message :: String}
| CEConnReqMessageProhibited
+3 -3
View File
@@ -4305,7 +4305,7 @@ processChatCommand cxt nm = \case
let (candidates, ctType) = case nameType of
NTContact -> (nrSimplexContact, CCTContact)
NTPublicGroup -> (nrSimplexChannel, CCTChannel)
sLnk <- maybe (throwChatError $ CESimplexNameError ni SNENoValidLink) pure $ firstNameLink ctType candidates
sLnk <- maybe (throwChatError $ CESimplexName ni SNENoValidLink) pure $ firstNameLink ctType candidates
(ccLink, plan) <- connectPlan user (CTLink (ACL SCMContact (CLShort sLnk))) False Nothing
case plan of
CPContactAddress (CAPOk (Just ContactShortLinkData {profile}) _) -> do
@@ -4320,13 +4320,13 @@ processChatCommand cxt nm = \case
_ -> pure (ccLink, plan)
verifiedContactPlan :: ACreatedConnLink -> Contact -> CM (ACreatedConnLink, ConnectionPlan)
verifiedContactPlan ccLink Contact {contactId, profile = LocalProfile {contactDomain}} = do
unless (contactDomain == Just ni) $ throwChatError $ CESimplexNameError ni SNEUnknownName
unless (contactDomain == Just ni) $ throwChatError $ CESimplexName ni SNEUnknownName
withStore' $ \db -> setContactDomainVerified db user contactId True
ct' <- withFastStore $ \db -> getContact db cxt user contactId
pure (ccLink, CPContactAddress (CAPKnown ct'))
verifiedGroupPlan :: ACreatedConnLink -> GroupInfo -> CM (ACreatedConnLink, ConnectionPlan)
verifiedGroupPlan ccLink GroupInfo {groupId, groupProfile = GroupProfile {publicGroup}} = do
unless ((publicGroup >>= publicGroupAccess >>= groupDomain) == Just (StrJSON ni)) $ throwChatError $ CESimplexNameError ni SNEUnknownName
unless ((publicGroup >>= publicGroupAccess >>= groupDomain) == Just (StrJSON ni)) $ throwChatError $ CESimplexName ni SNEUnknownName
withStore' $ \db -> setGroupDomainVerified db user groupId True
g' <- withFastStore $ \db -> getGroupInfo db cxt user groupId
pure (ccLink, CPGroupLink (GLPKnown g' (BoolDef False) Nothing (ListDef [])))
+4 -2
View File
@@ -1244,9 +1244,11 @@ memberInfo g m@GroupMember {memberId, memberRole, memberProfile, memberPubKey, a
}
redactedMemberProfile :: GroupInfo -> GroupMember -> Profile -> Profile
redactedMemberProfile g m Profile {displayName, fullName, shortDescr, image, contactLink, peerType, badge, contactDomain} =
Profile {displayName, fullName, shortDescr = removeSimplexLink =<< shortDescr, image, contactLink = if allowSimplexLinks then contactLink else Nothing, preferences = Nothing, peerType, badge, contactDomain = if allowDirect then contactDomain else Nothing, contactDomainProof = Nothing}
redactedMemberProfile g m Profile {displayName, fullName, shortDescr, image, contactLink = lnk, peerType, badge, contactDomain = d} =
Profile {displayName, fullName, shortDescr = removeSimplexLink =<< shortDescr, image, contactLink, preferences = Nothing, peerType, badge, contactDomain, contactDomainProof = Nothing}
where
contactLink = if allowSimplexLinks then lnk else Nothing
contactDomain = if allowDirect then d else Nothing
allowDirect = groupFeatureMemberAllowed SGFDirectMessages m g
allowSimplexLinks = groupFeatureMemberAllowed SGFSimplexLinks m g && allowDirect
removeSimplexLink s
@@ -5253,6 +5253,20 @@ Query:
Plan:
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_profiles
SET preferences = ?, updated_at = ?
WHERE group_profile_id IN (
SELECT group_profile_id
FROM groups
WHERE user_id = ? AND group_id = ?
)
Plan:
SEARCH group_profiles USING INTEGER PRIMARY KEY (rowid=?)
LIST SUBQUERY 1
SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_relays
SET base_web_url = ?, updated_at = ?
+1 -1
View File
@@ -2681,7 +2681,7 @@ viewChatError isCmd logLevel testView = \case
CEChatNotStopped -> ["error: chat not stopped"]
CEChatStoreChanged -> ["error: chat store changed, please restart chat"]
CEInvalidConnReq -> viewInvalidConnReq
CESimplexNameError ni nameErr ->
CESimplexName ni nameErr ->
let reason = case nameErr of
SNENoValidLink -> "has no usable connection link"
SNEUnknownName -> "is not included in the connection link's profile"