-
-
Notifications
You must be signed in to change notification settings - Fork 2
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 #31 from birdhouses/feature/discover-popular-posts…
…-based-on-hashtags Feature/discover popular posts based on hashtags
- Loading branch information
Showing
5 changed files
with
191 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 |
---|---|---|
|
@@ -10,6 +10,7 @@ session.json | |
/followed_users | ||
/comments | ||
/src/artifacts/* | ||
/src/saved_posts/ | ||
# C extensions | ||
*.so | ||
|
||
|
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 |
---|---|---|
@@ -1,30 +1,62 @@ | ||
{ | ||
"accounts": [ | ||
{ | ||
"username": "username", | ||
"password": "password", | ||
"follow_users": { | ||
"enabled": true, | ||
"follows_per_day": 100, | ||
"source_account": "instagram", | ||
"engagement": { | ||
"like_recent_posts": true, | ||
"like_count": 3 | ||
} | ||
}, | ||
"unfollow_users": { | ||
"enabled": true, | ||
"unfollow_after": "days-hours-minutes-seconds" | ||
"accounts": [ | ||
{ | ||
"username": "username", | ||
"password": "password", | ||
"follow_users": { | ||
"enabled": true, | ||
"follows_per_day": 20, | ||
"source_account": "instagram", | ||
"engagement": { | ||
"like_recent_posts": true, | ||
"like_count": 3 | ||
} | ||
}, | ||
"unfollow_users": { | ||
"enabled": true, | ||
"unfollow_after": "0-12-0-0" | ||
}, | ||
"comment_on_media": { | ||
"enabled": true, | ||
"comment_on_tag": "instagram", | ||
"amount_per_day": 50, | ||
"comments": [ | ||
"Comments ..." | ||
] | ||
}, | ||
"media_auto_discovery": { | ||
"enabled": true, | ||
"from_tag": "instagram", | ||
"amount_per_day": 2, | ||
"save_captions": true, | ||
"avoid_duplicates": true, | ||
"post_requirements": { | ||
"min_likes": 1, | ||
"min_comments": 1, | ||
"detect_caption_language": true, | ||
"languages": ["en"], | ||
"allowed_post_types": [ | ||
"photo", | ||
"video", | ||
"igtv", | ||
"reel", | ||
"album" | ||
] | ||
}, | ||
"comment_on_media": { | ||
"author_requirements": { | ||
"enabled": true, | ||
"comment_on_tag": "hashtag", | ||
"amount_per_day": 5, | ||
"comments": [ | ||
"Comment 1.." | ||
] | ||
"detect_biography_keywords": false, | ||
"biography_keywords": [ | ||
"keyword", | ||
"another one" | ||
], | ||
"detect_biography_language": true, | ||
"languages": ["en"], | ||
"min_followers": 1, | ||
"max_following": 1000 | ||
} | ||
} | ||
] | ||
} | ||
} | ||
] | ||
} | ||
|
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 |
---|---|---|
@@ -1,4 +1,5 @@ | ||
from .utils import get_client, load_config, get_user_id, get_followers, calculate_sleep_time, parse_time_string, logger | ||
from .utils import next_proxy, get_client, load_config, get_user_id, get_followers, calculate_sleep_time, parse_time_string, logger | ||
from .follow import follow_user_followers, unfollow_users, save_followed_user, load_followed_users, filter_users_to_unfollow, remove_unfollowed_user, mark_unfollowed_user, user_not_followed_before | ||
from .like_media import like_recent_posts | ||
from .comment import comment_on_media | ||
from .comment import comment_on_media | ||
from .media_discovery import media_auto_discovery |
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,122 @@ | ||
import instabot | ||
import asyncio | ||
import langdetect | ||
from instagrapi import Client | ||
import os | ||
|
||
MIN_LIKES = "min_likes" | ||
MIN_COMMENTS = "min_comments" | ||
DETECT_CAPTION_LANGUAGE = "detect_caption_language" | ||
LANGUAGES = "languages" | ||
ALLOWED_POST_TYPES = "allowed_post_types" | ||
|
||
async def media_auto_discovery(client, account): | ||
config = account['media_auto_discovery'] | ||
|
||
while True: | ||
posts = client.hashtag_medias_top(config['from_tag']) | ||
sleep_time = instabot.calculate_sleep_time(config['amount_per_day']) | ||
for post in posts: | ||
if await passes_requirements(client, post, config): | ||
store_post(client, account, post) | ||
await asyncio.sleep(sleep_time) | ||
|
||
async def passes_requirements(cl, post, config): | ||
return (await check_post_requirements(cl, post, config) and | ||
await check_author_requirements(cl, post, config)) | ||
|
||
async def check_post_requirements(cl, post, config): | ||
post_req = config['post_requirements'] | ||
post = cl.media_info(post.id) | ||
|
||
if post.like_count < post_req[MIN_LIKES] or post.comment_count < post_req[MIN_COMMENTS]: | ||
return False | ||
|
||
if post_req[DETECT_CAPTION_LANGUAGE]: | ||
if not is_language_allowed(post.caption_text, post_req[LANGUAGES]): | ||
return False | ||
|
||
if not is_post_type_allowed(post, post_req[ALLOWED_POST_TYPES]): | ||
return False | ||
|
||
return True | ||
|
||
async def check_author_requirements(cl, post, config): | ||
author_req = config['author_requirements'] | ||
if not author_req['enabled']: | ||
return True | ||
|
||
author = cl.user_info_by_username(post.user.username) | ||
|
||
if (author_req['min_followers'] >= author.follower_count or | ||
author_req['max_following'] <= author.following_count): | ||
return False | ||
|
||
if author_req['detect_biography_keywords']: | ||
if not has_keywords(author.biography, author_req['biography_keywords']): | ||
return False | ||
|
||
if author_req['detect_biography_language']: | ||
if not is_language_allowed(author.biography, author_req[LANGUAGES]): | ||
return False | ||
|
||
return True | ||
|
||
def is_language_allowed(text, allowed_languages): | ||
try: | ||
detected_language = langdetect.detect(text) | ||
return detected_language in allowed_languages | ||
except: | ||
return True | ||
|
||
def is_post_type_allowed(post, allowed_post_types): | ||
post_type = get_post_type(post) | ||
return post_type is None or post_type.lower() in allowed_post_types | ||
|
||
def get_post_type(post): | ||
if post.media_type == 1: | ||
return 'photo' | ||
elif post.media_type == 2 and post.product_type == 'feed': | ||
return 'video' | ||
elif post.media_type == 2 and post.product_type == 'igtv': | ||
return 'igtv' | ||
elif post.media_type == 8: | ||
return 'album' | ||
return None | ||
|
||
def has_keywords(biography, keywords): | ||
biography_lower = biography.lower() | ||
return any(keyword.lower() in biography_lower for keyword in keywords) | ||
|
||
def store_post(cl, account, post): | ||
photo_download_path = f"./saved_posts/{account['username']}/photo_downloads" | ||
video_download_path = f"./saved_posts/{account['username']}/video_downloads" | ||
album_download_path = f"./saved_posts/{account['username']}/album_downloads" | ||
|
||
if post.media_type == 1: | ||
download_photo(cl, post, photo_download_path) | ||
elif post.media_type == 2: | ||
download_video(cl, post, video_download_path) | ||
elif post.media_type == 8: | ||
download_album(cl, post, album_download_path) | ||
|
||
def download_photo(cl, post, photo_download_path): | ||
os.makedirs(photo_download_path, exist_ok=True) | ||
try: | ||
cl.photo_download(post.pk, photo_download_path) | ||
except: | ||
instabot.logger.info(f"Failed to download photo for post {post.id}") | ||
|
||
def download_video(cl, post, video_download_path): | ||
os.makedirs(video_download_path, exist_ok=True) | ||
try: | ||
cl.video_download(post.pk, video_download_path) | ||
except: | ||
instabot.logger.info(f"Failed to download video for post {post.id}") | ||
|
||
def download_album(cl, post, album_download_path): | ||
os.makedirs(album_download_path, exist_ok=True) | ||
try: | ||
cl.album_download(post.pk, album_download_path) | ||
except: | ||
instabot.logger.info(f"Failed to download album for post {post.id}") |
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