├── .gitignore ├── LICENSE ├── README.md ├── img └── tag-demo.gif ├── insta-like-feed.js ├── insta-like-tag.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | *.log 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright © 2017 Sinan Bolel 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Instagram Liker Script 2 | 3 | ### Demo: Liking Top Posts for a Tag 4 | 5 | ![tag demo](img/tag-demo.gif) 6 | -------------------------------------------------------------------------------- /img/tag-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbolel/insta-like-js/a288a9a896e1026281a2dd32a0a2c5cccb930926/img/tag-demo.gif -------------------------------------------------------------------------------- /insta-like-feed.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc Injects jQuery into Instagram feed page and likes X posts at a time 3 | * 4 | * Usage: Open https://www.instagram.com/ in Chrome and login to view your feed; 5 | * run this script in the JS console to like posts in your feed in batches. 6 | * 7 | * @param {number} start - starting post index (should be 0 unless manually continuing a prev. exec.) 8 | * @param {number} count - number of posts to like per batch (like X posts, wait a little, repeat) 9 | * @param {number} interval - number of milliseconds to wait between batches, +/- some randomness 10 | */ 11 | (function(start, count, interval) { 12 | // @todo find like button without using hard-coded class selector. 13 | // note: "._tk4ba" was the button class in my feed when I tried this out. 14 | const selector = '._tk4ba' 15 | 16 | const _body = () => document.getElementsByTagName('body')[0] 17 | const _getButtons = () => $('._tk4ba').toArray() 18 | const _plusOrMinus = () => Math.random() < 0.5 ? -1 : 1 19 | const _randomTimeout = () => (_plusOrMinus() * Math.floor((Math.random() * 500))) + 1500 20 | const _scrollToBottom = () => _body().scrollTop = _body().scrollHeight 21 | 22 | function _inject(doc, cb) { 23 | return (opts => { 24 | return Array.isArray(opts) 25 | ? Promise.all(opts.map(item => this.loadScript(item, opts))) 26 | : new Promise((resolve, reject) => { 27 | let r = false 28 | const t = doc.getElementsByTagName('script')[0] 29 | const s = doc.createElement('script') 30 | if (typeof opts === 'object' && typeof opts.src !== 'undefined') { 31 | for (key in opts) s[key] = opts[key] 32 | } else if (typeof opts === 'string') { 33 | s.src = opts 34 | } else { 35 | throw new Error('Script src undefined') 36 | } 37 | s.onerror = s.onabort = reject 38 | s.onload = s.onreadystatechange = () => { 39 | if (!r && (!this.readyState || this.readyState == 'complete')) { 40 | r = true 41 | resolve(s.src) 42 | } 43 | } 44 | t.parentNode.insertBefore(s, t) 45 | }) 46 | })({ 47 | async: true, 48 | src: 'https://code.jquery.com/jquery-3.2.1.min.js', 49 | type: 'text/javascript' 50 | }) 51 | .then(src => { 52 | console.log('Injected', src) 53 | _body.scrollTop = 0; 54 | }) 55 | } 56 | 57 | function _like(els) { 58 | if (!els || typeof els === 'undefined' || els.length < 1) { 59 | console.debug('Ran out of posts. Scrolling to bottom...') 60 | _scrollToBottom() 61 | setTimeout(() => {}, 750) 62 | } 63 | 64 | return Promise.all(els.map(el => new Promise((resolve, reject) => { 65 | if (el.firstChild.textContent === 'Like') { 66 | setTimeout(() => { 67 | el.click() 68 | console.debug(`CLICKED -> ${el}`) 69 | return resolve(el) 70 | }, _randomTimeout()) 71 | } else { 72 | console.debug(`Skipped -> ${el}`) 73 | return resolve(el) 74 | } 75 | }))) 76 | .then(res => console.debug(`Resolved ${res}`)) 77 | .catch(err => console.error(err)) 78 | } 79 | 80 | _inject(document) 81 | .then(() => { 82 | if (typeof interval === 'number') { 83 | let idx = start 84 | const getInterval = () => interval + _randomTimeout() 85 | const setNewTimeout = () => setTimeout(() => { 86 | console.debug(`Starting over at ${idx}!`) 87 | const elsArr = $(selector).toArray() 88 | _like(elsArr.slice(idx, idx+count)) 89 | idx += count 90 | setNewTimeout() 91 | }, getInterval()) 92 | setNewTimeout() 93 | } else { 94 | _like($(selector).toArray().slice(start, count)) 95 | } 96 | }) 97 | 98 | /// sample usage with ~4 second delay between batches of 3 posts 99 | })(0, 3, 4000) 100 | -------------------------------------------------------------------------------- /insta-like-tag.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc Inject jQuery in Instagram tag feed document then "Like" posts in series 3 | * @example Navigate to https://www.instagram.com/explore/tags/javascript/ in Chrome, 4 | * and run this script in the JS console to like the top posts one-at-a-time. 5 | */ 6 | (function() { 7 | const count = 1 8 | const interval = 3000 9 | const selector = 'a' 10 | const start = 0 11 | 12 | const getBody = () => document.getElementsByTagName('body')[0] 13 | const getButtons = () => $(selector).toArray() 14 | const getCloseBtn = () => $('button').last()[0] 15 | const getSign = () => Math.random() < 0.5 ? -1 : 1 16 | const getShortTimeout = () => Math.floor((Math.random() * 1000)) 17 | const getTimeout = () => (getSign() * Math.floor((Math.random() * 1000))) + interval 18 | const scrollToBottom = () => getBody().scrollTop = getBody().scrollHeight 19 | const closePost = () => getCloseBtn().click() 20 | 21 | function inject(cb) { 22 | return (opts => { 23 | return Array.isArray(opts) 24 | ? Promise.all(opts.map(item => this.loadScript(item, opts))) 25 | : new Promise((resolve, reject) => { 26 | let r = false 27 | const t = document.getElementsByTagName('script')[0] 28 | const s = document.createElement('script') 29 | if (typeof opts === 'object' && opts.src) for (key in opts) s[key] = opts[key] 30 | else if (typeof opts === 'string') s.src = opts 31 | else throw new Error('Script src undefined') 32 | s.onerror = s.onabort = reject 33 | s.onload = s.onreadystatechange = () => { 34 | if (!r && (!this.readyState || this.readyState == 'complete')) { 35 | r = true 36 | resolve(s.src) 37 | } 38 | } 39 | t.parentNode.insertBefore(s, t) 40 | }) 41 | })({async:true,src:'https://code.jquery.com/jquery-3.2.1.min.js',type:'text/javascript'}) 42 | .then(src => console.log('Injected', src)) 43 | } 44 | 45 | function like(els, idx) { 46 | if (!els || typeof els === 'undefined' || els.length < 1) 47 | throw new Error('Ran out of posts.') // @todo click "Load more posts" 48 | 49 | return els.map(el => { 50 | // open the post by triggering a click on the element 51 | el.click() 52 | 53 | // get reference to the "Like" button element. 54 | // click like if not clicked and close, else just close. 55 | setTimeout(() => { 56 | let article = $('article')[1] 57 | let btn = article.children[2].firstChild.firstChild.firstChild 58 | if (btn.classList.contains('coreSpriteLikeHeartFull')) { 59 | console.log(`%c 𝗫 Skipped ${idx}`, 'color: #D50000') 60 | return setTimeout(() => closePost(), getShortTimeout()) 61 | } 62 | btn.click() 63 | console.log(`%c ✔ Liked #${idx}!`, 'color: #00C853') 64 | setTimeout(() => closePost(), getShortTimeout()) 65 | }, getTimeout()) 66 | }) 67 | } 68 | 69 | function main() { 70 | getBody().scrollTop = 0 71 | inject().then(() => { 72 | let idx = start 73 | const setNewTimeout = () => setTimeout(() => { 74 | console.log(`Opening post #${idx}...`) 75 | like($(selector).toArray().slice(idx, idx + count), idx) 76 | idx += count 77 | setNewTimeout() 78 | }, getTimeout()*1.5) 79 | setNewTimeout() 80 | }) 81 | } 82 | 83 | main() 84 | })() 85 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "insta-like-js", 3 | "version": "0.1.1", 4 | "description": "A like-bot script for Instagram written in JavaScript ES6", 5 | "license": "MIT", 6 | "homepage": "https://github.com/sbolel/insta-like-js", 7 | "repository": { 8 | "type": "git", 9 | "url": "git@github.com:sbolel/insta-like-js.git" 10 | }, 11 | "author": { 12 | "name": "Sinan Bolel", 13 | "email": "hi@sinanbolel.com", 14 | "url": "https://sinanbolel.com" 15 | }, 16 | "contributors": [], 17 | "keywords": [ 18 | "Instagram", 19 | "bot", 20 | "like", 21 | "liker", 22 | "likebot", 23 | "social" 24 | ] 25 | } 26 | --------------------------------------------------------------------------------