카카오 webhook 응답을 위한 새 이미지 추가

This commit is contained in:
2025-06-30 15:00:03 +09:00
parent ed56635eb9
commit 1e3164a733
18 changed files with 194 additions and 70 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,7 +1,9 @@
# weather.py
import requests import requests
import json import json
import re import re
import sqlite3
import os
import time
from datetime import datetime from datetime import datetime
from config import serviceKey, TODAY from config import serviceKey, TODAY
@ -36,6 +38,7 @@ def get_precipitation_summary(retry=True):
try: try:
data = response.json() data = response.json()
total_rainfall = 0.0 total_rainfall = 0.0
time_precip_list = []
lines = [ lines = [
'<div class="weatherinfo" style="max-width: 100%; overflow-x: auto; padding: 10px; box-sizing: border-box;">', '<div class="weatherinfo" style="max-width: 100%; overflow-x: auto; padding: 10px; box-sizing: border-box;">',
@ -58,11 +61,17 @@ def get_precipitation_summary(retry=True):
time_str = f"{time[:2]}:{time[2:]}" time_str = f"{time[:2]}:{time[2:]}"
lines.append(f'<tr><td style="border: 1px solid #333; padding: 2px;text-align: center;">{time_str}</td><td style="border: 1px solid #333; padding: 2px;text-align: center;">{mm}mm</td></tr>') lines.append(f'<tr><td style="border: 1px solid #333; padding: 2px;text-align: center;">{time_str}</td><td style="border: 1px solid #333; padding: 2px;text-align: center;">{mm}mm</td></tr>')
total_rainfall += mm total_rainfall += mm
time_precip_list.append((time_str, mm))
lines.append(f'<tr><td colspan="2" style="border: 1px solid #333; padding: 2px;text-align: center; font-weight: bold;">영업시간 중 총 예상 강수량: {total_rainfall:.1f}mm</td></tr>') lines.append(f'<tr><td colspan="2" style="border: 1px solid #333; padding: 2px;text-align: center; font-weight: bold;">영업시간 중 총 예상 강수량: {total_rainfall:.1f}mm</td></tr>')
lines.append('</tbody></table><p style="text-align:right; font-size: 0.8em;">08:00 파주 조리읍 기상청 단기예보 기준</div>') lines.append('</tbody></table><p style="text-align:right; font-size: 0.8em;">08:00 파주 조리읍 기상청 단기예보 기준</div>')
return ''.join(lines) html_summary = ''.join(lines)
# SQLite 저장 함수 호출
save_weather_to_sqlite(date=TODAY, time_precip_list=time_precip_list, total_rainfall=total_rainfall)
return html_summary
except json.decoder.JSONDecodeError: except json.decoder.JSONDecodeError:
if retry: if retry:
@ -73,6 +82,45 @@ def get_precipitation_summary(retry=True):
print("응답이 JSON 형식이 아닙니다.") print("응답이 JSON 형식이 아닙니다.")
return '' return ''
def save_weather_to_sqlite(date, time_precip_list, total_rainfall):
db_path = '/data/weather.sqlite'
os.makedirs(os.path.dirname(db_path), exist_ok=True)
conn = sqlite3.connect(db_path)
curs = conn.cursor()
# 테이블 생성 (없으면)
curs.execute('''
CREATE TABLE IF NOT EXISTS precipitation (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT NOT NULL,
time TEXT NOT NULL,
rainfall REAL NOT NULL
)
''')
curs.execute('''
CREATE TABLE IF NOT EXISTS precipitation_summary (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT NOT NULL UNIQUE,
total_rainfall REAL NOT NULL
)
''')
# 기존 데이터 삭제 (동일 날짜)
curs.execute('DELETE FROM precipitation WHERE date = ?', (date,))
curs.execute('DELETE FROM precipitation_summary WHERE date = ?', (date,))
# 데이터 삽입
curs.executemany('INSERT INTO precipitation (date, time, rainfall) VALUES (?, ?, ?)',
[(date, t, r) for t, r in time_precip_list])
curs.execute('INSERT INTO precipitation_summary (date, total_rainfall) VALUES (?, ?)', (date, total_rainfall))
conn.commit()
conn.close()
print(f"[DB 저장 완료] {date} 강수량 데이터 저장됨")
# 테스트용 # 테스트용
if __name__ == "__main__": if __name__ == "__main__":
print(get_precipitation_summary()) print(get_precipitation_summary())

View File

@ -1,10 +1,26 @@
services: services:
fg-auto: gnu-autouploader:
build: build:
context: ./build context: ./build/autouploader
dockerfile: Dockerfile dockerfile: Dockerfile
image: reg.firstgarden.co.kr/fg-auto:latest image: reg.firstgarden.co.kr/gnu-autouploader:latest
container_name: fg-auto container_name: gnu-autouploader
volumes: volumes:
- ./data:/data - ./data:/data
- ./autouploader:/app
restart: unless-stopped
fg-webhook:
build:
context: ./build/webhook
dockerfile: Dockerfile
image: reg.firstgarden.co.kr/fg-webhook:latest
container_name: fg-webhook
volumes:
- ./data:/data
- ./webhook:/app
ports:
- 5000:5000
#environment:
# - DOMAIN=https://webhook.firstgarden.co.kr
restart: unless-stopped restart: unless-stopped

View File

@ -1,54 +0,0 @@
FROM python:3.10-slim
ENV LANG=ko_KR.UTF-8 \
LANGUAGE=ko_KR:ko \
LC_ALL=ko_KR.UTF-8 \
TZ=Asia/Seoul \
DEBIAN_FRONTEND=noninteractive
# 필수 패키지 설치
RUN apt-get update && \
apt-get install -y --no-install-recommends \
locales tzdata bash cron curl unzip wget gnupg ca-certificates \
xvfb x11-utils libglib2.0-0 libnss3 libgconf-2-4 libfontconfig1 \
libxss1 libxshmfence1 libasound2 libxtst6 libappindicator3-1 \
fonts-nanum libu2f-udev \
libjpeg-dev zlib1g-dev && \
sed -i '/ko_KR.UTF-8/s/^# //g' /etc/locale.gen && \
locale-gen && \
ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime && \
ln -sf /usr/bin/python3 /usr/bin/python && \
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# Chrome 설치
RUN curl -fsSL https://dl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o /usr/share/keyrings/google-linux-keyring.gpg && \
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-linux-keyring.gpg] http://dl.google.com/linux/chrome/deb/ stable main" \
> /etc/apt/sources.list.d/google-chrome.list && \
apt-get update && \
apt-get install -y google-chrome-stable && \
rm -rf /var/lib/apt/lists/*
# Python 패키지 설치
RUN pip install --no-cache-dir \
selenium>=4.10 \
pymysql \
ftputil \
pillow \
pyvirtualdisplay \
requests
# 작업 디렉토리 설정
WORKDIR /data
# 글꼴 캐시
RUN fc-cache -f -v
# 실행 스크립트 복사 및 실행 권한 부여
COPY run.sh /data/run.sh
RUN chmod +x /data/run.sh
# crontab 직접 등록
RUN echo "* * * * * /data/run.sh >> /proc/1/fd/1 2>&1" | crontab -
# 크론 실행
CMD ["bash", "-c", "cron -f"]

View File

@ -29,14 +29,14 @@ RUN pip install --no-cache-dir \
requests requests
WORKDIR /data WORKDIR /app
RUN fc-cache -f -v RUN fc-cache -f -v
COPY run.sh /data/run.sh COPY run.sh /app/run.sh
RUN chmod +x /data/run.sh RUN chmod +x /app/run.sh
# crontab 등록 # crontab 등록
RUN echo "0 9 * * * /data/run.sh >> /proc/1/fd/1 2>&1" | crontab - RUN echo "0 9 * * * /app/run.sh >> /proc/1/fd/1 2>&1" | crontab -
CMD ["/bin/bash", "-c", "cron -f"] CMD ["/bin/bash", "-c", "cron -f"]

30
build/webhook/Dockerfile Normal file
View File

@ -0,0 +1,30 @@
# Dockerfile for webhook server (Ubuntu 22.04 + Python Flask)
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive \
LANG=ko_KR.UTF-8 \
LANGUAGE=ko_KR:ko \
LC_ALL=ko_KR.UTF-8
# 기본 패키지 설치 및 로케일 설정
RUN apt-get update && \
apt-get install -y --no-install-recommends \
locales tzdata python3 python3-pip curl ca-certificates && \
locale-gen ko_KR.UTF-8 && \
ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime && \
dpkg-reconfigure --frontend noninteractive tzdata && \
ln -sf /usr/bin/python3 /usr/bin/python && \
apt-get clean && rm -rf /var/lib/apt/lists/*
# Flask 설치
RUN pip3 install --no-cache-dir flask requests
# 작업 디렉토리
WORKDIR /app
# 외부 접속 허용 포트
EXPOSE 5000
# Flask 앱 실행
CMD ["python3", "webhook.py"]

BIN
data/weather.sqlite Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

View File

@ -1,7 +1,16 @@
services: services:
fg-auto: gnu-autouploader:
image: reg.firstgarden.co.kr/fg-auto:latest image: reg.firstgarden.co.kr/gnu-autouploader:latest
container_name: fg-auto container_name: gnu-autouploader
volumes: volumes:
- ./data:/data - ./data:/data
- ./autouploader:/app
restart: unless-stopped
fg-webhook:
image: reg.firstgarden.co.kr/fg-webhook:latest
container_name: fg-webhook
volumes:
- ./data:/data
- ./webhook:/app
restart: unless-stopped restart: unless-stopped

View File

@ -2,9 +2,9 @@ services:
fg-auto: fg-auto:
build: build:
context: ./build context: ./build
dockerfile: Dockerfile-slim dockerfile: Dockerfile
image: reg.firstgarden.co.kr/fg-auto:slim image: reg.firstgarden.co.kr/fg-webhook:latest
container_name: fg-auto container_name: fg-webhook
volumes: volumes:
- ./data:/data - ./data:/data
restart: unless-stopped restart: unless-stopped

75
webhook/webhook.py Normal file
View File

@ -0,0 +1,75 @@
import os
from flask import Flask, request, jsonify
import sqlite3
from datetime import datetime
app = Flask(__name__)
DB_PATH = '/data/weather.sqlite'
# 환경변수에서 DOMAIN 읽기, 없으면 기본값 지정 (로컬 테스트용)
DOMAIN = os.getenv('DOMAIN', 'http://localhost:5000')
def get_rain_data(date):
conn = sqlite3.connect(DB_PATH)
curs = conn.cursor()
curs.execute('SELECT time, rainfall FROM precipitation WHERE date = ? ORDER BY time', (date,))
time_rain_list = curs.fetchall()
curs.execute('SELECT total_rainfall FROM precipitation_summary WHERE date = ?', (date,))
row = curs.fetchone()
total_rainfall = row[0] if row else 0.0
conn.close()
return time_rain_list, total_rainfall
@app.route('/webhook', methods=['POST'])
def webhook():
today = datetime.today().strftime('%Y%m%d')
response_text = ""
try:
time_rain_list, total_rainfall = get_rain_data(today)
if not time_rain_list:
response_text = f"{today} 날짜의 강수량 데이터가 없습니다."
else:
lines = []
for time_str, rain in time_rain_list:
rain_display = f"{rain}mm" if rain > 0 else "강수 없음"
lines.append(f"{time_str}{rain_display}")
lines.append(f"\n영업시간 내 총 강수량은 {total_rainfall:.1f}mm 입니다.")
response_text = '\n'.join(lines)
except Exception as e:
response_text = f"데이터 조회 중 오류가 발생했습니다: {e}"
image_filename = f"weather_capture_{today}.png"
image_path = f"/data/{image_filename}"
outputs = [{
"simpleText": {
"text": response_text
}
}]
if os.path.isfile(image_path):
image_url = f"{DOMAIN}/data/{image_filename}"
outputs.append({
"image": {
"imageUrl": image_url,
"altText": "오늘의 날씨 캡처 이미지"
}
})
return jsonify({
"version": "2.0",
"template": {
"outputs": outputs
}
})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)