ozellik: telegram uzerinden drive yukleme ve clean_chat komutunu ekle
This commit is contained in:
@@ -1,13 +1,17 @@
|
||||
import asyncio
|
||||
import json
|
||||
from contextlib import suppress
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from telegram import BotCommand, InputMediaPhoto, Update
|
||||
from telegram.constants import ChatAction
|
||||
from telegram.ext import Application, CommandHandler, ContextTypes, MessageHandler, filters
|
||||
|
||||
from app.db import AuditLogORM
|
||||
from app.orchestrator import WiseClawOrchestrator
|
||||
from app.telegram.auth import is_authorized
|
||||
from app.tools.registry import build_tools
|
||||
|
||||
|
||||
class TelegramBotService:
|
||||
@@ -75,6 +79,7 @@ class TelegramBotService:
|
||||
return
|
||||
self.application = Application.builder().token(self.token).build()
|
||||
self.application.add_handler(CommandHandler("start", self._on_start))
|
||||
self.application.add_handler(CommandHandler("clean_chat", self._on_clean_chat))
|
||||
self.application.add_handler(CommandHandler("tanisalim", self._on_command_passthrough))
|
||||
self.application.add_handler(CommandHandler("profilim", self._on_command_passthrough))
|
||||
self.application.add_handler(CommandHandler("tercihlerim", self._on_command_passthrough))
|
||||
@@ -85,6 +90,7 @@ class TelegramBotService:
|
||||
self.application.add_handler(CommandHandler("otomasyon_baslat", self._on_command_passthrough))
|
||||
self.application.add_handler(CommandHandler("otomasyon_sil", self._on_command_passthrough))
|
||||
self.application.add_handler(CommandHandler("notlarima_ekle", self._on_command_passthrough))
|
||||
self.application.add_handler(MessageHandler(filters.Document.ALL | filters.PHOTO, self._on_attachment))
|
||||
self.application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, self._on_text))
|
||||
await self.application.initialize()
|
||||
await self.application.bot.set_my_commands(self._telegram_commands())
|
||||
@@ -110,6 +116,8 @@ class TelegramBotService:
|
||||
async def _on_text(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
if update.message is None or update.effective_user is None or update.message.text is None:
|
||||
return
|
||||
if await self._maybe_handle_drive_upload_from_reply(update, context):
|
||||
return
|
||||
typing_task = asyncio.create_task(self._send_typing(update.effective_chat.id, context))
|
||||
try:
|
||||
reply = await self.process_message_payload(update.effective_user.id, update.message.text)
|
||||
@@ -127,6 +135,36 @@ class TelegramBotService:
|
||||
for chunk in self._chunk_message(text_reply):
|
||||
await update.message.reply_text(chunk)
|
||||
|
||||
async def _on_attachment(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
if update.message is None or update.effective_user is None:
|
||||
return
|
||||
if not self._message_has_supported_attachment(update.message):
|
||||
return
|
||||
if self._looks_like_drive_upload_request(update.message.caption or ""):
|
||||
await self._handle_drive_upload(update, context, update.message)
|
||||
return
|
||||
await update.message.reply_text(
|
||||
"Dosyayi aldim. Google Drive'a yuklemek icin bu mesaja reply yapip `Bunu Google Drive'a yukle` yazabilirsin.",
|
||||
)
|
||||
|
||||
async def _on_clean_chat(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
if update.message is None or update.effective_chat is None:
|
||||
return
|
||||
|
||||
chat_id = update.effective_chat.id
|
||||
latest_message_id = update.message.message_id
|
||||
consecutive_failures = 0
|
||||
|
||||
for message_id in range(latest_message_id, 0, -1):
|
||||
try:
|
||||
await context.bot.delete_message(chat_id=chat_id, message_id=message_id)
|
||||
consecutive_failures = 0
|
||||
except Exception:
|
||||
consecutive_failures += 1
|
||||
if consecutive_failures >= 50:
|
||||
break
|
||||
continue
|
||||
|
||||
async def _on_command_passthrough(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
del context
|
||||
if update.message is None or update.effective_user is None or update.message.text is None:
|
||||
@@ -147,6 +185,119 @@ class TelegramBotService:
|
||||
await context.bot.send_chat_action(chat_id=chat_id, action=ChatAction.TYPING)
|
||||
await asyncio.sleep(4)
|
||||
|
||||
async def _maybe_handle_drive_upload_from_reply(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> bool:
|
||||
if update.message is None or update.effective_user is None or update.message.text is None:
|
||||
return False
|
||||
if not self._looks_like_drive_upload_request(update.message.text):
|
||||
return False
|
||||
reply_target = update.message.reply_to_message
|
||||
if reply_target is None or not self._message_has_supported_attachment(reply_target):
|
||||
await update.message.reply_text(
|
||||
"Google Drive'a yuklemek icin once dosya veya fotograf gonder, sonra o mesaja reply yapip `Bunu Google Drive'a yukle` yaz.",
|
||||
)
|
||||
return True
|
||||
await self._handle_drive_upload(update, context, reply_target)
|
||||
return True
|
||||
|
||||
async def _handle_drive_upload(self, update: Update, context: ContextTypes.DEFAULT_TYPE, source_message: Any) -> None:
|
||||
if update.message is None or update.effective_user is None or update.effective_chat is None:
|
||||
return
|
||||
|
||||
with self.orchestrator_factory() as session:
|
||||
if not is_authorized(session, update.effective_user.id):
|
||||
await update.message.reply_text("This Telegram user is not authorized for WiseClaw.")
|
||||
return
|
||||
|
||||
orchestrator = WiseClawOrchestrator(session)
|
||||
runtime = orchestrator.get_runtime_settings()
|
||||
tools = build_tools(runtime, Path(__file__).resolve().parents[2], session)
|
||||
drive_tool = tools.get("google_drive")
|
||||
if drive_tool is None:
|
||||
await update.message.reply_text("Google Drive araci etkin degil.")
|
||||
return
|
||||
|
||||
temp_file = None
|
||||
try:
|
||||
attachment = await self._download_attachment(context, update.effective_chat.id, source_message)
|
||||
if attachment is None:
|
||||
await update.message.reply_text("Bu mesajdan yuklenebilir bir dosya cikarilamadi.")
|
||||
return
|
||||
temp_file = attachment["local_path"]
|
||||
result = await drive_tool.run(
|
||||
{
|
||||
"action": "upload",
|
||||
"local_path": attachment["local_path"],
|
||||
"filename": attachment["filename"],
|
||||
"mime_type": attachment["mime_type"],
|
||||
}
|
||||
)
|
||||
session.add(
|
||||
AuditLogORM(
|
||||
category="tool",
|
||||
message=f"tool:google_drive:{json.dumps({'action': 'upload', 'filename': attachment['filename']}, ensure_ascii=False)}",
|
||||
)
|
||||
)
|
||||
if result.get("status") != "ok":
|
||||
message = str(result.get("message", "Google Drive upload failed."))
|
||||
await update.message.reply_text(f"Dosyayi Google Drive'a yukleyemedim: {message}")
|
||||
return
|
||||
|
||||
file_info = result.get("file", {})
|
||||
if not isinstance(file_info, dict):
|
||||
file_info = {}
|
||||
link = str(file_info.get("web_view_link") or file_info.get("web_content_link") or "").strip()
|
||||
file_id = str(file_info.get("id", "")).strip()
|
||||
name = str(file_info.get("name", attachment["filename"])).strip()
|
||||
response_lines = [f"Dosya Google Drive'a yuklendi: {name}"]
|
||||
if link:
|
||||
response_lines.append(f"Link: {link}")
|
||||
if file_id:
|
||||
response_lines.append(f"Dosya ID: {file_id}")
|
||||
await update.message.reply_text("\n".join(response_lines))
|
||||
finally:
|
||||
if temp_file:
|
||||
with suppress(OSError):
|
||||
Path(temp_file).unlink()
|
||||
session.commit()
|
||||
|
||||
async def _download_attachment(self, context: ContextTypes.DEFAULT_TYPE, chat_id: int, message: Any) -> dict[str, str] | None:
|
||||
if getattr(message, "document", None) is not None:
|
||||
document = message.document
|
||||
tg_file = await context.bot.get_file(document.file_id)
|
||||
filename = document.file_name or f"telegram_document_{message.message_id}"
|
||||
mime_type = document.mime_type or "application/octet-stream"
|
||||
elif getattr(message, "photo", None):
|
||||
photo = message.photo[-1]
|
||||
tg_file = await context.bot.get_file(photo.file_id)
|
||||
filename = f"telegram_photo_{message.message_id}.jpg"
|
||||
mime_type = "image/jpeg"
|
||||
else:
|
||||
return None
|
||||
|
||||
temp_dir = Path(__file__).resolve().parents[2] / "tmp" / "telegram_uploads"
|
||||
temp_dir.mkdir(parents=True, exist_ok=True)
|
||||
safe_name = self._sanitize_filename(filename)
|
||||
local_path = temp_dir / f"{chat_id}_{message.message_id}_{safe_name}"
|
||||
await tg_file.download_to_drive(custom_path=str(local_path))
|
||||
return {
|
||||
"local_path": str(local_path),
|
||||
"filename": filename,
|
||||
"mime_type": mime_type,
|
||||
}
|
||||
|
||||
def _looks_like_drive_upload_request(self, text: str) -> bool:
|
||||
normalized = text.casefold()
|
||||
references_drive = "drive" in normalized or "google drive" in normalized
|
||||
upload_intent = any(term in normalized for term in ("yukle", "yükle", "gonder", "gönder", "upload"))
|
||||
return references_drive and upload_intent
|
||||
|
||||
def _message_has_supported_attachment(self, message: Any) -> bool:
|
||||
return bool(getattr(message, "document", None) is not None or getattr(message, "photo", None))
|
||||
|
||||
def _sanitize_filename(self, filename: str) -> str:
|
||||
cleaned = "".join(char if char.isalnum() or char in {"-", "_", "."} else "_" for char in filename.strip())
|
||||
return cleaned or "attachment.bin"
|
||||
|
||||
def _chunk_message(self, text: str) -> list[str]:
|
||||
if len(text) <= self.MAX_MESSAGE_LEN:
|
||||
return [text]
|
||||
@@ -170,6 +321,7 @@ class TelegramBotService:
|
||||
BotCommand("profilim", "Kayitli profil ozetimi goster (wc)"),
|
||||
BotCommand("tercihlerim", "Kayitli iletisim tercihlerini goster (wc)"),
|
||||
BotCommand("tanisalim_sifirla", "Tanisma profilini sifirla (wc)"),
|
||||
BotCommand("clean_chat", "Telegram ekranindaki mesajlari temizle (wc)"),
|
||||
BotCommand("otomasyon_ekle", "Yeni otomasyon wizard'ini baslat (wc)"),
|
||||
BotCommand("otomasyonlar", "Otomasyon listesini goster (wc)"),
|
||||
BotCommand("otomasyon_durdur", "Bir otomasyonu durdur: /otomasyon_durdur <id> (wc)"),
|
||||
|
||||
Reference in New Issue
Block a user