From 00553970b7bec516c109e4fc53e48fdc24759fa4 Mon Sep 17 00:00:00 2001 From: lyuksovannyy Date: Fri, 9 Oct 2026 15:54:31 +0200 Subject: [PATCH] [no bump] Ghost Mode: AyuGram's ghost mode with per-account and per-chat settings Read receipts, story views, typing and online status are held back per the options; read on interact, scheduled and silent sending as in AyuGram. Every option resolves chat -> account -> global; forum topics follow their group. Settings live in the plugin page, a chat/profile menu popup, and a drawer row for the current account. Co-Authored-By: Claude Opus 5.5 --- README.md | 70 ++- docs/development.md | 11 +- docs/how-it-works.md | 75 +++ plugin.json | 6 +- src/drawer.py | 74 +++ src/features.py | 115 ++++ src/greeting/__init__.py | 1 - src/greeting/template.py | 13 - src/hooking.py | 56 ++ src/icons.py | 90 +++ src/main.py | 93 +-- src/menus.py | 256 ++++++++ src/network.py | 306 ++++++++++ src/options_dialog.py | 135 +++++ src/peers.py | 237 ++++++++ src/prefs.py | 186 ++++++ src/sending.py | 243 ++++++++ src/settings_page.py | 186 ++++++ src/textutil.py | 2 - tools/verify_bundle.py | 1246 +++++++++++++++++++++++++++++++++++--- 20 files changed, 3223 insertions(+), 178 deletions(-) create mode 100644 docs/how-it-works.md create mode 100644 src/drawer.py create mode 100644 src/features.py delete mode 100644 src/greeting/__init__.py delete mode 100644 src/greeting/template.py create mode 100644 src/hooking.py create mode 100644 src/icons.py create mode 100644 src/menus.py create mode 100644 src/network.py create mode 100644 src/options_dialog.py create mode 100644 src/peers.py create mode 100644 src/prefs.py create mode 100644 src/sending.py create mode 100644 src/settings_page.py delete mode 100644 src/textutil.py diff --git a/README.md b/README.md index d32f405..e2b9564 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,72 @@

- exteraGram Plugin Base icon + Ghost Mode icon

-

exteraGram Plugin Base

+

Ghost Mode

