webmaxsocket 1.0.0 → 1.1.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/README.md +239 -13
- package/config/example.json +6 -0
- package/example-ios.js +186 -0
- package/example-sms.js +131 -0
- package/example-token.js +100 -0
- package/example.js +2 -0
- package/index.js +2 -0
- package/lib/client.js +345 -44
- package/lib/opcodes.js +3 -1
- package/lib/socketTransport.js +296 -0
- package/lib/userAgent.js +8 -4
- package/package.json +16 -3
package/README.md
CHANGED
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
## 📖 Описание / Description
|
|
4
4
|
|
|
5
|
-
**WebMaxSocket** — async Node.js библиотека для работы с внутренним API мессенджера Max.
|
|
5
|
+
**WebMaxSocket** — async Node.js библиотека для работы с внутренним API мессенджера Max. Поддерживает **QR-код авторизацию**, **Token авторизацию**, и работу через **WebSocket** (WEB) или **TCP Socket** (IOS/ANDROID).
|
|
6
6
|
|
|
7
7
|
## ✨ Особенности / Features
|
|
8
8
|
|
|
9
9
|
- ✅ **QR-код авторизация** / QR code authentication
|
|
10
|
-
- ✅ **
|
|
10
|
+
- ✅ **Token авторизация** / Token authentication
|
|
11
|
+
- ✅ **Два транспорта:** WebSocket (WEB) и TCP Socket (IOS/ANDROID)
|
|
11
12
|
- ✅ **Автоматическое сохранение сессий** / Automatic session storage
|
|
13
|
+
- ✅ **Автовыбор транспорта** после QR-авторизации (переход на TCP)
|
|
12
14
|
- ✅ **Отправка и получение сообщений** / Send and receive messages
|
|
13
15
|
- ✅ **Редактирование и удаление сообщений** / Edit and delete messages
|
|
14
16
|
- ✅ **Event-driven архитектура** / Event-driven architecture
|
|
@@ -21,6 +23,16 @@
|
|
|
21
23
|
npm install webmaxsocket
|
|
22
24
|
```
|
|
23
25
|
|
|
26
|
+
### Зависимости для Socket транспорта (IOS/ANDROID)
|
|
27
|
+
|
|
28
|
+
Для работы с TCP Socket транспортом требуется библиотека `lz4`. Если при установке возникают проблемы с `node-gyp`:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install lz4 --ignore-scripts
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Примечание:** Для обычной QR-авторизации (WEB) дополнительные зависимости не нужны. Socket транспорт используется только после сохранения сессии или при явном указании `deviceType: 'IOS'`/`'ANDROID'`.
|
|
35
|
+
|
|
24
36
|
## 🚀 Быстрый старт / Quick Start
|
|
25
37
|
|
|
26
38
|
### Базовый пример / Basic Example
|
|
@@ -63,9 +75,9 @@ main().catch(console.error);
|
|
|
63
75
|
|
|
64
76
|
### Авторизация / Authentication
|
|
65
77
|
|
|
66
|
-
|
|
78
|
+
#### Способ 1: QR-код (рекомендуется для первого запуска)
|
|
67
79
|
|
|
68
|
-
|
|
80
|
+
При первом запуске вы увидите QR-код в консоли:
|
|
69
81
|
|
|
70
82
|
```
|
|
71
83
|
🔐 АВТОРИЗАЦИЯ ЧЕРЕЗ QR-КОД
|
|
@@ -76,15 +88,76 @@ On first run, you'll see a QR code in the console:
|
|
|
76
88
|
📸 Отсканируйте QR-код
|
|
77
89
|
|
|
78
90
|
█████████████████████████████
|
|
79
|
-
█████████████████████████████
|
|
80
|
-
████ ▄▄▄▄▄ █▀█ █▄▄█ ▄▄▄▄▄ ████
|
|
81
|
-
████ █ █ █▀▀▀█ ▄█ █ █ ████
|
|
82
91
|
...
|
|
83
92
|
```
|
|
84
93
|
|
|
85
|
-
После
|
|
94
|
+
После сканирования:
|
|
95
|
+
- Токен и clientSessionId сохраняются автоматически
|
|
96
|
+
- При следующем запуске клиент **автоматически переключится на TCP Socket** для стабильности
|
|
97
|
+
- Повторная авторизация не требуется
|
|
98
|
+
|
|
99
|
+
#### Способ 2: SMS авторизация (IOS/ANDROID)
|
|
100
|
+
|
|
101
|
+
Авторизация по номеру телефона с кодом из SMS:
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
const client = new WebMaxClient({
|
|
105
|
+
name: 'my_session',
|
|
106
|
+
deviceType: 'IOS' // Обязательно для SMS авторизации
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
await client.connect();
|
|
110
|
+
|
|
111
|
+
// Запрос кода
|
|
112
|
+
const authSession = await client.authorizeBySMS('+79001234567');
|
|
113
|
+
|
|
114
|
+
// Получаем код из SMS и отправляем
|
|
115
|
+
const code = '123456'; // Код из SMS
|
|
116
|
+
await authSession.sendCode(code);
|
|
117
|
+
|
|
118
|
+
// Завершаем запуск
|
|
119
|
+
await client.triggerHandlers(client.handlers.START);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Или запустите готовый пример:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
node example-sms.js
|
|
126
|
+
node example-sms.js +79001234567 # с номером в аргументе
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### Способ 3: Token авторизация
|
|
130
|
+
|
|
131
|
+
Если у вас уже есть токен (от другого сервиса/приложения):
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
const client = new WebMaxClient({
|
|
135
|
+
name: 'my_session',
|
|
136
|
+
token: 'An_Sx6HQ9HDiftNkVBNf6Q5PG5D8Oyj...', // Ваш токен
|
|
137
|
+
configPath: 'config/myconfig.json', // Или из файла
|
|
138
|
+
saveToken: true
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
await client.start();
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Формат конфига (`config/default.json`):
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"token": "An_Sx6HQ9HDiftNk...",
|
|
148
|
+
"ua": "Mozilla/5.0 (iPhone...)",
|
|
149
|
+
"device_type": 2,
|
|
150
|
+
"deviceType": "IOS"
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
#### Транспорты
|
|
86
155
|
|
|
87
|
-
|
|
156
|
+
- **WEB** (`deviceType: 'WEB'` или `device_type: 1`) → WebSocket (ws-api.oneme.ru)
|
|
157
|
+
- **IOS** (`deviceType: 'IOS'` или `device_type: 2`) → TCP Socket (api.oneme.ru)
|
|
158
|
+
- **ANDROID** (`deviceType: 'ANDROID'` или `device_type: 3`) → TCP Socket (api.oneme.ru)
|
|
159
|
+
|
|
160
|
+
Клиент **автоматически выбирает** правильный транспорт на основе сохраненного deviceType.
|
|
88
161
|
|
|
89
162
|
## API
|
|
90
163
|
|
|
@@ -97,7 +170,11 @@ After scanning, the token is saved automatically.
|
|
|
97
170
|
```javascript
|
|
98
171
|
const client = new WebMaxClient({
|
|
99
172
|
name: 'session', // Имя сессии (для сохранения авторизации)
|
|
100
|
-
|
|
173
|
+
token: 'An_Sx6H...', // Токен авторизации (опционально)
|
|
174
|
+
configPath: 'myconfig', // Путь к config файлу (опционально)
|
|
175
|
+
deviceType: 'WEB', // Тип устройства: 'WEB', 'IOS', 'ANDROID' (опционально)
|
|
176
|
+
saveToken: true, // Сохранять токен в сессию (по умолчанию true)
|
|
177
|
+
debug: false, // Отладочный режим (опционально)
|
|
101
178
|
apiUrl: 'wss://...', // URL WebSocket API (опционально)
|
|
102
179
|
maxReconnectAttempts: 5,// Максимальное количество попыток переподключения
|
|
103
180
|
reconnectDelay: 3000 // Задержка между попытками переподключения (мс)
|
|
@@ -114,9 +191,24 @@ const client = new WebMaxClient({
|
|
|
114
191
|
await client.start();
|
|
115
192
|
```
|
|
116
193
|
|
|
194
|
+
##### `authorizeBySMS(phone)`
|
|
195
|
+
|
|
196
|
+
Авторизация по номеру телефона через SMS (только для IOS/ANDROID).
|
|
197
|
+
|
|
198
|
+
```javascript
|
|
199
|
+
// Подключаемся
|
|
200
|
+
await client.connect();
|
|
201
|
+
|
|
202
|
+
// Запрашиваем код
|
|
203
|
+
const authSession = await client.authorizeBySMS('+79001234567');
|
|
204
|
+
|
|
205
|
+
// Вводим код из SMS
|
|
206
|
+
await authSession.sendCode('123456');
|
|
207
|
+
```
|
|
208
|
+
|
|
117
209
|
##### `sendMessage(options)`
|
|
118
210
|
|
|
119
|
-
Отправляет сообщение в
|
|
211
|
+
Отправляет сообщение в чат с уведомлением (notify: true).
|
|
120
212
|
|
|
121
213
|
```javascript
|
|
122
214
|
const message = await client.sendMessage({
|
|
@@ -128,6 +220,20 @@ const message = await client.sendMessage({
|
|
|
128
220
|
});
|
|
129
221
|
```
|
|
130
222
|
|
|
223
|
+
##### `sendMessageChannel(options)`
|
|
224
|
+
|
|
225
|
+
Отправляет сообщение в канал без уведомления (notify: false).
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
const message = await client.sendMessageChannel({
|
|
229
|
+
chatId: 123,
|
|
230
|
+
text: 'Сообщение в канал',
|
|
231
|
+
cid: Date.now(),
|
|
232
|
+
replyTo: null, // ID сообщения для ответа (опционально)
|
|
233
|
+
attachments: [] // Вложения (опционально)
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
131
237
|
##### `editMessage(options)`
|
|
132
238
|
|
|
133
239
|
Редактирует сообщение.
|
|
@@ -362,22 +468,94 @@ ChatActions.RECORDING_VOICE // Записывает голосовое
|
|
|
362
468
|
ChatActions.RECORDING_VIDEO // Записывает видео
|
|
363
469
|
```
|
|
364
470
|
|
|
471
|
+
### MaxSocketTransport
|
|
472
|
+
|
|
473
|
+
Низкоуровневый TCP Socket транспорт для IOS/ANDROID (api.oneme.ru).
|
|
474
|
+
|
|
475
|
+
#### Прямое использование (advanced)
|
|
476
|
+
|
|
477
|
+
```javascript
|
|
478
|
+
const { MaxSocketTransport } = require('webmaxsocket');
|
|
479
|
+
|
|
480
|
+
const transport = new MaxSocketTransport({
|
|
481
|
+
deviceType: 'IOS',
|
|
482
|
+
ua: 'Mozilla/5.0 (iPhone...)',
|
|
483
|
+
deviceId: 'your-device-id',
|
|
484
|
+
debug: true
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
await transport.connect();
|
|
488
|
+
await transport.handshake(userAgentPayload);
|
|
489
|
+
const syncData = await transport.sync(token, userAgent);
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
**Примечание:** В большинстве случаев используйте `WebMaxClient`, который автоматически выбирает нужный транспорт.
|
|
493
|
+
|
|
494
|
+
## 📚 Примеры
|
|
495
|
+
|
|
496
|
+
### Пример 1: QR-авторизация (example.js)
|
|
497
|
+
|
|
498
|
+
```bash
|
|
499
|
+
node example.js
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
Первый запуск - QR-авторизация, повторные запуски - автоматический вход через TCP Socket.
|
|
503
|
+
|
|
504
|
+
### Пример 2: Token авторизация (example-token.js)
|
|
505
|
+
|
|
506
|
+
```bash
|
|
507
|
+
# Через config файл
|
|
508
|
+
node example-token.js
|
|
509
|
+
node example-token.js myconfig # config/myconfig.json
|
|
510
|
+
|
|
511
|
+
# Через переменную окружения
|
|
512
|
+
TOKEN="ваш_токен" node example-token.js
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### Пример 3: SMS авторизация (example-sms.js)
|
|
516
|
+
|
|
517
|
+
```bash
|
|
518
|
+
# Интерактивный ввод номера
|
|
519
|
+
node example-sms.js
|
|
520
|
+
|
|
521
|
+
# С номером в аргументе
|
|
522
|
+
node example-sms.js +79001234567
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### Пример 4: IOS/ANDROID Socket (example-ios.js)
|
|
526
|
+
|
|
527
|
+
```bash
|
|
528
|
+
# С готовым конфигом
|
|
529
|
+
node example-ios.js
|
|
530
|
+
|
|
531
|
+
# С отладкой
|
|
532
|
+
node example-ios.js --debug
|
|
533
|
+
```
|
|
534
|
+
|
|
365
535
|
## Структура проекта
|
|
366
536
|
|
|
367
537
|
```
|
|
368
538
|
webmaxsocket/
|
|
369
539
|
├── lib/
|
|
370
540
|
│ ├── client.js # Основной клиент
|
|
541
|
+
│ ├── socketTransport.js # TCP Socket транспорт
|
|
371
542
|
│ ├── session.js # Управление сессиями
|
|
543
|
+
│ ├── userAgent.js # UserAgent генератор
|
|
544
|
+
│ ├── opcodes.js # Протокол опкоды
|
|
372
545
|
│ ├── constants.js # Константы
|
|
373
546
|
│ └── entities/
|
|
374
547
|
│ ├── User.js # Класс пользователя
|
|
375
548
|
│ ├── Message.js # Класс сообщения
|
|
376
549
|
│ ├── ChatAction.js # Класс действия в чате
|
|
377
550
|
│ └── index.js # Экспорт сущностей
|
|
551
|
+
├── config/ # Конфигурационные файлы
|
|
552
|
+
│ └── example.json # Пример конфига
|
|
378
553
|
├── sessions/ # Директория с сохраненными сессиями
|
|
379
554
|
├── index.js # Точка входа
|
|
380
|
-
├── example.js #
|
|
555
|
+
├── example.js # QR-авторизация
|
|
556
|
+
├── example-token.js # Token авторизация
|
|
557
|
+
├── example-sms.js # SMS авторизация
|
|
558
|
+
├── example-ios.js # IOS/ANDROID Socket
|
|
381
559
|
├── package.json
|
|
382
560
|
└── README.md
|
|
383
561
|
```
|
|
@@ -408,4 +586,52 @@ try {
|
|
|
408
586
|
} catch (error) {
|
|
409
587
|
console.error('Ошибка:', error.message);
|
|
410
588
|
}
|
|
411
|
-
```
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
## 🔧 Отладка / Debug
|
|
592
|
+
|
|
593
|
+
Для включения отладочного вывода:
|
|
594
|
+
|
|
595
|
+
```javascript
|
|
596
|
+
const client = new WebMaxClient({
|
|
597
|
+
name: 'my_session',
|
|
598
|
+
debug: true // или process.env.DEBUG = '1'
|
|
599
|
+
});
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
Или через переменную окружения:
|
|
603
|
+
|
|
604
|
+
```bash
|
|
605
|
+
DEBUG=1 node example.js
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
## 💡 Важные замечания
|
|
609
|
+
|
|
610
|
+
1. **TCP Socket после QR-авторизации:** После первой успешной QR-авторизации клиент автоматически сохраняет `clientSessionId` и переключается на TCP Socket транспорт при следующем запуске для повышения стабильности.
|
|
611
|
+
|
|
612
|
+
2. **Разница между sendMessage и sendMessageChannel:**
|
|
613
|
+
- `sendMessage()` - отправка с уведомлением (notify: true) для обычных чатов
|
|
614
|
+
- `sendMessageChannel()` - отправка без уведомления (notify: false) для каналов
|
|
615
|
+
|
|
616
|
+
3. **Автоматический выбор транспорта:** Клиент автоматически определяет какой транспорт использовать на основе `deviceType` в сессии или config файле.
|
|
617
|
+
|
|
618
|
+
## 🔗 Ссылки / Links
|
|
619
|
+
|
|
620
|
+
- [GitHub Repository](https://github.com/Tellarion/webmaxsocket)
|
|
621
|
+
- [NPM Package](https://www.npmjs.com/package/webmaxsocket)
|
|
622
|
+
|
|
623
|
+
## 📄 Лицензия / License
|
|
624
|
+
|
|
625
|
+
MIT License - see LICENSE file for details
|
|
626
|
+
|
|
627
|
+
## 👤 Автор / Author
|
|
628
|
+
|
|
629
|
+
Tellarion - [tellarion.dev](https://tellarion.dev)
|
|
630
|
+
|
|
631
|
+
## 💝 Поддержка / Support
|
|
632
|
+
|
|
633
|
+
Если вам нравится эта библиотека и вы хотите поддержать разработку:
|
|
634
|
+
|
|
635
|
+
**USDT (TRC20):** `TXfs1iVbp2aLd3rbc4cenVzMoTevP5RbBE`
|
|
636
|
+
|
|
637
|
+
Спасибо за вашу поддержку!
|
package/example-ios.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IOS/ANDROID: вход через Socket (api.oneme.ru), не WebSocket
|
|
3
|
+
* WEB: через WebSocket (ws-api.oneme.ru)
|
|
4
|
+
*
|
|
5
|
+
* С готовым конфигом: node example-ios.js
|
|
6
|
+
* Без токена — запрос телефона и SMS
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const { WebMaxClient, MaxSocketTransport } = require('./index');
|
|
12
|
+
const { UserAgentPayload } = require('./lib/userAgent');
|
|
13
|
+
const { Opcode } = require('./lib/opcodes');
|
|
14
|
+
|
|
15
|
+
const argv = process.argv.slice(2).filter((a) => a !== '--debug' && a !== '-d');
|
|
16
|
+
if (process.argv.includes('--debug') || process.argv.includes('-d')) process.env.DEBUG = '1';
|
|
17
|
+
const CONFIG_NAME = argv[0] || process.env.CONFIG || 'default';
|
|
18
|
+
|
|
19
|
+
function loadConfig() {
|
|
20
|
+
const base = path.join(process.cwd(), 'config');
|
|
21
|
+
const p = path.join(base, CONFIG_NAME + (CONFIG_NAME.endsWith('.json') ? '' : '.json'));
|
|
22
|
+
if (!fs.existsSync(p)) return null;
|
|
23
|
+
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function configToUserAgent(config) {
|
|
27
|
+
const dt = config.deviceType || (config.device_type === 2 ? 'IOS' : config.device_type === 3 ? 'ANDROID' : 'WEB');
|
|
28
|
+
return new UserAgentPayload({
|
|
29
|
+
deviceType: dt,
|
|
30
|
+
locale: config.locale || 'ru',
|
|
31
|
+
deviceLocale: config.deviceLocale || config.locale || 'ru',
|
|
32
|
+
osVersion: config.osVersion || '18.6.2',
|
|
33
|
+
deviceName: config.deviceName || 'Safari',
|
|
34
|
+
headerUserAgent: config.headerUserAgent || config.ua || '',
|
|
35
|
+
appVersion: config.appVersion || '25.12.14',
|
|
36
|
+
screen: config.screen || '390x844 3.0x',
|
|
37
|
+
timezone: config.timezone || 'Europe/Moscow',
|
|
38
|
+
buildNumber: config.buildNumber,
|
|
39
|
+
clientSessionId: config.clientSessionId,
|
|
40
|
+
release: config.release
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function useSocket(config) {
|
|
45
|
+
const dt = config.deviceType || config.device_type;
|
|
46
|
+
return dt === 'IOS' || dt === 'ANDROID' || dt === 2 || dt === 3;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function main() {
|
|
50
|
+
const config = loadConfig();
|
|
51
|
+
const hasToken = config && config.token && config.token.length >= 50;
|
|
52
|
+
|
|
53
|
+
if (hasToken && useSocket(config)) {
|
|
54
|
+
console.log('🚀 Socket (api.oneme.ru) — IOS/ANDROID\n');
|
|
55
|
+
const ua = configToUserAgent(config);
|
|
56
|
+
const transport = new MaxSocketTransport({
|
|
57
|
+
deviceType: config.deviceType || (config.device_type === 2 ? 'IOS' : 'ANDROID'),
|
|
58
|
+
ua: config.headerUserAgent || config.ua,
|
|
59
|
+
deviceId: config.deviceId || require('uuid').v4(),
|
|
60
|
+
debug: process.env.DEBUG === '1'
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
transport.onNotification = (data) => {
|
|
64
|
+
if (data.opcode === Opcode.NOTIF_MESSAGE && data.payload) {
|
|
65
|
+
const m = data.payload;
|
|
66
|
+
if (m.senderId !== transport.me?.id) {
|
|
67
|
+
console.log('💬', (m.sender?.firstName || 'User') + ':', m.text || '[вложение]');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
await transport.connect();
|
|
73
|
+
await transport.handshake(ua);
|
|
74
|
+
|
|
75
|
+
const syncResp = await transport.sync(config.token, ua.toJSON());
|
|
76
|
+
|
|
77
|
+
if (process.env.DEBUG === '1') {
|
|
78
|
+
const s = JSON.stringify(syncResp, null, 2);
|
|
79
|
+
console.log('\n📋 Sync (сырой):', s.slice(0, 3000) + (s.length > 3000 ? '\n...' : ''));
|
|
80
|
+
}
|
|
81
|
+
console.log('\n📋 Ключи sync:', Object.keys(syncResp || {}).join(', '));
|
|
82
|
+
|
|
83
|
+
const contact = syncResp?.profile?.contact ?? syncResp?.profile?.user ?? syncResp?.contact;
|
|
84
|
+
if (contact) {
|
|
85
|
+
const names = contact.names || [];
|
|
86
|
+
const name = Array.isArray(names) ? names[0] : names;
|
|
87
|
+
const first = name?.firstName ?? name?.name ?? '';
|
|
88
|
+
const last = name?.lastName ?? '';
|
|
89
|
+
transport.me = {
|
|
90
|
+
id: contact.id,
|
|
91
|
+
firstname: first,
|
|
92
|
+
lastname: last,
|
|
93
|
+
fullname: [first, last].filter(Boolean).join(' ') || 'User'
|
|
94
|
+
};
|
|
95
|
+
console.log('\n👤 Профиль:');
|
|
96
|
+
console.log(' ID:', contact.id);
|
|
97
|
+
console.log(' Имя:', transport.me.fullname);
|
|
98
|
+
console.log(' Телефон:', contact.phone ? '+' + contact.phone : '—');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const syncChats = syncResp?.chats ?? syncResp?.chatList ?? syncResp?.list ?? [];
|
|
102
|
+
const chats = await transport.getChats();
|
|
103
|
+
const allChats = syncChats.length ? syncChats : chats;
|
|
104
|
+
console.log('\n📂 Диалоги (' + allChats.length + '):');
|
|
105
|
+
allChats.slice(0, 20).forEach((c, i) => {
|
|
106
|
+
const t = c.title ?? c.name ?? c.chat?.title ?? ('Chat ' + (c.id ?? c.chatId ?? i));
|
|
107
|
+
const lastMsg = c.lastMessage?.text ?? c.lastMessage?.message ?? '—';
|
|
108
|
+
console.log(` ${i + 1}. ${t} — ${String(lastMsg).slice(0, 35)}`);
|
|
109
|
+
});
|
|
110
|
+
if (allChats.length > 20) console.log(' ... и ещё', allChats.length - 20);
|
|
111
|
+
|
|
112
|
+
const contacts = syncResp?.contacts || [];
|
|
113
|
+
if (contacts.length) {
|
|
114
|
+
console.log('\n📇 Контакты:', contacts.length);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log('\n🤖 Socket работает (Ctrl+C — выход)\n');
|
|
118
|
+
process.on('SIGINT', () => { transport.close(); process.exit(0); });
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (hasToken) {
|
|
123
|
+
const client = new WebMaxClient({
|
|
124
|
+
name: 'web_session',
|
|
125
|
+
configPath: CONFIG_NAME,
|
|
126
|
+
saveToken: false,
|
|
127
|
+
debug: process.env.DEBUG === '1'
|
|
128
|
+
});
|
|
129
|
+
client.onStart(async () => {
|
|
130
|
+
if (client.me) console.log('\n✅ Вход:', client.me.fullname || client.me.firstname, '(ID:', client.me.id + ')\n');
|
|
131
|
+
try { console.log('📂 Диалогов:', (await client.getChats()).length); } catch (e) { console.log('⚠️', e.message); }
|
|
132
|
+
});
|
|
133
|
+
client.onMessage((m) => { if (m.senderId !== client.me?.id) console.log('💬', m.getSenderName() + ':', m.text); });
|
|
134
|
+
client.onError((e) => console.error('❌', e.message));
|
|
135
|
+
await client.start();
|
|
136
|
+
console.log('🤖 Бот работает (Ctrl+C — выход)\n');
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const readline = require('readline');
|
|
141
|
+
const { v4: uuidv4 } = require('uuid');
|
|
142
|
+
const IOS_UA = 'Mozilla/5.0 (iPhone15,2; CPU iPhone OS 18_6_2 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/602.1.50';
|
|
143
|
+
|
|
144
|
+
function ask(q) {
|
|
145
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
146
|
+
return new Promise((r) => rl.question(q, (a) => { rl.close(); r(a.trim()); }));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
console.log('📱 Получение токена по телефону (Socket)\n');
|
|
150
|
+
const phone = await ask('Номер (+79xxxxxxxxx): ');
|
|
151
|
+
if (!/^\+?\d{10,15}$/.test(phone.replace(/\s/g, ''))) { console.error('❌ Неверный формат'); process.exit(1); }
|
|
152
|
+
let clean = phone.replace(/\D/g, '');
|
|
153
|
+
if (clean.startsWith('8') && clean.length === 11) clean = '7' + clean.slice(1);
|
|
154
|
+
else if (clean.startsWith('9') && clean.length === 10) clean = '7' + clean;
|
|
155
|
+
const norm = '+' + clean;
|
|
156
|
+
|
|
157
|
+
const transport = new MaxSocketTransport({ deviceType: 'IOS', ua: IOS_UA, deviceId: uuidv4(), debug: process.env.DEBUG === '1' });
|
|
158
|
+
await transport.connect();
|
|
159
|
+
await transport.handshake(new UserAgentPayload({ deviceType: 'IOS', headerUserAgent: IOS_UA, appVersion: '25.12.14', osVersion: '18.6.2', deviceName: 'Safari', screen: '390x844 3.0x', locale: 'ru', deviceLocale: 'ru', timezone: 'Europe/Moscow' }));
|
|
160
|
+
|
|
161
|
+
console.log('📤 Запрос кода...');
|
|
162
|
+
const tempToken = await transport.requestCode(norm);
|
|
163
|
+
if (!tempToken) throw new Error('Не получен временный токен');
|
|
164
|
+
const code = await ask('Код из SMS (6 цифр): ');
|
|
165
|
+
if (!/^\d{6}$/.test(code)) throw new Error('Неверный код');
|
|
166
|
+
|
|
167
|
+
const authResp = await transport.sendCode(tempToken, code);
|
|
168
|
+
const token = authResp?.tokenAttrs?.LOGIN?.token;
|
|
169
|
+
if (!token) throw new Error(authResp?.passwordChallenge ? '2FA не поддерживается' : 'Токен не получен');
|
|
170
|
+
await transport.close();
|
|
171
|
+
|
|
172
|
+
const cfg = { token, ua: IOS_UA, device_type: 2, deviceType: 'IOS' };
|
|
173
|
+
const dir = path.join(process.cwd(), 'config');
|
|
174
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
175
|
+
fs.writeFileSync(path.join(dir, CONFIG_NAME + '.json'), JSON.stringify(cfg, null, 2), 'utf8');
|
|
176
|
+
console.log('\n✅ Конфиг сохранён:', path.join(dir, CONFIG_NAME + '.json'));
|
|
177
|
+
console.log(' Запуск: node example-ios.js', CONFIG_NAME);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
main().catch((e) => { console.error('❌', e.message); process.exit(1); });
|
|
181
|
+
process.on('SIGINT', () => {
|
|
182
|
+
console.log('\n\n👋 Завершение работы...');
|
|
183
|
+
console.log('\n💝 Нравится библиотека? Поддержите разработку:');
|
|
184
|
+
console.log(' USDT (TRC20): TXfs1iVbp2aLd3rbc4cenVzMoTevP5RbBE');
|
|
185
|
+
process.exit(0);
|
|
186
|
+
});
|
package/example-sms.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Пример авторизации по SMS (IOS/ANDROID)
|
|
3
|
+
*
|
|
4
|
+
* Использование:
|
|
5
|
+
* node example-sms.js
|
|
6
|
+
* node example-sms.js +79001234567 # с номером в аргументе
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const readline = require('readline');
|
|
10
|
+
const { WebMaxClient } = require('./index');
|
|
11
|
+
|
|
12
|
+
function ask(question) {
|
|
13
|
+
const rl = readline.createInterface({
|
|
14
|
+
input: process.stdin,
|
|
15
|
+
output: process.stdout
|
|
16
|
+
});
|
|
17
|
+
return new Promise((resolve) => {
|
|
18
|
+
rl.question(question, (answer) => {
|
|
19
|
+
rl.close();
|
|
20
|
+
resolve(answer.trim());
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function main() {
|
|
26
|
+
// Получаем номер телефона из аргумента или запрашиваем
|
|
27
|
+
let phone = process.argv[2];
|
|
28
|
+
|
|
29
|
+
if (!phone) {
|
|
30
|
+
phone = await ask('📱 Введите номер телефона (+79001234567): ');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Валидация номера
|
|
34
|
+
if (!/^\+?\d{10,15}$/.test(phone.replace(/\s/g, ''))) {
|
|
35
|
+
console.error('❌ Неверный формат номера телефона');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log('\n🚀 Запуск клиента с SMS авторизацией...\n');
|
|
40
|
+
|
|
41
|
+
// Создаем клиент с IOS deviceType для SMS авторизации
|
|
42
|
+
const client = new WebMaxClient({
|
|
43
|
+
name: 'sms_session',
|
|
44
|
+
deviceType: 'IOS', // Обязательно для SMS авторизации
|
|
45
|
+
debug: process.env.DEBUG === '1'
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Обработчик запуска
|
|
49
|
+
client.onStart(async () => {
|
|
50
|
+
if (client.me) {
|
|
51
|
+
console.log('\n📋 ДАННЫЕ ПОЛЬЗОВАТЕЛЯ:');
|
|
52
|
+
console.log('─'.repeat(40));
|
|
53
|
+
console.log(`👤 Имя: ${client.me.fullname || client.me.firstname}`);
|
|
54
|
+
console.log(`🆔 ID: ${client.me.id}`);
|
|
55
|
+
console.log(`📱 Телефон: +${client.me.phone || '—'}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const chats = await client.getChats();
|
|
60
|
+
console.log(`\n📂 Диалогов: ${chats.length}`);
|
|
61
|
+
} catch (e) {
|
|
62
|
+
console.log('⚠️ Не удалось загрузить диалоги:', e.message);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Обработчик сообщений
|
|
67
|
+
client.onMessage(async (message) => {
|
|
68
|
+
if (message.senderId === client.me?.id) return;
|
|
69
|
+
console.log(`\n💬 ${message.getSenderName()}: ${message.text}`);
|
|
70
|
+
|
|
71
|
+
await message.reply({
|
|
72
|
+
text: 'Автоответ: сообщение получено!',
|
|
73
|
+
cid: Date.now()
|
|
74
|
+
});
|
|
75
|
+
console.log('✅ Отправлен автоответ');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
client.onError((err) => console.error('❌', err.message));
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
// Подключаемся
|
|
82
|
+
await client.connect();
|
|
83
|
+
|
|
84
|
+
// Проверяем есть ли сохраненный токен
|
|
85
|
+
const savedToken = client.session.get('token');
|
|
86
|
+
|
|
87
|
+
if (savedToken) {
|
|
88
|
+
console.log('✅ Найдена сохраненная сессия, вход по токену...\n');
|
|
89
|
+
client._token = savedToken;
|
|
90
|
+
await client.sync();
|
|
91
|
+
client.isAuthorized = true;
|
|
92
|
+
} else {
|
|
93
|
+
// SMS авторизация
|
|
94
|
+
console.log('📱 Требуется SMS авторизация\n');
|
|
95
|
+
const authSession = await client.authorizeBySMS(phone);
|
|
96
|
+
|
|
97
|
+
// Запрашиваем код
|
|
98
|
+
const code = await ask('\n📲 Введите код из SMS (6 цифр): ');
|
|
99
|
+
|
|
100
|
+
if (!/^\d{6}$/.test(code)) {
|
|
101
|
+
console.error('❌ Неверный формат кода');
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Отправляем код и завершаем авторизацию
|
|
106
|
+
await authSession.sendCode(code);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Запускаем обработчики start
|
|
110
|
+
await client.triggerHandlers(client.handlers.START);
|
|
111
|
+
|
|
112
|
+
console.log('\n✅ Клиент запущен успешно!');
|
|
113
|
+
console.log('🤖 Бот работает (Ctrl+C — выход)\n');
|
|
114
|
+
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error('❌ Ошибка:', error.message);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
process.on('SIGINT', () => {
|
|
122
|
+
console.log('\n\n👋 Завершение работы...');
|
|
123
|
+
console.log('\n💝 Нравится библиотека? Поддержите разработку:');
|
|
124
|
+
console.log(' USDT (TRC20): TXfs1iVbp2aLd3rbc4cenVzMoTevP5RbBE');
|
|
125
|
+
process.exit(0);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
main().catch((error) => {
|
|
129
|
+
console.error('❌ Критическая ошибка:', error);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
});
|