Skip to content
This repository has been archived by the owner on Jan 27, 2025. It is now read-only.

Commit

Permalink
Update to v3.8.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexander Mishchenko committed Mar 9, 2022
1 parent 7c9d572 commit 1a8016b
Show file tree
Hide file tree
Showing 21 changed files with 340 additions and 78 deletions.
6 changes: 6 additions & 0 deletions config-examples-extra/all-params.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@
"value": "2.json",
"description" : "if you want to run multiple insomniac sessions one-by-one but with different parameters, for example - different action (interact and then unfollow), or same config but with different username, or any other variation of parameters you can think of, you can combine this parameter with the \"repeat\"-parameter, and after the sleep of the \"repeat\"-parameter, a new config file (referenced by this parameter) will be loaded. By default using the same config that been loaded in the first Insominac session. You must use \"repeat\"-parameter in order for that parameter take action!"
},
{
"parameter-name": "send_stats",
"enabled": true,
"value": "True",
"description" : "add this flag to send your anonymous statistics to the Telegram bot @your_insomniac_bot. This is useful when insomniac runs infinitely and you want to be able to track progress remotely"
},
{
"parameter-name": "username",
"enabled": false,
Expand Down
3 changes: 3 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ default uses the same config file as been loaded for
the first session. Note that you must use "--repeat"
with this argument!

#### --send-stats
add this flag to send your anonymous statistics to the Telegram bot @your_insomniac_bot. This is useful when insomniac runs infinitely and you want to be able to track progress remotely

### Advanced
Options for savvy users.

Expand Down
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.28'
__version__ = '3.8.0'
__debug_mode__ = False
__author__ = 'Insomniac Team'
__author_email__ = 'info@insomniac-bot.com'
Expand Down
4 changes: 3 additions & 1 deletion insomniac/action_get_my_profile_info.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from insomniac.hardban_indicator import HardBanError
from insomniac.navigation import switch_to_english
from insomniac.sleeper import sleeper
from insomniac.softban_indicator import ActionBlockedError
from insomniac.utils import *
from insomniac.views import TabBarView, ActionBarView, UserSwitchFailedException

Expand All @@ -21,7 +23,7 @@ def get_my_profile_info(device, username):
profile_view.refresh()
sleeper.random_sleep()
username, followers, following = profile_view.get_profile_info(swipe_up_if_needed=True)
except UserSwitchFailedException as e:
except (UserSwitchFailedException, HardBanError, ActionBlockedError) as e:
raise e
except Exception as e:
print(COLOR_FAIL + describe_exception(e) + COLOR_ENDC)
Expand Down
49 changes: 29 additions & 20 deletions insomniac/actions_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -661,20 +661,31 @@ def iterate_over_my_followers(device, iteration_callback, iteration_callback_pre
_iterate_over_my_followers_or_followings(device,
iteration_callback,
iteration_callback_pre_conditions,
is_followers=True)
is_followers=True,
is_swipes_allowed=True)


def iterate_over_my_followers_no_swipes(device, iteration_callback, iteration_callback_pre_conditions):
_iterate_over_my_followers_or_followings(device,
iteration_callback,
iteration_callback_pre_conditions,
is_followers=True,
is_swipes_allowed=False)


def iterate_over_my_followings(device, iteration_callback, iteration_callback_pre_conditions):
_iterate_over_my_followers_or_followings(device,
iteration_callback,
iteration_callback_pre_conditions,
is_followers=False)
is_followers=False,
is_swipes_allowed=True)


def _iterate_over_my_followers_or_followings(device,
iteration_callback,
iteration_callback_pre_conditions,
is_followers):
is_followers,
is_swipes_allowed):
entities_name = "followers" if is_followers else "followings"

