Skip to content

Commit

Permalink
Add --remove-mass-followers feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexander Mishchenko committed Aug 15, 2020
1 parent 533ff8f commit 4036897
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 11 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ sessions.json
.DS_Store
screenshots
filter.json
whitelist.txt
action_remove_mass_followers.py
52 changes: 50 additions & 2 deletions insomniac.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,12 @@ def main():
is_unfollow_enabled = int(args.unfollow) > 0
is_unfollow_non_followers_enabled = int(args.unfollow_non_followers) > 0
is_unfollow_any_enabled = int(args.unfollow_any) > 0
is_remove_mass_followers_enabled = args.remove_mass_followers is not None and int(args.remove_mass_followers) > 0
total_enabled = int(is_interact_enabled) + int(is_unfollow_enabled) + int(is_unfollow_non_followers_enabled) \
+ int(is_unfollow_any_enabled)
+ int(is_unfollow_any_enabled) + int(is_remove_mass_followers_enabled)
if total_enabled == 0:
print_timeless(COLOR_FAIL + "You have to specify one of the actions: --interact, --unfollow, "
"--unfollow-non-followers, --unfollow-any" + COLOR_ENDC)
"--unfollow-non-followers, --unfollow-any, --remove-mass-followers" + COLOR_ENDC)
return
elif total_enabled > 1:
print_timeless(COLOR_FAIL + "Running Insomniac with two or more actions is not supported yet." + COLOR_ENDC)
Expand All @@ -71,6 +72,9 @@ def main():
elif is_unfollow_any_enabled:
print("Action: unfollow any " + str(args.unfollow_any))
mode = Mode.UNFOLLOW_ANY
elif is_remove_mass_followers_enabled:
print("Action: remove " + str(args.remove_mass_followers) + " mass followers")
mode = Mode.REMOVE_MASS_FOLLOWERS

profile_filter = Filter()
on_interaction = partial(_on_interaction,
Expand Down Expand Up @@ -117,6 +121,8 @@ def main():
storage,
int(args.min_following),
UnfollowRestriction.ANY)
elif mode == Mode.REMOVE_MASS_FOLLOWERS:
_job_remove_mass_followers(device, int(args.remove_mass_followers), int(args.max_following), storage)

close_instagram(device_id)
print_copyright(session_state.my_username)
Expand Down Expand Up @@ -227,6 +233,40 @@ def job():
job()


def _job_remove_mass_followers(device, count, max_followings, storage):
class State:
def __init__(self):
pass

removed_count = 0
is_job_completed = False

state = State()
session_state = sessions[-1]

try:
from src.action_remove_mass_followers import remove_mass_followers
except ImportError:
print_blocked_feature(session_state.my_username, "--remove-mass-followers")
return

def on_remove():
state.removed_count += 1
session_state.totalRemovedMassFollowers += 1
can_continue = state.removed_count < count
if not can_continue:
print(COLOR_OKGREEN + "Removed " + str(state.removed_count) + " mass followers, finish." + COLOR_ENDC)
return can_continue

@_run_safely(device=device)
def job():
remove_mass_followers(device, max_followings, on_remove, storage)
state.is_job_completed = True

while not state.is_job_completed and state.removed_count < count:
job()


def _parse_arguments():
parser = argparse.ArgumentParser(
description='Instagram bot for automated Instagram interaction using Android device via ADB',
Expand Down Expand Up @@ -282,6 +322,13 @@ def _parse_arguments():
parser.add_argument('--device',
help='device identifier. Should be used only when multiple devices are connected at once',
metavar='2443de990e017ece')
# Remove mass followers from the list of your followers. "Mass followers" are those who has more than N followings,
# where N can be set via --max-following. This is an extra feature, requires Patreon $10 tier.
parser.add_argument('--remove-mass-followers',
help=argparse.SUPPRESS)
parser.add_argument('--max-following',
help=argparse.SUPPRESS,
default=1000)

if not len(sys.argv) > 1:
parser.print_help()
Expand Down Expand Up @@ -367,6 +414,7 @@ class Mode(Enum):
UNFOLLOW = 1
UNFOLLOW_NON_FOLLOWERS = 2
UNFOLLOW_ANY = 3
REMOVE_MASS_FOLLOWERS = 4


if __name__ == "__main__":
Expand Down
6 changes: 3 additions & 3 deletions src/action_get_my_profile_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ def get_my_profile_info(device):
followers = _get_followers_count(device)

try:
following = _get_following_count(device)
following = get_following_count(device)
except LanguageChangedException:
# Try again on the correct language
navigate(device, Tabs.PROFILE)
following = _get_following_count(device)
following = get_following_count(device)

report_string = ""
if username:
Expand Down Expand Up @@ -61,7 +61,7 @@ def _get_followers_count(device):
return followers


def _get_following_count(device):
def get_following_count(device):
following = None
following_text_view = device(resourceId='com.instagram.android:id/row_profile_header_textview_following_count',
className='android.widget.TextView')
Expand Down
5 changes: 5 additions & 0 deletions src/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ def print_full_report(sessions):
+ stringify_interactions(session.totalFollowed) + COLOR_ENDC)
print_timeless(COLOR_WARNING + "Total likes: " + str(session.totalLikes) + COLOR_ENDC)
print_timeless(COLOR_WARNING + "Total unfollowed: " + str(session.totalUnfollowed) + COLOR_ENDC)
print_timeless(COLOR_WARNING + "Total removed mass followers: "
+ str(session.totalRemovedMassFollowers) + COLOR_ENDC)

