Last active
December 12, 2022 09:50
-
-
Save XiaoMiku01/e3a04a534df2957d0f1e72a9ea76202a to your computer and use it in GitHub Desktop.
自定义 B站 NFT 空间背景和头像
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import time | |
from typing import Union | |
import requests | |
from hashlib import md5 | |
from typing import Union | |
from urllib.parse import urlencode | |
from requests_toolbelt.multipart.encoder import MultipartEncoder | |
import imghdr | |
UID = 0 # 你的UID | |
ACCESS_KEY = "" # 你的ACCESS_KEY (TV端,非TV端自行更换下面的APPKEY,APPSECRET,TV端access_key获取工具 https://github.com/XiaoMiku01/fansMedalHelper/releases/tag/logintool) | |
FACE_PATH = "face.jpg" # 头像路径 推荐正方形 | |
BG_PATH = "background.jpg" # 背景图路径 推荐 9:16 竖版原图 效果非常好 | |
class Crypto: | |
APPKEY = '4409e2ce8ffd12b8' | |
APPSECRET = '59b43e04ad6965f34319062b478f83dd' | |
@staticmethod | |
def md5(data: Union[str, bytes]) -> str: | |
'''generates md5 hex dump of `str` or `bytes`''' | |
if type(data) == str: | |
return md5(data.encode()).hexdigest() | |
return md5(data).hexdigest() | |
@staticmethod | |
def sign(data: Union[str, dict]) -> str: | |
'''salted sign funtion for `dict`(converts to qs then parse) & `str`''' | |
if isinstance(data, dict): | |
_str = urlencode(data) | |
elif type(data) != str: | |
raise TypeError | |
return Crypto.md5(_str + Crypto.APPSECRET) | |
class SingableDict(dict): | |
@property | |
def sorted(self): | |
'''returns a alphabetically sorted version of `self`''' | |
return dict(sorted(self.items())) | |
@property | |
def signed(self): | |
'''returns our sorted self with calculated `sign` as a new key-value pair at the end''' | |
_sorted = self.sorted | |
return {**_sorted, 'sign': Crypto.sign(_sorted)} | |
def get_image_type(file_path): | |
with open(file_path, 'rb') as f: | |
data = f.read() | |
return imghdr.what(None, data) | |
def upload_image(file_path): | |
url = "https://api.bilibili.com/x/upload/app/image?access_key=" + ACCESS_KEY | |
payload = {'bucket': 'medialist', 'dir': 'nft'} | |
with open(file_path, 'rb') as f: | |
type = f'image/{imghdr.what(f)}' | |
print(type) | |
files = [ | |
( | |
'file', | |
(file_path, f, type), | |
) | |
] | |
response = requests.request("POST", url, data=payload, files=files) | |
print(response.text) | |
return response.json()['data']['location'] | |
def get_one_card_id(): | |
url = "https://api.bilibili.com/x/vas/nftcard/cardlist" | |
params = SingableDict( | |
{ | |
"access_key": ACCESS_KEY, | |
"act_id": "4", | |
"appkey": "4409e2ce8ffd12b8", | |
"disable_rcmd": "0", | |
"ruid": UID, | |
"statistics": "{\"appId\":1,\"platform\":3,\"version\":\"7.9.0\",\"abtest\":\"\"}", | |
"ts": int(time.time()), | |
} | |
).signed | |
response = requests.request("GET", url, params=params) | |
data = response.json() | |
if data['code'] != 0: | |
print(data) | |
return | |
for round in data['data']['round_list']: | |
for card in round['card_list']: | |
if card['card_type'] == 1 and card['card_id_list']: | |
print(card['card_id_list'][0]['card_id']) | |
return card['card_id_list'][0]['card_id'] | |
print('没有 R 级别胶囊计划的卡片') | |
return None | |
def set_face(card_id): | |
api = "https://api.bilibili.com/x/member/app/face/digitalKit/update" | |
params = { | |
"access_key": ACCESS_KEY, | |
"appkey": "4409e2ce8ffd12b8", | |
"build": "7090300", | |
"c_locale": "zh_CN", | |
"channel": "xiaomi", | |
"disable_rcmd": "0", | |
"mobi_app": "android", | |
"platform": "android", | |
"s_locale": "zh_CN", | |
"statistics": "{\"appId\":1,\"platform\":3,\"version\":\"7.9.0\",\"abtest\":\"\"}", | |
"ts": int(time.time()), | |
} | |
m = MultipartEncoder( | |
fields={ | |
'digital_kit_id': str(card_id), | |
'face': ('face', open(FACE_PATH, 'rb'), 'application/octet-stream'), | |
} | |
) | |
headers = { | |
"Content-Type": m.content_type, | |
} | |
response = requests.request("POST", api, data=m, headers=headers, params=params) | |
if response.json()['code'] != 0: | |
print(response.json()) | |
return | |
print('设置头像成功, 请等待审核') | |
def set_bg_img(img_url, card_id): | |
api = "https://app.bilibili.com//x/v2/space/digital/bind" | |
data = { | |
"access_key": ACCESS_KEY, | |
"appkey": "4409e2ce8ffd12b8", | |
"build": "7090300", | |
"c_locale": "zh_CN", | |
"card_id": card_id, | |
"channel": "xiaomi", | |
"disable_rcmd": "0", | |
"img_url": img_url, | |
"mobi_app": "android", | |
"platform": "android", | |
"s_locale": "zh_CN", | |
"space_bg_type": "1", | |
"statistics": "{\"appId\":1,\"platform\":3,\"version\":\"7.9.0\",\"abtest\":\"\"}", | |
"ts": int(time.time()), | |
} | |
headers = { | |
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8", | |
} | |
response = requests.request("POST", api, data=data, headers=headers) | |
if response.json()['code'] != 0: | |
print(response.json()) | |
return | |
print('设置背景成功') | |
def main(): | |
card_id = get_one_card_id() | |
if not card_id: | |
return | |
img_url = upload_image(BG_PATH) | |
set_bg_img(img_url, card_id) | |
set_face(card_id) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment