mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-07-03 09:01:59 +00:00
Merge branch 'master' into f/public-groups
This commit is contained in:
@@ -33,8 +33,38 @@ from .types import T
|
||||
|
||||
@dataclass(slots=True)
|
||||
class BotCommand:
|
||||
"""One entry in the bot's advertised slash-command list (wire-side
|
||||
`groupPreferences.commands` or profile `preferences.commands`).
|
||||
|
||||
`keyword` and `label` are required: `keyword` is what the user
|
||||
types after `/`; `label` is the human-readable description shown
|
||||
next to the keyword in the SimpleX client's commands menu.
|
||||
|
||||
`params` is an optional placeholder string that controls how the
|
||||
client behaves when the user taps the command in the menu:
|
||||
|
||||
* `params=None` (default) — the client SENDS `/<keyword>`
|
||||
immediately on tap; no input-box detour. Use this for
|
||||
zero-argument commands (`/help`, `/ping`) where the action is
|
||||
unambiguous.
|
||||
|
||||
* `params="<value>"` — the client PASTES `/<keyword> <params>`
|
||||
into the input box and positions the cursor at the end. The
|
||||
user edits the placeholder and sends. Use this for commands
|
||||
that take a required argument (`/review <pr-url>`,
|
||||
`/order <number>`) so the user sees the expected shape
|
||||
without having to remember it.
|
||||
|
||||
Mirrors `CBCCommand` in the Haskell core
|
||||
(`Simplex.Chat.Types.Preferences`) and the wire TypedDict
|
||||
`ChatBotCommand_command`. Both SimpleX clients (Android/Kotlin
|
||||
and iOS/Swift) implement the paste-vs-send branch on the
|
||||
`params` field; see `CommandsMenuView.{kt,swift}` for the
|
||||
reference UI behaviour.
|
||||
"""
|
||||
keyword: str
|
||||
label: str
|
||||
params: str | None = None
|
||||
|
||||
|
||||
class Bot(Client):
|
||||
@@ -145,10 +175,24 @@ class Bot(Client):
|
||||
"files": {"allow": "yes" if self._allow_files else "no"},
|
||||
}
|
||||
if self._commands:
|
||||
prefs["commands"] = [
|
||||
{"type": "command", "keyword": c.keyword, "label": c.label}
|
||||
for c in self._commands
|
||||
]
|
||||
cmds: list[T.ChatBotCommand] = []
|
||||
for c in self._commands:
|
||||
entry: T.ChatBotCommand_command = {
|
||||
"type": "command",
|
||||
"keyword": c.keyword,
|
||||
"label": c.label,
|
||||
}
|
||||
# `params` is `NotRequired[str]` on the wire; omit the
|
||||
# key entirely when None so the Haskell parser sees
|
||||
# `Nothing` rather than `Just ""`. The two have
|
||||
# different client semantics: `Nothing` (`params=None`)
|
||||
# triggers an immediate send on tap; `Just ""` would
|
||||
# paste `/<keyword> ` (with a trailing space) into the
|
||||
# input box, which is rarely what the operator wants.
|
||||
if c.params is not None:
|
||||
entry["params"] = c.params
|
||||
cmds.append(entry)
|
||||
prefs["commands"] = cmds
|
||||
p["preferences"] = prefs
|
||||
p["peerType"] = "bot"
|
||||
return p
|
||||
|
||||
@@ -1683,6 +1683,10 @@ class Format_simplexLink(TypedDict):
|
||||
simplexUri: str
|
||||
smpHosts: list[str] # non-empty
|
||||
|
||||
class Format_simplexName(TypedDict):
|
||||
type: Literal["simplexName"]
|
||||
nameInfo: "SimplexNameInfo"
|
||||
|
||||
class Format_command(TypedDict):
|
||||
type: Literal["command"]
|
||||
commandStr: str
|
||||
@@ -1708,13 +1712,14 @@ Format = (
|
||||
| Format_uri
|
||||
| Format_hyperLink
|
||||
| Format_simplexLink
|
||||
| Format_simplexName
|
||||
| Format_command
|
||||
| Format_mention
|
||||
| Format_email
|
||||
| Format_phone
|
||||
)
|
||||
|
||||
Format_Tag = Literal["bold", "italic", "strikeThrough", "snippet", "secret", "small", "colored", "uri", "hyperLink", "simplexLink", "command", "mention", "email", "phone"]
|
||||
Format_Tag = Literal["bold", "italic", "strikeThrough", "snippet", "secret", "small", "colored", "uri", "hyperLink", "simplexLink", "simplexName", "command", "mention", "email", "phone"]
|
||||
|
||||
class FormattedText(TypedDict):
|
||||
format: NotRequired["Format"]
|
||||
@@ -1851,6 +1856,10 @@ class GroupLinkPlan_noRelays(TypedDict):
|
||||
type: Literal["noRelays"]
|
||||
groupSLinkData_: NotRequired["GroupShortLinkData"]
|
||||
|
||||
class GroupLinkPlan_updateRequired(TypedDict):
|
||||
type: Literal["updateRequired"]
|
||||
groupSLinkData_: NotRequired["GroupShortLinkData"]
|
||||
|
||||
GroupLinkPlan = (
|
||||
GroupLinkPlan_ok
|
||||
| GroupLinkPlan_ownLink
|
||||
@@ -1858,9 +1867,10 @@ GroupLinkPlan = (
|
||||
| GroupLinkPlan_connectingProhibit
|
||||
| GroupLinkPlan_known
|
||||
| GroupLinkPlan_noRelays
|
||||
| GroupLinkPlan_updateRequired
|
||||
)
|
||||
|
||||
GroupLinkPlan_Tag = Literal["ok", "ownLink", "connectingConfirmReconnect", "connectingProhibit", "known", "noRelays"]
|
||||
GroupLinkPlan_Tag = Literal["ok", "ownLink", "connectingConfirmReconnect", "connectingProhibit", "known", "noRelays", "updateRequired"]
|
||||
|
||||
class GroupMember(TypedDict):
|
||||
groupMemberId: int # int64
|
||||
@@ -1936,6 +1946,7 @@ class GroupRelay(TypedDict):
|
||||
userChatRelay: "UserChatRelay"
|
||||
relayStatus: "RelayStatus"
|
||||
relayLink: NotRequired[str]
|
||||
relayCap: "RelayCapabilities"
|
||||
|
||||
class GroupRootKey_private(TypedDict):
|
||||
type: Literal["private"]
|
||||
@@ -2344,6 +2355,12 @@ ProxyError = ProxyError_PROTOCOL | ProxyError_BROKER | ProxyError_BASIC_AUTH | P
|
||||
|
||||
ProxyError_Tag = Literal["PROTOCOL", "BROKER", "BASIC_AUTH", "NO_SESSION"]
|
||||
|
||||
class PublicGroupAccess(TypedDict):
|
||||
groupWebPage: NotRequired[str]
|
||||
groupDomain: NotRequired[str]
|
||||
domainWebPage: bool
|
||||
allowEmbedding: bool
|
||||
|
||||
class PublicGroupData(TypedDict):
|
||||
publicMemberCount: int # int64
|
||||
|
||||
@@ -2351,6 +2368,7 @@ class PublicGroupProfile(TypedDict):
|
||||
groupType: "GroupType"
|
||||
groupLink: str
|
||||
publicGroupId: str
|
||||
publicGroupAccess: NotRequired["PublicGroupAccess"]
|
||||
|
||||
class RCErrorType_internal(TypedDict):
|
||||
type: Literal["internal"]
|
||||
@@ -2619,6 +2637,9 @@ RcvMsgError = RcvMsgError_dropped | RcvMsgError_parseError
|
||||
|
||||
RcvMsgError_Tag = Literal["dropped", "parseError"]
|
||||
|
||||
class RelayCapabilities(TypedDict):
|
||||
webDomain: NotRequired[str]
|
||||
|
||||
class RelayProfile(TypedDict):
|
||||
displayName: str
|
||||
fullName: str
|
||||
@@ -2680,6 +2701,19 @@ class SimplePreference(TypedDict):
|
||||
|
||||
SimplexLinkType = Literal["contact", "invitation", "group", "channel", "relay"]
|
||||
|
||||
class SimplexNameDomain(TypedDict):
|
||||
nameTLD: "SimplexTLD"
|
||||
domain: str
|
||||
subDomain: list[str]
|
||||
|
||||
class SimplexNameInfo(TypedDict):
|
||||
nameType: "SimplexNameType"
|
||||
nameDomain: "SimplexNameDomain"
|
||||
|
||||
SimplexNameType = Literal["publicGroup", "contact"]
|
||||
|
||||
SimplexTLD = Literal["simplex", "testing", "web"]
|
||||
|
||||
SndCIStatusProgress = Literal["partial", "complete"]
|
||||
|
||||
class SndConnEvent_switchQueue(TypedDict):
|
||||
|
||||
Reference in New Issue
Block a user