Mohammed Foud commited on
Commit
ebccf84
·
1 Parent(s): d7f80c2

first commit

Browse files
.~lock.AI_telecom.xlsx# ADDED
@@ -0,0 +1 @@
 
 
1
+ ,mfoud444,mfoud444,09.02.2025 02:56,file:///home/mfoud444/.config/libreoffice/4;
AI_telecom.xlsx ADDED
Binary file (12 kB). View file
 
app.js DELETED
@@ -1,272 +0,0 @@
1
- const { Client, LocalAuth } = require('whatsapp-web.js');
2
- const qrcode = require('qrcode-terminal');
3
- const xlsx = require('xlsx');
4
- const createCsvWriter = require('csv-writer').createObjectCsvWriter;
5
- const fs = require('fs');
6
- const http = require('http'); // Add HTTP server
7
-
8
- const path = require('path');
9
- const client = new Client({
10
- authStrategy: new LocalAuth(),
11
- puppeteer: {
12
- headless: true,
13
- defaultViewport: null,
14
- executablePath: '/usr/bin/google-chrome',
15
- args: ['--no-sandbox'],}
16
- });
17
-
18
- const STATE_FILE = 'group_state.json';
19
- const MAX_GROUP_SIZE = 230;
20
- const DAILY_BATCH_SIZE = 1;
21
-
22
- // CSV Writer configuration
23
- const csvWriter = createCsvWriter({
24
- path: 'number_statuses.csv',
25
- header: [
26
- { id: 'phone', title: 'PHONE' },
27
- { id: 'name', title: 'NAME' },
28
- { id: 'status', title: 'STATUS' },
29
- { id: 'error_code', title: 'ERROR_CODE' },
30
- { id: 'message', title: 'MESSAGE' },
31
- { id: 'is_invite_sent', title: 'INVITE_SENT' }
32
- ],
33
- append: true
34
- });
35
-
36
- const server = http.createServer((req, res) => {
37
- if (req.url === '/number_statuses.csv') {
38
- // Serve the CSV file
39
- fs.readFile('number_statuses.csv', (err, data) => {
40
- if (err) {
41
- res.writeHead(500, { 'Content-Type': 'text/plain' });
42
- res.end('Error reading CSV file');
43
- } else {
44
- res.writeHead(200, { 'Content-Type': 'text/csv' });
45
- res.end(data);
46
- }
47
- });
48
- } else if (req.url === '/group_state.json') {
49
- // Serve the JSON file
50
- fs.readFile('group_state.json', (err, data) => {
51
- if (err) {
52
- res.writeHead(500, { 'Content-Type': 'text/plain' });
53
- res.end('Error reading JSON file');
54
- } else {
55
- res.writeHead(200, { 'Content-Type': 'application/json' });
56
- res.end(data);
57
- }
58
- });
59
- } else {
60
- res.writeHead(404, { 'Content-Type': 'text/plain' });
61
- res.end('File not found');
62
- }
63
- });
64
-
65
-
66
- client.on('qr', (qr) => {
67
- qrcode.generate(qr, { small: true });
68
- });
69
-
70
- client.on('ready', async () => {
71
- console.log('Client is ready!');
72
- let state = loadState();
73
-
74
- // if (!state) {
75
- // state = {
76
- // lastProcessedRow: 0,
77
- // currentGroupId: null,
78
- // currentGroupName: 'عمل 3',
79
- // currentGroupCount: 0
80
- // };
81
- // saveState(state);
82
- // }
83
-
84
- // async function processDailyBatch() {
85
- // try {
86
- // const startFrom = state.lastProcessedRow + 1;
87
- // const endTo = startFrom + DAILY_BATCH_SIZE - 1;
88
-
89
- // const numbers = readNumbersFromExcel('numbers.xlsx', startFrom, endTo);
90
- // if (numbers.length === 0) {
91
- // console.log('All numbers processed.');
92
- // return;
93
- // }
94
-
95
- // let currentGroup;
96
- // if (state.currentGroupId) {
97
- // try {
98
- // currentGroup = await client.getChatById(state.currentGroupId);
99
- // state.currentGroupCount = currentGroup.participants.length;
100
- // } catch (error) {
101
- // console.error('Error fetching current group:', error);
102
- // currentGroup = null;
103
- // }
104
- // }
105
-
106
- // if (!currentGroup || state.currentGroupCount >= MAX_GROUP_SIZE) {
107
- // currentGroup = await createNewGroup(state);
108
- // state.currentGroupCount = currentGroup.participants.length;
109
- // }
110
-
111
- // const availableSlots = MAX_GROUP_SIZE - state.currentGroupCount;
112
- // const numbersToAdd = Math.min(availableSlots, numbers.length);
113
- // const batchFrom = startFrom;
114
- // const batchTo = batchFrom + numbersToAdd - 1;
115
-
116
- // if (numbersToAdd > 0) {
117
- // await checkNumberStatuses(currentGroup.id._serialized, currentGroup.name, 'numbers.xlsx', batchFrom, batchTo);
118
- // const updatedGroup = await client.getChatById(currentGroup.id._serialized);
119
- // state.currentGroupCount = updatedGroup.participants.length;
120
- // state.lastProcessedRow = batchTo;
121
- // saveState(state);
122
- // }
123
-
124
- // const remainingNumbers = numbers.length - numbersToAdd;
125
- // if (remainingNumbers > 0) {
126
- // const remainingFrom = batchTo + 1;
127
- // const remainingTo = endTo;
128
- // const newGroup = await createNewGroup(state);
129
- // await checkNumberStatuses(newGroup.id._serialized, newGroup.name, 'numbers.xlsx', remainingFrom, remainingTo);
130
- // const updatedNewGroup = await client.getChatById(newGroup.id._serialized);
131
- // state.currentGroupCount = updatedNewGroup.participants.length;
132
- // state.lastProcessedRow = remainingTo;
133
- // saveState(state);
134
- // }
135
-
136
- // console.log(`Processed batch from ${startFrom} to ${endTo}`);
137
- // } catch (error) {
138
- // console.error('Error processing batch:', error);
139
- // } finally {
140
- // setTimeout(processDailyBatch, 60 * 60 * 1000);
141
- // }
142
- // }
143
-
144
- // processDailyBatch();
145
- });
146
-
147
- client.initialize();
148
-
149
- function loadState() {
150
- try {
151
- if (fs.existsSync(STATE_FILE)) {
152
- return JSON.parse(fs.readFileSync(STATE_FILE, 'utf-8'));
153
- }
154
- } catch (error) {
155
- console.error('Error loading state:', error);
156
- }
157
- return null;
158
- }
159
-
160
- function saveState(state) {
161
- try {
162
- fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
163
- } catch (error) {
164
- console.error('Error saving state:', error);
165
- }
166
- }
167
- async function createNewGroup(state) {
168
- try {
169
- // C
170
- const newGroupName = getNextGroupName(state.currentGroupName);
171
- const participants = ['[email protected]', '[email protected]', '[email protected]'];
172
-
173
-
174
- const creation = await client.createGroup(newGroupName, participants);
175
- console.error('Group creation failed:', creation);
176
- const newGroup = await client.getChatById(creation.gid._serialized);
177
-
178
- // Promote participants using the proper group instance
179
- await newGroup.promoteParticipants(['[email protected]']);
180
-
181
- // Set group settings
182
- await newGroup.setMessagesAdminsOnly(true);
183
- await newGroup.setInfoAdminsOnly(true);
184
-
185
- // Update state
186
- state.currentGroupId = creation.gid._serialized;
187
- state.currentGroupName = newGroupName;
188
- state.currentGroupCount = newGroup.participants.length;
189
- saveState(state);
190
-
191
- return newGroup;
192
- } catch (error) {
193
- console.error('Group creation failed:', error);
194
- throw error;
195
- }
196
- }
197
-
198
- function getNextGroupName(currentName) {
199
- const match = currentName.match(/عمل (\d+)/);
200
- if (match) {
201
- const num = parseInt(match[1], 10) + 1;
202
- return `عمل ${num}`;
203
- }
204
- return 'عمل 3';
205
- }
206
-
207
- function readNumbersFromExcel(filePath, from, to) {
208
- const workbook = xlsx.readFile(filePath);
209
- const sheetName = workbook.SheetNames[0];
210
- const sheet = workbook.Sheets[sheetName];
211
- const data = xlsx.utils.sheet_to_json(sheet);
212
- if (from < 1 || to > data.length || from > to) {
213
- throw new Error('Invalid range specified.');
214
- }
215
- const numbers = data.slice(from - 1, to).map(row => ({
216
- phone: `967${row['phone number']}@c.us`,
217
- name: row['name']
218
- }));
219
- return numbers;
220
- }
221
-
222
- async function checkNumberStatuses(groupId, groupName, filePath, from, to) {
223
- const numbers = readNumbersFromExcel(filePath, from, to);
224
- const groupChat = await client.getChatById(groupId);
225
- if (!groupChat.isGroup) {
226
- throw new Error('Invalid group ID provided');
227
- }
228
-
229
- for (const numberData of numbers) {
230
- const resultTemplate = {
231
- phone: numberData.phone,
232
- name: numberData.name,
233
- status: '',
234
- error_code: '',
235
- message: '',
236
- is_invite_sent: false
237
- };
238
-
239
- try {
240
- const contactId = await client.getNumberId(numberData.phone);
241
- if (!contactId) {
242
- await csvWriter.writeRecords([{ ...resultTemplate, status: 'UNREGISTERED', error_code: '404', message: 'Not registered on WhatsApp' }]);
243
- continue;
244
- }
245
-
246
- const addResult = await groupChat.addParticipants([numberData.phone], { autoSendInviteV4: false });
247
- const participantResult = addResult[numberData.phone];
248
- let resultEntry;
249
-
250
- if (participantResult.code === 200) {
251
- resultEntry = { ...resultTemplate, status: 'VALID', error_code: '200', message: 'Successfully added' };
252
- } else if (participantResult.code === 403) {
253
- resultEntry = { ...resultTemplate, status: 'PRIVATE_INVITE_ONLY', error_code: '403', message: participantResult.message, is_invite_sent: participantResult.isInviteV4Sent };
254
- if (participantResult.isInviteV4Sent) {
255
- await groupChat.sendInvite(numberData.phone);
256
- }
257
- } else {
258
- resultEntry = { ...resultTemplate, status: 'UNKNOWN_ERROR', error_code: participantResult.code, message: participantResult.message };
259
- }
260
-
261
- await csvWriter.writeRecords([resultEntry]);
262
- } catch (error) {
263
- await csvWriter.writeRecords([{ ...resultTemplate, status: 'UNKNOWN_ERROR', error_code: '500', message: error.message }]);
264
- }
265
- }
266
- }
267
- server.listen(7860, () => {
268
- console.log('File server running at http://localhost:7860');
269
- console.log('Access files at:');
270
- console.log('- http://localhost:7860/number_statuses.csv');
271
- console.log('- http://localhost:7860/group_state.json');
272
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e.py ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import xlsxwriter
2
+
3
+ # إنشاء ملف Excel جديد
4
+ workbook = xlsxwriter.Workbook('AI_telecom.xlsx')
5
+
6
+ # تنسيقات الخلايا
7
+ header_format = workbook.add_format({
8
+ 'bold': True,
9
+ 'bg_color': '#D7E4BC',
10
+ 'border': 1,
11
+ 'align': 'center',
12
+ 'valign': 'vcenter'
13
+ })
14
+ cell_format = workbook.add_format({
15
+ 'border': 1,
16
+ 'align': 'center',
17
+ 'valign': 'vcenter',
18
+ 'num_format': '#,##0.00' # رقمين عشريين مع فاصل الآلاف
19
+ })
20
+ date_format = workbook.add_format({
21
+ 'border': 1,
22
+ 'align': 'center',
23
+ 'num_format': 'yyyy-mm-dd'
24
+ })
25
+ normal_format = workbook.add_format({
26
+ 'border': 1,
27
+ 'align': 'center',
28
+ 'valign': 'vcenter'
29
+ })
30
+
31
+ #################################################
32
+ # الورقة الأولى: "إحصائيات الذكاء الاصطناعي"
33
+ #################################################
34
+ worksheet1 = workbook.add_worksheet('إحصائيات الذكاء الاصطناعي')
35
+
36
+ # عناوين الجدول للورقة الأولى
37
+ headers1 = ['رقم الشركة', 'اسم الشركة', 'عدد العملاء قبل تطبيق الذكاء الاصطناعي',
38
+ 'عدد العملاء بعد تطبيق الذكاء الاصطناعي', 'نسبة الزيادة في العملاء', 'تاريخ التطبيق']
39
+ for col, header in enumerate(headers1):
40
+ worksheet1.write(0, col, header, header_format)
41
+
42
+ # بيانات حقيقية تقريبية (10 صفوف) من شركات الاتصالات في السعودية:
43
+ data1 = [
44
+ [1, 'STC', 20000000, 21000000, None, '2023-01-15'],
45
+ [2, 'Mobily', 15000000, 15750000, None, '2023-02-10'],
46
+ [3, 'Zain KSA', 10000000, 10600000, None, '2023-03-05'],
47
+ [4, 'Virgin Mobile Saudi Arabia', 2000000, 2100000, None, '2023-03-20'],
48
+ [5, 'Lebara Mobile Saudi Arabia', 1000000, 1080000, None, '2023-04-10'],
49
+ [6, 'STC - فرع الشرق', 5000000, 5250000, None, '2023-04-15'],
50
+ [7, 'STC - فرع الغرب', 4500000, 4725000, None, '2023-04-20'],
51
+ [8, 'Mobily - فرع الرياض', 6000000, 6300000, None, '2023-05-05'],
52
+ [9, 'Zain KSA - فرع جدة', 3000000, 3150000, None, '2023-05-10'],
53
+ [10,'Virgin Mobile - فرع الدمام',1500000, 1575000, None, '2023-05-15'],
54
+ ]
55
+
56
+ # كتابة البيانات في الجدول مع إضافة صيغة حساب نسبة الزيادة
57
+ for row_num, row_data in enumerate(data1, start=1):
58
+ worksheet1.write(row_num, 0, row_data[0], normal_format)
59
+ worksheet1.write(row_num, 1, row_data[1], normal_format)
60
+ worksheet1.write_number(row_num, 2, row_data[2], cell_format)
61
+ worksheet1.write_number(row_num, 3, row_data[3], cell_format)
62
+ # صيغة حساب نسبة الزيادة: ((عدد العملاء بعد التطبيق - قبل التطبيق) / قبل التطبيق)*100
63
+ formula = f"=((D{row_num+1}-C{row_num+1})/C{row_num+1})*100"
64
+ worksheet1.write_formula(row_num, 4, formula, cell_format)
65
+ worksheet1.write(row_num, 5, row_data[5], date_format)
66
+
67
+ # ضبط عرض الأعمدة
68
+ worksheet1.set_column(0, 0, 10)
69
+ worksheet1.set_column(1, 1, 30)
70
+ worksheet1.set_column(2, 3, 25)
71
+ worksheet1.set_column(4, 4, 20)
72
+ worksheet1.set_column(5, 5, 15)
73
+
74
+ # إضافة مخطط عمودي (Chart) يعرض نسبة الزيادة في العملاء
75
+ chart1 = workbook.add_chart({'type': 'column'})
76
+ chart1.add_series({
77
+ 'name': 'نسبة الزيادة في العملاء',
78
+ 'categories': ['إحصائيات الذكاء الاصطناعي', 1, 1, len(data1), 1], # أسماء الشركات (العمود B)
79
+ 'values': ['إحصائيات الذكاء الاصطناعي', 1, 4, len(data1), 4], # النسب المحسوبة (العمود E)
80
+ })
81
+ chart1.set_title({'name': 'مخطط نسبة الزيادة في العملاء'})
82
+ chart1.set_x_axis({'name': 'الشركات'})
83
+ chart1.set_y_axis({'name': 'النسبة (%)', 'num_format': '0.00'})
84
+ worksheet1.insert_chart('H2', chart1, {'x_offset': 25, 'y_offset': 10})
85
+
86
+ #################################################
87
+ # الورقة الثانية: "تحليل الأداء"
88
+ #################################################
89
+ worksheet2 = workbook.add_worksheet('تحليل الأداء')
90
+
91
+ # عناوين الجدول للورقة الثانية
92
+ headers2 = ['رقم الشركة', 'اسم الشركة', 'الاستثمار في الذكاء الاصطناعي (ملايين ريال)',
93
+ 'زيادة الإيرادات بعد التطبيق (ملايين ريال)', 'العائد على الاستثمار (ROI)', 'ملاحظات']
94
+ for col, header in enumerate(headers2):
95
+ worksheet2.write(0, col, header, header_format)
96
+
97
+ # بيانات حقيقية تقريبية (10 صفوف) توضح استثمارات وعوائد الذكاء الاصطناعي:
98
+ data2 = [
99
+ [1, 'STC', 150, 20, None, 'ROI متوسط'],
100
+ [2, 'Mobily', 120, 18, None, 'ROI جيد'],
101
+ [3, 'Zain KSA', 100, 16, None, 'ROI جيد'],
102
+ [4, 'Virgin Mobile Saudi Arabia', 30, 5, None, 'ROI جيد'],
103
+ [5, 'Lebara Mobile Saudi Arabia', 25, 4, None, 'ROI جيد'],
104
+ [6, 'STC - فرع الشرق', 50, 7, None, 'ROI متوسط'],
105
+ [7, 'STC - فرع الغرب', 45, 6.5, None, 'ROI متوسط'],
106
+ [8, 'Mobily - فرع الرياض', 60, 9, None, 'ROI جيد'],
107
+ [9, 'Zain KSA - فرع جدة', 35, 5.5, None, 'ROI جيد'],
108
+ [10,'Virgin Mobile - فرع الدمام',28, 4.2, None, 'ROI جيد'],
109
+ ]
110
+
111
+ # كتابة البيانات في الجدول مع إضافة صيغة حساب ROI باستخدام دالة IF
112
+ for row_num, row_data in enumerate(data2, start=1):
113
+ worksheet2.write(row_num, 0, row_data[0], normal_format)
114
+ worksheet2.write(row_num, 1, row_data[1], normal_format)
115
+ worksheet2.write_number(row_num, 2, row_data[2], cell_format)
116
+ worksheet2.write_number(row_num, 3, row_data[3], cell_format)
117
+ # صيغة حساب ROI: إذا كان الاستثمار 0 فإن ROI = 0، وإلا (زيادة الإيرادات/الاستثمار)*100
118
+ formula = f"=IF(C{row_num+1}=0, 0, (D{row_num+1}/C{row_num+1})*100)"
119
+ worksheet2.write_formula(row_num, 4, formula, cell_format)
120
+ worksheet2.write(row_num, 5, row_data[5], normal_format)
121
+
122
+ # ضبط عرض الأعمدة للورقة الثانية
123
+ worksheet2.set_column(0, 0, 10)
124
+ worksheet2.set_column(1, 1, 30)
125
+ worksheet2.set_column(2, 3, 30)
126
+ worksheet2.set_column(4, 4, 25)
127
+ worksheet2.set_column(5, 5, 20)
128
+
129
+ # إضافة مخطط دائري (Pie Chart) يوضح توزيع الاستثمار في الذكاء الاصطناعي
130
+ chart2 = workbook.add_chart({'type': 'pie'})
131
+ chart2.add_series({
132
+ 'name': 'نسبة الاستثمار',
133
+ 'categories': ['تحليل الأداء', 1, 1, len(data2), 1], # أسماء الشركات (العمود B)
134
+ 'values': ['تحليل الأداء', 1, 2, len(data2), 2], # قيم الاستثمار (العمود C)
135
+ })
136
+ chart2.set_title({'name': 'مخطط توزيع الاستثمار'})
137
+ worksheet2.insert_chart('H2', chart2, {'x_offset': 25, 'y_offset': 10})
138
+
139
+ #################################################
140
+ # إغلاق ملف Excel
141
+ #################################################
142
+ workbook.close()
143
+ print("تم إنشاء ملف 'AI_telecom.xlsx' بنجاح!")
number_statuses.csv DELETED
File without changes
old/app.js ADDED
@@ -0,0 +1,305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ i want many account whatsapp running same time to start
2
+
3
+ const { Client, LocalAuth } = require('whatsapp-web.js');
4
+ // const qrcode = require('qrcode-terminal');
5
+ const xlsx = require('xlsx');
6
+ const createCsvWriter = require('csv-writer').createObjectCsvWriter;
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const http = require('http');
10
+
11
+ const server = http.createServer((req, res) => {
12
+ if (req.url === '/number_statuses.csv') {
13
+ // Serve the CSV file
14
+ fs.readFile('number_statuses.csv', (err, data) => {
15
+ if (err) {
16
+ res.writeHead(500, { 'Content-Type': 'text/plain' });
17
+ res.end('Error reading CSV file');
18
+ } else {
19
+ res.writeHead(200, { 'Content-Type': 'text/csv' });
20
+ res.end(data);
21
+ }
22
+ });
23
+ } else if (req.url === '/group_state.json') {
24
+ // Serve the JSON file
25
+ fs.readFile('group_state.json', (err, data) => {
26
+ if (err) {
27
+ res.writeHead(500, { 'Content-Type': 'text/plain' });
28
+ res.end('Error reading JSON file');
29
+ } else {
30
+ res.writeHead(200, { 'Content-Type': 'application/json' });
31
+ res.end(data);
32
+ }
33
+ });
34
+ } else if (req.url === '/qrcode.png') {
35
+ // Serve the QR code image file
36
+ fs.readFile('qrcode.png', (err, data) => {
37
+ if (err) {
38
+ res.writeHead(500, { 'Content-Type': 'text/plain' });
39
+ res.end('Error reading QR code image');
40
+ } else {
41
+ res.writeHead(200, { 'Content-Type': 'image/png' });
42
+ res.end(data);
43
+ }
44
+ });
45
+ } else if (req.url === '/start') {
46
+
47
+
48
+ const STATE_FILE = 'group_state.json';
49
+ const MAX_GROUP_SIZE = 230;
50
+ const DAILY_BATCH_SIZE = 1;
51
+ const qrcode = require('qrcode');
52
+
53
+ // CSV Writer configuration
54
+ const csvWriter = createCsvWriter({
55
+ path: 'number_statuses.csv',
56
+ header: [
57
+ { id: 'phone', title: 'PHONE' },
58
+ { id: 'name', title: 'NAME' },
59
+ { id: 'status', title: 'STATUS' },
60
+ { id: 'error_code', title: 'ERROR_CODE' },
61
+ { id: 'message', title: 'MESSAGE' },
62
+ { id: 'is_invite_sent', title: 'INVITE_SENT' }
63
+ ],
64
+ append: true
65
+ });
66
+ const client = new Client({
67
+ authStrategy: new LocalAuth(),
68
+ puppeteer: {
69
+ headless: true,
70
+ defaultViewport: null,
71
+ executablePath: '/usr/bin/google-chrome',
72
+ args: ['--no-sandbox'],
73
+ }
74
+ });
75
+
76
+
77
+
78
+ client.on('qr', (qr) => {
79
+ // Generate a small QR code for the terminal
80
+ qrcode.toString(qr, { type: 'terminal', scale: 1 }, (err, url) => {
81
+ if (err) throw err;
82
+ console.log(url); // Small QR code printed in the terminal
83
+ });
84
+
85
+ // Alternatively, save the QR code as an image file
86
+ qrcode.toFile('qrcode.png', qr, { scale: 2 }, (err) => {
87
+ if (err) throw err;
88
+ console.log('QR code saved as qrcode.png');
89
+ });
90
+ });
91
+
92
+
93
+ client.on('ready', async () => {
94
+ console.log('Client is ready!');
95
+ let state = loadState();
96
+
97
+ if (!state) {
98
+ state = {
99
+ lastProcessedRow: 0,
100
+ currentGroupId: null,
101
+ currentGroupName: 'عمل 3',
102
+ currentGroupCount: 0
103
+ };
104
+ saveState(state);
105
+ }
106
+
107
+ async function processDailyBatch() {
108
+ try {
109
+ const startFrom = state.lastProcessedRow + 1;
110
+ const endTo = startFrom + DAILY_BATCH_SIZE - 1;
111
+
112
+ const numbers = readNumbersFromExcel('numbers.xlsx', startFrom, endTo);
113
+ if (numbers.length === 0) {
114
+ console.log('All numbers processed.');
115
+ return;
116
+ }
117
+
118
+ let currentGroup;
119
+ if (state.currentGroupId) {
120
+ try {
121
+ currentGroup = await client.getChatById(state.currentGroupId);
122
+ state.currentGroupCount = currentGroup.participants.length;
123
+ } catch (error) {
124
+ console.error('Error fetching current group:', error);
125
+ currentGroup = null;
126
+ }
127
+ }
128
+
129
+ if (!currentGroup || state.currentGroupCount >= MAX_GROUP_SIZE) {
130
+ currentGroup = await createNewGroup(state);
131
+ state.currentGroupCount = currentGroup.participants.length;
132
+ }
133
+
134
+ const availableSlots = MAX_GROUP_SIZE - state.currentGroupCount;
135
+ const numbersToAdd = Math.min(availableSlots, numbers.length);
136
+ const batchFrom = startFrom;
137
+ const batchTo = batchFrom + numbersToAdd - 1;
138
+
139
+ if (numbersToAdd > 0) {
140
+ await checkNumberStatuses(currentGroup.id._serialized, currentGroup.name, 'numbers.xlsx', batchFrom, batchTo);
141
+ const updatedGroup = await client.getChatById(currentGroup.id._serialized);
142
+ state.currentGroupCount = updatedGroup.participants.length;
143
+ state.lastProcessedRow = batchTo;
144
+ saveState(state);
145
+ }
146
+
147
+ const remainingNumbers = numbers.length - numbersToAdd;
148
+ if (remainingNumbers > 0) {
149
+ const remainingFrom = batchTo + 1;
150
+ const remainingTo = endTo;
151
+ const newGroup = await createNewGroup(state);
152
+ await checkNumberStatuses(newGroup.id._serialized, newGroup.name, 'numbers.xlsx', remainingFrom, remainingTo);
153
+ const updatedNewGroup = await client.getChatById(newGroup.id._serialized);
154
+ state.currentGroupCount = updatedNewGroup.participants.length;
155
+ state.lastProcessedRow = remainingTo;
156
+ saveState(state);
157
+ }
158
+
159
+ console.log(`Processed batch from ${startFrom} to ${endTo}`);
160
+ } catch (error) {
161
+ console.error('Error processing batch:', error);
162
+ } finally {
163
+ setTimeout(processDailyBatch, 60 * 60 * 1000);
164
+ }
165
+ }
166
+
167
+ processDailyBatch();
168
+ });
169
+
170
+ client.initialize();
171
+
172
+ function loadState() {
173
+ try {
174
+ if (fs.existsSync(STATE_FILE)) {
175
+ return JSON.parse(fs.readFileSync(STATE_FILE, 'utf-8'));
176
+ }
177
+ } catch (error) {
178
+ console.error('Error loading state:', error);
179
+ }
180
+ return null;
181
+ }
182
+
183
+ function saveState(state) {
184
+ try {
185
+ fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
186
+ } catch (error) {
187
+ console.error('Error saving state:', error);
188
+ }
189
+ }
190
+ async function createNewGroup(state) {
191
+ try {
192
+ // C
193
+ const newGroupName = getNextGroupName(state.currentGroupName);
194
+ const participants = ['[email protected]', '[email protected]',];
195
+
196
+
197
+ const creation = await client.createGroup(newGroupName, participants);
198
+ console.error('Group creation failed:', creation);
199
+ const newGroup = await client.getChatById(creation.gid._serialized);
200
+
201
+ // Promote participants using the proper group instance
202
+ await newGroup.promoteParticipants(['[email protected]']);
203
+
204
+ // Set group settings
205
+ await newGroup.setMessagesAdminsOnly(true);
206
+ await newGroup.setInfoAdminsOnly(true);
207
+
208
+ // Update state
209
+ state.currentGroupId = creation.gid._serialized;
210
+ state.currentGroupName = newGroupName;
211
+ state.currentGroupCount = newGroup.participants.length;
212
+ saveState(state);
213
+
214
+ return newGroup;
215
+ } catch (error) {
216
+ console.error('Group creation failed:', error);
217
+ throw error;
218
+ }
219
+ }
220
+
221
+ function getNextGroupName(currentName) {
222
+ const match = currentName.match(/عمل (\d+)/);
223
+ if (match) {
224
+ const num = parseInt(match[1], 10) + 1;
225
+ return `عمل ${num}`;
226
+ }
227
+ return 'عمل 3';
228
+ }
229
+
230
+ function readNumbersFromExcel(filePath, from, to) {
231
+ const workbook = xlsx.readFile(filePath);
232
+ const sheetName = workbook.SheetNames[0];
233
+ const sheet = workbook.Sheets[sheetName];
234
+ const data = xlsx.utils.sheet_to_json(sheet);
235
+ if (from < 1 || to > data.length || from > to) {
236
+ throw new Error('Invalid range specified.');
237
+ }
238
+ const numbers = data.slice(from - 1, to).map(row => ({
239
+ phone: `967${row['phone number']}@c.us`,
240
+ name: row['name']
241
+ }));
242
+ return numbers;
243
+ }
244
+
245
+ async function checkNumberStatuses(groupId, groupName, filePath, from, to) {
246
+ const numbers = readNumbersFromExcel(filePath, from, to);
247
+ const groupChat = await client.getChatById(groupId);
248
+ if (!groupChat.isGroup) {
249
+ throw new Error('Invalid group ID provided');
250
+ }
251
+
252
+ for (const numberData of numbers) {
253
+ const resultTemplate = {
254
+ phone: numberData.phone,
255
+ name: numberData.name,
256
+ status: '',
257
+ error_code: '',
258
+ message: '',
259
+ is_invite_sent: false
260
+ };
261
+
262
+ try {
263
+ const contactId = await client.getNumberId(numberData.phone);
264
+ if (!contactId) {
265
+ await csvWriter.writeRecords([{ ...resultTemplate, status: 'UNREGISTERED', error_code: '404', message: 'Not registered on WhatsApp' }]);
266
+ continue;
267
+ }
268
+
269
+ const addResult = await groupChat.addParticipants([numberData.phone], { autoSendInviteV4: false });
270
+ const participantResult = addResult[numberData.phone];
271
+ let resultEntry;
272
+
273
+ if (participantResult.code === 200) {
274
+ resultEntry = { ...resultTemplate, status: 'VALID', error_code: '200', message: 'Successfully added' };
275
+ } else if (participantResult.code === 403) {
276
+ resultEntry = { ...resultTemplate, status: 'PRIVATE_INVITE_ONLY', error_code: '403', message: participantResult.message, is_invite_sent: participantResult.isInviteV4Sent };
277
+ if (participantResult.isInviteV4Sent) {
278
+ await groupChat.sendInvite(numberData.phone);
279
+ }
280
+ } else {
281
+ resultEntry = { ...resultTemplate, status: 'UNKNOWN_ERROR', error_code: participantResult.code, message: participantResult.message };
282
+ }
283
+
284
+ await csvWriter.writeRecords([resultEntry]);
285
+ } catch (error) {
286
+ await csvWriter.writeRecords([{ ...resultTemplate, status: 'UNKNOWN_ERROR', error_code: '500', message: error.message }]);
287
+ }
288
+ }
289
+ }
290
+
291
+ }
292
+ else {
293
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
294
+ res.end('File not found');
295
+ }
296
+ });
297
+
298
+ server.listen(8000, () => {
299
+ console.log('File server running at http://localhost:8000');
300
+ console.log('Access files at:');
301
+ console.log('- http://localhost:8000/number_statuses.csv');
302
+ console.log('- http://localhost:8000/group_state.json');
303
+ console.log('- http://localhost:8000/qrcode.png');
304
+ });
305
+
app.py → old/app.py RENAMED
File without changes
contacts.csv → old/contacts.csv RENAMED
File without changes
d.py → old/d.py RENAMED
File without changes
google_contacts.csv → old/google_contacts.csv RENAMED
File without changes
old/group_state.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "lastProcessedRow": 1,
3
+ "currentGroupId": "[email protected]",
4
+ "currentGroupName": "عمل 4",
5
+ "currentGroupCount": 4
6
+ }
history.txt → old/history.txt RENAMED
File without changes
index.js → old/index.js RENAMED
File without changes
old/mapp.js ADDED
@@ -0,0 +1,422 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const { Client, LocalAuth } = require('whatsapp-web.js');
2
+ const xlsx = require('xlsx');
3
+ const createCsvWriter = require('csv-writer').createObjectCsvWriter;
4
+ const fs = require('fs');
5
+ const http = require('http');
6
+ const qrcode = require('qrcode');
7
+ const path = require('path');
8
+
9
+ // Configuration
10
+ const CONFIG = {
11
+ PORT: 8081,
12
+ MAX_GROUP_SIZE: 230,
13
+ DAILY_BATCH_SIZE: 5,
14
+ EXCEL_FILE: 'numbers.xlsx',
15
+ BASE_GROUP_NAME: 'عمل',
16
+ STATE_FILE: 'shared_state.json',
17
+ CSV_FILE: 'shared_statuses.csv',
18
+ SESSION_DIR: 'sessions'
19
+ };
20
+
21
+ const accounts = new Map();
22
+
23
+ class WhatsAppAccountManager {
24
+ constructor(accountId) {
25
+ this.accountId = accountId;
26
+ this.status = 'initializing';
27
+ this.qrFile = path.join(CONFIG.SESSION_DIR, `qrcode_${accountId}.png`);
28
+ this.sessionPath = path.join(CONFIG.SESSION_DIR, `session_${accountId}`);
29
+
30
+ // Create session directory
31
+ if (!fs.existsSync(this.sessionPath)) {
32
+ fs.mkdirSync(this.sessionPath, { recursive: true });
33
+ }
34
+
35
+ this.client = new Client({
36
+ authStrategy: new LocalAuth({ clientId: accountId }),
37
+ puppeteer: {
38
+ headless: true,
39
+ args: [
40
+ '--no-sandbox',
41
+ `--user-data-dir=${this.sessionPath}`,
42
+ '--disable-setuid-sandbox'
43
+ ],
44
+ }
45
+ });
46
+
47
+ this.csvWriter = createCsvWriter({
48
+ path: CONFIG.CSV_FILE,
49
+ header: [
50
+ { id: 'phone', title: 'PHONE' },
51
+ { id: 'name', title: 'NAME' },
52
+ { id: 'status', title: 'STATUS' },
53
+ { id: 'error_code', title: 'ERROR_CODE' },
54
+ { id: 'message', title: 'MESSAGE' },
55
+ { id: 'is_invite_sent', title: 'INVITE_SENT' }
56
+ ],
57
+ append: true
58
+ });
59
+
60
+ this.setupHandlers();
61
+ this.initializeClient();
62
+ }
63
+
64
+ setupHandlers() {
65
+ this.client.on('qr', async (qr) => {
66
+ this.status = 'awaiting_qr';
67
+ await qrcode.toFile(this.qrFile, qr, { scale: 2 });
68
+ console.log(`[${this.accountId}] QR code generated`);
69
+ });
70
+
71
+ this.client.on('ready', () => {
72
+ this.status = 'ready';
73
+ console.log(`[${this.accountId}] Client ready`);
74
+ this.processBatch();
75
+ });
76
+
77
+ this.client.on('disconnected', () => {
78
+ this.status = 'disconnected';
79
+ console.log(`[${this.accountId}] Client disconnected`);
80
+ });
81
+ }
82
+
83
+ initializeClient() {
84
+ this.client.initialize().catch(err => {
85
+ console.error(`[${this.accountId}] Initialization error:`, err);
86
+ this.status = 'error';
87
+ });
88
+ }
89
+
90
+ loadSharedState() {
91
+ try {
92
+ return JSON.parse(fs.readFileSync(CONFIG.STATE_FILE));
93
+ } catch {
94
+ return {
95
+ lastProcessed: 0,
96
+ currentGroup: null,
97
+ groupCounter: 1,
98
+ activeGroups: []
99
+ };
100
+ }
101
+ }
102
+
103
+ saveSharedState(state) {
104
+ fs.writeFileSync(CONFIG.STATE_FILE, JSON.stringify(state, null, 2));
105
+ }
106
+
107
+ readNumbers(lastProcessed) {
108
+ try {
109
+ const workbook = xlsx.readFile(CONFIG.EXCEL_FILE);
110
+ const sheet = workbook.Sheets[workbook.SheetNames[0]];
111
+ const rows = xlsx.utils.sheet_to_json(sheet);
112
+
113
+ return rows
114
+ .slice(lastProcessed, lastProcessed + CONFIG.DAILY_BATCH_SIZE)
115
+ .map(row => ({
116
+ phone: `967${row['phone number']}@c.us`,
117
+ name: row['name']
118
+ }));
119
+ } catch (error) {
120
+ console.error('Error reading numbers:', error);
121
+ return [];
122
+ }
123
+ }
124
+
125
+ async processBatch() {
126
+ if (this.status !== 'ready') return;
127
+
128
+ try {
129
+ const state = this.loadSharedState();
130
+ const numbers = this.readNumbers(state.lastProcessed);
131
+
132
+ if (numbers.length === 0) {
133
+ console.log(`[${this.accountId}] No numbers to process`);
134
+ return;
135
+ }
136
+
137
+ let groupId = state.currentGroup;
138
+ if (!groupId || !await this.verifyGroup(groupId)) {
139
+ groupId = await this.createGroup(state);
140
+ }
141
+
142
+ await this.processNumbers(groupId, numbers, state);
143
+ this.saveSharedState(state);
144
+
145
+ console.log(`[${this.accountId}] Processed ${numbers.length} numbers`);
146
+ } catch (error) {
147
+ console.error(`[${this.accountId}] Batch processing error:`, error);
148
+ }
149
+ }
150
+
151
+ async verifyGroup(groupId) {
152
+ try {
153
+ const group = await this.client.getChatById(groupId);
154
+ return group.participants.length < CONFIG.MAX_GROUP_SIZE;
155
+ } catch {
156
+ return false;
157
+ }
158
+ }
159
+
160
+ async createGroup(state) {
161
+ try {
162
+ const groupName = `${CONFIG.BASE_GROUP_NAME} ${state.groupCounter++}`;
163
+ const creation = await this.client.createGroup(groupName, [
164
165
166
+ ]);
167
+
168
+ const group = await this.client.getChatById(creation.gid._serialized);
169
+ await group.promoteParticipants(['[email protected]']);
170
+ await group.setMessagesAdminsOnly(true);
171
+ await group.setInfoAdminsOnly(true);
172
+
173
+ state.currentGroup = creation.gid._serialized;
174
+ state.activeGroups.push(creation.gid._serialized);
175
+ return creation.gid._serialized;
176
+ } catch (error) {
177
+ console.error('Group creation failed:', error);
178
+ throw error;
179
+ }
180
+ }
181
+
182
+ async processNumbers(groupId, numbers, state) {
183
+ try {
184
+ const group = await this.client.getChatById(groupId);
185
+
186
+ for (const { phone, name } of numbers) {
187
+ const record = {
188
+ phone,
189
+ name,
190
+ status: 'PENDING',
191
+ error_code: '',
192
+ message: '',
193
+ is_invite_sent: false
194
+ };
195
+
196
+ try {
197
+ const contactId = await this.client.getNumberId(phone);
198
+ if (!contactId) {
199
+ record.status = 'INVALID';
200
+ record.message = 'Number not registered';
201
+ await this.csvWriter.writeRecords([record]);
202
+ continue;
203
+ }
204
+
205
+ const result = await group.addParticipants([phone], { autoSendInviteV4: false });
206
+ const participantResult = result[phone];
207
+
208
+ record.status = participantResult.code === 200 ? 'ADDED' : 'FAILED';
209
+ record.error_code = participantResult.code;
210
+ record.message = participantResult.message;
211
+ record.is_invite_sent = participantResult.isInviteV4Sent;
212
+
213
+ if (participantResult.code === 403 && !participantResult.isInviteV4Sent) {
214
+ await group.sendInvite(phone);
215
+ record.is_invite_sent = true;
216
+ }
217
+
218
+ await this.csvWriter.writeRecords([record]);
219
+ state.lastProcessed++;
220
+ } catch (error) {
221
+ record.status = 'ERROR';
222
+ record.message = error.message;
223
+ await this.csvWriter.writeRecords([record]);
224
+ }
225
+ }
226
+ } catch (error) {
227
+ console.error('Number processing error:', error);
228
+ }
229
+ }
230
+ }
231
+
232
+ // HTTP Server
233
+ const server = http.createServer((req, res) => {
234
+ if (req.url === '/' && req.method === 'GET') {
235
+ let accountsHTML = '';
236
+ accounts.forEach((manager, id) => {
237
+ accountsHTML += `
238
+ <div class="account-card">
239
+ <h3>${id}</h3>
240
+ <p class="status ${manager.status}">${manager.status.replace('_', ' ')}</p>
241
+ ${manager.status === 'awaiting_qr' ?
242
+ `<img src="/qrcode/${id}" class="qr-image" alt="QR Code">` :
243
+ '<p>QR Code unavailable</p>'}
244
+ <button onclick="startAccount('${id}')">Start Processing</button>
245
+ </div>
246
+ `;
247
+ });
248
+
249
+ const html = `
250
+ <!DOCTYPE html>
251
+ <html>
252
+ <head>
253
+ <title>WhatsApp Bot Manager</title>
254
+ <style>
255
+ body { font-family: Arial, sans-serif; padding: 20px; }
256
+ .account-card {
257
+ border: 1px solid #ddd;
258
+ padding: 15px;
259
+ margin: 10px;
260
+ border-radius: 5px;
261
+ max-width: 300px;
262
+ display: inline-block;
263
+ vertical-align: top;
264
+ }
265
+ .qr-image {
266
+ max-width: 200px;
267
+ height: auto;
268
+ display: block;
269
+ margin: 10px 0;
270
+ }
271
+ button {
272
+ padding: 8px 16px;
273
+ background: #007bff;
274
+ color: white;
275
+ border: none;
276
+ border-radius: 4px;
277
+ cursor: pointer;
278
+ }
279
+ button:hover { background: #0056b3; }
280
+ form { margin: 20px 0; }
281
+ input {
282
+ padding: 8px;
283
+ margin-right: 10px;
284
+ border: 1px solid #ddd;
285
+ border-radius: 4px;
286
+ }
287
+ .status {
288
+ padding: 4px 8px;
289
+ border-radius: 4px;
290
+ font-size: 0.9em;
291
+ display: inline-block;
292
+ }
293
+ .status.ready { background: #d4edda; color: #155724; }
294
+ .status.awaiting_qr { background: #fff3cd; color: #856404; }
295
+ </style>
296
+ </head>
297
+ <body>
298
+ <h1>WhatsApp Bot Manager</h1>
299
+
300
+ <form onsubmit="event.preventDefault(); addAccount()">
301
+ <input type="text" id="newAccountId" placeholder="Enter account ID">
302
+ <button type="submit">Add New Account</button>
303
+ </form>
304
+
305
+ <div id="accounts">${accountsHTML}</div>
306
+
307
+ <script>
308
+ function addAccount() {
309
+ const accountId = document.getElementById('newAccountId').value;
310
+ if (!accountId) return;
311
+
312
+ fetch('/add-account', {
313
+ method: 'POST',
314
+ headers: { 'Content-Type': 'application/json' },
315
+ body: JSON.stringify({ accountId })
316
+ })
317
+ .then(response => response.json())
318
+ .then(data => {
319
+ if (data.success) {
320
+ location.reload();
321
+ } else {
322
+ alert(data.error || 'Error adding account');
323
+ }
324
+ });
325
+ }
326
+
327
+ function startAccount(accountId) {
328
+ fetch('/start/' + accountId, { method: 'POST' })
329
+ .then(response => response.json())
330
+ .then(data => {
331
+ if (!data.success) {
332
+ alert('Error: ' + (data.error || 'Unknown error'));
333
+ }
334
+ });
335
+ }
336
+
337
+ // Auto-refresh QR codes every 3 seconds
338
+ // setInterval(() => {
339
+ // document.querySelectorAll('.qr-image').forEach(img => {
340
+ // img.src = img.src.split('?')[0] + '?t=' + Date.now();
341
+ // });
342
+ // }, 3000);
343
+ </script>
344
+ </body>
345
+ </html>
346
+ `;
347
+
348
+ res.writeHead(200, { 'Content-Type': 'text/html' });
349
+ res.end(html);
350
+ }
351
+ else if (req.method === 'POST' && req.url === '/add-account') {
352
+ let body = '';
353
+ req.on('data', chunk => body += chunk);
354
+ req.on('end', () => {
355
+ try {
356
+ const { accountId } = JSON.parse(body);
357
+ if (!accountId) {
358
+ res.writeHead(400);
359
+ return res.end(JSON.stringify({ error: 'Account ID required' }));
360
+ }
361
+
362
+ if (accounts.has(accountId)) {
363
+ res.writeHead(409);
364
+ return res.end(JSON.stringify({ error: 'Account already exists' }));
365
+ }
366
+
367
+ accounts.set(accountId, new WhatsAppAccountManager(accountId));
368
+ res.writeHead(201);
369
+ res.end(JSON.stringify({ success: true }));
370
+ } catch (error) {
371
+ res.writeHead(500);
372
+ res.end(JSON.stringify({ error: 'Invalid request' }));
373
+ }
374
+ });
375
+ }
376
+ else if (req.method === 'POST' && req.url.startsWith('/start/')) {
377
+ const accountId = req.url.split('/')[2];
378
+ const manager = accounts.get(accountId);
379
+
380
+ if (!manager) {
381
+ res.writeHead(404);
382
+ return res.end(JSON.stringify({ error: 'Account not found' }));
383
+ }
384
+
385
+ manager.processBatch();
386
+ res.writeHead(200);
387
+ res.end(JSON.stringify({ success: true }));
388
+ }
389
+ else if (req.url.startsWith('/qrcode/')) {
390
+ const accountId = req.url.split('/')[2];
391
+ const manager = accounts.get(accountId);
392
+
393
+ if (!manager) {
394
+ res.writeHead(404);
395
+ return res.end('Account not found');
396
+ }
397
+
398
+ fs.readFile(manager.qrFile, (err, data) => {
399
+ if (err) {
400
+ res.writeHead(404);
401
+ res.end('QR code not available');
402
+ } else {
403
+ res.writeHead(200, { 'Content-Type': 'image/png' });
404
+ res.end(data);
405
+ }
406
+ });
407
+ }
408
+ else {
409
+ res.writeHead(404);
410
+ res.end('Not found');
411
+ }
412
+ });
413
+
414
+ // Initialize session directory
415
+ if (!fs.existsSync(CONFIG.SESSION_DIR)) {
416
+ fs.mkdirSync(CONFIG.SESSION_DIR, { recursive: true });
417
+ }
418
+
419
+ // Start server
420
+ server.listen(CONFIG.PORT, () => {
421
+ console.log(`Server running on http://localhost:${CONFIG.PORT}`);
422
+ });
old/number_statuses.csv ADDED
@@ -0,0 +1 @@
 
 
1
+ [email protected],رقم خاص ع / م 59000,VALID,200,Successfully added,false
number_statuses1.csv → old/number_statuses1.csv RENAMED
File without changes
old/numbers.xlsx ADDED
Binary file (23.4 kB). View file
 
old/qrcode.png ADDED
package.json CHANGED
@@ -13,6 +13,7 @@
13
  "console.table": "^0.10.0",
14
  "csv-writer": "^1.6.0",
15
  "puppeteer": "^24.1.1",
 
16
  "qrcode-terminal": "^0.12.0",
17
  "whatsapp-web.js": "1.26.1-alpha.3",
18
  "xlsx": "^0.18.5"
 
13
  "console.table": "^0.10.0",
14
  "csv-writer": "^1.6.0",
15
  "puppeteer": "^24.1.1",
16
+ "qrcode": "^1.5.4",
17
  "qrcode-terminal": "^0.12.0",
18
  "whatsapp-web.js": "1.26.1-alpha.3",
19
  "xlsx": "^0.18.5"
pnpm-lock.yaml CHANGED
@@ -17,6 +17,9 @@ importers:
17
  puppeteer:
18
  specifier: ^24.1.1
19
  version: 24.1.1
 
 
 
20
  qrcode-terminal:
21
  specifier: ^0.12.0
22
  version: 0.12.0
@@ -171,6 +174,10 @@ packages:
171
  resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
172
  engines: {node: '>=6'}
173
 
 
 
 
 
174
175
  resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
176
  engines: {node: '>=0.8'}
@@ -186,6 +193,9 @@ packages:
186
  peerDependencies:
187
  devtools-protocol: '*'
188
 
 
 
 
189
190
  resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
191
  engines: {node: '>=12'}
@@ -265,6 +275,10 @@ packages:
265
  supports-color:
266
  optional: true
267
 
 
 
 
 
268
269
  resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
270
 
@@ -278,6 +292,9 @@ packages:
278
279
  resolution: {integrity: sha512-1CJABgqLxbYxVI+uJY/UDUHJtJ0KZTSjNYJYKqd9FRoXT33WDakDHNxRapMEgzeJ/C3rcs01+avshMnPmKQbvA==}
280
 
 
 
 
281
282
  resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==}
