UNPKG

shitgram

Version:

A JavaScript library to make requests to Instagram

636 lines (566 loc) 20 kB
'use strict'; const { Session } = require('../plugins'); const ExcludeType = require('../enums/EExcludeType.js'); const { GetEmails, RewriteObjects } = require('../components'); const { UserValidator, StoryValidator, HighlightValidator, MediaValidator } = require('../components/validators'); const axios = require('axios'); module.exports = class Shitgram { /** * * @param {Object} settings - Settings to generate a new session id or define an existing session */ constructor(settings = {}) { this.username = settings.username || false; this.password = settings.password || false; this.sessionID = settings.sessionID || false; } /** * Generate a new session id or return a defined sessionID * @returns {Promise} - Returned promise */ async getSessionID() { if (this.username && this.password && !this.sessionID) { return Session( this.username, this.password ) .then((data) => data.sessionID) .catch((error) => error); } else if (this.sessionID) { return this.sessionID; } else { return; } } /** * Get user data that is only available with a session id. * @param {String} userID - * @param {String} sessionID - * @returns {Promise} - Returned promise */ getUserDataWithSession(userID, sessionID) { if (typeof userID !== 'string' || typeof sessionID !== 'string') { throw new TypeError(`Expected a string, got ${typeof userID !== 'string' ? typeof userID : typeof sessionID}`); } return axios({ method: 'GET', url: `https://i.instagram.com/api/v1/users/${userID}/info/`, headers: { 'Cookie': `sessionid=${sessionID}`, 'User-Agent': 'Instagram 9.5.1 (iPhone9,2; iOS 10_0_2; en_US; en-US; scale=2.61; 1080x1920) AppleWebKit/420+' } }) .then((response) => response) .catch((error) => error); } /** * Get user stories that is only available with a session id. * @param {String} userID - * @param {String} sessionID - * @returns {Promise} - Returned promise */ getUserStoriesWithSession(userID, sessionID) { if (typeof userID !== 'string' || typeof sessionID !== 'string') { throw new TypeError(`Expected a string, got ${typeof userID !== 'string' ? typeof userID : typeof sessionID}`); } return axios({ method: 'GET', url: `https://i.instagram.com/api/v1/feed/user/${userID}/reel_media/`, headers: { 'Cookie': `sessionid=${sessionID}`, 'User-Agent': 'Instagram 9.5.1 (iPhone9,2; iOS 10_0_2; en_US; en-US; scale=2.61; 1080x1920) AppleWebKit/420+' } }) .then((response) => response) .catch((error) => error); } /** * Get user highlight that is only available with a session id. * @param {String} userID - * @param {String} sessionID - * @returns {Promise} - Returned promise */ getUserHighlightsWithSession(userID, sessionID) { if (typeof userID !== 'string' || typeof sessionID !== 'string') { throw new TypeError(`Expected a string, got ${typeof userID !== 'string' ? typeof userID : typeof sessionID}`); } return axios({ method: 'GET', url: `https://i.instagram.com/api/v1/highlights/${userID}/highlights_tray/`, headers: { 'Cookie': `sessionid=${sessionID}`, 'User-Agent': 'Instagram 9.5.1 (iPhone9,2; iOS 10_0_2; en_US; en-US; scale=2.61; 1080x1920) AppleWebKit/420+' } }) .then((response) => response) .catch((error) => error); } /** * Get user details. * @param {String} username - Username or link for the user profile you want details about * @param {Object} options - Response settings * @returns {Promise} - Returned promise */ async user(username, options = {}) { if (typeof username !== 'string') { throw new TypeError(`Expected a string, got ${typeof username}`); } options.defaultResponse = options.defaultResponse || false; username = UserValidator(username); try { const { data } = await axios.get(`https://www.instagram.com/${username}?__a=1`); const sessionID = await this.getSessionID(); if (sessionID) { const { data: data_ws } = await this.getUserDataWithSession(data.graphql.user.id, sessionID); if (options.defaultResponse) { return data; } else { return Object.assign({}, objectUser(data), { avatarURL: data_ws.user.hd_profile_pic_url_info.url }); } } else { if (options.defaultResponse) { return data; } else { return objectUser(data); } } function objectUser(data) { const user = data.graphql.user; return { id: user.id, url: `https://www.instagram.com/${user.username}`, avatarURL: user.profile_pic_url_hd, isPrivate: user.is_private, isVerified: user.is_verified, isBusiness: user.is_business_account, businessCategory: user.business_category_name, username: user.username, fullName: user.full_name, biography: user.biography, email: GetEmails(user.biography), website: user.external_url, followers: user.edge_followed_by.count, following: user.edge_follow.count, posts: user.edge_owner_to_timeline_media.count }; } } catch (error) { if (error.hasOwnProperty('response')) { if (error.response.status === 404) { error.message = `User ${username} not found`; } } throw { method: error.hasOwnProperty('config') ? error.config.method.toUpperCase() : undefined, statusCode: error.hasOwnProperty('response') ? error.response.status : undefined, message: error.message }; } } /** * Get story details. * @param {String} username - Username or link for the user stories you want details about * @param {Object} options - Response settings * @returns {Promise} - Returned promise */ async story(username, options = {}) { if (typeof username !== 'string') { throw new TypeError(`Expected a string, got ${typeof username}`); } options.defaultResponse = options.defaultResponse || false; options.exclude = options.exclude || undefined; username = StoryValidator(username); try { const sessionID = await this.getSessionID(); if (sessionID) { const { data: { graphql: { user: { id: userID } } } } = await axios.get(`https://www.instagram.com/${username}?__a=1`); const { data: data_st } = await this.getUserStoriesWithSession(userID, sessionID); const { data: data_ws } = await this.getUserDataWithSession(userID, sessionID); if (options.defaultResponse) { return data_st; } else { return RewriteObjects(objectStory(data_st), 'author.avatarURL', data_ws.user.hd_profile_pic_url_info.url); } } else { const { data } = await axios.get(`https://api.storiesig.com/stories/${username}`); if (options.defaultResponse) { return data; } else { return objectStory(data); } } function objectStory(data) { const stories = []; for (let i = 0; i < data.items.length; i++) { const story = data.items[i]; story.video_versions === undefined ? stories.push({ id: story.pk, shortcode: story.code, mediaURL: story.image_versions2.candidates[0].url, dimensions: { width: story.original_width, height: story.original_height }, isVideo: false }) : stories.push({ id: story.pk, shortcode: story.code, mediaURL: story.video_versions[0].url, dimensions: { width: story.original_width, height: story.original_height }, isVideo: true }); } return { id: data.id, stories: options.exclude !== undefined ? stories.filter((story) => story.mediaURL.split('?')[0].indexOf(ExcludeType[options.exclude.toUpperCase()]) === -1) : stories, totalStories: data.media_count, author: { id: data.user.pk, url: `https://www.instagram.com/${data.user.username}`, avatarURL: data.user.profile_pic_url, isPrivate: data.user.is_private, isVerified: data.user.is_verified, username: data.user.username, fullName: data.user.full_name } }; } } catch (error) { if (error.hasOwnProperty('response')) { if (error.response.status === 404) { error.message = `User ${username} not have stories`; } } throw { method: error.hasOwnProperty('config') ? error.config.method.toUpperCase() : undefined, statusCode: error.hasOwnProperty('response') ? error.response.status : undefined, message: error.message }; } } /** * Get highlight details. * @param {String} mediaID - Highlight id or link to it * @param {Object} options - Response settings * @returns {Promise} - Returned promise */ async highlight(mediaID, options = {}) { if (typeof mediaID !== 'string') { throw new TypeError(`Expected a string, got ${typeof mediaID}`); } options.defaultResponse = options.defaultResponse || false; options.exclude = options.exclude || undefined; mediaID = HighlightValidator(mediaID); try { const { data: { user: { id: userID } } } = await axios.get(`https://www.instagram.com/stories/highlights/${mediaID}?__a=1`); const sessionID = await this.getSessionID(); if (sessionID) { const { data: data_hl } = await this.getUserHighlightsWithSession(userID, sessionID); const { data: data_ws } = await this.getUserDataWithSession(userID, sessionID); if (options.defaultResponse) { return data_hl; } else { return RewriteObjects(objectHighlight(data_hl), 'author.avatarURL', data_ws.user.hd_profile_pic_url_info.url); } } else { throw new Error('Malformed credentials'); } function objectHighlight({ tray }) { let data; tray.filter(function(highlight) { if (highlight.id.split(':')[1] === mediaID && highlight.can_reshare) { let highlights = []; for (let i = 0; i < highlight.items.length; i++) { const media = highlight.items[i]; media.video_versions === undefined ? highlights.push({ mediaURL: media.image_versions2.candidates[0].url, isVideo: false }) : highlights.push({ mediaURL: media.video_versions.slice(-1)[0].url, isVideo: true }); } data = dataHandler(highlight, highlights); } else if (highlight.id.split(':')[1] === mediaID) { data = dataHandler(highlight, []); } function dataHandler(data, highlights) { return { id: data.id.split(':')[1], title: data.title, canReshare: data.can_reshare, highlights: highlights ? options.exclude !== undefined ? highlights.filter((highlight) => highlight.mediaURL.split('?')[0].indexOf(ExcludeType[options.exclude.toUpperCase()]) === -1) : highlights : highlights, createdAt: data.created_at, totalMedias: data.media_count, author: { id: data.user.pk, url: `https://www.instagram.com/${data.user.username}`, avatarURL: data.user.profile_pic_url, isPrivate: data.user.is_private, isVerified: data.user.is_verified, username: data.user.username, fullName: data.user.full_name } }; } }); return data; } } catch (error) { if (error.hasOwnProperty('response')) { if (error.response.status === 404) { error.message = `Highlight ${mediaID} not found`; } } throw { method: error.hasOwnProperty('config') ? error.config.method.toUpperCase() : undefined, statusCode: error.hasOwnProperty('response') ? error.response.status : undefined, message: error.message }; } } /** * Get image post details. * @param {String} mediaCode - Post code or link to it * @param {Object} options - Response settings * @returns {Promise} - Returned promise */ async image(mediaCode, options = {}) { if (typeof mediaCode !== 'string') { throw new TypeError(`Expected a string, got ${typeof mediaCode}`); } options.defaultResponse = options.defaultResponse || false; mediaCode = MediaValidator(mediaCode); try { const { data } = await axios.get(`https://www.instagram.com/p/${mediaCode}?__a=1`); const sessionID = await this.getSessionID(); if (sessionID) { const { data: data_ws } = await this.getUserDataWithSession(data.graphql.shortcode_media.owner.id, sessionID); if (options.defaultResponse) { return data; } else { return RewriteObjects(objectImage(data), 'author.avatarURL', data_ws.user.hd_profile_pic_url_info.url); } } else { if (options.defaultResponse) { return data; } else { return objectImage(data); } } function objectImage(data) { const media = data.graphql.shortcode_media; return { id: media.id, shortcode: media.shortcode, imageURL: media.display_resources.slice(-1)[0].src, description: media.edge_media_to_caption.edges.length > 0 ? media.edge_media_to_caption.edges[0].node.text : '', caption: media.accessibility_caption, dimensions: { width: media.dimensions.width, height: media.dimensions.height }, likes: media.edge_media_preview_like.count, comments: media.edge_media_preview_comment.count, isVideo: media.is_video, createdAt: media.taken_at_timestamp, author: { id: media.owner.id, url: `https://www.instagram.com/${media.owner.username}`, avatarURL: media.owner.profile_pic_url, isPrivate: media.owner.is_private, isVerified: media.owner.is_verified, username: media.owner.username, fullName: media.owner.full_name } }; } } catch (error) { if (error.hasOwnProperty('response')) { if (error.response.status === 404) { error.message = `Media ${mediaCode} not found`; } } throw { method: error.hasOwnProperty('config') ? error.config.method.toUpperCase() : undefined, statusCode: error.hasOwnProperty('response') ? error.response.status : undefined, message: error.message }; } } /** * Get video post details. * @param {String} mediaCode - Post code or link to it * @param {Object} options - Response settings * @returns {Promise} - Returned promise */ async video(mediaCode, options = {}) { if (typeof mediaCode !== 'string') { throw new TypeError(`Expected a string, got ${typeof mediaCode}`); } options.defaultResponse = options.defaultResponse || false; mediaCode = MediaValidator(mediaCode); try { const { data } = await axios.get(`https://www.instagram.com/p/${mediaCode}?__a=1`); const sessionID = await this.getSessionID(); if (sessionID) { const { data: data_ws } = await this.getUserDataWithSession(data.graphql.shortcode_media.owner.id, sessionID); if (options.defaultResponse) { return data; } else { return RewriteObjects(objectVideo(data), 'author.avatarURL', data_ws.user.hd_profile_pic_url_info.url); } } else { if (options.defaultResponse) { return data; } else { return objectVideo(data); } } function objectVideo(data) { const media = data.graphql.shortcode_media; return { id: media.id, shortcode: media.shortcode, videoURL: media.video_url, description: media.edge_media_to_caption.edges.length > 0 ? media.edge_media_to_caption.edges[0].node.text : '', dimensions: { width: media.dimensions.width, height: media.dimensions.height }, views: media.video_view_count, likes: media.edge_media_preview_like.count, comments: media.edge_media_to_parent_comment.count, isVideo: media.is_video, createdAt: media.taken_at_timestamp, author: { id: media.owner.id, url: `https://www.instagram.com/${media.owner.username}`, avatarURL: media.owner.profile_pic_url, isPrivate: media.owner.is_private, isVerified: media.owner.is_verified, username: media.owner.username, fullName: media.owner.full_name } }; } } catch (error) { if (error.hasOwnProperty('response')) { if (error.response.status === 404) { error.message = `Media ${mediaCode} not found`; } } throw { method: error.hasOwnProperty('config') ? error.config.method.toUpperCase() : undefined, statusCode: error.hasOwnProperty('response') ? error.response.status : undefined, message: error.message }; } } /** * Get album post details. * @param {String} albumCode - Album post code or link to it * @param {Object} options - Response settings * @returns {Promise} - Returned promise */ async album(albumCode, options = {}) { if (typeof albumCode !== 'string') { throw new TypeError(`Expected a string, got ${typeof albumCode}`); } options.defaultResponse = options.defaultResponse || false; options.exclude = options.exclude || undefined; albumCode = MediaValidator(albumCode); try { const { data } = await axios.get(`https://www.instagram.com/p/${albumCode}?__a=1`); const sessionID = await this.getSessionID(); if (sessionID) { const { data: data_ws } = await this.getUserDataWithSession(data.graphql.shortcode_media.owner.id, sessionID); if (options.defaultResponse) { return data; } else { return RewriteObjects(objectAlbum(data), 'author.avatarURL', data_ws.user.hd_profile_pic_url_info.url); } } else { if (options.defaultResponse) { return data; } else { return objectAlbum(data); } } function objectAlbum(data) { const post = data.graphql.shortcode_media; const album = post.edge_sidecar_to_children.edges; let medias = []; for (let i = 0; i < album.length; i++) { const media = album[i].node; !media.is_video ? medias.push({ id: media.id, shortcode: media.shortcode, mediaURL: media.display_resources.slice(-1)[0].src, caption: media.accessibility_caption, dimensions: { width: media.dimensions.width, height: media.dimensions.height }, isVideo: media.is_video }) : medias.push({ id: media.id, shortcode: media.shortcode, mediaURL: media.video_url, caption: null, dimensions: { width: media.dimensions.width, height: media.dimensions.height }, views: media.video_view_count, isVideo: media.is_video }); } return { id: post.id, shortcode: post.shortcode, medias: options.exclude !== undefined ? medias.filter((media) => media.mediaURL.split('?')[0].indexOf(ExcludeType[options.exclude.toUpperCase()]) === -1) : medias, totalMedias: medias.length, likes: post.edge_media_preview_like.count, comments: post.edge_media_to_parent_comment.count, createdAt: post.taken_at_timestamp, author: { id: post.owner.id, url: `https://www.instagram.com/${post.owner.username}`, avatarURL: post.owner.profile_pic_url, isPrivate: post.owner.is_private, isVerified: post.owner.is_verified, username: post.owner.username, fullName: post.owner.full_name } }; } } catch (error) { if (error.message === `Cannot read property 'edges' of undefined`) { error.message = `${albumCode} does not contain multiple medias`; } throw { method: error.hasOwnProperty('config') ? error.config.method.toUpperCase() : undefined, statusCode: error.hasOwnProperty('response') ? error.response.status : undefined, message: error.message }; } } };