Compare commits

17 Commits

Author SHA1 Message Date
7293845847 new: use local module 2025-02-21 02:45:46 -05:00
0ace306d49 Merge remote-tracking branch 'upstream/master' 2025-02-21 00:43:46 -05:00
LWR
ebf7343586 docs: Update version 2025-01-08 00:08:15 +08:00
LWR
4e70b4605d fix: Fixed None error caused by api risk control 2025-01-08 00:07:56 +08:00
LWR
79afbf8a96 refactor: Refactor wbi utils 2025-01-07 22:41:19 +08:00
方天宬
06334653da feat: Add wbi utils 2025-01-07 22:37:39 +08:00
LWR
354b1b661f docs: Update version 2024-12-04 00:27:40 +08:00
LWR
7965198c85 fix: Fixed error caused by api risk control 2024-12-04 00:25:59 +08:00
LWR
e683569d92 feat: Added a tip for expired cookie 2024-12-02 00:00:28 +08:00
LWR
f50b81e958 fix: Fixed exception when API returns non-JSON data 2024-12-01 23:54:51 +08:00
LWR
a7df9e58de fix: Fixed possible slice step is 0 when generate box profit diagram 2024-11-11 23:02:53 +08:00
474932c05d fix: fix the error when profits are zeros 2024-11-09 09:41:16 -05:00
8ac3078ea3 fix: fix numpy version 2024-11-07 02:10:12 +08:00
89532428d5 fix: make top_count nonzero 2024-11-06 13:06:16 -05:00
0e396728c6 fix: add dependences 2024-11-02 22:15:04 +08:00
c832e1da52 update: add fork info 2024-10-26 12:36:08 -04:00
221b2c8b0f set pipenv 2024-10-26 22:44:59 +08:00
21 changed files with 1776 additions and 27 deletions

11
Pipfile Normal file
View File

@@ -0,0 +1,11 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[requires]
python_version = "3.11"
[packages]
starbot-bilibili = { path = "./", editable = true }

1548
Pipfile.lock generated Normal file

File diff suppressed because it is too large Load Diff

4
setup.cfg Normal file
View File

@@ -0,0 +1,4 @@
[egg_info]
tag_build =
tag_date = 0

44
setup.py Normal file
View File

@@ -0,0 +1,44 @@
#!/usr/bin/env python
# coding: utf-8
from setuptools import setup, find_namespace_packages
from os import path
this_directory = path.abspath(path.dirname(__file__))
with open("README.md", "r", encoding="utf-8") as f:
long_description = f.read()
setup(
name='starbot-bilibili',
version='2.0.14',
license='GNU Affero General Public License v3.0',
description='一款极速,多功能的哔哩哔哩推送机器人',
author='LWR',
author_email='lwr1104@qq.com',
url='https://github.com/Starlwr/StarBot',
packages=find_namespace_packages(),
package_data={
'starbot.api': ['*.json'],
'starbot.resource': ['*.png', '*.ttf']
},
install_requires=[
'Brotli>=1.0.9',
'aiomysql>=0.1.1',
'redis>=4.5.5',
'emoji>=2.2.0',
'graia-broadcast==0.19.2',
'creart==0.2.2',
'creart-graia==0.1.5',
'graia-ariadne==0.9.8',
'graia-saya==0.0.16',
'jieba>=0.42.1',
'scipy>=1.10.0',
'Pillow==9.5.0',
'numpy==1.24.3',
'matplotlib==3.7.1',
'wordcloud>=1.8.2.2'
],
keywords='starbot',
long_description=long_description,
long_description_content_type='text/markdown',
python_requires=">=3.8"
)

View File

