简介

要求:

  1. 有公网IP,或内网穿透

  2. 自己的网络下有可以运行linux或者docker的机器(nas,软路由,甚至能刷机的电视盒子)

  3. 苹果手机(需要用到快捷指令,安卓一些机器应该也有,普通浏览器也可以但是不够美观)

编写程序

我这里编程语言用的是python

from flask import Flask, jsonify, request
from scapy.all import ARP, Ether, srp, conf
import paramiko
from wakeonlan import send_magic_packet
import logging
import os
from dotenv import load_dotenv


# 加载环境变量
load_dotenv()

app = Flask(__name__)


# 从环境变量获取配置
mac = os.getenv("MAC")
USERNAME = os.getenv("USERNAME")
PASSWORD = os.getenv("PASSWORD")
username3 = os.getenv("username3")
password3 = os.getenv("password3")
print(f"MAC: {mac}")
print(f"USERNAME: {USERNAME}")
print(f"PASSWORD: {PASSWORD}")
print(f"username3: {username3}")
print(f"password3: {password3}")
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
#根据ip获取mac
def get_ip_by_mac(mac):
    # 配置网络接口
    conf.verb = 0  # 关闭输出
    arp_request = ARP(pdst="192.168.5.1/24")  # 替换为你的子网
    broadcast = Ether(dst="ff:ff:ff:ff:ff:ff")
    arp_request_broadcast = broadcast / arp_request

    # 发送请求并接收响应
    answered_list = srp(arp_request_broadcast, timeout=1, verbose=False)[0]

    # 查找 MAC 地址对应的 IP
    for element in answered_list:
        if element[1].hwsrc.lower() == mac.lower():
            return element[1].psrc  # 返回 IP 地址

    return None



def ssh_shutdown(ip, username, password):
    print(f"The IP address for MAC {mac} is {ip}")
    try:
        print("Creating SSH client...")
        client = paramiko.SSHClient()
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        
        print("Connecting to the host...")
        client.connect(hostname=ip, username=username, password=password)
        
        print("Executing shutdown command...")
        stdin, stdout, stderr = client.exec_command('shutdown /s /t 0')
        
        output = stdout.read().decode()
        error = stderr.read().decode()
        
        return output, error
        
    except Exception as e:
        return None, str(e)
    finally:
        client.close()

#调用电脑关机
@app.route('/shutdown', methods=['GET'])
def shutdown():

    username2 = request.args.get('username')
    password2 = request.args.get('password')
    if username2 == username3 and password2 == password3:
       logging.info("用户名和密码正确,登录成功!")
       logging.info("欢迎来到系统!")
    else:
       logging.info("密码错误")
       return jsonify({'status': 'error', 'message': '用户名或密码错误!'}), 401
       
    ip = get_ip_by_mac(mac) 
    if ip:
       print(f"The IP address for MAC {mac} is {ip}")
    else:
       print(f"No IP address found for MAC {mac}")
    # 直接在代码中设置用户名、密码和目标 IP
    

    output, error = ssh_shutdown(ip, USERNAME, PASSWORD)  
    if error:
        return jsonify({'status': 'error', 'message': error}), 500
    else:
        # 返回关机成功的提示
        return jsonify({'status': 'success', 'message': 'Shutdown command executed successfully.', 'output': output}), 200

#调用电脑开机
@app.route('/WakeOnLan', methods=['GET'])
def WakeOnLan():

    username2 = request.args.get('username')
    password2 = request.args.get('password')
    if username2 == username3 and password2 == password3:
        logging.info("用户名和密码正确,登录成功!")
        logging.info("欢迎来到系统!")
    else:
        logging.info("密码错误")
        return jsonify({'status': 'error', 'message': '用户名或密码错误!'}), 401

    output, error = wake_on_lan(mac)
    
    if error:
        return jsonify({'status': 'error', 'message': error}), 500
    else:
        # 返回开机成功的提示
        return jsonify({'status': 'success', 'message': ' successfully.', 'output': output}), 200

def wake_on_lan(mac):
    try:
        send_magic_packet(mac)
        print(f"Magic packet sent to {mac}")
        return  "Magic packet sent successfully", None
    except Exception as e:
        print(f"Failed to send magic packet: {e}")
        return None, str(e)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8086)

可以直接运行,也可以打包成docker 运行

docker打包

.env 文件参考

MAC=00:e2:69:6e:bc:53 #需要开机电脑的MAC
USERNAME=lixiansen2 #电脑用户名  
PASSWORD=46827913  #电脑密码
PORT=8086 #随意端口
username3=12345678 #url的用户名和密码,为了安全可以改为post请求,我为了能让url直接访问使用的是get
password3=12345678 #

requirements.txt 文件参考

Flask==3.1.0
scapy==2.6.1
paramiko==3.5.0
wakeonlan==3.1.0
python-dotenv==1.0.1

Dockerfile文件参考

# 构建阶段:安装依赖和编译工具
FROM python:3.9-alpine AS builder

RUN apk add --no-cache \
    libpcap-dev \
    openssl-dev \
    musl-dev \
    gcc

WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt \
    --index-url https://mirrors.aliyun.com/pypi/simple/ \
    --trusted-host mirrors.aliyun.com

# 运行阶段:仅保留必要文件
FROM python:3.9-alpine

RUN apk add --no-cache libpcap  # Scapy 运行时依赖

WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY app.py .

ENV PATH=/root/.local/bin:$PATH \
    PYTHONUNBUFFERED=1

EXPOSE 8086
CMD ["python", "app.py"] #app.py 是python程序的名字必须一致

构建镜像

docker build --no-cache -t your-image-name .

运行容器

docker run -d -p 8086:8086 --name my-flask-app your-image-name

开机命令

https://这里是IP:8086/WakeOnLan?username=这里是账户&password=这里是密码

关机命令

https://这里是IP:8086/shutdown?username=这里是账户&password=这里是密码

快捷指令

关机同理

更改完成后放在桌面上就可以快速开关机了