카카오 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 json
import re
import sqlite3
import os
import time
from datetime import datetime
from config import serviceKey, TODAY
@ -36,6 +38,7 @@ def get_precipitation_summary(retry=True):
try:
data = response.json()
total_rainfall = 0.0
time_precip_list = []
lines = [
'<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:]}"
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
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('</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:
if retry:
@ -73,6 +82,45 @@ def get_precipitation_summary(retry=True):
print("응답이 JSON 형식이 아닙니다.")
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__":
print(get_precipitation_summary())

View File

@ -1,10 +1,26 @@
services:
fg-auto:
gnu-autouploader:
build:
context: ./build
context: ./build/autouploader
dockerfile: Dockerfile
image: reg.firstgarden.co.kr/fg-auto:latest
container_name: fg-auto
image: reg.firstgarden.co.kr/gnu-autouploader:latest
container_name: gnu-autouploader
volumes:
- ./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

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
WORKDIR /data
WORKDIR /app
RUN fc-cache -f -v
COPY run.sh /data/run.sh
RUN chmod +x /data/run.sh
COPY run.sh /app/run.sh
RUN chmod +x /app/run.sh
# 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"]

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:
fg-auto:
image: reg.firstgarden.co.kr/fg-auto:latest
container_name: fg-auto
gnu-autouploader:
image: reg.firstgarden.co.kr/gnu-autouploader:latest
container_name: gnu-autouploader
volumes:
- ./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

View File

@ -2,9 +2,9 @@ services:
fg-auto:
build:
context: ./build
dockerfile: Dockerfile-slim
image: reg.firstgarden.co.kr/fg-auto:slim
container_name: fg-auto
dockerfile: Dockerfile
image: reg.firstgarden.co.kr/fg-webhook:latest
container_name: fg-webhook
volumes:
- ./data:/data
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)