어쩌다가 다시 꺼내게 되었나
AWS 해커톤이 성공적으로 끝났다. 당연히 해커네컷도 사용이 끝났다. 아쉬움과 섭섭함이 남았다. 해커톤이 시작하기 전에 해커네컷을 완성하려고 아등바등 하던 일주일이 눈앞에 아른거렸다.
그러던 중 너무 반가운 소식이 들렸다.
짜릿했다. 사용료도 사용료지만 내가 만든 어플이 더 많은 사람들한테 사용될 수 있다는 사실이 행복했다. 동시에 만들어 놓은 해커네컷을 그냥 쓰는 게 아니라 더 고도화 시켜 보고 싶다고 생각했다. 해커네컷을 실제로 사용했을 때 불편했던 부분과 구현하고 싶었지만 시간이 부족해서 구현하지 못했던 부분을 해결해 보고 싶었다.
리팩토링은 처음이라
기존의 코드를 다시 훑어봤다. 개판이었다. 써내려갔다기보다는 덕지덕지 발랐다는 표현이 더 적절할 지경이었다. 내가 읽어도 코드가 어떻게 짜여져 있는지를 알 수가 없었다. 내가 읽어도 이 모양인데 남이 읽으면 얼마나 난잡해 보일까 싶었다. 그러던 와중 예전에 친구가 했던 말이 생각났다. 어떻게든 실행되는 코드를 짜는 것보다 코드를 아름답게 짜는 게 더 중요하다고 했었다. 결국 짜 놓은 코드를 다시 예쁘게 짜 보기로 했다. 인생 처음 짜 둔 코드를 다시 읽는 순간이다.
아래는 기존의 코드이다.
from PIL import Image
import time
import config
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
import re
import os
import requests
import logging
app = App(token=config.bot_token)
slack_client = WebClient(token=config.bot_token)
logging.basicConfig(filename='./log.txt', level=logging.DEBUG)
@app.message(re.compile("찍어줘"))
def show_(message, say, body, client):
message = {
"blocks":[
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": f"*카메라 꺼내는 중...*"
}
},
]
}
try:
img_files = body["event"]["files"]
print(img_files)
if(len(img_files) != 4):
say("네 컷 사진을 올려 주세요")
else:
file_srces = []
say("카메라 꺼내는 중...")
for img in img_files:
url = img["url_private_download"]
img_name = img["name"]
image_response = requests.get(url, headers={"Authorization": f"Bearer {config.bot_token}"}, stream=True)
file_src = f'./base_imgs/base_{img_name}'
file_srces.append(file_src)
with open(file_src, 'wb') as f:
for chunk in image_response.iter_content(1024):
f.write(chunk)
final_src = gogo_picture(file_srces)
say("사진 찍는 중...")
filepath = final_src # Your filepath
# channel_id = body["container"]["channel_id"]
# channel_name = body["channel"]["name"]
response = client.files_upload(channels = "gogo", file=filepath)
message = {
"blocks":[
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": f"*찰칵찰칵📷📷*"
}
},
]
}
say(message)
except:
say("사진 네 장과 함께 '찍어줘'라고 말씀해 보세요")
@app.action("say_gogo")
def say_gogo(ack, body, message, say, client):
ack()
say("가보자고")
filepath = "./frame.png" # Your filepath
channel_id = body["container"]["channel_id"]
channel_name = body["channel"]["name"]
response = client.files_upload(channels = channel_name, file=filepath)
message = {
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": f"*찰칵찰칵*"
}
}
]
}
say(message)
print(body["channel"]["name"])
def gogo_picture(fore_img_srces):
back_img_src = "./frame.png"
back_img = Image.open(back_img_src)
fore_imgs = []
for img_src in fore_img_srces:
now_img = Image.open(img_src)
if(now_img.width<now_img.height*1080/725):
now_img = now_img.crop((0, now_img.height*1/2-725/1080*now_img.width*1/2, now_img.width, now_img.height*1/2+725/1080*now_img.width*1/2))
now_img = now_img.resize((1080, 725))
else:
now_img = now_img.crop((now_img.width*1/2-1080/725*now_img.height*1/2, 0, now_img.width*1/2+1080/725*now_img.height*1/2, now_img.height))
now_img = now_img.resize((1080, 725))
fore_imgs.append(now_img)
back_img = back_img.resize((2386, 3602))
new_img = Image.new("RGB", back_img.size, "#ffffff")
new_img.paste(back_img)
new_img.paste(fore_imgs[0], (58, 380))
new_img.paste(fore_imgs[1], (58, 1148))
new_img.paste(fore_imgs[2], (58, 1921))
new_img.paste(fore_imgs[3], (58, 2694))
new_img.paste(fore_imgs[0], (58+1190, 380))
new_img.paste(fore_imgs[1], (58+1190, 1148))
new_img.paste(fore_imgs[2], (58+1190, 1921))
new_img.paste(fore_imgs[3], (58+1190, 2694))
final_src = f"./final_img/final_img_{time.time()}.png"
new_img.save(final_src)
print(new_img.size)
return final_src
if __name__ == '__main__':
SocketModeHandler(app, config.app_token).start()
logging.debug('debug')
logging.info('info')
logging.warning('warning')
logging.error('error')
logging.critical('critical')
해커네컷의 구조
'해커네컷'의 전체적인 구조는 다음과 같다.
모듈화 시키기
생각해 둔 껍데기는 다음과 같다.
더보기
when triggered "찍어줘"{
photo_card = get_photo_card(message);
send_message(photo_card);
}
get_photo_card(message){
src_imgs = pull_out_imgs_from(message);
rendered_imgs = render_imgs(src_imgs);
photo_card = get_photo_card(renderd_imgs);
}
send_message(photo_card){
channel = get_channel_id();
send photo_card to channel;
}
코드 리팩토링
리팩토링한 코드 전체
더보기
from PIL import Image
import time
import config
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
from slack_sdk import WebClient
import re
import requests
app = App(token=config.bot_token)
slack_client = WebClient(token=config.bot_token)
### main function ###
@app.message(re.compile("찍어줘"))
def fourcut(client, body, say):
say("카메라 꺼내는 중...")
photo_card_src = make_photo_card(body)
here = parse_channel_id(body)
say("사진 찍는 중...")
send_img_to_channel(photo_card_src, here, client, say)
### parse channel id from body, and then return it ###
def parse_channel_id(body):
return body['event']['channel']
### whole sequence of making photo card, and tnen return src of that photo card ###
def make_photo_card(body):
try:
file_objs = parse_file_object(body)
img_srces = save_imgs_from(file_objs)
photo_card_src = paste_imgs_to_frame(img_srces)
return photo_card_src
except:
return None
### parse file object from body ###
def parse_file_object(body):
return body["event"]["files"]
### request img from salck server and write file in local, and thel return src of that ###
def save_imgs_from(file_objs):
file_srces = []
for obj in file_objs:
url = obj["url_private_download"]
img_name = obj["name"]
file_src = f'./base_imgs/base_{img_name}'
file_srces.append(file_src)
image_response = requests.get(url, headers={"Authorization": f"Bearer {config.bot_token}"}, stream=True)
with open(file_src, 'wb') as f:
for chunk in image_response.iter_content(1024):
f.write(chunk)
return file_srces
### paste imgs to frame ###
def paste_imgs_to_frame(img_srces, frame_src):
frame_src = './frame.png'
frame = Image.open(frame_src).resize((2386, 3602))
imgs = get_pil_imgs_at(img_srces)
resized_imgs = get_resized_imgs(imgs)
new_img = Image.new("RGB", frame.size, "#ffffff")
new_img.paste(frame)
for i in range(4):
start_point_y = 375
start_point_x_left = 58
start_point_x_right = 1250
h_factor = 773
new_img.paste(resized_imgs[i], (start_point_x_left, start_point_y+i*h_factor))
new_img.paste(resized_imgs[i], (start_point_x_right, start_point_y+i*h_factor))
final_src = f"./final_img/final_img_{time.time()}.png"
new_img.save(final_src)
return final_src
## return pil object at src which recieved ###
def get_pil_imgs_at(img_srces):
imgs = []
for img_src in img_srces:
now_img = Image.open(img_src)
imgs.append(now_img)
return imgs
### recieve bulk of imgs and return them ###
def get_resized_imgs(imgs):
resized_imgs = []
for img in imgs:
resized_img = resize_img(1080, 725, img)
resized_imgs.append(resized_img)
return resized_imgs
### resize target_img ###
def resize_img(width, height, target_img):
now_img = target_img
if(now_img.width<now_img.height*1080/725):
now_img = now_img.crop((0, now_img.height*1/2-725/1080*now_img.width*1/2, now_img.width, now_img.height*1/2+725/1080*now_img.width*1/2))
now_img = now_img.resize((1080, 725))
else:
now_img = now_img.crop((now_img.width*1/2-1080/725*now_img.height*1/2, 0, now_img.width*1/2+1080/725*now_img.height*1/2, now_img.height))
now_img = now_img.resize((1080, 725))
return now_img
### send img at src to channel which has same id as received channel_id ###
def send_img_to_channel(img_src, cannel_id, client, say):
if(img_src != None):
response = client.files_upload_v2(channel = cannel_id, file=img_src)
if(response['ok']==True):
say("찰칵찰칵📸📷")
else:
say("다시 시도하세요.")
else:
say("사진 네 장과 함께 '찍어줘'라고 말해 보세요")
### run the app ###
if __name__ == '__main__':
SocketModeHandler(app, config.app_token).start()
'기타 프로젝트' 카테고리의 다른 글
[학식왕 김인하] 인하대학교 학식 메뉴 알리미 제작기(feat. AWS lambda, PDF 파일 파싱) (1) | 2023.11.05 |
---|---|
[AWS lambda] 1. lambda로 'Hello World!' 찍어보기 (0) | 2023.07.31 |
[해커네컷] 2. '찍을래' 기능 추가(1/2) (0) | 2023.07.25 |
[파이썬] 총동창회 회원 DB 주소 업데이트 프로그램 제작 (0) | 2023.01.20 |
[파이썬] 총동창회 회원 DB 전화번호 자동 수정 프로그램 만들기 (0) | 2023.01.16 |