Skip to content

Commit

Permalink
v1.2.0 (#137)
Browse files Browse the repository at this point in the history
## Release 1.2.0
### Bug Fixes

```
🐛 add new resource IDs to support more instagram versions
🐛 add short sleep before checking if the screen is unlocked to reduce issues
🐛 modify story watching to reduce crashes
🐛 substitute the .down with .find that caused a crash
🐛 fix analytics report when there is broken data
🐛 fix errors related to resourceid refactor
🐛 fix unfollow after unfollowing a follower
🐛 fix error on hashtag in biography
🐛 fix an infinite loop issue when running unfollow plugin
🐛 fix follow button class
🐛 fix follow not count when profile private
🐛 fix issue with sort followers and overlaying element
```

### New Features

```
🎁 add support for config files
🎁 add support for cloned apps
🎁 add feature - hashtag-posts-recent
🎁 add feature - hashtag-posts-top
🎁 add feature - interact-from-file
🎁 add feature - unfollow-any-non-followers
🎁 add feature - debug flag for debug output to console
🎁 add feature - speed modifier
🎁 add feature - add option to pause when exiting with ctrl-c
🎁 add feature - exit source after scrolling a configurable number of times
🎁 add filter - following
🎁 add filter - follower
🎁 add back optional fling support
🎁 add support for uia1 (kinda)
```

### Improvements

```
🐎 increase speed of things that don't need to be slow
🐎 sped up sleep after unfollow
🐈 kill uia2 agent while closing instagram app
🐈 ensure we are on profile at start of each job
🐈 improve swipe on hashtag-likers to support more instagram versions
🐈 randomize swipe points for better human-like emulation
🐈 refactor resource ids
🐈 include time in analytics report filename so you can generate multiple a day
🐈 change distro method to pypi
🐈 improve the unfollow message (only show if unfollowed)
🐈 remove other unnecessary `while True` loops
🐈 add more debug output to help with fixing issues
📝 fix some issues with argument help not matching
📝 add new logo
📝 add new demo
📝 update formatting and info
```


Co-authored-by: Philip Ulrich <[email protected]>
Co-authored-by: Dennis <[email protected]>
Co-authored-by: narkopolo <[email protected]>
Co-authored-by: Arthur Silva <[email protected]>
  • Loading branch information
4 people authored Dec 22, 2020
1 parent f1548e7 commit aeb9466
Show file tree
Hide file tree
Showing 39 changed files with 2,897 additions and 890 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: Enhancement Request
about: Suggest an enhancement to the Kubernetes project
about: Suggest an enhancement to the GramAddict project
labels: kind/feature

---
Expand Down
9 changes: 7 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
/venv*
/.venv
/build
/dist
/gramaddict.egg-info
/.idea
/.vscode
*/*.yml
interacted_users.*
sessions.json
*.pyc
Expand All @@ -12,5 +17,5 @@ whitelist.txt
Pipfile
Pipfile.lock
*.pdf
.venv
*.log*
*.log*
!config-examples/*
7 changes: 7 additions & 0 deletions DEPLOYMENT.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Process for Deploying Releases
- Make sure you are in the `master` branch and do a `git pull` prior to continuing
- Ensure version number in both `__version__.py` and `setup-beta.py` are both set to the new version number, without any `b` designation. e.g. 1.2.0, 1.2.1
- Ensure you have `twine` installed
- Remove any existing distribution data: `rm -rf dist/ gramaddict.egg-info/`
- Run the command: `python3 setup.py sdist`
- Run the command: `twine upload dist/*`
197 changes: 68 additions & 129 deletions GramAddict/__init__.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import argparse
import logging
import sys
from datetime import datetime
from sys import exit
from time import sleep

from colorama import Fore, Style

from GramAddict.core.device_facade import DeviceFacade, create_device
from GramAddict.core.config import Config
from GramAddict.core.device_facade import create_device
from GramAddict.core.filter import load_config as load_filter
from GramAddict.core.interaction import load_config as load_interaction
from GramAddict.core.log import (
configure_logger,
update_log_file_name,
is_log_file_updated,
)
from GramAddict.core.navigation import switch_to_english
from GramAddict.core.persistent_list import PersistentList
from GramAddict.core.plugin_loader import PluginLoader
from GramAddict.core.report import print_full_report
from GramAddict.core.session_state import SessionState, SessionStateEncoder
from GramAddict.core.storage import Storage
Expand All @@ -23,16 +24,25 @@
close_instagram,
get_instagram_version,
get_value,
load_config as load_utils,
open_instagram,
random_sleep,
save_crash,
update_available,
)
from GramAddict.core.views import TabBarView
from GramAddict.core.views import (
AccountView,
ProfileView,
TabBarView,
load_config as load_views,
)
from GramAddict.version import __version__

# Pre-Load Config
configs = Config(first_run=True)

# Logging initialization
configure_logger()
configure_logger(configs.debug, configs.username)
logger = logging.getLogger(__name__)
if update_available():
logger.warn(
Expand All @@ -42,123 +52,40 @@
f"GramAddict {__version__}", extra={"color": f"{Style.BRIGHT}{Fore.MAGENTA}"}
)


# Global Variables
device_id = None
plugins = PluginLoader("GramAddict.plugins").plugins
sessions = PersistentList("sessions", SessionStateEncoder)
parser = argparse.ArgumentParser(description="GramAddict Instagram Bot")


def load_plugins():
actions = {}

for plugin in plugins:
if plugin.arguments:
for arg in plugin.arguments:
try:
action = arg.get("action", None)
if action:
parser.add_argument(
arg["arg"], help=arg["help"], action=arg.get("action", None)
)
else:
parser.add_argument(
arg["arg"],
nargs=arg["nargs"],
help=arg["help"],
metavar=arg["metavar"],
default=arg["default"],
)
if arg.get("operation", False):
actions[arg["arg"]] = plugin
except Exception as e:
logger.error(
f"Error while importing arguments of plugin {plugin.__class__.__name__}. Error: Missing key from arguments dictionary - {e}"
)
return actions


def get_args():
logger.debug(f"Arguments used: {' '.join(sys.argv[1:])}")
if not len(sys.argv) > 1:
parser.print_help()
return False

args, unknown_args = parser.parse_known_args()

if unknown_args:
logger.error(
"Unknown arguments: " + ", ".join(str(arg) for arg in unknown_args)
)
parser.print_help()
return False

return args
# Load Config
configs.load_plugins()
configs.parse_args()


def run():
global device_id
loaded = load_plugins()
args = get_args()
enabled = []
if not args:
# Some plugins need config values without being passed
# through. Because we do a weird config/argparse hybrid,
# we need to load the configs in a weird way
load_filter(configs)
load_interaction(configs)
load_utils(configs)
load_views(configs)

if not configs.args or not check_adb_connection():
return
dargs = vars(args)

for item in sys.argv[1:]:
if item in loaded:
if item != "--interact" and item != "--hashtag-likers":
enabled.append(item)

for k in loaded:
if dargs[k.replace("-", "_")[2:]] != None:
if k == "--interact":
logger.warn(
'Using legacy argument "--interact". Please switch to new arguments as this will be deprecated in the near future.'
)
for source in args.interact:
if "@" in source:
enabled.append("--blogger-followers")
if type(args.blogger_followers) != list:
args.blogger_followers = [source]
else:
args.blogger_followers.append(source)
else:
enabled.append("--hashtag-likers-top")
if type(args.hashtag_likers_top) != list:
args.hashtag_likers_top = [source]
else:
args.hashtag_likers_top.append(source)
elif k == "--hashtag-likers":
logger.warn(
'Using legacy argument "--hashtag-likers". Please switch to new arguments as this will be deprecated in the near future.'
)
for source in args.hashtag_likers:
enabled.append("--hashtag-likers-top")
if type(args.hashtag_likers_top) != list:
args.hashtag_likers_top = [source]
else:
args.hashtag_likers_top.append(source)

enabled = list(dict.fromkeys(enabled))

if len(enabled) < 1:
logger.error("You have to specify one of the actions: " + ", ".join(loaded))
if len(configs.enabled) < 1:
logger.error(
"You have to specify one of the actions: " + ", ".join(configs.actions)
)
return

device_id = args.device
if not check_adb_connection(is_device_id_provided=(device_id is not None)):
return
logger.info("Instagram version: " + get_instagram_version(device_id))
device = create_device(device_id)
logger.info("Instagram version: " + get_instagram_version())
device = create_device(configs.device_id, configs.args.uia_version)

if device is None:
return

while True:
session_state = SessionState()
session_state.args = args.__dict__
session_state = SessionState(configs)
sessions.append(session_state)

device.wake_up()
Expand All @@ -168,23 +95,32 @@ def run():
extra={"color": f"{Style.BRIGHT}{Fore.YELLOW}"},
)

if not DeviceFacade(device_id).get_info()["screenOn"]:
DeviceFacade(device_id).press_power()
if DeviceFacade(device_id).is_screen_locked():
DeviceFacade(device_id).unlock()
if DeviceFacade(device_id).is_screen_locked():
if not device.get_info()["screenOn"]:
device.press_power()
if device.is_screen_locked():
device.unlock()
if device.is_screen_locked():
logger.error(
"Can't unlock your screen. There may be a passcode on it. If you would like your screen to be turned on and unlocked automatically, please remove the passcode."
)
sys.exit()
exit(0)

logger.info("Device screen on and unlocked.")

open_instagram(device_id)
open_instagram()

try:
profileView = TabBarView(device).navigateToProfile()
random_sleep()
if configs.args.username is not None:
success = AccountView(device).changeToUsername(configs.args.username)
if not success:
logger.error(
f"Not able to change to {configs.args.username}, abort!"
)
device.back()
break

(
session_state.my_username,
session_state.my_followers_count,
Expand All @@ -204,9 +140,9 @@ def run():
) = profileView.getProfileInfo()

if (
session_state.my_username == None
or session_state.my_followers_count == None
or session_state.my_following_count == None
session_state.my_username is None
or session_state.my_followers_count is None
or session_state.my_following_count is None
):
logger.critical(
"Could not get one of the following from your profile: username, # of followers, # of followings. This is typically due to a soft ban. Review the crash screenshot to see if this is the case."
Expand All @@ -231,40 +167,43 @@ def run():
logger.info(report_string, extra={"color": f"{Style.BRIGHT}"})

storage = Storage(session_state.my_username)
for plugin in enabled:
for plugin in configs.enabled:
if not session_state.check_limit(
args, limit_type=session_state.Limit.ALL, output=False
configs.args, limit_type=session_state.Limit.ALL, output=False
):
loaded[plugin].run(
device, device_id, args, enabled, storage, sessions, plugin
)
logger.info(f"Current job: {plugin}", extra={"color": f"{Fore.BLUE}"})
if ProfileView(device).getUsername() != session_state.my_username:
logger.debug("Not in your main profile.")
TabBarView(device).navigateToProfile()
configs.actions[plugin].run(device, configs, storage, sessions, plugin)

else:
logger.info(
"Successful or Total Interactions limit reached. Ending session."
)
break

close_instagram(device_id)
close_instagram()
session_state.finishTime = datetime.now()

if args.screen_sleep:
DeviceFacade(device_id).screen_off()
if configs.args.screen_sleep:
device.screen_off()
logger.info("Screen turned off for sleeping time")

logger.info(
"-------- FINISH: " + str(session_state.finishTime) + " --------",
extra={"color": f"{Style.BRIGHT}{Fore.YELLOW}"},
)

if args.repeat:
if configs.args.repeat:
print_full_report(sessions)
repeat = get_value(args.repeat, "Sleep for {} minutes", 180)
repeat = get_value(configs.args.repeat, "Sleep for {} minutes", 180)
try:
sleep(60 * repeat)
except KeyboardInterrupt:
print_full_report(sessions)
sessions.persist(directory=session_state.my_username)
sys.exit(0)
exit(0)
else:
break

Expand Down
Empty file added GramAddict/core/__init__.py
Empty file.
Loading

0 comments on commit aeb9466

Please sign in to comment.