mirror of
https://github.com/elisiariocouto/leggen.git
synced 2025-12-29 06:19:04 +00:00
Compare commits
5 Commits
d9a39c30ab
...
2025.9.14
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02c4f5c6ef | ||
|
|
30d7c2ed4e | ||
|
|
61442a598f | ||
|
|
b7da446fa5 | ||
|
|
5a626b5394 |
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(mkdir:*)",
|
||||
"Bash(uv sync:*)",
|
||||
"Bash(uv run pytest:*)",
|
||||
"Bash(git commit:*)",
|
||||
"Bash(ruff check:*)",
|
||||
"Bash(git add:*)",
|
||||
"Bash(mypy:*)",
|
||||
"WebFetch(domain:localhost)",
|
||||
"Bash(npm create:*)",
|
||||
"Bash(npm install)",
|
||||
"Bash(npm install:*)",
|
||||
"Bash(npx tailwindcss init:*)",
|
||||
"Bash(./node_modules/.bin/tailwindcss:*)",
|
||||
"Bash(npm run build:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
}
|
||||
}
|
||||
20
.github/workflows/ci.yml
vendored
20
.github/workflows/ci.yml
vendored
@@ -10,46 +10,48 @@ jobs:
|
||||
test-python:
|
||||
name: Test Python
|
||||
runs-on: ubuntu-latest
|
||||
if: "!contains(github.event.head_commit.message, 'chore(ci): Bump version to')"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version-file: "pyproject.toml"
|
||||
|
||||
|
||||
- name: Create config directory for tests
|
||||
run: |
|
||||
mkdir -p ~/.config/leggen
|
||||
cp config.example.toml ~/.config/leggen/config.toml
|
||||
|
||||
|
||||
- name: Run Python tests
|
||||
run: uv run pytest
|
||||
|
||||
test-frontend:
|
||||
name: Test Frontend
|
||||
runs-on: ubuntu-latest
|
||||
if: "!contains(github.event.head_commit.message, 'chore(ci): Bump version to')"
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./frontend
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: frontend/package-lock.json
|
||||
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
|
||||
- name: Run lint
|
||||
run: npm run lint
|
||||
|
||||
|
||||
- name: Run build
|
||||
run: npm run build
|
||||
run: npm run build
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -164,3 +164,4 @@ sql/
|
||||
leggen.db
|
||||
*.db
|
||||
config.toml
|
||||
.claude/
|
||||
|
||||
17
.mcp.json
Normal file
17
.mcp.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"shadcn": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"shadcn@latest",
|
||||
"mcp"
|
||||
]
|
||||
},
|
||||
"browsermcp": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"@browsermcp/mcp@latest"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
86
CHANGELOG.md
86
CHANGELOG.md
@@ -1,4 +1,90 @@
|
||||
|
||||
## 2025.9.14 (2025/09/18)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **config:** Remove aliases for configuration keys that were disabling telegram notifications in some cases. ([61442a59](https://github.com/elisiariocouto/leggen/commit/61442a598fa7f38c568e3df7e1d924ed85df7491))
|
||||
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- **ci:** Prevent double GitHub Actions runs on new releases. ([30d7c2ed](https://github.com/elisiariocouto/leggen/commit/30d7c2ed4e9aff144837a1f0ed67a8ded0b5d72a))
|
||||
|
||||
|
||||
|
||||
## 2025.9.14 (2025/09/18)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **config:** Remove aliases for configuration keys that were disabling telegram notifications in some cases. ([61442a59](https://github.com/elisiariocouto/leggen/commit/61442a598fa7f38c568e3df7e1d924ed85df7491))
|
||||
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- **ci:** Prevent double GitHub Actions runs on new releases. ([30d7c2ed](https://github.com/elisiariocouto/leggen/commit/30d7c2ed4e9aff144837a1f0ed67a8ded0b5d72a))
|
||||
|
||||
|
||||
|
||||
## 2025.9.13 (2025/09/17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **frontend:** Resolve linting issue in skeleton component ([fb310a59](https://github.com/elisiariocouto/leggen/commit/fb310a5953cf51d1cac181529311e76a0f4ea9ee))
|
||||
- **frontend:** Add index signature to PieDataPoint interface. ([81d7d163](https://github.com/elisiariocouto/leggen/commit/81d7d16301dafc62a95f63036819565ffb90ddb5))
|
||||
- **frontend:** Resolve dual scroll and excessive whitespace issues on transactions page. ([8ab76081](https://github.com/elisiariocouto/leggen/commit/8ab760815c9ae072b8c2cb2460e31144b193e0b3))
|
||||
- **frontend:** Remove broken running balance feature in transactions table. ([155a48d7](https://github.com/elisiariocouto/leggen/commit/155a48d7dc86b3f453ba6f8c37edf63c0b76c755))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
- **frontend:** Complete shadcn migration of skeleton and styling components ([c83386b1](https://github.com/elisiariocouto/leggen/commit/c83386b1d5b165910abe8b391ca483e5b48cd35f))
|
||||
- **frontend:** Add comprehensive PWA capabilities with dynamic theme support ([86891441](https://github.com/elisiariocouto/leggen/commit/86891441d65e13757f343cabc39ccdb3ca6adc75))
|
||||
- **frontend:** Add PWA install prompts, update notifications, and app shortcuts ([3049a8cd](https://github.com/elisiariocouto/leggen/commit/3049a8cd2fa80c14f970884fb14df2ab88c418dd))
|
||||
- **frontend:** Update brand identity with new logo and color scheme. ([2825dba2](https://github.com/elisiariocouto/leggen/commit/2825dba2e944b3fe31aaa33127b770e7474ce021))
|
||||
- **frontend:** Update analytics cards to match home page design consistency. ([d9a39c30](https://github.com/elisiariocouto/leggen/commit/d9a39c30ab1248a9fdacff068d401c3daff3f6a5))
|
||||
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- Enable browsermcp and shadcn MCP servers. ([5a626b53](https://github.com/elisiariocouto/leggen/commit/5a626b53947f7e2d1544faf3ee06f8a0f1fb5d7a))
|
||||
|
||||
|
||||
### Refactor
|
||||
|
||||
- **frontend:** Replace LoadingSpinner with shadcn skeleton components. ([84e609a7](https://github.com/elisiariocouto/leggen/commit/84e609a774ddc0caf9f84eaf1e8cdce021c82785))
|
||||
|
||||
|
||||
|
||||
## 2025.9.13 (2025/09/17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **frontend:** Resolve linting issue in skeleton component ([fb310a59](https://github.com/elisiariocouto/leggen/commit/fb310a5953cf51d1cac181529311e76a0f4ea9ee))
|
||||
- **frontend:** Add index signature to PieDataPoint interface. ([81d7d163](https://github.com/elisiariocouto/leggen/commit/81d7d16301dafc62a95f63036819565ffb90ddb5))
|
||||
- **frontend:** Resolve dual scroll and excessive whitespace issues on transactions page. ([8ab76081](https://github.com/elisiariocouto/leggen/commit/8ab760815c9ae072b8c2cb2460e31144b193e0b3))
|
||||
- **frontend:** Remove broken running balance feature in transactions table. ([155a48d7](https://github.com/elisiariocouto/leggen/commit/155a48d7dc86b3f453ba6f8c37edf63c0b76c755))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
- **frontend:** Complete shadcn migration of skeleton and styling components ([c83386b1](https://github.com/elisiariocouto/leggen/commit/c83386b1d5b165910abe8b391ca483e5b48cd35f))
|
||||
- **frontend:** Add comprehensive PWA capabilities with dynamic theme support ([86891441](https://github.com/elisiariocouto/leggen/commit/86891441d65e13757f343cabc39ccdb3ca6adc75))
|
||||
- **frontend:** Add PWA install prompts, update notifications, and app shortcuts ([3049a8cd](https://github.com/elisiariocouto/leggen/commit/3049a8cd2fa80c14f970884fb14df2ab88c418dd))
|
||||
- **frontend:** Update brand identity with new logo and color scheme. ([2825dba2](https://github.com/elisiariocouto/leggen/commit/2825dba2e944b3fe31aaa33127b770e7474ce021))
|
||||
- **frontend:** Update analytics cards to match home page design consistency. ([d9a39c30](https://github.com/elisiariocouto/leggen/commit/d9a39c30ab1248a9fdacff068d401c3daff3f6a5))
|
||||
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- Enable browsermcp and shadcn MCP servers. ([5a626b53](https://github.com/elisiariocouto/leggen/commit/5a626b53947f7e2d1544faf3ee06f8a0f1fb5d7a))
|
||||
|
||||
|
||||
### Refactor
|
||||
|
||||
- **frontend:** Replace LoadingSpinner with shadcn skeleton components. ([84e609a7](https://github.com/elisiariocouto/leggen/commit/84e609a774ddc0caf9f84eaf1e8cdce021c82785))
|
||||
|
||||
|
||||
|
||||
## 2025.9.12 (2025/09/15)
|
||||
|
||||
|
||||
|
||||
@@ -146,8 +146,8 @@ enabled = true
|
||||
|
||||
# Optional: Transaction filters for notifications
|
||||
[filters]
|
||||
case-insensitive = ["salary", "utility"]
|
||||
case-sensitive = ["SpecificStore"]
|
||||
case_insensitive = ["salary", "utility"]
|
||||
case_sensitive = ["SpecificStore"]
|
||||
```
|
||||
|
||||
## 📖 Usage
|
||||
|
||||
@@ -9,7 +9,7 @@ sqlite = true
|
||||
# Optional: Background sync scheduling
|
||||
[scheduler.sync]
|
||||
enabled = true
|
||||
hour = 3 # 3 AM
|
||||
hour = 3 # 3 AM
|
||||
minute = 0
|
||||
# cron = "0 3 * * *" # Alternative: use cron expression
|
||||
|
||||
@@ -20,11 +20,11 @@ enabled = true
|
||||
|
||||
# Optional: Telegram notifications
|
||||
[notifications.telegram]
|
||||
api-key = "your-bot-token"
|
||||
chat-id = 12345
|
||||
token = "your-bot-token"
|
||||
chat_id = 12345
|
||||
enabled = true
|
||||
|
||||
# Optional: Transaction filters for notifications
|
||||
[filters]
|
||||
case-insensitive = ["salary", "utility"]
|
||||
case-sensitive = ["SpecificStore"]
|
||||
case_insensitive = ["salary", "utility"]
|
||||
case_sensitive = ["SpecificStore"]
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": ["Bash(find:*)"],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
}
|
||||
}
|
||||
@@ -37,15 +37,15 @@ async def get_notification_settings() -> APIResponse:
|
||||
if discord_config.get("webhook")
|
||||
else None,
|
||||
telegram=TelegramConfig(
|
||||
token="***" if telegram_config.get("api-key") else "",
|
||||
chat_id=telegram_config.get("chat-id", 0),
|
||||
token="***" if telegram_config.get("token") else "",
|
||||
chat_id=telegram_config.get("chat_id", 0),
|
||||
enabled=telegram_config.get("enabled", True),
|
||||
)
|
||||
if telegram_config.get("api-key")
|
||||
if telegram_config.get("token")
|
||||
else None,
|
||||
filters=NotificationFilters(
|
||||
case_insensitive=filters_config.get("case-insensitive", []),
|
||||
case_sensitive=filters_config.get("case-sensitive"),
|
||||
case_insensitive=filters_config.get("case_insensitive", []),
|
||||
case_sensitive=filters_config.get("case_sensitive"),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -77,17 +77,17 @@ async def update_notification_settings(settings: NotificationSettings) -> APIRes
|
||||
|
||||
if settings.telegram:
|
||||
notifications_config["telegram"] = {
|
||||
"api-key": settings.telegram.token,
|
||||
"chat-id": settings.telegram.chat_id,
|
||||
"token": settings.telegram.token,
|
||||
"chat_id": settings.telegram.chat_id,
|
||||
"enabled": settings.telegram.enabled,
|
||||
}
|
||||
|
||||
# Update filters config
|
||||
filters_config: Dict[str, Any] = {}
|
||||
if settings.filters.case_insensitive:
|
||||
filters_config["case-insensitive"] = settings.filters.case_insensitive
|
||||
filters_config["case_insensitive"] = settings.filters.case_insensitive
|
||||
if settings.filters.case_sensitive:
|
||||
filters_config["case-sensitive"] = settings.filters.case_sensitive
|
||||
filters_config["case_sensitive"] = settings.filters.case_sensitive
|
||||
|
||||
# Save to config
|
||||
if notifications_config:
|
||||
@@ -153,12 +153,12 @@ async def get_notification_services() -> APIResponse:
|
||||
"telegram": {
|
||||
"name": "Telegram",
|
||||
"enabled": bool(
|
||||
notifications_config.get("telegram", {}).get("api-key")
|
||||
and notifications_config.get("telegram", {}).get("chat-id")
|
||||
notifications_config.get("telegram", {}).get("token")
|
||||
and notifications_config.get("telegram", {}).get("chat_id")
|
||||
),
|
||||
"configured": bool(
|
||||
notifications_config.get("telegram", {}).get("api-key")
|
||||
and notifications_config.get("telegram", {}).get("chat-id")
|
||||
notifications_config.get("telegram", {}).get("token")
|
||||
and notifications_config.get("telegram", {}).get("chat_id")
|
||||
),
|
||||
"active": notifications_config.get("telegram", {}).get("enabled", True),
|
||||
},
|
||||
|
||||
@@ -22,8 +22,8 @@ class DiscordNotificationConfig(BaseModel):
|
||||
|
||||
|
||||
class TelegramNotificationConfig(BaseModel):
|
||||
token: str = Field(..., alias="api-key", description="Telegram bot token")
|
||||
chat_id: int = Field(..., alias="chat-id", description="Telegram chat ID")
|
||||
token: str = Field(..., description="Telegram bot token")
|
||||
chat_id: int = Field(..., description="Telegram chat ID")
|
||||
enabled: bool = Field(default=True, description="Enable Telegram notifications")
|
||||
|
||||
|
||||
@@ -33,12 +33,8 @@ class NotificationConfig(BaseModel):
|
||||
|
||||
|
||||
class FilterConfig(BaseModel):
|
||||
case_insensitive: Optional[List[str]] = Field(
|
||||
default_factory=list, alias="case-insensitive"
|
||||
)
|
||||
case_sensitive: Optional[List[str]] = Field(
|
||||
default_factory=list, alias="case-sensitive"
|
||||
)
|
||||
case_insensitive: Optional[List[str]] = Field(default_factory=list)
|
||||
case_sensitive: Optional[List[str]] = Field(default_factory=list)
|
||||
|
||||
|
||||
class SyncScheduleConfig(BaseModel):
|
||||
@@ -60,6 +56,3 @@ class Config(BaseModel):
|
||||
notifications: Optional[NotificationConfig] = None
|
||||
filters: Optional[FilterConfig] = None
|
||||
scheduler: SchedulerConfig = Field(default_factory=SchedulerConfig)
|
||||
|
||||
class Config:
|
||||
validate_by_name = True
|
||||
|
||||
@@ -29,8 +29,8 @@ def escape_markdown(text: str) -> str:
|
||||
|
||||
|
||||
def send_expire_notification(ctx: click.Context, notification: dict):
|
||||
token = ctx.obj["notifications"]["telegram"]["api-key"]
|
||||
chat_id = ctx.obj["notifications"]["telegram"]["chat-id"]
|
||||
token = ctx.obj["notifications"]["telegram"]["token"]
|
||||
chat_id = ctx.obj["notifications"]["telegram"]["chat_id"]
|
||||
bot_url = f"https://api.telegram.org/bot{token}/sendMessage"
|
||||
info("Sending expiration notification to Telegram")
|
||||
message = "*💲 [Leggen](https://github.com/elisiariocouto/leggen)*\n"
|
||||
@@ -54,8 +54,8 @@ def send_expire_notification(ctx: click.Context, notification: dict):
|
||||
|
||||
|
||||
def send_transaction_message(ctx: click.Context, transactions: list):
|
||||
token = ctx.obj["notifications"]["telegram"]["api-key"]
|
||||
chat_id = ctx.obj["notifications"]["telegram"]["chat-id"]
|
||||
token = ctx.obj["notifications"]["telegram"]["token"]
|
||||
chat_id = ctx.obj["notifications"]["telegram"]["chat_id"]
|
||||
bot_url = f"https://api.telegram.org/bot{token}/sendMessage"
|
||||
info(f"Got {len(transactions)} new transactions, sending message to Telegram")
|
||||
message = "*💲 [Leggen](https://github.com/elisiariocouto/leggen)*\n"
|
||||
|
||||
@@ -63,8 +63,8 @@ class NotificationService:
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Filter transactions based on notification criteria"""
|
||||
matching = []
|
||||
filters_case_insensitive = self.filters_config.get("case-insensitive", [])
|
||||
filters_case_sensitive = self.filters_config.get("case-sensitive", [])
|
||||
filters_case_insensitive = self.filters_config.get("case_insensitive", [])
|
||||
filters_case_sensitive = self.filters_config.get("case_sensitive", [])
|
||||
|
||||
for transaction in transactions:
|
||||
description = transaction.get("description", "")
|
||||
@@ -159,8 +159,8 @@ class NotificationService:
|
||||
ctx.obj = {
|
||||
"notifications": {
|
||||
"telegram": {
|
||||
"api-key": telegram_config.get("token"),
|
||||
"chat-id": telegram_config.get("chat_id"),
|
||||
"token": telegram_config.get("token"),
|
||||
"chat_id": telegram_config.get("chat_id"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,8 +219,8 @@ class NotificationService:
|
||||
ctx.obj = {
|
||||
"notifications": {
|
||||
"telegram": {
|
||||
"api-key": telegram_config.get("token"),
|
||||
"chat-id": telegram_config.get("chat_id"),
|
||||
"token": telegram_config.get("token"),
|
||||
"chat_id": telegram_config.get("chat_id"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -277,8 +277,8 @@ class NotificationService:
|
||||
ctx.obj = {
|
||||
"notifications": {
|
||||
"telegram": {
|
||||
"api-key": telegram_config.get("token"),
|
||||
"chat-id": telegram_config.get("chat_id"),
|
||||
"token": telegram_config.get("token"),
|
||||
"chat_id": telegram_config.get("chat_id"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ def send_notification(ctx: click.Context, transactions: list):
|
||||
warning("No filters are enabled, skipping notifications")
|
||||
return
|
||||
|
||||
filters_case_insensitive = ctx.obj.get("filters", {}).get("case-insensitive", {})
|
||||
filters_case_insensitive = ctx.obj.get("filters", {}).get("case_insensitive", {})
|
||||
|
||||
# Add transaction to the list of transactions to be sent as a notification
|
||||
notification_transactions = []
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "leggen"
|
||||
version = "2025.9.12"
|
||||
version = "2025.9.14"
|
||||
description = "An Open Banking CLI"
|
||||
authors = [{ name = "Elisiário Couto", email = "elisiario@couto.io" }]
|
||||
requires-python = "~=3.13.0"
|
||||
|
||||
@@ -216,8 +216,8 @@ class TestConfig:
|
||||
"""Test filters configuration access."""
|
||||
custom_config = {
|
||||
"filters": {
|
||||
"case-insensitive": ["salary", "utility"],
|
||||
"case-sensitive": ["SpecificStore"],
|
||||
"case_insensitive": ["salary", "utility"],
|
||||
"case_sensitive": ["SpecificStore"],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,6 +225,6 @@ class TestConfig:
|
||||
config._config = custom_config
|
||||
|
||||
filters = config.filters_config
|
||||
assert "salary" in filters["case-insensitive"]
|
||||
assert "utility" in filters["case-insensitive"]
|
||||
assert "SpecificStore" in filters["case-sensitive"]
|
||||
assert "salary" in filters["case_insensitive"]
|
||||
assert "utility" in filters["case_insensitive"]
|
||||
assert "SpecificStore" in filters["case_sensitive"]
|
||||
|
||||
Reference in New Issue
Block a user