Skip to content

Commit

Permalink
Update to v3.7.13
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexander Mishchenko committed Jun 20, 2021
1 parent 7da8f4d commit 6acd2d3
Show file tree
Hide file tree
Showing 10 changed files with 222 additions and 117 deletions.
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
graft insomniac/apk
2 changes: 1 addition & 1 deletion insomniac/__version__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
__title__ = 'insomniac'
__description__ = 'Simple Instagram bot for automated Instagram interaction using Android.'
__url__ = 'https://github.com/alexal1/Insomniac/'
__version__ = '3.7.12'
__version__ = '3.7.13'
__debug_mode__ = False
__author__ = 'Insomniac Team'
__author_email__ = '[email protected]'
Expand Down
109 changes: 26 additions & 83 deletions insomniac/actions_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,14 @@
from insomniac.softban_indicator import softban_indicator
from insomniac.tools.spintax import spin
from insomniac.utils import *
from insomniac.validations import validate_ig_profile_existence
from insomniac.views import ActionBarView, ProfileView, PostsViewList, OpenedPostView, LikersListView
from insomniac.views import ActionBarView, ProfileView, PostsViewList, OpenedPostView, LikersListView, DialogView

FOLLOWERS_BUTTON_ID_REGEX = '{0}:id/row_profile_header_followers_container' \
'|{1}:id/row_profile_header_container_followers'
TEXTVIEW_OR_BUTTON_REGEX = 'android.widget.TextView|android.widget.Button'
FOLLOW_REGEX = 'Follow|Follow Back'
ALREADY_FOLLOWING_REGEX = 'Following|Requested'
SHOP_REGEX = 'Add Shop|View Shop'
UNFOLLOW_REGEX = 'Unfollow'
FOLLOWING_BUTTON_ID_REGEX = '{0}:id/row_profile_header_following_container' \
'|{1}:id/row_profile_header_container_following'
USER_AVATAR_VIEW_ID = '{0}:id/circular_image|^$'
LISTVIEW_OR_RECYCLERVIEW_REGEX = 'android.widget.ListView|androidx.recyclerview.widget.RecyclerView'

