byminseok.com

Daily Morning Weather KakaoTalk via Claude

Translated from Korean

Receive Daily Morning Weather Updates via KakaoTalk with Claude - Building Pangyo Weather Automation

> This document records the process of setting up an AI weather briefing delivered via KakaoTalk every morning at 6 AM using the Korea Meteorological Administration API + Claude API + KakaoTalk + GitHub Actions.

Final Result

Every morning at 6 AM, weather bot Nana🐱 sends this message via KakaoTalk:

🐱 오늘(2/12 목) 날씨 브리핑 ☀️

💻 분당
▸ 최저 2°C / 최고 8°C
▸ 하루 종일 맑은 하늘
▸ 저녁부터 기온 뚝, 습도는 85%까지 올라가요

🌲 서천
▸ 최저 0°C / 최고 7°C
▸ 종일 화창한 날씨
▸ 밤 10시부터 영하, 습도 95%로 쌀쌀해요

🌡️ 두 동네 온도차
▸ 분당이 서천보다 2도 높지만, 밤엔 둘 다 매서워요

👗 나나의 옷장
▸ 코트에 니트 조합이면 딱!

🐱 나나의 한마디
▸ 퇴근길엔 목도리 꼭 챙기세요, 체감온도 더 낮아요!

This AI weather assistant simultaneously checks the weather for two regions—Bundang and Seocheon—and even recommends outfits based on temperature.


Overall Architecture

[매일 아침 6시 KST]
       │
       ▼
  GitHub Actions (cron: 0 21 * * * UTC)
       │
       ▼
  pangyo_weather_kakao.py 실행
       │
       ├─① 기상청 단기예보 API 호출 × 2 (data.go.kr)
       │     → 분당(nx=62, ny=123) + 서천(nx=55, ny=94) 날씨 데이터
       │
       ├─② 기온별 옷차림 매칭 (CLOTHING_MAP)
       │     → 낮 최고기온 기준 옷차림 후보 선정
       │
       ├─③ Claude Sonnet API 호출 (Anthropic)
       │     → 두 지역 날씨 + 옷차림을 자연스러운 브리핑으로 변환
       │
       └─④ 카카오톡 '나에게 보내기' API 호출
             → 나나🐱의 브리핑 메시지를 카톡으로 전송

Step 1. Setting Up the KakaoTalk 'Send to Me' Skill (Claude Web)

I started on Claude Web (claude.ai).

1-1. Installing the KakaoTalk MCP Skill

Claude Web has a Skill feature that integrates with external services. I downloaded the KakaoTalk 'Send to Me' skill Robin had created. Download link Agit

This skill is an MCP (Model Context Protocol) skill that enables the Send to Me feature via the KakaoTalk REST API.

After installation, Kakao Developer App setup is required. Detailed steps are as follows:

Detailed Kakao App Setup Guide

Step 1: Create an App

  1. Log in to Kakao Developer with your Kakao account
  2. Go to "My Applications" → "Add Application"
  3. App Name: Anything (e.g., "Weather Bot")
  4. After creation, copy the REST API Key from the App Key tab → This is KAKAO_REST_API_KEY

Step 2: Enable Kakao Login

  1. Select the app → "Kakao Login" menu
  2. Enable setting: ON
  3. Register Redirect URI: https://example.com/oauth (Any URL is OK, just for receiving the code)

Step 3: Set Consent Items

  1. "Kakao Login" → "Consent Items"
  2. Send KakaoTalk Messages (talk_message) → Set to Optional Consent

Step 4: Issue Client Secret (Optional but Recommended)

  1. "Security" menu → Issue Client Secret
  2. Change status to "Enabled"
  3. Copy the issued value → This is KAKAO_CLIENT_SECRET

Step 5: Issue Token via OAuth Authentication

Access the URL below in your browser (replace REST_API_KEY with your own key):

https://kauth.kakao.com/oauth/authorize?client_id={REST_API_KEY}&redirect_uri=https://example.com/oauth&response_type=code&scope=talk_message

Kakao Login → Agree → Copy the code=XXXXX portion from the redirected URL.

