-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #110 from stanvanrooy/various_things
Various things
- Loading branch information
Showing
14 changed files
with
221 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import os | ||
|
||
from instauto.api.client import ApiClient | ||
from instauto.ready.interact_with_followers_of_account import interact_with_followers_of_account | ||
|
||
|
||
if __name__ == '__main__': | ||
if os.path.isfile('./.instauto.save'): | ||
client = ApiClient.initiate_from_file('./.instauto.save') | ||
else: | ||
client = ApiClient(user_name=os.environ.get("INSTAUTO_USER") or "your_username", password=os.environ.get("INSTAUTO_PASS") or "your_password") | ||
client.login() | ||
client.save_to_disk('./.instauto.save') | ||
|
||
# This snippet, does the following: | ||
# from the followers of the Instagram account: | ||
# like between 1 and 5 posts of the follower | ||
# comment on 0 to 1 posts of the user | ||
# with the comment: "Looks good, {full_name}!", where {full_name} is replaced | ||
# by the name set on the ig account | ||
# follow 1 out of 10 followers retrieved | ||
# wait between 20 and 120 seconds before moving on to the next follower | ||
|
||
interact_with_followers_of_account( | ||
client=client, | ||
target="instagram", | ||
delay=[20.0, 120.0], | ||
duration=60.0 * 60.0, | ||
likes_per_follower=[1, 5], | ||
comments_per_follower=[0, 1], | ||
follow_chance=10, | ||
comments=["Looks good, {full_name}!"], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
from instauto.api.client import ApiClient | ||
from instauto.api.actions.structs.friendships import GetFollowers, Create | ||
from instauto.helpers.search import get_user_id_from_username | ||
|
||
import typing | ||
import logging | ||
logger = logging.getLogger(__name__) | ||
|
||
from .common import is_resp_ok | ||
|
||
|
||
def get_followers(client: ApiClient, user_id: str, limit: int) -> typing.List[dict]: | ||
obj = GetFollowers(user_id) | ||
|
||
obj, result = client.followers_get(obj) | ||
followers = [] | ||
while result and len(followers) < limit: | ||
followers.extend( | ||
result.json()["users"] | ||
) | ||
logger.info("Retrieved {} followers, {} more to go.".format(len(followers), limit - len(followers))) | ||
obj, result = client.followers_get(obj) | ||
return followers[:min(len(followers), limit)] | ||
|
||
|
||
def follow_user(client: ApiClient, user_id: str = None, username: str = None) -> bool: | ||
if user_id is not None and username is not None: | ||
raise ValueError("Both `user_id` and `username` are provided.") | ||
|
||
if user_id is None and username is not None: | ||
user_id = get_user_id_from_username(client, username) | ||
|
||
if user_id is None: | ||
raise ValueError("Both `user_id` and `username` are not provided.") | ||
|
||
obj = Create(user_id) | ||
resp = client.user_follow(obj) | ||
return is_resp_ok(resp) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import random | ||
import string | ||
from typing import List | ||
|
||
|
||
def _get_fill_ins_from_comment(comment: str) -> List[str]: | ||
"""Get all format strings in the comment""" | ||
return [tup[1] for tup in string.Formatter().parse(comment) if tup[1] is not None] | ||
|
||
|
||
def fill_comment(comment: str, user: dict) -> str: | ||
"""Fill in the format strings in a comment from the user object. Currently, only top-level keys are supported.""" | ||
fill_ins = _get_fill_ins_from_comment(comment) | ||
for fill_in in fill_ins: | ||
comment = comment.format(**{fill_in: user[fill_in]}) | ||
return comment | ||
|
||
|
||
def get_random_num(sr: int, er: int, exclude: List[int]) -> int: | ||
n = random.randint(sr, er) | ||
while n in exclude: | ||
n = random.randint(sr, er) | ||
exclude.append(n) | ||
return n |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import random | ||
import time | ||
import logging | ||
import typing | ||
|
||
from instauto.helpers.search import * | ||
from instauto.helpers.post import retrieve_posts_from_user, like_post, comment_post | ||
from instauto.helpers.friendships import get_followers, follow_user | ||
from instauto.api.exceptions import AuthorizationError | ||
|
||
from helpers import fill_comment, get_random_num | ||
|
||
logger = logging.getLogger("Instauto") | ||
logger.setLevel(logging.INFO) | ||
|
||
|
||
def _get_posts(client: ApiClient, limit: int, user_id: str) -> typing.Tuple[List[dict], str]: | ||
"""Helper function for retrieving posts from a user""" | ||
try: | ||
# if the List is empty and no `AuthorizationError` was thrown, we assume the user has no posts | ||
return retrieve_posts_from_user(client, limit, user_id=user_id), "User has no posts" | ||
except AuthorizationError as e: | ||
return [], str(e) | ||
|
||
|
||
def _do_likes(client: ApiClient, limit: int, posts: List[dict]) -> None: | ||
already_liked = [] | ||
while len(already_liked) < limit: | ||
pi = get_random_num(0, len(posts) - 1, already_liked) | ||
post = posts[pi] | ||
logger.info(f"Liking post {post['id']} of {post['user']['username']}") | ||
like_post(client, post['id']) | ||
|
||
|
||
def _do_comments(client: ApiClient, limit: int, comments: List[str], user: dict, posts: List[dict]) -> None: | ||
already_commented_comments = [] | ||
already_commented_posts = [] | ||
|
||
while len(already_commented_posts) < limit: | ||
# get random comment | ||
comment = comments[get_random_num(0, len(comments) - 1, already_commented_comments)] | ||
comment = fill_comment(comment, user) | ||
|
||
# get random post | ||
pi = get_random_num(0, len(posts) - 1, already_commented_posts) | ||
post = posts[pi] | ||
logger.info(f"Commenting {comment} on post {post['id']} of user {user['username']}") | ||
# comment random comment on random post | ||
comment_post(client, post['id'], comment) | ||
|
||
|
||
def interact_with_followers_of_account(client: ApiClient, target: str, delay: List[float, float], duration: float, | ||
likes_per_follower: List[int, int], comments_per_follower: List[int, int], | ||
follow_chance: int, comments: List[str] = None): | ||
|
||
if comments_per_follower[1] > 0 and comments is None: | ||
raise ValueError("No comments provided.") | ||
if comments_per_follower[1] > len(comments): | ||
raise ValueError("The limit for comments per user is set higher then the length of the set of comments") | ||
if follow_chance < 0 or follow_chance > 100: | ||
raise ValueError("Follow chance needs to be a value between 0 and 100") | ||
|
||
target_user_id = get_user_id_from_username(client, target) | ||
followers = get_followers(client, target_user_id, int(duration // delay)) | ||
logger.info(f"Retrieved a total of {len(followers)} followers from {target}") | ||
|
||
for follower in followers: | ||
logger.info(f"Retrieving posts from {follower['username']}") | ||
|
||
posts, message = _get_posts(client, likes_per_follower[1] + comments_per_follower[1], follower['pk']) | ||
if not posts: | ||
logger.info(f"Can't retrieve posts from {follower['username']}: {message}") | ||
continue | ||
|
||
logger.info(f"Retrieved {len(posts)} posts from {follower['username']}") | ||
_do_likes(client, min(random.randint(*likes_per_follower), len(posts)), posts) | ||
_do_comments(client, min(random.randint(*comments_per_follower), len(posts)), comments, follower, posts) | ||
|
||
r = random.randint(0, 100) | ||
if r < int(follow_chance * 100): | ||
logger.info(f"Following user {follower['username']}") | ||
follow_user(client, user_id=follower['pk']) | ||
time.sleep(delay) | ||
|
||
|
||
# TODO: add de-duplication functionality, so we do not like/comment on the same user multiple times. | ||
# TODO: add multi-level comment fill-in functionality |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters