sesigram 0.1.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.
@@ -0,0 +1,58 @@
1
+ Metadata-Version: 2.4
2
+ Name: sesigram
3
+ Version: 0.1.0
4
+ Summary: Universal Telegram CLI Wrapper for AI Assistants and Terminals
5
+ Requires-Python: >=3.8
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: requests
8
+
9
+ # Sesigram
10
+
11
+ **Sesigram** es un wrapper universal de terminal que te permite interactuar con cualquier asistente de IA de línea de comandos (como Claude Code, Aider, Antigravity, etc.) o cualquier programa interactivo desde Telegram.
12
+
13
+ Con Sesigram, puedes alejarte de tu computadora y seguir respondiendo o leyendo las salidas de la terminal en tiempo real desde tu celular.
14
+
15
+ ## Instalación
16
+
17
+ Puedes instalar Sesigram globalmente usando `pipx` (recomendado) o `pip`:
18
+
19
+ ```bash
20
+ pip install sesigram
21
+ ```
22
+
23
+ *(O puedes instalarlo localmente desde el código fuente con `pip install -e .`)*
24
+
25
+ ## Configuración
26
+
27
+ Una vez instalado, ejecuta el comando de configuración inicial:
28
+
29
+ ```bash
30
+ sesigram setup
31
+ ```
32
+
33
+ Sigue las instrucciones en pantalla para vincular tu bot de Telegram y tu chat ID.
34
+
35
+ ## Uso
36
+
37
+ Para usar Sesigram, simplemente **envuelve** el comando que quieres ejecutar con `sesigram run`:
38
+
39
+ ```bash
40
+ # Ejemplo con bash interactivo
41
+ sesigram run bash
42
+
43
+ # Ejemplo con Aider
44
+ sesigram run aider
45
+
46
+ # Ejemplo con Claude Code
47
+ sesigram run claude
48
+ ```
49
+
50
+ A partir de ese momento, la terminal arrancará normalmente pero todo el texto que imprima te llegará a Telegram. Si respondes a tu bot de Telegram, el mensaje se inyectará como si lo hubieras escrito en la consola.
51
+
52
+ Para cerrar la sesión de forma remota, envía `/stop_sesigram` en Telegram.
53
+
54
+ ## Características
55
+
56
+ - **Agnóstico**: No requiere plugins ni modificaciones en el programa subyacente. Utiliza Pseudo-Terminales (PTY) nativas de Python.
57
+ - **Sin Demonios**: No se queda corriendo en segundo plano. El puente hacia Telegram solo existe mientras dura el comando envuelto.
58
+ - **Smart Buffering**: Acumula las salidas rápidas para no hacer spam ni exceder el límite de mensajes de la API de Telegram.
@@ -0,0 +1,50 @@
1
+ # Sesigram
2
+
3
+ **Sesigram** es un wrapper universal de terminal que te permite interactuar con cualquier asistente de IA de línea de comandos (como Claude Code, Aider, Antigravity, etc.) o cualquier programa interactivo desde Telegram.
4
+
5
+ Con Sesigram, puedes alejarte de tu computadora y seguir respondiendo o leyendo las salidas de la terminal en tiempo real desde tu celular.
6
+
7
+ ## Instalación
8
+
9
+ Puedes instalar Sesigram globalmente usando `pipx` (recomendado) o `pip`:
10
+
11
+ ```bash
12
+ pip install sesigram
13
+ ```
14
+
15
+ *(O puedes instalarlo localmente desde el código fuente con `pip install -e .`)*
16
+
17
+ ## Configuración
18
+
19
+ Una vez instalado, ejecuta el comando de configuración inicial:
20
+
21
+ ```bash
22
+ sesigram setup
23
+ ```
24
+
25
+ Sigue las instrucciones en pantalla para vincular tu bot de Telegram y tu chat ID.
26
+
27
+ ## Uso
28
+
29
+ Para usar Sesigram, simplemente **envuelve** el comando que quieres ejecutar con `sesigram run`:
30
+
31
+ ```bash
32
+ # Ejemplo con bash interactivo
33
+ sesigram run bash
34
+
35
+ # Ejemplo con Aider
36
+ sesigram run aider
37
+
38
+ # Ejemplo con Claude Code
39
+ sesigram run claude
40
+ ```
41
+
42
+ A partir de ese momento, la terminal arrancará normalmente pero todo el texto que imprima te llegará a Telegram. Si respondes a tu bot de Telegram, el mensaje se inyectará como si lo hubieras escrito en la consola.
43
+
44
+ Para cerrar la sesión de forma remota, envía `/stop_sesigram` en Telegram.
45
+
46
+ ## Características
47
+
48
+ - **Agnóstico**: No requiere plugins ni modificaciones en el programa subyacente. Utiliza Pseudo-Terminales (PTY) nativas de Python.
49
+ - **Sin Demonios**: No se queda corriendo en segundo plano. El puente hacia Telegram solo existe mientras dura el comando envuelto.
50
+ - **Smart Buffering**: Acumula las salidas rápidas para no hacer spam ni exceder el límite de mensajes de la API de Telegram.
@@ -0,0 +1,16 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "sesigram"
7
+ version = "0.1.0"
8
+ description = "Universal Telegram CLI Wrapper for AI Assistants and Terminals"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ dependencies = [
12
+ "requests",
13
+ ]
14
+
15
+ [project.scripts]
16
+ sesigram = "sesigram.cli:main"
@@ -0,0 +1,4 @@
1
+ """
2
+ Sesigram - Universal Telegram CLI Wrapper
3
+ """
4
+ __version__ = "0.1.0"
@@ -0,0 +1,108 @@
1
+ import sys
2
+ import time
3
+ from .telegram_api import get_updates, send_message, load_config, save_config
4
+ from .wrapper import TelegramWrapper
5
+
6
+ def print_step(text):
7
+ print(f"\n\033[1m> {text}\033[0m")
8
+
9
+ def print_ok(text):
10
+ print(f" \033[32m✓\033[0m {text}")
11
+
12
+ def print_err(text):
13
+ print(f" \033[31m✗\033[0m {text}")
14
+
15
+ def setup():
16
+ print("\n\033[1m🤖 Sesigram - Setup\033[0m")
17
+ print("=" * 45)
18
+
19
+ print("\nPaso 1: Crear tu bot de Telegram")
20
+ print("1. Abre Telegram y busca @BotFather")
21
+ print("2. Escribe /newbot")
22
+ print("3. Ponle un nombre y un username")
23
+ print("4. Copia el Token que te da al final")
24
+
25
+ token = input("\nPega aquí tu token: ").strip()
26
+ if not token:
27
+ print_err("Token inválido.")
28
+ sys.exit(1)
29
+
30
+ print_step("Paso 2: Conectar tu cuenta de Telegram")
31
+ print("1. Abre Telegram en tu móvil o escritorio")
32
+ print("2. Busca el bot que acabas de crear")
33
+ print("3. Pulsa START o escribe /start")
34
+ print("4. Espera (detección automática en curso...)")
35
+
36
+ chat_id = None
37
+ sys.stdout.write("\n Esperando que escribas a tu bot...")
38
+ sys.stdout.flush()
39
+
40
+ # Poll for messages to find chat_id
41
+ for _ in range(30): # 30 seconds wait
42
+ updates = get_updates(token, timeout=1)
43
+ if updates:
44
+ for update in updates:
45
+ if "message" in update:
46
+ msg = update["message"]
47
+ if msg.get("text") == "/start":
48
+ chat_id = msg["chat"]["id"]
49
+ first_name = msg["from"].get("first_name", "Usuario")
50
+ print(f"\n ✓ Conectado con {first_name} (ID: {chat_id})")
51
+ break
52
+ if chat_id:
53
+ break
54
+ sys.stdout.write(".")
55
+ sys.stdout.flush()
56
+ time.sleep(1)
57
+
58
+ if not chat_id:
59
+ print("\n\n ⚠ Timeout. Introduce tu chat_id manualmente.")
60
+ try:
61
+ chat_id = int(input(" Chat ID: ").strip())
62
+ except ValueError:
63
+ print_err("ID inválido. Abortando.")
64
+ sys.exit(1)
65
+
66
+ save_config(token, chat_id)
67
+ print_ok("Config guardada")
68
+
69
+ send_message(token, chat_id, "🎉 Sesigram vinculado correctamente en esta máquina.")
70
+ print_ok("Mensaje de prueba enviado a Telegram")
71
+
72
+ print("\n🎉 Setup completo!")
73
+ print("\nPara usar Sesigram, simplemente envuelve cualquier comando:")
74
+ print(" sesigram run bash")
75
+ print(" sesigram run claude")
76
+ print(" sesigram run python\n")
77
+
78
+ def main():
79
+ if len(sys.argv) < 2 or sys.argv[1] in ["--help", "-h", "help"]:
80
+ print("Uso: sesigram <comando>")
81
+ print(" sesigram setup -> Configura tu Bot de Telegram")
82
+ print(" sesigram run <comando> -> Ejecuta un comando envolviéndolo con Telegram")
83
+ sys.exit(1 if len(sys.argv) < 2 else 0)
84
+
85
+ command = sys.argv[1]
86
+
87
+ if command == "setup":
88
+ setup()
89
+ elif command == "run":
90
+ if len(sys.argv) < 3:
91
+ print("Error: falta el comando a ejecutar.")
92
+ print("Uso: sesigram run <comando>")
93
+ sys.exit(1)
94
+
95
+ config = load_config()
96
+ if not config:
97
+ print("Error: Sesigram no está configurado. Ejecuta 'sesigram setup' primero.")
98
+ sys.exit(1)
99
+
100
+ argv = sys.argv[2:]
101
+ wrapper = TelegramWrapper(config)
102
+ wrapper.run(argv)
103
+ else:
104
+ print(f"Comando desconocido: {command}")
105
+ sys.exit(1)
106
+
107
+ if __name__ == "__main__":
108
+ main()
@@ -0,0 +1,78 @@
1
+ import json
2
+ import socket
3
+ import requests
4
+ from pathlib import Path
5
+
6
+ CONFIG_FILE = Path.home() / ".sesigram-config"
7
+
8
+ def load_config():
9
+ try:
10
+ if CONFIG_FILE.exists():
11
+ with open(CONFIG_FILE, 'r') as f:
12
+ return json.load(f)
13
+ except Exception:
14
+ pass
15
+ return None
16
+
17
+ def save_config(token, chat_id, machine_name=None):
18
+ if not machine_name:
19
+ machine_name = socket.gethostname()
20
+ config = {
21
+ "bot_token": token,
22
+ "chat_id": chat_id,
23
+ "machine_name": machine_name
24
+ }
25
+ with open(CONFIG_FILE, 'w') as f:
26
+ json.dump(config, f, indent=2)
27
+
28
+ def escape_md(text):
29
+ """Escape MarkdownV2 special characters."""
30
+ special = r"_*[]()~`>#+-=|{}.!"
31
+ for c in special:
32
+ text = text.replace(c, f"\\{c}")
33
+ return text
34
+
35
+ def send_message(token, chat_id, text, machine_name=None, disable_notification=True):
36
+ if not text.strip():
37
+ return
38
+
39
+ if len(text) > 3800:
40
+ text = text[:3800] + "\n\n[... mensaje truncado ...]"
41
+
42
+ header = ""
43
+ if machine_name:
44
+ header = f"🖥️ *{escape_md(machine_name)}*\n\n"
45
+
46
+ full_msg = header + escape_md(text)
47
+
48
+ try:
49
+ requests.post(
50
+ f"https://api.telegram.org/bot{token}/sendMessage",
51
+ json={
52
+ "chat_id": chat_id,
53
+ "text": full_msg,
54
+ "parse_mode": "MarkdownV2",
55
+ "disable_notification": disable_notification
56
+ },
57
+ timeout=10
58
+ )
59
+ except Exception:
60
+ pass
61
+
62
+ def get_updates(token, offset=None, timeout=30):
63
+ """Long polling for new messages."""
64
+ params = {"timeout": timeout}
65
+ if offset:
66
+ params["offset"] = offset
67
+
68
+ try:
69
+ response = requests.get(
70
+ f"https://api.telegram.org/bot{token}/getUpdates",
71
+ params=params,
72
+ timeout=timeout + 5
73
+ )
74
+ if response.status_code == 200:
75
+ return response.json().get("result", [])
76
+ except Exception:
77
+ pass
78
+ return []
@@ -0,0 +1,122 @@
1
+ import os
2
+ import pty
3
+ import sys
4
+ import threading
5
+ import time
6
+ import re
7
+ from .telegram_api import get_updates, send_message
8
+
9
+ ANSI_ESCAPE_RE = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
10
+
11
+ class TelegramWrapper:
12
+ def __init__(self, config):
13
+ self.config = config
14
+ self.master_fd = None
15
+ self.output_buffer = ""
16
+ self.last_send_time = time.time()
17
+ self.buffer_lock = threading.Lock()
18
+ self.is_running = True
19
+
20
+ def _strip_ansi(self, text):
21
+ return ANSI_ESCAPE_RE.sub('', text)
22
+
23
+ def master_read(self, fd):
24
+ """Called by pty.spawn when there is output from the child process."""
25
+ data = os.read(fd, 1024)
26
+ text = data.decode('utf-8', errors='replace')
27
+
28
+ with self.buffer_lock:
29
+ self.output_buffer += text
30
+
31
+ return data
32
+
33
+ def telegram_sender_loop(self):
34
+ """Periodically sends buffered output to Telegram."""
35
+ while self.is_running:
36
+ time.sleep(1.0) # Check every second
37
+ with self.buffer_lock:
38
+ if not self.output_buffer:
39
+ continue
40
+
41
+ # Only send if there's a pause in output or buffer is large
42
+ current_time = time.time()
43
+ if current_time - self.last_send_time > 1.5 or len(self.output_buffer) > 2000:
44
+ text_to_send = self._strip_ansi(self.output_buffer).strip()
45
+ if text_to_send:
46
+ send_message(
47
+ self.config["bot_token"],
48
+ self.config["chat_id"],
49
+ text_to_send,
50
+ self.config["machine_name"],
51
+ disable_notification=True
52
+ )
53
+ self.output_buffer = ""
54
+ self.last_send_time = current_time
55
+
56
+ def telegram_polling_loop(self):
57
+ """Polls Telegram for new messages and injects them into the PTY."""
58
+ offset = None
59
+ while self.is_running:
60
+ updates = get_updates(self.config["bot_token"], offset=offset, timeout=10)
61
+ for update in updates:
62
+ offset = update["update_id"] + 1
63
+ if "message" in update and "text" in update["message"]:
64
+ # Only accept messages from the authorized chat_id
65
+ if str(update["message"]["chat"]["id"]) == str(self.config["chat_id"]):
66
+ text = update["message"]["text"]
67
+
68
+ # System command check
69
+ if text.strip().lower() == "/stop_sesigram":
70
+ send_message(
71
+ self.config["bot_token"],
72
+ self.config["chat_id"],
73
+ "🛑 Cerrando sesión remota.",
74
+ self.config["machine_name"]
75
+ )
76
+ # Escribir Ctrl+C y Exit para intentar matar el child
77
+ if self.master_fd:
78
+ os.write(self.master_fd, b'\x03exit\n')
79
+ continue
80
+
81
+ # Inject into PTY
82
+ if self.master_fd:
83
+ # Escribir el texto seguido de un salto de línea
84
+ payload = (text + "\n").encode('utf-8')
85
+ os.write(self.master_fd, payload)
86
+
87
+ time.sleep(0.5)
88
+
89
+ def run(self, argv):
90
+ # Setup threads
91
+ sender_thread = threading.Thread(target=self.telegram_sender_loop, daemon=True)
92
+ polling_thread = threading.Thread(target=self.telegram_polling_loop, daemon=True)
93
+
94
+ sender_thread.start()
95
+ polling_thread.start()
96
+
97
+ def _spawn_read(fd):
98
+ if self.master_fd is None:
99
+ self.master_fd = fd
100
+ return self.master_read(fd)
101
+
102
+ print(f"🚀 Iniciando Sesigram en modo remoto para: {' '.join(argv)}")
103
+ send_message(
104
+ self.config["bot_token"],
105
+ self.config["chat_id"],
106
+ f"🚀 Sesión iniciada: `{' '.join(argv)}`",
107
+ self.config["machine_name"]
108
+ )
109
+
110
+ try:
111
+ # pty.spawn bloquea hasta que el subproceso termine
112
+ pty.spawn(argv, master_read=_spawn_read)
113
+ except Exception as e:
114
+ print(f"Error en pty.spawn: {e}")
115
+ finally:
116
+ self.is_running = False
117
+ send_message(
118
+ self.config["bot_token"],
119
+ self.config["chat_id"],
120
+ f"🛑 Sesión terminada: `{' '.join(argv)}`",
121
+ self.config["machine_name"]
122
+ )
@@ -0,0 +1,58 @@
1
+ Metadata-Version: 2.4
2
+ Name: sesigram
3
+ Version: 0.1.0
4
+ Summary: Universal Telegram CLI Wrapper for AI Assistants and Terminals
5
+ Requires-Python: >=3.8
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: requests
8
+
9
+ # Sesigram
10
+
11
+ **Sesigram** es un wrapper universal de terminal que te permite interactuar con cualquier asistente de IA de línea de comandos (como Claude Code, Aider, Antigravity, etc.) o cualquier programa interactivo desde Telegram.
12
+
13
+ Con Sesigram, puedes alejarte de tu computadora y seguir respondiendo o leyendo las salidas de la terminal en tiempo real desde tu celular.
14
+
15
+ ## Instalación
16
+
17
+ Puedes instalar Sesigram globalmente usando `pipx` (recomendado) o `pip`:
18
+
19
+ ```bash
20
+ pip install sesigram
21
+ ```
22
+
23
+ *(O puedes instalarlo localmente desde el código fuente con `pip install -e .`)*
24
+
25
+ ## Configuración
26
+
27
+ Una vez instalado, ejecuta el comando de configuración inicial:
28
+
29
+ ```bash
30
+ sesigram setup
31
+ ```
32
+
33
+ Sigue las instrucciones en pantalla para vincular tu bot de Telegram y tu chat ID.
34
+
35
+ ## Uso
36
+
37
+ Para usar Sesigram, simplemente **envuelve** el comando que quieres ejecutar con `sesigram run`:
38
+
39
+ ```bash
40
+ # Ejemplo con bash interactivo
41
+ sesigram run bash
42
+
43
+ # Ejemplo con Aider
44
+ sesigram run aider
45
+
46
+ # Ejemplo con Claude Code
47
+ sesigram run claude
48
+ ```
49
+
50
+ A partir de ese momento, la terminal arrancará normalmente pero todo el texto que imprima te llegará a Telegram. Si respondes a tu bot de Telegram, el mensaje se inyectará como si lo hubieras escrito en la consola.
51
+
52
+ Para cerrar la sesión de forma remota, envía `/stop_sesigram` en Telegram.
53
+
54
+ ## Características
55
+
56
+ - **Agnóstico**: No requiere plugins ni modificaciones en el programa subyacente. Utiliza Pseudo-Terminales (PTY) nativas de Python.
57
+ - **Sin Demonios**: No se queda corriendo en segundo plano. El puente hacia Telegram solo existe mientras dura el comando envuelto.
58
+ - **Smart Buffering**: Acumula las salidas rápidas para no hacer spam ni exceder el límite de mensajes de la API de Telegram.
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ sesigram/__init__.py
4
+ sesigram/cli.py
5
+ sesigram/telegram_api.py
6
+ sesigram/wrapper.py
7
+ sesigram.egg-info/PKG-INFO
8
+ sesigram.egg-info/SOURCES.txt
9
+ sesigram.egg-info/dependency_links.txt
10
+ sesigram.egg-info/entry_points.txt
11
+ sesigram.egg-info/requires.txt
12
+ sesigram.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ sesigram = sesigram.cli:main
@@ -0,0 +1 @@
1
+ requests
@@ -0,0 +1 @@
1
+ sesigram
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+