Skip to content

Instantly share code, notes, and snippets.

@kw-lee
Last active October 8, 2023 05:09
Show Gist options
  • Save kw-lee/2edd7998d47e2bbc1c3d56b2c5c8ad75 to your computer and use it in GitHub Desktop.
Save kw-lee/2edd7998d47e2bbc1c3d56b2c5c8ad75 to your computer and use it in GitHub Desktop.
iPhone 15 사전예약 픽업
import time
import requests
import datetime
import random
from subprocess import Popen, PIPE
# iphone 15 model code
# https://www.korea-iphone.com/blog/hFRpmwTDhieo.html
iphone15_model_code = {
"max-1tb-BlackT": "MU7G3KH/A",
"max-1tb-BlueT": "MU7K3KH/A",
"max-1tb-NaturalT": "MU7J3KH/A",
"max-1tb-WhiteT": "MU7H3KH/A",
"max-512gb-BlackT": "MU7C3KH/A",
"max-512gb-BlueT": "MU7F3KH/A",
"max-512gb-NaturalT": "MU7E3KH/A",
"max-512gb-WhiteT": "MU7D3KH/A",
"max-256gb-BlackT": "MU773KH/A",
"max-256gb-BlueT": "MU7A3KH/A",
"max-256gb-NaturalT": "MU793KH/A",
"max-256gb-WhiteT": "MU783KH/A",
"pro-1tb-BlueT": "MTVG3KH/A",
"pro-1tb-BlackT": "MTVC3KH/A",
"pro-1tb-NaturalT": "MTVF3KH/A",
"pro-1tb-WhiteT": "MTVD3KH/A",
"pro-512gb-BlueT": "MTVA3KH/A",
"pro-512gb-BlackT": "MTV73KH/A",
"pro-512gb-NaturalT": "MTV93KH/A",
"pro-512gb-WhiteT": "MTV83KH/A",
"pro-256gb-BlueT": "MTV63KH/A",
"pro-256gb-BlackT": "MTV13KH/A",
"pro-256gb-NaturalT": "MTV53KH/A",
"pro-256gb-WhiteT": "MTV43KH/A",
"pro-128gb-BlueT": "MTV03KH/A",
"pro-128gb-BlackT": "MTUV3KH/A",
"pro-128gb-NaturalT": "MTUX3KH/A",
"pro-128gb-WhiteT": "MTUW3KH/A",
"plus-512gb-Black": "MU1H3KH/A",
"plus-512gb-Pink": "MU1J3KH/A",
"plus-512gb-Yellow": "MU1M3KH/A",
"plus-512gb-Green": "MU1Q3KH/A",
"plus-512gb-Blue": "MU1P3KH/A",
"plus-256gb-Black": "MU183KH/A",
"plus-256gb-Pink": "MU193KH/A",
"plus-256gb-Yellow": "MU1D3KH/A",
"plus-256gb-Green": "MU1G3KH/A",
"plus-256gb-Blue": "MU1F3KH/A",
"plus-128gb-Black": "MU0Y3KH/A",
"plus-128gb-Pink": "MU103KH/A",
"plus-128gb-Yellow": "MU123KH/A",
"plus-128gb-Green": "MU173KH/A",
"plus-128gb-Blue": "MU163KH/A",
"standard-512gb-Black": "MTPC3KH/A",
"standard-512gb-Pink": "MTPD3KH/A",
"standard-512gb-Yellow": "MTPF3KH/A",
"standard-512gb-Green": "MTPH3KH/A",
"standard-512gb-Blue": "MTPG3KH/A",
"standard-256gb-Black": "MTP63KH/A",
"standard-256gb-Pink": "MTP73KH/A",
"standard-256gb-Yellow": "MTP83KH/A",
"standard-256gb-Green": "MTPA3KH/A",
"standard-256gb-Blue": "MTP93KH/A",
"standard-128gb-Black": "MTP03KH/A",
"standard-128gb-Pink": "MTP13KH/A",
"standard-128gb-Yellow": "MTP23KH/A",
"standard-128gb-Green": "MTP53KH/A",
"standard-128gb-Blue": "MTP43KH/A"
}
model_dict = {
"max": "iphone-15-pro/6.7형-디스플레이",
"pro": "iphone-15-pro/6.1형-디스플레이",
"plus": "iphone-15/6.7형-디스플레이",
"standard": "iphone-15/6.1형-디스플레이"
}
color_dict = {
"Black": "블랙",
"Blue": "블루",
"Yellow": "옐로",
"Pink": "핑크",
"Green": "그린",
"WhiteT": "화이트-티타늄",
"NaturalT": "내추럴-티타늄",
"BlackT": "블랙-티타늄",
"BlueT": "블루-티타늄"
}
def addReminder(subject, buy_url, due_dt):
due_year = due_dt.year
due_month = due_dt.month
due_day = due_dt.day
due_hour = due_dt.hour
due_minute = due_dt.minute
script = f'''
set todo to "{subject}" as string
set due_dt to (current date)
set year of due_dt to "{due_year}"
set month of due_dt to "{due_month}"
set day of due_dt to "{due_day}"
set hours of due_dt to "{due_hour}"
set minutes of due_dt to "{due_minute}"
set buy_url to "{buy_url}" as string
tell application "Reminders"
activate
make new reminder at end with properties {{name:todo, due date:due_dt, remind me date:due_dt, body:buy_url}}
end tell
'''
args = []
p = Popen(['/usr/bin/osascript', '-'] + args,
stdin=PIPE, stdout=PIPE, stderr=PIPE)
stdout, stderr = p.communicate(script.encode("utf-8"))
def is_pickup_possible(models, loc_zip):
is_available = False
notes = []
buy_urls = []
for model in models:
code = iphone15_model_code[model]
url = f"https://www.apple.com/kr/shop/fulfillment-messages?parts.0={code}&searchNearby=true&location={loc_zip}"
r = requests.get(url)
splited_name = model.split("-")
model = model_dict[splited_name[0]]
capacity = splited_name[1]
color = color_dict[splited_name[2]]
buy_url = f"https://www.apple.com/kr/shop/buy-iphone/{model}-{capacity}-{color}"
if r.status_code == 200:
try:
d = r.json()
stores = d["body"]["content"]["pickupMessage"]["stores"]
for store in stores:
storeName = store["storeName"]
message = store["partsAvailability"][code]["pickupSearchQuote"]
modelname = store["partsAvailability"][code]["messageTypes"]["regular"]["storePickupProductTitle"]
note = f"Apple {storeName}의 {modelname}: {message}"
print(note)
if "가능" in message:
is_available = True
notes.append(note)
buy_urls.append(buy_url)
except Exception as e:
print(e)
return is_available, notes, buy_urls
def notify(notes, buy_urls):
# notify using Apple Reminder app
now = datetime.datetime.now() - datetime.timedelta(minutes=1)
for i in range(len(notes)):
addReminder(notes[i], buy_urls[i], now)
def timing(interval):
# sample from (interval * 0.7, interval * 1.2)
scaler = 0.7 + 0.5 * random.random()
return interval * scaler
def main(models, loc_zip, interval):
trial = 1
done = False
while True:
print(f"{trial}번째 시도:")
done, notes, buy_urls = is_pickup_possible(models, loc_zip)
if done:
break
print("-" * 50)
time.sleep(timing(interval))
trial += 1
print("성공!")
notify(notes, buy_urls)
if __name__ == "__main__":
models = ["pro-256gb-BlackT", "standard-128gb-Blue"]
interval = 10
loc_zip = "08826"
main(models, loc_zip, interval)
@kw-lee
Copy link
Author