@@ -15,6 +15,17 @@
}, },
"comment": "用户基本信息" "comment": "用户基本信息"
}, },
"info_wbi": {
"url": "https://api.bilibili.com/x/space/wbi/acc/info",
"method": "GET",
"verify": false,
"params": {
"mid": "int: uid",
"w_rid": "str: Wbi签名",
"wts": "int: 当前时间戳"
},
"comment": "用户基本信息WBI版本"
},
"relation": { "relation": {
"url": "https://api.bilibili.com/x/relation/stat", "url": "https://api.bilibili.com/x/relation/stat",
"method": "GET", "method": "GET",
@@ -42,6 +53,17 @@
}, },
"comment": "直播间基本信息" "comment": "直播间基本信息"
}, },
"live_wbi": {
"url": "https://api.bilibili.com/x/space/wbi/acc/info",
"method": "GET",
"verify": false,
"params": {
"mid": "int: uid",
"w_rid": "str: Wbi签名",
"wts": "int: 当前时间戳"
},
"comment": "直播间基本信息WBI版本"
},
"video": { "video": {
"url": "https://api.bilibili.com/x/space/arc/search", "url": "https://api.bilibili.com/x/space/arc/search",
"method": "GET", "method": "GET",

View File

@@ -117,6 +117,8 @@ async def room_data(app: Ariadne, source: Source, sender: Union[Friend, Group]):
pic.draw_text("") pic.draw_text("")
pic.draw_text_right(25, "Designed By StarBot", Color.GRAY) pic.draw_text_right(25, "Designed By StarBot", Color.GRAY)
pic.draw_text_right(25, "https://github.com/Starlwr/StarBot", Color.LINK) pic.draw_text_right(25, "https://github.com/Starlwr/StarBot", Color.LINK)
pic.draw_text_right(25, "本Bot由薛定谔的大喵维护", Color.LIGHTBLUE)
pic.draw_text_right(25, "https://gitea.phywyj.dynv6.net/wyj/starbot", Color.LINK)
pic.crop_and_paste_bottom() pic.crop_and_paste_bottom()
await app.send_message(sender, MessageChain(Image(base64=pic.base64()))) await app.send_message(sender, MessageChain(Image(base64=pic.base64())))

View File

@@ -109,6 +109,8 @@ async def room_data_total(app: Ariadne, source: Source, sender: Union[Friend, Gr
pic.draw_text("") pic.draw_text("")
pic.draw_text_right(25, "Designed By StarBot", Color.GRAY) pic.draw_text_right(25, "Designed By StarBot", Color.GRAY)
pic.draw_text_right(25, "https://github.com/Starlwr/StarBot", Color.LINK) pic.draw_text_right(25, "https://github.com/Starlwr/StarBot", Color.LINK)
pic.draw_text_right(25, "本Bot由薛定谔的大喵维护", Color.LIGHTBLUE)
pic.draw_text_right(25, "https://gitea.phywyj.dynv6.net/wyj/starbot", Color.LINK)
pic.crop_and_paste_bottom() pic.crop_and_paste_bottom()
await app.send_message(sender, MessageChain(Image(base64=pic.base64()))) await app.send_message(sender, MessageChain(Image(base64=pic.base64())))

View File

@@ -170,6 +170,8 @@ async def user_data(app: Ariadne, source: Source, sender: Union[Friend, Group],
pic.draw_text("") pic.draw_text("")
pic.draw_text_right(25, "Designed By StarBot", Color.GRAY) pic.draw_text_right(25, "Designed By StarBot", Color.GRAY)
pic.draw_text_right(25, "https://github.com/Starlwr/StarBot", Color.LINK) pic.draw_text_right(25, "https://github.com/Starlwr/StarBot", Color.LINK)
pic.draw_text_right(25, "本Bot由薛定谔的大喵维护", Color.LIGHTBLUE)
pic.draw_text_right(25, "https://gitea.phywyj.dynv6.net/wyj/starbot", Color.LINK)
pic.crop_and_paste_bottom() pic.crop_and_paste_bottom()
await app.send_message(sender, MessageChain(Image(base64=pic.base64()))) await app.send_message(sender, MessageChain(Image(base64=pic.base64())))

View File

@@ -183,6 +183,8 @@ async def user_data_total(app: Ariadne, source: Source, sender: Union[Friend, Gr
pic.draw_text("") pic.draw_text("")
pic.draw_text_right(25, "Designed By StarBot", Color.GRAY) pic.draw_text_right(25, "Designed By StarBot", Color.GRAY)
pic.draw_text_right(25, "https://github.com/Starlwr/StarBot", Color.LINK) pic.draw_text_right(25, "https://github.com/Starlwr/StarBot", Color.LINK)
pic.draw_text_right(25, "本Bot由薛定谔的大喵维护", Color.LIGHTBLUE)
pic.draw_text_right(25, "https://gitea.phywyj.dynv6.net/wyj/starbot", Color.LINK)
pic.crop_and_paste_bottom() pic.crop_and_paste_bottom()
await app.send_message(sender, MessageChain(Image(base64=pic.base64()))) await app.send_message(sender, MessageChain(Image(base64=pic.base64())))

View File

@@ -158,6 +158,8 @@ async def _help(app: Ariadne, sender: Union[Friend, Group]):
# 底部版权信息,请务必保留此处 # 底部版权信息,请务必保留此处
pic.draw_text_right(25, "Designed By StarBot", Color.GRAY) pic.draw_text_right(25, "Designed By StarBot", Color.GRAY)
pic.draw_text_right(25, "https://github.com/Starlwr/StarBot", Color.LINK) pic.draw_text_right(25, "https://github.com/Starlwr/StarBot", Color.LINK)
pic.draw_text_right(25, "本Bot由薛定谔的大喵维护", Color.LIGHTBLUE)
pic.draw_text_right(25, "https://gitea.phywyj.dynv6.net/wyj/starbot", Color.LINK)
pic.crop_and_paste_bottom() pic.crop_and_paste_bottom()
await app.send_message(sender, MessageChain(Image(base64=pic.base64()))) await app.send_message(sender, MessageChain(Image(base64=pic.base64())))

View File

@@ -129,6 +129,8 @@ async def ranking(app: Ariadne,
pic.draw_text("") pic.draw_text("")
pic.draw_text_right(25, "Designed By StarBot", Color.GRAY) pic.draw_text_right(25, "Designed By StarBot", Color.GRAY)
pic.draw_text_right(25, "https://github.com/Starlwr/StarBot", Color.LINK) pic.draw_text_right(25, "https://github.com/Starlwr/StarBot", Color.LINK)
pic.draw_text_right(25, "本Bot由薛定谔的大喵维护", Color.LIGHTBLUE)
pic.draw_text_right(25, "https://gitea.phywyj.dynv6.net/wyj/starbot", Color.LINK)
pic.crop_and_paste_bottom() pic.crop_and_paste_bottom()
await app.send_message(sender, MessageChain(Image(base64=pic.base64()))) await app.send_message(sender, MessageChain(Image(base64=pic.base64())))

View File

@@ -124,6 +124,8 @@ async def ranking_double(app: Ariadne,
pic.draw_text("") pic.draw_text("")
pic.draw_text_right(25, "Designed By StarBot", Color.GRAY) pic.draw_text_right(25, "Designed By StarBot", Color.GRAY)
pic.draw_text_right(25, "https://github.com/Starlwr/StarBot", Color.LINK) pic.draw_text_right(25, "https://github.com/Starlwr/StarBot", Color.LINK)
pic.draw_text_right(25, "本Bot由薛定谔的大喵维护", Color.LIGHTBLUE)
pic.draw_text_right(25, "https://gitea.phywyj.dynv6.net/wyj/starbot", Color.LINK)
pic.crop_and_paste_bottom() pic.crop_and_paste_bottom()
await app.send_message(sender, MessageChain(Image(base64=pic.base64()))) await app.send_message(sender, MessageChain(Image(base64=pic.base64())))

View File

@@ -26,7 +26,7 @@ class StarBot:
""" """
StarBot 类 StarBot 类
""" """
VERSION = "2.0.11" VERSION = "2.0.14"
STARBOT_ASCII_LOGO = "\n".join( STARBOT_ASCII_LOGO = "\n".join(
( (
r" _____ _ ____ _ ", r" _____ _ ____ _ ",
@@ -35,7 +35,7 @@ class StarBot:
r" \___ \| __/ _` | '__| _ < / _ \| __|", r" \___ \| __/ _` | '__| _ < / _ \| __|",
r" ____) | || (_| | | | |_) | (_) | |_ ", r" ____) | || (_| | | | |_) | (_) | |_ ",
r" |_____/ \__\__,_|_| |____/ \___/ \__|", r" |_____/ \__\__,_|_| |____/ \___/ \__|",
f" StarBot - (v{VERSION}) 2024-11-09", f" StarBot - (v{VERSION}) 2025-01-08",
r" Github: https://github.com/Starlwr/StarBot", r" Github: https://github.com/Starlwr/StarBot",
r"", r"",
r"", r"",

View File

@@ -31,6 +31,9 @@ async def dynamic_spider(datasource: DataSource):
except ResponseCodeException as ex: except ResponseCodeException as ex:
if ex.code == -6: if ex.code == -6:
continue continue
if ex.code == 4100000:
logger.error("B 站登录凭据已失效, 无法继续抓取动态,请配置新登录凭据后重启服务")
continue
logger.error(f"动态推送任务抓取最新动态异常, HTTP 错误码: {ex.code} ({ex.msg})") logger.error(f"动态推送任务抓取最新动态异常, HTTP 错误码: {ex.code} ({ex.msg})")
except NetworkException: except NetworkException:
continue continue

View File

@@ -167,6 +167,48 @@ class LiveRoom:
} }
return await request(api['method'], api["url"], params, credential=self.credential) return await request(api['method'], api["url"], params, credential=self.credential)
async def get_room_info_v2(self):
"""
获取直播间信息(标题,简介等)
"""
api = "https://api.live.bilibili.com/room/v1/Room/get_info"
params = {
"room_id": self.room_display_id
}
return await request("GET", api, params, credential=self.credential)
async def get_fans_medal_info(self, uid: int):
"""
获取粉丝勋章信息
Args:
uid: 用户 UID
"""
api = "https://api.live.bilibili.com/xlive/app-ucenter/v1/fansMedal/fans_medal_info"
params = {
"target_id": uid,
"room_id": self.room_display_id
}
return await request("GET", api, params, credential=self.credential)
async def get_guards_info(self, uid: int):
"""
获取大航海信息
Args:
uid: 用户 UID
"""
api = "https://api.live.bilibili.com/xlive/app-room/v2/guardTab/topListNew"
params = {
"roomid": self.room_display_id,
"page": 1,
"ruid": uid,
"page_size": 20,
"typ": 5,
"platform": "web"
}
return await request("GET", api, params, credential=self.credential)
async def get_user_info_in_room(self): async def get_user_info_in_room(self):
""" """
获取自己在直播间的信息(粉丝勋章等级,直播用户等级等) 获取自己在直播间的信息(粉丝勋章等级,直播用户等级等)

View File

@@ -2,6 +2,7 @@ import asyncio
import time import time
import typing import typing
from asyncio import AbstractEventLoop from asyncio import AbstractEventLoop
from datetime import datetime
from typing import Optional, Any, Union, List from typing import Optional, Any, Union, List
from loguru import logger from loguru import logger
@@ -183,6 +184,8 @@ class Up(BaseModel):
locked = False locked = False
room_info = {} room_info = {}
fans_medal_info = {}
guards_info = {}
# 是否为真正开播 # 是否为真正开播
if "live_time" in event["data"]: if "live_time" in event["data"]:
@@ -204,25 +207,34 @@ class Up(BaseModel):
logger.opt(colors=True).info(f"<magenta>[开播] {self.uname} ({self.room_id})</>") logger.opt(colors=True).info(f"<magenta>[开播] {self.uname} ({self.room_id})</>")
try: try:
room_info = await self.__live_room.get_room_info() room_info = await self.__live_room.get_room_info_v2()
fans_medal_info = await self.__live_room.get_fans_medal_info(self.uid)
guards_info = await self.__live_room.get_guards_info(self.uid)
except ResponseCodeException as ex: except ResponseCodeException as ex:
if ex.code == 19002005: if ex.code == 19002005:
locked = True locked = True
logger.warning(f"{self.uname} ({self.room_id}) 的直播间已加密") logger.warning(f"{self.uname} ({self.room_id}) 的直播间已加密")
else:
logger.error(f"{self.uname} ({self.room_id}) 的直播间信息获取失败, 错误信息: {ex.code} ({ex.msg})")
if not locked: if not locked:
self.uname = room_info["anchor_info"]["base_info"]["uname"] # 此处若有合适 API 需更新一下最新昵称
pass
live_start_time = room_info["room_info"]["live_start_time"] if not locked else int(time.time()) if locked:
live_start_time = int(time.time())
else:
if room_info["live_time"] != "0000-00-00 00:00:00":
time_format = "%Y-%m-%d %H:%M:%S"
live_start_time = int(datetime.strptime(room_info["live_time"], time_format).timestamp())
else:
live_start_time = int(time.time())
await redis.set_live_start_time(self.room_id, live_start_time) await redis.set_live_start_time(self.room_id, live_start_time)
if not locked: if not locked:
fans_count = room_info["anchor_info"]["relation_info"]["attention"] fans_count = room_info["attention"]
if room_info["anchor_info"]["medal_info"] is None: fans_medal_count = fans_medal_info["fans_medal_light_count"]
fans_medal_count = 0 guard_count = guards_info["info"]["num"]
else:
fans_medal_count = room_info["anchor_info"]["medal_info"]["fansclub"]
guard_count = room_info["guard_info"]["count"]
await redis.set_fans_count(self.room_id, live_start_time, fans_count) await redis.set_fans_count(self.room_id, live_start_time, fans_count)
await redis.set_fans_medal_count(self.room_id, live_start_time, fans_medal_count) await redis.set_fans_medal_count(self.room_id, live_start_time, fans_medal_count)
await redis.set_guard_count(self.room_id, live_start_time, guard_count) await redis.set_guard_count(self.room_id, live_start_time, guard_count)
@@ -231,12 +243,11 @@ class Up(BaseModel):
# 推送开播消息 # 推送开播消息
if not locked: if not locked:
arg_base = room_info["room_info"]
args = { args = {
"{uname}": self.uname, "{uname}": self.uname,
"{title}": arg_base["title"], "{title}": room_info["title"],
"{url}": f"https://live.bilibili.com/{self.room_id}", "{url}": f"https://live.bilibili.com/{self.room_id}",
"{cover}": "".join(["{urlpic=", arg_base["cover"], "}"]) "{cover}": "".join(["{urlpic=", room_info["user_cover"], "}"])
} }
else: else:
args = { args = {
@@ -482,11 +493,15 @@ class Up(BaseModel):
locked = False locked = False
room_info = {} room_info = {}
fans_medal_info = {}
guards_info = {}
# 基础数据变动 # 基础数据变动
if self.__any_live_report_item_enabled(["fans_change", "fans_medal_change", "guard_change"]): if self.__any_live_report_item_enabled(["fans_change", "fans_medal_change", "guard_change"]):
try: try:
room_info = await self.__live_room.get_room_info() room_info = await self.__live_room.get_room_info_v2()
fans_medal_info = await self.__live_room.get_fans_medal_info(self.uid)
guards_info = await self.__live_room.get_guards_info(self.uid)
except ResponseCodeException as ex: except ResponseCodeException as ex:
if ex.code == 19002005: if ex.code == 19002005:
locked = True locked = True
@@ -506,21 +521,18 @@ class Up(BaseModel):
else: else:
guard_count = -1 guard_count = -1
if room_info["anchor_info"]["medal_info"] is None: fans_medal_count_after = fans_medal_info["fans_medal_light_count"]
fans_medal_count_after = 0
else:
fans_medal_count_after = room_info["anchor_info"]["medal_info"]["fansclub"]
live_report_param.update({ live_report_param.update({
# 粉丝变动 # 粉丝变动
"fans_before": fans_count, "fans_before": fans_count,
"fans_after": room_info["anchor_info"]["relation_info"]["attention"], "fans_after": room_info["attention"],
# 粉丝团(粉丝勋章数)变动 # 粉丝团(粉丝勋章数)变动
"fans_medal_before": fans_medal_count, "fans_medal_before": fans_medal_count,
"fans_medal_after": fans_medal_count_after, "fans_medal_after": fans_medal_count_after,
# 大航海变动 # 大航海变动
"guard_before": guard_count, "guard_before": guard_count,
"guard_after": room_info["guard_info"]["count"] "guard_after": guards_info["info"]["num"]
}) })
# 直播数据 # 直播数据

View File

@@ -69,6 +69,8 @@ class DynamicPicGenerator:
pic.move_pos(0, 15) pic.move_pos(0, 15)
pic.draw_text_right(25, "Designed By StarBot", Color.GRAY) pic.draw_text_right(25, "Designed By StarBot", Color.GRAY)
pic.draw_text_right(25, "https://github.com/Starlwr/StarBot", Color.LINK) pic.draw_text_right(25, "https://github.com/Starlwr/StarBot", Color.LINK)
pic.draw_text_right(25, "本Bot由薛定谔的大喵维护", Color.LIGHTBLUE)
pic.draw_text_right(25, "https://gitea.phywyj.dynv6.net/wyj/starbot", Color.LINK)
pic.crop_and_paste_bottom() pic.crop_and_paste_bottom()
return pic.base64() return pic.base64()

View File

@@ -405,6 +405,8 @@ class LiveReportGenerator:
pic.set_row_space(10) pic.set_row_space(10)
pic.draw_text_right(50, "Designed By StarBot", Color.GRAY, logo_limit) pic.draw_text_right(50, "Designed By StarBot", Color.GRAY, logo_limit)
pic.draw_text_right(50, "https://github.com/Starlwr/StarBot", Color.LINK, logo_limit) pic.draw_text_right(50, "https://github.com/Starlwr/StarBot", Color.LINK, logo_limit)
pic.draw_text_right(50, "本Bot由薛定谔的大喵维护", Color.LIGHTBLUE, logo_limit)
pic.draw_text_right(50, "https://gitea.phywyj.dynv6.net/wyj/starbot", Color.LINK, logo_limit)
pic.crop_and_paste_bottom() pic.crop_and_paste_bottom()
return pic.base64() return pic.base64()
@@ -669,12 +671,12 @@ class LiveReportGenerator:
length = len(profits) length = len(profits)
indexs = list(range(0, length)) indexs = list(range(0, length))
abs_max = math.ceil(max(max(profits), abs(min(profits)))) abs_max = math.ceil(max(max(profits), abs(min(profits)), 0.01))
start = -abs_max - (-abs_max % 10) start = -abs_max - (-abs_max % 10)
end = abs_max + (-abs_max % 10) end = abs_max + (-abs_max % 10)
step = int((end - start) / 10) step = int((end - start) / 10)
yticks = list(range(start, end)[::step]) yticks = list(range(start, end)[::step]) if step != 0 else [0]
yticks.append(end) yticks.append(end)
return cls.__get_line_diagram( return cls.__get_line_diagram(
indexs, profits, [], yticks, [], [], (-1, length), (start, end), width indexs, profits, [], yticks, [], [], (-1, length), (start, end), width

View File

@@ -153,7 +153,7 @@ class RankingGenerator:
reverse_bar_x = face_x + offset reverse_bar_x = face_x + offset
top_bar_width = (width - face_size) / 2 + offset top_bar_width = (width - face_size) / 2 + offset
if top_count is None: if top_count is None:
top_count = max(max(counts), abs(min(counts))) top_count = max(max(counts), abs(min(counts)), 0.001)
chart = PicGenerator(width, (face_size * count) + (row_space * (count - 1))) chart = PicGenerator(width, (face_size * count) + (row_space * (count - 1)))
chart.set_row_space(row_space) chart.set_row_space(row_space)

View File

@@ -73,7 +73,7 @@ async def request(method: str,
# 使用 Referer 和 UA 请求头以绕过反爬虫机制 # 使用 Referer 和 UA 请求头以绕过反爬虫机制
default_headers = { default_headers = {
"Referer": "https://www.bilibili.com", "Referer": "https://www.bilibili.com",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Core/1.94.218.400 QQBrowser/12.1.5496.400" "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.97 Safari/537.36 Core/1.116.462.400 QQBrowser/13.3.6197.400"
} }
headers = default_headers headers = default_headers
@@ -134,7 +134,7 @@ async def request(method: str,
content_type = resp.headers.get("content-type") content_type = resp.headers.get("content-type")
# 不是 application/json # 不是 application/json
if content_type.lower().index("application/json") == -1: if content_type.lower().find("application/json") == -1:
raise ResponseException("响应不是 application/json 类型") raise ResponseException("响应不是 application/json 类型")
raw_data = await resp.text() raw_data = await resp.text()
@@ -156,7 +156,7 @@ async def request(method: str,
if code != 0: if code != 0:
# 4101131: 加载错误,请稍后再试, 22015: 您的账号异常,请稍后再试 # 4101131: 加载错误,请稍后再试, 22015: 您的账号异常,请稍后再试
if code == 4101131 or code == 22015: if code == 4101131 or code == 22015 or code == -352:
await asyncio.sleep(10) await asyncio.sleep(10)
continue continue

45
starbot/utils/wbi.py Normal file
View File

@@ -0,0 +1,45 @@
# https://socialsisteryi.github.io/bilibili-API-collect/docs/misc/sign/wbi.html#python
from functools import reduce
from hashlib import md5
import urllib.parse
import time
from starbot.utils.network import request
from starbot.utils.utils import get_credential
mixinKeyEncTab = [
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49,
33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40,
61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,
36, 20, 34, 44, 52
]
def get_mixin_key(orig: str):
return reduce(lambda s, i: s + orig[i], mixinKeyEncTab, '')[:32]
def enc_wbi(params: dict, img_key: str, sub_key: str):
mixin_key = get_mixin_key(img_key + sub_key)
curr_time = round(time.time())
params['wts'] = curr_time # 添加 wts 字段
params = dict(sorted(params.items())) # 按照 key 重排参数
# 过滤 value 中的 "!'()*" 字符
params = {
k: ''.join(filter(lambda c: c not in "!'()*", str(v)))
for k, v
in params.items()
}
query = urllib.parse.urlencode(params) # 序列化参数
wbi_sign = md5((query + mixin_key).encode()).hexdigest() # 计算 w_rid
params['w_rid'] = wbi_sign
return params
async def get_wbi_keys() -> tuple[str, str]:
"""获取最新的 img_key 和 sub_key"""
result = await request("GET", "https://api.bilibili.com/x/web-interface/nav", credential=get_credential())
img_url = result['wbi_img']['img_url']
sub_url = result['wbi_img']['sub_url']
return img_url.rsplit('/', 1)[1].split('.')[0], sub_url.rsplit('/', 1)[1].split('.')[0]