generated from exteraGramPlugins/pluginBase
Message Logger: deleted dates of logged photos, open them without a photo
Publish plugin / publish (push) Successful in 4s
Publish plugin / publish (push) Successful in 4s
- The photo viewer's subtitle of a logged photo the chat no longer has says when it was deleted and set. - Tapping the avatar of a chat without a photo opens the viewer on its photos, the logged ones included. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8535bde5ae
commit
5e4c2f2a6f
2 files changed
+98
-3
No files matched your search
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
"name": "Message Logger",
|
"name": "Message Logger",
|
||||||
"description": "Shows deleted and edited messages and their edit history from your telegram-panel message logger.",
|
"description": "Shows deleted and edited messages and their edit history from your telegram-panel message logger.",
|
||||||
"author": "@lyuksovannyy",
|
"author": "@lyuksovannyy",
|
||||||
"version": "0.1.5",
|
"version": "0.1.6",
|
||||||
"icon": "exteraGramPluginIcons/4",
|
"icon": "exteraGramPluginIcons/4",
|
||||||
"app_version": ">=12.5.1",
|
"app_version": ">=12.5.1",
|
||||||
"sdk_version": ">=1.4.4.3",
|
"sdk_version": ">=1.4.4.3",
|
||||||
|
|||||||
+97
-2
@@ -90,6 +90,7 @@ class Avatars:
|
|||||||
self._appending = False
|
self._appending = False
|
||||||
self._updates = {} # (account, dialog) -> args of its last dialogPhotosUpdate
|
self._updates = {} # (account, dialog) -> args of its last dialogPhotosUpdate
|
||||||
self._icon = None
|
self._icon = None
|
||||||
|
self._dates = {} # logged photo id -> (set at, deleted at), ISO strings
|
||||||
self._watched = {} # gallery hash -> its page listener (kept alive)
|
self._watched = {} # gallery hash -> its page listener (kept alive)
|
||||||
self._position = None # ProfileGalleryView.getRealPosition
|
self._position = None # ProfileGalleryView.getRealPosition
|
||||||
self._shown = None # PhotoViewer.isVisible()
|
self._shown = None # PhotoViewer.isVisible()
|
||||||
@@ -118,6 +119,21 @@ class Avatars:
|
|||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
diag.error("avatars.profile", exc)
|
diag.error("avatars.profile", exc)
|
||||||
|
|
||||||
|
class AfterSwitch(MethodHook):
|
||||||
|
def after_hooked_method(self, param):
|
||||||
|
try:
|
||||||
|
avatars.viewer_dates(param.thisObject, int(param.args[0]))
|
||||||
|
except Exception as exc:
|
||||||
|
diag.error("avatars.dates", exc)
|
||||||
|
|
||||||
|
class BeforeOpen(MethodHook):
|
||||||
|
def before_hooked_method(self, param):
|
||||||
|
try:
|
||||||
|
if avatars.open_without_photo(param.thisObject):
|
||||||
|
param.setResult(None)
|
||||||
|
except Exception as exc:
|
||||||
|
diag.error("avatars.open", exc)
|
||||||
|
|
||||||
class AfterThumbs(MethodHook):
|
class AfterThumbs(MethodHook):
|
||||||
def after_hooked_method(self, param):
|
def after_hooked_method(self, param):
|
||||||
try:
|
try:
|
||||||
@@ -135,6 +151,12 @@ class Avatars:
|
|||||||
self.handles += hook_methods(self.plugin, CONTROLLER, "getDialogPhotos", AfterPhotos())
|
self.handles += hook_methods(self.plugin, CONTROLLER, "getDialogPhotos", AfterPhotos())
|
||||||
self.handles += hook_methods(self.plugin, THUMBS, "onDraw", AfterThumbs())
|
self.handles += hook_methods(self.plugin, THUMBS, "onDraw", AfterThumbs())
|
||||||
self.handles += hook_methods(self.plugin, GALLERY, "setData", AfterSetData())
|
self.handles += hook_methods(self.plugin, GALLERY, "setData", AfterSetData())
|
||||||
|
self.handles += hook_methods(
|
||||||
|
self.plugin, VIEWER, "setIsAboutToSwitchToIndex", AfterSwitch()
|
||||||
|
)
|
||||||
|
self.handles += hook_methods(
|
||||||
|
self.plugin, "org.telegram.ui.ProfileActivity", "openAvatar", BeforeOpen()
|
||||||
|
)
|
||||||
for name in ("onDraw", "setAlphaValue"):
|
for name in ("onDraw", "setAlphaValue"):
|
||||||
self.handles += hook_methods(self.plugin, OVERLAYS, name, AfterOverlays())
|
self.handles += hook_methods(self.plugin, OVERLAYS, name, AfterOverlays())
|
||||||
self.handles += hook_methods(
|
self.handles += hook_methods(
|
||||||
@@ -193,6 +215,7 @@ class Avatars:
|
|||||||
recorded.append(
|
recorded.append(
|
||||||
(photo, int(original) if original else None, _epoch(avatar.get("date")))
|
(photo, int(original) if original else None, _epoch(avatar.get("date")))
|
||||||
)
|
)
|
||||||
|
self._dates[int(photo.id)] = (avatar.get("date"), avatar.get("deleted_at"))
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self._recorded[(account, dialog_id)] = recorded
|
self._recorded[(account, dialog_id)] = recorded
|
||||||
self._loaded_at[(account, dialog_id)] = time.monotonic()
|
self._loaded_at[(account, dialog_id)] = time.monotonic()
|
||||||
@@ -249,14 +272,14 @@ class Avatars:
|
|||||||
field(viewer, "avatarsDialogId") or 0
|
field(viewer, "avatarsDialogId") or 0
|
||||||
) == int(dialog_id)
|
) == int(dialog_id)
|
||||||
|
|
||||||
def _append(self, account, dialog_id):
|
def _append(self, account, dialog_id, force=False):
|
||||||
"""Puts the logged photos into the chat's photos and tells the
|
"""Puts the logged photos into the chat's photos and tells the
|
||||||
gallery, at most every few seconds (it reads the photos again)."""
|
gallery, at most every few seconds (it reads the photos again)."""
|
||||||
from org.telegram.messenger import MessagesController, NotificationCenter
|
from org.telegram.messenger import MessagesController, NotificationCenter
|
||||||
|
|
||||||
if not self.handles:
|
if not self.handles:
|
||||||
return # unloaded
|
return # unloaded
|
||||||
if self._viewing(dialog_id):
|
if not force and self._viewing(dialog_id):
|
||||||
on_ui(lambda: self._append(account, dialog_id), 1000) # once it closed
|
on_ui(lambda: self._append(account, dialog_id), 1000) # once it closed
|
||||||
return
|
return
|
||||||
self._appending = True # getDialogPhotos calls back into photos_wanted
|
self._appending = True # getDialogPhotos calls back into photos_wanted
|
||||||
@@ -461,6 +484,78 @@ class Avatars:
|
|||||||
root.addView(mark, params)
|
root.addView(mark, params)
|
||||||
return mark
|
return mark
|
||||||
|
|
||||||
|
# dates in the photo viewer
|
||||||
|
|
||||||
|
def viewer_dates(self, viewer, index):
|
||||||
|
"""A logged photo's subtitle: when it was deleted and set (instead of
|
||||||
|
its upload to the log group)."""
|
||||||
|
photos = field(viewer, "avatarsArr")
|
||||||
|
if photos is None or not 0 <= index < photos.size():
|
||||||
|
return
|
||||||
|
photo = photos.get(index)
|
||||||
|
dates = self._dates.get(int(photo.id)) if photo is not None else None
|
||||||
|
container = field(viewer, "actionBarContainer")
|
||||||
|
if dates is None or container is None:
|
||||||
|
return
|
||||||
|
from views.history import _when
|
||||||
|
|
||||||
|
set_at, deleted_at = dates
|
||||||
|
parts = [f"deleted {_when(deleted_at)}" if deleted_at else "deleted"]
|
||||||
|
if set_at:
|
||||||
|
parts.append(f"set {_when(set_at)}")
|
||||||
|
text = " · ".join(parts)
|
||||||
|
container.setSubtitle(text[:1].upper() + text[1:])
|
||||||
|
|
||||||
|
# a chat without a photo
|
||||||
|
|
||||||
|
def open_without_photo(self, profile):
|
||||||
|
"""openAvatar of a chat that has no photo but logged ones: the photo
|
||||||
|
viewer opens on the newest logged photo (with the rest)."""
|
||||||
|
from org.telegram.messenger import ImageLocation, MessagesController
|
||||||
|
from org.telegram.tgnet import TLRPC
|
||||||
|
from org.telegram.ui import PhotoViewer
|
||||||
|
|
||||||
|
account = int(profile.getCurrentAccount())
|
||||||
|
controller = MessagesController.getInstance(account)
|
||||||
|
user_id, chat_id = int(field(profile, "userId") or 0), int(field(profile, "chatId") or 0)
|
||||||
|
if user_id:
|
||||||
|
peer, dialog_id = controller.getUser(user_id), user_id
|
||||||
|
empty = TLRPC.TL_userProfilePhotoEmpty
|
||||||
|
else:
|
||||||
|
peer, dialog_id = controller.getChat(chat_id), -chat_id
|
||||||
|
empty = TLRPC.TL_chatPhotoEmpty
|
||||||
|
photo = getattr(peer, "photo", None) if peer is not None else None
|
||||||
|
if photo is not None and not isinstance(photo, empty):
|
||||||
|
return False # the app opens it
|
||||||
|
recorded = self._recorded.get((account, dialog_id))
|
||||||
|
if not recorded:
|
||||||
|
controller.getDialogPhotos(dialog_id) # loads the logged ones
|
||||||
|
return False
|
||||||
|
# The newest of the chat's photos (its own, then the logged ones).
|
||||||
|
holder = controller.getDialogPhotos(dialog_id)
|
||||||
|
if holder is not None:
|
||||||
|
self._add_to(account, dialog_id, holder)
|
||||||
|
listed = java_list(field(holder, "photos")) if holder is not None else []
|
||||||
|
first = next((p for p in listed if p is not None), recorded[0][0])
|
||||||
|
size = media_share.photo_size(first, 800)
|
||||||
|
if size is None:
|
||||||
|
return False
|
||||||
|
viewer = PhotoViewer.getInstance()
|
||||||
|
viewer.setParentActivity(profile)
|
||||||
|
viewer.openPhoto(
|
||||||
|
size.location, ImageLocation.getForPhoto(size, first), field(profile, "provider")
|
||||||
|
)
|
||||||
|
# The profile names no chat for a photo it doesn't show itself: the
|
||||||
|
# viewer is told, and gets the chat's photos (the logged ones).
|
||||||
|
from hook_utils import set_private_field
|
||||||
|
from java import jlong
|
||||||
|
|
||||||
|
set_private_field(viewer, "avatarsDialogId", jlong(dialog_id))
|
||||||
|
self._told.pop((account, dialog_id), None)
|
||||||
|
self._append(account, dialog_id, force=True)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def _trash(self):
|
def _trash(self):
|
||||||
if self._icon is None:
|
if self._icon is None:
|
||||||
from android.graphics import Paint, PorterDuff, PorterDuffColorFilter
|
from android.graphics import Paint, PorterDuff, PorterDuffColorFilter
|
||||||
|
|||||||
Reference in new issue
Block a user