tanvir143 0.0.0-stage → 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.
Files changed (48) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +453 -2
  3. package/examples/advanced.js +230 -0
  4. package/examples/getting-started.js +54 -0
  5. package/examples/send-media.js +77 -0
  6. package/examples/send-messages.js +87 -0
  7. package/examples/session.js +140 -0
  8. package/examples/threads.js +101 -0
  9. package/examples/users.js +64 -0
  10. package/index.d.ts +536 -0
  11. package/index.js +132 -0
  12. package/package.json +64 -4
  13. package/src/compatibility.js +393 -0
  14. package/src/db/database.js +267 -0
  15. package/src/instagramChat.js +714 -0
  16. package/src/methods/auth.js +412 -0
  17. package/src/methods/live.js +327 -0
  18. package/src/methods/markRead.js +133 -0
  19. package/src/methods/reactions.js +108 -0
  20. package/src/methods/search.js +279 -0
  21. package/src/methods/sendMedia.js +699 -0
  22. package/src/methods/sendMessage.js +408 -0
  23. package/src/methods/stories.js +356 -0
  24. package/src/methods/threadHistory.js +141 -0
  25. package/src/methods/threadInfo.js +148 -0
  26. package/src/methods/threadManagement.js +246 -0
  27. package/src/methods/typingIndicator.js +139 -0
  28. package/src/methods/unsend.js +140 -0
  29. package/src/methods/user.js +194 -0
  30. package/src/mqtt/instagramRealtime.js +1044 -0
  31. package/src/mqtt/mqttClient.js +283 -0
  32. package/src/utils/circuitBreaker.js +141 -0
  33. package/src/utils/constants.js +102 -0
  34. package/src/utils/cookies.js +443 -0
  35. package/src/utils/crypto.js +59 -0
  36. package/src/utils/formatter.js +250 -0
  37. package/src/utils/http.js +491 -0
  38. package/src/utils/idempotency.js +96 -0
  39. package/src/utils/logger.js +88 -0
  40. package/src/utils/rateLimiter.js +123 -0
  41. package/src/utils/scheduler.js +159 -0
  42. package/src/utils/setOptions.js +202 -0
  43. package/src/utils/totp.js +78 -0
  44. package/src/utils/user-agents.js +244 -0
  45. package/src/utils/validation.js +186 -0
  46. package/test/api-surface.test.js +73 -0
  47. package/test/index-smoke.test.js +72 -0
  48. package/test/static-contract.test.js +51 -0
package/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Maintainer: Tanvir Ahmed
2
+ WhatsApp: wa.me/+8801750079773
3
+ GitHub: www.github.com/143tanvir
4
+ Instagram: @ig.tanvir_ahmed
5
+
6
+ MIT License
7
+
8
+ Copyright (c) 2026 Hoon - Rakib Hasan
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,454 @@
1
- # Temporary Holding Version
1
+ # tanvir143
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ <p align="center">
4
+ <strong>tanvir143 — Instagram Chat API</strong><br>
5
+ <em>Bot-first Instagram messaging client • MQTT realtime • session persistence • InstaBot/FCA compatibility</em>
6
+ </p>
7
+
8
+ <p align="center">
9
+ <a href="https://www.npmjs.com/package/tanvir143"><img src="https://img.shields.io/npm/v/tanvir143.svg" alt="npm version"></a>
10
+ <a href="https://www.npmjs.com/package/tanvir143"><img src="https://img.shields.io/npm/dm/tanvir143.svg" alt="npm downloads"></a>
11
+ <a href="https://github.com/143tanvir/insta-robot-143/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/tanvir143.svg" alt="license"></a>
12
+ </p>
13
+
14
+ > **Unofficial/private API notice:** this project uses Instagram private/web endpoints and realtime messaging. Those endpoints can change without notice. Use only accounts and environments you are authorized to operate, and follow Instagram's terms and applicable laws.
15
+
16
+ ## Maintainer
17
+
18
+ ```text
19
+ Maintainer: Tanvir Ahmed
20
+ WhatsApp: wa.me/+8801750079773
21
+ GitHub: www.github.com/143tanvir
22
+ Instagram: @ig.tanvir_ahmed
23
+ ```
24
+
25
+ ## What tanvir143 is
26
+
27
+ `tanvir143` is a Node.js Instagram Chat API built from a working local ICA base and extended with a compatibility layer designed around the API surface used by the **insta-robot-143 / InstaBot-style** bot architecture.
28
+
29
+ The design keeps the Instagram client, MQTT listener, session system, message helpers and bot-compatibility aliases in one package so the bot can call both modern methods and FCA-style/legacy method names.
30
+
31
+ ## Compatibility target
32
+
33
+ The compatibility layer is intentionally centered on the methods the bot expects, including:
34
+
35
+ | Bot/API method | tanvir143 |
36
+ |---|---|
37
+ | `getThreadList()` | ✅ |
38
+ | `getInbox()` | ✅ |
39
+ | `getThreadInfo()` | ✅ |
40
+ | `getThreadHistory()` | ✅ |
41
+ | `sendMessage()` | ✅ |
42
+ | `sendImage()` | ✅ alias |
43
+ | `sendAudio()` | ✅ alias |
44
+ | `sendVideo()` | ✅ |
45
+ | `sendReaction()` | ✅ |
46
+ | `setMessageReaction()` | ✅ alias |
47
+ | `unsendMessage()` | ✅ |
48
+ | `sendTypingIndicator()` | ✅ |
49
+ | `markAsRead()` | ✅ |
50
+ | `markAsDelivered()` | ✅ compatibility method |
51
+ | `setTitle()` | ✅ alias |
52
+ | `addUserToThread()` | ✅ alias |
53
+ | `removeUserFromThread()` | ✅ alias |
54
+ | `changeThreadMute()` | ✅ alias |
55
+ | `getUserInfo()` | ✅ |
56
+ | `getUserInfoByUsername()` | ✅ |
57
+ | `musicSearch()` | ✅ search/fallback |
58
+ | `sendMusic()` | ✅ native when available |
59
+ | `listenMqtt()` | ✅ |
60
+
61
+ The package also exposes the broader modern feature set described below.
62
+
63
+ ## Installation
64
+
65
+ ```bash
66
+ npm install tanvir143
67
+ ```
68
+
69
+ Node.js **20.0.0+** is required.
70
+
71
+ ## Quick start
72
+
73
+ ### Cookie login
74
+
75
+ ```js
76
+ const { login } = require('tanvir143');
77
+
78
+ const api = await login('sessionid=...; ds_user_id=...; csrftoken=...; ig_did=...');
79
+
80
+ api.listenMqtt((err, event) => {
81
+ if (err) return console.error('[MQTT]', err);
82
+ if (event?.type !== 'message') return;
83
+
84
+ console.log(event.body);
85
+ api.sendMessage(`Echo: ${event.body}`, event.threadID);
86
+ });
87
+ ```
88
+
89
+ ### Bot-style callback API
90
+
91
+ ```js
92
+ api.sendImage('./image.jpg', threadID, 'Hello', (err, result) => {
93
+ if (err) return console.error(err);
94
+ console.log(result);
95
+ });
96
+ ```
97
+
98
+ ### Credentials login
99
+
100
+ ```js
101
+ const { login } = require('tanvir143');
102
+
103
+ const api = await login({
104
+ username: process.env.IG_USERNAME,
105
+ password: process.env.IG_PASSWORD
106
+ });
107
+ ```
108
+
109
+ Authentication can require additional verification. The package does not bypass Instagram checkpoints or verification requirements.
110
+
111
+ ## Login format supported by InstaBot-style adapters
112
+
113
+ A bot can pass a single object containing cookies plus connection options:
114
+
115
+ ```js
116
+ const api = await login({
117
+ cookies,
118
+ selfListen: false,
119
+ listenEvents: true,
120
+ autoReconnect: true,
121
+ autoSaveSession: true,
122
+ sessionFile: './session.json',
123
+ userAgent: 'Mozilla/5.0 ...'
124
+ });
125
+ ```
126
+
127
+ The login adapter extracts the `cookies` field and passes the actual cookie set to the authenticated client. This avoids accidentally sending the entire options object into the cookie parser.
128
+
129
+ ## Core features
130
+
131
+ ### Realtime MQTT
132
+
133
+ ```js
134
+ api.listenMqtt((err, event) => {
135
+ if (err) return console.error(err);
136
+ // event.type, event.threadID, event.senderID, event.body, event.messageID, ...
137
+ });
138
+
139
+ api.stopListening();
140
+ ```
141
+
142
+ Automatic reconnect and connection health information are supported by the underlying realtime client.
143
+
144
+ ### Messaging
145
+
146
+ ```js
147
+ await api.sendMessage('Hello', threadID);
148
+ await api.sendDirectMessage(userID, 'Hello');
149
+ await api.replyToMessage(threadID, 'Reply', messageID);
150
+ await api.unsendMessage(messageID);
151
+ ```
152
+
153
+ ### Media
154
+
155
+ ```js
156
+ await api.sendPhoto(threadID, './photo.jpg');
157
+ await api.sendVideo(threadID, './video.mp4');
158
+ await api.sendVoice(threadID, './voice.m4a');
159
+ await api.sendGIF(threadID, 'https://example.com/file.gif');
160
+ ```
161
+
162
+ URL helpers:
163
+
164
+ ```js
165
+ await api.sendPhotoFromUrl(threadID, 'https://example.com/photo.jpg');
166
+ await api.sendVideoFromUrl(threadID, 'https://example.com/video.mp4');
167
+ await api.sendVoiceFromUrl(threadID, 'https://example.com/audio.mp3');
168
+ ```
169
+
170
+ ### InstaBot/FCA media aliases
171
+
172
+ ```js
173
+ api.sendImage(source, threadID, caption, callback);
174
+ api.sendAudio(source, threadID, callback);
175
+ api.sendVideo(source, threadID, callback);
176
+ ```
177
+
178
+ ### Reactions
179
+
180
+ ```js
181
+ await api.sendReaction('❤️', messageID);
182
+ await api.setMessageReaction('🔥', messageID, threadID);
183
+ await api.removeReaction(messageID);
184
+ ```
185
+
186
+ ### Threads and groups
187
+
188
+ ```js
189
+ const inbox = await api.getInbox({ limit: 20 });
190
+ const list = await api.getThreadList(20);
191
+ const info = await api.getThreadInfo(threadID);
192
+ const history = await api.getThreadHistory(threadID, 30);
193
+
194
+ await api.setTitle(threadID, 'New group title');
195
+ await api.changeThreadMute(threadID, true);
196
+ ```
197
+
198
+ Additional helpers include pending request handling, nickname changes, add/remove member operations, thread search, delete, mute/unmute and history access.
199
+
200
+ ### Users
201
+
202
+ ```js
203
+ const user = await api.getUserInfo(userID);
204
+ const userByName = await api.getUserInfoByUsername('instagram');
205
+ const results = await api.searchUsers('tanvir', { limit: 10 });
206
+ ```
207
+
208
+ ### Music
209
+
210
+ ```js
211
+ const tracks = await api.musicSearch('song name');
212
+ await api.sendMusic(threadID, tracks[0]);
213
+ ```
214
+
215
+ `musicSearch()` uses the working base client's music search implementation when available. `sendMusic()` prefers a native sender; when a search result contains a usable audio URL, a voice-message fallback can be used. Private music-sticker sending is not claimed when the installed base does not expose that native endpoint.
216
+
217
+ ### Text effects
218
+
219
+ The compatibility names are exposed:
220
+
221
+ ```js
222
+ api.sendTextEffect(text, threadID, effect, callback);
223
+ api.sendAvatarTextEffect(text, threadID, effect, callback);
224
+ ```
225
+
226
+ These methods require a native private endpoint implementation. `tanvir143` deliberately reports a clear unsupported-method error when the working base does not expose the underlying endpoint instead of returning a false success result.
227
+
228
+ ### Read / delivery / typing
229
+
230
+ ```js
231
+ await api.markAsRead(threadID);
232
+ await api.markAsUnread(threadID);
233
+ await api.markAsDelivered(threadID, messageID);
234
+ await api.sendTypingIndicator(threadID);
235
+ await api.stopTypingIndicator(threadID);
236
+ ```
237
+
238
+ `markAsDelivered()` is a compatibility surface and requires a native delivery endpoint in the underlying ICA build.
239
+
240
+ ### Stories
241
+
242
+ ```js
243
+ await api.getUserStories(userID);
244
+ await api.getFeedStories({ limit: 10 });
245
+ await api.reactToStory(storyId, userId, '🔥');
246
+ await api.replyToStory(storyId, userId, 'Nice story');
247
+ ```
248
+
249
+ ### Live
250
+
251
+ ```js
252
+ await api.getLiveFeed({ limit: 5 });
253
+ await api.sendLiveComment(broadcastId, 'Hello');
254
+ await api.sendLiveHeart(broadcastId, 5);
255
+ ```
256
+
257
+ ### Search
258
+
259
+ ```js
260
+ await api.searchUsers('alice');
261
+ await api.searchHashtags('photography');
262
+ await api.searchPlaces('Dhaka');
263
+ await api.searchReels('travel');
264
+ ```
265
+
266
+ ### Session persistence
267
+
268
+ ```js
269
+ const session = api.getSession();
270
+ await api.loadSession(session);
271
+ await api.saveSession('./session.json');
272
+ await api.loadSessionFromFile('./session.json');
273
+ ```
274
+
275
+ Automatic saving can be enabled with:
276
+
277
+ ```js
278
+ const api = await login(cookies, {
279
+ autoSaveSession: true,
280
+ sessionFile: './session.json'
281
+ });
282
+ ```
283
+
284
+ Never commit session files or authentication cookies.
285
+
286
+ ### Health monitoring
287
+
288
+ ```js
289
+ const health = api.getHealth();
290
+ console.dir(health, { depth: 6 });
291
+ ```
292
+
293
+ The health snapshot can include authentication state, listener state, MQTT state, reconnect attempts, HTTP health/rate-limit information and optional database/scheduler state.
294
+
295
+ ### Optional database
296
+
297
+ ```js
298
+ const api = await login(cookies, {
299
+ database: true,
300
+ dbOptions: { storage: './messages.db', logging: false }
301
+ });
302
+
303
+ await api.initDatabase();
304
+ ```
305
+
306
+ ### Optional scheduler
307
+
308
+ ```js
309
+ const api = await login(cookies, { scheduler: true });
310
+
311
+ api.scheduleTask('morning', '0 9 * * *', async () => {
312
+ await api.sendMessage('Good morning!', threadID);
313
+ });
314
+ ```
315
+
316
+ ## Cookie utilities
317
+
318
+ ```js
319
+ const { login } = require('tanvir143');
320
+
321
+ const jar = login.CookieUtils.parse('sessionid=abc; ds_user_id=123');
322
+ const header = login.CookieUtils.parseHeaderString('sessionid=abc; csrftoken=xyz');
323
+ ```
324
+
325
+ Common cookie export formats are supported by the bundled utility layer.
326
+
327
+ ## Options
328
+
329
+ ```js
330
+ const api = await login(cookies, {
331
+ selfListen: false,
332
+ listenEvents: true,
333
+ autoMarkRead: false,
334
+ autoMarkDelivery: true,
335
+ logLevel: 'info',
336
+ logColors: true,
337
+ database: false,
338
+ scheduler: false,
339
+ autoReconnect: true,
340
+ autoListen: false,
341
+ autoSaveSession: true,
342
+ sessionFile: './session.json',
343
+ mqttConnectionTimeout: 30000,
344
+ maxRetries: 3,
345
+ userAgent: 'Mozilla/5.0 ...',
346
+ proxy: null
347
+ });
348
+ ```
349
+
350
+ ## API style
351
+
352
+ The package supports both Promise-style and callback-style usage where practical:
353
+
354
+ ```js
355
+ // Promise
356
+ await api.sendMessage('hello', threadID);
357
+
358
+ // Callback
359
+ api.sendMessage('hello', threadID, (err, result) => {
360
+ if (err) return console.error(err);
361
+ console.log(result);
362
+ });
363
+ ```
364
+
365
+ ## InstaBot integration
366
+
367
+ The intended integration point is a single ICA import/adapter location inside the bot. Keep the rest of the command/event system unchanged unless the bot uses an API that is genuinely absent from the ICA.
368
+
369
+ For `insta-robot-143`, the bot's realtime startup expects `listenMqtt()` and the command layer expects several FCA-style methods such as `getThreadList()` and `sendImage()`. `tanvir143` exposes these aliases specifically so the bot does not have to be rewritten merely because the underlying ICA uses modern method names.
370
+
371
+ ### Should `src/bot.js` be changed?
372
+
373
+ The preferred setup is **no change to `src/bot.js`** when the compatibility layer is sufficient. The package exposes the methods used by the current bot runtime.
374
+
375
+ A bot-side change is appropriate only when the bot depends on behavior that cannot be represented safely by an ICA adapter—for example, an event schema that is materially different from the available realtime payload. In that case the required bot file should be changed explicitly rather than silently masking the mismatch.
376
+
377
+ ## Validation and reliability layers
378
+
379
+ The package retains the working base's defensive infrastructure, including:
380
+
381
+ - request validation and media size checks
382
+ - adaptive rate limiting
383
+ - circuit-breaker protection
384
+ - retry/backoff for transient network failures
385
+ - per-thread send pacing
386
+ - message idempotency support
387
+ - MQTT reconnect handling
388
+ - session persistence
389
+ - optional database and scheduler support
390
+
391
+ These mechanisms do not guarantee that Instagram private endpoints will remain stable; upstream endpoint changes may still require maintenance.
392
+
393
+ ## Testing
394
+
395
+ Run local tests with:
396
+
397
+ ```bash
398
+ npm test
399
+ npm run syntax
400
+ npm run pack:check
401
+ ```
402
+
403
+ The test suite covers the public compatibility surface, callback/Promise behavior, package metadata and static package-contract checks.
404
+
405
+ A live Instagram login/MQTT test requires an authenticated account/session and a real network connection. It should be run only in your own controlled deployment environment; test sessions should never be committed or published.
406
+
407
+ ## Publishing
408
+
409
+ Before publishing:
410
+
411
+ ```bash
412
+ npm login
413
+ npm whoami
414
+ npm publish --access public
415
+ ```
416
+
417
+ A local package can be inspected first with:
418
+
419
+ ```bash
420
+ npm pack
421
+ ```
422
+
423
+ The package uses `publishConfig.access = public`, so the package metadata is configured for a public npm release. Actual publishing still requires an authenticated npm account with permission to publish the package name.
424
+
425
+ ## Security
426
+
427
+ Never publish:
428
+
429
+ ```text
430
+ session.json
431
+ account.txt
432
+ cookies
433
+ sessionid
434
+ csrftoken
435
+ passwords
436
+ access tokens
437
+ ```
438
+
439
+ Use environment variables or secure deployment secrets instead.
440
+
441
+ ## License and attribution
442
+
443
+ This distribution retains the MIT license and preserves upstream license notices where required. The project maintainer information is added separately:
444
+
445
+ ```text
446
+ Maintainer: Tanvir Ahmed
447
+ WhatsApp: wa.me/+8801750079773
448
+ GitHub: www.github.com/143tanvir
449
+ Instagram: @ig.tanvir_ahmed
450
+ ```
451
+
452
+ ## Disclaimer
453
+
454
+ `tanvir143` is an unofficial/private Instagram client and is not affiliated with Instagram or Meta. Private endpoints can change or stop working without notice.
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Maintainer: Tanvir Ahmed
3
+ * WhatsApp: wa.me/+8801750079773
4
+ * GitHub: www.github.com/143tanvir
5
+ * Instagram: @ig.tanvir_ahmed
6
+ */
7
+
8
+ /**
9
+ * @license MIT
10
+ *
11
+ * Covers:
12
+ * - All client options
13
+ * - All MQTT event types
14
+ * - Proxy support
15
+ * - Stories (get, react, reply)
16
+ * - Live (feed, comment, heart)
17
+ * - Task scheduler (cron)
18
+ * - SQLite message database
19
+ * - Error handling and graceful shutdown
20
+ * - Multi-account setup (separate client instances)
21
+ */
22
+
23
+ 'use strict';
24
+
25
+ const { login } = require('../index');
26
+
27
+ const COOKIES = 'sessionid=YOUR_SESSION_ID; ds_user_id=YOUR_USER_ID; csrftoken=YOUR_CSRF_TOKEN; ig_did=YOUR_IG_DID';
28
+
29
+ // ── All available options ─────────────────────────────────────────────────────
30
+ //
31
+ // Pass any of these as the second argument to login():
32
+ //
33
+ // const api = await login(cookies, {
34
+ // logLevel: 'info',
35
+ // selfListen: false,
36
+ // listenEvents: true,
37
+ // autoMarkRead: false,
38
+ // autoMarkDelivery: true,
39
+ // proxy: null,
40
+ // userAgent: null,
41
+ // timeout: 30000,
42
+ // autoReconnect: true,
43
+ // maxRetries: 3,
44
+ // rateLimitDelay: 1000,
45
+ // database: false,
46
+ // dbOptions: { storage: './tanvir143.db', logging: false },
47
+ // scheduler: false,
48
+ // deviceId: null,
49
+ // phoneId: null,
50
+ // uuid: null,
51
+ // advertisingId: null
52
+ // });
53
+
54
+ // ── Full event reference ──────────────────────────────────────────────────────
55
+
56
+ async function eventDemo() {
57
+ const api = await login(COOKIES, { logLevel: 'info', listenEvents: true });
58
+
59
+ api.listen((err, event) => {
60
+ if (err) return console.error('Listen error:', err.message);
61
+
62
+ switch (event.type) {
63
+ case 'message':
64
+ console.log('Message from', event.senderID, 'in', event.threadID);
65
+ console.log(' body:', event.body);
66
+ console.log(' attachments:', event.attachments.length);
67
+ console.log(' isGroup:', event.isGroup);
68
+ console.log(' replyTo:', event.replyTo);
69
+ break;
70
+
71
+ case 'message_reaction':
72
+ console.log('Reaction', event.reaction, 'by', event.senderID, 'on', event.messageID);
73
+ break;
74
+
75
+ case 'read_receipt':
76
+ console.log('Read receipt in', event.threadID, 'at', event.messageID);
77
+ break;
78
+
79
+ case 'typing':
80
+ console.log(event.senderID, event.isTyping ? 'started' : 'stopped', 'typing in', event.threadID);
81
+ break;
82
+
83
+ case 'story_share':
84
+ console.log('Story share from', event.senderID, '— story id:', event.storyId);
85
+ break;
86
+
87
+ case 'voice':
88
+ console.log('Voice message from', event.senderID, '— duration:', event.attachments[0]?.duration, 'ms');
89
+ break;
90
+
91
+ default:
92
+ console.log('Unknown event type:', event.type);
93
+ }
94
+ });
95
+
96
+ api.on('connected', ({ method }) => console.log('Connected via', method));
97
+ api.on('disconnected', () => console.log('Disconnected'));
98
+ api.on('reconnecting', () => console.log('Reconnecting...'));
99
+ api.on('error', (err) => console.error('Client error:', err.message));
100
+
101
+ return api;
102
+ }
103
+
104
+ // ── Proxy support ─────────────────────────────────────────────────────────────
105
+
106
+ async function proxyDemo() {
107
+ const api = await login(COOKIES, {
108
+ logLevel: 'info',
109
+ proxy: 'http://username:password@proxy-host:8080'
110
+ });
111
+ console.log('Connected through proxy');
112
+ }
113
+
114
+ // ── Stories ───────────────────────────────────────────────────────────────────
115
+
116
+ async function storiesDemo() {
117
+ const api = await login(COOKIES, { logLevel: 'info' });
118
+
119
+ const USER_ID = 'TARGET_USER_ID';
120
+ const stories = await api.getUserStories(USER_ID);
121
+ console.log(`${USER_ID} has ${stories.length} active stories`);
122
+
123
+ if (stories.length > 0) {
124
+ const storyId = stories[0].id;
125
+ await api.reactToStory(storyId, USER_ID, '🔥');
126
+ console.log('Reacted to story');
127
+ await api.replyToStory(storyId, USER_ID, 'Great story!');
128
+ console.log('Replied to story');
129
+ }
130
+
131
+ const feed = await api.getFeedStories({ limit: 10 });
132
+ console.log('Feed stories from', feed.length, 'accounts');
133
+ }
134
+
135
+ // ── Live ──────────────────────────────────────────────────────────────────────
136
+
137
+ async function liveDemo() {
138
+ const api = await login(COOKIES, { logLevel: 'info' });
139
+
140
+ const BROADCAST_ID = 'LIVE_BROADCAST_ID';
141
+ const feed = await api.getLiveFeed({ limit: 5 });
142
+ console.log('Live broadcasts:', feed.length);
143
+
144
+ await api.sendLiveComment(BROADCAST_ID, 'Hello from tanvir143!');
145
+ await api.sendLiveHeart(BROADCAST_ID, 5);
146
+ }
147
+
148
+ // ── Task scheduler (cron) ─────────────────────────────────────────────────────
149
+
150
+ async function schedulerDemo() {
151
+ const api = await login(COOKIES, { logLevel: 'info', scheduler: true });
152
+
153
+ const THREAD_ID = 'YOUR_THREAD_ID';
154
+
155
+ api.scheduleTask('morning-greeting', '0 9 * * *', async () => {
156
+ await api.sendMessage('Good morning!', THREAD_ID);
157
+ console.log('Morning greeting sent');
158
+ });
159
+
160
+ api.scheduleTask('weekly-reminder', '0 8 * * 1', async () => {
161
+ await api.sendMessage('Weekly check-in time!', THREAD_ID);
162
+ });
163
+ }
164
+
165
+ // ── SQLite database ───────────────────────────────────────────────────────────
166
+
167
+ async function databaseDemo() {
168
+ const api = await login(COOKIES, {
169
+ logLevel: 'info',
170
+ database: true,
171
+ dbOptions: { storage: './messages.db' }
172
+ });
173
+
174
+ await api.initDatabase();
175
+
176
+ const THREAD_ID = 'YOUR_THREAD_ID';
177
+
178
+ api.listen((err, event) => {
179
+ if (err) return;
180
+ // Events are persisted to SQLite automatically
181
+ });
182
+
183
+ const local = await api._client.getMessagesFromDB(THREAD_ID, { limit: 50 });
184
+ console.log(`DB has ${local.length} messages for thread ${THREAD_ID}`);
185
+ }
186
+
187
+ // ── Multi-account ─────────────────────────────────────────────────────────────
188
+
189
+ async function multiAccountDemo() {
190
+ const [api1, api2] = await Promise.all([
191
+ login('sessionid=ACCOUNT_1_SESSION; ds_user_id=UID1; csrftoken=CSRF1; ig_did=DID1', { logLevel: 'warn' }),
192
+ login('sessionid=ACCOUNT_2_SESSION; ds_user_id=UID2; csrftoken=CSRF2; ig_did=DID2', { logLevel: 'warn' })
193
+ ]);
194
+
195
+ console.log('Account 1:', api1.getCurrentUserID().username);
196
+ console.log('Account 2:', api2.getCurrentUserID().username);
197
+
198
+ api1.listen((err, event) => {
199
+ if (err || event.type !== 'message') return;
200
+ console.log('[account1]', event.senderID, ':', event.body);
201
+ });
202
+
203
+ api2.listen((err, event) => {
204
+ if (err || event.type !== 'message') return;
205
+ console.log('[account2]', event.senderID, ':', event.body);
206
+ });
207
+ }
208
+
209
+ // ── Graceful shutdown ─────────────────────────────────────────────────────────
210
+
211
+ function setupShutdown(api) {
212
+ async function shutdown(signal) {
213
+ console.log(`\n${signal} received — shutting down`);
214
+ api.stopListening();
215
+ await api.logout();
216
+ process.exit(0);
217
+ }
218
+
219
+ process.on('SIGINT', () => shutdown('SIGINT'));
220
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
221
+ }
222
+
223
+ // ── Entry point ───────────────────────────────────────────────────────────────
224
+
225
+ async function main() {
226
+ const api = await eventDemo();
227
+ setupShutdown(api);
228
+ }
229
+
230
+ main().catch(console.error);