abdulader commited on
Commit
55e1213
·
verified ·
1 Parent(s): e7aa463

Upload 5 files

Browse files
Files changed (5) hide show
  1. Dockerfile +47 -0
  2. aternos-starter.js +238 -0
  3. bot-team.js +0 -0
  4. keep-alive.yml +30 -0
  5. package.json +22 -0
Dockerfile ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ╔══════════════════════════════════════════════════════╗
2
+ # Stage 1 — Builder: تثبيت وبناء كل الـ native modules
3
+ # ╚══════════════════════════════════════════════════════╝
4
+ FROM node:20-slim AS builder
5
+
6
+ WORKDIR /app
7
+
8
+ # build tools لـ node-gyp + native modules (mineflayer, pathfinder)
9
+ RUN apt-get update && apt-get install -y --no-install-recommends \
10
+ python3 make g++ gcc \
11
+ && rm -rf /var/lib/apt/lists/*
12
+
13
+ COPY package.json ./
14
+ RUN npm install --production --no-optional
15
+
16
+ # ╔══════════════════════════════════════════════════════╗
17
+ # Stage 2 — Final: image نظيفة بدون build tools
18
+ # ╚══════════════════════════════════════════════════════╝
19
+ FROM node:20-slim
20
+
21
+ WORKDIR /app
22
+
23
+ # فقط curl و dnsutils للـ healthcheck والشبكة
24
+ RUN apt-get update && apt-get install -y --no-install-recommends \
25
+ curl dnsutils \
26
+ && rm -rf /var/lib/apt/lists/*
27
+
28
+ # node:20-slim جاي بـ user "node" بـ UID 1000 تلقائياً
29
+ RUN chown -R node:node /app
30
+
31
+ # ننقل node_modules المبنية من الـ builder بس
32
+ COPY --from=builder --chown=node:node /app/node_modules ./node_modules
33
+ COPY --chown=node:node package.json ./
34
+ COPY --chown=node:node bot-team.js ./
35
+ COPY --chown=node:node aternos-starter.js ./
36
+
37
+ USER node
38
+
39
+ EXPOSE 7860
40
+ ENV PORT=7860
41
+ ENV NODE_ENV=production
42
+ ENV NODE_OPTIONS="--max-old-space-size=450"
43
+
44
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=40s \
45
+ CMD curl -sf http://localhost:7860/healthz | grep -q '"status":"ok"' || exit 1
46
+
47
+ CMD ["node", "--max-old-space-size=450", "bot-team.js"]
aternos-starter.js ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use strict';
2
+ try { require('dotenv').config(); } catch (_) {}
3
+
4
+ /**
5
+ * ╔══════════════════════════════════════════════════════════════════════╗
6
+ * aternos-starter.js — v16.0 | تشغيل Aternos تلقائياً
7
+ * ╠══════════════════════════════════════════════════════════════════════╣
8
+ * يُستدعى من bot-team.js عند DeepSleep (ECONNREFUSED × 3)
9
+ *
10
+ * متغيرات البيئة:
11
+ * ATERNOS_USER — اسم المستخدم
12
+ * ATERNOS_PASS — كلمة المرور
13
+ * ATERNOS_SERVER — اسم السيرفر (اختياري)
14
+ *
15
+ * ملاحظة: لا يعمل في HuggingFace (لا يوجد Chrome)
16
+ * يعمل على VPS / محلياً
17
+ * ╚══════════════════════════════════════════════════════════════════════╝
18
+ */
19
+
20
+ const ATERNOS_USER = process.env.ATERNOS_USER || '';
21
+ const ATERNOS_PASS = process.env.ATERNOS_PASS || '';
22
+ const ATERNOS_SERVER = process.env.ATERNOS_SERVER || '';
23
+ const TOTAL_TIMEOUT = 9 * 60000;
24
+ const ONLINE_TIMEOUT = 6 * 60000;
25
+
26
+ const sleep = ms => new Promise(r => setTimeout(r, Math.max(0, ms)));
27
+ const randInt = (a, b) => Math.floor(Math.random() * (b - a + 1)) + a;
28
+
29
+ function log(emoji, msg) {
30
+ const ts = new Date().toISOString().replace('T', ' ').slice(0, 19);
31
+ console.log(`[${ts}] [ATERNOS] ${emoji} ${msg}`);
32
+ }
33
+
34
+ async function humanType(page, selector, text) {
35
+ await page.focus(selector);
36
+ for (const ch of text) {
37
+ await page.keyboard.type(ch, { delay: randInt(60, 180) });
38
+ if (Math.random() < 0.08) await sleep(randInt(100, 400));
39
+ }
40
+ }
41
+
42
+ async function loadPuppeteer() {
43
+ try {
44
+ const p = require('puppeteer');
45
+ log('📦', 'puppeteer محلي');
46
+ return { puppeteer: p };
47
+ } catch (_) {}
48
+ try {
49
+ const p = require('puppeteer-core');
50
+ const ch = require('@sparticuz/chromium');
51
+ log('📦', 'puppeteer-core + @sparticuz/chromium');
52
+ return {
53
+ puppeteer: p,
54
+ executablePath: await ch.executablePath(),
55
+ args: ch.args,
56
+ defaultViewport: ch.defaultViewport,
57
+ };
58
+ } catch (_) {}
59
+ return null;
60
+ }
61
+
62
+ async function getServerStatus(page) {
63
+ try {
64
+ return await page.evaluate(() => {
65
+ const sels = [
66
+ '.server-status','.status','[class*="status"]',
67
+ '#server-status','.statuslabel','.label',
68
+ ];
69
+ for (const sel of sels) {
70
+ const el = document.querySelector(sel);
71
+ if (el) {
72
+ const t = el.textContent?.trim().toLowerCase() || '';
73
+ if (t.includes('online')) return 'online';
74
+ if (t.includes('offline')) return 'offline';
75
+ if (t.includes('starting') || t.includes('loading')) return 'starting';
76
+ if (t.includes('stopping')) return 'stopping';
77
+ }
78
+ }
79
+ const btn = document.querySelector('.btn-success, [class*="start"]');
80
+ if (btn) return 'offline';
81
+ return 'unknown';
82
+ });
83
+ } catch (_) { return 'unknown'; }
84
+ }
85
+
86
+ async function clickStart(page) {
87
+ try {
88
+ const btns = [
89
+ '.btn-success','.start-btn','[class*="start"]',
90
+ 'button[class*="start"]','#start',
91
+ ];
92
+ for (const sel of btns) {
93
+ const btn = await page.$(sel);
94
+ if (btn) { await btn.click(); return true; }
95
+ }
96
+ return false;
97
+ } catch (_) { return false; }
98
+ }
99
+
100
+ async function main() {
101
+ if (!ATERNOS_USER || !ATERNOS_PASS) {
102
+ log('⚠️', 'ATERNOS_USER / ATERNOS_PASS غير مضبوطان — تخطي');
103
+ log('💡', 'أضف ATERNOS_USER و ATERNOS_PASS في Settings → Variables');
104
+ process.exit(1);
105
+ }
106
+
107
+ const lib = await loadPuppeteer();
108
+ if (!lib) {
109
+ log('❌', 'puppeteer غير متاح في هذه البيئة');
110
+ log('💡', 'في HuggingFace: شغّل السيرفر يدوياً من لوحة Aternos');
111
+ process.exit(1);
112
+ }
113
+
114
+ const { puppeteer, executablePath, args, defaultViewport } = lib;
115
+ log('🌐', `تسجيل دخول | مستخدم: ${ATERNOS_USER}`);
116
+
117
+ let browser = null;
118
+ const timer = setTimeout(() => {
119
+ log('⏱️', 'انتهت المهلة الإجمالية (9 دقائق)');
120
+ try { browser?.close(); } catch (_) {}
121
+ process.exit(2);
122
+ }, TOTAL_TIMEOUT);
123
+
124
+ try {
125
+ browser = await puppeteer.launch({
126
+ headless: 'new',
127
+ args: args || [
128
+ '--no-sandbox','--disable-setuid-sandbox','--disable-dev-shm-usage',
129
+ '--disable-gpu','--no-first-run','--no-zygote','--single-process',
130
+ '--disable-extensions','--disable-background-networking',
131
+ ],
132
+ defaultViewport: defaultViewport || { width: 1280, height: 720 },
133
+ ...(executablePath ? { executablePath } : {}),
134
+ });
135
+
136
+ const page = await browser.newPage();
137
+ await page.setUserAgent(
138
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'
139
+ );
140
+ await page.setExtraHTTPHeaders({ 'Accept-Language': 'en-US,en;q=0.9' });
141
+
142
+ log('🔗', 'الانتقال إلى aternos.org...');
143
+ await page.goto('https://aternos.org/go/', { waitUntil: 'networkidle2', timeout: 30000 });
144
+
145
+ log('🔑', 'ملء بيانات الدخول...');
146
+ await page.waitForSelector('#user', { timeout: 15000 });
147
+ await humanType(page, '#user', ATERNOS_USER);
148
+ await sleep(randInt(400, 900));
149
+ await humanType(page, '#password', ATERNOS_PASS);
150
+ await sleep(randInt(300, 700));
151
+ await page.click('.login-button, [data-id="login"], button[type="submit"]');
152
+ log('⏳', 'انتظار تسجيل الدخول...');
153
+ await page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 20000 }).catch(() => {});
154
+ await sleep(randInt(1500, 3000));
155
+
156
+ log('🔗', 'صفحة السيرفرات...');
157
+ await page.goto('https://aternos.org/servers/', { waitUntil: 'networkidle2', timeout: 20000 })
158
+ .catch(() => page.goto('https://aternos.org/panel/', { waitUntil: 'networkidle2', timeout: 20000 }).catch(() => {}));
159
+ await sleep(randInt(1000, 2500));
160
+
161
+ if (ATERNOS_SERVER) {
162
+ log('🖱️', `البحث عن: ${ATERNOS_SERVER}`);
163
+ try {
164
+ const links = await page.$$('.server-name,.server .name,[class*="server"]');
165
+ for (const link of links) {
166
+ const text = await page.evaluate(el => el.textContent?.trim(), link);
167
+ if (text?.toLowerCase().includes(ATERNOS_SERVER.toLowerCase())) {
168
+ await link.click();
169
+ log('✅', `اختيار: ${text}`);
170
+ await sleep(randInt(1500, 3000));
171
+ break;
172
+ }
173
+ }
174
+ } catch (_) {}
175
+ } else {
176
+ try {
177
+ const first = await page.$('.server,.server-card,[class*="server-item"]');
178
+ if (first) { await first.click(); await sleep(randInt(1500, 3000)); }
179
+ } catch (_) {}
180
+ }
181
+
182
+ const url = page.url();
183
+ if (!url.includes('/server/')) {
184
+ await page.goto('https://aternos.org/server/', { waitUntil: 'networkidle2', timeout: 20000 }).catch(() => {});
185
+ await sleep(randInt(1000, 2000));
186
+ }
187
+
188
+ log('🔍', 'فحص حالة السيرفر...');
189
+ let status = await getServerStatus(page);
190
+ log('📊', `الحالة: ${status}`);
191
+
192
+ if (status === 'online') {
193
+ log('✅', 'السيرفر يعمل بالفعل!');
194
+ clearTimeout(timer);
195
+ await browser.close();
196
+ process.exit(0);
197
+ }
198
+
199
+ if (status === 'offline' || status === 'unknown') {
200
+ const clicked = await clickStart(page);
201
+ log(clicked ? '✅' : '⚠️', clicked ? 'تم النقر على Start' : 'لم يُعثر على زر Start');
202
+ }
203
+
204
+ log('⏳', `انتظار Online (مهلة: ${Math.round(ONLINE_TIMEOUT / 60000)} دقائق)...`);
205
+ const start = Date.now();
206
+ let last = '';
207
+ while (Date.now() - start < ONLINE_TIMEOUT) {
208
+ await sleep(15000);
209
+ await page.reload({ waitUntil: 'networkidle2', timeout: 15000 }).catch(() => {});
210
+ await sleep(2000);
211
+ status = await getServerStatus(page);
212
+ if (status !== last) { log('📊', `الحالة: ${status}`); last = status; }
213
+ if (status === 'online') {
214
+ log('🎉', '✅ السيرفر Online! البوتات ستتصل قريباً');
215
+ clearTimeout(timer);
216
+ await browser.close();
217
+ process.exit(0);
218
+ }
219
+ if (status === 'offline') {
220
+ log('🔄', 'لا يزال Offline — محاولة Start مرة أخرى...');
221
+ await clickStart(page);
222
+ }
223
+ }
224
+
225
+ log('⏱️', 'انتهت مهلة الانتظار — السيرفر لم يبدأ');
226
+ clearTimeout(timer);
227
+ await browser.close();
228
+ process.exit(2);
229
+
230
+ } catch (err) {
231
+ log('❌', `خطأ: ${err.message}`);
232
+ clearTimeout(timer);
233
+ try { browser?.close(); } catch (_) {}
234
+ process.exit(1);
235
+ }
236
+ }
237
+
238
+ main();
bot-team.js ADDED
The diff for this file is too large to render. See raw diff
 
