├── test.js ├── enums └── EExcludeType.js ├── components ├── index.js ├── validators │ ├── UserValidator.js │ ├── index.js │ ├── MediaValidator.js │ ├── StoryValidator.js │ └── HighlightValidator.js ├── GetEmails.js └── RewriteObjects.js ├── plugins ├── index.js └── Session.js ├── index.js ├── examples └── generate_session.example.js ├── package.json ├── LICENSE ├── .gitignore ├── README.md └── lib └── Shitgram.js /test.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /enums/EExcludeType.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @enum EExcludeType 3 | */ 4 | module.exports = { 5 | IMAGE: 'jpg', 6 | VIDEO: 'mp4' 7 | }; -------------------------------------------------------------------------------- /components/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | GetEmails: require('./GetEmails.js').index, 3 | RewriteObjects: require('./RewriteObjects.js').index 4 | }; -------------------------------------------------------------------------------- /plugins/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | Session(username, password) { 3 | return require('./Session.js').sessionID(username, password); 4 | } 5 | }; -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const Shitgram = require('./lib/Shitgram.js'); 2 | 3 | Shitgram.ExcludeType = require('./enums/EExcludeType.js'); 4 | Shitgram.Plugins = { 5 | Session: require('./plugins').Session 6 | }; 7 | 8 | module.exports = Shitgram; -------------------------------------------------------------------------------- /components/validators/UserValidator.js: -------------------------------------------------------------------------------- 1 | module.exports = class UserValidator { 2 | static index(input, filter = /^(?:https?:\/\/)?(?:www\.)?(?:instagram\.com\/)([A-Za-z0-9-_]+)/) { 3 | if (input.match(filter)) { 4 | return input.match(filter)[1]; 5 | } 6 | 7 | return input; 8 | } 9 | }; -------------------------------------------------------------------------------- /components/validators/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | UserValidator: require('./UserValidator.js').index, 3 | StoryValidator: require('./StoryValidator.js').index, 4 | HighlightValidator: require('./HighlightValidator.js').index, 5 | MediaValidator: require('./MediaValidator.js').index 6 | }; -------------------------------------------------------------------------------- /components/validators/MediaValidator.js: -------------------------------------------------------------------------------- 1 | module.exports = class MediaValidator { 2 | static index(input, filter = /^(?:https?:\/\/)?(?:www\.)?(?:instagram\.com\/(?:p\/))((\w|-){11})(?:\S+)?$/) { 3 | if (input.match(filter)) { 4 | return input.match(filter)[1]; 5 | } 6 | 7 | return input; 8 | } 9 | }; -------------------------------------------------------------------------------- /components/validators/StoryValidator.js: -------------------------------------------------------------------------------- 1 | module.exports = class StoryValidator { 2 | static index(input, filter = /^(?:https?:\/\/)?(?:www\.)?(?:instagram\.com\/stories\/)([A-Za-z0-9-_]+)/) { 3 | if (input.match(filter)) { 4 | return input.match(filter)[1]; 5 | } 6 | 7 | return input; 8 | } 9 | }; -------------------------------------------------------------------------------- /components/validators/HighlightValidator.js: -------------------------------------------------------------------------------- 1 | module.exports = class HighlightValidator { 2 | static index(input, filter = /^(?:https?:\/\/)?(?:www\.)?(?:instagram\.com\/(?:stories\/highlights\/))((\w|-){17})(?:\S+)?$/) { 3 | if (input.match(filter)) { 4 | return input.match(filter)[1]; 5 | } 6 | 7 | return input; 8 | } 9 | }; -------------------------------------------------------------------------------- /components/GetEmails.js: -------------------------------------------------------------------------------- 1 | module.exports = class GetEmails { 2 | /** 3 | * Filters all emails contained in a string 4 | * @param {String} input - Text containing emails to be filtered 5 | * @returns {Array} - Filtered emails 6 | */ 7 | static index(input, filter = /(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))/g) { 8 | return input.match(filter); 9 | } 10 | }; -------------------------------------------------------------------------------- /components/RewriteObjects.js: -------------------------------------------------------------------------------- 1 | module.exports = class RewriteObjects { 2 | /** 3 | * Rewrite property of an object 4 | * @param {Object} object - Object to be rewritten 5 | * @param {String} property - Object property to rewrite 6 | * @param {} value - New value that will be assigned to property 7 | * @returns {Object} - Rewritten Object 8 | */ 9 | static index(object, property, value) { 10 | return property.split('.').reduce((result, part, index, array) => { 11 | if (index === array.length - 1) { 12 | result[part] = value; 13 | return object; 14 | } 15 | 16 | return result[part]; 17 | }, object); 18 | } 19 | }; -------------------------------------------------------------------------------- /examples/generate_session.example.js: -------------------------------------------------------------------------------- 1 | // Declaring Session from "shitgram" library 2 | const { Session } = require('../').Plugins; 3 | 4 | // Declaring Session from "shitgram/plugins" library plugins path 5 | const { Session } = require('../plugins'); 6 | 7 | Session(process.env.USERNAME, process.env.PASSWORD) 8 | .then(function(data) { 9 | // Handle success 10 | console.log(data); 11 | 12 | /* 13 | { userID: '1234567890', 14 | csrfToken: 'k44Ha0E2cDxc5lBNz3tfd3tk1LgTlhFa', 15 | sessionID: '7565175908%3ARVdJQzLsBldS9G%3A20' } 16 | */ 17 | 18 | }) 19 | .catch(function(error) { 20 | // Handle error 21 | console.log(error); 22 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@shitgram/node-shitgram", 3 | "version": "1.4.3", 4 | "description": "A JavaScript library to make requests to Instagram", 5 | "main": "index.js", 6 | "repository": "git://github.com/shitgram/node-shitgram.git", 7 | "publishConfig": { 8 | "registry": "https://npm.pkg.github.com/" 9 | }, 10 | "author": { 11 | "name": "Tenasa Tupitsyn (EL TENASA)", 12 | "url": "http://tenasa.gq" 13 | }, 14 | "engines": { 15 | "node": ">=10" 16 | }, 17 | "dependencies": { 18 | "axios": "^0.19.0", 19 | "request": "^2.88.0" 20 | }, 21 | "keywords": [ 22 | "shitgram", 23 | "instagram", 24 | "insta", 25 | "users", 26 | "stories", 27 | "highlights", 28 | "images", 29 | "videos", 30 | "albums", 31 | "session", 32 | "scrap", 33 | "fetch" 34 | ], 35 | "bugs": "https://github.com/shitgram/node-shitgram/issues", 36 | "homepage": "https://github.com/shitgram/node-shitgram#readme", 37 | "license": "MIT" 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Shitgram 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | *.lock 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # next.js build output 63 | .next 64 | -------------------------------------------------------------------------------- /plugins/Session.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const axios = require('axios'); 4 | 5 | module.exports = class Session { 6 | /** 7 | * Requesting csrftoken 8 | * @returns {Promise} - New csrf token 9 | */ 10 | static async csrfToken() { 11 | try { 12 | let csrfToken; 13 | 14 | const { headers } = await axios({ 15 | method: 'GET', 16 | url: 'https://www.instagram.com/accounts/login/' 17 | }); 18 | 19 | for (const item in headers['set-cookie']) { 20 | if (headers['set-cookie'][item].match('^csrftoken=')) { 21 | csrfToken = headers['set-cookie'][item].split(';')[0].split('=')[1]; 22 | } 23 | } 24 | 25 | return csrfToken; 26 | } catch (error) { 27 | throw error; 28 | } 29 | } 30 | 31 | /** 32 | * Generating new session ID 33 | * @param {String} username - Instagram account username 34 | * @param {String} password - Instagram account password 35 | * @returns {Promise} - New session info 36 | */ 37 | static async sessionID(username, password) { 38 | if (typeof username !== 'string' || typeof password !== 'string') { 39 | throw new TypeError(`Expected a string, got ${typeof username !== 'string' ? typeof username : typeof password}`); 40 | } 41 | 42 | try { 43 | const csrfToken = await this.csrfToken(); 44 | let sessionID; 45 | 46 | const { headers, body } = await this.request({ 47 | method: 'POST', 48 | url: 'https://www.instagram.com/accounts/login/ajax/', 49 | form: { 50 | username, 51 | password 52 | }, 53 | headers: { 54 | 'X-CSRFToken': csrfToken 55 | } 56 | }); 57 | 58 | const { userId: userID, authenticated } = JSON.parse(body); 59 | 60 | if (authenticated) { 61 | for (const item in headers['set-cookie']) { 62 | if (headers['set-cookie'][item].match('^sessionid=')) { 63 | sessionID = headers['set-cookie'][item].split(';')[0].split('=')[1]; 64 | } 65 | } 66 | 67 | return { 68 | userID, 69 | csrfToken, 70 | sessionID 71 | }; 72 | } else { 73 | throw new Error('Username or password is incorrect. Please check and try again'); 74 | } 75 | } catch (error) { 76 | throw error; 77 | } 78 | } 79 | 80 | static request(data) { 81 | return new Promise(function(resolve, reject) { 82 | require('request')(data, function(error, response, body) { 83 | if (!error) { 84 | resolve(response); 85 | } else { 86 | reject(error); 87 | } 88 | }); 89 | }); 90 | } 91 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 | A JavaScript library to make requests to Instagram 4 |

