Stalker akun Instagram (akun Public Only)

JavaScriptPublic
by zx-apiOct 10, 2026, 11:45 AMExpires: Never224 views
Stalker akun Instagram (akun Public Only)
Raw
1/**
2 * Stalker Instagram 
3 * Source: https://igwatcher.com/
4 * 
5 * Ngambil Info Akun Instagram Reels, Postingan, Story
6 * Khusus Akun Instagram Public No Privat 
7 */
8 
9 
10/**
11 * Credit By Zx
12 * Sumber Kode: https://whatsapp.com/channel/0029VbDLqe7EquiSF4STU13o
13 * Note: Kalau Mau Di sher Lagi Minimal Credit atau sumber jangan di hapus dong
14 * Website React WhatsApp Gratis:
15 * https://react.v1.zfile.web.id/
16 */
17 
18/**
19 
20Yang Mau Beli Akun Netflix, Top-Up game, Sunt1k S0Smed
21Bisa Ke Zix Store.
22 
23 https://zixstore.kiospremium.site/
24 
25Terimakasih ๐Ÿ˜๐Ÿ‘€
26*/
27
28const https = require('https');
29const crypto = require('crypto');
30const zlib = require('zlib');
31
32const BASE = 'https://igwatcher.com';
33
34const UA =
35  'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/154.0.0.0 Mobile Safari/537.36';
36
37const SIG_JS_VERSION = 478;
38
39let IGW_PT = '';
40
41function sleep(ms) {
42  return new Promise((resolve) => setTimeout(resolve, ms));
43}
44
45function normalizeUsername(raw) {
46  let s = String(raw || '').trim();
47
48  try {
49    if (/^https?:\/\//i.test(s)) {
50      const u = new URL(s);
51      if ((u.hostname || '').includes('instagram.com')) {
52        const parts = u.pathname.replace(/^\/+|\/+$/g, '').split('/');
53        if (parts[0] && parts[0].toLowerCase() === 'stories') {
54          s = parts[1] || '';
55        } else {
56          s = parts[0] || '';
57        }
58      }
59    }
60  } catch (err) {
61  }
62
63  if (s.startsWith('@')) s = s.slice(1);
64
65  s = s.toLowerCase().replace(/\/+$/g, '');
66
67  if (!/^[a-z0-9._]{1,30}$/.test(s) || /^[._]+$/.test(s)) {
68    throw new Error('Username tidak valid: ' + raw);
69  }
70
71  return s;
72}
73
74function canonicalize(urlObj) {
75  const ps = [];
76
77  for (const [k, v] of urlObj.searchParams.entries()) {
78    ps.push([k, v]);
79  }
80
81  ps.sort(function (a, b) {
82    if (a[0] === b[0]) {
83      if (a[1] < b[1]) return -1;
84      if (a[1] > b[1]) return 1;
85      return 0;
86    }
87
88    if (a[0] < b[0]) return -1;
89    if (a[0] > b[0]) return 1;
90    return 0;
91  });
92
93  const query = ps
94    .map((p) => {
95      return encodeURIComponent(p[0]) + '=' + encodeURIComponent(p[1]);
96    })
97    .join('&');
98
99  return urlObj.pathname + '?' + query;
100}
101
102function buildSignature(urlObj) {
103  if (!IGW_PT) {
104    throw new Error('IGW_PT belum di-set. Jalankan initToken() dulu.');
105  }
106
107  const ts = String(Date.now());
108  const canon = canonicalize(urlObj);
109
110  const payload = IGW_PT + '\n' + ts + '\n' + canon;
111
112  const s = crypto
113    .createHash('sha256')
114    .update(payload, 'utf8')
115    .digest('hex');
116
117  return `v=1;j=${SIG_JS_VERSION};t=${ts};p=${IGW_PT};s=${s}`;
118}
119
120function httpRequest(url, options, redirectCount) {
121  options = options || {};
122  redirectCount = redirectCount || 0;
123
124  return new Promise((resolve, reject) => {
125    let parsed;
126
127    try {
128      parsed = new URL(url);
129    } catch (err) {
130      reject(new Error('URL tidak valid: ' + url));
131      return;
132    }
133
134    const headers = Object.assign(
135      {
136        'User-Agent': UA,
137        'Accept': '*/*',
138        'Accept-Language': 'id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7',
139        'Accept-Encoding': 'identity',
140        'Referer': BASE + '/',
141      },
142      options.headers || {}
143    );
144
145    const reqOptions = {
146      protocol: parsed.protocol || 'https:',
147      hostname: parsed.hostname,
148      port: parsed.port || 443,
149      path: parsed.pathname + parsed.search,
150      method: options.method || 'GET',
151      headers: headers,
152    };
153
154    const req = https.request(reqOptions, function (res) {
155      if (
156        [301, 302, 303, 307, 308].includes(res.statusCode) &&
157        res.headers.location &&
158        redirectCount < 5
159      ) {
160        res.resume();
161        const nextUrl = new URL(res.headers.location, url).toString();
162        resolve(httpRequest(nextUrl, options, redirectCount + 1));
163        return;
164      }
165
166      const chunks = [];
167
168      res.on('data', function (chunk) {
169        chunks.push(chunk);
170      });
171
172      res.on('end', function () {
173        let buf = Buffer.concat(chunks);
174
175        const encoding = String(
176          res.headers['content-encoding'] || ''
177        ).toLowerCase();
178
179        try {
180          if (buf.length > 0) {
181            if (encoding.includes('gzip')) {
182              buf = zlib.gunzipSync(buf);
183            } else if (encoding.includes('br')) {
184              buf = zlib.brotliDecompressSync(buf);
185            } else if (encoding.includes('deflate')) {
186              try {
187                buf = zlib.inflateSync(buf);
188              } catch (err) {
189                buf = zlib.inflateRawSync(buf);
190              }
191            } else if (encoding.includes('zstd')) {
192              reject(
193                new Error(
194                  'Response memakai zstd. Node bawaan belum tentu bisa decode zstd. Coba jalankan di Node versi lebih baru atau gunakan library HTTP lain.'
195                )
196              );
197              return;
198            }
199          }
200        } catch (err) {
201          reject(new Error('Gagal decode response: ' + err.message));
202          return;
203        }
204
205        resolve({
206          statusCode: res.statusCode,
207          headers: res.headers,
208          body: buf.toString('utf8'),
209        });
210      });
211    });
212
213    req.on('error', function (err) {
214      reject(err);
215    });
216
217    req.setTimeout(60000, function () {
218      req.destroy(new Error('Request timeout 60 detik'));
219    });
220
221    if (options.body) {
222      req.write(options.body);
223    }
224
225    req.end();
226  });
227}
228
229async function initToken(force) {
230  if (IGW_PT && !force) return IGW_PT;
231
232  const res = await httpRequest(BASE + '/', {
233    headers: {
234      Accept:
235        'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
236    },
237  });
238
239  if (res.statusCode !== 200) {
240    throw new Error('Gagal membuka homepage igwatcher.com: HTTP ' + res.statusCode);
241  }
242
243  const m = res.body.match(/data-igw-pt\s*=\s*"([^"]+)"/i);
244
245  if (!m || !m[1]) {
246    throw new Error('data-igw-pt tidak ditemukan di HTML homepage.');
247  }
248
249  const pt = m[1];
250
251  // /^1\.\d{9,11}\.[0-9a-f]{32}$/
252  if (!/^1\.\d{9,11}\.[0-9a-f]{32}$/.test(pt)) {
253    throw new Error('Format data-igw-pt tidak dikenali: ' + pt);
254  }
255
256  IGW_PT = pt;
257
258  return IGW_PT;
259}
260
261async function apiGet(pathname, params) {
262  await initToken(false);
263
264  const url = new URL(pathname, BASE);
265
266  const keys = Object.keys(params || {}).sort();
267
268  for (const key of keys) {
269    const val = params[key];
270    if (val === undefined || val === null) continue;
271    url.searchParams.append(key, String(val));
272  }
273
274  const sig = buildSignature(url);
275
276  const res = await httpRequest(url.toString(), {
277    headers: {
278      'X-IGW-Sig': sig,
279      'X-IGW-TS': 'e=timeout',
280      Accept: 'application/json, text/plain, */*',
281    },
282  });
283
284  let json = null;
285
286  try {
287    json = JSON.parse(res.body);
288  } catch (err) {
289    json = null;
290  }
291
292  if (res.statusCode !== 200) {
293    let msg = 'HTTP ' + res.statusCode;
294
295    if (json && json.message) {
296      msg += ' | ' + json.message;
297    } else if (json && json.note) {
298      msg += ' | ' + json.note;
299    } else {
300      msg += ' | ' + res.body.slice(0, 300);
301    }
302
303    throw new Error(msg);
304  }
305
306  if (!json) {
307    throw new Error(
308      'Response bukan JSON dari ' + pathname + ': ' + res.body.slice(0, 300)
309    );
310  }
311
312  if (json.status && json.status !== 'success') {
313    throw new Error(
314      'API error: ' + (json.message || json.note || JSON.stringify(json).slice(0, 300))
315    );
316  }
317
318  return json;
319}
320
321function getItems(resp) {
322  if (!resp) return [];
323  return resp.data || resp.items || resp.reels || [];
324}
325
326async function stalkerig(username) {
327  username = normalizeUsername(username);
328
329  await initToken(false);
330
331  const errors = [];
332
333  async function safeCall(type, fn) {
334    try {
335      return await fn();
336    } catch (err) {
337      errors.push({
338        type: type,
339        message: err.message,
340      });
341      return null;
342    }
343  }
344
345  const search = await safeCall('search', function () {
346    return apiGet('/wp-json/igw/v1/search', {
347      username: username,
348    });
349  });
350
351  if (!search) {
352    const errMsg =
353      errors.find((e) => e.type === 'search')?.message ||
354      'Gagal mengambil data profil.';
355    throw new Error(errMsg);
356  }
357
358  const user =
359    search && search.data && search.data.user ? search.data.user : null;
360
361  if (!user || !user.username) {
362    throw new Error(
363      'Profil tidak ditemukan atau unavailable. note=' +
364        (search.note || 'unknown')
365    );
366  }
367
368  const result = {
369    username: user.username || username,
370    full_name: user.full_name || '',
371    is_private: Boolean(user.is_private),
372    is_verified: Boolean(user.is_verified),
373    biography: user.biography || '',
374    external_url: user.external_url || '',
375    profile_pic_url: user.profile_pic_url || '',
376    followers:
377      user.follower_count != null
378        ? user.follower_count
379        : user.edge_followed_by && user.edge_followed_by.count != null
380        ? user.edge_followed_by.count
381        : null,
382    following:
383      user.following_count != null
384        ? user.following_count
385        : user.edge_follow && user.edge_follow.count != null
386        ? user.edge_follow.count
387        : null,
388    posts_count:
389      user.edge_owner_to_timeline_media &&
390      user.edge_owner_to_timeline_media.count != null
391        ? user.edge_owner_to_timeline_media.count
392        : null,
393    latest_reel_media: user.latest_reel_media || null,
394    scraped_at: new Date().toISOString(),
395    media: {
396      stories: [],
397      reels: [],
398      posts: [],
399      highlights: [],
400      tagged: [],
401    },
402    meta: {
403      stories_total: null,
404      reels_total: null,
405      posts_total: null,
406      highlights_total: null,
407      tagged_total: null,
408      posts_has_more: false,
409      reels_has_more: false,
410      tagged_has_more: false,
411    },
412    profile_raw: user,
413    errors: errors,
414  };
415
416  if (result.is_private) {
417    result.errors.push({
418      type: 'private',
419      message: 'Akun private. Media tidak diambil.',
420    });
421
422    return result;
423  }
424
425  await sleep(1200);
426
427  const stories = await safeCall('stories', function () {
428    return apiGet('/wp-json/igw/v1/stories', {
429      username: username,
430    });
431  });
432
433  if (stories) {
434    result.media.stories = getItems(stories);
435    result.meta.stories_total =
436      stories.total != null ? stories.total : result.media.stories.length;
437  }
438
439  await sleep(1200);
440
441  const reels = await safeCall('reels', function () {
442    return apiGet('/api/reels', {
443      username: username,
444      limit: 24,
445      lazy: 1,
446    });
447  });
448
449  if (reels) {
450    result.media.reels = getItems(reels);
451    result.meta.reels_total =
452      reels.total != null ? reels.total : result.media.reels.length;
453    result.meta.reels_has_more = Boolean(reels.has_more);
454  }
455
456  await sleep(1200);
457
458  const posts = await safeCall('posts', function () {
459    return apiGet('/wp-json/igw/v1/posts', {
460      username: username,
461      limit: 24,
462      lazy: 1,
463    });
464  });
465
466  if (posts) {
467    result.media.posts = getItems(posts);
468    result.meta.posts_total =
469      posts.total != null ? posts.total : result.media.posts.length;
470    result.meta.posts_has_more = Boolean(posts.has_more);
471  }
472
473  await sleep(1200);
474
475  const highlights = await safeCall('highlights', function () {
476    return apiGet('/wp-json/igw/v1/highlights', {
477      username: username,
478    });
479  });
480
481  if (highlights) {
482    result.media.highlights = getItems(highlights);
483    result.meta.highlights_total =
484      highlights.total != null
485        ? highlights.total
486        : result.media.highlights.length;
487  }
488
489  await sleep(1200);
490
491  const tagged = await safeCall('tagged', function () {
492    return apiGet('/wp-json/igw/v1/tagged', {
493      username: username,
494      limit: 24,
495    });
496  });
497
498  if (tagged) {
499    result.media.tagged = getItems(tagged);
500    result.meta.tagged_total =
501      tagged.total != null ? tagged.total : result.media.tagged.length;
502    result.meta.tagged_has_more = Boolean(tagged.has_more);
503  }
504
505  return result;
506}
507
508module.exports = stalkerig;
509module.exports.stalkerig = stalkerig;
510
511/**
512 * CLI:
513 * node index.js username
514 */
515 
516 
517/**
518if (require.main === module) {
519  const username = process.argv[2];
520
521  if (!username) {
522    console.error('Usage: node index.js <username>');
523    console.error('Example: node index.js iniipasya__');
524    process.exit(1);
525  }
526
527  stalkerig(username)
528    .then((data) => {
529      console.log(JSON.stringify(data, null, 2));
530    })
531    .catch((err) => {
532      console.error('ERROR:', err.message);
533      process.exit(1);
534    });
535}
536**/
536 linesยท12,490 charsยท12.2 KB