Then, in the terminal:

curl -X POST "https://kauth.kakao.com/oauth/token" \
  -d "grant_type=authorization_code" \
  -d "client_id={REST_API_KEY}" \
  -d "redirect_uri=https://example.com/oauth" \
  -d "code={위에서 복사한 CODE}"

Copy the access_token and refresh_token from the response. These become KAKAO_ACCESS_TOKEN and KAKAO_REFRESH_TOKEN respectively.

> Note: The access_token expires after 6 hours, while the refresh_token expires after 2 months. The script has auto-renewal logic, so only the refresh_token needs to remain active.

1-2. Requesting Weather KakaoTalk Messages from Claude

After setting up the skill, I made this request on Claude Web:

> "Send me tomorrow's Pangyo weather via KakaoTalk message"

I confirmed Claude called the Korea Meteorological Administration API and sent the message through the KakaoTalk skill. Seeing it work well, I wanted to run this automatically every day.

1-3. Requesting Python Script Generation

> "Write a Python script that sends Pangyo weather via KakaoTalk message"

Claude's web interface generated pangyo_weather_kakao.py. This script:

  • Queries Pangyo weather via the Korea Meteorological Administration's short-term forecast API
  • Parses hourly temperature, sky condition, and precipitation data
  • Sends the message via KakaoTalk

I saved this file locally and continued working with Claude Code from here.


Step 2. Building GitHub Actions Automation (Claude Code)

I requested Claude Code:

> "Modify pangyo_weather_kakao.py to run daily at 6 AM and create a GitHub Action script for it"

2-1. Script Modification - Environment Variable Support

The original script had the API key hardcoded in the code. To run it in GitHub Actions, it must be managed via GitHub Secrets, so I modified it to use environment variables first.

# 변경 전
CONFIG = {
    "KMA_API_KEY": "YOUR_API_KEY_HERE",
    "KAKAO_SCRIPT": "/mnt/skills/user/kakaotalk/scripts/send_message.py",
}

# 변경 후
CONFIG = {
    "KMA_API_KEY": os.environ.get("KMA_API_KEY", "YOUR_API_KEY_HERE"),
    "KAKAO_ACCESS_TOKEN": os.environ.get("KAKAO_ACCESS_TOKEN", ""),
    "KAKAO_REFRESH_TOKEN": os.environ.get("KAKAO_REFRESH_TOKEN", ""),
    "KAKAO_REST_API_KEY": os.environ.get("KAKAO_REST_API_KEY", ""),
    "KAKAO_CLIENT_SECRET": os.environ.get("KAKAO_CLIENT_SECRET", ""),
    # ...
}

2-2. Added Direct Call to KakaoTalk REST API

The original script called the local send_message.py script via subprocess, but that script doesn't exist in GitHub Actions. I added code to directly call the KakaoTalk REST API.

def send_kakao_api(message, prefix, access_token):
    """카카오톡 REST API로 '나에게 보내기' 메시지를 전송"""
    url = "https://kapi.kakao.com/v2/api/talk/memo/default/send"
    headers = {"Authorization": f"Bearer {access_token}"}

    template = {
        "object_type": "text",
        "text": f"{prefix} {message}",
        "link": {"web_url": "https://weather.naver.com"},
    }
    data = {"template_object": json.dumps(template)}
    resp = requests.post(url, headers=headers, data=data)
    return resp.status_code == 200

I also designed the transmission priority:

  1. Direct Kakao REST API call (for GitHub Actions)
  2. If failed → Automatically renew token and retry
  3. If still failed → Fallback to send_message.py script (for local use)

2-3. Creating the GitHub Actions Workflow

.github/workflows/pangyo-weather.yml:

name: 판교 날씨 카카오톡 전송

on:
  schedule:
    # 매일 아침 6시 (KST) = UTC 21:00 (전날)
    - cron: '0 21 * * *'
  workflow_dispatch: # 수동 실행 지원

