2022-11-04 08:08:45 +00:00
|
|
|
import contextlib
|
|
|
|
import re
|
|
|
|
from datetime import datetime
|
2022-11-04 15:54:19 +00:00
|
|
|
from typing import Optional
|
2022-11-04 08:08:45 +00:00
|
|
|
|
|
|
|
from bs4 import BeautifulSoup
|
|
|
|
from init import request
|
2023-08-18 13:28:16 +00:00
|
|
|
from models.services.fragment import (
|
2023-01-12 13:19:54 +00:00
|
|
|
AuctionStatus,
|
|
|
|
UserName,
|
|
|
|
TON_TO_USD_RATE,
|
|
|
|
Price,
|
|
|
|
FragmentSubText,
|
|
|
|
FragmentSub,
|
|
|
|
)
|
2022-11-04 08:08:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
class NotAvailable(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
async def get_fragment_html(username: str):
|
|
|
|
try:
|
2023-01-12 13:19:54 +00:00
|
|
|
resp = await request.get(
|
|
|
|
f"https://fragment.com/username/{username}", follow_redirects=False
|
|
|
|
)
|
2022-11-04 08:08:45 +00:00
|
|
|
assert resp.status_code == 200
|
|
|
|
return resp.text
|
|
|
|
except AssertionError as e:
|
|
|
|
raise AssertionError from e
|
|
|
|
except Exception as e:
|
|
|
|
raise NotAvailable from e
|
|
|
|
|
|
|
|
|
|
|
|
def refresh_rate(html: str) -> None:
|
|
|
|
pattern = re.compile(r'"tonRate":"(.+)"}}')
|
|
|
|
with contextlib.suppress(Exception):
|
|
|
|
TON_TO_USD_RATE["rate"] = float(pattern.findall(html)[0])
|
|
|
|
|
|
|
|
|
|
|
|
def parse_user(username: str, html: str) -> UserName:
|
|
|
|
soup = BeautifulSoup(html, "lxml")
|
|
|
|
try:
|
|
|
|
refresh_rate(html)
|
2023-01-12 13:19:54 +00:00
|
|
|
status = AuctionStatus(
|
|
|
|
soup.find("span", {"class": "tm-section-header-status"}).getText()
|
|
|
|
)
|
2022-11-04 08:08:45 +00:00
|
|
|
if status == AuctionStatus.OnAuction and "Highest Bid" not in html:
|
|
|
|
status = AuctionStatus.Available
|
|
|
|
user = UserName(name=username, status=status)
|
|
|
|
if user.status == AuctionStatus.Available:
|
2023-01-12 13:19:54 +00:00
|
|
|
user.now_price = Price(
|
|
|
|
ton=int(
|
|
|
|
soup.find(
|
|
|
|
"div",
|
|
|
|
{"class": "table-cell-value tm-value icon-before icon-ton"},
|
|
|
|
)
|
|
|
|
.getText()
|
|
|
|
.replace(",", "")
|
|
|
|
)
|
|
|
|
)
|
2022-11-30 13:30:37 +00:00
|
|
|
elif user.status in [AuctionStatus.OnAuction, AuctionStatus.Sale]:
|
2022-11-04 08:08:45 +00:00
|
|
|
info = soup.find("div", {"class": "tm-section-box tm-section-bid-info"})
|
2023-01-12 13:19:54 +00:00
|
|
|
user.now_price = Price(
|
|
|
|
ton=int(
|
|
|
|
info.find(
|
|
|
|
"div",
|
|
|
|
{"class": "table-cell-value tm-value icon-before icon-ton"},
|
|
|
|
)
|
|
|
|
.getText()
|
|
|
|
.replace(",", "")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
user.end_time = datetime.fromisoformat(
|
|
|
|
soup.find("time", {"class": "tm-countdown-timer"})["datetime"]
|
|
|
|
)
|
2022-11-04 08:08:45 +00:00
|
|
|
elif user.status == AuctionStatus.Sold:
|
|
|
|
info = soup.find("div", {"class": "tm-section-box tm-section-bid-info"})
|
2023-01-12 13:19:54 +00:00
|
|
|
user.now_price = Price(
|
|
|
|
ton=int(
|
|
|
|
info.find(
|
|
|
|
"div",
|
|
|
|
{"class": "table-cell-value tm-value icon-before icon-ton"},
|
|
|
|
)
|
|
|
|
.getText()
|
|
|
|
.replace(",", "")
|
|
|
|
)
|
|
|
|
)
|
2022-11-04 08:08:45 +00:00
|
|
|
user.purchaser = info.find("a")["href"].split("/")[-1]
|
|
|
|
user.end_time = datetime.fromisoformat(info.find("time")["datetime"])
|
|
|
|
return user
|
|
|
|
except (AttributeError, ValueError) as e:
|
|
|
|
raise NotAvailable from e
|
|
|
|
|
|
|
|
|
|
|
|
async def search_fragment_html(username: str) -> str:
|
|
|
|
try:
|
2023-01-12 13:19:54 +00:00
|
|
|
resp = await request.get(
|
|
|
|
f"https://fragment.com/?query={username}", follow_redirects=False
|
|
|
|
)
|
2022-11-04 08:08:45 +00:00
|
|
|
return resp.text
|
|
|
|
except Exception as e:
|
|
|
|
raise NotAvailable from e
|
|
|
|
|
|
|
|
|
|
|
|
def search_user(username: str, html: str) -> UserName:
|
|
|
|
soup = BeautifulSoup(html, "lxml")
|
|
|
|
try:
|
|
|
|
user = soup.find_all("tr", {"class": "tm-row-selectable"})[0]
|
2023-01-12 13:19:54 +00:00
|
|
|
status = AuctionStatus(
|
|
|
|
user.find("div", {"class": "table-cell-status-thin"}).getText()
|
|
|
|
)
|
2022-11-04 08:08:45 +00:00
|
|
|
return UserName(name=username, status=status)
|
|
|
|
except (AttributeError, ValueError, IndexError) as e:
|
|
|
|
raise NotAvailable from e
|
|
|
|
|
|
|
|
|
|
|
|
async def parse_fragment(username: str) -> UserName:
|
|
|
|
try:
|
|
|
|
html = await get_fragment_html(username)
|
|
|
|
return parse_user(username, html)
|
|
|
|
except AssertionError:
|
|
|
|
html = await search_fragment_html(username)
|
|
|
|
return search_user(username, html)
|
2022-11-04 15:54:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def parse_sub(status: FragmentSubText, user: Optional[UserName], cid: int) -> str:
|
|
|
|
if status == FragmentSubText.Subscribe:
|
|
|
|
if user.status == [AuctionStatus.Sold, AuctionStatus.Unavailable]:
|
|
|
|
return "用户名已被卖出或者已被注册,无法订阅"
|
|
|
|
if await FragmentSub.get_by_cid_and_username(cid, user.name):
|
|
|
|
return "已经订阅过了这个用户名"
|
|
|
|
await FragmentSub.subscribe(cid, user.name)
|
|
|
|
return "订阅成功"
|
|
|
|
elif status == FragmentSubText.Unsubscribe:
|
|
|
|
if data := (await FragmentSub.get_by_cid_and_username(cid, user.name)):
|
|
|
|
await FragmentSub.unsubscribe(data)
|
|
|
|
return "取消订阅成功"
|
|
|
|
return "当前没有订阅这个用户名"
|
|
|
|
elif status == FragmentSubText.List:
|
|
|
|
if data := (await FragmentSub.get_by_cid(cid)):
|
2023-01-12 13:19:54 +00:00
|
|
|
return "目前已订阅:\n\n" + "\n".join(
|
|
|
|
[f"{i+1}. @{d.username}" for i, d in enumerate(data)]
|
|
|
|
)
|
2022-11-04 15:54:19 +00:00
|
|
|
return "还没有订阅任何用户名"
|