buypeeb/listing.py

179 lines
4.8 KiB
Python

import requests
import re, json, math
from datetime import datetime, timezone, timedelta
import functions
JST = timezone(timedelta(hours = 9))
LOCALTIME = timezone(timedelta(hours = 10)) # TODO: allow for customisation
class YahooAuctionsItem:
"""
A class for handling items on Yahoo! Auctions Japan
Attributes
----------
name : str
The name given to the auction item by the user.
original_name : str
The name as it appears on Y!A,
favourite : bool
Whether or not the user has "favourited" this item.
url : bool
The URL this item was added from. If not provided, defaults to https://buyee.jp/item/yahoo/auction/{id}.
id : str
The ID of the item, found at the end of the URL.
last_checked : datetime
The time that the item's status was last checked by buypeeb.
available : bool
Whether or not the item is still available (if the auction is still available)
ready : bool
Whether or not the object is ready. If not, some of the data (price, available, etc.) may be out of date or unset.
updating : bool
If true, the object is currently being updated. Should never be true if ready is true.
price : float
The price of the item in yen.
bids : int
The number of bids that have been placed on the item.
start_date : datetime
The start date and time of the item in JST.
end_date : datetime
The end date and time of the item in JST.
"""
def __init__(self, url: str, id: str, name: str = "", from_json: dict = None):
# note - incoming url is not validated in any way!
self.name = name
self.price = 0
self.win_price = 0
self.original_name = ""
self.favourite = None
self.end_date = datetime.fromisoformat('1970-01-01').replace(tzinfo = JST)
self.start_date = datetime.fromisoformat('1970-01-01').replace(tzinfo = JST)
self.bids = 0
self.auto_extension = None
self.rate = 75.0
if url == None and from_json != None and id != None:
self.id = id
self.name = from_json['name']
self.original_name = from_json['original_name']
if self.name == "":
self.name = self.original_name
self.favourite = from_json['favourite']
self.url = f"https://buyee.jp/item/yahoo/auction/{self.id}"
elif url != None and id != None:
self.url = url
self.id = id
else:
raise RuntimeError
self.last_checked = datetime.fromisoformat('1970-01-01')
self.available = True
self.ready = False
self.updating = False
def update(self):
self.updating = True
try:
# the good news is, yahoo japan returns all the data we need in handy json format
# the bad news is that the only way to get that json format is to download the whole auction page and grep it
r = requests.get(f"https://page.auctions.yahoo.co.jp/jp/auction/{self.id}").text
# r = open("yahoo.html").read()
j = json.loads(re.match(r'.*var pageData ?= ?(\{.*?\});', r, re.DOTALL).group(1))
ae = re.search(r'自動延長.+\n.+>(.+)<.+', r)
if ae != None:
ae = ae.group(1)
self.auto_extension = (ae == "あり")
except:
raise
j = j['items']
self.price = float(j['price'])
self.win_price = float(j['winPrice'])
self.bids = j['bids']
self.start_date = datetime.fromisoformat(j['starttime']).replace(tzinfo = JST)
self.end_date = datetime.fromisoformat(j['endtime']).replace(tzinfo = JST)
self.last_checked = datetime.now(JST)
self.original_name = j['productName']
if self.name == None or self.name == "":
self.name = j['productName']
self.available = (self.end_date_local() >= datetime.now(LOCALTIME))
self.ready = True
self.updating = False
return self.id, self
def price_jpy(self, win = False):
p = self.price
if win:
p = self.win_price
return f"¥{p:.0f}"
def price_aud(self, rate = 0, win = False):
if rate == 0:
rate = self.rate
else:
self.rate = rate
p = self.price
if win:
p = self.win_price
return f"${self.price / rate:.2f}"
def end_date_local(self):
return self.end_date.astimezone(LOCALTIME)
def start_date_local(self):
return self.start_date.astimezone(LOCALTIME)
def ending_in(self, local = False):
if local:
difference = self.end_date_local() - datetime.now(tz = LOCALTIME)
else:
difference = self.end_date - datetime.now(tz = JST)
ds = int(difference.total_seconds())
seconds = ds % 60
minutes = math.floor(ds / 60) % 60
hours = math.floor(ds / (60 * 60)) % 24
days = math.floor(ds / (60 * 60 * 24))
out = ""
if days > 0: out += f"{days}d "
if hours > 0: out += f"{hours}h "
if minutes > 0: out += f"{minutes}m "
return f"{out}{seconds}s"
def ending_at(self, local = False):
now = datetime.now(tz = LOCALTIME)
ndate = now.strftime("%d %b")
if self.end_date != None:
ed = self.end_date_local() if local else self.end_date
idate, itime = ed.strftime("%d %b"), ed.strftime("%H:%M")
if idate == ndate:
return itime
else:
return f"{idate} {itime}"