jobs:
  send-weather:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
          cache: 'pip'

      - run: pip install -r requirements.txt

      - name: 🌤️ 날씨 조회 및 카카오톡 전송
        env:
          ANTHROPIC_API_KEY: $
          KMA_API_KEY: $
          KAKAO_ACCESS_TOKEN: $
          KAKAO_REFRESH_TOKEN: $
          KAKAO_REST_API_KEY: $
          KAKAO_CLIENT_SECRET: $
          TZ: Asia/Seoul
        run: python pangyo_weather_kakao.py

Key points:

  • cron: '0 21 * * *' — GitHub Actions cron uses UTC, so KST 6 AM = UTC 9 PM the previous day
  • TZ: Asia/Seoul — Ensures Python's datetime.now() returns Korean time
  • workflow_dispatch — Allows manual execution via the Actions tab

Step 3. Create GitHub Repo and Register Secrets

3-1. Prepare API Keys

Required keys:

Key Issuer Purpose
KMA_API_KEY data.go.kr → Korea Meteorological Administration_Short-term Forecast Service Weather data
KAKAO_REST_API_KEY developers.kakao.com → App Key Kakao Authentication
KAKAO_CLIENT_SECRET Kakao Developer → Security → Client Secret Token Refresh
KAKAO_ACCESS_TOKEN Issued after OAuth authentication KakaoTalk transmission
KAKAO_REFRESH_TOKEN Issued after OAuth authentication Automatic token renewal
ANTHROPIC_API_KEY console.anthropic.com AI Briefing

Korea Meteorological Administration API Key Issuance Details

  1. Register as a member on the Public Data Portal
  2. Search for "Korea Meteorological Administration_Short-Term Forecast Inquiry Service" in the search bar
  3. Click Application for Use → Enter any purpose → Submit application
  4. Approval usually within immediately ~ 1 hour (automatic approval)
  5. My Page → Authentication Key Issuance Status → Copy General Authentication Key (Decoding)

> ⚠️ You must use the Decoding key, not the Encoding key!

Anthropic API Key Issuance Details

  1. Sign up at console.anthropic.com
  2. Settings → API Keys → "Create Key"
  3. Copy the key (format: sk-ant-…)
  4. Settings → Billing → Top up $5 (Valid for years for this purpose)

> Note: Claude Max plans ($100/$200/month) are for claude.ai web only; API is billed separately.

Finding Your Neighborhood Grid Coordinates (nx, ny)

The Korea Meteorological Administration API uses grid coordinates, not latitude/longitude.

How to find them:

  1. Download the KMA grid coordinates Excel and search for your neighborhood
  2. Or use the latlon_to_grid() function in the script:
# 예: 강남역 좌표 확인
python3 -c "
from pangyo_weather_kakao import latlon_to_grid
print(latlon_to_grid(37.4979, 127.0276))  # 강남역
"
# 출력: (61, 126)

Major area grid coordinates:

Region nx ny
Seoul (Jongno) 60 127
Bundang/Pangyo 62 123
Gangnam Station 61 126
Seocheon 55 94
Jeju 52 38
Busan 98 76

Simply add/edit desired regions in the LOCATIONS list.

3-2. GitHub CLI (gh) Installation and Repo Creation

gh CLI Installation

# macOS
brew install gh

# Ubuntu/Linux
sudo apt install gh

# Windows
winget install --id GitHub.cli

GitHub Authentication + Repo Creation

# GitHub 로그인 (처음 한 번만)
gh auth login

# workflow 권한 추가 (Actions 파일 push에 필요)
gh auth refresh -h github.com -s workflow

# 레포 생성 (private)
gh repo create my-weather-bot --private --source=. --push

Claude Code handled it all at once with the gh CLI:

# 레포 생성 (private)
gh repo create pangyo-action --private --source=. --push

# Secrets 등록
gh secret set KMA_API_KEY -b "xxx..."
gh secret set KAKAO_REST_API_KEY -b "xxx..."
gh secret set KAKAO_CLIENT_SECRET -b "xxx..."
gh secret set KAKAO_ACCESS_TOKEN -b "xxx..."
gh secret set KAKAO_REFRESH_TOKEN -b "xxx..."
gh secret set ANTHROPIC_API_KEY -b "xxx..."

