noho_bot 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +30 -0
- package/noho_bot.js +808 -0
- package/noho_bot.js. +549 -0
- package/noho_bot.js.. +549 -0
- package/noho_bot.py +812 -0
- package/package.json +30 -0
- package/setup.py +37 -0
package/noho_bot.js
ADDED
|
@@ -0,0 +1,808 @@
|
|
|
1
|
+
// noho_bot.js
|
|
2
|
+
// NOHO Bot Library - Universal Bot Builder
|
|
3
|
+
// Created by: nohojs (Omar) - NOHO Company
|
|
4
|
+
|
|
5
|
+
const { default: makeWASocket, DisconnectReason, useMultiFileAuthState } = require('@whiskeysockets/baileys');
|
|
6
|
+
const { Telegraf } = require('telegraf');
|
|
7
|
+
const { Client: DiscordClient, GatewayIntentBits } = require('discord.js');
|
|
8
|
+
const { WebClient } = require('@slack/web-api');
|
|
9
|
+
const axios = require('axios');
|
|
10
|
+
const qrcode = require('qrcode-terminal');
|
|
11
|
+
const pino = require('pino');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const crypto = require('crypto');
|
|
15
|
+
const { spawn } = require('child_process');
|
|
16
|
+
|
|
17
|
+
// ==========================================
|
|
18
|
+
// UTILS
|
|
19
|
+
// ==========================================
|
|
20
|
+
const colors = {
|
|
21
|
+
reset: '\x1b[0m', bright: '\x1b[1m', green: '\x1b[32m',
|
|
22
|
+
yellow: '\x1b[33m', red: '\x1b[31m', cyan: '\x1b[36m',
|
|
23
|
+
magenta: '\x1b[35m', blue: '\x1b[34m'
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function log(type, msg) {
|
|
27
|
+
const icons = {
|
|
28
|
+
info: 'ℹ️', success: '✅', error: '❌', warning: '⚠️',
|
|
29
|
+
bot: '🤖', lock: '🔒', daemon: '👻', whatsapp: '📱',
|
|
30
|
+
telegram: '✈️', discord: '🎮', slack: '💬', facebook: '📘'
|
|
31
|
+
};
|
|
32
|
+
console.log(`${icons[type] || '•'} ${msg}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ==========================================
|
|
36
|
+
// DAEMON MANAGER (24/7)
|
|
37
|
+
// ==========================================
|
|
38
|
+
class DaemonManager {
|
|
39
|
+
constructor() {
|
|
40
|
+
this.lockDir = path.join(process.cwd(), '.noho_daemon');
|
|
41
|
+
this.lockFile = path.join(this.lockDir, 'daemon.lock');
|
|
42
|
+
this.pidFile = path.join(this.lockDir, 'daemon.pid');
|
|
43
|
+
this.logFile = path.join(this.lockDir, 'daemon.log');
|
|
44
|
+
this.sessionsFile = path.join(this.lockDir, 'sessions.json');
|
|
45
|
+
this.ensureDir();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
ensureDir() {
|
|
49
|
+
if (!fs.existsSync(this.lockDir)) {
|
|
50
|
+
fs.mkdirSync(this.lockDir, { recursive: true });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
start(sessionConfig) {
|
|
55
|
+
if (this.isRunning()) {
|
|
56
|
+
log('warning', 'الديمون يعمل بالفعل!');
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.saveSession(sessionConfig);
|
|
61
|
+
const botScript = this.generateScript(sessionConfig);
|
|
62
|
+
const botFile = path.join(this.lockDir, 'running_bot.js');
|
|
63
|
+
fs.writeFileSync(botFile, botScript);
|
|
64
|
+
|
|
65
|
+
const out = fs.openSync(this.logFile, 'a');
|
|
66
|
+
const err = fs.openSync(this.logFile, 'a');
|
|
67
|
+
|
|
68
|
+
const child = spawn('node', [botFile], {
|
|
69
|
+
detached: true,
|
|
70
|
+
stdio: ['ignore', out, err]
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
child.unref();
|
|
74
|
+
|
|
75
|
+
fs.writeFileSync(this.pidFile, child.pid.toString());
|
|
76
|
+
fs.writeFileSync(this.lockFile, JSON.stringify({
|
|
77
|
+
pid: child.pid,
|
|
78
|
+
startedAt: new Date().toISOString(),
|
|
79
|
+
platform: sessionConfig.platform,
|
|
80
|
+
sessionName: sessionConfig.name
|
|
81
|
+
}));
|
|
82
|
+
|
|
83
|
+
log('daemon', `تم التشغيل (PID: ${child.pid})`);
|
|
84
|
+
log('success', 'البوت يعمل 24/7!');
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
stop() {
|
|
89
|
+
if (!this.isRunning()) {
|
|
90
|
+
log('warning', 'لا يوجد ديمون يعمل');
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const pid = parseInt(fs.readFileSync(this.pidFile, 'utf8'));
|
|
95
|
+
try {
|
|
96
|
+
process.kill(pid, 'SIGTERM');
|
|
97
|
+
log('success', `تم الإيقاف (PID: ${pid})`);
|
|
98
|
+
} catch (e) {
|
|
99
|
+
log('error', 'خطأ في الإيقاف');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (fs.existsSync(this.lockFile)) fs.unlinkSync(this.lockFile);
|
|
103
|
+
if (fs.existsSync(this.pidFile)) fs.unlinkSync(this.pidFile);
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
isRunning() {
|
|
108
|
+
if (!fs.existsSync(this.lockFile) || !fs.existsSync(this.pidFile)) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const pid = parseInt(fs.readFileSync(this.pidFile, 'utf8'));
|
|
113
|
+
process.kill(pid, 0);
|
|
114
|
+
return true;
|
|
115
|
+
} catch (e) {
|
|
116
|
+
if (fs.existsSync(this.lockFile)) fs.unlinkSync(this.lockFile);
|
|
117
|
+
if (fs.existsSync(this.pidFile)) fs.unlinkSync(this.pidFile);
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
logs(tail = 20) {
|
|
123
|
+
if (!fs.existsSync(this.logFile)) {
|
|
124
|
+
log('error', 'لا توجد سجلات');
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const logs = fs.readFileSync(this.logFile, 'utf8').split('\n').filter(Boolean);
|
|
128
|
+
console.log(logs.slice(-tail).join('\n'));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
saveSession(config) {
|
|
132
|
+
fs.writeFileSync(this.sessionsFile, JSON.stringify(config, null, 2));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
generateScript(config) {
|
|
136
|
+
return `
|
|
137
|
+
const noho = require('${path.join(__dirname, 'noho_bot.js').replace(/\\/g, '\\\\')}');
|
|
138
|
+
const bot = new noho();
|
|
139
|
+
|
|
140
|
+
async function start() {
|
|
141
|
+
const session = await bot.create('${config.platform}', {
|
|
142
|
+
sessionPath: '${config.sessionPath}',
|
|
143
|
+
${config.token ? `token: '${config.token}',` : ''}
|
|
144
|
+
${config.appId ? `appId: '${config.appId}',` : ''}
|
|
145
|
+
${config.appSecret ? `appSecret: '${config.appSecret}',` : ''}
|
|
146
|
+
autoReply: true
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
await session.connect(${config.method ? `'${config.method}'` : ''}${config.phoneNumber ? `, '${config.phoneNumber}'` : ''});
|
|
150
|
+
|
|
151
|
+
session.onMessage(async (msg, from, platform) => {
|
|
152
|
+
console.log('[DAEMON]', platform, from, msg);
|
|
153
|
+
if (msg === '.ping') {
|
|
154
|
+
await session.sendMessage(from, 'pong! (24/7 mode)');
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
start().catch(err => {
|
|
160
|
+
console.error('Daemon error:', err);
|
|
161
|
+
setTimeout(start, 10000);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
process.on('SIGTERM', () => process.exit(0));
|
|
165
|
+
`;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ==========================================
|
|
170
|
+
// BASE ADAPTER
|
|
171
|
+
// ==========================================
|
|
172
|
+
class BaseAdapter {
|
|
173
|
+
constructor(name, config = {}) {
|
|
174
|
+
this.name = name;
|
|
175
|
+
this.config = config;
|
|
176
|
+
this.connected = false;
|
|
177
|
+
this.authData = null;
|
|
178
|
+
this.messageHandler = null;
|
|
179
|
+
this.sock = null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
onMessage(handler) {
|
|
183
|
+
this.messageHandler = handler;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async sendMessage(to, text) {
|
|
187
|
+
throw new Error('Not implemented');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
getFingerprint() {
|
|
191
|
+
return {
|
|
192
|
+
platform: this.name,
|
|
193
|
+
connected: this.connected,
|
|
194
|
+
user: this.authData?.user || null
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ==========================================
|
|
200
|
+
// WHATSAPP ADAPTER
|
|
201
|
+
// ==========================================
|
|
202
|
+
class WhatsAppAdapter extends BaseAdapter {
|
|
203
|
+
constructor(config = {}) {
|
|
204
|
+
super('whatsapp', {
|
|
205
|
+
sessionPath: config.sessionPath || './.noho_sessions/whatsapp',
|
|
206
|
+
printQR: config.printQR !== false,
|
|
207
|
+
...config
|
|
208
|
+
});
|
|
209
|
+
this.qrCode = null;
|
|
210
|
+
this.pairingCode = null;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async connect(method = 'qr', phoneNumber = null) {
|
|
214
|
+
const { state, saveCreds } = await useMultiFileAuthState(this.config.sessionPath);
|
|
215
|
+
|
|
216
|
+
this.sock = makeWASocket({
|
|
217
|
+
logger: pino({ level: 'silent' }),
|
|
218
|
+
printQRInTerminal: false,
|
|
219
|
+
auth: state,
|
|
220
|
+
browser: ['NOHO Bot', 'Chrome', '1.0']
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
return new Promise((resolve, reject) => {
|
|
224
|
+
const timeout = setTimeout(() => reject(new Error('timeout')), 120000);
|
|
225
|
+
|
|
226
|
+
this.sock.ev.on('connection.update', async (update) => {
|
|
227
|
+
const { connection, lastDisconnect, qr } = update;
|
|
228
|
+
|
|
229
|
+
if (qr && method === 'qr' && this.config.printQR) {
|
|
230
|
+
this.qrCode = qr;
|
|
231
|
+
console.log('\n┌─────────────────────────────┐');
|
|
232
|
+
console.log('│ امسح QR Code بالكاميرا │');
|
|
233
|
+
console.log('└─────────────────────────────┘\n');
|
|
234
|
+
qrcode.generate(qr, { small: true });
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (update.isNewLogin && method === 'pairing' && phoneNumber) {
|
|
238
|
+
try {
|
|
239
|
+
this.pairingCode = await this.sock.requestPairingCode(phoneNumber);
|
|
240
|
+
console.log(`\nالكود: ${this.pairingCode}`);
|
|
241
|
+
} catch (err) {
|
|
242
|
+
console.error('خطأ:', err.message);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (connection === 'open') {
|
|
247
|
+
clearTimeout(timeout);
|
|
248
|
+
this.connected = true;
|
|
249
|
+
this.authData = {
|
|
250
|
+
id: this.sock.user.id,
|
|
251
|
+
user: {
|
|
252
|
+
name: this.sock.user.name,
|
|
253
|
+
phone: this.sock.user.id.split(':')[0]
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
log('whatsapp', `متصل: ${this.authData.user.name}`);
|
|
257
|
+
resolve(this.getFingerprint());
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (connection === 'close') {
|
|
261
|
+
this.connected = false;
|
|
262
|
+
const shouldReconnect = lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut;
|
|
263
|
+
if (shouldReconnect) {
|
|
264
|
+
setTimeout(() => this.connect(method, phoneNumber), 5000);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
this.sock.ev.on('creds.update', saveCreds);
|
|
270
|
+
|
|
271
|
+
this.sock.ev.on('messages.upsert', async (m) => {
|
|
272
|
+
if (m.type !== 'notify') return;
|
|
273
|
+
const msg = m.messages[0];
|
|
274
|
+
if (msg.key.fromMe) return;
|
|
275
|
+
|
|
276
|
+
const text = msg.message?.conversation || msg.message?.extendedTextMessage?.text || '';
|
|
277
|
+
const from = msg.key.remoteJid;
|
|
278
|
+
|
|
279
|
+
if (this.messageHandler) {
|
|
280
|
+
await this.messageHandler(text, from, 'whatsapp');
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async sendMessage(to, text) {
|
|
287
|
+
if (!this.connected || !this.sock) throw new Error('Not connected');
|
|
288
|
+
await this.sock.sendMessage(to, { text });
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async disconnect() {
|
|
292
|
+
if (this.sock) {
|
|
293
|
+
await this.sock.logout();
|
|
294
|
+
this.sock = null;
|
|
295
|
+
}
|
|
296
|
+
this.connected = false;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// ==========================================
|
|
301
|
+
// TELEGRAM ADAPTER
|
|
302
|
+
// ==========================================
|
|
303
|
+
class TelegramAdapter extends BaseAdapter {
|
|
304
|
+
constructor(config = {}) {
|
|
305
|
+
super('telegram', config);
|
|
306
|
+
this.bot = null;
|
|
307
|
+
this.token = config.token;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async connect() {
|
|
311
|
+
if (!this.token) {
|
|
312
|
+
throw new Error('Telegram token required. Get from @BotFather');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
this.bot = new Telegraf(this.token);
|
|
316
|
+
|
|
317
|
+
this.bot.on('text', async (ctx) => {
|
|
318
|
+
const text = ctx.message.text;
|
|
319
|
+
const from = ctx.chat.id.toString();
|
|
320
|
+
|
|
321
|
+
if (this.messageHandler) {
|
|
322
|
+
await this.messageHandler(text, from, 'telegram');
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
await this.bot.launch();
|
|
327
|
+
this.connected = true;
|
|
328
|
+
|
|
329
|
+
const botInfo = await this.bot.telegram.getMe();
|
|
330
|
+
this.authData = {
|
|
331
|
+
id: botInfo.id.toString(),
|
|
332
|
+
user: {
|
|
333
|
+
name: botInfo.first_name,
|
|
334
|
+
username: botInfo.username
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
log('telegram', `متصل: @${botInfo.username}`);
|
|
339
|
+
return this.getFingerprint();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
async sendMessage(to, text) {
|
|
343
|
+
if (!this.connected || !this.bot) throw new Error('Not connected');
|
|
344
|
+
await this.bot.telegram.sendMessage(to, text);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async disconnect() {
|
|
348
|
+
if (this.bot) {
|
|
349
|
+
this.bot.stop();
|
|
350
|
+
this.bot = null;
|
|
351
|
+
}
|
|
352
|
+
this.connected = false;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// ==========================================
|
|
357
|
+
// DISCORD ADAPTER
|
|
358
|
+
// ==========================================
|
|
359
|
+
class DiscordAdapter extends BaseAdapter {
|
|
360
|
+
constructor(config = {}) {
|
|
361
|
+
super('discord', config);
|
|
362
|
+
this.client = null;
|
|
363
|
+
this.token = config.token;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async connect() {
|
|
367
|
+
if (!this.token) {
|
|
368
|
+
throw new Error('Discord token required from Developer Portal');
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
this.client = new DiscordClient({
|
|
372
|
+
intents: [
|
|
373
|
+
GatewayIntentBits.Guilds,
|
|
374
|
+
GatewayIntentBits.GuildMessages,
|
|
375
|
+
GatewayIntentBits.MessageContent
|
|
376
|
+
]
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
this.client.on('messageCreate', async (message) => {
|
|
380
|
+
if (message.author.bot) return;
|
|
381
|
+
|
|
382
|
+
const text = message.content;
|
|
383
|
+
const from = message.channel.id;
|
|
384
|
+
|
|
385
|
+
if (this.messageHandler) {
|
|
386
|
+
await this.messageHandler(text, from, 'discord');
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
await this.client.login(this.token);
|
|
391
|
+
this.connected = true;
|
|
392
|
+
|
|
393
|
+
this.authData = {
|
|
394
|
+
id: this.client.user.id,
|
|
395
|
+
user: {
|
|
396
|
+
name: this.client.user.username,
|
|
397
|
+
tag: this.client.user.tag
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
log('discord', `متصل: ${this.client.user.tag}`);
|
|
402
|
+
return this.getFingerprint();
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
async sendMessage(to, text) {
|
|
406
|
+
if (!this.connected || !this.client) throw new Error('Not connected');
|
|
407
|
+
const channel = await this.client.channels.fetch(to);
|
|
408
|
+
await channel.send(text);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
async disconnect() {
|
|
412
|
+
if (this.client) {
|
|
413
|
+
this.client.destroy();
|
|
414
|
+
this.client = null;
|
|
415
|
+
}
|
|
416
|
+
this.connected = false;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// ==========================================
|
|
421
|
+
// SLACK ADAPTER
|
|
422
|
+
// ==========================================
|
|
423
|
+
class SlackAdapter extends BaseAdapter {
|
|
424
|
+
constructor(config = {}) {
|
|
425
|
+
super('slack', config);
|
|
426
|
+
this.client = null;
|
|
427
|
+
this.token = config.token;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
async connect() {
|
|
431
|
+
if (!this.token) {
|
|
432
|
+
throw new Error('Slack token required from api.slack.com');
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
this.client = new WebClient(this.token);
|
|
436
|
+
|
|
437
|
+
const auth = await this.client.auth.test();
|
|
438
|
+
this.connected = true;
|
|
439
|
+
|
|
440
|
+
this.authData = {
|
|
441
|
+
id: auth.user_id,
|
|
442
|
+
user: {
|
|
443
|
+
name: auth.user,
|
|
444
|
+
team: auth.team
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
log('slack', `متصل: ${auth.user} @ ${auth.team}`);
|
|
449
|
+
return this.getFingerprint();
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
async sendMessage(channel, text) {
|
|
453
|
+
if (!this.connected || !this.client) throw new Error('Not connected');
|
|
454
|
+
await this.client.chat.postMessage({ channel, text });
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
async disconnect() {
|
|
458
|
+
this.connected = false;
|
|
459
|
+
this.client = null;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// ==========================================
|
|
464
|
+
// FACEBOOK ADAPTER
|
|
465
|
+
// ==========================================
|
|
466
|
+
class FacebookAdapter extends BaseAdapter {
|
|
467
|
+
constructor(config = {}) {
|
|
468
|
+
super('facebook', config);
|
|
469
|
+
this.appId = config.appId;
|
|
470
|
+
this.appSecret = config.appSecret;
|
|
471
|
+
this.accessToken = null;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
async connect() {
|
|
475
|
+
if (!this.appId || !this.appSecret) {
|
|
476
|
+
throw new Error('Facebook App ID and Secret required');
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// الحصول على access token
|
|
480
|
+
const url = `https://graph.facebook.com/oauth/access_token?client_id=${this.appId}&client_secret=${this.appSecret}&grant_type=client_credentials`;
|
|
481
|
+
const response = await axios.get(url);
|
|
482
|
+
this.accessToken = response.data.access_token;
|
|
483
|
+
this.connected = true;
|
|
484
|
+
|
|
485
|
+
this.authData = {
|
|
486
|
+
id: this.appId,
|
|
487
|
+
user: { appId: this.appId }
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
log('facebook', 'متصل');
|
|
491
|
+
return this.getFingerprint();
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
async sendMessage(to, text) {
|
|
495
|
+
if (!this.connected) throw new Error('Not connected');
|
|
496
|
+
// إرسال رسالة عبر Messenger API
|
|
497
|
+
await axios.post(`https://graph.facebook.com/v18.0/me/messages?access_token=${this.accessToken}`, {
|
|
498
|
+
recipient: { id: to },
|
|
499
|
+
message: { text }
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
async disconnect() {
|
|
504
|
+
this.connected = false;
|
|
505
|
+
this.accessToken = null;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// ==========================================
|
|
510
|
+
// NOHO USER SYSTEM
|
|
511
|
+
// ==========================================
|
|
512
|
+
class NohoUser {
|
|
513
|
+
constructor() {
|
|
514
|
+
this.dataPath = path.join(process.cwd(), '.noho');
|
|
515
|
+
this.userFile = path.join(this.dataPath, 'user.json');
|
|
516
|
+
this.projectsFile = path.join(this.dataPath, 'projects.json');
|
|
517
|
+
this.apiFile = path.join(this.dataPath, 'api.key');
|
|
518
|
+
this.ensureDir();
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
ensureDir() {
|
|
522
|
+
if (!fs.existsSync(this.dataPath)) {
|
|
523
|
+
fs.mkdirSync(this.dataPath, { recursive: true });
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
register(username, password) {
|
|
528
|
+
if (this.exists()) {
|
|
529
|
+
log('error', 'أنت مسجل بالفعل!');
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const apiKey = 'NPI-' + crypto.randomBytes(32).toString('hex').toUpperCase();
|
|
534
|
+
const userData = {
|
|
535
|
+
username,
|
|
536
|
+
password: crypto.createHash('sha256').update(password).digest('hex'),
|
|
537
|
+
apiKey,
|
|
538
|
+
createdAt: new Date().toISOString()
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
fs.writeFileSync(this.userFile, JSON.stringify(userData, null, 2));
|
|
542
|
+
fs.writeFileSync(this.apiFile, apiKey);
|
|
543
|
+
|
|
544
|
+
log('success', `تم التسجيل: ${username}`);
|
|
545
|
+
log('bot', `API: ${apiKey.slice(0, 20)}...`);
|
|
546
|
+
return true;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
login(username, password) {
|
|
550
|
+
if (!this.exists()) {
|
|
551
|
+
log('error', 'ليس لديك حساب!');
|
|
552
|
+
return false;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
const user = JSON.parse(fs.readFileSync(this.userFile, 'utf8'));
|
|
556
|
+
const hash = crypto.createHash('sha256').update(password).digest('hex');
|
|
557
|
+
|
|
558
|
+
if (user.username !== username || user.password !== hash) {
|
|
559
|
+
log('error', 'بيانات غير صحيحة');
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
log('success', `مرحباً ${username}!`);
|
|
564
|
+
return true;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
getInfo() {
|
|
568
|
+
if (!this.exists()) return null;
|
|
569
|
+
const user = JSON.parse(fs.readFileSync(this.userFile, 'utf8'));
|
|
570
|
+
return {
|
|
571
|
+
username: user.username,
|
|
572
|
+
apiKey: user.apiKey.slice(0, 20) + '...',
|
|
573
|
+
createdAt: user.createdAt
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
renewAPI() {
|
|
578
|
+
if (!this.exists()) return null;
|
|
579
|
+
const newApi = 'NPI-' + crypto.randomBytes(32).toString('hex').toUpperCase();
|
|
580
|
+
const user = JSON.parse(fs.readFileSync(this.userFile, 'utf8'));
|
|
581
|
+
user.apiKey = newApi;
|
|
582
|
+
fs.writeFileSync(this.userFile, JSON.stringify(user, null, 2));
|
|
583
|
+
fs.writeFileSync(this.apiFile, newApi);
|
|
584
|
+
log('success', 'تم تجديد API!');
|
|
585
|
+
return newApi;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
saveProject(name, platform, config) {
|
|
589
|
+
const projects = fs.existsSync(this.projectsFile)
|
|
590
|
+
? JSON.parse(fs.readFileSync(this.projectsFile, 'utf8'))
|
|
591
|
+
: [];
|
|
592
|
+
projects.push({ name, platform, config, createdAt: new Date().toISOString() });
|
|
593
|
+
fs.writeFileSync(this.projectsFile, JSON.stringify(projects, null, 2));
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
exists() {
|
|
597
|
+
return fs.existsSync(this.userFile);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// ==========================================
|
|
602
|
+
// CODE CHECKER
|
|
603
|
+
// ==========================================
|
|
604
|
+
class CodeChecker {
|
|
605
|
+
check(filePath) {
|
|
606
|
+
if (!fs.existsSync(filePath)) {
|
|
607
|
+
log('error', `الملف غير موجود: ${filePath}`);
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const code = fs.readFileSync(filePath, 'utf8');
|
|
612
|
+
const issues = [];
|
|
613
|
+
const warnings = [];
|
|
614
|
+
|
|
615
|
+
if (code.includes('console.log') && !code.includes('// debug')) {
|
|
616
|
+
warnings.push('يوجد console.log - احذفها قبل النشر');
|
|
617
|
+
}
|
|
618
|
+
if (!code.includes('try') && code.includes('await')) {
|
|
619
|
+
warnings.push('يفضل استخدام try-catch');
|
|
620
|
+
}
|
|
621
|
+
if (code.includes('eval(')) {
|
|
622
|
+
issues.push('⚠️ خطير: eval() غير آمن!');
|
|
623
|
+
}
|
|
624
|
+
if (code.includes('password') && !code.includes('hash')) {
|
|
625
|
+
warnings.push('تأكد من تشفير كلمات المرور');
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
console.log(`\n${colors.cyan}═══ فحص الكود ═══${colors.reset}`);
|
|
629
|
+
console.log(`الملف: ${filePath}`);
|
|
630
|
+
console.log(`الأسطر: ${code.split('\n').length}`);
|
|
631
|
+
|
|
632
|
+
if (issues.length === 0 && warnings.length === 0) {
|
|
633
|
+
log('success', 'الكود نظيف! ✅');
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
issues.forEach(i => console.log(` ❌ ${i}`));
|
|
638
|
+
warnings.forEach(w => console.log(` ⚠️ ${w}`));
|
|
639
|
+
return issues.length === 0;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// ==========================================
|
|
644
|
+
// MAIN NOHO BOT CLASS
|
|
645
|
+
// ==========================================
|
|
646
|
+
class NohoBot {
|
|
647
|
+
constructor() {
|
|
648
|
+
this.user = new NohoUser();
|
|
649
|
+
this.checker = new CodeChecker();
|
|
650
|
+
this.daemon = new DaemonManager();
|
|
651
|
+
this.adapters = new Map();
|
|
652
|
+
|
|
653
|
+
this.register('whatsapp', WhatsAppAdapter);
|
|
654
|
+
this.register('telegram', TelegramAdapter);
|
|
655
|
+
this.register('discord', DiscordAdapter);
|
|
656
|
+
this.register('slack', SlackAdapter);
|
|
657
|
+
this.register('facebook', FacebookAdapter);
|
|
658
|
+
|
|
659
|
+
this.setupCLI();
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
register(name, AdapterClass) {
|
|
663
|
+
this.adapters.set(name, AdapterClass);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// إنشاء بوت جديد (للمستخدم)
|
|
667
|
+
async create(platform, config = {}) {
|
|
668
|
+
const AdapterClass = this.adapters.get(platform);
|
|
669
|
+
if (!AdapterClass) {
|
|
670
|
+
throw new Error(`المنصة ${platform} غير مدعومة`);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
const adapter = new AdapterClass(config);
|
|
674
|
+
|
|
675
|
+
return {
|
|
676
|
+
connect: (...args) => adapter.connect(...args),
|
|
677
|
+
disconnect: () => adapter.disconnect(),
|
|
678
|
+
sendMessage: (to, text) => adapter.sendMessage(to, text),
|
|
679
|
+
onMessage: (handler) => adapter.onMessage(handler),
|
|
680
|
+
getFingerprint: () => adapter.getFingerprint(),
|
|
681
|
+
adapter
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
setupCLI() {
|
|
686
|
+
const args = process.argv.slice(2);
|
|
687
|
+
const cmd = args[0];
|
|
688
|
+
|
|
689
|
+
switch(cmd) {
|
|
690
|
+
case '.longin':
|
|
691
|
+
args[1] && args[2] ? this.user.register(args[1], args[2]) : console.log('الاستخدام: .longin <user> <pass>');
|
|
692
|
+
break;
|
|
693
|
+
case '.d.noho':
|
|
694
|
+
this.showInfo();
|
|
695
|
+
break;
|
|
696
|
+
case 'NPI':
|
|
697
|
+
this.user.renewAPI();
|
|
698
|
+
break;
|
|
699
|
+
case '.m':
|
|
700
|
+
args[1] ? this.checker.check(args[1]) : console.log('الاستخدام: .m <file.js>');
|
|
701
|
+
break;
|
|
702
|
+
case '.link':
|
|
703
|
+
this.listPlatforms();
|
|
704
|
+
break;
|
|
705
|
+
case '.noho.lock.link':
|
|
706
|
+
this.cmdDaemonStart(args[1], args[2], args[3]);
|
|
707
|
+
break;
|
|
708
|
+
case '.noho.stop':
|
|
709
|
+
this.daemon.stop();
|
|
710
|
+
break;
|
|
711
|
+
case '.noho.status':
|
|
712
|
+
this.daemon.isRunning() ? log('success', 'الديمون يعمل ✅') : log('error', 'الديمون متوقف ❌');
|
|
713
|
+
break;
|
|
714
|
+
case '.noho.logs':
|
|
715
|
+
this.daemon.logs(parseInt(args[1]) || 20);
|
|
716
|
+
break;
|
|
717
|
+
default:
|
|
718
|
+
this.showHelp();
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
showInfo() {
|
|
723
|
+
const info = this.user.getInfo();
|
|
724
|
+
if (!info) {
|
|
725
|
+
log('error', 'ليس لديك حساب! استخدم: .longin <user> <pass>');
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
console.log(`\n${colors.cyan}═══ حساب NOHO ═══${colors.reset}`);
|
|
729
|
+
console.log(`المستخدم: ${info.username}`);
|
|
730
|
+
console.log(`API: ${info.apiKey}`);
|
|
731
|
+
console.log(`منذ: ${info.createdAt}`);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
listPlatforms() {
|
|
735
|
+
console.log(`\n${colors.cyan}═══ المنصات المتاحة ═══${colors.reset}`);
|
|
736
|
+
console.log('1. whatsapp - QR Code / Pairing Code');
|
|
737
|
+
console.log('2. telegram - Bot Token (@BotFather)');
|
|
738
|
+
console.log('3. discord - Bot Token (Discord Dev)');
|
|
739
|
+
console.log('4. slack - App Token (api.slack.com)');
|
|
740
|
+
console.log('5. facebook - App ID + Secret (developers.facebook.com)');
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
async cmdDaemonStart(platform, name, method) {
|
|
744
|
+
if (!platform || !name) {
|
|
745
|
+
console.log('الاستخدام: .noho.lock.link <platform> <name> [qr|pairing|token]');
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
if (!this.user.exists()) {
|
|
750
|
+
log('error', 'سجل دخولك أولاً: .longin <user> <pass>');
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
const sessionConfig = {
|
|
755
|
+
platform: platform.toLowerCase(),
|
|
756
|
+
name: name,
|
|
757
|
+
method: method || 'qr',
|
|
758
|
+
sessionPath: path.join(process.cwd(), '.noho_sessions', name),
|
|
759
|
+
createdAt: new Date().toISOString()
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
this.user.saveProject(name, platform, sessionConfig);
|
|
763
|
+
|
|
764
|
+
if (platform === 'whatsapp') {
|
|
765
|
+
log('whatsapp', 'جاري الاتصال... امسح QR Code');
|
|
766
|
+
const session = await this.create('whatsapp', { sessionPath: sessionConfig.sessionPath });
|
|
767
|
+
await session.connect(method || 'qr');
|
|
768
|
+
await session.disconnect();
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
this.daemon.start(sessionConfig);
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
showHelp() {
|
|
775
|
+
console.log(`
|
|
776
|
+
${colors.cyan}╔══════════════════════════════════════╗${colors.reset}
|
|
777
|
+
${colors.cyan}║ NOHO Bot Library v1.0 🤖 ║${colors.reset}
|
|
778
|
+
${colors.cyan}╚══════════════════════════════════════╝${colors.reset}
|
|
779
|
+
|
|
780
|
+
👤 المستخدم:
|
|
781
|
+
.longin <u> <p> تسجيل/دخول
|
|
782
|
+
.d.noho بيانات الحساب
|
|
783
|
+
NPI تجديد API
|
|
784
|
+
|
|
785
|
+
🔍 الكود:
|
|
786
|
+
.m <file.js> فحص الكود
|
|
787
|
+
|
|
788
|
+
🔗 الربط:
|
|
789
|
+
.link عرض المنصات
|
|
790
|
+
|
|
791
|
+
👻 الديمون (24/7):
|
|
792
|
+
.noho.lock.link <p> <n> [m] تشغيل
|
|
793
|
+
.noho.stop إيقاف
|
|
794
|
+
.noho.status الحالة
|
|
795
|
+
.noho.logs [n] السجلات
|
|
796
|
+
|
|
797
|
+
💡 مثال:
|
|
798
|
+
node noho_bot.js .longin omar 123456
|
|
799
|
+
node noho_bot.js .noho.lock.link whatsapp mybot qr
|
|
800
|
+
`);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
module.exports = NohoBot;
|
|
805
|
+
|
|
806
|
+
if (require.main === module) {
|
|
807
|
+
new NohoBot();
|
|
808
|
+
}
|