From 247452051410d7f4a121082a4df0080c7e172985 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 6 Feb 2026 17:51:03 +0100 Subject: [PATCH] feat: add Discord bot integration for PDF receipt uploads and manual entry Co-authored-by: aider (openai/unsloth/Qwen3-Coder-Next) --- app.py | 141 +++++++++++++++++++++++++++++++++++++---------- requirements.txt | 8 ++- 2 files changed, 118 insertions(+), 31 deletions(-) diff --git a/app.py b/app.py index a993544..27d897f 100644 --- a/app.py +++ b/app.py @@ -1,6 +1,9 @@ import sqlite3 from datetime import datetime import os +import discord +from discord.ext import commands +import io DB_PATH = "grocery_receipts.db" @@ -151,34 +154,114 @@ def list_receipts(): # Initialize the database when the module is imported init_db() +# Discord bot setup +intents = discord.Intents.default() +intents.message_content = True +intents.members = True + +bot = commands.Bot(command_prefix='!', intents=intents) + +@bot.event +async def on_ready(): + print(f'{bot.user} has connected to Discord!') + +@bot.event +async def on_message(message): + # Ignore messages from the bot itself + if message.author == bot.user: + return + + # Process commands + await bot.process_commands(message) + + # Handle receipt uploads + if message.attachments: + for attachment in message.attachments: + if attachment.filename.lower().endswith('.pdf'): + # Download the PDF + pdf_bytes = await attachment.read() + + # For now, we'll just acknowledge receipt and save the file + # In a full implementation, you'd extract text from the PDF using OCR + + # Save the PDF to a receipts folder + os.makedirs('receipts', exist_ok=True) + file_path = os.path.join('receipts', attachment.filename) + with open(file_path, 'wb') as f: + f.write(pdf_bytes) + + # Send confirmation message + await message.channel.send(f"Receipt '{attachment.filename}' received and saved!") + +@bot.command(name='add_receipt') +async def add_receipt_command(ctx, store_name: str, date: str, *, items: str): + """ + Add a receipt manually. + Usage: !add_receipt StoreName 2023-10-15 "Product1,2,1.50;Product2,1,2.00" + """ + try: + # Parse items: format is "Product1,quantity,price;Product2,quantity,price" + item_list = [] + for item_str in items.split(';'): + parts = item_str.strip().split(',') + if len(parts) == 3: + product_name = parts[0].strip() + quantity = float(parts[1].strip()) + price = float(parts[2].strip()) + item_list.append((product_name, quantity, price)) + + add_receipt(store_name, date, item_list) + await ctx.send(f"Receipt added for {store_name} on {date} with {len(item_list)} items!") + except Exception as e: + await ctx.send(f"Error adding receipt: {str(e)}") + +@bot.command(name='prices') +async def prices_command(ctx, *, product_name: str): + """ + Get price history for a product. + Usage: !prices ProductName + """ + try: + prices = get_product_prices(product_name) + if not prices: + await ctx.send(f"No price history found for '{product_name}'.") + return + + message = f"Price history for '{product_name}':\n" + for date, store, qty, price in prices: + message += f"- {date} at {store}: ${price:.2f} per unit (qty: {qty})\n" + + await ctx.send(message) + except Exception as e: + await ctx.send(f"Error retrieving prices: {str(e)}") + +@bot.command(name='receipts') +async def receipts_command(ctx): + """ + List all receipts. + Usage: !receipts + """ + try: + receipts = list_receipts() + if not receipts: + await ctx.send("No receipts found in the database.") + return + + message = "All receipts:\n" + for receipt_id, store, date, total in receipts: + message += f"- ID: {receipt_id}, Store: {store}, Date: {date}, Total: ${total:.2f}\n" + + await ctx.send(message) + except Exception as e: + await ctx.send(f"Error listing receipts: {str(e)}") + +# Run the bot if __name__ == "__main__": - # Example usage - print("Grocery Receipt Tracker - Demo") - print("=" * 30) + # You'll need to set your Discord bot token as an environment variable + # or replace the token parameter with your actual token (not recommended for production) + DISCORD_TOKEN = os.getenv('DISCORD_BOT_TOKEN') + if not DISCORD_TOKEN: + print("Error: DISCORD_BOT_TOKEN environment variable not set") + exit(1) - # Add a sample receipt - items = [ - ("Milk", 2, 1.50), - ("Bread", 1, 2.00), - ("Apples", 3, 0.80) - ] - add_receipt("SuperMarket", "2023-10-15", items) - print("Added sample receipt to database.") - - # Add another receipt for comparison - items2 = [ - ("Milk", 1, 1.30), - ("Bread", 1, 1.80) - ] - add_receipt("Corner Store", "2023-10-20", items2) - print("Added another receipt to database.") - - # Show all receipts - print("\nAll Receipts:") - for receipt in list_receipts(): - print(f" ID: {receipt[0]}, Store: {receipt[1]}, Date: {receipt[2]}, Total: ${receipt[3]:.2f}") - - # Show prices for a specific product - print("\nMilk Prices Over Time:") - for price_info in get_product_prices("Milk"): - print(f" Date: {price_info[0]}, Store: {price_info[1]}, Qty: {price_info[2]}, Price: ${price_info[3]:.2f}") + bot.run(DISCORD_TOKEN) diff --git a/requirements.txt b/requirements.txt index c737efd..2f64fd7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,6 @@ -# No external dependencies required for this SQLite-based implementation -# Python standard library is sufficient for the core functionality +# Core dependencies for the grocery receipt tracker +discord.py>=2.0.0 +# For PDF processing (optional, for future OCR implementation) +# PyPDF2 +# pytesseract +# pillow