283
 
@@ -330,6 +347,10 @@ packages:
330
331
  resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
332
 
 
 
 
 
333
334
  resolution: {integrity: sha512-IZTB4kq5GK0DPp7sGQ0q/BWurGHffRtQQwVkiqDgeO6wYJLLV5ZhgNOQ65loZxxuPMKZKZcICCUnaGtlxBiR0Q==}
335
  engines: {node: '>=0.8.0'}
@@ -441,6 +462,10 @@ packages:
441
442
  resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==}
443
 
 
 
 
 
444
445
  resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}
446
 
@@ -523,6 +548,18 @@ packages:
523
524
  resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
525
 
 
 
 
 
 
 
 
 
 
 
 
 
526
527
  resolution: {integrity: sha512-Z5FnLVVZSnX7WjBg0mhDtydeRZ1xMcATZThjySQUHqr+0ksP8kqaw23fNKkaaN/Z8gwLUs/W7xdl0I75eP2Xyw==}
528
  engines: {node: '>= 14'}
@@ -539,6 +576,10 @@ packages:
539
  resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
540
  engines: {node: '>=8'}
541
 
 
 
 
 
542
543
  resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
544
  engines: {node: '>=0.10.0'}
@@ -549,6 +590,10 @@ packages:
549
550
  resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
