Init
This commit is contained in:
commit
bf706e58d8
14 changed files with 297 additions and 0 deletions
5
.env.example
Normal file
5
.env.example
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
ROOT="/root/today.isangjeong/"
|
||||
INSTAGRAM_ID="today.isangjeong"
|
||||
INSTAGRAM_PASSWORD="passwd"
|
||||
KEY="key"
|
||||
WEBHOOK_URL="webhook_url"
|
||||
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
20*.png
|
||||
|
||||
# Auth
|
||||
cookies.json
|
||||
|
||||
# Temp Files
|
||||
temp/
|
||||
|
||||
.env
|
||||
|
||||
venv
|
||||
.venv
|
||||
26
Dockerfile
Normal file
26
Dockerfile
Normal file
|
|
@ -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"]
|
||||
13
README.MD
Normal file
13
README.MD
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# Today.isangjeong
|
||||
|
||||
오늘 급식도 인스타로
|
||||
|
||||
|
||||
실행하기 전에 `.env`가 필요합니다.
|
||||
|
||||
`.env.example`을 참고해서 만들어주세요.
|
||||
|
||||
|
||||
**이 프로젝트는 백업을 위한 레포로 친절하게 문서를 쓸 생각이 없습니다.**
|
||||
|
||||
`docker build --tag today-isangjeong:latest`
|
||||
BIN
app/library/Pretendard-Bold.ttf
Normal file
BIN
app/library/Pretendard-Bold.ttf
Normal file
Binary file not shown.
17
app/library/init-auth.py
Normal file
17
app/library/init-auth.py
Normal file
|
|
@ -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()}")
|
||||
82
app/library/lib.py
Normal file
82
app/library/lib.py
Normal file
|
|
@ -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("<br/>", "\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}.")
|
||||
BIN
app/library/skeleton-vts.png
Normal file
BIN
app/library/skeleton-vts.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
BIN
app/library/skeleton.png
Normal file
BIN
app/library/skeleton.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
57
app/library/vts.py
Normal file
57
app/library/vts.py
Normal file
|
|
@ -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
|
||||
|
||||
18
app/requirements.txt
Normal file
18
app/requirements.txt
Normal file
|
|
@ -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
|
||||
43
app/run.py
Normal file
43
app/run.py
Normal file
|
|
@ -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)
|
||||
22
app/run.sh
Normal file
22
app/run.sh
Normal file
|
|
@ -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
|
||||
2
crontab
Normal file
2
crontab
Normal file
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue