File size: 3,885 Bytes
bfd6783
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ad4bf51
 
 
bfd6783
ad4bf51
 
 
bfd6783
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
"""Onboarding, help and diagnostics. Registered last so /help sees everything."""

from __future__ import annotations

from typing import Sequence

from .. import render
from ..analytics import month_bounds
from .base import BotContext, CommandSpec, Reply, SimpleFeature

_WELCOME = """<b>Hello πŸ‘‹</b>

Just tell me what you spent and I'll keep track.

<b>Log it</b>
<code>250 coffee</code>
<code>β‚Ή1,240 groceries at bigbasket</code>
<code>45.50 lunch yesterday</code>
<code>320 uber #transport</code>

<b>Send me things</b>
πŸ“· a receipt photo β€” I'll read the total
πŸ“„ a bank statement (PDF, CSV, XLSX) β€” I'll pull out the transactions and ask before saving

<b>See it back</b>
/today Β· /week Β· /month Β· /recurring Β· /advice

/help for everything."""


class CoreFeature(SimpleFeature):
    name = "core"
    description = "βš™οΈ General"
    priority = 90

    def commands(self) -> Sequence[CommandSpec]:
        return (
            CommandSpec("start", "What this bot does"),
            CommandSpec("help", "All commands"),
            CommandSpec("status", "Configuration and storage health"),
            CommandSpec("whoami", "Your Telegram user ID", hidden=True),
        )

    def cmd_start(self, ctx: BotContext) -> Reply:
        return Reply(_WELCOME)

    def cmd_help(self, ctx: BotContext) -> Reply:
        body = ctx.services.registry.help_text()
        tail = (
            "\n\n<i>Plain text with a number is logged as an expense. "
            "Add #category to override the guess.</i>"
        )
        return Reply(body + tail)

    def cmd_whoami(self, ctx: BotContext) -> Reply:
        return Reply(f"Your Telegram user ID is <code>{ctx.user_id}</code>.")

    def cmd_status(self, ctx: BotContext) -> Reply:
        settings = ctx.settings
        store = ctx.store

        start, _ = month_bounds(ctx.today)
        month_count = len(ctx.store.list_expenses(ctx.user_id, start, ctx.today))

        lines = [
            "<b>Status</b>",
            f"Transactions logged: {store.count(ctx.user_id)} ({month_count} this month)",
            f"Today (in {render.escape(settings.timezone)}): {ctx.today.isoformat()}",
            "",
            "<b>Inference</b>",
        ]

        llm = ctx.services.llm
        if llm.enabled:
            lines.append(f"βœ… {render.escape(llm.describe())}")
        else:
            lines.append(f"⚠️ {render.escape(llm.describe())}")
            lines.append("   Receipts, statements and written advice are off.")
            lines.append("   Typed expenses, summaries and budgets still work.")

        lines.append("")
        lines.append("<b>Access</b>")
        if settings.open_access:
            cap = settings.uploads_per_user_per_day
            lines.append("🌍 Open β€” anyone on Telegram can use this bot.")
            lines.append("   Each person's ledger is separate; nobody sees yours.")
            lines.append(
                f"   Uploads capped at {cap}/user/day."
                if cap > 0
                else "   ⚠️ Uploads uncapped β€” strangers can spend your inference credits."
            )
        else:
            lines.append(f"πŸ”’ Allowlist β€” {len(settings.allowed_user_ids)} user(s).")

        lines.append("")
        lines.append("<b>Storage</b>")
        if settings.sync_enabled:
            lines.append(f"βœ… {render.escape(store.sync_status)}")
        else:
            lines.append(
                "πŸ”΄ <b>Local only β€” Space disk is ephemeral.</b>\n"
                "   Your data will be lost on the next restart or sleep.\n"
                "   Set HF_DATASET_REPO to fix this."
            )

        if gaps := settings.missing_required():
            lines.append("")
            lines.append("<b>Not configured</b>")
            lines.extend(f"β€’ {render.escape(gap)}" for gap in gaps)

        return Reply("\n".join(lines))