@eryxenx/insta-chat 2.0.3 → 2.0.4
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/CHANGELOG.md +17 -0
- package/package.json +1 -1
- package/src/methods/media.js +17 -11
- package/src/utils/antiSuspension.js +18 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,23 @@ All notable changes to `@eryxenx/insta-chat` are documented here.
|
|
|
4
4
|
The format follows [Keep a Changelog](https://keepachangelog.com/) and uses
|
|
5
5
|
[Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [2.0.4] - 2026-10-10
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Captioned media lagged behind its text.** A message carrying both text and
|
|
12
|
+
an attachment uploads the media first, then commits the caption and the media
|
|
13
|
+
back-to-back, so the text and the image land together instead of the image
|
|
14
|
+
trailing by the whole upload time. Instagram's DM media mutation has no caption
|
|
15
|
+
field, so caption + attachment is still two messages; this only removes the
|
|
16
|
+
perceived gap after the caption.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- `antiSuspension.waitForTurn(threadID, { immediate })`: an immediate turn that
|
|
21
|
+
still counts against the volume caps but skips the inter-message gap, so a
|
|
22
|
+
captioned attachment can follow its caption at once.
|
|
23
|
+
|
|
7
24
|
## [1.0.12] - 2026-10-10
|
|
8
25
|
|
|
9
26
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eryxenx/insta-chat",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"description": " Unofficial Instagram Chat API for Node.js | Build Instagram DM bots on personal accounts \u2014 send messages, media, voice notes, reactions, edit & unsend, listen for incoming chats. No browser at runtime",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
package/src/methods/media.js
CHANGED
|
@@ -95,22 +95,28 @@ class MediaMethods {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
async _send(threadID, uploadOptions, options) {
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
await globalAntiSuspension.waitForTurn(threadID);
|
|
98
|
+
// Instagram's DM media mutation has no caption field, so an attachment with
|
|
99
|
+
// text is necessarily two messages. The recipient-visible contract is that
|
|
100
|
+
// the caption reaches Instagram before the media (so the text renders on top
|
|
101
|
+
// and the image underneath — what GoatBot-Pro commands intend).
|
|
102
|
+
//
|
|
103
|
+
// The upload is by far the slow part of a media send. If the caption is
|
|
104
|
+
// committed while the upload is still in flight, the text appears at once
|
|
105
|
+
// and the image arrives a couple of seconds later on its own. Doing the
|
|
106
|
+
// upload first and then committing caption + media back-to-back makes the
|
|
107
|
+
// two land together, which is what a person perceives as "one message".
|
|
109
108
|
const metadata = await this._upload(threadID, uploadOptions);
|
|
110
109
|
const fbid = metadata.fbid || metadata.video_id || metadata.audio_id;
|
|
111
110
|
if (!fbid) throw new Error(`Upload returned no media id: ${JSON.stringify(metadata)}`);
|
|
112
111
|
let result;
|
|
113
112
|
try {
|
|
113
|
+
if (options.text) {
|
|
114
|
+
await globalAntiSuspension.waitForTurn(threadID);
|
|
115
|
+
await this._sendCaption(threadID, options.text);
|
|
116
|
+
}
|
|
117
|
+
// Count the media against the same pacing/volume budget, but with no gap
|
|
118
|
+
// after a caption so the image immediately follows the text.
|
|
119
|
+
await globalAntiSuspension.waitForTurn(threadID, { immediate: !!options.text });
|
|
114
120
|
result = await this._commitMedia(threadID, fbid, options);
|
|
115
121
|
} catch (error) {
|
|
116
122
|
globalAntiSuspension.noteSendError(error);
|
|
@@ -204,19 +204,34 @@ class SendPacer extends EventEmitter {
|
|
|
204
204
|
return true;
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
waitForTurn(threadID) {
|
|
207
|
+
waitForTurn(threadID, opts) {
|
|
208
208
|
if (!this.enabled) return Promise.resolve();
|
|
209
|
-
const run = this._queue.then(() => this._turn(threadID));
|
|
209
|
+
const run = this._queue.then(() => this._turn(threadID, opts));
|
|
210
210
|
this._queue = run.catch(() => {});
|
|
211
211
|
return run.catch(() => {});
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
-
async _turn(threadID) {
|
|
214
|
+
async _turn(threadID, opts = {}) {
|
|
215
215
|
this._rollWindows();
|
|
216
216
|
this._decayStrikes();
|
|
217
217
|
this._prune();
|
|
218
218
|
|
|
219
219
|
const key = String(threadID);
|
|
220
|
+
|
|
221
|
+
// A captioned media is one logical message. Instagram's own client sends the
|
|
222
|
+
// caption text and the attachment back-to-back, not seconds apart, so the
|
|
223
|
+
// media pass asks for an immediate turn: still counted against the volume
|
|
224
|
+
// caps (so the hourly/daily budget stays accurate) but without the
|
|
225
|
+
// inter-message gap that made the image trail the text.
|
|
226
|
+
if (opts && opts.immediate) {
|
|
227
|
+
const stamp = Date.now();
|
|
228
|
+
this.lastSendAt = stamp;
|
|
229
|
+
this.perThreadLastSendAt.set(key, stamp);
|
|
230
|
+
this.hourSendCount++;
|
|
231
|
+
this.daySendCount++;
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
220
235
|
const cooling = this.isCoolingDown();
|
|
221
236
|
const overCap = this.hourSendCount >= this._hourlyCap() || this.daySendCount >= this.dailyCap;
|
|
222
237
|
|