diff --git a/src/database/client.py b/src/database/client.py
index 2a4b5f9..44181d8 100644
--- a/src/database/client.py
+++ b/src/database/client.py
@@ -1613,6 +1613,12 @@ class DatabaseClient:
lambda: self.client.table("email_action_tokens").update({"used": True}).eq("token", token).execute()
)
+ async def mark_all_post_tokens_used(self, post_id: UUID) -> None:
+ """Mark all email action tokens for a post as used (invalidate approve + reject together)."""
+ await asyncio.to_thread(
+ lambda: self.client.table("email_action_tokens").update({"used": True}).eq("post_id", str(post_id)).execute()
+ )
+
async def cleanup_expired_email_tokens(self) -> None:
"""Delete expired email tokens from the database."""
now = datetime.now(timezone.utc).isoformat()
diff --git a/src/services/telegram_service.py b/src/services/telegram_service.py
index 5d98dd6..0b084f3 100644
--- a/src/services/telegram_service.py
+++ b/src/services/telegram_service.py
@@ -336,6 +336,13 @@ class TelegramService:
async def _handle_callback_query(self, chat_id: str, cb_id: str, data: str, message_id: int, db) -> None:
"""Handle inline keyboard button presses."""
+ # approve/reject callbacks are token-authenticated — no account lookup needed
+ if data.startswith("approve:") or data.startswith("reject:"):
+ action, token = data.split(":", 1)
+ await self._handle_approval_callback(chat_id, message_id, action, token, db)
+ return
+
+ # All other callbacks require a linked account
tg_account = await db.get_telegram_account_by_chat_id(chat_id)
if not tg_account:
return
@@ -356,6 +363,49 @@ class TelegramService:
await self._clear_conv(chat_id)
await self.send_message(chat_id, "🔄 Alles klar! Schreib mir dein neues Thema.")
+ async def _handle_approval_callback(
+ self, chat_id: str, message_id: int, action: str, token: str, db
+ ) -> None:
+ """Handle approve/reject callback from inline keyboard buttons."""
+ from src.services.email_service import validate_token
+ from uuid import UUID
+
+ token_data = await validate_token(token)
+ if not token_data:
+ await self.edit_message(
+ chat_id, message_id,
+ "❌ Dieser Link ist ungültig oder wurde bereits verwendet."
+ )
+ return
+
+ post_id = UUID(token_data["post_id"])
+ post = await db.get_generated_post(post_id)
+ if not post:
+ await self.edit_message(chat_id, message_id, "❌ Post nicht gefunden.")
+ return
+
+ if action == "approve":
+ new_status = "ready"
+ result_text = (
+ f"✅ Post freigegeben!\n\n"
+ f"{post.topic_title or 'Post'}\n\n"
+ f"Der Post kann jetzt eingeplant werden."
+ )
+ else:
+ new_status = "draft"
+ result_text = (
+ f"❌ Post abgelehnt.\n\n"
+ f"{post.topic_title or 'Post'}\n\n"
+ f"Der Post wurde zurück in die Entwürfe verschoben."
+ )
+
+ await db.update_generated_post(post_id, {"status": new_status})
+ # Invalidate both approve + reject tokens for this post
+ await db.mark_all_post_tokens_used(post_id)
+
+ # Edit the original message to show the result (removes buttons)
+ await self.edit_message(chat_id, message_id, result_text)
+
async def _handle_post_type_selected(
self, chat_id: str, user_id: str, post_type_id_str: str, conv: dict, message_id: int, db
) -> None:
diff --git a/src/web/user/routes.py b/src/web/user/routes.py
index f5fd7b7..2f2d479 100644
--- a/src/web/user/routes.py
+++ b/src/web/user/routes.py
@@ -2328,13 +2328,12 @@ async def update_post_status(
chat_id=telegram_account.telegram_chat_id,
text=(
f"📋 {company_name} hat deinen Post bearbeitet und bittet um deine Freigabe:\n\n"
- f"{post.topic_title or 'Untitled Post'}\n\n"
- f"Bitte gib den Post frei oder lehne ihn ab:"
+ f"{post.topic_title or 'Untitled Post'}"
),
reply_markup={
"inline_keyboard": [[
- {"text": "✅ Freigeben", "url": approve_url},
- {"text": "❌ Ablehnen", "url": reject_url}
+ {"text": "✅ Freigeben", "callback_data": f"approve:{approve_token}"},
+ {"text": "❌ Ablehnen", "callback_data": f"reject:{reject_token}"}
]]
}
)
@@ -2453,8 +2452,8 @@ async def handle_email_action(request: Request, token: str):
# Update post status
await db.update_generated_post(post_id, {"status": new_status})
- # Mark token as used
- await mark_token_used(token)
+ # Invalidate ALL tokens for this post (both approve + reject)
+ await db.mark_all_post_tokens_used(post_id)
# Send notification to creator
if profile and profile.creator_email: