Skill: hoanghust2003/hoang-expense-tracker (version 1.0.0)

--- name: expense-tracker description: Trợ lý tài chính cá nhân đa năng. Dùng khi user muốn GHI NHẬN CHI TIÊU (nay ăn hết 50k, đổ xăng 50k...), XEM BÁO CÁO (vẽ biểu đồ chi tiêu), ĐẶT NGÂN SÁCH (budget tháng này), hoặc NHỜ CHỬI (roast cách tiêu tiền). --- # AI Personal CFO Skill này là một trợ lý tài chính xịn xò. Hãy xác định nhu cầu của user và dùng tool `exec` gọi script tương ứng. ## 1. Ghi nhận chi tiêu (Log Expense) - Trích xuất số tiền (`amount`), danh mục (`category` - tiếng Anh: Food, Drink, Transport, Shopping, Other), và mô tả (`desc`). - Gọi: `python3 /home/hoang/.openclaw/workspace/expense-tracker/scripts/finance_manager.py log --amount <tiền> --category <nhóm> --desc "<mô tả>"` - Phản hồi user xác nhận. ## 2. Báo cáo (Report & Chart) - Khi user bảo "Báo cáo chi tiêu", "Vẽ biểu đồ", "Tổng kết". - Gọi: `python3 /home/hoang/.openclaw/workspace/expense-tracker/scripts/finance_manager.py report` - Script sẽ trả về đường dẫn `chart_path` tới ảnh PNG. - Phản hồi: Dùng tool `message` để gửi ảnh đó lại cho user (kèm theo lời giải thích ngắn gọn). ## 3. Khẩu nghiệp / Tư vấn (Roast & Advise) - Khi user bảo "Chửi tao đi", "Nhận xét cách tiêu tiền", "Roast me". - Gọi: `python3 /home/hoang/.openclaw/workspace/expense-tracker/scripts/finance_manager.py roast` - Phản hồi: Chế giễu cách tiêu tiền một cách vui vẻ hoặc khuyên răn dựa trên output của script. ## 4. Đặt ngân sách (Budget) - Khi user bảo "Đặt ngân sách Food 2 triệu". - Gọi: `python3 /home/hoang/.openclaw/workspace/expense-tracker/scripts/finance_manager.py budget --amount 2000000 --category "Food"` - Phản hồi xác nhận.

path: scripts/finance_manager.py
content: import argparse
import csv
import os
import json
from datetime import datetime
from collections import defaultdict

def get_csv_path():
    return os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "expenses.csv")

def log_expense(amount, category, desc, date):
    file_path = get_csv_path()
    file_exists = os.path.isfile(file_path)
    try:
        with open(file_path, mode='a', newline='', encoding='utf-8') as f:
            writer = csv.writer(f)
            if not file_exists:
                writer.writerow(["Date", "Category", "Amount", "Description"])
            writer.writerow([date, category.capitalize(), amount, desc])
        print(json.dumps({"status": "success", "message": f"✅ Ghi nhận: {amount:,.0f}đ - {category} ({desc})"}, ensure_ascii=False))
    except Exception as e:
        print(json.dumps({"status": "error", "message": str(e)}, ensure_ascii=False))

def report():
    file_path = get_csv_path()
    if not os.path.exists(file_path):
        print(json.dumps({"status": "error", "message": "Chưa có dữ liệu thu chi."}))
        return
    
    totals = defaultdict(float)
    total_spent = 0
    try:
        import matplotlib.pyplot as plt
        import pandas as pd
        df = pd.read_csv(file_path)
        
        # Calculate totals
        for index, row in df.iterrows():
            totals[row['Category']] += float(row['Amount'])
            total_spent += float(row['Amount'])
            
        # Generate chart
        chart_path = os.path.join(os.path.dirname(file_path), "expense_chart.png")
        plt.figure(figsize=(8, 6))
        plt.pie(totals.values(), labels=totals.keys(), autopct='%1.1f%%', startangle=140)
        plt.title(f"Tổng chi tiêu: {total_spent:,.0f} đ")
        plt.savefig(chart_path)
        plt.close()
        
        summary = "\\n".join([f"- {k}: {v:,.0f}đ" for k, v in totals.items()])
        print(json.dumps({
            "status": "success", 
            "message": f"📊 Báo cáo tổng quan:\\n{summary}\\nTổng cộng: {total_spent:,.0f}đ",
            "chart_path": chart_path
        }, ensure_ascii=False))
    except Exception as e:
        print(json.dumps({"status": "error", "message": f"Lỗi tạo báo cáo: {e}"}, ensure_ascii=False))

def roast():
    file_path = get_csv_path()
    if not os.path.exists(file_path):
        print(json.dumps({"status": "success", "message": "Chưa có data, tiêu tiền đi rồi chửi."}))
        return
        
    totals = defaultdict(float)
    try:
        with open(file_path, mode='r', encoding='utf-8') as f:
            reader = csv.DictReader(f)
            for row in reader:
                totals[row['Category']] += float(row['Amount'])
                
        # Simple roast logic
        food = totals.get('Food', 0)
        other = totals.get('Other', 0)
        drink = totals.get('Drink', 0)
        total = sum(totals.values())
        
        roast_msg = "Chê:"
        if food > total * 0.5:
            roast_msg += " Ăn lắm thế, tính làm food reviewer hay gì?"
        elif drink > 0 or other > 0:
            roast_msg += f" Tiêu linh tinh (Trà đá/Thuốc lá/Đồ lặt vặt) {drink+other:,.0f}đ rồi đấy, tích tiểu thành đại nghèo lúc nào không hay!"
        else:
            roast_msg += " Tiêu cũng có vẻ hợp lý, nhưng nhớ tiết kiệm phòng thân."
            
        print(json.dumps({"status": "success", "message": roast_msg}, ensure_ascii=False))
    except Exception as e:
        print(json.dumps({"status": "error", "message": str(e)}, ensure_ascii=False))

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("action", choices=["log", "report", "roast", "budget"], help="Action to perform")
    parser.add_argument("--amount", type=float, default=0)
    parser.add_argument("--category", type=str, default="Other")
    parser.add_argument("--desc", type=str, default="")
    parser.add_argument("--date", type=str, default=datetime.now().strftime("%Y-%m-%d"))
    args = parser.parse_args()

    if args.action == "log":
        log_expense(args.amount, args.category, args.desc, args.date)
    elif args.action == "report":
        report()
    elif args.action == "roast":
        roast()
    elif args.action == "budget":
        print(json.dumps({"status": "success", "message": f"Đã set ngân sách cho {args.category} là {args.amount:,.0f}đ (Mô phỏng)."}, ensure_ascii=False))

if __name__ == "__main__":
    main()

path: scripts/log_expense.py
content: import argparse
import csv
import os
from datetime import datetime
import json

def main():
    parser = argparse.ArgumentParser(description="AI Expense Tracker - Log a daily expense")
    parser.add_argument("--amount", type=float, required=True, help="Amount spent (numeric, e.g., 50000)")
    parser.add_argument("--category", type=str, required=True, help="Category (e.g., Food, Transport, Shopping)")
    parser.add_argument("--desc", type=str, required=True, help="Short description of the expense")
    parser.add_argument("--date", type=str, default=datetime.now().strftime("%Y-%m-%d"), help="Date of expense (YYYY-MM-DD)")
    
    args = parser.parse_args()

    # Lưu file expenses.csv ra ngoài workspace root cho dễ quản lý
    workspace_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
    file_path = os.path.join(workspace_dir, "expenses.csv")
    
    file_exists = os.path.isfile(file_path)
    
    try:
        with open(file_path, mode='a', newline='', encoding='utf-8') as f:
            writer = csv.writer(f)
            if not file_exists:
                writer.writerow(["Date", "Category", "Amount", "Description"])
            writer.writerow([args.date, args.category.capitalize(), args.amount, args.desc])
        
        print(json.dumps({
            "status": "success",
            "message": f"✅ Đã ghi nhận: {args.amount:,.0f}đ - {args.category} ({args.desc})",
            "file": file_path
        }, ensure_ascii=False))
        
    except Exception as e:
        print(json.dumps({
            "status": "error",
            "message": str(e)
        }, ensure_ascii=False))

if __name__ == "__main__":
    main()

Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)