mirror of
https://github.com/Xtao-Labs/iShotaBot.git
synced 2024-11-16 04:35:55 +00:00
✨ 支持查询用户名遗产
This commit is contained in:
parent
356ccafe6c
commit
aa74550fa0
85
defs/fragment.py
Normal file
85
defs/fragment.py
Normal file
@ -0,0 +1,85 @@
|
||||
import contextlib
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
from init import request
|
||||
from models.fragment import AuctionStatus, UserName, TON_TO_USD_RATE, Price
|
||||
|
||||
|
||||
class NotAvailable(Exception):
|
||||
pass
|
||||
|
||||
|
||||
async def get_fragment_html(username: str):
|
||||
try:
|
||||
resp = await request.get(f"https://fragment.com/username/{username}", follow_redirects=False)
|
||||
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)
|
||||
status = AuctionStatus(soup.find("span", {"class": "tm-section-header-status"}).getText())
|
||||
if status == AuctionStatus.OnAuction and "Highest Bid" not in html:
|
||||
status = AuctionStatus.Available
|
||||
user = UserName(name=username, status=status)
|
||||
if user.status == AuctionStatus.Available:
|
||||
user.now_price = Price(ton=int(soup.find(
|
||||
"div", {"class": "table-cell-value tm-value icon-before icon-ton"}
|
||||
).getText().replace(",", "")))
|
||||
elif user.status == [AuctionStatus.OnAuction, AuctionStatus.Sale]:
|
||||
info = soup.find("div", {"class": "tm-section-box tm-section-bid-info"})
|
||||
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"])
|
||||
elif user.status == AuctionStatus.Sold:
|
||||
info = soup.find("div", {"class": "tm-section-box tm-section-bid-info"})
|
||||
user.now_price = Price(ton=int(info.find(
|
||||
"div", {"class": "table-cell-value tm-value icon-before icon-ton"}
|
||||
).getText().replace(",", "")))
|
||||
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:
|
||||
resp = await request.get(f"https://fragment.com/?query={username}", follow_redirects=False)
|
||||
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]
|
||||
status = AuctionStatus(user.find("div", {"class": "table-cell-status-thin"}).getText())
|
||||
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)
|
87
models/fragment.py
Normal file
87
models/fragment.py
Normal file
@ -0,0 +1,87 @@
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
TON_TO_USD_RATE = {"rate": 1.61}
|
||||
|
||||
|
||||
class Price(BaseModel):
|
||||
ton: int
|
||||
|
||||
@property
|
||||
def usd(self) -> float:
|
||||
return self.ton * TON_TO_USD_RATE["rate"]
|
||||
|
||||
@property
|
||||
def text(self) -> str:
|
||||
return f"{self.ton} TON ~ ${round(self.usd, 2)}"
|
||||
|
||||
|
||||
class AuctionStatus(Enum):
|
||||
Available = "Available"
|
||||
OnAuction = "On auction"
|
||||
Sold = "Sold"
|
||||
Sale = "For sale"
|
||||
ComingSoon = "Coming soon"
|
||||
Unavailable = "Unavailable"
|
||||
|
||||
@property
|
||||
def text(self) -> str:
|
||||
if self.value == "Available":
|
||||
return "等待出价"
|
||||
elif self.value == "On auction":
|
||||
return "正在拍卖"
|
||||
elif self.value == "Sold":
|
||||
return "已售出"
|
||||
elif self.value == "For sale":
|
||||
return "正在出售"
|
||||
elif self.value == "Coming soon":
|
||||
return "即将拍卖"
|
||||
elif self.value == "Unavailable":
|
||||
return "暂时不会出售"
|
||||
|
||||
|
||||
class UserName(BaseModel):
|
||||
name: str
|
||||
now_price: Optional[Price]
|
||||
end_time: Optional[datetime]
|
||||
purchaser: Optional[str]
|
||||
status: AuctionStatus
|
||||
|
||||
@property
|
||||
def end_human_time(self) -> str:
|
||||
diff = self.end_time - datetime.now(timezone.utc)
|
||||
args = []
|
||||
if diff.days:
|
||||
args.append(f"{diff.days} 天")
|
||||
if diff.seconds // 3600:
|
||||
args.append(f"{diff.seconds // 3600} 时")
|
||||
if diff.seconds % 3600 // 60:
|
||||
args.append(f"{diff.seconds % 3600 // 60} 分")
|
||||
if diff.seconds % 60:
|
||||
args.append(f"{diff.seconds % 60} 秒")
|
||||
return " ".join(args)
|
||||
|
||||
@property
|
||||
def strf_end_time(self) -> str:
|
||||
return (self.end_time + timedelta(hours=8)).strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
@property
|
||||
def text(self) -> str:
|
||||
text = f"用户名:@{self.name}\n" \
|
||||
f"状态:{self.status.text}\n"
|
||||
if self.status == AuctionStatus.Available:
|
||||
text += f"最低出价:{self.now_price.text}\n"
|
||||
elif self.status == AuctionStatus.OnAuction:
|
||||
text += f"目前最高出价:{self.now_price.text}\n" \
|
||||
f"距离拍卖结束:{self.end_human_time}\n"
|
||||
elif self.status == AuctionStatus.Sold:
|
||||
text += f"售出价格:{self.now_price.text}\n" \
|
||||
f"最终买家:<a href='https://tonapi.io/account/{self.purchaser}'>{self.purchaser[:12]}...</a>\n" \
|
||||
f"售出时间:{self.strf_end_time}\n"
|
||||
elif self.status == AuctionStatus.Sale:
|
||||
text += f"售价:{self.now_price.text}\n" \
|
||||
f"距离出售结束:{self.end_human_time}\n"
|
||||
return text
|
61
modules/fragment.py
Normal file
61
modules/fragment.py
Normal file
@ -0,0 +1,61 @@
|
||||
import re
|
||||
|
||||
from pyrogram import Client
|
||||
from pyrogram.types import InlineQuery, InlineQueryResultArticle, InputTextMessageContent, InlineKeyboardMarkup, \
|
||||
InlineKeyboardButton
|
||||
|
||||
from defs.fragment import parse_fragment, NotAvailable
|
||||
|
||||
QUERY_PATTERN = re.compile(r"^@\w[a-zA-Z0-9_]{3,32}$")
|
||||
|
||||
|
||||
@Client.on_inline_query()
|
||||
async def fragment_inline(_, inline_query: InlineQuery):
|
||||
username = inline_query.query
|
||||
if not username.startswith("@"):
|
||||
username = f"@{username}"
|
||||
if not QUERY_PATTERN.match(username):
|
||||
return await inline_query.answer(
|
||||
results=[],
|
||||
switch_pm_text="请输入 @username 来查询遗产",
|
||||
switch_pm_parameter="start",
|
||||
cache_time=0,
|
||||
)
|
||||
username = username[1:]
|
||||
try:
|
||||
user = await parse_fragment(username)
|
||||
text = user.text
|
||||
except NotAvailable:
|
||||
text = f"用户名:@{username}\n" \
|
||||
f"状态:暂未开放购买\n"
|
||||
except Exception:
|
||||
text = ""
|
||||
if not text:
|
||||
return await inline_query.answer(
|
||||
results=[],
|
||||
switch_pm_text="查询失败了 ~ 呜呜呜",
|
||||
switch_pm_parameter="start",
|
||||
cache_time=0,
|
||||
)
|
||||
results = [
|
||||
InlineQueryResultArticle(
|
||||
title=username,
|
||||
input_message_content=InputTextMessageContent(text),
|
||||
url=f"https://fragment.com/username/{username}",
|
||||
description="点击发送详情",
|
||||
reply_markup=InlineKeyboardMarkup(
|
||||
[
|
||||
[InlineKeyboardButton(
|
||||
"Open",
|
||||
url=f"https://fragment.com/username/{username}"
|
||||
)]
|
||||
]
|
||||
)
|
||||
),
|
||||
]
|
||||
await inline_query.answer(
|
||||
results=results,
|
||||
switch_pm_text="查询成功",
|
||||
switch_pm_parameter="start",
|
||||
cache_time=0
|
||||
)
|
@ -1,5 +1,5 @@
|
||||
pyrogram==2.0.57
|
||||
tgcrypto
|
||||
pyrogram==2.0.59
|
||||
tgcrypto==1.2.4
|
||||
httpx
|
||||
pillow
|
||||
cashews
|
||||
@ -18,3 +18,4 @@ lxml
|
||||
sqlalchemy
|
||||
sqlmodel
|
||||
aiosqlite
|
||||
pydantic
|
||||
|
Loading…
Reference in New Issue
Block a user