liuyuqi-cnb 1 week ago
commit
e142a256a8
10 changed files with 685 additions and 0 deletions
  1. 24 0
      .cnb.yml
  2. 58 0
      .github/workflows/build.yml
  3. 1 0
      .gitignore
  4. 8 0
      Dockerfile
  5. 13 0
      README.md
  6. 228 0
      alidrive_checkin/__init__.py
  7. 338 0
      alidrive_checkin/alidrive_checkin.py
  8. 0 0
      data/.gitkeep
  9. 14 0
      main.py
  10. 1 0
      requirements.txt

+ 24 - 0
.cnb.yml

@@ -0,0 +1,24 @@
+master:
+  "crontab: 0 1 * * *":
+    - name: crontab-sift
+      docker:
+        image: python:3.11
+      runner:
+        cpus: 2
+      stages:
+        - name: install-deps
+          script: pip install -r requirements.txt
+        - name: run-checkin
+          script: python main.py checkin
+
+        # - name: build-image
+        #   script: docker build -t sift/nightly-checkin .
+        # - name: run-checkin
+        #   script: docker run --rm sift/nightly-checkin
+
+        # - name: docker login
+        #   script: docker login -u ${CNB_TOKEN_USER_NAME} -p "${CNB_TOKEN}" ${CNB_DOCKER_REGISTRY}
+        # - name: docker build
+        #   script: docker build -t ${CNB_DOCKER_REGISTRY}/${CNB_REPO_SLUG_LOWERCASE}:${CNB_COMMIT} .
+        # - name: docker push
+        #   script: docker push ${CNB_DOCKER_REGISTRY}/${CNB_REPO_SLUG_LOWERCASE}:${CNB_COMMIT}

+ 58 - 0
.github/workflows/build.yml

@@ -0,0 +1,58 @@
+name: auto CI
+
+on:
+  push:
+    branches: [ master ]
+  pull_request:
+    branches: [ master ]
+  schedule:
+  - cron: "0 2 * * 1-5"
+
+env:
+  POETRY_VERSION: 1.8.3
+  PYTHON_VERSION: "3.12"
+
+jobs:
+  build:
+    strategy:
+      matrix:
+        os: [ubuntu-latest]
+        include:
+        - os: ubuntu-latest
+          path: ~/.cache/pip
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v4
+        
+      - name: Set up Python
+        id: setup-python
+        uses: actions/setup-python@v5
+        with:
+          python-version: ${{ env.PYTHON_VERSION }}
+
+      - name: Install Poetry ${{ env.POETRY_VERSION }}
+        uses: abatilo/actions-poetry@v3.0.0
+        with:
+          poetry-version: ${{ env.POETRY_VERSION }}
+
+      - uses: actions/cache@v2
+        with:
+          path: ${{ matrix.path }}
+          key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
+          restore-keys: |
+            ${{ runner.os }}-pip-
+
+      - uses: actions/cache@v2
+        with:
+          path: ~/.local/share/virtualenvs
+          key: ${{ runner.os }}-python-${{ steps.setup-python.outputs.python-version }}-pipenv-${{ hashFiles('Pipfile.lock') }}
+        
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
+
+      - name: checkin
+        run: |
+          python main.py checkin

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+*.pyc

+ 8 - 0
Dockerfile

@@ -0,0 +1,8 @@
+FROM python:3.8
+
+WORKDIR /app
+
+COPY . /app
+
+RUN pip install -r requirements.txt -i https://mirrors.cloud.tencent.com/pypi/simple
+CMD python3 main.py checkin

+ 13 - 0
README.md

@@ -0,0 +1,13 @@
+# alidriver_checkin
+
+阿里网盘自动签到-action
+
+由于阿里网盘限速,签到扩容没有意义,本项目不再更新。
+
+## Usage
+
+## Develop
+
+
+## Licenses
+

+ 228 - 0
alidrive_checkin/__init__.py

@@ -0,0 +1,228 @@
+#!/usr/bin/env python
+# -*- encoding: utf-8 -*-
+'''
+@Contact :   liuyuqi.gov@msn.cn
+@Time    :   2025/09/30 22:31:02
+@License :   Copyright © 2017-2022 liuyuqi. All Rights Reserved.
+@Desc    :   alidriver_checkin script main entry
+'''
+
+import os
+import argparse
+from .crawl_lycheeshare import LycheeShare, DATA_DIR
+
+
+def main():
+    parser = argparse.ArgumentParser(description='LycheeShare 自动化工具')
+    parser.add_argument('command', choices=['generate', 'register', 'checkin', 'token', 'credit'],
+                        help='命令: generate(生成账户), register(批量注册), checkin(批量签到), token(批量获取token), credit(查询额度)')
+    parser.add_argument('-n', '--number', type=int, default=1, help='生成账户数量 (用于generate命令)')
+
+    args = parser.parse_args()
+
+    if args.command == 'generate':
+        lychee = LycheeShare('', '')
+        lychee.generate_account(args.number)
+        print(f"已生成 {args.number} 个账户到 {os.path.join(DATA_DIR, 'generate_account.txt')}")
+
+    elif args.command == 'register':
+        # 读取 generate_account.txt 批量注册
+        generate_file = os.path.join(DATA_DIR, 'generate_account.txt')
+        if not os.path.exists(generate_file):
+            print(f"错误: 未找到 {generate_file} 文件,请先运行 generate 命令")
+            return
+
+        with open(generate_file, 'r') as f:
+            lines = f.readlines()
+
+        print(f"开始批量注册,共 {len(lines)} 个账户")
+        success_count = 0
+
+        for line in lines:
+            parts = line.strip().split(',')
+            if len(parts) < 3:
+                print(f"跳过格式错误的行: {line.strip()}")
+                continue
+
+            username, email, password = parts[0], parts[1], parts[2]
+            print(f"\n正在注册: {username} ({email})")
+
+            lychee = LycheeShare(email, password)
+
+            # 发送验证码(自动返回000000)
+            success, result = lychee.get_captcha()
+            if not success:
+                print(f"发送验证码失败,跳过: {email}")
+                continue
+
+            # 自动使用验证码000000
+            code = "000000"
+            print(f"使用验证码: {code}")
+
+            # 注册
+            success, result = lychee.register(username, code)
+            if success:
+                # 注册成功,保存到 account.txt
+                account_file = os.path.join(DATA_DIR, 'account.txt')
+                with open(account_file, 'a') as af:
+                    af.write(f"{username},{email},{password}\n")
+                success_count += 1
+                print(f"注册成功并保存到 {account_file}: {email}")
+
+            # 添加延迟避免频繁请求
+            import time
+            import random
+            time.sleep(10*60*random.random())  # 随机延迟1到10分钟
+
+        print(f"\n批量注册完成!成功 {success_count}/{len(lines)} 个账户")
+
+    elif args.command == 'checkin':
+        # 读取 account.txt 批量签到
+        account_file = os.path.join(DATA_DIR, 'account.txt')
+        if not os.path.exists(account_file):
+            print(f"错误: 未找到 {account_file} 文件,请先运行 register 命令")
+            return
+
+        with open(account_file, 'r') as f:
+            lines = f.readlines()
+
+        print(f"开始批量签到,共 {len(lines)} 个账户")
+        success_count = 0
+
+        for line in lines:
+            parts = line.strip().split(',')
+            if len(parts) < 3:
+                print(f"跳过格式错误的行: {line.strip()}")
+                continue
+
+            username, email, password = parts[0], parts[1], parts[2]
+            print(f"\n正在签到: {username} ({email})")
+
+            lychee = LycheeShare(email, password)
+
+            # 尝试加载cookie
+            if lychee.load_cookies():
+                print("使用已保存的登录状态")
+            else:
+                print("未找到登录状态,正在登录...")
+                success, _ = lychee.login()
+                if not success:
+                    print(f"登录失败,跳过: {email}")
+                    continue
+
+            # 签到
+            success, _ = lychee.checkin()
+            if success:
+                success_count += 1
+
+            # 添加延迟避免频繁请求
+            import time
+            time.sleep(1)
+
+        print(f"\n批量签到完成!成功 {success_count}/{len(lines)} 个账户")
+
+    elif args.command == 'token':
+        # 读取 account.txt 批量获取token
+        account_file = os.path.join(DATA_DIR, 'account.txt')
+        if not os.path.exists(account_file):
+            print(f"错误: 未找到 {account_file} 文件,请先运行 register 命令")
+            return
+
+        with open(account_file, 'r') as f:
+            lines = f.readlines()
+
+        print(f"开始批量获取token,共 {len(lines)} 个账户")
+        success_count = 0
+
+        # 清空原有的 api_tokens.txt
+        token_file = os.path.join(DATA_DIR, 'api_tokens.txt')
+        if os.path.exists(token_file):
+            os.remove(token_file)
+
+        for line in lines:
+            parts = line.strip().split(',')
+            if len(parts) < 3:
+                print(f"跳过格式错误的行: {line.strip()}")
+                continue
+
+            username, email, password = parts[0], parts[1], parts[2]
+            print(f"\n正在获取token: {username} ({email})")
+
+            lychee = LycheeShare(email, password)
+
+            # 尝试加载cookie
+            if lychee.load_cookies():
+                print("使用已保存的登录状态")
+            else:
+                print("未找到登录状态,正在登录...")
+                success, _ = lychee.login()
+                if not success:
+                    print(f"登录失败,跳过: {email}")
+                    continue
+
+            # 获取token
+            success, _ = lychee.get_token()
+            if success:
+                success_count += 1
+
+            # 添加延迟避免频繁请求
+            import time
+            time.sleep(1)
+
+        print(f"\n批量获取token完成!成功 {success_count}/{len(lines)} 个账户")
+        print(f"所有token已保存到 {os.path.join(DATA_DIR, 'api_tokens.txt')}")
+
+    elif args.command == 'credit':
+        # 读取 account.txt 批量查询额度
+        account_file = os.path.join(DATA_DIR, 'account.txt')
+        if not os.path.exists(account_file):
+            print(f"错误: 未找到 {account_file} 文件,请先运行 register 命令")
+            return
+
+        with open(account_file, 'r') as f:
+            lines = f.readlines()
+
+        print(f"开始批量查询额度,共 {len(lines)} 个账户")
+        success_count = 0
+
+        # 清空原有的 credits.txt
+        credit_file = os.path.join(DATA_DIR, 'credits.txt')
+        if os.path.exists(credit_file):
+            os.remove(credit_file)
+
+        for line in lines:
+            parts = line.strip().split(',')
+            if len(parts) < 3:
+                print(f"跳过格式错误的行: {line.strip()}")
+                continue
+
+            username, email, password = parts[0], parts[1], parts[2]
+            print(f"\n正在查询额度: {username} ({email})")
+
+            lychee = LycheeShare(email, password)
+
+            # 尝试加载cookie
+            if lychee.load_cookies():
+                print("使用已保存的登录状态")
+            else:
+                print("未找到登录状态,正在登录...")
+                success, _ = lychee.login()
+                if not success:
+                    print(f"登录失败,跳过: {email}")
+                    continue
+
+            # 查询额度
+            success, _ = lychee.get_credit()
+            if success:
+                success_count += 1
+
+            # 添加延迟避免频繁请求
+            import time
+            time.sleep(1)
+
+        print(f"\n批量查询额度完成!成功 {success_count}/{len(lines)} 个账户")
+        print(f"所有额度信息已保存到 {os.path.join(DATA_DIR, 'credits.txt')}")
+
+
+if __name__ == '__main__':
+    main()

+ 338 - 0
alidrive_checkin/alidrive_checkin.py

@@ -0,0 +1,338 @@
+#!/usr/bin/env python
+# -*- encoding: utf-8 -*-
+'''
+@Contact :   liuyuqi.gov@msn.cn
+@Time    :   2025/09/30 22:31:02
+@License :   Copyright © 2017-2022 liuyuqi. All Rights Reserved.
+@Desc    :   alidriver_checkin crawler class
+'''
+
+import requests
+import pickle
+import os
+
+
+# 数据目录
+DATA_DIR = 'data'
+
+# 确保data目录存在
+if not os.path.exists(DATA_DIR):
+    os.makedirs(DATA_DIR)
+
+
+class AliDriverCheckin:
+    _host = 'https://www.aliyundrive.com'
+
+    headers = {
+        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36',
+        'Accept': 'application/json',
+        'Content-Type': 'application/json',
+        'Origin': 'https://lycheeshare.com',
+        'Referer': 'https://lycheeshare.com/profile'
+    }
+
+    def generate_account(self, number:int=20):
+        """Generate random account credentials.
+        :param int number: Number of accounts to generate.
+        :return: A tuple of (username, password, email).
+        """
+        import random
+        import string
+
+        # 常用单词和拼音列表
+        words = ['sky', 'sun', 'moon', 'star', 'cloud', 'rain', 'wind', 'fire', 'water', 'earth',
+                 'cat', 'dog', 'bird', 'fish', 'tiger', 'lion', 'bear', 'wolf', 'fox', 'deer',
+                 'blue', 'red', 'green', 'black', 'white', 'gold', 'silver', 'purple', 'orange',
+                 'happy', 'lucky', 'smart', 'cool', 'fast', 'strong', 'brave', 'super', 'magic',
+                 'wang', 'li', 'zhang', 'liu', 'chen', 'yang', 'huang', 'zhao', 'wu', 'zhou',
+                 'xiao', 'ming', 'hong', 'wei', 'jie', 'lei', 'tao', 'jun', 'feng', 'long']
+
+        if number < 1:
+            number = 1
+        if number > 100:
+            number = 100
+        accounts = []
+        for _ in range(number):
+            # 组合单词/拼音和数字
+            # word1 = random.choice(words)
+            # word2 = random.choice(words)
+            num = random.randint(100000000, 999999999)
+            username = f"1{num}"
+
+            # 密码:字母+数字组合,8-12位
+            password_len = random.randint(8, 12)
+            password = ''.join(random.choices(string.ascii_letters + string.digits, k=password_len))
+
+            email = f"{username}@qq.com"
+            accounts.append((username, email, password))
+
+        with open(os.path.join(DATA_DIR, 'generate_account.txt'), 'w') as f:
+            for account in accounts:
+                f.write(','.join(account) + '\n')
+
+        return accounts
+
+    def __init__(self, email, password):
+        self.email = email
+        self.password = password
+        self.session = requests.Session()
+        self.session.headers.update(self.headers)
+        self.token = None
+        self.cookie_file = os.path.join(DATA_DIR, "cookie" ,f'cookies_{email.replace("@", "_at_").replace(".", "_")}.pkl')
+        self.x_client_id = self._get_x_client_id() or 'mgn9qt0o-3d5ecd48cff583a6'
+
+    def get_captcha(self):
+        """Send verification code to email."""
+        return True, "000000"
+
+    def register(self, username, code):
+        """Register a new account with verification code."""
+        url = f'{self._host}/api/auth/register'
+        data = {
+            'email': self.email,
+            'password': self.password,
+            'username': username,
+            'code': code
+        }
+        try:
+            response = self.session.post(url, json=data)
+            result = response.json()
+            if response.status_code == 200 and result.get('code') == 0:
+                print(f"注册成功: {username}")
+                return True, result
+            else:
+                print(f"注册失败: {result}")
+                return False, result
+        except Exception as e:
+            print(f"注册异常: {e}")
+            return False, str(e)
+
+    def login(self):
+        """Login to account and save auth token."""
+        url = f'{self._host}/api/auth/login'
+        data = {
+            'email': self.email,
+            'password': self.password
+        }
+        try:
+            response = self.session.post(url, json=data)
+            result = response.json()
+            if response.status_code == 200 and result.get('code') == 0:
+                # Token在 data.token 中
+                if 'data' in result and 'token' in result['data']:
+                    self.token = result['data']['token']
+                    # 设置 auth-token cookie
+                    self.session.cookies.set('auth-token', self.token, domain='lycheeshare.com', path='/')
+                    self.session.headers.update({'Authorization': f'Bearer {self.token}'})
+                    # Save cookies to file
+                    self.save_cookies()
+                    print(f"登录成功: {self.email}")
+                    return True, result
+            print(f"登录失败: {result}")
+            return False, result
+        except Exception as e:
+            print(f"登录异常: {e}")
+            return False, str(e)
+
+    def _get_x_client_id(self):
+        """Fetch x-client-id from the homepage.
+        访问:https://lycheeshare.com ,在源码中查找类似如下内容:
+        window.__CLIENT_ID__ = 'mgn9qt0o-3d5ecd48cff583a6';
+        """
+        try:
+            response = self.session.get(self._host)
+            if response.status_code == 200:
+                import re
+                match = re.search(r"window\.__CLIENT_ID__\s*=\s*'([a-z0-9\-]+)'", response.text)
+                if match:
+                    print(f"获取 x-client-id: {match.group(1)}")
+                    return match.group(1)
+            print("无法获取 x-client-id")
+            return None
+        except Exception as e:
+            print(f"获取 x-client-id 异常: {e}")
+            return None
+
+    def checkin(self):
+        """Daily check-in to earn rewards."""
+        url = f'{self._host}/api/user/checkin'
+        try:
+            self.session.headers.update({'x-client-id': self.x_client_id})
+            response = self.session.post(url)
+            result = response.json()
+            if response.status_code == 200 and result.get('code') == 0:
+                print(f"签到成功: {self.email} - {result.get('message', '')}")
+                return True, result
+            else:
+                print(f"签到失败: {self.email} - {result}")
+                return False, result
+        except Exception as e:
+            print(f"签到异常: {e}")
+            return False, str(e)
+
+    def reset_token(self):
+        """Reset API token if get token fails."""
+        url = f'{self._host}/api/user/reset-api-token'
+        try:
+            response = self.session.post(url)
+            result = response.json()
+            if response.status_code == 200 and result.get('code') == 0:
+                print(f"重置Token成功: {self.email}")
+                return True, result
+            else:
+                print(f"重置Token失败: {result}")
+                return False, result
+        except Exception as e:
+            print(f"重置Token异常: {e}")
+            return False, str(e)
+
+    def get_token(self):
+        """Get API token for the account. If fails, reset and try again."""
+        url = f'{self._host}/api/user/get-api-token'
+        try:
+            response = self.session.get(url)
+            result = response.json()
+            if response.status_code == 200 and result.get('code') == 0:
+                api_token = None
+                if 'data' in result:
+                    api_token = result['data'].get('token') or result['data'].get('api_token')
+
+                if api_token:
+                    print(f"获取Token成功: {self.email}")
+                    print(f"API Token: {api_token}")
+                    # Save to file
+                    with open(os.path.join(DATA_DIR, 'api_tokens.txt'), 'a') as f:
+                        f.write(f"{self.email},{api_token}\n")
+                    return True, result
+                else:
+                    # Token为空,尝试重置
+                    print(f"Token为空,尝试重置Token: {self.email}")
+                    success, reset_result = self.reset_token()
+                    if success:
+                        # 重置成功后再次获取
+                        import time
+                        time.sleep(1)
+                        response = self.session.get(url)
+                        result = response.json()
+                        if response.status_code == 200 and result.get('code') == 0:
+                            if 'data' in result:
+                                api_token = result['data'].get('token') or result['data'].get('api_token')
+                            if api_token:
+                                print(f"重置后获取Token成功: {self.email}")
+                                print(f"API Token: {api_token}")
+                                with open(os.path.join(DATA_DIR, 'api_tokens.txt'), 'a') as f:
+                                    f.write(f"{self.email},{api_token}\n")
+                                return True, result
+                    return False, result
+            else:
+                # 如果获取失败,尝试重置token
+                print(f"获取Token失败,尝试重置Token: {self.email}")
+                success, reset_result = self.reset_token()
+                if success:
+                    # 重置成功后再次获取
+                    import time
+                    time.sleep(1)
+                    response = self.session.get(url)
+                    result = response.json()
+                    if response.status_code == 200 and result.get('code') == 0:
+                        if 'data' in result:
+                            api_token = result['data'].get('token') or result['data'].get('api_token')
+                        if api_token:
+                            print(f"重置后获取Token成功: {self.email}")
+                            print(f"API Token: {api_token}")
+                            with open(os.path.join(DATA_DIR, 'api_tokens.txt'), 'a') as f:
+                                f.write(f"{self.email},{api_token}\n")
+                            return True, result
+                return False, result
+        except Exception as e:
+            print(f"获取Token异常: {e}")
+            return False, str(e)
+
+    def get_credit(self):
+        """查询账户额度信息"""
+        # 确保有有效会话
+        try:
+            self.session.get(f'{self._host}/profile')
+        except:
+            pass
+
+        url = f'{self._host}/api/user/credit-bundles'
+        try:
+            response = self.session.get(url)
+            result = response.json()
+            if response.status_code == 200 and result.get('code') == 0:
+                if 'data' in result:
+                    credit_data = result['data']
+                    print(credit_data)
+                    total_credit = credit_data["summary"].get('total_credit', 0)
+                    used_credit = credit_data["summary"].get('used_credit', 0)
+                    available_credit = credit_data["summary"].get('available_credit', 0)
+
+                    print(f"额度查询成功: {self.email}")
+                    print(f"  总额度: {total_credit}")
+                    print(f"  已使用: {used_credit}")
+                    print(f"  可用额度: {available_credit}")
+
+                    # 保存到文件
+                    with open(os.path.join(DATA_DIR, 'credits.txt'), 'a') as f:
+                        f.write(f"{self.email},{total_credit},{used_credit},{available_credit}\n")
+
+                    return True, result
+                else:
+                    print(f"额度数据为空: {self.email}")
+                    return False, result
+            else:
+                print(f"查询额度失败: {self.email} - {result}")
+                return False, result
+        except Exception as e:
+            print(f"查询额度异常: {e}")
+            return False, str(e)
+
+
+    def save_cookies(self):
+        """Save session cookies to file."""
+        try:
+            # 确保cookie目录存在
+            cookie_dir = os.path.dirname(self.cookie_file)
+            if not os.path.exists(cookie_dir):
+                os.makedirs(cookie_dir)
+            
+            with open(self.cookie_file, 'wb') as f:
+                pickle.dump({
+                    'cookies': self.session.cookies,
+                    'token': self.token
+                }, f)
+            print(f"Cookies已保存到 {self.cookie_file}")
+        except Exception as e:
+            print(f"保存Cookies失败: {e}")
+
+    def load_cookies(self):
+        """Load session cookies from file and validate."""
+        if os.path.exists(self.cookie_file):
+            try:
+                with open(self.cookie_file, 'rb') as f:
+                    data = pickle.load(f)
+                    self.session.cookies.update(data['cookies'])
+                    self.token = data.get('token')
+                    if self.token:
+                        self.session.headers.update({'Authorization': f'Bearer {self.token}'})
+                print(f"Cookies已加载: {self.cookie_file}")
+
+                # 验证cookie有效性
+                print("验证Cookie有效性...")
+                url = f'{self._host}/api/user/credit-bundles'
+                try:
+                    response = self.session.get(url)
+                    if response.status_code == 200 and response.json().get('code') == 0:
+                        print("Cookie有效")
+                        return True
+                    else:
+                        print(f"Cookie已失效 (状态码: {response.status_code}),需要重新登录")
+                        return False
+                except Exception as e:
+                    print(f"验证Cookie时出错: {e},需要重新登录")
+                    return False
+            except Exception as e:
+                print(f"加载Cookies失败: {e}")
+                return False
+        return False

+ 0 - 0
data/.gitkeep


+ 14 - 0
main.py

@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+# -*- encoding: utf-8 -*-
+'''
+@Contact :   liuyuqi.gov@msn.cn
+@Time    :   2025/09/30 22:31:02
+@License :   Copyright © 2017-2022 liuyuqi. All Rights Reserved.
+@Desc    :   enter point
+
+'''
+
+from alidriver_checkin import main
+
+if __name__ == '__main__':
+    main()

+ 1 - 0
requirements.txt

@@ -0,0 +1 @@
+requests