yonilev commited on
Commit
3bd4e4d
Β·
verified Β·
1 Parent(s): 55abd63

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +117 -0
app.py CHANGED
@@ -553,5 +553,122 @@ with gr.Blocks(title="Text2Receipt") as demo:
553
  outputs=[chatbot, doc_output, recs_output, st_parse, st_answers, note_input],
554
  )
555
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
556
  if __name__ == "__main__":
 
557
  demo.launch(css=CSS)
 
553
  outputs=[chatbot, doc_output, recs_output, st_parse, st_answers, note_input],
554
  )
555
 
556
+ # ═════════════════════════════════════════════════════════════════════════════
557
+ # 8. Discord bot β€” runs as a background thread inside the Space (+5% bonus)
558
+ # ═════════════════════════════════════════════════════════════════════════════
559
+ # The bot shares the SAME model loaded by _lazy_init(), so no extra memory.
560
+ # Token is read from the DISCORD_TOKEN secret (Space β†’ Settings β†’ Secrets).
561
+ # If the secret is absent, the bot is silently skipped and the UI still works.
562
+ import threading, asyncio
563
+
564
+ def _format_doc_for_discord(completed: dict) -> str:
565
+ """Render a completed fiscal document as Discord-friendly monospace text."""
566
+ c = completed
567
+ issuer = c.get("issuer", {})
568
+ client = c.get("client", {})
569
+ lines = c.get("lines", [])
570
+ vat_pct = int(round(c.get("vat_rate", 0) * 100))
571
+
572
+ line_rows = "\n".join(
573
+ f" β€’ {ln['description']} Γ— {ln['quantity']} β†’ β‚ͺ{ln['line_total']:,.2f}"
574
+ for ln in lines
575
+ )
576
+ alloc = (f"\nπŸ”– מב׀ר הקצאה: {c.get('allocation_number')}"
577
+ if c.get("allocation_required") else "")
578
+
579
+ return (
580
+ f"```\n"
581
+ f"{'━'*40}\n"
582
+ f" {c.get('doc_type_he','מבמך')} | מב׳ {c.get('serial_number','')}\n"
583
+ f" {c.get('issue_date','')}\n"
584
+ f"{'━'*40}\n"
585
+ f" ΧžΧ Χ€Χ™Χ§ : {issuer.get('name','')} (Χ—.Χ€. {issuer.get('tax_id','')})\n"
586
+ f" ΧœΧ§Χ•Χ— : {client.get('name','')}"
587
+ f"{' [Χ’Χ‘Χ§]' if client.get('is_business') else ''}\n"
588
+ f"{'━'*40}\n"
589
+ f"{line_rows}\n"
590
+ f"{'━'*40}\n"
591
+ f" בכום Χ Χ˜Χ• : β‚ͺ{c.get('subtotal',0):>12,.2f}\n"
592
+ f" מג\"מ {vat_pct:2d}% : β‚ͺ{c.get('vat_amount',0):>12,.2f}\n"
593
+ f" Χ‘Χ”\"Χ› : β‚ͺ{c.get('total',0):>12,.2f}\n"
594
+ f" ΧͺΧ©ΧœΧ•Χ : {core.PAYMENT_HE.get(c.get('payment_method',''),'β€”')}\n"
595
+ f"{'━'*40}\n"
596
+ f"```"
597
+ f"{alloc}"
598
+ )
599
+
600
+
601
+ def _discord_pipeline(raw_text: str) -> str:
602
+ """Full pipeline for Discord: raw Hebrew note β†’ formatted document string.
603
+ Uses safe defaults (no agentic loop in chat β€” Discord is single-shot)."""
604
+ _lazy_init()
605
+ parse = model_parse(raw_text)
606
+ if parse is None:
607
+ return "❌ לא Χ”Χ¦ΧœΧ—ΧͺΧ™ ΧœΧ—ΧœΧ₯ אΧͺ Χ”Χ€Χ¨Χ˜Χ™Χ. Χ Χ‘Χ” ΧœΧ Χ‘Χ— ΧžΧ—Χ“Χ©."
608
+ final = apply_answers(parse, {}) # apply_answers fills safe defaults
609
+ try:
610
+ completed = core.complete(DEMO_ISSUER, final, _STATE["rng"])
611
+ except Exception as e:
612
+ return f"❌ שגיאה Χ‘Χ’Χ™Χ‘Χ•Χ“: {e}"
613
+ return _format_doc_for_discord(completed)
614
+
615
+
616
+ def _start_discord_bot():
617
+ """Launch the Discord bot in a background thread if DISCORD_TOKEN is set."""
618
+ token = os.environ.get("DISCORD_TOKEN", "").strip()
619
+ if not token:
620
+ print("β„Ή DISCORD_TOKEN not set β€” Discord bot skipped (UI still runs).")
621
+ return
622
+
623
+ try:
624
+ import discord
625
+ from discord.ext import commands
626
+ except ImportError:
627
+ print("⚠ discord.py not installed β€” add 'discord.py' to requirements.txt")
628
+ return
629
+
630
+ intents = discord.Intents.default()
631
+ intents.message_content = True
632
+ bot = commands.Bot(command_prefix="!", intents=intents)
633
+
634
+ @bot.event
635
+ async def on_ready():
636
+ print(f"βœ… Discord bot online: {bot.user}")
637
+
638
+ @bot.command(name="receipt")
639
+ async def receipt_cmd(ctx, *, note: str):
640
+ await ctx.message.add_reaction("⏳")
641
+ try:
642
+ loop = asyncio.get_event_loop()
643
+ result = await loop.run_in_executor(None, _discord_pipeline, note)
644
+ await ctx.reply(result)
645
+ except Exception as e:
646
+ await ctx.reply(f"❌ שגיאה: {e}")
647
+ finally:
648
+ try:
649
+ await ctx.message.remove_reaction("⏳", bot.user)
650
+ await ctx.message.add_reaction("βœ…")
651
+ except Exception:
652
+ pass
653
+
654
+ @bot.command(name="help_t2r")
655
+ async def help_cmd(ctx):
656
+ await ctx.reply(
657
+ "**Text2Receipt Bot** 🧾\n"
658
+ "Χ”ΧžΧ¨ Χ”Χ’Χ¨Χͺ Χ”Χ›Χ Χ‘Χ” Χ’Χ‘Χ¨Χ™Χͺ למבמך Χ€Χ™Χ‘Χ§ΧœΧ™ Χ™Χ©Χ¨ΧΧœΧ™.\n\n"
659
+ "Χ©Χ™ΧžΧ•Χ©: `!receipt <Χ”Χ’Χ¨Χ” Χ‘Χ’Χ‘Χ¨Χ™Χͺ>`\n"
660
+ "Χ“Χ•Χ’ΧžΧ”: `!receipt Χ§Χ™Χ‘ΧœΧͺΧ™ 500 שקל ΧžΧžΧ©Χ” גל Χ™Χ™Χ’Χ•Χ₯`"
661
+ )
662
+
663
+ def _run():
664
+ asyncio.run(bot.start(token))
665
+
666
+ threading.Thread(target=_run, daemon=True).start()
667
+ print("πŸ€– Discord bot thread started.")
668
+
669
+ # ═══════════════════════════════════��═════════════════════════════════════════
670
+ # 9. Launch
671
+ # ═════════════════════════════════════════════════════════════════════════════
672
  if __name__ == "__main__":
673
+ _start_discord_bot() # starts only if DISCORD_TOKEN secret is set
674
  demo.launch(css=CSS)