From 147e8e41f3c69add9f4a686a781c11e5956622e1 Mon Sep 17 00:00:00 2001 From: Jean Galea <1651502+jgalea@users.noreply.github.com> Date: Sun, 15 Mar 2026 02:42:05 +0100 Subject: [PATCH 1/3] feat: add edit and delete commands Add `tg edit CHAT MSG_ID NEW_TEXT` to edit previously sent messages and `tg delete CHAT MSG_ID [MSG_ID ...]` to delete one or more messages. Both commands follow the existing structured output pattern (--json/--yaml). Co-Authored-By: Jean Galea <1651502+jgalea@users.noreply.github.com> --- src/tg_cli/cli/tg.py | 47 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/tg_cli/cli/tg.py b/src/tg_cli/cli/tg.py index c5f7076..cfc0da8 100644 --- a/src/tg_cli/cli/tg.py +++ b/src/tg_cli/cli/tg.py @@ -1,4 +1,4 @@ -"""Telegram subcommands — chats, history, sync, refresh, listen, info, whoami, status, send.""" +"""Telegram subcommands — chats, history, sync, refresh, listen, info, whoami, status, send, edit, delete.""" import asyncio import time @@ -412,3 +412,48 @@ async def _run(): if emit_structured(payload, as_json=as_json, as_yaml=as_yaml): return console.print(f"[green]\u2713[/green] Message sent (id: {msg.id})") + + +@tg_group.command("edit") +@click.argument("chat") +@click.argument("msg_id", type=int) +@click.argument("new_text") +@structured_output_options +def tg_edit(chat: str, msg_id: int, new_text: str, as_json: bool, as_yaml: bool): + """Edit a previously sent message. CHAT MSG_ID NEW_TEXT.""" + + async def _run(): + from telethon.tl.functions.messages import EditMessageRequest + + async with connect() as client: + entity = await client.get_input_entity(_parse_chat(chat)) + await client(EditMessageRequest( + peer=entity, + id=msg_id, + message=new_text, + )) + + asyncio.run(_run()) + payload = {"edited": True, "msg_id": msg_id, "chat": chat} + if emit_structured(payload, as_json=as_json, as_yaml=as_yaml): + return + console.print(f"[green]\u2713[/green] Message {msg_id} edited") + + +@tg_group.command("delete") +@click.argument("chat") +@click.argument("msg_ids", nargs=-1, type=int, required=True) +@structured_output_options +def tg_delete(chat: str, msg_ids: tuple[int, ...], as_json: bool, as_yaml: bool): + """Delete one or more messages. CHAT MSG_ID [MSG_ID ...].""" + + async def _run(): + async with connect() as client: + entity = await client.get_entity(_parse_chat(chat)) + await client.delete_messages(entity, list(msg_ids)) + + asyncio.run(_run()) + payload = {"deleted": True, "msg_ids": list(msg_ids), "chat": chat} + if emit_structured(payload, as_json=as_json, as_yaml=as_yaml): + return + console.print(f"[green]\u2713[/green] Deleted {len(msg_ids)} message(s)") From ae5d71195547e6e977b3e3d18f3a2ad334d709d2 Mon Sep 17 00:00:00 2001 From: Jean Galea <1651502+jgalea@users.noreply.github.com> Date: Sun, 15 Mar 2026 02:44:45 +0100 Subject: [PATCH 2/3] feat: add --no-preview flag to send and edit commands Allows disabling Telegram's automatic link preview card when sending or editing messages containing URLs. Co-Authored-By: Jean Galea <1651502+jgalea@users.noreply.github.com> --- src/tg_cli/cli/tg.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/tg_cli/cli/tg.py b/src/tg_cli/cli/tg.py index cfc0da8..f67f554 100644 --- a/src/tg_cli/cli/tg.py +++ b/src/tg_cli/cli/tg.py @@ -396,13 +396,16 @@ async def _run(): @click.argument("chat") @click.argument("message") @click.option("-r", "--reply", type=int, default=None, help="Message ID to reply to") +@click.option("--no-preview", is_flag=True, help="Disable link preview") @structured_output_options -def tg_send(chat: str, message: str, reply: int | None, as_json: bool, as_yaml: bool): +def tg_send(chat: str, message: str, reply: int | None, no_preview: bool, as_json: bool, as_yaml: bool): """Send a MESSAGE to CHAT (name, username, or numeric ID).""" async def _run(): async with connect() as client: - msg = await client.send_message(_parse_chat(chat), message, reply_to=reply) + msg = await client.send_message( + _parse_chat(chat), message, reply_to=reply, link_preview=not no_preview, + ) return msg msg = asyncio.run(_run()) @@ -418,19 +421,25 @@ async def _run(): @click.argument("chat") @click.argument("msg_id", type=int) @click.argument("new_text") +@click.option("--no-preview", is_flag=True, help="Disable link preview") @structured_output_options -def tg_edit(chat: str, msg_id: int, new_text: str, as_json: bool, as_yaml: bool): +def tg_edit(chat: str, msg_id: int, new_text: str, no_preview: bool, as_json: bool, as_yaml: bool): """Edit a previously sent message. CHAT MSG_ID NEW_TEXT.""" async def _run(): from telethon.tl.functions.messages import EditMessageRequest + kwargs = {} + if no_preview: + kwargs["no_webpage"] = True + async with connect() as client: entity = await client.get_input_entity(_parse_chat(chat)) await client(EditMessageRequest( peer=entity, id=msg_id, message=new_text, + **kwargs, )) asyncio.run(_run()) From 7b2eab4f6c647746febb229695aedeccc9bd176f Mon Sep 17 00:00:00 2001 From: Jean Galea <1651502+jgalea@users.noreply.github.com> Date: Sun, 15 Mar 2026 03:20:11 +0100 Subject: [PATCH 3/3] fix: resolve ruff E501 line-too-long errors Co-Authored-By: Jean Galea <1651502+jgalea@users.noreply.github.com> --- src/tg_cli/cli/tg.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tg_cli/cli/tg.py b/src/tg_cli/cli/tg.py index f67f554..f1c06ea 100644 --- a/src/tg_cli/cli/tg.py +++ b/src/tg_cli/cli/tg.py @@ -1,4 +1,4 @@ -"""Telegram subcommands — chats, history, sync, refresh, listen, info, whoami, status, send, edit, delete.""" +"""Telegram subcommands — send, edit, delete, and more.""" import asyncio import time @@ -398,7 +398,10 @@ async def _run(): @click.option("-r", "--reply", type=int, default=None, help="Message ID to reply to") @click.option("--no-preview", is_flag=True, help="Disable link preview") @structured_output_options -def tg_send(chat: str, message: str, reply: int | None, no_preview: bool, as_json: bool, as_yaml: bool): +def tg_send( + chat: str, message: str, reply: int | None, + no_preview: bool, as_json: bool, as_yaml: bool, +): """Send a MESSAGE to CHAT (name, username, or numeric ID).""" async def _run():