print_timeless("\n")
print_timeless(COLOR_WARNING + "TOTAL" + COLOR_ENDC)
Expand Down Expand Up @@ -67,6 +69,9 @@ def print_full_report(sessions):
total_unfollowed = sum(session.totalUnfollowed for session in sessions)
print_timeless(COLOR_WARNING + "Total unfollowed: " + str(total_unfollowed) + COLOR_ENDC)

total_removed_mass_followers = sum(session.totalRemovedMassFollowers for session in sessions)
print_timeless(COLOR_WARNING + "Total removed mass followers: " + str(total_removed_mass_followers) + COLOR_ENDC)


def print_short_report(blogger, session_state):
total_likes = session_state.totalLikes
Expand Down
3 changes: 3 additions & 0 deletions src/session_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class SessionState:
totalFollowed = {}
totalLikes = 0
totalUnfollowed = 0
totalRemovedMassFollowers = 0
startTime = None
finishTime = None

Expand All @@ -28,6 +29,7 @@ def __init__(self):
self.totalFollowed = {}
self.totalLikes = 0
self.totalUnfollowed = 0
self.totalRemovedMassFollowers = 0
self.startTime = datetime.now()
self.finishTime = None

Expand Down Expand Up @@ -63,6 +65,7 @@ def default(self, session_state: SessionState):
"total_followed": sum(session_state.totalFollowed.values()),
"total_likes": session_state.totalLikes,
"total_unfollowed": session_state.totalUnfollowed,
"total_removed_mass_followers": session_state.totalRemovedMassFollowers,
"start_time": str(session_state.startTime),
"finish_time": str(session_state.finishTime),
"args": session_state.args,
Expand Down
10 changes: 10 additions & 0 deletions src/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
USER_LAST_INTERACTION = "last_interaction"
USER_FOLLOWING_STATUS = "following_status"

FILENAME_WHITELIST = "whitelist.txt"


class Storage:
interacted_users_path = ""
interacted_users = {}
whitelist = []

def __init__(self, my_username):
if not os.path.exists(my_username):
Expand All @@ -19,6 +22,10 @@ def __init__(self, my_username):
if os.path.exists(self.interacted_users_path):
with open(self.interacted_users_path) as json_file:
self.interacted_users = json.load(json_file)
whitelist_path = my_username + "/" + FILENAME_WHITELIST
if os.path.exists(whitelist_path):
with open(whitelist_path) as file:
self.whitelist = [line.rstrip() for line in file]

def check_user_was_interacted(self, username):
return not self.interacted_users.get(username) is None
Expand Down Expand Up @@ -49,6 +56,9 @@ def add_interacted_user(self, username, followed=False, unfollowed=False):
self.interacted_users[username] = user
self._update_file()

def is_user_in_whitelist(self, username):
return username in self.whitelist

def _update_file(self):
with open(self.interacted_users_path, 'w') as outfile:
json.dump(self.interacted_users, outfile, indent=4, sort_keys=False)
Expand Down
18 changes: 12 additions & 6 deletions src/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
COLOR_BOLD = '\033[1m'
COLOR_UNDERLINE = '\033[4m'

COPYRIGHT_BLACKLIST = (
'2a978d696a5bbc8536fe2859a61ee01d86e7a20f',
'ab1d65a93ec9b6fb90a67dec1ca1480ff71ef725'
)


def get_version():
stream = os.popen('git describe --tags')
Expand Down Expand Up @@ -92,12 +97,7 @@ def take_screenshot(device):


def print_copyright(username):
copyright_blacklist = (
'2a978d696a5bbc8536fe2859a61ee01d86e7a20f',
'ab1d65a93ec9b6fb90a67dec1ca1480ff71ef725'
)

if hashlib.sha1(username.encode('utf-8')).hexdigest() not in copyright_blacklist:
if hashlib.sha1(username.encode('utf-8')).hexdigest() not in COPYRIGHT_BLACKLIST:
print_timeless("\nIf you like this script and want it to be improved, " + COLOR_BOLD + "donate please"
+ COLOR_ENDC + ".")
print_timeless(COLOR_BOLD + "$3" + COLOR_ENDC + " - support this project")
Expand All @@ -106,6 +106,12 @@ def print_copyright(username):
print_timeless("https://www.patreon.com/insomniac_bot\n")


def print_blocked_feature(username, feature_name):
if hashlib.sha1(username.encode('utf-8')).hexdigest() not in COPYRIGHT_BLACKLIST:
print_timeless(COLOR_FAIL + "Sorry, " + feature_name + " is available for Patrons only!" + COLOR_ENDC)
print_timeless(COLOR_FAIL + "Please visit https://www.patreon.com/insomniac_bot\n" + COLOR_ENDC)


def _print_with_time_decorator(standard_print, print_time):
def wrapper(*args, **kwargs):
if print_time:
Expand Down

0 comments on commit 4036897

Please sign in to comment.