Mr-Help commited on
Commit
f980c60
ยท
verified ยท
1 Parent(s): a0d681f

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +143 -7
main.py CHANGED
@@ -1,17 +1,32 @@
1
  # main.py
2
  import os
 
 
 
3
  import httpx
4
  from fastapi import FastAPI, Request
5
 
 
 
 
6
  app = FastAPI()
7
 
8
- # ู„ูˆ ุญุงุจุจ ุชุญุท ู‚ูŠู…ุฉ ุงูุชุฑุงุถูŠุฉ ู„ู„ุชุณุช
9
  NOTION_SECRET = os.getenv("NOTION_SECRET", "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")
10
  NOTION_VERSION = "2025-09-03"
 
11
 
 
 
 
 
 
 
12
 
 
 
13
  def build_content_template_children():
14
- """ู†ูุณ ุงู„ุชู…ุจู„ุช ุจุงู„ู…ู„ู‘ูŠ ู…ู† ูƒูˆุฏ 2 ู„ูƒู† ูƒู€ children ูู‚ุท."""
15
  return [
16
  # ===== Assets link =====
17
  {
@@ -309,6 +324,8 @@ def build_content_template_children():
309
  ]
310
 
311
 
 
 
312
  async def get_page_title(page_id: str) -> str:
313
  """Fetch page title from Notion."""
314
  async with httpx.AsyncClient() as client:
@@ -331,6 +348,7 @@ async def get_page_title(page_id: str) -> str:
331
 
332
  return "(No Title)"
333
 
 
334
  async def is_block_archived(page_id: str) -> bool:
335
  """ูŠุฑุฌุน True ู„ูˆ ุงู„ุจู„ูˆูƒ/ุงู„ุตูุญุฉ archived."""
336
  async with httpx.AsyncClient() as client:
@@ -348,8 +366,7 @@ async def is_block_archived(page_id: str) -> bool:
348
  print("[ARCHIVE CHECK] Response:", res.json())
349
  except Exception:
350
  print("[ARCHIVE CHECK] Raw response:", res.text)
351
- # ู„ูˆ ู…ุด ุนุงุฑููŠู† ุญุงู„ุชู‡ุŒ ู‡ู†ุนุชุจุฑู‡ ู…ุด archived ุนุดุงู† ู…ุง ู†ูƒุณุฑุด ุงู„ูู„ูˆ
352
- return False
353
 
354
  data = res.json()
355
  archived = data.get("archived", False)
@@ -381,6 +398,119 @@ async def apply_template_to_page(page_id: str):
381
  print("[TEMPLATE] Raw Response:", res.text)
382
 
383
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
384
  @app.post("/webhook")
385
  async def webhook(request: Request):
386
  body = await request.json()
@@ -392,7 +522,6 @@ async def webhook(request: Request):
392
  print("\n===== NEW WEBHOOK EVENT =====")
393
  print("Raw body:", body)
394
 
395
- # ู†ุชุนุงู…ู„ ู…ุน ุฅู†ุดุงุก ุตูุญุฉ ุฌุฏูŠุฏุฉ ูู‚ุท
396
  if event_type == "page.created":
397
  title = await get_page_title(page_id)
398
 
@@ -404,14 +533,21 @@ async def webhook(request: Request):
404
  # ุชุดูŠูƒ ุงู„ุฃูˆู„: ู‡ู„ ุงู„ุตูุญุฉ ุฏูŠ Archived (ุฒูŠ ู„ู…ุง ุชุฏูˆุณ Escape ุนู„ู‰ placeholder)ุŸ
405
  archived = await is_block_archived(page_id)
406
  if archived:
407
- print(f"[TEMPLATE] Page {page_id} is archived right after creation. Skipping template apply.")
408
  else:
409
- # ู†ุทุจู‘ู‚ ุงู„ุชู…ุจู„ุช ุนู„ู‰ ุงู„ู€ Page ุฏูŠ
410
  try:
411
  await apply_template_to_page(page_id)
412
  except Exception as e:
413
  print("[ERROR] While applying template:", e)
414
 
 
 
 
 
 
 
 
415
  elif event_type == "page.deleted":
416
  print(f"Page deleted. ID: {page_id}, Timestamp: {timestamp}")
417
 
 
1
  # main.py
2
  import os
3
+ import json
4
+ from datetime import datetime
5
+
6
  import httpx
7
  from fastapi import FastAPI, Request
8
 
9
+ from google.oauth2 import service_account
10
+ from googleapiclient.discovery import build
11
+
12
  app = FastAPI()
13
 
14
+ # ===== Notion Config =====
15
  NOTION_SECRET = os.getenv("NOTION_SECRET", "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")
16
  NOTION_VERSION = "2025-09-03"
17
+ NOTION_DRIVE_PROP_ID = os.getenv("NOTION_DRIVE_PROP_ID", "H=Fs") # ID ุจุชุงุน ุญู‚ู„ Google Drive Link
18
 
19
+ # ===== Google Drive Config =====
20
+ GOOGLE_CREDENTIALS = os.getenv("GOOGLE_CREDENTIALS") # JSON string ู„ู„ู€ service account
21
+ PARENT_FOLDER_ID = os.getenv(
22
+ "PARENT_FOLDER_ID",
23
+ "1ddMDm2SFnNfmt6pGIMB5-HI54ywd52Jt" # ุงู„ู„ูŠ ุงู†ุช ุจุนุชู‡ุงู„ูŠ
24
+ )
25
 
26
+
27
+ # ========== Notion Template Blocks ==========
28
  def build_content_template_children():
29
+ """ู†ูุณ ุงู„ุชู…ุจู„ุช ุจุงู„ู…ู„ู‘ูŠ ู„ูƒู† ูƒู€ children ูู‚ุท."""
30
  return [
31
  # ===== Assets link =====
32
  {
 
324
  ]
325
 
326
 
327
+ # ========== Notion Helpers ==========
328
+
329
  async def get_page_title(page_id: str) -> str:
330
  """Fetch page title from Notion."""
331
  async with httpx.AsyncClient() as client:
 
348
 
349
  return "(No Title)"
350
 
351
+
352
  async def is_block_archived(page_id: str) -> bool:
353
  """ูŠุฑุฌุน True ู„ูˆ ุงู„ุจู„ูˆูƒ/ุงู„ุตูุญุฉ archived."""
354
  async with httpx.AsyncClient() as client:
 
366
  print("[ARCHIVE CHECK] Response:", res.json())
367
  except Exception:
368
  print("[ARCHIVE CHECK] Raw response:", res.text)
369
+ return False # ู…ุง ู†ูƒุณุฑุด ุงู„ูู„ูˆ
 
370
 
371
  data = res.json()
372
  archived = data.get("archived", False)
 
398
  print("[TEMPLATE] Raw Response:", res.text)
399
 
400
 
401
+ async def update_page_drive_link(page_id: str, drive_link: str):
402
+ """ูŠุญุฏุซ ุญู‚ู„ URL ููŠ ุงู„ุตูุญุฉ ุจุฑุงุจุท Google Drive."""
403
+ print(f"[NOTION] Updating drive link for page {page_id} -> {drive_link}")
404
+
405
+ async with httpx.AsyncClient() as client:
406
+ res = await client.patch(
407
+ f"https://api.notion.com/v1/pages/{page_id}",
408
+ headers={
409
+ "Authorization": f"Bearer {NOTION_SECRET}",
410
+ "Notion-Version": NOTION_VERSION,
411
+ "Content-Type": "application/json",
412
+ },
413
+ json={
414
+ "properties": {
415
+ NOTION_DRIVE_PROP_ID: {
416
+ "url": drive_link
417
+ }
418
+ }
419
+ },
420
+ )
421
+
422
+ print(f"[NOTION] Drive link update status: {res.status_code}")
423
+ try:
424
+ print("[NOTION] Drive link response:", res.json())
425
+ except Exception:
426
+ print("[NOTION] Drive link raw response:", res.text)
427
+
428
+
429
+ # ========== Google Drive Helpers ==========
430
+
431
+ def get_drive_service():
432
+ if not GOOGLE_CREDENTIALS:
433
+ raise RuntimeError("GOOGLE_CREDENTIALS env var is missing")
434
+
435
+ creds_info = json.loads(GOOGLE_CREDENTIALS)
436
+ creds = service_account.Credentials.from_service_account_info(
437
+ creds_info,
438
+ scopes=["https://www.googleapis.com/auth/drive"]
439
+ )
440
+ service = build("drive", "v3", credentials=creds)
441
+ return service
442
+
443
+
444
+ def search_folder(name: str, parent_id: str, drive_service):
445
+ query = (
446
+ f"name = '{name}' and "
447
+ f"'{parent_id}' in parents and "
448
+ "mimeType = 'application/vnd.google-apps.folder' and trashed = false"
449
+ )
450
+ results = drive_service.files().list(
451
+ q=query,
452
+ fields="files(id, name)"
453
+ ).execute()
454
+ files = results.get("files", [])
455
+ return files[0] if files else None
456
+
457
+
458
+ def create_folder(name: str, parent_id: str, drive_service):
459
+ metadata = {
460
+ "name": name,
461
+ "mimeType": "application/vnd.google-apps.folder",
462
+ "parents": [parent_id],
463
+ }
464
+ folder = drive_service.files().create(
465
+ body=metadata,
466
+ fields="id, name"
467
+ ).execute()
468
+ return folder
469
+
470
+
471
+ def ensure_drive_folder_for_page(page_title: str) -> str:
472
+ """
473
+ - ุฌูˆู‡ PARENT_FOLDER_ID
474
+ - ููˆู„ุฏุฑ ุดู‡ุฑ (Dec 2025 ู…ุซู„ุงู‹)
475
+ - ุฌูˆู‡ ููˆู„ุฏุฑ ุจุงุณู… ุงู„ู€ page_title
476
+ ูˆุชุฑุฌุน Google Drive folder link.
477
+ """
478
+ drive_service = get_drive_service()
479
+
480
+ # ู„ูˆ ุงู„ุนู†ูˆุงู† ูุงุถูŠุŒ ุงุณุชุฎุฏู… page_id ุฃูˆ ุงุณู… fallback ู…ู† ุจุฑู‘ู‡
481
+ safe_title = page_title.strip() if page_title and page_title.strip() else "Untitled Task"
482
+
483
+ # ู…ู† ุงู„ุฃูุถู„ ู†ุดูŠู„ Slash ุนุดุงู† ู…ุง ูŠูƒุณุฑุด ุงู„ุงุณู…
484
+ safe_title = safe_title.replace("/", "-")
485
+
486
+ month_name = datetime.now().strftime("%b %Y") # ู…ุซุงู„: "Dec 2025"
487
+
488
+ print(f"[DRIVE] Ensuring month folder '{month_name}' under parent {PARENT_FOLDER_ID}")
489
+ month_folder = search_folder(month_name, PARENT_FOLDER_ID, drive_service)
490
+ if not month_folder:
491
+ month_folder = create_folder(month_name, PARENT_FOLDER_ID, drive_service)
492
+ print(f"[DRIVE] Created month folder: {month_folder['id']} - {month_folder['name']}")
493
+ else:
494
+ print(f"[DRIVE] Found month folder: {month_folder['id']} - {month_folder['name']}")
495
+
496
+ month_folder_id = month_folder["id"]
497
+
498
+ print(f"[DRIVE] Ensuring page folder '{safe_title}' under month folder {month_folder_id}")
499
+ page_folder = search_folder(safe_title, month_folder_id, drive_service)
500
+ if not page_folder:
501
+ page_folder = create_folder(safe_title, month_folder_id, drive_service)
502
+ print(f"[DRIVE] Created page folder: {page_folder['id']} - {page_folder['name']}")
503
+ else:
504
+ print(f"[DRIVE] Found page folder: {page_folder['id']} - {page_folder['name']}")
505
+
506
+ folder_id = page_folder["id"]
507
+ drive_link = f"https://drive.google.com/drive/folders/{folder_id}"
508
+ print(f"[DRIVE] Final folder link: {drive_link}")
509
+ return drive_link
510
+
511
+
512
+ # ========== Webhook ==========
513
+
514
  @app.post("/webhook")
515
  async def webhook(request: Request):
516
  body = await request.json()
 
522
  print("\n===== NEW WEBHOOK EVENT =====")
523
  print("Raw body:", body)
524
 
 
525
  if event_type == "page.created":
526
  title = await get_page_title(page_id)
527
 
 
533
  # ุชุดูŠูƒ ุงู„ุฃูˆู„: ู‡ู„ ุงู„ุตูุญุฉ ุฏูŠ Archived (ุฒูŠ ู„ู…ุง ุชุฏูˆุณ Escape ุนู„ู‰ placeholder)ุŸ
534
  archived = await is_block_archived(page_id)
535
  if archived:
536
+ print(f"[TEMPLATE] Page {page_id} is archived right after creation. Skipping template & drive.")
537
  else:
538
+ # 1) ู†ุทุจู‘ู‚ ุงู„ุชู…ุจู„ุช
539
  try:
540
  await apply_template_to_page(page_id)
541
  except Exception as e:
542
  print("[ERROR] While applying template:", e)
543
 
544
+ # 2) ู†ู†ุดุฆ ููˆู„ุฏุฑ ุนู„ู‰ ุงู„ุฏุฑุงูŠู ุจุงุณู… ุงู„ุตูุญุฉ ูˆู†ุญุท ุงู„ู„ูŠู†ูƒ ููŠ ุงู„ู†ูˆุดู†
545
+ try:
546
+ drive_link = ensure_drive_folder_for_page(title or page_id)
547
+ await update_page_drive_link(page_id, drive_link)
548
+ except Exception as e:
549
+ print("[ERROR] While creating drive folder or updating Notion URL:", e)
550
+
551
  elif event_type == "page.deleted":
552
  print(f"Page deleted. ID: {page_id}, Timestamp: {timestamp}")
553