Continue seeded Qinglong script collection

This commit is contained in:
Hermes Agent
2026-05-24 05:33:05 +00:00
parent ca1a4275e4
commit b082da3fe1
392 changed files with 131619 additions and 7 deletions

View File

@@ -0,0 +1,462 @@
# Source: https://github.com/LinYuanovo/AutoTaskScripts/blob/main/app/%E6%AC%A1%E5%85%83%E5%A7%AC%E5%B0%8F%E8%AF%B4.py
# Raw: https://raw.githubusercontent.com/LinYuanovo/AutoTaskScripts/main/app/%E6%AC%A1%E5%85%83%E5%A7%AC%E5%B0%8F%E8%AF%B4.py
# Repo: LinYuanovo/AutoTaskScripts
# Path: app/次元姬小说.py
# UploadedAt: 2025-08-27T15:31:08+08:00
# SHA256: d9d5f110790abe6c16dad9d7b17c22b51314611f9430b5eb988f21cd45158305
# Category: web版/抓包
# Evidence: web/H5关键词 + cookie/token/header
"""
作者: 临渊
日期: 2025/6/27
name: 次元姬小说
入口: APP
功能: 签到、阅读、领取奖励 (阅读需要模拟真实时间,所以脚本会运行比较长时间,预计一个小时)
变量: cyj_account = 'deviceno&token' api.hwnovel.com下请求的deviceno和token用&分割)
定时: 一天一到两次
cron: 10 8,15 * * *
------------更新日志------------
2025/6/27 V1.0 初始化脚本
2025/6/28 V1.1 优化阅读逻辑 增加阅读时间判断如果已阅读时间超过50分钟则跳过模拟阅读
2025/6/28 V1.2 增加自动订阅 需要填写bookId和从第多少章开始订阅
2025/7/28 V1.3 修改头部注释,以便拉库
"""
MULTI_ACCOUNT_SPLIT = ["\n", "@"] # 多账户分隔符列表
NOTIFY = False # 是否推送日志默认不推送True则推送
AUTO_ORDER_BOOK_ID = "" # 自动订阅的小说bookId填了则自动订阅否则不订阅只有第一个号会进行订阅
AUTO_ORDER_BOOK_CHAPTER = 1 # 从第多少章开始自动订阅,默认使用所有代币往后进行订阅
import os
import random
import time
import json
import logging
import traceback
import requests
import base64
import hashlib
import threading
import uuid as uuidlib
from Crypto.Cipher import DES
from Crypto.Util.Padding import pad
class AutoTask:
def __init__(self, script_name):
self.script_name = script_name
self.log_msgs = []
self.local = threading.local()
self.base_url = "https://api.hwnovel.com/api/ciyuanji/client/"
self.setup_logging()
def setup_logging(self):
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s\t- %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
handlers=[logging.StreamHandler()]
)
def log(self, msg, level="info", push=True):
account_prefix = getattr(self.local, "account_prefix", "")
msg = f"{account_prefix}{msg}"
if level == "info":
logging.info(msg)
elif level == "error":
logging.error(msg)
elif level == "warning":
logging.warning(msg)
if push:
self.log_msgs.append(msg)
def check_env(self):
"""
检查环境变量
:return: 环境变量字符串
"""
try:
# 从环境变量获取cookie
cyj_account = os.getenv(f"cyj_account")
if not cyj_account:
self.log("[检查环境变量]没有找到环境变量cyj_account请检查环境变量", level="error")
return None
# 自动检测分隔符
split_char = None
for sep in MULTI_ACCOUNT_SPLIT:
if sep in cyj_account:
split_char = sep
break
if not split_char:
# 如果都没有分隔符,默认当作单账号
cyj_accounts = [cyj_account]
else:
cyj_accounts = cyj_account.split(split_char)
for cyj_account in cyj_accounts:
if "=" in cyj_account:
cyj_account = cyj_account.split("=")[1]
deviceno, token = cyj_account.split("&")
yield deviceno, token
else:
deviceno, token = cyj_account.split("&")
yield deviceno, token
except Exception as e:
self.log(f"[检查环境变量]发生错误: {str(e)}\n{traceback.format_exc()}", level="error")
raise
def encrypt(self, data):
key = b"ZUreQN0E"
cipher = DES.new(key, DES.MODE_ECB)
padded_data = pad(data.encode("utf-8"), DES.block_size)
encrypted = cipher.encrypt(padded_data)
return base64.b64encode(encrypted).decode("utf-8")
def base64_encode(self, s):
return base64.b64encode(s.encode("utf-8")).decode("utf-8")
def digest(self, s, method="md5"):
if method == "md5":
return hashlib.md5(s.encode("utf-8")).hexdigest()
raise NotImplementedError("只支持md5")
def uuid(self):
return str(uuidlib.uuid4())
def request(self, method, url, headers, data=None):
timestamp = int(time.time() * 1000)
request_id = self.uuid()
param = self.encrypt(json.dumps({**(data or {}), "timestamp": timestamp}))
sign_str = f"param={param}&requestId={request_id}&timestamp={timestamp}&key=NpkTYvpvhJjEog8Y051gQDHmReY54z5t3F0zSd9QEFuxWGqfC8g8Y4GPuabq0KPdxArlji4dSnnHCARHnkqYBLu7iIw55ibTo18"
sign = self.digest(self.base64_encode(sign_str), "md5").upper()
try:
if method == "GET":
resp = requests.get(
self.base_url + url,
params={
"param": param,
"requestId": request_id,
"sign": sign,
"timestamp": timestamp
},
headers=headers,
timeout=10
)
else:
resp = requests.post(
self.base_url + url,
json={
"param": param,
"requestId": request_id,
"sign": sign,
"timestamp": timestamp
},
headers=headers,
timeout=10
)
resp.raise_for_status()
return resp.json()
except Exception as e:
self.log(f"请求异常: {url} {str(e)}\n{traceback.format_exc()}", level="error")
return None
def signin(self, headers):
"""
每日签到
"""
response = self.request("POST", "sign/sign", headers, {})
if response["code"] == "200":
self.log(f"签到: {response['msg']}")
return response
else:
self.log(f"签到: {response['msg']}", level="warning")
return False
def get_task_list(self, headers):
"""
获取任务列表
:return: 任务列表
"""
response = self.request("GET", "task/getTaskList", headers, {})
if response["code"] == "200":
task_list = response["data"]["daliyTask"]
return task_list
else:
self.log(f"获取任务列表: {response['msg']}", level="warning")
return False
def read_start(self, headers):
"""
开始阅读
"""
data = {
"startChapter": "3632603",
"startChapterName": "第1章 邪恶app",
"bookName": "人渣男主,为所欲为",
"bookId": "14827",
"timestamp": int(time.time() * 1000)
}
response = self.request("POST", "read/start", headers, data)
if response["code"] == "200":
readId = response["data"]["readId"]
# self.log(f"开始阅读: readId={readId}", level="info")
return readId
else:
self.log(f"开始阅读: {response['msg']}", level="error")
return False
def read_start_chapter(self, headers, readId):
"""
阅读章节
:param readId: 阅读ID
"""
data = {
"isBuy": "0",
"isFee": "0",
"readId": readId,
"chapterId": "3632603",
"chapterName": "第1章 邪恶app",
"bookName": "人渣男主,为所欲为",
"bookId": "14827",
"timestamp": int(time.time() * 1000)
}
response = self.request("POST", "read/startChapter", headers, data)
if response["code"] == "200":
readChapterId = response["data"]["readChapterId"]
# self.log(f"阅读章节: readChapterId={readChapterId}", level="info")
return readChapterId
else:
self.log(f"阅读章节: {response['msg']}", level="error")
return False
def read_end(self, headers, readId):
"""
结束阅读
:param readId: 阅读ID
"""
data = {
"endChapterName": "第1章 邪恶app",
"readId": readId,
"chapterCount": "1",
"endChapter": "3632603",
"bookId": "14827",
"timestamp": int(time.time() * 1000)
}
response = self.request("POST", "read/end", headers, data)
if response["code"] == "200":
# self.log(f"结束阅读: {response['msg']}", level="info")
return response
else:
self.log(f"结束阅读: {response['msg']}", level="error")
return False
def read_end_chapter(self, headers,readChapterId):
"""
结束阅读章节
:param readChapterId: 阅读章节ID
"""
data = {
"isBuy": "0",
"isFee": "0",
"isUserMember": "0",
"isBookMember": "0",
"readChapterId": readChapterId,
"timestamp": int(time.time() * 1000)
}
response = self.request("POST", "read/endChapter", headers, data)
if response["code"] == "200":
# self.log(f"结束阅读章节: {response['msg']}", level="info")
return response
else:
self.log(f"结束阅读章节: {response['msg']}", level="error")
return False
def receive_task_reward(self, headers, task_id, reward_id):
"""
领取任务奖励
:param task_id: 任务ID
:param reward_id: 奖励ID
"""
data = {
"taskId": task_id,
"rewardId": reward_id
}
response = self.request("POST", "task/receiveTaskReward", headers, data)
if response["code"] == "200":
self.log(f"领取奖励: {response['msg']}", push=False)
return response
else:
self.log(f"领取奖励: {response['msg']}", level="error")
return False
def get_user_info(self, headers):
"""
获取用户信息
"""
response = self.request("GET", "account/getAccountByUser", headers, {})
if response["code"] == "200":
nikeName = response["data"]["accountInfo"]["nickName"]
# 代币
couponBalance = float(response["data"]["accountInfo"]["couponBalance"])
# 推荐票
dayCount = int(response["data"]["accountInfo"]["dayCount"])
self.log(f"{nikeName} 代币:{couponBalance} 推荐票:{dayCount}")
return couponBalance
else:
self.log(f"获取用户信息: {response['msg']}", level="error")
return False
def get_book_chapter_info(self, headers, bookId):
"""
获取小说章节信息
:param bookId: 小说ID
:return: 小说名称, 章节列表
"""
data = {
"sortType": "1",
"pageNo": "1",
"pageSize": "9999",
"bookId": bookId,
"timestamp": int(time.time() * 1000)
}
response = self.request("GET", "chapter/getChapterListByBookId", headers, data)
if response["code"] == "200":
book_name = response["data"]["bookChapter"]["bookName"]
chapter_list = response["data"]["bookChapter"]["chapterList"]
return book_name, chapter_list
else:
self.log(f"获取小说章节信息: {response['msg']}", level="error")
return False
def order_book_chapter(self, headers, bookId, chapterId):
"""
订阅章节
:param bookId: 小说ID
:param chapterId: 章节ID
"""
data = {
"buyCount": "1",
"productId": chapterId,
"viewType": "2",
"consumeType": "1",
"bookId": bookId,
"timestamp": int(time.time() * 1000)
}
response = self.request("POST", "order/consume", headers, data)
if response["code"] == "200":
return True
else:
self.log(f"订阅章节: {response['msg']}", level="error")
return False
def run_for_account(self, index, token, deviceno):
self.local.account_prefix = f"[账号{index}] "
# 防止串号
headers = {
"channel": "25",
"deviceno": deviceno,
"platform": "1",
"imei": "",
"targetmodel": "M2012K11AC",
"oaid": "",
"version": "3.4.3",
"token": token,
"user-agent": "Mozilla/5.0 (Linux; Android 11; Pixel 4 XL Build/RP1A.200720.009; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.115 Mobile Safari/537.36"
}
# 签到
self.signin(headers)
# 获取任务列表
task_list = self.get_task_list(headers)
reader_time = 0
for task in task_list:
if task['actionName'] == "阅读" and task['taskState'] != "0":
reader_time = int(task['rewardList'][0]['actionRequire'])
if reader_time >= 0:
need_read_time = 50 - reader_time
self.log(f"还需要阅读{need_read_time}分钟")
if need_read_time > 0:
# 5分钟一次太长时间会失效
for i in range(need_read_time // 5):
# 开始阅读
readId = self.read_start(headers)
time.sleep(5)
if readId:
# 阅读章节
readChapterId = self.read_start_chapter(headers, readId)
self.log(f"模拟第{i+1}次阅读中,请勿停止运行脚本", push=False)
time.sleep(random.randint(300, 320))
if readChapterId:
# 结束阅读章节
self.read_end_chapter(headers, readChapterId)
time.sleep(5)
# 结束阅读
self.read_end(headers, readId)
else:
self.log(f"已阅读{reader_time}分钟,跳过模拟阅读")
# 获取任务列表
task_list = self.get_task_list(headers)
if task_list:
# 领取奖励
for task in task_list:
if task["taskState"] == "1":
self.receive_task_reward(headers, task["taskId"], task["rewardId"])
# 再次获取任务列表
task_list = self.get_task_list(headers)
if task_list:
for task in task_list:
taskname = task["taskName"]
if "阅读" in taskname:
status = "已完成" if task["taskState"] == "2" else "未完成"
self.log(f"任务-{taskname}-{status}", level="info")
# 获取用户信息
coupon_balance = self.get_user_info(headers)
# 自动订阅小说
if AUTO_ORDER_BOOK_ID and index == 1:
book_name, chapter_list = self.get_book_chapter_info(headers, AUTO_ORDER_BOOK_ID)
if chapter_list:
for index, chapter in enumerate(chapter_list, 1):
# 大于等于指定章节开始订阅
if index >= AUTO_ORDER_BOOK_CHAPTER and int(chapter['isFee']) == 1 and int(chapter['isBuy']) == 0:
if coupon_balance >= float(chapter['price']):
# 订阅
if self.order_book_chapter(headers, AUTO_ORDER_BOOK_ID, chapter["chapterId"]):
self.log(f"订阅 [{book_name}] - {chapter['chapterName']} 成功")
coupon_balance -= float(chapter['price'])
else:
break
def run(self):
self.log(f"{self.script_name}】开始执行任务")
threads = []
for index, (deviceno, token) in enumerate(self.check_env(), 1):
t = threading.Thread(
target=self.run_for_account,
args=(index, token, deviceno)
)
t.start()
threads.append(t)
for t in threads:
t.join()
if NOTIFY:
# 如果notify模块不存在从远程下载至本地
if not os.path.exists("notify.py"):
url = "https://raw.githubusercontent.com/whyour/qinglong/refs/heads/develop/sample/notify.py"
response = requests.get(url)
with open("notify.py", "w", encoding="utf-8") as f:
f.write(response.text)
import notify
else:
import notify
# 任务结束后推送日志
title = f"{self.script_name} 运行日志"
header = "作者:临渊\n\n"
content = header + "\n" +"\n".join(self.log_msgs)
notify.send(title, content)
if __name__ == "__main__":
auto_task = AutoTask("次元姬小说")
auto_task.run()