185 lines
7.2 KiB
Python
185 lines
7.2 KiB
Python
|
|
# -*- coding: utf-8 -*-
|
|||
|
|
"""
|
|||
|
|
cron: 0 8 * * *
|
|||
|
|
new Env('网易音乐合伙人');
|
|||
|
|
"""
|
|||
|
|
import requests
|
|||
|
|
import base64
|
|||
|
|
import codecs
|
|||
|
|
import execjs
|
|||
|
|
import json
|
|||
|
|
|
|||
|
|
from Crypto.Cipher import AES
|
|||
|
|
from notify import send
|
|||
|
|
|
|||
|
|
|
|||
|
|
def to_16(key):
|
|||
|
|
while len(key) % 16 != 0:
|
|||
|
|
key += '\0'
|
|||
|
|
return str.encode(key)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def aes_encrypt(text, key, iv):
|
|||
|
|
bs = AES.block_size
|
|||
|
|
pad2 = lambda s: s + (bs - len(s) % bs) * chr(bs - len(s) % bs)
|
|||
|
|
encryptor = AES.new(to_16(key), AES.MODE_CBC, to_16(iv))
|
|||
|
|
encrypt_aes = encryptor.encrypt(str.encode(pad2(text)))
|
|||
|
|
encrypt_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8')
|
|||
|
|
return encrypt_text
|
|||
|
|
|
|||
|
|
|
|||
|
|
def rsa_encrypt(text, pubKey, modulus):
|
|||
|
|
text = text[::-1]
|
|||
|
|
rs = int(codecs.encode(text.encode('utf-8'), 'hex_codec'), 16) ** int(pubKey, 16) % int(modulus, 16)
|
|||
|
|
return format(rs, 'x').zfill(256)
|
|||
|
|
|
|||
|
|
|
|||
|
|
# 获取i值的函数,即随机生成长度为16的字符串
|
|||
|
|
get_i = execjs.compile(r"""
|
|||
|
|
function a(a) {
|
|||
|
|
var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
|
|||
|
|
for (d = 0; a > d; d += 1)
|
|||
|
|
e = Math.random() * b.length,
|
|||
|
|
e = Math.floor(e),
|
|||
|
|
c += b.charAt(e);
|
|||
|
|
return c
|
|||
|
|
}
|
|||
|
|
""")
|
|||
|
|
|
|||
|
|
|
|||
|
|
class Copartner():
|
|||
|
|
name = "音乐合伙人"
|
|||
|
|
|
|||
|
|
def __init__(self, check_item):
|
|||
|
|
self.csrf = None
|
|||
|
|
self.musicDataUrl = "http://interface.music.163.com/api/music/partner/daily/task/get"
|
|||
|
|
self.userInfoUrl = "https://music.163.com/api/nuser/account/get"
|
|||
|
|
self.signUrl = "https://interface.music.163.com/weapi/music/partner/work/evaluate?csrf_token="
|
|||
|
|
self.g = '0CoJUm6Qyw8W8jud' # buU9L(["爱心", "女孩", "惊恐", "大笑"])的值
|
|||
|
|
self.b = "010001" # buU9L(["流泪", "强"])的值
|
|||
|
|
# buU9L(Rg4k.md)的值
|
|||
|
|
self.c = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
|
|||
|
|
self.i = get_i.call('a', 16) # 随机生成长度为16的字符串
|
|||
|
|
self.iv = "0102030405060708" # 偏移量
|
|||
|
|
self.headers = {
|
|||
|
|
"Accept": "application/json, text/javascript",
|
|||
|
|
"Accept-Encoding": "gzip, deflate",
|
|||
|
|
"Accept-Language": "zh-CN,zh-Hans;q=0.9",
|
|||
|
|
"Content-Type": "application/x-www-form-urlencoded",
|
|||
|
|
"Origin": "http://mp.music.163.com",
|
|||
|
|
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 CloudMusic/0.1.1 NeteaseMusic/8.8.01"
|
|||
|
|
}
|
|||
|
|
self.musicTags = "3-A-1"
|
|||
|
|
self.musicScore = "3"
|
|||
|
|
self.check_item = check_item
|
|||
|
|
|
|||
|
|
def get_enc_sec_key(self):
|
|||
|
|
return rsa_encrypt(self.i, self.b, self.c)
|
|||
|
|
|
|||
|
|
def get_params(self, data):
|
|||
|
|
enc_text = str(data)
|
|||
|
|
return aes_encrypt(aes_encrypt(enc_text, self.g, self.iv), self.i, self.iv)
|
|||
|
|
|
|||
|
|
def sign(self, session, music_data, msg):
|
|||
|
|
works = music_data['works']
|
|||
|
|
task_id = music_data['id']
|
|||
|
|
begin = False
|
|||
|
|
for work in works:
|
|||
|
|
_work = work['work']
|
|||
|
|
if work['completed']:
|
|||
|
|
msg.append({
|
|||
|
|
"name": f"{_work['name']}({_work['authorName']})",
|
|||
|
|
"value": f"评分完成:{work['score']}分"
|
|||
|
|
})
|
|||
|
|
else:
|
|||
|
|
if not begin:
|
|||
|
|
msg.append({"name": "本次进度", "value": ""})
|
|||
|
|
begin = True
|
|||
|
|
data = {
|
|||
|
|
"params": self.get_params({
|
|||
|
|
"taskId": task_id,
|
|||
|
|
"workId": _work['id'],
|
|||
|
|
"score": self.musicScore,
|
|||
|
|
"tags": self.musicTags,
|
|||
|
|
"customTags": "%5B%5D",
|
|||
|
|
"comment": "",
|
|||
|
|
"syncYunCircle": "true",
|
|||
|
|
"csrf_token": self.csrf
|
|||
|
|
}).replace("\n", ""),
|
|||
|
|
"encSecKey": self.get_enc_sec_key()
|
|||
|
|
}
|
|||
|
|
try:
|
|||
|
|
response = session.post(
|
|||
|
|
url=f"{self.signUrl}={self.csrf}",
|
|||
|
|
data=data, headers=self.headers).json()
|
|||
|
|
if response["code"] == 200:
|
|||
|
|
msg.append({
|
|||
|
|
"name": f"{_work['name']}({_work['authorName']})",
|
|||
|
|
"value": f"评分完成:{self.musicScore}分"
|
|||
|
|
})
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"歌曲 {_work['name']} 评分异常,原因{str(e)}")
|
|||
|
|
|
|||
|
|
def valid(self, session):
|
|||
|
|
try:
|
|||
|
|
content = session.get(url=self.musicDataUrl,
|
|||
|
|
headers={**self.headers, "Referer": "https://mp.music.163.com/"})
|
|||
|
|
except Exception as e:
|
|||
|
|
return False, f"登录验证异常,错误信息: {e}"
|
|||
|
|
data = content.json()
|
|||
|
|
if data["code"] == 301:
|
|||
|
|
return False, data["message"]
|
|||
|
|
if data["code"] == 200:
|
|||
|
|
music_data = data["data"]
|
|||
|
|
user_name = self.login_info(session=session)["profile"]["nickname"]
|
|||
|
|
return music_data, user_name
|
|||
|
|
return False, "登录信息异常"
|
|||
|
|
|
|||
|
|
def login_info(self, session):
|
|||
|
|
try:
|
|||
|
|
return session.get(url=self.userInfoUrl, headers=self.headers).json()
|
|||
|
|
except Exception as e:
|
|||
|
|
print(e)
|
|||
|
|
return {
|
|||
|
|
"profile": {
|
|||
|
|
"nickname": "获取用户信息异常"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
def main(self):
|
|||
|
|
session = requests.session()
|
|||
|
|
cookie = self.check_item.get("cookie")
|
|||
|
|
music_cookie = {item.split("=")[0]: item.split("=")[1] for item in cookie.split("; ")}
|
|||
|
|
self.csrf = music_cookie['__csrf']
|
|||
|
|
requests.utils.add_dict_to_cookiejar(session.cookies, music_cookie)
|
|||
|
|
music_data, user_name = self.valid(session)
|
|||
|
|
if music_data:
|
|||
|
|
completed = music_data['completed']
|
|||
|
|
msg = [
|
|||
|
|
{"name": "帐号信息", "value": f"{user_name}"},
|
|||
|
|
{"name": "当前进度", "value": ""},
|
|||
|
|
{"name": "今日完成状态", "value": f"{'已完成' if completed else '未完成'}"},
|
|||
|
|
{"name": "当前获得积分", "value": music_data['integral']},
|
|||
|
|
{"name": "已完成评定数", "value": music_data["completedCount"]},
|
|||
|
|
]
|
|||
|
|
if not completed:
|
|||
|
|
self.sign(session, music_data, msg)
|
|||
|
|
else:
|
|||
|
|
msg = [
|
|||
|
|
{"name": "帐号信息", "value": user_name},
|
|||
|
|
{"name": "cookie信息", "value": "Cookie 可能过期"},
|
|||
|
|
]
|
|||
|
|
msg = "\n".join([f"{one.get('name')}: {one.get('value')}" for one in msg])
|
|||
|
|
return msg
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
with open("/ql/config/netease_music_cookie.json", "r", encoding="utf-8") as f:
|
|||
|
|
all_data = json.loads(f.read())
|
|||
|
|
_check_item = all_data.get("MUSIC_COPARTNER", [])
|
|||
|
|
res = ""
|
|||
|
|
for index, item in enumerate(_check_item):
|
|||
|
|
res = f'{res}账号{index + 1}:\n{Copartner(item).main()}\n'
|
|||
|
|
print(res)
|
|||
|
|
send('网易音乐合伙人', res)
|