From 02484679e2c03a9982871c759f785fc6567e7540 Mon Sep 17 00:00:00 2001 From: JOUNGWOOK KWON Date: Mon, 30 Mar 2026 12:21:06 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=B8=94=EB=A1=9C=EA=B7=B8=20=EB=8C=80?= =?UTF-8?q?=ED=91=9C=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=9E=90=EB=8F=99=20?= =?UTF-8?q?=EC=82=BD=EC=9E=85=20(Pexels/Unsplash)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 발행 시 본문에 가 없으면 자동으로 대표 이미지 추가 - Pexels API (PEXELS_API_KEY 있을 때) → Unsplash Source (무료 폴백) - 글 태그/코너 기반 키워드로 관련 이미지 검색 - Blogger가 첫 번째 를 자동으로 thumbnail로 사용 Co-Authored-By: Claude Opus 4.6 --- bots/publisher_bot.py | 51 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/bots/publisher_bot.py b/bots/publisher_bot.py index e222334..3daacfa 100644 --- a/bots/publisher_bot.py +++ b/bots/publisher_bot.py @@ -206,12 +206,59 @@ def build_json_ld(article: dict, blog_url: str = '') -> str: return f'' +def fetch_featured_image(article: dict) -> str: + """글 키워드 기반 무료 대표 이미지 URL 가져오기 (Pexels → Unsplash fallback)""" + # 검색 키워드: 태그 또는 코너 기반 + tags = article.get('tags', []) + if isinstance(tags, str): + tags = [t.strip() for t in tags.split(',')] + corner = article.get('corner', '') + query = tags[0] if tags else corner or 'technology' + + # Pexels API (키가 있을 때) + pexels_key = os.getenv('PEXELS_API_KEY', '') + if pexels_key: + try: + resp = requests.get( + 'https://api.pexels.com/v1/search', + headers={'Authorization': pexels_key}, + params={'query': query, 'per_page': 1, 'orientation': 'landscape'}, + timeout=10, + ) + if resp.status_code == 200: + photos = resp.json().get('photos', []) + if photos: + return photos[0]['src']['large'] + except Exception as e: + logger.warning(f"Pexels 이미지 검색 실패: {e}") + + # Unsplash Source (API 키 불필요, 직접 URL) + import urllib.parse + encoded = urllib.parse.quote(query) + return f'https://source.unsplash.com/1200x630/?{encoded}' + + def build_full_html(article: dict, body_html: str, toc_html: str) -> str: - """최종 HTML 조합: JSON-LD + 목차 + 본문 + 면책 문구""" + """최종 HTML 조합: 대표이미지 + JSON-LD + 목차 + 본문 + 면책 문구""" json_ld = build_json_ld(article) disclaimer = article.get('disclaimer', '') - html_parts = [json_ld] + # 본문에 이미 태그가 있으면 대표 이미지 삽입 건너뜀 + has_image = '' + f'{title}' + f'' + ) + + html_parts.append(json_ld) if toc_html: html_parts.append(f'
{toc_html}
') html_parts.append(body_html)