diff --git a/autouploader/__pycache__/config.cpython-310.pyc b/autouploader/__pycache__/config.cpython-310.pyc new file mode 100644 index 0000000..bab9e2e Binary files /dev/null and b/autouploader/__pycache__/config.cpython-310.pyc differ diff --git a/autouploader/__pycache__/config.cpython-311.pyc b/autouploader/__pycache__/config.cpython-311.pyc new file mode 100644 index 0000000..6063f49 Binary files /dev/null and b/autouploader/__pycache__/config.cpython-311.pyc differ diff --git a/autouploader/__pycache__/weather.cpython-310.pyc b/autouploader/__pycache__/weather.cpython-310.pyc new file mode 100644 index 0000000..babf456 Binary files /dev/null and b/autouploader/__pycache__/weather.cpython-310.pyc differ diff --git a/app/config.sample.py b/autouploader/config.sample.py similarity index 100% rename from app/config.sample.py rename to autouploader/config.sample.py diff --git a/app/gnu_autoupload.py b/autouploader/gnu_autoupload.py similarity index 100% rename from app/gnu_autoupload.py rename to autouploader/gnu_autoupload.py diff --git a/app/run.sh b/autouploader/run.sh similarity index 100% rename from app/run.sh rename to autouploader/run.sh diff --git a/app/weather.py b/autouploader/weather.py similarity index 63% rename from app/weather.py rename to autouploader/weather.py index f018dac..20c2b15 100644 --- a/app/weather.py +++ b/autouploader/weather.py @@ -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 = [ '
', @@ -58,11 +61,17 @@ def get_precipitation_summary(retry=True): time_str = f"{time[:2]}:{time[2:]}" lines.append(f'{time_str}{mm}mm') total_rainfall += mm + time_precip_list.append((time_str, mm)) lines.append(f'영업시간 중 총 예상 강수량: {total_rainfall:.1f}mm') lines.append('

08:00 파주 조리읍 기상청 단기예보 기준

') - 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()) diff --git a/app/weather_capture.py b/autouploader/weather_capture.py similarity index 100% rename from app/weather_capture.py rename to autouploader/weather_capture.py diff --git a/build.yml b/build.yml index d7beb10..8a055bf 100644 --- a/build.yml +++ b/build.yml @@ -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 diff --git a/build/Dockerfile-slim b/build/Dockerfile-slim deleted file mode 100644 index 4465851..0000000 --- a/build/Dockerfile-slim +++ /dev/null @@ -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"] diff --git a/build/Dockerfile b/build/autouploader/Dockerfile similarity index 89% rename from build/Dockerfile rename to build/autouploader/Dockerfile index e709ca2..685c363 100644 --- a/build/Dockerfile +++ b/build/autouploader/Dockerfile @@ -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"] diff --git a/build/run.sh b/build/autouploader/run.sh similarity index 100% rename from build/run.sh rename to build/autouploader/run.sh diff --git a/build/webhook/Dockerfile b/build/webhook/Dockerfile new file mode 100644 index 0000000..5a43488 --- /dev/null +++ b/build/webhook/Dockerfile @@ -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"] diff --git a/data/weather.sqlite b/data/weather.sqlite new file mode 100644 index 0000000..b189424 Binary files /dev/null and b/data/weather.sqlite differ diff --git a/data/weather_capture_20250630.png b/data/weather_capture_20250630.png new file mode 100644 index 0000000..d342362 Binary files /dev/null and b/data/weather_capture_20250630.png differ diff --git a/docker-compose.yml b/docker-compose.yml index 802437d..6eca8ee 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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 diff --git a/build-slim.yml b/webhook-build.yml similarity index 51% rename from build-slim.yml rename to webhook-build.yml index 79f3018..62da351 100644 --- a/build-slim.yml +++ b/webhook-build.yml @@ -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 diff --git a/webhook/webhook.py b/webhook/webhook.py new file mode 100644 index 0000000..5eb22cb --- /dev/null +++ b/webhook/webhook.py @@ -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)