diff --git a/src/config/index.ts b/src/config/index.ts index 28f271b..2c74229 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -4,6 +4,8 @@ export const config = { instagram_base_z4: 'https://z-p4.www.instagram.com', /** Instagram Api v1 */ instagram_api_v1: 'https://i.instagram.com/api/v1', + /** Instagram Api v2 */ + instagram_api_v2: 'https://i.instagram.com/api/v2', /** Instagram API Search User */ instagram_search_url: 'https://www.instagram.com/web/search/topsearch/?query=', /** Android User-Agent */ @@ -12,4 +14,4 @@ export const config = { desktop: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36', /** iPhone User-Agent */ iPhone: 'Instagram 123.0.0.21.114 (iPhone; CPU iPhone OS 11_4 like Mac OS X; en_US; en-US; scale=2.00; 750x1334) AppleWebKit/605.1.15' -} \ No newline at end of file +} diff --git a/src/index.ts b/src/index.ts index 022fc9d..35cddf6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,13 +1,13 @@ /* Muhamad Ristiyanto _ https://github.com/Gimenz * Created, Published at Selasa, 9 Maret 2021 - * Modified, Updated at Minggu, 20 Februari 2022 + * Modified, Updated at Minggu, 19 Januari 2025 */ import fs from 'fs' import FormData from 'form-data'; import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'; import { bufferToStream, getPostType, parseCookie, randInt, shortcodeFormatter } from './utils/index'; -import { username, userId, seachTerm, url, IgCookie, ProductType, MediaType, IChangedProfilePicture, ISearchFollow, IGPostMetadata, PostGraphQL } from './types'; +import { username, userId, seachTerm, url, IgCookie, ProductType, MediaType, IChangedProfilePicture, ISearchFollow, IGPostMetadata, PostGraphQL, UserFollow } from './types'; import { IGUserMetadata, UserGraphQL } from './types/UserMetadata'; import { IGStoriesMetadata, ItemStories, StoriesGraphQL } from './types/StoriesMetadata'; import { highlight_ids_query, highlight_media_query, post_shortcode_query } from './helper/query'; @@ -25,6 +25,8 @@ import { IPaginatedPosts } from './types/PaginatedPosts'; export * from './utils' export * as InstagramMetadata from './types' export * from './helper/Session'; +const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); + export class igApi { /** * Recommended to set cookie for most all IG Request @@ -94,7 +96,7 @@ export class igApi { public searchFollower = async (userId: userId, seachTerm: seachTerm): Promise => { const res = await this.FetchIGAPI( config.instagram_api_v1, - `/friendships/${userId}/followers/?count=12&query=${seachTerm}&search_surface=follow_list_page`, + `/friendships/${userId}/followers/?count=50&query=${seachTerm}&search_surface=follow_list_page`, config.iPhone, ); return res?.data || res @@ -109,6 +111,124 @@ export class igApi { return res?.data || res } + public getAllFollowers = async (userId: string, searchTerm: string = ""): Promise => { + let followers: UserFollow[] = []; + let nextMaxId: string | undefined = undefined; + + do { + const res = await this.FetchIGAPI( + config.instagram_api_v1, + `/friendships/${userId}/followers/`, + config.iPhone, + { + params: { + count: 12, // Adjust `count` to the maximum allowed by Instagram + query: searchTerm, + max_id: nextMaxId, // Pagination parameter + search_surface: "follow_list_page", + }, + } + ); + + const data: ISearchFollow = res?.data || res; + followers.push(...(data.users || [])); + nextMaxId = data.next_max_id; // Update nextMaxId for the next page + await sleep(2000); + } while (nextMaxId); + + return { users: followers, status: "success" }; + }; + + public getAllFollowing = async (userId: string, searchTerm: string = ""): Promise => { + let following: UserFollow[] = []; + let nextMaxId: string | undefined = undefined; + + do { + const res = await this.FetchIGAPI( + config.instagram_api_v1, + `/friendships/${userId}/following/`, + config.iPhone, + { + params: { + count: 12, // Adjust `count` to the maximum allowed by Instagram + query: searchTerm, + max_id: nextMaxId, // Pagination parameter + }, + } + ); + + const data: ISearchFollow = res?.data || res; + following.push(...(data.users || [])); + nextMaxId = data.next_max_id; // Update nextMaxId for the next page + await sleep(2000); + } while (nextMaxId); + + return { users: following, status: "success" }; + }; + + /* + * add new for automate view stories & like stories + * fitur dari Rizka Nugraha + */ + + public getStories = async (): Promise => { + try { + const res = await this.FetchIGAPI( + config.instagram_api_v1, + `/feed/reels_tray/`, + config.android + ); + return res?.data?.tray || []; + } catch (error) { + console.error("Error fetching stories:", error); + return []; + } + }; + + public getStoryById = async (reelId: string): Promise => { + const res = await this.FetchIGAPI(config.instagram_api_v1, `/feed/reels_media/?reel_ids=${reelId}`, config.android) + if (res && res.data && res.data.reels_media && res.data.reels_media.length > 0) { + return res.data.reels_media[0]; + } + } + + public viewStories = async (reelId: string, itemId: string): Promise => { + try { + const res = await this.FetchIGAPI( + config.instagram_api_v2, + `/media/seen/`, + config.android, + { + data: { + reels: { [`${reelId}_${itemId}`]: [0] }, // Reel ID and timestamp + reel_media_id: itemId, + reel_author_id: reelId, + view_source: "feed_timeline", + }, + method: "POST", + } + ); + return res; + } catch (error) { + console.error("Error viewing story:", error); + } + }; + + public likeStories = async (mediaId: string): Promise => { + try { + const res = await this.FetchIGAPI( + config.instagram_api_v1, + `/media/${mediaId}/like/`, + config.android, + { method: "POST" } + ); + return res; + } catch (error) { + console.error("Error liking story:", error); + } + }; + + private _formatSidecar = (data: IRawBody): Array => { const gql = data.items[0] let urls: MediaUrls[] = [] @@ -405,7 +525,7 @@ export class igApi { return graphql.data.reels_media[0].items.map((item) => ({ owner: graphql.data.reels_media[0].owner, media_id: item.id, - mimetype: item.is_video ? 'video/mp4' || 'video/gif' : 'image/jpeg', + mimetype: item.is_video ? 'video/mp4' : 'image/jpeg', taken_at: item.taken_at_timestamp, type: item.is_video ? 'video' : 'image', url: item.is_video ? item.video_resources[0].src : item.display_url, diff --git a/src/types/searchFollow.ts b/src/types/searchFollow.ts index 0aa7da1..d7edfff 100644 --- a/src/types/searchFollow.ts +++ b/src/types/searchFollow.ts @@ -1,18 +1,20 @@ import { User } from "./PostModels"; export interface ISearchFollow { - users?: UserFollow[]; - big_list?: boolean; - page_size?: number; - has_more?: boolean; + users?: UserFollow[]; + big_list?: boolean; + page_size?: number; + has_more?: boolean; should_limit_list_of_followers?: boolean; - use_clickable_see_more?: boolean; - show_spam_follow_request_tab?: boolean; - status?: string; + use_clickable_see_more?: boolean; + show_spam_follow_request_tab?: boolean; + status?: string; + next_max_id?: string; } export type UserFollow = User & { - pk_id?: string; + pk_id?: string; third_party_downloads_enabled?: number; - strong_id__?: string; -} \ No newline at end of file + strong_id__?: string; +} +