core: add custom data commands, fix groups parser (#6691)

* core: add custom data commands, fix groups parser

- Add APISetGroupCustomData and APISetContactCustomData to ChatCommand,
  with parsers (/_set custom #, /_set custom @) and processors
  following the APISetChatUIThemes pattern
- Fix APIListGroups parser missing space ("/_groups" -> "/_groups ")
  to align with auto-generated cmdString
- Add chatCommandsDocsData entries for APISetGroupCustomData,
  APISetContactCustomData, and APISetUserAutoAcceptMemberContacts

* core: named fields for codegen, run codegen

- Use named record fields for APISetGroupCustomData,
  APISetContactCustomData, APISetUserAutoAcceptMemberContacts
  (required for chatCommandsDocsData field resolution)
- Fix OnOff field name to "onOff" (avoids clash with User field)
- Remove APISetUserAutoAcceptMemberContacts from undocumentedCommands
- Regenerate COMMANDS.md and commands.ts

* nodejs: add ChatApi wrappers for custom data and apiGetChat

- apiSetGroupCustomData, apiSetContactCustomData
- apiSetAutoAcceptMemberContacts
- apiGetChat (manual wrapper, APIGetChat undocumented)
This commit is contained in:
sh
2026-03-19 09:10:19 +00:00
committed by GitHub
parent b8178d01a8
commit 2df13dad36
6 changed files with 219 additions and 3 deletions

View File

@@ -50,6 +50,9 @@ This file is generated automatically.
- [APIListContacts](#apilistcontacts)
- [APIListGroups](#apilistgroups)
- [APIDeleteChat](#apideletechat)
- [APISetGroupCustomData](#apisetgroupcustomdata)
- [APISetContactCustomData](#apisetcontactcustomdata)
- [APISetUserAutoAcceptMemberContacts](#apisetuserautoacceptmembercontacts)
[User profile commands](#user-profile-commands)
- [ShowActiveUser](#showactiveuser)
@@ -1526,6 +1529,117 @@ ChatCmdError: Command error (only used in WebSockets API).
---
### APISetGroupCustomData
Set group custom data.
*Network usage*: no.
**Parameters**:
- groupId: int64
- customData: JSONObject?
**Syntax**:
```
/_set custom #<groupId>[ <json(customData)>]
```
```javascript
'/_set custom #' + groupId + (customData ? ' ' + JSON.stringify(customData) : '') // JavaScript
```
```python
'/_set custom #' + str(groupId) + ((' ' + json.dumps(customData)) if customData is not None else '') # Python
```
**Responses**:
CmdOk: Ok.
- type: "cmdOk"
- user_: [User](./TYPES.md#user)?
ChatCmdError: Command error (only used in WebSockets API).
- type: "chatCmdError"
- chatError: [ChatError](./TYPES.md#chaterror)
---
### APISetContactCustomData
Set contact custom data.
*Network usage*: no.
**Parameters**:
- contactId: int64
- customData: JSONObject?
**Syntax**:
```
/_set custom @<contactId>[ <json(customData)>]
```
```javascript
'/_set custom @' + contactId + (customData ? ' ' + JSON.stringify(customData) : '') // JavaScript
```
```python
'/_set custom @' + str(contactId) + ((' ' + json.dumps(customData)) if customData is not None else '') # Python
```
**Responses**:
CmdOk: Ok.
- type: "cmdOk"
- user_: [User](./TYPES.md#user)?
ChatCmdError: Command error (only used in WebSockets API).
- type: "chatCmdError"
- chatError: [ChatError](./TYPES.md#chaterror)
---
### APISetUserAutoAcceptMemberContacts
Set auto-accept member contacts.
*Network usage*: no.
**Parameters**:
- userId: int64
- onOff: bool
**Syntax**:
```
/_set accept member contacts <userId> on|off
```
```javascript
'/_set accept member contacts ' + userId + ' ' + (onOff ? 'on' : 'off') // JavaScript
```
```python
'/_set accept member contacts ' + str(userId) + ' ' + ('on' if onOff else 'off') # Python
```
**Responses**:
CmdOk: Ok.
- type: "cmdOk"
- user_: [User](./TYPES.md#user)?
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).

View File

@@ -142,7 +142,10 @@ chatCommandsDocsData =
"Commands to list and delete conversations.",
[ ("APIListContacts", [], "Get contacts.", ["CRContactsList", "CRChatCmdError"], [], Nothing, "/_contacts " <> Param "userId"),
("APIListGroups", [], "Get groups.", ["CRGroupsList", "CRChatCmdError"], [], Nothing, "/_groups " <> Param "userId" <> Optional "" (" @" <> Param "$0") "contactId_" <> Optional "" (" " <> Param "$0") "search"),
("APIDeleteChat", [], "Delete chat.", ["CRContactDeleted", "CRContactConnectionDeleted", "CRGroupDeletedUser", "CRChatCmdError"], [], Just UNBackground, "/_delete " <> Param "chatRef" <> " " <> Param "chatDeleteMode")
("APIDeleteChat", [], "Delete chat.", ["CRContactDeleted", "CRContactConnectionDeleted", "CRGroupDeletedUser", "CRChatCmdError"], [], Just UNBackground, "/_delete " <> Param "chatRef" <> " " <> Param "chatDeleteMode"),
("APISetGroupCustomData", [], "Set group custom data.", ["CRCmdOk", "CRChatCmdError"], [], Nothing, "/_set custom #" <> Param "groupId" <> Optional "" (" " <> Json "$0") "customData"),
("APISetContactCustomData", [], "Set contact custom data.", ["CRCmdOk", "CRChatCmdError"], [], Nothing, "/_set custom @" <> Param "contactId" <> Optional "" (" " <> Json "$0") "customData"),
("APISetUserAutoAcceptMemberContacts", [], "Set auto-accept member contacts.", ["CRCmdOk", "CRChatCmdError"], [], Nothing, "/_set accept member contacts " <> Param "userId" <> " " <> OnOff "onOff")
-- ("APIChatItemsRead", [], "Mark items as read.", ["CRItemsReadForChat"], [], Nothing, ""),
-- ("APIChatRead", [], "Mark chat as read.", ["CRCmdOk"], [], Nothing, ""),
-- ("APIChatUnread", [], "Mark chat as unread.", ["CRCmdOk"], [], Nothing, ""),
@@ -398,7 +401,6 @@ undocumentedCommands =
"APISetServerOperators",
"APISetUserContactReceipts",
"APISetUserGroupReceipts",
"APISetUserAutoAcceptMemberContacts",
"APISetUserServers",
"APISetUserUIThemes",
"APIStandaloneFileInfo",

View File

@@ -557,6 +557,51 @@ export namespace APIDeleteChat {
}
}
// Set group custom data.
// Network usage: no.
export interface APISetGroupCustomData {
groupId: number // int64
customData?: object
}
export namespace APISetGroupCustomData {
export type Response = CR.CmdOk | CR.ChatCmdError
export function cmdString(self: APISetGroupCustomData): string {
return '/_set custom #' + self.groupId + (self.customData ? ' ' + JSON.stringify(self.customData) : '')
}
}
// Set contact custom data.
// Network usage: no.
export interface APISetContactCustomData {
contactId: number // int64
customData?: object
}
export namespace APISetContactCustomData {
export type Response = CR.CmdOk | CR.ChatCmdError
export function cmdString(self: APISetContactCustomData): string {
return '/_set custom @' + self.contactId + (self.customData ? ' ' + JSON.stringify(self.customData) : '')
}
}
// Set auto-accept member contacts.
// Network usage: no.
export interface APISetUserAutoAcceptMemberContacts {
userId: number // int64
onOff: boolean
}
export namespace APISetUserAutoAcceptMemberContacts {
export type Response = CR.CmdOk | CR.ChatCmdError
export function cmdString(self: APISetUserAutoAcceptMemberContacts): string {
return '/_set accept member contacts ' + self.userId + ' ' + (self.onOff ? 'on' : 'off')
}
}
// 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).

View File

@@ -747,6 +747,47 @@ export class ChatApi {
throw new ChatCommandError("error deleting chat", r)
}
/**
* Set group custom data.
* Network usage: no.
*/
async apiSetGroupCustomData(groupId: number, customData?: object): Promise<void> {
const r = await this.sendChatCmd(CC.APISetGroupCustomData.cmdString({groupId, customData}))
if (r.type === "cmdOk") return
throw new ChatCommandError("error setting group custom data", r)
}
/**
* Set contact custom data.
* Network usage: no.
*/
async apiSetContactCustomData(contactId: number, customData?: object): Promise<void> {
const r = await this.sendChatCmd(CC.APISetContactCustomData.cmdString({contactId, customData}))
if (r.type === "cmdOk") return
throw new ChatCommandError("error setting contact custom data", r)
}
/**
* Set auto-accept member contacts.
* Network usage: no.
*/
async apiSetAutoAcceptMemberContacts(userId: number, onOff: boolean): Promise<void> {
const r = await this.sendChatCmd(CC.APISetUserAutoAcceptMemberContacts.cmdString({userId, onOff}))
if (r.type === "cmdOk") return
throw new ChatCommandError("error setting auto-accept member contacts", r)
}
/**
* Get chat items.
* Network usage: no.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async apiGetChat(chatType: T.ChatType, chatId: number, count: number): Promise<any> {
const r: any = await this.sendChatCmd(`/_get chat ${T.ChatType.cmdString(chatType)}${chatId} count=${count}`)
if (r.type === "apiChat") return r.chat
throw new ChatCommandError("error getting chat", r)
}
/**
* Get active user profile
* Network usage: no.

View File

@@ -275,7 +275,7 @@ data ChatCommand
| SetUserContactReceipts UserMsgReceiptSettings
| APISetUserGroupReceipts UserId UserMsgReceiptSettings
| SetUserGroupReceipts UserMsgReceiptSettings
| APISetUserAutoAcceptMemberContacts UserId Bool
| APISetUserAutoAcceptMemberContacts {userId :: UserId, onOff :: Bool}
| SetUserAutoAcceptMemberContacts Bool
| APIHideUser UserId UserPwd
| APIUnhideUser UserId UserPwd
@@ -362,6 +362,8 @@ data ChatCommand
| APISetConnectionAlias {connectionId :: Int64, localAlias :: LocalAlias}
| APISetUserUIThemes UserId (Maybe UIThemeEntityOverrides)
| APISetChatUIThemes ChatRef (Maybe UIThemeEntityOverrides)
| APISetGroupCustomData {groupId :: GroupId, customData :: Maybe CustomData}
| APISetContactCustomData {contactId :: ContactId, customData :: Maybe CustomData}
| APIGetNtfToken
| APIRegisterToken DeviceToken NotificationsMode
| APIVerifyToken DeviceToken C.CbNonce ByteString

View File

@@ -1409,6 +1409,16 @@ processChatCommand vr nm = \case
liftIO $ setGroupUIThemes db user g uiThemes
ok user
_ -> throwCmdError "not supported"
APISetGroupCustomData groupId customData_ -> withUser $ \user -> do
withFastStore $ \db -> do
g <- getGroupInfo db vr user groupId
liftIO $ setGroupCustomData db user g customData_
ok user
APISetContactCustomData contactId customData_ -> withUser $ \user -> do
withFastStore $ \db -> do
ct <- getContact db vr user contactId
liftIO $ setContactCustomData db user ct customData_
ok user
APIGetNtfToken -> withUser' $ \_ -> crNtfToken <$> withAgent getNtfToken
APIRegisterToken token mode -> withUser $ \_ ->
CRNtfTokenStatus <$> withAgent (\a -> registerNtfToken a nm token mode)
@@ -4415,6 +4425,8 @@ chatCommandP =
"/_set prefs @" *> (APISetContactPrefs <$> A.decimal <* A.space <*> jsonP),
"/_set theme user " *> (APISetUserUIThemes <$> A.decimal <*> optional (A.space *> jsonP)),
"/_set theme " *> (APISetChatUIThemes <$> chatRefP <*> optional (A.space *> jsonP)),
"/_set custom #" *> (APISetGroupCustomData <$> A.decimal <*> optional (A.space *> jsonP)),
"/_set custom @" *> (APISetContactCustomData <$> A.decimal <*> optional (A.space *> jsonP)),
"/_ntf get" $> APIGetNtfToken,
"/_ntf register " *> (APIRegisterToken <$> strP_ <*> strP),
"/_ntf verify " *> (APIVerifyToken <$> strP <* A.space <*> strP <* A.space <*> strP),