mirror of
https://github.com/HChaZZY/NodeSeek-Signin.git
synced 2025-12-06 11:33:49 +08:00
Add files via upload
This commit is contained in:
94
README.md
94
README.md
@@ -1,46 +1,60 @@
|
||||
# NodeSeek-Signin
|
||||
|
||||
## 项目介绍
|
||||
<div align="center">
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
</div>
|
||||
|
||||
## 📝 项目介绍
|
||||
|
||||
这是一个用于 NodeSeek 论坛自动签到的工具,支持通过 GitHub Actions 或青龙面板进行定时自动签到操作。签到模式默认为随机签到,帮助用户轻松获取论坛每日签到奖励。
|
||||
|
||||
## 功能特点
|
||||
|
||||
- 支持 GitHub Actions 自动运行
|
||||
- 支持青龙面板定时任务
|
||||
- 支持 Cookie 或账号密码登录方式
|
||||
- 可配置 Telegram 机器人通知
|
||||
## ✨ 功能特点
|
||||
|
||||
- 📅 支持 GitHub Actions 自动运行
|
||||
- 🦉 支持青龙面板定时任务
|
||||
- 🍪 支持 Cookie 或账号密码登录方式
|
||||
- 🔐 支持多种验证码解决方案
|
||||
- 自建 CloudFreed 服务(免费)
|
||||
- YesCaptcha 商业服务(付费/赠送)
|
||||
- 📱 支持多种通知推送渠道
|
||||
|
||||
## 使用方法
|
||||
## 🚀 使用方法
|
||||
|
||||
### 方式一:GitHub Actions
|
||||
|
||||
1. Fork 本仓库到自己的 GitHub 账号下
|
||||
2. 在仓库的 Settings > Secrets and variables > Actions 中添加以下必要配置:
|
||||
2. 在仓库的 `Settings > Secrets and variables > Actions` 中添加以下必要配置:
|
||||
|
||||
| 变量名称 | 必要性 | 说明 |
|
||||
| :------: | :----: | :--- |
|
||||
| `NS_COOKIE` | **必需** | NodeSeek 论坛的用户 Cookie,可在浏览器开发者工具(F12)的网络请求中获取 |
|
||||
| `NS_COOKIE` | **建议** | NodeSeek 论坛的用户 Cookie,可在浏览器开发者工具(F12)的网络请求中获取 |
|
||||
| `TG_BOT_TOKEN` | 可选 | Telegram 机器人的 Token,用于通知签到结果 |
|
||||
| `TG_USER_ID` | 可选 | Telegram 用户 ID,用于接收通知 |
|
||||
| `TG_THREAD_ID` | 可选 | Telegram 超级群组话题 ID,用于在特定话题中发送通知 |
|
||||
|
||||
> **注意**:如果签到结果显示 "USER NOT FOUND" 或返回 HTML 内容,说明 Cookie 已失效,需要重新获取。
|
||||
> **注意**:若仅设置 Cookie 但未配置验证码服务,当 Cookie 过期后无法自动登录获取新 Cookie。
|
||||
|
||||
### 方式二:青龙面板
|
||||
|
||||
在青龙面板中执行以下命令克隆本仓库:
|
||||
|
||||
```bash
|
||||
ql clone https://github.com/yowiv/NodeSeek-Signin.git
|
||||
ql repo https://github.com/yowiv/NodeSeek-Signin.git
|
||||
```
|
||||
|
||||
然后在环境变量中添加所需配置。
|
||||
|
||||
### 方式三:账号密码登录
|
||||
### 方式三:账号密码登录(自动获取新Cookie)
|
||||
|
||||
如需使用账号密码登录方式,需要先部署 Cloudflare 验证码求解服务:
|
||||
当 Cookie 失效时,系统会尝试使用账号密码方式登录并获取新的 Cookie。登录需要通过验证码验证,支持以下两种验证码解决方案:
|
||||
|
||||
#### 方案A:CloudFreed 自建服务(推荐家宽用户)
|
||||
|
||||
```bash
|
||||
docker run -itd --name cloudfreed -p 3000:3000 \
|
||||
@@ -50,17 +64,53 @@ docker run -itd --name cloudfreed -p 3000:3000 \
|
||||
sanling000/cloudfreed
|
||||
```
|
||||
|
||||
> **提示**:建议在家宽 IP 的机器上安装此服务以获得更好的稳定性。
|
||||
配置以下环境变量:
|
||||
|
||||
然后配置以下环境变量:
|
||||
| 变量名称 | 说明 |
|
||||
| :------: | :--- |
|
||||
| `API_BASE_URL` | CloudFreed 服务地址,如 `http://192.168.1.100:3000` |
|
||||
| `CLIENTT_KEY` | CloudFreed 服务的客户端密钥 |
|
||||
| `USER` | NodeSeek 论坛用户名 |
|
||||
| `PASS` | NodeSeek 论坛密码 |
|
||||
| `SOLVER_TYPE` | 设置为 `turnstile`(默认值) |
|
||||
|
||||
| 变量名称 | 必要性 | 说明 |
|
||||
| :------: | :----: | :--- |
|
||||
| `API_BASE_URL` | 可选 | Cloudflare 求解服务的 URL |
|
||||
| `CLIENTT_KEY` | 可选 | 验证码服务的客户端密钥 |
|
||||
| `USER` | 可选 | NodeSeek 论坛用户名 |
|
||||
| `PASS` | 可选 | NodeSeek 论坛密码 |
|
||||
#### 方案B:YesCaptcha 商业服务(推荐无法自建服务的用户)
|
||||
|
||||
## 免责声明
|
||||
1. 访问 [YesCaptcha](https://yescaptcha.com/i/k2Hy3Q) 注册账号
|
||||
2. 注册后联系客服可免费获得余额(约可使用60次登录)
|
||||
3. 配置以下环境变量:
|
||||
|
||||
| 变量名称 | 说明 |
|
||||
| :------: | :--- |
|
||||
| `CLIENTT_KEY` | YesCaptcha 的 API 密钥 |
|
||||
| `USER` | NodeSeek 论坛用户名 |
|
||||
| `PASS` | NodeSeek 论坛密码 |
|
||||
| `SOLVER_TYPE` | 设置为 `yescaptcha` |
|
||||
|
||||
> **提示**:YesCaptcha 提供两个服务节点,可根据网络情况选择:
|
||||
> - 国际节点:`https://api.yescaptcha.com`(默认)
|
||||
> - 国内节点:`https://cn.yescaptcha.com`
|
||||
|
||||
## 🔧 环境变量完整说明
|
||||
|
||||
| 变量名称 | 必要性 | 默认值 | 说明 |
|
||||
| :------: | :----: | :----: | :--- |
|
||||
| `NS_COOKIE` | 建议 | - | NodeSeek 论坛的用户 Cookie |
|
||||
| `USER` | 可选 | - | NodeSeek 论坛用户名,当 Cookie 失效时使用 |
|
||||
| `PASS` | 可选 | - | NodeSeek 论坛密码 |
|
||||
| `NS_RANDOM` | 可选 | true | 是否随机签到(true/false) |
|
||||
| `SOLVER_TYPE` | 可选 | turnstile | 验证码解决方案(turnstile/yescaptcha) |
|
||||
| `API_BASE_URL` | 条件必需 | - | CloudFreed 服务地址,当 SOLVER_TYPE=turnstile 时必填 |
|
||||
| `CLIENTT_KEY` | 必需 | - | 验证码服务客户端密钥 |
|
||||
| 各类通知变量 | 可选 | - | 支持多种推送通知平台配置 |
|
||||
|
||||
## 📊 验证码服务对比
|
||||
|
||||
| 服务 | 类型 | 优点 | 缺点 |
|
||||
| :--: | :--: | :--- | :--- |
|
||||
| CloudFreed | 自建服务 | 免费、无次数限制 | 需要自行部署维护
|
||||
| YesCaptcha | 商业服务 | 稳定可靠、易于配置 | 付费服务(有免费额度)
|
||||
|
||||
## ⚠️ 免责声明
|
||||
|
||||
本项目仅供学习交流使用,请遵守 NodeSeek 论坛的相关规定和条款。
|
||||
|
||||
@@ -4,6 +4,7 @@ import sys
|
||||
import time
|
||||
from curl_cffi import requests
|
||||
from turnstile_solver import TurnstileSolver, TurnstileSolverError
|
||||
from yescaptcha import YesCaptchaSolver, YesCaptchaSolverError
|
||||
|
||||
# 配置参数
|
||||
API_BASE_URL = os.environ.get("API_BASE_URL", "")
|
||||
@@ -12,6 +13,7 @@ NS_RANDOM = os.environ.get("NS_RANDOM", "true")
|
||||
NS_COOKIE = os.environ.get("NS_COOKIE", "")
|
||||
USER = os.environ.get("USER", "")
|
||||
PASS = os.environ.get("PASS", "")
|
||||
SOLVER_TYPE = os.environ.get("SOLVER_TYPE", "turnstile")
|
||||
|
||||
def load_send():
|
||||
global send
|
||||
@@ -31,30 +33,34 @@ def load_send():
|
||||
|
||||
load_send()
|
||||
|
||||
|
||||
def session_login():
|
||||
# 使用TurnstileSolver模块解决验证码
|
||||
# 根据环境变量选择使用哪个验证码解决器
|
||||
try:
|
||||
print("正在使用TurnstileSolver解决验证码...")
|
||||
solver = TurnstileSolver(
|
||||
api_base_url=API_BASE_URL,
|
||||
client_key=CLIENTT_KEY
|
||||
)
|
||||
if SOLVER_TYPE.lower() == "yescaptcha":
|
||||
print("正在使用 YesCaptcha 解决验证码...")
|
||||
solver = YesCaptchaSolver(
|
||||
api_base_url="https://api.yescaptcha.com",
|
||||
client_key=CLIENTT_KEY
|
||||
)
|
||||
else: # 默认使用 turnstile_solver
|
||||
print("正在使用 TurnstileSolver 解决验证码...")
|
||||
solver = TurnstileSolver(
|
||||
api_base_url=API_BASE_URL,
|
||||
client_key=CLIENTT_KEY
|
||||
)
|
||||
|
||||
token = solver.solve(
|
||||
url="https://www.nodeseek.com/signIn.html",
|
||||
sitekey="0x4AAAAAAAaNy7leGjewpVyR",
|
||||
action="login",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
if not token:
|
||||
print("获取验证码令牌失败,无法登录")
|
||||
return None
|
||||
|
||||
#print(f"成功获取验证码令牌: {token[:30]}...{token[-10:]}]")
|
||||
#print(token)
|
||||
|
||||
except TurnstileSolverError as e:
|
||||
except (TurnstileSolverError, YesCaptchaSolverError) as e:
|
||||
print(f"验证码解析错误: {e}")
|
||||
return None
|
||||
except Exception as e:
|
||||
|
||||
192
yescaptcha.py
Normal file
192
yescaptcha.py
Normal file
@@ -0,0 +1,192 @@
|
||||
from curl_cffi import requests
|
||||
import time
|
||||
import os
|
||||
from typing import Dict, Optional, Any, Union
|
||||
|
||||
class YesCaptchaSolverError(Exception):
|
||||
"""YesCaptcha 解决器错误基类"""
|
||||
pass
|
||||
|
||||
class YesCaptchaSolver:
|
||||
"""
|
||||
YesCaptcha 验证码解决工具
|
||||
|
||||
使用 YesCaptcha API 解决 Turnstile 验证码,获取验证令牌
|
||||
参考文档: https://yescaptcha.atlassian.net/wiki/spaces/YESCAPTCHA/overview
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
api_base_url: str = "https://api.yescaptcha.com",
|
||||
client_key: str = "",
|
||||
max_retries: int = 20,
|
||||
retry_interval: int = 3,
|
||||
timeout: int = 60,
|
||||
advanced: bool = False
|
||||
):
|
||||
"""
|
||||
初始化 YesCaptcha 验证码解决器
|
||||
|
||||
参数:
|
||||
api_base_url: API 基础 URL,默认为 YesCaptcha 国际节点
|
||||
client_key: API 客户端密钥
|
||||
max_retries: 最大重试次数
|
||||
retry_interval: 重试间隔(秒)
|
||||
timeout: 请求超时时间(秒)
|
||||
advanced: 是否使用高级解析模式(M1)
|
||||
"""
|
||||
self.api_base_url = api_base_url
|
||||
self.create_task_url = f"{api_base_url}/createTask"
|
||||
self.get_result_url = f"{api_base_url}/getTaskResult"
|
||||
self.client_key = client_key
|
||||
self.max_retries = max_retries
|
||||
self.retry_interval = retry_interval
|
||||
self.timeout = timeout
|
||||
self.advanced = advanced
|
||||
|
||||
def solve(
|
||||
self,
|
||||
url: str,
|
||||
sitekey: str,
|
||||
user_agent: Optional[str] = None,
|
||||
verbose: bool = False
|
||||
) -> str:
|
||||
"""
|
||||
解决 Turnstile 验证并返回令牌
|
||||
|
||||
参数:
|
||||
url: 目标网站 URL
|
||||
sitekey: Turnstile sitekey
|
||||
user_agent: 自定义 User-Agent
|
||||
verbose: 是否打印详细日志
|
||||
|
||||
返回:
|
||||
验证令牌字符串
|
||||
|
||||
异常:
|
||||
YesCaptchaSolverError: 解决验证码时出错
|
||||
"""
|
||||
if verbose:
|
||||
print("正在创建 YesCaptcha 验证任务...")
|
||||
|
||||
task_id = self._create_task(url, sitekey, user_agent, verbose)
|
||||
if not task_id:
|
||||
raise YesCaptchaSolverError("创建验证码任务失败")
|
||||
|
||||
# 获取任务结果
|
||||
token = self._get_task_result(task_id, verbose)
|
||||
if not token:
|
||||
raise YesCaptchaSolverError("获取验证码结果失败")
|
||||
|
||||
if verbose:
|
||||
print(f"验证码解决成功: {token[:30]}...{token[-10:] if len(token) > 30 else ''}")
|
||||
|
||||
return token
|
||||
|
||||
def _create_task(
|
||||
self,
|
||||
url: str,
|
||||
sitekey: str,
|
||||
user_agent: Optional[str] = None,
|
||||
verbose: bool = False
|
||||
) -> Optional[str]:
|
||||
"""创建验证码任务并返回任务ID"""
|
||||
|
||||
# 准备任务数据 - 严格按照YesCaptcha官方文档格式
|
||||
task_type = "TurnstileTaskProxylessM1" if self.advanced else "TurnstileTaskProxyless"
|
||||
|
||||
data = {
|
||||
"clientKey": self.client_key,
|
||||
"task": {
|
||||
"type": task_type,
|
||||
"websiteURL": url,
|
||||
"websiteKey": sitekey
|
||||
}
|
||||
}
|
||||
|
||||
# 如果需要添加用户代理
|
||||
if user_agent:
|
||||
data["task"]["userAgent"] = user_agent
|
||||
|
||||
try:
|
||||
#if verbose:
|
||||
# print(f"发送创建任务请求: {data}")
|
||||
|
||||
response = requests.post(
|
||||
self.create_task_url,
|
||||
json=data,
|
||||
timeout=self.timeout,
|
||||
impersonate="chrome110"
|
||||
)
|
||||
result = response.json()
|
||||
|
||||
#if verbose:
|
||||
# print(f"创建任务响应: {result}")
|
||||
|
||||
if result.get("errorId") == 0:
|
||||
task_id = result.get("taskId")
|
||||
if verbose:
|
||||
print(f"成功创建任务,ID: {task_id}")
|
||||
return task_id
|
||||
else:
|
||||
error_desc = result.get('errorDescription', '未知错误')
|
||||
if verbose:
|
||||
print(f"创建任务失败: {error_desc}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
if verbose:
|
||||
print(f"创建任务过程中发生异常: {e}")
|
||||
return None
|
||||
|
||||
def _get_task_result(self, task_id: str, verbose: bool = False) -> Optional[str]:
|
||||
"""获取任务结果"""
|
||||
|
||||
data = {
|
||||
"clientKey": self.client_key,
|
||||
"taskId": task_id
|
||||
}
|
||||
|
||||
for attempt in range(1, self.max_retries + 1):
|
||||
try:
|
||||
if verbose:
|
||||
print(f"尝试获取任务结果 ({attempt}/{self.max_retries})...")
|
||||
|
||||
response = requests.post(
|
||||
self.get_result_url,
|
||||
json=data,
|
||||
timeout=self.timeout,
|
||||
impersonate="chrome110"
|
||||
)
|
||||
result = response.json()
|
||||
|
||||
if result.get("errorId") > 0:
|
||||
error_desc = result.get('errorDescription', '未知错误')
|
||||
if verbose:
|
||||
print(f"获取结果失败: {error_desc}")
|
||||
return None
|
||||
|
||||
status = result.get("status")
|
||||
|
||||
# 按照文档,状态为ready时表示已完成
|
||||
if status == "ready":
|
||||
token = result.get("solution", {}).get("token")
|
||||
if verbose:
|
||||
print("任务已完成")
|
||||
return token
|
||||
|
||||
# 按照文档,状态为processing时表示处理中,需等待重试
|
||||
elif status == "processing":
|
||||
if verbose:
|
||||
print(f"任务处理中,等待 {self.retry_interval} 秒后重试...")
|
||||
time.sleep(self.retry_interval)
|
||||
continue
|
||||
|
||||
except Exception as e:
|
||||
if verbose:
|
||||
print(f"获取任务结果过程中发生异常: {e}")
|
||||
return None
|
||||
|
||||
if verbose:
|
||||
print("获取任务结果超时")
|
||||
return None
|
||||
Reference in New Issue
Block a user