├── README.md ├── index.js ├── package.json └── workers └── worker.js /README.md: -------------------------------------------------------------------------------- 1 | # Auto-keep-online 2 | 3 | 此脚本是基于workers或node环境,定时访问网页,包括间断访问和不间断访问两种模式一起运行,以保证容器活跃。 4 | 5 | # 使用说明 6 | 1:workers文件夹里worker.js为cloudflared workers使用,复制整个代码到新建的workers里,修改需要访问的链接部署 7 | 8 | 2:在“设置”---”触发事件“菜单里设置访问频率,例如2分钟,保存即可,可开启日志查看是否运行,看下图 9 | ![image](https://github.com/user-attachments/assets/09e2474d-cf43-472e-a4ac-0df6504739e7) 10 | 11 | 3:可以添加环境变量 12 | ![PixPin_2024-12-19_18-16-11](https://github.com/user-attachments/assets/dbf1a840-55f5-4ed9-a662-8d3588a90431) 13 | 14 | 15 | 16 | 17 | ## 其他方式部署 18 | 1:在基于node环境下的容器或vps(需要自己安装node环境)使用。 19 | 20 | 2:上传index.js和package.json文件至运行环境根目录。 21 | 22 | 3:index.js中第9至16行中的网址是24小时不间断访问的url,多个往下继续增加,不限数量;86行为访问周期,默认2分钟,可根据需求自行设置。 23 | 24 | 4:index.js中的第23至26行为凌晨1点至6点暂停访问,其他时间正常访问的url,64行为访问周期,默认为2分钟,可根据需求自行设置。 25 | 26 | # 适用平台 27 | * 包括但不限于Glitch,Rendenr,Back4app,clever cloud,Zeabur,codesanbox,replit。。。等等,不支持物理停机的容器。 28 | * 部署在huggingface上的保活项目https://huggingface.co/spaces/rides/keep-alive 可直接复制我的space,修改index.js中的地址即可。 29 | 30 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | const moment = require('moment-timezone'); 3 | const http = require('http'); 4 | const https = require('https'); 5 | const cron = require('node-cron'); 6 | const port = process.env.SERVER_PORT || process.env.PORT || 7860; 7 | 8 | // 添加24小时访问的URL数组 9 | const urls = [ 10 | 'https://www.baidu.com', // 此处可备注名称,例如:glitch 11 | 'https://www.yahoo.com', // 此处可备注名称,例如:glitch 12 | 'https://www.baidu.com', // 此处可备注名称,例如:glitch 13 | 'https://www.yahoo.com', // 此处可备注名称,例如:glitch 14 | 'https://www.baidu.com', // 此处可备注名称,例如:glitch 15 | 'https://www.yahoo.com', // 此处可备注名称,例如:glitch 16 | 'https://www.baidu.com', // 此处可备注名称,例如:glitch 17 | 'https://www.yahoo.com', // 此处可备注名称,例如:glitch 18 | // 添加更多24小时不间断访问的URL 19 | ]; 20 | 21 | // 忽略SSL证书验证 22 | const httpsAgent = new https.Agent({ 23 | rejectUnauthorized: false 24 | }); 25 | 26 | // 添加在01:00至05:00暂停访问,其他时间正常访问的URL数组 27 | function visitWebsites() { 28 | const websites = [ 29 | 'https://www.google.com', // 此处可备注名称,例如:Back4app 30 | 'https://www.google.com', // 此处可备注名称,例如:Back4app 31 | 'https://www.google.com', // 此处可备注名称,例如:Back4app 32 | 'https://www.google.com' // 此处可备注名称,例如:Back4app,最后一个url后面没有逗号 33 | //添加更多的指定时间访问的URL 34 | ]; 35 | 36 | // 遍历网页数组并发送请求 37 | websites.forEach(async (url) => { 38 | try { 39 | const response = await axios.get(url, { httpsAgent }); 40 | console.log(`${moment().tz('Asia/Hong_Kong').format('YYYY-MM-DD HH:mm:ss')} Visited web successfully: ${url} - Status code: ${response.status}\n`); 41 | } catch (error) { 42 | console.error(`Error visiting ${url}: ${error.message}\n`); 43 | } 44 | }); 45 | } 46 | 47 | // 检查并设置定时器 48 | function checkAndSetTimer() { 49 | const currentMoment = moment().tz('Asia/Hong_Kong'); 50 | const formattedTime = currentMoment.format('YYYY-MM-DD HH:mm:ss'); 51 | if (currentMoment.hours() >= 1 && currentMoment.hours() < 5) { 52 | console.log(`Stop visit from 1:00 to 5:00 --- ${moment().tz('Asia/Hong_Kong').format('YYYY-MM-DD HH:mm:ss')}`); 53 | clearInterval(visitIntervalId); // 清除定时器 54 | const nextVisitTime = moment().tz('Asia/Hong_Kong').add(0, 'day').hours(5).minutes(0).seconds(0); 55 | const nextVisitInterval = nextVisitTime.diff(currentMoment); 56 | setTimeout(() => { 57 | startVisits(); 58 | }, nextVisitInterval); 59 | } else { 60 | startVisits(); 61 | } 62 | } 63 | 64 | let visitIntervalId; 65 | function startVisits() { 66 | clearInterval(visitIntervalId); 67 | // visitWebsites(); 68 | visitIntervalId = setInterval(() => { 69 | visitWebsites(); 70 | }, 2 * 60 * 1000); // 每2分钟执行一次访问 71 | } 72 | 73 | function runScript() { 74 | const runScriptIntervalId = setInterval(() => { 75 | //console.log('Running script'); 76 | checkAndSetTimer(); 77 | }, 2 * 60 * 1000); // 每2分钟检查一次 78 | } 79 | checkAndSetTimer(); 80 | runScript(); 81 | 82 | // 24小时不间断访问 83 | async function scrapeAndLog(url) { 84 | try { 85 | const response = await axios.get(url, { httpsAgent }); 86 | console.log(`${moment().tz('Asia/Hong_Kong').format('YYYY-MM-DD HH:mm:ss')} Web visited Successfully: ${url} - Status code: ${response.status}\n`); 87 | } catch (error) { 88 | console.error(`${moment().tz('Asia/Hong_Kong').format('YYYY-MM-DD HH:mm:ss')}: Web visited Error: ${url}: ${error.message}\n`); 89 | } 90 | } 91 | // 每2分钟访问一次 92 | cron.schedule('*/2 * * * *', () => { 93 | console.log('Running webpage access...'); 94 | urls.forEach((url) => { 95 | scrapeAndLog(url); 96 | }); 97 | }); 98 | 99 | // 创建HTTP服务 100 | const server = http.createServer((req, res) => { 101 | if (req.url === '/') { 102 | res.writeHead(200, {'Content-Type': 'text/plain'}); 103 | res.end('Hello, World!\n'); 104 | } else { 105 | res.writeHead(404, {'Content-Type': 'text/plain'}); 106 | res.end('Not Found\n'); 107 | } 108 | }); 109 | 110 | server.listen(port, () => { 111 | console.log(`Server is running on port:${port}`); 112 | }); 113 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "keep-online", 3 | "version": "2.0.0", 4 | "description": "Auto-keep-online", 5 | "main": "index.js", 6 | "author": "eoovve", 7 | "repository": "https://github.com/eoovve/Auto-keep-online", 8 | "license": "MIT", 9 | "private": false, 10 | "scripts": { 11 | "start": "node index.js" 12 | }, 13 | "dependencies": { 14 | "axios": "^1.6.2", 15 | "node-cron": "^2.0.1", 16 | "moment-timezone": "latest" 17 | }, 18 | "engines": { 19 | "node": ">=14" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /workers/worker.js: -------------------------------------------------------------------------------- 1 | // 此js为cloudflared workers使用,复制整个代码到新建的workers里,修改需要访问的链接或部署后添加环境变量 2 | // 在设置---触发事件 里设置访问频率,例如2分钟,保存即可,可开启日志查看,检查是否运行 3 | 4 | // Telegram配置(不需要可忽略) 5 | const TG_ID = ''; // 替换为你的Telegram用户chat_id 6 | const TG_TOKEN = ''; // 替换为你的Telegram Bot的token 7 | 8 | // 24小时不间断访问的URL数组,可添加环境变量,环境变量名格式:URL_1 URL_2 URL_3... 9 | const defaultUrls = [ 10 | 'https://www.google.com', 11 | 'https://www.google.com', 12 | 'https://www.google.com' // 可添加多个URL,每个URL之间用英文逗号分隔,最后一个URL后不要加逗号 13 | ]; 14 | 15 | // 指定时间段(1:00~5:00)访问的URL数组,可添加环境变量,环境变量名格式:WEBSITE_1 WEBSITE_2 WEBSITE_3... 16 | const defaultWebsites = [ 17 | 'https://www.baidu.com', 18 | 'https://www.baidu.com', 19 | 'https://www.baidu.com' // 可添加多个URL,每个URL之间用英文逗号分隔,最后一个URL后不要加逗号 20 | // ... 添加更多URL 21 | ]; 22 | 23 | // 从环境变量获取URL数组 24 | function getUrlsFromEnv(prefix) { 25 | const envUrls = []; 26 | let index = 1; 27 | while (true) { 28 | const url = globalThis[`${prefix}${index}`]; 29 | if (!url) break; 30 | envUrls.push(url); 31 | index++; 32 | } 33 | return envUrls; 34 | } 35 | 36 | // 读取默认URL和环境变量中的URL 37 | const urls = [...defaultUrls, ...getUrlsFromEnv('URL_')]; 38 | const websites = [...defaultWebsites, ...getUrlsFromEnv('WEBSITE_')]; 39 | 40 | // 检查是否在暂停时间内 (1:00-5:00) 41 | function isInPauseTime(hour) { 42 | return hour >= 1 && hour < 5; 43 | } 44 | 45 | // 发送消息到Telegram 46 | async function sendToTelegram(message) { 47 | const url = `https://api.telegram.org/bot${TG_TOKEN}/sendMessage`; 48 | const body = JSON.stringify({ 49 | chat_id: TG_ID, 50 | text: message, 51 | }); 52 | 53 | try { 54 | const response = await fetch(url, { 55 | method: 'POST', 56 | headers: { 'Content-Type': 'application/json' }, 57 | body, 58 | }); 59 | 60 | if (!response.ok) { 61 | console.error(`Telegram推送失败: ${response.statusText}`); 62 | } 63 | } catch (error) { 64 | console.error(`Telegram推送出错: ${error.message}`); 65 | } 66 | } 67 | 68 | // 生成随机IP 69 | function getRandomIP() { 70 | return `${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}`; 71 | } 72 | 73 | // 生成随机版本号 74 | function getRandomVersion() { 75 | const chromeVersion = Math.floor(Math.random() * (131 - 100 + 1)) + 100; 76 | return chromeVersion; 77 | } 78 | 79 | // 获取随机 User-Agent 80 | function getRandomUserAgent() { 81 | const agents = [ 82 | `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${getRandomVersion()}.0.0.0 Safari/537.36`, 83 | `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${getRandomVersion()}.0.0.0 Safari/537.36`, 84 | `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Edge/${getRandomVersion()}.0.0.0`, 85 | `Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1` 86 | ]; 87 | return agents[Math.floor(Math.random() * agents.length)]; 88 | } 89 | 90 | async function axiosLikeRequest(url, index, retryCount = 0) { 91 | try { 92 | // 随机延迟 1-6 秒 93 | await new Promise(resolve => setTimeout(resolve, 1000 + Math.random() * 5000)); 94 | 95 | const config = { 96 | method: 'get', 97 | headers: { 98 | 'User-Agent': getRandomUserAgent(), 99 | 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 100 | 'Accept-Language': 'en-US,en;q=0.9', 101 | 'Accept-Encoding': 'gzip, deflate, br', 102 | 'Connection': 'keep-alive', 103 | 'Cache-Control': 'no-cache', 104 | 'Pragma': 'no-cache', 105 | 'Sec-Fetch-Dest': 'document', 106 | 'Sec-Fetch-Mode': 'navigate', 107 | 'Sec-Fetch-Site': 'none', 108 | 'Sec-Fetch-User': '?1', 109 | 'Upgrade-Insecure-Requests': '1', 110 | 'X-Forwarded-For': getRandomIP(), 111 | 'X-Real-IP': getRandomIP(), 112 | 'Origin': 'https://glitch.com', 113 | 'Referer': 'https://glitch.com/' 114 | }, 115 | redirect: 'follow', 116 | timeout: 30000 117 | }; 118 | 119 | const controller = new AbortController(); 120 | const timeoutId = setTimeout(() => controller.abort(), config.timeout); 121 | 122 | const response = await fetch(url, { 123 | ...config, 124 | signal: controller.signal 125 | }); 126 | 127 | clearTimeout(timeoutId); 128 | const status = response.status; 129 | const timestamp = new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Hong_Kong' }); 130 | 131 | if (status !== 200) { 132 | // 非200状态码发送通知 133 | await sendToTelegram(`保活日志:${timestamp}\n访问失败: ${url}\n状态码: ${status}`); 134 | } 135 | 136 | return { 137 | index, 138 | url, 139 | status, 140 | success: status === 200, 141 | timestamp 142 | }; 143 | 144 | } catch (error) { 145 | if (retryCount < 2) { 146 | // 如果出错且重试次数小于2,等待后重试 147 | await new Promise(resolve => setTimeout(resolve, 10000)); 148 | return axiosLikeRequest(url, index, retryCount + 1); 149 | } 150 | const timestamp = new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Hong_Kong' }); 151 | // 发送错误通知 152 | await sendToTelegram(`保活日志:${timestamp}\n访问出错: ${url}\n错误信息: ${error.message}`); 153 | console.error(`${timestamp} 访问失败: ${url} 状态码: 500`); 154 | return { 155 | index, 156 | url, 157 | status: 500, 158 | success: false, 159 | timestamp 160 | }; 161 | } 162 | } 163 | 164 | async function handleScheduled() { 165 | const now = new Date(new Date().toLocaleString('en-US', { timeZone: 'Asia/Hong_Kong' })); 166 | const hour = now.getHours(); 167 | 168 | // 24小时访问 - 并行执行但保持顺序 169 | const results = await Promise.all(urls.map((url, index) => axiosLikeRequest(url, index))); 170 | 171 | // 按原始顺序排序并打印结果 172 | results.sort((a, b) => a.index - b.index).forEach(result => { 173 | if (result.success) { 174 | console.log(`${result.timestamp} 访问成功: ${result.url}`); 175 | } else { 176 | console.error(`${result.timestamp} 访问失败: ${result.url} 状态码: ${result.status}`); 177 | } 178 | }); 179 | 180 | // 检查是否在暂停时间 181 | if (!isInPauseTime(hour)) { 182 | const websiteResults = await Promise.all(websites.map((url, index) => axiosLikeRequest(url, index))); 183 | 184 | websiteResults.sort((a, b) => a.index - b.index).forEach(result => { 185 | if (result.success) { 186 | console.log(`${result.timestamp} 访问成功: ${result.url}`); 187 | } else { 188 | console.error(`${result.timestamp} 访问失败: ${result.url} 状态码: ${result.status}`); 189 | } 190 | }); 191 | } else { 192 | console.log(`当前处于暂停时间 1:00-5:00 --- ${now.toLocaleString()}`); 193 | } 194 | } 195 | 196 | // 处理HTTP请求 197 | async function handleRequest() { 198 | return new Response("Worker is running!", { 199 | headers: { 'content-type': 'text/plain' }, 200 | }); 201 | } 202 | 203 | // 监听请求 204 | addEventListener('fetch', event => { 205 | event.respondWith(handleRequest()); 206 | }); 207 | 208 | // 监听定时任务 209 | addEventListener('scheduled', event => { 210 | event.waitUntil(handleScheduled()); 211 | }); 212 | --------------------------------------------------------------------------------