551
 
 
 
 
 
552
553
  resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
554
 
@@ -588,6 +633,11 @@ packages:
588
  resolution: {integrity: sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==}
589
  hasBin: true
590
 
 
 
 
 
 
591
592
  resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
593
 
@@ -602,6 +652,9 @@ packages:
602
  resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
603
  engines: {node: '>=0.10.0'}
604
 
 
 
 
605
606
  resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
607
  engines: {node: '>=4'}
@@ -627,6 +680,9 @@ packages:
627
  engines: {node: '>=10'}
628
  hasBin: true
629
 
 
 
 
630
631
  resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
632
 
@@ -730,6 +786,9 @@ packages:
730
731
  resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
732
 
 
 
 
733
734
  resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
735
  hasBin: true
@@ -742,6 +801,10 @@ packages:
742
  resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==}
743
  engines: {node: '>=0.8'}
744
 
 
 
 
 
745
746
  resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
747
  engines: {node: '>=10'}
@@ -778,14 +841,25 @@ packages:
778
  engines: {node: '>=0.8'}
779
  hasBin: true
780
 
 
 
 
781
782
  resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
783
  engines: {node: '>=10'}
784
 
 
 
 
 
785
786
  resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
787
  engines: {node: '>=12'}
788
 
 
 
 
 
