Nutriotion / app.py
Pranesh64's picture
Create app.py
a3f6574 verified
import os
import re
import json
import asyncio
import tempfile
import gradio as gr
from telethon import TelegramClient
from dotenv import load_dotenv
# ================== LOAD ENV ==================
load_dotenv()
# ================== CONFIG ==================
BOT_USERNAME = "mealschatbot"
api_id = int(os.getenv("TG_API_ID"))
api_hash = os.getenv("TG_API_HASH")
phone_number = os.getenv("TG_PHONE")
# ================== PARSER ==================
def parse_food_message(text: str):
if "Calories" not in text:
return None
data = {}
# Item name
name_match = re.search(r"You added:\s*\*\*(.+?)\*\*", text)
if name_match:
data["item"] = name_match.group(1).strip()
# Calories
cal_match = re.search(
r"\*\*Calories\*\*\s*\n+\s*(\d+)\s*kcal",
text,
re.IGNORECASE,
)
if cal_match:
data["calories_kcal"] = int(cal_match.group(1))
# Macros
macros = {}
for key, pattern in {
"protein_g": r"Protein:\s*(\d+)g",
"carbs_g": r"Carbs:\s*(\d+)g",
"fat_g": r"Fat:\s*(\d+)g",
}.items():
m = re.search(pattern, text)
if m:
macros[key] = int(m.group(1))
if macros:
data["macros"] = macros
# Ingredients
ingredients = []
block = re.search(
r"\*\*Likely Ingredients\*\*\s*\n+([\s\S]+?)(?:πŸ“Š|$)",
text,
)
if block:
for line in block.group(1).splitlines():
line = line.strip("β€’ ").strip()
if not line:
continue
m = re.match(r"(.+?)\s*\((\d+)g,\s*(\d+)kcal\)", line)
if m:
ingredients.append({
"name": m.group(1),
"quantity_g": int(m.group(2)),
"calories_kcal": int(m.group(3)),
})
if ingredients:
data["likely_ingredients"] = ingredients
return data
# ================== TELEGRAM HANDLER ==================
async def analyze_image(image_path: str):
async with TelegramClient("session", api_id, api_hash) as client:
await client.start(phone=phone_number)
bot = await client.get_entity(BOT_USERNAME)
history = await client.get_messages(bot, limit=1)
last_id_before = history[0].id if history else 0
await client.send_file(bot, image_path)
await asyncio.sleep(8)
replies = await client.get_messages(
bot,
min_id=last_id_before,
limit=10,
)
for msg in reversed(replies):
if msg.text:
parsed = parse_food_message(msg.text)
if parsed:
return parsed
return {"error": "No food data detected"}
# ================== GRADIO WRAPPER ==================
def gradio_handler(image):
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f:
image.save(f.name)
result = asyncio.run(analyze_image(f.name))
return json.dumps(result, indent=2)
# ================== UI ==================
with gr.Blocks(title="Meal Image β†’ Nutrition JSON") as demo:
gr.Markdown("## 🍽️ Meal Image β†’ Nutrition JSON")
gr.Markdown("Upload a food image. Output will be **pure JSON only**.")
image_input = gr.Image(type="pil", label="Upload Food Image")
json_output = gr.Code(label="Parsed JSON", language="json")
analyze_btn = gr.Button("Analyze")
analyze_btn.click(
fn=gradio_handler,
inputs=image_input,
outputs=json_output,
)
demo.launch()