feat: add Planka service provider with full notification and command support

Webhook-based provider for Planka (self-hosted Kanban board) with:
- 15 event types (cards, boards, lists, comments, tasks, attachments, labels)
- Bearer token webhook authentication
- Async API client for boards/cards/lists
- 30 notification templates (en/ru) + 26 command templates (en/ru)
- Bot commands: /status, /boards, /cards, /lists
- Default tracking config, template config, command config seeded on startup
- DB migration for 15 new tracking_config columns
- Frontend: provider config UI with auto-name, Planka-specific hints
- Frontend: tracking config event toggles for all 15 Planka events
This commit is contained in:
2026-03-23 15:54:00 +03:00
parent 39bac828fd
commit 0fde3c6b3d
83 changed files with 1827 additions and 3 deletions
@@ -159,6 +159,32 @@ async def migrate_schema(engine: AsyncEngine) -> None:
)
logger.info("Added %s column to tracking_config table", col_name)
# Add Planka tracking flags to tracking_config if missing
if await _has_table(conn, "tracking_config"):
planka_flags = [
("track_card_created", "INTEGER DEFAULT 1"),
("track_card_updated", "INTEGER DEFAULT 0"),
("track_card_moved", "INTEGER DEFAULT 1"),
("track_card_deleted", "INTEGER DEFAULT 0"),
("track_card_commented", "INTEGER DEFAULT 1"),
("track_comment_updated", "INTEGER DEFAULT 0"),
("track_board_created", "INTEGER DEFAULT 1"),
("track_board_updated", "INTEGER DEFAULT 0"),
("track_board_deleted", "INTEGER DEFAULT 1"),
("track_list_created", "INTEGER DEFAULT 0"),
("track_list_updated", "INTEGER DEFAULT 0"),
("track_list_deleted", "INTEGER DEFAULT 0"),
("track_attachment_created", "INTEGER DEFAULT 1"),
("track_card_label_added", "INTEGER DEFAULT 0"),
("track_task_completed", "INTEGER DEFAULT 1"),
]
for col_name, col_type in planka_flags:
if not await _has_column(conn, "tracking_config", col_name):
await conn.execute(
text(f"ALTER TABLE tracking_config ADD COLUMN {col_name} {col_type}")
)
logger.info("Added %s column to tracking_config table", col_name)
# Add collection_name and shared to tracker_state if missing
state_table = "notification_tracker_state" if await _has_table(conn, "notification_tracker_state") else "tracker_state"
if await _has_table(conn, state_table):
@@ -128,6 +128,23 @@ class TrackingConfig(SQLModel, table=True):
track_pr_commented: bool = Field(default=False)
track_release_published: bool = Field(default=True)
# Planka event tracking
track_card_created: bool = Field(default=True)
track_card_updated: bool = Field(default=False)
track_card_moved: bool = Field(default=True)
track_card_deleted: bool = Field(default=False)
track_card_commented: bool = Field(default=True)
track_comment_updated: bool = Field(default=False)
track_board_created: bool = Field(default=True)
track_board_updated: bool = Field(default=False)
track_board_deleted: bool = Field(default=True)
track_list_created: bool = Field(default=False)
track_list_updated: bool = Field(default=False)
track_list_deleted: bool = Field(default=False)
track_attachment_created: bool = Field(default=True)
track_card_label_added: bool = Field(default=False)
track_task_completed: bool = Field(default=True)
# Scheduler event tracking
track_scheduled_message: bool = Field(default=True)
@@ -183,6 +183,7 @@ async def _seed_default_templates() -> None:
async with AsyncSession(engine) as session:
await _seed_provider_template(session, "immich", "Immich")
await _seed_provider_template(session, "gitea", "Gitea")
await _seed_provider_template(session, "planka", "Planka")
await _seed_provider_template(session, "scheduler", "Scheduler")
await session.commit()
@@ -201,6 +202,9 @@ async def _seed_default_command_templates() -> None:
await _seed_provider_command_template(
session, "gitea", "Default Gitea Commands", "Default Gitea command templates",
)
await _seed_provider_command_template(
session, "planka", "Default Planka Commands", "Default Planka command templates",
)
await session.commit()
@@ -227,6 +231,25 @@ async def _seed_default_tracking_configs() -> None:
"track_pr_commented": False,
"track_release_published": True,
},
{
"provider_type": "planka",
"name": "Default Planka",
"track_card_created": True,
"track_card_updated": False,
"track_card_moved": True,
"track_card_deleted": False,
"track_card_commented": True,
"track_comment_updated": False,
"track_board_created": True,
"track_board_updated": False,
"track_board_deleted": True,
"track_list_created": False,
"track_list_updated": False,
"track_list_deleted": False,
"track_attachment_created": True,
"track_card_label_added": False,
"track_task_completed": True,
},
{
"provider_type": "scheduler",
"name": "Default Scheduler",
@@ -280,6 +303,16 @@ async def _seed_default_command_configs() -> None:
"default_count": 10,
"rate_limits": {"api": 15, "default": 10},
},
{
"provider_type": "planka",
"name": "Default Planka",
"enabled_commands": [
"help", "status", "boards", "cards", "lists",
],
"response_mode": "text",
"default_count": 10,
"rate_limits": {"api": 15, "default": 10},
},
]
for cfg in defaults: