├── .dockerignore ├── .github └── FUNDING.yml ├── .gitignore ├── .idea └── .gitignore ├── Dockerfile ├── README.md ├── package.json └── src ├── index.js └── insta-automator.js /.dockerignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | README.md 4 | .gitignore 5 | Dockerfile 6 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: thinhvux 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /.env 3 | /node_modules/ 4 | /src/.env 5 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12 2 | 3 | WORKDIR /app 4 | 5 | RUN apt-get update \ 6 | && apt-get install -y wget gnupg libxss1 \ 7 | && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ 8 | && sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \ 9 | && apt-get update \ 10 | && apt-get install -y google-chrome-unstable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf \ 11 | --no-install-recommends \ 12 | && rm -rf /var/lib/apt/lists/* 13 | 14 | ENV TINI_VERSION v0.19.0 15 | ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini 16 | RUN chmod +x /tini 17 | ENTRYPOINT ["/tini", "--"] 18 | 19 | COPY package.json . 20 | 21 | RUN npm i 22 | 23 | COPY . . 24 | 25 | ENV NODE_ENV=production 26 | 27 | CMD ["node", "src/index.js"] 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # INTRODUCTION 2 | 3 | It's been 6 years, Instagram has challenged us a lots with their *public* api. 4 | 5 | With a ton of work to implement Instagram application with OAuth stuff. 6 | 7 | All we want is making a curl request and the data must be present in response data. That's all. 8 | 9 | It's a time to get rid of this stupid issue. 10 | 11 | -- 12 | 13 | I know this war will never end. And the win side is always Instagram, but I'll try my best to fight back. 14 | 15 | My bruh, show me your support via: paypal.me: paypal.me/vutro 16 | 17 | # GUIDE 18 | 19 | For latest update, please visit following SO thread: 20 | 21 | https://stackoverflow.com/questions/11796349/instagram-how-to-get-my-user-id-from-username/38342137#38342137 22 | 23 | 24 | # RESOURCES 25 | 26 | There is a prebuilt Docker image, you can using it instead. 27 | 28 | https://hub.docker.com/repository/docker/dockerer123456/insta-proxy-server 29 | 30 | 31 | # DEMO 32 | 33 | NOTE THAT: This demo video may be outdated over time, but the code & Docker image is always the latest. 34 | 35 | https://www.youtube.com/watch?v=frHC1jOfK1k 36 | 37 | 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gmb-server", 3 | "version": "2.0.0", 4 | "description": "", 5 | "main": "./src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/gigasource/gmb-server.git" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/gigasource/gmb-server/issues" 18 | }, 19 | "homepage": "https://github.com/gigasource/gmb-server#readme", 20 | "dependencies": { 21 | "body-parser": "^1.19.0", 22 | "cors": "^2.8.5", 23 | "dotenv": "^8.2.0", 24 | "express": "^4.17.1", 25 | "puppeteer": "^14.2.1", 26 | "uuid": "^8.2.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const bodyParser = require('body-parser') 3 | const cors = require('cors') 4 | const InstaAutomator = require('./insta-automator') 5 | 6 | const app = express() 7 | app.use(cors()) 8 | app.use(bodyParser.urlencoded({extended: false})) 9 | app.use(bodyParser.json()) 10 | 11 | InstaAutomator.login() 12 | app.get('/v1', async (req, res) => res.send(await InstaAutomator.proxy(req.query.url))) 13 | app.get('/xigappid', async (req, res) => res.send(await InstaAutomator.gatherXIGAppId())) 14 | app.get('/', async (req, res) => { 15 | res.send(`/v1/url?={instagram_api} to get json data. \r\n /xigappid to re-evaluate x-ig-app-id if the bootstrapped value is outdated in some cases.`) 16 | }) 17 | const port = process.env.PORT || 8084 18 | app.listen(port, () => console.log(`App start at port: ${port}`)) 19 | -------------------------------------------------------------------------------- /src/insta-automator.js: -------------------------------------------------------------------------------- 1 | const puppeteer = require('puppeteer'); 2 | 3 | function log() { 4 | if (process.env.ENABLE_LOG) { 5 | console.log(...arguments) 6 | } 7 | } 8 | 9 | if (process.env.NODE_ENV !== 'production') { 10 | log('Not in production mode. Using .env file!') 11 | require('dotenv').config() 12 | } 13 | 14 | const cred = { 15 | username: process.env.INSTAGRAM_USERNAME, 16 | password: process.env.INSTAGRAM_PASSWORD 17 | } 18 | 19 | log('Using', process.env.INSTAGRAM_USERNAME, 'as authenticated user.'); 20 | 21 | const makeClick = (page) => { 22 | return async (sel, shouldThrow = true) => { 23 | try { 24 | return await page.click(sel) 25 | } catch (e) { 26 | log('click exception: ', e.message) 27 | if (shouldThrow) 28 | throw e 29 | } 30 | } 31 | } 32 | 33 | const selector = { 34 | login: { 35 | url: "https://www.instagram.com/accounts/login/", 36 | username: "input[name=\"username\"]", 37 | password: "input[name=\"password\"]", 38 | loginBtn: "button[type=\"submit\"]", 39 | }, 40 | } 41 | 42 | let browser; 43 | async function login() { 44 | if (browser) 45 | return 46 | await loginHeadless(); 47 | await gatherXIGAppId(); 48 | } 49 | 50 | process.on('uncaughtException', e => console.log(e)) 51 | 52 | async function loginWithUI() { 53 | try { 54 | browser = await puppeteer.launch({ 55 | headless: false, 56 | args: [ 57 | '--no-sandbox', 58 | ] 59 | }) 60 | 61 | const page = await browser.newPage() 62 | await page.goto(selector.login.url, { 63 | waitUntil: 'networkidle0', 64 | }); 65 | 66 | const click = makeClick(page) 67 | await click(selector.login.username) 68 | await page.keyboard.type(cred.username) 69 | await click(selector.login.password) 70 | await page.keyboard.type(cred.password) 71 | await click(selector.login.loginBtn) 72 | await page.waitForNavigation({ 73 | waitUntil: 'networkidle0', 74 | }); 75 | } catch (e) { 76 | console.error(e) 77 | } 78 | } 79 | 80 | let loggedIn = false; 81 | let xIGAppId = ''; 82 | 83 | async function loginHeadless() { 84 | try { 85 | log('LoginHeadless...') 86 | browser = await puppeteer.launch({ 87 | headless: true, 88 | args: [ 89 | '--no-sandbox', 90 | ] 91 | }) 92 | const page = await browser.newPage() 93 | await page.goto(selector.login.url, { 94 | waitUntil: 'networkidle0', 95 | }); 96 | const click = makeClick(page) 97 | log('Selecting username input...') 98 | await click(selector.login.username) 99 | await page.keyboard.type(cred.username) 100 | log('Selecting password input...') 101 | await click(selector.login.password) 102 | await page.keyboard.type(cred.password) 103 | log('Click login button...') 104 | await click(selector.login.loginBtn) 105 | await page.waitForNavigation({ 106 | waitUntil: 'networkidle0', 107 | }); 108 | // TODO: ensure that login is not error! 109 | log('Logged in...') 110 | loggedIn = true; 111 | } catch (e) { 112 | console.error(e) 113 | } 114 | } 115 | 116 | async function gatherXIGAppId() { 117 | log('gatherXIGAppId...'); 118 | const page = await browser.newPage(); 119 | await page.setRequestInterception(true); 120 | page.on('request', request => { 121 | const request_headers = request.headers(); 122 | log('request_headers', request_headers) 123 | if (request_headers['x-ig-app-id']) { 124 | xIGAppId = request_headers['x-ig-app-id']; 125 | log('Found: x-ig-app-id', request_headers['x-ig-app-id']); 126 | } 127 | request.continue(); 128 | }); 129 | const theRockProfile = 'https://www.instagram.com/therock/' 130 | await page.goto(theRockProfile, {waitUntil: 'networkidle0'}); 131 | } 132 | 133 | async function proxy(url) { 134 | try { 135 | if (!loggedIn) 136 | return "Not logged in"; 137 | 138 | log('proxy to', url); 139 | const page = await browser.newPage(); 140 | if (xIGAppId) { 141 | page.setExtraHTTPHeaders({ 'x-ig-app-id': xIGAppId }) 142 | } 143 | const response = await page.goto(url, { waitUntil: 'networkidle0', }) 144 | const responseBody = await response.text(); 145 | await page.close(); 146 | log('responseBody', responseBody); 147 | return responseBody; 148 | } catch (e) { 149 | console.error(e) 150 | return ""; 151 | } 152 | } 153 | 154 | module.exports = { 155 | login, 156 | proxy, 157 | gatherXIGAppId 158 | } 159 | --------------------------------------------------------------------------------