# Wait until list is rendered
Expand All @@ -685,6 +696,7 @@ def _iterate_over_my_followers_or_followings(device,
print(f"Iterate over visible {entities_name}")
sleeper.random_sleep()
screen_iterated_followings = 0
screen_skipped_followings = 0

for item in device.find(resourceId=f'{device.app_id}:id/follow_list_container',
className='android.widget.LinearLayout'):
Expand All @@ -702,6 +714,7 @@ def _iterate_over_my_followers_or_followings(device,
screen_iterated_followings += 1

if not iteration_callback_pre_conditions(username, user_name_view, follow_status_button_view):
screen_skipped_followings += 1
continue

to_continue = iteration_callback(username, user_name_view, follow_status_button_view)
Expand All @@ -711,10 +724,15 @@ def _iterate_over_my_followers_or_followings(device,
print(COLOR_OKBLUE + f"Stopping iteration over {entities_name}" + COLOR_ENDC)
return

if screen_iterated_followings > 0:
list_view = device.find(resourceId='android:id/list',
className='android.widget.ListView')

if screen_skipped_followings == screen_iterated_followings > 0 and is_swipes_allowed:
print(COLOR_OKGREEN + "All followings skipped, let's do a swipe" + COLOR_ENDC)
list_view.swipe(DeviceFacade.Direction.BOTTOM)
sleeper.random_sleep(multiplier=2.0)
elif screen_iterated_followings > 0:
print(COLOR_OKGREEN + "Need to scroll now" + COLOR_ENDC)
list_view = device.find(resourceId='android:id/list',
className='android.widget.ListView')
list_view.scroll(DeviceFacade.Direction.BOTTOM)
else:
print(COLOR_OKGREEN + f"No {entities_name} were iterated, finish." + COLOR_ENDC)
Expand Down Expand Up @@ -809,20 +827,11 @@ def do_unfollow(device, my_username, username, storage, check_if_is_follower, us
unfollow_confirmed = dialog_view.click_unfollow()

if unfollow_confirmed:
try:
# If the account is private, another popup is shown
confirm_button = device.find(classNameMatches=TEXTVIEW_OR_BUTTON_REGEX,
clickable=True,
text='Unfollow')
# If it exists, click unfollow
if confirm_button.exists():
print("Private account, confirming unfollow...")
confirm_button.click()
# Either way, sleep
except:
pass
finally:
sleeper.random_sleep()
sleeper.random_sleep()
if dialog_view.is_visible():
print("Confirming unfollow again...")
if dialog_view.click_unfollow():
sleeper.random_sleep()
else:
softban_indicator.detect_action_blocked_dialog(device)

Expand Down
Binary file modified insomniac/assets/ADBKeyboard.apk
Binary file not shown.
40 changes: 26 additions & 14 deletions insomniac/db_models.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import uuid
from collections import defaultdict
from typing import Optional
from typing import List

from peewee import *
from playhouse.migrate import SqliteMigrator, migrate

from insomniac.utils import *
from insomniac.globals import executable_name
from insomniac.utils import *

DATABASE_NAME = f'{executable_name}.db'
DATABASE_VERSION = 4

db = SqliteDatabase(DATABASE_NAME, autoconnect=False)

# Uncomment the line below to use PostgreSQL database instead. You may want to use it if you have multiple engine
# instances running at the same time and trying to write to the same database. SQLite is not good for this,
# while PostgreSQL has client-server architecture and easily handles concurrent operations.
#
# Note that you'll have to install PostgreSQL (it's not included in Python distribution like SQLite), create scheme
# and then replace "your_database_name", "your_username" and "your_password" parameters according to your setup.
#
# db = PostgresqlDatabase('your_database_name', user='your_username', password='your_password', host='localhost', port=5432, autoconnect=False)


class InsomniacModel(Model):
class Meta:
Expand All @@ -25,7 +34,7 @@ class InstagramProfile(InsomniacModel):
class Meta:
db_table = 'instagram_profiles'

def start_session(self, app_id, app_version, args, profile_status, followers_count, following_count) -> uuid.UUID:
def start_session(self, app_id, app_version, args, profile_status, followers_count, following_count, start_time=None) -> uuid.UUID:
"""
Create InstagramProfileInfo record
Create SessionInfo record
Expand All @@ -35,21 +44,23 @@ def start_session(self, app_id, app_version, args, profile_status, followers_cou
profile_info = InstagramProfileInfo.create(profile=self,
status=profile_status,
followers=followers_count,
following=following_count)
following=following_count,
timestamp=(start_time or datetime.now()))

session = SessionInfo.create(app_id=app_id,
app_version=app_version,
start=(start_time or datetime.now()),
args=args,
profile_info=profile_info)
return session.id

def end_session(self, session_id):
def end_session(self, session_id, end_time=None):
"""
Add end-timestamp to the SessionInfo record
"""
with db.connection_context():
session_info = SessionInfo.get(SessionInfo.id == session_id)
session_info.end = datetime.now()
session_info.end = end_time or datetime.now()
session_info.save()

def add_session(self, app_id, app_version, args, profile_status, followers_count, following_count, start, end):
Expand Down Expand Up @@ -79,17 +90,17 @@ def update_profile_info(self, profile_status, followers_count, following_count):
followers=followers_count,
following=following_count)

def get_latsest_profile_info(self) -> Optional['InstagramProfileInfo']:
def get_last_profile_infos(self, count) -> List['InstagramProfileInfo']:
with db.connection_context():
query = InstagramProfileInfo.select() \
.where(InstagramProfileInfo.profile == self) \
.group_by(InstagramProfileInfo.profile) \
.having(InstagramProfileInfo.timestamp == fn.MAX(InstagramProfileInfo.timestamp))

for obj in query:
return obj
.order_by(InstagramProfileInfo.timestamp.desc()) \
.limit(count)
return list(query)

return None
def get_latsest_profile_info(self) -> Optional['InstagramProfileInfo']:
last_profile_infos = self.get_last_profile_infos(1)
return last_profile_infos[0] if last_profile_infos is not None else None

def log_get_profile_action(self, session_id, phase, username, task_id='', execution_id='', timestamp=None):
"""
Expand Down Expand Up @@ -888,7 +899,7 @@ def get_actions_count_within_hours_for_profiles(action_types=None, hours=None,


def get_actions_count_for_profiles(action_types=None, timestamp_from=None, timestamp_to=None,
profiles=None, task_ids=None, session_phases=None) -> dict:
profiles=None, task_ids=None, session_phases=None, execution_ids_to_exclude=None) -> dict:
"""
Returns the amount of actions by 'action_type', within the last 'hours', that been done by 'profiles'
"""
Expand All @@ -900,6 +911,7 @@ def get_actions_count_for_profiles(action_types=None, timestamp_from=None, times
query = InsomniacAction.select(InsomniacAction.actor_profile, InsomniacAction.type, fn.COUNT(InsomniacAction.id).alias('ct'))\
.where((InsomniacAction.actor_profile.in_(profiles) if profiles else True) &
(InsomniacAction.task_id.in_(task_ids) if task_ids else True) &
(InsomniacAction.execution_id.not_in(execution_ids_to_exclude) if execution_ids_to_exclude else True) &
(InsomniacAction.phase.in_(session_phases_values) if session_phases_values else True) &
(InsomniacAction.type.in_(action_types_names) if action_types_names else True) &
(InsomniacAction.timestamp >= timestamp_from if timestamp_from else True) &
Expand Down
5 changes: 4 additions & 1 deletion insomniac/device_facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,10 @@ def is_alive(self):
if self.deviceV1 is not None:
return self.deviceV1.server.alive
else:
return self.deviceV2._is_alive()
try:
return self.deviceV2.server.alive
except AttributeError:
return self.deviceV2._is_alive()

def wake_up(self):
""" Make sure agent is alive or bring it back up before starting. """
Expand Down
3 changes: 3 additions & 0 deletions insomniac/extra_features/report_sender.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from insomniac import activation_controller

exec(activation_controller.get_extra_feature('report_sender'))
12 changes: 5 additions & 7 deletions insomniac/globals.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
# These constants can be set by the external UI-layer process, don't change them manually
from typing import Callable

is_ui_process = False
execution_id = ''
task_id = ''
executable_name = 'insomniac'
do_location_permission_dialog_checks = True # no need in these checks if location permission is denied beforehand


def callback(profile_name):
pass


hardban_detected_callback = callback
softban_detected_callback = callback
hardban_detected_callback: Callable[[str], None] = lambda profile_name: None # call when hard ban detected
softban_detected_callback: Callable[[str], None] = lambda profile_name: None # call when soft ban detected
is_session_allowed_callback: Callable[[str, object], bool] = lambda: True # call to check whether UI app allows this session


def is_insomniac():
Expand Down
51 changes: 50 additions & 1 deletion insomniac/navigation.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from insomniac.utils import *
from insomniac.views import TabBarView, ProfileView, TabBarTabs, LanguageNotEnglishException, DialogView
from insomniac.views import TabBarView, ProfileView, TabBarTabs, LanguageNotEnglishException, DialogView, OpenedPostView

SEARCH_CONTENT_DESC_REGEX = '[Ss]earch and [Ee]xplore'

Expand Down Expand Up @@ -40,11 +40,60 @@ def switch_to_english(device):
.switch_to_english()


def open_instagram_with_network_check(device) -> bool:
"""
:return: true if IG app was opened, false if it was already opened
"""
print("Open Instagram app with network check")
device_id = device.device_id
app_id = device.app_id

# Try via starter
cmd = ("adb" + ("" if device_id is None else " -s " + device_id) +
f" shell am start -a com.alexal1.starter.CHECK_CONNECTION_AND_LAUNCH_APP --es \"package\" \"{app_id}\"")
cmd_res = subprocess.run(cmd, stdout=PIPE, stderr=PIPE, shell=True, encoding="utf8")
err = cmd_res.stderr.strip()
if err:
# Fallback to standard way
print(COLOR_FAIL + "Didn't work :(" + COLOR_ENDC)
return open_instagram(device_id, app_id)

# Wait until Instagram is actually opened
max_attempts = 10
attempt = 0
while True:
sleep(5)
attempt += 1
resumed_activity_output = execute_command("adb" + ("" if device_id is None else " -s " + device_id) +
f" shell dumpsys activity | grep 'mResumedActivity'")
if app_id in resumed_activity_output:
break

if attempt < max_attempts:
print(COLOR_OKGREEN + "Instagram is not yet opened, waiting..." + COLOR_ENDC)
sleep(10)
else:
return open_instagram_with_network_check(device)
return True


def close_instagram_and_system_dialogs(device):
close_instagram(device.device_id, device.app_id)
# If the app crashed there will be a system dialog
DialogView(device).close_not_responding_dialog_if_visible()


def is_user_exists(device, username):
return TabBarView(device).navigate_to_search().find_username(username)


def is_post_exists(device, post_link):
if not open_instagram_with_url(device.device_id, device.app_id, post_link):
return False
is_post_opened = OpenedPostView(device).is_visible()
device.back()
return is_post_opened


class LanguageChangedException(Exception):
pass
Loading

0 comments on commit 1a8016b

Please sign in to comment.