侧边栏壁纸
  • 累计撰写 10 篇文章
  • 累计创建 7 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

【Scripts】乐青映射-签到(根据API)

CloudCranes | 云鹤
2025-04-15 / 0 评论 / 0 点赞 / 38 阅读 / 11869 字 / 正在检测是否收录...

【Scripts】乐青映射-签到(根据API)

一、API文档地址

LoCyanFrp API v2 文档


二、获取refresh_token

1. 后台创建应用

重定向地址填写
http://127.0.0.1:3000/oauth/callback

2. 创建本地oauth服务(可选操作)

from flask import Flask, request
from flask_cors import CORS

app = Flask(__name__)
CORS(app)  # 可选:仅在需要跨域时启用

@app.route("/oauth/callback")
def oauth_callback():
    refresh_token = request.args.get("refresh_token")
    error = request.args.get("error")
    state = request.args.get("state")  # 可选:用于防御 CSRF

    # 可选:验证 state 参数(需与授权请求时生成的随机值匹配)
    # if state != expected_state:
    #     return "非法请求: State 不匹配", 400

    if refresh_token:
        print(f"[SUCCESS] 刷新令牌: {refresh_token}")
        return "授权成功!", 200
    elif error:
        print(f"[ERROR] 错误类型: {error}")
        return f"授权失败: {error}", 400
    else:
        return "非法请求", 400

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=3000, debug=True)

3. 获取refresh_token

创建好本地oauth服务后,访问以下地址:

https://dashboard.locyanfrp.cn/auth/oauth/authorize?app_id=99&scopes=Sign,User&redirect_url=http://127.0.0.1:3000/oauth/callback

注意app_id根据自己实际情况填写

http://127.0.0.1:3000/oauth/callback?refresh_token=xxxxx

三、签到代码

send_message函数请自行编写

import configparser
import os
import requests
import logging
from datetime import datetime, timedelta
from typing import Optional, Dict, Any
from tenacity import retry, stop_after_attempt, wait_exponential

# 日志配置
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[logging.StreamHandler()]
)
logger = logging.getLogger("LocyanSign")
logger.setLevel(logging.DEBUG)

class LocyanOAuthError(Exception):
    """自定义认证异常"""
    pass


class LocyanSign:
    REQUIRED_SCOPES = {'Sign.Info', 'Sign.Sign', 'User.Info'}

    def __init__(self):
        self.msg = []
        # 初始化配置校验
        self._validate_env_vars()
        self._init_api_client()
        self._acquire_initial_token()

    def send_message(self, msg: str, msg_type: str = "info"):
        pass

    def _validate_env_vars(self):
        """环境变量校验"""
        self.user_id = "用户id,自己抓包浏览器获取"
        self.refresh_token = "刚刚获取的refresh_token"
        self.id = "官网创建应用的id"

        if not all([self.user_id, self.refresh_token]):
            raise ValueError("LOCYAN_USER_ID 和 LOCYAN_REFRESH_TOKEN 必须配置")
        if len(self.refresh_token) != 64 or not self.refresh_token.isalnum():
            raise ValueError("Refresh Token格式无效")

        try:
            self.user_id = int(self.user_id)  # 转换为整数
        except ValueError:
            raise ValueError("User ID必须是数字")

    def _init_api_client(self):
        """初始化API客户端"""
        self.api_endpoints = [
            "https://api.locyanfrp.cn/v2",
            "https://backup.api.locyanfrp.cn/v2"
        ]

        self.session = requests.Session()
        self.session.headers.update({
            "User-Agent": "LocyanSign/3.0",
            "Accept": "application/json",
            "Content-Type": "application/x-www-form-urlencoded"
        })

        self.access_token = None
        self.token_expiry = None

    @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, max=10))
    def _request(self, method: str, endpoint: str, ** kwargs) -> Optional[Dict[str, Any]]:
        """增强型请求方法"""
        for base_url in self.api_endpoints:
            try:
                url = f"{base_url}{endpoint}"
                logger.debug(f"请求 {method} {url}")

                # 动态添加认证头
                headers = kwargs.get('headers', {})
                headers.setdefault("Authorization", f"Bearer {self.access_token}")
                kwargs['headers'] = headers

                response = self.session.request(
                    method, url,
                    timeout=(3.05, 15),
                    ** kwargs
                )
                logger.debug(f"响应状态: {response.status_code}")
                logger.debug(f"响应内容: {response.text[:200]}...")

                response.raise_for_status()
                return response.json()

            except requests.exceptions.RequestException as e:
                logger.warning(f"{base_url} 请求失败: {str(e)}")
                continue

        logger.error("所有API端点请求失败")
        raise LocyanOAuthError("API服务不可用")

    def _acquire_initial_token(self):
        """获取初始访问令牌"""
        logger.info("正在初始化访问令牌...")
        data = {
            "app_id": self.id,
            "refresh_token": self.refresh_token
        }

        response = self._request("POST", "/auth/oauth/access-token", data=data)
        if not response or response.get("status") != 200:
            raise LocyanOAuthError("令牌获取失败")

        token_data = response.get("data", {})
        self.access_token = token_data.get("access_token")
        if not self.access_token:
            raise LocyanOAuthError("无效的令牌响应")

        self.token_expiry = datetime.now() + timedelta(hours=1)
        logger.info(f"令牌获取成功,有效期至 {self.token_expiry.strftime('%Y-%m-%d %H:%M:%S')}")

    def _ensure_valid_token(self):
        """令牌有效性保障"""
        if datetime.now() >= self.token_expiry:
            logger.warning("检测到令牌过期,正在刷新...")
            self._acquire_initial_token()

    @staticmethod
    def format_traffic(traffic: str) -> str:
        """精确的流量格式化 (基于API返回的MB单位)"""
        try:
            mb_value = int(traffic)
            units = [
                ('TB', 1024 ** 2),  # 1TB = 1024 GB = 1024^2 MB
                ('GB', 1024),  # 1GB = 1024 MB
                ('MB', 1)
            ]

            for unit, divisor in units:
                if mb_value >= divisor:
                    value = mb_value / divisor
                    # 保留三位有效数字
                    if value >= 100:
                        return f"{value:.0f} {unit}"
                    else:
                        return f"{value:.2f} {unit}".rstrip('0').rstrip('.')
            return f"{mb_value} MB"

        except (ValueError, TypeError) as e:
            logger.error(f"流量解析错误: {str(e)}")
            return "未知格式"
    def check_sign_status(self) -> bool:
        """查询签到状态"""
        self._ensure_valid_token()
        try:
            response = self._request("GET", "/sign", params={"user_id": self.user_id})
            return response.get("data", {}).get("status", False)
        except Exception as e:
            logger.error(f"状态查询失败: {str(e)}")
            return False

    def perform_sign(self) -> Dict[str, Any]:
        """执行签到操作"""
        self._ensure_valid_token()
        try:
            response = self._request("POST", "/sign", data={"user_id": self.user_id})
            return response.get("data", {})
        except Exception as e:
            logger.error(f"签到失败: {str(e)}")
            return {}

    def get_user_info(self) -> Dict[str, Any]:
        """获取用户信息"""
        self._ensure_valid_token()
        try:
            response = self._request("GET", "/user/info", params={"user_id": self.user_id})
            return response.get("data", {})
        except Exception as e:
            logger.error(f"信息获取失败: {str(e)}")
            return {}

    def execute(self):
        """主业务流程"""
        try:
            if self.check_sign_status():
                info = self.get_user_info()
                message = (
                    "✅ 今日已签到\r"
                    f"👤 用户:{info.get('username', '未知')}\r"
                    f"📊 剩余流量:{self.format_traffic(info.get('traffic', '0'))}"
                )
                logger.info(message.replace("\r", " - "))
                self.send_message(message)
                return

            result = self.perform_sign()
            info = self.get_user_info()
            message = (
                "🎉 签到成功\r"
                f"👤 用户:{info.get('username', '未知')}\r"
                f"📥 本次获得:{result.get('get_traffic', 0)}GB\r"
                f"📆 累计签到:{result.get('sign_count', 0)}次\r"
                f"📊 总流量:{self.format_traffic(info.get('traffic', '0'))}"
            )
            logger.info(message.replace("\r", " - "))
            self.send_message(message)

        except Exception as e:
            error_msg = f"❌ 签到失败\r错误详情:{str(e)}"
            logger.exception(error_msg)
            self.send_message(error_msg, "error")
            raise


if __name__ == "__main__":
    try:
        client = LocyanSign()
        client.execute()
    except Exception as e:
        logger.critical(f"系统故障: {str(e)}")
        exit(1)
0

评论区