Files
qinglong/脚本库/web版/抓包/库街区自动签到/2026-05-23_ql_kuro_46013225.py
2026-05-24 03:53:42 +00:00

354 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Source: https://github.com/Small-tailqwq/ql_script/blob/master/kuro/ql_kuro.py
# Raw: https://raw.githubusercontent.com/Small-tailqwq/ql_script/master/kuro/ql_kuro.py
# Repo: Small-tailqwq/ql_script
# Path: kuro/ql_kuro.py
# UploadedAt: 2026-05-23T15:50:58Z
# SHA256: 46013225510794598730a4bad8387ebfbcee626a372ce9a1e605ff7acbf18bac
# Category: web版/抓包
# Evidence: web/H5关键词 + cookie/token/header
# -*- coding: utf-8 -*-
"""
cron: 30 8 * * *
new Env('库街区自动签到');
基于 mxyooR/Kuro-autosignin (GPL-3.0) 精简适配青龙单文件版
上游版本: a334796 (2026-04-21)
最后更新: 2026-05-23 09:00 (UTC+8)
"""
import datetime
import json
import logging
import os
import random
import socket
import time
import uuid
from typing import Dict, List, Optional
import requests
try:
from sendNotify import send
except Exception:
try:
from notify import send
except Exception:
send = None
TIMEOUT = 30
GAME_WUWA_ID = "3"
GAME_PGR_ID = "2"
SERVER_WUWA = "76402e5b20be2c39f095a152090afddc"
SERVER_PGR = "1000"
MAX_RETRIES = 3
class API:
USER_MINE = "https://api.kurobbs.com/user/mineV2"
USER_SIGN_IN = "https://api.kurobbs.com/user/signIn"
USER_ROLE_LIST = "https://api.kurobbs.com/user/role/findRoleList"
FORUM_LIST = "https://api.kurobbs.com/forum/list"
FORUM_POST_DETAIL = "https://api.kurobbs.com/forum/getPostDetail"
FORUM_LIKE = "https://api.kurobbs.com/forum/like"
TASK_PROCESS = "https://api.kurobbs.com/encourage/level/getTaskProcess"
TASK_SHARE = "https://api.kurobbs.com/encourage/level/shareTask"
GAME_SIGN_IN = "https://api.kurobbs.com/encourage/signIn/v2"
GAME_SIGN_RECORD = "https://api.kurobbs.com/encourage/signIn/queryRecordV2"
def get_ip_address() -> str:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.connect(("8.8.8.8", 80))
return s.getsockname()[0]
except socket.error:
return "127.0.0.1"
finally:
s.close()
class KuroSigner:
def __init__(self, token_data: Dict[str, str]):
self.token = token_data['token']
self.note = token_data.get('note', '未命名账号')
self.ip = get_ip_address()
self.devcode = str(uuid.uuid4())
self.distinct_id = str(uuid.uuid4())
self.session = requests.Session()
self.user_id = None
self.wuwa_role_id = None
self.pgr_role_id = None
self.messages = []
self.success = True
def _log(self, msg: str):
logging.info(f"[{self.note}] {msg}")
self.messages.append(msg)
def _req(self, url: str, method: str = 'POST', data: Optional[dict] = None, req_type: str = 'bbs') -> dict:
headers = {
"Accept": "*/*",
"Accept-Language": "zh-CN,zh-Hans;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Connection": "keep-alive",
"Host": "api.kurobbs.com",
"source": "ios",
}
if req_type == 'bbs':
headers.update({
"lang": "zh-Hans",
"User-Agent": "KuroGameBox/48 CFNetwork/1492.0.1 Darwin/23.3.0",
"channelId": "1",
"channel": "appstore",
"version": "2.2.0",
"model": "iPhone15,2",
"osVersion": "17.3",
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
"Cookie": f"user_token={self.token}",
"Ip": self.ip,
"distinct_id": self.distinct_id,
"devCode": self.devcode,
"token": self.token,
})
elif req_type == 'game':
headers.update({
"Accept": "application/json, text/plain, */*",
"Sec-Fetch-Site": "same-site",
"source": "ios",
"Sec-Fetch-Mode": "cors",
"Origin": "https://web-static.kurobbs.com",
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) KuroGameBox/2.2.0",
"devCode": f"{self.ip}, Mozilla/5.0 (iPhone; CPU iPhone OS 17_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) KuroGameBox/2.2.0",
"token": self.token,
})
elif req_type == 'user_info':
headers.update({
"osversion": "Android",
"countrycode": "CN",
"ip": self.ip,
"model": "2211133C",
"source": "android",
"lang": "zh-Hans",
"version": "1.0.9",
"versioncode": "1090",
"content-type": "application/x-www-form-urlencoded",
"user-agent": "okhttp/3.10.0",
"devcode": self.devcode,
"distinct_id": self.distinct_id,
"token": self.token,
})
try:
if method == 'POST':
resp = self.session.post(url, headers=headers, data=data, timeout=TIMEOUT).json()
else:
resp = self.session.get(url, headers=headers, timeout=TIMEOUT).json()
return resp
except Exception as e:
return {"code": -1, "msg": f"请求异常: {str(e)}"}
def init_user_data(self) -> bool:
res = self._req(API.USER_MINE, req_type='user_info')
if res.get('code') != 200:
self._log(f"获取用户信息失败或Token已过期: {res.get('msg')}")
self.success = False
return False
self.user_id = res['data']['mine']['userId']
res_wuwa = self._req(API.USER_ROLE_LIST, data={"gameId": GAME_WUWA_ID}, req_type='user_info')
if res_wuwa.get('code') == 200 and res_wuwa.get('data'):
self.wuwa_role_id = res_wuwa['data'][0].get('roleId')
res_pgr = self._req(API.USER_ROLE_LIST, data={"gameId": GAME_PGR_ID}, req_type='user_info')
if res_pgr.get('code') == 200 and res_pgr.get('data'):
self.pgr_role_id = res_pgr['data'][0].get('roleId')
return True
def _get_sign_reward(self, game_name: str, game_id: str, server_id: str, role_id: str) -> Optional[str]:
try:
data = {
"gameId": game_id,
"serverId": server_id,
"roleId": role_id,
"userId": self.user_id,
}
res = self._req(API.GAME_SIGN_RECORD, data=data, req_type='game')
if res.get('code') == 200 and res.get('data'):
if isinstance(res['data'], list) and len(res['data']) > 0:
return res['data'][0].get('goodsName')
return None
except Exception:
return None
def game_sign(self):
month = datetime.datetime.now().strftime("%m")
def _do_sign(game_name, game_id, server_id, role_id):
if not role_id:
self._log(f"[{game_name}] 未找到角色ID跳过签到")
return
data = {
"gameId": game_id,
"serverId": server_id,
"roleId": role_id,
"userId": self.user_id,
"reqMonth": month,
}
res = self._req(API.GAME_SIGN_IN, data=data, req_type='game')
code = res.get('code')
if code == 200:
reward = self._get_sign_reward(game_name, game_id, server_id, role_id)
msg = f"[{game_name}] 签到成功"
if reward:
msg += f",奖励: {reward}"
self._log(msg)
elif code == 1511:
reward = self._get_sign_reward(game_name, game_id, server_id, role_id)
msg = f"[{game_name}] 今日已签到"
if reward:
msg += f",奖励: {reward}"
self._log(msg)
elif code == 1513:
self._log(f"[{game_name}] 签到失败:用户信息异常,即将禁用该账号")
self.success = False
elif code == 220:
self._log(f"[{game_name}] 签到失败:登录已过期,请重新登录")
self.success = False
else:
self._log(f"[{game_name}] 签到失败: {res.get('msg', '未知错误')} (code={code})")
raise Exception(f"{game_name} 签到失败: {res.get('msg', '未知错误')}")
_do_sign("鸣潮", GAME_WUWA_ID, SERVER_WUWA, self.wuwa_role_id)
time.sleep(1)
_do_sign("战双", GAME_PGR_ID, SERVER_PGR, self.pgr_role_id)
def forum_tasks(self):
res = self._req(API.USER_SIGN_IN, data={"gameId": "2"}, req_type='bbs')
if res.get('code') == 200:
self._log("论坛签到成功")
time.sleep(1)
res_tasks = self._req(API.TASK_PROCESS, data={"gameId": "0"}, req_type='bbs')
if res_tasks.get('code') != 200:
self._log("获取每日任务列表失败跳过")
return
tasks = res_tasks.get('data', {}).get('dailyTask', [])
tasks_map = {t.get('remark'): t.get('process') for t in tasks}
posts = []
if tasks_map.get("浏览3篇帖子", 1) == 0 or tasks_map.get("点赞5次", 1) == 0:
res_posts = self._req(API.FORUM_LIST, data={"forumId": "9", "gameId": "3", "pageIndex": "1", "pageSize": "20", "searchType": "3", "timeType": "0"}, req_type='bbs')
if res_posts.get('code') == 200:
posts = res_posts.get('data', {}).get('postList', [])
if tasks_map.get("浏览3篇帖子", 1) == 0 and posts:
view_count = 0
for p in posts[:3]:
self._req(API.FORUM_POST_DETAIL, data={"isOnlyPublisher": "0", "postId": p['postId'], "showOrderTyper": "2"}, req_type='bbs')
view_count += 1
time.sleep(random.uniform(1, 2))
self._log(f"完成浏览帖子任务 ({view_count}/3)")
if tasks_map.get("点赞5次", 1) == 0 and posts:
like_count = 0
for p in posts[:5]:
self._req(API.FORUM_LIKE, data={"forumId": 11, "gameId": 3, "likeType": 1, "operateType": 1, "postCommentId": "", "postCommentReplyId": "", "postId": p['postId'], "postType": 1, "toUserId": p['userId']}, req_type='bbs')
like_count += 1
time.sleep(random.uniform(1, 2))
self._log(f"完成点赞帖子任务 ({like_count}/5)")
if tasks_map.get("分享1次帖子", 1) == 0:
self._req(API.TASK_SHARE, data={"gameId": 3}, req_type='bbs')
self._log("完成分享帖子任务 (1/1)")
def run(self):
for attempt in range(MAX_RETRIES):
try:
if attempt > 0:
delay = random.uniform(5, 15)
self._log(f"将在 {delay:.1f} 秒后进行第 {attempt + 1}/{MAX_RETRIES} 次重试...")
time.sleep(delay)
if self.init_user_data():
self.game_sign()
self.forum_tasks()
if not self.success:
raise Exception("签到执行失败")
break
except Exception as e:
self._log(f"执行异常: {str(e)}")
self.success = False
if attempt < MAX_RETRIES - 1:
continue
else:
self._log(f"重试 {MAX_RETRIES} 次后仍然失败,放弃签到")
return "\n".join(self.messages)
def get_tokens() -> List[Dict[str, str]]:
raw = os.environ.get("KURO_TOKEN", "").strip()
if not raw:
return []
raw = raw.replace('&', '\n').replace(',', '\n')
token_list = []
for line in raw.split('\n'):
line = line.strip()
if not line:
continue
parts = line.split('#', 1)
token_val = parts[0].strip()
note = parts[1].strip() if len(parts) > 1 else f"账号{len(token_list) + 1}"
if token_val:
token_list.append({'token': token_val, 'note': note})
return token_list
def main():
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
tokens = get_tokens()
if not tokens:
logging.error("未找到 Token请在环境变量中设置 'KURO_TOKEN'")
return
logging.info(f"✅ 检测到 {len(tokens)} 个账号,开始执行库街区签到...\n" + "-" * 30)
notify_content = []
for token_data in tokens:
signer = KuroSigner(token_data)
result_msg = signer.run()
status_icon = "" if signer.success else ""
header = f"{signer.note}{status_icon}"
notify_content.append(header)
notify_content.append(result_msg)
notify_content.append("-" * 20)
final_content = "\n".join(notify_content)
logging.info(f"\n{final_content}")
if send:
send("库街区签到", final_content)
if __name__ == '__main__':
main()