pytgcli 0.7.0__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.
- pytgcli-0.7.0/.github/dependabot.yml +15 -0
- pytgcli-0.7.0/.github/workflows/ci.yml +29 -0
- pytgcli-0.7.0/.github/workflows/publish.yml +17 -0
- pytgcli-0.7.0/.gitignore +15 -0
- pytgcli-0.7.0/.pre-commit-config.yaml +12 -0
- pytgcli-0.7.0/.python-version +1 -0
- pytgcli-0.7.0/AGENTS.md +37 -0
- pytgcli-0.7.0/CLAUDE.md +1 -0
- pytgcli-0.7.0/LICENSE +21 -0
- pytgcli-0.7.0/PKG-INFO +173 -0
- pytgcli-0.7.0/README.md +150 -0
- pytgcli-0.7.0/pyproject.toml +57 -0
- pytgcli-0.7.0/src/tgcli/__init__.py +0 -0
- pytgcli-0.7.0/src/tgcli/auth.py +73 -0
- pytgcli-0.7.0/src/tgcli/cli.py +336 -0
- pytgcli-0.7.0/src/tgcli/client.py +224 -0
- pytgcli-0.7.0/src/tgcli/config.py +62 -0
- pytgcli-0.7.0/src/tgcli/formatting.py +151 -0
- pytgcli-0.7.0/src/tgcli/session.py +28 -0
- pytgcli-0.7.0/tests/__init__.py +0 -0
- pytgcli-0.7.0/tests/conftest.py +38 -0
- pytgcli-0.7.0/tests/test_auth.py +100 -0
- pytgcli-0.7.0/tests/test_cli.py +419 -0
- pytgcli-0.7.0/tests/test_client.py +312 -0
- pytgcli-0.7.0/tests/test_config.py +74 -0
- pytgcli-0.7.0/tests/test_formatting.py +118 -0
- pytgcli-0.7.0/tests/test_session.py +43 -0
- pytgcli-0.7.0/uv.lock +818 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
14
|
+
- uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7.3.0
|
|
15
|
+
- run: uv sync --group dev
|
|
16
|
+
- run: uv run ruff check
|
|
17
|
+
- run: uv run ruff format --check
|
|
18
|
+
|
|
19
|
+
test:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
strategy:
|
|
22
|
+
matrix:
|
|
23
|
+
python-version: ["3.12", "3.13"]
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
26
|
+
- uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7.3.0
|
|
27
|
+
- run: uv python install ${{ matrix.python-version }}
|
|
28
|
+
- run: uv sync --group dev
|
|
29
|
+
- run: uv run pytest
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
environment: pypi
|
|
11
|
+
permissions:
|
|
12
|
+
id-token: write
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
15
|
+
- uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7.3.0
|
|
16
|
+
- run: uv build
|
|
17
|
+
- run: uv publish --trusted-publishing always
|
pytgcli-0.7.0/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
pytgcli-0.7.0/AGENTS.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
## Architecture
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
src/tgcli/
|
|
7
|
+
cli.py # Typer app, thin entrypoints
|
|
8
|
+
auth.py # Login/logout/status logic
|
|
9
|
+
client.py # Telethon wrapper (search, read, context)
|
|
10
|
+
config.py # Config loading (TOML + env vars)
|
|
11
|
+
formatting.py # Pure output formatting functions (no I/O)
|
|
12
|
+
session.py # Keychain-backed StringSession via keyring
|
|
13
|
+
tests/ # pytest + pytest-asyncio, Telethon fully mocked
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Layers: CLI (Typer) -> business logic (auth, client) -> Telethon.
|
|
17
|
+
|
|
18
|
+
## Constraints
|
|
19
|
+
|
|
20
|
+
- Keep Telethon isolated from CLI and formatting layers
|
|
21
|
+
- Keep CLI entrypoints thin; push logic into library modules
|
|
22
|
+
- Formatting functions are pure (no I/O)
|
|
23
|
+
- All output to stdout, errors to stderr
|
|
24
|
+
- Exit codes: 0 success, 1 error, 2 auth required
|
|
25
|
+
- Mock Telethon entirely in tests, no real API calls
|
|
26
|
+
|
|
27
|
+
## Telethon Reference
|
|
28
|
+
|
|
29
|
+
For Telethon questions (API methods, entity resolution, session handling, etc.), check https://docs.telethon.dev/en/stable/ first.
|
|
30
|
+
|
|
31
|
+
## Verify
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
uv run ruff check
|
|
35
|
+
uv run ruff format --check
|
|
36
|
+
uv run pytest
|
|
37
|
+
```
|
pytgcli-0.7.0/CLAUDE.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
AGENTS.md
|
pytgcli-0.7.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Takeshi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pytgcli-0.7.0/PKG-INFO
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pytgcli
|
|
3
|
+
Version: 0.7.0
|
|
4
|
+
Summary: CLI tool to read Telegram messages from the terminal
|
|
5
|
+
Project-URL: Repository, https://github.com/tksohishi/tgcli
|
|
6
|
+
Author: Takeshi
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: cli,messages,telegram
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Topic :: Communications :: Chat
|
|
16
|
+
Classifier: Typing :: Typed
|
|
17
|
+
Requires-Python: >=3.12
|
|
18
|
+
Requires-Dist: keyring>=25
|
|
19
|
+
Requires-Dist: rich>=13
|
|
20
|
+
Requires-Dist: telethon>=1.42
|
|
21
|
+
Requires-Dist: typer>=0.15
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# tgcli — Telegram for your terminal and your AI agents.
|
|
25
|
+
|
|
26
|
+
Give AI agents (Claude Code, Codex, Cursor, etc.) direct access to your Telegram conversations. Structured JSONL output, minimal command surface, fuzzy name resolution. Works equally well for humans with `--pretty`.
|
|
27
|
+
|
|
28
|
+
## Features
|
|
29
|
+
|
|
30
|
+
- **JSONL by default** — one JSON object per line; agents parse it natively, scripts pipe it freely
|
|
31
|
+
- **Minimal surface** — a handful of commands; easy for agents to discover and invoke
|
|
32
|
+
- **Fuzzy resolution** — chat and user names match by display name (no numeric IDs required)
|
|
33
|
+
- **`--pretty` for humans** — Rich tables when you want to read output yourself
|
|
34
|
+
- **Secure session storage** — Telethon session key stored in macOS Keychain via `keyring`
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
Requires Python 3.12+ and [uv](https://docs.astral.sh/uv/).
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
uv tool install pytgcli
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Or install from source:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
git clone https://github.com/tksohishi/tgcli.git
|
|
48
|
+
cd tgcli
|
|
49
|
+
uv tool install .
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
### 1. Get API Credentials
|
|
55
|
+
|
|
56
|
+
Create a Telegram API app at [my.telegram.org/apps](https://my.telegram.org/apps). You'll get an `api_id` and `api_hash`.
|
|
57
|
+
|
|
58
|
+
### 2. Authenticate
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
tg auth
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
This walks you through setup: saves your API credentials to `~/.config/tgcli/config.toml`, then logs in with phone number + verification code.
|
|
65
|
+
|
|
66
|
+
### 3. Read Messages
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
tg read "Alice"
|
|
70
|
+
tg read "Finance Team" --limit 20
|
|
71
|
+
tg read "Finance Team" -q "budget"
|
|
72
|
+
tg read "Finance Team" -q "deadline" --from "Alice" --after 2025-01-01
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 4. View Context
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
tg context "Finance Team" 12345
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Use with AI Agents
|
|
82
|
+
|
|
83
|
+
Once authenticated, any AI coding agent with shell access can use tgcli directly. A few examples:
|
|
84
|
+
|
|
85
|
+
**Ask Claude Code to summarize a group chat:**
|
|
86
|
+
|
|
87
|
+
> "Read the last 30 messages from 'Engineering' and summarize the key decisions."
|
|
88
|
+
|
|
89
|
+
The agent runs `tg read "Engineering" --limit 30`, parses the JSONL, and responds.
|
|
90
|
+
|
|
91
|
+
**Find a past conversation:**
|
|
92
|
+
|
|
93
|
+
> "What did I discuss with Alice last week about the deployment?"
|
|
94
|
+
|
|
95
|
+
The agent runs `tg read "Alice" -q "deployment" --after 2025-02-14` and surfaces the relevant messages.
|
|
96
|
+
|
|
97
|
+
**Pipe into scripts:**
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
tg read "Alerts" --limit 100 | jq 'select(.text | test("ERROR"))'
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
No wrapper libraries or API adapters needed. The structured output and simple command surface mean agents can use tgcli out of the box.
|
|
104
|
+
|
|
105
|
+
## Commands
|
|
106
|
+
|
|
107
|
+
### `tg auth`
|
|
108
|
+
|
|
109
|
+
Smart entrypoint: creates config if missing, logs in if needed, shows status if already authenticated.
|
|
110
|
+
|
|
111
|
+
Explicit subcommands:
|
|
112
|
+
|
|
113
|
+
- `tg auth login` - interactive login (phone + code/2FA)
|
|
114
|
+
- `tg auth logout` - remove session from Keychain
|
|
115
|
+
- `tg auth status` - show auth state
|
|
116
|
+
|
|
117
|
+
### `tg chats`
|
|
118
|
+
|
|
119
|
+
List your Telegram chats. Returns JSONL by default.
|
|
120
|
+
|
|
121
|
+
| Flag | Description |
|
|
122
|
+
|------------|------------------------------|
|
|
123
|
+
| `--filter` | Fuzzy filter by chat name |
|
|
124
|
+
| `--limit` | Max chats to list (default 100) |
|
|
125
|
+
| `--pretty` | Rich table output instead of JSONL |
|
|
126
|
+
|
|
127
|
+
### `tg read <chat>`
|
|
128
|
+
|
|
129
|
+
Read recent messages from a chat. Returns JSONL by default, newest first.
|
|
130
|
+
|
|
131
|
+
| Flag | Description |
|
|
132
|
+
|----------------|----------------------------------------|
|
|
133
|
+
| `--query`/`-q` | Filter messages by text |
|
|
134
|
+
| `--from` | Filter by sender |
|
|
135
|
+
| `--limit` | Max messages (default 50) |
|
|
136
|
+
| `--head` | Oldest messages first |
|
|
137
|
+
| `--after` | Only messages after date (YYYY-MM-DD) |
|
|
138
|
+
| `--before` | Only messages before date (YYYY-MM-DD) |
|
|
139
|
+
| `--pretty` | Rich table output instead of JSONL |
|
|
140
|
+
|
|
141
|
+
### `tg context <chat> <message_id>`
|
|
142
|
+
|
|
143
|
+
View a message with surrounding context. Returns JSONL by default.
|
|
144
|
+
|
|
145
|
+
| Flag | Description |
|
|
146
|
+
|-------------|-----------------------------------|
|
|
147
|
+
| `--context` | Messages before/after (default 5) |
|
|
148
|
+
| `--pretty` | Rich text output instead of JSONL |
|
|
149
|
+
|
|
150
|
+
## Configuration
|
|
151
|
+
|
|
152
|
+
Config lives at `~/.config/tgcli/config.toml`:
|
|
153
|
+
|
|
154
|
+
```toml
|
|
155
|
+
api_id = 123456
|
|
156
|
+
api_hash = "your_api_hash"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Alternatively, set `TELEGRAM_API_ID` and `TELEGRAM_API_HASH` environment variables.
|
|
160
|
+
|
|
161
|
+
## Contributing
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
uv sync --group dev
|
|
165
|
+
uv run pytest
|
|
166
|
+
uv run ruff check
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Tests mock Telethon entirely; no real API calls are made.
|
|
170
|
+
|
|
171
|
+
## License
|
|
172
|
+
|
|
173
|
+
[MIT](LICENSE)
|
pytgcli-0.7.0/README.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# tgcli — Telegram for your terminal and your AI agents.
|
|
2
|
+
|
|
3
|
+
Give AI agents (Claude Code, Codex, Cursor, etc.) direct access to your Telegram conversations. Structured JSONL output, minimal command surface, fuzzy name resolution. Works equally well for humans with `--pretty`.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **JSONL by default** — one JSON object per line; agents parse it natively, scripts pipe it freely
|
|
8
|
+
- **Minimal surface** — a handful of commands; easy for agents to discover and invoke
|
|
9
|
+
- **Fuzzy resolution** — chat and user names match by display name (no numeric IDs required)
|
|
10
|
+
- **`--pretty` for humans** — Rich tables when you want to read output yourself
|
|
11
|
+
- **Secure session storage** — Telethon session key stored in macOS Keychain via `keyring`
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Requires Python 3.12+ and [uv](https://docs.astral.sh/uv/).
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uv tool install pytgcli
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or install from source:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
git clone https://github.com/tksohishi/tgcli.git
|
|
25
|
+
cd tgcli
|
|
26
|
+
uv tool install .
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
### 1. Get API Credentials
|
|
32
|
+
|
|
33
|
+
Create a Telegram API app at [my.telegram.org/apps](https://my.telegram.org/apps). You'll get an `api_id` and `api_hash`.
|
|
34
|
+
|
|
35
|
+
### 2. Authenticate
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
tg auth
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This walks you through setup: saves your API credentials to `~/.config/tgcli/config.toml`, then logs in with phone number + verification code.
|
|
42
|
+
|
|
43
|
+
### 3. Read Messages
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
tg read "Alice"
|
|
47
|
+
tg read "Finance Team" --limit 20
|
|
48
|
+
tg read "Finance Team" -q "budget"
|
|
49
|
+
tg read "Finance Team" -q "deadline" --from "Alice" --after 2025-01-01
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 4. View Context
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
tg context "Finance Team" 12345
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Use with AI Agents
|
|
59
|
+
|
|
60
|
+
Once authenticated, any AI coding agent with shell access can use tgcli directly. A few examples:
|
|
61
|
+
|
|
62
|
+
**Ask Claude Code to summarize a group chat:**
|
|
63
|
+
|
|
64
|
+
> "Read the last 30 messages from 'Engineering' and summarize the key decisions."
|
|
65
|
+
|
|
66
|
+
The agent runs `tg read "Engineering" --limit 30`, parses the JSONL, and responds.
|
|
67
|
+
|
|
68
|
+
**Find a past conversation:**
|
|
69
|
+
|
|
70
|
+
> "What did I discuss with Alice last week about the deployment?"
|
|
71
|
+
|
|
72
|
+
The agent runs `tg read "Alice" -q "deployment" --after 2025-02-14` and surfaces the relevant messages.
|
|
73
|
+
|
|
74
|
+
**Pipe into scripts:**
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
tg read "Alerts" --limit 100 | jq 'select(.text | test("ERROR"))'
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
No wrapper libraries or API adapters needed. The structured output and simple command surface mean agents can use tgcli out of the box.
|
|
81
|
+
|
|
82
|
+
## Commands
|
|
83
|
+
|
|
84
|
+
### `tg auth`
|
|
85
|
+
|
|
86
|
+
Smart entrypoint: creates config if missing, logs in if needed, shows status if already authenticated.
|
|
87
|
+
|
|
88
|
+
Explicit subcommands:
|
|
89
|
+
|
|
90
|
+
- `tg auth login` - interactive login (phone + code/2FA)
|
|
91
|
+
- `tg auth logout` - remove session from Keychain
|
|
92
|
+
- `tg auth status` - show auth state
|
|
93
|
+
|
|
94
|
+
### `tg chats`
|
|
95
|
+
|
|
96
|
+
List your Telegram chats. Returns JSONL by default.
|
|
97
|
+
|
|
98
|
+
| Flag | Description |
|
|
99
|
+
|------------|------------------------------|
|
|
100
|
+
| `--filter` | Fuzzy filter by chat name |
|
|
101
|
+
| `--limit` | Max chats to list (default 100) |
|
|
102
|
+
| `--pretty` | Rich table output instead of JSONL |
|
|
103
|
+
|
|
104
|
+
### `tg read <chat>`
|
|
105
|
+
|
|
106
|
+
Read recent messages from a chat. Returns JSONL by default, newest first.
|
|
107
|
+
|
|
108
|
+
| Flag | Description |
|
|
109
|
+
|----------------|----------------------------------------|
|
|
110
|
+
| `--query`/`-q` | Filter messages by text |
|
|
111
|
+
| `--from` | Filter by sender |
|
|
112
|
+
| `--limit` | Max messages (default 50) |
|
|
113
|
+
| `--head` | Oldest messages first |
|
|
114
|
+
| `--after` | Only messages after date (YYYY-MM-DD) |
|
|
115
|
+
| `--before` | Only messages before date (YYYY-MM-DD) |
|
|
116
|
+
| `--pretty` | Rich table output instead of JSONL |
|
|
117
|
+
|
|
118
|
+
### `tg context <chat> <message_id>`
|
|
119
|
+
|
|
120
|
+
View a message with surrounding context. Returns JSONL by default.
|
|
121
|
+
|
|
122
|
+
| Flag | Description |
|
|
123
|
+
|-------------|-----------------------------------|
|
|
124
|
+
| `--context` | Messages before/after (default 5) |
|
|
125
|
+
| `--pretty` | Rich text output instead of JSONL |
|
|
126
|
+
|
|
127
|
+
## Configuration
|
|
128
|
+
|
|
129
|
+
Config lives at `~/.config/tgcli/config.toml`:
|
|
130
|
+
|
|
131
|
+
```toml
|
|
132
|
+
api_id = 123456
|
|
133
|
+
api_hash = "your_api_hash"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Alternatively, set `TELEGRAM_API_ID` and `TELEGRAM_API_HASH` environment variables.
|
|
137
|
+
|
|
138
|
+
## Contributing
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
uv sync --group dev
|
|
142
|
+
uv run pytest
|
|
143
|
+
uv run ruff check
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Tests mock Telethon entirely; no real API calls are made.
|
|
147
|
+
|
|
148
|
+
## License
|
|
149
|
+
|
|
150
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pytgcli"
|
|
7
|
+
version = "0.7.0"
|
|
8
|
+
description = "CLI tool to read Telegram messages from the terminal"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.12"
|
|
12
|
+
authors = [{ name = "Takeshi" }]
|
|
13
|
+
keywords = ["telegram", "cli", "messages"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Topic :: Communications :: Chat",
|
|
21
|
+
"Typing :: Typed",
|
|
22
|
+
]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"typer>=0.15",
|
|
25
|
+
"telethon>=1.42",
|
|
26
|
+
"keyring>=25",
|
|
27
|
+
"rich>=13",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Repository = "https://github.com/tksohishi/tgcli"
|
|
32
|
+
|
|
33
|
+
[dependency-groups]
|
|
34
|
+
dev = [
|
|
35
|
+
"pip-audit>=2",
|
|
36
|
+
"pytest>=8",
|
|
37
|
+
"pytest-asyncio>=0.25",
|
|
38
|
+
"ruff>=0.15",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[project.scripts]
|
|
42
|
+
tg = "tgcli.cli:app"
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build.targets.wheel]
|
|
45
|
+
packages = ["src/tgcli"]
|
|
46
|
+
|
|
47
|
+
[tool.ruff]
|
|
48
|
+
target-version = "py312"
|
|
49
|
+
|
|
50
|
+
[tool.ruff.lint]
|
|
51
|
+
select = ["E", "F", "I", "S", "UP"]
|
|
52
|
+
|
|
53
|
+
[tool.ruff.lint.per-file-ignores]
|
|
54
|
+
"tests/**/*.py" = ["S101", "S108"]
|
|
55
|
+
|
|
56
|
+
[tool.pytest.ini_options]
|
|
57
|
+
asyncio_mode = "auto"
|
|
File without changes
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from telethon.sessions import StringSession
|
|
4
|
+
|
|
5
|
+
from tgcli.client import create_client
|
|
6
|
+
from tgcli.session import delete_session, load_session, save_session
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def login() -> None:
|
|
10
|
+
"""Interactive login: phone + code/2FA. Saves session to Keychain."""
|
|
11
|
+
client = create_client()
|
|
12
|
+
try:
|
|
13
|
+
await client.start(phone=lambda: input("Phone number: "))
|
|
14
|
+
session_str = StringSession.save(client.session)
|
|
15
|
+
save_session(session_str)
|
|
16
|
+
finally:
|
|
17
|
+
await client.disconnect()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async def logout() -> None:
|
|
21
|
+
"""Log out and remove session from Keychain.
|
|
22
|
+
|
|
23
|
+
Always deletes the local session, even if the remote logout fails.
|
|
24
|
+
"""
|
|
25
|
+
try:
|
|
26
|
+
client = create_client()
|
|
27
|
+
try:
|
|
28
|
+
await client.connect()
|
|
29
|
+
if await client.is_user_authorized():
|
|
30
|
+
await client.log_out()
|
|
31
|
+
finally:
|
|
32
|
+
await client.disconnect()
|
|
33
|
+
except Exception: # noqa: S110
|
|
34
|
+
pass
|
|
35
|
+
delete_session()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
async def get_status() -> dict:
|
|
39
|
+
"""Return auth status info.
|
|
40
|
+
|
|
41
|
+
Returns dict with keys: authenticated, phone, session_exists.
|
|
42
|
+
"""
|
|
43
|
+
session_exists = load_session() is not None
|
|
44
|
+
|
|
45
|
+
if not session_exists:
|
|
46
|
+
return {
|
|
47
|
+
"authenticated": False,
|
|
48
|
+
"phone": None,
|
|
49
|
+
"session_exists": False,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
client = create_client()
|
|
53
|
+
try:
|
|
54
|
+
await client.connect()
|
|
55
|
+
authorized = await client.is_user_authorized()
|
|
56
|
+
phone = None
|
|
57
|
+
if authorized:
|
|
58
|
+
me = await client.get_me()
|
|
59
|
+
if me and me.phone:
|
|
60
|
+
# Mask phone: show first 3 and last 2 digits
|
|
61
|
+
p = me.phone
|
|
62
|
+
if len(p) > 5:
|
|
63
|
+
phone = p[:3] + "*" * (len(p) - 5) + p[-2:]
|
|
64
|
+
else:
|
|
65
|
+
phone = p
|
|
66
|
+
finally:
|
|
67
|
+
await client.disconnect()
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
"authenticated": authorized,
|
|
71
|
+
"phone": phone,
|
|
72
|
+
"session_exists": session_exists,
|
|
73
|
+
}
|