kw-lee commented Sep 30, 2022

필요사항

  • macOS 환경 + 미리 알림 앱
  • python3
  • applescript 패키지 (pip3 install applescript로 설치)

사용법

본문 내 코드 중 맨 아래 부분을 다음과 같이 수정

if __name__ == "__main__":
    models = # 원하는 model들을 list 형태로 입력
    interval = # 원하는 시간 간격을 초 단위로 입력
    loc_zip = # 확인을 원하는 지점의 우편번호 5자리
    main(models, loc_zip, interval)

다음의 코드를 실행

python ./iphone-15-sniper.py

작동 방식

  • 현재 위치에서 가까운 애플스토어의 아이폰 15 픽업 재고들을 확인
  • 픽업이 가능한 지점이 있으면 미리 알림 앱을 통해 구매 알림을 보내줌

실행 결과 예시

python ./iphone-15-sniper.py
1번째 시도:
Apple 강남의 iPhone 15 Pro 256GB 화이트 티타늄: 픽업 불가
Apple 여의도의 iPhone 15 Pro 256GB 화이트 티타늄: 픽업 불가
Apple 가로수길의 iPhone 15 Pro 256GB 화이트 티타늄: 픽업 불가
Apple 명동의 iPhone 15 Pro 256GB 화이트 티타늄: 픽업 불가
Apple 잠실의 iPhone 15 Pro 256GB 화이트 티타늄: 픽업 불가
Apple 강남의 iPhone 15 128GB 블루: 픽업 불가
Apple 여의도의 iPhone 15 128GB 블루: 픽업 불가
Apple 가로수길의 iPhone 15 128GB 블루: 픽업 불가
Apple 명동의 iPhone 15 128GB 블루: 픽업 불가
Apple 잠실의 iPhone 15 128GB 블루: 픽업 불가
--------------------------------------------------
2번째 시도:
Apple 강남의 iPhone 15 Pro 256GB 화이트 티타늄: 픽업 불가
Apple 여의도의 iPhone 15 Pro 256GB 화이트 티타늄: 픽업 불가
Apple 가로수길의 iPhone 15 Pro 256GB 화이트 티타늄: 픽업 불가
Apple 명동의 iPhone 15 Pro 256GB 화이트 티타늄: 픽업 불가
Apple 잠실의 iPhone 15 Pro 256GB 화이트 티타늄: 픽업 불가
Apple 강남의 iPhone 15 128GB 블루: 픽업 불가
Apple 여의도의 iPhone 15 128GB 블루: 픽업 불가
Apple 가로수길의 iPhone 15 128GB 블루: 픽업 불가
Apple 명동의 iPhone 15 128GB 블루: 픽업 불가
Apple 잠실의 iPhone 15 128GB 블루: 픽업 불가
--------------------------------------------------
...