> Security Point: The code contains no API keys at all; it only references them via os.environ.get(). GitHub Secrets are stored encrypted and masked as *** in logs.

3-3. First Test — Token Renewal Failure

⚠️  access_token 만료 - 갱신 시도 중...
❌ 토큰 갱신 실패: 401 Client Error

Attempted renewal with the refresh_token while the access_token was expired, resulting in a 401 error.

Cause: If a Client Secret is set in the Kakao app, the client_secret parameter must also be included during token renewal.

# 수정: client_secret 파라미터 추가
def refresh_kakao_token(refresh_token, rest_api_key, client_secret=""):
    data = {
        "grant_type": "refresh_token",
        "client_id": rest_api_key,
        "refresh_token": refresh_token,
    }
    if client_secret:
        data["client_secret"] = client_secret  # 이거 빠져서 401이었음

Added KAKAO_CLIENT_SECRET to Secrets and rerun → Success! The first KakaoTalk message arrived.


Step 4. Change to Today's Weather

The initial script defaulted to tomorrow's weather. However, since it runs daily at 6 AM, today's weather is correct.

# 변경 전: 내일이 기본
if args.today:
    target = now
else:
    target = now + timedelta(days=1)  # 기본값

# 변경 후: 오늘이 기본
if args.tomorrow:
    target = now + timedelta(days=1)
else:
    target = now  # 기본값

Step 5. Generate Natural Briefings with Claude API

5-1. Problem: Rigid Data Listing

The initial message looked like this:

🐰🔔 판교 날씨 (2/13 금) 맑음 ☀️

🌡️ 1°C / 11°C
🌤️ 맑음 ☀️
☂️ 강수 예상 없음

🕐 6시 1° → 9시 3° → 12시 8° → 15시 10° → 18시 8° → 21시 6°

👉 좋은 하루 보내세요!

Not bad, but I wanted this format:

🐰🔔 오늘(2/12 목) 판교 날씨 ☀️

▸ 하루종일 화창한 맑은 날씨가 이어져요.
▸ 기온은 2~9°C로 선선하며, 오후 3시경 8°C까지 올라갑니다.
▸ 바람은 약하고 강수 확률 0%로 쾌청한 하루예요.
▸ 저녁부터 습도가 올라가며 체감온도가 낮아질 수 있어요.

👉 햇살은 좋지만 쌀쌀하니 가벼운 겉옷 챙기세요!

Natural descriptions like "Cloudy, clearing up in the afternoon" have limitations with code logic alone. We decided to leverage the Claude API once more to convert weather data into a natural language briefing.

5-2. Implementation: Weather Data → Claude → Briefing

We organize the Korea Meteorological Administration API response by time slot and pass it to Claude:

def build_message_claude(forecast, location, label):
    # 시간대별 데이터를 텍스트로 구성
    weather_data = f"""날짜: 2/12 (목)
지역: 판교
최저기온: 2°C / 최고기온: 9°C
대표 하늘: 맑음 ☀️

시간대별:
6시: 2°C, 맑음, 강수확률=0%, 풍속=1.4m/s
9시: 3°C, 맑음, 강수확률=0%, 풍속=1.1m/s
12시: 5°C, 맑음, 강수확률=0%, 풍속=2.0m/s
..."""

    prompt = f"""아래 기상청 데이터를 바탕으로 카카오톡 날씨 브리핑 메시지를 작성해줘.

{weather_data}

규칙:
- 첫 줄: "오늘(2/12 목) 판교 날씨 [이모지]"
- ▸ 로 시작하는 3~5줄의 자연스러운 한국어 브리핑
- 하늘 변화 흐름을 자연스럽게 서술
- 마지막에 👉 한줄 팁 (친근한 톤)
- 메시지 본문만 출력"""

    client = anthropic.Anthropic(api_key=api_key)
    response = client.messages.create(
        model="claude-sonnet-4-5-20250929",
        max_tokens=500,
        messages=[{"role": "user", "content": prompt}],
    )
    return response.content[0].text.strip()

