chore: move to top level
This commit is contained in:
@@ -1,94 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# /// script
|
|
||||||
# requires-python = ">=3.10"
|
|
||||||
# dependencies = ["requests", "markdown-it-py", "sulguk", "tomli; python_version < '3.11'"]
|
|
||||||
# ///
|
|
||||||
import json
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import requests
|
|
||||||
from markdown_it import MarkdownIt
|
|
||||||
from sulguk import transform_html
|
|
||||||
|
|
||||||
CONFIG_PATH = Path.home() / ".codex" / "telegram.toml"
|
|
||||||
ERR_PATH = Path.home() / ".codex" / "telegram_last_error.txt"
|
|
||||||
|
|
||||||
|
|
||||||
def _load_toml(path: Path) -> dict:
|
|
||||||
if not path.exists():
|
|
||||||
return {}
|
|
||||||
try:
|
|
||||||
import tomllib # type: ignore[attr-defined]
|
|
||||||
except ModuleNotFoundError:
|
|
||||||
import tomli as tomllib # type: ignore[import-not-found]
|
|
||||||
return tomllib.loads(path.read_text(encoding="utf-8"))
|
|
||||||
|
|
||||||
|
|
||||||
def _config_get(config: dict, key: str):
|
|
||||||
if key in config:
|
|
||||||
return config[key]
|
|
||||||
nested = config.get("telegram")
|
|
||||||
if isinstance(nested, dict) and key in nested:
|
|
||||||
return nested[key]
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
config = _load_toml(CONFIG_PATH)
|
|
||||||
bot_token = _config_get(config, "bot_token")
|
|
||||||
chat_id = _config_get(config, "chat_id")
|
|
||||||
if not bot_token or chat_id is None:
|
|
||||||
raise KeyError("telegram.toml must include bot_token and chat_id")
|
|
||||||
bot_token = str(bot_token)
|
|
||||||
chat_id = str(chat_id)
|
|
||||||
|
|
||||||
event = json.loads(sys.argv[1])
|
|
||||||
|
|
||||||
md = event["last-assistant-message"].rstrip()
|
|
||||||
thread_id = event.get("thread-id")
|
|
||||||
if thread_id:
|
|
||||||
md += f"\n\nthread: `{thread_id}`"
|
|
||||||
|
|
||||||
html = MarkdownIt("commonmark", {"html": False}).render(md)
|
|
||||||
rendered = transform_html(html)
|
|
||||||
|
|
||||||
text = re.sub(r"(?m)^(\s*)•", r"\1-", rendered.text)
|
|
||||||
|
|
||||||
# FIX: Telegram requires MessageEntity.language (if present) to be a String.
|
|
||||||
entities = []
|
|
||||||
for e in rendered.entities:
|
|
||||||
d = dict(e)
|
|
||||||
if "language" in d and not isinstance(d["language"], str):
|
|
||||||
d.pop("language", None)
|
|
||||||
entities.append(d)
|
|
||||||
|
|
||||||
r = requests.post(
|
|
||||||
f"https://api.telegram.org/bot{bot_token}/sendMessage",
|
|
||||||
json={
|
|
||||||
"chat_id": chat_id,
|
|
||||||
"text": text,
|
|
||||||
"entities": entities,
|
|
||||||
"disable_web_page_preview": True,
|
|
||||||
},
|
|
||||||
timeout=15,
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
data = r.json()
|
|
||||||
except Exception:
|
|
||||||
data = {"ok": False, "description": r.text}
|
|
||||||
|
|
||||||
if not (r.status_code == 200 and data.get("ok") is True):
|
|
||||||
ERR_PATH.write_text(
|
|
||||||
f"{r.status_code}\n{data.get('description','')}\n",
|
|
||||||
encoding="utf-8",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
try:
|
|
||||||
main()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# Notify Telegram (Codex)
|
|
||||||
|
|
||||||
Send Codex completion summaries to Telegram with safe Markdown rendering and stable list bullets.
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
1. Ensure `uv` is installed.
|
|
||||||
2. Copy the script to `~/.codex/notify_telegram.py`.
|
|
||||||
3. Create your [Telegram creds](https://t.me/botfather) file at `~/.codex/telegram.toml`.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
bot_token = "123456:ABCDEF..."
|
|
||||||
chat_id = 462722
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configure
|
|
||||||
|
|
||||||
Add a `notify` entry to `~/.codex/config.toml`:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
notify = ["uv", "run", "-q", "/home/user/.codex/notify_telegram.py"]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- The script reads `last-assistant-message` and treats it as Markdown.
|
|
||||||
- Markdown is rendered to HTML, converted to Telegram text/entities via `sulguk`, then posted with `requests`.
|
|
||||||
- List bullets are normalized from `•` to `-` to keep Telegram output consistent.
|
|
||||||
|
|
||||||
## Files
|
|
||||||
|
|
||||||
- `notify_telegram.py`: the notifier script
|
|
||||||
@@ -1,6 +1,104 @@
|
|||||||
# Scripts
|
# Takopi
|
||||||
|
|
||||||
## Codex
|
> 🐙 A little helper from Happy Planet, here to make your Codex sessions happier-pi!
|
||||||
|
|
||||||
- [Notify Telegram](notify_telegram/readme.md) — Send Codex completion summaries to Telegram with safe Markdown rendering.
|
A Telegram bot that bridges messages to [Codex](https://github.com/openai/codex) sessions using non-interactive `codex exec` and `codex exec resume`.
|
||||||
- [Takopi](takopi/readme.md) — Route Telegram replies into Codex sessions (exec, MCP, or tmux).
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Stateless Resume**: No database required—sessions are resumed via `resume: <uuid>` lines embedded in messages
|
||||||
|
- **Progress Updates**: Real-time progress edits showing commands, tools, and elapsed time
|
||||||
|
- **Markdown Rendering**: Full Telegram-compatible markdown with entity support
|
||||||
|
- **Concurrency**: Handles multiple conversations with per-session serialization
|
||||||
|
- **Token Redaction**: Automatically redacts Telegram tokens from logs
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Python 3.12+
|
||||||
|
- [uv](https://github.com/astral-sh/uv) package manager
|
||||||
|
- Codex CLI on PATH
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone and enter the directory
|
||||||
|
cd takopi
|
||||||
|
|
||||||
|
# Run directly with uv (installs deps automatically)
|
||||||
|
uv run takopi --help
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
Create `~/.codex/telegram.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
bot_token = "123456789:ABCdefGHIjklMNOpqrsTUVwxyz"
|
||||||
|
chat_id = 123456789
|
||||||
|
```
|
||||||
|
|
||||||
|
| Key | Description |
|
||||||
|
|-----|-------------|
|
||||||
|
| `bot_token` | Telegram Bot API token from [@BotFather](https://t.me/BotFather) |
|
||||||
|
| `chat_id` | Allowed chat ID (also used for startup notifications) |
|
||||||
|
|
||||||
|
The bridge only accepts messages where the chat ID equals the sender ID and both match `chat_id` (i.e., private chat with that user).
|
||||||
|
|
||||||
|
### Running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv run takopi
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Options
|
||||||
|
|
||||||
|
| Flag | Default | Description |
|
||||||
|
|------|---------|-------------|
|
||||||
|
| `--final-notify` / `--no-final-notify` | `--final-notify` | Send final response as new message (vs. edit) |
|
||||||
|
| `--debug` / `--no-debug` | `--no-debug` | Enable verbose logging |
|
||||||
|
| `--cd PATH` | cwd | Working directory for Codex |
|
||||||
|
| `--model NAME` | (codex default) | Model to use |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### New Conversation
|
||||||
|
|
||||||
|
Send any message to your bot. The bridge will:
|
||||||
|
|
||||||
|
1. Send a silent progress message
|
||||||
|
2. Stream events from `codex exec`
|
||||||
|
3. Update progress every ~2 seconds
|
||||||
|
4. Send final response with session ID
|
||||||
|
|
||||||
|
### Resume a Session
|
||||||
|
|
||||||
|
Reply to a bot message (containing `resume: <uuid>`), or include the resume line in your message:
|
||||||
|
|
||||||
|
```
|
||||||
|
resume: `019b66fc-64c2-7a71-81cd-081c504cfeb2`
|
||||||
|
```
|
||||||
|
|
||||||
|
## Behavior Notes
|
||||||
|
|
||||||
|
- **Startup**: Pending updates are drained (ignored) on startup
|
||||||
|
- **Progress**: Updates are throttled to ~2s intervals, sent silently
|
||||||
|
- **Notifications**: Codex's built-in notify is disabled (bridge handles it)
|
||||||
|
- **Filtering**: Only accepts messages where chat ID equals sender ID and matches `chat_id`
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
See [`developing.md`](developing.md) for architecture details.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run tests
|
||||||
|
uv run pytest
|
||||||
|
|
||||||
|
# Run with debug logging
|
||||||
|
uv run takopi --debug 2>&1 | tee debug.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|||||||
@@ -1,104 +0,0 @@
|
|||||||
# Takopi
|
|
||||||
|
|
||||||
> 🐙 A little helper from Happy Planet, here to make your Codex sessions happier-pi!
|
|
||||||
|
|
||||||
A Telegram bot that bridges messages to [Codex](https://github.com/openai/codex) sessions using non-interactive `codex exec` and `codex exec resume`.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Stateless Resume**: No database required—sessions are resumed via `resume: <uuid>` lines embedded in messages
|
|
||||||
- **Progress Updates**: Real-time progress edits showing commands, tools, and elapsed time
|
|
||||||
- **Markdown Rendering**: Full Telegram-compatible markdown with entity support
|
|
||||||
- **Concurrency**: Handles multiple conversations with per-session serialization
|
|
||||||
- **Token Redaction**: Automatically redacts Telegram tokens from logs
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
### Prerequisites
|
|
||||||
|
|
||||||
- Python 3.12+
|
|
||||||
- [uv](https://github.com/astral-sh/uv) package manager
|
|
||||||
- Codex CLI on PATH
|
|
||||||
|
|
||||||
### Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Clone and enter the directory
|
|
||||||
cd takopi
|
|
||||||
|
|
||||||
# Run directly with uv (installs deps automatically)
|
|
||||||
uv run takopi --help
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
Create `~/.codex/telegram.toml`:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
bot_token = "123456789:ABCdefGHIjklMNOpqrsTUVwxyz"
|
|
||||||
chat_id = 123456789
|
|
||||||
```
|
|
||||||
|
|
||||||
| Key | Description |
|
|
||||||
|-----|-------------|
|
|
||||||
| `bot_token` | Telegram Bot API token from [@BotFather](https://t.me/BotFather) |
|
|
||||||
| `chat_id` | Allowed chat ID (also used for startup notifications) |
|
|
||||||
|
|
||||||
The bridge only accepts messages where the chat ID equals the sender ID and both match `chat_id` (i.e., private chat with that user).
|
|
||||||
|
|
||||||
### Running
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uv run takopi
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Options
|
|
||||||
|
|
||||||
| Flag | Default | Description |
|
|
||||||
|------|---------|-------------|
|
|
||||||
| `--final-notify` / `--no-final-notify` | `--final-notify` | Send final response as new message (vs. edit) |
|
|
||||||
| `--debug` / `--no-debug` | `--no-debug` | Enable verbose logging |
|
|
||||||
| `--cd PATH` | cwd | Working directory for Codex |
|
|
||||||
| `--model NAME` | (codex default) | Model to use |
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### New Conversation
|
|
||||||
|
|
||||||
Send any message to your bot. The bridge will:
|
|
||||||
|
|
||||||
1. Send a silent progress message
|
|
||||||
2. Stream events from `codex exec`
|
|
||||||
3. Update progress every ~2 seconds
|
|
||||||
4. Send final response with session ID
|
|
||||||
|
|
||||||
### Resume a Session
|
|
||||||
|
|
||||||
Reply to a bot message (containing `resume: <uuid>`), or include the resume line in your message:
|
|
||||||
|
|
||||||
```
|
|
||||||
resume: `019b66fc-64c2-7a71-81cd-081c504cfeb2`
|
|
||||||
```
|
|
||||||
|
|
||||||
## Behavior Notes
|
|
||||||
|
|
||||||
- **Startup**: Pending updates are drained (ignored) on startup
|
|
||||||
- **Progress**: Updates are throttled to ~2s intervals, sent silently
|
|
||||||
- **Notifications**: Codex's built-in notify is disabled (bridge handles it)
|
|
||||||
- **Filtering**: Only accepts messages where chat ID equals sender ID and matches `chat_id`
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
See [`developing.md`](developing.md) for architecture details.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Run tests
|
|
||||||
uv run pytest
|
|
||||||
|
|
||||||
# Run with debug logging
|
|
||||||
uv run takopi --debug 2>&1 | tee debug.log
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
MIT
|
|
||||||
Reference in New Issue
Block a user