commit bf706e58d8aae7d2a4e2c8e905513d2c5e197b35 Author: imnyang Date: Wed Nov 6 17:14:20 2024 +0900 Init diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..7e36e26 --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +ROOT="/root/today.isangjeong/" +INSTAGRAM_ID="today.isangjeong" +INSTAGRAM_PASSWORD="passwd" +KEY="key" +WEBHOOK_URL="webhook_url" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..957ce53 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +20*.png + +# Auth +cookies.json + +# Temp Files +temp/ + +.env + +venv +.venv \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..36b40f8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +FROM python:3.12 + +RUN sed -i 's@deb.debian.org@ftp.kaist.ac.kr@g' /etc/apt/sources.list.d/debian.sources + +# Setup Crontab + +RUN apt update && apt -y install cron + +COPY crontab /etc/cron.d/crontab +RUN chmod 0644 /etc/cron.d/crontab +RUN /usr/bin/crontab /etc/cron.d/crontab + +# Setup Environment Variables + +RUN export $(cat .env | xargs) + +# Setup Python Environment +WORKDIR /app + +RUN pip config set global.break-system-packages true +RUN pip install -r requirements.txt + +RUN rm -rf /etc/localtime +RUN ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime + +CMD ["cron", "-f"] \ No newline at end of file diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..0edf8ec --- /dev/null +++ b/README.MD @@ -0,0 +1,13 @@ +# Today.isangjeong + +오늘 급식도 인스타로 + + +실행하기 전에 `.env`가 필요합니다. + +`.env.example`을 참고해서 만들어주세요. + + +**이 프로젝트는 백업을 위한 레포로 친절하게 문서를 쓸 생각이 없습니다.** + +`docker build --tag today-isangjeong:latest` \ No newline at end of file diff --git a/app/library/Pretendard-Bold.ttf b/app/library/Pretendard-Bold.ttf new file mode 100644 index 0000000..fb07fc6 Binary files /dev/null and b/app/library/Pretendard-Bold.ttf differ diff --git a/app/library/init-auth.py b/app/library/init-auth.py new file mode 100644 index 0000000..52968c3 --- /dev/null +++ b/app/library/init-auth.py @@ -0,0 +1,17 @@ +import library.vts as vts +vts.download(vts.get_board()) + +from instagrapi import Client +import json, os +import time + +cl = Client() +cl.login(os.getenv("INSTAGRAM_ID"), os.getenv("INSTAGRAM_PASSWORD")) + +json.dump( + cl.get_settings(), + open(f'{os.getenv("ROOT")}temp/cookies.json', 'w') +) + +print("🍪 | Account information was successfully retrieved.") +print(f"📅 | Job ended at {time.time()}") \ No newline at end of file diff --git a/app/library/lib.py b/app/library/lib.py new file mode 100644 index 0000000..ca12b46 --- /dev/null +++ b/app/library/lib.py @@ -0,0 +1,82 @@ +import re + +def 영양정보_삭제(값:str): + 줄들 = 값.strip().split('\n') + 청소된_줄 = [re.sub(r'\(.*?\)', '', 줄).strip() for 줄 in 줄들] + 결과 = '\n'.join(청소된_줄) + + return 결과 + +import requests +from PIL import Image +from PIL import ImageDraw +from PIL import ImageFont + +import library.vts as vts +import os + +KEY = os.getenv("KEY") + +# DDISH_NM +def 급식_정보_얻기(MLSV_YMD:str): + 답장 = requests.get( + f"https://open.neis.go.kr/hub/mealServiceDietInfo?Type=json&ATPT_OFCDC_SC_CODE=E10&SD_SCHUL_CODE=7331071&MLSV_YMD={MLSV_YMD}&KEY={KEY}" + ) + DDISH_NM = 답장.json()['mealServiceDietInfo'][1]['row'][0]['DDISH_NM'] + + return 영양정보_삭제(DDISH_NM.replace("
", "\n")) + +def 급식_칼로리_얻기(MLSV_YMD:str): + 답장 = requests.get( + f"https://open.neis.go.kr/hub/mealServiceDietInfo?Type=json&ATPT_OFCDC_SC_CODE=E10&SD_SCHUL_CODE=7331071&MLSV_YMD={MLSV_YMD}&KEY={KEY}" + ) + return 답장.json()['mealServiceDietInfo'][1]['row'][0]['CAL_INFO'] + +font = f"{os.getenv("ROOT")}library/Pretendard-Bold.ttf" + +def 얻기(MLSV_YMD:str): + 급식 = 급식_정보_얻기(MLSV_YMD) + + 사진 = Image.open(f'{os.getenv("ROOT")}library/skeleton.png') + if vts.get_vts_true_or_false() == True: 사진 = Image.open(f'{os.getenv("ROOT")}library/skeleton-vts.png') + + 급식_폰트 = ImageFont.truetype(font, 56) + 세부_폰트 = ImageFont.truetype(font, 24) + + 제목요소 = ImageDraw.Draw(사진) + 제목요소.text((180, 750), 급식, font=급식_폰트, fill=(255, 255, 255)) + + 세부요소 = ImageDraw.Draw(사진) + 세부요소.text((560, 623), f"{MLSV_YMD[:4]}.{MLSV_YMD[4:6]}.{MLSV_YMD[6:8]}", font=세부_폰트, fill=(255, 255, 255)) + 세부요소.text((540, 650), f"{급식_칼로리_얻기(MLSV_YMD)}", font=세부_폰트, fill=(137, 202, 255)) + + 사진.save(f'{os.getenv("ROOT")}temp/{MLSV_YMD}.png') + + print("🍲 | Meal Info Image Saved") + +def 디스코드(MLSV_YMD:str): + + 오늘급식 = 급식_정보_얻기(MLSV_YMD) + + data = { + "content" : MLSV_YMD, + "username" : "@today.isangjeong" + } + + data["embeds"] = [ + { + "description" : 오늘급식, + "title" : "인천상정중학교" + } + ] + + print("🏓 | Sending Payload") + result = requests.post(os.getenv('WEBHOOK_URL'), json = data) + print("🏓 | Payload Sent") + + try: + result.raise_for_status() + except requests.exceptions.HTTPError as err: + print(err) + else: + print(f"✨ | Payload successfully, code {result.status_code}.") \ No newline at end of file diff --git a/app/library/skeleton-vts.png b/app/library/skeleton-vts.png new file mode 100644 index 0000000..c843e94 Binary files /dev/null and b/app/library/skeleton-vts.png differ diff --git a/app/library/skeleton.png b/app/library/skeleton.png new file mode 100644 index 0000000..f3d5fe0 Binary files /dev/null and b/app/library/skeleton.png differ diff --git a/app/library/vts.py b/app/library/vts.py new file mode 100644 index 0000000..4dbcc77 --- /dev/null +++ b/app/library/vts.py @@ -0,0 +1,57 @@ +import requests, datetime +from bs4 import BeautifulSoup +import openpyxl, os + +def get_board(): + url = "http://isangjeong.icems.kr/boardCnts/list.do?searchType=S&page=1&boardID=33523&prntBoardID=0&prntBoardSeq=0&prntLev=0&m=0601&s=isangjeong" + + response = requests.get(url) + + soup = BeautifulSoup(response.text, "html.parser") + + element = soup.select_one("html body div:nth-of-type(2) div:nth-of-type(3) div div:nth-of-type(2) section:nth-of-type(2) div:nth-of-type(2) div:nth-of-type(2) div:nth-of-type(2) div form table tbody tr:nth-of-type(1) td:nth-of-type(2) a") + onclick_value = element.get("onclick") + + values = str(onclick_value[18:-1].replace("'", "")).replace(" ", "").split(",") +# for i in values: +# print(i) + + return f"http://isangjeong.icems.kr/boardCnts/updateCnt.do?boardID={values[0]}&viewBoardID={values[1]}&boardSeq={values[2]}&lev={values[3]}" + +path = f'{os.getenv("ROOT")}temp/downloaded_file.xlsx' + +def download(url:str): + response = requests.get(url) + soup = BeautifulSoup(response.text, "html.parser") + element = soup.select_one("html > body > div:nth-of-type(2) > div:nth-of-type(3) > div > div:nth-of-type(2) > section:nth-of-type(2) > div:nth-of-type(2) > div:nth-of-type(2) > div:nth-of-type(2) > div > form > table > tbody > tr:nth-of-type(2) > td > p > a:nth-of-type(1)") + herf_value = element.get("href") + + response = requests.get("http://isangjeong.icems.kr"+herf_value) + with open(path, "wb") as file: + file.write(response.content) + +def get_vts(): + #download(get_board()) + ws = openpyxl.load_workbook(path).active + + vts_list = [] + + for row in ws.iter_rows(min_row=5, max_row=5, min_col=7, max_col=14): + for cell in row: + if cell.fill.start_color.index == 'FFFFFF00': + value = str(cell.value).split('(')[0].strip()\ + .replace(" ", "")\ + .replace("월", "")\ + .replace("일", "") + vts_list.append(f"{datetime.datetime.today().year}{value}") + return vts_list + +def get_vts_true_or_false(): + vts_list = get_vts() + today = datetime.datetime.today().strftime("%Y%m%d") + if today in vts_list: + return True + else: + return False + return False + diff --git a/app/requirements.txt b/app/requirements.txt new file mode 100644 index 0000000..3d7fab4 --- /dev/null +++ b/app/requirements.txt @@ -0,0 +1,18 @@ +annotated-types==0.7.0 +beautifulsoup4==4.12.3 +bs4==0.0.2 +certifi==2024.8.30 +charset-normalizer==3.3.2 +et-xmlfile==1.1.0 +idna==3.8 +instagrapi==2.1.2 +openpyxl==3.1.5 +pillow==10.4.0 +pycryptodomex==3.20.0 +pydantic==2.7.1 +pydantic_core==2.18.2 +PySocks==1.7.1 +requests==2.32.3 +soupsieve==2.6 +typing_extensions==4.12.2 +urllib3==2.2.2 diff --git a/app/run.py b/app/run.py new file mode 100644 index 0000000..24ac916 --- /dev/null +++ b/app/run.py @@ -0,0 +1,43 @@ +from instagrapi import Client +import json, os +from datetime import datetime, timedelta +import library.lib as lib + +start_time = datetime.now() + +print("🍪 | Retrieving saved account information.") +cl = Client(json.load(open('./cookies.json'))) +print("🍪 | Account information was successfully retrieved.") + +print("📅 | Getting MLSV_YMD Timestamp") +MLSV_YMD = str((datetime.now() + timedelta(days=1)).strftime('%Y%m%d')) +print("📅 | Date:", MLSV_YMD) + +print("🍲 | Getting Meal Info Image") +lib.얻기(MLSV_YMD) + +path = f"{os.getenv("ROOT")}temp/{MLSV_YMD}.png" + +print("📸 | Uploading Story") +cl.photo_upload_to_story( + path=path, + caption=f"#인천상정중학교 #상정중학교 #급식 \n{MLSV_YMD}일자 급식", + extra_data = {'is_paid_partnership' : False} +) +print("📸 | Story Uploaded") + +print("🖼️ | Uploading Post") +cl.photo_upload( + path, + caption=f"#인천상정중학교 #상정중학교 #급식 \n{MLSV_YMD}일자 급식", + extra_data = {'is_paid_partnership' : False} +) +print("🖼️ | Post Uploaded") + +print("🗨️ | Uploading at Discord") +lib.디스코드(MLSV_YMD) +print("🗨️ | Uploaded at Discord") + +print("🎉 | All tasks completed.") +# calculate running time +print("🕒 | Running Time:", datetime.now() - start_time) \ No newline at end of file diff --git a/app/run.sh b/app/run.sh new file mode 100644 index 0000000..827a0f6 --- /dev/null +++ b/app/run.sh @@ -0,0 +1,22 @@ +cd /root/isangjeong.today +#rm -rf 20*.png + +#rm -rf /etc/localtime +#ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime + +# If doesn't have .venv folder, create it +if [ ! -d "venv" ]; then + python3 -m venv venv + .venv/bin/pip install -r requirements.txt +fi + +# If doesn't have temp folder, create it +if [ ! -d "temp" ]; then + mkdir temp +fi +# If doesn't have temp/cookies.json, create it +if [ ! -f "temp/cookies.json" ]; then + .venv/bin/python3 library/init-auth.py +fi + +.venv/bin/python3 run.py > temp/$(date +%Y%m%d-%H%M%S).log 2>&1 \ No newline at end of file diff --git a/crontab b/crontab new file mode 100644 index 0000000..a797853 --- /dev/null +++ b/crontab @@ -0,0 +1,2 @@ +0 22 * * * /usr/bin/bash /root/isangjeong.today/run.sh +5 4 * */2 6 /root/isangjeong.today/venv/bin/python3 /root/isangjeong.today/library/init-auth.py > /root/isangjeong.today/temp/auth-$(date +%Y%m%d-%H%M%S).log 2>&1 \ No newline at end of file