keep-alive.yml ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Keep HuggingFace Space Alive
2
+
3
+ on:
4
+ schedule:
5
+ # كل 25 دقيقة — يمنع HF Space من الـ pause
6
+ - cron: '*/25 * * * *'
7
+ workflow_dispatch: # تشغيل يدوي عند الحاجة
8
+
9
+ jobs:
10
+ ping:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Ping HuggingFace Space
14
+ run: |
15
+ echo "🏓 Pinging Space: ${{ secrets.HF_SPACE_URL }}"
16
+ STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
17
+ --max-time 30 \
18
+ --retry 3 \
19
+ --retry-delay 5 \
20
+ "${{ secrets.HF_SPACE_URL }}/wake")
21
+ echo "Response: $STATUS"
22
+ if [ "$STATUS" -ge 200 ] && [ "$STATUS" -lt 400 ]; then
23
+ echo "✅ Space is alive!"
24
+ else
25
+ echo "⚠️ Space returned $STATUS — may be starting up"
26
+ fi
27
+
28
+ - name: Ping healthz too
29
+ run: |
30
+ curl -s --max-time 30 "${{ secrets.HF_SPACE_URL }}/healthz" | head -c 200 || true
package.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "sayed-bot-team",
3
+ "version": "16.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "start": "node bot-team.js",
7
+ "dev": "node bot-team.js"
8
+ },
9
+ "dependencies": {
10
+ "dotenv": "^16.4.7",
11
+ "express": "^4.18.2",
12
+ "minecraft-data": "^3.81.0",
13
+ "minecraft-protocol": "^1.47.0",
14
+ "mineflayer": "^4.37.1",
15
+ "mineflayer-pathfinder": "^2.4.5"
16
+ },
17
+ "optionalDependencies": {
18
+ "@sparticuz/chromium": "^123.0.1",
19
+ "puppeteer": "^22.6.5",
20
+ "puppeteer-core": "^22.6.5"
21
+ }
22
+ }