Files
ERP/bot/app/sync.py
whqreza 638076dbaa refactor(bot)!: rename 'ai procurement' -> 'bot/' so the compose build context is committed
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.
2026-10-02 12:52:25 +03:30

60 lines
2.8 KiB
Python

"""Background sync loop: watches bot_drafts for updates from Web Wizard or external API.
If a draft was updated in the Wizard:
- If status == 'active': re-renders review text & keyboard in Bale so chat reflects edits.
- If status == 'committed': edits Bale message to show it was committed and removes buttons.
- If status == 'cancelled': edits Bale message to show it was cancelled and removes buttons.
"""
import asyncio
import logging
from app import drafts as draft_store
from app.handlers import quote
logger = logging.getLogger("bot.sync")
async def sync_drafts_loop(ctx) -> None:
while True:
try:
pending = await draft_store.get_pending_bale_updates()
for draft in pending:
try:
if not draft.review_message_id:
await draft_store.set_bale_updated_at(draft.id)
continue
if draft.status == "committed":
payload = draft.payload or {}
quote_id = payload.get("committed_quote_id") or ""
msg = "✅ این پیشنهاد با موفقیت در سیستم ثبت نهایی شد."
if quote_id:
msg += f"\nشناسه پیشنهاد: {quote_id}"
await ctx.bale.edit_message_text(draft.chat_id, draft.review_message_id, msg, reply_markup=None)
await draft_store.set_bale_updated_at(draft.id)
logger.info("Synced committed draft %s to Bale message %s", draft.id, draft.review_message_id)
elif draft.status == "cancelled":
msg = "❌ این پیش‌نویس لغو شد."
await ctx.bale.edit_message_text(draft.chat_id, draft.review_message_id, msg, reply_markup=None)
await draft_store.set_bale_updated_at(draft.id)
logger.info("Synced cancelled draft %s to Bale message %s", draft.id, draft.review_message_id)
elif draft.status == "active" and draft.kind == draft_store.KIND_QUOTE:
await quote.render(ctx, draft.chat_id, draft, fresh=False)
await draft_store.set_bale_updated_at(draft.id)
logger.info("Synced active draft %s edits to Bale message %s", draft.id, draft.review_message_id)
else:
await draft_store.set_bale_updated_at(draft.id)
except Exception as exc:
logger.warning("Failed to sync draft %s to Bale: %s", draft.id, exc)
await draft_store.set_bale_updated_at(draft.id)
except asyncio.CancelledError:
break
except Exception:
logger.exception("Error in sync_drafts_loop")
await asyncio.sleep(1.5)