diff --git a/contrib/telega-adblock.el b/contrib/telega-adblock.el index dfb4316..e4c7849 100644 --- a/contrib/telega-adblock.el +++ b/contrib/telega-adblock.el @@ -263,7 +263,7 @@ an URL." (defun telega-adblock-msg-has-erid-p (msg) "Return non-nil if MSG text contains ERID label." - (or (telega-msg-match-p msg '(contains "\\")) + (or (telega-msg-match-p msg '(contains "\\")) ;; NOTE: also check links in the message to have "erid" get ;; parameter (seq-some (lambda (link-spec) diff --git a/docs/index.html b/docs/index.html index 548f630..4b76b5c 100644 --- a/docs/index.html +++ b/docs/index.html @@ -3,10 +3,10 @@ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> - + -Telega Manual (v0.8.240) +Telega Manual (v0.8.241) @@ -36,7 +36,7 @@
-

 Telega Manual (v0.8.240)

+

 Telega Manual (v0.8.241)

@@ -827,7 +828,7 @@

rootbuf lists the chat buttons, such as:

-
+
 {🎗Saved Messages            }📌  📹 Video (10s)               Fri✓
 [Emacs | Emacs (english)     ]  @oldosfan: same                Fri
 ...
@@ -845,7 +846,7 @@ 

-
+
 [243:📑Main      4890]  [51:Groups       4677]  [27:Channels      210]
 [53:Contacts         ]  [0:Important         ]  [3:📑Archive      670]
 
@@ -934,7 +935,7 @@

-
+
 -/------------------------------(main)--------------------------------
 
@@ -972,7 +973,7 @@

-
+
 -\---------------------(unread-count join-date)-----------------------
 
@@ -2002,7 +2003,7 @@

+
 MSG1                              <--- msg sent on 27dec
 -------(28 December 2020)------   <--- date break
 MSG2                              <--- msg sent on 28dec
@@ -2393,7 +2394,7 @@ 

+
 MSG1                              <--- msg sent on 27dec
 -------(28 December 2020)------   <--- date break
 MSG2                              <--- msg sent on 28dec
@@ -2584,7 +2585,7 @@ 