Expand Down Expand Up @@ -596,18 +592,11 @@ def _open_user(device, username, open_followers=False, open_followings=False,
if username is None:
if open_followers:
print("Open your followers")
followers_button = device.find(resourceIdMatches=FOLLOWERS_BUTTON_ID_REGEX.format(device.app_id, device.app_id))
followers_button.click()
ProfileView(device, is_own_profile=True).navigate_to_followers()

if open_followings:
print("Open your followings")
followings_button = device.find(resourceIdMatches=FOLLOWING_BUTTON_ID_REGEX.format(device.app_id, device.app_id))
followings_button.click()
sleeper.random_sleep()
followings_tab = device.find(classNameMatches=TEXTVIEW_OR_BUTTON_REGEX,
clickable=True,
textContains=' Following')
followings_tab.click()
print("Open your following")
ProfileView(device, is_own_profile=True).navigate_to_following()
else:
should_open_user_with_search = True
deep_link_usage_chance = randint(1, 100)
Expand Down Expand Up @@ -635,18 +624,11 @@ def _open_user(device, username, open_followers=False, open_followings=False,

if open_followers:
print("Open @" + username + " followers")
followers_button = device.find(resourceIdMatches=FOLLOWERS_BUTTON_ID_REGEX.format(device.app_id, device.app_id))
followers_button.click()
ProfileView(device, is_own_profile=True).navigate_to_followers()

if open_followings:
print("Open @" + username + " followings")
followings_button = device.find(resourceIdMatches=FOLLOWING_BUTTON_ID_REGEX.format(device.app_id, device.app_id))
followings_button.click()
sleeper.random_sleep()
followings_tab = device.find(classNameMatches=TEXTVIEW_OR_BUTTON_REGEX,
clickable=True,
textContains=' Following')
followings_tab.click()
print("Open @" + username + " following")
ProfileView(device, is_own_profile=True).navigate_to_following()

return True

Expand Down Expand Up @@ -762,12 +744,17 @@ def sort_followings_by_date(device, sort_order):
return

if sort_order == FollowingsSortOrder.DEFAULT:
sort_options_recycler_view.child(index=0).child(index=1).click()
sort_item = sort_options_recycler_view.child(index=0)
elif sort_order == FollowingsSortOrder.LATEST:
sort_options_recycler_view.child(index=1).child(index=1).click()
sort_item = sort_options_recycler_view.child(index=1)
else: # EARLIEST
sort_options_recycler_view.child(index=2).child(index=1).click()
sort_item = sort_options_recycler_view.child(index=2)

if not sort_item.exists():
print(COLOR_FAIL + f"Cannot find an option to sort by {sort_order.name}" + COLOR_ENDC)
device.back()
return
sort_item.click()

def do_unfollow(device, my_username, username, storage, check_if_is_follower, username_view, follow_status_button_view, on_action):
"""
Expand All @@ -782,7 +769,7 @@ def do_unfollow(device, my_username, username, storage, check_if_is_follower, us
print("Unfollowing a profile directly from the following list.")
follow_status_button_view.click()
else:
print("Unfollowing a profile from his profile page.")
print("Unfollowing a profile from their profile page.")
username_view.click()
on_action(GetProfileAction(user=username))
sleeper.random_sleep()
Expand Down Expand Up @@ -813,21 +800,18 @@ def do_unfollow(device, my_username, username, storage, check_if_is_follower, us

print(f"Unfollowing @{username}...")
unfollow_button.click()

sleeper.random_sleep()

confirm_unfollow_button = device.find(resourceId=f'{device.app_id}:id/follow_sheet_unfollow_row',
className='android.widget.TextView')
if not confirm_unfollow_button.exists():
print(COLOR_FAIL + "Cannot confirm unfollow." + COLOR_ENDC)
save_crash(device)
device.back()
return False
confirm_unfollow_button.click()
unfollow_confirmed = False
dialog_view = DialogView(device)
if dialog_view.is_visible():
print("Confirming unfollow...")
unfollow_confirmed = dialog_view.click_unfollow()

sleeper.random_sleep()
_close_confirm_dialog_if_shown(device)
softban_indicator.detect_action_blocked_dialog(device)
if unfollow_confirmed:
sleeper.random_sleep()
else:
softban_indicator.detect_action_blocked_dialog(device)

if need_to_go_back_to_list:
print("Back to the followings list.")
Expand Down Expand Up @@ -872,9 +856,7 @@ def interact_with_feed(navigate_to_feed, should_continue, interact_with_feed_pos

def _check_is_follower(device, username, my_username):
print(COLOR_OKGREEN + "Check if @" + username + " is following you." + COLOR_ENDC)
following_container = device.find(resourceIdMatches=FOLLOWING_BUTTON_ID_REGEX.format(device.app_id, device.app_id))
following_container.click()

ProfileView(device, is_own_profile=True).navigate_to_following()
sleeper.random_sleep()

is_list_empty = softban_indicator.detect_empty_list(device)
Expand All @@ -895,45 +877,6 @@ def _check_is_follower(device, username, my_username):
return result


def _close_confirm_dialog_if_shown(device):
if not _close_confirm_dialog_by_version(device, 2):
_close_confirm_dialog_by_version(device, 1)


def _close_confirm_dialog_by_version(device, version):
if version == 1:
dialog_root_view = device.find(resourceId=f'{device.app_id}:id/dialog_root_view',
className='android.widget.FrameLayout')
elif version == 2:
dialog_root_view = device.find(resourceId=f'{device.app_id}:id/dialog_container',
className='android.view.ViewGroup')
else:
raise ValueError("Close unfollow confrim dialog for vis not exists.")

if not dialog_root_view.exists(quick=True):
return False

# Avatar existence is the way to distinguish confirm dialog from block dialog
user_avatar_view = device.find(resourceIdMatches=USER_AVATAR_VIEW_ID.format(device.app_id),
className='android.widget.ImageView')
if not user_avatar_view.exists(quick=True):
return False

print(COLOR_OKGREEN + "Dialog shown, confirm unfollowing." + COLOR_ENDC)
sleeper.random_sleep()
if version == 1:
unfollow_button = dialog_root_view.child(resourceId=f'{device.app_id}:id/primary_button',
classNameMatches=TEXTVIEW_OR_BUTTON_REGEX)
elif version == 2:
unfollow_button = dialog_root_view.child(resourceId=f'{device.app_id}:id/primary_button',
classNameMatches=TEXTVIEW_OR_BUTTON_REGEX,
textMatches=UNFOLLOW_REGEX)

unfollow_button.click()
sleeper.random_sleep()
return True


def _get_action_bar(device):
tab_bar = device.find(
resourceIdMatches=case_insensitive_re(
Expand Down
3 changes: 2 additions & 1 deletion insomniac/activation.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ def validate(self, activation_code, ui=False):
f"{dot}{COLOR_BOLD}Filtering{COLOR_ENDC} - skip unwanted accounts by various parameters"
f"{dot}{COLOR_BOLD}Scrapping{COLOR_ENDC} - technique that makes interactions "
f"significantly safer and faster"
f"{dot}{COLOR_BOLD}Warmup{COLOR_ENDC} - interact with your feed and Explore several minutes "
f"before session to behave more like a human"
f"{dot}{COLOR_BOLD}Working hours{COLOR_ENDC} - the script will wait till specified hours "
f"before each session"
f"{dot}{COLOR_BOLD}Removing mass followers{COLOR_ENDC} - automate \"cleaning\" you account"
f"{dot}{COLOR_BOLD}Analytics tool{COLOR_ENDC} - build presentation that shows your growth\n"
f"Activate by supporting our small team: {COLOR_BOLD}{HOST}{PATH_ACTIVATE}{COLOR_ENDC}\n")

def get_extra_feature(self, module, ui=False):
Expand Down
16 changes: 10 additions & 6 deletions insomniac/db_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,22 +623,22 @@ class ProfileStatus(Enum):
]


def init() -> bool:
def init():
"""
Initialize database and return whether it was just created.
Initialize database.
"""
with db.connection_context():
if len(db.get_tables()) == 0:
print(f"Creating tables in {DATABASE_NAME}...")
db.create_tables(MODELS)
SchemaVersion.create()
return True
return

# Migration logic
current_db_version = _get_db_version()
if current_db_version is None:
print(COLOR_FAIL + "Cannot read database version from SchemaVersion table!" + COLOR_ENDC)
return False
return

if current_db_version > DATABASE_VERSION:
raise Exception("Bot version is too low, cannot work with the current database! "
Expand All @@ -650,8 +650,6 @@ def init() -> bool:
migrator = SqliteMigrator(db)
_migrate(current_db_version, migrator)
current_db_version = _get_db_version()
return False
return False


def get_ig_profile_by_profile_name(profile_name) -> InstagramProfile:
Expand All @@ -660,6 +658,12 @@ def get_ig_profile_by_profile_name(profile_name) -> InstagramProfile:
return profile


def is_ig_profile_exists(profile_name) -> bool:
with db.connection_context():
is_exists = InstagramProfile.select().where(InstagramProfile.name == profile_name).exists()
return is_exists


def get_ig_profiles_actions_by_task_id(actions_task_id, action_types_list) -> dict:
"""Returns a dict of ig_profile_name -> list of InsomniacAction objects"""
ig_profiles_to_actions = defaultdict(list)
Expand Down
5 changes: 3 additions & 2 deletions insomniac/migration.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json

from insomniac.db_models import DATABASE_NAME
from insomniac.db_models import DATABASE_NAME, is_ig_profile_exists
from insomniac.session_state import SessionState
from insomniac.sessions import FILENAME_SESSIONS, Sessions
from insomniac.storage import *
Expand Down Expand Up @@ -155,7 +155,8 @@ def migrate_from_sql_to_peewee(my_username):
if not check_database_exists(my_username, False):
return

if not db_models.init():
db_models.init()
if is_ig_profile_exists(my_username):
return

database = get_database(my_username)
Expand Down
24 changes: 24 additions & 0 deletions insomniac/safely_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from socket import timeout

from insomniac.device_facade import DeviceFacade
from insomniac.extra_features.actions_impl import airplane_mode_on_off
from insomniac.extra_features.utils import new_identity
from insomniac.navigation import navigate, LanguageChangedException
from insomniac.sleeper import sleeper
from insomniac.utils import *
Expand Down Expand Up @@ -37,3 +39,25 @@ def wrapper(*args, **kwargs):
navigate(device_wrapper.get(), TabBarTabs.PROFILE)
return wrapper
return actual_decorator


def run_registration_safely(device_wrapper):
def actual_decorator(func):
def wrapper(*args, **kwargs):
try:
func(*args, **kwargs)
except (DeviceFacade.JsonRpcError, IndexError, HTTPException, timeout) as ex:
print(COLOR_FAIL + describe_exception(ex) + COLOR_ENDC)
save_crash(device_wrapper.get(), ex)
print("No idea what it was. Let's try again.")

# For the registration flow we create new identity and turn airplane mode on-and-off before each
# Instagram app restart
new_identity(device_wrapper.device_id, device_wrapper.app_id)
sleeper.random_sleep(multiplier=2.0)
close_instagram(device_wrapper.device_id, device_wrapper.app_id)
airplane_mode_on_off(device_wrapper.get())
open_instagram(device_wrapper.device_id, device_wrapper.app_id)
sleeper.random_sleep()
return wrapper
return actual_decorator
5 changes: 2 additions & 3 deletions insomniac/softban_indicator.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from enum import unique, Enum

from insomniac.utils import *
from insomniac.views import ProfileView, FollowersFollowingListView, InstagramView
from insomniac.views import ProfileView, FollowersFollowingListView, DialogView

EMPTY_LIST_TRESHOLD = 5
EMPTY_PROFILE_TRESHOLD = 5
Expand Down Expand Up @@ -74,8 +74,7 @@ def detect_empty_profile(self, device):

@check_softban_feature_flag
def detect_action_blocked_dialog(self, device):
curr_view = InstagramView(device)
is_blocked = curr_view.is_block_dialog_present()
is_blocked = DialogView(device).is_visible()
if is_blocked:
print(COLOR_FAIL + "Probably block dialog is shown. "
"Counting that as a soft-ban indicator!." + COLOR_ENDC)
Expand Down
Loading

0 comments on commit 6acd2d3

Please sign in to comment.