789
790
  resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
791
  engines: {node: '>=12'}
@@ -979,6 +1053,8 @@ snapshots:
979
 
980
981
 
 
 
982
983
  dependencies:
984
  adler-32: 1.3.1
@@ -997,6 +1073,12 @@ snapshots:
997
  mitt: 3.0.1
998
  zod: 3.24.1
999
 
 
 
 
 
 
 
1000
1001
  dependencies:
1002
  string-width: 4.2.3
@@ -1064,6 +1146,8 @@ snapshots:
1064
  dependencies:
1065
  ms: 2.1.3
1066
 
 
 
1067
1068
  dependencies:
1069
  clone: 1.0.4
@@ -1079,6 +1163,8 @@ snapshots:
1079
 
1080
1081
 
 
 
1082
1083
  dependencies:
1084
  readable-stream: 2.3.8
@@ -1132,6 +1218,11 @@ snapshots:
1132
  dependencies:
1133
  pend: 1.2.0
1134
 
 
 
 
 
 
1135
1136
  dependencies:
1137
  async: 3.2.6
@@ -1260,6 +1351,10 @@ snapshots:
1260
1261
  optional: true
1262
 
 
 
 
 
1263
1264
  optional: true
1265
 
@@ -1323,6 +1418,16 @@ snapshots:
1323
  dependencies:
1324
  wrappy: 1.0.2
1325
 
 
 
 
 
 
 
 
 
 
 
1326
1327
  dependencies:
1328
  '@tootallnate/quickjs-emscripten': 0.23.0
@@ -1352,12 +1457,16 @@ snapshots:
1352
  json-parse-even-better-errors: 2.3.1
1353
  lines-and-columns: 1.2.4
1354
 
 
 
1355
1356
 
1357
1358
 
1359
1360
 
 
 
1361
1362
  optional: true
1363
 
@@ -1444,6 +1553,12 @@ snapshots:
1444
 
1445
1446
 
 
 
 
 
 
 
1447
1448
  dependencies:
1449
  core-util-is: 1.0.3
@@ -1468,6 +1583,8 @@ snapshots:
1468
 
1469
1470
 
 
 
1471
1472
 
1473
@@ -1486,6 +1603,8 @@ snapshots:
1486
 
1487
1488
 
 
 
1489
1490
  optional: true
1491
 
@@ -1642,6 +1761,8 @@ snapshots:
1642
  tr46: 0.0.3
1643
  webidl-conversions: 3.0.1
1644
 
 
 
1645
1646
  dependencies:
1647
  isexe: 2.0.0
@@ -1650,6 +1771,12 @@ snapshots:
1650
 
1651
1652
 
 
 
 
 
 
 
1653
1654
  dependencies:
1655
  ansi-styles: 4.3.0
@@ -1672,10 +1799,31 @@ snapshots:
1672
  wmf: 1.0.2
1673
  word: 0.3.0
1674
 
 
 
1675
1676
 
 
 
 
 
 
1677
1678
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1679
1680
  dependencies:
1681
  cliui: 8.0.1
 
17
  puppeteer:
18
  specifier: ^24.1.1
19
  version: 24.1.1
20
+ qrcode:
21
+ specifier: ^1.5.4
22
+ version: 1.5.4
23
  qrcode-terminal:
24
  specifier: ^0.12.0
25
  version: 0.12.0
 
174
  resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
175
  engines: {node: '>=6'}
176
 
177
178
+ resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
179
+ engines: {node: '>=6'}
180
+
181
182
  resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
183
  engines: {node: '>=0.8'}
 
193
  peerDependencies:
194
  devtools-protocol: '*'
195
 
196
197
+ resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
198
+
199
200
  resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
201
  engines: {node: '>=12'}
 
275
  supports-color:
276
  optional: true
277
 
278
279
+ resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
280
+ engines: {node: '>=0.10.0'}
281
+
282
283
  resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
284
 
 
292
293
  resolution: {integrity: sha512-1CJABgqLxbYxVI+uJY/UDUHJtJ0KZTSjNYJYKqd9FRoXT33WDakDHNxRapMEgzeJ/C3rcs01+avshMnPmKQbvA==}
294
 
295
296
+ resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
297
+
298
299
  resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==}
300
 
 
347
348
  resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
349
 
350
351
+ resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
352
+ engines: {node: '>=8'}
353
+
354
355
  resolution: {integrity: sha512-IZTB4kq5GK0DPp7sGQ0q/BWurGHffRtQQwVkiqDgeO6wYJLLV5ZhgNOQ65loZxxuPMKZKZcICCUnaGtlxBiR0Q==}
356
  engines: {node: '>=0.8.0'}
 
462
463
  resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==}
464
 
465
466
+ resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
467
+ engines: {node: '>=8'}
468
+
469
470
  resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}
471
 
 
548
549
  resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
550
 
551
552
+ resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
553
+ engines: {node: '>=6'}
554
+
555
556
+ resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
557
+ engines: {node: '>=8'}
558
+
559
560
+ resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
561
+ engines: {node: '>=6'}
562
+
563
564
  resolution: {integrity: sha512-Z5FnLVVZSnX7WjBg0mhDtydeRZ1xMcATZThjySQUHqr+0ksP8kqaw23fNKkaaN/Z8gwLUs/W7xdl0I75eP2Xyw==}
565
  engines: {node: '>= 14'}
 
576
  resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
577
  engines: {node: '>=8'}
578
 
579
580
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
581
+ engines: {node: '>=8'}
582
+
583
584
  resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
585
  engines: {node: '>=0.10.0'}
 
590
591
  resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
592
 
593
594
+ resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
595
+ engines: {node: '>=10.13.0'}
596
+
597
598
  resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
599
 
 
633
  resolution: {integrity: sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==}
634
  hasBin: true
635
 
636
637
+ resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==}
638
+ engines: {node: '>=10.13.0'}
639
+ hasBin: true
640
+
641
642
  resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
643
 
 
652
  resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
653
  engines: {node: '>=0.10.0'}
654
 
655
656
+ resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
657
+
658
659
  resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
660
  engines: {node: '>=4'}
 
680
  engines: {node: '>=10'}
681
  hasBin: true
682
 
683
684
+ resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
685
+
686
687
  resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
688
 
 
786
787
  resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
788
 
789
790
+ resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
791
+
792
793
  resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
794
  hasBin: true
 
801
  resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==}
802
  engines: {node: '>=0.8'}
803
 
804
805
+ resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
806
+ engines: {node: '>=8'}
807
+
808
809
  resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
810
  engines: {node: '>=10'}
 
841
  engines: {node: '>=0.8'}
842
  hasBin: true
843
 
844
845
+ resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
846
+
847
848
  resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
849
  engines: {node: '>=10'}
850
 
851
852
+ resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
853
+ engines: {node: '>=6'}
854
+
855
856
  resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
857
  engines: {node: '>=12'}
858
 
859
860
+ resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
861
+ engines: {node: '>=8'}
862
+
863
864
  resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
865
  engines: {node: '>=12'}
 
1053
 
1054
1055
 
1056
1057
+
1058
1059
  dependencies:
1060
  adler-32: 1.3.1
 
1073
  mitt: 3.0.1
1074
  zod: 3.24.1
1075
 
1076
1077
+ dependencies:
1078
+ string-width: 4.2.3
1079
+ strip-ansi: 6.0.1
1080
+ wrap-ansi: 6.2.0
1081
+
1082
1083
  dependencies:
1084
  string-width: 4.2.3
 
1146
  dependencies:
1147
  ms: 2.1.3
1148
 
1149
1150
+
1151
1152
  dependencies:
1153
  clone: 1.0.4
 
1163
 
1164
1165
 
1166
1167
+
1168
1169
  dependencies:
1170
  readable-stream: 2.3.8
 
1218
  dependencies:
1219
  pend: 1.2.0
1220
 
1221
1222
+ dependencies:
1223
+ locate-path: 5.0.0
1224
+ path-exists: 4.0.0
1225
+
1226
1227
  dependencies:
