def manage_user(uid, user_grade, user_class, req_id, debugging): log.info("[#%s] manage_user@modules/user.py: Started Managing User Info" % req_id) try: with open(path, encoding="utf-8") as data_file: enc = hashlib.sha256() enc.update(uid.encode("utf-8")) enc_uid = enc.hexdigest() data = json.load(data_file) if debugging: print(data) if enc_uid in data: # 사용자 정보 있을 때 if data[enc_uid][0] == user_grade and data[enc_uid][1] == user_class: # 사용자 정보 똑같을 때 log.info("[#%s] manage_user@modules/user.py: Same" % req_id) return "Same" else: # 사용자 정보 있고 같지도 않을 때 - 업데이트 (삭제 후 재생성) del data[enc_uid] if debugging: print("DEL USER") log.info("[#%s] manage_user@modules/user.py: Updated" % req_id) return_msg = "Updated" else: # 사용자 정보 없을 때 - 생성 log.info("[#%s] manage_user@modules/user.py: Registered" % req_id) return_msg = "Registered" user_data = [int(user_grade), int(user_class)] data[enc_uid] = user_data with open(path, "w", encoding="utf-8") as write_file: json.dump(data, write_file, ensure_ascii=False, indent="\t") log.info("[#%s] manage_user@modules/user.py: Succeeded" % req_id) return return_msg except Exception: log.err("[#%s] manage_user@modules/user.py: Failed" % req_id) return Exception
def meal(reqdata, req_id, debugging): log.info("[#%s] meal@modules/skill.py: New Request" % req_id) try: sys_date = json.loads( json.loads(reqdata)["action"]["params"]["sys_date"])[ "date"] # 날짜 가져오기 except Exception: log.err("[#%s] meal@modules/skill.py: Error while Parsing Request" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) try: # 년월일 파싱 year = datetime.datetime.strptime(sys_date, "%Y-%m-%d").timetuple()[0] month = datetime.datetime.strptime(sys_date, "%Y-%m-%d").timetuple()[1] date = datetime.datetime.strptime(sys_date, "%Y-%m-%d").timetuple()[2] wday = datetime.datetime.strptime(sys_date, "%Y-%m-%d").timetuple()[6] except ValueError: # 파싱중 값오류 발생시 log.err("[#%s] meal@modules/skill.py: ValueError while Parsing Date" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) if debugging: print(sys_date) print(year) print(month) print(date) print(wday) return skill(meal_core(year, month, date, wday, req_id, debugging))
def weather(date_ko, req_id, debugging): global weather_data now = datetime.datetime.now() log.info("[#%s] weather@modules/getData.py: Started Fetching Weather Data" % req_id) # 날씨 파싱 후 캐싱 def parse(): global weather_data log.info("[#%s] weather@modules/getData.py: Started Parsing Weather Data" % req_id) weather_data = weatherParser.parse(req_id, debugging) # 지금의 날짜와 시간까지만 취함 weather_data["Timestamp"] = int(datetime.datetime(now.year, now.month, now.day, now.hour).timestamp()) with open('data/cache/weather.json', 'w', encoding="utf-8") as make_file: # 캐시 만들기 json.dump(weather_data, make_file, ensure_ascii=False, indent="\t") print("File Created") log.info("[#%s] weather@modules/getData.py: Succeeded" % req_id) if os.path.isfile('data/cache/weather.json'): # 캐시 있으면 try: log.info("[#%s] weather@modules/getData.py: Read Data in Cache" % req_id) with open('data/cache/weather.json', encoding="utf-8") as data_file: # 캐시 읽기 data = json.load(data_file, object_pairs_hook=OrderedDict) except Exception: # 캐시 읽을 수 없으면 try: os.remove('data/cache/weather.json') # 캐시 삭제 except Exception: log.err("[#%s] weather@modules/getData.py: Failed to Delete Cache" % req_id) return "측정소 또는 서버 오류입니다." parse() # 파싱 # 캐시 유효하면 if now - datetime.datetime.fromtimestamp(data["Timestamp"]) < datetime.timedelta(hours=1): global weather_data log.info("[#%s] weather@modules/getData.py: Use Data in Cache" % req_id) weather_data = data else: # 캐시 무효하면 log.info("[#%s] weather@modules/getData.py: Cache Expired" % req_id) parse() # 파싱 else: # 캐시 없으면 log.info("[#%s] weather@modules/getData.py: No Cache" % req_id) parse() # 파싱 return_data = ("🌡️ %s 최소/최대 기온: %s℃/%s℃\n\n" "등굣길 예상 날씨: %s\n" "🌡️ 기온: %s℃\n" "🌦️ 강수 형태: %s\n" "❔ 강수 확률: %s%%\n" "💧 습도: %s%%" % (date_ko, weather_data['temp_min'], weather_data['temp_max'], weather_data['sky'], weather_data['temp'], weather_data['pty'], weather_data['pop'], weather_data['reh']) ) log.info("[#%s] weather@modules/getData.py: Succeeded" % req_id) return return_data
def f_tt(): global briefing_tt tt_grade = str() tt_class = str() try: uid = json.loads(reqdata)["userRequest"]["user"]["id"] user_data = user.get_user(uid, req_id, debugging) # 사용자 정보 불러오기 tt_grade = user_data[0] tt_class = user_data[1] except Exception: log.err( "[#%s] briefing@modules/skill.py: Failed to Fetch Timetable" % req_id) briefing_tt = "시간표 조회 중 오류가 발생했습니다." if tt_grade is not None or tt_class is not None: # 사용자 정보 있을 때 tt = getData.tt(tt_grade, tt_class, date.year, date.month, date.day, req_id, debugging) if tt == "등록된 데이터가 없습니다.": briefing_tt = "등록된 시간표가 없습니다." else: briefing_tt = "%s 시간표:\n%s" % (date_ko, tt.split('):\n\n')[1] ) # 헤더부분 제거 else: log.info("[#%s] briefing@modules/skill.py: Non-Registered User" % req_id) briefing_tt = "등록된 사용자만 시간표를 볼 수 있습니다."
def commits(req_id, debugging): # GitHub API 사용 # API 사양은 https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository 참조 try: response = ( urllib.request.urlopen(url="https://api.github.com/repos/hgyoseo/hdmeal/commits").read().decode('utf-8') ) data = json.loads(response) except Exception as error: if debugging: print(error) log.err("[#%s] commits@modules/getData.py: Failed to Parse Commits" % req_id) return error # 마지막 커밋이 일어난 시간를 파싱함 # 시간은 UTC 기준, datetime에서 인식할 수 있게 하기 위해 Z를 떼고 9시간을 더해 한국 표준시로 변환 updated_at_datetime = (datetime.datetime.fromisoformat(data[0]["commit"]["committer"]["date"].replace("Z", "")) + datetime.timedelta(hours=9)) updated_at = "%s년 %s월 %s일 %s시 %s분" % ( updated_at_datetime.year, updated_at_datetime.month, updated_at_datetime.day, updated_at_datetime.hour, updated_at_datetime.minute ) # 최근 5개 커밋의 메시지 가져오기 messages = list(map(lambda loc: data[loc]["commit"]["message"], range(5))) # 리스트의 0번에 마지막 커밋 시간 삽입 messages.insert(0, updated_at) log.info("[#%s] commits@modules/getData.py: Succeeded" % req_id) return messages
def user_settings_rest_delete(req, req_id, debugging): try: delete_user(decoded['uid'], req_id, debugging) except Exception as error: log.err("[#%s] user_settings_rest_delete@modules/user.py: %s" % (req_id, error)) return errors["ServerError"] return {'message': "삭제했습니다."}
def inner(req, req_id, debugging): if "uid" in decoded: try: user = get_user(decoded['uid'], req_id, debugging) except Exception as error: log.err("[#%s] user_settings_rest_get@modules/user.py: %s" % (req_id, error)) return errors["ServerError"] return {'token': token, 'classes': list(range(1, classes + 1)), 'uid': decoded["uid"], 'current_grade': user[0], 'current_class': user[1]} else: return errors["InvalidToken"]
def tt(reqdata, req_id, debugging): log.info("[#%s] tt@modules/skill.py: New Request" % req_id) try: tt_grade = json.loads(reqdata)["action"]["params"]["Grade"] # 학년 파싱 except Exception: log.err("[#%s] tt@modules/skill.py: Error while Parsing Grade" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) try: tt_class = json.loads(reqdata)["action"]["params"]["Class"] # 반 파싱 except Exception: log.err("[#%s] tt@modules/skill.py: Error while Parsing Class" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) try: sys_date = json.loads( json.loads(reqdata)["action"]["params"]["sys_date"])[ "date"] # 날짜 파싱 except Exception: log.err("[#%s] tt@modules/skill.py: Error while Parsing Date" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) try: date = datetime.datetime.strptime(sys_date, "%Y-%m-%d") except ValueError: # 값오류 발생시 log.err("[#%s] tt@modules/skill.py: ValueError while Parsing Date" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) if debugging: print(tt_grade) print(tt_class) msg = getData.tt(tt_grade, tt_class, date.year, date.month, date.day, req_id, debugging) return skill(msg)
def user_settings_rest_post(req, req_id, debugging): if "user_grade" in req_data and "user_class" in req_data: user_grade = int(req_data["user_grade"]) user_class = int(req_data["user_class"]) if 0 < user_grade < 4 and 0 < user_class <= classes: try: manage_user(decoded['uid'], user_grade, user_class, req_id, debugging) except Exception as error: log.err("[#%s] user_settings_rest_post@modules/user.py: %s" % (req_id, error)) return errors["ServerError"] return {'message': "저장했습니다."} else: return errors["InvalidRequest"] else: return errors["InvalidRequest"]
def parse(): log.info("[#%s] wtemp@modules/getData.py: Started Parsing Water Temperature Data" % req_id) try: global date, temp parser = WTempParser.get(req_id, debugging) date = parser[0] temp = parser[1] except Exception: log.err("[#%s] wtemp@modules/getData.py: Failed to Fetch Water Temperature Data" % req_id) return "측정소 또는 서버 오류입니다." if not temp.isalpha(): # 무효값 걸러냄(값이 유효할 경우에만 캐싱) with open('data/cache/wtemp.json', 'w', encoding="utf-8") as make_file: # 캐시 만들기 json.dump({"timestamp": int(date.timestamp()), "temp": temp}, make_file, ensure_ascii=False, indent="\t") print("File Created") temp = temp + "°C" log.info("[#%s] wtemp@modules/getData.py: Succeeded" % req_id)
def purge_cache(reqdata, req_id, debugging): log.info("[#%s] purge_cache@modules/skill.py: New Request" % req_id) # 사용자 ID 가져오고 검증 uid = json.loads(reqdata)["userRequest"]["user"]["id"] if user.auth_admin(uid, req_id, debugging): if cache.purge(req_id, debugging)["status"] == "OK": # 삭제 실행, 결과 검증 msg = "캐시를 비웠습니다." else: log.err( "[#%s] purge_cache@modules/skill.py: Failed to Purge Cache" % req_id) msg = "삭제에 실패하였습니다. 오류가 발생했습니다." else: log.info("[#%s] purge_cache@modules/skill.py: Non-Authorized User" % req_id) msg = "사용자 인증에 실패하였습니다.\n당신의 UID는 %s 입니다." % uid return skill(msg)
def purge(req_id, debugging): dict_data = OrderedDict() try: file_list = [ file for file in os.listdir("data/cache/") if file.endswith(".json") ] for file in file_list: os.remove("data/cache/" + file) except Exception as error: log.err("[#%s] purge@modules/cache.py: Failed" % req_id) if debugging: dict_data["status"] = error dict_data["status"] = "Error" return dict_data dict_data["status"] = "OK" log.info("[#%s] purge@modules/cache.py: Succeeded" % req_id) return dict_data
def get(req_id, debugging): log.info("[#%s] get@modules/WTempParser.py: Started Parsing Water Temperature" % req_id) try: url = urllib.request.urlopen("http://koreawqi.go.kr/wQSCHomeLayout_D.wq?action_type=T") except Exception as error: if debugging: print(error) log.err("[#%s] get@modules/WTempParser.py: Failed" % req_id) return error data = BeautifulSoup(url, 'html.parser') # 측정일시 파싱 date = data.find('span', class_='data').get_text().split('"')[1] date = int(date[0:4]), int(date[4:6]), int(date[6:8]) time = int(data.find('span', class_='data').get_text().split('"')[3]) measurement_date = datetime.datetime(date[0], date[1], date[2], time) # 수온 파싱 wtemp = data.find('tr', class_='site_S01004').get_text() # 구리측정소 사용 wtemp = wtemp.replace("구리", "").strip() log.info("[#%s] get@modules/WTempParser.py: Succeeded" % req_id) return measurement_date, wtemp
def delete_user(uid, req_id, debugging): log.info("[#%s] delete_user@modules/user.py: Started Deleting User Info" % req_id) try: with open(path, encoding="utf-8") as data_file: enc = hashlib.sha256() enc.update(uid.encode("utf-8")) enc_uid = enc.hexdigest() data = json.load(data_file) if enc_uid in data: # 사용자 정보 있을 때 if debugging: print("DEL USER") del data[enc_uid] else: # 사용자 정보 없을 때 log.info("[#%s] delete_user@modules/user.py: No User Info" % req_id) return "NotExist" with open(path, "w", encoding="utf-8") as write_file: json.dump(data, write_file, ensure_ascii=False, indent="\t") log.info("[#%s] delete_user@modules/user.py: Succeeded" % req_id) return "Deleted" except Exception: log.err("[#%s] delete_user@modules/user.py: Failed" % req_id) return Exception
def user_settings_web(reqdata, jwt_secret, req_id, debugging): url = os.getenv("SETTINGS_URL") try: uid = json.loads(reqdata)["userRequest"]["user"]["id"] except Exception: log.err("[#%s] briefing@modules/skill.py: Failed to Fetch Timetable" % req_id) return skill_simpletext("토큰 생성 중 오류가 발생했습니다.") encoded = jwt.encode( { 'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=300), 'uid': uid }, jwt_secret, algorithm='HS384') return { 'version': '2.0', 'template': { 'outputs': [{ "basicCard": { "title": "내 정보 관리", "description": "아래 버튼을 클릭해 관리 페이지로 접속해 주세요.\n" "링크는 5분 뒤 만료됩니다.", "buttons": [{ "action": "webLink", "label": "내 정보 관리", "webLinkUrl": url + "?token=" + encoded.decode("UTF-8") }] } }] } }
def delete_user(reqdata, req_id, debugging): log.info("[#%s] delete_user@modules/skill.py: New Request" % req_id) try: uid = json.loads(reqdata)["userRequest"]["user"]["id"] # UID 파싱 except Exception: log.err("[#%s] delete_user@modules/skill.py: Error while Parsing UID" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) if debugging: print(uid) req = user.delete_user(uid, req_id, debugging) if req == "NotExist": log.info("[#%s] delete_user@modules/skill.py: User does Not Exist" % req_id) msg = "존재하지 않는 사용자입니다." elif req == "Deleted": log.info("[#%s] delete_user@modules/skill.py: Deleted" % req_id) msg = "삭제에 성공했습니다." else: log.err( "[#%s] delete_user@modules/skill.py: Failed to Process Request" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) return skill(msg)
def manage_user(reqdata, req_id, debugging): log.info("[#%s] manage_user@modules/skill.py: New Request" % req_id) try: user_grade = json.loads(reqdata)["action"]["params"]["Grade"] # 학년 파싱 except Exception: log.err( "[#%s] manage_user@modules/skill.py: Error while Parsing Grade" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) try: user_class = json.loads(reqdata)["action"]["params"]["Class"] # 반 파싱 except Exception: log.err( "[#%s] manage_user@modules/skill.py: Error while Parsing Class" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) try: uid = json.loads(reqdata)["userRequest"]["user"]["id"] # UID 파싱 except Exception: log.err("[#%s] manage_user@modules/skill.py: Error while Parsing UID" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) if debugging: print(user_grade) print(user_class) print(uid) req = user.manage_user(uid, user_grade, user_class, req_id, debugging) if req == "Registered": log.info("[#%s] manage_user@modules/skill.py: Created" % req_id) msg = "등록에 성공했습니다." elif req == "Same": log.info("[#%s] manage_user@modules/skill.py: Same" % req_id) msg = "저장된 정보와 수정할 정보가 같아 수정하지 않았습니다." elif req == "Updated": log.info("[#%s] manage_user@modules/skill.py: Updated" % req_id) msg = "수정되었습니다." else: log.err( "[#%s] manage_user@modules/skill.py: Failed to Process Request" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) return skill(msg)
def tt_registered(reqdata, req_id, debugging): log.info("[#%s] tt_registered@modules/skill.py: New Request" % req_id) try: uid = json.loads(reqdata)["userRequest"]["user"]["id"] user_data = user.get_user(uid, req_id, debugging) # 사용자 정보 불러오기 tt_grade = user_data[0] tt_class = user_data[1] except Exception: log.err( "[#%s] tt_registered@modules/skill.py: Error while Parsing Request" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) if tt_grade and tt_class: # 사용자 정보 있을 때 try: sys_date = json.loads( json.loads(reqdata)["action"]["params"]["sys_date"])[ "date"] # 날짜 파싱 except Exception: log.err( "[#%s] tt_registered@modules/skill.py: Error while Parsing Date" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) try: date = datetime.datetime.strptime(sys_date, "%Y-%m-%d") except ValueError: # 값오류 발생시 log.err( "[#%s] tt_registered@modules/skill.py: ValueError while Parsing Date" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) if debugging: print(tt_grade) print(tt_class) msg = getData.tt(tt_grade, tt_class, date.year, date.month, date.day, req_id, debugging) else: log.info("[#%s] tt_registered@modules/skill.py: Non-Registered User" % req_id) msg = "힉년/반 정보가 없습니다.\n먼저 내 정보 등록을 해 주시기 바랍니다." return skill(msg)
def schdl(reqdata, req_id, debugging): global msg log.info("[#%s] cal@modules/skill.py: New Request" % req_id) try: data = json.loads(reqdata)["action"]["params"] except Exception: log.err("[#%s] cal@modules/skill.py: Error while Parsing Request" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) # 특정일자 조회 if "sys_date" in data: try: date = datetime.datetime.strptime( json.loads(data["sys_date"])["date"], "%Y-%m-%d") # 날짜 파싱 except Exception: log.err("[#%s] cal@modules/skill.py: Error while Parsing Date" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) prsnt_schdl = getData.schdl(date.year, date.month, date.day, req_id, debugging) prsnt_schdl = prsnt_schdl if prsnt_schdl: msg = "%s-%s-%s(%s):\n%s" % (str( date.year).zfill(4), str(date.month).zfill(2), str( date.day).zfill(2), wday(date), prsnt_schdl ) # YYYY-MM-DD(Weekday) else: msg = "일정이 없습니다." # 특정일자 조회 끝 # 기간 조회 elif "sys_date_period" in data: # 기간 body = str() try: start = json.loads( data["sys_date_period"])["from"]["date"] # 시작일 파싱 start = datetime.datetime.strptime(start, "%Y-%m-%d") except Exception: log.err( "[#%s] cal@modules/skill.py: Error while Parsing StartDate" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) try: end = json.loads(data["sys_date_period"])["to"]["date"] # 종료일 파싱 end = datetime.datetime.strptime(end, "%Y-%m-%d") except Exception: log.err("[#%s] cal@modules/skill.py: Error while Parsing EndDate" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) if (end - start).days > 90: # 90일 이상을 조회요청한 경우, head = ("서버 성능상의 이유로 최대 90일까지만 조회가 가능합니다." "\n조회기간이 %s부터 %s까지로 제한되었습니다.\n\n" % (start.date(), (start + datetime.timedelta(days=90)).date())) end = start + datetime.timedelta(days=90) # 종료일 앞당김 else: head = "%s부터 %s까지 조회합니다.\n\n" % (start.date(), end.date()) schdls = getData.schdl_mass(start, end, req_id, debugging) # 년, 월, 일, 일정 정보를 담은 튜플이 리스트로 묶여서 반환됨 # body 쓰기, 연속되는 일정은 묶어 처리함 for content, group in groupby(schdls, lambda k: k[3]): list = [*group] if list[0] != list[-1]: start_date = datetime.date(*list[0][:3]) end_date = datetime.date(*list[-1][:3]) body = '%s%s(%s)~%s(%s):\n%s\n' % (body, start_date, wday(start_date), end_date, wday(end_date), content) else: date = datetime.date(*list[0][:3]) body = '%s%s(%s):\n%s\n' % (body, date, wday(date), content) if not body: body = "일정이 없습니다.\n" msg = (head + body)[:-1] # 맨 끝의 줄바꿈을 제거함 # 기간 조회 끝 else: # 아무런 파라미터도 넘겨받지 못한 경우 log.info("[#%s] cal@modules/skill.py: No Parameter" % req_id) return skill("오늘, 이번 달 등의 날짜 또는 기간을 입력해 주세요.") return skill(msg)
def wtemp(req_id, debugging): log.info("[#%s] wtemp@modules/getData.py: Started Fetching Water Temperature Data" % req_id) global date, temp def parse(): log.info("[#%s] wtemp@modules/getData.py: Started Parsing Water Temperature Data" % req_id) try: global date, temp parser = WTempParser.get(req_id, debugging) date = parser[0] temp = parser[1] except Exception: log.err("[#%s] wtemp@modules/getData.py: Failed to Fetch Water Temperature Data" % req_id) return "측정소 또는 서버 오류입니다." if not temp.isalpha(): # 무효값 걸러냄(값이 유효할 경우에만 캐싱) with open('data/cache/wtemp.json', 'w', encoding="utf-8") as make_file: # 캐시 만들기 json.dump({"timestamp": int(date.timestamp()), "temp": temp}, make_file, ensure_ascii=False, indent="\t") print("File Created") temp = temp + "°C" log.info("[#%s] wtemp@modules/getData.py: Succeeded" % req_id) if os.path.isfile('data/cache/wtemp.json'): # 캐시 있으면 try: log.info("[#%s] wtemp@modules/getData.py: Read Data in Cache" % req_id) with open('data/cache/wtemp.json', encoding="utf-8") as data_file: # 캐시 읽기 data = json.load(data_file, object_pairs_hook=OrderedDict) except Exception: # 캐시 읽을 수 없으면 try: os.remove('data/cache/wtemp.json') # 캐시 삭제 except Exception: log.err("[#%s] wtemp@modules/getData.py: Failed to Delete Cache" % req_id) return "측정소 또는 서버 오류입니다." parser_response = parse() # 파싱 # 캐시 유효하면 if (datetime.datetime.now() - datetime.datetime.fromtimestamp(data["timestamp"]) < datetime.timedelta(minutes=76)): # 실시간수질정보시스템상 자료처리 시간 고려, 유효기간 76분으로 설정 log.info("[#%s] wtemp@modules/getData.py: Use Data in Cache" % req_id) date = datetime.datetime.fromtimestamp(data["timestamp"]) temp = data["temp"] + "°C" parser_response = None else: # 캐시 무효하면 log.info("[#%s] wtemp@modules/getData.py: Cache Expired" % req_id) parser_response = parse() # 파싱 else: # 캐시 없으면 log.info("[#%s] temp@modules/getData.py: No Cache" % req_id) parser_response = parse() # 파싱 if parser_response == "측정소 또는 서버 오류입니다.": return "측정소 또는 서버 오류입니다." time = date.hour # 24시간제 -> 12시간제 변환 if time == 0 or time == 24: # 자정 time = "오전 12시" elif time < 12: # 오전 time = "오전 %s시" % time elif time == 12: # 정오 time = "오후 12시" else: # 오후 time = "오후 %s시" % (time - 12) body = "%s %s 측정자료:\n한강 수온은 %s 입니다." % (date.date(), time, temp) log.info("[#%s] wtemp@modules/getData.py: Succeeded" % req_id) return body
def parse(summoner_name, req_id, debugging): global api_key log.info("[#%s] parse@modules/LoL.py: Started" % req_id) if debugging: if not summoner_name: summoner_name = summoner_name_for_debug api_key = api_key_for_debug header = {'X-Riot-Token': api_key} # 소환사 정보 가져오기 try: summoner_req = urllib.request.Request( api_baseurl + 'lol/summoner/v4/summoners/by-name/' + urllib.parse.quote(summoner_name), data=None, headers=header) summoner_data = json.load(urllib.request.urlopen(summoner_req)) summoner_id = summoner_data["id"] account_id = summoner_data["accountId"] except Exception as error: if "404" in str(error): return None if "401" in str(error) or "403" in str(error): return "Invalid Token" if debugging: print(error) log.err("[#%s] parse@modules/LoL.py: Failed to Parse Summoner Data" % req_id) return error # 소환사의 리그정보 가져오기 try: league_req = urllib.request.Request( api_baseurl + 'lol/league/v4/entries/by-summoner/' + summoner_id, data=None, headers=header) league_data = json.load(urllib.request.urlopen(league_req)) except Exception as error: if debugging: print(error) log.err("[#%s] parse@modules/LoL.py: Failed to Parse League Data" % req_id) return error # 소환사의 경기전적 가져오기 try: match_req = urllib.request.Request( api_baseurl + 'lol/match/v4/matchlists/by-account/' + account_id + '?endIndex=20', data=None, headers=header) match_data = json.load(urllib.request.urlopen(match_req)) except Exception as error: if "404" in str(error): match_data = None else: if debugging: print(error) log.err("[#%s] parse@modules/LoL.py: Failed to Parse Match Data" % req_id) return error # 소환사 정보 딕셔너리로 만들기 data = dict() data["summoner"] = { "name": summoner_data["name"], # 소환사명 "level": summoner_data["summonerLevel"], # 소환사 레벨 "profileIcon": ("https://ddragon.leagueoflegends.com/cdn/9.24.2/img/profileicon/%s.png" % summoner_data["profileIconId"]), # 프로필 아이콘 "OPGG": "https://www.op.gg/summoner/userName="******"ranked_solo"] = None data["ranked_flex"] = None for league in league_data: # 솔랭 전적 if league["queueType"] == "RANKED_SOLO_5x5": data["ranked_solo"] = { "wins": league["wins"], "losses": league["losses"], "winningRate": calculate_winning_late(league["wins"], league["losses"]), "rank": league["rank"], "tier": league["tier"], "leaguePoints": league["leaguePoints"] } # 자유랭 전적 elif league["queueType"] == "RANKED_FLEX_SR": data["ranked_flex"] = { "wins": league["wins"], "losses": league["losses"], "winningRate": calculate_winning_late(league["wins"], league["losses"]), "rank": league["rank"], "tier": league["tier"], "leaguePoints": league["leaguePoints"] } else: continue if match_data: data["games"] = match_data["endIndex"] # 선호도 계산에 사용된 게임 판수(최대 20판) lane = list() champion = list() # 레인과 챔피언으로 List 만들기 for i in match_data["matches"]: if not i["lane"] == "NONE": lane.append(i["lane"]) champion.append(i["champion"]) # 챔피언 정보 가져오고 ID와 한국어 이름으로 딕셔너리 만들기 champion_names = dict() try: if os.path.isfile('data/champion.json'): with open('data/champion.json', encoding="utf-8") as data_file: # 캐시 읽기 champion_data = json.load(data_file)["data"] else: # 캐시 없으면 if os.path.isfile('../data/champion.json'): with open('../data/champion.json', encoding="utf-8") as data_file: # 캐시 읽기 champion_data = json.load(data_file)["data"] except Exception as error: if debugging: print(error) log.err( "[#%s] parse@modules/LoL.py: Failed to Parse Champion Data" % req_id) return error for i in champion_data: champion_names[int( champion_data[i]["key"])] = champion_data[i]["name"] # 선호레인과 챔피언 구하고 플레이 비율 구하기 data["preferredLane"] = Counter(lane).most_common(1)[0][0], int( Counter(lane).most_common(1)[0][1] / data["games"] * 100) preferred_champion = Counter(champion).most_common(1)[0] data["preferredChampion"] = champion_names[preferred_champion[0]], int( preferred_champion[1] / data["games"] * 100) else: data["games"] = 0 data["preferredLane"] = None data["preferredChampion"] = None log.info("[#%s] parse@modules/LoL.py: Succeeded" % req_id) return data
def publish(fb_token, req_id, debugging): fb = "OK" status = 200 tomorrow = datetime.now() + timedelta(days=1) # 내일 meal = getData.meal(str(tomorrow.year), str(tomorrow.month), str(tomorrow.day), req_id, debugging) # 급식정보 불러오기 log.info("[#%s] publish@modules/FB.py: Started Publishing" % req_id) # 급식데이터가 있는지 확인 if "message" in meal: if meal["message"] == "등록된 데이터가 없습니다.": if debugging: print("NoData") log.info("[#%s] publish@modules/FB.py: No Data" % req_id) return {"Parser": "NoData"}, 200 else: if debugging: print("NoData") log.err("[#%s] publish@modules/FB.py: Failed to Publish" % req_id) return {"Parser": "ERROR"}, 500 else: date = meal["date"] # 날짜 - YYYY-MM-DD(Weekday) menu = re.sub(r'\[[^\]]*\]', '', meal["menu"]).split('\n') # 괄호(알레르기정보) 제거, 행별로 나누기 kcal = meal["kcal"] + " kcal" # 열량값에 단위(kcal)붙이기 # 템플릿 try: tmpl = Image.open('data/FB_Template.png') pmap_tmpl = tmpl.load() except FileNotFoundError: log.err("[#%s] publish@modules/FB.py: Failed" % req_id) return {"Parser": "OK", "IMG": "Missing File"}, 500 except Exception: log.err("[#%s] publish@modules/FB.py: Failed" % req_id) return {"Parser": "OK", "IMG": "FAIL"}, 500 # 이미지 생성 output = Image.new(tmpl.mode, tmpl.size) pmap_output = output.load() # 템플릿의 Pixel Map을 복제 for x in range(output.size[0]): for y in range(output.size[1]): pmap_output[x, y] = pmap_tmpl[x, y] # 글꼴과 크기 정의 font_date = ImageFont.truetype('data/NotoSansCJKkr-Bold.otf', 72) font_menu = ImageFont.truetype('data/NotoSansCJKkr-Bold.otf', 64) font_kcal = ImageFont.truetype('data/NotoSansCJKkr-Bold.otf', 56) # 날짜 그리기 ImageDraw.Draw(output).text((84, 62), date, (59, 111, 217), font=font_date) # 메뉴 그리기 for i in range(len(menu)): # 별이 있는 경우(맛있는 메뉴) 파란색으로 강조하기 if '⭐' in menu[i]: ImageDraw.Draw(output).text((84, 204 + (77 * i)), menu[i].replace('⭐', ''), (59, 111, 217), font=font_menu) else: ImageDraw.Draw(output).text((84, 204 + (77 * i)), menu[i], (0, 0, 0), font=font_menu) # 열량 그리기 ImageDraw.Draw(output).text((219, 923), kcal, (59, 111, 217), font=font_kcal) # RAM에 임시로 파일 저장 temp = io.BytesIO() output.save(temp, format="png") # Facebook에 업로드 if fb_token: try: graph = facebook.GraphAPI(access_token=fb_token) graph.put_photo(image=temp.getvalue(), message=date + " 급식") # YYYY-MM-DD(Weekday) 급식 except facebook.GraphAPIError as error: if debugging: print(error) if str(error) == "Invalid OAuth access token.": fb = "Invalid Token" status = 400 else: fb = "ERR" status = 500 except Exception: fb = "ERR" status = 500 else: fb = "No Token" # OK 반환 log.info("[#%s] publish@modules/FB.py: Succeeded" % req_id) return {"Parser": "OK", "IMG": "OK", "fb": fb}, status
def parse(req_id, debugging): log.info("[#%s] parse@modules/weatherParser.py: Started Parsing Weather" % req_id) try: url = urllib.request.urlopen( "https://www.kma.go.kr/wid/queryDFSRSS.jsp" "?zone=%s" % region) except Exception as error: if debugging: print(error) log.err("[#%s] parse@modules/weatherParser.py: Failed" % req_id) return error data = xml.etree.ElementTree.fromstring( url.read().decode('utf-8')).findall('.//data') weather = dict() for i in range(6): if data[i].find('hour').text == '9': # 9시 찾기 # 위치 weather['loc'] = i # 시간 weather['hour'] = data[i].find('hour').text # 기온/최대 기온/최소 기온 weather['temp'] = data[i].find('temp').text weather['temp_max'] = data[i].find('tmx').text weather['temp_min'] = data[i].find('tmn').text # 하늘 상태 - 1: 맑음 2: 구름조금 3: 구름많음 4: 흐림 weather['sky'] = data[i].find('sky').text # 강수 형태 - 0: 없음 1: 비 2: 비&눈 3: 눈 weather['pty'] = data[i].find('pty').text # 강수 확률 weather['pop'] = data[i].find('pop').text # 습도 weather['reh'] = data[i].find('reh').text break if not weather: # 날씨데이터 없을 경우(다음날 9시로 밀린 경우) 그 다음 데이터를 취함 # 위치 weather['loc'] = 0 # 시간 weather['hour'] = data[0].find('hour').text # 기온/최대 기온/최소 기온 weather['temp'] = data[0].find('temp').text weather['temp_max'] = data[0].find('tmx').text weather['temp_min'] = data[0].find('tmn').text # 하늘 상태 - 1: 맑음 2: 구름조금 3: 구름많음 4: 흐림 weather['sky'] = data[0].find('sky').text # 강수 형태 - 0: 없음 1: 비 2: 비&눈 3: 눈 weather['pty'] = data[0].find('pty').text # 강수 확률 weather['pop'] = data[0].find('pop').text # 습도 weather['reh'] = data[0].find('reh').text weather['1st_hour'] = data[0].find('hour').text if weather['1st_hour'] == "24": weather['1st_hour'] = "0" # 하늘 상태, 강수 형태 대응값 sky = ['☀ 맑음', '🌤️ 구름 조금', '🌥️ 구름 많음', '☁ 흐림'] pty = ['❌ 없음', '🌧️ 비', '🌤️ 구름 조금', '🌥️ 구름 많음'] # 하늘 상태 대응값 적용 if int(weather['sky']) <= 4: weather['sky'] = sky[int(weather['sky']) - 1] # 1부터 시작 else: weather['sky'] = '⚠ 오류' log.err("[#%s] parse@modules/weatherParser.py: Failed to Parse Sky" % req_id) # 강수 형태 대응값 적용 if int(weather['pty']) < 4: weather['pty'] = pty[int(weather['pty'])] else: weather['pty'] = '⚠ 오류' log.err( "[#%s] parse@modules/weatherParser.py: Failed to Parse Precipitation Type" % req_id) log.info("[#%s] parse@modules/weatherParser.py: Succeeded" % req_id) return weather
def fetch(): global part_code, data_1, data_2 # 학교명으로 검색해 학교코드 알아내기 try: search_req = urllib.request.Request( 'http://comci.kr:4081/98372?92744l%s' % urlparse.quote(school_name.encode("EUC-KR")), data=None, headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/78.0.3904.70 Safari/537.36' }) search_url = urllib.request.urlopen(search_req) except Exception as error: if debugging: print(error) log.err( "[#%s] fetch.parse@modules/TTParser.py: Failed to Parse Timetable(%s-%s, %s)" % (req_id, tt_grade, tt_class, tt_date)) return error # 학교 검색결과 가져오기 school_list = ast.literal_eval( search_url.read().decode('utf-8').replace('\x00', ''))["학교검색"] if debugging: print(school_list) # 검색결과를 지역으로 구분하고 학교코드 가져오기 for i in school_list: if i[1] == school_region: part_code = i[3] break # 이번 주 시간표와 다음 주 시간표 가져오기 try: fetch_req_1 = urllib.request.Request( 'http://comci.kr:4081/98372?' + base64.b64encode(bytes("34739_%s_0_1" % part_code, 'utf-8')).decode("utf-8"), data=None, headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/78.0.3904.70 Safari/537.36' }) fetch_req_2 = urllib.request.Request( 'http://comci.kr:4081/98372?' + base64.b64encode(bytes("34739_%s_0_2" % part_code, 'utf-8')).decode("utf-8"), data=None, headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/78.0.3904.70 Safari/537.36' }) # 시간표 디코딩 url_1 = urllib.request.urlopen(fetch_req_1).read().decode( 'utf-8').replace('\x00', '') url_2 = urllib.request.urlopen(fetch_req_2).read().decode( 'utf-8').replace('\x00', '') except Exception as error: if debugging: print(error) log.err( "[#%s] fetch.parse@modules/TTParser.py: Failed to Parse Timetable(%s-%s, %s)" % (req_id, tt_grade, tt_class, tt_date)) return error # JSON 파싱 data_1 = json.loads(url_1) data_2 = json.loads(url_2) timestamp = int(datetime.datetime.now().timestamp()) # 타임스탬프 생성 # 캐시 만들기 with open('data/cache/TT.json', 'w', encoding="utf-8") as make_file: json_cache = dict() json_cache["1"] = dict() json_cache["2"] = dict() json_cache["Timestamp"] = timestamp # 필요한 자료들만 선별에서 캐싱하기 json_cache["1"]["자료14"] = data_1["자료14"] json_cache["1"]["자료81"] = data_1["자료81"] json_cache["1"]["자료46"] = data_1["자료46"] json_cache["1"]["자료92"] = data_1["자료92"] json_cache["2"]["자료14"] = data_2["자료14"] json_cache["2"]["자료81"] = data_2["자료81"] json_cache["2"]["자료46"] = data_2["자료46"] json_cache["2"]["자료92"] = data_2["자료92"] json_cache["2"]["시작일"] = data_2["시작일"] json.dump(json_cache, make_file, ensure_ascii=False, indent="\t") print("File Created")
def parse(tt_grade, tt_class, year, month, date, req_id, debugging): global sunday, data_1, data_2 part_code = str() tt_date = datetime.date(year, month, date) tt_grade = int(tt_grade) tt_class = int(tt_class) log.info( "[#%s] parse@modules/TTParser.py: Started Parsing Timetable(%s-%s, %s)" % (req_id, tt_grade, tt_class, tt_date)) # 데이터 가져오기 def fetch(): global part_code, data_1, data_2 # 학교명으로 검색해 학교코드 알아내기 try: search_req = urllib.request.Request( 'http://comci.kr:4081/98372?92744l%s' % urlparse.quote(school_name.encode("EUC-KR")), data=None, headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/78.0.3904.70 Safari/537.36' }) search_url = urllib.request.urlopen(search_req) except Exception as error: if debugging: print(error) log.err( "[#%s] fetch.parse@modules/TTParser.py: Failed to Parse Timetable(%s-%s, %s)" % (req_id, tt_grade, tt_class, tt_date)) return error # 학교 검색결과 가져오기 school_list = ast.literal_eval( search_url.read().decode('utf-8').replace('\x00', ''))["학교검색"] if debugging: print(school_list) # 검색결과를 지역으로 구분하고 학교코드 가져오기 for i in school_list: if i[1] == school_region: part_code = i[3] break # 이번 주 시간표와 다음 주 시간표 가져오기 try: fetch_req_1 = urllib.request.Request( 'http://comci.kr:4081/98372?' + base64.b64encode(bytes("34739_%s_0_1" % part_code, 'utf-8')).decode("utf-8"), data=None, headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/78.0.3904.70 Safari/537.36' }) fetch_req_2 = urllib.request.Request( 'http://comci.kr:4081/98372?' + base64.b64encode(bytes("34739_%s_0_2" % part_code, 'utf-8')).decode("utf-8"), data=None, headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/78.0.3904.70 Safari/537.36' }) # 시간표 디코딩 url_1 = urllib.request.urlopen(fetch_req_1).read().decode( 'utf-8').replace('\x00', '') url_2 = urllib.request.urlopen(fetch_req_2).read().decode( 'utf-8').replace('\x00', '') except Exception as error: if debugging: print(error) log.err( "[#%s] fetch.parse@modules/TTParser.py: Failed to Parse Timetable(%s-%s, %s)" % (req_id, tt_grade, tt_class, tt_date)) return error # JSON 파싱 data_1 = json.loads(url_1) data_2 = json.loads(url_2) timestamp = int(datetime.datetime.now().timestamp()) # 타임스탬프 생성 # 캐시 만들기 with open('data/cache/TT.json', 'w', encoding="utf-8") as make_file: json_cache = dict() json_cache["1"] = dict() json_cache["2"] = dict() json_cache["Timestamp"] = timestamp # 필요한 자료들만 선별에서 캐싱하기 json_cache["1"]["자료14"] = data_1["자료14"] json_cache["1"]["자료81"] = data_1["자료81"] json_cache["1"]["자료46"] = data_1["자료46"] json_cache["1"]["자료92"] = data_1["자료92"] json_cache["2"]["자료14"] = data_2["자료14"] json_cache["2"]["자료81"] = data_2["자료81"] json_cache["2"]["자료46"] = data_2["자료46"] json_cache["2"]["자료92"] = data_2["자료92"] json_cache["2"]["시작일"] = data_2["시작일"] json.dump(json_cache, make_file, ensure_ascii=False, indent="\t") print("File Created") if os.path.isfile('data/cache/TT.json'): # 캐시 있으면 try: log.info("[#%s] parse@modules/TTParser.py: Read Data in Cache" % req_id) with open('data/cache/TT.json', encoding="utf-8") as data_file: # 캐시 읽기 global data_1, data_2 data = json.load(data_file) data_1 = data["1"] data_2 = data["2"] except Exception: # 캐시 읽을 수 없으면 try: # 캐시 삭제 os.remove('data/cache/TT.json') except Exception as error: log.err( "[#%s] parse@modules/TTParser.py: Failed to Delete Cache" % req_id) return error fetch() # 파싱 # 캐시 유효하면 if datetime.datetime.now() - datetime.datetime.fromtimestamp( data["Timestamp"]) < datetime.timedelta(hours=3): log.info("[#%s] parse@modules/TTParser.py: Use Data in Cache" % req_id) else: # 캐시 무효하면 log.info("[#%s] parse@modules/TTParser.py: Cache Expired" % req_id) fetch() # 파싱 else: # 캐시 없으면 log.info("[#%s] parse@modules/TTParser.py: No Cache" % req_id) fetch() # 파싱 # 날짜비교 기준일(2번째 자료의 시작일) 파싱 data_2_date = data_2["시작일"].split("-") data_2_date = datetime.date(int(data_2_date[0]), int(data_2_date[1]), int(data_2_date[2])) # 기준일이 조회일보다 미래이고, 날짜차이가 7일 이내일 때, 첫 번째 자료 선택 if data_2_date >= tt_date and (data_2_date - tt_date).days <= 7: data = data_1 # 조회일이 기준일보다 미래이고, 날짜차이가 6일 이내일 때, 두 번째 자료 선택 elif data_2_date <= tt_date and (tt_date - data_2_date).days <= 6: data = data_2 else: return None tt = data["자료14"][tt_grade][tt_class] # 자료14에 각 반의 일일시간표 정보가 담겨있음 og_tt = data["자료81"][tt_grade][tt_class] # 자료81에 각 반의 원본시간표 정보가 담겨있음 teacher_list = data["자료46"] # 교사명단 subject_list = data["자료92"] # 2글자로 축약한 과목명단 - 전체 명칭은 긴자료92에 담겨있음 # 파이썬의 weekday는 월요일부터 시작하지만 # 컴시간의 weekday는 일요일부터 시작한다 # 파이썬 → 컴시간 형식변환 if tt_date.weekday() == 6: comci_weekday = 0 else: comci_weekday = tt_date.weekday() + 1 if debugging: print(tt[comci_weekday]) return_data = list() for i in range(len(tt[comci_weekday])): if tt[comci_weekday][i] != 0: subject = subject_list[int(str( tt[comci_weekday][i])[-2:])] # 뒤의 2자리는 과목명을 나타냄 teacher = teacher_list[int(str( tt[comci_weekday][i])[:-2])] # 나머지 숫자는 교사명을 나타냄 if not tt[comci_weekday][i] == og_tt[comci_weekday][i]: return_data.append("⭐%s(%s)" % (subject, teacher)) # 시간표 변경사항 표시 else: return_data.append("%s(%s)" % (subject, teacher)) # 리스트에서 0 제거 tt[comci_weekday] = [i for i in tt[comci_weekday] if i != 0] og_tt[comci_weekday] = [i for i in og_tt[comci_weekday] if i != 0] # 단축수업, 연장수업, 시간표없음 표시 if tt[comci_weekday] and og_tt[comci_weekday]: # 교시수 구하기 tt_num = len(tt[comci_weekday]) og_tt_num = len(og_tt[comci_weekday]) if tt_num == og_tt_num: pass elif tt_num < og_tt_num: return_data.append("[MSG]⭐단축수업이 있습니다. (%s교시 → %s교시)" % (og_tt_num, tt_num)) elif tt_num > og_tt_num: return_data.append("[MSG]⭐연장수업이 있습니다. (%s교시 → %s교시)" % (og_tt_num, tt_num)) log.info("[#%s] parse@modules/TTParser.py: Succeeded(%s-%s, %s)" % (req_id, tt_grade, tt_class, tt_date)) else: return_data.append("[MSG]⭐수업을 실시하지 않습니다. (시간표 없음)") return return_data
def lol(reqdata, req_id, debugging): log.info("[#%s] lol@modules/skill.py: New Request" % req_id) try: summoner_name = json.loads(reqdata)["action"]["params"]["summonerName"] except Exception: log.err( "[#%s] lol@modules/skill.py: Error while Parsing Summoner Name" % req_id) return skill("오류가 발생했습니다.\n요청 ID: " + req_id) # 소환사명 16자 초과하면 if len(summoner_name) > 16: log.info("[#%s] lol@modules/skill.py: Summoner Name is Too Long" % req_id) return { 'version': '2.0', 'template': { 'outputs': [{ "basicCard": { "title": "소환사명이 너무 김", "description": "소환사명이 너무 깁니다.\n" "소환사명은 영문 16자, 한글 8자 이내입니다.\n" "잘못 입력하진 않았는지 확인해주세요.", "buttons": [{ "action": "message", "label": "다시 검색하기", "messageText": "롤 전적 조회하기" }] } }] } } try: summoner_data = LoL.parse(summoner_name, req_id, debugging) except Exception: log.err( "[#%s] lol@modules/skill.py: Error while Parsing Summoner Data" % req_id) return skill_simpletext("오류가 발생했습니다.\n요청 ID: " + req_id) if summoner_data == 'Invalid Token': log.err("[#%s] lol@modules/skill.py: Invalid Token" % req_id) return skill_simpletext("오류가 발생했습니다.\n요청 ID: " + req_id) elif summoner_data: # 솔랭 전적 구하기 if summoner_data["ranked_solo"]: solo = ("솔랭 전적:\n" "%s %s (%s LP)\n" "%s승 %s패 (%s%%)\n\n" % (summoner_data["ranked_solo"]["tier"], summoner_data["ranked_solo"]["rank"], summoner_data["ranked_solo"]["leaguePoints"], summoner_data["ranked_solo"]["wins"], summoner_data["ranked_solo"]["losses"], summoner_data["ranked_solo"]["winningRate"])) else: solo = "솔랭 전적이 없습니다. 분발하세요!\n\n" # 자유랭 전적 구하기 if summoner_data["ranked_flex"]: flex = ("자유랭 전적:\n" "%s %s (%s LP)\n" "%s승 %s패 (%s%%)\n\n" % (summoner_data["ranked_flex"]["tier"], summoner_data["ranked_flex"]["rank"], summoner_data["ranked_flex"]["leaguePoints"], summoner_data["ranked_flex"]["wins"], summoner_data["ranked_flex"]["losses"], summoner_data["ranked_flex"]["winningRate"])) else: flex = "자유랭 전적이 없습니다. 분발하세요!\n\n" # 통계 내기 if summoner_data["games"]: if summoner_data["preferredLane"]: preferred_lane = "%s(%s%%)" % ( summoner_data["preferredLane"][0], summoner_data["preferredLane"][1]) else: preferred_lane = "정보없음" if summoner_data["preferredChampion"]: preferred_champion = ("%s(%s%%)" % (summoner_data["preferredChampion"][0], summoner_data["preferredChampion"][1])) else: preferred_champion = "정보없음" preferences = ("최근 %s번의 매치를 바탕으로 분석한 결과입니다:\n" "많이한 라인: %s\n" "많이한 챔피언: %s") % (summoner_data["games"], preferred_lane, preferred_champion) else: preferences = "통계가 없습니다. 분발하세요!" return { 'version': '2.0', 'template': { 'outputs': [{ "basicCard": { "title": "%s (레벨 %s)" % (summoner_data["summoner"]["name"], summoner_data["summoner"]["level"]), "description": solo + flex + preferences, "thumbnail": { "imageUrl": summoner_data["summoner"]["profileIcon"] }, "buttons": [{ "action": "webLink", "label": "OP.GG에서 보기", "webLinkUrl": summoner_data["summoner"]["OPGG"] }, { "action": "message", "label": "다른 소환사 검색하기", "messageText": "롤 전적 조회하기" }] } }] } } else: return { 'version': '2.0', 'template': { 'outputs': [{ "basicCard": { "title": "소환사를 찾을 수 없음", "description": summoner_name + " 소환사를 찾을 수 없습니다.\n" "한국 서버에 등록된 소환사가 맞는지, " "잘못 입력하진 않았는지 확인해주세요.", "buttons": [{ "action": "message", "label": "다시 검색하기", "messageText": "롤 전적 조회하기" }] } }] } }