├── README.md ├── Serv00-Renew.sh ├── alist_freebsd_update.py ├── Auto_connect_SSH-TG.py ├── Auto_connect_SSH-PushPlus.py └── Auto_connect_SSH-WeCom.py /README.md: -------------------------------------------------------------------------------- 1 | # Serv00 - 自动续期脚本,Fork自bg8ixz/bg8ixz/Serv00_auto_script 2 | 3 | 4 | ## 当前脚本 5 | | 脚本名称 | 用途 |备注 | 6 | | :------------: | :------------: | :------------: | 7 | |Auto_connect_SSH.py|自动SSH登录以续期|| 8 | |alist_freebsd_update.py|自动从uubulb/alist-freebsd拉取最新版本更新|| 9 | |Auto_connect_SSH-WeCom.py|企业微信推送方式|需要企业微信机器人Webhook Key 10 | |Auto_connect_SSH-TG.py|TG推送方式|需要BOT_TOKEN和CHAT_ID 11 | |Auto_connect_SSH-PushPLus.py|推送方式|需要PushPlus Token 12 | 13 | # 自动续期脚本说明 14 | ## 说明 15 |   bg8ixz/Serv00_auto_script基于[LINUX DO](https://linux.do)论坛佬友`maohai`的[脚本](https://linux.do/t/topic/66483/15)进行修改,在其基础上增加了`发送通知`、以及`运行日志`功能。本项目基于bg8ixz/Serv00_auto_script的脚本增加tg和pushplus推送方式,并通过sh脚本设置变量 16 | 17 | 18 | ## 发送说明 19 | | 主机状态/设置条件 | 说明 |发送内容 |执行动作 | 20 | | :------------: | :------------: | :------------: | :------------: | 21 | | 200 | 正常 |不发送|无| 22 | | 502| 机子凉了 |服务不可用|通过SSH执行PM2命令| 23 | | [Errno 1] Address family for hostname not supported| 解析凉了 |解析失败|无| 24 | | 自定义日期 | 固定日启动连接 |每月固定SSH连接提醒|连接SSH| 25 | 26 | ## 使用方法 27 |    28 | 29 |   进入domains目录并执行一键脚本: 30 | ```shell 31 | cd domains 32 | curl -O https://raw.githubusercontent.com/curry-he/Serv00_auto_script/master/Serv00-Renew.sh && chmod +x Serv00-Renew.sh && ./Serv00-Renew.sh 33 | 34 | ``` 35 | #### 参考设置图 36 | ![参考图片](https://cdn.linux.do/uploads/default/optimized/3X/f/6/f6516994395858a19637f5acf5baeecec96ea3fa_2_690x445.png) 37 | 38 | # Alist自动更新脚本说明 39 |   将脚本放到`domains`目录下你的 `Alist` 存储路径中,使用`chmod +x alist_freebsd_update.py`给`alist_freebsd_update.py`添加可执行权限。 40 |   然后自己到控制台创建一个定时任务。 41 | #### 运行效果图 42 | ![没有Alist文件](https://cdn.linux.do/uploads/default/original/3X/1/f/1f5b378d086d1935cfaf3927c9fc6c33d531eeb7.jpeg) 43 | ![已经是最新版](https://cdn.linux.do/uploads/default/original/3X/e/7/e72105ffe5f1ee572cca2ded4138472241553bdb.jpeg) 44 | ![正常更新](https://cdn.linux.do/uploads/default/original/3X/f/5/f58f94d755825005eae30df9dce0ad1f0b661f43.jpeg) 45 | 46 | ## 参考来源 47 | | 名称 |来源|地址| 48 | | :------------: | :------------: | :------------: | 49 | |Saika|Github|https://github.com/k0baya| 50 | |Eric Lee|Github|https://github.com/giturass| 51 | |maohai|LINUX DO|https://linux.do/t/topic/66483/15| 52 | -------------------------------------------------------------------------------- /Serv00-Renew.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 函数:获取用户输入并验证不为空 4 | get_input() { 5 | local prompt="$1" 6 | local input="" 7 | while [ -z "$input" ]; do 8 | read -p "$prompt" input 9 | if [ -z "$input" ]; then 10 | echo "输入不能为空,请重新输入。" 11 | fi 12 | done 13 | echo "$input" 14 | } 15 | 16 | # 函数:下载 Python 文件 17 | download_python_file() { 18 | local url="$1" 19 | local filename="$2" 20 | if command -v curl &> /dev/null; then 21 | curl -L "$url" -o "$filename" 22 | elif command -v wget &> /dev/null; then 23 | wget -O "$filename" "$url" 24 | else 25 | echo "错误:未找到 curl 或 wget。请安装其中之一以继续。" 26 | exit 1 27 | fi 28 | 29 | if [ $? -eq 0 ]; then 30 | echo "成功下载 $filename" 31 | else 32 | echo "下载 $filename 失败" 33 | exit 1 34 | fi 35 | } 36 | 37 | # 函数:安装 Python 文件所需的模块 38 | install_required_modules() { 39 | local filename="$1" 40 | echo "正在分析 $filename 所需的模块..." 41 | 42 | modules=$(grep -E "^import |^from " "$filename" | sed -E 's/^import //; s/^from ([^ ]+).*/\1/' | sort -u) 43 | 44 | for module in $modules; do 45 | if ! python3 -c "import $module" 2>/dev/null; then 46 | echo "正在安装模块: $module" 47 | pip3 install "$module" 48 | fi 49 | done 50 | } 51 | 52 | # 设置环境变量 53 | export HOSTNAME=$(hostname) 54 | export USERNAME=$(whoami) 55 | export URL="https://${USERNAME}.serv00.net" 56 | export SSH_PASSWORD=$(get_input "请输入SSH密码: ") 57 | 58 | echo "当前主机名是: $HOSTNAME" 59 | echo "构建的 URL 是: $URL" 60 | 61 | # 初始化变量 62 | use_wecom=false 63 | use_tg=false 64 | use_pushplus=false 65 | 66 | # 添加明确的提示 67 | echo "===================================" 68 | echo " 请选择推送方式" 69 | echo "===================================" 70 | echo "本脚本将帮助您设置自动推送通知。" 71 | echo "您可以选择以下一种或多种推送方式:" 72 | echo "" 73 | echo "1. 企业微信(需要企业微信机器人 KEY)" 74 | echo "2. Telegram(需要BOT TOKEN和CHAT ID)" 75 | echo "3. PushPlus(需要PushPlus Token)" 76 | echo "" 77 | echo "请仔细阅读以下说明并做出选择。" 78 | echo "===================================" 79 | 80 | # 询问用户选择推送方式 81 | read -p "请输入选项编号(可多选,用空格分隔): " choices 82 | 83 | # 解析用户选择并下载相应的 Python 文件 84 | for choice in $choices; do 85 | case $choice in 86 | 1) 87 | use_wecom=true 88 | download_python_file "https://raw.githubusercontent.com/curry-he/Serv00_auto_script/master/Auto_connect_SSH-WeCom.py" "Auto_connect_SSH-WeCom.py" 89 | install_required_modules "Auto_connect_SSH-WeCom.py" 90 | ;; 91 | 2) 92 | use_tg=true 93 | download_python_file "https://raw.githubusercontent.com/curry-he/Serv00_auto_script/master/Auto_connect_SSH-TG.py" "Auto_connect_SSH-TG.py" 94 | install_required_modules "Auto_connect_SSH-TG.py" 95 | ;; 96 | 3) 97 | use_pushplus=true 98 | download_python_file "https://raw.githubusercontent.com/curry-he/Serv00_auto_script/master/Auto_connect_SSH-PushPlus.py" "Auto_connect_SSH-PushPlus.py" 99 | install_required_modules "Auto_connect_SSH-PushPlus.py" 100 | ;; 101 | *) echo "无效选项: $choice" ;; 102 | esac 103 | done 104 | 105 | # 根据选择获取配置信息并设置为环境变量 106 | if $use_wecom; then 107 | export WECHAT_ROBOT_KEY=$(get_input "请输入企业微信机器人 KEY: ") 108 | fi 109 | 110 | if $use_tg; then 111 | export BOT_TOKEN=$(get_input "请输入 Telegram BOT TOKEN: ") 112 | export CHAT_ID=$(get_input "请输入 Telegram CHAT ID: ") 113 | fi 114 | 115 | if $use_pushplus; then 116 | export PUSHPLUS_TOKEN=$(get_input "请输入 PushPlus Token: ") 117 | fi 118 | 119 | # 执行对应的 Python 脚本 120 | if $use_wecom; then 121 | python3 Auto_connect_SSH-WeCom.py 122 | fi 123 | 124 | if $use_tg; then 125 | python3 Auto_connect_SSH-TG.py 126 | fi 127 | 128 | if $use_pushplus; then 129 | python3 Auto_connect_SSH-PushPlus.py 130 | fi 131 | 132 | echo "所有选定的推送任务已完成。" 133 | -------------------------------------------------------------------------------- /alist_freebsd_update.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | ''' 3 | 说明:脚本文件请放置于Alist FreeBSD程序目录下,并命名为alist_freebsd_auto_update.py,在终端内使用chmod +x alist_freebsd_auto_update.py赋予执行权限,然后执行./alist_freebsd_auto_update.py 4 | 测试方法:在Alist FreeBSD程序目录下执行命令 python3 alist_freebsd_auto_update.py或./alist_freebsd_auto_update.py 5 | ''' 6 | import requests 7 | import datetime 8 | import subprocess 9 | import os 10 | import re 11 | 12 | # GitHub API URL 用于获取最新版本 13 | API_URL = "https://api.github.com/repos/uubulb/alist-freebsd/releases/latest" 14 | 15 | # 从 API 获取最新版本的数据 16 | try: 17 | response = requests.get(API_URL) 18 | response.raise_for_status() # 如果请求返回了一个错误状态码,将抛出异常 19 | except requests.exceptions.RequestException as e: 20 | print(f"从 GitHub 获取最新版本信息出错: {e}") 21 | exit(1) 22 | release_data = response.json() 23 | 24 | # 检查 alist 文件是否存在 25 | if not os.path.exists('alist'): 26 | print("Alist FreeBSD 程序未找到,正在准备下载...") 27 | else: 28 | # 执行 ./alist version 命令并获取输出 29 | try: 30 | version_output = subprocess.check_output(['./alist', 'version'], text=True).strip() 31 | except subprocess.CalledProcessError as e: 32 | print("警告:执行查询Alist版本号命令出错:", e) 33 | exit(1) 34 | 35 | # 解析获取到的版本号 36 | version_pattern = r"Version: v(\d+\.\d+\.\d+)-\d+-g[a-fA-F0-9]+" 37 | match = re.search(version_pattern, version_output) 38 | if match: 39 | get_version = match.group(1) 40 | current_version = f"v{get_version}" 41 | print(f"当前 Alist 版本: {current_version}") 42 | else: 43 | print("未能从输出中找到正确的版本号。") 44 | exit(1) 45 | 46 | # 从获取到的json数据中找到版本号 47 | alist_freebsd_version = release_data.get('name', None) 48 | if not alist_freebsd_version: 49 | print("没有找到 Alist FreeBSD 版本号。") 50 | exit(1) 51 | print(f"最新 Alist 版本: {alist_freebsd_version}") # 打印最新版本号(测试用) 52 | 53 | # 比较版本号 54 | if current_version == alist_freebsd_version: # 如果前面没做v字符,则需要使用alist_freebsd_version.lstrip('v')移除获取到的GitHub上版本号的'v' 55 | print(f"当前 Alist FreeBSD 已经是最新版本,不需要更新!\n=============================================\n【Alist FreeBSD 信息如下】\n\n{version_output}\n=============================================\n") 56 | exit(0) 57 | else: 58 | print(f"发现新版本 {alist_freebsd_version},当前版本为 {current_version},正在执行更新...\n") 59 | 60 | current_datetime = datetime.datetime.now().strftime('%Y%m%d%H%M%S') # 获取当前日期和时间并格式化为 YYYYMMDDHHMMSS 61 | new_name = f"{current_datetime}_{current_version}_alist_bak" # 重命名现有文件,格式为 YYYYMMDDHHMMSS_alist_bak 62 | if os.path.exists(new_name): 63 | print(f"文件 '{new_name}' 已存在,尝试生成一个新的文件名...") # 如果重命名后的文件名已存在,再次循环直到找到一个不存在的文件名 64 | new_name = datetime.datetime.now().strftime('%Y%m%d%H%M%S') + f"_{current_version}_alist_bak" 65 | os.rename('alist', new_name) 66 | print(f"温馨提醒:'alist' 文件已存在,以防万一已重命名为 '{new_name}'。\n") 67 | 68 | # 查找名为 'alist' 的文件的下载链接 69 | for asset in release_data['assets']: 70 | if 'alist' in asset['name'].lower(): 71 | DOWNLOAD_URL = asset['browser_download_url'] 72 | #DOWNLOAD_URL = DOWNLOAD_URL.replace("https://github.com", "https://download.nuaa.cf") # 本地测试加速用,Serv00可能会文件下载不全 73 | break 74 | else: 75 | print("在发布中未找到名为 'alist' 的文件。") 76 | exit(1) 77 | 78 | # 开始下载文件 79 | print(f"正在从 {DOWNLOAD_URL} 下载最新 FreeBSD 版 Alist!\n下载中,请稍后...\n") 80 | response = requests.get(DOWNLOAD_URL, stream=True) 81 | with open('alist', 'wb') as f: 82 | for chunk in response.iter_content(chunk_size=32768): # 从 HTTP 响应中读取数据块(chunks)的大小,8192(8KB)、32768(32KB)、65536(64KB),自己换! 83 | f.write(chunk) 84 | 85 | # 赋予 alist 文件可执行权限 86 | os.chmod('alist', 0o755) 87 | 88 | # 检查是否存在 config.json 文件 89 | if os.path.exists('./data/config.json'): 90 | new_version_output = subprocess.check_output(['./alist', 'version'], text=True).strip() 91 | print(f"---------------------------------------------\n温馨提示:Alist-FreeBSD 最新版本已经下载完成!\n---------------------------------------------\n【Alist FreeBSD 最新信息如下】\n{new_version_output}\n---------------------------------------------\n") 92 | # 启动 alist 服务 93 | try: 94 | print("启动 Alist 服务...") 95 | result = subprocess.run(['./alist', 'server'], text=True, capture_output=True) 96 | stdout = result.stdout.strip() 97 | stderr = result.stderr.strip() 98 | print(result.stderr) # 打印启动服务的输出 99 | except subprocess.CalledProcessError as e: 100 | exit(1) 101 | # 使用 pm2 restart alist 102 | try: 103 | print("使用 pm2 重启 Alist 服务...") 104 | subprocess.run(['pm2', 'restart', 'alist'], check=True, text=True) 105 | except subprocess.CalledProcessError as e: 106 | exit(1) 107 | print("Alist 服务已成功启动并重启。") 108 | else: 109 | subprocess.Popen(['./alist', 'server'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) # 启动 alist 服务 110 | print("已下载最新版本的 Alist-FreeBSD ,并生成 config.json 文件。") 111 | print("配置文件 config.json 已成功生成,路径:./data/config.json ,请自行修改端口!") 112 | print("使用命令 cd 命令进入 data 路径下") 113 | print("再使用文本编辑器编辑 config.json 文件,修改 port 字段为你放行的端口!") 114 | print("例如,使用 vim: vim config.json") 115 | print("修改完成后,使用命令 cd .. 回到上级目录") -------------------------------------------------------------------------------- /Auto_connect_SSH-TG.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import requests 4 | import paramiko 5 | import socket 6 | from datetime import datetime 7 | import pytz 8 | from telegram import Bot 9 | import asyncio 10 | 11 | # # Telegram Bot 设置 12 | # BOT_TOKEN = "YOUR_BOT_TOKEN" 13 | # CHAT_ID = "YOUR_CHAT_ID" 14 | 15 | # # 预先定义的常量 16 | # url = '你检测的地址,参考下一行注释' 17 | # # 测试URL 这个URL是个凉了的 url = 'https://edwgiz.serv00.net/' 18 | # ssh_info = { 19 | # 'host': 's3.serv00.com', # 主机地址 20 | # 'port': 22, 21 | # 'username': '你的用户名', # 你的用户名,别写错了 22 | # 'password': '你的SSH密码' # 你注册的时候收到的密码或者你自己改了的密码 23 | # } 24 | 25 | # 脚本获取的常量 26 | url = os.environ.get('URL') 27 | 28 | hostname = os.environ.get('HOSTNAME') 29 | ssh_password = os.environ.get('SSH_PASSWORD') 30 | username = os.environ.get('USERNAME') 31 | 32 | ssh_info = { 33 | 'host': hostname, # 主机地址 34 | 'port': 22, 35 | 'username': username, # 你的用户名,别写错了 36 | 'password': ssh_password # 你注册的时候收到的密码或者你自己改了的密码 37 | } 38 | 39 | # Telegram Bot 设置 40 | BOT_TOKEN = os.environ.get('BOT_TOKEN') 41 | CHAT_ID = os.environ.get('CHAT_ID') 42 | 43 | # 获取当前脚本文件的绝对路径 44 | script_dir = os.path.dirname(os.path.abspath(__file__)) 45 | 46 | # 日志文件将保存在脚本所在的目录中 47 | log_file_path = os.path.join(script_dir, 'Auto_connect_SSH.log') 48 | tg_message_sent = False 49 | flush_log_message = [] 50 | 51 | # 写入日志的函数 52 | def write_log(log_message): 53 | global flush_log_message 54 | if not os.path.exists(log_file_path): 55 | open(log_file_path, 'a').close() 56 | os.chmod(log_file_path, 0o644) 57 | log_info = f"{log_message}" 58 | flush_log_message.append(log_info) 59 | 60 | # 把所有的日志信息写入日志文件 61 | def flush_log(): 62 | global flush_log_message 63 | username = ssh_info['username'] 64 | system_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') 65 | beijing_time = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%Y-%m-%d %H:%M:%S') 66 | current_day = datetime.now(pytz.timezone('Asia/Shanghai')).weekday() 67 | weekdays = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"] 68 | current_weekday_name = weekdays[current_day] 69 | flush_log_messages = f"{system_time} - {beijing_time} - {current_weekday_name} - {url} - {username} - {' - '.join(flush_log_message)}" 70 | with open(log_file_path, "a", encoding="utf-8") as log_file: 71 | log_file.write(flush_log_messages + '\n') 72 | flush_log_message.clear() 73 | 74 | # 发送Telegram消息的异步函数 75 | async def send_telegram_message_async(message): 76 | global tg_message_sent 77 | bot = Bot(token=BOT_TOKEN) 78 | try: 79 | await bot.send_message(chat_id=CHAT_ID, text=message) 80 | tg_status = "Telegram提醒消息发送成功" 81 | print("温馨提醒:Telegram提醒消息发送成功。") 82 | except Exception as e: 83 | tg_status = f"Telegram提醒消息发送失败,错误码: {e}" 84 | print(f"警告:Telegram提醒消息发送失败!\n错误码: {e}") 85 | finally: 86 | if not tg_message_sent: 87 | write_log(f"{tg_status}") 88 | tg_message_sent = True 89 | 90 | # 发送Telegram消息的同步包装函数 91 | def send_telegram_message(message): 92 | asyncio.run(send_telegram_message_async(message)) 93 | 94 | # 尝试通过SSH恢复PM2进程的函数 95 | def restore_pm2_processes(): 96 | try: 97 | ssh = paramiko.SSHClient() 98 | ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 99 | ssh.connect(**ssh_info) 100 | 101 | stdin, stdout, stderr = ssh.exec_command('pm2 resurrect') 102 | output = stdout.read().decode() 103 | error = stderr.read().decode() 104 | 105 | if error: 106 | write_log(f"PM2恢复失败: {error}") 107 | print(f"PM2恢复失败: {error}") 108 | else: 109 | write_log(f"PM2恢复成功: {output}") 110 | print(f"PM2恢复成功: {output}") 111 | 112 | ssh.close() 113 | except Exception as e: 114 | write_log(f"SSH连接失败: {str(e)}") 115 | print(f"SSH连接失败: {str(e)}") 116 | 117 | # 尝试通过SSH连接的函数 118 | def ssh_connect(): 119 | try: 120 | ssh = paramiko.SSHClient() 121 | ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 122 | ssh.connect(**ssh_info) 123 | 124 | stdin, stdout, stderr = ssh.exec_command('ls -l') 125 | output = stdout.read().decode() 126 | 127 | write_log("SSH连接成功") 128 | print("SSH连接成功") 129 | 130 | ssh.close() 131 | except Exception as e: 132 | write_log(f"SSH连接失败: {str(e)}") 133 | print(f"SSH连接失败: {str(e)}") 134 | 135 | # 检查是否为每月的1号 136 | def is_first_day_of_month(): 137 | return datetime.now().day == 1 138 | 139 | # 返回当前的天、月和一年中的第几天 140 | def get_day_info(): 141 | now = datetime.now() 142 | return now.day, now.month, now.timetuple().tm_yday, ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"][now.weekday()] 143 | 144 | # 每个月发送仅包含URL和时间的提醒消息 145 | def send_monthly_reminder(): 146 | current_day, current_month, current_year_day, current_weekday_name = get_day_info() 147 | system_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') 148 | beijing_time = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%Y-%m-%d %H:%M:%S') 149 | message = f"🎉每月固定SSH提醒🎉\n-------------------------------------\n检测地址:\n{url}\n-------------------------------------\n  今天是{current_month}月{current_day}日( {current_weekday_name} ),本月的第 {current_day} 天,今年的第 {current_year_day} 天,例行SSH连接已经成功执行,以防万一空了可以到后台查看记录!\n-------------------------------------\n系统时间: {system_time}\n北京时间: {beijing_time}" 150 | return message 151 | 152 | if __name__ == '__main__': 153 | # 每月一次检查提醒 154 | if is_first_day_of_month(): 155 | message = send_monthly_reminder() 156 | send_telegram_message(message) 157 | ssh_connect() 158 | 159 | # 检查URL状态和DNS 160 | try: 161 | # 尝试解析URL的域名 162 | host = socket.gethostbyname(url.split('/')[2]) 163 | print(f"解析成功,IP地址为: {host}") 164 | write_log(f"{host}") 165 | 166 | # 尝试获取URL的状态码 167 | response = requests.get(url, timeout=10) 168 | status_code = response.status_code 169 | if status_code != 200: 170 | # URL状态码不是200,发送通知并尝试恢复PM2进程 171 | system_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') 172 | beijing_time = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%Y-%m-%d %H:%M:%S') 173 | message = f"💥 当前服务不可用 💥\n地址: {url}\n状态码: {status_code}\n💪 正在尝试通过SSH恢复PM2进程,请稍后手动检查恢复情况!\n-------------------------------------\n系统时间: {system_time}\n北京时间: {beijing_time}" 174 | write_log(f"主机状态码: {status_code}") 175 | send_telegram_message(message) 176 | restore_pm2_processes() 177 | else: 178 | write_log(f"主机状态码: {status_code}") 179 | print(f"主机状态码: {status_code}") 180 | 181 | except socket.gaierror as e: 182 | # 解析失败,发送通知 183 | write_log(f"Error: {e}") 184 | system_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') 185 | beijing_time = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%Y-%m-%d %H:%M:%S') 186 | message = f"💣 解析失败提醒 💣\n地址: {url}\n错误: {e}\n😱 抓紧尝试检查解析配置或联系管事的老铁。\n-------------------------------------\n系统时间: {system_time}\n北京时间: {beijing_time}" 187 | send_telegram_message(message) 188 | host = "解析失败" 189 | status_code = "N/A" 190 | 191 | # 添加这些行 192 | system_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') 193 | beijing_time = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%Y-%m-%d %H:%M:%S') 194 | status_message = f"脚本执行完毕\n检测地址: {url}\n解析IP: {host}\n状态码: {status_code}\n系统时间: {system_time}\n北京时间: {beijing_time}" 195 | send_telegram_message(status_message) 196 | 197 | # 所有日志信息已经收集完成,写入日志文件 198 | flush_log() 199 | -------------------------------------------------------------------------------- /Auto_connect_SSH-PushPlus.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import requests 4 | import paramiko 5 | import socket 6 | from datetime import datetime 7 | import pytz 8 | 9 | # # PushPlus 设置 10 | # PUSHPLUS_TOKEN = "" # 在pushplus公众号-功能-个人中心-开发设置里获取Token 11 | 12 | # # 预先定义的常量 13 | # url = '你检测的地址,参考下一行注释' 14 | # # 测试URL 这个URL是个凉了的 url = 'https://edwgiz.serv00.net/' 15 | # ssh_info = { 16 | # 'host': 's3.serv00.com', # 主机地址 17 | # 'port': 22, 18 | # 'username': '你的用户名', # 你的用户名,别写错了 19 | # 'password': '你的SSH密码' # 你注册的时候收到的密码或者你自己改了的密码 20 | # } 21 | 22 | # 脚本获取的常量 23 | url = os.environ.get('URL') 24 | 25 | hostname = os.environ.get('HOSTNAME') 26 | ssh_password = os.environ.get('SSH_PASSWORD') 27 | username = os.environ.get('USERNAME') 28 | 29 | ssh_info = { 30 | 'host': hostname, # 主机地址 31 | 'port': 22, 32 | 'username': username, # 你的用户名,别写错了 33 | 'password': ssh_password # 你注册的时候收到的密码或者你自己改了的密码 34 | } 35 | 36 | 37 | # PushPlus 设置 38 | PUSHPLUS_TOKEN = os.environ.get('PUSHPLUS_TOKEN') # 在pushplus公众号-功能-个人中心-开发设置里获取Token 39 | 40 | 41 | # 获取当前脚本文件的绝对路径 42 | script_dir = os.path.dirname(os.path.abspath(__file__)) 43 | 44 | # 日志文件将保存在脚本所在的目录中 45 | log_file_path = os.path.join(script_dir, 'Auto_connect_SSH.log') 46 | pushplus_message_sent = False 47 | flush_log_message = [] 48 | 49 | # 写入日志的函数 50 | def write_log(log_message): 51 | global flush_log_message 52 | if not os.path.exists(log_file_path): 53 | open(log_file_path, 'a').close() 54 | os.chmod(log_file_path, 0o644) 55 | log_info = f"{log_message}" 56 | flush_log_message.append(log_info) 57 | 58 | # 把所有的日志信息写入日志文件 59 | def flush_log(): 60 | global flush_log_message 61 | username = ssh_info['username'] 62 | system_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') 63 | beijing_time = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%Y-%m-%d %H:%M:%S') 64 | current_day = datetime.now(pytz.timezone('Asia/Shanghai')).weekday() 65 | weekdays = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"] 66 | current_weekday_name = weekdays[current_day] 67 | flush_log_messages = f"{system_time} - {beijing_time} - {current_weekday_name} - {url} - {username} - {' - '.join(flush_log_message)}" 68 | with open(log_file_path, "a", encoding="utf-8") as log_file: 69 | log_file.write(flush_log_messages + '\n') 70 | flush_log_message.clear() 71 | 72 | # 发送PushPlus消息的函数 73 | def send_pushplus_message(title, content): 74 | global pushplus_message_sent 75 | pushplus_url = "http://www.pushplus.plus/send" 76 | data = { 77 | "token": PUSHPLUS_TOKEN, 78 | "title": title, 79 | "content": content 80 | } 81 | try: 82 | response = requests.post(pushplus_url, json=data) 83 | if response.status_code == 200: 84 | pushplus_status = "PushPlus提醒消息发送成功" 85 | print("温馨提醒:PushPlus提醒消息发送成功。") 86 | else: 87 | pushplus_status = f"PushPlus提醒消息发送失败,状态码: {response.status_code}" 88 | print(f"警告:PushPlus提醒消息发送失败!状态码: {response.status_code}") 89 | except Exception as e: 90 | pushplus_status = f"PushPlus提醒消息发送失败,错误: {str(e)}" 91 | print(f"警告:PushPlus提醒消息发送失败!错误: {str(e)}") 92 | 93 | if not pushplus_message_sent: 94 | write_log(pushplus_status) 95 | pushplus_message_sent = True 96 | 97 | # 尝试通过SSH恢复PM2进程的函数 98 | def restore_pm2_processes(): 99 | try: 100 | ssh = paramiko.SSHClient() 101 | ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 102 | ssh.connect(**ssh_info) 103 | 104 | stdin, stdout, stderr = ssh.exec_command('pm2 resurrect') 105 | output = stdout.read().decode() 106 | error = stderr.read().decode() 107 | 108 | if error: 109 | write_log(f"PM2恢复失败: {error}") 110 | print(f"PM2恢复失败: {error}") 111 | else: 112 | write_log(f"PM2恢复成功: {output}") 113 | print(f"PM2恢复成功: {output}") 114 | 115 | ssh.close() 116 | except Exception as e: 117 | write_log(f"SSH连接失败: {str(e)}") 118 | print(f"SSH连接失败: {str(e)}") 119 | 120 | # 尝试通过SSH连接的函数 121 | def ssh_connect(): 122 | try: 123 | ssh = paramiko.SSHClient() 124 | ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 125 | ssh.connect(**ssh_info) 126 | 127 | stdin, stdout, stderr = ssh.exec_command('ls -l') 128 | output = stdout.read().decode() 129 | 130 | write_log("SSH连接成功") 131 | print("SSH连接成功") 132 | 133 | ssh.close() 134 | except Exception as e: 135 | write_log(f"SSH连接失败: {str(e)}") 136 | print(f"SSH连接失败: {str(e)}") 137 | 138 | # 检查是否为每月的1号 139 | def is_first_day_of_month(): 140 | return datetime.now().day == 1 141 | 142 | # 返回当前的天、月和一年中的第几天 143 | def get_day_info(): 144 | now = datetime.now() 145 | return now.day, now.month, now.timetuple().tm_yday, ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"][now.weekday()] 146 | 147 | # 每个月发送仅包含URL和时间的提醒消息 148 | def send_monthly_reminder(): 149 | current_day, current_month, current_year_day, current_weekday_name = get_day_info() 150 | system_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') 151 | beijing_time = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%Y-%m-%d %H:%M:%S') 152 | title = "🎉每月固定SSH提醒🎉" 153 | content = f""" 154 | 检测地址: 155 | {url} 156 | ------------------------------------- 157 |   今天是{current_month}月{current_day}日( {current_weekday_name} ),本月的第 {current_day} 天,今年的第 {current_year_day} 天,例行SSH连接已经成功执行,以防万一空了可以到后台查看记录! 158 | ------------------------------------- 159 | 系统时间: {system_time} 160 | 北京时间: {beijing_time} 161 | """ 162 | return title, content 163 | 164 | if __name__ == '__main__': 165 | # 每月一次检查提醒 166 | if is_first_day_of_month(): 167 | title, content = send_monthly_reminder() 168 | send_pushplus_message(title, content) 169 | ssh_connect() 170 | 171 | # 检查URL状态和DNS 172 | try: 173 | # 尝试解析URL的域名 174 | host = socket.gethostbyname(url.split('/')[2]) 175 | print(f"解析成功,IP地址为: {host}") 176 | write_log(f"{host}") 177 | 178 | # 尝试获取URL的状态码 179 | response = requests.get(url, timeout=10) 180 | status_code = response.status_code 181 | if status_code != 200: 182 | # URL状态码不是200,发送通知并尝试恢复PM2进程 183 | system_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') 184 | beijing_time = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%Y-%m-%d %H:%M:%S') 185 | title = "💥 当前服务不可用 💥" 186 | content = f""" 187 | 地址: {url} 188 | 状态码: {status_code} 189 | 💪 正在尝试通过SSH恢复PM2进程,请稍后手动检查恢复情况! 190 | ------------------------------------- 191 | 系统时间: {system_time} 192 | 北京时间: {beijing_time} 193 | """ 194 | write_log(f"主机状态码: {status_code}") 195 | send_pushplus_message(title, content) 196 | restore_pm2_processes() 197 | else: 198 | write_log(f"主机状态码: {status_code}") 199 | print(f"主机状态码: {status_code}") 200 | 201 | except socket.gaierror as e: 202 | # 解析失败,发送通知 203 | write_log(f"Error: {e}") 204 | system_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') 205 | beijing_time = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%Y-%m-%d %H:%M:%S') 206 | title = "💣 解析失败提醒 💣" 207 | content = f""" 208 | 地址: {url} 209 | 错误: {e} 210 | 😱 抓紧尝试检查解析配置或联系管事的老铁。 211 | ------------------------------------- 212 | 系统时间: {system_time} 213 | 北京时间: {beijing_time} 214 | """ 215 | send_pushplus_message(title, content) 216 | host = "解析失败" 217 | status_code = "N/A" 218 | 219 | # 添加这些行 220 | system_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') 221 | beijing_time = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%Y-%m-%d %H:%M:%S') 222 | title = "脚本执行完毕" 223 | content = f""" 224 | 检测地址: {url} 225 | 解析IP: {host} 226 | 状态码: {status_code} 227 | 系统时间: {system_time} 228 | 北京时间: {beijing_time} 229 | """ 230 | send_pushplus_message(title, content) 231 | 232 | # 所有日志信息已经收集完成,写入日志文件 233 | flush_log() 234 | -------------------------------------------------------------------------------- /Auto_connect_SSH-WeCom.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import requests 4 | import paramiko 5 | import socket 6 | from datetime import datetime 7 | import pytz 8 | 9 | # # 预先定义的常量 10 | # url = '你检测的地址,参考下一行注释' 11 | # # 测试URL 这个URL是个凉了的 url = 'https://edwgiz.serv00.net/' 12 | # ssh_info = { 13 | # 'host': 's3.serv00.com', # 主机地址 14 | # 'port': 22, 15 | # 'username': '你的用户名', # 你的用户名,别写错了 16 | # 'password': '你的SSH密码' # 你注册的时候收到的密码或者你自己改了的密码 17 | # } 18 | 19 | # WECHAT_ROBOT_KEY = '你的企业微信机器人的Key部分' # 需要替换成你的企业微信机器人的Webhook Key,参考 https://open.work.weixin.qq.com/help2/pc/14931 20 | 21 | 22 | # 脚本获取的常量 23 | url = os.environ.get('URL') 24 | 25 | hostname = os.environ.get('HOSTNAME') 26 | ssh_password = os.environ.get('SSH_PASSWORD') 27 | username = os.environ.get('USERNAME') 28 | 29 | ssh_info = { 30 | 'host': hostname, # 主机地址 31 | 'port': 22, 32 | 'username': username, # 你的用户名,别写错了 33 | 'password': ssh_password # 你注册的时候收到的密码或者你自己改了的密码 34 | } 35 | 36 | wechat_robot_key = os.environ.get('WECHAT_ROBOT_KEY') 37 | 38 | webhook_url = f'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={WECHAT_ROBOT_KEY}' # 企业微信机器人的Webhook地址 39 | 40 | # 获取当前脚本文件的绝对路径 41 | script_dir = os.path.dirname(os.path.abspath(__file__)) 42 | 43 | # 日志文件将保存在脚本所在的目录中 44 | log_file_path = os.path.join(script_dir, 'Auto_connect_SSH.log') 45 | wechat_message_sent = False # 标记是否已经发送了成功的企业微信提醒消息 46 | flush_log_message = [] # 用于存储日志信息的全局变量 47 | # 写入日志的函数 48 | def write_log(log_message): 49 | global flush_log_message 50 | if not os.path.exists(log_file_path): 51 | open(log_file_path, 'a').close() # 创建日志文件 52 | os.chmod(log_file_path, 0o644) # 设置#日志文件有可编辑权限(644权限) 53 | log_info = f"{log_message}" 54 | flush_log_message.append(log_info) 55 | 56 | # 把所有的日志信息写入日志文件 57 | def flush_log(): 58 | global flush_log_message 59 | # 获取当前系统时间、北京时间、星期以及 SSH 用户名,然后将所有信息合并为一行写到日志里面 60 | username = ssh_info['username'] 61 | system_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') 62 | beijing_time = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%Y-%m-%d %H:%M:%S') 63 | current_day = datetime.now(pytz.timezone('Asia/Shanghai')).weekday() 64 | weekdays = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"] 65 | current_weekday_name = weekdays[current_day] 66 | flush_log_messages = f"{system_time} - {beijing_time} - {current_weekday_name} - {url} - {username} - {' - '.join(flush_log_message)}" 67 | # 写入日志文件,并添加换行符 68 | with open(log_file_path, "a", encoding="utf-8") as log_file: 69 | log_file.write(flush_log_messages + '\n') 70 | # 清空累积的日志信息列表 71 | flush_log_message.clear() # 清空累积的消息,避免下一次会有重复消息 72 | 73 | # 发送企业微信消息的函数 74 | def send_wechat_message(message): 75 | global wechat_message_sent # 声明为全局变量 76 | headers = {'Content-Type': 'application/json'} 77 | try: 78 | response_wechat = requests.post(webhook_url, json=message, headers=headers) 79 | response_wechat.raise_for_status() 80 | wechat_status = "企业微信提醒消息发送成功" 81 | print("温馨提醒:企业微信提醒消息发送成功。") 82 | except requests.RequestException as e: 83 | wechat_status = f"企业微信提醒消息发送失败,错误码: {e}" 84 | print(f"警告:企业微信提醒消息发送失败!\n错误码: {e}") 85 | finally: 86 | # 测试的时候发现如果碰到又是规定的定期执行时间还有碰巧主机凉了会写两遍日志,改为只有在消息尚未发送的情况下才记录日志 87 | if not wechat_message_sent: 88 | write_log(f"{wechat_status}") 89 | wechat_message_sent = True # 消息发送后,将标记设置为 True 90 | 91 | # 尝试通过SSH恢复PM2进程的函数 92 | def restore_pm2_processes(): 93 | transport = paramiko.Transport((ssh_info['host'], ssh_info['port'])) 94 | try: 95 | transport.connect(username=ssh_info['username'], password=ssh_info['password']) 96 | # 创建SSH通道 97 | ssh = paramiko.SSHClient() 98 | ssh._transport = transport 99 | try: # 执行pm2 resurrect命令 100 | stdin, stdout, stderr = ssh.exec_command('/home/你的用户名/.npm-global/bin/pm2 resurrect') 101 | print("STDOUT: ", stdout.read().decode()) 102 | print("STDERR: ", stderr.read().decode()) 103 | stdout.channel.recv_exit_status() # 等待命令执行完成 104 | if stdout.channel.exit_status == 0: 105 | write_log("通过SSH执行PM2命令成功") 106 | print("温馨提醒:PM2进程恢复成功。") 107 | else: 108 | write_log(f"通过SSH执行PM2命令时出错,错误信息:{stderr.read().decode()}") 109 | print("警告:PM2进程恢复失败!\n错误信息:", stderr.read().decode()) 110 | except Exception as e: 111 | write_log(f"通过SSH执行PM2命令时出错: {e}") 112 | print(f"通过SSH执行命令时出错: {e}") 113 | finally: 114 | ssh.close() # 关闭SSHClient 115 | transport.close() # 关闭Transport连接 116 | 117 | # 尝试通过SSH连接的函数 118 | def ssh_connect(): 119 | try: 120 | transport = paramiko.Transport((ssh_info['host'], ssh_info['port'])) 121 | transport.connect(username=ssh_info['username'], password=ssh_info['password']) 122 | ssh_status = "SSH连接成功" 123 | print("SSH连接成功。") 124 | except Exception as e: 125 | ssh_status = f"SSH连接失败,错误信息: {e}" 126 | print(f"SSH连接失败: {e}") 127 | finally: 128 | transport.close() 129 | write_log(f"{ssh_status}") 130 | 131 | # 检查是否为每月的1号 132 | def is_first_day_of_month(): 133 | now = datetime.now(pytz.timezone('Asia/Shanghai')) # 机子时间是UTC时间,为了便于识别这里需要使用东八区北京时间 134 | print("本来应该是系统时间,但是我要改成北京时间增强辨识度:",now) 135 | current_day = now.day # 获取当前的天数 136 | return current_day == 1 or current_day == 15 # 设置每个月的哪一天或哪几天为每月固定SSH日期(如果只想每个月第一天就只需要保留return current_day == 1即可) 137 | 138 | # 返回当前的天、月和一年中的第几天 139 | def get_day_info(): 140 | now = datetime.now(pytz.timezone('Asia/Shanghai')) # 使用东八区北京时间 141 | print("北京时间:",now) 142 | current_day = now.day 143 | current_month = now.month 144 | current_year_day = now.timetuple().tm_yday # 今年中的第几天 145 | current_weekday = now.weekday() # 返回一个 0-6 的整数,0 是星期一,6 是星期日 146 | weekdays = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"] 147 | current_weekday_name = weekdays[current_weekday] 148 | return current_day, current_month, current_year_day, current_weekday_name 149 | 150 | # 每个月发送仅包含URL和时间的提醒消息 151 | def send_monthly_reminder(): 152 | current_day, current_month, current_year_day, current_weekday_name = get_day_info() 153 | system_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') 154 | beijing_time = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%Y-%m-%d %H:%M:%S') 155 | message = { 156 | "msgtype": "text", 157 | "text": { 158 | "content": f" [鼓掌]每月固定SSH提醒[鼓掌] \n-------------------------------------\n检测地址:\n{url}\n-------------------------------------\n  今天是{current_month}月{current_day}日( {current_weekday_name} ),本月的第 {current_day} 天,今年的第 {current_year_day} 天,例行SSH连接已经成功执行,以防万一空了可以到后台查看记录!\n-------------------------------------\n系统时间: {system_time}\n北京时间: {beijing_time}" 159 | } 160 | } 161 | return message 162 | 163 | # 每月一次检查提醒 164 | if is_first_day_of_month(): 165 | message = send_monthly_reminder() 166 | send_wechat_message(message) 167 | ssh_connect() 168 | 169 | # 检查URL状态和DNS的函数 170 | def check_url_status_and_dns(): 171 | try: 172 | # 尝试解析URL的域名 173 | host = socket.gethostbyname(url.split('/')[2]) 174 | print(f"解析成功,IP地址为: {host}") 175 | write_log(f"{host}") 176 | except socket.gaierror as e: 177 | # 解析失败,发送通知 178 | write_log(f"Error: {e}") 179 | system_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') 180 | beijing_time = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%Y-%m-%d %H:%M:%S') 181 | message = { 182 | "msgtype": "text", 183 | "text": { 184 | "content": f"----- [炸弹]解析失败提醒[炸弹] -----\n地址: {url}\n错误: {e}\n[恐惧]抓紧尝试检查解析配置或联系管事的老铁。\n-------------------------------------\n系统时间: {system_time}\n北京时间: {beijing_time}" 185 | } 186 | } 187 | send_wechat_message(message) 188 | return 189 | 190 | # 尝试获取URL的状态码 191 | response = requests.get(url, timeout=10) 192 | if response.status_code != 200: 193 | # URL状态码不是200,发送通知并尝试恢复PM2进程 194 | system_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') 195 | beijing_time = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%Y-%m-%d %H:%M:%S') 196 | message = { 197 | "msgtype": "text", 198 | "text": { 199 | "content": f"----- [裂开]当前服务不可用[裂开] -----\n地址: {url}\n状态码: {response.status_code}\n[加油]正在尝试通过SSH恢复PM2进程,请稍后手动检查恢复情况!\n-------------------------------------\n系统时间: {system_time}\n北京时间: {beijing_time}" 200 | } 201 | } 202 | write_log(f"主机状态码: {response.status_code}") 203 | send_wechat_message(message) 204 | restore_pm2_processes() 205 | else: 206 | write_log(f"主机状态码: {response.status_code}") 207 | print(f"主机状态码: {response.status_code}") 208 | 209 | if __name__ == '__main__': 210 | # 检查URL状态和DNS 211 | check_url_status_and_dns() 212 | # 所有日志信息已经收集完成,写入日志文件 213 | flush_log() 214 | --------------------------------------------------------------------------------