1228
  async: 3.2.6
 
1351
1352
  optional: true
1353
 
1354
1355
+ dependencies:
1356
+ p-locate: 4.1.0
1357
+
1358
1359
  optional: true
1360
 
 
1418
  dependencies:
1419
  wrappy: 1.0.2
1420
 
1421
1422
+ dependencies:
1423
+ p-try: 2.2.0
1424
+
1425
1426
+ dependencies:
1427
+ p-limit: 2.3.0
1428
+
1429
1430
+
1431
1432
  dependencies:
1433
  '@tootallnate/quickjs-emscripten': 0.23.0
 
1457
  json-parse-even-better-errors: 2.3.1
1458
  lines-and-columns: 1.2.4
1459
 
1460
1461
+
1462
1463
 
1464
1465
 
1466
1467
 
1468
1469
+
1470
1471
  optional: true
1472
 
 
1553
 
1554
1555
 
1556
1557
+ dependencies:
1558
+ dijkstrajs: 1.0.3
1559
+ pngjs: 5.0.0
1560
+ yargs: 15.4.1
1561
+
1562
1563
  dependencies:
1564
  core-util-is: 1.0.3
 
1583
 
1584
1585
 
1586
1587
+
1588
1589
 
1590
 
1603
 
1604
1605
 
1606
1607
+
1608
1609
  optional: true
1610
 
 
1761
  tr46: 0.0.3
1762
  webidl-conversions: 3.0.1
1763
 
1764
1765
+
1766
1767
  dependencies:
1768
  isexe: 2.0.0
 
1771
 
1772
1773
 
1774
1775
+ dependencies:
1776
+ ansi-styles: 4.3.0
1777
+ string-width: 4.2.3
1778
+ strip-ansi: 6.0.1
1779
+
1780
1781
  dependencies:
1782
  ansi-styles: 4.3.0
 
1799
  wmf: 1.0.2
1800
  word: 0.3.0
1801
 
1802
1803
+
1804
1805
 
1806
1807
+ dependencies:
1808
+ camelcase: 5.3.1
1809
+ decamelize: 1.2.0
1810
+
1811
1812
 
1813
1814
+ dependencies:
1815
+ cliui: 6.0.0
1816
+ decamelize: 1.2.0
1817
+ find-up: 4.1.0
1818
+ get-caller-file: 2.0.5
1819
+ require-directory: 2.1.1
1820
+ require-main-filename: 2.0.0
1821
+ set-blocking: 2.0.0
1822
+ string-width: 4.2.3
1823
+ which-module: 2.0.1
1824
+ y18n: 4.0.3
1825
+ yargs-parser: 18.1.3
1826
+
1827
1828
  dependencies:
1829
  cliui: 8.0.1
r.sh CHANGED
@@ -1,3 +1,3 @@
1
- sudo docker build -t whatsapp-bot .
2
 
3
- sudo docker run -it --name whatsapp-bot-container whatsapp-bot
 
1
+ # sudo docker build -t whatsapp-bot .
2
 
3
+ # sudo docker run -it --name whatsapp-bot-container whatsapp-bot
run.sh CHANGED
@@ -1 +1 @@
1
- node index.js
 
