The bot service in docker-compose.yml builds from ./bot, but the agent code lived in 'ai procurement/' and ./bot was only a Windows junction on reza.g's machine (and was gitignored) — exactly as the team reported: 'bot/ was never committed — prod-v7's bot is un-buildable from the repo.' - junction removed; 'ai procurement/' renamed to 'bot/' via git mv (all 41 files keep their history as pure renames) - 'bot/' dropped from .gitignore (voice/ stays ignored — runtime voice notes; bot/.env stays ignored by bot/.gitignore) - compose needs no change: context: ./bot and ./bot/app now resolve to committed files - bot/INTEGRATION-REPORT.md added: full story (problem, root cause, git state, security note on the committed BALE_BOT_TOKEN / AI_API_KEY compose defaults, and usage instructions for the team) Verified: 41 renames staged with zero secret/cache files; voice/ and bot/.env still ignored.
83 lines
3.0 KiB
Python
83 lines
3.0 KiB
Python
"""/commands — /start /help /drafts /cancel /whoami /unlink."""
|
|
import logging
|
|
|
|
from app import drafts as draft_store
|
|
from app.runtime import ROLE_FA
|
|
from app.texts import CANCELLED, DRAFTS_EMPTY, DRAFTS_LIST_HEADER, HELP, UNLINKED, WELCOME, esc
|
|
|
|
logger = logging.getLogger("bot.commands")
|
|
|
|
KIND_FA = {"quote": "استعلام", "supplier": "تأمینکننده", "material": "کالا"}
|
|
STATUS_FA = {"active": "باز", "committed": "ثبتشده", "cancelled": "لغوشده", "expired": "منقضی"}
|
|
|
|
|
|
async def handle_command(ctx, chat_id: int, text: str) -> None:
|
|
command = text.split()[0].split("@")[0].lower()
|
|
if command == "/start":
|
|
await _start(ctx, chat_id)
|
|
elif command in ("/help", "/راهنما"):
|
|
await ctx.send(chat_id, HELP)
|
|
elif command == "/drafts":
|
|
await _drafts(ctx, chat_id)
|
|
elif command in ("/cancel", "/لغو"):
|
|
await _cancel(ctx, chat_id)
|
|
elif command == "/whoami":
|
|
await _whoami(ctx, chat_id)
|
|
elif command == "/unlink":
|
|
await ctx.links.delete(chat_id)
|
|
await ctx.send(chat_id, "اتصال قطع شد. برای اتصال دوباره /start بزن.")
|
|
else:
|
|
await ctx.send(chat_id, HELP)
|
|
|
|
|
|
async def _start(ctx, chat_id: int) -> None:
|
|
await ctx.send(chat_id, WELCOME)
|
|
link = await ctx.links.get(chat_id)
|
|
if not link or not link.user_id:
|
|
if chat_id not in ctx.admin_chat_ids:
|
|
await ctx.send(chat_id, "برای شروع، حساب خود را متصل کن — نام کاربری Casdoor را بفرست:")
|
|
ctx.pending.set(chat_id, "link_username", {})
|
|
|
|
|
|
async def _drafts(ctx, chat_id: int) -> None:
|
|
from sqlalchemy import select
|
|
|
|
from app.db import SessionLocal
|
|
from app.models import BotDraft
|
|
|
|
async with SessionLocal() as session:
|
|
rows = (await session.execute(
|
|
select(BotDraft)
|
|
.where(BotDraft.chat_id == chat_id, BotDraft.status == "active")
|
|
.order_by(BotDraft.updated_at.desc())
|
|
)).scalars().all()
|
|
if not rows:
|
|
await ctx.send(chat_id, DRAFTS_EMPTY)
|
|
return
|
|
lines = [DRAFTS_LIST_HEADER]
|
|
for d in rows:
|
|
p = d.payload or {}
|
|
title = p.get("supplier_name") or p.get("name") or "بدون عنوان"
|
|
lines.append(f"• {KIND_FA.get(d.kind, d.kind)}: {esc(title)}")
|
|
await ctx.send(chat_id, "\n".join(lines))
|
|
|
|
|
|
async def _cancel(ctx, chat_id: int) -> None:
|
|
ctx.pending.pop(chat_id)
|
|
active = await draft_store.get_active(chat_id)
|
|
if active:
|
|
await draft_store.set_status(active.id, "cancelled")
|
|
await ctx.send(chat_id, CANCELLED)
|
|
|
|
|
|
async def _whoami(ctx, chat_id: int) -> None:
|
|
link = await ctx.links.get(chat_id)
|
|
if chat_id in ctx.admin_chat_ids:
|
|
await ctx.send(chat_id, "حالت ادمین (بدون اتصال Casdoor).")
|
|
return
|
|
if not link or not link.user_id:
|
|
await ctx.send(chat_id, UNLINKED)
|
|
return
|
|
role = ROLE_FA.get(link.role or "", link.role or "بدون نقش")
|
|
await ctx.send(chat_id, f"اتصال: {esc(link.display_name)} — {role}")
|