Skip to content

Commit

Permalink
Added intervals, cookies, default language
Browse files Browse the repository at this point in the history
Added triangular random intervals defined by user
Save and use accounts' cookie to auto-login and prevent Instagram from
finding suspicious activity
Added Default Language's Option
  • Loading branch information
Fytex committed Sep 16, 2020
1 parent b48438f commit 8cdfb85
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 34 deletions.
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ But don't worry... if you feel unsafe you can install these files by yourself (j

### Setup

1. Install Google Chrome (Don't change the instalation's path) -> if you guys start complaining about this specific step I'll make some updates to have wider options
1. Install Google Chrome (Check `config.ini` Chrome's category if you want a different path) -> if you guys start complaining about this specific step I'll make some updates to have wider options
2. Install Python 3.6+ (Don't forget to add in system variable `PATH`)
3. Open terminal, change directory to Instagram-Giveaways-Winner's folder and type: `pip install -r requirements.txt`
4. Edit config.ini (See next category)
Expand Down Expand Up @@ -65,6 +65,7 @@ Browser's window can be invisible (in background) if Window's option is deactiva
- Step 4 is where all the fun begins. It starts spamming mentions in the post.
- By enabling SaveOnly's option this step won't run. This option is used in case you only want to save the followers/followings and use them later.
- You can edit the message and add as many mentions as you want (mentions will never be repeated).
- Interval Category in `config` file lets you choose how much time it waits before each comment. You have to find out by yourself the best interval that fits your account. It has a min, max and weight so the number isn't always the same preventing Instagram finding out it is a bot. (Later I will try to create kind of an A.I. to do this for you)



Expand All @@ -78,6 +79,12 @@ Depending on what you choose it will save in the respective directory. Then it w
When searching for a `UserTarget` if the lowest one between the `limit` (in the config file) and the number of followers/followings from the `UserTarget` is already met in a file then it will skip Step 2 and use that file automatically.



### How do the cookies work (auto-login)?

When the program logs into your account it saves in a separated folder called `cookies` the correspondent cookie to use it in a next time. This prevents Instagram from blocking your account from suspicious activity because you logged in too many times.


### Warnings:

I would recommend using an alternative account which you aren't afraid of losing because it could go wrong in the worst case. However I've never experienced any bad situations using this script.
Expand All @@ -90,4 +97,11 @@ Instagram has a comment's request rate-limit to avoid spamming. From my research
- [ ] Add a way to find followers/followings some at a time until it reaches the limit/maximum. This way we can find followers/followings and post comments in a cycle.
- [ ] Add specific file from records (database) to use as users to mention
- [ ] Find out the best interval between each comment for your account
- [ ] Login using cookies to prevent logging in too many times using username and password


### Known Bugs:
```
raise exception_class(message, screen, stacktrace)
# selenium.common.exceptions.WebDriverException: Message: unknown error: cannot find Chrome binary
```
- Since Chrome has updated their files' location, Selenium hasn't fixed it yet. Check `config.ini` Chrome's Category to fix it.
31 changes: 30 additions & 1 deletion config.ini
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,33 @@
# Limit time to wait in case it gets stuck

#SaveOnly = False
# Don't spam (only save data)
# Don't spam (only save data)


[Interval] # Remove '#' before each line to enable option
# These timers are in seconds

#Min = 60

#Max = 120

#Weight = 90
# Numbers tend to be close to Weight's number
# if not specified it will use the midpoint between Min and Max


[Chrome] # Remove '#' before each line to enable option

#DefaultLang = False
# if False it uses English
# if True it uses your Chrome's Language
# Warning: Very low chance of crashing program if True

#Location = C:\Program Files\Google\Chrome\Application\chrome.exe
# if it raises an error saying:
#
# raise exception_class(message, screen, stacktrace)
# selenium.common.exceptions.WebDriverException: Message: unknown error: cannot find Chrome binary
#
# enable this option because Chrome is changing paths and Selenium hasn't fixed yet.
# if error still persists find where Chrome is located and put the path ending in the executable
96 changes: 67 additions & 29 deletions modules/instagram_bot.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.wait import WebDriverWait
from typing import List, Iterator
from selenium.common.exceptions import WebDriverException
from typing import List, Iterator, Callable
from itertools import chain
from time import perf_counter
from time import perf_counter, sleep
import re
import sys
import os
Expand Down Expand Up @@ -40,17 +41,10 @@ def generate(self) -> Iterator[str]:

class Bot:

__version__ = '1.0.4'
__version__ = '1.2.0'


def __init__(self, window:bool=True, timeout=30):

'''
Web Driver's path
Credits: https://github.com/nateshmbhat/webbot/blob/master/webbot/webbot.py
'''
def __init__(self, window:bool=True, timeout:int=30, binary_location:str=None, default_lang:bool=False):

if sys.platform == 'linux' or sys.platform == 'linux2':
driver_file_name = 'chrome_linux'
Expand All @@ -64,8 +58,14 @@ def __init__(self, window:bool=True, timeout=30):
os.chmod(driver_path , 0o755)

options = webdriver.ChromeOptions()
options.add_experimental_option('prefs', {'intl.accept_languages': 'en,en_US'})
options.headless = not window ;

if not default_lang:
options.add_experimental_option('prefs', {'intl.accept_languages': 'en,en_US'})

options.headless = not window ;

if binary_location:
options.binary_location = binary_location


self.driver = webdriver.Chrome(executable_path=driver_path, options=options)
Expand All @@ -76,18 +76,54 @@ def __init__(self, window:bool=True, timeout=30):
self.timeout = timeout

def log_in(self, username:str, password:str):

COOKIE_NAME = 'sessionid'

self.driver.get(self.url_login)

try:

with open(f'cookies/{username}.json', 'r') as file:
cookie = json.load(file)

except FileNotFoundError:
pass

else:
self.driver.add_cookie(cookie)

# I find out that chrome sends a warning message after loading a cookie
WebDriverWait(self.driver, self.timeout).until(
lambda x: self.driver.get_log('browser'))

self.driver.refresh()


# Waits for information about logged status
WebDriverWait(self.driver, self.timeout).until(
lambda x: x.find_elements_by_css_selector('form input'))

username_input, password_input, *_ = self.driver.find_elements_by_css_selector('form input')

username_input.send_keys(username)
password_input.send_keys(password + Keys.ENTER)

WebDriverWait(self.driver, self.timeout).until(
lambda x: 'js logged-in' in x.find_element_by_tag_name('html').get_attribute('class'))
if 'not-logged-in' in self.driver.find_element_by_tag_name('html').get_attribute('class'):

# Waits for form
WebDriverWait(self.driver, self.timeout).until(
lambda x: x.find_elements_by_css_selector('form input'))

username_input, password_input, *_ = self.driver.find_elements_by_css_selector('form input')

username_input.send_keys(username)
password_input.send_keys(password + Keys.ENTER)

WebDriverWait(self.driver, self.timeout).until(
lambda x: 'js logged-in' in x.find_element_by_tag_name('html').get_attribute('class'))

cookie = self.driver.get_cookie(COOKIE_NAME)

Path('cookies/').mkdir(exist_ok=True)

with open(f'cookies/{username}.json', 'w') as file:
json.dump(cookie, file)


def new_tab(self, url:str='https://www.google.com'):
Expand Down Expand Up @@ -249,10 +285,6 @@ def get_user_from_post(self, url:str) -> str:

def send_comment(self, comment:str):

# Message Popup `Retry` (Timeout=Inf since this won't be an obstacle from the internet)
WebDriverWait(self.driver, float('Inf'), 10).until_not(
lambda x: x.find_element_by_tag_name('p'))

# Text in Comment's Box
if not self.driver.find_element_by_css_selector('article[role=\'presentation\'] form > textarea').text:

Expand All @@ -265,19 +297,24 @@ def send_comment(self, comment:str):
comment_box = self.driver \
.find_element_by_css_selector('article[role=\'presentation\'] form > textarea') \
.send_keys(comment)

# Click Post's Button to send Comment
self.driver \
.find_element_by_css_selector('article[role=\'presentation\'] form > button') \
.click()

try:

# Click Post's Button to send Comment
self.driver \
.find_element_by_css_selector('article[role=\'presentation\'] form > button') \
.click()

except WebDriverException:
sleep(60) # Couldn't comment error pop up. No specific css selector. (<p> was too risky because of pop up's warnings such as cookies one)

# Wait the loading icon disappear
WebDriverWait(self.driver, self.timeout).until_not(
lambda x: x.find_element_by_css_selector('article[role=\'presentation\'] form > div'))



def comment_post(self, url:str, expr:str, connections:List[str]):
def comment_post(self, url:str, expr:str, connections:List[str], get_interval:Callable[[], float]):
expr_parts = re.split(r'(?<!\\)@', expr)
n = len(expr_parts) - 1

Expand All @@ -297,6 +334,7 @@ def chunks() -> Iterator[str]:

for comment in comments.generate():
self.send_comment(comment)
sleep(get_interval())


def close_driver(self):
Expand Down
18 changes: 16 additions & 2 deletions script.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from configparser import ConfigParser
from modules.instagram_bot import Bot
from functools import partial
from random import triangular


ASCII = '''
Expand Down Expand Up @@ -35,9 +37,19 @@
timeout = parser.getint('Optional', 'Timeout', fallback=30)
save_only = parser.getboolean('Optional', 'SaveOnly', fallback=False)


low = parser.getint('Interval', 'Min', fallback=60)
high = parser.getint('Interval', 'Max', fallback=120)
mode = parser.getint('Interval', 'Weight', fallback=None) # None goes for midpoint


default_lang = parser.getboolean('Chrome', 'DefaultLang', fallback=False)
binary_location = parser.get('Chrome', 'Location', fallback=None)


print(ASCII)

bot = Bot(window, timeout)
bot = Bot(window, timeout, binary_location, default_lang)

print('Logging in...')

Expand All @@ -62,7 +74,9 @@

if not save_only:
print('Let\'s win this giveaway together! Spamming...')

get_interval = partial(triangular, low, high, mode)

bot.comment_post(post_link, expr, connections)
bot.comment_post(post_link, expr, connections, get_interval)

print('Program finished with success!')

0 comments on commit 8cdfb85

Please sign in to comment.