-
+
 ```<language-name>
 first line of multiline preformatted code
 second line
@@ -2616,7 +2617,7 @@ 

+
 #+begin_src <language-name>
 code line
 next code line
@@ -3532,7 +3533,7 @@ 

-
+
 [⏪] [⏩] [2×] [Stop] 
 
@@ -3795,22 +3796,24 @@

+
 https://github.com/zevlg/telega.el/issues/105
 https://gitlab.com/jessieh/mood-line/issues/6
 https://www.youtube.com/watch?v=0m2jR6_eMkU
@@ -5388,6 +5391,22 @@ 

+

[telega bug]

+
+

+Q: I've got a lot of [telega bug] things in the rootbuf or +chatbuf. +

+ +

+A: Set debug-on-error to non-nil and trigger the bug to get the +backtrace buffer. Report this backtrace to github issues or to the +telega.el Telegram Chat +

+
+

+

Stickers are not shown

@@ -5398,9 +5417,11 @@

Stickers / Animations for details. +A: If your Emacs does not have bultin WEBP format +support. telega will use dwebp tool to convert stickers from WEBP +format to the format supported by your Emacs. See Stickers / +Animations for details. So, make sure dwebp tool is available on +your machine.

diff --git a/docs/telega-ellit.org b/docs/telega-ellit.org index 86b908f..423fee2 100644 --- a/docs/telega-ellit.org +++ b/docs/telega-ellit.org @@ -1223,15 +1223,26 @@ project. ** Invalid TDLib version **TODO** +** ~[telega bug]~ + +**Q**: I've got a lot of ~[telega bug]~ things in the rootbuf or +chatbuf. + +**A**: Set ~debug-on-error~ to non-nil and trigger the bug to get the +backtrace buffer. Report this backtrace to github issues or to the +[[https://t.me/emacs_telega][telega.el Telegram Chat]] + ** Stickers are not shown **Q**: Stickers are not fully shown, I see only outlines for some stickers, like on the screenshot: [[file:https:/zevlg.github.io/telega/bad-stickers.jpg]] -**A**: =telega= uses =dwebp= tool to convert stickers in the WEBP -format to the format supported by Emacs. See -[[#stickers--animations][Stickers / Animations]] for details. +**A**: If your Emacs does not have bultin WEBP format +support. =telega= will use =dwebp= tool to convert stickers from WEBP +format to the format supported by your Emacs. See [[#stickers--animations][Stickers / +Animations]] for details. So, make sure =dwebp= tool is available on +your machine. ** Notifications does not work diff --git a/docs/telega-manual.org b/docs/telega-manual.org index 4afe5dd..668e451 100644 --- a/docs/telega-manual.org +++ b/docs/telega-manual.org @@ -1,5 +1,5 @@ #+options: timestamp:nil \n:t num:nil ellit-cid:t -#+title: Telega Manual (v0.8.240) +#+title: Telega Manual (v0.8.241) #+author: Zajcev Evgeny #+startup: showall @@ -122,6 +122,7 @@ Do not edit manually. Modify =telega-ellit.org= or comments in - [[#troubleshooting][Troubleshooting]] - [[#cant-compile-telega-server][Can't compile =telega-server=]] - [[#invalid-tdlib-version][Invalid TDLib version]] + - [[#telega-bug][~[telega bug]~]] - [[#stickers-are-not-shown][Stickers are not shown]] - [[#notifications-does-not-work][Notifications does not work]] @@ -2808,14 +2809,16 @@ chatbuf's footer. This behaviour is controlled by custom options: - User Option: ~telega-story-show-active-stories-for~ - Show active stories in the chatbuf's footer for chats matching this temex. + Show active stories in the chatbuf's footer for chats matching this temex. + If temex returns a number then limit number of shown active stories. - Default value: ~all~ + Default value: ~(return 10)~ - User Option: ~telega-story-show-pinned-stories-for~ - Show pinned stories in the chatbuf's footer for chats matching this temex. + Show pinned stories in the chatbuf's footer for chats matching this temex. + If temex returns a number then limit number of shown pinned stories. - Default value: ~all~ + Default value: ~(return 10)~ =telega= can preload media content for stories from users or channels of your interest. This is controlled by custom option: @@ -3814,6 +3817,18 @@ This is required because only user in the room can access the member info. **TODO** +** ~[telega bug]~ +:PROPERTIES: +:CUSTOM_ID: telega-bug +:END: + +**Q**: I've got a lot of ~[telega bug]~ things in the rootbuf or +chatbuf. + +**A**: Set ~debug-on-error~ to non-nil and trigger the bug to get the +backtrace buffer. Report this backtrace to github issues or to the +[[https://t.me/emacs_telega][telega.el Telegram Chat]] + ** Stickers are not shown :PROPERTIES: :CUSTOM_ID: stickers-are-not-shown @@ -3823,9 +3838,11 @@ This is required because only user in the room can access the member info. stickers, like on the screenshot: [[file:https:/zevlg.github.io/telega/bad-stickers.jpg]] -**A**: =telega= uses =dwebp= tool to convert stickers in the WEBP -format to the format supported by Emacs. See -[[#stickers--animations][Stickers / Animations]] for details. +**A**: If your Emacs does not have bultin WEBP format +support. =telega= will use =dwebp= tool to convert stickers from WEBP +format to the format supported by your Emacs. See [[#stickers--animations][Stickers / +Animations]] for details. So, make sure =dwebp= tool is available on +your machine. ** Notifications does not work :PROPERTIES: diff --git a/etc/Dockerfile b/etc/Dockerfile index 884ea9b..ec7f31a 100644 --- a/etc/Dockerfile +++ b/etc/Dockerfile @@ -21,7 +21,7 @@ ARG telega_branch=master RUN set +x && apk update && apk upgrade && \ apk add --update alpine-sdk linux-headers git zlib-dev openssl-dev gperf php cmake -RUN echo "TDLib v1.8.24-437c2d0c6" > tdlib-version && \ +RUN echo "TDLib v1.8.24-d79bd4b69" > tdlib-version && \ git clone https://github.com/tdlib/td.git && \ cd td && \ git checkout ${tdlib_branch} && \ diff --git a/etc/langs/en.plist b/etc/langs/en.plist index da93bc8..2a2f7a7 100644 --- a/etc/langs/en.plist +++ b/etc/langs/en.plist @@ -600,6 +600,10 @@ Example: 23 y.o. designer from San Francisco") :value "Photo has expired") ("lng_ttl_video_expired" :value "Video has expired") + ("lng_ttl_voice_expired" + :value "Voice message expired") + ("lng_ttl_round_expired" + :value "Round message expired") ;; message_ttl_setting ("lng_ttl_about_duration1" :value "24 hours") @@ -1205,6 +1209,8 @@ You can send them an invite link as message instead.") ;; Stories ("lng_stories_my_name" :value "My Story") + ("lng_stories_show_more" + :value "Show more") ("lng_view_button_story" :value "View story") ("lng_contacts_stories_status" diff --git a/telega-chat.el b/telega-chat.el index 06e77e4..b84d4d7 100644 --- a/telega-chat.el +++ b/telega-chat.el @@ -2015,35 +2015,52 @@ Use this to surrond header with some prefix and suffix." "active-stories" "updateChatActiveStories") - (when (and (telega-chatbuf-match-p telega-story-show-active-stories-for) - (not (plist-get telega-chatbuf--hidden-headers :active-stories))) - (when-let* ((active-stories - (telega-chat--active-stories telega-chatbuf--chat)) - (stories (plist-get active-stories :stories))) - (unless (seq-empty-p stories) - (telega-ins--as-string - (telega-ins--text-button (telega-symbol 'button-close) + (when-let* ((match-result + (telega-chatbuf-match-p telega-story-show-active-stories-for)) + (shown-p + (not (plist-get telega-chatbuf--hidden-headers :active-stories))) + (active-stories + (telega-chat--active-stories telega-chatbuf--chat)) + (stories (plist-get active-stories :stories)) + (show-nstories + (if (and (integerp match-result) + (not (plist-get telega-chatbuf--hidden-headers + :active-stories-show-more))) + match-result + (length stories)))) + (unless (seq-empty-p stories) + (telega-ins--as-string + (telega-ins--text-button (telega-symbol 'button-close) + 'face 'telega-link + 'action (lambda (_ignored) + (plist-put telega-chatbuf--hidden-headers :active-stories t) + (telega-chatbuf--chat-update "active-stories"))) + (telega-ins " " (telega-i18n "lng_stories_row_count" + :count (seq-length stories)) + ":") + (seq-doseq (story-info (seq-take stories show-nstories)) + (if-let* ((chat-id (plist-get telega-chatbuf--chat :id)) + (story-id (plist-get story-info :story_id)) + (story (telega-story-get chat-id story-id 'offline))) + (progn + (telega-ins " ") + (telega-ins--button-story-one-line-no-caption story)) + + ;; NOTE: need to fetch story structure to display it + (telega--getStory chat-id story-id nil + (lambda (story) + (with-telega-chatbuf (telega-story-chat story) + (telega-chatbuf--chat-update "active-stories")))))) + + (when (> (length stories) show-nstories) + (telega-ins (telega-symbol 'eliding)) + (telega-ins--box-button (telega-i18n "lng_stories_show_more") 'face 'telega-link 'action (lambda (_ignored) - (plist-put telega-chatbuf--hidden-headers :active-stories t) - (telega-chatbuf--chat-update "active-stories"))) - (telega-ins " " (telega-i18n "lng_stories_row_count" - :count (seq-length stories)) - ":") - (seq-doseq (story-info stories) - (if-let* ((chat-id (plist-get telega-chatbuf--chat :id)) - (story-id (plist-get story-info :story_id)) - (story (telega-story-get chat-id story-id 'offline))) - (progn - (telega-ins " ") - (telega-ins--button-story-one-line-no-caption story)) - - ;; NOTE: need to fetch story structure to display it - (telega--getStory chat-id story-id nil - (lambda (story) - (with-telega-chatbuf (telega-story-chat story) - (telega-chatbuf--chat-update "active-stories")))))) - ))))) + (plist-put telega-chatbuf--hidden-headers + :active-stories-show-more t) + (telega-chatbuf--chat-update "active-stories")))) + )))) (defun telega-chatbuf-footer-pinned-stories () (telega-chatbuf--dirtiness-init @@ -2051,25 +2068,43 @@ Use this to surrond header with some prefix and suffix." "updateChatActiveStories" ; For story's `seen' status ) - (when (and (telega-chatbuf-match-p telega-story-show-pinned-stories-for) - (not (plist-get telega-chatbuf--hidden-headers :pinned-stories))) - (when-let ((pinned-stories - (plist-get telega-chatbuf--chat :telega-pinned-stories))) - (telega-ins--as-string - (telega-ins--text-button (telega-symbol 'button-close) + (when-let* ((match-result + (telega-chatbuf-match-p telega-story-show-pinned-stories-for)) + (shown-p + (not (plist-get telega-chatbuf--hidden-headers :pinned-stories))) + (pinned-stories + (plist-get telega-chatbuf--chat :telega-pinned-stories)) + (show-nstories + (if (and (integerp match-result) + (not (plist-get telega-chatbuf--hidden-headers + :pinned-stories-show-more))) + match-result + (length pinned-stories)))) + (telega-ins--as-string + (telega-ins--text-button (telega-symbol 'button-close) + 'face 'telega-link + 'action (lambda (_ignored) + (plist-put telega-chatbuf--hidden-headers :pinned-stories t) + (telega-chatbuf--chat-update "pinned-stories"))) + (telega-ins (telega-symbol 'pin) " " + (telega-i18n "lng_stories_row_count" + :count (seq-length pinned-stories)) + ": ") + (seq-doseq (story (seq-take pinned-stories show-nstories)) + (telega-button--insert 'telega story + :inserter (lambda (story) + (telega-ins--story-content-one-line story nil t)) + :action #'telega-story-open)) + + (when (> (length pinned-stories) show-nstories) + (telega-ins (telega-symbol 'eliding)) + (telega-ins--box-button (telega-i18n "lng_stories_show_more") 'face 'telega-link 'action (lambda (_ignored) - (plist-put telega-chatbuf--hidden-headers :pinned-stories t) - (telega-chatbuf--chat-update "active-stories"))) - (telega-ins (telega-symbol 'pin) " " - (telega-i18n "lng_stories_row_count" - :count (seq-length pinned-stories)) - ": ") - (seq-doseq (story pinned-stories) - (telega-button--insert 'telega story - :inserter (lambda (story) - (telega-ins--story-content-one-line story nil t)) - :action #'telega-story-open)))))) + (plist-put telega-chatbuf--hidden-headers + :pinned-stories-show-more t) + (telega-chatbuf--chat-update "pinned-stories")))) + ))) (defun telega-chatbuf-footer-active-vvnote () "Formatter for the active voice/video note currently playing in the chatbuf." @@ -2567,6 +2602,10 @@ Global chat bindings: telega-chatbuf--administrators nil telega-chatbuf--group-call-users nil telega-chatbuf--bot-start-parameter nil + + telega-chatbuf--hidden-headers + (list :active-stories nil :pinned-stories nil :video-chat nil + :active-stories-show-more nil :pinned-stories-show-more nil) ) ;; Make usernames with "_" be completable @@ -5048,7 +5087,7 @@ Uses `telega-screenshot-function' to take a screenshot." (defun telega-chatbuf-sticker-insert (sticker) "Attach STICKER to the input." (let ((thumb (plist-get sticker :thumbnail)) - (preview (telega-sticker--image sticker))) + (preview (copy-sequence (telega-sticker--image sticker)))) ;; Scale down preview to single char (plist-put (cdr preview) :scale (/ 1.0 (car telega-sticker-size))) @@ -5085,7 +5124,7 @@ EMOJI - emoji string to use instead of emoji associated with the STICKER." (defun telega-chatbuf-animation-insert (animation) "Attach ANIMATION to the input." (let ((thumb (plist-get animation :thumbnail)) - (preview (telega-animation--create-image animation))) + (preview (copy-sequence (telega-animation--create-image animation)))) ;; Scale down preview to single char (plist-put (cdr preview) :scale (/ 1.0 telega-animation-height)) diff --git a/telega-core.el b/telega-core.el index 7f4b0ed..f6119af 100644 --- a/telega-core.el +++ b/telega-core.el @@ -485,7 +485,8 @@ Asynchronously loaded when chatbuf is created.") (make-variable-buffer-local 'telega-chatbuf--administrators) (defvar telega-chatbuf--hidden-headers - '(:active-stories nil :pinned-stories nil :video-chat nil) + (list :active-stories nil :pinned-stories nil :video-chat nil + :active-stories-show-more nil :pinned-stories-show-more nil) "Plist to check whether some header is hidden by pressing [x] button.") (make-variable-buffer-local 'telega-chatbuf--hidden-headers) @@ -805,8 +806,8 @@ Strips `line-prefix' and `wrap-prefix' text properties from copied text." "Execute BODY in help BUFFER-OR-NAME." (declare (indent 1)) `(progn - (with-help-window ,buffer-or-name) - (redisplay) + ;; (with-help-window ,buffer-or-name) + ;; (redisplay) (with-help-window ,buffer-or-name (set-buffer standard-output) (setq-local x-underline-at-descent-line t) diff --git a/telega-customize.el b/telega-customize.el index 74b6750..8608fd1 100644 --- a/telega-customize.el +++ b/telega-customize.el @@ -1886,14 +1886,16 @@ fallback to cdr argument." :type 'telega-chat-temex :group 'telega-story) -(defcustom telega-story-show-active-stories-for 'all - "Show active stories in the chatbuf's footer for chats matching this temex." +(defcustom telega-story-show-active-stories-for '(return 10) + "Show active stories in the chatbuf's footer for chats matching this temex. +If temex returns a number then limit number of shown active stories." :type 'telega-chat-temex :options '((type private) (active-stories-list main)) :group 'telega-story) -(defcustom telega-story-show-pinned-stories-for 'all - "Show pinned stories in the chatbuf's footer for chats matching this temex." +(defcustom telega-story-show-pinned-stories-for '(return 10) + "Show pinned stories in the chatbuf's footer for chats matching this temex. +If temex returns a number then limit number of shown pinned stories." :type 'telega-chat-temex :options '((and (user is-close-friend) (active-stories-list main))) :group 'telega-story) @@ -2374,8 +2376,13 @@ By default `(?+ . ?>)' is used resulting in +++++> progress bar." :type 'string :group 'telega-symbol) +(defcustom telega-symbol-timer-clock "⏲" + "Symbol used as timer clock in live location context." + :type 'string + :group 'telega-symbol) + (defcustom telega-symbol-distance "📏" - "Symbol used to display distance." + "Symbol used to display distance in location context." :type 'string :group 'telega-symbol) @@ -2551,6 +2558,7 @@ Used in one line message inserter." (telega-etc-file-create-image "symbols/reply-quote.svg" 2))) (right-arrow (when (and telega-use-images (image-type-available-p 'svg)) (telega-etc-file-create-image "symbols/right-arrow.svg" 2))) + timer-clock video video-chat-active video-chat-passive "⏪" "⏩" diff --git a/telega-i18n.el b/telega-i18n.el index cf7b8ac..40d0b7e 100644 --- a/telega-i18n.el +++ b/telega-i18n.el @@ -47,7 +47,8 @@ (defconst telega-i18n--alias-alist '(("telega_show" . "lng_usernames_activate_confirm") ("telega_loading" . "lng_profile_loading") - ("telega_for_n_hours" . "lng_mute_duration_hours")) + ("telega_for_n_hours" . "lng_mute_duration_hours") + ("telega_stop" . "lng_export_stop")) "i18n names aliases alist.") (defconst telega-i18n--en-strings nil diff --git a/telega-inline.el b/telega-inline.el index 19d23fb..b40b039 100644 --- a/telega-inline.el +++ b/telega-inline.el @@ -60,13 +60,15 @@ (when link (telega-browse-url link))))) -(defun telega--getCallbackQueryAnswer (msg payload) +(cl-defun telega--getCallbackQueryAnswer (msg payload &key (callback #'ignore)) "Async send callback to bot." - (telega-server--send + (declare (indent 2)) + (telega-server--call (list :@type "getCallbackQueryAnswer" :chat_id (plist-get msg :chat_id) :message_id (plist-get msg :id) - :payload payload))) + :payload payload) + callback)) (defun telega-inline--callback (kbd-button msg) "Action to take when KBD-BUTTON is pressed." diff --git a/telega-ins.el b/telega-ins.el index de44997..531e41a 100644 --- a/telega-ins.el +++ b/telega-ins.el @@ -895,8 +895,11 @@ If NO-2X-BUTTON is specified, then do not display \"2x\" button." (telega-ins-progress-bar played dur 15 ?\# ?\.) (telega-ins "]")) - ;; Duration + ;; Duration / self destruct (telega-ins " (" (telega-duration-human-readable dur) ")") + (when-let ((tl-ttl (plist-get msg :self_destruct_type))) + (telega-ins ", ") + (telega-ins--self-destruct-type tl-ttl 'short)) ;; ffplay controls to seek/2x/stop (when (telega-ffplay-playing-p proc) @@ -924,7 +927,8 @@ If NO-2X-BUTTON is specified, then do not display \"2x\" button." (dur (plist-get note :duration)) (note-file (telega-file--renew note :video)) (recognition (plist-get note :speech_recognition_result)) - (ffplay-proc (plist-get msg :telega-ffplay-proc))) + (ffplay-proc (plist-get msg :telega-ffplay-proc)) + (tl-ttl (plist-get msg :self_destruct_type))) (telega-ins (propertize "NOTE" 'face 'telega-shadow)) (telega-ins-fmt " (%dx%d %s %s)" (plist-get note :length) (plist-get note :length) @@ -932,6 +936,9 @@ If NO-2X-BUTTON is specified, then do not display \"2x\" button." (telega-duration-human-readable dur)) (when (telega--tl-get msg :content :is_viewed) (telega-ins (telega-symbol 'eye))) + (when tl-ttl + (telega-ins ", ") + (telega-ins--self-destruct-type tl-ttl 'short)) ;; ffplay controls to seek/2x/stop (when (telega-ffplay-playing-p ffplay-proc) @@ -959,7 +966,10 @@ If NO-2X-BUTTON is specified, then do not display \"2x\" button." (when-let ((img (or (plist-get msg :telega-ffplay-frame) (when (or minithumb thumb) (telega-media--image - (cons note 'telega-vvnote-video--create-image) + (cons note + (if tl-ttl + #'telega-vvnote-video-ttl--create-image + #'telega-vvnote-video--create-image)) (cons thumb :file)))))) (telega-ins "\n") (telega-ins--image-slices img) @@ -979,9 +989,10 @@ If NO-ATTACH-SYMBOL is specified, then do not insert attachment symbol." (telega-ins (telega-symbol 'attachment) " ")) (if (telega-file--downloaded-p doc-file) - (telega-ins--with-face 'telega-link - (telega-ins (telega-short-filename - (telega--tl-get doc-file :local :path)))) + (let ((local-path (telega--tl-get doc-file :local :path))) + (telega-ins--raw-button (telega-link-props 'file local-path + 'face 'telega-link) + (telega-ins (telega-short-filename local-path)))) (telega-ins fname)) (telega-ins " (" (file-size-human-readable (telega-file--size doc-file)) @@ -1136,25 +1147,33 @@ Return `non-nil' if WEB-PAGE has been inserted." (telega-ins-fmt "%fN, %fE" (plist-get location :latitude) (plist-get location :longitude))) +(defun telega-ins--location-live-header (live-for updated-ago) + "Insert live location header." + (telega-ins--with-face 'telega-shadow + (telega-ins "Live")) + (when (> live-for 0) + (telega-ins " " (telega-time-ago-human-readable updated-ago) + " " (telega-symbol 'timer-clock) + (telega-duration-human-readable + live-for (if (> live-for 3600) 2 1) + (unless (> live-for 3600) 'long)))) + t) + (defun telega-ins--location-live (msg) "Insert live location description for location message MSG." ;; NOTE: in case of unexpired live location show last update ;; time and expiration period (when-let ((live-for-spec (telega-msg-location-live-for msg))) (cl-destructuring-bind (live-for updated-ago) live-for-spec - (telega-ins " " (propertize "Live" 'face 'telega-shadow)) - (when (> live-for 0) - (telega-ins-fmt " for %s" - (telega-duration-human-readable live-for 1 'long)) - (telega-ins-fmt " (updated %s ago)" - (telega-duration-human-readable updated-ago 1 'long)) + (telega-ins " ") + (telega-ins--location-live-header live-for updated-ago) + + (when (plist-get msg :can_be_edited) (telega-ins " ") - (telega-ins--box-button (telega-i18n "telega_stop_live_location" - :count 1) + (telega-ins--box-button (telega-i18n "telega_stop") 'action (lambda (_button) - (telega--editMessageLiveLocation msg nil)))) + (telega--editMessageLiveLocation msg nil))) - (when (plist-get msg :can_be_edited) (let ((proximity-radius (telega--tl-get msg :content :proximity_alert_radius))) (telega-ins "\n") @@ -1178,7 +1197,7 @@ Return `non-nil' if WEB-PAGE has been inserted." (with-online-status-p t) (with-username-p t) (with-phone-p t)) - "One line variant inserter for CONTACT." + "Multiple line variant inserter for CONTACT." (declare (indent 1)) (let* ((user (if (eq (telega--tl-type contact) 'user) contact @@ -1222,14 +1241,19 @@ Return `non-nil' if WEB-PAGE has been inserted." (user-id (plist-get contact :user_id)) (user (unless (zerop user-id) (telega-user-get user-id))) (user-ava (when (and telega-user-show-avatars user) - (telega-msg-sender-avatar-image user)))) + (telega-msg-sender-avatar-image-three-lines user)))) (when user-ava (telega-ins--image user-ava 0)) + (telega-ins--with-face 'telega-shadow + (telega-ins-i18n "lng_in_dlg_contact")) + (telega-ins "\n") + (when user-ava + (telega-ins--image user-ava 1)) (telega-ins--contact (plist-get content :contact) :with-avatar-p nil) (telega-ins "\n") (when user-ava - (telega-ins--image user-ava 1)) + (telega-ins--image user-ava 2)) (telega-ins--box-button (concat " VIEW CONTACT ") 'action 'telega-msg-button--action))) @@ -1695,6 +1719,8 @@ If NO-THUMBNAIL-P is non-nil, then do not insert thumbnail." messageChatSetTtl messageExpiredPhoto messageExpiredVideo + messageExpiredVoiceNote + messageExpiredVideoNote messageChatChangePhoto messageChatDeletePhoto messageChatUpgradeTo @@ -1802,6 +1828,10 @@ Special messages are determined with `telega-msg-special-p'." (telega-ins-i18n "lng_ttl_photo_expired")) (messageExpiredVideo (telega-ins-i18n "lng_ttl_video_expired")) + (messageExpiredVoiceNote + (telega-ins-i18n "lng_ttl_voice_expired")) + (messageExpiredVideoNote + (telega-ins-i18n "lng_ttl_round_expired")) (messageChatChangePhoto (let ((animated-p (telega--tl-get content :photo :animation))) (if (plist-get msg :is_channel_post) @@ -3057,7 +3087,7 @@ If SHORT-P is non-nil then use short version." (telega-ins " ") (telega-ins--image (let ((telega-video-note-height 1)) - (telega-vvnote-video--svg thumb-filename nil nil 'png)))) + (telega-vvnote-video--svg thumb-filename)))) (telega-ins " (" (telega-duration-human-readable duration) ")"))) (inputMessageSticker (telega-ins--input-file (plist-get imc :sticker) "Sticker")) diff --git a/telega-match.el b/telega-match.el index a43c555..fca09d5 100644 --- a/telega-match.el +++ b/telega-match.el @@ -1019,7 +1019,8 @@ function." :telega-last-ignored))) (when (eq (plist-get msg :id) (car last-ignored)) (cdr last-ignored))))))) - (eq ignored-by (or reason ignored-by)))) + (or (and reason (eq reason ignored-by)) + ignored-by))) ;;; ellit-org: msg-temex ;; - (contains ~REGEXP~ ) :: diff --git a/telega-modes.el b/telega-modes.el index dabacdc..27bcef3 100644 --- a/telega-modes.el +++ b/telega-modes.el @@ -1308,6 +1308,8 @@ your actual location to \"Saved Messages\" using mobile Telegram client." (declare-function telega-root-aux-remove "telega-root" (inserter)) (declare-function telega-root-aux-redisplay "telega-root" (&optional inserter)) +(defvar telega-active-locations--timer nil + "Timer to update active locations.") (defvar telega-active-location--messages nil "List of recently active live location messages.") @@ -1346,8 +1348,15 @@ your actual location to \"Saved Messages\" using mobile Telegram client." (telega-root-aux-append #'telega-ins--active-locations) (when (telega-server-live-p) (telega-active-locations--fetch)) + + ;; Always update all locations once a minute + (setq telega-active-locations--timer + (run-with-timer 60 60 #'telega-active-locations--check)) ) + (when telega-active-locations--timer + (cancel-timer telega-active-locations--timer) + (setq telega-active-locations--timer nil)) (telega-root-aux-remove #'telega-ins--active-locations) (remove-hook 'telega-connection-state-hook #'telega-active-locations--check) @@ -1433,15 +1442,11 @@ EVENT must be \"updateDeleteMessages\"." (telega-ins--msg-sender chat :with-username-p t :with-brackets-p t))) - (telega-ins--with-face 'telega-shadow - (telega-ins " Live")) + + (telega-ins " ") (cl-destructuring-bind (live-for updated-ago) (telega-msg-location-live-for msg) - (telega-ins-fmt " for %s" - (telega-duration-human-readable live-for 1 'long)) - (telega-ins-fmt " (%s ago)" - (telega-duration-human-readable updated-ago 1 'long))) - + (telega-ins--location-live-header live-for updated-ago)) (when (and (not (telega-me-p user)) telega-my-location) (telega-ins " " (telega-symbol 'distance)) (telega-ins @@ -1472,8 +1477,7 @@ EVENT must be \"updateDeleteMessages\"." :action #'telega-msg-goto-highlight)) (when (telega-me-p (telega-msg-sender loc-msg)) (telega-ins " ") - (telega-ins--box-button (telega-i18n "telega_stop_live_location" - :count 1) + (telega-ins--box-button (telega-i18n "telega_stop") 'action (lambda (_button) (telega--editMessageLiveLocation loc-msg nil))))) t)) diff --git a/telega-msg.el b/telega-msg.el index 2fa88c8..877ce3f 100644 --- a/telega-msg.el +++ b/telega-msg.el @@ -650,9 +650,9 @@ note finishes." (ffplay-frame (when frame (telega-vvnote-video--svg (cdr frame) - (if (telega-ffplay-paused-p proc) - (cons 'paused normalized-progress) - normalized-progress))))) + :progress (if (telega-ffplay-paused-p proc) + (cons 'paused normalized-progress) + normalized-progress))))) ;; NOTE: Update proc's `:progress' property to start from correct ;; place if [x2] button is pressed (set-process-plist proc (plist-put proc-plist :progress played)) @@ -1624,12 +1624,16 @@ If `\\[universal-argument]' is supplied, then copy without text properties." (let* ((ent (get-text-property (point) :tl-entity)) (ent-type (when ent (telega--tl-type (plist-get ent :type)))) + (telega-link (get-text-property (point) :telega-link)) (telega-inhibit-telega-display-by t) (ctext (cond ((region-active-p) (prog1 (buffer-substring (region-beginning) (region-end)) (deactivate-mark))) + ((memq (car telega-link) '(file url)) + (cdr telega-link)) + ((eq 'textEntityTypeUrl ent-type) (telega-msg--tl-entity-text msg ent)) @@ -1748,6 +1752,10 @@ Requires administrator rights in the chat." :with-username-p 'telega-username :with-brackets-p t)))) + (when-let ((ignored-by (telega-msg-match-p msg 'ignored))) + (telega-ins-describe-item "Ignored By" + (telega-ins-fmt "%S" ignored-by))) + ;; Link to the message (when-let ((link (ignore-errors ;; NOTE: we ignore any errors such as diff --git a/telega-notifications.el b/telega-notifications.el index a80b760..c9ffbc4 100644 --- a/telega-notifications.el +++ b/telega-notifications.el @@ -396,15 +396,13 @@ FORCE is used for testing only, should not be used in real code." (defun telega-notifications-history () "Show notifications history, most recent notification goes first." (interactive) - (with-help-window "*Telegram Notification Messages*" - (set-buffer standard-output) - (let ((inhibit-read-only t)) + (with-telega-help-win "*Telegram Notification Messages*" + (save-excursion (dolist (msg (ring-elements telega--notification-messages-ring)) (telega-button--insert 'telega-msg msg :inserter #'telega-ins--message-with-chat-header :action #'telega-msg-goto-highlight) - (telega-ins "\n")) - (goto-char (point-min))))) + (telega-ins "\n"))))) (provide 'telega-notifications) diff --git a/telega-sticker.el b/telega-sticker.el index 3e64e9a..e6a0685 100644 --- a/telega-sticker.el +++ b/telega-sticker.el @@ -381,11 +381,19 @@ Return path to png file." ;; :max-width (* (telega-chars-xwidth 1) ;; (cdr sticker-size)) :scale 1.0 :ascent 'center - ; :mask 'heuristic :margin (cons (cdr cwidth-xmargin) 0) :telega-text (make-string (car cwidth-xmargin) ?X) + ;; NOTE: For adaptive custom emojis use also + ;; heuristic mask, because it is predicatable + :mask (when-let ((sset (telega-stickerset-get + (plist-get sticker :set_id) + 'locally))) + (when (plist-get sset :needs_repainting) + 'heuristic)) + (when (telega-sticker-favorite-p sticker) - (list :background telega-sticker-favorite-background)))) + (list :background telega-sticker-favorite-background)) + )) (t ;; Fallback to svg (telega-sticker--progress-svg sticker))))) @@ -518,6 +526,10 @@ SSET can be either `sticker' or `stickerSetInfo'." (telega-ins-fmt "(telega-stickerset-get \"%s\")" (plist-get sset :id)))) + (when (plist-get sset :needs_repainting) + (telega-ins-describe-item "Adaptive" + (telega-ins (telega-symbol 'checkmark)))) + ;; NOTE: In case SSET is "stickerSetInfo" fetch real sticker set ;; and insert all the stickers (let ((sticker-list-ins @@ -526,7 +538,7 @@ SSET can be either `sticker' or `stickerSetInfo'." (telega-ins-describe-item (cl-ecase (telega--tl-type (plist-get sticker-set :sticker_type)) (stickerTypeMask "Masks") - (stickerTypeCustomEmoji "Custom Emoji Stickers") + (stickerTypeCustomEmoji "Custom Emojis") (stickerTypeRegular (cl-ecase (telega--tl-type (plist-get sticker-set :sticker_format)) @@ -588,21 +600,23 @@ stickers for the EMOJI." (setq telega-help-win--emoji emoji) ;; NOTE: use callbacks for async stickers loading - (telega-ins--with-face 'telega-describe-item-title - (telega-ins-fmt "Custom Emoji for %s:\n" emoji)) - (telega-ins--line-wrap-prefix " " - (telega--getStickers emoji - :chat for-chat - :tl-sticker-type '(:@type "stickerTypeCustomEmoji") - :callback (telega--gen-ins-continuation-callback 'loading - (lambda (stickers &rest args) - (apply #'telega-ins--sticker-list - (mapcar #'telega-custom-emoji-from-sticker - stickers) - args))))) + (when (or (telega-chat-match-p for-chat 'saved-messages) + (telega-user-match-p (telega-user-me) 'is-premium)) + (telega-ins--with-face 'telega-describe-item-title + (telega-ins-fmt "Custom Emoji for %s:\n" emoji)) + (telega-ins--line-wrap-prefix " " + (telega--getStickers emoji + :chat for-chat + :tl-sticker-type '(:@type "stickerTypeCustomEmoji") + :callback (telega--gen-ins-continuation-callback 'loading + (lambda (stickers &rest args) + (apply #'telega-ins--sticker-list + (mapcar #'telega-custom-emoji-from-sticker + stickers) + args))))) + (telega-ins "\n")) (unless custom-emojis-only - (telega-ins "\n") (telega-ins--with-face 'telega-describe-item-title (telega-ins "Installed Stickers:\n")) (telega-ins--line-wrap-prefix " " diff --git a/telega-tdlib.el b/telega-tdlib.el index 98cf3f8..37aa663 100644 --- a/telega-tdlib.el +++ b/telega-tdlib.el @@ -1068,7 +1068,7 @@ Pass REVOKE to try to delete chat history for all users." ;; not set `:message_thread_id', because threads does ;; not have pinned messages, and request with thread ;; id will result in error. - ;; + ;; ;; NOTE: `searchMessagesFilterUnreadReaction' can ;; only be used with topics, not with ordinary ;; threads @@ -2088,7 +2088,7 @@ REASON is one of: \"Spam\", \"Violence\", \"Pornography\", (telega-server--send (list :@type "reportChat" :chat_id (plist-get chat :id) - :reason (list :@type (concat "chatReportReason" reason)) + :reason (list :@type (concat "reportReason" reason)) :message_ids (cl-map 'vector (telega--tl-prop :id) messages) :text (or text "")))) @@ -2097,7 +2097,7 @@ REASON is one of: \"Spam\", \"Violence\", \"Pornography\", (list :@type "reportChatPhoto" :chat_id (plist-get chat :id) :file_id (plist-get photo-file :id) - :reason (list :@type (concat "chatReportReason" reason)) + :reason (list :@type (concat "reportReason" reason)) :text (or text "")))) (defun telega--removeChatActionBar (chat) @@ -2902,11 +2902,25 @@ Mode activates for :chat_id (plist-get chat :id)) callback)) -(defun telega--boostChat (chat &rest slot-ids) - (telega-server--send - (list :@type "boostChat" - :chat_id (plist-get chat :id) - :slot_ids (apply 'vector reactions)))) +(defun telega--getAvailableChatBoostSlots (&optional callback) + "Return list of available boost slots." + (with-telega-server-reply (reply) + (append (plist-get reply :slots) nil) + + '(:@type "getAvailableChatBoostSlots") + callback)) + +(cl-defun telega--boostChat (chat &key boost-slots callback) + "Boost a CHAT. +Return list of available boost slots." + (declare (indent 1)) + (with-telega-server-reply (reply) + (append (plist-get reply :slots) nil) + + (list :@type "boostChat" + :chat_id (plist-get chat :id) + :slot_ids (cl-map 'vector (telega--tl-prop :id) boost-slots)) + (or callback 'ignore))) (defun telega--getChatBoostLinkInfo (url &optional callback) (telega-server--call @@ -3004,6 +3018,20 @@ URL to open after a link of the type internalLinkTypeWebApp is clicked." :message_id (plist-get msg :id)) callback)) +(defun telega--getCloseFriends (&optional callback) + "Return all close friends of me." + (with-telega-server-reply (reply) + (mapcar #'telega-user-get (plist-get reply :user_ids)) + + (list :@type "getCloseFriends") + callback)) + +(defun telega--setCloseFriends (users) + "Changes the list of close friends of me." + (telega-server--send + (list :@type "setCloseFriends" + :user_ids (cl-map #'vector (telega--tl-prop :id) users)))) + (provide 'telega-tdlib) ;;; telega-tdlib.el ends here diff --git a/telega-util.el b/telega-util.el index ac38aa0..c884187 100644 --- a/telega-util.el +++ b/telega-util.el @@ -543,6 +543,24 @@ EMOJI-SYMBOL is the emoji symbol to be used. (Default is `telega-symbol-flames') '((scale . "80")) "Attributes to the \"feDisplacementMap\" node.") +(defun telega-svg-append-spoiler-node (svg node-id) + "Append spoiler noise node with NODE-ID into SVG." + (svg--append + svg + (dom-node 'filter + `((id . ,node-id)) + (dom-node 'feTurbulence + `((type . "turbulence") + (result . "NOISE") + ,@telega-spoiler-turbulence-attrs)) + (dom-node 'feDisplacementMap + `((in . "SourceGraphic") + (in2 . "NOISE") + (xChannelSelector . "R") + (yChannelSelector . "G") + ,@telega-spoiler-displacement-attrs)) + ))) + (defun telega-spoiler-create-svg (minithumb &optional width height limits) "Create svg image for MINITHUMB that has spoiler." (let* ((width (or width (plist-get minithumb :width))) @@ -552,21 +570,7 @@ EMOJI-SYMBOL is the emoji symbol to be used. (Default is `telega-symbol-flames') (xh (telega-chars-xheight cheight)) (xw (telega-chars-xwidth (car cwidth-xmargin))) (svg (telega-svg-create xw xh))) - (svg--append - svg - (dom-node 'filter - `((id . "noise")) - (dom-node 'feTurbulence - `((type . "turbulence") - (result . "NOISE") - ,@telega-spoiler-turbulence-attrs)) - (dom-node 'feDisplacementMap - `((in . "SourceGraphic") - (in2 . "NOISE") - (xChannelSelector . "R") - (yChannelSelector . "G") - ,@telega-spoiler-displacement-attrs)) - )) + (telega-svg-append-spoiler-node svg "noise") (svg-embed svg (base64-decode-string (plist-get minithumb :data)) "image/jpeg" t :x 0 :y 0 :width xw :height xh @@ -669,7 +673,7 @@ If AS-IS is non-nil, then do not apply time adjustment using :count (/ meters 1000))) ((>= meters 1000) (telega-i18n "lng_action_proximity_distance_km" - :count (string-to-number (format "%.1f" (/ meters 1000.0))))) + :count (telega-float-clamp (/ meters 1000.0) 1))) (t (telega-i18n "lng_action_proximity_distance_m" :count meters)))) @@ -727,6 +731,16 @@ If LONG-P is specified, then use long form." (mapconcat #'identity comps ":"))) +(defun telega-time-ago-human-readable (timestamp-ago) + (cond ((< timestamp-ago 60) + (telega-i18n "lng_mediaview_just_now")) + ((< timestamp-ago 3600) + (telega-i18n "lng_mediaview_minutes_ago" + :count (/ timestamp-ago 60))) + (t + (telega-i18n "lng_mediaview_hours_ago" + :count (telega-float-clamp (/ timestamp-ago 3600.0) 1))))) + (defun telega-link-props (link-type link-to &rest props) "Generate props for link button openable with `telega-link--button-action'." (cl-assert (memq link-type '(url file username user sender hashtag))) @@ -2702,15 +2716,17 @@ If FOR-PARAM is specified, then insert only if (telega-help-win--rm-tdlib-callback telega-server--callback-extra) (let ((inhibit-read-only t) - ;; NOTE: retain text properties of the + ;; NOTE: retain `line-prefix' and `wrap-prefix' + ;; text properties of the "Loading..." label, so + ;; new text will be inserted at the same place as ;; "Loading..." label - (text-props (when show-loading-p - (text-properties-at marker)))) + (lwprefix (when show-loading-p + (get-text-property marker 'line-prefix)))) (when show-loading-p (delete-region marker (+ marker marker-len))) (telega-save-excursion (goto-char marker) - (telega-ins--with-props text-props + (telega-ins--line-wrap-prefix lwprefix (apply insert-func insert-args))))))))))) (defun telega-translate-region (beg end &optional choose-language-p inplace-p) @@ -2798,6 +2814,11 @@ Also return nil if resulting string is empty." ("lng_menu_formatting_clear" nil)))) +(defun telega-float-clamp (number digits) + "Clamp NUMBER to the number of DIGITS after the dot." + (string-to-number + (string-trim-right (format (format "%%.%df" digits) number) "\\.0+?"))) + ;; Help functions for developers (defun telega-check-tdlib-methods () (interactive) diff --git a/telega-vvnote.el b/telega-vvnote.el index dec3e75..eba3a25 100644 --- a/telega-vvnote.el +++ b/telega-vvnote.el @@ -283,16 +283,20 @@ Each sample is in range [-128;128]." (cl-assert (cl-every (apply-partially #'>= 31) n-waves)) (telega-vvnote--waveform-encode n-waves))) -(defun telega-vvnote-video--svg (framefile &optional progress - data-p frame-img-type) +(cl-defun telega-vvnote-video--svg (framefile &key as-data-image-type + progress with-noise-p) "Generate svg image for the video note FRAMEFILE. PROGRESS is value from 0 to 1 indicating played content. PROGRESS might be nil, meaning video not is not started. PROGRESS also can be a cons cell, where car is `paused' and cdr is value from 0 to 1, meaning video note is paused at given progress. -If DATA-P is non-nil then FRAMEFILE is actually an image data. -If DATA-P is non-nil then FRAME-IMG-TYPE specifies type of the image." - (let* ((img-type (or frame-img-type (telega-image-supported-file-p framefile))) +If AS-DATA-IMAGE-TYPE is non-nil, then FRAMEFILE is actually an image +data and AS-DATA-IMAGE-TYPE specifies type of the image. +If WITH-NOISE-P is non-nil, then use noise filter above the image." + (declare (indent 1)) + (let* ((data-p (when as-data-image-type t)) + (img-type (or as-data-image-type + (telega-image-supported-file-p framefile))) (size (telega-chars-xheight (if (consp telega-video-note-height) (car telega-video-note-height) @@ -307,6 +311,8 @@ If DATA-P is non-nil then FRAME-IMG-TYPE specifies type of the image." (clip1 (telega-svg-clip-path svg "clip1")) (playing-p (numberp progress))) (svg-circle clip (/ w 2) (/ h 2) (/ size 2)) + (when with-noise-p + (telega-svg-append-spoiler-node svg "noise")) (telega-svg-embed svg (if data-p framefile (list (file-name-nondirectory framefile) @@ -314,6 +320,8 @@ If DATA-P is non-nil then FRAME-IMG-TYPE specifies type of the image." (format "image/%S" img-type) data-p :x xoff :y yoff :width size :height size + :filter (when with-noise-p + "url(#noise)") :clip-path "url(#clip)") (when progress @@ -366,18 +374,20 @@ If DATA-P is non-nil then FRAME-IMG-TYPE specifies type of the image." :ascent 'center :telega-text (make-string aw-chars ?#)))) -(defun telega-vvnote-video--create-image (note &optional _file) +(defun telega-vvnote-video--create-image (note &optional _file with-noise-p) "Create image for video NOTE frame." (let* ((thumb (plist-get note :thumbnail)) (thumb-file (telega-file--renew thumb :file)) (minithumb (plist-get note :minithumbnail))) (cond ((telega-file--downloaded-p thumb-file) (telega-vvnote-video--svg - (telega--tl-get thumb-file :local :path))) + (telega--tl-get thumb-file :local :path) + :with-noise-p with-noise-p)) (minithumb (telega-vvnote-video--svg - (base64-decode-string (plist-get minithumb :data)) - nil t 'jpeg)) + (base64-decode-string (plist-get minithumb :data)) + :as-data-image-type 'jpeg + :with-noise-p with-noise-p)) (t (let* ((cheight (if (consp telega-video-note-height) (car telega-video-note-height) @@ -385,6 +395,9 @@ If DATA-P is non-nil then FRAME-IMG-TYPE specifies type of the image." (x-size (telega-chars-xheight cheight))) (telega-media--progress-svg thumb-file x-size x-size cheight)))))) +(defun telega-vvnote-video-ttl--create-image (note &optional file) + (telega-vvnote-video--create-image note file :with-noise)) + ;; Recording notes (defvar telega-vvnote--record-progress nil @@ -465,7 +478,7 @@ Return filename with recorded voice note." ;; Display frame (telega-ins--image - (telega-vvnote-video--svg (cdr frame) nil nil 'png)))))) + (telega-vvnote-video--svg (cdr frame))))))) (defun telega-vvnote-video--record () "Record a video note. diff --git a/telega.el b/telega.el index 6f60056..26eaaea 100644 --- a/telega.el +++ b/telega.el @@ -8,8 +8,8 @@ ;; Keywords: comm ;; Package-Requires: ((emacs "27.1") (visual-fill-column "1.9") (rainbow-identifiers "0.2.2")) ;; URL: https://github.com/zevlg/telega.el -;; Version: 0.8.240 -(defconst telega-version "0.8.240") +;; Version: 0.8.241 +(defconst telega-version "0.8.241") (defconst telega-server-min-version "0.7.7") (defconst telega-tdlib-min-version "1.8.24") (defconst telega-tdlib-max-version nil)