@eryxenx/insta-chat 2.0.7 → 2.0.9

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 CHANGED
@@ -4,6 +4,32 @@ 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.9] - 2026-10-11
8
+
9
+ ### Fixed
10
+
11
+ - **`unsendMessage` still never deleted the message.** The 2.0.8 fix sent
12
+ `_csrftoken` + `_uuid` to the REST delete endpoint
13
+ (`/api/v1/direct_v2/threads/<t>/items/<id>/delete/`), but Instagram retired
14
+ that endpoint: on `www.instagram.com` the whole `/api/v1` namespace now
15
+ returns `{"message":"useragent mismatch","status":"fail"}` /
16
+ `not-logged-in` 404, and on `i.instagram.com` it needs the mobile app
17
+ identity. Unsend now goes through the web DM client's Relay mutation
18
+ `IGDMessageUnsendDialogOffMsysMutation` (doc_id `26948700068153789`) with
19
+ `{ message_id, send_data: { thread_id } }`, which answers
20
+ `{ data: { direct_unsend_message: true } }`. Verified against a real message.
21
+
22
+ ## [2.0.8] - 2026-10-11
23
+
24
+ ### Fixed
25
+
26
+ - **`unsendMessage` never deleted the message.** The REST delete request was
27
+ sent with an empty body, which Instagram rejects with `status: "fail"` (or a
28
+ 404/redirect). The web client sends `_csrftoken` and `_uuid`, so `/unsend`
29
+ now includes both. Previously the call resolved to `false`, which the bot
30
+ surfaced as "Could not unsend this message (Instagram refused it, or the
31
+ message is too old)."
32
+
7
33
  ## [2.0.7] - 2026-10-11
8
34
 
9
35
  ### Fixed
package/DOCS.md CHANGED
@@ -182,6 +182,7 @@ Relay **doc_ids** (stable per friendly-name, rotate rarely):
182
182
  | `IGDirectCutoutStickerSendMutation` | `26601474479505807` |
183
183
  | `PolarisDirectInboxQuery` | `27909866362025854` |
184
184
  | `useFeatureLimitsOffMsysQuery` (login probe) | `28374543635529813` |
185
+ | `IGDMessageUnsendDialogOffMsysMutation` (unsend) | `26948700068153789` |
185
186
  | Thread detail | `29619996517588618` |
186
187
 
187
188
  ### Text send
@@ -213,6 +214,21 @@ Relay **doc_ids** (stable per friendly-name, rotate rarely):
213
214
  `reaction_status` is `"created"` or `"deleted"`. (Reverse-engineered from the
214
215
  live `comet/CometDirectPageWeb` bundle; the legacy REST reaction endpoint 404s.)
215
216
 
217
+ ### Unsend (delete for everyone)
218
+
219
+ `IGDMessageUnsendDialogOffMsysMutation` (doc_id `26948700068153789`) with
220
+ variables:
221
+
222
+ ```javascript
223
+ { message_id: "<mid.$...>", send_data: { thread_id: "<threadID>" } }
224
+ ```
225
+
226
+ It answers `{ data: { direct_unsend_message: true } }`. The legacy REST delete
227
+ endpoint (`/api/v1/direct_v2/threads/<t>/items/<id>/delete/`) is gone: on
228
+ `www.instagram.com` the whole `/api/v1` namespace returns `not-logged-in` 404,
229
+ and on `i.instagram.com` it needs the mobile app identity. (Reverse-engineered
230
+ from the live `IGDMessageUnsendDialogOffMsys.react` chunk.)
231
+
216
232
  ### Session validation
217
233
 
218
234
  The REST probe endpoints lie (they 302 to login even for healthy sessions), so
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eryxenx/insta-chat",
3
- "version": "2.0.7",
3
+ "version": "2.0.9",
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",
@@ -23,7 +23,7 @@
23
23
  "CHANGELOG.md"
24
24
  ],
