├── .gitignore ├── requirements.txt ├── count_remain_seat.py ├── check_map_seat.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .venv -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2023.11.17 2 | cffi==1.16.0 3 | charset-normalizer==3.3.2 4 | cryptography==41.0.7 5 | distlib==0.3.8 6 | filelock==3.13.1 7 | idna==3.6 8 | Jinja2==3.1.3 9 | lxml==5.1.0 10 | MarkupSafe==2.1.4 11 | packaging==23.2 12 | platformdirs==4.1.0 13 | protobuf==4.21.12 14 | pycparser==2.21 15 | pyparsing==3.1.1 16 | python-dateutil==2.8.2 17 | pytz==2023.3.post1 18 | requests==2.31.0 19 | six==1.16.0 20 | urllib3==2.2.1 21 | -------------------------------------------------------------------------------- /count_remain_seat.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import time 3 | 4 | ####################################################### 5 | ########### 아래 값 채워준 뒤 실행해주시면 됩니다. ############# 6 | ####################################################### 7 | prodId = "" 8 | pocCode = "" 9 | scheduleNo = "" 10 | cookie = "" 11 | slack_webhook_url = "" 12 | ####################################################### 13 | ####################################################### 14 | 15 | def main() -> None: 16 | for i in range(30): 17 | seats = get_seats_summary() 18 | messages = check_remaining_seats(seats['summary']) 19 | send_message(messages) 20 | time.sleep(2) 21 | 22 | def get_seats_summary() -> None: 23 | url = "https://ticket.melon.com/tktapi/product/summary.json?v=1" 24 | 25 | body = { 26 | 'prodId': prodId, 27 | 'pocCode': pocCode, 28 | 'scheduleNo': scheduleNo 29 | } 30 | 31 | header = { 32 | 'Accept': 'text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01', 33 | 'Content-Length': '76', 34 | 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 35 | 'Cookie': cookie, 36 | 'Host': 'ticket.melon.com', 37 | 'Referer': 'https://ticket.melon.com/reservation/popup/stepBlock.htm', 38 | 'User-Agent': 'X' 39 | } 40 | 41 | response = requests.post(url,headers=header,data=body) 42 | return response.json() 43 | 44 | def check_remaining_seats(seats: list) -> list: 45 | result = [] 46 | 47 | for seat in seats: 48 | if seat['realSeatCntlk'] > 0: 49 | result.append(generate_message(seat)) 50 | 51 | return result 52 | 53 | def send_message(messages: list) -> None: 54 | for message in messages: 55 | response = requests.post(slack_webhook_url, json={'text' : message}) 56 | 57 | def generate_message(seat: dict) -> str: 58 | message = "" 59 | message += seat['seatGradeName'] + ", " if 'seatGradeName' in seat else "" 60 | message += seat['floorNo'] if 'floorNo' in seat else "" 61 | message += seat['floorName'] + " " if 'floorName' in seat else "" 62 | message += seat['areaNo'] if 'areaNo' in seat else "" 63 | message += seat['areaName'] if 'areaName' in seat else "" 64 | message += "에 잔여좌석 " + str(seat['realSeatCntlk']) + "개 발생! " 65 | return message 66 | 67 | main() -------------------------------------------------------------------------------- /check_map_seat.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import time 4 | 5 | ####################################################### 6 | ########### 아래 값 채워준 뒤 실행해주시면 됩니다. ############# 7 | ####################################################### 8 | prodId = "" 9 | pocCode = "" 10 | scheduleNo = "" 11 | cookie = "" 12 | slack_webhook_url = "" 13 | ####################################################### 14 | ####################################################### 15 | 16 | header = { 17 | 'Accept': 'text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01', 18 | 'Content-Length': '75', 19 | 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 20 | 'Cookie': cookie, 21 | 'Host': 'ticket.melon.com', 22 | 'Referer': 'https://ticket.melon.com/reservation/popup/stepBlock.htm', 23 | 'User-Agent': 'X' 24 | } 25 | 26 | def get_block_list() -> list: 27 | url = "https://ticket.melon.com/tktapi/product/getAreaMap.json?v=1&callback=getBlockGradeSeatMapCallBack" 28 | 29 | body = { 30 | 'prodId': prodId, 31 | 'pocCode': pocCode, 32 | 'scheduleNo': scheduleNo 33 | } 34 | 35 | response = requests.post(url,headers=header,data=body) 36 | block_datas = json.loads(response.text.replace("/**/getBlockGradeSeatMapCallBack(","").replace(");", "")) 37 | 38 | return block_datas['seatData']['da']['sb'] 39 | 40 | 41 | def get_remain_seat_in_block(block) -> int: 42 | url = "https://ticket.melon.com/tktapi/product/seat/seatMapList.json?v=1&callback=getSeatListCallBack" 43 | 44 | body = { 45 | 'prodId': prodId, 46 | 'pocCode': pocCode, 47 | 'scheduleNo': scheduleNo, 48 | 'blockId': block['sbid'], #getAreaMap.json > seatData > st > sbid 49 | 'corpCodeNo': '' 50 | } 51 | 52 | response = requests.post(url,headers=header,data=body) 53 | map_datas = json.loads(response.text.replace("/**/getSeatListCallBack(","").replace(");", "")) 54 | count = 0 55 | 56 | if "seatData" in map_datas: 57 | for st in map_datas['seatData']['st'][0]['ss']: 58 | if st['sid'] != None: 59 | count += 1 60 | 61 | return count 62 | 63 | def send_message(message: str) -> None: 64 | response = requests.post(slack_webhook_url, json={'text' : message}) 65 | 66 | def main() -> None: 67 | for i in range(30): 68 | blocks = get_block_list() 69 | for block in blocks: 70 | count = get_remain_seat_in_block(block) 71 | if count > 0: 72 | send_message(block['sntv']['a'] + "구역에 잔여좌석 " + str(count) + "개 발생!") 73 | time.sleep(2) 74 | 75 | main() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Melon 티켓 자동 알림 스크립트 2 | 3 | Python으로 멜론 콘서트 자리 발생시 자동으로 알림을 받습니다. 4 | 5 | 준비 6 | - Python3 7 | - Crontab 8 | 9 | # 사용법 (MacOS 기준) 10 | ## 멜론에서 남은 좌석 정보를 제공할 경우 11 | 12 | 1. `count_remain_seat.py`에 알림을 받고자하는 콘서트 API 정보를 입력합니다. 괄호 표시된 부분만 확인해서 변경해주세요. 13 | ```python 14 | ... 15 | 16 | def get_seats_summary() -> None: 17 | url = "https://ticket.melon.com/tktapi/product/block/summary.json?v=1" 18 | 19 | # 확인 후 필요한 정보만 채워줍니다. 20 | body = { 21 | 'prodId': '', 22 | 'pocCode': '', 23 | 'scheduleNo': '', 24 | 'perfDate': '', 25 | 'seatGradeNo': '', 26 | 'corpCodeNo': '' 27 | } 28 | 29 | # Cookie만 변경해주세요. 30 | header = { 31 | ... 32 | 'Cookie': '', 33 | ... 34 | } 35 | 36 | response = requests.post(url,headers=header,data=body) 37 | return response.json() 38 | 39 | ... 40 | ``` 41 | 2. `main.py`에 Slack Webhook URL도 추가해줍니다. 42 | ```python 43 | ... 44 | 45 | def send_message(messages: list) -> None: 46 | slack_webhook_url = "" 47 | 48 | for message in messages: 49 | response = requests.post(slack_webhook_url, json={'text' : message}) 50 | 51 | ... 52 | ``` 53 | 3. 가상환경을 생성 후 활성화합니다. 54 | ```sh 55 | $ python3 -m venv .venv 56 | $ . .venv/bin/activate 57 | ``` 58 | 59 | 4. 라이브러리를 설치합니다. 60 | ```sh 61 | $ pip install -r requirements.txt 62 | ``` 63 | 64 | 5. Crontab을 설정합니다. (아래는 1분 기준 설정방법) 65 | ```sh 66 | $ crontab -e 67 | # 에는 실제 Python 경로를 입력해주세요. 전체 경로를 입력해야합니다. 68 | # 에는 main.py의 전체 경로를 입력해주세요. 69 | * * * * * /python3 /count_remain_seat.py 70 | ``` 71 | 72 | ## 멜론에서 남은 좌석 정보를 제공하지 않을 경우 73 | 74 | 1. `check_map_seat.py`에 알림을 받고자하는 콘서트 API 정보를 입력합니다. 최상단에 주석으로 표시된 부분만 확인해서 변경해주세요. 75 | ```python 76 | ... 77 | 78 | ####################################################### 79 | ########### 아래 값 채워준 뒤 실행해주시면 됩니다. ############# 80 | ####################################################### 81 | prodId = "" 82 | pocCode = "" 83 | scheduleNo = "" 84 | cookie = "" 85 | slack_webhook_url = "" 86 | ####################################################### 87 | ####################################################### 88 | 89 | ... 90 | ``` 91 | 3. 가상환경을 생성 후 활성화합니다. 92 | ```sh 93 | $ python3 -m venv .venv 94 | $ . .venv/bin/activate 95 | ``` 96 | 97 | 4. 라이브러리를 설치합니다. 98 | ```sh 99 | $ pip install -r requirements.txt 100 | ``` 101 | 102 | 5. Crontab을 설정합니다. (아래는 1분 기준 설정방법) 103 | ```sh 104 | $ crontab -e 105 | # 에는 실제 Python 경로를 입력해주세요. 전체 경로를 입력해야합니다. 106 | # 에는 main.py의 전체 경로를 입력해주세요. 107 | * * * * * /python3 /check_map_seat.py 108 | ``` 109 | 110 | --------------------------------------------------------------------------------