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.
- package/LICENSE +26 -0
- package/README.md +453 -2
- package/examples/advanced.js +230 -0
- package/examples/getting-started.js +54 -0
- package/examples/send-media.js +77 -0
- package/examples/send-messages.js +87 -0
- package/examples/session.js +140 -0
- package/examples/threads.js +101 -0
- package/examples/users.js +64 -0
- package/index.d.ts +536 -0
- package/index.js +132 -0
- package/package.json +64 -4
- package/src/compatibility.js +393 -0
- package/src/db/database.js +267 -0
- package/src/instagramChat.js +714 -0
- package/src/methods/auth.js +412 -0
- package/src/methods/live.js +327 -0
- package/src/methods/markRead.js +133 -0
- package/src/methods/reactions.js +108 -0
- package/src/methods/search.js +279 -0
- package/src/methods/sendMedia.js +699 -0
- package/src/methods/sendMessage.js +408 -0
- package/src/methods/stories.js +356 -0
- package/src/methods/threadHistory.js +141 -0
- package/src/methods/threadInfo.js +148 -0
- package/src/methods/threadManagement.js +246 -0
- package/src/methods/typingIndicator.js +139 -0
- package/src/methods/unsend.js +140 -0
- package/src/methods/user.js +194 -0
- package/src/mqtt/instagramRealtime.js +1044 -0
- package/src/mqtt/mqttClient.js +283 -0
- package/src/utils/circuitBreaker.js +141 -0
- package/src/utils/constants.js +102 -0
- package/src/utils/cookies.js +443 -0
- package/src/utils/crypto.js +59 -0
- package/src/utils/formatter.js +250 -0
- package/src/utils/http.js +491 -0
- package/src/utils/idempotency.js +96 -0
- package/src/utils/logger.js +88 -0
- package/src/utils/rateLimiter.js +123 -0
- package/src/utils/scheduler.js +159 -0
- package/src/utils/setOptions.js +202 -0
- package/src/utils/totp.js +78 -0
- package/src/utils/user-agents.js +244 -0
- package/src/utils/validation.js +186 -0
- package/test/api-surface.test.js +73 -0
- package/test/index-smoke.test.js +72 -0
- 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
|
-
#
|
|
1
|
+
# tanvir143
|
|
2
2
|
|
|
3
|
-
|
|
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);
|