25
25
  "scripts": {
26
- "test": "node test/smoke.js && node test/antiSuspension.test.js && node test/replyContent.test.js && node test/edit.test.js && node test/sendReply.test.js && node test/client.test.js && node test/relayError.test.js && node test/safetyWiring.test.js && node test/mediaOrder.test.js && node test/sessionSafety.test.js && node test/emptySend.test.js && node test/pollFloor.test.js && node test/unsend.test.js",
26
+ "test": "node test/smoke.js && node test/antiSuspension.test.js && node test/replyContent.test.js && node test/edit.test.js && node test/sendReply.test.js && node test/client.test.js && node test/relayError.test.js && node test/safetyWiring.test.js && node test/mediaOrder.test.js && node test/sessionSafety.test.js && node test/emptySend.test.js && node test/pollFloor.test.js && node test/unsend.test.js && node test/unsendGraphQL.test.js",
27
27
  "build": "node scripts/build.js"
28
28
  },
29
29
  "keywords": [
package/src/api.js CHANGED
@@ -43,7 +43,7 @@ class InstaAPI {
43
43
  const sessionParams = options.sessionParams || this._loadSessionParams(options.sessionParamsFile);
44
44
  if (sessionParams) this.http.setSessionParams(sessionParams);
45
45
 
46
- this.message = new MessageMethods(this.http);
46
+ this.message = new MessageMethods(this.http, this.identity);
47
47
  this.reaction = new ReactionMethods(this.http);
48
48
  this.typing = new TypingMethods(this.http);
49
49
  this.thread = new ThreadMethods(this.http);
package/src/constants.js CHANGED
@@ -25,7 +25,8 @@ const DOC_ID = {
25
25
  CUTOUT_STICKER_SEND: "26601474479505807",
26
26
  MARK_THREAD_READ: "27399783383056109",
27
27
  THREAD_DETAIL: "29619996517588618",
28
- INBOX: "27909866362025854"
28
+ INBOX: "27909866362025854",
29
+ UNSEND_MESSAGE: "26948700068153789"
29
30
  };
30
31
 
31
32
  const EP = {
@@ -12,8 +12,9 @@ const { classify } = require("../utils/errors");
12
12
  const NO_FALLBACK_KINDS = new Set(["rate_limited", "challenge", "blocked", "suspended", "auth"]);
13
13
 
14
14
  class MessageMethods {
15
- constructor(http) {
15
+ constructor(http, identity) {
16
16
  this.http = http;
17
+ this.identity = identity;
17
18
  }
18
19
 
19
20
  // Confirmed send path from a live web session: the IGDirectTextSendMutation
@@ -98,9 +99,24 @@ class MessageMethods {
98
99
  });
99
100
  }
100
101
 
102
+ // Unsend (delete for everyone) via the Relay mutation the web DM client uses:
103
+ // `IGDMessageUnsendDialogOffMsysMutation` (doc_id 26948700068153789). The
104
+ // legacy REST delete path (`/direct_v2/threads/<t>/items/<id>/delete/`) is
105
+ // dead on www.instagram.com (the whole /api/v1 namespace 404s) and on
106
+ // i.instagram.com it needs the mobile app identity, so we go straight to
107
+ // GraphQL. Captured live; verified against a real message.
101
108
  async unsend(threadID, itemID) {
102
- const res = await this.http.postForm(EP.ITEM_DELETE(threadID, itemID), {});
103
- return res?.status === "ok";
109
+ try {
110
+ const res = await this.http.postGraphQL(
111
+ "IGDMessageUnsendDialogOffMsysMutation",
112
+ DOC_ID.UNSEND_MESSAGE,
113
+ { message_id: String(itemID), send_data: { thread_id: String(threadID) } },
114
+ `https://www.instagram.com/direct/t/${threadID}/`
115
+ );
116
+ return Boolean(res && res.data && res.data.direct_unsend_message) && !res.errors;
117
+ } catch (_) {
118
+ return false;
119
+ }
104
120
  }
105
121
 
106
122
  // Instagram lets you edit an outgoing DM within 15 minutes of sending it.