We also created a fallback structure:

  • Claude API succeeds → Use AI briefing
  • Claude API fails → Fall back to default template message (KakaoTalk somehow manages)

5-3. Model Selection: Haiku vs Sonnet

We initially started with the most affordable Haiku, but KakaoTalk responded like this:

👉 퇴근 시간쯤엔 쌌쌌하니까 겨울옷 필수예요!

There was a spelling mistake where "쌀쌀" (chilly) was written as "쌌쌌" 😂

Comparing costs:

Model Cost per Call Monthly Cost (1 call/day)
Haiku 4.5 ~$0.002 ~$0.06
Sonnet 4.5 ~$0.006 ~$0.18

At $0.18/month, the difference wasn't huge, so I upgraded to Sonnet 4.5. Korean quality improved significantly.

> Note: The Claude Max plan ($100/$200/month) is exclusively for the claude.ai web/app; API usage is billed separately. You must top up credits at console.anthropic.com.


Step 6. Simultaneous Briefing for Two Regions + Weather Bot Nana 🐱

Reading blogs sparked an idea. I work in Bundang on weekdays and often visit Seocheon on weekends — what if I could check both locations' weather at once?

6-1. Design

I requested this from Claude Code:

> "Brief me on both Bundang and Seocheon weather, and tell me the temperature difference."

We designed the message structure together:

  • 💻 Bundang — Where I commute to work
  • 🌲 Seocheon — Where I go on weekends
  • 🌡️ Temperature difference between the two areas — Temperature comparison
  • 🐱 Nana's Quick Tip — Giving the weather bot a name and personality!
    • Mon-Thu: Commute-based tips for Bundang
    • Fri-Sun: Weekend tips in a "If you're heading to Seocheon!" tone
# 조회 지역 목록
LOCATIONS = [
    {"name": "분당", "emoji": "💻", "nx": 62, "ny": 123},
    {"name": "서천", "emoji": "🌲", "nx": 55, "ny": 94},
]

6-2. Implementation

Changed the existing structure, which queried a single location, to call twice by iterating through the LOCATIONS list.

forecasts = []
for loc in LOCATIONS:
    items = fetch_kma_forecast(target_date, loc["nx"], loc["ny"], api_key)
    forecast = parse_forecast(items, target_date)
    forecasts.append((loc, forecast))

message = build_message(forecasts, label)

In the Claude prompt, I specified different tones for Nana's comment based on the day of the week:

if is_weekend:  # 금, 토, 일
    tip_rule = '🐱 나나의 한마디: "서천에 간다면!" 톤으로 한줄 팁'
else:  # 월~목
    tip_rule = '🐱 나나의 한마디: 분당 출퇴근 기반 한줄 팁'

Step 7. Nana's Wardrobe — Temperature-Based Outfit Recommendations 👗

7-1. Origin Story

When I shared this weather bot with my team, I got this reaction:

> Team Member: "Oh, can I request this on KakaoTalk too? (No way lol) It'd be great if it also told me what thickness of clothes to wear in the morning."

Hearing that reminded me of my college days.

> Me: "Oh, that reminds me of a service I made for a team project in my HCI class back in college lol. The app was called 'Wear This'."

Then Joy said:

> Joy: "Make a tailored version of 'Wear This' for me!"

In 2018, it took 3 months to plan and build that service, but this time I made it in 10 minutes.

7-2. Building the Data

I fed Claude Code 3 images of outfits for each temperature range, removed duplicates, and organized them into 8 temperature tiers.

CLOTHING_MAP = [
    (28, None, "민소매, 반팔, 반바지, 숏팬츠, 린넨 의류, 원피스"),
    (23, 27,   "반팔, 티셔츠, 얇은 셔츠, 반바지, 면바지"),
    (20, 22,   "블라우스, 긴팔 티, 셔츠, 얇은 가디건, 면바지, 청바지, 7부바지"),
    (17, 19,   "얇은 니트, 맨투맨, 가디건, 후드티, 바람막이, 긴바지, 청바지, 슬랙스"),
    (12, 16,   "자켓, 가디건, 청자켓, 야상, 기모후드티, 니트, 맨투맨, 스타킹, 청바지"),
    (9, 11,    "트렌치코트, 야상, 자켓, 점퍼, 니트, 청바지, 스타킹, 기모바지"),
    (5, 8,     "코트, 울 코트, 가죽자켓, 히트텍, 니트, 후리스, 기모 옷, 레깅스"),
    (None, 4,  "패딩, 두꺼운 코트, 누빔 옷, 기모제품, 히트텍, 목도리, 장갑"),
]

