결정(2026-09-29): 카톡과 메일 둘 다, 승인은 링크가 아니라 연결된 계정 신원으로. 이 커밋은 발송과 메시지 그리기까지고 [승인] 버튼은 다음 단계(2-2)다. - blog_jobs._send_one: 메일에 더해 Event API 로 보낸다. 하나라도 나가면 SENT, 아무 데도 안 나가면 SENT 로 표시하지 않아 다음 스윕이 다시 시도한다. 카톡은 채널 친구가 아니거나 차단했으면 실패하므로 메일을 빼지 않는다. send_now 도 같은 경로 - 카톡 params 로 post_id 와 수정용 일회용 코드(edit_token)를 넘긴다. 코드 평문은 발송 시점에만 알아서다. 로그에는 params 의 키만 남기고 값은 남기지 않는다 - channel.approval_notice: 발화자 키 → 사장님 → 그 글이 그 사장님 가게 것·미처리· 기한 전일 때만 본문과 [수정하기] 를 준다. 연결 안 됨·남의 글·처리됨·만료·이상한 ID 는 구분 없이 같은 안내(구분해 주면 글 ID 를 탐색할 수 있다) - kakao_bot: userRequest.params.post_id 가 있으면 승인 알림 요청으로 처리하고 링크 버튼은 본문과 따로 textCard 로 그린다(카드 설명 길이 제한을 피한다) - KAKAO_APPROVAL_PUSH_ENABLED(기본 0), KAKAO_APPROVAL_EVENT_NAME 추가 — 오픈빌더 이벤트 블록(스킬 연결)과 배포가 끝나기 전에는 켜지 않는다 test_blog_owner 6건·test_kakao_webhook 6건 추가, 카카오·미니블로그 스위트 119 passed, 인접 스위트 110 passed. 실제 카톡 수신은 콘솔 설정·운영 배포 뒤에 확인한다.
83 lines
4.6 KiB
Python
83 lines
4.6 KiB
Python
"""사장님 에이전트 설정 — 루트 .env 하나만 읽는다(APP_ENV=test 면 .env 를 읽지 않는다).
|
|
|
|
★ SNS 게재(social_config)와 파일을 가른 이유는 도메인이 다르기 때문이다.
|
|
SNS 게재는 **되돌릴 수 없는** 대외 발화이고, 에이전트는 사장님이 자기 사이트를
|
|
고치는 창구다. 승인 강도도 보관하는 것도 다르다 — 설정이 한 파일에 섞이면
|
|
"이 값이 무엇을 여는가" 가 흐려진다.
|
|
"""
|
|
|
|
from pydantic_settings import BaseSettings
|
|
|
|
from config.config_models import _BASE
|
|
|
|
|
|
class AgentConfig(BaseSettings):
|
|
model_config = _BASE
|
|
|
|
# 카카오톡 채널 공개 ID(`_xaBcD` 형태). 사장님이 채널을 찾아 코드를 입력해야 하므로
|
|
# ★ 이 값이 없으면 연결 화면 자체를 열지 않는다 — 어디에 코드를 칠지 말해 줄 수
|
|
# 없는데 코드만 발급하면, 사장님에게는 고장난 화면이다(Threads 카드와 같은 규칙).
|
|
KAKAO_CHANNEL_PUBLIC_ID: str = ""
|
|
# 코드 수명. 사장님이 화면을 보고 카톡을 열어 치는 동작이라 짧아도 된다.
|
|
KAKAO_LINK_CODE_TTL_MIN: int = 10
|
|
# 코드가 짧아서(사람이 손으로 친다) 무차별 대입이 가능하다. 시도 수로 끊는다.
|
|
KAKAO_LINK_MAX_ATTEMPTS: int = 5
|
|
|
|
# 빌더 화면의 대화창. 2026-09-21 에 한 번 닫았다가(카카오 채널 보류) 채널 인증이
|
|
# 끝나 다시 열었다(2026-09-22).
|
|
# ★ 이 값이 "1" 이어도 **LLM 키가 없으면 안 열린다**(runtime.is_configured 가 둘 다 본다) —
|
|
# 키 없는 환경에서 켜 둔 채 잊어도 "눌러도 안 되는 입구" 가 생기지 않는다.
|
|
# 다시 닫을 일이 생기면 이 값만 "0" 으로 되돌린다. 코드를 되짚지 않는다.
|
|
AGENT_CHAT_ENABLED: str = "1"
|
|
|
|
# ★ 카카오 웹훅 인증. **오픈빌더는 서명을 주지 않는다** — URL 만 알면 누구나 이 엔드포인트를
|
|
# 때릴 수 있고, user.id 를 아무 값이나 넣으면 **그 사장님 행세를 한다.** 신원 연결
|
|
# (owner_kakao_links)이 통째로 무의미해진다.
|
|
# 그래서 이 값이 없으면 **엔드포인트 자체를 띄우지 않는다**(404). 반쯤 열린 상태를
|
|
# 만들지 않는 것은 Threads 연결과 같은 규칙이다.
|
|
# 만드는 법: python -c "import secrets; print(secrets.token_urlsafe(32))"
|
|
KAKAO_WEBHOOK_SECRET: str = ""
|
|
# 우리 봇이 맞는지 한 겹 더 본다. 시크릿이 아니라 오발송을 거르는 용도라 비워도 된다.
|
|
# ★ Event API(챗봇이 먼저 보내기)는 이 값이 **필수**다 — 요청 주소에 봇 ID 가 들어간다.
|
|
KAKAO_BOT_ID: str = ""
|
|
|
|
# ★ Event API — 연결된 사장님에게 챗봇이 먼저 말을 거는 통로(services/external/kakao_event.py).
|
|
# **채널을 연결한 비즈니스 인증 앱**의 REST API 키. 비어 있으면 Event API 는 존재하지 않는다.
|
|
# ★ 기존 KAKAO_REST_API_KEY(카카오 로컬 API, 주소 검색 어댑터)와 이름을 일부러 갈랐다 —
|
|
# 앱이 다를 수 있고, 같은 이름이면 한쪽을 채울 때 다른 쪽이 조용히 켜지거나 틀린 키로 나간다.
|
|
KAKAO_BOT_REST_API_KEY: str = ""
|
|
# "1" 이면 봇 ID 뒤에 "!" 를 붙여 **개발 채널**로 보낸다(운영 채널과 요청 주소가 다르다).
|
|
# ★ KAKAO_BOT_ID 자체를 고쳐 쓰지 않는다 — 웹훅이 그 값으로 요청의 bot.id 를 대조한다.
|
|
KAKAO_EVENT_DEV: str = "0"
|
|
# 미니블로그 승인 알림을 메일에 더해 카톡으로도 보낸다. ★ 기본 꺼짐 — 오픈빌더에 이벤트
|
|
# 블록(스킬 연결)과 배포가 끝나기 전에 켜면 사장님 카톡에 빈 말풍선이 간다.
|
|
KAKAO_APPROVAL_PUSH_ENABLED: str = "0"
|
|
# 오픈빌더에 정의한 이벤트 이름. 말풍선을 그리는 블록이 이 이름에 걸려 있다.
|
|
KAKAO_APPROVAL_EVENT_NAME: str = "post_approval"
|
|
|
|
|
|
def get(name, default=""):
|
|
return getattr(AgentConfig(), name, default) or default
|
|
|
|
|
|
def chat_enabled() -> bool:
|
|
return get("AGENT_CHAT_ENABLED", "0") == "1"
|
|
|
|
|
|
def webhook_secret() -> str:
|
|
return get("KAKAO_WEBHOOK_SECRET")
|
|
|
|
|
|
def approval_push_enabled() -> bool:
|
|
return get("KAKAO_APPROVAL_PUSH_ENABLED", "0") == "1"
|
|
|
|
|
|
def kakao_link_enabled() -> bool:
|
|
return bool(get("KAKAO_CHANNEL_PUBLIC_ID"))
|
|
|
|
|
|
def channel_url() -> str:
|
|
"""사장님이 눌러서 채널로 가는 주소. 공개 ID 가 없으면 빈 문자열이다."""
|
|
public_id = get("KAKAO_CHANNEL_PUBLIC_ID")
|
|
return f"http://pf.kakao.com/{public_id}" if public_id else ""
|