feat: initial commit - unified FGTools from static, weather, mattermost-noti
This commit is contained in:
15
services/notification/__init__.py
Normal file
15
services/notification/__init__.py
Normal file
@ -0,0 +1,15 @@
|
||||
# ===================================================================
|
||||
# services/notification/__init__.py
|
||||
# 알림 서비스 패키지 초기화
|
||||
# ===================================================================
|
||||
# Notion 웹훅 처리 및 다양한 알림 서비스를 제공합니다.
|
||||
# ===================================================================
|
||||
|
||||
from .notion import NotionWebhookHandler, get_page_details
|
||||
from .mattermost import MattermostNotifier
|
||||
|
||||
__all__ = [
|
||||
'NotionWebhookHandler',
|
||||
'get_page_details',
|
||||
'MattermostNotifier',
|
||||
]
|
||||
335
services/notification/mattermost.py
Normal file
335
services/notification/mattermost.py
Normal file
@ -0,0 +1,335 @@
|
||||
# ===================================================================
|
||||
# services/notification/mattermost.py
|
||||
# Mattermost 알림 서비스 모듈
|
||||
# ===================================================================
|
||||
# Mattermost로 알림 메시지를 발송하는 전용 서비스입니다.
|
||||
# 웹훅 및 API 방식을 모두 지원합니다.
|
||||
# ===================================================================
|
||||
"""
|
||||
Mattermost 알림 서비스 모듈
|
||||
|
||||
Mattermost 채널로 알림 메시지를 발송합니다.
|
||||
웹훅과 Bot API 두 가지 방식을 지원합니다.
|
||||
|
||||
사용 예시:
|
||||
from services.notification.mattermost import MattermostNotifier
|
||||
|
||||
notifier = MattermostNotifier.from_config()
|
||||
notifier.send_message("서버 점검 알림입니다.")
|
||||
"""
|
||||
|
||||
from typing import Dict, List, Optional, Any
|
||||
|
||||
import requests
|
||||
|
||||
from core.logging_utils import get_logger
|
||||
from core.config import get_config
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class MattermostNotifier:
|
||||
"""
|
||||
Mattermost 알림 발송 클래스
|
||||
|
||||
Mattermost 채널로 메시지를 발송합니다.
|
||||
웹훅 방식과 Bot API 방식을 모두 지원합니다.
|
||||
|
||||
Attributes:
|
||||
server_url: Mattermost 서버 URL
|
||||
bot_token: Bot 인증 토큰
|
||||
channel_id: 기본 채널 ID
|
||||
webhook_url: 웹훅 URL
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
server_url: str = "",
|
||||
bot_token: str = "",
|
||||
channel_id: str = "",
|
||||
webhook_url: str = ""
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
server_url: Mattermost 서버 URL (예: https://mattermost.example.com)
|
||||
bot_token: Bot 인증 토큰
|
||||
channel_id: 기본 채널 ID
|
||||
webhook_url: Incoming 웹훅 URL
|
||||
"""
|
||||
self.server_url = server_url.rstrip('/') if server_url else ""
|
||||
self.bot_token = bot_token
|
||||
self.channel_id = channel_id
|
||||
self.webhook_url = webhook_url
|
||||
|
||||
@classmethod
|
||||
def from_config(cls) -> 'MattermostNotifier':
|
||||
"""
|
||||
설정에서 인스턴스 생성
|
||||
|
||||
Returns:
|
||||
설정이 적용된 MattermostNotifier 인스턴스
|
||||
"""
|
||||
config = get_config()
|
||||
mm_config = config.mattermost
|
||||
|
||||
return cls(
|
||||
server_url=mm_config.get('url', ''),
|
||||
bot_token=mm_config.get('token', ''),
|
||||
channel_id=mm_config.get('channel_id', ''),
|
||||
webhook_url=mm_config.get('webhook_url', ''),
|
||||
)
|
||||
|
||||
def _validate_api_config(self) -> bool:
|
||||
"""API 방식 설정 검증"""
|
||||
if not self.server_url:
|
||||
logger.error("Mattermost 서버 URL이 설정되지 않았습니다.")
|
||||
return False
|
||||
if not self.server_url.startswith(('http://', 'https://')):
|
||||
logger.error(f"유효하지 않은 서버 URL: {self.server_url}")
|
||||
return False
|
||||
if not self.bot_token:
|
||||
logger.error("Bot 토큰이 설정되지 않았습니다.")
|
||||
return False
|
||||
if not self.channel_id:
|
||||
logger.error("채널 ID가 설정되지 않았습니다.")
|
||||
return False
|
||||
return True
|
||||
|
||||
def send_message(
|
||||
self,
|
||||
message: str,
|
||||
channel_id: Optional[str] = None,
|
||||
use_webhook: bool = False,
|
||||
attachments: Optional[List[Dict]] = None,
|
||||
props: Optional[Dict] = None
|
||||
) -> bool:
|
||||
"""
|
||||
메시지 발송
|
||||
|
||||
Args:
|
||||
message: 발송할 메시지
|
||||
channel_id: 채널 ID (None이면 기본 채널)
|
||||
use_webhook: 웹훅 사용 여부
|
||||
attachments: 첨부 데이터 (Mattermost 형식)
|
||||
props: 추가 속성
|
||||
|
||||
Returns:
|
||||
발송 성공 여부
|
||||
"""
|
||||
if use_webhook:
|
||||
return self._send_via_webhook(message, attachments, props)
|
||||
else:
|
||||
return self._send_via_api(message, channel_id, attachments, props)
|
||||
|
||||
def _send_via_webhook(
|
||||
self,
|
||||
message: str,
|
||||
attachments: Optional[List[Dict]] = None,
|
||||
props: Optional[Dict] = None
|
||||
) -> bool:
|
||||
"""
|
||||
웹훅으로 메시지 발송
|
||||
|
||||
Args:
|
||||
message: 발송할 메시지
|
||||
attachments: 첨부 데이터
|
||||
props: 추가 속성
|
||||
|
||||
Returns:
|
||||
발송 성공 여부
|
||||
"""
|
||||
if not self.webhook_url:
|
||||
logger.error("웹훅 URL이 설정되지 않았습니다.")
|
||||
return False
|
||||
|
||||
payload = {"text": message}
|
||||
|
||||
if attachments:
|
||||
payload["attachments"] = attachments
|
||||
if props:
|
||||
payload["props"] = props
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
self.webhook_url,
|
||||
json=payload,
|
||||
headers={"Content-Type": "application/json"},
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
logger.info("Mattermost 웹훅 전송 성공")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"웹훅 전송 실패: {response.status_code} - {response.text}")
|
||||
return False
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
logger.error("웹훅 전송 타임아웃")
|
||||
return False
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"웹훅 전송 예외: {e}")
|
||||
return False
|
||||
|
||||
def _send_via_api(
|
||||
self,
|
||||
message: str,
|
||||
channel_id: Optional[str] = None,
|
||||
attachments: Optional[List[Dict]] = None,
|
||||
props: Optional[Dict] = None
|
||||
) -> bool:
|
||||
"""
|
||||
Bot API로 메시지 발송
|
||||
|
||||
Args:
|
||||
message: 발송할 메시지
|
||||
channel_id: 채널 ID
|
||||
attachments: 첨부 데이터
|
||||
props: 추가 속성
|
||||
|
||||
Returns:
|
||||
발송 성공 여부
|
||||
"""
|
||||
if not self._validate_api_config():
|
||||
return False
|
||||
|
||||
target_channel = channel_id or self.channel_id
|
||||
url = f"{self.server_url}/api/v4/posts"
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.bot_token}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
payload = {
|
||||
"channel_id": target_channel,
|
||||
"message": message
|
||||
}
|
||||
|
||||
if attachments:
|
||||
payload["props"] = payload.get("props", {})
|
||||
payload["props"]["attachments"] = attachments
|
||||
if props:
|
||||
payload["props"] = {**payload.get("props", {}), **props}
|
||||
|
||||
try:
|
||||
response = requests.post(url, json=payload, headers=headers, timeout=10)
|
||||
|
||||
if response.status_code in [200, 201]:
|
||||
logger.info("Mattermost API 전송 성공")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"API 전송 실패: {response.status_code} - {response.text}")
|
||||
return False
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
logger.error("API 전송 타임아웃")
|
||||
return False
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"API 전송 예외: {e}")
|
||||
return False
|
||||
|
||||
def send_formatted_message(
|
||||
self,
|
||||
title: str,
|
||||
text: str,
|
||||
color: str = "#3498db",
|
||||
fields: Optional[List[Dict]] = None,
|
||||
channel_id: Optional[str] = None,
|
||||
use_webhook: bool = False
|
||||
) -> bool:
|
||||
"""
|
||||
서식화된 메시지 발송 (Attachment 사용)
|
||||
|
||||
Args:
|
||||
title: 메시지 제목
|
||||
text: 메시지 본문
|
||||
color: 테두리 색상 (hex)
|
||||
fields: 필드 목록 [{"title": "", "value": "", "short": bool}]
|
||||
channel_id: 채널 ID
|
||||
use_webhook: 웹훅 사용 여부
|
||||
|
||||
Returns:
|
||||
발송 성공 여부
|
||||
"""
|
||||
attachment = {
|
||||
"fallback": f"{title}: {text}",
|
||||
"color": color,
|
||||
"title": title,
|
||||
"text": text,
|
||||
}
|
||||
|
||||
if fields:
|
||||
attachment["fields"] = fields
|
||||
|
||||
return self.send_message(
|
||||
message="",
|
||||
channel_id=channel_id,
|
||||
use_webhook=use_webhook,
|
||||
attachments=[attachment]
|
||||
)
|
||||
|
||||
def send_alert(
|
||||
self,
|
||||
title: str,
|
||||
message: str,
|
||||
level: str = "info",
|
||||
channel_id: Optional[str] = None
|
||||
) -> bool:
|
||||
"""
|
||||
알림 메시지 발송 (레벨에 따른 색상)
|
||||
|
||||
Args:
|
||||
title: 알림 제목
|
||||
message: 알림 내용
|
||||
level: 알림 레벨 (info, warning, error, success)
|
||||
channel_id: 채널 ID
|
||||
|
||||
Returns:
|
||||
발송 성공 여부
|
||||
"""
|
||||
colors = {
|
||||
"info": "#3498db",
|
||||
"warning": "#f39c12",
|
||||
"error": "#e74c3c",
|
||||
"success": "#27ae60"
|
||||
}
|
||||
|
||||
icons = {
|
||||
"info": "ℹ️",
|
||||
"warning": "⚠️",
|
||||
"error": "🚨",
|
||||
"success": "✅"
|
||||
}
|
||||
|
||||
color = colors.get(level, colors["info"])
|
||||
icon = icons.get(level, icons["info"])
|
||||
|
||||
return self.send_formatted_message(
|
||||
title=f"{icon} {title}",
|
||||
text=message,
|
||||
color=color,
|
||||
channel_id=channel_id
|
||||
)
|
||||
|
||||
|
||||
def send_mattermost_notification(
|
||||
message: str,
|
||||
channel_id: Optional[str] = None,
|
||||
use_webhook: bool = False
|
||||
) -> bool:
|
||||
"""
|
||||
Mattermost 알림 간편 함수
|
||||
|
||||
설정에서 자동으로 설정을 로드하여 메시지를 발송합니다.
|
||||
|
||||
Args:
|
||||
message: 발송할 메시지
|
||||
channel_id: 채널 ID (None이면 기본 채널)
|
||||
use_webhook: 웹훅 사용 여부
|
||||
|
||||
Returns:
|
||||
발송 성공 여부
|
||||
"""
|
||||
notifier = MattermostNotifier.from_config()
|
||||
return notifier.send_message(message, channel_id, use_webhook)
|
||||
344
services/notification/notion.py
Normal file
344
services/notification/notion.py
Normal file
@ -0,0 +1,344 @@
|
||||
# ===================================================================
|
||||
# services/notification/notion.py
|
||||
# Notion 웹훅 처리 서비스 모듈
|
||||
# ===================================================================
|
||||
# Notion 웹훅 이벤트를 처리하고 알림 메시지를 생성합니다.
|
||||
# 페이지 생성, 수정, 삭제 등의 이벤트를 지원합니다.
|
||||
# ===================================================================
|
||||
"""
|
||||
Notion 웹훅 처리 서비스 모듈
|
||||
|
||||
Notion 웹훅 이벤트를 수신하고 처리하여 알림 메시지를 생성합니다.
|
||||
|
||||
사용 예시:
|
||||
from services.notification.notion import NotionWebhookHandler
|
||||
|
||||
handler = NotionWebhookHandler(api_secret)
|
||||
message = handler.handle_event(event_data)
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Optional, Any
|
||||
|
||||
import requests
|
||||
|
||||
from core.logging_utils import get_logger
|
||||
from core.config import get_config
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
# Notion API 설정
|
||||
NOTION_API_BASE = "https://api.notion.com/v1"
|
||||
NOTION_VERSION = "2022-06-28"
|
||||
|
||||
# 이벤트 타입별 라벨
|
||||
EVENT_TYPE_LABELS = {
|
||||
"page.created": "📝 새 페이지 생성",
|
||||
"page.content_updated": "✏️ 페이지 내용 수정",
|
||||
"page.properties_updated": "🔄 페이지 속성 변경",
|
||||
"page.deleted": "🗑️ 페이지 삭제",
|
||||
"page.restored": "♻️ 페이지 복구",
|
||||
"page.moved": "📁 페이지 이동",
|
||||
"page.locked": "🔒 페이지 잠금",
|
||||
"page.unlocked": "🔓 페이지 잠금 해제",
|
||||
"database.created": "📊 새 데이터베이스 생성",
|
||||
"database.content_updated": "📊 데이터베이스 업데이트",
|
||||
"block.created": "➕ 블록 생성",
|
||||
"block.changed": "📝 블록 변경",
|
||||
"block.deleted": "➖ 블록 삭제",
|
||||
"comment.created": "💬 새 댓글",
|
||||
"comment.updated": "💬 댓글 수정",
|
||||
"comment.deleted": "💬 댓글 삭제",
|
||||
}
|
||||
|
||||
|
||||
def get_page_details(page_id: str, api_secret: str) -> Optional[Dict]:
|
||||
"""
|
||||
Notion 페이지 상세 정보 조회
|
||||
|
||||
Args:
|
||||
page_id: 페이지 ID
|
||||
api_secret: Notion API 시크릿
|
||||
|
||||
Returns:
|
||||
페이지 상세 정보 또는 None
|
||||
"""
|
||||
url = f"{NOTION_API_BASE}/pages/{page_id}"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {api_secret}",
|
||||
"Notion-Version": NOTION_VERSION,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.get(url, headers=headers, timeout=10)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
logger.error(f"페이지 조회 실패: {response.status_code} - {response.text}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"페이지 조회 예외: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def get_database_details(database_id: str, api_secret: str) -> Optional[Dict]:
|
||||
"""
|
||||
Notion 데이터베이스 상세 정보 조회
|
||||
|
||||
Args:
|
||||
database_id: 데이터베이스 ID
|
||||
api_secret: Notion API 시크릿
|
||||
|
||||
Returns:
|
||||
데이터베이스 상세 정보 또는 None
|
||||
"""
|
||||
url = f"{NOTION_API_BASE}/databases/{database_id}"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {api_secret}",
|
||||
"Notion-Version": NOTION_VERSION,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.get(url, headers=headers, timeout=10)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
logger.error(f"데이터베이스 조회 실패: {response.status_code}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"데이터베이스 조회 예외: {e}")
|
||||
return None
|
||||
|
||||
|
||||
class NotionWebhookHandler:
|
||||
"""
|
||||
Notion 웹훅 이벤트 핸들러
|
||||
|
||||
Notion에서 전송하는 웹훅 이벤트를 처리하고
|
||||
알림 메시지를 생성합니다.
|
||||
|
||||
Attributes:
|
||||
api_secret: Notion API 시크릿
|
||||
allowed_workspaces: 허용된 워크스페이스 이름 목록
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
api_secret: Optional[str] = None,
|
||||
allowed_workspaces: Optional[List[str]] = None
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
api_secret: Notion API 시크릿 (None이면 설정에서 로드)
|
||||
allowed_workspaces: 허용된 워크스페이스 목록 (None이면 모두 허용)
|
||||
"""
|
||||
if api_secret is None:
|
||||
config = get_config()
|
||||
api_secret = config.notion.get('api_secret', '')
|
||||
|
||||
self.api_secret = api_secret
|
||||
self.allowed_workspaces = allowed_workspaces or []
|
||||
|
||||
self.headers = {
|
||||
"Authorization": f"Bearer {self.api_secret}",
|
||||
"Notion-Version": NOTION_VERSION,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
def fetch_entity_detail(self, entity_type: str, entity_id: str) -> Optional[Dict]:
|
||||
"""
|
||||
엔티티 상세 정보 조회
|
||||
|
||||
Args:
|
||||
entity_type: 엔티티 타입 ('page', 'database', 'block' 등)
|
||||
entity_id: 엔티티 ID
|
||||
|
||||
Returns:
|
||||
엔티티 상세 정보 또는 None
|
||||
"""
|
||||
if entity_type == "page":
|
||||
url = f"{NOTION_API_BASE}/pages/{entity_id}"
|
||||
elif entity_type == "database":
|
||||
url = f"{NOTION_API_BASE}/databases/{entity_id}"
|
||||
elif entity_type == "block":
|
||||
url = f"{NOTION_API_BASE}/blocks/{entity_id}"
|
||||
else:
|
||||
logger.warning(f"지원하지 않는 엔티티 타입: {entity_type}")
|
||||
return None
|
||||
|
||||
try:
|
||||
response = requests.get(url, headers=self.headers, timeout=10)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
logger.error(f"엔티티 조회 실패: {response.status_code}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"엔티티 조회 예외: {e}")
|
||||
return None
|
||||
|
||||
def extract_title_from_properties(self, properties: Dict) -> str:
|
||||
"""
|
||||
속성에서 제목 추출
|
||||
|
||||
Args:
|
||||
properties: Notion 속성 딕셔너리
|
||||
|
||||
Returns:
|
||||
제목 문자열 (없으면 기본값)
|
||||
"""
|
||||
# 일반적인 제목 속성 이름들
|
||||
title_keys = ['Name', 'name', '이름', '제목', 'Title', '작업 이름']
|
||||
|
||||
for key in title_keys:
|
||||
if key in properties:
|
||||
prop = properties[key]
|
||||
if prop.get('type') == 'title':
|
||||
title_arr = prop.get('title', [])
|
||||
if title_arr:
|
||||
return ''.join(t.get('plain_text', '') for t in title_arr)
|
||||
|
||||
# title 타입 속성 찾기
|
||||
for prop in properties.values():
|
||||
if prop.get('type') == 'title':
|
||||
title_arr = prop.get('title', [])
|
||||
if title_arr:
|
||||
return ''.join(t.get('plain_text', '') for t in title_arr)
|
||||
|
||||
return "(제목 없음)"
|
||||
|
||||
def extract_editor_from_properties(self, properties: Dict) -> str:
|
||||
"""
|
||||
속성에서 편집자 정보 추출
|
||||
|
||||
Args:
|
||||
properties: Notion 속성 딕셔너리
|
||||
|
||||
Returns:
|
||||
편집자 이름 (없으면 기본값)
|
||||
"""
|
||||
editor_keys = ['최종 편집자', 'Last edited by', 'Editor']
|
||||
|
||||
for key in editor_keys:
|
||||
if key in properties:
|
||||
prop = properties[key]
|
||||
if prop.get('type') == 'last_edited_by':
|
||||
editor = prop.get('last_edited_by', {})
|
||||
return editor.get('name', '(알 수 없음)')
|
||||
|
||||
return "(알 수 없음)"
|
||||
|
||||
def handle_event(self, event: Dict) -> Optional[str]:
|
||||
"""
|
||||
Notion 웹훅 이벤트 처리
|
||||
|
||||
Args:
|
||||
event: 웹훅 이벤트 데이터
|
||||
|
||||
Returns:
|
||||
알림 메시지 또는 None (무시할 이벤트)
|
||||
"""
|
||||
# 이벤트 로깅
|
||||
logger.info(f"수신된 Notion 이벤트: {json.dumps(event, ensure_ascii=False)[:500]}")
|
||||
|
||||
# 워크스페이스 확인
|
||||
workspace_id = event.get('workspace_id')
|
||||
if not workspace_id:
|
||||
logger.warning("workspace_id 없음 - 이벤트 무시")
|
||||
return None
|
||||
|
||||
# 허용된 워크스페이스 확인 (설정된 경우)
|
||||
if self.allowed_workspaces:
|
||||
workspace_name = self.get_workspace_name(workspace_id)
|
||||
if workspace_name not in self.allowed_workspaces:
|
||||
logger.info(f"허용되지 않은 워크스페이스: {workspace_name}")
|
||||
return None
|
||||
|
||||
# 이벤트 정보 추출
|
||||
event_type = event.get('type', 'unknown')
|
||||
timestamp = event.get('timestamp')
|
||||
|
||||
# 시간 포맷팅
|
||||
readable_time = None
|
||||
if timestamp:
|
||||
try:
|
||||
readable_time = datetime.fromisoformat(
|
||||
timestamp.replace('Z', '+00:00')
|
||||
).strftime('%Y-%m-%d %H:%M:%S')
|
||||
except Exception:
|
||||
readable_time = timestamp
|
||||
|
||||
# 엔티티 정보
|
||||
entity = event.get('entity', {})
|
||||
entity_id = entity.get('id', 'unknown')
|
||||
entity_type = entity.get('type', 'unknown')
|
||||
|
||||
# 상세 정보 조회
|
||||
detail = self.fetch_entity_detail(entity_type, entity_id)
|
||||
|
||||
if not detail:
|
||||
logger.warning("상세 정보 조회 실패")
|
||||
return None
|
||||
|
||||
# 정보 추출
|
||||
properties = detail.get('properties', {})
|
||||
page_title = self.extract_title_from_properties(properties)
|
||||
editor_name = self.extract_editor_from_properties(properties)
|
||||
page_url = detail.get('url', 'URL 없음')
|
||||
|
||||
# 이벤트 라벨
|
||||
event_label = EVENT_TYPE_LABELS.get(event_type, f"📢 Notion 이벤트: `{event_type}`")
|
||||
|
||||
# 메시지 구성
|
||||
message = (
|
||||
f"{event_label}\n"
|
||||
f"- 🕒 시간: {readable_time or '알 수 없음'}\n"
|
||||
f"- 📄 페이지: {page_title}\n"
|
||||
f"- 👤 작업자: {editor_name}\n"
|
||||
f"- 🔗 [바로가기]({page_url})"
|
||||
)
|
||||
|
||||
logger.info(f"생성된 메시지: {message[:200]}...")
|
||||
return message
|
||||
|
||||
def get_workspace_name(self, workspace_id: str) -> str:
|
||||
"""
|
||||
워크스페이스 이름 조회
|
||||
|
||||
Note: Notion API에서 별도의 워크스페이스 조회 엔드포인트가 없어
|
||||
현재는 하드코딩되어 있습니다.
|
||||
|
||||
Args:
|
||||
workspace_id: 워크스페이스 ID
|
||||
|
||||
Returns:
|
||||
워크스페이스 이름
|
||||
"""
|
||||
# TODO: 실제 API가 지원되면 구현
|
||||
return "퍼스트가든"
|
||||
|
||||
def create_summary_from_page(self, page_data: Dict) -> str:
|
||||
"""
|
||||
페이지 데이터에서 요약 메시지 생성
|
||||
|
||||
Args:
|
||||
page_data: 페이지 상세 데이터
|
||||
|
||||
Returns:
|
||||
요약 메시지
|
||||
"""
|
||||
properties = page_data.get('properties', {})
|
||||
title = self.extract_title_from_properties(properties)
|
||||
url = page_data.get('url', 'URL 없음')
|
||||
|
||||
return f"📌 노션 페이지 업데이트됨\n**제목**: {title}\n🔗 [바로가기]({url})"
|
||||
Reference in New Issue
Block a user