@kangwifi-pro/waliwa 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +600 -0
- package/dist/advanced/features.d.ts +211 -0
- package/dist/advanced/features.d.ts.map +1 -0
- package/dist/advanced/features.js +671 -0
- package/dist/advanced/features.js.map +1 -0
- package/dist/auth/auth-state.d.ts +89 -0
- package/dist/auth/auth-state.d.ts.map +1 -0
- package/dist/auth/auth-state.js +330 -0
- package/dist/auth/auth-state.js.map +1 -0
- package/dist/auth/pairing-code.d.ts +78 -0
- package/dist/auth/pairing-code.d.ts.map +1 -0
- package/dist/auth/pairing-code.js +191 -0
- package/dist/auth/pairing-code.js.map +1 -0
- package/dist/auth/qr-code.d.ts +60 -0
- package/dist/auth/qr-code.d.ts.map +1 -0
- package/dist/auth/qr-code.js +151 -0
- package/dist/auth/qr-code.js.map +1 -0
- package/dist/calls/handler.d.ts +61 -0
- package/dist/calls/handler.d.ts.map +1 -0
- package/dist/calls/handler.js +117 -0
- package/dist/calls/handler.js.map +1 -0
- package/dist/core/binary.d.ts +74 -0
- package/dist/core/binary.d.ts.map +1 -0
- package/dist/core/binary.js +448 -0
- package/dist/core/binary.js.map +1 -0
- package/dist/core/crypto.d.ts +60 -0
- package/dist/core/crypto.d.ts.map +1 -0
- package/dist/core/crypto.js +170 -0
- package/dist/core/crypto.js.map +1 -0
- package/dist/core/noise.d.ts +106 -0
- package/dist/core/noise.d.ts.map +1 -0
- package/dist/core/noise.js +307 -0
- package/dist/core/noise.js.map +1 -0
- package/dist/events/emitter.d.ts +44 -0
- package/dist/events/emitter.d.ts.map +1 -0
- package/dist/events/emitter.js +79 -0
- package/dist/events/emitter.js.map +1 -0
- package/dist/features/index.d.ts +342 -0
- package/dist/features/index.d.ts.map +1 -0
- package/dist/features/index.js +755 -0
- package/dist/features/index.js.map +1 -0
- package/dist/fixes/index.d.ts +333 -0
- package/dist/fixes/index.d.ts.map +1 -0
- package/dist/fixes/index.js +762 -0
- package/dist/fixes/index.js.map +1 -0
- package/dist/groups/management.d.ts +86 -0
- package/dist/groups/management.d.ts.map +1 -0
- package/dist/groups/management.js +443 -0
- package/dist/groups/management.js.map +1 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +236 -0
- package/dist/index.js.map +1 -0
- package/dist/messages/media.d.ts +93 -0
- package/dist/messages/media.d.ts.map +1 -0
- package/dist/messages/media.js +252 -0
- package/dist/messages/media.js.map +1 -0
- package/dist/messages/message-encoder.d.ts +70 -0
- package/dist/messages/message-encoder.d.ts.map +1 -0
- package/dist/messages/message-encoder.js +453 -0
- package/dist/messages/message-encoder.js.map +1 -0
- package/dist/messages/send.d.ts +101 -0
- package/dist/messages/send.d.ts.map +1 -0
- package/dist/messages/send.js +409 -0
- package/dist/messages/send.js.map +1 -0
- package/dist/recovery/index.d.ts +125 -0
- package/dist/recovery/index.d.ts.map +1 -0
- package/dist/recovery/index.js +584 -0
- package/dist/recovery/index.js.map +1 -0
- package/dist/skdm/index.d.ts +220 -0
- package/dist/skdm/index.d.ts.map +1 -0
- package/dist/skdm/index.js +600 -0
- package/dist/skdm/index.js.map +1 -0
- package/dist/socket/hybrid.d.ts +118 -0
- package/dist/socket/hybrid.d.ts.map +1 -0
- package/dist/socket/hybrid.js +352 -0
- package/dist/socket/hybrid.js.map +1 -0
- package/dist/socket/wa-socket.d.ts +300 -0
- package/dist/socket/wa-socket.d.ts.map +1 -0
- package/dist/socket/wa-socket.js +1094 -0
- package/dist/socket/wa-socket.js.map +1 -0
- package/dist/socket/ws-socket.d.ts +96 -0
- package/dist/socket/ws-socket.d.ts.map +1 -0
- package/dist/socket/ws-socket.js +302 -0
- package/dist/socket/ws-socket.js.map +1 -0
- package/dist/types/index.d.ts +444 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/jid.d.ts +46 -0
- package/dist/utils/jid.d.ts.map +1 -0
- package/dist/utils/jid.js +152 -0
- package/dist/utils/jid.js.map +1 -0
- package/dist/utils/logger.d.ts +45 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +79 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/retry.d.ts +43 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +175 -0
- package/dist/utils/retry.js.map +1 -0
- package/docs/API.md +745 -0
- package/docs/ARCHITECTURE.md +307 -0
- package/docs/BAILEYS_FIXES.md +360 -0
- package/docs/FEATURES.md +532 -0
- package/docs/PROTOCOL.md +489 -0
- package/docs/RECOVERY.md +409 -0
- package/docs/SKDM.md +233 -0
- package/examples/ai-bot/index.ts +479 -0
- package/examples/ai-bot/package.json +17 -0
- package/examples/echo-bot/index.ts +171 -0
- package/examples/echo-bot/package.json +18 -0
- package/examples/group-bot/index.ts +557 -0
- package/examples/group-bot/package.json +17 -0
- package/examples/rest-gateway/index.ts +499 -0
- package/examples/rest-gateway/package.json +19 -0
- package/package.json +75 -0
- package/src/advanced/features.ts +817 -0
- package/src/auth/auth-state.ts +342 -0
- package/src/auth/pairing-code.ts +246 -0
- package/src/auth/qr-code.ts +191 -0
- package/src/calls/handler.ts +153 -0
- package/src/core/binary.ts +464 -0
- package/src/core/crypto.ts +189 -0
- package/src/core/noise.ts +406 -0
- package/src/events/emitter.ts +88 -0
- package/src/features/index.ts +921 -0
- package/src/fixes/index.ts +882 -0
- package/src/groups/management.ts +497 -0
- package/src/index.ts +274 -0
- package/src/messages/media.ts +372 -0
- package/src/messages/message-encoder.ts +520 -0
- package/src/messages/send.ts +521 -0
- package/src/recovery/index.ts +704 -0
- package/src/skdm/index.ts +693 -0
- package/src/socket/hybrid.ts +414 -0
- package/src/socket/wa-socket.ts +1347 -0
- package/src/socket/ws-socket.ts +355 -0
- package/src/types/index.ts +457 -0
- package/src/utils/jid.ts +156 -0
- package/src/utils/logger.ts +84 -0
- package/src/utils/retry.ts +205 -0
- package/tsconfig.json +30 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Waliwa - Group management
|
|
3
|
+
* API untuk create, modify, invite, kick, promote, demote group members
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
WAGroupMetadata,
|
|
8
|
+
WAGroupParticipant,
|
|
9
|
+
GroupCreateOptions,
|
|
10
|
+
JIDLike,
|
|
11
|
+
WALogger
|
|
12
|
+
} from '../types/index.js';
|
|
13
|
+
import { encodeBinaryNode, BinaryNode } from '../core/binary.js';
|
|
14
|
+
import { jidToString, parseJid } from '../utils/jid.js';
|
|
15
|
+
import { generateStanzaId } from '../messages/message-encoder.js';
|
|
16
|
+
import { SilentLogger } from '../utils/logger.js';
|
|
17
|
+
|
|
18
|
+
// ============== Group Sender ==============
|
|
19
|
+
|
|
20
|
+
export interface GroupManagerOptions {
|
|
21
|
+
/** Send raw bytes via WebSocket */
|
|
22
|
+
send: (data: Uint8Array) => void;
|
|
23
|
+
logger?: WALogger;
|
|
24
|
+
/** IQ response handler registry */
|
|
25
|
+
registerIqHandler: (id: string, handler: (node: BinaryNode) => void) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class GroupManager {
|
|
29
|
+
private send: (data: Uint8Array) => void;
|
|
30
|
+
private logger: WALogger;
|
|
31
|
+
private registerIqHandler: (id: string, handler: (node: BinaryNode) => void) => void;
|
|
32
|
+
|
|
33
|
+
constructor(opts: GroupManagerOptions) {
|
|
34
|
+
this.send = opts.send;
|
|
35
|
+
this.logger = opts.logger ?? new SilentLogger();
|
|
36
|
+
this.registerIqHandler = opts.registerIqHandler;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create new group
|
|
41
|
+
*/
|
|
42
|
+
async createGroup(opts: GroupCreateOptions): Promise<WAGroupMetadata> {
|
|
43
|
+
const id = generateStanzaId();
|
|
44
|
+
this.logger.debug(`Creating group "${opts.subject}" with ${opts.participants.length} participants`);
|
|
45
|
+
|
|
46
|
+
const participantNodes = opts.participants.map(p => ({
|
|
47
|
+
tag: 'participant',
|
|
48
|
+
attrs: { jid: jidToString(p) }
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
const node: BinaryNode = {
|
|
52
|
+
tag: 'iq',
|
|
53
|
+
attrs: {
|
|
54
|
+
type: 'set',
|
|
55
|
+
to: 'g.us',
|
|
56
|
+
id
|
|
57
|
+
},
|
|
58
|
+
content: [
|
|
59
|
+
{
|
|
60
|
+
tag: 'create',
|
|
61
|
+
attrs: {
|
|
62
|
+
subject: opts.subject
|
|
63
|
+
},
|
|
64
|
+
content: participantNodes
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return this.sendIqAndWait(node, id, 30000).then(response => this.parseGroupMetadata(response));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get group metadata
|
|
74
|
+
*/
|
|
75
|
+
async getGroupMetadata(groupJid: JIDLike): Promise<WAGroupMetadata> {
|
|
76
|
+
const id = generateStanzaId();
|
|
77
|
+
const node: BinaryNode = {
|
|
78
|
+
tag: 'iq',
|
|
79
|
+
attrs: {
|
|
80
|
+
type: 'get',
|
|
81
|
+
to: jidToString(groupJid),
|
|
82
|
+
id
|
|
83
|
+
},
|
|
84
|
+
content: [
|
|
85
|
+
{
|
|
86
|
+
tag: 'query',
|
|
87
|
+
attrs: { request: 'interactive' }
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return this.sendIqAndWait(node, id, 15000).then(response => this.parseGroupMetadata(response));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Update group subject (name)
|
|
97
|
+
*/
|
|
98
|
+
async updateGroupSubject(groupJid: JIDLike, subject: string): Promise<void> {
|
|
99
|
+
const id = generateStanzaId();
|
|
100
|
+
const node: BinaryNode = {
|
|
101
|
+
tag: 'iq',
|
|
102
|
+
attrs: {
|
|
103
|
+
type: 'set',
|
|
104
|
+
to: jidToString(groupJid),
|
|
105
|
+
id
|
|
106
|
+
},
|
|
107
|
+
content: [
|
|
108
|
+
{
|
|
109
|
+
tag: 'subject',
|
|
110
|
+
attrs: { subject }
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
await this.sendIqAndWait(node, id, 10000);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Update group description
|
|
120
|
+
*/
|
|
121
|
+
async updateGroupDescription(groupJid: JIDLike, desc: string): Promise<void> {
|
|
122
|
+
const id = generateStanzaId();
|
|
123
|
+
const node: BinaryNode = {
|
|
124
|
+
tag: 'iq',
|
|
125
|
+
attrs: {
|
|
126
|
+
type: 'set',
|
|
127
|
+
to: jidToString(groupJid),
|
|
128
|
+
id
|
|
129
|
+
},
|
|
130
|
+
content: [
|
|
131
|
+
{
|
|
132
|
+
tag: 'description',
|
|
133
|
+
attrs: {},
|
|
134
|
+
content: desc
|
|
135
|
+
}
|
|
136
|
+
]
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
await this.sendIqAndWait(node, id, 10000);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Add participants to group
|
|
144
|
+
*/
|
|
145
|
+
async addParticipants(groupJid: JIDLike, participants: JIDLike[]): Promise<void> {
|
|
146
|
+
const id = generateStanzaId();
|
|
147
|
+
const node: BinaryNode = {
|
|
148
|
+
tag: 'iq',
|
|
149
|
+
attrs: {
|
|
150
|
+
type: 'set',
|
|
151
|
+
to: jidToString(groupJid),
|
|
152
|
+
id
|
|
153
|
+
},
|
|
154
|
+
content: [
|
|
155
|
+
{
|
|
156
|
+
tag: 'add',
|
|
157
|
+
attrs: {},
|
|
158
|
+
content: participants.map(p => ({
|
|
159
|
+
tag: 'participant',
|
|
160
|
+
attrs: { jid: jidToString(p) }
|
|
161
|
+
}))
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
await this.sendIqAndWait(node, id, 15000);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Remove participants from group
|
|
171
|
+
*/
|
|
172
|
+
async removeParticipants(groupJid: JIDLike, participants: JIDLike[]): Promise<void> {
|
|
173
|
+
const id = generateStanzaId();
|
|
174
|
+
const node: BinaryNode = {
|
|
175
|
+
tag: 'iq',
|
|
176
|
+
attrs: {
|
|
177
|
+
type: 'set',
|
|
178
|
+
to: jidToString(groupJid),
|
|
179
|
+
id
|
|
180
|
+
},
|
|
181
|
+
content: [
|
|
182
|
+
{
|
|
183
|
+
tag: 'remove',
|
|
184
|
+
attrs: {},
|
|
185
|
+
content: participants.map(p => ({
|
|
186
|
+
tag: 'participant',
|
|
187
|
+
attrs: { jid: jidToString(p) }
|
|
188
|
+
}))
|
|
189
|
+
}
|
|
190
|
+
]
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
await this.sendIqAndWait(node, id, 15000);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Promote participants jadi admin
|
|
198
|
+
*/
|
|
199
|
+
async promoteParticipants(groupJid: JIDLike, participants: JIDLike[]): Promise<void> {
|
|
200
|
+
const id = generateStanzaId();
|
|
201
|
+
const node: BinaryNode = {
|
|
202
|
+
tag: 'iq',
|
|
203
|
+
attrs: {
|
|
204
|
+
type: 'set',
|
|
205
|
+
to: jidToString(groupJid),
|
|
206
|
+
id
|
|
207
|
+
},
|
|
208
|
+
content: [
|
|
209
|
+
{
|
|
210
|
+
tag: 'promote',
|
|
211
|
+
attrs: {},
|
|
212
|
+
content: participants.map(p => ({
|
|
213
|
+
tag: 'participant',
|
|
214
|
+
attrs: { jid: jidToString(p) }
|
|
215
|
+
}))
|
|
216
|
+
}
|
|
217
|
+
]
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
await this.sendIqAndWait(node, id, 15000);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Demote admin kembali jadi member
|
|
225
|
+
*/
|
|
226
|
+
async demoteParticipants(groupJid: JIDLike, participants: JIDLike[]): Promise<void> {
|
|
227
|
+
const id = generateStanzaId();
|
|
228
|
+
const node: BinaryNode = {
|
|
229
|
+
tag: 'iq',
|
|
230
|
+
attrs: {
|
|
231
|
+
type: 'set',
|
|
232
|
+
to: jidToString(groupJid),
|
|
233
|
+
id
|
|
234
|
+
},
|
|
235
|
+
content: [
|
|
236
|
+
{
|
|
237
|
+
tag: 'demote',
|
|
238
|
+
attrs: {},
|
|
239
|
+
content: participants.map(p => ({
|
|
240
|
+
tag: 'participant',
|
|
241
|
+
attrs: { jid: jidToString(p) }
|
|
242
|
+
}))
|
|
243
|
+
}
|
|
244
|
+
]
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
await this.sendIqAndWait(node, id, 15000);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Leave group
|
|
252
|
+
*/
|
|
253
|
+
async leaveGroup(groupJid: JIDLike): Promise<void> {
|
|
254
|
+
const id = generateStanzaId();
|
|
255
|
+
const node: BinaryNode = {
|
|
256
|
+
tag: 'iq',
|
|
257
|
+
attrs: {
|
|
258
|
+
type: 'set',
|
|
259
|
+
to: 'g.us',
|
|
260
|
+
id
|
|
261
|
+
},
|
|
262
|
+
content: [
|
|
263
|
+
{
|
|
264
|
+
tag: 'leave',
|
|
265
|
+
attrs: {},
|
|
266
|
+
content: [{
|
|
267
|
+
tag: 'group',
|
|
268
|
+
attrs: { id: jidToString(groupJid) }
|
|
269
|
+
}]
|
|
270
|
+
}
|
|
271
|
+
]
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
await this.sendIqAndWait(node, id, 10000);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Get invite code untuk group
|
|
279
|
+
*/
|
|
280
|
+
async getInviteCode(groupJid: JIDLike): Promise<string> {
|
|
281
|
+
const id = generateStanzaId();
|
|
282
|
+
const node: BinaryNode = {
|
|
283
|
+
tag: 'iq',
|
|
284
|
+
attrs: {
|
|
285
|
+
type: 'get',
|
|
286
|
+
to: jidToString(groupJid),
|
|
287
|
+
id
|
|
288
|
+
},
|
|
289
|
+
content: [
|
|
290
|
+
{
|
|
291
|
+
tag: 'invite',
|
|
292
|
+
attrs: {}
|
|
293
|
+
}
|
|
294
|
+
]
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
const response = await this.sendIqAndWait(node, id, 10000);
|
|
298
|
+
const inviteNode = response.content as any[];
|
|
299
|
+
const invite = inviteNode?.find(c => c.tag === 'invite');
|
|
300
|
+
return invite?.attrs?.code ?? '';
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Revoke invite code (generate new)
|
|
305
|
+
*/
|
|
306
|
+
async revokeInviteCode(groupJid: JIDLike): Promise<string> {
|
|
307
|
+
const id = generateStanzaId();
|
|
308
|
+
const node: BinaryNode = {
|
|
309
|
+
tag: 'iq',
|
|
310
|
+
attrs: {
|
|
311
|
+
type: 'set',
|
|
312
|
+
to: jidToString(groupJid),
|
|
313
|
+
id
|
|
314
|
+
},
|
|
315
|
+
content: [
|
|
316
|
+
{
|
|
317
|
+
tag: 'invite',
|
|
318
|
+
attrs: { action: 'revoke' }
|
|
319
|
+
}
|
|
320
|
+
]
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
const response = await this.sendIqAndWait(node, id, 10000);
|
|
324
|
+
const inviteNode = (response.content as any[])?.find(c => c.tag === 'invite');
|
|
325
|
+
return inviteNode?.attrs?.code ?? '';
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Join group via invite code
|
|
330
|
+
*/
|
|
331
|
+
async joinGroupViaCode(code: string): Promise<JIDLike> {
|
|
332
|
+
const id = generateStanzaId();
|
|
333
|
+
const node: BinaryNode = {
|
|
334
|
+
tag: 'iq',
|
|
335
|
+
attrs: {
|
|
336
|
+
type: 'set',
|
|
337
|
+
to: 'g.us',
|
|
338
|
+
id
|
|
339
|
+
},
|
|
340
|
+
content: [
|
|
341
|
+
{
|
|
342
|
+
tag: 'join',
|
|
343
|
+
attrs: {
|
|
344
|
+
code,
|
|
345
|
+
reason: 'invite'
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
]
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
const response = await this.sendIqAndWait(node, id, 15000);
|
|
352
|
+
const groupNode = (response.content as any[])?.find(c => c.tag === 'group');
|
|
353
|
+
return groupNode?.attrs?.jid ?? '';
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Set group settings: restrict (only admins can send messages)
|
|
358
|
+
*/
|
|
359
|
+
async setGroupRestrict(groupJid: JIDLike, restrict: boolean): Promise<void> {
|
|
360
|
+
const id = generateStanzaId();
|
|
361
|
+
const node: BinaryNode = {
|
|
362
|
+
tag: 'iq',
|
|
363
|
+
attrs: {
|
|
364
|
+
type: 'set',
|
|
365
|
+
to: jidToString(groupJid),
|
|
366
|
+
id
|
|
367
|
+
},
|
|
368
|
+
content: [
|
|
369
|
+
{
|
|
370
|
+
tag: 'announce',
|
|
371
|
+
attrs: {
|
|
372
|
+
type: restrict ? 'restrict' : 'unrestrict'
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
]
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
await this.sendIqAndWait(node, id, 10000);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Set group settings: announce mode (only admins can edit info)
|
|
383
|
+
*/
|
|
384
|
+
async setGroupAnnounce(groupJid: JIDLike, announce: boolean): Promise<void> {
|
|
385
|
+
const id = generateStanzaId();
|
|
386
|
+
const node: BinaryNode = {
|
|
387
|
+
tag: 'iq',
|
|
388
|
+
attrs: {
|
|
389
|
+
type: 'set',
|
|
390
|
+
to: jidToString(groupJid),
|
|
391
|
+
id
|
|
392
|
+
},
|
|
393
|
+
content: [
|
|
394
|
+
{
|
|
395
|
+
tag: 'info',
|
|
396
|
+
attrs: {
|
|
397
|
+
type: announce ? 'announce' : 'unannounce'
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
]
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
await this.sendIqAndWait(node, id, 10000);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// ============== Internal: Send IQ + Wait ==============
|
|
407
|
+
|
|
408
|
+
private sendIqAndWait(node: BinaryNode, id: string, timeoutMs: number): Promise<BinaryNode> {
|
|
409
|
+
return new Promise<BinaryNode>((resolve, reject) => {
|
|
410
|
+
const timeout = setTimeout(() => {
|
|
411
|
+
reject(new Error(`IQ ${id} timeout after ${timeoutMs}ms`));
|
|
412
|
+
}, timeoutMs);
|
|
413
|
+
|
|
414
|
+
this.registerIqHandler(id, (response: BinaryNode) => {
|
|
415
|
+
clearTimeout(timeout);
|
|
416
|
+
const type = response.attrs?.type;
|
|
417
|
+
if (type === 'error') {
|
|
418
|
+
const errorNode = (response.content as any[])?.find(c => c.tag === 'error');
|
|
419
|
+
reject(new Error(errorNode?.attrs?.text ?? 'Unknown group operation error'));
|
|
420
|
+
} else {
|
|
421
|
+
resolve(response);
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
try {
|
|
426
|
+
this.send(encodeBinaryNode(node));
|
|
427
|
+
} catch (err: any) {
|
|
428
|
+
clearTimeout(timeout);
|
|
429
|
+
reject(err);
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// ============== Parser ==============
|
|
435
|
+
|
|
436
|
+
private parseGroupMetadata(node: BinaryNode): WAGroupMetadata {
|
|
437
|
+
const groupNode = node.tag === 'group' ? node :
|
|
438
|
+
(Array.isArray(node.content) ? node.content.find(c => c?.tag === 'group') : null);
|
|
439
|
+
|
|
440
|
+
if (!groupNode) {
|
|
441
|
+
throw new Error('Group node not found in response');
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const attrs = groupNode.attrs || {};
|
|
445
|
+
const participants: WAGroupParticipant[] = [];
|
|
446
|
+
|
|
447
|
+
if (Array.isArray(groupNode.content)) {
|
|
448
|
+
for (const child of groupNode.content) {
|
|
449
|
+
if (child?.tag === 'participant') {
|
|
450
|
+
const pAttrs = child.attrs || {};
|
|
451
|
+
participants.push({
|
|
452
|
+
id: pAttrs.jid,
|
|
453
|
+
isAdmin: pAttrs.type === 'admin',
|
|
454
|
+
isSuperAdmin: pAttrs.type === 'superadmin',
|
|
455
|
+
role: pAttrs.type
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return {
|
|
462
|
+
id: attrs.id,
|
|
463
|
+
subject: attrs.subject,
|
|
464
|
+
subjectOwner: attrs.subject_owner,
|
|
465
|
+
subjectTime: attrs.subject_time ? parseInt(attrs.subject_time, 10) : undefined,
|
|
466
|
+
desc: attrs.desc,
|
|
467
|
+
descOwner: attrs.desc_owner,
|
|
468
|
+
descTime: attrs.desc_time ? parseInt(attrs.desc_time, 10) : undefined,
|
|
469
|
+
creation: attrs.creation ? parseInt(attrs.creation, 10) : undefined,
|
|
470
|
+
owner: attrs.owner,
|
|
471
|
+
participants,
|
|
472
|
+
restrict: attrs.restrict === 'true',
|
|
473
|
+
announce: attrs.announce === 'true',
|
|
474
|
+
isCommunity: attrs.is_community === 'true',
|
|
475
|
+
isParentGroup: attrs.is_parent_group === 'true',
|
|
476
|
+
linkedParent: attrs.linked_parent,
|
|
477
|
+
ephemeralSetting: attrs.ephemeral ? parseInt(attrs.ephemeral, 10) : undefined
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// ============== Helper: Build invite link ==============
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Build WhatsApp invite URL dari code
|
|
486
|
+
*/
|
|
487
|
+
export function buildInviteLink(code: string): string {
|
|
488
|
+
return `https://chat.whatsapp.com/${code}`;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Parse invite code dari URL
|
|
493
|
+
*/
|
|
494
|
+
export function parseInviteLink(url: string): string | null {
|
|
495
|
+
const match = url.match(/chat\.whatsapp\.com\/([A-Za-z0-9]+)/);
|
|
496
|
+
return match ? match[1] : null;
|
|
497
|
+
}
|