├── __init__.py ├── alphaSandol ├── __init__.py ├── Pipfile ├── zappa_settings.json ├── Imoge.json ├── weather.py ├── test_constant.py ├── covid.py ├── announcement.py ├── settings.py ├── feedback.py ├── test_block.py ├── main.py ├── return_type_generator.py ├── subway.py ├── restaurant.py └── Pipfile.lock ├── statistic ├── now.csv └── sandol_200109-211018.csv ├── img ├── card1.png ├── card2.png ├── card3.png ├── logo1.png ├── logo2.png ├── logo3.png ├── 광고_미가.png ├── 광고_세미콘.png ├── diagram.jpg ├── meal_map.png ├── card_covid.png ├── card_food.png ├── card_miga.png ├── card_other.png ├── card_wells.png ├── 산돌이레포짓토리QR.jpg ├── 산돌이친구추가QR.jpeg ├── logo_profile1.png ├── logo_profile2.png └── logo_profile3.png ├── Persona ├── 도움말_임시.png ├── Persona_logic.png └── snadol_Persona.drawio ├── .gitignore ├── test_stored_data ├── feedback.txt └── restaurant_menu.txt ├── return_type_img ├── Text Type.JPG ├── set_img.PNG ├── Carousel Type.JPG ├── List Card Type.JPG ├── Basic Card Field.JPG ├── Basic Card Test.JPG ├── Basic Card Type.JPG ├── Simple Text Test.JPG ├── Commerce Card Field.JPG └── Commerce Card Type.JPG ├── commerce_img ├── commerce_test1.png ├── commerce_test2.png ├── commerce_test3.png ├── commerce_test4.png ├── commerce_test5.png └── commerce_test6.png ├── betaSandol ├── Pipfile ├── zappa_settings.json ├── lambda_prototype.py ├── return_type_generator.py ├── Pipfile.lock └── lambda_prototype_module.py ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ └── cd.yml ├── LICENSE ├── README.md └── Return Type Generator.md /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /alphaSandol/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /statistic/now.csv: -------------------------------------------------------------------------------- 1 | awd 2 | -------------------------------------------------------------------------------- /img/card1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/card1.png -------------------------------------------------------------------------------- /img/card2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/card2.png -------------------------------------------------------------------------------- /img/card3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/card3.png -------------------------------------------------------------------------------- /img/logo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/logo1.png -------------------------------------------------------------------------------- /img/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/logo2.png -------------------------------------------------------------------------------- /img/logo3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/logo3.png -------------------------------------------------------------------------------- /img/광고_미가.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/광고_미가.png -------------------------------------------------------------------------------- /img/광고_세미콘.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/광고_세미콘.png -------------------------------------------------------------------------------- /img/diagram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/diagram.jpg -------------------------------------------------------------------------------- /img/meal_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/meal_map.png -------------------------------------------------------------------------------- /Persona/도움말_임시.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/Persona/도움말_임시.png -------------------------------------------------------------------------------- /img/card_covid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/card_covid.png -------------------------------------------------------------------------------- /img/card_food.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/card_food.png -------------------------------------------------------------------------------- /img/card_miga.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/card_miga.png -------------------------------------------------------------------------------- /img/card_other.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/card_other.png -------------------------------------------------------------------------------- /img/card_wells.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/card_wells.png -------------------------------------------------------------------------------- /img/산돌이레포짓토리QR.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/산돌이레포짓토리QR.jpg -------------------------------------------------------------------------------- /img/산돌이친구추가QR.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/산돌이친구추가QR.jpeg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.idea 2 | **/__pycache__ 3 | .DS_Store 4 | ./venv 5 | ./alphaSandol/test_constant.py 6 | -------------------------------------------------------------------------------- /img/logo_profile1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/logo_profile1.png -------------------------------------------------------------------------------- /img/logo_profile2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/logo_profile2.png -------------------------------------------------------------------------------- /img/logo_profile3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/img/logo_profile3.png -------------------------------------------------------------------------------- /Persona/Persona_logic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/Persona/Persona_logic.png -------------------------------------------------------------------------------- /test_stored_data/feedback.txt: -------------------------------------------------------------------------------- 1 | [2021-11-10 12:11:28.482798] : BYE!! 2 | [2021-11-10 12:11:53.859025] : BYE!! 3 | -------------------------------------------------------------------------------- /return_type_img/Text Type.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/return_type_img/Text Type.JPG -------------------------------------------------------------------------------- /return_type_img/set_img.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/return_type_img/set_img.PNG -------------------------------------------------------------------------------- /commerce_img/commerce_test1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/commerce_img/commerce_test1.png -------------------------------------------------------------------------------- /commerce_img/commerce_test2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/commerce_img/commerce_test2.png -------------------------------------------------------------------------------- /commerce_img/commerce_test3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/commerce_img/commerce_test3.png -------------------------------------------------------------------------------- /commerce_img/commerce_test4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/commerce_img/commerce_test4.png -------------------------------------------------------------------------------- /commerce_img/commerce_test5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/commerce_img/commerce_test5.png -------------------------------------------------------------------------------- /commerce_img/commerce_test6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/commerce_img/commerce_test6.png -------------------------------------------------------------------------------- /return_type_img/Carousel Type.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/return_type_img/Carousel Type.JPG -------------------------------------------------------------------------------- /return_type_img/List Card Type.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/return_type_img/List Card Type.JPG -------------------------------------------------------------------------------- /return_type_img/Basic Card Field.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/return_type_img/Basic Card Field.JPG -------------------------------------------------------------------------------- /return_type_img/Basic Card Test.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/return_type_img/Basic Card Test.JPG -------------------------------------------------------------------------------- /return_type_img/Basic Card Type.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/return_type_img/Basic Card Type.JPG -------------------------------------------------------------------------------- /return_type_img/Simple Text Test.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/return_type_img/Simple Text Test.JPG -------------------------------------------------------------------------------- /return_type_img/Commerce Card Field.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/return_type_img/Commerce Card Field.JPG -------------------------------------------------------------------------------- /return_type_img/Commerce Card Type.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/HEAD/return_type_img/Commerce Card Type.JPG -------------------------------------------------------------------------------- /test_stored_data/restaurant_menu.txt: -------------------------------------------------------------------------------- 1 | 🐾미가식당 2 | '2001-09-03', 'ㅁ ㅁ ㅁ ㅁ', 'ㄹ ㄹ ㄹ ㄹ' 3 | 🐾세미콘식당 4 | '2021-09-03', '업데이트되지않았습니다', '업데이트되지않았습니다' 5 | 🐾푸드라운지 6 | '2021-09-03', '업데이트되지않았습니다', '업데이트되지않았습니다' 7 | 🐾웰스프레쉬 8 | '2021-09-03', '업데이트되지않았습니다', '업데이트되지않았습니다' 9 | -------------------------------------------------------------------------------- /alphaSandol/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | requests = "*" 8 | bs4 = "*" 9 | boto3 = "*" 10 | zappa = { git = "https://github.com/zappa/Zappa.git"} 11 | 12 | [dev-packages] 13 | 14 | [requires] 15 | python_version = "3.8" 16 | -------------------------------------------------------------------------------- /betaSandol/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | requests = "*" 8 | bs4 = "*" 9 | boto3 = "*" 10 | troposphere = { version = "<3.0", ignore = ["zappa"] } 11 | zappa = "*" 12 | 13 | [dev-packages] 14 | 15 | [requires] 16 | python_version = "3.8" 17 | -------------------------------------------------------------------------------- /betaSandol/zappa_settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dev": { 3 | "profile_name": null, 4 | "project_name": "betasandol", 5 | "runtime": "python3.8", 6 | "s3_bucket": "zappa-m9m800toh", 7 | "aws_region": "ap-northeast-2", 8 | "keep_warm": false, 9 | "lambda_handler": "lambda_prototype.lambda_handler" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /alphaSandol/zappa_settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": { 3 | "profile_name": null, 4 | "project_name": "Kakao-Sandol", 5 | "runtime": "python3.8", 6 | "s3_bucket": "aws-sandol-bucket", 7 | "aws_region": "ap-northeast-2", 8 | "keep_warm": false, 9 | "lambda_handler": "lambda_function.lambda_handler" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: 아프지마 산돌. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Describe the bug 11 | 어떤 버그인지 간략하게 설명해주세요. 12 | 13 | ### To Reproduce 14 | 어떤 식으로 발생했는지 간략하게 적어주세요. 15 | 16 | 1. 발화블럭명 17 | 2. 발화내용 18 | 3. ... 19 | 4. See error 20 | 21 | ### Expected behavior 22 | 예상 결과 23 | 24 | ### Environment 25 | - [x] Mobile 26 | - [ ] PC 27 | - [ ] Kakao i openbuilder 28 | 29 | ### Additional context 30 | 추가적 사항을 적어주세요. 31 | -------------------------------------------------------------------------------- /alphaSandol/Imoge.json: -------------------------------------------------------------------------------- 1 | { 2 | "emotion": { 3 | "paw": "🐾", 4 | "smile": "😺", 5 | "happy": "😸", 6 | "sad": "😹", 7 | "love": "😻", 8 | "confident": "😼", 9 | "angry": "😾", 10 | "surprise": "🙀", 11 | "walk": "🐈", 12 | "nexpression": "🐱" 13 | }, 14 | "weather": { 15 | "흐림": "☁", 16 | "구름많음": "⛅", 17 | "hvy_rain": "⛈", 18 | "비": "☔", 19 | "약간흐림": "🌤", 20 | "맑음": "☀", 21 | "sun_wth_rain": "🌦", 22 | "thunder": "🌩", 23 | "바람": "🌪", 24 | "안개": "🌫" 25 | } 26 | } -------------------------------------------------------------------------------- /.github/workflows/cd.yml: -------------------------------------------------------------------------------- 1 | name: Deploy on AWS Lambda 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - main 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout Code 14 | uses: actions/checkout@v1 15 | 16 | - name: Set up Python 3.8 17 | uses: actions/setup-python@v1 18 | with: 19 | python-version: 3.8 20 | 21 | - name: Install Dependencies and Deploy 22 | env: 23 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 24 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 25 | run: cd alphaSandol && pip install pipenv && pipenv install && pipenv run zappa update 26 | -------------------------------------------------------------------------------- /alphaSandol/weather.py: -------------------------------------------------------------------------------- 1 | import settings 2 | from bs4 import BeautifulSoup 3 | import requests 4 | import datetime 5 | 6 | 7 | class Weather: 8 | def __init__(self): 9 | self.URL = r"https://weather.naver.com/today/02390132" 10 | 11 | def weather(self, location: str = "정왕") -> dict: 12 | soup = BeautifulSoup(requests.get(self.URL).text, 'html.parser') 13 | weather_box = soup.select('#content > div > div.section_center > div.card.card_today > div.today_weather > div.weather_area')[0] 14 | today = weather_box.find('p', {'class': 'summary'}).text.replace('\n', ' ').strip().split(' ') # 날씨, 어제와 비교 15 | today_date = weather_box.find('i', {'class': 'ico_animation _cnLazy'})['data-ymdt'] 16 | dates = datetime.datetime.strptime(today_date, '%Y%m%d%H') 17 | result = f"{dates}기준 정왕 날씨입니다\n현재날씨는 {today[0]}이고, {today[1]}" 18 | return settings.GEN.set_text(result) 19 | 20 | 21 | if __name__ == "__main__": 22 | print(Weather().weather()) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 min HEO 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /alphaSandol/test_constant.py: -------------------------------------------------------------------------------- 1 | import json 2 | PARAM = "perm_chk" 3 | PARAM_DATA = '공지' 4 | 5 | JSON_DATA = { 6 | 'body': { 7 | "intent": { 8 | "id": "ttc0v82yotpkittnmfpq9lcy", 9 | "name": "블록 이름" 10 | }, 11 | "userRequest": { 12 | "timezone": "Asia/Seoul", 13 | "params": { 14 | "ignoreMe": "true" 15 | }, 16 | "block": { 17 | "id": "ttc0v82yotpkittnmfpq9lcy", 18 | "name": "블록 이름" 19 | }, 20 | "utterance": "발화 내용", 21 | "lang": 'null', 22 | "user": { 23 | "id": "616332", 24 | "type": "accountId", 25 | "properties": {} 26 | } 27 | }, 28 | "bot": { 29 | "id": "5e0f180affa74800014bd33d", 30 | "name": "봇 이름" 31 | }, 32 | "action": { 33 | "name": "o8fhv36eao", 34 | "clientExtra": 'null', 35 | "params": { 36 | PARAM : PARAM_DATA 37 | }, 38 | "id": "koow1xlhycjsjacfcqy49w6w", 39 | "detailParams": { 40 | "weather": { 41 | "origin": "정왕 날씨 궁금해", 42 | "value": "정왕 날씨 궁금해", 43 | "groupName": "" 44 | } 45 | } 46 | } 47 | } 48 | } 49 | TEST_EVENT = json.dumps(JSON_DATA) -------------------------------------------------------------------------------- /alphaSandol/covid.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import settings 4 | 5 | 6 | class Covid: 7 | def __init__(self): 8 | self.return_string = "" 9 | 10 | def today_covid(self) -> dict: 11 | try: 12 | url = 'https://m.search.naver.com/p/csearch/content/nqapirender.nhn?where=nexearch&pkid=9005&key=diffV2API' 13 | html = requests.get(url).text 14 | data = json.loads(html) 15 | result = f"{data['result']['baseDate'].split(' ')[0][:-1]}일까지 코로나 발생현황이에요 {settings.IMOGE('emotion', 'walk')}\n" \ 16 | f"{settings.IMOGE('emotion', 'paw')}지역발생은 {data['result']['data']['dailyCnt'][-1]}명\n이며," \ 17 | f"전일대비 {int(data['result']['data']['dailyCnt'][-1].replace(',','')) - int(data['result']['data']['dailyCnt'][-2].replace(',',''))}명 이에요" \ 18 | f" 건강에 유의하세요!{settings.IMOGE('emotion', 'nexpression')}" 19 | # 결과 컨텍스트 20 | 21 | self.return_string = settings.GEN.set_card(settings.SANDOL_COVID_IMG, is_title="코로나 확진자 수", 22 | is_description=result) 23 | 24 | except Exception as e: 25 | description = f"코로나 확진자 정보를 불러오는데 실패했어요{settings.IMOGE('emotion', 'sad')}\n" 26 | self.return_string = settings.GEN.set_card(settings.SANDOL_COVID_IMG, is_title=f"{e}", 27 | is_description=description) 28 | 29 | finally: 30 | return self.return_string 31 | 32 | 33 | if __name__ == "__main__": 34 | print(Covid().today_covid()) -------------------------------------------------------------------------------- /alphaSandol/announcement.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from bs4 import BeautifulSoup 3 | import settings 4 | 5 | 6 | class Announcement: 7 | def __init__(self): 8 | self.URL = "https://www.kpu.ac.kr/front/boardlist.do?bbsConfigFK=1&siteGubun=14&menuGubun=1" 9 | self.ORIGIN = "https://www.kpu.ac.kr" 10 | self.TITLE = "교내 최신 학사공지 내역입니다" 11 | self.MAX_ANNOUNCEMENT_CNT = 5 # 최대 가져올 공지 수 12 | self.WEB_LINK_URL = "https://www.kpu.ac.kr/contents/main/cor/noticehaksa.html" 13 | 14 | def announce(self) -> dict: 15 | try: 16 | req = requests.get(self.URL) 17 | soup = BeautifulSoup(req.text, 'html.parser') 18 | announce_list = soup.find('table').find('tbody').find_all('tr') 19 | result = [] # title, date, URl 20 | 21 | for i in range(self.MAX_ANNOUNCEMENT_CNT): 22 | result.append(settings.ParamOption('detail_list', 23 | title=announce_list[i].find_all("td")[1].find('a').text.strip(), 24 | description=announce_list[i].find_all("td")[4].text.strip(), 25 | link=self.ORIGIN + announce_list[i].find_all("td")[1].find("a")['href'] 26 | ) 27 | ) 28 | 29 | button = [settings.ParamOption('button', label='바로가기', action='webLink', webLinkUrl=self.WEB_LINK_URL)] 30 | return settings.GEN.set_list(self.TITLE, button, *result) 31 | 32 | except Exception as e: 33 | return settings.GEN.set_text(f"{e}") 34 | 35 | 36 | if __name__ == "__main__": 37 | print(Announcement().announce()) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 한국공학대학교 카카오톡 챗봇 산돌이 2 | ## Index 3 | - [Overview](#overview) 4 | - [Getting Started](#getting-started) 5 | - [Authors](#authors) 6 | - [Honors](#honors) 7 | - [License](#license) 8 | - [Others](#others) 9 | 10 | ## About Repository 11 | 12 | 카카오톡 챗봇 산돌이는 학생들을 위한 편의를 제공하고자 개발되었으며, 누적 5500명의 학생이 산돌이를 이용하고 있습니다. 13 | 해당 프로젝트는 카카오 i 오픈빌더를 통해 제작되었습니다. AWS Lambda를 이용한 서버리스 아키텍쳐를 활용하고 있으며, CI/CD 배포 파이프라인을 구축하기 위해 해당 레포지토리가 생성되었습니다. 14 | 15 | 16 | 17 | 18 | 19 | ## Overview 20 | 21 | **산돌이 제공 기능** 22 | - 금일 학식/외부식당 메뉴 조회 23 | - 셔틀버스 시간표 조회 24 | - 인근 전철/버스 막차시간 조회 25 | - 지역별 날씨 조회 26 | - 교내 시설 조회 27 | - 교내 유선번호 조회 28 | - 산기대학로 먹거리/즐길거리 추천 29 | - 최신 교내공지 요약 30 | - 건의 및 후원 31 | 32 | ## Getting Started 33 | **[여기](https://pf.kakao.com/_pRxlZxb)를 클릭하세요. 바로 산돌이와 대화할 수 있습니다.** 34 | 35 | ## Installing 36 | 1. https://pf.kakao.com/_pRxlZxb 링크 접속 37 | 2. 우측 상단 플러스 친구 등록하기 38 | 3. 챗봇 채팅하기 누르기 39 | 40 | ## Authors 41 | - [koptimizer](https://github.com/koptimizer) - **고광종** - 42 | 산돌팀21 총괄 및 경영 43 | - [Cycrypto](https://github.com/Cycrypto) - **박준하** - 44 | 산돌이 스킬 및 로직파트 메인개발 45 | - [hhhminme](https://github.com/hhhminme) - **허민** - 46 | 경영지원 및 프론트엔드 개발 47 | 48 | ## Honors 49 | ### "우리 대학의 빛나는 주역들 (본교 학보 509호 5면)" 50 | 51 | 52 | ## License 53 | 54 | ``` 55 | MIT License 56 | 57 | Copyright (c) 2020 always0ne 58 | 59 | Permission is hereby granted, free of charge, to any person obtaining a copy 60 | of this software and associated documentation files (the "Software"), to deal 61 | in the Software without restriction, including without limitation the rights 62 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 63 | copies of the Software, and to permit persons to whom the Software is 64 | furnished to do so, subject to the following conditions: 65 | 66 | The above copyright notice and this permission notice shall be included in all 67 | copies or substantial portions of the Software. 68 | 69 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 70 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 71 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 72 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 73 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 74 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 75 | SOFTWARE. 76 | ``` 77 | 78 | ## Others 79 | - [산돌팀 체제 이전 초기 레포 바로가기](https://github.com/koptimizer/kakaotalk_chatbot_sandol) 80 | -------------------------------------------------------------------------------- /Persona/snadol_Persona.drawio: -------------------------------------------------------------------------------- 1 | 7VzbkqM2EP0aqpKHuABxkR7B9mxSlWxtMpXK7lOKAdkmi40L48w4Xx9JSAYk2WOPjWEu3ocFIQmmW336dLfAAOPl06ciWi9+yxOcGbaZPBlgYti25UOT/EdbdlWL5zlVw7xIE96pbrhP/8O8kY+bb9MEb1odyzzPynTdbozz1QrHZastKor8sd1tlmftu66jOVYa7uMoU1v/SpNyUbVC16zbf8bpfCHubJn8yjISnXnDZhEl+WOjCUwNMC7yvKyOlk9jnFHhCblU4+4OXN0/WIFX5SkDvF/+mIR/e9svn1fl54dP8e9/mruffLua5t8o2/K/mD9tuRMimBf5ds274aLETzrBRw+iu6k+mLX/c8k6wfkSl8WOdOETiRG7tuYfG+IWQl00RO0g3hhxFc/3M9dSIAdcEGcIxXGeFwqRySrBdBbTAOHjIi3x/TqK6dVHYgmkbVEuyV0nFjnclEX+HY/zLC/YaPAAXcelA2dpljXaZzDGcbwfIVYcnSMns6cllRGgAw/qoyn3Iyo/qA0A2+pArqIOC2nUYZtuR+qwntcGXiUBNXZyFmfRZpNWMoyKUm1u6KWaBycKBLxQhA0ZuRoRibYCZ1GZ/tu+qU5s/A5f8pQ8zl5DXltBEI18gjr7nzTfJt8WMeZTNOFBmvXMaYls57hUpmUK3kvk5Tp3VZ1PxwYEBrSM6YQeoLGyCogplMcMb5WvsGRzvCnK0vmKrhKid0zaQ2pYKfECAb+wTJOE3kZr6W0smOWr8p4/lFi6VzRToR4VJR2tVXZklJ5GQRMDeQZEVFNEO9B51wqyoTeyVey8qZKEK5XMKLSN0DGmIVURhXIvo1p5KMjRnB798IXcbrzMH9IM/3jE81nPez7JvyURhrNY5xG9GOKHWZeOzZdpho5naGhGZyzDU90Y8URiZeZFucjn+SrKpnWrtJLrPr/m+ZqL/B9cljtOpKNtmbcVgp/S8mvj+BudauTys8kTn5md7MTJivy9X5snjVH0tB7GzsS4y9wrkQxzXcfWN+9YOaNnGZ26NM7zxIRLRLtGhzX1fpvDjhpILtWSGfpz/RGUVln1BFf1tEKGLYwIjZAguUkPgsAIIHO+DkV1egnRY9pCLjka+GDQcsc7oVBM5BOweTtwMjg8sYCqyHfNkzWM1oUX82RXjo8kfXZMjO0Pr3Gx13BP9BrWgeV78rq8zJ71MRDB09BkLC7kLC6EBmIHgWWgyVUBdubSf1qAZT/Ophvt1a9L4LWGBryuBmc/TPI8kzRPNUm3T5MEVh+a7l76/usARF/LVBmvNKaBEVoUCukBpBB5VRyczexYSzQT78FzvVsSTeD2jHe23yfe1Rj3rXHlzeKdf6HF6UmsZZkj0Pg5sO1S/duSWguqlv2uQxc5yCChi9NMxjvtCU+NXfzzpu06lDmQnSQs1me81jGgzyjvhDFdlvaHUJtwCCjiI5f2DkyWpyAHtgFd5iHIsPHbyTx4SCLArj1SE803dQnegCiw/Uo5sKg6P+sTGrsHbpjMdOWwywfSsukgOWlrar8fyckXQITZP0T0Eju9V4g4sOPm0p0HcoHC8UaWV//80wjExdBjt6BHfUxwtH9HUGVrOCxHHwE+Gnpjs1wegR8OSFPKWfg48hz1UNl4hlFL7wzBLKutQ08NevW7kDqCL1tXXVHUyzwSJ50m9UU28y6EmxKXU6makFlkMSdE2j9UTa4A3x2WqnXb/zSW3I4v9KrWxC3vWdWeOyyr9tQ9SrcgJYJf7E8Gwi9s1C+/2O/zFY7bQiPU+EHUCb+Qb+v4x/dpKI95E36BVFSi0HInqoAmpRbvrxwop8edvtPjQEcEbw8ynYPFyVWjauX2VrPTZRmFS6Z83BOZg+7sBluJi32d3SDPB1Gn5SNPAqvey0dur+Wjl5XLa9f8reWZ9X46iTaL/Urp3A5PTgqAq+QN1Wjbkbwh8kewWVzyT3La1/KTQMPeaXnAZVViEnaxzc92VTEGtJ3H31euGA/QVTpS+cfuv3AAdJuctNoK6bZ1CtkkfoYUrN+6tgAanrZ0b32w/RaVL6UBLyu9wZAe8yQHYk4Vce9KOHyoRl1nKe9mIh9A2hzoNr2MaeIBVakFVsGo8g1U6mNeC4XsKrJ070kNVNgDWN+afQhVcah6q4nIlZeb2pJ+HQK2B7CadUGsFu73Mma1veBCGb8CuFecc//acnSh05gpyaz32VG1jVkZdUJ3YCAkYqixWn0VAZfDX0OrFIymRsBoGPRYIvWdaXoAwOfo6uyKY7eFjQZVetuhaDhM7FNk3L81uSe8n7NZRGt6GG+LbBcWUfydRnTPSbEtcvXTAZpqwa2+JiDHhlo12Bo1AOiPuvqggKcJOpSUhG674QBy+C/eHXSbjYtKalw899lbFcFBd3ijRIKnC3ZYXbfGxKpm7zZ2EwHOXlDAvdz1fVqvG4t6Th56unCoyUQCSiUOS/uNfazF6l0f+oipJoQs7O/cJHp9P6N3HeiCKgJUd+J1GNcIxjxvdmmwOqDCBuhb7r4uPKo2p0LhF6DYkX4MlN5IWNO3PgBQWdwwC0u3LRCdXKi9uEB0mTl1952v+t0xv/nymHlUSYeqf3vCf4DhKyZ5d9cyvSFo+tD3UG4TJAApKgRAgoRTgwQlwwmlia60QQgA/QMfei5H2tEo9T93gxA5rT8TWXWvP7YJpv8D -------------------------------------------------------------------------------- /alphaSandol/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | # 리턴 타입 5 | from return_type_generator import ReturnType, ParamOption 6 | 7 | GEN = ReturnType() # kakao-i type json generator 8 | ParamOption = ParamOption 9 | 10 | # Settings 11 | _PATH = os.path.abspath(os.path.dirname(__file__)) # 프로젝트 절대경로 12 | DEBUG = False # True 일때 디버그 모드 작동 13 | 14 | # BUCKET 15 | BUCKET_NAME: str = 'aws-sandol-bucket' 16 | 17 | RESTAURANT_MENU: str = "restaurant_menu.txt" # 학식이 저장된 파일 이름 (Bucket) 18 | LOCAL_RESTAURANT_MENU: str = "/tmp/" + RESTAURANT_MENU # 람다 서버의 해당 디렉토리에 불러옴 19 | 20 | FEEDBACK_FILE: str = "feedback.txt" # 피드백이 저장된 파일 이름 21 | LOCAL_FEEDBACK_FILE: str = "/tmp/" + FEEDBACK_FILE # 람다 서버 tmp 디렉토리에 불러와 실행 22 | 23 | # Access Token 24 | SANDOL_ACCESS_ID: dict = {'MANAGER': "d367f2ec55f41b4207156f4b8fce5ce885b05d8c3b238cf8861c55a9012f6f5895", 25 | 'CONT1': "339b0444bfabbffa0f13508ea7c45b61675b5720234cca8f73cd7421c22de9e546", 26 | 'CONT2': "04eabc8b965bf5ae6cccb122a18521969cc391162e3fd5f61b85efe8bb12e5e98a", 27 | 'CONT3': "def99464e022b38389697fe68d54bbba723d1da291094c19bbf5eaace7b059a997", 28 | 'MINJU': "1ddffbb02b29a6beb212d2f6fe2469523b9a90f260344b9d30677abcd977e1b56c", 29 | 'YURIM': "16b3777b75026553a1807d1f361773e080e6441d30c65ea6e89dd0b84c9b58f071", 30 | 'HAMIN': "c0657ad2b0ade045e546d8abb33f45d85f3c826ce797800e0bf25aac0652bf175c", 31 | 'JDONG' : "cbdb6ec7c1427fd603a9c87ee5a1f7d1cc948ca896a2d65f88c770aa742218cef0" 32 | } 33 | # 산돌팀만 접근할 수 있는 컨텐츠에 인증 수단으로 사용 (현재 아이디의 정확한 위치가 기억이 나지 않아.. KEY를 메니저와, CONTRIBUTOR로 명명함.) 34 | 35 | RESTAURANT_ACCESS_ID: dict = {'미가식당': "32d8a05a91242ffb4c64b5630ec55953121dffd83a121d985e26e06e2c457197e6", 36 | '세미콘식당': "380da59920b84d81eb8c444e684a53290021b38f544fe0029f4b38ab347bc44e08", 37 | '푸드라운지': "46f338132e6af63c32c07220c318f0e7c570e8eb6f375c9e8bb59ce33776f27c4c", 38 | '웰스프레쉬': "...", 39 | } 40 | 41 | # Urls 42 | SANDOL_CATEGORY_1: str = "https://github.com/hhhminme/kpu_sandol_team/blob/06916e07fe02d36d3384dfe96c8d2dc4cb300aa7/img/card1.png" # 인기 메뉴 43 | SANDOL_CATEGORY_2: str = "https://github.com/hhhminme/kpu_sandol_team/blob/06916e07fe02d36d3384dfe96c8d2dc4cb300aa7/img/card2.png" # 놀거리 44 | SANDOL_CATEGORY_3: str = "https://github.com/hhhminme/kpu_sandol_team/blob/06916e07fe02d36d3384dfe96c8d2dc4cb300aa7/img/card3.png" # 교내 정보 45 | SANDOL_CATEGORY_4: str = "https://github.com/hhhminme/kpu_sandol_team/blob/main/img/card_other.png" # 기타 기능 46 | SANDOL_COVID_IMG: str = "https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/main/img/card_covid.png" # 코로나 47 | SANDOL_RSTRNT_FOOD_IMG: str = "https://github.com/hhhminme/kpu_sandol_team/blob/main/img/card_food.png" # 푸드라운지 48 | SANDOL_RSTRNT_MIGA_IMG: str = "https://github.com/hhhminme/kpu_sandol_team/blob/main/img/card_miga.png" # 미가식당 49 | SANDOL_RSTRNT_MAP: str = "https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/main/img/meal_map.png" # 식당지도 50 | SANDOL_LOGO1: str = "https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/main/img/logo1.png" # 산돌이 로고 (필요시 사용) 51 | SANDOL_PROFILE1: str = "https://github.com/hhhminme/kpu_sandol_team/blob/main/img/logo_profile1.png" # 산돌이 프로필 (필요시 사용) 52 | 53 | 54 | # Others 55 | def IMOGE(category, key): # 이모지를 불러올 56 | with open(rf"{_PATH}/Imoge.json", encoding='UTF-8') as imo: 57 | data = json.load(imo) 58 | return data[category][key] 59 | 60 | 61 | def DEBUGGING(msgtype, sender, msg): # 디버깅시 사용할 함수 62 | if DEBUG is False: 63 | pass 64 | 65 | else: 66 | if msgtype == 'debug': 67 | return f"from {sender} : {msg}" 68 | 69 | elif msgtype == 'error': 70 | return f"error from {sender} : {msg}" 71 | 72 | else: 73 | return DEBUGGING("error", "init.DEBUGGING", "호출형식이 잘못되었습니다") 74 | -------------------------------------------------------------------------------- /alphaSandol/feedback.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import settings 3 | import datetime 4 | 5 | 6 | 7 | class Feedback: 8 | def __init__(self): 9 | if not settings.DEBUG: # 디버그모드가 아닌경우 S3를 import함 10 | self.S3 = boto3.resource('s3') 11 | self.S3_client = boto3.client('s3') 12 | self.bucket = self.S3.Bucket(settings.BUCKET_NAME) 13 | 14 | self.data = "" 15 | 16 | def upload_feedback(self, data): 17 | self.data = f"[{str(datetime.datetime.today())}] : {data}\n" 18 | if not settings.DEBUG: 19 | try: 20 | self.bucket.download_file(settings.FEEDBACK_FILE, settings.LOCAL_FEEDBACK_FILE) 21 | 22 | except Exception as e: 23 | return settings.GEN.set_text( 24 | f"[File-Open-Error #101] 서버에서 피드백 파일을 불러오는 중 오류가 발생했어요{settings.IMOGE('emotion', 'sad')}\n{e}") 25 | 26 | try: 27 | with open(settings.LOCAL_FEEDBACK_FILE, "a", encoding="UTF-8") as f: 28 | f.writelines(self.data) 29 | 30 | except Exception as e: 31 | return settings.GEN.set_text( 32 | f"[File-Open-Error #102] 파일을 저장 중 오류가 발생했습니다{settings.IMOGE('emotion', 'sad')}\n{e}") 33 | 34 | try: 35 | self.S3_client.upload_file(settings.LOCAL_FEEDBACK_FILE, settings.BUCKET_NAME, settings.FEEDBACK_FILE) 36 | 37 | except Exception as e: 38 | return settings.GEN.set_text( 39 | f"[File-Open-Error #103] 파일을 서버에 업로드 하는 중 오류가 발생했습니다{settings.IMOGE('emotion', 'sad')}\n{e}") 40 | 41 | else: # 디버그 모드일때 발생하는 분기 42 | with open("../test_stored_data/feedback.txt", "a", encoding='UTF-8') as f: 43 | f.writelines(self.data) 44 | 45 | return settings.GEN.set_text(f"피드백 주셔서 감사해요! 빠른 시일내에 검토 후 적용해볼게요!{settings.IMOGE('emotion','love')}") 46 | 47 | def read_feedback(self, uid): 48 | if uid not in (settings.SANDOL_ACCESS_ID.values()): 49 | return settings.GEN.set_text("권한이 없습니다") 50 | 51 | if not settings.DEBUG: 52 | try: 53 | self.bucket.download_file(settings.FEEDBACK_FILE, settings.LOCAL_FEEDBACK_FILE) 54 | 55 | except Exception as e: 56 | settings.GEN.set_text(f"[File-Open-Error #111] 서버에서 피드백 파일을 불러오는 중 오류가 발생했어요\n{e}") 57 | 58 | try: 59 | with open(settings.LOCAL_FEEDBACK_FILE, 'r', encoding='UTF-8') as f: 60 | txt = ''.join(f.readlines()) 61 | 62 | except Exception as e: 63 | return settings.GEN.set_text(f"[File-Open-Error #112] 파일을 읽는 중 오류가 발생했습니다\n{e}") 64 | 65 | return settings.GEN.set_text(txt) 66 | 67 | else: # 디버그 모드일때 발생하는 분기 68 | with open("../test_stored_data/feedback.txt", encoding='UTF-8') as f: 69 | txt = ''.join(f.readlines()) 70 | return settings.GEN.set_text(txt) 71 | 72 | def delete_feedback(self, uid): 73 | if uid not in (settings.SANDOL_ACCESS_ID.values()): 74 | return settings.GEN.set_text("권한이 없습니다") 75 | 76 | basic_text = "#feedbacks\n" 77 | if not settings.DEBUG: 78 | try: 79 | self.bucket.download_file(settings.FEEDBACK_FILE, settings.LOCAL_FEEDBACK_FILE) 80 | except Exception as e: 81 | return settings.GEN.set_text(f"[File-Open-Error #113] 서버에서 피드백 파일을 불러오는 중 오류가 발생했어요\n{e}") 82 | 83 | try: 84 | with open(settings.LOCAL_FEEDBACK_FILE, 'w', encoding="UTF-8") as f: 85 | f.writelines(basic_text) 86 | 87 | except Exception as e: 88 | return settings.GEN.set_text(f"[File-Open-Error #114] 파일 데이터를 삭제 중 오류가 발생했습니다{e}") 89 | 90 | try: 91 | s3 = boto3.client('s3') 92 | s3.upload_file(settings.LOCAL_FEEDBACK_FILE, settings.BUCKET_NAME, settings.FEEDBACK_FILE) 93 | 94 | except Exception as e: 95 | return settings.GEN.set_text(f"[File-Open-Error #115] 파일을 서버에 업로드 하는 중 오류가 발생했습니다{e}") 96 | 97 | else: # 디버그 모드일때 98 | with open("../test_stored_data/feedback.txt", "w", encoding='UTF-8') as f: 99 | f.writelines(basic_text) 100 | 101 | return settings.GEN.set_text("성공적으로 파일 내용을 삭제했습니다") 102 | -------------------------------------------------------------------------------- /betaSandol/lambda_prototype.py: -------------------------------------------------------------------------------- 1 | import json 2 | import lambda_prototype_module as Module 3 | import return_type_generator as Generator 4 | import base64 5 | 6 | def lambda_handler(event, context): 7 | return_string = None 8 | try: 9 | request_body = event['body'] 10 | request_body = json.loads(base64.b64decode(request_body)) 11 | param = request_body['action']['params'] 12 | key = list(param.keys()) # 입력으로 들어오는 값을 여기서 처리함 13 | # 여러개 들어오는 경우 필수 파라미터 명이 key[0]에 들어감 14 | if key[0] == 'weather': #날씨 관련 15 | return_string = Module.CrawlingFunction.weather(Module.CrawlingFunction, param[key[0]]) 16 | 17 | elif key[0] == 'covid': 18 | return_string = Module.CrawlingFunction.today_covid(Module.CrawlingFunction) 19 | 20 | elif key[0] == 'feedback_upload': 21 | return_string = Module.s3IOEvent.upload_feedback(Module.CrawlingFunction, params=str(param[key[0]])) 22 | 23 | elif key[0] == 'read_feedback': 24 | return_string = Module.s3IOEvent.read_feedback(Module.CrawlingFunction, params=str(param[key[0]]), bot_id=str(request_body['userRequest']['user']['properties']['botUserKey'])) 25 | 26 | elif key[0] == 'perm_chk': 27 | return_string = request_body['userRequest']['user']['id'] 28 | 29 | elif key[0] == 'store_name': 30 | return_string = Module.s3IOEvent.upload_meal(Module.s3IOEvent, param[key[0]],param[key[1]], param[key[2]],json.loads( param[key[3]])['date'], str(request_body['userRequest']['user']['properties']['botUserKey'])) 31 | 32 | elif key[0] == 'read_meal': 33 | return_string = Module.s3IOEvent.read_meal(Module.s3IOEvent) 34 | 35 | elif key[0] == "reset_meal": 36 | return_string = Module.s3IOEvent.reset_meal(Module.s3IOEvent, str(request_body['userRequest']['user']['properties']['botUserKey']), json.loads(param[key[0]])['date']) 37 | 38 | elif key[0] == 'subway': 39 | gen = Generator.Return_Type() 40 | try: 41 | setting_time = str(json.loads(request_body['action']['detailParams']['date_time']['value'])['time']) 42 | return_string = "[4호선]\n" + Module.Test(time = setting_time).arrival_time() + "\n\n[수인분당선]\n" 43 | return_string += Module.Test(time = setting_time, station_no="11120").arrival_time() 44 | return_string = gen.set_text(return_string) 45 | 46 | except Exception as e: 47 | return_string = gen.set_text(str(e)) 48 | 49 | elif key[0] == "ann": 50 | # return_string = Module.CrawlingFunction.subway(Module.CrawlingFunction) 51 | return_string = Module.CrawlingFunction.announcement(Module.CrawlingFunction) 52 | 53 | elif key[0] == 'last_subway': 54 | return_string = Module.CrawlingFunction.last_subway(Module.CrawlingFunction) 55 | 56 | elif key[0] == "param1": 57 | g = Generator.Return_Type() 58 | try: 59 | for i in range (1,3): 60 | g.set_text("str"+str(i), is_init=False) 61 | return_string = g.set_text("str3", is_init=False) 62 | except Exception as e: 63 | return_string = str(e) 64 | 65 | 66 | 67 | 68 | else: 69 | raise Exception("산돌이가 작업을 마무리하지 못했어요ㅠㅠ\n 피드백을 통해 어떤 기능에서 오류가 발생했는지 알려주시면 빠른 시일 내에 작동 하도록 할게요") 70 | 71 | 72 | except Exception as e: 73 | return_string = { 74 | "version": "2.0", 75 | "template": { 76 | "outputs": [ 77 | { 78 | "simpleText": { 79 | "text": "[Main Function Error]"+str(e) 80 | } 81 | } 82 | ], 83 | "quickReplies" : [ 84 | { 85 | "messageText" : "도움말", 86 | "action" : "message", 87 | "label" : "도움말" 88 | } 89 | ] 90 | } 91 | } 92 | 93 | return { 94 | 'statusCode': 200, 95 | 'body': json.dumps(return_string), 96 | 'headers': { 97 | 'Access-Control-Allow-Origin': '*', 98 | } 99 | } 100 | 101 | -------------------------------------------------------------------------------- /Return Type Generator.md: -------------------------------------------------------------------------------- 1 | # return_type_generator.py 2 | 3 | ## OverView 4 | 5 | * 카카오 오픈빌더의 각 타입에 들어가는 인자 값을 함수의 파라미터로 받아 자동으로 리턴 값을 생성해주는 Python 코드 6 | * 총 6종류의 JSON 형식을 지원함 7 | * Simple Text : 간단한 텍스트 타입 8 | * Simple Image : 간단한 이미지 타입 9 | * Basic Card : 기본 카드형 타입 10 | * Commerce Card : 제품 소개등을 할 때 사용하는 카드형 타입 11 | * List Card : 표현하고자 하는 대상이 다수일 때 효과적으로 사용하는 카드형 타입 12 | * Carousel : 여러 장의 카드를 하나의 메시지에 일렬로 포함하는 타입 13 | 14 | 15 | 16 | ## Usage of Basic Type 17 | 18 | ### Simple Text 19 | 20 | 21 | 22 | 23 | 24 | ```Python 25 | def is_Text(self, text): 26 | ``` 27 | 28 | **필수 파라미터** 29 | 30 | > text\ : 출력할 string을 입력으로 받음 31 | 32 | 33 | 34 | **사용 예시** 35 | 36 | ```python 37 | import return_type_generator as Generator 38 | gen = Generator() 39 | result = gen.is_Text("<날씨 스킬 파트 실행>") 40 | ``` 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | ### Basic Card 49 | 50 | 51 | 52 | 53 | 54 | ```python 55 | def is_Card(self,thumb_img, Button(), is_title = None, is_description = None): 56 | ``` 57 | 58 | 59 | 60 | 61 | **필수 파라미터** 62 | 63 | > thumb_img \ : img의 주소를 `string` 형태로 전달 64 | 65 | 66 | 67 | **선택 파라미터** 68 | 69 | > Button() \ : button 을 생성하는 dictionary(JSON) 형태로 전달. 70 | > 71 | > is_title \ : 카드의 제목을 문자열 형태로 전달 72 | > 73 | > is_description \ : 카드에 대한 설명을 문자열 형태로 전달 74 | 75 | 76 | 77 | **사용 예시** 78 | 79 | ```python 80 | import return_type_generator as Generator 81 | gen = Generator() 82 | result = gen.is_Card("https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/main/img/logo1.png", is_title="logo", is_description = "<코로나 스킬 파트 실행>") 83 | ``` 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | ### Commerce Card 92 | 93 | ```Python 94 | def is_commerce(self,thumbnail, description, price, currency, Button(), is_discount = None, is_discountRate = None, is_discountedPrice = None, profile = None): 95 | ``` 96 | 97 | 98 | 99 | **필수 파라미터** 100 | 101 | > description \ : 판매 항목의 설명을 문자열 형태로 전달 102 | > 103 | > price \ : 제품의 가격을 문자열 형태로 전달 104 | > 105 | > currency \ : 제품 가격의 통화를 전달합니다 (`won`만 가능) 106 | > 107 | > thumbnail \ : 썸네일 이미지 URL을 전달합니다. 108 | > 109 | > Button() : 기본적으로 `구매하기` 버튼이 생성되며, 추가 버튼을 전달합니다 (최대 2개) 110 | 111 | 112 | 113 | **선택 파라미터** 114 | 115 | > is_discount \ : 제품의 할인한 금액을 전달합니다. 116 | > 117 | > is_discountRate \ : 제품의 가격에 대한 할인된 가격의 비율을 전달합니다. 118 | > 119 | > is_discountPrice\ : 제품의 가격에 대한 할인가(할인된 가격)을 전달합니다. 120 | > 121 | > profile : 제품을 판매하는 프로필 정보를 전달합니다. 122 | 123 | 124 | ### List Card 125 | ```Python 126 | def is_List(self, title, data, is_Button = None): 127 | ``` 128 | **필수 파라미터** 129 | 130 | > title : 리스트 최 상단에 노출 될 제목 131 | > data : 리스트에 들어갈 값, [title, subtitle, link]의 형태를 가진 리스트가 들어가야함. 132 | 133 | 134 | 135 | **선택 파라미터** 136 | 137 | > is_Button : 버튼 타입 파라미터가 들어가야함. 138 | 139 | 140 | 141 | 142 | ## Usage of Parameter Type 143 | 144 | ### Button 145 | 146 | ```python 147 | def Button(self, **kwargs): 148 | ``` 149 | 150 | * **Kwargs** 에 들어갈 내용 151 | 152 | > `'label'` : 버튼에 들어가는 내용 (필수) 153 | > 154 | > `'action'` : 버튼 클릭시 수행되는 작업 (필수) 155 | > 156 | > `'webLinkUrl'` 157 | > 158 | > `'messageText'` 159 | > 160 | > `'phoneNumber'` 161 | > 162 | > `'blockId'` 163 | 164 | 165 | 166 | * **action** 종류 167 | 168 | > - `webLink`: 웹 브라우저를 열고 webLinkUrl 의 주소로 이동합니다. 169 | > - `message`: 사용자의 발화로 messageText를 실행합니다. (바로가기 응답의 메세지 연결 기능과 동일) 170 | > - `phone`: phoneNumber에 있는 번호로 전화를 겁니다. `ex) 010-0000-1234` 171 | > - `block`: blockId를 갖는 블록을 호출합니다. (바로가기 응답의 블록 연결 기능과 동일) 172 | > - messageText가 있다면, 해당 messageText가 사용자의 발화로 나가게 됩니다. 173 | > - messageText가 없다면, button의 label이 사용자의 발화로 나가게 됩니다. 174 | > 175 | > - `share`: 말풍선을 다른 유저에게 공유합니다. share action은 특히 케로셀을 공유해야 하는 경우 유용합니다. 176 | 177 | 178 | 179 | **사용 예시** 180 | 181 | ```python 182 | try: 183 | a = gen.is_Card("https://avatars.githubusercontent.com/u/25563122?v=4", 184 | opt.button(label="Test", action="webLink", webLinkUrl="https://github.com/Cycrypto"), 185 | is_description="Button Test2") 186 | except Exception as e: 187 | a = gen.is_Card("https://avatars.githubusercontent.com/u/25563122?v=4", is_description=str(e)) 188 | 189 | return a 190 | ``` 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /alphaSandol/test_block.py: -------------------------------------------------------------------------------- 1 | from return_type_generator import (ReturnType, ParamOption) 2 | 3 | 4 | def text_test(): # set_text 예시 5 | Gen = ReturnType() 6 | text_list = ["안녕!", "내 이름은 산돌이!", "만나서 반가워"] 7 | for txt in text_list: 8 | Gen.set_text(txt, is_init=True) 9 | return Gen 10 | 11 | 12 | def commerce_test(): # set_commerce 예시 13 | import random 14 | # 1~5 산돌 분식, 6 산돌 카페 15 | commerce_image = [ 16 | f'https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/main/commerce_img/commerce_test{x}.png' 17 | for x in range(1, 7)] 18 | choice = random.choice(commerce_image) 19 | 20 | Gen = ReturnType() 21 | 22 | return Gen.set_commerce( 23 | ParamOption('thumbnail', img_url=choice, link=ParamOption("link", url="https://naver.me/FMA7h2K7")), 24 | # thumbnail 25 | "산돌분식을 방문하여 해당 광고를 보여주시면 해당 메뉴의 할인이 적용됩니다.", # description 26 | 4000, # price 27 | ParamOption('button', label="네이버 플레이스 연결", action="webLink", webLinkUrl="https://naver.me/FMA7h2K7"), 28 | ParamOption('button', label="네이버 플레이스 연결", action="webLink", webLinkUrl="https://naver.me/FMA7h2K7"), 29 | ParamOption('button', label="전화하기", action="phone", webLinkUrl="010-4183-2998"), 30 | ParamOption('button', label="공유하기", action="share"), # buttons 31 | is_discount=500, # discount 32 | profile=ParamOption('profile', imageUrl=choice, nickname="산돌분식"), # profile 33 | ) 34 | 35 | 36 | def card_test(): # set_card 37 | Gen = ReturnType() 38 | THUMBNAIL = r"https://github.com/teamSANDOL/kpu_sandol_team/blob/main/return_type_img/Basic%20Card%20Test.JPG?raw=true" 39 | buttons = [ 40 | ParamOption('button', label='label1'), 41 | ParamOption('button', label='label2'), 42 | ParamOption('button', label='label3'), 43 | ] 44 | return Gen.set_card(THUMBNAIL, *buttons, is_title="테스트!", is_description="산돌이 카드 테스트") 45 | 46 | 47 | def image_test(): # set_image 48 | Gen = ReturnType() 49 | URL = r"https://github.com/hhhminme/kpu_sandol_team/blob/main/img/logo1.png?raw=true" 50 | return Gen.set_image(URL, text="산돌이 로고 이미지입니다!") 51 | 52 | 53 | def carousel_test(): 54 | Gen = ReturnType() 55 | return Gen.set_carousel("basicCard", 56 | Gen.set_card("THUMB1", "THUMB_IMG", 57 | ParamOption('button', label="button1", action="messageText"), 58 | ParamOption('button', label="button2", action="messageText"), 59 | ParamOption('button', label="button3", action="messageText"), 60 | is_carousel=True 61 | ), 62 | Gen.set_card("THUMB2", "THUMB_IMG", 63 | ParamOption('button', label="button1", action="messageText"), 64 | ParamOption('button', label="button2", action="messageText"), 65 | ParamOption('button', label="button3", action="messageText"), 66 | is_carousel=True 67 | ), 68 | Gen.set_card("THUMB3", "THUMB_IMG", 69 | ParamOption('button', label="button1", action="messageText"), 70 | ParamOption('button', label="button2", action="messageText"), 71 | ParamOption('button', label="button3", action="messageText"), 72 | is_carousel=True 73 | ) 74 | ) 75 | 76 | 77 | def list_test(): 78 | Gen = ReturnType() 79 | buttons = [ 80 | ParamOption('button', label='button1', webLinkUrl='https://www.naver.com') 81 | ] 82 | return Gen.set_list("Header Title", 83 | buttons, 84 | ParamOption('detail_list', title='item title1', description='item description1', 85 | imageUrl='test.jpg'), 86 | ParamOption('detail_list', title='item title2', description='item description2', 87 | imageUrl='test.jpg'), 88 | ParamOption('detail_list', title='item title3', description='item description3', 89 | imageUrl='test.jpg'), 90 | ParamOption('detail_list', title='item title4', description='item description4', 91 | imageUrl='test.jpg'), 92 | ParamOption('detail_list', title='item title5', description='item description5', 93 | imageUrl='test.jpg'), 94 | ) 95 | 96 | 97 | if __name__ == "__main__": 98 | import pprint 99 | 100 | print(list_test()) 101 | -------------------------------------------------------------------------------- /alphaSandol/main.py: -------------------------------------------------------------------------------- 1 | if __name__ == '__main__': 2 | # main 파일을 엔트리포인트로 사용할 경우, path에 폴더 경로를 추가해 절대 경로로 임포트가 가능하게 함 3 | from sys import path 4 | import os 5 | 6 | path.append(os.path.dirname(__file__)) 7 | 8 | import json 9 | import base64 10 | import settings 11 | from settings import DEBUGGING as debugging 12 | 13 | 14 | 15 | def lambda_handler(event, context): 16 | try: 17 | if settings.DEBUG: 18 | req = "None" # request body 19 | func = "feedback_upload" # 테스트할 함수 20 | params = ["BYE!!"] # 파라미터 21 | params = {func: params[0]} 22 | access_id = 'd367f2ec55f41b4207156f4b8fce5ce885b05d8c3b238cf8861c55a9012f6f5895' # 접근 ID 23 | 24 | return_json = function_handler(func, req, params, access_id) 25 | 26 | else: 27 | request_body = event['body'] 28 | request_body = json.loads(base64.b64decode(request_body)) # base64로 디코딩해야 제대로된 값을 받음 29 | 30 | param = request_body['action']['params'] # request json 접근용 31 | key = list(param.keys()) 32 | func = key[0] # 함수 호출시 사용할 값 33 | ACCESS_ID = str(request_body['userRequest']['user']['id']) # 접근 권한을 가진 ID 확인용 34 | return_json = function_handler(func, request_body, param, ACCESS_ID) 35 | 36 | 37 | except Exception as e: 38 | return_json = { 39 | "version": "2.0", 40 | "template": { 41 | "outputs": [ 42 | { 43 | "simpleText": { 44 | "text": f"{settings.DEBUGGING('debug', 'main.lambda_handler', e)}" 45 | } 46 | } 47 | ], 48 | "quickReplies": [ 49 | { 50 | "messageText": "도움말", 51 | "action": "message", 52 | "label": "도움말" 53 | } 54 | ] 55 | } 56 | } 57 | 58 | return return_json 59 | 60 | 61 | def function_handler(func, req, param, access_id): 62 | vals = list(param.values()) # 함수에 파라미터에 사용할 값 63 | return_json = '' 64 | if func == 'weather': # 날씨 관련 65 | from weather import Weather 66 | return_json = Weather().weather() 67 | 68 | elif func == "covid": # 코로나 관련 69 | from covid import Covid 70 | return_json = Covid().today_covid() 71 | 72 | elif func == "feedback_upload" or func == "read_feedback": # 피드백 관련 73 | from feedback import Feedback 74 | feedback_class = Feedback() 75 | 76 | if func == "feedback_upload": 77 | return_json = feedback_class.upload_feedback(vals[0]) 78 | 79 | elif func == "read_feedback": 80 | if str(vals[0]) == '2': # 삭제 81 | return_json = feedback_class.delete_feedback(access_id) 82 | 83 | else: # 접근 84 | return_json = feedback_class.read_feedback(access_id) 85 | 86 | 87 | elif func in ["store_name", "read_meal", "reset_meal", "time_meal", "payment_meal"]: 88 | import restaurant 89 | meal_class = restaurant.AboutMeal() 90 | 91 | if func == "store_name": # 학식 업로드 92 | upload_date = json.loads(vals[3])['date'] 93 | return_json = meal_class.upload_meal(vals[0], vals[1], vals[2], upload_date, access_id) 94 | 95 | elif func == "read_meal": # 식단 읽기 96 | return_json = meal_class.read_meal(access_id) 97 | 98 | elif func == "reset_meal": # 학식 초기화 99 | upload_date = json.loads(vals[3])['date'] 100 | return_json = meal_class.reset_meal(access_id, upload_date) 101 | 102 | elif func == "time_meal": # 영업시간 103 | return_json = restaurant.time_meal() 104 | 105 | else: # 결제 시스템 (사용에 어려울 수도 있음 => kakao pay 송금 제한) 106 | return_json = restaurant.payment_meal() 107 | 108 | elif func == "ann": # 공지사항 109 | from announcement import Announcement 110 | return_json = Announcement().announce() 111 | 112 | elif func in ["subway", "last_subway"]: 113 | import subway 114 | if func == "subway": 115 | time = json.loads(req['action']['detailParams']['date_time']['value'])['time'] 116 | return_json = subway.LiveSubwayTraffic().get_string(time) 117 | 118 | else: 119 | return_json = subway.LastTraffic().real_time_traffic() 120 | 121 | elif func == "commerce": 122 | import test_block 123 | return_json = test_block.commerce_test() # 원래 기능 124 | # return_json = test_block.image_test() 125 | # return_json = test_block.card_test() 126 | 127 | elif func == "perm_chk": 128 | from return_type_generator import ReturnType as GEN 129 | return_json = GEN().set_text(access_id) 130 | 131 | else: 132 | from return_type_generator import ReturnType as GEN 133 | GEN().set_text(text=debugging("error", "main.function_handler", "전달된 파라미터가 잘못되었습니다.")) 134 | 135 | return { 136 | 'statusCode': 200, 137 | 'body': json.dumps(return_json, ensure_ascii=False), 138 | 'headers': { 139 | 'Access-Control-Allow-Origin': '*', 140 | } 141 | } 142 | 143 | # if __name__ == "__main__": # Deploy 할때 무조건 주석처리 하기 144 | # import pprint 145 | # func_call: json = lambda_handler("event", "context") 146 | # pprint.pprint(func_call) # 디버깅용 함수 호출 147 | # print(json.loads(func_call['body'])) # json 유니코드 -> UTF 확인용 148 | -------------------------------------------------------------------------------- /alphaSandol/return_type_generator.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | type_check = lambda x, y: type(x) == y 4 | 5 | 6 | def init_json(): 7 | return { 8 | "version": "2.0", 9 | "template": { 10 | "outputs": [ 11 | ], 12 | "quickReplies": [ 13 | { 14 | "messageText": "도움말", 15 | "action": "message", 16 | "label": "도움말" 17 | } 18 | ] 19 | } 20 | } 21 | 22 | 23 | def ParamOption(param_type, **kw): 24 | def button(**kwargs): 25 | data = {} 26 | params = ['label', 'action', 'webLinkUrl', 'messageText', 'phoneNumber', 'blockId'] 27 | for item in kwargs.items(): 28 | if item[0] not in params: 29 | data.clear() 30 | break 31 | data[item[0]] = item[1] 32 | 33 | return data 34 | 35 | def link(url): 36 | return { 37 | 'web': url 38 | } 39 | 40 | def thumbnail(img_url, **kwargs): 41 | data = {'imageUrl': img_url} 42 | param = ['link'] # 'fixedRatio', 'width', 'height' 옵션은 제작하지 않음, 추후 필요한경우 제작하여 사용 43 | print(kwargs.keys()) 44 | for key in kwargs.keys(): 45 | if key in param: 46 | data[key] = kwargs[key] 47 | return data 48 | 49 | def profile(nickname, imageUrl=None): 50 | result = { 51 | 'nickname': nickname, 52 | } 53 | if imageUrl is not None: 54 | result["imageUrl"] = imageUrl 55 | 56 | return result 57 | 58 | def detail_list(**kwargs): 59 | param = ['title', 'description', 'imageUrl'] 60 | result = {} 61 | if 'link' in kwargs.keys(): 62 | result['link'] = ParamOption('link', url=kwargs['link']) # 링크가 있는 경우 63 | 64 | for key in kwargs.keys(): # 나머지 파라미터 적용 65 | if key in param: 66 | result[key] = kwargs[key] 67 | return result 68 | 69 | return locals()[param_type](**kw) 70 | 71 | 72 | class ReturnType: # 리턴 타입별 JSON 형식을 만드는 곳 입니다. 73 | def __init__(self, reply_json: dict = None): 74 | self.return_json = init_json() 75 | if reply_json is not None: 76 | self.return_json['template']['quickReplies'].append(reply_json) 77 | 78 | def set_text(self, text, is_init=True): # 텍스트 형식 79 | if is_init: 80 | self.return_json = init_json() # 이전에 들어간 텍스트가 유지될건지 여부 81 | basic_text = { 82 | "simpleText": { 83 | "text": str(text) 84 | } 85 | } 86 | self.return_json["template"]["outputs"].append(basic_text) 87 | return self.return_json 88 | 89 | def set_card(self, thumb_img, *is_buttons, is_title=None, is_description=None, is_carousel=False): # 카드 형식 90 | self.return_json = init_json() 91 | basic_card = { 92 | "thumbnail": { 93 | "imageUrl": thumb_img 94 | } 95 | } 96 | 97 | try: 98 | if is_title is not None: 99 | basic_card["title"] = is_title # 타이틀 입력 100 | 101 | if is_description is not None: 102 | basic_card["description"] = is_description # 설명 입력 103 | 104 | if 0 < len(is_buttons) <= 3: 105 | basic_card.update({"buttons": list(is_buttons)}) 106 | 107 | if is_carousel: # flag 가 True이면 Card Json만 반환하지만, False이면 return 해야하는 기본 JSON도 포함이 된다. 108 | return basic_card 109 | 110 | else: 111 | self.return_json["template"]["outputs"].append({"basicCard": basic_card}) # 위 정보들을 return_json에 입력 112 | return self.return_json 113 | 114 | except Exception as e: # 오류 발생시 오류 코드 리턴 115 | basic_card["description"] = "error :" + str(e) 116 | self.return_json["template"]["outputs"].append({"basicCard": basic_card}) 117 | return self.return_json 118 | 119 | def set_image(self, src, text=None, is_init=True): # 이미지 반환 형식 120 | if is_init: 121 | init_json() 122 | 123 | basic_image = { 124 | "simpleImage": { 125 | "imageUrl": src, 126 | } 127 | } 128 | 129 | if text is not None: 130 | basic_image["simpleImage"]["altText"] = text 131 | 132 | self.return_json["template"]["outputs"].append(basic_image) 133 | return self.return_json 134 | 135 | def set_commerce(self, thumbnail, description, price, *is_buttons, currency="won", is_discount=None, 136 | is_discountRate=None, is_discountedPrice=None, profile=None, is_carousel=False): # 커머스 반환 형식 137 | commerce_card = { 138 | "commerceCard": { 139 | "description": description, 140 | "price": price, 141 | "currecy": currency, 142 | "thumbnails": [ 143 | 144 | ] 145 | } 146 | } 147 | commerce_card["commerceCard"]["thumbnails"].append(thumbnail) 148 | 149 | if 0 < len(is_buttons) <= 3: # 버튼 관련 파라미터 150 | commerce_card["commerceCard"]["buttons"] = list(is_buttons) 151 | 152 | if is_discount is not None and \ 153 | is_discountRate is not None and \ 154 | is_discountedPrice is not None: # 할인 관련 파라미터 155 | commerce_card["commerceCard"]["discount"] = is_discount 156 | commerce_card["commerceCard"]["discountRate"] = is_discountRate 157 | commerce_card["commerceCard"]["discountedPrice"] = is_discountedPrice 158 | 159 | if profile is not None: # 프로필 관련 파라미터 160 | commerce_card["commerceCard"]["profile"] = profile 161 | 162 | if is_carousel: # 리턴 형식 확인 163 | return commerce_card 164 | 165 | else: 166 | self.return_json["template"]["outputs"].append(commerce_card) 167 | return self.return_json 168 | 169 | def set_list(self, header_title, is_button: list, *data, is_carousel=False): 170 | basic_list = { 171 | "listCard": { 172 | "header": { 173 | "title": header_title 174 | }, 175 | "items": [] 176 | } 177 | } 178 | if 0 < len(is_button) <= 2: # 버튼은 최대 2개까지 설정 가능 179 | basic_list['listCard']['buttons'] = [] 180 | for btn in is_button: 181 | basic_list['listCard']['buttons'].append(btn) 182 | 183 | for itm in data: # 값 연결 184 | basic_list['listCard']['items'].append(itm) 185 | 186 | if is_carousel: # 반환 형식 설정 187 | return basic_list 188 | 189 | else: 190 | self.return_json["template"]["outputs"].append(basic_list) 191 | return self.return_json 192 | 193 | def set_carousel(self, card_type, *items): # 케로셀 반환 형식 #(link, Title, description) 194 | init_json() 195 | basic_carousel = { 196 | "carousel": { 197 | "type": card_type, 198 | "items": [] 199 | } 200 | } 201 | 202 | for itm in list(items): 203 | basic_carousel['carousel']['items'].append(itm) 204 | 205 | self.return_json["template"]["outputs"].append(basic_carousel) 206 | return self.return_json 207 | 208 | def __str__(self): 209 | return str(self.return_json) 210 | 211 | 212 | if __name__ == "__main__": 213 | Gen = ReturnType() 214 | buttons = [ 215 | ParamOption('button', label='label1'), 216 | ParamOption('button', label='label2'), 217 | ParamOption('button', label='label3'), 218 | ParamOption('button', label='label4'), 219 | ] 220 | print(Gen.set_card("THUMB", *buttons, is_title="테스트!", is_description="산돌이 카드 테스트")) 221 | -------------------------------------------------------------------------------- /betaSandol/return_type_generator.py: -------------------------------------------------------------------------------- 1 | class Return_Type: # 리턴 타입별 JSON 형식을 만드는 곳 입니다. 2 | def __init__(self): 3 | self.return_json = { 4 | "version": "2.0", 5 | "template": { 6 | "outputs": [ 7 | ], 8 | "quickReplies": [ 9 | { 10 | "messageText": "도움말", 11 | "action": "message", 12 | "label": "도움말" 13 | } 14 | ] 15 | } 16 | } 17 | self.common_params = common_params() 18 | 19 | def init_json(self): 20 | self.return_json = { 21 | "version": "2.0", 22 | "template": { 23 | "outputs": [ 24 | ], 25 | "quickReplies": [ 26 | { 27 | "messageText": "도움말", 28 | "action": "message", 29 | "label": "도움말" 30 | } 31 | ] 32 | } 33 | } 34 | 35 | def set_text(self, text, is_init = True): #텍스트 형식 36 | if (is_init == True): 37 | self.init_json() 38 | basic_text ={ 39 | "simpleText": { 40 | "text": str(text) 41 | } 42 | } 43 | self.return_json["template"]["outputs"].append(basic_text) 44 | return self.return_json 45 | 46 | def set_card(self,thumb_img, *is_buttons, is_title = None, is_description = None, flag = False): #카드 형식 47 | self.init_json() 48 | basic_card = { 49 | "thumbnail": { 50 | "imageUrl": thumb_img 51 | } 52 | } 53 | 54 | try: 55 | if is_title != None: 56 | basic_card["title"] = is_title #타이틀 입력 57 | 58 | if is_description != None: 59 | basic_card["description"] = is_description # 설명 입력 60 | 61 | if is_buttons != (): 62 | if len(is_buttons) > 3: 63 | raise Exception("Buttons are must less then 3") # 버튼이 3개 이상이라면 오류 발생 64 | else: 65 | basic_card.update({"buttons": list(is_buttons)}) 66 | 67 | if flag == True: #flag 가 True이면 Card Json만 반환하지만, False이면 return해야하는 기본 JSON도 포함이 된다. 68 | return basic_card 69 | 70 | else: 71 | self.return_json["template"]["outputs"].append({"basicCard": basic_card}) # 위 정보들을 return_json에 입력 72 | return self.return_json 73 | 74 | except Exception as e: #오류 발생시 오류 코드 리턴 75 | basic_card["description"] = "error :" + str(e) 76 | self.return_json["template"]["outputs"].append({"basicCard": basic_card}) 77 | return self.return_json 78 | 79 | def set_image(self, src, text = None): # 이미지 반환 형식 80 | self.init_json() 81 | basic_image ={ 82 | "simpleImage": { 83 | "imageUrl": src, 84 | } 85 | } 86 | 87 | if text != None: 88 | basic_image["simpleImage"]["altText"] = text 89 | 90 | self.return_json["template"]["outputs"].append(basic_image) 91 | return self.return_json 92 | 93 | def set_commerce(self,thumbnail, description, price, currency, is_discount = None, is_discountRate = None, is_discountedPrice = None, profile = None, **kwargs): # 커머스 반환 형식 94 | self.common_params.Button(**kwargs) 95 | return_json = { 96 | "version": "2.0", 97 | "template": { 98 | "outputs": [ 99 | { 100 | "commerceCard": { 101 | "description": "따끈따끈한 보물 상자 팝니다", 102 | "price": 10000, 103 | "discount": 1000, 104 | "currency": "won", 105 | "thumbnails": [ 106 | { 107 | "imageUrl": "http://k.kakaocdn.net/dn/83BvP/bl20duRC1Q1/lj3JUcmrzC53YIjNDkqbWK/i_6piz1p.jpg", 108 | "link": { 109 | "web": "https://store.kakaofriends.com/kr/products/1542" 110 | } 111 | } 112 | ], 113 | "profile": { 114 | "imageUrl": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT4BJ9LU4Ikr_EvZLmijfcjzQKMRCJ2bO3A8SVKNuQ78zu2KOqM", 115 | "nickname": "보물상자 팝니다" 116 | }, 117 | "buttons": [ 118 | { 119 | "label": "구매하기", 120 | "action": "webLink", 121 | "webLinkUrl": "https://store.kakaofriends.com/kr/products/1542" 122 | }, 123 | { 124 | "label": "전화하기", 125 | "action": "phone", 126 | "phoneNumber": "354-86-00070" 127 | }, 128 | { 129 | "label": "공유하기", 130 | "action": "share" 131 | } 132 | ] 133 | } 134 | } 135 | ] 136 | } 137 | } 138 | return return_json 139 | 140 | 141 | def set_carousel(self, card_type, card_num, *params): #케로셀 반환 형식 #(link, Title, description) 142 | self.init_json() 143 | basic_carousel = { 144 | "carousel": { 145 | "type": card_type, 146 | "items": [] 147 | } 148 | } 149 | if card_type == "basicCard": 150 | for param in range(card_num): # thumb_img, *is_buttons, is_title = None, is_description = None 151 | basic_carousel['carousel']['items'].append(self.is_Card(thumb_img = params[param][0], is_title = params[param][1], is_description = params[param][2], flag= True)) 152 | 153 | else: 154 | for param in range(card_num): # thumb_img, *is_buttons, is_title = None, is_description = None 155 | basic_carousel['carousel']['items'].append(self.is_Card(thumb_img = params[param][0], is_title = params[param][1], is_description = params[param][2], flag= True)) 156 | self.return_json["template"]["outputs"].append(basic_carousel) 157 | return self.return_json 158 | 159 | def set_list(self, title, data, is_Button = None): # [title, desc, url], 만약 없으면 None #data = list 160 | self.init_json() 161 | order = ['title', 'description', 'link'] 162 | basic_list = { 163 | "listCard": { 164 | "header": { 165 | "title": title 166 | }, 167 | "items": [] 168 | } 169 | } 170 | for column in data: 171 | item = {} 172 | for idx, dat in enumerate(column): 173 | if dat is not None: 174 | if order[idx] == "link": 175 | item[order[idx]] = self.common_params.Link(dat) 176 | 177 | else: 178 | item[order[idx]] = dat 179 | 180 | basic_list['listCard']['items'].append(item) 181 | if is_Button != None: 182 | basic_list['listCard']['buttons'] = list() 183 | basic_list['listCard']['buttons'].append(is_Button) 184 | 185 | self.return_json["template"]["outputs"].append(basic_list) 186 | return self.return_json 187 | 188 | 189 | class common_params: 190 | 191 | #kwargs로 들어올 수 있는 값은 DOCS를 참조 192 | # label = string 193 | # action = string 194 | # webLinkUrl = string | action = webLink 195 | # messageText = string | action = message or block 196 | # phoneNumber = string | action = phone 197 | # blockId = string | action = block 198 | # extra ... 199 | def Button(self, **kwargs): 200 | data = {} 201 | params = ['label', 'action', 'webLinkUrl', 'messageText', 'phoneNumber', 'blockId'] 202 | for item in kwargs.items(): 203 | if item[0] not in params: 204 | data.clear() 205 | data["label"] = "error, Check Parameter" 206 | data["action"] = "message" 207 | break 208 | data[item[0]] = item[1] 209 | 210 | return data 211 | 212 | def Link(self, url): 213 | return { 214 | 'web' : url 215 | } 216 | -------------------------------------------------------------------------------- /alphaSandol/subway.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import datetime 4 | from bs4 import BeautifulSoup 5 | import settings 6 | 7 | 8 | class LiveSubwayTraffic: 9 | def __init__(self, station_no=None) -> None: 10 | self.URL = "https://map.naver.com/v5/api/transit/subway/stations/" 11 | self.time = None 12 | self.station_name: str 13 | self.return_data = '' 14 | self.station_no = station_no 15 | 16 | def get_data(self) -> dict: 17 | URL = self.URL + self.station_no + "/schedule?lang=ko&stationID=" + self.station_no 18 | html = requests.get(URL).text 19 | soup = BeautifulSoup(html, 'html.parser') 20 | 21 | json_data = json.loads(soup.text) 22 | # print(json_data) 23 | return json_data 24 | 25 | def arrival_time(self): 26 | try: 27 | if self.data['todayServiceDay']['name'] == '평일': # 평일 시간표 28 | schedule_data_up = self.data['weekdaySchedule']['up'] 29 | schedule_data_down = self.data['weekdaySchedule']['down'] 30 | 31 | it = schedule_data_up.__iter__() # 상행선 32 | flag = False 33 | for i in schedule_data_up: 34 | it.__next__() 35 | if datetime.datetime.strptime(i['departureTime'], '%H:%M:%S') > self.time: 36 | self.return_data += i['headsign'] + " 방면 " + i['departureTime'] + ", " \ 37 | + it.__next__()['departureTime'] + "\n" 38 | flag = True 39 | break 40 | else: 41 | continue 42 | 43 | if not flag: 44 | self.return_data += schedule_data_up[-1]['headsign'] + schedule_data_up[-1][ 45 | 'departureTime'] + " 막차입니다" 46 | 47 | 48 | it = schedule_data_down.__iter__() # 하행선 49 | for i in schedule_data_down: 50 | it.__next__() 51 | if datetime.datetime.strptime(i['departureTime'], '%H:%M:%S') > self.time: 52 | self.return_data += i['headsign'] + "방면 " + i['departureTime'] + ", " \ 53 | + it.__next__()['departureTime'] + "\n" 54 | flag = True 55 | # print(i['departureTime'], end=' ') 56 | # print(it.__next__()['departureTime'], end=' ') 57 | break 58 | 59 | else: 60 | continue 61 | 62 | if not flag: 63 | self.return_data += schedule_data_down[-1]['headsign'] + schedule_data_down[-1][ 64 | 'departureTime'] + " 막차입니다" 65 | 66 | else: # 주말 시간표 67 | schedule_data_up = self.data['sundaySchedule']['up'] 68 | schedule_data_down = self.data['sundaySchedule']['down'] 69 | 70 | flag = False 71 | 72 | it = schedule_data_up.__iter__() 73 | for i in schedule_data_up: 74 | it.__next__() 75 | if datetime.datetime.strptime(i['departureTime'], '%H:%M:%S') > self.time: 76 | self.return_data += i['headsign'] + "방면 " + i['departureTime'] + ", " \ 77 | + it.__next__()['departureTime'] + "\n" 78 | flag = True 79 | # print(i['departureTime'], end=' ') 80 | # print(it.__next__()['departureTime']) 81 | break 82 | 83 | else: 84 | continue 85 | 86 | if not flag: 87 | self.return_data += schedule_data_up[-1]['headsign'] + schedule_data_up[-1][ 88 | 'departureTime'] + " 막차입니다" 89 | 90 | flag = False 91 | it = schedule_data_down.__iter__() 92 | for i in schedule_data_down: 93 | it.__next__() 94 | if datetime.datetime.strptime(i['departureTime'], '%H:%M:%S') > self.time: 95 | self.return_data += i['headsign'] + "방면 " + i['departureTime'] + ", " \ 96 | + it.__next__()['departureTime'] + "\n" 97 | flag = True 98 | # print(i['departureTime'], end=' ') 99 | # print(it.__next__()['departureTime']) 100 | break 101 | 102 | else: 103 | continue 104 | 105 | if not flag: 106 | self.return_data += schedule_data_down[-1]['headsign'] + schedule_data_down[-1][ 107 | 'departureTime'] + " 막차입니다" 108 | 109 | except Exception as e: 110 | return str(e) 111 | 112 | def get_time(self) -> dict: 113 | return settings.GEN.set_text(self.time) 114 | 115 | def get_string(self, time): 116 | self.time = datetime.datetime.strptime(time, '%H:%M:%S') # time 모듈로 변환 117 | for subway in ["455", "11120"]: 118 | self.station_no = subway 119 | self.data = self.get_data() 120 | self.arrival_time() 121 | return settings.GEN.set_text(self.return_data) 122 | 123 | 124 | class LastTraffic: # 교통 관련 클래스 125 | def __init__(self): 126 | 127 | self.SUBWAY_URL = ["https://map.naver.com/v5/api/transit/subway/stations/455/schedule?lang=ko&stationID=455", 128 | "https://map.naver.com/v5/api/transit/subway/stations/11120/schedule?lang=ko&stationID=11120"] 129 | 130 | def real_time_traffic(self): 131 | context = '' 132 | header = [f"{settings.IMOGE('emotion', 'walk')}4호선 막차시간입니다\n", 133 | f"\n{settings.IMOGE('emotion', 'walk')}수인선 막차시간입니다\n"] 134 | 135 | try: 136 | for iteration in range(len(self.SUBWAY_URL)): 137 | context += ''.join(header[iteration]) 138 | html = requests.get(self.SUBWAY_URL[iteration]) 139 | soup = BeautifulSoup(html.text, 'html.parser') 140 | 141 | last_arrival_weekday = json.loads(soup.text)['weekdaySchedule'] # 평일 막차 142 | last_arrival_weekend = json.loads(soup.text)['sundaySchedule'] # 주말 막차 143 | if iteration == 0: 144 | weekday_last = lambda sign: [last_arrival_weekday[sign][101 + i] for i in 145 | range(len(last_arrival_weekday[sign]) - 101)][::-1] 146 | weekend_last = lambda sign: [last_arrival_weekend[sign][85 + i] for i in 147 | range(len(last_arrival_weekend[sign]) - 85)][::-1] 148 | 149 | else: 150 | weekday_last = lambda sign: [last_arrival_weekday[sign][i] for i in 151 | range(len(last_arrival_weekday[sign]))][::-1] 152 | weekend_last = lambda sign: [last_arrival_weekend[sign][i] for i in 153 | range(len(last_arrival_weekend[sign]))][::-1] 154 | # usage : weekend_last('up') 155 | # 마지막에 있는 열차 10개 정도를 가지고 와서 각 막차 시간 비교 156 | # 모두 불러오지 않는 이유는 속도 때문 157 | station = [i['headsign'] for i in weekday_last('up')] # headsign이 가장 처음으로 나오는 경우의 인덱스를 반환하기 위한 리스트 158 | station_weekend = [i['headsign'] for i in 159 | weekend_last('up')] # headsign이 가장 처음으로 나오는 경우의 인덱스를 반환하기 위한 리스트 160 | # 상행선에서의 막차별 역을 저장하는 리스트 (역 중복 가능) 161 | 162 | station2 = [i['headsign'] for i in weekday_last('down')] # headsign이 가장 처음으로 나오는 경우의 인덱스를 반환하기 위한 리스트 163 | station_weekend2 = [i['headsign'] for i in 164 | weekend_last('down')] # headsign이 가장 처음으로 나오는 경우의 인덱스를 반환하기 위한 리스트 165 | # 상행선에서의 막차별 역을 저장하는 리스트 (역 중복 가능) 166 | 167 | find_weekday = station.index 168 | find_weekend = station_weekend.index 169 | 170 | find_weekday2 = station2.index 171 | find_weekend2 = station_weekend2.index 172 | 173 | find_arrival_time_up = lambda a: weekday_last('up')[a]["departureTime"][:-3] # 평일 상행선 174 | find_arrival_time_down = lambda a: weekday_last('down')[a]["departureTime"][:-3] # 평일 하행선 175 | 176 | find_arrival_time_up2 = lambda a: weekend_last('up')[a]["departureTime"][:-3] # 주말 상행선 177 | find_arrival_time_down2 = lambda a: weekend_last('down')[a]["departureTime"][:-3] # 주말 하행선 178 | 179 | station_name_up: list = [["당고개", "안산", "노원", "금정", "한성대입구", "사당"], ["왕십리", "죽전", "고색"]] 180 | station_name_down: list = [["오이도"], ["오이도", "인천"]] 181 | 182 | for arv in (station_name_up[iteration]): 183 | context += ''.join(f"{arv} - ") 184 | try: 185 | context += ''.join(f"(평일) {find_arrival_time_up(find_weekday(arv))}") 186 | except Exception: 187 | pass 188 | 189 | try: 190 | context += "".join(f"(휴일) {find_arrival_time_up2(find_weekend(arv))}\n") # 휴일 시간이 있으면 시간 추가 191 | except Exception: 192 | context += "".join("\n") # 휴일 시간 없으면 개행문자 넣고 pass 193 | 194 | for arv in (station_name_down[iteration]): 195 | context += ''.join(f"{arv} - ") 196 | try: 197 | context += ''.join(f"(평일) {find_arrival_time_down(find_weekday2(arv))}") 198 | except Exception: 199 | pass 200 | 201 | try: 202 | context += "".join(f"(휴일) {find_arrival_time_down2(find_weekend2(arv))}\n") # 휴일 시간이 있으면 시간 추가 203 | except Exception: 204 | context += "".join("\n") # 휴일 시간 없으면 개행문자 넣고 pass 205 | except Exception as e: 206 | return settings.GEN.set_text(str(e)) 207 | return settings.GEN.set_text(str(context[:-1])) 208 | 209 | 210 | class EfficientTransfer(LastTraffic): # 수인분당선 211 | def __init__(self): 212 | super().__init__() 213 | 214 | def real_time_traffic(self): 215 | return super().real_time_traffic() 216 | 217 | 218 | if __name__ == "__main__": 219 | import pprint 220 | pprint.pprint(LiveSubwayTraffic().get_string("12:13:30")) 221 | # pprint.pprint(LastTraffic().real_time_traffic()) 222 | # pprint.pprint(EfficientTransfer().real_time_traffic()) 223 | -------------------------------------------------------------------------------- /alphaSandol/restaurant.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import settings 3 | import datetime 4 | 5 | 6 | class Time: 7 | DATE = 0 8 | LUNCH = 1 9 | DINNER = 2 10 | 11 | 12 | class AboutMeal: # 학식 관련 클래스 13 | def __init__(self): 14 | self.S3 = boto3.resource('s3') 15 | self.S3_client = boto3.client('s3') 16 | self.bucket = self.S3.Bucket(settings.BUCKET_NAME) 17 | self.data = "" 18 | self.URL_MENU = "https://ibook.kpu.ac.kr/Viewer/menu01" 19 | 20 | def read_meal(self, uid) -> dict: # 학식 불러오기 21 | restaurant_position = {"messageText": "운영시간", 22 | "action": "message", 23 | "label": "운영시간 및 위치" 24 | } # quick reply 형식 25 | MEAL_GEN = settings.ReturnType(reply_json=restaurant_position) # 따로 리턴타입을 불러옴, 이유는 발화안에 여러 응답을 줘야하기때문 26 | # 이전과 같은 id의 인스턴스로 사용하면 다른 발화에도 영향 27 | if not settings.DEBUG: # 디버그 모드가 아닌 경우 28 | try: 29 | self.bucket.download_file(settings.RESTAURANT_MENU, settings.LOCAL_RESTAURANT_MENU) 30 | 31 | except Exception as e: 32 | return settings.GEN.set_text( 33 | f"[File-Open-Error #131] 저장소에서 파일을 가져오는데 실패했습니다.{settings.IMOGE('emotion', 'sad')}\n{e}") 34 | # 버킷을 로컬 임시 폴더에 다운로드 35 | 36 | rst_name = list(settings.RESTAURANT_ACCESS_ID.values()) # 식당id만 뽑아낸 리스트 37 | if uid not in rst_name: 38 | try: 39 | weekday = ['월', '화', '수', '목', '금', '토', '일'] 40 | file_dir = lambda \ 41 | is_debug: settings.LOCAL_RESTAURANT_MENU if is_debug is False else "../test_stored_data/restaurant_menu.txt" 42 | 43 | with open(file_dir(settings.DEBUG), "r", encoding='UTF-8') as f: 44 | data = f.readlines() 45 | ret = '[교외식당 메뉴입니다!]\n' 46 | for restaurant in range(0, len(data) - 4, 2): # 파일에서 식당 구분이 2칸 간격으로 되어있음 교외식당 47 | menu_list = data[restaurant + 1].replace("\'", '').split(", ") 48 | last_update_date = datetime.date.fromisoformat(menu_list[0]) 49 | form = data[restaurant].replace("\n", '').replace("🐾", settings.IMOGE('emotion', 'walk')) 50 | 51 | ret += f"{form}[{str(last_update_date)} {weekday[last_update_date.weekday()]}요일]\n" \ 52 | f"{settings.IMOGE('emotion', 'paw')} 중식 : {menu_list[Time.LUNCH].replace(' ', ', ')}\n" \ 53 | f"{settings.IMOGE('emotion', 'paw')} 석식 : {menu_list[Time.DINNER].replace(' ', ', ')}\n" 54 | ret = ret[:-2] 55 | MEAL_GEN.set_text(ret, is_init=False) # 교외식당 저장 56 | ret = '[교내식당 메뉴입니다!]\n' 57 | for school_restaurant in range(len(data) - 4, len(data) - 2, 2): 58 | menu_list = data[school_restaurant + 1].replace("\'", '').split(", ") 59 | last_update_date = datetime.date.fromisoformat(menu_list[0]) 60 | form = data[school_restaurant].replace("\n", '').replace("🐾", 61 | settings.IMOGE('emotion', 'walk')) 62 | 63 | ret += f"{form}[{str(last_update_date)} {weekday[last_update_date.weekday()]}요일]\n포장메뉴도 있어요\n" \ 64 | f"{settings.IMOGE('emotion', 'paw')} 중식 : {menu_list[Time.LUNCH]}\n" \ 65 | f"{settings.IMOGE('emotion', 'paw')} 석식 : {menu_list[Time.DINNER]}\n" 66 | ret += "🐾웰스프레쉬(E동 교직원식당) [URL 참조]\nhttps://ibook.kpu.ac.kr/Viewer/menu01" 67 | 68 | return_string = MEAL_GEN.set_text(ret, is_init=False) # 교외식당 저장 69 | return return_string 70 | 71 | except Exception as e: 72 | return settings.GEN.set_text( 73 | "[File-Open-Error #132] 파일을 여는 중 오류가 발생했어요.." + settings.IMOGE('emotion', 'sad') + str(e)) 74 | 75 | else: 76 | selected_restaurant = rst_name.index(uid) * 2 # 식당 이름 포인터 77 | try: 78 | weekday = ['월', '화', '수', '목', '금', '토', '일'] 79 | file_dir = lambda \ 80 | is_debug: settings.LOCAL_RESTAURANT_MENU if is_debug is False else "../test_stored_data/restaurant_menu.txt" 81 | with open(file_dir(settings.DEBUG), "r", encoding='UTF-8') as f: 82 | data = f.readlines() 83 | 84 | menu_list = data[selected_restaurant + 1].replace("\'", '').split(", ") 85 | last_update_date = datetime.date.fromisoformat(menu_list[0]) 86 | form = data[selected_restaurant].replace("\n", '').replace("🐾", settings.IMOGE('emotion', 'walk')) 87 | 88 | if uid == settings.RESTAURANT_ACCESS_ID['푸드라운지']: 89 | ret = f"{form}[{str(last_update_date)} {weekday[last_update_date.weekday()]}요일]\n포장메뉴도 있어요\n" \ 90 | f"{settings.IMOGE('emotion', 'paw')} 중식 : {menu_list[Time.LUNCH].replace(' ', ', ')}\n" \ 91 | f"{settings.IMOGE('emotion', 'paw')} 석식 : {menu_list[Time.DINNER].replace(' ', ', ')}\n" 92 | else: 93 | ret = f"{form}[{str(last_update_date)} {weekday[last_update_date.weekday()]}요일]\n" \ 94 | f"{settings.IMOGE('emotion', 'paw')} 중식 : {menu_list[Time.LUNCH].replace(' ', ', ')}\n" \ 95 | f"{settings.IMOGE('emotion', 'paw')} 석식 : {menu_list[Time.DINNER].replace(' ', ', ')}\n" 96 | return_string = settings.GEN.set_text(ret) 97 | 98 | return return_string 99 | 100 | except Exception as e: 101 | return settings.GEN.set_text( 102 | "[File-Open-Error #132] 파일을 여는 중 오류가 발생했어요.." + settings.IMOGE('emotion', 'sad') + str(e)) 103 | 104 | def upload_meal(self, store_name, lunch_list: str, dinner_list: str, input_date, owner_id) -> dict: # 학식 업로드 105 | if (owner_id != settings.RESTAURANT_ACCESS_ID[store_name]) and owner_id not in list( 106 | settings.SANDOL_ACCESS_ID.values()): 107 | return settings.GEN.set_text( 108 | f"[Permission-Error #121-1] 권한이 없습니다{owner_id}{settings.IMOGE('emotion', 'angry')}") 109 | # 권한 확인 110 | 111 | if store_name not in settings.RESTAURANT_ACCESS_ID.keys(): 112 | return settings.GEN.set_text(f"[Not-Found-Error #121-2] 해당하는 식당이 없습니다.{settings.IMOGE('emotion', 'sad')}") 113 | # 식당 존재 여부 확인 114 | 115 | if not settings.DEBUG: 116 | try: 117 | self.S3.meta.client.download_file(settings.BUCKET_NAME, settings.RESTAURANT_MENU, 118 | settings.LOCAL_RESTAURANT_MENU) 119 | 120 | except Exception as e: 121 | return settings.GEN.set_text( 122 | f"[File-Open-Error #122] 저장소에서 파일을 찾을 수 없습니다.{settings.IMOGE('emotion', 'sad')}\n{e}") 123 | 124 | file_dir = lambda \ 125 | is_debug: settings.LOCAL_RESTAURANT_MENU if is_debug is False else "../test_stored_data/restaurant_menu.txt" 126 | with open(file_dir(settings.DEBUG), "r", encoding='UTF-8') as f: 127 | try: 128 | data = f.readlines() 129 | menu_info = data[data.index("🐾" + store_name + "\n") + 1].replace('\'', '').replace("\n", "").split( 130 | ", ") 131 | menu_info[Time.DATE] = input_date 132 | 133 | menu_info[Time.LUNCH] = lunch_list 134 | menu_info[Time.DINNER] = dinner_list 135 | 136 | final_string = str(menu_info)[1:-1] 137 | 138 | data[data.index("🐾" + store_name + "\n") + 1] = final_string + "\n" # 최종 문자열 139 | with open(file_dir(settings.DEBUG), "w", encoding='UTF-8') as rf: 140 | rf.writelines(data) 141 | 142 | except Exception as e: 143 | return settings.GEN.set_text( 144 | f"[File-Open-Error #123]파일을 수정하는 중 오류가 발생했습니다.{settings.IMOGE('emotion', 'sad')}\n{e}") 145 | 146 | if not settings.DEBUG: 147 | try: 148 | s3 = boto3.client('s3') # 이 부분 해당 버킷 생성 후 적절히 수정 예정 149 | s3.upload_file(settings.LOCAL_RESTAURANT_MENU, 'sandol', settings.RESTAURANT_MENU) 150 | 151 | except Exception as e: 152 | return settings.GEN.set_text( 153 | f"[File-Open-Error #124]파일을 저장소에 업로드하는 중 오류가 발생했습니다.{settings.IMOGE('emotion', 'sad')}\n{e}") 154 | 155 | return settings.GEN.set_text(f"네! 학생들에게 잘 전달할게요! 감사합니다!{settings.IMOGE('emotion', 'walk')}") 156 | 157 | def reset_meal(self, bot_id, date) -> dict: # 학식 초기화 158 | if bot_id not in list(settings.SANDOL_ACCESS_ID.values()): 159 | return settings.GEN.set_text(f"[Permission-Error #141] 권한이 없습니다{settings.IMOGE('emotion', 'angry')}") 160 | 161 | if not settings.DEBUG: 162 | try: 163 | self.S3.meta.client.download_file(settings.BUCKET_NAME, settings.RESTAURANT_MENU, 164 | settings.LOCAL_RESTAURANT_MENU) 165 | 166 | except Exception as e: 167 | return settings.GEN.set_text( 168 | f"[File-Open-Error #122] 저장소에서 파일을 찾을 수 없습니다.{settings.IMOGE('emotion', 'sad')}\n{e}") 169 | 170 | try: 171 | file_dir = lambda \ 172 | is_debug: settings.LOCAL_RESTAURANT_MENU if is_debug is False else "../test_stored_data/restaurant_menu.txt" 173 | print(file_dir(settings.DEBUG)) 174 | with open(file_dir(settings.DEBUG), "w", encoding='UTF-8') as f: 175 | rest_name = [f"{settings.IMOGE('emotion', 'paw')}미가식당\n", 176 | f"{settings.IMOGE('emotion', 'paw')}세미콘식당\n", 177 | f"{settings.IMOGE('emotion', 'paw')}푸드라운지\n", 178 | f"{settings.IMOGE('emotion', 'paw')}웰스프레쉬\n" 179 | ] 180 | 181 | return_string = '' 182 | for i in range(len(rest_name)): 183 | return_string += rest_name[i] + "\'" + date + "\', \'업데이트되지않았습니다\', \'업데이트되지않았습니다\'\n" 184 | # 초기화 작업 185 | 186 | f.writelines(return_string) 187 | 188 | except Exception as e: 189 | return settings.GEN.set_text( 190 | f"[File-Open-Error #143]파일을 수정하는 중 오류가 발생했습니다.{settings.IMOGE('emotion', 'sad')}\n{e}") 191 | 192 | if not settings.DEBUG: 193 | try: 194 | s3 = boto3.client('s3') # 이 부분 해당 버킷 생성 후 적절히 수정 예정 195 | s3.upload_file(settings.LOCAL_RESTAURANT_MENU, 'sandol', settings.RESTAURANT_MENU) 196 | 197 | except Exception as e: 198 | return settings.GEN.set_text( 199 | f"[File-Open-Error #124]파일을 저장소에 업로드하는 중 오류가 발생했습니다.{settings.IMOGE('emotion', 'sad')}\n{e}") 200 | 201 | return settings.GEN.set_text(f"파일을 정상적으로 초기화했습니다") 202 | 203 | 204 | # 식당 운영시간 불러오기 205 | def time_meal(): 206 | MEAL_GEN = settings.ReturnType() 207 | MEAL_GEN.set_image(settings.SANDOL_RSTRNT_MAP, is_init=False) # 식당 지도 208 | 209 | time_meal_string = f"교외식당 운영시간입니다! \n" \ 210 | f"{settings.IMOGE('emotion', 'walk')}미가식당 \n" \ 211 | f"{settings.IMOGE('emotion', 'paw')}운영시간 : 08:30 ~ 19:30 \n" \ 212 | f"{settings.IMOGE('emotion', 'paw')}운영시간동안 항시 식사 가능합니다. \n\n" \ 213 | f"{settings.IMOGE('emotion', 'walk')}세미콘 식당 \n" \ 214 | f"{settings.IMOGE('emotion', 'paw')}중식 : 11:30 ~ 1:30\n" \ 215 | f"{settings.IMOGE('emotion', 'paw')}석식 : 5:00 ~ 6:30\n" 216 | MEAL_GEN.set_text(time_meal_string, is_init=False) 217 | 218 | time_meal_string = f"교내식당 운영시간입니다! \n" \ 219 | f"{settings.IMOGE('emotion', 'walk')}웰스 프레쉬(E동 교직원식당) \n" \ 220 | f"{settings.IMOGE('emotion', 'paw')}중식 : 11:30 ~ 13:30 \n" \ 221 | f"{settings.IMOGE('emotion', 'paw')}석식 : 영업하지 않습니다. \n\n" \ 222 | f"{settings.IMOGE('emotion', 'walk')}푸드라운지 \n" \ 223 | f"{settings.IMOGE('emotion', 'paw')}천원의 아침 : 8시 30분 ~ 10시 \n" \ 224 | f"{settings.IMOGE('emotion', 'paw')}운영시간 : 11:00 ~ 20:00 \n" \ 225 | f"{settings.IMOGE('emotion', 'paw')}토,일,공유일 영업하지 않습니다. \n" 226 | 227 | return MEAL_GEN.set_text(time_meal_string, is_init=False) 228 | 229 | 230 | # 식당 계좌이체 결제 231 | def payment_meal(): 232 | btn_list = [{ 233 | "label": "세미콘 식당", 234 | "action": "webLink", 235 | "webLinkUrl": "https://qr.kakaopay.com/2810060111751110120069009c404611" 236 | }, 237 | { 238 | "label": "민이 식당", 239 | "action": "webLink", 240 | "webLinkUrl": "https://qr.kakaopay.com/2810060110000075262686359c406394" 241 | }] 242 | title = "hello" 243 | dsc = "dsc" 244 | params = ['label', 'action', 'webLinkUrl', 'messageText', 'phoneNumber', 'blockId'] 245 | return settings.GEN.set_card(settings.SANDOL_LOGO1, settings.GEN_OPTION.button(label="세미콘 식당", action="webLink", 246 | webLinkUrl="https://qr.kakaopay.com/2810060111751110120069009c404611"), 247 | is_title=title, is_description=dsc, flag=False) 248 | 249 | 250 | if __name__ == "__main__": 251 | # print(AboutMeal().read_meal(uid="d367f2ec55f41b4207156f4b8fce5ce885b05d8c3b238cf8861c55a9012f6f5895")) # read meal (산돌팀 (학생)) 기준 252 | # print(AboutMeal().read_meal(uid="32d8a05a91242ffb4c64b5630ec55953121dffd83a121d985e26e06e2c457197e6")) # read meal (미가식당 (업주)) 기준 253 | # print(AboutMeal().reset_meal(bot_id="d367f2ec55f41b4207156f4b8fce5ce885b05d8c3b238cf8861c55a9012f6f5895", date="2001-09-03")) # reset meal 테스트 데이터 형식 254 | # print(AboutMeal().upload_meal(store_name="미가식당", lunch_list="ㅁ ㅁ ㅁ ㅁ", dinner_list="ㄹ ㄹ ㄹ ㄹ", input_date="2001-09-03",owner_id="d367f2ec55f41b4207156f4b8fce5ce885b05d8c3b238cf8861c55a9012f6f5895")) # read meal (미가식당 (업주)) 기준 255 | # print(time_meal()) # time meal 256 | print(payment_meal()) 257 | -------------------------------------------------------------------------------- /alphaSandol/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "49e3f1ab224f2bb5ff92bb139bae000bac6aca1fde8c9e5c8957ec9ebf64f797" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.8" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "argcomplete": { 20 | "hashes": [ 21 | "sha256:291f0beca7fd49ce285d2f10e4c1c77e9460cf823eef2de54df0c0fec88b0d81", 22 | "sha256:2c7dbffd8c045ea534921e63b0be6fe65e88599990d8dc408ac8c542b72a5445" 23 | ], 24 | "version": "==1.12.3" 25 | }, 26 | "beautifulsoup4": { 27 | "hashes": [ 28 | "sha256:9a315ce70049920ea4572a4055bc4bd700c940521d36fc858205ad4fcde149bf", 29 | "sha256:c23ad23c521d818955a4151a67d81580319d4bf548d3d49f4223ae041ff98891" 30 | ], 31 | "markers": "python_version >= '3.1'", 32 | "version": "==4.10.0" 33 | }, 34 | "boto3": { 35 | "hashes": [ 36 | "sha256:035191ad6c7e8aed972e1374f4e0ecb38767c497fd6c961e4ae33898b62f78fb", 37 | "sha256:cd58563dd3f36d5909815752b12c80a2c510c051474f8296e28dbd3ef5634d65" 38 | ], 39 | "index": "pypi", 40 | "version": "==1.20.11" 41 | }, 42 | "botocore": { 43 | "hashes": [ 44 | "sha256:133fa0837762587fb4e5da3fb61ac0b45495cd9fd2d2be7679ba64899da1f3ba", 45 | "sha256:497234f137810909289a600433cec5583ea8dc05a78b644653d76484138d78b9" 46 | ], 47 | "markers": "python_version >= '3.6'", 48 | "version": "==1.23.11" 49 | }, 50 | "bs4": { 51 | "hashes": [ 52 | "sha256:36ecea1fd7cc5c0c6e4a1ff075df26d50da647b75376626cc186e2212886dd3a" 53 | ], 54 | "index": "pypi", 55 | "version": "==0.0.1" 56 | }, 57 | "certifi": { 58 | "hashes": [ 59 | "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872", 60 | "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569" 61 | ], 62 | "version": "==2021.10.8" 63 | }, 64 | "cfn-flip": { 65 | "hashes": [ 66 | "sha256:003e02a089c35e1230ffd0e1bcfbbc4b12cc7d2deb2fcc6c4228ac9819307362", 67 | "sha256:faca8e77f0d32fb84cce1db1ef4c18b14a325d31125dae73c13bcc01947d2722" 68 | ], 69 | "version": "==1.3.0" 70 | }, 71 | "charset-normalizer": { 72 | "hashes": [ 73 | "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0", 74 | "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b" 75 | ], 76 | "markers": "python_version >= '3'", 77 | "version": "==2.0.7" 78 | }, 79 | "click": { 80 | "hashes": [ 81 | "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3", 82 | "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b" 83 | ], 84 | "markers": "python_version >= '3.6'", 85 | "version": "==8.0.3" 86 | }, 87 | "durationpy": { 88 | "hashes": [ 89 | "sha256:5ef9416b527b50d722f34655becfb75e49228eb82f87b855ed1911b3314b5408" 90 | ], 91 | "version": "==0.5" 92 | }, 93 | "future": { 94 | "hashes": [ 95 | "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d" 96 | ], 97 | "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", 98 | "version": "==0.18.2" 99 | }, 100 | "hjson": { 101 | "hashes": [ 102 | "sha256:2838fd7200e5839ea4516ece953f3a19892c41089f0d933ba3f68e596aacfcd5", 103 | "sha256:5546438bf4e1b52bc964c6a47c4ed10fa5fba8a1b264e22efa893e333baad2db" 104 | ], 105 | "version": "==3.0.2" 106 | }, 107 | "idna": { 108 | "hashes": [ 109 | "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", 110 | "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" 111 | ], 112 | "markers": "python_version >= '3'", 113 | "version": "==3.3" 114 | }, 115 | "jmespath": { 116 | "hashes": [ 117 | "sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9", 118 | "sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f" 119 | ], 120 | "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", 121 | "version": "==0.10.0" 122 | }, 123 | "kappa": { 124 | "hashes": [ 125 | "sha256:4b5b372872f25d619e427e04282551048dc975a107385b076b3ffc6406a15833", 126 | "sha256:4d6b7b3accce4a0aaaac92b36237a6304f0f2fffbbe3caea3f7c9f52d12c9989" 127 | ], 128 | "version": "==0.6.0" 129 | }, 130 | "placebo": { 131 | "hashes": [ 132 | "sha256:03157f8527bbc2965b71b88f4a139ef8038618b346787f20d63e3c5da541b047" 133 | ], 134 | "version": "==0.9.0" 135 | }, 136 | "python-dateutil": { 137 | "hashes": [ 138 | "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", 139 | "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" 140 | ], 141 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 142 | "version": "==2.8.2" 143 | }, 144 | "python-slugify": { 145 | "hashes": [ 146 | "sha256:6d8c5df75cd4a7c3a2d21e257633de53f52ab0265cd2d1dc62a730e8194a7380", 147 | "sha256:f13383a0b9fcbe649a1892b9c8eb4f8eab1d6d84b84bb7a624317afa98159cab" 148 | ], 149 | "markers": "python_version >= '3.6'", 150 | "version": "==5.0.2" 151 | }, 152 | "pyyaml": { 153 | "hashes": [ 154 | "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", 155 | "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", 156 | "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", 157 | "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", 158 | "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", 159 | "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", 160 | "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", 161 | "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", 162 | "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", 163 | "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", 164 | "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", 165 | "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", 166 | "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", 167 | "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", 168 | "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", 169 | "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", 170 | "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", 171 | "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", 172 | "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", 173 | "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", 174 | "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", 175 | "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", 176 | "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", 177 | "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", 178 | "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", 179 | "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", 180 | "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", 181 | "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", 182 | "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", 183 | "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", 184 | "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", 185 | "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", 186 | "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" 187 | ], 188 | "markers": "python_version >= '3.6'", 189 | "version": "==6.0" 190 | }, 191 | "requests": { 192 | "hashes": [ 193 | "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24", 194 | "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7" 195 | ], 196 | "index": "pypi", 197 | "version": "==2.26.0" 198 | }, 199 | "s3transfer": { 200 | "hashes": [ 201 | "sha256:50ed823e1dc5868ad40c8dc92072f757aa0e653a192845c94a3b676f4a62da4c", 202 | "sha256:9c1dc369814391a6bda20ebbf4b70a0f34630592c9aa520856bf384916af2803" 203 | ], 204 | "markers": "python_version >= '3.6'", 205 | "version": "==0.5.0" 206 | }, 207 | "six": { 208 | "hashes": [ 209 | "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", 210 | "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" 211 | ], 212 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 213 | "version": "==1.16.0" 214 | }, 215 | "soupsieve": { 216 | "hashes": [ 217 | "sha256:1a3cca2617c6b38c0343ed661b1fa5de5637f257d4fe22bd9f1338010a1efefb", 218 | "sha256:b8d49b1cd4f037c7082a9683dfa1801aa2597fb11c3a1155b7a5b94829b4f1f9" 219 | ], 220 | "markers": "python_version >= '3.6'", 221 | "version": "==2.3.1" 222 | }, 223 | "text-unidecode": { 224 | "hashes": [ 225 | "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", 226 | "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93" 227 | ], 228 | "version": "==1.3" 229 | }, 230 | "toml": { 231 | "hashes": [ 232 | "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", 233 | "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" 234 | ], 235 | "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", 236 | "version": "==0.10.2" 237 | }, 238 | "tqdm": { 239 | "hashes": [ 240 | "sha256:8dd278a422499cd6b727e6ae4061c40b48fce8b76d1ccbf5d34fca9b7f925b0c", 241 | "sha256:d359de7217506c9851b7869f3708d8ee53ed70a1b8edbba4dbcb47442592920d" 242 | ], 243 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 244 | "version": "==4.62.3" 245 | }, 246 | "troposphere": { 247 | "hashes": [ 248 | "sha256:2de96a37c9037c4344d561612042e3a83d258667f82f402abe734926e3de1f76", 249 | "sha256:68313c119c3e5ad457d2a41f7396baadd54551f221268ab97d44134f15bdb2f3" 250 | ], 251 | "markers": "python_full_version >= '3.6.2'", 252 | "version": "==3.1.1" 253 | }, 254 | "urllib3": { 255 | "hashes": [ 256 | "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece", 257 | "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844" 258 | ], 259 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", 260 | "version": "==1.26.7" 261 | }, 262 | "werkzeug": { 263 | "hashes": [ 264 | "sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f", 265 | "sha256:aa2bb6fc8dee8d6c504c0ac1e7f5f7dc5810a9903e793b6f715a9f015bdadb9a" 266 | ], 267 | "markers": "python_version >= '3.6'", 268 | "version": "==2.0.2" 269 | }, 270 | "wheel": { 271 | "hashes": [ 272 | "sha256:21014b2bd93c6d0034b6ba5d35e4eb284340e09d63c59aef6fc14b0f346146fd", 273 | "sha256:e2ef7239991699e3355d54f8e968a21bb940a1dbf34a4d226741e64462516fad" 274 | ], 275 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", 276 | "version": "==0.37.0" 277 | }, 278 | "wsgi-request-logger": { 279 | "hashes": [ 280 | "sha256:445d7ec52799562f812006394d0b4a7064b37084c6ea6bd74ea7a2136c97ed83" 281 | ], 282 | "version": "==0.4.6" 283 | }, 284 | "zappa": { 285 | "git": "https://github.com/zappa/Zappa.git", 286 | "ref": "b5b80cf096227e4eb0feb1de5ff8097711d53c88" 287 | } 288 | }, 289 | "develop": {} 290 | } 291 | -------------------------------------------------------------------------------- /betaSandol/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "2a672084bd1c014ac6c38a2b2151153a467491cfb27ee3e8de251fd9a6828084" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.8" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "argcomplete": { 20 | "hashes": [ 21 | "sha256:291f0beca7fd49ce285d2f10e4c1c77e9460cf823eef2de54df0c0fec88b0d81", 22 | "sha256:2c7dbffd8c045ea534921e63b0be6fe65e88599990d8dc408ac8c542b72a5445" 23 | ], 24 | "version": "==1.12.3" 25 | }, 26 | "beautifulsoup4": { 27 | "hashes": [ 28 | "sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35", 29 | "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25", 30 | "sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666" 31 | ], 32 | "version": "==4.9.3" 33 | }, 34 | "boto3": { 35 | "hashes": [ 36 | "sha256:00be3c440db39a34a049eabce79377a0b3d453b6a24e2fa52e5156fa08f929bd", 37 | "sha256:3a7b183def075f6fe17c1154ecec42fc42f9c4ac05a7e7e018f267b7d5ef5961" 38 | ], 39 | "index": "pypi", 40 | "version": "==1.17.110" 41 | }, 42 | "botocore": { 43 | "hashes": [ 44 | "sha256:3500d0f0f15240a86efa6be91bf37df412d8cc10fc4b98ffea369dc13fb014da", 45 | "sha256:b69fd6c72d30b2ea0a42e7a2c3b9d65da3f4ccdff57bfaf6c721b0555a971bd6" 46 | ], 47 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", 48 | "version": "==1.20.110" 49 | }, 50 | "bs4": { 51 | "hashes": [ 52 | "sha256:36ecea1fd7cc5c0c6e4a1ff075df26d50da647b75376626cc186e2212886dd3a" 53 | ], 54 | "index": "pypi", 55 | "version": "==0.0.1" 56 | }, 57 | "certifi": { 58 | "hashes": [ 59 | "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee", 60 | "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8" 61 | ], 62 | "version": "==2021.5.30" 63 | }, 64 | "cfn-flip": { 65 | "hashes": [ 66 | "sha256:2bed32a1f4dca26dc64178d52511fd4ef778b5ccbcf32559cac884ace75bde6a" 67 | ], 68 | "version": "==1.2.3" 69 | }, 70 | "chardet": { 71 | "hashes": [ 72 | "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa", 73 | "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5" 74 | ], 75 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", 76 | "version": "==4.0.0" 77 | }, 78 | "click": { 79 | "hashes": [ 80 | "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a", 81 | "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6" 82 | ], 83 | "markers": "python_version >= '3.6'", 84 | "version": "==8.0.1" 85 | }, 86 | "durationpy": { 87 | "hashes": [ 88 | "sha256:5ef9416b527b50d722f34655becfb75e49228eb82f87b855ed1911b3314b5408" 89 | ], 90 | "version": "==0.5" 91 | }, 92 | "future": { 93 | "hashes": [ 94 | "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d" 95 | ], 96 | "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", 97 | "version": "==0.18.2" 98 | }, 99 | "hjson": { 100 | "hashes": [ 101 | "sha256:2838fd7200e5839ea4516ece953f3a19892c41089f0d933ba3f68e596aacfcd5", 102 | "sha256:5546438bf4e1b52bc964c6a47c4ed10fa5fba8a1b264e22efa893e333baad2db" 103 | ], 104 | "version": "==3.0.2" 105 | }, 106 | "idna": { 107 | "hashes": [ 108 | "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", 109 | "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" 110 | ], 111 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 112 | "version": "==2.10" 113 | }, 114 | "jmespath": { 115 | "hashes": [ 116 | "sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9", 117 | "sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f" 118 | ], 119 | "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", 120 | "version": "==0.10.0" 121 | }, 122 | "kappa": { 123 | "hashes": [ 124 | "sha256:4b5b372872f25d619e427e04282551048dc975a107385b076b3ffc6406a15833", 125 | "sha256:4d6b7b3accce4a0aaaac92b36237a6304f0f2fffbbe3caea3f7c9f52d12c9989" 126 | ], 127 | "version": "==0.6.0" 128 | }, 129 | "pep517": { 130 | "hashes": [ 131 | "sha256:ac59f3f6b9726a49e15a649474539442cf76e0697e39df4869d25e68e880931b", 132 | "sha256:eba39d201ef937584ad3343df3581069085bacc95454c80188291d5b3ac7a249" 133 | ], 134 | "version": "==0.10.0" 135 | }, 136 | "pip-tools": { 137 | "hashes": [ 138 | "sha256:77727ef7457d1865e61fe34c2b1439f9b971b570cc232616a22ce82ab89d357d", 139 | "sha256:9ed38c73da4993e531694ea151f77048b4dbf2ba7b94c4a569daa39568cc6564" 140 | ], 141 | "markers": "python_version >= '3.6'", 142 | "version": "==6.2.0" 143 | }, 144 | "placebo": { 145 | "hashes": [ 146 | "sha256:03157f8527bbc2965b71b88f4a139ef8038618b346787f20d63e3c5da541b047" 147 | ], 148 | "version": "==0.9.0" 149 | }, 150 | "python-dateutil": { 151 | "hashes": [ 152 | "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", 153 | "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" 154 | ], 155 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 156 | "version": "==2.8.1" 157 | }, 158 | "python-slugify": { 159 | "hashes": [ 160 | "sha256:6d8c5df75cd4a7c3a2d21e257633de53f52ab0265cd2d1dc62a730e8194a7380", 161 | "sha256:f13383a0b9fcbe649a1892b9c8eb4f8eab1d6d84b84bb7a624317afa98159cab" 162 | ], 163 | "markers": "python_version >= '3.6'", 164 | "version": "==5.0.2" 165 | }, 166 | "pyyaml": { 167 | "hashes": [ 168 | "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", 169 | "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696", 170 | "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393", 171 | "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77", 172 | "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922", 173 | "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5", 174 | "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8", 175 | "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10", 176 | "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc", 177 | "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", 178 | "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", 179 | "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", 180 | "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347", 181 | "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", 182 | "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541", 183 | "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", 184 | "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", 185 | "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc", 186 | "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", 187 | "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa", 188 | "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", 189 | "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122", 190 | "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", 191 | "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", 192 | "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", 193 | "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc", 194 | "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247", 195 | "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", 196 | "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" 197 | ], 198 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", 199 | "version": "==5.4.1" 200 | }, 201 | "requests": { 202 | "hashes": [ 203 | "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804", 204 | "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e" 205 | ], 206 | "index": "pypi", 207 | "version": "==2.25.1" 208 | }, 209 | "s3transfer": { 210 | "hashes": [ 211 | "sha256:9b3752887a2880690ce628bc263d6d13a3864083aeacff4890c1c9839a5eb0bc", 212 | "sha256:cb022f4b16551edebbb31a377d3f09600dbada7363d8c5db7976e7f47732e1b2" 213 | ], 214 | "version": "==0.4.2" 215 | }, 216 | "six": { 217 | "hashes": [ 218 | "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", 219 | "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" 220 | ], 221 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 222 | "version": "==1.16.0" 223 | }, 224 | "soupsieve": { 225 | "hashes": [ 226 | "sha256:052774848f448cf19c7e959adf5566904d525f33a3f8b6ba6f6f8f26ec7de0cc", 227 | "sha256:c2c1c2d44f158cdbddab7824a9af8c4f83c76b1e23e049479aa432feb6c4c23b" 228 | ], 229 | "markers": "python_version >= '3.0'", 230 | "version": "==2.2.1" 231 | }, 232 | "text-unidecode": { 233 | "hashes": [ 234 | "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", 235 | "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93" 236 | ], 237 | "version": "==1.3" 238 | }, 239 | "toml": { 240 | "hashes": [ 241 | "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", 242 | "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" 243 | ], 244 | "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", 245 | "version": "==0.10.2" 246 | }, 247 | "tqdm": { 248 | "hashes": [ 249 | "sha256:5aa445ea0ad8b16d82b15ab342de6b195a722d75fc1ef9934a46bba6feafbc64", 250 | "sha256:8bb94db0d4468fea27d004a0f1d1c02da3cdedc00fe491c0de986b76a04d6b0a" 251 | ], 252 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 253 | "version": "==4.61.2" 254 | }, 255 | "troposphere": { 256 | "hashes": [ 257 | "sha256:ea2e5f2f82c224eaa1414a008b1939aae124c3e3e1dd993301968f155b333bd7" 258 | ], 259 | "ignore": [ 260 | "zappa" 261 | ], 262 | "index": "pypi", 263 | "version": "==2.7.1" 264 | }, 265 | "urllib3": { 266 | "hashes": [ 267 | "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4", 268 | "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f" 269 | ], 270 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", 271 | "version": "==1.26.6" 272 | }, 273 | "werkzeug": { 274 | "hashes": [ 275 | "sha256:1e0dedc2acb1f46827daa2e399c1485c8fa17c0d8e70b6b875b4e7f54bf408d2", 276 | "sha256:b353856d37dec59d6511359f97f6a4b2468442e454bd1c98298ddce53cac1f04" 277 | ], 278 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 279 | "version": "==0.16.1" 280 | }, 281 | "wheel": { 282 | "hashes": [ 283 | "sha256:78b5b185f0e5763c26ca1e324373aadd49182ca90e825f7853f4b2509215dc0e", 284 | "sha256:e11eefd162658ea59a60a0f6c7d493a7190ea4b9a85e335b33489d9f17e0245e" 285 | ], 286 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", 287 | "version": "==0.36.2" 288 | }, 289 | "wsgi-request-logger": { 290 | "hashes": [ 291 | "sha256:445d7ec52799562f812006394d0b4a7064b37084c6ea6bd74ea7a2136c97ed83" 292 | ], 293 | "version": "==0.4.6" 294 | }, 295 | "zappa": { 296 | "hashes": [ 297 | "sha256:46a0afc1218de17bf713a8f4cfcb4dde2ba0466d18088918b9d2bc22ec6f9434", 298 | "sha256:a3623197a1dd30faa028f62fda1b7ba5dcbb643bc6ac43ad4cffdfd89a3b85c8" 299 | ], 300 | "index": "pypi", 301 | "version": "==0.53.0" 302 | } 303 | }, 304 | "develop": {} 305 | } 306 | -------------------------------------------------------------------------------- /betaSandol/lambda_prototype_module.py: -------------------------------------------------------------------------------- 1 | from bs4 import BeautifulSoup 2 | import requests 3 | import boto3 4 | import random 5 | import datetime 6 | import json 7 | import time 8 | import return_type_generator as Generator 9 | 10 | imoge_mapping = { 11 | 'emotion':{ 12 | 'paw' : '🐾', 13 | 'smile' : '😺', 14 | 'happy' : '😸', 15 | 'sad' : '😹', 16 | 'love' : '😻', 17 | 'confident' : '😼', 18 | 'angry' : '😾', 19 | 'surprise' : '🙀', 20 | 'walk' : '🐈', 21 | 'nexpression' : '🐱' 22 | 23 | }, 24 | 'weather':{ 25 | '흐림' : '☁', 26 | '구름많음' : '⛅', 27 | 'hvy_rain' : '⛈', 28 | '비' : '☔', 29 | '약간흐림' : '🌤', 30 | '맑음' : '☀', 31 | 'sun_wth_rain' : '🌦', 32 | 'thunder' : '🌩', 33 | '바람' : '🌪', 34 | '안개' : '🌫' 35 | } 36 | } 37 | gen = Generator.Return_Type() 38 | opt = Generator.common_params() 39 | class CrawlingFunction(): 40 | def subway(self, station='정왕'): 41 | try: 42 | header = { 43 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36"} 44 | arrival_subway_api_url = "http://swopenapi.seoul.go.kr/api/subway/49664c636d6a68303634704d4f7649/json/realtimeStationArrival/0/5/" + station 45 | soup = requests.get(arrival_subway_api_url, headers=header) # 여기까지 기본 크롤링 준비 46 | 47 | if soup.status_code != 200: 48 | raise Exception('[Crawling-Error #001] API 서버에 연결을 실패했습니다 잠시후 다시 시도해주세요'+ imoge_mapping['emotion']['sad']) 49 | 50 | receptdata = soup.json() 51 | reprocess = {'subwayStatus': [], # arivlCd 52 | 'subwayPos': [], # arivlMsg2 53 | 'reqDate': None, # recptnDt 54 | 'heading': [], # trainLineNm 55 | 'arivlTime': [] # barvlDt 56 | } 57 | 58 | reprocess['reqDate'] = receptdata['realtimeArrivalList'][0]['recptnDt'] 59 | for i in range(len(receptdata['realtimeArrivalList'])): 60 | reprocess['subwayStatus'].append(receptdata['realtimeArrivalList'][i]['arvlCd']) 61 | reprocess['subwayPos'].append(receptdata['realtimeArrivalList'][i]['arvlMsg2']) 62 | reprocess['heading'].append(receptdata['realtimeArrivalList'][i]['trainLineNm']) 63 | reprocess['arivlTime'].append( 64 | receptdata['realtimeArrivalList'][i]['barvlDt']) # 여기까지 크롤링 한 내용들 기준으로 업데이트 65 | 66 | retn_str = reprocess['reqDate'] + "기준 " + station + " 도착정보입니다"+ imoge_mapping['emotion']['walk']+"\n" 67 | for i in range(len(reprocess['arivlTime'])): 68 | rept_str = str(i + 1) + ".\n[" + reprocess['heading'][i] + "] - " + "\n" + reprocess['subwayPos'][i] + "\n\n" 69 | retn_str += rept_str 70 | 71 | retn_str += imoge_mapping['emotion']['paw']+"실제 열차 도착 시간과 상이할 수 있습니다.\n" 72 | 73 | return gen.set_text(retn_str) 74 | 75 | 76 | except Exception as e: 77 | return gen.set_text("[Crawling_Error #002] 현재 열차 운행 시간이 아니거나, API 서버와의 통신에 실패하였습니다"+ imoge_mapping['emotion']['sad']) 78 | def last_subway(self): 79 | def station_no4(): 80 | target_url = "https://map.naver.com/v5/api/transit/subway/stations/455/schedule?lang=ko&stationID=455" 81 | html = requests.get(target_url) 82 | soup = BeautifulSoup(html.text, 'html.parser') 83 | 84 | last_arrival_weekday = json.loads(soup.text)["weekdaySchedule"] # 평일 85 | last_arrival_weekend = json.loads(soup.text)["sundaySchedule"] # 주말 86 | 87 | weekday_last = [last_arrival_weekday['up'][101 + i] for i in range(len(last_arrival_weekday['up']) - 101)][ 88 | ::-1] # 역순으로 변경 89 | weekend_last = [last_arrival_weekend['up'][85 + i] for i in range(len(last_arrival_weekend['up']) - 85)][ 90 | ::-1] # 역순으로 변경 91 | station = [i['headsign'] for i in weekday_last] # headsign이 가장 처음으로 나오는 경우의 인덱스를 반환하기 위한 리스트 92 | station2 = [i['headsign'] for i in weekend_last] # headsign이 가장 처음으로 나오는 경우의 인덱스를 반환하기 위한 리스트 93 | find_station_idx = station.index # 가독성 up 94 | find_station2_idx = station2.index # 가독성 up 95 | 96 | return "당고개 - (평일) ", weekday_last[find_station_idx("당고개")]['departureTime'][:-3], " (휴일) ", \ 97 | weekend_last[find_station2_idx("당고개")]['departureTime'][:-3] \ 98 | + "\n안산 - (평일) ", weekday_last[find_station_idx("안산")]['departureTime'][:-3] \ 99 | + "\n노원 - (평일) ", weekday_last[find_station_idx("노원")]['departureTime'][:-3], " (휴일) ", \ 100 | weekend_last[find_station2_idx("노원")]['departureTime'][:-3] \ 101 | + "\n금정 - (평일) ", weekday_last[find_station_idx("금정")]['departureTime'][:-3], " (휴일) ", \ 102 | weekend_last[find_station2_idx("금정")]['departureTime'][:-3] \ 103 | + "\n한성대입구 - (휴일) ", weekend_last[find_station2_idx("한성대입구")]['departureTime'][:-3] \ 104 | + "\n사당 - (휴일) ", weekend_last[find_station2_idx("사당")]['departureTime'][:-3] \ 105 | + "\n오이도 - (평일) ", last_arrival_weekday['down'][-1]['departureTime'][:-3], " (휴일) ", \ 106 | last_arrival_weekend['down'][-1]['departureTime'][:-3] 107 | 108 | def station_suin(): 109 | target_url = "https://map.naver.com/v5/api/transit/subway/stations/11120/schedule?lang=ko&stationID=11120" 110 | html = requests.get(target_url) 111 | soup = BeautifulSoup(html.text, 'html.parser') 112 | 113 | last_arrival_weekday = json.loads(soup.text)["weekdaySchedule"] # 평일 114 | last_arrival_weekend = json.loads(soup.text)["sundaySchedule"] # 주말 115 | weekday_last = [last_arrival_weekday['up'][i] for i in range(len(last_arrival_weekday['up']))][ 116 | ::-1] # 역순으로 변경 (상행) 117 | weekend_last = [last_arrival_weekend['up'][i] for i in range(len(last_arrival_weekend['up']))][ 118 | ::-1] # 역순으로 변경 119 | 120 | weekday_last2 = [last_arrival_weekday['down'][i] for i in range(len(last_arrival_weekday['down']))][ 121 | ::-1] # 역순으로 변경 (하행) 122 | weekend_last2 = [last_arrival_weekend['down'][i] for i in range(len(last_arrival_weekend['down']))][ 123 | ::-1] # 역순으로 변경 124 | 125 | station = [i['headsign'] for i in weekday_last] # headsign이 가장 처음으로 나오는 경우의 인덱스를 반환하기 위한 리스트 (상행) 126 | station2 = [i['headsign'] for i in weekend_last] # headsign이 가장 처음으로 나오는 경우의 인덱스를 반환하기 위한 리스트 127 | 128 | station_down = [i['headsign'] for i in weekday_last2] # headsign이 가장 처음으로 나오는 경우의 인덱스를 반환하기 위한 리스트 (하행) 129 | station_down2 = [i['headsign'] for i in weekend_last2] # headsign이 가장 처음으로 나오는 경우의 인덱스를 반환하기 위한 리스트 130 | 131 | find_station_idx = station.index # 가독성 up (상행) 132 | find_station2_idx = station2.index # 가독성 up 133 | 134 | find_station_down_idx = station_down.index # 가독성 up (하행) 135 | find_station_down_idx2 = station_down2.index # 가독성 up 136 | return "왕십리 - (평일) ", weekday_last[find_station_idx("왕십리")]['departureTime'][:-3], " (휴일) ", \ 137 | weekend_last[find_station2_idx("왕십리")]['departureTime'][:-3] \ 138 | + "\n죽전 - (평일) ", weekday_last[find_station_idx("죽전")]['departureTime'][:-3], " (휴일) ", \ 139 | weekend_last[find_station2_idx("죽전")]['departureTime'][:-3] \ 140 | + "\n고색 - (평일) ", weekday_last[find_station_idx("고색")]['departureTime'][:-3], " (휴일) ", \ 141 | weekend_last[find_station2_idx("고색")]['departureTime'][:-3] \ 142 | + "\n오이도 - (평일) ", weekday_last2[find_station_down_idx("오이도")]['departureTime'][:-3], " (휴일) ", \ 143 | weekend_last2[find_station_down_idx2("오이도")]['departureTime'][:-3] \ 144 | + "\n인천 - (평일) ", weekday_last2[find_station_down_idx("인천")]['departureTime'][:-3], " (휴일) ", \ 145 | weekend_last2[find_station_down_idx2("인천")]['departureTime'][:-3] 146 | 147 | # station_suin() 148 | return_str = imoge_mapping['emotion']['walk'] + '4호선 막차 시간입니다\n' 149 | return_str += ''.join(station_no4()) 150 | return_str += '\n\n' + imoge_mapping['emotion']['walk'] + '수인선 막차 시간입니다\n' 151 | return_str += ''.join(station_suin()) 152 | return gen.set_text(return_str) 153 | # boto3 주석 해제하기 154 | # def random_meal(self): 155 | # s3 = boto3.resource('s3') 156 | # bucket = s3.Bucket("sandol") 157 | # try: 158 | # local_file = "/tmp/" + "test.txt" 159 | # bucket.download_file("tmp/test.txt", local_file) # s3에서 파일을 다운로드 후 /tmp에 저장 160 | # 161 | # except Exception as e: 162 | # return str(e) 163 | # 164 | # with open("/tmp/test.txt", "r", encoding='UTF-8') as rf: 165 | # data = rf.readlines() # 파일을 전부 읽어서 list로 변환 166 | # 167 | # idx = random.randint(0, 100) 168 | # result_string = data[idx] 169 | # return "☆빠밤★\n" + result_string.split("->")[0] + " 에서, " + result_string.split("->")[1].replace("\n", 170 | # '') + " 어떠세요?" 171 | 172 | def today_covid(self): 173 | try: 174 | url = 'https://m.search.naver.com/p/csearch/content/nqapirender.nhn?where=nexearch&pkid=9005&key=diffV2API' 175 | html = requests.get(url).text 176 | data = json.loads(html) 177 | result = data['result']['list'][-1]['date'] +"일까지 코로나 발생 현황이에요"+imoge_mapping['emotion']['walk']+"\n"+imoge_mapping['emotion']['paw']+"지역발생 : " + data['result']['list'][-1]['local'] +"명\n" + imoge_mapping['emotion']['paw'] + "해외유입 : "+data['result']['list'][-1]['oversea']+"명 입니다!\n코로나 조심하세요"+imoge_mapping['emotion']['nexpression'] 178 | return gen.set_card("https://raw.githubusercontent.com/hhhminme/kpu_sandol_team/main/img/card_covid.png", is_title="코로나 확진자 수", is_description= result) 179 | 180 | except Exception as e: 181 | return gen.set_text("코로나 확진자 정보를 불러오는데 실패했어요" + imoge_mapping['emotion']['sad']) 182 | 183 | def weather(self, location): 184 | 185 | url = 'https://search.naver.com/search.naver?query=' + location + "날씨" 186 | html = requests.get(url).text 187 | soup = BeautifulSoup(html, 'html.parser') 188 | form = soup.find("div", {'class': 'api_subject_bx'}).find("div", {'class': 'main_info'}).find("div", { 189 | 'class': 'info_data'}) 190 | sub_form = soup.find("div", {'class': 'api_subject_bx'}).find("div", {'class': 'sub_info'}).find("div", { 191 | 'class': 'detail_box'}) 192 | today_temp = form.find("span", {'class': 'todaytemp'}).text 193 | 194 | try: 195 | today_temp_min = form.find("span", {'class': 'min'}).text 196 | except: 197 | today_temp_min = "-" 198 | 199 | try: 200 | today_temp_max = form.find("span", {'class': 'max'}).text 201 | except: 202 | today_temp_max = "-" 203 | 204 | try: 205 | today_temp_ray = form.find("span", {'class': 'indicator'}).find("span").text 206 | except: 207 | today_temp_ray = "-" 208 | update_date = soup.find("div", {'class': 'guide_bx _guideBox'}).find("span", {'class': 'guide_txt'}).find( 209 | 'span', {'class': 'update'}).text 210 | 211 | today_weather = form.find("ul").find("li").text.strip() 212 | today_dust_list = sub_form.find_all("dd") 213 | today_dust10 = today_dust_list[0].text 214 | today_dust25 = today_dust_list[1].text 215 | 216 | try: 217 | weather_icon = imoge_mapping['weather'][today_weather.split(', ')[0]] 218 | 219 | except: 220 | weather_icon = '' 221 | 222 | result = imoge_mapping['emotion']['walk']+ location + "의 기상정보입니다" \ 223 | "\n\n기온 : " + today_temp + "°C (" + today_temp_min + "C / " + today_temp_max + "C)\n" + weather_icon + today_weather + \ 224 | "\n\n미세먼지 : " + today_dust10.replace("㎥", "㎥, ") + "\n초미세먼지 : " + today_dust25.replace("㎥", "㎥, ") + \ 225 | "\n자외선 : " + today_temp_ray + "이에요!\n\n" + update_date + "시에 업데이트 된 네이버 날씨 정보입니다!" 226 | 227 | return gen.set_text(result) 228 | 229 | def announcement(self): 230 | URL = "http://www.kpu.ac.kr/front/boardlist.do?bbsConfigFK=1&siteGubun=14&menuGubun=1" 231 | ORIGIN = "http://www.kpu.ac.kr" 232 | req = requests.get(URL) 233 | soup = BeautifulSoup(req.text, 'html.parser') 234 | announce_list = soup.find('table').find('tbody').find_all('tr') 235 | result = [] # title, date, URl 236 | 237 | for i in range (5): 238 | result.append([announce_list[i].find_all("td")[1].find('a').text.strip(), announce_list[i].find_all("td")[4].text.strip(), ORIGIN+announce_list[i].find_all("td")[1].find("a")['href']]) 239 | return gen.set_list("교내 최신 학사공지 내역입니다", result, is_Button= opt.Button(label="바로가기", action="webLink", webLinkUrl = "http://www.kpu.ac.kr/contents/main/cor/noticehaksa.html")) 240 | 241 | class s3IOEvent(): 242 | def upload_feedback(self, params): # 피드백 업로드 기능 243 | s3 = boto3.resource('s3') 244 | bucket = s3.Bucket('sandol') # 이 부분 해당 버킷 생성 후 적절히 수정 예정 245 | params = "[" + str(datetime.datetime.today()) + "] :" + params + "\n" 246 | try: 247 | local_file = "/tmp/" + "feedback.txt" 248 | bucket.download_file("feedback.txt", local_file) 249 | except Exception as e: 250 | return gen.set_text("[File-Open-Error #101] 서버에서 피드백 파일을 불러오는 중 오류가 발생했어요"+ imoge_mapping['emotion']['sad']) 251 | 252 | try: 253 | with open("/tmp/feedback.txt", "a", encoding="UTF-8") as f: 254 | f.writelines(params) 255 | except Exception as e: 256 | return gen.set_text("[File-Open-Error #102] 파일을 저장 중 오류가 발생했습니다" + imoge_mapping['emotion']['sad']) 257 | 258 | try: 259 | s3 = boto3.client('s3') # 이 부분 해당 버킷 생성 후 적절히 수정 예정 260 | s3.upload_file("/tmp/feedback.txt", 'sandol', 'feedback.txt') 261 | 262 | except Exception as e: 263 | return gen.set_text("[File-Open-Error #103] 파일을 서버에 업로드 하는 중 오류가 발생했습니다" + imoge_mapping['emotion']['sad']) 264 | 265 | return gen.set_text("피드백 주셔서 감사해요! 빠른 시일내에 검토 후 적용해볼게요!" + imoge_mapping['emotion']['love']) 266 | 267 | def read_feedback(self, params, bot_id): # 피드백 읽기 기능 (관리자 전용) 268 | sandol_team = ['d367f2ec55f41b4207156f4b8fce5ce885b05d8c3b238cf8861c55a9012f6f5895', 269 | '339b0444bfabbffa0f13508ea7c45b61675b5720234cca8f73cd7421c22de9e546', 270 | '04eabc8b965bf5ae6cccb122a18521969cc391162e3fd5f61b85efe8bb12e5e98a', 271 | 'def99464e022b38389697fe68d54bbba723d1da291094c19bbf5eaace7b059a997'] 272 | 273 | if bot_id not in sandol_team: 274 | return gen.set_text("권한이 없습니다") 275 | 276 | if params == '1': # 읽기 277 | s3 = boto3.resource('s3') 278 | bucket = s3.Bucket("sandol") 279 | 280 | try: 281 | local_file = "/tmp/" + "feedback.txt" 282 | bucket.download_file("feedback.txt", local_file) 283 | except Exception as e: 284 | return gen.set_text("[File-Open-Error #111] 서버에서 피드백 파일을 불러오는 중 오류가 발생했어요 ") 285 | 286 | try: 287 | with open("/tmp/feedback.txt", "r", encoding="UTF-8") as f: # 이 부분 해당 버킷 생성 후 적절히 수정 예정 288 | txt = ''.join(f.readlines()) 289 | return gen.set_text(txt) 290 | 291 | except Exception as e: 292 | return gen.set_text("[File-Open-Error #112] 파일을 읽는 중 오류가 발생했습니다") 293 | 294 | elif params == '2': # 지우기 295 | s3 = boto3.resource('s3') 296 | bucket = s3.Bucket('sandol') # 이 부분 해당 버킷 생성 후 적절히 수정 예정 297 | params = "#feedbacks\n" 298 | try: 299 | local_file = "/tmp/" + "feedback.txt" 300 | bucket.download_file("feedback.txt", local_file) 301 | except Exception as e: 302 | return gen.set_text("[File-Open-Error #113] 서버에서 피드백 파일을 불러오는 중 오류가 발생했어요") 303 | 304 | try: 305 | with open("/tmp/feedback.txt", "w", encoding="UTF-8") as f: # 이 부분 해당 버킷 생성 후 적절히 수정 예정 306 | f.writelines(params) 307 | except Exception as e: 308 | return gen.set_text("[File-Open-Error #114] 파일을 삭제 중 오류가 발생했습니다") 309 | 310 | try: 311 | s3 = boto3.client('s3') 312 | s3.upload_file("/tmp/feedback.txt", 'sandol', 'feedback.txt') 313 | 314 | except Exception as e: 315 | return gen.set_text("[File-Open-Error #115] 파일을 서버에 업로드 하는 중 오류가 발생했습니다") 316 | return gen.set_text("성공적으로 파일 내용을 삭제했습니다") 317 | 318 | 319 | else: # param error 320 | return gen.set_text('[Param-Error #116] 잘못된 파라미터') 321 | 322 | def upload_meal(self, store_name, lunch_list, dinner_list,input_date, owner_id): # 식사 업로드 기능 323 | owner_id_dec = {'미가식당': "32d8a05a91242ffb4c64b5630ec55953121dffd83a121d985e26e06e2c457197e6", 324 | '웰스프레쉬': "d367f2ec55f41b4207156f4b8fce5ce885b05d8c3b238cf8861c55a9012f6f5895", 325 | '푸드라운지': "46f338132e6af63c32c07220c318f0e7c570e8eb6f375c9e8bb59ce33776f27c4c" 326 | } 327 | sandol_team = ['d367f2ec55f41b4207156f4b8fce5ce885b05d8c3b238cf8861c55a9012f6f5895', 328 | '339b0444bfabbffa0f13508ea7c45b61675b5720234cca8f73cd7421c22de9e546', 329 | '04eabc8b965bf5ae6cccb122a18521969cc391162e3fd5f61b85efe8bb12e5e98a', 330 | 'def99464e022b38389697fe68d54bbba723d1da291094c19bbf5eaace7b059a997'] 331 | 332 | if (owner_id_dec[store_name] != owner_id) and owner_id not in sandol_team: 333 | return gen.set_text("[Permission-Error #121-1] 권한이 없습니다" + imoge_mapping['emotion']['angry']) 334 | 335 | if store_name not in owner_id_dec.keys(): 336 | return gen.set_text("[Not-Found-Error #121-2] 해당하는 식당이 없습니다."+ imoge_mapping['emotion']['sad']) 337 | 338 | else: 339 | store_file = "../test_stored_data/restaurant_menu.txt" 340 | s3 = boto3.resource('s3') 341 | bucket = s3.Bucket("sandol") 342 | local_file = "/tmp/" + store_file 343 | 344 | try: 345 | # local_file = "./restaurant_menu/" + store_file 346 | s3.meta.client.download_file("sandol", "restaurant_menu.txt", '/tmp/restaurant_menu.txt') 347 | 348 | except Exception as e: 349 | return gen.set_text("[File-Open-Error #122] 저장소에서 파일을 찾을 수 없습니다."+ imoge_mapping['emotion']['sad']) 350 | 351 | with open(local_file, "r", encoding="UTF-8") as f: 352 | try: 353 | data = f.readlines() 354 | print(data) 355 | menu_info = data[data.index("🐾"+store_name+"\n") + 1].replace('\'','').replace("\n","").split(", ") #내부 데이터 처리 356 | menu_info[0] = input_date 357 | 358 | menu_info[1] = lunch_list.replace(", ",",").replace(" ",",") 359 | menu_info[2] = dinner_list.replace(",","").replace(" ",",") #메뉴 수정 360 | 361 | menu_info[1] = lunch_list.replace(" ",",") 362 | menu_info[2] = dinner_list.replace(" ",",") #메뉴 수정 363 | 364 | menu_info[1] = lunch_list.replace(" ",",") 365 | menu_info[2] = dinner_list.replace(" ",",") #메뉴 수정 366 | 367 | data[data.index("🐾"+store_name+"\n") + 1] = str(menu_info)[1:-1] + "\n" #최종 문자열 368 | with open(local_file, "w", encoding='UTF-8') as rf: 369 | rf.writelines(data) 370 | 371 | 372 | except Exception as e: 373 | return gen.set_text("[File-Open-Error #123]파일을 수정하는 중 오류가 발생했습니다."+ imoge_mapping['emotion']['sad']) 374 | try: 375 | s3 = boto3.client('s3') # 이 부분 해당 버킷 생성 후 적절히 수정 예정 376 | s3.upload_file(local_file, 'sandol', store_file) 377 | 378 | except Exception: 379 | return gen.set_text("[File-Open-Error #124]파일을 저장소에 업로드하는 중 오류가 발생했습니다."+ imoge_mapping['emotion']['sad']) 380 | 381 | 382 | return gen.set_text("네! 학생들에게 잘 전달할게요! 감사합니다!"+ imoge_mapping['emotion']['walk']) 383 | 384 | 385 | def read_meal(self): 386 | store_file = "../test_stored_data/restaurant_menu.txt" 387 | s3 = boto3.resource('s3') # 이 부분 해당 버킷 생성 후 적절히 수정 예정 388 | bucket = s3.Bucket("sandol") 389 | try: 390 | local_file = "/tmp/" + store_file 391 | # local_file = "./restaurant_menu/" + store_file #이 부분 해당 버킷 생성 후 적절히 수정 예정 392 | bucket.download_file(store_file, local_file) 393 | 394 | except Exception: 395 | return gen.set_text( 396 | "[File-Open-Error #131] 저장소에서 파일을 가져오는데 실패했습니다" + imoge_mapping['emotion']['sad']) # 파일을 /tmp/에 복사하여 다운로드 397 | 398 | try: 399 | meal_gen = Generator.Return_Type() 400 | t = ['월', '화', '수', '목', '금', '토', '일'] 401 | return_string = '' 402 | with open(local_file, "r", encoding='UTF-8') as f: 403 | data = f.readlines() 404 | for restaurant in range(0, len(data), 2): 405 | menu_list = data[restaurant + 1].replace("\'", '').split(", ") 406 | last_update_date = datetime.date.fromisoformat(menu_list[0]) 407 | if restaurant == 2: 408 | continue 409 | 410 | ret = data[restaurant].replace("\n", '').replace("🐾", imoge_mapping['emotion'][ 411 | 'walk']) + " [" + str(last_update_date) + " " + t[last_update_date.weekday()] + "요일]\n" + \ 412 | imoge_mapping['emotion']['paw'] + "중식 : " + menu_list[1] + "\n" + \ 413 | imoge_mapping['emotion']['paw'] + "석식 : " + menu_list[2] 414 | 415 | meal_gen.set_text(ret, is_init=False) 416 | 417 | return_string = meal_gen.set_text("웰스프레쉬\nhttps://ibook.kpu.ac.kr/Viewer/menu01", is_init=False) 418 | return return_string 419 | 420 | except Exception as e: 421 | return gen.set_text("[File-Open-Error #132] 파일을 여는 중 오류가 발생했어요.." + imoge_mapping['emotion']['sad'] + str(e)) 422 | 423 | def reset_meal(self, bot_id, date): 424 | sandol_team = ['d367f2ec55f41b4207156f4b8fce5ce885b05d8c3b238cf8861c55a9012f6f5895', 425 | '339b0444bfabbffa0f13508ea7c45b61675b5720234cca8f73cd7421c22de9e546', 426 | '04eabc8b965bf5ae6cccb122a18521969cc391162e3fd5f61b85efe8bb12e5e98a', 427 | 'def99464e022b38389697fe68d54bbba723d1da291094c19bbf5eaace7b059a997'] 428 | if bot_id not in sandol_team: 429 | return gen.set_text("[Permission-Error #141] 권한이 없습니다" + imoge_mapping['emotion']['angry']) 430 | 431 | store_file = "../test_stored_data/restaurant_menu.txt" 432 | s3 = boto3.resource('s3') 433 | bucket = s3.Bucket("sandol") 434 | local_file = "/tmp/" + store_file 435 | 436 | try: 437 | # local_file = "./restaurant_menu/" + store_file 438 | s3.meta.client.download_file("sandol", "restaurant_menu.txt", '/tmp/restaurant_menu.txt') 439 | 440 | except Exception as e: 441 | return gen.set_text("[File-Open-Error #142] 저장소에서 파일을 찾을 수 없습니다." + imoge_mapping['emotion']['sad']) 442 | try: 443 | with open(local_file, "w", encoding="UTF-8") as f: 444 | rest_name = [imoge_mapping['emotion']['paw'] + "미가식당\n", imoge_mapping['emotion']['paw'] + "웰스프레쉬\n", 445 | imoge_mapping['emotion']['paw'] + "푸드라운지\n"] 446 | 447 | return_string = '' 448 | for i in range(len(rest_name)): 449 | return_string += rest_name[i] + "\'" + date + "\', \'업데이트되지않았습니다\', \'업데이트되지않았습니다\'\n" 450 | f.writelines(return_string) 451 | 452 | except Exception as e: 453 | return gen.set_text("[File-Open-Error #143]파일을 수정하는 중 오류가 발생했습니다." + imoge_mapping['emotion']['sad']) 454 | 455 | try: 456 | s3 = boto3.client('s3') # 이 부분 해당 버킷 생성 후 적절히 수정 예정 457 | s3.upload_file(local_file, 'sandol', store_file) 458 | 459 | except Exception: 460 | return gen.set_text("[File-Open-Error #144]파일을 저장소에 업로드하는 중 오류가 발생했습니다." + imoge_mapping['emotion']['sad']) 461 | return gen.set_text("파일을 정상적으로 초기화했습니다" + imoge_mapping['emotion']['happy']) 462 | 463 | class Test(): 464 | def __init__(self, time: str = "00:00:00", station_no: str = "455") ->None: 465 | self.time = datetime.datetime.strptime(time, '%H:%M:%S') #time 모듈로 변환 466 | self.station_no = station_no 467 | self.rail: str 468 | self.station_name: str 469 | self.data = self.get_data() 470 | 471 | def get_data(self) -> dict: 472 | URL = "https://map.naver.com/v5/api/transit/subway/stations/"+self.station_no+"/schedule?lang=ko&stationID="+self.station_no 473 | html = requests.get(URL).text 474 | soup = BeautifulSoup(html, 'html.parser') 475 | 476 | json_data = json.loads(soup.text) 477 | # print(json_data) 478 | return json_data 479 | 480 | def arrival_time(self) -> str: 481 | return_data = '' 482 | 483 | try: 484 | if self.data['todayServiceDay']['name'] == '평일': # 평일 시간표 485 | schedule_data_up = self.data['weekdaySchedule']['up'] 486 | schedule_data_down = self.data['weekdaySchedule']['down'] 487 | 488 | it = schedule_data_up.__iter__() # 상행선 489 | flag = False 490 | for i in schedule_data_up: 491 | it.__next__() 492 | if datetime.datetime.strptime(i['departureTime'], '%H:%M:%S') > self.time: 493 | return_data += i['headsign'] +"방면 "+ i['departureTime'] + ", " + it.__next__()['departureTime'] 494 | # print(i['departureTime'], end=' ') 495 | # print(it.__next__()['departureTime']) 496 | flag = True 497 | break 498 | else: 499 | continue 500 | 501 | if flag == False: 502 | return_data += schedule_data_up[-1]['headsign'] + schedule_data_up[-1]['departureTime']+ " 막차입니다" 503 | 504 | return_data += "\n\n" 505 | 506 | flag = False 507 | it = schedule_data_down.__iter__() # 하행선 508 | for i in schedule_data_down: 509 | it.__next__() 510 | if datetime.datetime.strptime(i['departureTime'], '%H:%M:%S') > self.time: 511 | return_data += i['headsign'] +"방면 "+i['departureTime'] + ", " + it.__next__()['departureTime'] 512 | flag = True 513 | # print(i['departureTime'], end=' ') 514 | # print(it.__next__()['departureTime'], end=' ') 515 | break 516 | 517 | else: 518 | continue 519 | 520 | if flag == False: 521 | return_data += schedule_data_down[-1]['headsign'] + schedule_data_down[-1]['departureTime']+ " 막차입니다" 522 | 523 | else: # 주말 시간표 524 | schedule_data_up = self.data['sundaySchedule']['up'] 525 | schedule_data_down = self.data['sundaySchedule']['down'] 526 | 527 | flag = False 528 | 529 | it = schedule_data_up.__iter__() 530 | for i in schedule_data_up: 531 | it.__next__() 532 | if datetime.datetime.strptime(i['departureTime'], '%H:%M:%S') > self.time: 533 | return_data += i['headsign'] +"방면 "+i['departureTime'] + ", " + it.__next__()['departureTime'] 534 | flag = True 535 | # print(i['departureTime'], end=' ') 536 | # print(it.__next__()['departureTime']) 537 | break 538 | 539 | else: 540 | continue 541 | 542 | if flag == False: 543 | return_data += schedule_data_up[-1]['headsign'] +schedule_data_up[-1]['departureTime']+ " 막차입니다" 544 | return_data += "\n\n" 545 | 546 | flag = False 547 | it = schedule_data_down.__iter__() 548 | for i in schedule_data_down: 549 | it.__next__() 550 | if datetime.datetime.strptime(i['departureTime'], '%H:%M:%S') > self.time: 551 | return_data += i['headsign'] +"방면 "+i['departureTime'] + ", " + it.__next__()['departureTime'] 552 | flag = True 553 | # print(i['departureTime'], end=' ') 554 | # print(it.__next__()['departureTime']) 555 | break 556 | 557 | else: 558 | continue 559 | 560 | if flag == False: 561 | return_data += schedule_data_down[-1]['headsign'] +schedule_data_down[-1]['departureTime'] + " 막차입니다" 562 | 563 | except Exception as e: 564 | return str(e) 565 | 566 | return return_data 567 | 568 | def get_time(self): 569 | return gen.set_text(self.time) 570 | #boto3 주석 해제하기 571 | # print(Test("23:59:11", "455").arrival_time()) -------------------------------------------------------------------------------- /statistic/sandol_200109-211018.csv: -------------------------------------------------------------------------------- 1 | ,날짜,활성사용자,웰컴 및 event 수신 제외 활성사용자,신규 사용자,재방문사용자,누적사용자,전체 세션 수,세션 당 평균 메시지 수,친구수,채널 추가수 합계,채팅 요청 친구수 2 | 0,2020-01-09,1,1,1,0,1,2,2.0,1,1,1 3 | 1,2020-01-10,0,0,0,0,1,0,0.0,1,0,0 4 | 2,2020-01-11,0,0,0,0,1,0,0.0,1,0,0 5 | 3,2020-01-12,1,1,0,1,1,1,1.0,1,0,1 6 | 4,2020-01-13,1,1,1,0,2,1,3.0,1,0,1 7 | 5,2020-01-14,1,1,0,1,2,1,3.0,1,0,1 8 | 6,2020-01-15,0,0,0,0,2,0,0.0,1,0,0 9 | 7,2020-01-16,8,7,7,1,9,9,3.56,7,6,7 10 | 8,2020-01-17,1,1,0,1,9,1,14.0,7,0,1 11 | 9,2020-01-18,0,0,0,0,9,0,0.0,7,0,0 12 | 10,2020-01-19,1,1,1,0,10,1,4.0,7,0,1 13 | 11,2020-01-20,1,1,0,1,10,1,3.0,7,0,1 14 | 12,2020-01-21,1,1,0,1,10,1,2.0,7,0,1 15 | 13,2020-01-22,1,1,0,1,10,1,1.0,7,0,1 16 | 14,2020-01-23,0,0,0,0,10,0,0.0,7,0,0 17 | 15,2020-01-24,0,0,0,0,10,0,0.0,7,0,0 18 | 16,2020-01-25,0,0,0,0,10,0,0.0,7,0,0 19 | 17,2020-01-26,1,1,0,1,10,2,2.0,7,0,1 20 | 18,2020-01-27,0,0,0,0,10,0,0.0,7,0,0 21 | 19,2020-01-28,19,16,18,1,28,32,3.94,20,13,15 22 | 20,2020-01-29,19,19,9,10,37,41,4.32,27,7,18 23 | 21,2020-01-30,29,25,21,8,58,42,3.36,44,17,21 24 | 22,2020-01-31,10,10,4,6,62,14,2.93,47,3,10 25 | 23,2020-02-01,6,6,2,4,64,10,3.0,49,2,5 26 | 24,2020-02-02,7,7,1,6,65,9,3.11,49,0,7 27 | 25,2020-02-03,37,34,26,11,91,45,3.29,74,25,32 28 | 26,2020-02-04,32,32,14,18,105,42,2.1,83,9,31 29 | 27,2020-02-05,32,32,11,21,116,48,2.63,89,6,31 30 | 28,2020-02-06,40,40,10,30,126,50,1.88,97,8,33 31 | 29,2020-02-07,30,30,3,27,129,42,1.5,100,3,28 32 | 30,2020-02-08,7,7,4,3,133,7,5.0,104,4,7 33 | 31,2020-02-09,3,3,0,3,133,4,1.0,104,0,2 34 | 32,2020-02-10,24,24,4,20,137,30,1.57,108,4,23 35 | 33,2020-02-11,32,31,4,28,141,49,1.71,111,3,29 36 | 34,2020-02-12,33,32,14,19,155,50,2.36,125,14,30 37 | 35,2020-02-13,77,67,45,32,200,125,2.23,169,44,64 38 | 36,2020-02-14,46,44,11,35,211,59,1.63,180,11,42 39 | 37,2020-02-15,10,10,2,8,213,11,2.0,181,1,10 40 | 38,2020-02-16,2,2,0,2,213,2,1.0,181,0,2 41 | 39,2020-02-17,40,40,2,38,215,89,2.48,183,2,38 42 | 40,2020-02-18,48,47,6,42,221,70,2.0,189,6,41 43 | 41,2020-02-19,45,44,8,37,229,98,2.51,193,4,43 44 | 42,2020-02-20,40,40,3,37,232,47,1.81,194,1,35 45 | 43,2020-02-21,28,28,1,27,233,53,2.02,194,0,25 46 | 44,2020-02-22,5,5,1,4,234,5,2.4,194,1,5 47 | 45,2020-02-23,2,2,0,2,234,2,1.5,194,0,1 48 | 46,2020-02-24,34,34,3,31,237,55,2.02,195,1,30 49 | 47,2020-02-25,35,34,6,29,243,60,2.08,201,6,33 50 | 48,2020-02-26,32,32,4,28,247,53,1.91,204,4,28 51 | 49,2020-02-27,30,30,1,29,248,35,1.49,205,1,27 52 | 50,2020-02-28,24,24,2,22,250,31,1.9,207,2,22 53 | 51,2020-02-29,5,5,0,5,250,5,2.4,207,0,5 54 | 52,2020-03-01,5,5,0,5,250,5,1.6,207,0,5 55 | 53,2020-03-02,28,28,3,25,253,34,1.44,208,1,25 56 | 54,2020-03-03,31,31,1,30,254,44,1.48,209,1,27 57 | 55,2020-03-04,36,36,5,31,259,40,2.02,213,4,33 58 | 56,2020-03-05,33,33,4,29,263,40,1.85,215,2,32 59 | 57,2020-03-06,33,33,5,28,268,57,1.21,220,5,29 60 | 58,2020-03-07,7,7,0,7,268,7,4.0,220,0,7 61 | 59,2020-03-08,5,5,2,3,270,6,2.5,222,2,4 62 | 60,2020-03-09,38,38,3,35,273,44,1.59,224,2,36 63 | 61,2020-03-10,34,34,3,31,276,42,1.33,226,3,25 64 | 62,2020-03-11,43,43,3,40,279,57,1.79,228,2,37 65 | 63,2020-03-12,40,40,1,39,280,65,1.58,229,1,40 66 | 64,2020-03-13,42,42,3,39,283,48,1.42,230,1,38 67 | 65,2020-03-14,5,5,2,3,285,8,4.13,231,2,5 68 | 66,2020-03-15,1,1,0,1,285,2,1.0,231,0,1 69 | 67,2020-03-16,36,36,1,35,286,44,1.25,231,0,34 70 | 68,2020-03-17,50,50,1,49,287,54,1.52,232,1,46 71 | 69,2020-03-18,44,44,3,41,290,52,1.42,234,2,40 72 | 70,2020-03-19,44,44,3,41,293,67,2.25,234,0,39 73 | 71,2020-03-20,39,39,2,37,295,67,2.94,236,2,36 74 | 72,2020-03-21,5,5,0,5,295,5,1.2,236,0,5 75 | 73,2020-03-22,2,2,0,2,295,2,1.0,236,0,2 76 | 74,2020-03-23,43,43,2,41,297,58,1.63,237,1,39 77 | 75,2020-03-24,46,46,4,42,301,63,1.62,240,3,44 78 | 76,2020-03-25,38,38,1,37,302,42,1.31,240,0,31 79 | 77,2020-03-26,40,40,4,36,306,43,1.65,242,2,35 80 | 78,2020-03-27,37,37,1,36,307,53,1.66,243,1,32 81 | 79,2020-03-28,0,0,0,0,307,0,0.0,243,0,0 82 | 80,2020-03-29,2,2,1,1,308,2,2.0,243,0,2 83 | 81,2020-03-30,42,42,2,40,310,73,1.81,244,1,40 84 | 82,2020-03-31,43,43,1,42,311,60,1.35,245,1,35 85 | 83,2020-04-01,42,42,2,40,313,45,1.49,247,2,33 86 | 84,2020-04-02,45,45,4,41,317,53,1.38,250,3,40 87 | 85,2020-04-03,0,43,0,0,317,58,1.66,255,5,37 88 | 86,2020-04-04,0,3,0,0,317,3,2.0,256,1,3 89 | 87,2020-04-05,0,0,0,0,317,0,0.0,256,0,0 90 | 88,2020-04-06,0,47,0,0,317,51,1.35,258,2,40 91 | 89,2020-04-07,0,45,0,0,317,80,1.56,258,0,41 92 | 90,2020-04-08,0,50,0,0,317,56,1.52,261,3,43 93 | 91,2020-04-09,0,44,0,0,317,49,1.45,262,1,38 94 | 92,2020-04-10,0,42,0,0,317,95,2.18,266,4,38 95 | 93,2020-04-11,0,2,0,0,317,2,2.0,266,0,2 96 | 94,2020-04-12,0,1,0,0,317,1,1.0,266,0,1 97 | 95,2020-04-13,0,44,0,0,317,75,1.37,266,0,37 98 | 96,2020-04-14,0,36,0,0,317,64,1.91,266,0,33 99 | 97,2020-04-15,0,9,0,0,317,12,1.42,266,0,8 100 | 98,2020-04-16,0,49,0,0,317,83,1.43,270,4,47 101 | 99,2020-04-17,0,37,0,0,317,67,1.63,271,1,33 102 | 100,2020-04-18,0,1,0,0,317,1,1.0,271,0,1 103 | 101,2020-04-19,0,1,0,0,317,1,1.0,271,0,0 104 | 102,2020-04-20,0,45,0,0,317,61,1.54,272,1,41 105 | 103,2020-04-21,0,38,0,0,317,44,1.59,274,2,34 106 | 104,2020-04-22,0,45,0,0,317,55,1.38,275,1,41 107 | 105,2020-04-23,0,45,0,0,317,57,1.28,275,0,40 108 | 106,2020-04-24,0,36,0,0,317,66,1.52,276,1,33 109 | 107,2020-04-25,0,2,0,0,317,3,1.67,276,0,2 110 | 108,2020-04-26,0,0,0,0,317,0,0.0,276,0,0 111 | 109,2020-04-27,0,48,0,0,317,57,1.46,277,1,45 112 | 110,2020-04-28,0,32,0,0,317,42,1.29,278,1,25 113 | 111,2020-04-29,0,28,0,0,317,30,1.23,278,0,24 114 | 112,2020-04-30,0,5,0,0,317,5,1.4,278,0,5 115 | 113,2020-05-01,0,21,0,0,317,37,1.43,278,0,20 116 | 114,2020-05-02,0,2,0,0,317,2,1.0,278,0,2 117 | 115,2020-05-03,0,4,0,0,317,4,4.25,279,1,3 118 | 116,2020-05-04,0,27,0,0,317,44,1.66,283,4,23 119 | 117,2020-05-05,0,6,0,0,317,6,2.5,283,0,7 120 | 118,2020-05-06,0,33,0,0,317,38,1.26,284,1,31 121 | 119,2020-05-07,0,39,0,0,317,48,1.19,285,1,35 122 | 120,2020-05-08,0,36,0,0,317,43,1.49,286,1,32 123 | 121,2020-05-09,0,4,0,0,317,4,1.75,287,1,4 124 | 122,2020-05-10,0,5,0,0,317,5,2.8,287,0,5 125 | 123,2020-05-11,0,106,0,0,317,151,2.72,352,65,102 126 | 124,2020-05-12,0,78,0,0,317,90,1.84,365,13,71 127 | 125,2020-05-13,0,92,0,0,317,106,1.8,374,9,84 128 | 126,2020-05-14,0,84,0,0,317,101,1.43,380,6,71 129 | 127,2020-05-15,0,71,0,0,317,84,1.82,390,10,65 130 | 128,2020-05-16,0,15,0,0,317,18,2.44,392,2,13 131 | 129,2020-05-17,0,7,0,0,317,7,1.71,393,1,7 132 | 130,2020-05-18,0,77,0,0,317,90,1.44,396,3,73 133 | 131,2020-05-19,0,90,0,0,317,140,1.96,405,9,85 134 | 132,2020-05-20,0,80,0,0,317,118,1.67,407,2,72 135 | 133,2020-05-21,0,80,0,0,317,94,1.5,413,6,72 136 | 134,2020-05-22,0,69,0,0,317,85,1.51,416,3,59 137 | 135,2020-05-23,0,10,0,0,317,10,2.8,416,0,10 138 | 136,2020-05-24,0,3,0,0,317,4,1.25,416,0,2 139 | 137,2020-05-25,0,90,0,0,317,121,1.68,423,7,82 140 | 138,2020-05-26,0,74,0,0,317,82,1.45,428,5,70 141 | 139,2020-05-27,0,83,0,0,317,98,1.84,434,6,75 142 | 140,2020-05-28,0,84,0,0,317,93,1.4,437,3,77 143 | 141,2020-05-29,0,74,0,0,317,96,1.55,441,4,70 144 | 142,2020-05-30,18,17,3,15,547,22,2.73,444,3,15 145 | 143,2020-05-31,0,12,0,0,547,13,1.46,444,0,11 146 | 144,2020-06-01,0,91,0,0,547,108,1.39,445,1,79 147 | 145,2020-06-02,0,98,0,0,547,121,1.54,447,2,89 148 | 146,2020-06-03,0,90,0,0,547,120,1.49,450,3,76 149 | 147,2020-06-04,0,90,0,0,547,104,1.48,452,2,81 150 | 148,2020-06-05,0,84,0,0,547,125,1.7,454,2,76 151 | 149,2020-06-06,0,20,0,0,547,21,1.95,457,3,19 152 | 150,2020-06-07,0,2,0,0,547,2,1.0,457,0,1 153 | 151,2020-06-08,0,108,0,0,547,133,1.56,462,5,99 154 | 152,2020-06-09,0,100,0,0,547,118,1.5,470,9,86 155 | 153,2020-06-10,99,99,4,95,585,132,1.55,472,2,90 156 | 154,2020-06-11,0,104,0,0,585,114,1.64,479,7,91 157 | 155,2020-06-12,0,104,0,0,585,116,1.39,482,3,94 158 | 156,2020-06-13,0,24,0,0,585,28,2.07,483,1,23 159 | 157,2020-06-14,0,15,0,0,585,17,2.12,483,0,13 160 | 158,2020-06-15,0,107,0,0,585,126,1.4,486,3,95 161 | 159,2020-06-16,0,111,0,0,585,122,1.32,487,1,95 162 | 160,2020-06-17,0,112,0,0,585,134,1.69,498,11,101 163 | 161,2020-06-18,0,124,0,0,585,164,1.48,504,6,114 164 | 162,2020-06-19,0,117,0,0,585,136,1.38,507,3,96 165 | 163,2020-06-20,0,42,0,0,585,48,2.0,518,11,38 166 | 164,2020-06-21,0,18,0,0,585,19,1.84,520,2,17 167 | 165,2020-06-22,0,128,0,0,585,152,1.46,525,5,118 168 | 166,2020-06-23,0,124,0,0,585,139,1.47,528,3,114 169 | 167,2020-06-24,0,102,0,0,585,126,1.74,533,5,90 170 | 168,2020-06-25,0,109,0,0,585,126,1.38,537,4,100 171 | 169,2020-06-26,0,86,0,0,585,127,1.5,538,1,76 172 | 170,2020-06-27,0,12,0,0,585,12,1.92,539,1,10 173 | 171,2020-06-28,0,1,0,0,585,1,2.0,539,0,1 174 | 172,2020-06-29,0,79,0,0,585,108,1.44,543,4,72 175 | 173,2020-06-30,0,62,0,0,585,75,1.32,543,0,58 176 | 174,2020-07-01,0,63,0,0,585,91,1.49,545,2,59 177 | 175,2020-07-02,0,62,0,0,585,83,1.25,545,0,56 178 | 176,2020-07-03,0,50,0,0,585,64,1.59,546,1,49 179 | 177,2020-07-04,0,4,0,0,585,4,1.5,546,0,4 180 | 178,2020-07-05,0,8,0,0,585,8,1.5,547,1,8 181 | 179,2020-07-06,0,61,0,0,585,88,1.28,547,0,58 182 | 180,2020-07-07,0,68,0,0,585,89,1.31,550,3,64 183 | 181,2020-07-08,0,82,0,0,585,104,1.4,556,6,73 184 | 182,2020-07-09,0,67,0,0,585,80,1.38,558,2,60 185 | 183,2020-07-10,0,47,0,0,585,57,1.37,558,0,43 186 | 184,2020-07-11,0,7,0,0,585,7,1.57,559,1,4 187 | 185,2020-07-12,0,5,0,0,585,5,1.4,558,0,4 188 | 186,2020-07-13,0,69,0,0,585,96,1.45,562,4,65 189 | 187,2020-07-14,0,68,0,0,585,83,1.37,564,2,62 190 | 188,2020-07-15,0,75,0,0,585,127,1.72,567,3,65 191 | 189,2020-07-16,0,0,0,0,585,97,1.43,575,8,73 192 | 190,2020-07-17,0,55,0,0,585,64,1.36,575,0,49 193 | 191,2020-07-18,0,9,0,0,585,11,2.45,575,0,8 194 | 192,2020-07-19,0,5,0,0,585,6,1.83,575,0,5 195 | 193,2020-07-20,0,76,0,0,585,101,1.46,580,5,71 196 | 194,2020-07-21,0,93,0,0,585,118,1.46,588,8,76 197 | 195,2020-07-22,0,93,0,0,585,121,1.41,594,6,84 198 | 196,2020-07-23,0,87,0,0,585,121,1.31,594,0,72 199 | 197,2020-07-24,0,83,0,0,585,109,1.37,600,6,78 200 | 198,2020-07-25,0,7,0,0,585,8,1.5,600,0,7 201 | 199,2020-07-26,0,3,0,0,585,3,1.67,601,1,3 202 | 200,2020-07-27,0,75,0,0,585,80,1.21,601,1,69 203 | 201,2020-07-28,0,79,0,0,585,91,1.43,603,2,73 204 | 202,2020-07-29,0,79,0,0,585,92,1.25,603,0,68 205 | 203,2020-07-30,0,78,0,0,585,82,1.27,604,1,62 206 | 204,2020-07-31,0,62,0,0,585,77,1.48,606,2,53 207 | 205,2020-08-01,0,1,0,0,585,1,1.0,606,0,1 208 | 206,2020-08-02,0,2,0,0,585,2,1.0,606,0,2 209 | 207,2020-08-03,50,60,17,33,602,75,1.35,607,1,53 210 | 208,2020-08-04,0,65,0,0,602,76,1.2,610,3,57 211 | 209,2020-08-05,0,66,0,0,602,70,1.26,610,0,55 212 | 210,2020-08-06,0,59,0,0,602,69,1.13,610,0,52 213 | 211,2020-08-07,0,60,0,0,602,76,1.47,611,1,52 214 | 212,2020-08-08,0,6,0,0,602,7,1.29,611,0,6 215 | 213,2020-08-09,0,2,0,0,602,2,3.0,611,0,2 216 | 214,2020-08-10,0,56,0,0,602,63,1.29,612,1,50 217 | 215,2020-08-11,0,58,0,0,602,85,1.46,613,1,54 218 | 216,2020-08-12,0,61,0,0,602,68,1.3,616,4,52 219 | 217,2020-08-13,0,75,0,0,602,84,1.35,616,1,66 220 | 218,2020-08-14,0,33,0,0,602,35,2.14,616,0,27 221 | 219,2020-08-15,0,5,0,0,602,5,1.2,616,0,3 222 | 220,2020-08-16,0,5,0,0,602,5,1.0,616,0,4 223 | 221,2020-08-17,0,21,0,0,602,31,1.68,616,0,18 224 | 222,2020-08-18,0,68,0,0,602,72,1.14,616,0,52 225 | 223,2020-08-19,0,52,0,0,602,58,1.19,616,0,43 226 | 224,2020-08-20,0,62,0,0,602,71,1.3,617,1,54 227 | 225,2020-08-21,0,45,0,0,602,58,1.43,617,0,36 228 | 226,2020-08-22,0,5,0,0,602,5,2.6,617,0,5 229 | 227,2020-08-23,0,0,0,0,602,0,0.0,617,0,0 230 | 228,2020-08-24,0,46,0,0,602,54,1.46,617,0,39 231 | 229,2020-08-25,0,48,0,0,602,74,1.64,617,0,43 232 | 230,2020-08-26,0,53,0,0,602,58,1.21,618,1,44 233 | 231,2020-08-27,0,45,0,0,602,49,1.33,619,1,39 234 | 232,2020-08-28,0,0,0,0,602,36,1.39,623,5,29 235 | 233,2020-08-29,0,2,0,0,602,2,1.5,623,0,1 236 | 234,2020-08-30,0,2,0,0,602,3,1.0,623,0,2 237 | 235,2020-08-31,0,43,0,0,602,49,1.39,624,1,35 238 | 236,2020-09-01,41,41,0,41,778,50,1.3,624,0,33 239 | 237,2020-09-02,44,44,0,44,778,48,1.27,623,0,38 240 | 238,2020-09-03,0,49,0,0,778,58,1.31,624,1,44 241 | 239,2020-09-04,0,38,0,0,778,41,1.29,625,1,32 242 | 240,2020-09-05,0,3,0,0,778,3,1.67,625,0,3 243 | 241,2020-09-06,0,5,0,0,778,6,1.33,625,0,3 244 | 242,2020-09-07,0,44,0,0,778,49,1.33,625,0,36 245 | 243,2020-09-08,0,55,0,0,778,62,1.42,629,4,53 246 | 244,2020-09-09,0,57,0,0,778,67,1.45,632,3,48 247 | 245,2020-09-10,0,58,0,0,778,74,1.58,633,1,50 248 | 246,2020-09-11,0,58,0,0,778,73,1.34,633,0,44 249 | 247,2020-09-12,0,8,0,0,778,10,2.3,633,0,5 250 | 248,2020-09-13,0,8,0,0,778,11,1.45,633,0,7 251 | 249,2020-09-14,0,95,0,0,778,127,1.46,638,5,74 252 | 250,2020-09-15,0,96,0,0,778,115,1.59,644,7,75 253 | 251,2020-09-16,0,115,0,0,778,148,1.46,649,6,92 254 | 252,2020-09-17,0,101,0,0,778,126,1.41,651,2,73 255 | 253,2020-09-18,0,100,0,0,778,129,1.74,670,19,75 256 | 254,2020-09-19,16,16,4,12,842,22,2.05,674,4,12 257 | 255,2020-09-20,16,16,2,14,844,17,2.24,675,1,11 258 | 256,2020-09-21,138,137,9,129,853,166,1.39,684,9,107 259 | 257,2020-09-22,120,120,10,110,863,155,1.53,692,9,94 260 | 258,2020-09-23,141,141,16,125,879,167,1.51,708,16,102 261 | 259,2020-09-24,139,139,6,133,885,164,1.57,713,6,104 262 | 260,2020-09-25,114,114,3,111,888,142,1.61,716,3,95 263 | 261,2020-09-26,15,15,5,10,893,16,3.0,719,3,12 264 | 262,2020-09-27,10,10,1,9,894,12,1.83,720,1,10 265 | 263,2020-09-28,129,129,3,126,897,158,1.4,720,1,104 266 | 264,2020-09-29,96,96,4,92,901,127,1.47,723,3,73 267 | 265,2020-09-30,12,11,2,10,903,12,1.83,726,3,10 268 | 266,2020-10-01,4,3,3,1,906,4,2.5,728,3,2 269 | 267,2020-10-02,4,4,0,4,906,4,3.0,728,0,3 270 | 268,2020-10-03,6,6,1,5,907,7,3.0,729,1,6 271 | 269,2020-10-04,6,6,1,5,908,7,3.14,729,1,6 272 | 270,2020-10-05,137,137,10,127,918,181,1.62,740,11,110 273 | 271,2020-10-06,161,161,11,150,929,187,1.5,749,9,124 274 | 272,2020-10-07,165,164,14,151,943,213,1.46,762,15,129 275 | 273,2020-10-08,161,161,8,153,951,186,1.48,771,9,119 276 | 274,2020-10-09,43,43,3,40,954,62,1.9,773,2,39 277 | 275,2020-10-10,17,17,2,15,956,20,2.2,775,2,16 278 | 276,2020-10-11,12,12,0,12,956,14,1.71,775,0,10 279 | 277,2020-10-12,168,168,12,156,968,205,1.47,785,10,142 280 | 278,2020-10-13,186,183,12,174,980,214,1.44,795,10,153 281 | 279,2020-10-14,156,156,4,152,984,178,1.46,798,4,120 282 | 280,2020-10-15,186,185,16,170,1000,223,1.56,814,16,144 283 | 281,2020-10-16,148,147,9,139,1009,175,1.55,821,7,111 284 | 282,2020-10-17,32,32,2,30,1011,35,2.06,823,2,24 285 | 283,2020-10-18,11,11,0,11,1011,11,1.36,823,0,8 286 | 284,2020-10-19,196,195,13,183,1024,287,1.64,831,8,166 287 | 285,2020-10-20,193,193,9,184,1033,220,1.49,838,8,155 288 | 286,2020-10-21,201,200,7,194,1040,239,1.36,845,7,146 289 | 287,2020-10-22,200,200,17,183,1057,296,1.7,859,14,167 290 | 288,2020-10-23,177,176,30,147,1087,217,1.85,886,27,137 291 | 289,2020-10-24,30,30,1,29,1088,33,1.85,887,1,22 292 | 290,2020-10-25,20,20,0,20,1088,21,2.48,887,0,19 293 | 291,2020-10-26,180,180,4,176,1092,217,1.4,890,3,145 294 | 292,2020-10-27,165,165,1,164,1093,187,1.4,893,3,132 295 | 293,2020-10-28,164,164,8,156,1101,184,1.48,898,6,129 296 | 294,2020-10-29,156,156,6,150,1107,188,1.47,904,6,126 297 | 295,2020-10-30,107,107,3,104,1110,115,1.5,906,2,85 298 | 296,2020-10-31,9,9,1,8,1111,10,1.6,906,1,7 299 | 297,2020-11-01,9,9,0,9,1111,10,1.2,906,0,5 300 | 298,2020-11-02,184,184,12,172,1123,232,1.66,918,12,151 301 | 299,2020-11-03,175,174,2,173,1125,199,1.48,921,3,141 302 | 300,2020-11-04,170,170,7,163,1132,185,1.5,926,5,125 303 | 301,2020-11-05,227,226,14,213,1146,272,1.56,936,11,161 304 | 302,2020-11-06,132,132,3,129,1149,152,1.43,939,3,100 305 | 303,2020-11-07,19,19,2,17,1151,19,1.84,941,2,16 306 | 304,2020-11-08,10,10,0,10,1151,10,1.8,941,0,9 307 | 305,2020-11-09,188,188,7,181,1158,210,1.46,948,7,148 308 | 306,2020-11-10,225,225,10,215,1168,388,1.79,956,8,192 309 | 307,2020-11-11,212,211,15,197,1183,256,1.61,969,14,176 310 | 308,2020-11-12,238,237,15,223,1198,278,1.72,982,13,184 311 | 309,2020-11-13,149,148,5,144,1203,165,1.53,986,4,113 312 | 310,2020-11-14,15,15,0,15,1203,15,1.87,986,0,12 313 | 311,2020-11-15,13,13,1,12,1204,13,1.54,987,1,9 314 | 312,2020-11-16,219,219,5,214,1209,299,1.47,992,5,182 315 | 313,2020-11-17,245,244,9,236,1218,301,1.5,998,6,177 316 | 314,2020-11-18,232,231,7,225,1225,284,1.51,1005,7,170 317 | 315,2020-11-19,220,220,7,213,1232,190,1.55,1012,7,176 318 | 316,2020-11-20,165,165,4,161,1236,278,1.7,1014,3,142 319 | 317,2020-11-21,21,21,2,19,1238,22,2.14,1015,1,17 320 | 318,2020-11-22,12,12,0,12,1238,13,2.38,1015,0,9 321 | 319,2020-11-23,196,196,4,192,1242,258,1.45,1019,4,150 322 | 320,2020-11-24,200,200,6,194,1248,232,1.56,1025,6,152 323 | 321,2020-11-25,190,189,8,182,1256,220,1.45,1034,9,146 324 | 322,2020-11-26,196,196,2,194,1258,217,1.44,1036,2,152 325 | 323,2020-11-27,136,136,4,132,1262,195,1.69,1038,2,109 326 | 324,2020-11-28,24,24,0,24,1262,25,2.08,1038,0,16 327 | 325,2020-11-29,8,8,0,8,1262,8,1.63,1038,0,7 328 | 326,2020-11-30,181,181,2,179,1264,236,1.62,1041,3,137 329 | 327,2020-12-01,183,183,1,182,1265,221,1.43,1042,1,141 330 | 328,2020-12-02,207,207,10,197,1275,225,1.4,1052,10,161 331 | 329,2020-12-03,195,195,8,187,1283,226,1.51,1059,7,142 332 | 330,2020-12-04,185,185,6,179,1289,235,1.55,1063,4,149 333 | 331,2020-12-05,34,34,2,32,1291,41,1.68,1065,2,25 334 | 332,2020-12-06,25,25,1,24,1292,26,1.92,1065,1,20 335 | 333,2020-12-07,200,200,6,194,1298,231,1.42,1069,4,149 336 | 334,2020-12-08,208,208,4,204,1302,249,1.39,1072,3,152 337 | 335,2020-12-09,216,216,3,213,1305,238,1.34,1074,2,157 338 | 336,2020-12-10,195,195,2,193,1307,335,1.67,1076,2,163 339 | 337,2020-12-11,148,148,4,144,1311,169,1.52,1080,4,115 340 | 338,2020-12-12,34,34,1,33,1312,38,1.42,1079,0,27 341 | 339,2020-12-13,19,19,2,17,1314,20,1.55,1081,2,11 342 | 340,2020-12-14,173,173,1,172,1315,220,1.4,1082,1,143 343 | 341,2020-12-15,160,160,8,152,1323,207,1.5,1087,5,127 344 | 342,2020-12-16,175,175,2,173,1325,232,1.45,1087,1,129 345 | 343,2020-12-17,132,132,1,131,1326,175,1.58,1088,1,104 346 | 344,2020-12-18,84,84,2,82,1328,92,1.4,1088,1,63 347 | 345,2020-12-19,13,13,0,13,1328,13,1.38,1088,0,9 348 | 346,2020-12-20,6,6,0,6,1328,6,1.33,1088,0,3 349 | 347,2020-12-21,52,52,2,50,1330,75,1.67,1090,2,44 350 | 348,2020-12-22,43,43,0,43,1330,56,1.48,1090,0,35 351 | 349,2020-12-23,41,41,0,41,1330,47,1.34,1090,0,33 352 | 350,2020-12-24,20,20,0,20,1330,23,1.43,1090,0,15 353 | 351,2020-12-25,3,3,0,3,1330,5,1.6,1090,0,2 354 | 352,2020-12-26,3,3,0,3,1330,3,2.0,1091,1,3 355 | 353,2020-12-27,4,4,0,4,1330,4,1.5,1091,0,4 356 | 354,2020-12-28,45,45,0,45,1330,72,1.4,1091,0,34 357 | 355,2020-12-29,36,36,1,35,1331,48,1.56,1092,1,30 358 | 356,2020-12-30,31,31,0,31,1331,54,1.69,1091,0,24 359 | 357,2020-12-31,24,24,0,24,1331,33,1.61,1091,0,20 360 | 358,2021-01-01,1,1,0,1,1331,1,1.0,1091,0,1 361 | 359,2021-01-02,1,1,0,1,1331,1,2.0,1091,0,1 362 | 360,2021-01-03,4,4,1,3,1332,5,1.4,1091,0,3 363 | 361,2021-01-04,49,49,0,49,1332,59,1.31,1091,0,36 364 | 362,2021-01-05,54,54,2,52,1334,97,1.55,1093,2,44 365 | 363,2021-01-06,63,62,5,58,1339,72,1.46,1096,3,46 366 | 364,2021-01-07,64,64,5,59,1344,80,1.82,1098,3,52 367 | 365,2021-01-08,33,33,1,32,1345,37,2.19,1099,1,25 368 | 366,2021-01-09,4,4,0,4,1345,5,1.4,1099,0,3 369 | 367,2021-01-10,5,4,1,4,1346,5,1.6,1100,1,2 370 | 368,2021-01-11,54,54,1,53,1347,66,1.71,1101,1,49 371 | 369,2021-01-12,58,58,3,55,1350,68,1.53,1103,2,41 372 | 370,2021-01-13,65,65,7,58,1357,70,1.54,1107,5,51 373 | 371,2021-01-14,55,55,1,54,1358,75,1.59,1108,2,36 374 | 372,2021-01-15,38,38,1,37,1359,43,1.4,1109,2,26 375 | 373,2021-01-16,4,4,0,4,1359,4,1.25,1109,0,2 376 | 374,2021-01-17,3,3,0,3,1359,3,1.33,1109,0,3 377 | 375,2021-01-18,56,56,0,56,1359,60,1.4,1108,0,46 378 | 376,2021-01-19,62,62,2,60,1361,72,1.5,1109,1,46 379 | 377,2021-01-20,57,57,2,55,1363,86,1.53,1111,2,44 380 | 378,2021-01-21,52,52,1,51,1364,62,1.37,1112,1,39 381 | 379,2021-01-22,45,45,0,45,1364,57,1.39,1112,0,36 382 | 380,2021-01-23,3,3,0,3,1364,3,1.67,1112,0,2 383 | 381,2021-01-24,2,2,0,2,1364,2,2.5,1112,0,2 384 | 382,2021-01-25,52,52,0,52,1364,54,1.37,1112,0,38 385 | 383,2021-01-26,48,48,1,47,1365,53,1.49,1112,0,32 386 | 384,2021-01-27,58,58,2,56,1367,86,1.71,1114,2,48 387 | 385,2021-01-28,52,52,5,47,1372,59,1.46,1120,6,42 388 | 386,2021-01-29,49,48,2,47,1374,66,2.06,1121,2,43 389 | 387,2021-01-30,3,3,0,3,1374,3,1.33,1120,0,2 390 | 388,2021-01-31,2,2,0,2,1374,2,2.0,1120,0,1 391 | 389,2021-02-01,46,46,0,46,1374,53,1.38,1120,0,35 392 | 390,2021-02-02,41,41,1,40,1375,56,1.38,1120,1,35 393 | 391,2021-02-03,50,50,1,49,1376,53,1.4,1121,1,41 394 | 392,2021-02-04,51,51,1,50,1377,75,1.45,1121,1,44 395 | 393,2021-02-05,43,42,3,40,1380,48,1.5,1124,4,29 396 | 394,2021-02-06,5,5,1,4,1381,5,1.4,1125,1,4 397 | 395,2021-02-07,3,3,0,3,1381,3,1.33,1125,0,1 398 | 396,2021-02-08,40,40,1,39,1382,53,1.45,1126,1,31 399 | 397,2021-02-09,44,44,1,43,1383,48,1.63,1127,1,32 400 | 398,2021-02-10,21,21,0,21,1383,31,1.55,1127,0,18 401 | 399,2021-02-11,2,2,1,1,1384,3,1.67,1128,1,2 402 | 400,2021-02-12,2,2,0,2,1384,2,1.5,1128,0,2 403 | 401,2021-02-13,0,0,0,0,1384,0,0.0,1128,0,0 404 | 402,2021-02-14,2,2,0,2,1384,3,1.33,1128,0,2 405 | 403,2021-02-15,43,43,0,43,1384,59,1.34,1128,0,33 406 | 404,2021-02-16,38,38,0,38,1384,39,1.49,1128,0,29 407 | 405,2021-02-17,48,47,2,46,1386,60,1.43,1129,1,39 408 | 406,2021-02-18,41,38,9,32,1395,44,1.45,1140,11,33 409 | 407,2021-02-19,31,30,11,20,1406,33,2.09,1150,11,29 410 | 408,2021-02-20,12,12,10,2,1416,15,2.93,1160,10,9 411 | 409,2021-02-21,6,6,4,2,1420,6,2.5,1162,3,4 412 | 410,2021-02-22,30,30,2,28,1422,31,1.42,1163,2,23 413 | 411,2021-02-23,29,29,3,26,1425,33,1.52,1166,3,25 414 | 412,2021-02-24,25,27,6,19,1431,32,1.53,1172,6,21 415 | 413,2021-02-25,33,32,5,28,1436,34,1.35,1176,4,29 416 | 414,2021-02-26,24,23,1,23,1437,33,1.82,1177,1,20 417 | 415,2021-02-27,17,17,4,13,1441,18,1.89,1181,4,13 418 | 416,2021-02-28,12,10,6,6,1447,13,1.69,1186,5,6 419 | 417,2021-03-01,31,30,13,18,1460,38,2.42,1195,10,20 420 | 418,2021-03-02,131,130,10,121,1470,169,1.53,1206,11,93 421 | 419,2021-03-03,138,137,15,123,1485,172,1.66,1222,16,113 422 | 420,2021-03-04,176,171,50,126,1535,248,1.97,1269,47,149 423 | 421,2021-03-05,143,140,40,103,1575,214,2.01,1309,40,116 424 | 422,2021-03-06,19,19,2,17,1577,21,1.81,1311,2,16 425 | 423,2021-03-07,19,18,8,11,1585,23,2.39,1319,8,18 426 | 424,2021-03-08,157,157,12,145,1597,181,1.52,1329,11,120 427 | 425,2021-03-09,158,158,16,142,1613,176,1.71,1343,14,125 428 | 426,2021-03-10,217,216,37,180,1650,266,1.84,1371,29,166 429 | 427,2021-03-11,163,163,12,151,1662,167,1.64,1382,11,129 430 | 428,2021-03-12,137,137,6,131,1668,166,1.55,1387,5,108 431 | 429,2021-03-13,41,41,4,37,1672,50,2.16,1389,2,32 432 | 430,2021-03-14,37,36,10,27,1682,42,2.5,1396,7,28 433 | 431,2021-03-15,201,201,12,189,1694,220,1.91,1410,14,152 434 | 432,2021-03-16,227,227,15,212,1709,265,1.63,1424,14,182 435 | 433,2021-03-17,242,240,22,220,1731,322,1.89,1444,21,194 436 | 434,2021-03-18,233,233,25,208,1756,285,1.66,1465,22,182 437 | 435,2021-03-19,160,160,9,151,1765,190,1.61,1475,10,115 438 | 436,2021-03-20,49,49,9,40,1774,57,2.02,1483,8,38 439 | 437,2021-03-21,26,25,6,20,1780,33,2.06,1488,5,21 440 | 438,2021-03-22,244,242,19,225,1799,282,1.7,1505,17,189 441 | 439,2021-03-23,299,299,18,281,1817,347,1.63,1519,15,229 442 | 440,2021-03-24,265,263,27,238,1844,314,1.63,1542,26,207 443 | 441,2021-03-25,268,266,18,250,1862,310,1.63,1560,18,225 444 | 442,2021-03-26,198,197,13,185,1875,232,1.72,1573,13,151 445 | 443,2021-03-27,47,47,6,41,1881,52,1.92,1578,5,36 446 | 444,2021-03-28,24,24,2,22,1883,29,1.76,1578,2,20 447 | 445,2021-03-29,254,253,15,239,1898,315,2.61,1592,15,202 448 | 446,2021-03-30,308,308,20,288,1918,377,2.07,1610,18,253 449 | 447,2021-03-31,260,260,10,250,1928,287,1.58,1620,10,199 450 | 448,2021-04-01,262,260,13,249,1941,332,1.55,1632,13,201 451 | 449,2021-04-02,201,199,12,189,1953,261,2.07,1641,9,159 452 | 450,2021-04-03,25,25,0,25,1953,33,1.94,1641,0,19 453 | 451,2021-04-04,26,25,3,23,1956,27,2.15,1643,2,21 454 | 452,2021-04-05,277,277,11,266,1967,340,1.81,1654,11,223 455 | 453,2021-04-06,263,263,5,258,1972,345,1.76,1659,5,189 456 | 454,2021-04-07,255,255,6,249,1978,283,1.48,1665,7,177 457 | 455,2021-04-08,234,234,7,227,1985,259,1.9,1671,6,176 458 | 456,2021-04-09,227,227,5,222,1990,260,1.97,1676,5,164 459 | 457,2021-04-10,53,53,3,50,1993,67,1.84,1679,3,41 460 | 458,2021-04-11,35,35,3,32,1996,40,2.92,1680,2,32 461 | 459,2021-04-12,243,243,7,236,2003,392,1.9,1687,7,201 462 | 460,2021-04-13,298,298,13,285,2016,327,1.65,1699,12,229 463 | 461,2021-04-14,126,263,10,116,2026,136,1.18,1709,11,201 464 | 462,2021-04-15,270,270,10,260,2036,316,1.66,1719,10,214 465 | 463,2021-04-16,217,217,7,210,2043,258,1.66,1726,7,164 466 | 464,2021-04-17,48,46,2,46,2045,54,1.54,1728,2,34 467 | 465,2021-04-18,25,24,4,21,2049,31,2.32,1732,4,19 468 | 466,2021-04-19,261,261,7,254,2056,295,1.48,1739,7,199 469 | 467,2021-04-20,251,251,5,246,2061,276,1.51,1742,5,187 470 | 468,2021-04-21,254,254,3,251,2064,271,1.41,1745,4,200 471 | 469,2021-04-22,225,225,4,221,2068,267,1.42,1748,4,169 472 | 470,2021-04-23,180,180,5,175,2073,204,1.53,1753,5,138 473 | 471,2021-04-24,35,35,2,33,2075,37,1.73,1755,2,27 474 | 472,2021-04-25,19,19,2,17,2077,23,2.22,1757,2,15 475 | 473,2021-04-26,174,173,4,170,2081,268,2.24,1761,4,174 476 | 474,2021-04-27,215,215,9,206,2090,241,1.54,1771,10,166 477 | 475,2021-04-28,197,197,4,193,2094,217,1.44,1775,5,147 478 | 476,2021-04-29,195,194,4,191,2098,223,1.46,1779,4,139 479 | 477,2021-04-30,137,137,1,136,2099,151,1.38,1778,0,112 480 | 478,2021-05-01,27,26,2,25,2101,37,1.89,1778,2,23 481 | 479,2021-05-02,13,13,1,12,2102,14,1.71,1778,0,9 482 | 480,2021-05-03,206,206,7,199,2109,243,1.46,1785,8,163 483 | 481,2021-05-04,200,200,7,193,2116,230,1.48,1791,6,154 484 | 482,2021-05-05,94,94,3,91,2119,126,1.67,1794,3,76 485 | 483,2021-05-06,193,193,7,186,2126,211,1.47,1797,5,154 486 | 484,2021-05-07,139,139,2,137,2128,149,1.5,1799,2,106 487 | 485,2021-05-08,19,19,0,19,2128,21,1.5,1799,0,14 488 | 486,2021-05-09,18,18,1,17,2129,18,2.1,1800,1,14 489 | 487,2021-05-10,188,188,3,185,2132,219,1.5,1803,3,145 490 | 488,2021-05-11,245,245,7,238,2139,278,1.7,1810,7,194 491 | 489,2021-05-12,227,227,7,220,2146,253,1.6,1817,7,164 492 | 490,2021-05-13,214,214,4,210,2150,266,1.58,1820,3,172 493 | 491,2021-05-14,142,142,6,136,2156,107,1.37,1825,6,112 494 | 492,2021-05-15,23,23,1,22,2157,24,1.9,1824,0,18 495 | 493,2021-05-16,9,9,1,8,2158,12,1.5,1824,0,9 496 | 494,2021-05-17,177,177,2,175,2160,202,1.49,1827,3,185 497 | 495,2021-05-18,202,202,4,198,2164,228,1.46,1831,4,202 498 | 496,2021-05-19,77,77,2,75,2166,95,1.62,1832,2,77 499 | 497,2021-05-20,182,182,4,178,2170,205,1.37,1833,2,182 500 | 498,2021-05-21,211,202,49,162,2219,251,2.36,1881,49,202 501 | 499,2021-05-22,46,43,13,33,2232,59,2.71,1895,14,43 502 | 500,2021-05-23,19,19,5,14,2237,20,2.6,1898,4,19 503 | 501,2021-05-24,219,219,9,210,2246,260,1.56,1904,8,219 504 | 502,2021-05-25,232,232,11,221,2257,263,1.58,1913,9,232 505 | 503,2021-05-26,236,236,7,229,2264,274,1.46,1918,6,236 506 | 504,2021-05-27,229,229,4,225,2268,251,1.57,1922,4,230 507 | 505,2021-05-28,183,183,7,176,2275,227,1.93,1929,7,183 508 | 506,2021-05-29,29,29,2,27,2277,35,2.0,1929,0,29 509 | 507,2021-05-30,14,14,2,12,2279,18,1.72,1931,2,14 510 | 508,2021-05-31,233,233,7,226,2286,263,1.44,1938,7,233 511 | 509,2021-06-01,247,247,4,243,2290,261,1.39,1940,2,247 512 | 510,2021-06-02,235,235,1,234,2291,268,1.38,1941,1,235 513 | 511,2021-06-03,233,233,7,226,2298,262,1.5,1947,7,233 514 | 512,2021-06-04,216,216,4,212,2302,240,1.57,1950,3,216 515 | 513,2021-06-05,52,52,0,52,2302,68,1.54,1949,1,52 516 | 514,2021-06-06,24,24,0,24,2302,24,1.75,1949,0,25 517 | 515,2021-06-07,247,247,2,245,2304,278,1.4,1951,2,247 518 | 516,2021-06-08,258,258,13,245,2317,285,1.49,1963,12,258 519 | 517,2021-06-09,247,247,7,240,2324,268,1.38,1970,7,247 520 | 518,2021-06-10,243,243,4,239,2328,268,1.46,1975,5,243 521 | 519,2021-06-11,204,204,9,195,2337,229,1.56,1984,9,204 522 | 520,2021-06-12,41,41,2,39,2339,51,2.14,1986,2,41 523 | 521,2021-06-13,20,20,1,19,2340,22,2.09,1987,1,20 524 | 522,2021-06-14,217,217,3,214,2343,251,1.45,1990,4,217 525 | 523,2021-06-15,179,179,3,176,2346,193,1.41,1992,3,179 526 | 524,2021-06-16,172,172,2,170,2348,201,1.55,1993,1,172 527 | 525,2021-06-17,159,159,0,159,2348,176,1.46,1994,1,160 528 | 526,2021-06-18,111,111,1,110,2349,118,1.4,1993,1,111 529 | 527,2021-06-19,24,24,0,24,2349,28,1.39,1993,0,24 530 | 528,2021-06-20,3,3,1,2,2350,3,3.0,1994,1,3 531 | 529,2021-06-21,99,99,2,97,2352,117,1.44,1994,1,99 532 | 530,2021-06-22,102,101,7,95,2359,118,1.54,2000,6,101 533 | 531,2021-06-23,84,84,1,83,2360,92,1.34,2001,1,84 534 | 532,2021-06-24,98,98,2,96,2362,108,1.46,2000,1,98 535 | 533,2021-06-25,82,82,0,82,2362,96,1.65,1999,0,82 536 | 534,2021-06-26,9,9,0,9,2362,9,2.11,1999,0,9 537 | 535,2021-06-27,4,4,0,4,2362,4,1.25,1998,0,4 538 | 536,2021-06-28,85,85,1,84,2363,105,1.48,1999,1,85 539 | 537,2021-06-29,92,92,2,90,2365,109,1.83,2001,2,92 540 | 538,2021-06-30,104,104,1,103,2366,121,1.5,2002,1,104 541 | 539,2021-07-01,102,102,5,97,2371,125,1.55,2007,5,102 542 | 540,2021-07-02,78,78,1,77,2372,91,1.47,2007,1,78 543 | 541,2021-07-03,18,18,0,18,2372,19,1.63,2008,1,19 544 | 542,2021-07-04,7,7,1,6,2373,9,5.0,2009,1,7 545 | 543,2021-07-05,100,100,1,99,2374,122,1.61,2010,1,101 546 | 544,2021-07-06,118,118,2,116,2376,141,1.48,2010,2,118 547 | 545,2021-07-07,121,120,3,118,2379,158,1.51,2013,4,120 548 | 546,2021-07-08,106,106,1,105,2380,138,1.3,2014,1,106 549 | 547,2021-07-09,116,115,8,108,2388,152,2.25,2020,6,115 550 | 548,2021-07-10,23,22,2,21,2390,28,2.18,2021,1,22 551 | 549,2021-07-11,3,3,0,3,2390,5,1.4,2021,0,3 552 | 550,2021-07-12,115,115,6,109,2396,142,1.64,2026,5,115 553 | 551,2021-07-13,142,142,2,140,2398,249,2.4,2026,0,142 554 | 552,2021-07-14,120,120,2,118,2400,142,2.28,2026,2,120 555 | 553,2021-07-15,121,121,0,121,2400,167,1.47,2025,0,121 556 | 554,2021-07-16,92,92,2,90,2402,138,1.8,2025,1,92 557 | 555,2021-07-17,21,21,0,21,2402,30,1.4,2025,0,21 558 | 556,2021-07-18,4,4,0,4,2402,4,1.0,2025,0,4 559 | 557,2021-07-19,121,121,2,119,2404,143,1.57,2027,2,121 560 | 558,2021-07-20,113,113,1,112,2405,144,1.47,2028,1,113 561 | 559,2021-07-21,108,108,0,108,2405,135,1.41,2027,0,108 562 | 560,2021-07-22,125,125,0,125,2405,190,1.56,2027,0,125 563 | 561,2021-07-23,101,101,0,101,2405,129,1.37,2027,0,101 564 | 562,2021-07-24,19,19,0,19,2405,26,1.73,2027,0,19 565 | 563,2021-07-25,4,4,0,4,2405,5,1.8,2027,0,4 566 | 564,2021-07-26,120,120,2,118,2407,151,1.32,2028,2,121 567 | 565,2021-07-27,118,118,2,116,2409,144,1.42,2031,3,118 568 | 566,2021-07-28,130,130,3,127,2412,156,1.35,2034,3,130 569 | 567,2021-07-29,120,120,0,120,2412,153,1.35,2035,1,120 570 | 568,2021-07-30,100,100,1,99,2413,133,1.55,2035,0,100 571 | 569,2021-07-31,29,29,1,28,2414,33,1.61,2036,1,30 572 | 570,2021-08-01,3,3,0,3,2414,3,1.0,2036,0,3 573 | 571,2021-08-02,105,105,1,104,2415,127,1.47,2036,0,105 574 | 572,2021-08-03,117,117,2,115,2417,130,1.48,2039,3,117 575 | 573,2021-08-04,112,112,0,112,2417,131,1.49,2039,0,112 576 | 574,2021-08-05,111,111,0,111,2417,124,1.37,2039,0,111 577 | 575,2021-08-06,91,91,0,91,2417,135,1.6,2039,0,91 578 | 576,2021-08-07,24,24,2,22,2419,25,2.32,2039,1,24 579 | 577,2021-08-08,5,5,0,5,2419,7,1.86,2039,0,5 580 | 578,2021-08-09,96,96,0,96,2419,116,1.82,2039,0,99 581 | 579,2021-08-10,109,109,1,108,2420,134,1.38,2039,0,109 582 | 580,2021-08-11,118,118,0,118,2420,149,1.43,2039,0,118 583 | 581,2021-08-12,111,111,2,109,2422,138,1.55,2041,2,111 584 | 582,2021-08-13,78,78,2,76,2424,156,1.68,2043,2,78 585 | 583,2021-08-14,11,11,0,11,2424,23,3.22,2043,0,11 586 | 584,2021-08-15,4,4,0,4,2424,15,17.53,2042,0,4 587 | 585,2021-08-16,44,44,0,44,2424,61,2.69,2042,0,44 588 | 586,2021-08-17,85,85,2,83,2426,105,1.61,2044,2,87 589 | 587,2021-08-18,74,74,0,74,2426,89,1.8,2044,0,74 590 | 588,2021-08-19,77,77,0,77,2426,84,1.35,2043,0,77 591 | 589,2021-08-20,64,64,0,64,2426,72,1.43,2044,1,64 592 | 590,2021-08-21,14,13,9,5,2435,14,2.86,2045,1,13 593 | 591,2021-08-22,5,5,0,5,2435,5,3.0,2045,1,5 594 | 592,2021-08-23,70,70,0,70,2435,95,1.42,2045,1,70 595 | 593,2021-08-24,69,69,1,68,2436,77,1.39,2046,1,69 596 | 594,2021-08-25,75,75,1,74,2437,87,1.4,2048,2,75 597 | 595,2021-08-26,60,60,0,60,2437,72,1.5,2048,0,60 598 | 596,2021-08-27,67,67,0,67,2437,82,1.43,2048,0,67 599 | 597,2021-08-28,16,16,1,15,2438,18,1.83,2049,1,16 600 | 598,2021-08-29,5,5,0,5,2438,7,1.29,2049,0,5 601 | 599,2021-08-30,92,92,2,90,2440,98,1.34,2050,2,92 602 | 600,2021-08-31,91,91,0,91,2440,103,1.47,2050,0,91 603 | 601,2021-09-01,146,144,7,139,2447,172,1.48,2058,9,145 604 | 602,2021-09-02,154,154,8,146,2455,188,1.47,2065,8,154 605 | 603,2021-09-03,138,138,4,134,2459,164,1.44,2069,4,138 606 | 604,2021-09-04,47,47,2,45,2461,62,1.84,2071,2,47 607 | 605,2021-09-05,13,12,0,13,2461,14,3.07,2070,1,13 608 | 606,2021-09-06,167,165,2,165,2463,211,1.44,2071,2,166 609 | 607,2021-09-07,166,166,3,163,2466,188,1.36,2074,3,166 610 | 608,2021-09-08,181,181,4,177,2470,221,1.33,2076,3,181 611 | 609,2021-09-09,183,183,7,176,2477,220,1.49,2085,9,183 612 | 610,2021-09-10,137,137,2,135,2479,173,1.43,2086,1,137 613 | 611,2021-09-11,36,36,0,36,2479,43,1.58,2086,0,36 614 | 612,2021-09-12,13,13,0,13,2479,16,1.5,2086,0,13 615 | 613,2021-09-13,156,156,4,152,2483,198,1.36,2088,3,156 616 | 614,2021-09-14,177,172,14,163,2497,207,1.48,2101,13,172 617 | 615,2021-09-15,174,174,5,169,2502,220,1.58,2107,6,174 618 | 616,2021-09-16,169,169,1,168,2503,187,1.36,2108,2,169 619 | 617,2021-09-17,113,113,3,110,2506,138,1.57,2111,3,113 620 | 618,2021-09-18,28,28,4,24,2510,33,1.85,2115,4,28 621 | 619,2021-09-19,3,3,0,3,2510,3,1.33,2115,0,3 622 | 620,2021-09-20,6,6,0,6,2510,6,1.83,2114,0,6 623 | 621,2021-09-21,8,8,0,8,2510,8,1.13,2113,0,8 624 | 622,2021-09-22,13,13,1,12,2511,15,2.27,2114,1,13 625 | 623,2021-09-23,107,107,3,104,2514,130,1.32,2117,3,107 626 | 624,2021-09-24,121,121,0,121,2514,145,1.61,2116,0,121 627 | 625,2021-09-25,48,48,1,47,2515,59,1.93,2117,1,48 628 | 626,2021-09-26,19,19,0,19,2515,21,1.43,2117,0,19 629 | 627,2021-09-27,199,199,3,196,2518,245,1.49,2120,3,199 630 | 628,2021-09-28,221,221,3,218,2521,266,1.61,2123,3,221 631 | 629,2021-09-29,209,209,0,209,2521,256,1.55,2123,0,209 632 | 630,2021-09-30,387,384,64,323,2585,494,1.88,2181,61,384 633 | 631,2021-10-01,397,386,76,321,2661,501,1.9,2256,76,387 634 | 632,2021-10-02,92,92,8,84,2669,115,1.67,2264,8,92 635 | 633,2021-10-03,33,31,4,29,2673,38,1.92,2267,4,31 636 | 634,2021-10-04,154,154,6,148,2679,216,1.92,2272,5,155 637 | 635,2021-10-05,307,304,21,286,2700,397,1.63,2291,19,305 638 | 636,2021-10-06,307,307,4,303,2704,415,1.52,2295,4,307 639 | 637,2021-10-07,272,272,3,269,2707,349,1.41,2298,3,272 640 | 638,2021-10-08,217,216,6,211,2713,297,1.51,2304,6,216 641 | 639,2021-10-09,74,73,2,72,2715,92,1.52,2306,2,73 642 | 640,2021-10-10,23,23,1,22,2716,23,1.52,2306,1,23 643 | 641,2021-10-11,155,155,4,151,2720,202,1.78,2310,4,155 644 | 642,2021-10-12,329,329,11,318,2731,404,1.54,2322,12,329 645 | 643,2021-10-13,305,305,6,299,2737,401,1.5,2327,5,305 646 | 644,2021-10-14,305,305,7,298,2744,398,1.42,2333,6,305 647 | 645,2021-10-15,268,267,6,262,2750,351,1.43,2338,5,267 648 | 646,2021-10-16,100,100,0,100,2750,129,1.64,2338,0,100 649 | 647,2021-10-17,24,24,2,22,2752,26,1.88,2339,1,24 650 | 648,2021-10-18,316,315,9,307,2761,405,1.78,2345,7,316 651 | --------------------------------------------------------------------------------