├── _config.yml ├── basic.png ├── README.md ├── LICENSE ├── duplicateDetection.py └── instagramBot.py /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-rccg/instaCompetitionBot/HEAD/basic.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # instaCompetitionBot 2 | Want to participate in those Instagram competitions, but it's not worth your time? Now you can! The instaCompetitionBot enters you into any contest it can find! Both via tag searches and crawling from linked users to linked users! 3 | 4 | This developed out of a 4h coding challenge and was added upon sporadically over the course of two months. 5 | I've won countless items ranging from fidget spinners, to festival tickets, to bar tab giftcards, to a $500 gift basket full of hair care products! I do not have time to bring it to the next level, but please enjoy getting free gifts delivered to your door! 6 | It was a fun learning experience for me, maybe one of you wants to continue the project so here it is open to all on github! 7 | 8 | Let me know if you find it useful! 9 | 10 | 11 | Best, 12 | RCCG 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 RCCG 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /duplicateDetection.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Determining Duplicate images 4 | 5 | http://blog.iconfinder.com/detecting-duplicate-images-using-python/ 6 | """ 7 | 8 | import os 9 | from PIL import Image 10 | 11 | def phash(image, hash_size=8, highfreq_factor=4): 12 | """ 13 | from: import imagehash 14 | Perceptual Hash computation. 15 | Implementation follows http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html 16 | @image must be a PIL instance. 17 | """ 18 | import numpy 19 | if hash_size < 0: 20 | raise ValueError("Hash size must be positive") 21 | import scipy.fftpack 22 | img_size = hash_size * highfreq_factor 23 | image = image.convert("L").resize((img_size, img_size), Image.ANTIALIAS) 24 | pixels = numpy.array(image.getdata(), dtype=numpy.float).reshape((img_size, img_size)) 25 | dct = scipy.fftpack.dct(scipy.fftpack.dct(pixels, axis=0), axis=1) 26 | dctlowfreq = dct[:hash_size, :hash_size] 27 | med = numpy.median(dctlowfreq) 28 | diff = dctlowfreq > med 29 | return diff 30 | 31 | 32 | def getImageHashesPrevious(): 33 | # Get Images 34 | filesInDirectory = os.listdir() 35 | imagesInDirectory = [filename for filename in filesInDirectory if filename[-4:] == '.jpg'] 36 | 37 | # Declare Bookkeeping Structure 38 | imageHashDic = {} 39 | imageHashList = [] 40 | 41 | # First Image 42 | imageName = imagesInDirectory[0] 43 | imageFile = Image.open(imagesInDirectory[0]) 44 | hashArray = phash(imageFile,hash_size=16, highfreq_factor=8) 45 | imageHashList.append(hashArray) 46 | imageHashDic[imageName] = hashArray 47 | 48 | # Cycle through every image 49 | for imageName in imagesInDirectory[1:]: 50 | print(imageName) 51 | imageFile = Image.open(imageName) 52 | hashArray = phash(imageFile,hash_size=16, highfreq_factor=8) 53 | duplicate = False 54 | while not duplicate: 55 | for otherFile in imageHashDic.keys(): 56 | otherHash = imageHashDic[otherFile] 57 | diff = (otherHash ^ hashArray).sum() 58 | # Possible Duplicate 59 | if diff < 30: 60 | print(imageName, otherFile, diff) 61 | duplicate = True 62 | # Actual duplicate 63 | if duplicate: 64 | os.remove(imageName) 65 | # Not duplicate 66 | if not duplicate: 67 | imageHashDic[imageName] = hashArray 68 | imageHashList.append(hashArray) 69 | 70 | def checkDuplicate(hashArray, imageHashDic, sensitivity = 30): 71 | ''' ''' 72 | duplicate = False 73 | while not duplicate: 74 | for otherFile in imageHashDic.keys(): 75 | otherHash = imageHashDic[otherFile] 76 | diff = (otherHash ^ hashArray).sum() 77 | # Possible Duplicate 78 | if diff < sensitivity: 79 | duplicate = True 80 | return duplicate 81 | 82 | def getSelfImageHashes(instagram): 83 | ''' returns dictionary of pk:hash of all posts ''' 84 | import requests 85 | from io import BytesIO 86 | selfImageHashDic = {} # Timestamp:HashArray 87 | selfFeedJson = instagram.getTotalSelfUserFeed() 88 | for post in selfFeedJson: 89 | postImageURL = post['image_versions2']['candidates'][-1]['url'] 90 | imageResponse = requests.get(postImageURL) 91 | imageFile = Image.open(BytesIO(imageResponse.content)) 92 | imageHashArray = phash(imageFile, hash_size=16, highfreq_factor=8) 93 | selfImageHashDic[post['pk']] = imageHashArray 94 | return selfImageHashDic 95 | 96 | def checkPrevPosted(selfImageHashDic, post, sensitivity=30): 97 | ''' returns pk of duplicate post ''' 98 | from PIL import Image 99 | import requests 100 | from io import BytesIO 101 | postImageURL = post['image_versions2']['candidates'][-1]['url'] 102 | imageResponse = requests.get(postImageURL) 103 | imageFile = Image.open(BytesIO(imageResponse.content)) 104 | imageHashArray = phash(imageFile, hash_size=16, highfreq_factor=8) 105 | checkedImages = 0 106 | duplicate = False 107 | for otherFile in selfImageHashDic.keys(): 108 | otherHashArray = selfImageHashDic[otherFile] 109 | diff = (otherHashArray ^ imageHashArray).sum() 110 | # Possible Duplicate 111 | if diff < sensitivity: 112 | duplicate = otherFile 113 | checkedImages = checkedImages + 1 114 | print("checkedImages: ", checkedImages) 115 | return duplicate 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /instagramBot.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Instagram Competition Automation 4 | @author: RCCG 5 | 6 | pip install git+https://github.com/LevPasha/Instagram-API-python 7 | 8 | TODOs: 9 | [ ] create log, make timing dependent on log since last action was taken, log account name to allow expansion for multiple accounts later on. 10 | [ ] split on "bonus" "optional" "double/extra chance" etc to find repost required versus bonus 11 | [ ] recognize: pick a number between 12 | 13 | [ ] implement wait list for posts to be confirmed to allow continuous running 14 | [ ] implement recognizing deadlines 15 | [ ] implement cross check for "today" and time < 24 hours 16 | [ ] implement get people tags from previous comments 17 | [ ] count 404 429 errors 18 | [ ] implement check if commented before 19 | [ ] implement in indivudual comments 20 | [ ] implement check own feed instead 21 | [ ] implement new/viewed parameter 22 | [ ] implement unfollow after no contest in X months 23 | [ ] implement after reaching old time 24 | [ ] delete old reposts 25 | [ ] implement post real content: if not posted in a while, post content from a folder, move image to archive 26 | [ ] implement check for repost / tag to count towards contest count 27 | [ ] implement blocked account List 28 | [ ] outlay positive / negative dictionaries 29 | [ ] implement google translate of foreign languages 30 | [ ] differentiate between additional/extra repost vs required 31 | [ ] implement check after 7 days for counting people entered 32 | [ ] implement keeping track of other tags in contests entered/won/etc. 33 | [ ] implement keeping track of failed follow finding 34 | [ ] recogniyze: 'shoe size' 35 | [ ] reduce checks for to 1: for Followers (usernames), Followings (ID) 36 | [ ] maximum number of tags 37 | 38 | Features: 39 | [x] Uses Follow List as 40 | [x] Given Names, searches for relevant users and follows them 41 | [x] Given Tags, adds the Tag feeds to search for contests 42 | [x] Detects contests based on key word values 43 | [x] Detects if contest requires: Like, Comment, Tags, Repost 44 | [x] Allows for Liking, Commenting, Tagging, Reposting 45 | [x] Checks if already entered by checking for like 46 | [x] Prevents double posts via phash check of previously posted 47 | 48 | [x] implement follow users tagged in post 49 | [x] implement get users to follow 50 | [x] breaks loop on liked item (Warning, can create gaps) 51 | [x] implement minimum followers for entering contest 52 | [x] implement own followers retrieval for > 200 53 | [x] implement check if already reposted using picture hash comparison 54 | [x] implement only items newer than X retrieved 55 | [x] implement retrieve more items 56 | [x] implement help function 57 | 58 | [x] After following checks users again to allow for loops 59 | 60 | 61 | Maybe Future: 62 | searching of bios 63 | searching of facebook pages 64 | """ 65 | 66 | 67 | import InstagramAPI_local as insta 68 | #from InstagramAPI import InstagramAPI as insta 69 | import pandas as pd 70 | import random 71 | import datetime 72 | import time 73 | import urllib.request 74 | import itertools 75 | from duplicateDetection import checkPrevPosted, getSelfImageHashes 76 | print("Successfully loaded all modules") 77 | 78 | 79 | # Account Details 80 | accName = "" 81 | accPw = "" 82 | primaryAccount = "" 83 | # Settings 84 | validLanguageList = ["en", "de"] 85 | 86 | # Book keeping Parameters 87 | filenames = ['contests.csv', 'stats.csv'] 88 | testing = 2 # 2=check, 1=no-repost, 0=automated. 89 | slowdownFactor = 5 # time between cycles: 5 min * slowdown Factor 90 | # Run parameters 91 | maxLength = 1000 # Maximum # of characters for display 92 | minScore = 3 # Minimum # of points to enter (contest score) 93 | maxDays = 7 # Maximum # of days old to check for contest 94 | # Prevent Fake Accounts 95 | minFollowers = 400 # Minimum # of followers for search 96 | minFollowing = 10 # Minimum # of followed for search 97 | minPosts = 5 # Minimum # of posts of an account to enter to prevent spam 98 | # Prevent Banning 99 | #maxToLike = 500 100 | # Commenting Parameters 101 | baseComment1 = ['yes', 'perfect', 'awesome', 'fantastic', 'done', 'love it', 'amazing'] #'wish me luck', '#win hopefully', 'youre the best' 102 | baseComment2 = ['!', ' ❤️', ' ', ' !', ' <3'] 103 | baseComments = [word+ending for word,ending in itertools.product(baseComment1, baseComment2)] 104 | minComments = 3 # Minimum # of comments before taking most commented answer 105 | maxTagsCopy = 5 # Maximum # of tags to copy from post 106 | minLength = 80 # Minimum # of characters in alphanumeric words to check 107 | minTimePassed = 72*60 # Minimum # of minutes before checking followers again 108 | #inactiveMonths = 6 # unfollow if not posted in X months 109 | 110 | # New to add 111 | searchUserList = [] # 'Thomas Pink' 112 | searchTagList = ['#verlosung','#giveaway']#, '#freebies', '#nopurchasenecessary', '#chancetowin'] # , 'freegift' 113 | ################################################################# 114 | print("Successfully loaded all parameters") 115 | 116 | # Functions 117 | contestCols = [ # Contest History 118 | 'username', # User Name (str) 119 | 'userPk', # User ID (num) 120 | 'timestamp', # Post Time (num) 121 | 'postId', # Post ID (str) 122 | 'postPk', # Post ??? (num) 123 | 'caption', # Post Description (str) 124 | # Action Taken 125 | 'commented', # WHAT (str) 126 | #'tagCount', # How Many (int) 127 | #'contestScore', # Con. Sore (int) 128 | 'commentNumber', # WHEN (int) 129 | 'commentTimestamp', # Timestamp (int) 130 | 'liked', # Liked? (bool) 131 | 'shared', # Reposted? (bool) 132 | 'shareCaption' # Caption (str) 133 | # Review / Analysis Columns 134 | #'hitScore' # 0=Perfect, 1=Mistake, 2=False Positive, 3=Positive False 135 | #'hitNote' # What went wrong? (str) 136 | #'trueTagCount' # How many (int) 137 | #'trueLiked' # Should have Liked? (bool) 138 | #'trueShared' # Should have Shared? (bool) 139 | #'trueCommented' # Should have commented? (str) 140 | #'trueWon' # Did I win? (bool) 141 | #'totalEntered' # How many entered contest? (int) 142 | ] 143 | statCols = [ # Bot Statistics 144 | 'timestamp', # Timestamp (num) 145 | 'entered', # Contests Entered (num) 146 | 'searched', # Posts Searched (num) 147 | 'newFollows', # Username List (list) 148 | 'searchUserIdList', # Userid List (list) 149 | 'searchHashTagList' # Tags List (list) 150 | ] 151 | 152 | 153 | 154 | def loadSettings(filename, headers): 155 | try: 156 | ret = pd.DataFrame.from_csv(filename, encoding = 'utf-16') 157 | # New Columns Added: Append new, Insert NaN, Sort cols 158 | if len(ret.keys()) < len(headers) and set(headers) != set(ret.keys()): 159 | newCols = list(set(headers)-set(ret.keys())) 160 | import numpy as np 161 | for column in newCols: 162 | ret[column] = np.nan # Append & Insert dummmy value 163 | ret = ret[headers] # Sort again 164 | # Unmatched Columns: Create new, Rename old 165 | elif set(headers) != set(ret.keys()): 166 | ret = pd.DataFrame(columns=headers) 167 | import os; os.rename(filename, filename[:-4]+"_"+datetime.datetime.now().strftime('%Y%m%d_%H%M%S')+".csv") 168 | print("Weird columns. Created new. Renamed Old.") 169 | # All clear. 170 | else: print("Successfully loaded %s" %filename) 171 | # Something went very wrong 172 | except: 173 | ret = pd.DataFrame(columns=headers) 174 | print("Failed loading %s. Created new. Renamed Old." %filename) 175 | import os; os.rename(filename, filename[:-4]+"_"+datetime.datetime.now().strftime('%Y%m%d_%H%M%S')+".csv") 176 | return ret 177 | 178 | def saveSettings(filename, dataframe): 179 | dataframe.to_csv(filename, encoding='utf-16') 180 | 181 | def getBaseComment(baseComments): 182 | return baseComments[random.randint(0,len(baseComments)-1)] 183 | ################################################################# 184 | 185 | # Analyzing Post 186 | def getPostTags(post): 187 | ''' returns primary, and all tags in post ''' 188 | import re 189 | item = post['caption'] 190 | primaryTag = re.findall('(?<=Tag ).\S*', item['text'], flags=re.IGNORECASE) 191 | allTags = re.findall('([#@][\w_+#@]*)', item['text']) 192 | print(primaryTag, allTags) 193 | return primaryTag, allTags 194 | 195 | def removeTags(text): 196 | ''' removes all hash- and people tags ''' 197 | import re 198 | return re.sub(r'\s[#@][\w#.]*','',text) 199 | 200 | def getPlainText(text): 201 | ''' Only alphanumeric words ''' 202 | return " ".join([word for word in text.split(" ") if word.isalnum()==True]) 203 | 204 | def checkMinLength(caption): 205 | wordCaption = getPlainText(caption) 206 | if len(wordCaption) < minLength: 207 | print(" Caption Length: ", len(wordCaption)) 208 | return False 209 | else: return True 210 | 211 | def verifyUser(instagram, userPk, minPosts, minFollower, minFollowing): 212 | ''' verify user meets minimum required: posts, followers, followings ''' 213 | attempt = 1 # Retry to succeed 214 | while not instagram.getUsernameInfo(userPk): time.sleep(60*attempt); attempt+=1 215 | userInfoDic = instagram.LastJson['user'] 216 | postCountOK = (userInfoDic['media_count'] > minPosts) 217 | followerCountOK = (userInfoDic['follower_count'] > minFollower) 218 | followingCountOK = (userInfoDic['following_count'] > minFollowing) 219 | if postCountOK and followerCountOK and followingCountOK: return True 220 | else: return False 221 | 222 | def checkPhoneNumber(item): 223 | ''' search for all major phone number types, prints phone number ''' 224 | import re 225 | if re.search(r'(\d{3}[-\.\s]??\d{3}[-\.\s]??\d{4}|\(\d{3}\)\s*\d{3}[-\.\s]??\d{4}|\d{3}[-\.\s]??\d{4})', item) is not None: 226 | print("phone number recognized", re.search(r'(\d{3}[-\.\s]??\d{3}[-\.\s]??\d{4}|\(\d{3}\)\s*\d{3}[-\.\s]??\d{4}|\d{3}[-\.\s]??\d{4})', item).group(1)) 227 | return True 228 | else: return False 229 | 230 | def checkLanguage(text): 231 | ''' check if item is in the english language, prints if foreign ''' 232 | global validLanguageList 233 | from langdetect import detect 234 | wordCaption = getPlainText(text) # Only alpha numerical words should count 235 | if detect(wordCaption) not in validLanguageList: 236 | print(" foreign: ", detect(wordCaption)); 237 | return False 238 | else: return True 239 | 240 | def translateToEnglish(text): 241 | ''' uses google translate to translate post to english ''' 242 | from langdetect import detect 243 | wordCaption = getPlainText(text) # Only alpha numerical words should count 244 | if detect(wordCaption) != "en": 245 | from googletrans import Translator # pip install googletrans 246 | translator = Translator() 247 | translation = None 248 | while not translation: 249 | try: translation = translator.translate(wordCaption, dest='en').text 250 | except: randomSleepTimer(1,3) 251 | return translation 252 | else: 253 | return text 254 | 255 | def checkForContest(post, minScore): 256 | ''' checks post for contest by keywords below rules below rules on keywords ''' 257 | item = post['caption']['text'].lower() 258 | contestScore = 0 259 | allTags = [] 260 | # Keywords 261 | positives = {'giveaway':2, 'give away':3, 'giving away':3, 'free':2, 'no purchase':4, #'nopurchase':2, 262 | 'contest':1, 'win':1, 'comment':1, 'chance':2, 'like':1, 'tag':1, 'friend':1, 'gift package':2} 263 | negatives = { 264 | 'open house':1, 'raffle':3, 265 | 'snap':1, 'best':1, 'class':1, 'go to':1, #'lucky customer':1, 'your address':1, 'results':1, 266 | 'casino':1, 'stay tuned':3, 267 | # Avoid good causes to spam 268 | 'fundraiser':6, 'fundraising':6, 'donate':1, 'donations':4, 269 | 'raredisease':6, 270 | # useless stuff 271 | '#slime':8, ' slime':8, 'dragon ball':6, 'csgo':6, 'stickers':4, 'pokemon':6, 272 | 'weed':8, # gets you banned 273 | 'photoshoot':4, 'ebook':8, 'e-book':8, 'mommy':6, 'mother':6, 274 | 'service desired':9, 275 | # require to comment 276 | 'tell us':2, 'post a photo':6, 'post a pic':5, 'best photo':6, 'answer this question':6, 277 | 'tell us why':7, 'tell me why':7, 278 | 'screenshot':2, 'your order':3, 'every order':3, 279 | # location based 280 | "we're at":4, '#roadshow':8, 'find us':6, 'come down to':8, 'stop in':1, 'get down here':7, 'come out to':9, 281 | # signups.... 282 | 'sign up':2, 'check it out':2, 'message me':2, 'register':2, 'free event':2, #'winner announced':2, 283 | 'must be new to':12, 'fill out':6, 284 | # Live shows 285 | 'watch us live':10, 'tune in live':10, ' live at ':9, 286 | # Facebook and Websites 287 | 'www.':1, '.com':1, 'http://':3, 'https://':3, '.net':3, 288 | 'facebook':3, 'facebook page':3, 'fb page':3, 'facebook.com':3, 'page on facebook':3, 'like us on fb':6, 289 | 'iphone':6, 'dm or ws':8, 290 | 'twitter':4, 291 | 'email':2, 'e-mail':2, 292 | 'youtube channel':3, 293 | 'bitly':8, 'goo.gl':5, 294 | 'whatsapp':4, 295 | 'twitch':8, 296 | 'instastory':5, 297 | # Link to something 298 | 'link in':5, 'link on my':5, 'click link':4, '#linkinbio':5, 'dm me':2, 299 | # Purchase indicators 300 | 'on orders':2, 'call today':7, 'call now':6, 'with every order':6, 301 | 'coupon':6, 'sale':1, 'purchase':1, 'purchasing':1, 302 | 'place an order':6, 'with the purchase':8, 'with every purchase':6, 303 | 'buy':4, 'forsale':4, 'invoice':4, 'sales event':10, 304 | '% off':5, 'with any purchase':6, 'with every purchase':6, 305 | 'book now':4, 'when you order':6, 306 | # Finished 307 | 'for participating':2, 'thank you for participating':4, 'the winner of':6, 308 | 'better luck':4, 'closed':2, 'now closed':4, 'is our giveaway winner':6, 'have our first winner':8, 309 | 'won this':3, 310 | # Repost Indicators 311 | '#repost':8, '#reposting':8, 'repost @':8, ' via ':8, 'wish me luck':10, 312 | 'pickme':6, 'reposted using':10, 313 | 'visit her ':6 314 | } 315 | dealbreakers = {'spend $':6, 'sales over $':6, 'purchase over':12, 'order over':12, #'$300 required' through regex 316 | 'buy 1 get 1 free':6, 'order now':9, 'order something':9, 317 | # Spam 318 | 'free followers':9, 'famenow':9, 319 | # Ad for giveaway? 320 | 'head over':8, 'head back':7, 'over at @':7, 'over on my blog':5, 321 | 'pop over':10, 322 | 'check out @':4, 'check out the @':4, 323 | 'jump over':10, 'earlier post':4, 324 | # Reminder post? 325 | 'previous post':10, 'last post':10, 'posts back':5, 'post back':5, 'original post':10, 326 | 'see their acc':8, 'already posted':8, 'past post':3, 'swing over':4, 'back few posts':5, 'back a few posts':5, 'few posts':5, 327 | 'original photo':4, 'check out our post':11, 'out latest post':5, 'see my previous':5, 328 | # thank you for received? 329 | 'i received':4, 'thanks to @':4, 'thank you to @':4, 330 | # Repost ? 331 | '@instatoolsapp':6, '@regram.app':6, 'repostapp':6, 'regrann':6, 'regram':6, '@get_repost':6, '@renstapp':6, 332 | 'repost any':6, 333 | 'respost from @':6, 'repost from @':6, 'repost from @':8, 334 | 'repostby @':6, 'Posted @withrepost':6, '@ziapplications':8, 335 | # Closed 336 | 'congratulation':6, 'congrats ':6, 'congrats to':6, 337 | 'winner is @':6, 'winner is... @':6, 'winners are @':6, 'winner was @':6, 'winner @':6, 338 | '1st winner':6, 'won my giveaway':6, 'we have a winner':6, 339 | 'giveaway closed':6 340 | } 341 | # Check for minimum length -> end 342 | if not checkMinLength(item): return contestScore, allTags 343 | # Check if foreign / not in English -> end 344 | if not checkLanguage(item): return contestScore, allTags 345 | # Translate if Foreign 346 | item = translateToEnglish(item) 347 | # Check for phone number -> end 348 | if checkPhoneNumber(item): return contestScore, allTags 349 | # If first letter is a @, its a repost 350 | if item[0] == "@": return contestScore, allTags 351 | # Check user stats (Leads to spam disconnect of server after a few) 352 | # if not verifyUser(instagram, post['user']['pk'], minPosts, minFollowers, minFollowing): 353 | # return contestScore, allTags 354 | # Passed All Tests 355 | try: # Scoring 356 | positiveList = [phrase for phrase in positives.keys() if phrase in item] 357 | contestScore += sum(positives[phrase] for phrase in positives.keys() if phrase in item) 358 | #contest_count -= sum(negatives[phrase] for phrase in negatives.keys() if phrase in item) 359 | if contestScore >= minScore: 360 | negativeList = [phrase for phrase in negatives.keys() if phrase in item] 361 | contestScore -= sum(negatives[phrase] for phrase in negatives.keys() if phrase in item) 362 | contestScore -= sum(10 for phrase in dealbreakers.keys() if phrase in item) 363 | if len(negativeList) > 0: print(" Negatives:", " ", negativeList) 364 | except: print("failed to determine score!") 365 | try: 366 | if contestScore >= minScore: 367 | print(" Contest Score: %i (%s)" %(contestScore, ", ".join(print(positiveList)))); 368 | # Find Tagsent 369 | if (item.find('tag') != -1): # or '#' 370 | primaryTag, allTags = getPostTags(post) 371 | except: print(" Failed: Determining any Tags!") 372 | return contestScore, allTags 373 | 374 | def mostCommonComment(instagram, post, minComments=10, minCount=3): 375 | ''' Comment on Contest to enter: 1) most common answer 2) just yes ''' 376 | commented = baseComment 377 | if 'comments_disabled' in post.keys() and post['comments_disabled'] == True: return commented 378 | if post['comment_count'] > minComments: # Comment the same as most commented 379 | instagram.getMediaComments(post['id']) 380 | commentDf = pd.DataFrame(instagram.LastJson['comments']) 381 | commentDf['text'] = commentDf['text'].str.replace('([@][\w_.@]*)','') # Remove: People Tags 382 | commentDf = commentDf[commentDf['text'].apply(set).apply(len) > 2] # Remove: Whitespace Only Entries 383 | commentDf['text'] = commentDf['text'].str.replace(' ',' ') # Remove: People Tags 384 | decOrder = commentDf['text'].str.lower().value_counts() 385 | print(decOrder) 386 | if len(decOrder) > 0 and decOrder[0] > minCount: 387 | print(" commented: ", decOrder[0], decOrder.index[0]) 388 | mostCommented = decOrder.index[0] 389 | commented = mostCommented 390 | else: commented = baseComment 391 | # TODO: Check if commmented before - using liking as checkmark 392 | return commented 393 | 394 | def searchPost4PeopleTag(post): 395 | ''' returns number of people needed to tag ''' 396 | from re import findall, IGNORECASE 397 | item = post['caption']['text'].lower() 398 | #item = " ".join([word for word in item.split(" ") if word.isalnum()==True]) 399 | # Phrase List 400 | import itertools 401 | tagList = ["tag", "tagging"] 402 | attList = ["at least", "a minimum of", "a minimal", "me and"] 403 | phraseList = tagList + [tag+" "+att for tag,att in itertools.product(tagList,attList)] 404 | # Works if: Integer 405 | for phrase in phraseList: 406 | hitList = findall('(?<='+phrase+' ).[0-9]*', item, flags=IGNORECASE) 407 | hitList = [int(k) for k in hitList if k.isdigit()] 408 | if len(hitList) == 1: return hitList[0] 409 | elif len(hitList) > 1: # This should never happen 410 | print(hitList); 411 | randomSleepTimer(9,10) 412 | return hitList[0] 413 | # Works if: Non-Integer 414 | # comment <-> tag 415 | if 'tag a friend' in item: return 1 416 | elif 'mention a friend' in item: return 1 417 | elif 'tag a mate' in item: return 1 418 | elif 'tag the friend' in item: return 1 419 | elif 'tag one friend' in item: return 1 420 | elif 'tag your bff' in item: return 1 421 | elif 'tag your friend' in item: return 1 422 | elif 'tag someone' in item: return 1 423 | elif 'comment who' in item: return 1 424 | elif 'tag as many' in item: return 1 425 | elif 'tag at least one' in item: return 1 426 | elif 'tag your friends' in item: return 2 427 | elif 'tag two friends' in item: return 2 428 | elif 'tag two people' in item: return 2 429 | elif 'tag two ' in item: return 2 430 | elif 'tag two of your friends' in item: return 2 431 | elif 'tag two or' in item: return 2 432 | elif 'tag some' in item: return 2 433 | elif 'tag lots' in item: return 2 434 | elif 'tag firend' in item: return 2 435 | elif 'tag three friends' in item: return 3 436 | elif 'tag four friends' in item: return 4 437 | elif 'tag five friends' in item: return 5 438 | elif 'get tagging' in item: return 2 439 | else: print(" [ ] No people tags necessary"); return 0 440 | 441 | def addPeopleTags(post, numPeopleToTag, instagram): 442 | ''' returns string of tagged usernames in amount needed ''' 443 | usernameList = usernamesToTagList(numPeopleToTag, instagram) 444 | if numPeopleToTag > 0: print(" [ ] Tagging %s people required" %numPeopleToTag); return "@"+" @".join(usernameList) 445 | else: return "" 446 | 447 | def repost(post, captionText, instagram): 448 | ''' repost picture in post with previous caption attached ''' 449 | global sleepCounter 450 | # check if posted before: Add Name 451 | selfImageHashDic = getSelfImageHashes(instagram) 452 | # TODO: make selfImageHashDic global once at the beginning of the script and add values rather than recalculating 453 | duplicatePk = checkPrevPosted(selfImageHashDic, post, sensitivity=30) 454 | if duplicatePk: 455 | print(" Duplicate - not reposting") 456 | while not instagram.mediaInfo(duplicatePk): sleepCounter += randomSleepTimer(20,60) 457 | if instagram.LastJson['items'][0]['caption'] is None: 458 | checkCaptions(contests) 459 | while not instagram.mediaInfo(duplicatePk): sleepCounter += randomSleepTimer(20,60) 460 | oldCaption = instagram.LastJson['items'][0]['caption']['text'] 461 | if post['user']['username'] not in oldCaption: 462 | newCaption = oldCaption[:oldCaption.find('@')] + '@' + post['user']['username'] + ' ' + oldCaption[oldCaption.find('@'):] 463 | instagram.editMedia(duplicatePk, newCaption) 464 | print(" Added tag @%s" % post['user']['username']) 465 | print(" Already tagged @%s" % post['user']['username']) 466 | return True 467 | else: 468 | # else 469 | url = post['image_versions2']['candidates'][0]['url'] # link of largest 470 | photoName = post['user']['username'] + ".jpg" 471 | try: 472 | urllib.request.urlretrieve(url, photoName) 473 | instagram.uploadPhoto(photoName, caption = captionText, upload_id = None) 474 | print(" [x] Reposted") 475 | # TODO !!! 476 | # Caption seems to be broken. 477 | # Check for caption 478 | # instagram.mediaInfro(mediaId) 479 | # instagram.editMedia(self, mediaId, captionText = '') 480 | return True 481 | except: 482 | print(" Failed: repost") 483 | return False 484 | 485 | def check4Repost(post): 486 | ''' Check if repost of picture required ''' 487 | import itertools 488 | caption = post['caption']['text'].lower() 489 | verbIndicators = ["post", "posts", "repost", "reposts", "share", "shares", "pot", "repot", 490 | "like/share", "like&share", "like &share", "like & share"] 491 | adjIndicators = ["the", "this photo", "this picture", "this pic", "this post", "this image", "this and", "and", "a screenshot", "picture", "image", "&"] 492 | specIndicators = ["shoutout this account", "shout out this account"] 493 | repostIndicators = [verb+" "+adj for verb,adj in itertools.product(verbIndicators, adjIndicators)] + specIndicators 494 | repostIndicators.remove("post and") 495 | repostIndicators.remove("posts and") 496 | repostIndicators.remove("share the") 497 | repostIndicators.remove("post &") 498 | repostIndicators.remove("posts &") 499 | repostIndicators.append("repost n tag") 500 | repostIndicators.append("repost with") # followed by tag often 501 | repostIndicators.append("& share") 502 | for indicator in repostIndicators: 503 | if caption.find(indicator) != -1: 504 | print(" [ ] Repost required") 505 | return True 506 | return False 507 | 508 | def getPeopleTagged(post): 509 | ''' Get all people tags in the post ''' 510 | #TODO: Does not include some characters, e.g. had hit for @by 511 | from re import findall 512 | text = post['caption']['text'].lower() 513 | allFollows = [] 514 | if text.find("follow") != -1: 515 | allFollows = findall('([@][\w@_.]*)', text) 516 | print(" [ ] Follow requried: (%s)" % ", ".join(set(allFollows))) 517 | return allFollows 518 | 519 | def enterContest(instagram, sleepCounter, contests, post, commented, usernameList, caption=""): 520 | ''' Likes, follows, comments, and reposts if required ''' 521 | global searchList 522 | # Follow all tagged users 523 | userIdList = tagsToUserIds(usernameList) 524 | sleepCounter += followUsers(userIdList + [post['user']['pk']]) 525 | if len(userIdList) > 0: searchList = searchList + [str(userId) for userId in userIdList] # Add new follows to current search for loops 526 | # Comment Post 527 | instagram.comment(post['pk'], commented) # Comment 528 | print(" [x] Commented") 529 | # Repost Post 530 | reposted = False 531 | if len(caption) > 1: repost(post, caption, instagram); reposted = True # Repost 532 | # Add to History 533 | newRow = { 534 | # Post Details 535 | 'username': post['user']['username'], 536 | 'userPk': post['user']['pk'], 537 | 'timestamp': post['taken_at'], 538 | 'postPk': post['pk'], 539 | 'postId': post['id'], 540 | 'caption': post['caption']['text'].strip().replace('\n',' ').replace('\n',' '), 541 | # Action Taken 542 | 'commented': commented, 543 | 'commentNumber': post['comment_count'], 544 | 'commentTimestamp': round(time.time()), 545 | 'liked': True, 546 | 'shared': reposted, 547 | 'shareCaption': caption 548 | } 549 | # Like Post 550 | instagram.like(post['pk']) # Like 551 | print(" [x] Liked") 552 | print(" Successfully entered contest!") 553 | sleepCounter += randomSleepTimer(0,3) 554 | return contests.append(newRow, ignore_index=True) # Add Stats 555 | ################################################################### 556 | 557 | # User Searches 558 | def cleanUserSearch(userSearchJson, minFollowers): 559 | ''' Cleans Json of User search, Returns Dataframe ''' 560 | userSearchData = pd.DataFrame(userSearchJson['users']) # To Dataframe 561 | # Remove useless 562 | del userSearchData['byline'] 563 | userSearchData = userSearchData[userSearchData['is_private'] == False] # Remove Private 564 | userSearchData = userSearchData[userSearchData['follower_count'] > minFollowers] # Cutoff follower 565 | # Organize 566 | userSearchData = userSearchData.set_index('username') # Username = Index 567 | return userSearchData 568 | 569 | def findUser(searchUser, minFollowers, returnType='list'): 570 | ''' given name, search, clean, return restults ''' 571 | instagram.searchUsers(searchUser) # Search: for User 572 | userSearchJson = instagram.LastJson # Save: Return Data 573 | if userSearchJson['status'] == 'fail': # Catch: Fail 574 | print("fail ", datetime.datetime.now()) 575 | return 576 | else: 577 | userSearchData = cleanUserSearch(userSearchJson, minFollowers) # Clean Search Data 578 | print("Users found: ", len(userSearchData)) 579 | if returnType=='list': return userSearchData['pk'].tolist() 580 | elif returnType=='dict': return userSearchData['pk'].to_dict() 581 | else: print('return type not recognized, returning List'); return userSearchData['pk'].tolist() 582 | 583 | def getFollowIdList(instagram): 584 | ''' returns list of IDs from people I follow ''' 585 | followingList = instagram.getTotalSelfFollowings() 586 | followingDf = pd.DataFrame(followingList) 587 | try: return followingDf['pk'].tolist() 588 | except: return [] 589 | 590 | def usernamesToTagList(peopleNeeded, instagram): 591 | ''' returns List of n usernames from own followers ''' 592 | instagram.getSelfUserFollowers() 593 | followers = pd.DataFrame(instagram.LastJson['users']) 594 | # Case 1: Not enough followers 595 | if len(followers) < peopleNeeded: 596 | print("not enough followers to tag") 597 | followerNames=["braidsbybreann", "brownhairedbliss", "emmaa.w", "daisy_cullenn", "tara_sagexo", "madisinw", "jenny_filt", "garymannwx", "cam_raee", "tori_auricht", "braids_by_lisa", "mybraidings", "ellaboag", "Jada_autumn", "mimi_floreani", "saramccoy11", "jovanoviclj", "dulce_lo_", "saraawegz", "brunosaltor", "nailp0lish_", "natalieszenas", "mia__chan", "indieconnolly", "zoerelf", "tay.k.18", "fatom_3sk", "dyna_kd", "meryemlaaraich", "maryam_tariq", "lujain_alesawy", "______555suzi__", "xx_anoode", "omneat", "saragrammm", "joslexispam"] 598 | # Case 2: No one Needed 599 | elif peopleNeeded == 0: followerNames = [''] 600 | # Case 3: Enougn followers 601 | else: 602 | followerNames = followers['username'].tolist() 603 | #TODO: Remove spam accounts 604 | try: followerNames.remove(primaryAccount) 605 | except ValueError: pass 606 | followerNames = [name for name in followerNames if not any(["follow" in name, 607 | "golf" in name, "shop" in name, "app" in name, "furniture" in name, 608 | "free" in name, "hack" in name, "store" in name])] 609 | # Attempt to randomize picking 610 | try: firstTagIx = random.randrange(0,len(followerNames)-1-peopleNeeded) 611 | except: firstTagIx = 0 612 | # Tag self first always 613 | followerNames = [primaryAccount] + followerNames[firstTagIx:firstTagIx+peopleNeeded-1] 614 | return followerNames 615 | 616 | def search4NewUsers(searchUserList, sleepCounter): 617 | ''' search for new Users given list ''' 618 | totalFollowingIdList = getFollowIdList(instagram) # User Already Followed 619 | for searchUser in searchUserList: 620 | print("Searching for: ", searchUser) 621 | newUserIds = findUser(searchUser, minFollowers) 622 | for userId in newUserIds: 623 | if userId not in totalFollowingIdList: # check if already followed 624 | instagram.follow(userId) # Follow New Users 625 | sleepCounter += randomSleepTimer(0,2) 626 | print("Successfully finished search") 627 | return sleepCounter 628 | 629 | def tagsToUserIds(tagList): 630 | ''' given a list of userids, gets user tag by search and match ''' 631 | ids = [] 632 | for tag in tagList: 633 | if len(tag)>1: 634 | added = False 635 | instagram.searchUsers(tag) 636 | userList = instagram.LastJson['users'] 637 | for user in userList: 638 | if user['username'].lower() == tag.lower(): 639 | ids.append(user['pk']) 640 | added = True 641 | elif user['username'].lower() == tag[1:].lower(): # if @ doesnt work 642 | ids.append(user['pk']) 643 | added = True 644 | elif user['username'].lower() == ('@'+tag).lower(): # if @ doesnt work 645 | ids.append(user['pk']) 646 | added = True 647 | if added == False: # Not found 648 | try: 649 | ids.append(userList[0]['pk']) # Follow first 650 | print(" Failed: finding %s. Following: %s" % (tag, instagram.LastJson['users'][0]['username'])) 651 | except: 652 | print(" Failed: Cannot recognize ID !!!") 653 | print(ids) 654 | return ids 655 | 656 | def followUsers(userIdList): 657 | ''' follow a list of new users if not already followed ''' 658 | # check if already followed 659 | totalFollowingIdList = getFollowIdList(instagram) 660 | toFollow = set([str(userId) for userId in userIdList if userId not in totalFollowingIdList]) 661 | sleepCounter = 0 662 | for userId in toFollow: 663 | instagram.follow(userId) # Follow 664 | newFollows.append(userId) # Keeps track of new accounts followed 665 | sleepCounter += randomSleepTimer(1,2) # Sleep 666 | # TODO: Already followed not working 667 | print(" [x] Followed: " + ", ".join(toFollow), "- already followed: ", ", ".join([str(item) for item in list(set(userIdList)-set(toFollow))])) 668 | return sleepCounter 669 | 670 | def checkCaptions(contests): 671 | ''' Check and update captions of posts ''' 672 | changed = 0 # Keep track of changes made 673 | selfFeed = instagram.getTotalSelfUserFeed() 674 | postsToCheck = contests[contests['shared'] == True][::-1] 675 | for i in range(min(len(selfFeed), len(postsToCheck))): 676 | # TODO: Implement Image Hash Check 677 | # Case 1: Neither has values -> pass 678 | if selfFeed[i]['caption'] == None and postsToCheck.iloc[i]['shareCaption'] == 'nan': continue 679 | # Case 2: Posted has none -> edit 680 | elif selfFeed[i]['caption'] == None and postsToCheck.iloc[i]['shareCaption'] != 'nan': 681 | print("Changing caption") 682 | # try. except: delete 683 | print(selfFeed[i]['pk']) 684 | try: 685 | print(postsToCheck.iloc[i]['shareCaption']) 686 | if instagram.editMedia(mediaId = selfFeed[i]['pk'], captionText = postsToCheck.iloc[i]['shareCaption']): 687 | changed = changed + 1 688 | else: 689 | print("Could not edit image") 690 | except: 691 | print("Unable to change. Deleting.") 692 | instagram.deleteMedia(selfFeed[i]['pk']) 693 | # Case 3: Has a caption -> pass 694 | else: continue 695 | print("Changed items: ", changed) 696 | ################################################################# 697 | 698 | # Other Functions 699 | def randomSleepTimer(start, stop): 700 | ''' prevent spam / overflow / suspension ''' 701 | count = random.randrange(start, stop)*slowdownFactor 702 | print("sleeping ", "."*count) 703 | time.sleep(count) # Count sheeps 704 | return count # Return 'Count' to track time slept 705 | 706 | def searchWebsite4Account(website, account='instagram.com'): 707 | ''' finds instagram (or other) account linked on website ''' 708 | ''' e.g. print(searchWebsite4Account('https://www.thomaspink.com/', account='instagram.com')) ''' 709 | import re, urllib 710 | checkWebsite = account.encode('ascii') 711 | req = urllib.request.Request(website) # Define Request for website 712 | with urllib.request.urlopen(req) as response: # Open Connection to Website 713 | pageHTML = response.read() # Read website content and save as pageHTML 714 | pattern = b'([^"$"]*'+checkWebsite+b'*[a-zA-Z0-9_+/]*)' # Start ", end ", website, allowed characters following 715 | results = re.findall(pattern, pageHTML) 716 | return results 717 | ################################################################# 718 | def crashSave(contests, stats, prevContests, totalPostCount, newFollows, totalFollowingIdList, searchTagList): 719 | print("initiating exit") 720 | newContests = pd.concat([contests, prevContests]).drop_duplicates(keep=False) 721 | # Save Files 722 | newRow = {'timestamp':cycleStartTime, 'entered':len(newContests), 'searched':totalPostCount, 'newFollows':newFollows, 'searchUserIdList':totalFollowingIdList, 'searchHashTagList':searchTagList} 723 | stats = stats.append(newRow, ignore_index = True) 724 | saveSettings(filenames[0], contests) 725 | saveSettings(filenames[1], stats) 726 | # Restart 727 | with open("instagramBot.py", encoding='utf8') as f: 728 | code = compile(f.read(), "instagramBot.py", 'exec') 729 | exec(code) 730 | 731 | import atexit 732 | ################################################################# 733 | print("Successfully loaded all functions!") 734 | 735 | 736 | try: 737 | # Save Files 738 | newRow = {'timestamp':cycleStartTime, 'entered':len(newContests), 'newFollows':newFollows, 'searchUserIdList':totalFollowingIdList, 'searchHashTagList':searchTagList, 'searched':totalPostCount} 739 | stats = stats.append(newRow, ignore_index = True) 740 | saveSettings(filenames[0], contests) 741 | saveSettings(filenames[1], stats) 742 | print("successfully saved new stats!") 743 | except: 744 | pass 745 | 746 | # Initilializing 747 | contests = loadSettings(filenames[0], contestCols) 748 | contests['shareCaption'] = contests['shareCaption'].apply(str) 749 | stats = loadSettings(filenames[1], statCols) 750 | #selfImageHashDic = load 751 | prevContests = contests 752 | newFollows = [] 753 | # Test Parameters 754 | #if len(stats) == 0: 755 | # newRow = {'timestamp':round(time.time())-(60*60*24*maxDays), 'entered':0, 'newFollows':[], 'searchUserIdList':[], 'searchHashTagList':[]} 756 | # stats = stats.append(newRow, ignore_index = True) 757 | ################################################################# 758 | print("Successfully loaded all test parameters") 759 | print("Last check: %s" % (datetime.datetime.strftime(datetime.datetime.fromtimestamp(stats['timestamp'].max()), '%c'))) 760 | 761 | 762 | # Start up 763 | try: 764 | instagram 765 | print("already logged in") 766 | except: 767 | instagram = insta.InstagramAPI(accName, accPw) 768 | instagram.login() 769 | print ("Successfully logged in") 770 | sleepCounter = 0 771 | 772 | 773 | # Get global parameters 774 | totalSelfFollowers = pd.DataFrame(instagram.getTotalSelfFollowers()) 775 | #totalFollowerIdList = totalSelfFollowers['pk'].tolist() 776 | #totalFollowerUsenameList = totalSelfFollowers['username'].tolist() 777 | 778 | 779 | # If new Usernames given, add them 780 | if len(searchUserList)!=0: sleepCounter += search4NewUsers(searchUserList, sleepCounter) 781 | 782 | # Get things running 783 | cycleStartTime = time.time() 784 | totalPostCount = 0 785 | # Look through Feed 786 | # TODO: Check Own Feed instead 787 | #instagram.getSelfUserFeed(maxid = '', minTimestamp = None) # Doesnt get feed, gets own posts 788 | totalFollowingIdList = [] 789 | searchDict = {} # Keeps track of how many post searched for each 790 | searchList = searchTagList 791 | # Only check users if it has been enough time 792 | if time.time() > stats[stats['searchUserIdList'].apply(lambda c: c!=[])]['timestamp'].max() + (minTimePassed*60): # Check only if enough time has passed 793 | totalFollowingIdList = getFollowIdList(instagram) # Follow = Check User 794 | searchList = searchTagList + totalFollowingIdList 795 | print(datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S'), datetime.datetime.fromtimestamp(stats['timestamp'].max() + (minTimePassed*60)).strftime('%Y-%m-%d %H:%M:%S'), ) 796 | else: print("Too soon to check followed. Next time at:", datetime.datetime.fromtimestamp(stats['timestamp'].max()).strftime('%Y-%m-%d %H:%M:%S')) 797 | 798 | # Start Search 799 | for searchTerm in searchList: # Over User ID's / Personal Key ? 800 | userPostCount = 0 801 | # TODO: only get items newer than X / use the new/unseen feature? 802 | if type(searchTerm) is int: # User ID 803 | instagram.getUserFeed(searchTerm) # searchTerm = userPk feed(userID) # getTotalUserFeed(userPk) 804 | itemFeed = instagram.LastJson['items'] 805 | totPosts = len(itemFeed) 806 | if totPosts != 0: print(itemFeed[0]['user']['username']) 807 | else: print("%s has no posts!" % str(searchTerm)); continue 808 | else: # Hashtag 809 | print(searchTerm) 810 | searchTerm = searchTerm[1:] 811 | lastTimestamp = int(stats['timestamp'].max()) 812 | if instagram.getHashTagFeedSince(searchTerm, lastTimestamp): 813 | #getHashtagFeed(searchTerm): # feed(userID) # getTotalUserFeed(userPk) 814 | itemFeed = instagram.LastJson['items'] 815 | totPosts = len(itemFeed) 816 | else: 817 | print("failed to getHastagFeed") 818 | if instagram.tagFeed(searchTerm): 819 | itemFeed = instagram.LastJson['items'] 820 | totPosts = len(itemFeed) 821 | else: 822 | print("failed to tagFeed") 823 | instagram.searchTags(searchTerm) 824 | itemFeed = instagram.LastJson 825 | if len(itemFeed) < 10: 826 | print("failed to searchTags. ItemFeed:", itemFeed) 827 | continue 828 | # Each Post 829 | for post in itemFeed: 830 | if post['taken_at'] < stats['timestamp'].max(): # device_timestamp < taken_at < current 831 | print(" too old: ", datetime.datetime.fromtimestamp(post['taken_at'] ).strftime('%Y-%m-%d %H:%M:%S')) 832 | print(" cut-off: ", datetime.datetime.fromtimestamp(stats['timestamp'].max()).strftime('%Y-%m-%d %H:%M:%S')) 833 | #print(int(stats['timestamp'].max()-post['device_timestamp']), "s") 834 | break # Only look at new => skip rest 835 | else: 836 | if post['caption'] is None: continue 837 | if post['user']['username'] == accName: continue 838 | contestScore, postTags = checkForContest(post, minScore) 839 | userPostCount += 1 840 | # Check if already entered => Next loop 841 | if post['has_liked']: print(" [x] Contest already entered."); break # Stop when reached old one 842 | if contestScore >= minScore: # if contest detected in 843 | print(" Contest Found: ", datetime.datetime.fromtimestamp(post['taken_at']).strftime('%Y-%m-%d %H:%M:%S')) 844 | # Print hit (username uncoded or hashtag) 845 | if type(searchTerm) is int: print(" ", post['user']['username']) 846 | else: print(" ", searchTerm) 847 | # Print User 848 | print(" User: %s (%s)" %(post['user']['full_name'], post['user']['username'])) 849 | # Comment = Most Common > minComments | else 'yes ❤️' 850 | baseComment = getBaseComment(baseComments) 851 | commented = mostCommonComment(instagram, post, minComments) 852 | # Create Caption 853 | caption = "" 854 | if check4Repost(post): 855 | caption = commented + " " + " ".join(postTags) + " @" + post['user']['username'] + "\n.......\n" + post['caption']['text'] 856 | # Add People Tags | 'yes @friend1 @friend2' 857 | numPeopleToTag = searchPost4PeopleTag(post) 858 | commented = commented + " " + addPeopleTags(post, numPeopleToTag, instagram) 859 | # Add Hashtags | 'yes @friend1 @friend2 #cool #love' 860 | commented = commented + " " + " ".join(postTags[:maxTagsCopy-1]) 861 | # Get People Follows 862 | usernameList = getPeopleTagged(post) 863 | # Double Check Manually for testing 864 | print(" TEXT: ", post['caption']['text'][:maxLength].strip().replace('\n',' ')) 865 | print(" Comment: ", commented) 866 | #print(" Caption: ", caption) 867 | # Just do it 868 | if commented.find(baseComment) == -1 and contestScore > 5: 869 | contests = enterContest(instagram, sleepCounter, contests, post, commented, usernameList, caption) 870 | elif testing < 2: 871 | if caption == "" and numPeopleToTag > 0: contests = enterContest(instagram, sleepCounter, contests, post, commented, usernameList, caption) 872 | elif testing == 0 and numPeopleToTag > 0: contests = enterContest(instagram, sleepCounter, contests, post, commented, usernameList, caption) 873 | else: continue 874 | else: # Confirm Manually 875 | done = False 876 | while not done: 877 | confirm = input("Confirm [Y/N] or write new Comment: ") 878 | if confirm.lower() == 'y': 879 | # Comment, Like, Repost, Follow 880 | contests = enterContest(instagram, sleepCounter, contests, post, commented, usernameList, caption) 881 | done = True 882 | elif confirm.lower() == 'n': print(" Aborted."); done = True 883 | elif confirm.lower() == 'cancel': import sys; sys.exit("cancelled") 884 | elif confirm.lower() == 'finish': saveSettings(filenames[0], contests); saveSettings(filenames[1], stats); print("Successfully saved new stats"); import sys; sys.exit("cancelled") 885 | elif confirm.lower() == 'ignore': instagram.like(post['pk']); print("liked. ignored in future."); done = True 886 | elif confirm.lower() == 'unfollow': instagram.unfollow(post['user']['pk']); print("unfollowed."); done=True 887 | elif confirm.lower() == 'break': break 888 | elif confirm.lower() == 'comments': mostCommonComment(instagram, post, minComments=1, minCount=1) 889 | elif confirm.lower() == 'help': print("cancel, finish, ignore, unfollow, break, comment") 890 | else: # not approved 891 | commented = confirm 892 | print(" Comment: ", commented) 893 | # Post complete 894 | # Post too old 895 | print(" Searched: ", userPostCount, "/", totPosts) 896 | # User Feed Complete 897 | searchDict[searchTerm] = userPostCount 898 | totalPostCount +=userPostCount # update overall count 899 | print(" Posts searched for: User", userPostCount, " Total", totalPostCount) 900 | sleepCounter += randomSleepTimer(10,20) 901 | # TODO: Save after X posts searched, or thousands might get lost 902 | # THIS CREATED A LOOP AT THE END OF THE FIRST WHILE 903 | #atexit.unregister(crashSave) 904 | #atexit.register(crashSave(contests, stats, prevContests, totalPostCount, newFollows, totalFollowingIdLists, searchTagList)) 905 | 906 | 907 | # Check captions for missing text -> working again 908 | #checkCaptions(contests) 909 | # All done. 910 | cycleFinishTime = time.time() 911 | # Wrap up 912 | newContests = pd.concat([contests, prevContests]).drop_duplicates(keep=False) 913 | if len(newContests)!=0: 914 | print(newContests[['username', 'liked', 'shared']]) 915 | # Save Files 916 | newRow = {'timestamp':cycleStartTime, 'entered':len(newContests), 'newFollows':newFollows, 'searchUserIdList':totalFollowingIdList, 'searchHashTagList':searchTagList, 'searched':totalPostCount} 917 | stats = stats.append(newRow, ignore_index = True) 918 | saveSettings(filenames[0], contests) 919 | saveSettings(filenames[1], stats) 920 | print("successfully saved new stats!") 921 | 922 | # Show Statistics 923 | print() 924 | print("Successfully finished cycle.") 925 | print("-----------------------------------") 926 | #print("".join(["{0:25}: {1:>4} \n".format(str(searchTerm), str(searchDict[searchTerm])) for searchTerm in searchDict.keys()]), end="") 927 | #print("{0:25}k: {1:>4}".format("total searched", totalPostCount)) 928 | print("----------- Cycle Stats -----------") 929 | print("Cycle Time: {:10,.2f} sec ".format((cycleFinishTime - cycleStartTime)) ) 930 | print("Slept For: {:10,.2f} sec ".format( sleepCounter)) 931 | print("Seconds/post: {:10,.2f} sec/post ".format((cycleFinishTime-cycleStartTime-sleepCounter)/totalPostCount)) 932 | print("Posts searched: {:10,.0f} posts ".format(totalPostCount)) 933 | print("---------- Contest Stats ----------") 934 | print("Contests entered:{:9,.0f} contests ".format(len(newContests))) 935 | print("Newly followed: {:10,.0f} users ".format(len(newFollows))) 936 | print("Shared Posts: {:10,.0f} posts ".format(len(newContests[newContests['shared'] == True]))) 937 | print("---------- Account Stats ----------") 938 | instagram.getUsernameInfo(instagram.username_id) 939 | userInfoDic = instagram.LastJson['user'] 940 | print("Following: {:10,.0f} users ".format(userInfoDic['following_count'])) #len(totalFollowingIdList)+len(newFollows))) 941 | print("Followers: {:10,.0f} users ".format(userInfoDic['follower_count'])) #len(instagram.getTotalSelfFollowers()))) 942 | print("Posts: {:10,.0f} posts ".format(userInfoDic['media_count'])) #len(instagram.getTotalSelfUserFeed()))) 943 | print("-----------------------------------") 944 | print("Lifetime Score: {:10,.0f} contests ".format(len(contests))) 945 | print("Lifetime Search:{:10,.0f} posts ".format(stats['searched'].sum()+totalPostCount)) 946 | print("Lifetime Cycles:{:10,.0f} cycles ".format(len(stats))) 947 | print("First Run: {:%d %b %Y} ".format(datetime.datetime.fromtimestamp(stats['timestamp'][0]))) 948 | print() 949 | 950 | 951 | 952 | 953 | # TODO: Accelerate checking of very active tags 954 | #randomSleepTimer(240,360) 955 | #with open("instagramBot.py", encoding='utf8') as f: 956 | # code = compile(f.read(), "instagramBot.py", 'exec') 957 | # exec(code) 958 | --------------------------------------------------------------------------------