7-3. Claude's Tasteful Selection

Listing all outfit candidates would be tedious, so we had Claude Sonnet tastefully select just 1-2 options to recommend naturally.

The prompt instructed:

오늘 낮 최고기온(8°C) 기준 옷차림 후보: 코트, 울 코트, 가죽자켓, 히트텍, 니트, 후리스, ...

규칙:
- "👗 나나의 옷장" 섹션: 위 후보에서 센스있게 1~2개만 골라 자연스럽게 추천
  예: "코트에 니트 조합이면 딱!" 또는 "후드티 하나면 충분해요~"

Result:

👗 나나의 옷장
▸ 코트에 니트 조합이면 딱!

Troubleshooting Summary

1. GitHub Actions refuses to push workflow file

! [remote rejected] refusing to allow an OAuth App to create or update workflow

Resolved: Added workflow scope with gh auth refresh -h github.com -s workflow, then pushed.

2. Kakao token renewal 401 error

❌ 토큰 갱신 실패: 401 Client Error

Resolved: If a Client Secret is set in the Kakao app, the client_secret parameter is required for renewal requests. Added KAKAO_CLIENT_SECRET to GitHub Secrets.

3. Insufficient Anthropic API Credits

Your credit balance is too low to access the Anthropic API

Solution: Top up $5 at console.anthropic.com. This amount will last for years for this purpose.


Final Project Structure

pangyo-action/
├── .github/
│   └── workflows/
│       └── pangyo-weather.yml    # GitHub Actions (매일 6시 KST)
├── pangyo_weather_kakao.py       # 메인 스크립트
└── requirements.txt              # requests, anthropic

GitHub Secrets (Total 6)

Secret Purpose
KMA_API_KEY Korea Meteorological Administration short-term forecast API
KAKAO_REST_API_KEY Kakao App REST API key
KAKAO_CLIENT_SECRET Kakao App Client Secret
KAKAO_ACCESS_TOKEN Token for sending messages via KakaoTalk
KAKAO_REFRESH_TOKEN For token auto-renewal
ANTHROPIC_API_KEY Claude API (Briefing generation)

Operational Costs

Item Monthly Cost
GitHub Actions Free (2,000 minutes/month free for both public/private)
Korea Meteorological Administration API × 2 (Bundang+Seocheon) Free (Public data)
KakaoTalk API Free
Claude Sonnet API ~$0.20 (Once/day, reflecting token increase)
Total ~$0.20/month

Future Improvement Ideas

  • Add fine dust information: Integrate AirKorea API
  • Weekly weather summary: Sunday evening briefing on the week's weather
  • Auto-renew Kakao refresh_token: Automatically update GitHub Secrets 1 month before expiration (currently manual)
  • Team Member Subscription Feature: Allow anyone who wants to receive KakaoTalk messages from Nana

Conclusion

Starting from Claude Web and automating with Claude Code, the base version took less than an hour. Adding region comparisons, outfit recommendations, and a weather bot character took just 30 minutes.

Particularly impressive points:

  • Claude Code handles repository creation, secrets registration, and workflow execution all at once via the gh CLI
  • Immediately identified token renewal errors from logs → fixed → redeployed
  • Leveraged the Claude API again to list data → enhanced quality with natural language briefings
  • Automatically extracts and codes data when shown over 3 temperature-specific outfit images
  • The "Wear This" service, which took 3 months to build as a 2018 HCI class team project, was created in just 10 minutes in 2026

A system where AI weather assistant Nana🐱 sends a KakaoTalk message every morning was completed for less than 300 won per month.