@junmini7
Copy link

https://gist.github.com/junmini7/10587e89d1571afc658a8b1144643218
저거로 링크도 넣어서 웹페이지 만들수 있는 기능 추가해봤습니다
우편번호는 서울대걸로 하니까 스토어 4개 전부 다 나와서 그대로 넣었습니다

@kw-lee
Copy link
Author

kw-lee commented Nov 8, 2022

https://gist.github.com/junmini7/10587e89d1571afc658a8b1144643218
저거로 링크도 넣어서 웹페이지 만들수 있는 기능 추가해봤습니다
우편번호는 서울대걸로 하니까 스토어 4개 전부 다 나와서 그대로 넣었습니다

멋지네요 감사합니다!

@wnsgh78b
Copy link

wnsgh78b commented Oct 6, 2023

맥 운영체제 소노마 버전 에서 미리알림 추가시 알림 시간 설정이 오전12시로 고정되어서 알림이 오지않아

notify 함수를 아래와 같이 수정하였습니다.

def notify(notes):
now = datetime.datetime.now() + datetime.timedelta(seconds=1)
now = now.strftime('%Y-%m-%d %p %H:%M:%S')
for note in notes:
applescript.tell.app("Reminders", f"make new reminder with properties {{name:"{note}",remind me date:date "{now}"}}")

@kw-lee
Copy link
Author

kw-lee commented Oct 8, 2023

맥 운영체제 소노마 버전 에서 미리알림 추가시 알림 시간 설정이 오전12시로 고정되어서 알림이 오지않아

notify 함수를 아래와 같이 수정하였습니다.

def notify(notes): now = datetime.datetime.now() + datetime.timedelta(seconds=1) now = now.strftime('%Y-%m-%d %p %H:%M:%S') for note in notes: applescript.tell.app("Reminders", f"make new reminder with properties {{name:"{note}",remind me date:date "{now}"}}")

동일한 문제 확인했습니다! 다만 올려주신 코드에서도 비슷한 문제가 발생해서 미리알림에 시간을 구체적으로 입력할 수 있도록 코드 수정했습니다. 이 외에도 다음과 같이 업데이트하였습니다.

  • 아이폰 15로 업데이트 (일반, 플러스 모델도 추가)
  • 미리알림에 구매 URL 추가
  • applescript 패키지 의존성 제거

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment