MoreRSS

site iconuptoz | 小小修改

软件开发工程师,INFP,比较喜欢研究数码和软件,想要探究在互联网上的事物是如何被创造和发展。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

uptoz | 小小的 RSS 预览

【Docker】搭建一款跨平台、基于规则的开源代理工具 - Clash

2025-05-31 10:21:12

前言

本教程基于飞牛系统 fnOS 0.9.8Docker Compose 功能进行搭建。目前该dreamacro/clash-premium​镜像暂时不支持自动更新订阅,自动更新订阅需要配合脚本进行自动更新。

简介

Clash 是一款跨平台基于规则的开源代理工具,主要用于科学上网、网络加速和隐私保护。它通过灵活的规则引擎和丰富的协议支持,帮助用户高效管理网络流量,绕过地域限制。

部署

  1. 创建以下目录结构。

    /clash
    ├── docker-compose.yml
    └── data/config/
        └── config.yaml          # 主配置文件
  2. 创建并编辑config.yaml​文件,然后保存至./clash/data/config/​文件夹下。

    一般情况下订阅链接复制到浏览器打开便是一个xxxx.yaml​的配置文件,将其内容复制粘贴放至config.yaml​文件。
    注意将external-controller​127.0.0.1:9090​务必修改成0.0.0.0:9090​

    port: 7890
    socks-port: 7891
    redir-port: 7892
    allow-lan: true
    mode: Rule
    log-level: silent
    external-controller: '0.0.0.0:9090'
    secret: "your_strong_password"   # 与compose中SECRET一致
    
    
    # 代理规则示例(替换为实际订阅)
    #proxies:
    #  - name: "Proxy-Server"
    #    type: ss
    #    server: server-ip
    #    port: 443
    #    cipher: aes-256-gcm
    #    password: "password"
    
    #proxy-groups:
    #  - name: PROXY
    #    type: select
    #    proxies:
    #      - Proxy-Server
    
    #rules:
    #  - DOMAIN-SUFFIX,google.com,PROXY
    #  - GEOIP,CN,DIRECT
    #  - MATCH,PROXY
  3. 打开Docker管理器,选择「Compose」,点击右上角「新增项目」。

  4. 在创建项目窗口中填写「项目名称」和选择数据存放「路径」,然后选择「创建docker-compose.yml」,将下列代码根据自己实际情况修改后复制粘贴进去。

    version: '3.8'
    services:
      # Clash 核心服务
      clash:
        image: dreamacro/clash-premium:latest  # 使用 Premium 版支持高级功能
        container_name: clash-proxy
        restart: unless-stopped
        ports:
          - "7890:7890"  # HTTP 代理端口
          - "7891:7891"  # SOCKS5 代理端口
          - "9090:9090"  # 控制端口
        volumes:
          - ./data/config:/root/.config/clash  # 配置文件目录
        environment:
          - TZ=Asia/Shanghai
          - EXTERNAL_CONTROLLER=0.0.0.0:9090  # 控制端口监听
          - SECRET=your_strong_password  # API 访问密钥
    
      # Clash 控制面板
      yacd:
        image: haishanh/yacd:latest
        container_name: clash-dashboard
        restart: unless-stopped
        ports:
          - "8080:80"  # 面板访问端口 http://<ip>:8080
        environment:
          - YACD_DEFAULT_BACKEND=http://clash:9090  # 通过服务名连接Clash
          - YACD_AUTH=your_strong_password  # 与SECRET一致

    勾选「创建项目后立即启动」,最后点击「确定」。

  5. 等待构建完成,然后在浏览器中输入IP:Port​访问。

使用

  1. 添加链接,进入管理后台。

  2. 进入后台查看「代理」,如能正常显示信息,则表示config.yaml​文件格式没有问题。

  3. 在「配置」中打开「Allow LAN」

Windows11使用代理

  1. Windows 11 为例,使用代理。「设置」→「网络和Internet」→「代理」

  2. 手动设置代理

  3. 尝试访问地域限制网站,如谷歌(https://www.google.com/)、YouTube(https://www.youtube.com/)等进行试验。

单个Docker容器使用代理

  1. 以单个 Docker容器 为例,使用代理。使用 DockerCompose 构建镜像时,可以在 DockerCompose 中设置代理环境变量。

    version: "3.8"
    services:
      metatube:
        image: metatube/metatube-server:latest
        container_name: metatube
        restart: always
        ports:
          - "28080:8080"      # Web访问端口
        environment:
          TZ: Asia/Shanghai    # 设置为中国时区
          http_proxy: http://192.168.1.6:7890  # 容器内HTTP代理
          https_proxy: http://192.168.1.6:7890 # 容器内HTTPS代理
          no_proxy: localhost,127.0.0.1,.internal,clash,yacd  # 代理排除列表

【Python】iKuuu青龙自动签到脚本

2025-05-27 21:48:14

前言

基于青龙面板编写的iKuuu每日自动签到脚本,请提前安装好青龙面板

特性说明:

  • 多账户支持:自动读取环境变量中的多个账户信息

  • 智能域名更新:自动检测官网域名变更并更新脚本

  • 备用域名切换:主域名不可用时自动尝试备用域名

  • 异常处理:单个账户失败不影响其他账户执行

  • 结果通知:通过青龙内置通知服务推送聚合签到结果

代码

# -*- coding: utf-8 -*
'''
定时自定义
0 0 1 * * ? iKuuu.py
new Env('iKuuu签到');
'''
import requests
import re
import json
import os
import datetime
import urllib.parse
import sys
import time
from bs4 import BeautifulSoup

# 添加青龙脚本根目录到Python路径
QL_SCRIPTS_DIR = '/ql/scripts'  # 青龙脚本默认目录
sys.path.append(QL_SCRIPTS_DIR)

# 添加notify可能存在的其他路径
POSSIBLE_PATHS = [
    '/ql',                      # 青龙根目录
    '/ql/data/scripts',         # 新版青龙数据目录
    '/ql/scripts/notify',       # 自定义通知目录
    os.path.dirname(__file__)   # 当前脚本目录
]

for path in POSSIBLE_PATHS:
    if os.path.exists(os.path.join(path, 'notify.py')):
        sys.path.append(path)
        break

try:
    from notify import send
except ImportError:
    print("⚠️ 无法加载通知模块,请检查路径配置")
    send = lambda title, content: None  # 创建空函数防止报错

# 初始域名
ikun_host = "ikuuu.one"  # 自动更新于2025-04-29 13:08:20
backup_hosts = ["ikuuu.one", "ikuuu.pw", "ikuuu.me"]  # 备用域名列表

def get_latest_ikun_host():
    test_url = f"https://{ikun_host}/"
    try:
        response = requests.get(test_url, timeout=10)
        if response.status_code == 200:
            if "官网域名已更改" in response.text or "Domain deprecated" in response.text:
                print("检测到域名变更通知,正在提取新域名...")
                h2_matches = re.findall(r'<h2>.*?(?:域名|domain)[::]\s*([a-zA-Z0-9.-]+)</h2>', response.text)
                if h2_matches:
                    return h2_matches[0]
                js_matches = re.findall(r'https?://([a-zA-Z0-9.-]+)/auth/login', response.text)
                if js_matches:
                    return js_matches[0]
                fallback_match = re.search(r'(?:域名|domain)[::]\s*([a-zA-Z0-9.-]+)', response.text)
                if fallback_match:
                    return fallback_match.group(1)
                print("⚠️ 检测到域名变更但无法提取新域名")
                return None
            else:
                print("✅ 当前域名正常")
                return None
    except Exception as e:
        print(f"域名检测异常: {e}")
    return None

def update_self_host(new_host):
    script_path = os.path.abspath(__file__)
    with open(script_path, "r", encoding="utf-8") as f:
        lines = f.readlines()
    updated = False
    for i, line in enumerate(lines):
        if line.strip().startswith("ikun_host = "):
            lines[i] = f'ikun_host = "{new_host}"  # 自动更新于{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\n'
            updated = True
            break
    if updated:
        with open(script_path, "w", encoding="utf-8") as f:
            f.writelines(lines)
        print(f"✅ 脚本已更新至域名: {new_host}")
        return True
    else:
        print("⚠️ 域名更新失败")
        return False

def test_host_reachable(host):
    try:
        response = requests.get(f"https://{host}/", timeout=10)
        return response.status_code == 200
    except:
        return False

def get_remaining_flow(cookies):
    """获取用户剩余流量信息"""
    user_url = f'https://{ikun_host}/user'
    try:
        # 获取用户页面
        user_page = requests.get(user_url, cookies=cookies, timeout=15)
        if user_page.status_code != 200:
            return "获取流量失败", "状态码: " + str(user_page.status_code)
        
        # 使用BeautifulSoup解析HTML
        soup = BeautifulSoup(user_page.text, 'html.parser')
        
        # 查找包含剩余流量的卡片
        flow_cards = soup.find_all('div', class_='card card-statistic-2')
        for card in flow_cards:
            h4_tag = card.find('h4')
            if h4_tag and '剩余流量' in h4_tag.text:
                # 查找流量数值
                counter_span = card.find('span', class_='counter')
                if counter_span:
                    flow_value = counter_span.text.strip()
                    
                    # 查找流量单位
                    unit_text = ""
                    next_sibling = counter_span.next_sibling
                    if next_sibling:
                        unit_text = next_sibling.strip()
                    
                    return flow_value, unit_text
        
        # 如果没有找到,尝试其他方式
        flow_div = soup.find('div', string='剩余流量')
        if flow_div:
            parent_div = flow_div.find_parent('div', class_='card-body')
            if parent_div:
                flow_text = parent_div.get_text(strip=True).replace('剩余流量', '')
                return flow_text.split()[0], flow_text.split()[1] if len(flow_text.split()) > 1 else ""
        
        return "未找到", "流量信息"
        
    except Exception as e:
        return "流量获取异常", str(e)

def ikuuu_signin(email, password):
    params = {'email': email, 'passwd': password, 'code': ''}
    login_url = f'https://{ikun_host}/auth/login'
    try:
        # 登录请求
        login_res = requests.post(login_url, data=params, timeout=15)
        if login_res.status_code != 200:
            flow_value, flow_unit = "登录失败", "无法获取"
            return False, f"登录失败(状态码{login_res.status_code})", flow_value, flow_unit
        
        login_data = login_res.json()
        if login_data.get('ret') != 1:
            flow_value, flow_unit = "登录失败", "无法获取"
            return False, f"登录失败:{login_data.get('msg', '未知错误')}", flow_value, flow_unit
        
        # 获取用户剩余流量
        cookies = login_res.cookies
        flow_value, flow_unit = get_remaining_flow(cookies)
        
        # 执行签到
        checkin_res = requests.post(f'https://{ikun_host}/user/checkin', cookies=cookies, timeout=15)
        if checkin_res.status_code != 200:
            return False, f"签到失败(状态码{checkin_res.status_code})", flow_value, flow_unit
        
        checkin_data = checkin_res.json()
        if checkin_data.get('ret') == 1:
            return True, f"成功 | {checkin_data.get('msg', '')}", flow_value, flow_unit
        else:
            return False, f"签到失败:{checkin_data.get('msg', '未知错误')}", flow_value, flow_unit
    except json.JSONDecodeError:
        return False, "响应解析失败", "未知", "未知"
    except requests.exceptions.Timeout:
        return False, "请求超时", "未知", "未知"
    except Exception as e:
        return False, f"请求异常:{str(e)}", "未知", "未知"

def send_qinglong_notification(results):
    """
    使用青龙面板内置通知系统发送通知
    需要青龙面板已配置通知渠道(如钉钉、企业微信等)
    """
    title = "iKuuu签到通知"
    
    # 构建消息内容
    success_count = sum(1 for res in results if res['success'])
    failure_count = len(results) - success_count
    
    message = [
        f"🔔 签到完成 | 成功:{success_count} 失败:{failure_count}",
        "================================"
    ]
    
    for index, res in enumerate(results, 1):
        status = "✅ 成功" if res['success'] else "❌ 失败"
        message.append(f"{index}. {res['email']}")
        message.append(f"  状态:{status}")
        message.append(f"  详情:{res['message']}")
        message.append(f"  剩余流量:{res['flow_value']} {res['flow_unit']}")
        message.append("--------------------------------")
    
    # 添加统计信息
    message.append("\n🕒 执行时间:" + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
    
    try:
        # 发送通知(青龙自动处理多通知渠道)
        send(title, "\n".join(message))
        print("✅ 通知已发送")
    except Exception as e:
        print(f"⚠️ 通知发送失败,请检查通知配置: {str(e)}")

if __name__ == "__main__":
    # ==================== 域名更新逻辑 ====================
    print(f"当前域名: {ikun_host}")
    latest_host = get_latest_ikun_host()
    if latest_host:
        print(f"检测到新域名: {latest_host}")
        if update_self_host(latest_host):
            ikun_host = latest_host
    
    # ==================== 域名可用性检查 ====================
    if not test_host_reachable(ikun_host):
        print("主域名不可用,尝试备用域名...")
        found = False
        for host in backup_hosts:
            if test_host_reachable(host):
                ikun_host = host
                print(f"切换到备用域名: {ikun_host}")
                found = True
                break
        if not found:
            print("❌ 所有域名均不可用")
            exit(1)
    
    # ==================== 账户处理 ====================
    accounts = []
    account_str = os.getenv('IKUUU_ACCOUNTS')
    if not account_str:
        print("❌ 未找到环境变量 IKUUU_ACCOUNTS")
        exit(1)
    
    for line in account_str.strip().splitlines():
        if ':' in line:
            email, pwd = line.split(':', 1)
            accounts.append((email.strip(), pwd.strip()))
        else:
            print(f"⚠️ 忽略无效账户行: {line}")
    
    if not accounts:
        print("❌ 未找到有效账户")
        exit(1)
    
    # ==================== 执行签到 ====================
    results = []
    for email, pwd in accounts:
        print(f"\n处理账户: {email}")
        success, msg, flow_value, flow_unit = ikuuu_signin(email, pwd)
        results.append({
            'email': email, 
            'success': success, 
            'message': msg,
            'flow_value': flow_value,
            'flow_unit': flow_unit
        })
        print(f"结果: {'成功' if success else '失败'} - {msg}")
        print(f"剩余流量: {flow_value} {flow_unit}")
        
        # 账户间延迟防止请求过快
        time.sleep(1)
    
    # ==================== 结果通知 ====================
    print("\n正在发送通知...")
    send_qinglong_notification(results)
    
    # ==================== 本地结果输出 ====================
    print("\n签到结果汇总:")
    for res in results:
        print(f"邮箱: {res['email']}")
        print(f"状态: {'成功' if res['success'] else '失败'}")
        print(f"详情: {res['message']}")
        print(f"剩余流量: {res['flow_value']} {res['flow_unit']}\n{'-'*40}")

使用

  1. 环境变量配置(青龙面板),在「环境变量」里创建IKUUU_ACCOUNTS​:多个账户用换行分隔,格式为 邮箱:密码​

    [email protected]:password1
    [email protected]:password2

  2. 配置推送通知(青龙面板),在「配置文件」中的config.sh​中根据提示配置通知服务。

  3. 将上述代码复制保存到记事本,并且重命名为iKuuu.py​。

  4. 打开青龙面板后台,「脚本管理」→「+」→「本地文件」,将保存的iKuuu.py​上传后点击「确定」。

  5. 添加定时任务。「定时任务」→「创建任务」,可自定义「名称」,「命令/脚本」固定为task iKuuu.py​,「定时类型」选择常规定时​,「定时规则」填写0 0 1 * * ?​(每天凌晨一点执行)。

  6. 通知示例:

    iKuuu签到通知
    
    🔔 签到完成 | 成功:1 失败:1
    ================================
    1. [email protected]
      状态:❌ 失败
      详情:签到失败:您似乎已经签到过了...
      剩余流量:59.14 GB
    --------------------------------
    2. [email protected]
      状态:✅ 成功
      详情:成功 | 你获得了 2582 MB流量
      剩余流量:54.34 GB
    --------------------------------
    
    🕒 执行时间:2025-05-27 13:51:46
  7. 日志输出:

    • 每个账户的执行结果会在控制台详细输出

    • 最终汇总结果通过内置通知模块推送(根据自己需求进行配置)

【CSharp】抽象类和接口详解

2025-05-17 13:24:03

抽象类(Abstract Class)

概念

  • 不能实例化:抽象类不能被实例化,它通常作为基类存在,为子类提供一套通用的接口和部分实现。

  • 包含实现:抽象类可以包含具体的方法实现和抽象方法。抽象方法必须在子类中被重写。

  • 单继承:一个类只能继承自一个抽象类(C#中不支持多重继承)。

应用场景

当你希望提供一个通用的基类,该基类定义了一些子类共有的方法实现,并且还有一些方法需要由子类提供具体实现时,使用抽象类是一个不错的选择。

示例

示例1:图形基类

namespace App01
{
    // 抽象的图形基类
    public abstract class Shape
    {
        // 抽象方法:计算面积
        public abstract double Area();

        // 具体实现的方法:显示形状信息
        public void Display()
        {
            Console.WriteLine("This is a shape.");
        }
    }

    // 圆形类,继承自Shape
    publicclass Circle : Shape
    {
        publicdouble Radius { get; set; }

        // 重写抽象方法:计算圆的面积
        public override double Area()
        {
            return Math.PI * Radius * Radius;
        }
    }

    // 矩形类,继承自Shape
    publicclass Rectangle : Shape
    {
        publicdouble Width { get; set; }
        publicdouble Height { get; set; }

        // 重写抽象方法:计算矩形的面积
        public override double Area()
        {
            return Width * Height;
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            Circle circle = new Circle();
            circle.Radius = 5;
            Console.WriteLine("The area of the circle is {0}.", circle.Area());

            Rectangle rectangle = new Rectangle();
            rectangle.Width = 4;
            rectangle.Height = 6;
            Console.WriteLine("The area of the rectangle is {0}.", rectangle.Area());
        }
    }
}

说明:

  • Shape​类是一个抽象类,包含一个抽象方法Area()​和一个具体方法Display()​。

  • Circle​和Rectangle​类继承自Shape​,并实现了Area()​方法。

示例2:动物基类

namespace App02  
{
    // 抽象的动物基类
    public abstract class Animal
    {
        // 抽象方法:发出叫声
        public abstract void MakeSound();

        // 具体方法:共同的行为
        public void Sleep()
        {
            Console.WriteLine("The animal is sleeping.");
        }
    }

    // 狗类,继承自Animal
    publicclass Dog : Animal
    {
        public override void MakeSound()
        {
            Console.WriteLine("Dog barks: Woof!");
        }
    }

    // 猫类,继承自Animal
    publicclass Cat : Animal
    {
        public override void MakeSound()
        {
            Console.WriteLine("Cat meows: Meow!");
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            // 创建Dog对象
            Dog dog = new Dog();
            // 调用Dog的共同行为
            dog.Sleep();
            // 调用Dog的叫声
            dog.MakeSound();
            // 创建Cat对象
            Cat cat = new Cat();
            // 调用Cat的共同行为
            cat.Sleep();
            // 调用Cat的叫声
            cat.MakeSound();
        }
    }
}

说明:

  • Animal​类定义了共有的行为Sleep()​,并要求子类实现MakeSound()​方法。

接口(Interface)

概念

  • 完全抽象:接口只能包含方法、属性、事件、索引器的声明,不能包含任何实现。

  • 多实现:一个类可以实现多个接口,实现接口即需要实现其所有成员。

  • 成员默认是公共的:接口成员默认是公共的,不能包含访问修饰符。

应用场景

当你希望定义一组不相关类之间的通用行为契约,并且不涉及实现细节时,接口是最好的选择。

示例

示例1:可绘制和可变形

namespace App01
{
    // 可绘制的接口
    public interface IDrawable
    {
        void Draw();
    }

    // 可变形的接口
    public interface ITransformable
    {
        void Rotate(double angle);
        void Scale(double factor);
    }

    // 实现了IDrawable和ITransformable的形状类
    publicclass TransformableShape : IDrawable, ITransformable
    {
        public void Draw()
        {
            Console.WriteLine("Drawing the shape.");
        }

        public void Rotate(double angle)
        {
            Console.WriteLine($"Rotating the shape by {angle} degrees.");
        }

        public void Scale(double factor)
        {
            Console.WriteLine($"Scaling the shape by a factor of {factor}.");
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            TransformableShape shape = new TransformableShape();
            shape.Draw();
            shape.Rotate(45);
            shape.Scale(2.0);
            Console.ReadKey();
        }
    }
}

说明:

  • ​IDrawable​和ITransformable​是两个接口,定义了绘制和变形的行为。

  • ​TransformableShape​类实现了这两个接口,必须提供所有方法的实现。

示例2:数据存储接口

namespace App02
{
    // 数据存储接口
    public interface IDataStore
    {
        void Save(string data);
        string Load();
    }

    // 本地文件存储类
    publicclass FileDataStore : IDataStore
    {
        public void Save(string data)
        {
            Console.WriteLine("Saving data to file.");
            // 实际的文件保存逻辑
        }

        public string Load()
        {
            Console.WriteLine("Loading data from file.");
            // 实际的文件加载逻辑
            return"Data from file";
        }
    }

    // 云端存储类
    publicclass CloudDataStore : IDataStore
    {
        public void Save(string data)
        {
            Console.WriteLine("Saving data to cloud.");
            // 实际的云保存逻辑
        }

        public string Load()
        {
            Console.WriteLine("Loading data from cloud.");
            // 实际的云加载逻辑
            return"Data from cloud";
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            // 选择存储方式
            IDataStore dataStore = new FileDataStore();
            // 保存数据
            dataStore.Save("Some data");
            // 加载数据
            string loadedData = dataStore.Load();
            Console.WriteLine("Loaded data: " + loadedData);
        }
    }
}

抽象类与接口的组合使用

有时,我们可以将抽象类和接口结合起来使用,以充分利用它们的优势。

示例:动物行为

namespace App03
{
    // 抽象的动物类
    public abstract class Animal
    {
        public abstract void Eat();

        public void Breathe()
        {
            Console.WriteLine("Animal breathes.");
        }
    }

    // 可移动的接口
    public interface IMovable
    {
        void Move();
    }

    // 可飞行的接口
    public interface IFlyable
    {
        void Fly();
    }

    // 狗类,继承自Animal并实现IMovable接口
    publicclass Dog : Animal, IMovable
    {
        public override void Eat()
        {
            Console.WriteLine("Dog eats.");
        }

        public void Move()
        {
            Console.WriteLine("Dog runs.");
        }
    }

    // 鸟类,继承自Animal并实现IMovable和IFlyable接口
    publicclass Bird : Animal, IMovable, IFlyable
    {
        public override void Eat()
        {
            Console.WriteLine("Bird eats.");
        }

        public void Move()
        {
            Console.WriteLine("Bird walks.");
        }

        public void Fly()
        {
            Console.WriteLine("Bird flies.");
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            // 创建Dog对象并调用方法
            Dog dog = new Dog();
            dog.Eat();
            dog.Breathe();
            dog.Move();
            // 创建Bird对象并调用方法
            Bird bird = new Bird();
            bird.Eat();
            bird.Breathe();
            bird.Move();
            bird.Fly();
            Console.ReadKey();
        }
    }
}

说明:

  • Animal​是一个抽象类,定义了所有动物的共有行为。

  • IMovable​和IFlyable​是接口,定义了可移动和可飞行的行为。

  • Dog​类继承自Animal​并实现了IMovable​接口。

  • ​Bird​类继承自Animal​并实现了IMovable​和IFlyable​接口。

总结

抽象类和接口的区别

特性

抽象类

接口

实例化

❌ 不能实例化

❌ 不能实例化

实现内容

✅ 可包含具体方法和抽象方法

❌ 仅声明成员,无实现

继承/实现数量

单继承(一个子类只能继承一个父类)

多实现(一个类可实现多个接口)

成员访问修饰符

支持(如public​、protected​)

成员默认public​,不可显式修饰

字段/属性

✅ 可定义字段、属性

❌ 只能声明属性(无字段)

设计目的

提供代码复用和部分通用逻辑

定义行为契约,实现多态

组合使用场景

  • 结合优势:抽象类提供基础实现,接口扩展额外功能

    • 示例

      • Animal​抽象类定义Eat()​Breathe()​

      • IMovable​IFlyable​接口分别定义移动和飞行能力。

      • Dog​继承Animal​并实现IMovable​。

      • Bird​继承Animal​并实现IMovable​和IFlyable​。

何时选择?

  • 抽象类:聚焦于代码复用层次化设计,适合“是什么”(Is-A)关系。

  • 接口:聚焦于行为契约功能扩展,适合“能做什么”(Can-Do)关系。

  • 组合使用:通过抽象类提供基础能力,接口扩展多样化功能,实现灵活设计。

【Docker】搭建一款开源的自动化任务管理平台 - 青龙面板

2025-05-06 00:11:15

前言

本教程基于飞牛系统fnOS 0.8.47Docker Compose功能进行搭建。

简介

青龙面板(Qinglong Panel)是一款开源的自动化任务管理平台,凭借其灵活的任务调度和脚本支持能力,成为开发者、运维人员及普通用户提升效率的利器。
定义:青龙面板是一款基于Web界面的多语言脚本支持工具,专注于服务器管理和自动化任务执行,适用于定时任务调度、脚本运行监控等场景。

  • 核心功能

  1. 任务管理
    定时任务:支持Crontab表达式配置,秒级任务执行精度。
    脚本执行:兼容Python、JavaScript、Shell、TypeScript等多种语言,用户可直接上传或在线编辑脚本。
    任务日志:实时查看任务执行状态、输出结果及错误排查信息。

  2. 自动化生态支持
    环境变量管理:全局或任务级变量配置,提升脚本灵活性。
    依赖管理:支持Node.js、Python等依赖库的批量安装与维护。
    通知系统:集成邮件、微信、钉钉等通知方式,任务异常时自动告警。

  • 典型应用场景

  1. 服务器运维
    自动化执行备份脚本、服务重启等维护任务。

  2. 自动化任务
    电商场景:京东自动签到、价格监控、抢购脚本。
    数据采集:爬虫脚本定时运行,支持数据清洗与存储。
    个人工具:B站每日任务、云盘签到、游戏脚本托管。

  3. 开发与测试
    自动化测试脚本调度(如API接口测试)。
    开发环境的快速部署与配置。

部署

  1. 打开Docker管理器,选择「Compose」,点击右上角「新增项目」。

  2. 在创建项目窗口中填写「项目名称」和选择数据存放「路径」,然后选择「创建docker-compose.yml」,将下列代码根据自己实际情况修改后复制粘贴进去。

    version: '3'
    services:
      qinglong:
        image: whyour/qinglong:latest
        container_name: qinglong
        restart: unless-stopped
        environment:
          - TZ=Asia/Shanghai  # 设置时区
        volumes:
          - ./data:/ql/data    # 持久化存储配置和脚本
        ports:
          - "5700:5700"        # 宿主机端口:容器端口(可自定义宿主机端口)

    勾选「创建项目后立即启动」,最后点击「确定」。

  3. 等待构建完成,然后在浏览器中输入IP:Port​访问。

使用

  1. 初始化配置,直接点击「开始安装」。

  2. 通知设置,先点击「跳过」。

  3. 账户设置,可自定义青龙面板后台管理的用户名和密码,然后点击「提交」。

  4. 完成安装,点击「去登录」。

  5. 输入自定义的用户名和密码,然后点击「登录」。

  6. Node.js​依赖环境安装,在最左侧菜单栏选择「依赖管理」,然后点击「创建依赖」,「依赖类型」选择node.js​,「自动拆分」选择​,「名称」填写以下内容:

    crypto-js
    prettytable
    dotenv
    jsdom
    date-fns
    tough-cookie
    tslib
    [email protected]
    ts-md5
    jsdom -g
    jieba
    fs
    form-data
    json5
    global-agent
    png-js
    @types/node
    require
    typescript
    js-base64
    axios
    moment
    ds

    最后点击「确定」。

  7. Python3​依赖环境安装,在最左侧菜单栏选择「依赖管理」,然后点击「创建依赖」,「依赖类型」选择python3​,「自动拆分」选择​,「名称」填写以下内容:

    requests
    canvas
    ping3
    jieba
    aiohttp

    最后点击「确定」。

使用

  1. 更多使用教程,请参照官方文档 Qinglong Doc(https://qinglong.online/guide/user-guide

定时规则

Cron 表达式格式

  • *(默认支持 6 字段 格式(包含秒),但部分场景可能兼容 5 字段(省略秒))

*(0-59)

*(0-59)

*(0-23)

*(1-31)

*(1-12)

*(0-7)

秒(可选)

小时

星期

特殊符合

  1. ​*​:任意值

    • 例:0 * * * *​ 表示每小时的第 0 分钟执行。

  2. ​,​:多个值

    • 例:0 3,6,9 * * *​ 表示每天 3:00、6:00、9:00 执行。

  3. ​-​:范围

    • 例:0 9-18/2 * * *​ 表示 9:00 到 18:00 之间每 2 小时执行一次。

  4. ​/​:步长

    • 例:*/30 * * * *​ 表示每 30 分钟执行一次。

  5. ​?​:忽略(仅用于日或周几)

    • 例:0 0 * * ?​ 表示每天 0 点执行,忽略周几。

  6. ​L​:最后一天(仅日字段)

    • 例:0 0 L * *​ 表示每月最后一天执行。

常用示例

表达式

说明

​0 0 * * *​

每天 00:00 执行

​0 */6 * * *​

每 6 小时执行一次(0:00, 6:00等)

​0 0 */2 * *​

每 2 天的 0:00 执行

​0 0 12 * * ?​

每天 12:00 执行

​0 0 0 1 * *​

每月 1 日 0:00 执行

​0 0 8-18 * * 1-5​

工作日(周一至周五)8:00-18:00 每小时执行

飞牛虚拟机开启IOMMU硬件直通

2025-04-28 22:03:20

前言

本教程基于物理机搭建的飞牛系统fnOS 0.8.47进行演示。

前置条件

  1. 硬件支持

    • CPU需支持虚拟化技术(Intel VT-x/AMD-V)及IOMMU(Intel VT-d/AMD-Vi)。

    • 主板需在BIOS中启用相关选项:

      • Intel平台:开启VT-d;AMD平台:开启SVM Mode及IOMMU。

      • 关闭CSM(兼容性支持模块),确保系统为纯UEFI模式。

  2. 系统要求

    • 飞牛OS需更新至最新版本(应用中心可见虚拟机功能)。

教程

  1. 启用SSH功能

    • 进入飞牛OS的「系统设置」→「SSH选项」,开启SSH服务。

  2. 连接飞牛

    • 通过SSH工具(如PowerShell)连接飞牛OS,输入sudo -i​回车后输入密码切换至root权限。

  3. 修改GRUB引导参数

    • 切换至root权限后执行:

      nano /etc/default/grub
    • 找到GRUB_CMDLINE_LINUX_DEFAULT​行,添加以下参数:

      • Intel CPU:intel_iommu=on iommu=pt​

      • AMD CPU:amd_iommu=on​

      (注意保留原有参数,例如原内容可能包含quiet i915.force_probe=7d55​)。

    • 保存退出(Ctrl+S​ Ctrl+X​),更新GRUB并重启:

      update-grub && update-initramfs -u -k all && reboot

    常见应用场景

    1. GPU硬件解码
      • 直通核显或独立显卡,提升影视转码效率(如Jellyfin/Plex)。

    2. 软路由部署
      • 直通多网口实现专业级路由功能(参考OpenWrt镜像部署方案)。

    3. USB设备独占
      • 外接硬盘或加密狗可直接映射给虚拟机,绕过虚拟化层延迟。

    注意事项

    1. 操作风险:修改系统配置可能导致无法启动,建议提前备份重要数据。

    2. 兼容性问题:部分设备可能因驱动或IOMMU分组问题无法直通,需多次测试。

    3. 性能优化:直通后虚拟机独占硬件资源,建议根据需求合理分配。

【Docker】搭建一个高质量的在线开发工具箱 - 极速箱

2025-04-27 23:56:52

前言

本教程基于飞牛系统fnOS 0.8.47Docker Compose功能进行搭建。

简介

极速箱(Jisuxiang)是一款由ShowDoc团队开发的高颜值在线开发工具箱,专为程序员设计,旨在通过集成多种实用工具提升开发效率。

  • 功能丰富性
    极速箱覆盖了开发场景中高频使用的工具类型,包括:
    • JSON工具:格式化、验证、编辑器及与XML/CSV/YAML格式互转。

    • 编码与加密:支持Base64/URL/Unicode编解码、JWT解析,以及MD5/SHA/AES/DES等加密算法。

    • 网络工具:HTTP请求测试、IP地址查询等。

    • 时间日期工具:时间戳转换、日期计算器、时区转换等。

    • 图像工具:图片压缩、二维码生成、CSS渐变生成器等。

    • 其他工具:正则表达式测试、代码格式化、文本统计等。

  • 用户体验优化
    • 界面设计:采用深色主题(默认)与浅色主题切换,配色现代且护眼,适配长时间使用需求。

    • 高效交互:支持工具分类导航、全局搜索及收藏夹功能,快速定位常用工具。

    • 响应式布局:适配PC、移动端等多种设备,保证跨平台使用流畅性。

  • 技术实现
    • 技术栈:基于Next.js框架开发,使用TypeScript增强代码质量,结合TailwindCSS实现优雅的样式设计。

    • 本地化运行:大多数工具直接在浏览器本地执行,无需后端计算,保障数据安全与响应速度。

  • 开源与生态
    • 开源状态:项目已在GitHub(star7th/jisuxiang)开源,鼓励开发者参与贡献。

    • 部署方式:支持在线使用(jisuxiang.com)或本地部署,满足不同场景需求。

  • 应用场景
    极速箱适用于日常开发调试、API接口测试、数据格式转换、代码优化等场景,尤其适合需要快速处理碎片化任务的开发者。

部署

  1. 打开Docker管理器,选择「Compose」,点击右上角「新增项目」。

  2. 在创建项目窗口中填写「项目名称」和选择数据存放「路径」,然后选择「创建docker-compose.yml」,将下列代码根据自己实际情况修改后复制粘贴进去。

    
    version: '3.8'  # 推荐使用较新的版本
    services:
      jisuxiang:
        image: star7th/jisuxiang:latest  # 指定镜像名称和标签
        container_name: jisuxiang  # 自定义容器名称
        restart: always  # 自动重启策略
        ports:
          - "1330:3000"  # 端口映射(主机端口:容器端口)
        environment:
          TZ: Asia/Shanghai    # 设置为中国时区

    勾选「创建项目后立即启动」,最后点击「确定」。

  3. 等待构建完成,然后在浏览器中输入IP:Port​访问。