mc838 commited on
Commit
01e5ee4
·
verified ·
1 Parent(s): 4b98b7d

Upload 4 files

Browse files
Files changed (4) hide show
  1. Dockerfile +63 -0
  2. login.js +161 -0
  3. package.json +14 -0
  4. server.js +116 -0
Dockerfile ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:20-slim
2
+
3
+ WORKDIR /app
4
+
5
+ RUN apt-get update && apt-get install -y \
6
+ wget \
7
+ gnupg \
8
+ ca-certificates \
9
+ libgconf-2-4 \
10
+ libxss1 \
11
+ libappindicator1 \
12
+ libasound2 \
13
+ libatk1.0-0 \
14
+ libc6 \
15
+ libcairo2 \
16
+ libcups2 \
17
+ libdbus-1-3 \
18
+ libexpat1 \
19
+ libfontconfig1 \
20
+ libgcc1 \
21
+ libgdk-pixbuf2.0-0 \
22
+ libglib2.0-0 \
23
+ libgtk-3-0 \
24
+ libnspr4 \
25
+ libpango-1.0-0 \
26
+ libpangocairo-1.0-0 \
27
+ libstdc++6 \
28
+ libx11-6 \
29
+ libx11-xcb1 \
30
+ libxcb1 \
31
+ libxcomposite1 \
32
+ libxcursor1 \
33
+ libxdamage1 \
34
+ libxext6 \
35
+ libxfixes3 \
36
+ libxi6 \
37
+ libxrandr2 \
38
+ libxrender1 \
39
+ libxss1 \
40
+ libxtst6 \
41
+ lsb-release \
42
+ xdg-utils \
43
+ && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o /usr/share/keyrings/google-chrome.gpg \
44
+ && echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome.gpg] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list \
45
+ && apt-get update \
46
+ && apt-get install -y google-chrome-stable \
47
+ && rm -rf /var/lib/apt/lists/*
48
+
49
+ # 创建 logs 目录并更改其权限
50
+ RUN mkdir -p /app/logs && chown -R node:node /app
51
+
52
+ # 切换到非 root 用户
53
+ USER node
54
+
55
+ COPY --chown=node:node package*.json ./
56
+
57
+ RUN npm install
58
+
59
+ COPY --chown=node:node . .
60
+
61
+ EXPOSE 8080
62
+
63
+ CMD ["node", "server.js"]
login.js ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const puppeteer = require('puppeteer');
2
+ const axios = require('axios');
3
+
4
+ // Telegram 配置
5
+ let telegramConfig;
6
+ try {
7
+ telegramConfig = JSON.parse(process.env.TELEGRAM_JSON || '{}');
8
+ } catch (error) {
9
+ console.error('Error parsing TELEGRAM_JSON:', error);
10
+ }
11
+
12
+ function formatToISO(date) {
13
+ return date.toISOString().replace('T', ' ').replace('Z', '').replace(/\.\d{3}Z/, '');
14
+ }
15
+
16
+ async function delayTime(ms) {
17
+ return new Promise(resolve => setTimeout(resolve, ms));
18
+ }
19
+
20
+ async function sendTelegramMessage(message) {
21
+ if (!telegramConfig || !telegramConfig.cloudflareWorkerUrl || !telegramConfig.telegramBotToken || !telegramConfig.telegramBotUserId || !telegramConfig.customAuthKey) {
22
+ console.log('Telegram configuration not set or incomplete, skipping notification');
23
+ return;
24
+ }
25
+
26
+ console.log('Attempting to send Telegram message...');
27
+ const url = `${telegramConfig.cloudflareWorkerUrl}/${telegramConfig.telegramBotToken}/sendMessage`;
28
+ try {
29
+ const response = await axios.post(url, {
30
+ chat_id: telegramConfig.telegramBotUserId,
31
+ text: message
32
+ }, {
33
+ headers: {
34
+ 'X-Custom-Auth': telegramConfig.customAuthKey
35
+ }
36
+ });
37
+ console.log('Telegram notification sent successfully');
38
+ } catch (error) {
39
+ console.error('Error sending Telegram notification:', error.response ? error.response.data : error.message);
40
+ }
41
+ }
42
+
43
+ async function loginAccount(account, browser) {
44
+ const { username, password, panelnum, type } = account;
45
+ const page = await browser.newPage();
46
+
47
+ let url = type === 'ct8'
48
+ ? 'https://panel.ct8.pl/login/?next=/'
49
+ : `https://panel${panelnum}.serv00.com/login/?next=/`;
50
+
51
+ try {
52
+ // 设置自定义 headers 来绕过 Cloudflare
53
+ await page.setExtraHTTPHeaders({
54
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
55
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
56
+ 'Accept-Language': 'en-US,en;q=0.5',
57
+ 'Accept-Encoding': 'gzip, deflate, br',
58
+ 'Connection': 'keep-alive',
59
+ 'Upgrade-Insecure-Requests': '1',
60
+ 'Sec-Fetch-Dest': 'document',
61
+ 'Sec-Fetch-Mode': 'navigate',
62
+ 'Sec-Fetch-Site': 'none',
63
+ 'Sec-Fetch-User': '?1',
64
+ 'Cache-Control': 'max-age=0',
65
+ });
66
+
67
+ await page.goto(url, { waitUntil: 'networkidle0' });
68
+
69
+ const usernameInput = await page.$('#id_username');
70
+ if (usernameInput) {
71
+ await usernameInput.click({ clickCount: 3 });
72
+ await usernameInput.press('Backspace');
73
+ }
74
+
75
+ await page.type('#id_username', username);
76
+ await page.type('#id_password', password);
77
+
78
+ const loginButton = await page.$('#submit');
79
+ if (loginButton) {
80
+ await loginButton.click();
81
+ } else {
82
+ throw new Error('无法找到登录按钮');
83
+ }
84
+
85
+ await page.waitForNavigation({ timeout: 30000 });
86
+
87
+ const isLoggedIn = await page.evaluate(() => {
88
+ const logoutButton = document.querySelector('a[href="/logout/"]');
89
+ return logoutButton !== null;
90
+ });
91
+
92
+ const nowUtc = formatToISO(new Date());
93
+ const nowBeijing = formatToISO(new Date(new Date().getTime() + 8 * 60 * 60 * 1000));
94
+
95
+ if (isLoggedIn) {
96
+ const message = `账号 ${username} (${type}) 于北京时间 ${nowBeijing}(UTC时间 ${nowUtc})登录成功!`;
97
+ console.log(message);
98
+ await sendTelegramMessage(`${type}, [${nowBeijing.split(' ')[1].split('.')[0]}]\n${message}`);
99
+ return { success: true, message };
100
+ } else {
101
+ const message = `账号 ${username} (${type}) 登录失败,请检查账号和密码是否正确。`;
102
+ console.error(message);
103
+ await sendTelegramMessage(`${type}, [${nowBeijing.split(' ')[1].split('.')[0]}]\n${message}`);
104
+ return { success: false, message };
105
+ }
106
+ } catch (error) {
107
+ const message = `账号 ${username} (${type}) 登录时出现错误: ${error}`;
108
+ console.error(message);
109
+ await sendTelegramMessage(`${type}, [${formatToISO(new Date()).split(' ')[1].split('.')[0]}]\n${message}`);
110
+ return { success: false, message };
111
+ } finally {
112
+ await page.close();
113
+ }
114
+ }
115
+
116
+ (async () => {
117
+ const accountsJson = process.env.ACCOUNTS_JSON;
118
+ const accounts = JSON.parse(accountsJson);
119
+
120
+ const browser = await puppeteer.launch({
121
+ headless: true,
122
+ args: ['--no-sandbox', '--disable-setuid-sandbox'],
123
+ executablePath: '/usr/bin/google-chrome-stable'
124
+ });
125
+
126
+ const results = [];
127
+ for (const account of accounts) {
128
+ const result = await loginAccount(account, browser);
129
+ results.push({ ...account, ...result });
130
+
131
+ const delay = Math.floor(Math.random() * 8000) + 1000;
132
+ await delayTime(delay);
133
+ }
134
+
135
+ await browser.close();
136
+
137
+ const successfulLogins = results.filter(r => r.success);
138
+ const failedLogins = results.filter(r => !r.success);
139
+
140
+ // 生成表格形式的输出
141
+ console.log('\n登录结果汇总:');
142
+ console.log('| 账号 | 类型 | 状态 | 消息 |');
143
+ console.log('|------|------|------|------|');
144
+ results.forEach(({ username, type, success, message }) => {
145
+ console.log(`| ${username} | ${type} | ${success ? '成功' : '失败'} | ${message} |`);
146
+ });
147
+
148
+ let summaryMessage = '\n登录结果统计:\n';
149
+ summaryMessage += `成功登录的账号:${successfulLogins.length}\n`;
150
+ summaryMessage += `登录失败的账号:${failedLogins.length}\n`;
151
+
152
+ if (failedLogins.length > 0) {
153
+ summaryMessage += '\n登录失败的账号列表:\n';
154
+ failedLogins.forEach(({ username, type }) => {
155
+ summaryMessage += `- ${username} (${type})\n`;
156
+ });
157
+ }
158
+
159
+ console.log(summaryMessage);
160
+ await sendTelegramMessage(`${accounts[0].type}, [${formatToISO(new Date()).split(' ')[1].split('.')[0]}]\n${summaryMessage}`);
161
+ })();
package.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "account-keep-alive",
3
+ "version": "1.0.0",
4
+ "description": "Auto-login account keep-alive script",
5
+ "main": "server.js",
6
+ "dependencies": {
7
+ "puppeteer": "^10.0.0",
8
+ "express": "^4.17.1",
9
+ "axios": "^0.21.1"
10
+ },
11
+ "scripts": {
12
+ "start": "node server.js"
13
+ }
14
+ }
server.js ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const express = require('express');
2
+ const { exec } = require('child_process');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const app = express();
6
+ const port = process.env.PORT || 8080;
7
+
8
+ // 使用 HOME 环境变量来创建日志目录
9
+ const logDir = path.join(process.env.HOME, 'logs');
10
+ if (!fs.existsSync(logDir)) {
11
+ fs.mkdirSync(logDir, { recursive: true });
12
+ }
13
+
14
+ let lastRunTime = null;
15
+ let nextRunTime = null;
16
+ let lastRunResults = null;
17
+
18
+ function log(message) {
19
+ const timestamp = new Date().toISOString();
20
+ const logMessage = `${timestamp}: ${message}\n`;
21
+ console.log(logMessage);
22
+ fs.appendFileSync(path.join(logDir, 'app.log'), logMessage);
23
+ }
24
+
25
+ app.get('/', (req, res) => {
26
+ let tableHtml = '';
27
+ let summaryHtml = '';
28
+
29
+ if (lastRunResults) {
30
+ tableHtml = '<h2>上次登录结果:</h2>';
31
+ tableHtml += '<table border="1"><tr><th>账号</th><th>类型</th><th>状态</th><th>消息</th></tr>';
32
+ lastRunResults.forEach(result => {
33
+ tableHtml += `<tr><td>${result.username}</td><td>${result.type}</td><td>${result.success ? '成功' : '失败'}</td><td>${result.message}</td></tr>`;
34
+ });
35
+ tableHtml += '</table>';
36
+
37
+ const successfulLogins = lastRunResults.filter(r => r.success);
38
+ const failedLogins = lastRunResults.filter(r => !r.success);
39
+
40
+ summaryHtml = '<h2>登录结果统计:</h2>';
41
+ summaryHtml += `<p>成功登录的账号:${successfulLogins.length}</p>`;
42
+ summaryHtml += `<p>登录失败的账号:${failedLogins.length}</p>`;
43
+
44
+ if (failedLogins.length > 0) {
45
+ summaryHtml += '<h3>登录失败的账号列表:</h3><ul>';
46
+ failedLogins.forEach(({ username, type }) => {
47
+ summaryHtml += `<li>${username} (${type})</li>`;
48
+ });
49
+ summaryHtml += '</ul>';
50
+ }
51
+ }
52
+
53
+ res.send(`
54
+ <h1>登录脚本状态</h1>
55
+ <p>上次执行时间:${lastRunTime || '尚未执行'}</p>
56
+ <p>下次执行时间:${nextRunTime || '未设置'}</p>
57
+ <a href="/run">立即执行脚本</a>
58
+ <br><br>
59
+ <a href="/logs">查看日志</a>
60
+ ${tableHtml}
61
+ ${summaryHtml}
62
+ `);
63
+ });
64
+
65
+ app.get('/logs', (req, res) => {
66
+ const logPath = path.join(logDir, 'app.log');
67
+ fs.readFile(logPath, 'utf8', (err, data) => {
68
+ if (err) {
69
+ res.status(500).send('无法读取日志文件');
70
+ return;
71
+ }
72
+ res.send(`<pre>${data}</pre>`);
73
+ });
74
+ });
75
+
76
+ function runLoginScript() {
77
+ log('开始执行登录脚本');
78
+ exec('node login.js', (error, stdout, stderr) => {
79
+ if (error) {
80
+ log(`执行错误: ${error.message}`);
81
+ return;
82
+ }
83
+ if (stderr) {
84
+ log(`脚本错误输出: ${stderr}`);
85
+ return;
86
+ }
87
+ log(`脚本输出:\n${stdout}`);
88
+
89
+ // 解析输出以获取结果
90
+ const lines = stdout.split('\n');
91
+ const resultStartIndex = lines.findIndex(line => line.includes('| 账号 | 类型 | 状态 | 消息 |'));
92
+ if (resultStartIndex !== -1) {
93
+ lastRunResults = lines.slice(resultStartIndex + 2)
94
+ .filter(line => line.trim().startsWith('|'))
95
+ .map(line => {
96
+ const [, username, type, status, message] = line.split('|').map(item => item.trim());
97
+ return { username, type, success: status === '成功', message };
98
+ });
99
+ }
100
+ });
101
+ lastRunTime = new Date().toISOString();
102
+ nextRunTime = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString();
103
+ }
104
+
105
+ app.get('/run', (req, res) => {
106
+ runLoginScript();
107
+ res.send('脚本执行已启动,请稍后刷新页面查看结果。');
108
+ });
109
+
110
+ // 每7天自动运行一次脚本
111
+ setInterval(runLoginScript, 7 * 24 * 60 * 60 * 1000);
112
+
113
+ app.listen(port, () => {
114
+ log(`服务器运行在 http://localhost:${port}`);
115
+ runLoginScript(); // 启动时立即运行一次
116
+ });