-**TL;DR:** a template repo for writing [exteraGram](https://exteragram.app) plugins as normal -multi-file Python projects. Write code in `src/`, run one command, get a single `.plugin` -file deployed to your phone or published to Releases. +**TL;DR:** AyuGram's ghost mode as an [exteraGram](https://exteragram.app) plugin: read, +browse and type without anyone seeing it. You can set every option globally, per account +and per chat. ## Description -exteraGram loads every plugin from exactly one Python file. This template lets you split a -plugin into ordinary modules and packages anyway: a bundler compiles `src/` and `plugin.json` -into that single file, an offline harness verifies the result without a device, ADB scripts -push it to a connected phone and reload it, and CI publishes every version to the repo's -Releases tab. +Ghost Mode stops the app from sending the signals that give you away: read receipts, story +views, typing statuses and your online status. It also adds AyuGram's companion options: +read on interact, scheduled sending and silent sending. Nothing is sent and nothing changes +until you turn something on. + +Every option works on three levels, and the most specific one with a value wins: + +1. **Chat**: open a chat or profile, tap ⋮ → *Ghost mode*. +2. **Account**: plugin settings → *Accounts* → pick an account. +3. **Global**: the switches on the plugin's main settings page. + +So you can stay a ghost everywhere except in one chat, or only on a second account. A forum +(a group with topics) is one chat: its settings cover every topic, and there's nothing per +topic. ## Features -None for the end user. The bundled sample plugin (`Hello Bundle`) only proves that the -toolchain works. This repo is meant to be used as a **template** for creating other plugins: -click "Use this template" (or clone it), then replace `plugin.json` and `src/` with your own. +**Ghost essentials** (the *Ghost mode* switch turns all five on or off) + +- **Don't read messages.** Read receipts aren't sent. Chats still look read on your side. +- **Don't read stories.** Story views aren't sent. +- **Don't send online.** Your online status is sent as offline. *Account level only.* +- **Don't send typing.** Typing, recording and uploading statuses aren't sent. +- **Go offline automatically.** Every online status is followed straight away by an + offline one. *Account level only.* + +**Options** + +- **Read on interact** (on by default). Sending a message to a chat where messages aren't + read marks that chat as read, as AyuGram does. +- **Use scheduled messages.** Messages and forwards are scheduled 12 seconds ahead (media + gets more time to upload). Telegram's servers then deliver them, so sending doesn't put you + online. Secret chats, bots and Saved Messages are never scheduled. +- **Send without sound.** Messages and forwards are sent silently. + +**In the app** + +- Chat and profile ⋮ menu → **Ghost mode**: this chat's options. Each one shows its value + and whether it comes from the chat, the account or the global settings. Tapping an option + cycles *Default → On → Off*. The menu also has a ghost toggle for the chat, *Read all + messages now* and a reset. +- Message menu → **Read until here**: sends a read receipt up to that message. +- Side menu → **Ghost mode**: the options of the account in use. It has the same list and + *Global → On → Off* cycling as its settings page, plus a ghost toggle and a reset. +- Plugin settings → **Accounts**: each account's options (*Global / On / Off*) and its list + of chat exceptions. ## Documentation +- [How Ghost Mode works](docs/how-it-works.md): what is intercepted, how settings resolve, + runtime notes - [Development](docs/development.md): getting started, repo layout, building and verifying, icon export - [How exteraGram plugins work](docs/how-plugins-work.md): the plugin format and lifecycle - [Device setup and ADB automation](docs/device-and-adb.md): deploy, watch, logs, debugger - [Versioning and releases](docs/versioning-and-releases.md): git hooks, auto-bump, CI publishing - [SDK notes](docs/sdk-notes.md): condensed exteraGram SDK API surface -## Quick start +## Credits -```bash -python3 tools/bundle.py --check # once after cloning: enables the git hooks -bash scripts/deploy.sh --enable # build + push to a connected device + enable -``` +Ghost mode's behaviour follows [AyuGram](https://github.com/AyuGram/AyuGram4A) by Radolyn. diff --git a/docs/development.md b/docs/development.md index 8f49826..cc61fab 100644 --- a/docs/development.md +++ b/docs/development.md @@ -17,8 +17,15 @@ plugin.json single source of truth for plugin metadata src/ the plugin, written as an ordinary Python package main.py entry module — exactly one BasePlugin subclass, absolute imports only - greeting/ sample package (proves relative + cross-module imports survive bundling) - textutil.py sample top-level sibling module + features.py the ghost mode options and their defaults + prefs.py storage and resolution: chat -> account -> global + network.py request hooks: reads, stories, typing, online status, read on interact + sending.py scheduled and silent sending, forward hook, scheduled-screen guard + menus.py chat/profile/message/drawer menu items, the chat and account dialogs + drawer.py the drawer row for drawer layouts without a Plugins entry + settings_page.py the plugin settings screen and the per-account pages + peers.py accounts, dialog ids and names + hooking.py Xposed and UI-thread helpers tools/ bundle.py compiles plugin.json + src/ into dist/.plugin devclient.py speaks the on-device dev-server protocol (127.0.0.1:42690) diff --git a/docs/how-it-works.md b/docs/how-it-works.md new file mode 100644 index 0000000..386385a --- /dev/null +++ b/docs/how-it-works.md @@ -0,0 +1,75 @@ +# How Ghost Mode works + +## What is intercepted + +Like AyuGram's hook in `ConnectionsManager.sendRequestInternal`, the plugin checks requests +on their way out (`pre_request_hook`). Each request is checked against the options for the +account it comes from and, where it has one, the chat it's about: + +| Option | Requests | What happens | +| --- | --- | --- | +| Don't read messages | `messages.readHistory`, `messages.readDiscussion`, `messages.readEncryptedHistory`, `messages.readMessageContents`, `channels.readHistory`, `channels.readMessageContents` | dropped | +| Don't read stories | `stories.readStories`, `stories.incrementStoryViews` | dropped | +| Don't send typing | `messages.setTyping`, `messages.setEncryptedTyping` | dropped | +| Don't send online | `account.updateStatus` | `offline` forced to true | +| Go offline automatically | `account.updateStatus` | an offline status is sent after each online one | +| Read on interact | `messages.sendMessage`, `sendMedia`, `sendMultiMedia`, `sendInlineBotResult`, `forwardMessages` | once the send succeeds, the chat is read up to its newest message | + +Forum topics have no settings of their own. A topic's read receipt (`messages.readDiscussion`) +and typing status carry the group as their peer, so the group's settings apply to every topic. +The plugin's own read receipts follow the app's behaviour in forums: *Read until here* and +*Read on interact* read the message's topic, and *Read all messages now* reads each loaded +topic up to its newest message. + +`messages.readMessageContents` carries only message ids. It is sent for the chat on screen, +so that chat's settings apply. The app still marks messages read locally, so chats look read +to you and unread to the sender. AyuGram behaves the same way. + +The plugin's own read receipts (*Read on interact*, *Read all messages now*, *Read until +here*) are let through by a one-shot allowance per chat that expires after 10 seconds. + +Scheduled and silent sending change the message before the app builds it: +`on_send_message_hook` handles text and media, and an Xposed hook on +`SendMessagesHelper.sendMessage(ArrayList, …)` handles forwards. Telegram sends +anything scheduled less than 10 seconds ahead immediately, so the plugin schedules 12 seconds +ahead, plus 10 seconds for photos and 15 for documents. All parts of an album share one date. +When a scheduled message lands in the chat, the app would normally jump to the scheduled +messages screen. For messages the plugin scheduled, it stays in the chat. + +## How settings resolve + +``` +chat override → account override → global switch +``` + +- Global: `g_