1
+ node src/index.js
src/AccountManager.js ADDED
@@ -0,0 +1,309 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const { Client, LocalAuth } = require('whatsapp-web.js');
2
+ const xlsx = require('xlsx');
3
+ const createCsvWriter = require('csv-writer').createObjectCsvWriter;
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const qrcode = require('qrcode');
7
+ const CONFIG = require('./config');
8
+
9
+ class WhatsAppAccountManager {
10
+ constructor(accountId) {
11
+ this.accountId = accountId;
12
+ this.status = 'initializing';
13
+ this.qrFile = path.join(CONFIG.SESSION_DIR, `qrcode_${accountId}.png`);
14
+ this.sessionPath = path.join(CONFIG.SESSION_DIR, `session_${accountId}`);
15
+ this.processDetails = [];
16
+ if (!fs.existsSync(this.sessionPath)) {
17
+ fs.mkdirSync(this.sessionPath, { recursive: true });
18
+ }
19
+
20
+ this.client = new Client({
21
+ authStrategy: new LocalAuth({ clientId: accountId }),
22
+
23
+ puppeteer: {
24
+ headless: true,
25
+ executablePath: '/usr/bin/google-chrome',
26
+ args: [
27
+ '--no-sandbox',
28
+ `--user-data-dir=${this.sessionPath}`,
29
+ '--disable-setuid-sandbox'
30
+ ],
31
+ }
32
+ });
33
+
34
+ this.csvWriter = createCsvWriter({
35
+ path: CONFIG.CSV_FILE,
36
+ header: [
37
+ { id: 'phone', title: 'PHONE' },
38
+ { id: 'name', title: 'NAME' },
39
+ { id: 'status', title: 'STATUS' },
40
+ { id: 'error_code', title: 'ERROR_CODE' },
41
+ { id: 'message', title: 'MESSAGE' },
42
+ { id: 'is_invite_sent', title: 'INVITE_SENT' }
43
+ ],
44
+ append: true
45
+ });
46
+
47
+ this.setupHandlers();
48
+ this.initializeClient();
49
+ }
50
+
51
+
52
+ setupHandlers() {
53
+ this.client.on('qr', async (qr) => {
54
+ this.status = 'awaiting_qr';
55
+ await qrcode.toFile(this.qrFile, qr, { scale: 2 });
56
+ console.log(`[${this.accountId}] QR code generated`);
57
+ });
58
+
59
+ this.client.on('ready', () => {
60
+ this.status = 'ready';
61
+ console.log(`[${this.accountId}] Client ready`);
62
+ this.processBatch();
63
+ });
64
+
65
+ this.client.on('disconnected', () => {
66
+ this.status = 'disconnected';
67
+ console.log(`[${this.accountId}] Client disconnected`);
68
+ });
69
+ }
70
+
71
+ initializeClient() {
72
+ this.client.initialize().catch(err => {
73
+ console.error(`[${this.accountId}] Initialization error:`, err);
74
+ this.status = 'error';
75
+ });
76
+ }
77
+
78
+ loadSharedState() {
79
+ try {
80
+ return JSON.parse(fs.readFileSync(CONFIG.STATE_FILE));
81
+ } catch {
82
+ return {
83
+ lastProcessed: 0,
84
+ currentGroup: null,
85
+ groupCounter: 1,
86
+ activeGroups: []
87
+ };
88
+ }
89
+ }
90
+
91
+ saveSharedState(state) {
92
+ fs.writeFileSync(CONFIG.STATE_FILE, JSON.stringify(state, null, 2));
93
+ }
94
+
95
+ readNumbers(lastProcessed) {
96
+ try {
97
+ const workbook = xlsx.readFile(CONFIG.EXCEL_FILE);
98
+ const sheet = workbook.Sheets[workbook.SheetNames[0]];
99
+ const rows = xlsx.utils.sheet_to_json(sheet);
100
+
101
+ return rows
102
+ .slice(lastProcessed, lastProcessed + CONFIG.DAILY_BATCH_SIZE)
103
+ .map(row => ({
104
+ phone: `967${row['phone number']}@c.us`,
105
+ name: row['name']
106
+ }));
107
+ } catch (error) {
108
+ console.error('Error reading numbers:', error);
109
+ return [];
110
+ }
111
+ }
112
+
113
+ async processBatch() {
114
+ if (this.status !== 'running') return;
115
+
116
+ try {
117
+ const state = this.loadSharedState();
118
+ const numbers = this.readNumbers(state.lastProcessed);
119
+
120
+ if (numbers.length === 0) {
121
+ console.log(`[${this.accountId}] No numbers to process`);
122
+ return;
123
+ }
124
+
125
+ let groupId = state.currentGroup;
126
+ if (!groupId || !await this.verifyGroup(groupId)) {
127
+ groupId = await this.createGroup(state);
128
+ }
129
+
130
+ await this.processNumbers(groupId, numbers, state);
131
+
132
+ this.saveSharedState(state);
133
+
134
+ console.log(`[${this.accountId}] Processed ${numbers.length} numbers`);
135
+ } catch (error) {
136
+ console.error(`[${this.accountId}] Batch processing error:`, error);
137
+ }
138
+ }
139
+
140
+ async verifyGroup(groupId) {
141
+ try {
142
+ const group = await this.client.getChatById(groupId);
143
+ return group.participants.length < CONFIG.MAX_GROUP_SIZE;
144
+ } catch {
145
+ return false;
146
+ }
147
+ }
148
+
149
+ async createGroup(state) {
150
+ try {
151
+ const numberAdded = [
152
153
154
155
156
157
+
158
+ '[email protected]',//mohammed old
159
+
160
161
162
+
163
+
164
+
165
+ '[email protected]', //unclue
166
+ '[email protected]' //mohammed alhas
167
+ ]
168
+
169
+
170
+ const groupName = `${CONFIG.BASE_GROUP_NAME} ${state.groupCounter++}`;
171
+ const creation = await this.client.createGroup(groupName, numberAdded);
172
+
173
+ const group = await this.client.getChatById(creation.gid._serialized);
174
+ await group.promoteParticipants([
175
176
177
178
179
180
181
182
+ await group.setMessagesAdminsOnly(true);
183
+ await group.setInfoAdminsOnly(true);
184
+
185
+ state.currentGroup = creation.gid._serialized;
186
+ state.activeGroups.push(creation.gid._serialized);
187
+ return creation.gid._serialized;
188
+ } catch (error) {
189
+ console.error('Group creation failed:', error);
190
+ throw error;
191
+ }
192
+ }
193
+
194
+ async processNumbers(groupId, numbers, state) {
195
+ try {
196
+ const group = await this.client.getChatById(groupId);
197
+
198
+ for (const { phone, name } of numbers) {
199
+ const record = {
200
+ phone,
201
+ name,
202
+ status: 'PENDING',
203
+ error_code: '',
204
+ message: '',
205
+ is_invite_sent: false
206
+ };
207
+
208
+ try {
209
+ const contactId = await this.client.getNumberId(phone);
210
+ if (!contactId) {
211
+ record.status = 'INVALID';
212
+ record.message = 'Number not registered';
213
+ await this.csvWriter.writeRecords([record]);
214
+ continue;
215
+ }
216
+
217
+ const result = await group.addParticipants([phone], { autoSendInviteV4: false });
218
+ const participantResult = result[phone];
219
+
220
+ record.status = participantResult.code === 200 ? 'ADDED' : 'FAILED';
221
+ record.error_code = participantResult.code;
222
+ record.message = participantResult.message;
223
+ record.is_invite_sent = participantResult.isInviteV4Sent;
224
+
225
+ if (participantResult.code === 403 && !participantResult.isInviteV4Sent) {
226
+ await group.sendInvite(phone);
227
+ record.is_invite_sent = true;
228
+ }
229
+ this.processDetails.push(record);
230
+ await this.csvWriter.writeRecords([record]);
231
+ state.lastProcessed++;
232
+ } catch (error) {
233
+ record.status = 'ERROR';
234
+ record.message = error.message;
235
+ this.processDetails.push(record);
236
+ await this.csvWriter.writeRecords([record]);
237
+ }
238
+ }
239
+ } catch (error) {
240
+ console.error('Number processing error:', error);
241
+ }
242
+ }
243
+
244
+
245
+ async addNumberToGroup(phone, groupName) {
246
+ try {
247
+ if (!phone.endsWith('@c.us')) {
248
+ phone = phone.replace(/\D/g, ''); // Remove non-numeric characters
249
+ if (!phone.startsWith('967')) {
250
+ phone = `967${phone}`; // Add Yemen country code if missing
251
+ }
252
+ phone = `${phone}@c.us`; // Append @c.us suffix
253
+ }
254
+
255
+ console.log(`Formatted phone number: ${phone}`);
256
+
257
+ // Get all chats and find the group
258
+ const chats = await this.client.getChats();
259
+ const group = chats.find(chat => chat.isGroup && chat.name === groupName);
260
+
261
+ if (!group) {
262
+ console.error(`Group '${groupName}' not found.`);
263
+ return { success: false, message: "Group not found" };
264
+ }
265
+
266
+ const record = {
267
+ phone,
268
+ status: 'PENDING',
269
+ error_code: '',
270
+ message: '',
271
+ is_invite_sent: false
272
+ };
273
+
274
+ // Check if number is valid
275
+ const contactId = await this.client.getNumberId(phone);
276
+ if (!contactId) {
277
+ record.status = 'INVALID';
278
+ record.message = 'Number not registered';
279
+ await this.csvWriter.writeRecords([record]);
280
+ return { success: false, message: "Number not registered" };
281
+ }
282
+
283
+ // Add participant
284
+ const result = await group.addParticipants([phone], { autoSendInviteV4: false });
285
+ const participantResult = result[phone];
286
+
287
+ record.status = participantResult.code === 200 ? 'ADDED' : 'FAILED';
288
+ record.error_code = participantResult.code;
289
+ record.message = participantResult.message;
290
+ record.is_invite_sent = participantResult.isInviteV4Sent;
291
+
292
+ // Handle invitation if needed
293
+ if (participantResult.code === 403 && !participantResult.isInviteV4Sent) {
294
+ await group.sendInvite(phone);
295
+ record.is_invite_sent = true;
296
+ }
297
+
298
+ await this.csvWriter.writeRecords([record]);
299
+
300
+ return { success: participantResult.code === 200, message: participantResult.message };
301
+
302
+ } catch (error) {
303
+ console.error('Error adding number to group:', error);
304
+ return { success: false, message: error.message };
305
+ }
306
+ }
307
+
308
+ }
309
+ module.exports = WhatsAppAccountManager;
src/config.js ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const path = require('path');
2
+
3
+ module.exports = {
4
+ PORT: 8081,
5
+ MAX_GROUP_SIZE: 230,
6
+ DAILY_BATCH_SIZE: 1,
7
+ EXCEL_FILE: 'numbers.xlsx',
8
+ BASE_GROUP_NAME: 'عمل',
9
+ STATE_FILE: 'shared_state.json',
10
+ CSV_FILE: 'shared_statuses.csv',
11
+ SESSION_DIR: path.join(__dirname, 'sessions'),
12
+ STATE_REFRESH_INTERVAL: 5000
13
+ };
src/index.js ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const http = require('http');
2
+ const CONFIG = require('./config');
3
+ const routes = require('./routes');
4
+ const { accounts } = require('./shared');
5
+ const fs = require('fs');
6
+ const server = http.createServer((req, res) => {
7
+ routes(req, res);
8
+ });
9
+
10
+ // Initialize session directory
11
+ if (!fs.existsSync(CONFIG.SESSION_DIR)) {
12
+ fs.mkdirSync(CONFIG.SESSION_DIR, { recursive: true });
13
+ }
14
+
15
+ server.listen(CONFIG.PORT, () => {
16
+ console.log(`Server running on http://localhost:${CONFIG.PORT}`);
17
+ });
src/numbers.xlsx ADDED
Binary file (23.4 kB). View file
 
src/routes.js ADDED
@@ -0,0 +1,321 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const CONFIG = require('./config');
4
+ const WhatsAppAccountManager = require('./AccountManager');
5
+ const { accounts } = require('./shared');
6
+
7
+ const htmlTemplate = fs.readFileSync(path.join(__dirname, 'views', 'index.html'), 'utf8');
8
+
9
+ module.exports = function (req, res) {
10
+ if (req.url === '/app' && req.method === 'GET') {
11
+ handleRoot(req, res);
12
+ } else if (req.method === 'POST' && req.url === '/add-account') {
13
+ handleAddAccount(req, res);
14
+ } else if (req.method === 'POST' && req.url.startsWith('/start/')) {
15
+ handleStartAccount(req, res);
16
+ } else if (req.url.startsWith('/qrcode/')) {
17
+ handleQrCode(req, res);
18
+ }
19
+ else if (req.url === '/shared_state.json') {
20
+ handleSharedState(req, res);
21
+ } else if (req.url === '/shared_statuses.csv') {
22
+ handleSharedStatuses(req, res);
23
+ }else if (req.url === '/dashboard-data') {
24
+ handleDashboardData(req, res);
25
+ }else if (req.url === '/accounts-status') {
26
+ handleAccountsStatus(req, res);
27
+ } else if (req.method === 'POST' && req.url === '/add-number-to-group') {
28
+ // Handle adding number to group
29
+ let body = '';
30
+ req.on('data', chunk => body += chunk);
31
+ req.on('end', async () => {
32
+ try {
33
+ const { accountId, phone, groupName } = JSON.parse(body);
34
+ const manager = accounts.get(accountId);
35
+
36
+ if (!manager) {
37
+ res.writeHead(404);
38
+ return res.end(JSON.stringify({ success: false, message: 'Account not found' }));
39
+ }
40
+
41
+ const result = await manager.addNumberToGroup(phone, groupName);
42
+ res.writeHead(200, { 'Content-Type': 'application/json' });
43
+ res.end(JSON.stringify(result));
44
+ } catch (error) {
45
+ res.writeHead(500);
46
+ res.end(JSON.stringify({ success: false, message: error.message }));
47
+ }
48
+ });
49
+ }
50
+ else {
51
+ res.writeHead(404);
52
+ res.end('Not found');
53
+ }
54
+ };
55
+
56
+ function handleAccountsStatus(req, res) {
57
+ const accountsData = Array.from(accounts).map(([id, manager]) => ({
58
+ id,
59
+ status: manager.status
60
+ }));
61
+
62
+ res.writeHead(200, { 'Content-Type': 'application/json' });
63
+ res.end(JSON.stringify({ accounts: accountsData }));
64
+ }
65
+
66
+
67
+ function checkSessionFolders() {
68
+ const sessionDir = CONFIG.SESSION_DIR;
69
+
70
+ // Check if the session directory exists
71
+ if (!fs.existsSync(sessionDir)) {
72
+ console.log('Session directory does not exist.');
73
+ return;
74
+ }
75
+
76
+ // Read all files/folders in the session directory
77
+ const sessionFolders = fs.readdirSync(sessionDir);
78
+
79
+ sessionFolders.forEach(folder => {
80
+ // Only process folders that start with "session_"
81
+ if (folder.startsWith('session_') && !folder.endsWith('.png')) {
82
+ // Extract account ID from folder name (e.g., "session_123456")
83
+ const accountId = folder.replace('session_', '');
84
+
85
+ // Check if the account ID is not in the accounts map
86
+ if (!accounts.has(accountId)) {
87
+ console.log(`Found session folder for account ${accountId}, but no manager exists. Creating new manager...`);
88
+
89
+ // Create a new WhatsAppAccountManager for this account
90
+ accounts.set(accountId, new WhatsAppAccountManager(accountId));
91
+ }
92
+ }
93
+ });
94
+ }
95
+
96
+ function handleAddAccount(req, res) {
97
+ let body = '';
98
+ req.on('data', chunk => body += chunk);
99
+ req.on('end', () => {
100
+ try {
101
+ const { accountId } = JSON.parse(body);
102
+ if (!accountId) {
103
+ res.writeHead(400);
104
+ return res.end(JSON.stringify({ error: 'Account ID required' }));
105
+ }
106
+
107
+ if (accounts.has(accountId)) {
108
+ res.writeHead(409);
109
+ return res.end(JSON.stringify({ error: 'Account already exists' }));
110
+ }
111
+
112
+ accounts.set(accountId, new WhatsAppAccountManager(accountId));
113
+ res.writeHead(201);
114
+ res.end(JSON.stringify({ success: true }));
115
+ } catch (error) {
116
+ res.writeHead(500);
117
+ res.end(JSON.stringify({ error: 'Invalid request' }));
118
+ }
119
+ });
120
+ }
121
+
122
+ function handleStartAccount(req, res) {
123
+ const accountId = req.url.split('/')[2];
124
+ const manager = accounts.get(accountId);
125
+
126
+ if (!manager) {
127
+ res.writeHead(404);
128
+ return res.end(JSON.stringify({ error: 'Account not found' }));
129
+ }
130
+
131
+ manager.status = 'running';
132
+ manager.processBatch();
133
+ res.writeHead(200);
134
+ res.end(JSON.stringify({ success: true }));
135
+ }
136
+
137
+ function handleQrCode(req, res) {
138
+ const accountId = req.url.split('/')[2];
139
+ const manager = accounts.get(accountId);
140
+
141
+ if (!manager) {
142
+ res.writeHead(404);
143
+ return res.end('Account not found');
144
+ }
145
+
146
+ fs.readFile(manager.qrFile, (err, data) => {
147
+ if (err) {
148
+ res.writeHead(404);
149
+ res.end('QR code not available');
150
+ } else {
151
+ res.writeHead(200, { 'Content-Type': 'image/png' });
152
+ res.end(data);
153
+ }
154
+ });
155
+ }
156
+
157
+ // Add these new handler functions
158
+ function handleSharedState(req, res) {
159
+ fs.readFile(CONFIG.STATE_FILE, (err, data) => {
160
+ if (err) {
161
+ res.writeHead(404);
162
+ res.end('State file not found');
163
+ } else {
164
+ res.writeHead(200, {
165
+ 'Content-Type': 'application/json',
166
+ 'Content-Disposition': 'attachment; filename="shared_state.json"'
167
+ });
168
+ res.end(data);
169
+ }
170
+ });
171
+ }
172
+
173
+ function handleSharedStatuses(req, res) {
174
+ fs.readFile(CONFIG.CSV_FILE, (err, data) => {
175
+ if (err) {
176
+ res.writeHead(404);
177
+ res.end('Status file not found');
178
+ } else {
179
+ res.writeHead(200, {
180
+ 'Content-Type': 'text/csv',
181
+ 'Content-Disposition': 'attachment; filename="shared_statuses.csv"'
182
+ });
183
+ res.end(data);
184
+ }
185
+ });
186
+ }
187
+
188
+
189
+
190
+ function handleRoot(req, res) {
191
+ checkSessionFolders();
192
+ let state = {};
193
+ try {
194
+ state = JSON.parse(fs.readFileSync(CONFIG.STATE_FILE));
195
+ } catch (error) {
196
+ state = {
197
+ currentGroup: null,
198
+ groupCounter: 1,
199
+ activeGroups: [],
200
+ lastProcessed: 0
201
+ };
202
+ }
203
+
204
+
205
+
206
+ let accountsHTML = '';
207
+ accounts.forEach((manager, id) => {
208
+ accountsHTML += generateAccountCard(id, manager);
209
+ });
210
+
211
+ let html = htmlTemplate
212
+ .replace('{{ACCOUNTS}}', accountsHTML);
213
+
214
+ res.writeHead(200, { 'Content-Type': 'text/html' });
215
+ res.end(html);
216
+ }
217
+
218
+ function generateAccountCard(accountId, manager) {
219
+ let content = '';
220
+ const baseContent =
221
+ `<h3 class='text-xl font-semibold text-gray-900 mb-2'>${accountId}</h3>
222
+ <p class='text-sm font-medium py-1 px-3 rounded-full text-white ${manager.status === 'ready' ? 'bg-green-500' : manager.status === 'running' ? 'bg-blue-500' : 'bg-yellow-500'}'>
223
+ ${manager.status.replace('_', ' ')}
224
+ </p>`;
225
+
226
+ if (manager.status === 'initializing') {
227
+ content =
228
+ `${baseContent}
229
+ <div class='flex items-center gap-2 text-gray-600 mt-2'>
230
+ <i class='fas fa-spinner fa-spin'></i>
231
+ <span>Initializing...</span>
232
+ </div>`;
233
+ } else if (manager.status === 'awaiting_qr') {
234
+ content =
235
+ `${baseContent}
236
+ <div class='flex flex-col items-center mt-4'>
237
+ <img src='/qrcode/${accountId}' class='w-64 h-64 rounded-lg shadow-md border' alt='QR Code'>
238
+ <p class='mt-2 text-gray-600'>Scan this QR code with your phone</p>
239
+ </div>`;
240
+ } else if (manager.status === 'ready') {
241
+ content =
242
+ `${baseContent}
243
+ <button onclick="startAccount('${accountId}')" class='w-full mt-4 px-6 py-2 bg-blue-600 text-white rounded-lg shadow-md hover:bg-blue-700 flex items-center justify-center'>
244
+ <i class='fas fa-play mr-2'></i> Start Processing
245
+ </button>`;
246
+ } else if (manager.status === 'running') {
247
+ content =
248
+ `${baseContent}
249
+ <div class='flex items-center gap-2 text-gray-600 mt-2'>
250
+ <i class='fas fa-spinner fa-spin'></i>
251
+ <span>Processing...</span>
252
+ </div>`;
253
+ } else {
254
+ content = baseContent;
255
+ }
256
+
257
+ const processDetails = manager.status === 'running' ? `
258
+ <div class="mt-4 w-full">
259
+ <h4 class="text-lg font-semibold mb-2">Process Details</h4>
260
+ <button onclick="toggleDetails('${accountId}')" class="text-blue-600 hover:text-blue-800 text-sm">
261
+ ${manager.processDetailsOpen ? 'Hide Details' : 'Show Details'}
262
+ </button>
263
+ <div id="details-${accountId}" class="details-section ${manager.processDetailsOpen ? '' : 'hidden'}">
264
+ <table class="w-full border-collapse border border-gray-300">
265
+ <thead>
266
+ <tr class="bg-gray-200">
267
+ <th class="border border-gray-300 px-4 py-2">Phone</th>
268
+ <th class="border border-gray-300 px-4 py-2">Name</th>
269
+ <th class="border border-gray-300 px-4 py-2">Status</th>
270
+ <th class="border border-gray-300 px-4 py-2">Error Code</th>
271
+ <th class="border border-gray-300 px-4 py-2">Message</th>
272
+ <th class="border border-gray-300 px-4 py-2">Invite Sent</th>
273
+ </tr>
274
+ </thead>
275
+ <tbody>
276
+ ${manager.processDetails.map(detail => `
277
+ <tr>
278
+ <td class="border border-gray-300 px-4 py-2">${detail.phone}</td>
279
+ <td class="border border-gray-300 px-4 py-2">${detail.name}</td>
280
+ <td class="border border-gray-300 px-4 py-2">${detail.status}</td>
281
+ <td class="border border-gray-300 px-4 py-2">${detail.error_code || 'N/A'}</td>
282
+ <td class="border border-gray-300 px-4 py-2">${detail.message || 'N/A'}</td>
283
+ <td class="border border-gray-300 px-4 py-2">${detail.is_invite_sent || 'N/A'}</td>
284
+ </tr>
285
+ `).join('')}
286
+ </tbody>
287
+ </table>
288
+ </div>
289
+ </div>
290
+ ` : '';
291
+
292
+ return `
293
+ <div class='bg-white p-6 rounded-lg shadow-lg border flex flex-col items-center' data-status="${manager.status}" data-account-id="${accountId}">
294
+ ${content}
295
+ ${processDetails}
296
+ </div>`;
297
+ }
298
+
299
+
300
+ function handleDashboardData(req, res) {
301
+ try {
302
+ const state = JSON.parse(fs.readFileSync(CONFIG.STATE_FILE));
303
+ const data = {
304
+ currentGroupName: state.currentGroupName ?
305
+ `${CONFIG.BASE_GROUP_NAME} ${state.groupCounter - 1}` : 'No active group',
306
+ currentGroupMembers: state.currentGroup ?
307
+ (state.activeGroups.find(g => g.id === state.currentGroup)?.members || 0) : 0,
308
+ maxGroupSize: CONFIG.MAX_GROUP_SIZE,
309
+ currentGroupId: state.currentGroup ?
310
+ state.currentGroup.substring(0, 8) + '...' : 'N/A',
311
+ totalProcessed: state.lastProcessed,
312
+ activeGroupsCount: state.activeGroups.length,
313
+ nextGroupName: `${CONFIG.BASE_GROUP_NAME} ${state.groupCounter}`
314
+ };
315
+ res.writeHead(200, { 'Content-Type': 'application/json' });
316
+ res.end(JSON.stringify(data));
317
+ } catch (error) {
318
+ res.writeHead(500);
319
+ res.end(JSON.stringify({ error: 'Could not load dashboard data' }));
320
+ }
321
+ }
src/shared.js ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ module.exports = {
2
+ accounts: new Map()
3
+ };
src/shared_state.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "lastProcessed": 52,
3
+ "currentGroup": "[email protected]",
4
+ "currentGroupName": "عمل 4",
5
+ "groupCounter": 4,
6
+ "activeGroups": [
7
8
+ ]
9
+ }
src/shared_statuses.csv ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ 735201518,,INVALID,,Number not registered,false
2
+ [email protected],,INVALID,,Number not registered,false
3
+ [email protected],,INVALID,,Number not registered,false
4
+ [email protected],رقم خاص ع / م 59049,ADDED,200,The participant was added successfully,false
5
+ [email protected],رقم خاص ع / م 59050,ADDED,200,The participant was added successfully,false
6
+ [email protected],,ADDED,200,The participant was added successfully,false
7
+ [email protected],رقم خاص ع / م 59051,ADDED,200,The participant was added successfully,false
src/views/index.html ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>WhatsApp Bot Manager</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ </head>
10
+ <body class="bg-gray-100 p-6">
11
+ <!-- Add Number Modal -->
12
+ <div id="addNumberModal" class="fixed inset-0 bg-black bg-opacity-50 hidden">
13
+ <div class="bg-white rounded-lg p-6 max-w-md mx-auto mt-20">
14
+ <h3 class="text-xl font-bold mb-4">Add Number to Group</h3>
15
+ <form id="addNumberForm">
16
+ <div class="mb-4">
17
+ <label class="block mb-2">Phone Number</label>
18
+ <input type="text" id="phoneNumber" class="w-full px-3 py-2 border rounded" required>
19
+ </div>
20
+ <div class="mb-4">
21
+ <label class="block mb-2">Group Name</label>
22
+ <input type="text" id="groupName" class="w-full px-3 py-2 border rounded" required>
23
+ </div>
24
+ <div class="mb-4">
25
+ <label class="block mb-2">Account ID</label>
26
+ <input type="text" id="accountId" class="w-full px-3 py-2 border rounded" required>
27
+ <p class="text-sm text-gray-500 mt-1">Enter the account ID of a ready account</p>
28
+ </div>
29
+ <div class="flex justify-end">
30
+ <button type="button" onclick="closeModal()" class="mr-2 px-4 py-2 bg-gray-500 text-white rounded">Cancel</button>
31
+ <button type="submit" class="px-4 py-2 bg-blue-600 text-white rounded">Add Number</button>
32
+ </div>
33
+ </form>
34
+ </div>
35
+ </div>
36
+
37
+ <!-- Main Content -->
38
+ <div class="bg-white shadow-lg border rounded-lg p-4 mb-6 flex justify-between items-center">
39
+ <h1 class="text-3xl font-bold bg-gradient-to-r from-blue-500 to-purple-600 text-transparent bg-clip-text">WhatsApp Bot Manager</h1>
40
+ <button onclick="location.reload()" class="px-6 py-2 bg-gray-600 text-white rounded-lg shadow-md hover:bg-gray-700 flex items-center">
41
+ <i class="fas fa-sync-alt mr-2"></i> Refresh
42
+ </button>
43
+ </div>
44
+
45
+ <div class="flex justify-center space-x-4 mb-6">
46
+ <a href="/shared_statuses.csv" download class="px-6 py-2 bg-green-600 text-white rounded-lg shadow-md hover:bg-green-700">Download Status Report</a>
47
+ <a href="/shared_state.json" download class="px-6 py-2 bg-blue-600 text-white rounded-lg shadow-md hover:bg-blue-700">View Shared State</a>
48
+ <button onclick="openAddNumberModal()" class="px-6 py-2 bg-purple-600 text-white rounded-lg shadow-md hover:bg-purple-700">
49
+ <i class="fas fa-plus mr-2"></i>Add Number to Group
50
+ </button>
51
+ </div>
52
+
53
+ <form onsubmit="event.preventDefault(); addAccount()" class="flex justify-center mb-6">
54
+ <input type="text" id="newAccountId" placeholder="Enter account ID" class="px-4 py-2 border rounded-lg shadow-md w-1/3">
55
+ <button type="submit" class="ml-4 px-6 py-2 bg-indigo-600 text-white rounded-lg shadow-md hover:bg-indigo-700">Add New Account</button>
56
+ </form>
57
+
58
+ <div id="accounts" class="grid grid-cols-1 md:grid-cols-2 gap-6">{{ACCOUNTS}}</div>
59
+
60
+ <script>
61
+ function openAddNumberModal() {
62
+ document.getElementById('addNumberModal').classList.remove('hidden');
63
+ }
64
+
65
+ function closeModal() {
66
+ document.getElementById('addNumberModal').classList.add('hidden');
67
+ }
68
+
69
+ async function addNumberToGroup() {
70
+ const phone = document.getElementById('phoneNumber').value;
71
+ const groupName = document.getElementById('groupName').value;
72
+ const accountId = document.getElementById('accountId').value;
73
+
74
+ // Validate account status
75
+
76
+
77
+ try {
78
+ const response = await fetch('/add-number-to-group', {
79
+ method: 'POST',
80
+ headers: { 'Content-Type': 'application/json' },
81
+ body: JSON.stringify({ accountId, phone, groupName })
82
+ });
83
+
84
+ const result = await response.json();
85
+ if (result.success) {
86
+ alert('Number added successfully!');
87
+ closeModal();
88
+ } else {
89
+ alert('Error: ' + result.message);
90
+ }
91
+ } catch (error) {
92
+ alert('Error: ' + error.message);
93
+ }
94
+ }
95
+
96
+ document.getElementById('addNumberForm').addEventListener('submit', (e) => {
97
+ e.preventDefault();
98
+ addNumberToGroup();
99
+ });
100
+
101
+ function addAccount() {
102
+ const accountId = document.getElementById('newAccountId').value;
103
+ if (!accountId) return;
104
+
105
+ fetch('/add-account', {
106
+ method: 'POST',
107
+ headers: { 'Content-Type': 'application/json' },
108
+ body: JSON.stringify({ accountId })
109
+ })
110
+ .then(response => response.json())
111
+ .then(data => {
112
+ if (data.success) {
113
+ location.reload();
114
+ } else {
115
+ alert(data.error || 'Error adding account');
116
+ }
117
+ });
118
+ }
119
+
120
+ function startAccount(accountId) {
121
+ fetch('/start/' + accountId, { method: 'POST' })
122
+ .then(response => response.json())
123
+ .then(data => {
124
+ if (!data.success) {
125
+ alert('Error: ' + (data.error || 'Unknown error'));
126
+ }
127
+ });
128
+ }
129
+
130
+ function refreshAccounts() {
131
+ fetch('/accounts-status')
132
+ .then(response => response.json())
133
+ .then(data => {
134
+ const accountsContainer = document.getElementById('accounts');
135
+ accountsContainer.innerHTML = data.accounts
136
+ .map(account => generateAccountCard(account.id, account))
137
+ .join('');
138
+ });
139
+ }
140
+
141
+ // setInterval(refreshAccounts, 5000);
142
+
143
+
144
+ function toggleDetails(accountId) {
145
+ const detailsSection = document.getElementById(`details-${accountId}`);
146
+ const button = detailsSection.previousElementSibling;
147
+
148
+ if (detailsSection.classList.contains('hidden')) {
149
+ detailsSection.classList.remove('hidden');
150
+ button.textContent = 'Hide Details';
151
+ } else {
152
+ detailsSection.classList.add('hidden');
153
+ button.textContent = 'Show Details';
154
+ }
155
+
156
+ // Update the manager state (optional, depending on how you want to store the open/close state)
157
+ const accountCard = document.querySelector(`[data-account-id='${accountId}']`);
158
+ const manager = accountCard.dataset.manager; // You'll need to handle the state persistence
159
+ manager.processDetailsOpen = !manager.processDetailsOpen;
160
+ }
161
+ </script>
162
+ </body>
163
+ </html>