tg-notes 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tg_notes-0.1.0 → tg_notes-0.1.2}/.claude-plugin/marketplace.json +1 -1
- {tg_notes-0.1.0 → tg_notes-0.1.2}/.claude-plugin/plugin.json +1 -1
- {tg_notes-0.1.0 → tg_notes-0.1.2}/.github/workflows/ci.yml +5 -1
- tg_notes-0.1.2/AGENTS.md +41 -0
- tg_notes-0.1.2/CHANGELOG.md +339 -0
- {tg_notes-0.1.0 → tg_notes-0.1.2}/CLAUDE.md +19 -0
- tg_notes-0.1.0/README.md → tg_notes-0.1.2/PKG-INFO +82 -0
- tg_notes-0.1.0/PKG-INFO → tg_notes-0.1.2/README.md +49 -24
- tg_notes-0.1.2/TODO.md +101 -0
- {tg_notes-0.1.0 → tg_notes-0.1.2}/docs/architecture.md +47 -5
- tg_notes-0.1.2/docs/features.md +64 -0
- tg_notes-0.1.2/docs/keepassxc.md +148 -0
- tg_notes-0.1.2/docs/sandbox-testing.md +140 -0
- {tg_notes-0.1.0 → tg_notes-0.1.2}/pyproject.toml +5 -1
- tg_notes-0.1.2/scripts/sandbox.py +315 -0
- {tg_notes-0.1.0 → tg_notes-0.1.2}/skills/tg-notes/SKILL.md +26 -0
- {tg_notes-0.1.0 → tg_notes-0.1.2}/skills/tg-notes-send/SKILL.md +4 -1
- tg_notes-0.1.2/tests/conftest.py +28 -0
- tg_notes-0.1.2/tests/test_cli.py +1048 -0
- tg_notes-0.1.2/tests/test_config.py +128 -0
- {tg_notes-0.1.0 → tg_notes-0.1.2}/tests/test_contacts.py +16 -0
- tg_notes-0.1.2/tests/test_live_media.py +102 -0
- tg_notes-0.1.2/tests/test_live_transcribe.py +45 -0
- tg_notes-0.1.2/tests/test_mcp.py +233 -0
- tg_notes-0.1.2/tests/test_media.py +503 -0
- {tg_notes-0.1.0 → tg_notes-0.1.2}/tests/test_notes.py +9 -3
- tg_notes-0.1.2/tests/test_relaunch.py +245 -0
- tg_notes-0.1.2/tests/test_sandbox_script.py +205 -0
- tg_notes-0.1.2/tests/test_secrets.py +446 -0
- tg_notes-0.1.2/tests/test_transcribe.py +404 -0
- {tg_notes-0.1.0 → tg_notes-0.1.2}/tg_notes/__init__.py +1 -1
- tg_notes-0.1.2/tg_notes/cli.py +886 -0
- {tg_notes-0.1.0 → tg_notes-0.1.2}/tg_notes/config.py +29 -1
- tg_notes-0.1.2/tg_notes/mcp_server.py +149 -0
- tg_notes-0.1.2/tg_notes/relaunch.py +85 -0
- tg_notes-0.1.2/tg_notes/secrets.py +347 -0
- {tg_notes-0.1.0 → tg_notes-0.1.2}/tg_notes/telegram.py +108 -30
- tg_notes-0.1.2/tg_notes/transcribe.py +278 -0
- tg_notes-0.1.0/AGENTS.md +0 -20
- tg_notes-0.1.0/CHANGELOG.md +0 -133
- tg_notes-0.1.0/TODO.md +0 -75
- tg_notes-0.1.0/docs/features.md +0 -36
- tg_notes-0.1.0/tests/test_cli.py +0 -533
- tg_notes-0.1.0/tests/test_config.py +0 -67
- tg_notes-0.1.0/tg_notes/cli.py +0 -391
- {tg_notes-0.1.0 → tg_notes-0.1.2}/.github/workflows/release.yml +0 -0
- {tg_notes-0.1.0 → tg_notes-0.1.2}/.gitignore +0 -0
- {tg_notes-0.1.0 → tg_notes-0.1.2}/LICENSE +0 -0
- {tg_notes-0.1.0 → tg_notes-0.1.2}/tests/__init__.py +0 -0
- {tg_notes-0.1.0 → tg_notes-0.1.2}/tests/test_notebooks.py +0 -0
- {tg_notes-0.1.0 → tg_notes-0.1.2}/tests/test_send.py +0 -0
- {tg_notes-0.1.0 → tg_notes-0.1.2}/tests/test_setup.py +0 -0
- {tg_notes-0.1.0 → tg_notes-0.1.2}/tests/test_telegram.py +0 -0
- {tg_notes-0.1.0 → tg_notes-0.1.2}/tg_notes/contacts.py +0 -0
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"name": "tg-notes",
|
|
10
10
|
"source": "./",
|
|
11
11
|
"description": "Notes → compile → publish via a private Telegram group, under your own account.",
|
|
12
|
-
"version": "0.1.
|
|
12
|
+
"version": "0.1.2",
|
|
13
13
|
"homepage": "https://github.com/korkin25/tg_notes",
|
|
14
14
|
"license": "GPL-3.0-or-later",
|
|
15
15
|
"keywords": ["telegram", "notes", "userbot", "reports", "cli"]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tg-notes",
|
|
3
3
|
"description": "Notes → compile → publish via a private Telegram group, posting under your own account. Bundles the tg-notes capture and compile-&-send skills, which drive the tg-notes CLI (install separately: pipx install tg-notes).",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.2",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "korkin25",
|
|
7
7
|
"url": "https://github.com/korkin25"
|
|
@@ -17,7 +17,11 @@ jobs:
|
|
|
17
17
|
with:
|
|
18
18
|
python-version: ${{ matrix.python-version }}
|
|
19
19
|
- name: Install (with dev extras)
|
|
20
|
-
|
|
20
|
+
# Upgrade pip/setuptools first so pip-audit does not flag the runner's own
|
|
21
|
+
# (incidental) build tooling — setuptools is not a runtime dependency here.
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install --upgrade pip setuptools
|
|
24
|
+
pip install -e .[dev]
|
|
21
25
|
- name: Lint (ruff)
|
|
22
26
|
run: ruff check .
|
|
23
27
|
- name: Test (pytest)
|
tg_notes-0.1.2/AGENTS.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
This file is the standard entry point that AI coding agents look for.
|
|
4
|
+
|
|
5
|
+
**The canonical rules for this repository live in [CLAUDE.md](CLAUDE.md).** Read it
|
|
6
|
+
first: it defines the language rules, the documentation-sync policy, the security
|
|
7
|
+
conventions, and the autonomous development workflow (TDD, feature branches,
|
|
8
|
+
Conventional Commits, CI, releases). Everything in `CLAUDE.md` applies to every
|
|
9
|
+
agent working here, not only to Claude Code.
|
|
10
|
+
|
|
11
|
+
## Cross-agent portability
|
|
12
|
+
|
|
13
|
+
This project follows the **Agent Skills** standard (<https://agentskills.io>) and its
|
|
14
|
+
`SKILL.md` format. Skills are authored once as plain `SKILL.md` files (standard core
|
|
15
|
+
frontmatter: `name`, `description`) so the same skill is read unchanged by any
|
|
16
|
+
Agent-Skills-compatible runtime — Claude Code, OpenCode, Hermes, and others — with no
|
|
17
|
+
per-agent rewrite. Only *distribution* differs per agent; the skill content stays portable.
|
|
18
|
+
|
|
19
|
+
### What ships
|
|
20
|
+
|
|
21
|
+
- **CLI — `tg-notes`** (on PyPI: `pipx install tg-notes`). Does all Telegram I/O; agent-
|
|
22
|
+
neutral. Every skill below just shells out to it, so it must be on `PATH`.
|
|
23
|
+
- **Two skills** (`skills/`), portable as-is:
|
|
24
|
+
- `tg-notes` — capture a note (`tg-notes note add`).
|
|
25
|
+
- `tg-notes-send` — compile stored notes per a contact's style and send them
|
|
26
|
+
(`tg-notes send`), with a mandatory confirmation; daily-report preset included.
|
|
27
|
+
|
|
28
|
+
### Per-agent distribution (skill content is identical everywhere)
|
|
29
|
+
|
|
30
|
+
- **Claude Code** — a plugin from the git marketplace in this repo
|
|
31
|
+
(`.claude-plugin/`): `/plugin marketplace add korkin25/tg_notes` then
|
|
32
|
+
`/plugin install tg-notes@tg-notes-marketplace`.
|
|
33
|
+
- **OpenCode / OpenClaw** — discover `~/.claude/skills/*/SKILL.md` and `.claude/skills/*`
|
|
34
|
+
natively; drop or symlink the two `skills/` dirs there.
|
|
35
|
+
- **Hermes** (Nous Research) — agentskills.io-compatible but keeps skills in its own
|
|
36
|
+
`~/.hermes/` store; import the same `SKILL.md` files unchanged (and it can call the
|
|
37
|
+
`tg-notes` CLI via its terminal toolset / MCP).
|
|
38
|
+
- Keep the frontmatter to the standard core (`name`, `description`); avoid Claude-only
|
|
39
|
+
fields so the files stay runtime-agnostic.
|
|
40
|
+
|
|
41
|
+
For anything an agent needs to know or do in this repo, defer to `CLAUDE.md`.
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
6
|
+
this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.2] - 2026-07-25
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Audio transcription now **auto-fetches the whisper engine on first use**. When a
|
|
15
|
+
transcription is needed and no engine is present, `transcribe.ensure_engine(cfg)` installs
|
|
16
|
+
`faster-whisper` once per process — best-effort: `pipx inject tg-notes faster-whisper`
|
|
17
|
+
inside a pipx-managed venv (detected via `"pipx"` in `sys.prefix` + a `pipx` on `PATH`),
|
|
18
|
+
otherwise `<python> -m pip install faster-whisper` — then re-checks and proceeds; the model
|
|
19
|
+
itself still downloads on first `WhisperModel` use. It is **best-effort and never aborts the
|
|
20
|
+
upload**: a failed or non-zero install logs a one-line stderr hint (`install it manually:
|
|
21
|
+
pipx inject tg-notes faster-whisper`) and the file uploads with no caption, exactly as when
|
|
22
|
+
no engine is available. A module-level guard attempts the install at most once per process.
|
|
23
|
+
New non-secret config key `transcriber_autoinstall` (absent/`None` ⇒ enabled; set
|
|
24
|
+
`transcriber_autoinstall = false` to disable — e.g. to always pre-install the engine
|
|
25
|
+
yourself). No CLI/MCP surface change — the auto-fetch happens transparently inside the
|
|
26
|
+
existing `transcribe()` path. Fully-mocked tests in `tests/test_transcribe.py` (existing
|
|
27
|
+
engine short-circuit, disabled, pipx-inject vs pip-install argv, non-zero/raising install,
|
|
28
|
+
the one-time guard) and a config round-trip in `tests/test_config.py`.
|
|
29
|
+
- Sandbox testing helper + mandatory-sandbox rule: new `scripts/sandbox.py` automates a
|
|
30
|
+
THROWAWAY, fully isolated tg-notes install for all live/integration testing so the real
|
|
31
|
+
`~/.config/tg-notes`, the real keyring, and the real storage group are never touched. It
|
|
32
|
+
seeds a sandbox config dir (`$TG_NOTES_SANDBOX_DIR` or `~/.config/tg-notes-sandbox`) from
|
|
33
|
+
the real credentials (real `api_id` from config, real `api_hash` + session from the keyring,
|
|
34
|
+
read with overrides unset), uses the **file** secrets backend so `tg-notes setup` runs
|
|
35
|
+
non-interactively, and creates a **dedicated test group** (a fresh `-100…` id, never the
|
|
36
|
+
real one). Subcommands: `setup` (idempotent seed + prints the `export TG_NOTES_CONFIG_DIR`
|
|
37
|
+
line), `run -- <cmd>` (exec a command against the sandbox), `pytest -- <args>` (run the
|
|
38
|
+
gated live tests with `TG_NOTES_LIVE=1`), and `reset` (delete the sandbox). The sandbox
|
|
39
|
+
session file is a full-access credential (`chmod 600`, outside the repo, never committed).
|
|
40
|
+
CLAUDE.md now makes sandbox isolation **mandatory** for any test touching real
|
|
41
|
+
config/keyring/Telegram, and `docs/sandbox-testing.md` documents the helper alongside the
|
|
42
|
+
existing env-var protocol.
|
|
43
|
+
|
|
44
|
+
## [0.1.1] - 2026-07-25
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
|
|
48
|
+
- Media notes — Phase 3 (TGN-21): media capture is now exposed on the agent surfaces. The
|
|
49
|
+
local MCP server gains a `note_add_file(file, notebook="daily", caption=None,
|
|
50
|
+
transcribe=True, hashtags=None)` tool that uploads a **local file** as a note (native
|
|
51
|
+
Telegram media, kind auto-detected) and returns the same dict as `telegram.note_add_file`.
|
|
52
|
+
It mirrors the CLI handler's best-effort audio transcription: for an audio file with no
|
|
53
|
+
`caption` and an available local engine it transcribes to the caption (offloaded via
|
|
54
|
+
`asyncio.to_thread`), and on `TranscriptionUnavailable`/`TranscriptionError` it logs to
|
|
55
|
+
stderr and uploads with no caption — transcription never aborts the upload; a missing file
|
|
56
|
+
raises a clear error before any core call. An agent may pass its own `caption` (e.g. an
|
|
57
|
+
image description) to skip transcription, or `transcribe=False`. The `notes_list` tool
|
|
58
|
+
already surfaces each note's `media` key verbatim. The `tg-notes` capture skill documents
|
|
59
|
+
media capture (`note add --file`, audio auto-transcription, agent-supplied captions), and
|
|
60
|
+
`tg-notes-send` notes that media notes compile from their caption (`media` + caption-as-`text`).
|
|
61
|
+
- Media notes — Phase 2 (TGN-20): audio note files **auto-transcribe to the caption**. When
|
|
62
|
+
`note add --file <path>` is an audio file (`.ogg`/`.oga`/`.opus`/`.mp3`/`.m4a`/`.wav`/
|
|
63
|
+
`.flac`/`.aac`/`.webm`) and no `--caption` is given, tg-notes transcribes it **locally**
|
|
64
|
+
and uses the transcript as the caption — so a dictated voice note becomes searchable text
|
|
65
|
+
with no manual step. New `tg_notes/transcribe.py` provides a **pluggable** local
|
|
66
|
+
transcriber detected on demand in order: a configured whisper CLI (`whisper_cmd`), a
|
|
67
|
+
whisper CLI on `PATH` (`whisper-cli` / `whisper` / `main` — whisper.cpp or openai-whisper),
|
|
68
|
+
then the `faster-whisper` package (imported lazily). It is **best-effort**: if no engine is
|
|
69
|
+
installed (`TranscriptionUnavailable`) or transcription fails (`TranscriptionError`) the
|
|
70
|
+
file **still uploads** without a caption and a one-line stderr hint/warning is printed —
|
|
71
|
+
transcription never aborts the upload. `telegram.py` stays pure Telegram I/O; the CLI
|
|
72
|
+
handler does the transcription and passes the text into the existing `note_add_file` as the
|
|
73
|
+
caption. New `--transcribe`/`--no-transcribe` flag on `note add` (default = auto: only for
|
|
74
|
+
an audio file with no `--caption` and an available engine); a non-audio file, an explicit
|
|
75
|
+
`--caption`, or `--no-transcribe` skips it. New non-secret config keys `transcriber`,
|
|
76
|
+
`whisper_cmd`, `whisper_model`, and a `tg-notes[transcribe]` extra
|
|
77
|
+
(`pipx inject tg-notes faster-whisper`). The whisper engines need `ffmpeg` to decode audio.
|
|
78
|
+
Fully-mocked tests in `tests/test_transcribe.py` (backends, detection order, CLI arg
|
|
79
|
+
templates, error paths) and `tests/test_media.py` (CLI wiring); a gated live test in
|
|
80
|
+
`tests/test_live_transcribe.py` (skipped unless `TG_NOTES_LIVE=1` and an engine is present).
|
|
81
|
+
- Media notes — Phase 1 (TGN-19): `tg-notes note add --file <path> [--caption <text>]`
|
|
82
|
+
uploads a photo, video, audio, or document into the notebook topic as **native Telegram
|
|
83
|
+
media** (Telethon auto-detects the kind), creating the topic on demand. The `--caption`
|
|
84
|
+
(plus any repeatable `--hashtag`) becomes the message caption — the note's searchable
|
|
85
|
+
text; a missing file is rejected with a clear error before any network call. New
|
|
86
|
+
`telegram.note_add_file` returns `{notebook, topic_id, message_id, date, media_type,
|
|
87
|
+
caption}` (`media_type` ∈ `photo`/`voice`/`audio`/`video`/`gif`/`document`). `notes_list`
|
|
88
|
+
is now additive: every note dict carries a `media` key (the type, or `null` for text) and
|
|
89
|
+
for a media note `text` is its caption, so text and media notes compile through the same
|
|
90
|
+
path. `note add`'s `--text-file` is now optional (use it or `--file`). Fully-mocked tests
|
|
91
|
+
in `tests/test_media.py`; a gated live round-trip in `tests/test_live_media.py` (skipped
|
|
92
|
+
unless `TG_NOTES_LIVE=1`). Captions are passed explicitly for now — Phase 2 will auto-fill
|
|
93
|
+
an audio note's caption from its transcription.
|
|
94
|
+
- On Linux with the keyring backend, the CLI re-execs through a named launcher
|
|
95
|
+
(`<venv>/libexec/tg-notes`, a copy of the interpreter with the venv's site-packages
|
|
96
|
+
re-added via `site.addsitedir` under `-S`) so the vault confirmation prompt
|
|
97
|
+
(KeePassXC / Secret Service) identifies the app as `tg-notes` instead of `python3.12`.
|
|
98
|
+
The launcher is created once and reused; best-effort — a read-only venv (or any
|
|
99
|
+
copy/exec failure) silently skips it and the CLI keeps working (the prompt then shows
|
|
100
|
+
`python`). `TG_NOTES_RELAUNCHED` is an internal loop guard. No-op on non-Linux, on the
|
|
101
|
+
file backend, or in frozen builds.
|
|
102
|
+
- `docs/sandbox-testing.md` — a protocol for isolated end-to-end testing of
|
|
103
|
+
`setup`/`secrets doctor`/`secrets migrate`/pickers via `TG_NOTES_CONFIG_DIR` +
|
|
104
|
+
`TG_NOTES_KEYRING_SERVICE`, so a throwaway install never touches the real
|
|
105
|
+
config/session/vault (with before/after invariants on the real config + `tg-notes`
|
|
106
|
+
keyring entries).
|
|
107
|
+
- `TG_NOTES_CONFIG_DIR` and `TG_NOTES_KEYRING_SERVICE` env vars for an isolated sandbox
|
|
108
|
+
install: `TG_NOTES_CONFIG_DIR` overrides the exact config directory (its own `config.toml`
|
|
109
|
+
+ `*.session`, taking precedence over XDG), and `TG_NOTES_KEYRING_SERVICE` overrides the
|
|
110
|
+
keyring service namespace so a sandbox `secrets migrate` never overwrites the real
|
|
111
|
+
`tg-notes` vault entries — safe end-to-end testing of `setup`/`secrets doctor`/`migrate`.
|
|
112
|
+
With neither set, behavior is unchanged. `secrets doctor` and `secrets status` now show
|
|
113
|
+
the active config dir and keyring service.
|
|
114
|
+
- Interactive pickers for `send --contact`, `contacts remove`, `secrets migrate --to`, and
|
|
115
|
+
`notes list --notebook` — omit the value in an interactive terminal to choose from a list
|
|
116
|
+
via a fuzzy finder (`fzf`/`sk`/`fzy`) when one is installed, else a numbered menu. The
|
|
117
|
+
picker fires only when both stdin and stdout are TTYs *and* the value was not passed on the
|
|
118
|
+
command line, so scripted/agent use with flags is byte-for-byte unchanged; a non-interactive
|
|
119
|
+
run without the value fails fast with a clear message (exit 2). `notes list --notebook` still
|
|
120
|
+
falls back to `daily` when non-interactive or when the pick is cancelled.
|
|
121
|
+
- `tg-notes secrets doctor` — diagnoses the secret store (active backend, a classified
|
|
122
|
+
vault round-trip via `keyring_probe`, the process owning the Secret Service bus, and the
|
|
123
|
+
detected stores) and prints ordered, actionable recommendations: install the
|
|
124
|
+
`tg-notes[keyring]` extra, expose a KeePassXC group, turn off KeePassXC per-access
|
|
125
|
+
confirmation, hand the Secret Service bus from gnome-keyring over to KeePassXC, or migrate
|
|
126
|
+
when the vault is ready (`--json` for machine-readable output). `secrets migrate --to
|
|
127
|
+
keyring` now runs the same pre-flight and prints those recommendations instead of a
|
|
128
|
+
generic error when the vault isn't ready, and `setup` ends with a tip pointing at
|
|
129
|
+
`secrets doctor`.
|
|
130
|
+
- `docs/keepassxc.md` — a guide to using KeePassXC as tg-notes' secret store: handing
|
|
131
|
+
`org.freedesktop.secrets` over from gnome-keyring (three reversible user-level changes
|
|
132
|
+
+ revert), exposing a dedicated minimal group, the per-connection confirmation model
|
|
133
|
+
(why a short-lived CLI re-prompts / locks under `ConfirmAccessItem`, keepassxc#6458),
|
|
134
|
+
the resulting security model, and the `secrets doctor`/`migrate` workflow.
|
|
135
|
+
- `tg-notes secrets status` now lists the detected secret stores (`available_stores`),
|
|
136
|
+
each annotated with whether it serves the Secret Service or is merely running —
|
|
137
|
+
gnome-keyring, KeePassXC, KWallet/ksecretd — so it's clear which vault the keyring
|
|
138
|
+
backend would actually use and what you could switch to.
|
|
139
|
+
- Pluggable secrets backend (TGN-18): `tg_notes/secrets.py` abstracts where the two real
|
|
140
|
+
secrets — `api_hash` and the Telethon session — live. **file** (default, unchanged): in
|
|
141
|
+
`config.toml` (600) + a `*.session` file. **keyring** (opt-in, `tg-notes[keyring]`): in
|
|
142
|
+
the OS Secret Service via the `keyring` library (gnome-keyring / KWallet / KeePassXC —
|
|
143
|
+
whichever owns `org.freedesktop.secrets`), the session stored as a Telethon
|
|
144
|
+
`StringSession`; provider-agnostic, so enabling KeePassXC's Secret Service integration
|
|
145
|
+
routes secrets there. `api_id`/`storage_group_id` stay in config (not secret). New CLI:
|
|
146
|
+
`tg-notes secrets status` (active backend, whether configured/has-session, keyring
|
|
147
|
+
availability, and which process owns the Secret Service bus) and `tg-notes secrets
|
|
148
|
+
migrate --to file|keyring` (moves both secrets; keyring migration verifies the vault
|
|
149
|
+
round-trip before removing the on-disk session). `build_client`/`login` in `telegram.py`
|
|
150
|
+
now resolve credentials + session through the active backend. Verified end-to-end on the
|
|
151
|
+
real account (isolated session copy: file → keyring → `whoami` from the vault → back to
|
|
152
|
+
file), the file default untouched. Tests in `tests/test_secrets.py`.
|
|
153
|
+
- Local MCP server (TGN-17): `tg-notes-mcp` — a stdio MCP server (`mcp` / FastMCP) that
|
|
154
|
+
exposes the core as tools `note_add` / `notes_list` / `contacts_list` / `send`, so agent
|
|
155
|
+
hosts that can't shell out (Claude Desktop, …) can drive tg-notes. Same local core; the
|
|
156
|
+
Telethon session/secrets stay on the machine (stdio only, nothing hosted). Tools are
|
|
157
|
+
async and offload the blocking Telethon calls to a worker thread (the sync core can't run
|
|
158
|
+
inside the server's event loop). Ships behind an optional extra:
|
|
159
|
+
`pipx install "tg-notes[mcp]"`; new `tg_notes/mcp_server.py`, tests in `tests/test_mcp.py`.
|
|
160
|
+
Verified end-to-end against the real account (all four tools via the server).
|
|
161
|
+
|
|
162
|
+
### Changed
|
|
163
|
+
|
|
164
|
+
- The keyring backend now reads/writes the vault via `secretstorage` with an explicit
|
|
165
|
+
unlock-and-wait (plus a small re-request retry) instead of the plain `keyring` API, so a
|
|
166
|
+
per-access-confirmation vault (KeePassXC) makes tg-notes **wait for the confirmation
|
|
167
|
+
prompt** — like every other app — rather than failing outright with "locked". Locked
|
|
168
|
+
collections/items are `unlock()`ed (which blocks until the user answers), retrying if the
|
|
169
|
+
prompt is dismissed. Existing `keyring`-written entries remain compatible (looked up by
|
|
170
|
+
the `{service, username}` attributes); where `secretstorage` is unavailable (non-Linux)
|
|
171
|
+
it falls back to the plain `keyring` API. The `tg-notes[keyring]` extra now also pulls
|
|
172
|
+
`secretstorage` on Linux.
|
|
173
|
+
- `secrets doctor`'s "locked" recommendation now explains the KeePassXC
|
|
174
|
+
per-connection-grant limitation (each grant is bound to a short-lived D-Bus connection,
|
|
175
|
+
so a fresh CLI run re-prompts and via keyring usually fails; persistent per-app
|
|
176
|
+
authorization is unimplemented, keepassxc#6458) and steers to a dedicated exposed group
|
|
177
|
+
holding only tg-notes' secrets plus confirmation OFF — the same trust boundary as the
|
|
178
|
+
on-disk session file. Points at `docs/keepassxc.md`.
|
|
179
|
+
- Expanded `AGENTS.md` (TGN-16) with the concrete shipped pieces (the `tg-notes` CLI on
|
|
180
|
+
PyPI + the `tg-notes` / `tg-notes-send` skills) and per-agent distribution notes (Claude
|
|
181
|
+
Code plugin marketplace, OpenCode/OpenClaw `~/.claude/skills` discovery, Hermes import).
|
|
182
|
+
Cross-agent portability confirmed (TGN-D3): the same `SKILL.md` files are read unchanged
|
|
183
|
+
by every Agent-Skills runtime; only distribution differs.
|
|
184
|
+
|
|
185
|
+
### Fixed
|
|
186
|
+
|
|
187
|
+
- The keyring backend now self-heals `DBUS_SESSION_BUS_ADDRESS` when it is unset. A process
|
|
188
|
+
spawned with a sanitized environment (an MCP host, a cron job) has no
|
|
189
|
+
`DBUS_SESSION_BUS_ADDRESS`, so `secretstorage.dbus_init()` failed with "Environment variable
|
|
190
|
+
DBUS_SESSION_BUS_ADDRESS is unset" and the Secret Service was unreachable. `_ss_collection`
|
|
191
|
+
now calls `_ensure_dbus_env()` before `dbus_init()`: if the variable is unset and the
|
|
192
|
+
standard per-user bus socket (`/run/user/<uid>/bus`) exists, it points the variable there.
|
|
193
|
+
Best-effort — it never raises, and an already-set address always wins.
|
|
194
|
+
- The gated live media test's photo fixture used an invalid 1x1 PNG that Telegram's
|
|
195
|
+
server-side image pipeline rejected with `ImageProcessFailedError`, so
|
|
196
|
+
`TG_NOTES_LIVE=1 pytest tests/test_live_media.py` failed on the photo round-trip. It now
|
|
197
|
+
generates a valid 160x160 8-bit truecolor PNG in-process (signature + IHDR + a
|
|
198
|
+
zlib-compressed IDAT with per-row filter bytes + IEND, all via `struct`/`zlib`, no PIL),
|
|
199
|
+
which round-trips cleanly.
|
|
200
|
+
- The keyring backend now reuses a single `secretstorage` D-Bus connection per process
|
|
201
|
+
(`_ss_collection` lazily opens it once and caches it) instead of opening a new one on every
|
|
202
|
+
vault read. A single command reads the vault several times (`api_hash`, session,
|
|
203
|
+
`has_session`), and KeePassXC binds each per-access-confirmation grant to the requesting
|
|
204
|
+
D-Bus connection — so this makes each command trigger **at most one** KeePassXC confirmation
|
|
205
|
+
prompt instead of one per read.
|
|
206
|
+
- `secrets migrate --to keyring` no longer overwrites the vault with an empty
|
|
207
|
+
(unauthorized) session: `migrate_to_keyring` aborts if the exported session string is
|
|
208
|
+
empty, so a not-logged-in run can't clobber a good vault entry.
|
|
209
|
+
- `contacts set` no longer errors when re-setting a contact to unchanged values: the
|
|
210
|
+
underlying `edit_message` `MessageNotModifiedError` is caught and treated as a successful
|
|
211
|
+
no-op (found while exercising the MCP server).
|
|
212
|
+
- CI: upgrade `pip`/`setuptools` before `pip-audit` so it no longer flags the runner's
|
|
213
|
+
incidental build tooling (`setuptools` PYSEC-2026-3447, absent on 3.12 but present on
|
|
214
|
+
3.11), which was failing the pipeline. `tg-notes` does not depend on `setuptools`.
|
|
215
|
+
|
|
216
|
+
## [0.1.0] - 2026-07-24
|
|
217
|
+
|
|
218
|
+
### Added
|
|
219
|
+
|
|
220
|
+
- Project documentation and rules: `README.md`, `CLAUDE.md` (language and doc-sync
|
|
221
|
+
rules), `docs/architecture.md`, `docs/features.md`.
|
|
222
|
+
- Implementation plan in `TODO.md` (planning phase; no code yet).
|
|
223
|
+
- `GPL-3.0` license (`LICENSE`).
|
|
224
|
+
- Project scaffolding (TGN-1): `pyproject.toml` (CLI `tg-notes`, Telethon dependency,
|
|
225
|
+
editable-installable), `tg_notes` package, argparse command tree (`setup`, `note add`,
|
|
226
|
+
`notes list`, `contacts list/set/remove`, `send`, `notebooks list` — stubs so far),
|
|
227
|
+
local TOML config module (XDG path, `chmod 600`), and `.gitignore` for secrets and
|
|
228
|
+
`*.session`.
|
|
229
|
+
- Autonomous development-workflow rules in `CLAUDE.md` (TDD-first, feature branches,
|
|
230
|
+
Conventional Commits, tag-based SemVer releases, CI expectations, security bar),
|
|
231
|
+
replacing the previous minimal `Git` section.
|
|
232
|
+
- GitHub Actions workflows: CI (`.github/workflows/ci.yml`) running ruff, pytest
|
|
233
|
+
(Python 3.11 and 3.12), bandit, and pip-audit on every push and pull request; and
|
|
234
|
+
release (`.github/workflows/release.yml`) building and publishing to PyPI on `v*` tags.
|
|
235
|
+
- `dev` optional-dependency group in `pyproject.toml` (`pytest`, `pytest-mock`, `ruff`,
|
|
236
|
+
`bandit`, `pip-audit`, `build`).
|
|
237
|
+
- `AGENTS.md` pointing agents to `CLAUDE.md` as the canonical rules and documenting the
|
|
238
|
+
Agent Skills (`SKILL.md`) portability standard.
|
|
239
|
+
- Telegram client layer (TGN-2): `tg_notes/telegram.py` — a synchronous Telethon wrapper
|
|
240
|
+
(`import telethon.sync`) with `build_client` / `connect_authorized` / `whoami` / `login`
|
|
241
|
+
and typed `NotConfiguredError` / `NotAuthorizedError`. Sessions are locked to `chmod 600`
|
|
242
|
+
after login.
|
|
243
|
+
- CLI commands `login` (one-time interactive phone/code/2FA login, stores the session) and
|
|
244
|
+
`whoami` (prints the logged-in account identity as JSON); both report missing credentials
|
|
245
|
+
or an unauthorized session with a clear message and a nonzero exit.
|
|
246
|
+
- Test suite under `tests/` (pytest + pytest-mock): config round-trip / file mode / session
|
|
247
|
+
path, the Telegram layer with Telethon fully mocked (offline), and the CLI argument
|
|
248
|
+
surface. Pytest and ruff config added to `pyproject.toml`.
|
|
249
|
+
- Storage provisioning (TGN-3): `tg-notes setup` creates (or idempotently attaches to) the
|
|
250
|
+
private forum supergroup that stores notes. It ensures the `contacts` topic and a default
|
|
251
|
+
`daily` notebook topic (override with `--notebook`), pins a recovery marker in the group
|
|
252
|
+
so a lost store can be re-discovered, and persists the resolved group id to local config.
|
|
253
|
+
New `telegram.setup` plus helpers (`_resolve_or_create` / `_create_storage_group` /
|
|
254
|
+
`_ensure_topics` / `_pin_marker`) built on Telethon's forum raw API
|
|
255
|
+
(`CreateChannelRequest(megagroup=True, forum=True)`, `CreateForumTopicRequest`,
|
|
256
|
+
`GetForumTopicsRequest`); tests in `tests/test_setup.py` with Telethon fully mocked.
|
|
257
|
+
`setup` drives first-run onboarding itself: when `api_id`/`api_hash` are missing it
|
|
258
|
+
prompts for them and saves them to local config (mode 600); when the device is not
|
|
259
|
+
logged in it runs the interactive `login` (phone → code → 2FA) and retries the
|
|
260
|
+
provisioning. If the prompt is left blank (or the values are unusable) it falls back to
|
|
261
|
+
printing step-by-step manual guidance (my.telegram.org → config path → `chmod 600` →
|
|
262
|
+
`login` → re-run) and exits nonzero.
|
|
263
|
+
- Note capture (TGN-4): `tg-notes note add --notebook <nb> --text-file <f>` appends a note
|
|
264
|
+
to the notebook's forum topic, creating the topic on demand. Reads the note text from a
|
|
265
|
+
file or stdin (`--text-file -`), appends optional `--hashtag TAG` tokens (repeatable) on
|
|
266
|
+
a trailing line, refuses to post an empty note, and prints the posted note as JSON
|
|
267
|
+
(`notebook` / `topic_id` / `message_id` / `date`). New `telegram.note_add` (+ helpers
|
|
268
|
+
`_compose_note` / `_normalize_hashtag`) and a `NotSetUpError` that tells the user to run
|
|
269
|
+
`setup` first; tests in `tests/test_notes.py` with Telethon fully mocked.
|
|
270
|
+
- Note listing (TGN-5): `tg-notes notes list --notebook <nb> [--since <t>]` returns a
|
|
271
|
+
notebook's raw notes as a JSON array (oldest first), each `{message_id, date, text}`,
|
|
272
|
+
skipping the topic-opening service message. `--since` accepts `today`, `HH:MM`,
|
|
273
|
+
`YYYY-MM-DD`, or a full ISO datetime (interpreted as local time when it carries no
|
|
274
|
+
offset) and bounds the messages by date; an unknown notebook yields `[]`. New
|
|
275
|
+
`telegram.notes_list` and a CLI `_parse_since` parser; tests in `tests/test_notes.py`
|
|
276
|
+
and `tests/test_cli.py` (Telethon mocked).
|
|
277
|
+
- Contacts address book (TGN-6): `tg-notes contacts list|set|remove` over the
|
|
278
|
+
message-per-contact schema in the `contacts` topic. New pure `tg_notes.contacts` module
|
|
279
|
+
((de)serializes the `#contact <key>` block; single-line fields, colons preserved) plus
|
|
280
|
+
`telegram.contacts_list` / `contacts_set` / `contacts_remove`. `set` creates or updates
|
|
281
|
+
in place (only the given fields override an existing record; a new contact requires
|
|
282
|
+
`--chat-id`; edits reuse the same message), `remove` deletes the contact's message
|
|
283
|
+
(missing key is a no-op), `list` returns all contacts as JSON sorted by key. Tests in
|
|
284
|
+
`tests/test_contacts.py` (pure + Telethon-mocked layer). Refactor: shared
|
|
285
|
+
`_resolve_store` helper (in `tg_notes.telegram`) and `_handle_store_errors` /
|
|
286
|
+
`_STORE_ERRORS` (in the CLI) deduplicate the not-set-up / not-configured / not-authorized
|
|
287
|
+
handling across `note add`, `notes list`, and `contacts`.
|
|
288
|
+
- Publish (TGN-7): `tg-notes send --contact <key> --text-file <f>` posts compiled text to a
|
|
289
|
+
contact's chat **as the user** — into a forum topic (`reply_to`) when the contact has a
|
|
290
|
+
`topic_id`, prepending the contact's `mention` on its own line when set. Text comes from
|
|
291
|
+
a file or stdin (`-`); `--dry-run` composes and prints the outgoing message (target,
|
|
292
|
+
topic, text) without sending. New `telegram.send` (+ `_compose_outgoing` /
|
|
293
|
+
`_target_from_chat_id`, resolving `chat_id` as `-100…` int or `@user` / `me`) and a
|
|
294
|
+
`ContactNotFoundError` (CLI exit 5); empty text is refused. Tests in `tests/test_send.py`
|
|
295
|
+
(Telethon mocked). This is the first command that posts outside the storage group.
|
|
296
|
+
- Notebook listing (TGN-8): `tg-notes notebooks list` returns the storage group's notebook
|
|
297
|
+
topics as JSON (`{name, topic_id}`, sorted), excluding the reserved `General` and
|
|
298
|
+
`contacts` topics. New `telegram.notebooks_list` and `RESERVED_TOPICS`; tests in
|
|
299
|
+
`tests/test_notebooks.py`. Completes **Phase 1 (CLI core)** — all deterministic Telegram
|
|
300
|
+
commands are implemented and verified end-to-end. Removed the leftover `_todo` stub
|
|
301
|
+
handler now that no command is a stub.
|
|
302
|
+
- Capture skill (TGN-9): `skills/tg-notes/SKILL.md` — a portable Agent Skill (standard
|
|
303
|
+
`name`/`description` frontmatter) that captures a work note into the Telegram-backed
|
|
304
|
+
store by shelling out to `tg-notes note add`. Two modes: verbatim (user dictates) and
|
|
305
|
+
session summary (composed from real facts — git commits since 00:00, changed files,
|
|
306
|
+
tickets — never invented), 2–6 concise bullets in the user's language, default `daily`
|
|
307
|
+
notebook, optional hashtags. Capture only — it never sends. Verified end-to-end against
|
|
308
|
+
the real account (both modes filed and read back). Succeeds the retired local `report`
|
|
309
|
+
skill, now Telegram-native.
|
|
310
|
+
- Compile & send skill (TGN-10): `skills/tg-notes-send/SKILL.md` — reads notes
|
|
311
|
+
(`tg-notes notes list`), rewrites them per the target contact's `style`, previews with
|
|
312
|
+
`tg-notes send --dry-run`, **always shows a draft and requires an explicit confirmation**
|
|
313
|
+
(the message goes out under the user's own account), then `tg-notes send`. Contacts are
|
|
314
|
+
the single source of chat/topic/mention/style. Verified end-to-end against the real
|
|
315
|
+
account (dry-run → confirmed send to Saved Messages → read back; unknown-contact exit 5).
|
|
316
|
+
Succeeds the retired local `report-send` skill, now driven by the `tg-notes` CLI.
|
|
317
|
+
- Daily-report preset (TGN-11): a first-class section of `skills/tg-notes-send/SKILL.md`
|
|
318
|
+
(`/tg-report`, "отправь дневной отчёт") — the compile & send flow with fixed defaults
|
|
319
|
+
(notebook `daily`, `--since today`), one confirmed send per recipient. Completes
|
|
320
|
+
**Phase 2 (the Claude Code Skill)**: capture (`tg-notes`) + compile & send / daily report
|
|
321
|
+
(`tg-notes-send`), the Telegram-native successor to the old `report`/`report-send` pair.
|
|
322
|
+
- Claude Code plugin packaging (TGN-13): `.claude-plugin/plugin.json` — plugin manifest
|
|
323
|
+
(`name: tg-notes`, version, author, GPL-3.0-or-later) that bundles the two skills
|
|
324
|
+
(auto-discovered from `skills/`). Passes `claude plugin validate . --strict`.
|
|
325
|
+
- Git plugin marketplace (TGN-14): `.claude-plugin/marketplace.json` — single-plugin
|
|
326
|
+
marketplace (`tg-notes-marketplace`) with `source: "./"` (plugin is the repo root).
|
|
327
|
+
Install: `/plugin marketplace add korkin25/tg_notes` then
|
|
328
|
+
`/plugin install tg-notes@tg-notes-marketplace`. The bundled skills drive the `tg-notes`
|
|
329
|
+
CLI, installed separately with `pipx install tg-notes`.
|
|
330
|
+
|
|
331
|
+
### Changed
|
|
332
|
+
|
|
333
|
+
- Single-source the package version: `pyproject.toml` reads it from
|
|
334
|
+
`tg_notes/__init__.py` (`[tool.hatch.version]`, `dynamic = ["version"]`) so it can no
|
|
335
|
+
longer drift between the two. Bumped to `0.1.0` for the first release.
|
|
336
|
+
- Release workflow (`.github/workflows/release.yml`) now publishes to PyPI via **Trusted
|
|
337
|
+
Publishing** (OIDC, `id-token: write`, `pypi` environment) instead of a stored API
|
|
338
|
+
token, and runs `twine check` on the built artifacts. Publishing still triggers only on
|
|
339
|
+
a `v*` tag; it needs a one-time PyPI Trusted Publisher bound to `korkin25/tg_notes`.
|
|
@@ -62,3 +62,22 @@ This project is developed by an AI agent under continuous, autonomous iteration.
|
|
|
62
62
|
- High bar: type hints, docstrings, ruff-clean, meaningful tests. Work like a top-tier engineer + DevOps.
|
|
63
63
|
- Auto-logging: started/ongoing work goes to TODO.md (Current state + phase tables); completed and verified work moves to CHANGELOG.md, in the same change. Never mark a task done without a passing test.
|
|
64
64
|
- Cold-start: keep the top of TODO.md a "Current state / next action" block so a fresh session knows exactly what to do next.
|
|
65
|
+
|
|
66
|
+
### Testing in a sandbox (mandatory for live tests)
|
|
67
|
+
|
|
68
|
+
Any test that touches the real config, the real keyring, or Telegram MUST run in an
|
|
69
|
+
**isolated sandbox**, never against your day-to-day install. The real
|
|
70
|
+
`~/.config/tg-notes`, the real keyring (`tg-notes` namespace), and the real storage group
|
|
71
|
+
`-1004432534270` must stay untouched.
|
|
72
|
+
|
|
73
|
+
- Use `scripts/sandbox.py` — `setup` seeds a throwaway config dir (`$TG_NOTES_SANDBOX_DIR`
|
|
74
|
+
or `~/.config/tg-notes-sandbox`, file backend, a copy of the real session) and creates a
|
|
75
|
+
**dedicated test group** (a fresh `-100…` id, never the real one); `run -- <cmd>` runs a
|
|
76
|
+
command against it; `pytest -- <args>` runs the gated live tests (`TG_NOTES_LIVE=1`)
|
|
77
|
+
there; `reset` deletes it. All idempotent.
|
|
78
|
+
- Or follow the manual protocol: prefix every command with `TG_NOTES_CONFIG_DIR=<sandbox>`
|
|
79
|
+
(and `TG_NOTES_KEYRING_SERVICE=tg-notes-sandbox` for keyring tests) against a dedicated
|
|
80
|
+
group. See [docs/sandbox-testing.md](docs/sandbox-testing.md).
|
|
81
|
+
- Unit tests are mocked and already isolated — they must never reach real config, keyring,
|
|
82
|
+
or network. The sandbox session file is a full-access credential: `chmod 600`, outside
|
|
83
|
+
the repo, never committed.
|
|
@@ -1,3 +1,36 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tg-notes
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Notes → compile → publish, using a private Telegram group as the store and posting under your own account.
|
|
5
|
+
Project-URL: Homepage, https://github.com/korkin25/tg_notes
|
|
6
|
+
Project-URL: Repository, https://github.com/korkin25/tg_notes
|
|
7
|
+
Author: korkin25
|
|
8
|
+
License-Expression: GPL-3.0-or-later
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: cli,notes,telegram,telethon,userbot
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Communications :: Chat
|
|
14
|
+
Requires-Python: >=3.11
|
|
15
|
+
Requires-Dist: telethon>=1.44
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: bandit==1.9.4; extra == 'dev'
|
|
18
|
+
Requires-Dist: build; extra == 'dev'
|
|
19
|
+
Requires-Dist: keyring>=24; extra == 'dev'
|
|
20
|
+
Requires-Dist: mcp>=1.2; extra == 'dev'
|
|
21
|
+
Requires-Dist: pip-audit; extra == 'dev'
|
|
22
|
+
Requires-Dist: pytest-mock; extra == 'dev'
|
|
23
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
24
|
+
Requires-Dist: ruff==0.16.0; extra == 'dev'
|
|
25
|
+
Provides-Extra: keyring
|
|
26
|
+
Requires-Dist: keyring>=24; extra == 'keyring'
|
|
27
|
+
Requires-Dist: secretstorage>=3; (platform_system == 'Linux') and extra == 'keyring'
|
|
28
|
+
Provides-Extra: mcp
|
|
29
|
+
Requires-Dist: mcp>=1.2; extra == 'mcp'
|
|
30
|
+
Provides-Extra: transcribe
|
|
31
|
+
Requires-Dist: faster-whisper>=1; extra == 'transcribe'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
1
34
|
# tg_notes
|
|
2
35
|
|
|
3
36
|
Turn a private Telegram group into your notes store, then publish tailored updates
|
|
@@ -60,6 +93,19 @@ The skills drive the CLI, so `tg-notes` must be on `PATH`. The two skills:
|
|
|
60
93
|
stored notes per a contact's style, confirm, and publish with `tg-notes send` (daily
|
|
61
94
|
report is a preset).
|
|
62
95
|
|
|
96
|
+
For agent hosts that can't shell out (Claude Desktop, …), a local **stdio MCP server**
|
|
97
|
+
exposes the same core (`note_add` / `note_add_file` / `notes_list` / `contacts_list` /
|
|
98
|
+
`send`). `note_add_file` uploads a local file as a media note — audio auto-transcribes to
|
|
99
|
+
the caption, or the agent passes its own `caption` (e.g. an image description) to skip it:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
pipx install "tg-notes[mcp]" # then point your MCP client at the `tg-notes-mcp` command
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
It runs over stdio only — the session and secrets never leave the machine. When the host
|
|
106
|
+
spawns the server with a sanitized environment, the (opt-in) keyring backend self-heals
|
|
107
|
+
`DBUS_SESSION_BUS_ADDRESS` so it can still reach the Secret Service.
|
|
108
|
+
|
|
63
109
|
Available so far:
|
|
64
110
|
|
|
65
111
|
```bash
|
|
@@ -76,6 +122,24 @@ tg-notes setup [--notebook <name>]
|
|
|
76
122
|
tg-notes note add --notebook daily --text-file note.txt --hashtag infra
|
|
77
123
|
echo "quick note" | tg-notes note add --text-file -
|
|
78
124
|
|
|
125
|
+
# Or attach a media file (photo/video/audio/document) as the note — stored as native
|
|
126
|
+
# Telegram media in the topic, kind auto-detected. --caption is the searchable text (any
|
|
127
|
+
# --hashtag is appended to it).
|
|
128
|
+
tg-notes note add --notebook daily --file ~/clips/demo.mp4 --caption "walkthrough" --hashtag demo
|
|
129
|
+
|
|
130
|
+
# Audio notes auto-transcribe: when --file is audio (.ogg/.opus/.mp3/.m4a/.wav/…) and no
|
|
131
|
+
# --caption is given, tg-notes transcribes it LOCALLY and uses the transcript as the caption
|
|
132
|
+
# (searchable text) — best-effort, so if no engine is installed the file still uploads
|
|
133
|
+
# without a caption. On the first transcription with no engine, tg-notes auto-fetches the
|
|
134
|
+
# faster-whisper engine (once, best-effort; disable with transcriber_autoinstall = false).
|
|
135
|
+
# Or pre-install a local engine yourself (both need `ffmpeg` to decode audio):
|
|
136
|
+
# pipx inject tg-notes faster-whisper # the faster-whisper backend
|
|
137
|
+
# # …or set whisper_cmd in config.toml to a whisper.cpp/openai-whisper binary
|
|
138
|
+
# whisper_model (e.g. base/small) picks the model (downloads on first use).
|
|
139
|
+
# Force/skip with --transcribe/--no-transcribe.
|
|
140
|
+
tg-notes note add --notebook daily --file ~/voice/idea.ogg # caption ← transcript
|
|
141
|
+
tg-notes note add --notebook daily --file ~/voice/idea.ogg --no-transcribe
|
|
142
|
+
|
|
79
143
|
# List the raw notes of a notebook (oldest first) as JSON, optionally bounded by --since
|
|
80
144
|
# (today | HH:MM | YYYY-MM-DD | ISO datetime; local when no offset). Feeds compilation.
|
|
81
145
|
tg-notes notes list --notebook daily --since today
|
|
@@ -96,6 +160,20 @@ echo "..." | tg-notes send --contact boss --text-file - --dry-run
|
|
|
96
160
|
# List the storage group's notebook topics as JSON (excludes General/contacts).
|
|
97
161
|
tg-notes notebooks list
|
|
98
162
|
|
|
163
|
+
# Secrets backend. By default the api_hash + session live in local files (config.toml
|
|
164
|
+
# 600 + *.session). Optionally move them into the OS Secret Service (gnome-keyring /
|
|
165
|
+
# KWallet / KeePassXC — whichever owns org.freedesktop.secrets; the session is stored as
|
|
166
|
+
# a StringSession). `secrets status` shows the active backend and which provider answers.
|
|
167
|
+
pipx install "tg-notes[keyring]"
|
|
168
|
+
tg-notes secrets status
|
|
169
|
+
tg-notes secrets doctor # diagnose the store + get setup/migration steps
|
|
170
|
+
tg-notes secrets migrate --to keyring # or: --to file to move back
|
|
171
|
+
# Using KeePassXC as the store (serve Secret Service, dedicated group, the
|
|
172
|
+
# confirmation trade-off): see docs/keepassxc.md. On Linux + keyring, the CLI re-execs
|
|
173
|
+
# through a named launcher so the vault prompt shows "tg-notes", not "python3.12".
|
|
174
|
+
# Test setup/doctor/migrate end-to-end against a throwaway isolated install
|
|
175
|
+
# (never touches the real config/session/vault): see docs/sandbox-testing.md.
|
|
176
|
+
|
|
99
177
|
# Log in on its own (setup calls this for you; useful to re-authorize a new device).
|
|
100
178
|
# Requires api_id/api_hash in local config; writes a chmod-600 session file.
|
|
101
179
|
tg-notes login
|
|
@@ -104,6 +182,10 @@ tg-notes login
|
|
|
104
182
|
tg-notes whoami
|
|
105
183
|
```
|
|
106
184
|
|
|
185
|
+
Omit `--contact` / `--to` / `--notebook` in an interactive terminal to pick from a list
|
|
186
|
+
(uses `fzf`/`sk`/`fzy` if installed, else a numbered menu); with the flag or when piped,
|
|
187
|
+
behavior is unchanged.
|
|
188
|
+
|
|
107
189
|
## Security & Telegram ToS
|
|
108
190
|
|
|
109
191
|
- Userbot automation is a **gray area of Telegram's ToS**; you run it on your own
|