5 | Version 6 | Node 7 | Dependencies 8 | License 9 |

10 | 11 | ## Contents 12 | 13 | - [Installation](#installation) 14 | - [Example](#example) 15 | - [Documentation](#documentation) 16 | - [Plugins](#plugins) 17 | 18 | ## Installation 19 | 20 | ``` 21 | $ yarn add shitgram 22 | ``` 23 | 24 | ## Example 25 | 26 | ```js 27 | const Shitgram = require('shitgram'); 28 | 29 | const shitgram = new Shitgram(); 30 | 31 | shitgram.user('tenasatupitsyn') 32 | .then((data) => { 33 | // Handle success 34 | console.log(data); 35 | 36 | /* 37 | { id: '7661979279', 38 | url: 'https://www.instagram.com/tenasatupitsyn', 39 | avatarURL: 'https://instagram.frec8-1.fna.fbcdn.net/vp/d5...', 40 | isPrivate: false, 41 | isVerified: false, 42 | isBusiness: true, 43 | businessCategory: 'Creators & Celebrities', 44 | username: 'tenasatupitsyn', 45 | fullName: 'Tenasa M. Tupitsyn', 46 | biography: 'YuGi TeNaSa 1010.\nLara/VE 🇻🇪', 47 | email: null, 48 | website: null, 49 | followers: 0, 50 | following: 0, 51 | posts: 0 } 52 | */ 53 | 54 | }) 55 | .catch((error) => { 56 | // Handle error 57 | console.log(error); 58 | }); 59 | ``` 60 | 61 | ## Documentation 62 | 63 | ### new Shitgram(credentials) ⇒ [Constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/constructor) 64 | 65 | - **`credentials`** : [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) _(Optional)_ 66 | - `username` : [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)
67 |        Instagram account username 68 | - `password` : [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)
69 |        Instagram account password 70 | - `sessionID` : [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)
71 |        An instagram session id. Will be used if you have not set `username` and `password`. 72 | 73 | You will not need to set a session id if you have already set username and password. 74 | 75 | ### getSessionID ⇒ [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 76 | Generate a new session id or return a defined sessionID 77 | 78 | If username and password are set, a new session id will always be generated. So that a unique session id will be returned set property sessionID in the credentials. 79 | 80 | It is possible to get the session ID without the builder by using [plug-in](#plugins). 81 | 82 | **Returns**:    [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)> — Session id generated or returning from credentials 83 | 84 | ### getUserDataWithSession(userID, sessionID) ⇒ [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 85 | Get user data that is only available with a session id 86 | 87 | - `userID` : [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) _(Required)_ 88 | - `sessionID` : [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) _(Required)_ 89 | 90 | **Returns**:    [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)> — User data obtained only in session 91 | 92 | ### getUserStoriesWithSession(userID, sessionID) ⇒ [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 93 | Get user stories that is only available with a session id 94 | 95 | - `userID` : [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) _(Required)_ 96 | - `sessionID` : [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) _(Required)_ 97 | 98 | **Returns**:    [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)> — User story data obtained in one session only 99 | 100 | ### getUserHighlightsWithSession(userID, sessionID) ⇒ [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 101 | Get user highlight that is only available with a session id 102 | 103 | - `userID` : [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) _(Required)_ 104 | - `sessionID` : [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) _(Required)_ 105 | 106 | **Returns**:    [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)> — User Highlight data obtained in one session only 107 | 108 | ### user(param[, options]) ⇒ [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 109 | Get user details 110 | 111 | - **`param`** : [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) _(Required)_
112 |        `Username` or `link` for the user profile you want details about 113 | - **`options`** : [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) _(Optional)_ 114 | - `defaultResponse` : [Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)
115 |        Set `true` to return instagram default response, `false` is set to default. 116 | 117 | **Returns**:    [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)> — User data 118 | 119 | ### story(param[, options]) ⇒ [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 120 | Get story details 121 | 122 | - **`param`** : [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) _(Required)_
123 |        `Username` or `link` for the user stories you want details about 124 | - **`options`** : [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) _(Optional)_ 125 | - `defaultResponse` : [Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)
126 |        Set `true` to return default response from storiesig.com or if you have set credentials the response will be from instagram.com, `false` is set to default. 127 | - `exclude` : [ExcludeType](#excludetype--enumstring)
128 |        The file type to [exclude](https://github.com/shitgram/node-shitgram/blob/master/enums/EExcludeType.js) from the response, will not exclude if **defaultResponse** is `true`. 129 | 130 | **Returns**:    [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)> — User story data 131 | 132 | ### highlight(param[, options]) ⇒ [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 133 | Get highlight details 134 | 135 | - **`param`** : [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) _(Required)_
136 |        `Highlight id` or `link` to it 137 | - **`options`** : [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) _(Optional)_ 138 | - `defaultResponse` : [Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)
139 |        Set `true` to return instagram default response, `false` is set to default. 140 | - `exclude` : [ExcludeType](#excludetype--enumstring)
141 |        The file type to [exclude](https://github.com/shitgram/node-shitgram/blob/master/enums/EExcludeType.js) from the response, will not exclude if **defaultResponse** is `true`. 142 | 143 | Highlights will be returned if they have been set by the author to be shared, check availability of highlights in the `canReshare` property; if null, highlights will be an empty array. 144 | 145 | **Returns**:    [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)> — User highlight data 146 | 147 | ### image(param[, options]) ⇒ [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 148 | Get image post details 149 | 150 | - **`param`** : [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) _(Required)_
151 |        `Post code` or `link` to it 152 | - **`options`** : [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) _(Optional)_ 153 | - `defaultResponse` : [Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)
154 |        Set `true` to return instagram default response, `false` is set to default. 155 | 156 | **Returns**:    [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)> — Image data 157 | 158 | ### video(param[, options]) ⇒ [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 159 | Get video post details 160 | 161 | - **`param`** : [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) _(Required)_
162 |        `Post code` or `link` to it 163 | - **`options`** : [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) _(Optional)_ 164 | - `defaultResponse` : [Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)
165 |        Set `true` to return instagram default response, `false` is set to default. 166 | 167 | **Returns**:    [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)> — Video data 168 | 169 | ### album(param[, options]) ⇒ [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 170 | Get album post details 171 | 172 | - **`param`** : [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) _(Required)_
173 |        Album `post code` or `link` to it 174 | - **`options`** : [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) _(Optional)_ 175 | - `defaultResponse` : [Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)
176 |        Set `true` to return instagram default response, `false` is set to default. 177 | - `exclude` : [ExcludeType](#excludetype--enumstring)
178 |        The file type to [exclude](https://github.com/shitgram/node-shitgram/blob/master/enums/EExcludeType.js) from the response, will not exclude if **defaultResponse** is `true`. 179 | 180 | **Returns**:    [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)> — Album data 181 | 182 | ### ExcludeType : _enum<[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)>_ 183 | File type to exclude from response 184 | 185 | Available properties: `IMAGE` - `VIDEO` 186 | 187 | ## Plugins 188 | 189 | ### Session(username, password) ⇒ [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 190 | Generate a new csrfToken and sessionID from Instagram username and password 191 | 192 | - `username` : [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) _(Required)_
193 |        Instagram account username 194 | - `password` : [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) _(Required)_
195 |        Instagram account password 196 | 197 | A brief example of use [here](https://github.com/shitgram/node-shitgram/blob/master/examples/generate_session.example.js) 198 | 199 | **Returns**:    [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)> — New session info 200 | 201 |

202 | BACK TO TOP 203 |

-------------------------------------------------------------------------------- /lib/Shitgram.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { Session } = require('../plugins'); 4 | const ExcludeType = require('../enums/EExcludeType.js'); 5 | const { 6 | GetEmails, 7 | RewriteObjects 8 | } = require('../components'); 9 | const { 10 | UserValidator, 11 | StoryValidator, 12 | HighlightValidator, 13 | MediaValidator 14 | } = require('../components/validators'); 15 | const axios = require('axios'); 16 | 17 | module.exports = class Shitgram { 18 | 19 | /** 20 | * 21 | * @param {Object} settings - Settings to generate a new session id or define an existing session 22 | */ 23 | constructor(settings = {}) { 24 | this.username = settings.username || false; 25 | this.password = settings.password || false; 26 | this.sessionID = settings.sessionID || false; 27 | } 28 | 29 | /** 30 | * Generate a new session id or return a defined sessionID 31 | * @returns {Promise} - Session id generated or returning from credentials 32 | */ 33 | async getSessionID() { 34 | if (this.username && this.password && !this.sessionID) { 35 | return Session( 36 | this.username, 37 | this.password 38 | ) 39 | .then((data) => data.sessionID) 40 | .catch((error) => error); 41 | } else if (this.sessionID) { 42 | return this.sessionID; 43 | } else { 44 | return; 45 | } 46 | } 47 | 48 | /** 49 | * Get user data that is only available with a session id 50 | * @param {String} userID 51 | * @param {String} sessionID 52 | * @returns {Promise} - User data obtained only in session 53 | */ 54 | getUserDataWithSession(userID, sessionID) { 55 | if (typeof userID !== 'string' || typeof sessionID !== 'string') { 56 | throw new TypeError(`Expected a string, got ${typeof userID !== 'string' ? typeof userID : typeof sessionID}`); 57 | } 58 | 59 | return axios({ 60 | method: 'GET', 61 | url: `https://i.instagram.com/api/v1/users/${userID}/info/`, 62 | headers: { 63 | 'Cookie': `sessionid=${sessionID}`, 64 | 'User-Agent': 'Instagram 9.5.1 (iPhone9,2; iOS 10_0_2; en_US; en-US; scale=2.61; 1080x1920) AppleWebKit/420+' 65 | } 66 | }) 67 | .then((response) => response) 68 | .catch((error) => error); 69 | } 70 | 71 | /** 72 | * Get user stories that is only available with a session id 73 | * @param {String} userID 74 | * @param {String} sessionID 75 | * @returns {Promise} - User story data obtained in one session only 76 | */ 77 | getUserStoriesWithSession(userID, sessionID) { 78 | if (typeof userID !== 'string' || typeof sessionID !== 'string') { 79 | throw new TypeError(`Expected a string, got ${typeof userID !== 'string' ? typeof userID : typeof sessionID}`); 80 | } 81 | 82 | return axios({ 83 | method: 'GET', 84 | url: `https://i.instagram.com/api/v1/feed/user/${userID}/reel_media/`, 85 | headers: { 86 | 'Cookie': `sessionid=${sessionID}`, 87 | 'User-Agent': 'Instagram 9.5.1 (iPhone9,2; iOS 10_0_2; en_US; en-US; scale=2.61; 1080x1920) AppleWebKit/420+' 88 | } 89 | }) 90 | .then((response) => response) 91 | .catch((error) => error); 92 | } 93 | 94 | /** 95 | * Get user highlight that is only available with a session id 96 | * @param {String} userID 97 | * @param {String} sessionID 98 | * @returns {Promise} - User Highlight data obtained in one session only 99 | */ 100 | getUserHighlightsWithSession(userID, sessionID) { 101 | if (typeof userID !== 'string' || typeof sessionID !== 'string') { 102 | throw new TypeError(`Expected a string, got ${typeof userID !== 'string' ? typeof userID : typeof sessionID}`); 103 | } 104 | 105 | return axios({ 106 | method: 'GET', 107 | url: `https://i.instagram.com/api/v1/highlights/${userID}/highlights_tray/`, 108 | headers: { 109 | 'Cookie': `sessionid=${sessionID}`, 110 | 'User-Agent': 'Instagram 9.5.1 (iPhone9,2; iOS 10_0_2; en_US; en-US; scale=2.61; 1080x1920) AppleWebKit/420+' 111 | } 112 | }) 113 | .then((response) => response) 114 | .catch((error) => error); 115 | } 116 | 117 | /** 118 | * Get user details 119 | * @param {String} username - Username or link for the user profile you want details about 120 | * @param {Object} options - Response settings 121 | * @returns {Promise} - User data 122 | */ 123 | async user(username, options = {}) { 124 | if (typeof username !== 'string') { 125 | throw new TypeError(`Expected a string, got ${typeof username}`); 126 | } 127 | 128 | options.defaultResponse = options.defaultResponse || false; 129 | 130 | username = UserValidator(username); 131 | 132 | try { 133 | const { data } = await axios.get(`https://www.instagram.com/${username}?__a=1`); 134 | const sessionID = await this.getSessionID(); 135 | 136 | if (sessionID) { 137 | const { data: data_ws } = await this.getUserDataWithSession(data.graphql.user.id, sessionID); 138 | 139 | if (options.defaultResponse) { 140 | return data; 141 | } else { 142 | return Object.assign({}, objectUser(data), { avatarURL: data_ws.user.hd_profile_pic_url_info.url }); 143 | } 144 | } else { 145 | if (options.defaultResponse) { 146 | return data; 147 | } else { 148 | return objectUser(data); 149 | } 150 | } 151 | 152 | function objectUser(data) { 153 | const user = data.graphql.user; 154 | return { 155 | id: user.id, 156 | url: `https://www.instagram.com/${user.username}`, 157 | avatarURL: user.profile_pic_url_hd, 158 | isPrivate: user.is_private, 159 | isVerified: user.is_verified, 160 | isBusiness: user.is_business_account, 161 | businessCategory: user.business_category_name, 162 | username: user.username, 163 | fullName: user.full_name, 164 | biography: user.biography, 165 | email: GetEmails(user.biography), 166 | website: user.external_url, 167 | followers: user.edge_followed_by.count, 168 | following: user.edge_follow.count, 169 | posts: user.edge_owner_to_timeline_media.count 170 | }; 171 | } 172 | } catch (error) { 173 | if (error.hasOwnProperty('response')) { 174 | if (error.response.status === 404) { 175 | error.message = `User ${username} not found`; 176 | } 177 | } 178 | 179 | throw { 180 | method: error.hasOwnProperty('config') ? error.config.method.toUpperCase() : undefined, 181 | statusCode: error.hasOwnProperty('response') ? error.response.status : undefined, 182 | message: error.message 183 | }; 184 | } 185 | } 186 | 187 | /** 188 | * Get story details 189 | * @param {String} username - Username or link for the user stories you want details about 190 | * @param {Object} options - Response settings 191 | * @returns {Promise} - User story data 192 | */ 193 | async story(username, options = {}) { 194 | if (typeof username !== 'string') { 195 | throw new TypeError(`Expected a string, got ${typeof username}`); 196 | } 197 | 198 | options.defaultResponse = options.defaultResponse || false; 199 | options.exclude = options.exclude || undefined; 200 | 201 | username = StoryValidator(username); 202 | 203 | try { 204 | const sessionID = await this.getSessionID(); 205 | 206 | if (sessionID) { 207 | const { data: { graphql: { user: { id: userID } } } } = await axios.get(`https://www.instagram.com/${username}?__a=1`); 208 | const { data: data_st } = await this.getUserStoriesWithSession(userID, sessionID); 209 | const { data: data_ws } = await this.getUserDataWithSession(userID, sessionID); 210 | 211 | if (options.defaultResponse) { 212 | return data_st; 213 | } else { 214 | return RewriteObjects(objectStory(data_st), 'author.avatarURL', data_ws.user.hd_profile_pic_url_info.url); 215 | } 216 | } else { 217 | const { data } = await axios.get(`https://api.storiesig.com/stories/${username}`); 218 | 219 | if (options.defaultResponse) { 220 | return data; 221 | } else { 222 | return objectStory(data); 223 | } 224 | } 225 | 226 | function objectStory(data) { 227 | const stories = []; 228 | 229 | for (let i = 0; i < data.items.length; i++) { 230 | const story = data.items[i]; 231 | story.video_versions === undefined 232 | ? stories.push({ 233 | id: story.pk, 234 | shortcode: story.code, 235 | mediaURL: story.image_versions2.candidates[0].url, 236 | dimensions: { 237 | width: story.original_width, 238 | height: story.original_height 239 | }, 240 | isVideo: false 241 | }) 242 | : stories.push({ 243 | id: story.pk, 244 | shortcode: story.code, 245 | mediaURL: story.video_versions[0].url, 246 | dimensions: { 247 | width: story.original_width, 248 | height: story.original_height 249 | }, 250 | isVideo: true 251 | }); 252 | } 253 | 254 | return { 255 | id: data.id, 256 | stories: options.exclude !== undefined ? stories.filter((story) => story.mediaURL.split('?')[0].indexOf(ExcludeType[options.exclude.toUpperCase()]) === -1) : stories, 257 | totalStories: data.media_count, 258 | author: { 259 | id: data.user.pk, 260 | url: `https://www.instagram.com/${data.user.username}`, 261 | avatarURL: data.user.profile_pic_url, 262 | isPrivate: data.user.is_private, 263 | isVerified: data.user.is_verified, 264 | username: data.user.username, 265 | fullName: data.user.full_name 266 | } 267 | }; 268 | } 269 | } catch (error) { 270 | if (error.hasOwnProperty('response')) { 271 | if (error.response.status === 404) { 272 | error.message = `User ${username} not have stories`; 273 | } 274 | } 275 | 276 | throw { 277 | method: error.hasOwnProperty('config') ? error.config.method.toUpperCase() : undefined, 278 | statusCode: error.hasOwnProperty('response') ? error.response.status : undefined, 279 | message: error.message 280 | }; 281 | } 282 | } 283 | 284 | /** 285 | * Get highlight details 286 | * @param {String} mediaID - Highlight id or link to it 287 | * @param {Object} options - Response settings 288 | * @returns {Promise} - User highlight data 289 | */ 290 | async highlight(mediaID, options = {}) { 291 | if (typeof mediaID !== 'string') { 292 | throw new TypeError(`Expected a string, got ${typeof mediaID}`); 293 | } 294 | 295 | options.defaultResponse = options.defaultResponse || false; 296 | options.exclude = options.exclude || undefined; 297 | 298 | mediaID = HighlightValidator(mediaID); 299 | 300 | try { 301 | const { data: { user: { id: userID } } } = await axios.get(`https://www.instagram.com/stories/highlights/${mediaID}?__a=1`); 302 | const sessionID = await this.getSessionID(); 303 | 304 | if (sessionID) { 305 | const { data: data_hl } = await this.getUserHighlightsWithSession(userID, sessionID); 306 | const { data: data_ws } = await this.getUserDataWithSession(userID, sessionID); 307 | 308 | if (options.defaultResponse) { 309 | return data_hl; 310 | } else { 311 | return RewriteObjects(objectHighlight(data_hl), 'author.avatarURL', data_ws.user.hd_profile_pic_url_info.url); 312 | } 313 | } else { 314 | throw new Error('Malformed credentials'); 315 | } 316 | 317 | function objectHighlight({ tray }) { 318 | let data; 319 | 320 | tray.filter(function(highlight) { 321 | if (highlight.id.split(':')[1] === mediaID && highlight.can_reshare) { 322 | let highlights = []; 323 | 324 | for (let i = 0; i < highlight.items.length; i++) { 325 | const media = highlight.items[i]; 326 | 327 | media.video_versions === undefined 328 | ? highlights.push({ 329 | mediaURL: media.image_versions2.candidates[0].url, 330 | isVideo: false 331 | }) 332 | : highlights.push({ 333 | mediaURL: media.video_versions.slice(-1)[0].url, 334 | isVideo: true 335 | }); 336 | } 337 | 338 | data = dataHandler(highlight, highlights); 339 | } else if (highlight.id.split(':')[1] === mediaID) { 340 | data = dataHandler(highlight, []); 341 | } 342 | 343 | function dataHandler(data, highlights) { 344 | return { 345 | id: data.id.split(':')[1], 346 | title: data.title, 347 | canReshare: data.can_reshare, 348 | highlights: highlights ? options.exclude !== undefined ? highlights.filter((highlight) => highlight.mediaURL.split('?')[0].indexOf(ExcludeType[options.exclude.toUpperCase()]) === -1) : highlights : highlights, 349 | createdAt: data.created_at, 350 | totalMedias: data.media_count, 351 | author: { 352 | id: data.user.pk, 353 | url: `https://www.instagram.com/${data.user.username}`, 354 | avatarURL: data.user.profile_pic_url, 355 | isPrivate: data.user.is_private, 356 | isVerified: data.user.is_verified, 357 | username: data.user.username, 358 | fullName: data.user.full_name 359 | } 360 | }; 361 | } 362 | }); 363 | 364 | return data; 365 | } 366 | } catch (error) { 367 | if (error.hasOwnProperty('response')) { 368 | if (error.response.status === 404) { 369 | error.message = `Highlight ${mediaID} not found`; 370 | } 371 | } 372 | 373 | throw { 374 | method: error.hasOwnProperty('config') ? error.config.method.toUpperCase() : undefined, 375 | statusCode: error.hasOwnProperty('response') ? error.response.status : undefined, 376 | message: error.message 377 | }; 378 | } 379 | } 380 | 381 | /** 382 | * Get image post details 383 | * @param {String} mediaCode - Post code or link to it 384 | * @param {Object} options - Response settings 385 | * @returns {Promise} - Image data 386 | */ 387 | async image(mediaCode, options = {}) { 388 | if (typeof mediaCode !== 'string') { 389 | throw new TypeError(`Expected a string, got ${typeof mediaCode}`); 390 | } 391 | 392 | options.defaultResponse = options.defaultResponse || false; 393 | 394 | mediaCode = MediaValidator(mediaCode); 395 | 396 | try { 397 | const { data } = await axios.get(`https://www.instagram.com/p/${mediaCode}?__a=1`); 398 | const sessionID = await this.getSessionID(); 399 | 400 | if (sessionID) { 401 | const { data: data_ws } = await this.getUserDataWithSession(data.graphql.shortcode_media.owner.id, sessionID); 402 | 403 | if (options.defaultResponse) { 404 | return data; 405 | } else { 406 | return RewriteObjects(objectImage(data), 'author.avatarURL', data_ws.user.hd_profile_pic_url_info.url); 407 | } 408 | } else { 409 | if (options.defaultResponse) { 410 | return data; 411 | } else { 412 | return objectImage(data); 413 | } 414 | } 415 | 416 | function objectImage(data) { 417 | const media = data.graphql.shortcode_media; 418 | return { 419 | id: media.id, 420 | shortcode: media.shortcode, 421 | imageURL: media.display_resources.slice(-1)[0].src, 422 | description: media.edge_media_to_caption.edges.length > 0 ? media.edge_media_to_caption.edges[0].node.text : '', 423 | caption: media.accessibility_caption, 424 | dimensions: { 425 | width: media.dimensions.width, 426 | height: media.dimensions.height 427 | }, 428 | likes: media.edge_media_preview_like.count, 429 | comments: media.edge_media_preview_comment.count, 430 | isVideo: media.is_video, 431 | createdAt: media.taken_at_timestamp, 432 | author: { 433 | id: media.owner.id, 434 | url: `https://www.instagram.com/${media.owner.username}`, 435 | avatarURL: media.owner.profile_pic_url, 436 | isPrivate: media.owner.is_private, 437 | isVerified: media.owner.is_verified, 438 | username: media.owner.username, 439 | fullName: media.owner.full_name 440 | } 441 | }; 442 | } 443 | } catch (error) { 444 | if (error.hasOwnProperty('response')) { 445 | if (error.response.status === 404) { 446 | error.message = `Media ${mediaCode} not found`; 447 | } 448 | } 449 | 450 | throw { 451 | method: error.hasOwnProperty('config') ? error.config.method.toUpperCase() : undefined, 452 | statusCode: error.hasOwnProperty('response') ? error.response.status : undefined, 453 | message: error.message 454 | }; 455 | } 456 | } 457 | 458 | /** 459 | * Get video post details 460 | * @param {String} mediaCode - Post code or link to it 461 | * @param {Object} options - Response settings 462 | * @returns {Promise} - Video data 463 | */ 464 | async video(mediaCode, options = {}) { 465 | if (typeof mediaCode !== 'string') { 466 | throw new TypeError(`Expected a string, got ${typeof mediaCode}`); 467 | } 468 | 469 | options.defaultResponse = options.defaultResponse || false; 470 | 471 | mediaCode = MediaValidator(mediaCode); 472 | 473 | try { 474 | const { data } = await axios.get(`https://www.instagram.com/p/${mediaCode}?__a=1`); 475 | const sessionID = await this.getSessionID(); 476 | 477 | if (sessionID) { 478 | const { data: data_ws } = await this.getUserDataWithSession(data.graphql.shortcode_media.owner.id, sessionID); 479 | 480 | if (options.defaultResponse) { 481 | return data; 482 | } else { 483 | return RewriteObjects(objectVideo(data), 'author.avatarURL', data_ws.user.hd_profile_pic_url_info.url); 484 | } 485 | } else { 486 | if (options.defaultResponse) { 487 | return data; 488 | } else { 489 | return objectVideo(data); 490 | } 491 | } 492 | 493 | function objectVideo(data) { 494 | const media = data.graphql.shortcode_media; 495 | return { 496 | id: media.id, 497 | shortcode: media.shortcode, 498 | videoURL: media.video_url, 499 | description: media.edge_media_to_caption.edges.length > 0 ? media.edge_media_to_caption.edges[0].node.text : '', 500 | dimensions: { 501 | width: media.dimensions.width, 502 | height: media.dimensions.height 503 | }, 504 | views: media.video_view_count, 505 | likes: media.edge_media_preview_like.count, 506 | comments: media.edge_media_to_parent_comment.count, 507 | isVideo: media.is_video, 508 | createdAt: media.taken_at_timestamp, 509 | author: { 510 | id: media.owner.id, 511 | url: `https://www.instagram.com/${media.owner.username}`, 512 | avatarURL: media.owner.profile_pic_url, 513 | isPrivate: media.owner.is_private, 514 | isVerified: media.owner.is_verified, 515 | username: media.owner.username, 516 | fullName: media.owner.full_name 517 | } 518 | }; 519 | } 520 | } catch (error) { 521 | if (error.hasOwnProperty('response')) { 522 | if (error.response.status === 404) { 523 | error.message = `Media ${mediaCode} not found`; 524 | } 525 | } 526 | 527 | throw { 528 | method: error.hasOwnProperty('config') ? error.config.method.toUpperCase() : undefined, 529 | statusCode: error.hasOwnProperty('response') ? error.response.status : undefined, 530 | message: error.message 531 | }; 532 | } 533 | } 534 | 535 | /** 536 | * Get album post details 537 | * @param {String} albumCode - Album post code or link to it 538 | * @param {Object} options - Response settings 539 | * @returns {Promise} - Album data 540 | */ 541 | async album(albumCode, options = {}) { 542 | if (typeof albumCode !== 'string') { 543 | throw new TypeError(`Expected a string, got ${typeof albumCode}`); 544 | } 545 | 546 | options.defaultResponse = options.defaultResponse || false; 547 | options.exclude = options.exclude || undefined; 548 | 549 | albumCode = MediaValidator(albumCode); 550 | 551 | try { 552 | const { data } = await axios.get(`https://www.instagram.com/p/${albumCode}?__a=1`); 553 | const sessionID = await this.getSessionID(); 554 | 555 | if (sessionID) { 556 | const { data: data_ws } = await this.getUserDataWithSession(data.graphql.shortcode_media.owner.id, sessionID); 557 | 558 | if (options.defaultResponse) { 559 | return data; 560 | } else { 561 | return RewriteObjects(objectAlbum(data), 'author.avatarURL', data_ws.user.hd_profile_pic_url_info.url); 562 | } 563 | } else { 564 | if (options.defaultResponse) { 565 | return data; 566 | } else { 567 | return objectAlbum(data); 568 | } 569 | } 570 | 571 | function objectAlbum(data) { 572 | const post = data.graphql.shortcode_media; 573 | const album = post.edge_sidecar_to_children.edges; 574 | let medias = []; 575 | 576 | for (let i = 0; i < album.length; i++) { 577 | const media = album[i].node; 578 | 579 | !media.is_video 580 | ? medias.push({ 581 | id: media.id, 582 | shortcode: media.shortcode, 583 | mediaURL: media.display_resources.slice(-1)[0].src, 584 | caption: media.accessibility_caption, 585 | dimensions: { 586 | width: media.dimensions.width, 587 | height: media.dimensions.height 588 | }, 589 | isVideo: media.is_video 590 | }) 591 | : medias.push({ 592 | id: media.id, 593 | shortcode: media.shortcode, 594 | mediaURL: media.video_url, 595 | caption: null, 596 | dimensions: { 597 | width: media.dimensions.width, 598 | height: media.dimensions.height 599 | }, 600 | views: media.video_view_count, 601 | isVideo: media.is_video 602 | }); 603 | } 604 | 605 | return { 606 | id: post.id, 607 | shortcode: post.shortcode, 608 | medias: options.exclude !== undefined ? medias.filter((media) => media.mediaURL.split('?')[0].indexOf(ExcludeType[options.exclude.toUpperCase()]) === -1) : medias, 609 | totalMedias: medias.length, 610 | likes: post.edge_media_preview_like.count, 611 | comments: post.edge_media_to_parent_comment.count, 612 | createdAt: post.taken_at_timestamp, 613 | author: { 614 | id: post.owner.id, 615 | url: `https://www.instagram.com/${post.owner.username}`, 616 | avatarURL: post.owner.profile_pic_url, 617 | isPrivate: post.owner.is_private, 618 | isVerified: post.owner.is_verified, 619 | username: post.owner.username, 620 | fullName: post.owner.full_name 621 | } 622 | }; 623 | } 624 | } catch (error) { 625 | if (error.message === `Cannot read property 'edges' of undefined`) { 626 | error.message = `${albumCode} does not contain multiple medias`; 627 | } 628 | 629 | throw { 630 | method: error.hasOwnProperty('config') ? error.config.method.toUpperCase() : undefined, 631 | statusCode: error.hasOwnProperty('response') ? error.response.status : undefined, 632 | message: error.message 633 | }; 634 | } 635 | } 636 | }; --------------------------------------------------------------------------------