diff --git a/.gitignore b/.gitignore index c5316d9..fd94dd6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,10 @@ /venv* /.idea -interacted_users.* -scrapped_users.json -filtered_users.json -sessions.json +interaction_data.db targets.txt +targets_loaded.txt *.pyc .DS_Store -screenshots crashes filter.json whitelist.txt @@ -15,3 +12,4 @@ blacklist.txt /build /dist /*.egg-info +*.log diff --git a/insomniac/__init__.py b/insomniac/__init__.py index 2010097..d2dfb11 100644 --- a/insomniac/__init__.py +++ b/insomniac/__init__.py @@ -1,11 +1,11 @@ -from insomniac.__version__ import __debug_mode__, __logo__ +import insomniac.__version__ as __version__ from insomniac.activation import activation_controller from insomniac.utils import * def run(activation_code=""): - if not __debug_mode__: - print_timeless(COLOR_OKGREEN + __logo__ + COLOR_ENDC) + if not __version__.__debug_mode__: + print_timeless(COLOR_OKGREEN + __version__.__logo__ + COLOR_ENDC) print_version() activation_controller.validate(activation_code) diff --git a/insomniac/__version__.py b/insomniac/__version__.py index 312d3e4..a6c4cab 100644 --- a/insomniac/__version__.py +++ b/insomniac/__version__.py @@ -13,7 +13,7 @@ __title__ = 'insomniac' __description__ = 'Simple Instagram bot for automated Instagram interaction using Android.' __url__ = 'https://github.com/alexal1/Insomniac/' -__version__ = '3.6.5' +__version__ = '3.6.6' __debug_mode__ = False __author__ = 'Insomniac Team' __author_email__ = 'info@insomniac-bot.com' diff --git a/insomniac/actions_impl.py b/insomniac/actions_impl.py index 3216cb7..672769c 100644 --- a/insomniac/actions_impl.py +++ b/insomniac/actions_impl.py @@ -824,9 +824,10 @@ def _close_confirm_dialog_by_version(device, version): sleeper.random_sleep() if version == 1: unfollow_button = dialog_root_view.child(resourceId=f'{device.app_id}:id/primary_button', - className='android.widget.TextView') + 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() diff --git a/insomniac/database_engine.py b/insomniac/database_engine.py index 304d25b..b436cac 100644 --- a/insomniac/database_engine.py +++ b/insomniac/database_engine.py @@ -21,6 +21,8 @@ "provider = 'TARGETS_LIST' AND interactions_count = 0" SQL_SELECT_FROM_FILTERED_USERS_BY_USERNAME = "SELECT * from filtered_users WHERE username = :username" SQL_SELECT_FROM_SCRAPED_USERS_BY_USERNAME = "SELECT * from scraped_users WHERE username = :username" +SQL_SELECT_ALL_SESSIONS = "SELECT * from sessions INNER JOIN profiles ON sessions.profile_id = profiles.id" +SQL_SELECT_PROFILE_BY_ID = "SELECT * from profiles WHERE id = :profile_id" SQL_INSERT_DEFAULT_INTO_METADATA = "INSERT INTO metadata DEFAULT VALUES" SQL_INSERT_INTO_METADATA = "INSERT INTO metadata (version) VALUES (?)" @@ -521,6 +523,32 @@ def add_sessions(address, session_states): connection.close() +def get_all_sessions(address): + connection = None + sessions = None + try: + connection = sqlite3.connect(address) + connection.row_factory = dict_factory + cursor = connection.cursor() + cursor.execute(SQL_SELECT_ALL_SESSIONS) + sessions = cursor.fetchall() + except Exception as e: + print(COLOR_FAIL + f"[Database] Cannot get all sessions: {e}" + COLOR_ENDC) + finally: + if connection: + # Close the opened connection + connection.close() + + return list(sessions) if sessions is not None else () + + +def dict_factory(cursor, row): + d = {} + for idx, col in enumerate(cursor.description): + d[col[0]] = row[idx] + return d + + def _run_migrations(cursor): current_version = _get_database_version(cursor) latest_version = max(DB_VERSIONS.values()) diff --git a/insomniac/device_facade.py b/insomniac/device_facade.py index 4f32906..94e6a17 100644 --- a/insomniac/device_facade.py +++ b/insomniac/device_facade.py @@ -1,4 +1,5 @@ from enum import Enum, unique +from os import listdir from random import uniform from re import search @@ -8,6 +9,8 @@ UI_TIMEOUT_LONG = 5 UI_TIMEOUT_SHORT = 1 +SCREEN_RECORDS_PATH = "screen_records" + def create_device(is_old, device_id, app_id): print("Using uiautomator v" + ("1" if is_old else "2")) @@ -86,6 +89,47 @@ def screenshot(self, path): else: self.deviceV2.screenshot(path) + def start_screen_record(self, fps=10): + """Available for uiautomator2 only""" + if self.deviceV1 is not None: + print(COLOR_FAIL + "Screen record doesn't work when you use the --old flag" + COLOR_ENDC) + else: + if not os.path.exists(SCREEN_RECORDS_PATH): + os.makedirs(SCREEN_RECORDS_PATH) + mp4_files = [f for f in listdir(SCREEN_RECORDS_PATH) if f.endswith(".mp4")] + if mp4_files: + last_mp4 = mp4_files[-1] + debug_number = "{0:0=4d}".format(int(last_mp4[-8:-4]) + 1) + else: + debug_number = "0000" + output = os.path.join(SCREEN_RECORDS_PATH, f"debug_{debug_number}.mp4") + try: + self.deviceV2.screenrecord(output, fps) + except ModuleNotFoundError: + print(COLOR_FAIL + "To use screen recording please install additional packages:" + COLOR_ENDC) + print(COLOR_FAIL + COLOR_BOLD + + 'pip3 install -U "uiautomator2[image]" -i https://pypi.doubanio.com/simple' + COLOR_ENDC) + return + print(COLOR_OKGREEN + f'Started screen recording: it will be saved as "{output}".' + COLOR_ENDC) + + def stop_screen_record(self): + """Available for uiautomator2 only""" + if self.deviceV1 is not None: + return + + try: + is_recorded = self.deviceV2.screenrecord.stop() + except ModuleNotFoundError: + is_recorded = False + if is_recorded: + if not os.path.exists(SCREEN_RECORDS_PATH): + return + mp4_files = [f for f in listdir(SCREEN_RECORDS_PATH) if f.endswith(".mp4")] + if mp4_files: + last_mp4 = mp4_files[-1] + path = os.path.join(SCREEN_RECORDS_PATH, last_mp4) + print(COLOR_OKGREEN + f'Screen recorder has been stopped successfully! Saved as "{path}".' + COLOR_ENDC) + def dump_hierarchy(self, path): if self.deviceV1 is not None: xml_dump = self.deviceV1.dump() diff --git a/insomniac/navigation.py b/insomniac/navigation.py index a0c4744..7196930 100644 --- a/insomniac/navigation.py +++ b/insomniac/navigation.py @@ -1,11 +1,11 @@ from enum import Enum, unique -from insomniac.actions_types import GetProfileAction -from insomniac.sleeper import sleeper from insomniac.utils import * from insomniac.views import TabBarView SEARCH_CONTENT_DESC_REGEX = '[Ss]earch and [Ee]xplore' +SETTINGS_LIST_ID_REGEX = 'android:id/list|{0}:id/recycler_view' +SETTINGS_LIST_CLASS_NAME_REGEX = 'android.widget.ListView|androidx.recyclerview.widget.RecyclerView' def navigate(device, tab): @@ -49,9 +49,9 @@ def switch_to_english(device): action_bar = device.find(resourceId=f'{device.app_id}:id/action_bar', className='android.widget.LinearLayout') - # We wanna pick last ImageView in the action bar + # We wanna pick last view in the action bar options_view = None - for options_view in action_bar.child(className='android.widget.ImageView'): + for options_view in action_bar.child(clickable=True): pass if options_view is None or not options_view.exists(): print(COLOR_FAIL + "No idea how to open menu..." + COLOR_ENDC) @@ -63,19 +63,19 @@ def switch_to_english(device): settings_button.click() for account_item_index in range(6, 9): - list_view = device.find(resourceId='android:id/list', - className='android.widget.ListView') - account_item = list_view.child(index=account_item_index) + list_view = device.find(resourceIdMatches=SETTINGS_LIST_ID_REGEX.format(device.app_id), + classNameMatches=SETTINGS_LIST_CLASS_NAME_REGEX) + account_item = list_view.child(index=account_item_index, clickable=True) account_item.click() - list_view = device.find(resourceId='android:id/list', - className='android.widget.ListView') - if not list_view.exists(): + list_view = device.find(resourceIdMatches=SETTINGS_LIST_ID_REGEX.format(device.app_id), + classNameMatches=SETTINGS_LIST_CLASS_NAME_REGEX) + if not list_view.exists(quick=True): print("Opened a wrong tab, going back") device.back() continue - language_item = list_view.child(index=4) - if not language_item.exists(): + language_item = list_view.child(index=4, clickable=True) + if not language_item.exists(quick=True): print("Opened a wrong tab, going back") device.back() continue @@ -83,7 +83,7 @@ def switch_to_english(device): search_edit_text = device.find(resourceId=f'{device.app_id}:id/search', className='android.widget.EditText') - if not search_edit_text.exists(): + if not search_edit_text.exists(quick=True): print("Opened a wrong tab, going back") device.back() device.back() diff --git a/insomniac/session.py b/insomniac/session.py index 42830bc..d3e931a 100644 --- a/insomniac/session.py +++ b/insomniac/session.py @@ -1,10 +1,9 @@ import random -import sys import colorama -import insomniac.__version__ -from insomniac.__version__ import __debug_mode__ +import insomniac.__version__ as __version__ +import insomniac.softban_indicator as softban_indicator from insomniac.action_get_my_profile_info import get_my_profile_info from insomniac.action_runners.actions_runners_manager import ActionRunnersManager from insomniac.device import DeviceWrapper @@ -112,8 +111,8 @@ def reset_params(self): self.repeat = None self.username = None self.next_config_file = None - insomniac.__version__.__debug_mode__ = False - insomniac.softban_indicator.should_indicate_softban = True + __version__.__debug_mode__ = False + softban_indicator.should_indicate_softban = True def set_session_args(self, args): self.reset_params() @@ -121,11 +120,11 @@ def set_session_args(self, args): if args.repeat is not None: self.repeat = get_value(args.repeat, "Sleep time (min) before repeat: {}", 180) - if args.debug is not None: - insomniac.__version__.__debug_mode__ = True + if args.debug is not None and bool(args.debug): + __version__.__debug_mode__ = True if args.dont_indicate_softban: - insomniac.softban_indicator.should_indicate_softban = False + softban_indicator.should_indicate_softban = False if args.username is not None: self.username = args.username @@ -164,6 +163,8 @@ def start_session(self, args, device_wrapper, app_version): close_instagram(device_wrapper.device_id, device_wrapper.app_id) sleeper.random_sleep() + if __version__.__debug_mode__: + device_wrapper.get().start_screen_record() open_instagram(args.device, args.app_id) sleeper.random_sleep() self.session_state.my_username, \ @@ -174,6 +175,8 @@ def start_session(self, args, device_wrapper, app_version): def end_session(self, device_wrapper): close_instagram(device_wrapper.device_id, device_wrapper.app_id) + if __version__.__debug_mode__: + device_wrapper.get().stop_screen_record() print_copyright() self.session_state.finishTime = datetime.now() print_timeless(COLOR_REPORT + "-------- FINISH: " + str(self.session_state.finishTime) + " --------" + COLOR_ENDC) @@ -247,13 +250,13 @@ def run(self): self.end_session(device_wrapper) return except Exception as ex: - if __debug_mode__: + if __version__.__debug_mode__: raise ex else: print_timeless(COLOR_FAIL + f"\nCaught an exception:\n{ex}" + COLOR_ENDC) print(COLOR_FAIL + traceback.format_exc() + COLOR_ENDC) save_crash(device_wrapper.get(), ex) - + self.end_session(device_wrapper) if self.repeat is not None: if not self.repeat_session(args): diff --git a/insomniac/utils.py b/insomniac/utils.py index 80c3c4e..c1c13b1 100644 --- a/insomniac/utils.py +++ b/insomniac/utils.py @@ -14,7 +14,7 @@ from urllib.error import URLError from urllib.parse import urlparse -from insomniac.__version__ import __version__, __debug_mode__ +import insomniac.__version__ as __version__ COLOR_HEADER = '\033[95m' COLOR_OKBLUE = '\033[94m' @@ -30,7 +30,7 @@ def print_version(): def versiontuple(v): return tuple(map(int, (v.split(".")))) - current_version = __version__ + current_version = __version__.__version__ print_timeless(COLOR_HEADER + f"Insomniac v{current_version}" + COLOR_ENDC) latest_version = _get_latest_version('insomniac') if latest_version is not None and versiontuple(latest_version) > versiontuple(current_version): @@ -181,7 +181,7 @@ def print_copyright(): def _print_with_time_decorator(standard_print, print_time, debug): def wrapper(*args, **kwargs): - if debug and not __debug_mode__: + if debug and not __version__.__debug_mode__: return global print_log