rfc: bot messages and buttons, core: command markdown, supported commands in profile preferences, chat sessions preference, peer type field in profile to identify bots (#5360)

* rfc: bot messages and buttons

* update

* update bot rfc

* core: add bot commands to chat preferences and peer type to profile

* update postgresql schema

* update query plans

* chat sessions preference

* markdown for bot commands

* schema

* core: file preference, options to create bot from CLI

* core: different command type

* ios: commands menu

* update types

* update ios

* improve command markdown

* core, ios: update types

* android, desktop: clickable commands in messages in chats with bots

* android, desktop: commands menu

* command menu button, bot icon

* ios: connect flow for bots

* android, desktop: connect flow for bots

* icon

* CLI commands to view and set commands, remove "hidden" property of command, bot api docs

* corrections

* fix inheriting profile preferences to business groups

* note on business address

* ios: export localizations

* fix test

* commands to set file preference on user/contact, tidy up layout and display of command and attachment buttons
This commit is contained in:
Evgeny authored and GitHub committed 2025-08-07 11:13:35 +01:00
1 parent 9596029c30
commit 4811d663e6
93 files changed
+2810 -466

No files matched your search

+32 -10
View File
@@ -50,6 +50,7 @@ data Format
| Colored {color :: FormatColor}
| Uri
| SimplexLink {linkType :: SimplexLinkType, simplexUri :: AConnectionLink, smpHosts :: NonEmpty Text}
| Command {commandStr :: Text}
| Mention {memberName :: Text}
| Email
| Phone
@@ -134,11 +135,21 @@ unmarked :: Text -> Markdown
unmarked = Markdown Nothing
parseMaybeMarkdownList :: Text -> Maybe MarkdownList
parseMaybeMarkdownList s
| all (isNothing . format) ml = Nothing
| otherwise = Just . reverse $ foldl' acc [] ml
parseMaybeMarkdownList s = case ls of
[] -> Nothing
[l]
| T.null cmd -> Nothing
| isCmd -> Just [FormattedText (Just $ Command cmd) l]
where
(isCmd, cmd) = case T.uncons $ T.dropWhile (== ' ') l of
Just (c, rest) -> (c == '/', rest)
Nothing -> (False, "")
_
| all (isNothing . format) ml -> Nothing
| otherwise -> Just . reverse $ foldl' acc [] ml
where
ml = intercalate ["\n"] . map (markdownToList . parseMarkdown) $ T.lines s
ls = T.lines s
ml = intercalate ["\n"] $ map (markdownToList . parseMarkdown) ls
acc [] m = [m]
acc ms@(FormattedText f t : ms') ft@(FormattedText f' t')
| f == f' = FormattedText f (t <> t') : ms'
@@ -175,6 +186,7 @@ markdownP = mconcat <$> A.many' fragmentP
'#' -> A.char '#' *> secretP
'!' -> coloredP <|> wordP
'@' -> mentionP <|> wordP
'/' -> commandP <|> wordP
_
| isDigit c -> phoneP <|> wordP
| otherwise -> wordP
@@ -204,12 +216,14 @@ markdownP = mconcat <$> A.many' fragmentP
if T.null s || T.last s == ' '
then fail "not colored"
else pure $ markdown (colored clr) s
mentionP = do
c <- A.char '@' *> A.peekChar'
(name, punct) <- displayNameTextP_
let sName = if c == '\'' then '\'' `T.cons` name `T.snoc` '\'' else name
mention = markdown (Mention name) ('@' `T.cons` sName)
pure $ if T.null punct then mention else mention :|: unmarked punct
mentionP = prefixedStringP '@' displayNameTextP_ Mention
commandP = prefixedStringP '/' commandTextP Command
prefixedStringP pfx parser format = do
c <- A.char pfx *> A.peekChar'
(str, punct) <- parser
let origStr = if c == '\'' then '\'' `T.cons` str `T.snoc` '\'' else str
res = markdown (format str) (pfx `T.cons` origStr)
pure $ if T.null punct then res else res :|: unmarked punct
colorP =
A.anyChar >>= \case
'r' -> optional "ed" $> Red
@@ -304,6 +318,7 @@ markdownText (FormattedText f_ t) = case f_ of
Uri -> t
SimplexLink {} -> t
Mention _ -> t
Command _ -> t
Email -> t
Phone -> t
Unknown _ -> t
@@ -336,6 +351,13 @@ displayNameTextP_ = (,"") <$> quoted '\'' <|> splitPunctuation <$> takeNameTill
quoted c = A.char c *> takeNameTill (== c) <* A.char c
refChar c = c > ' ' && c /= '#' && c /= '@' && c /= '\''
commandTextP :: Parser (Text, Text)
commandTextP = do
(cmd, punct) <- displayNameTextP_
case T.words cmd of
(keyword : _) | T.all (\c -> isAlpha c || isDigit c || c == '_') keyword -> pure (cmd, punct)
_ -> fail "invalid command keyword"
-- quotes names that contain spaces or end on punctuation
viewName :: Text -> Text
viewName s = if T.any isSpace s || maybe False (isPunctuation . snd) (T.unsnoc s) then "'" <> s <> "'" else s