|
1 | 1 | import json |
| 2 | +from datetime import datetime, timezone |
2 | 3 |
|
3 | 4 | import httpx |
4 | 5 | import structlog |
|
21 | 22 | build_approval_modal, |
22 | 23 | build_draft_card, |
23 | 24 | build_generation_modal, |
| 25 | + build_schedule_modal, |
24 | 26 | build_upload_modal, |
25 | 27 | ) |
26 | 28 |
|
@@ -117,8 +119,31 @@ async def slack_interactions( |
117 | 119 | draft=db_draft.content or "", |
118 | 120 | platform=db_draft.platform, |
119 | 121 | draft_id=str(db_draft.id), |
120 | | - channel_id="", # Порожньо, бо відкриваємо не з чату |
121 | | - message_ts="", # Порожньо, бо відкриваємо не з чату |
| 122 | + channel_id="", |
| 123 | + message_ts="", |
| 124 | + ) |
| 125 | + async with httpx.AsyncClient() as client: |
| 126 | + await client.post( |
| 127 | + "https://slack.com/api/views.open", |
| 128 | + headers=headers, |
| 129 | + json={"trigger_id": trigger_id, "view": modal_view}, |
| 130 | + ) |
| 131 | + return Response(status_code=200) |
| 132 | + |
| 133 | + if action_id == "action_schedule_draft": |
| 134 | + if draft_id.isdigit(): |
| 135 | + repo = DraftRepository(session) |
| 136 | + db_draft = await repo.get_by_id(int(draft_id)) |
| 137 | + if db_draft: |
| 138 | + sched_ts = ( |
| 139 | + int(db_draft.scheduled_at.timestamp()) |
| 140 | + if db_draft.scheduled_at |
| 141 | + else None |
| 142 | + ) |
| 143 | + modal_view = build_schedule_modal( |
| 144 | + draft_id=draft_id, |
| 145 | + platform=platform, |
| 146 | + scheduled_at=sched_ts, |
122 | 147 | ) |
123 | 148 | async with httpx.AsyncClient() as client: |
124 | 149 | await client.post( |
@@ -363,7 +388,51 @@ async def slack_interactions( |
363 | 388 | status_code=200, |
364 | 389 | ) |
365 | 390 |
|
366 | | - # --- СЦЕНАРІЙ 3: Завантаження гайдлайну --- |
| 391 | + # --- СЦЕНАРІЙ 3: Планування публікації --- |
| 392 | + elif callback_id == "modal_schedule_draft": |
| 393 | + metadata_parts = view.get("private_metadata", "").split("|") |
| 394 | + draft_id = metadata_parts[0] if len(metadata_parts) > 0 else "" |
| 395 | + platform = metadata_parts[1] if len(metadata_parts) > 1 else "telegram" |
| 396 | + |
| 397 | + schedule_timestamp = ( |
| 398 | + state_values.get("block_schedule_time", {}) |
| 399 | + .get("input_schedule_time", {}) |
| 400 | + .get("selected_date_time") |
| 401 | + ) |
| 402 | + |
| 403 | + if not schedule_timestamp or not draft_id.isdigit(): |
| 404 | + return Response( |
| 405 | + content=json.dumps({ |
| 406 | + "response_action": "errors", |
| 407 | + "errors": { |
| 408 | + "block_schedule_time": SLACK_UI["schedule_no_time_error"] |
| 409 | + }, |
| 410 | + }), |
| 411 | + media_type="application/json", |
| 412 | + status_code=200, |
| 413 | + ) |
| 414 | + |
| 415 | + scheduled_at = datetime.fromtimestamp(int(schedule_timestamp), tz=timezone.utc) |
| 416 | + repo = DraftRepository(session) |
| 417 | + await repo.update( |
| 418 | + int(draft_id), |
| 419 | + DraftUpdate(status=DraftStatus.SCHEDULED, scheduled_at=scheduled_at), |
| 420 | + ) |
| 421 | + |
| 422 | + logger.info( |
| 423 | + "draft_scheduled", |
| 424 | + draft_id=draft_id, |
| 425 | + platform=platform, |
| 426 | + scheduled_at=scheduled_at.isoformat(), |
| 427 | + ) |
| 428 | + |
| 429 | + return Response( |
| 430 | + content=json.dumps({"response_action": "clear"}), |
| 431 | + media_type="application/json", |
| 432 | + status_code=200, |
| 433 | + ) |
| 434 | + |
| 435 | + # --- СЦЕНАРІЙ 4: Завантаження гайдлайну --- |
367 | 436 | elif callback_id == "modal_upload_guideline": |
368 | 437 | files = ( |
369 | 438 | state_values.get("block_file_upload", {}) |
@@ -406,22 +475,32 @@ async def slack_events( |
406 | 475 | event = data.get("event", {}) |
407 | 476 | user_id = event.get("user") |
408 | 477 |
|
| 478 | + logger.info("slack_event_received", event_type=event.get("type"), user_id=user_id) |
| 479 | + |
409 | 480 | if event.get("type") == "app_home_opened": |
410 | 481 | # 1. Витягуємо останні 10 драфтів |
411 | 482 | repo = DraftRepository(session) |
412 | 483 | recent_drafts = await repo.get_recent_drafts(limit=10) |
413 | 484 |
|
| 485 | + logger.info("slack_home_opened", user_id=user_id, drafts_count=len(recent_drafts)) |
| 486 | + |
414 | 487 | # 2. Рендеримо дашборд |
415 | 488 | slack_token = ( |
416 | 489 | settings.SLACK_BOT_TOKEN.get_secret_value() |
417 | 490 | if hasattr(settings.SLACK_BOT_TOKEN, "get_secret_value") |
418 | 491 | else settings.SLACK_BOT_TOKEN |
419 | 492 | ) |
| 493 | + home_view = build_app_home(drafts=recent_drafts) |
420 | 494 | async with httpx.AsyncClient() as client: |
421 | | - await client.post( |
| 495 | + res = await client.post( |
422 | 496 | "https://slack.com/api/views.publish", |
423 | 497 | headers={"Authorization": f"Bearer {slack_token}"}, |
424 | | - json={"user_id": user_id, "view": build_app_home(drafts=recent_drafts)}, |
| 498 | + json={"user_id": user_id, "view": home_view}, |
425 | 499 | ) |
| 500 | + resp_data = res.json() |
| 501 | + if not resp_data.get("ok"): |
| 502 | + logger.error("slack_home_publish_error", error=resp_data) |
| 503 | + else: |
| 504 | + logger.info("slack_home_published", user_id=user_id) |
426 | 505 |
|
427 | 506 | return Response(status_code=200) |
0 commit comments