├── .gitignore ├── BotTwitter ├── bypass_antibot.py ├── helper.py ├── manage_follow.py ├── manage_rss.py └── retweet_giveaway.py ├── Dockerfile ├── LICENSE ├── README.md ├── configuration.yml.dist ├── logs └── .gitkeep ├── main.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | __pycache__/ 3 | 4 | configuration.yml 5 | data.db 6 | *.log -------------------------------------------------------------------------------- /BotTwitter/bypass_antibot.py: -------------------------------------------------------------------------------- 1 | # Standard libraries 2 | import logging 3 | import random 4 | import time 5 | 6 | # Third Party Libraries 7 | import tweepy 8 | import feedparser 9 | 10 | # Local libraries 11 | from BotTwitter.manage_rss import ManageRss 12 | 13 | 14 | class BypassAntiBot: 15 | def __init__(self, api, flux_rss): 16 | """ 17 | BypassAntiBot constructor containing anti bot avoidance logic 18 | 19 | :param api tweepy.API: api object for current tweepy user 20 | :param flux_rss list: List of rss feeds 21 | """ 22 | self.api = api 23 | self.flux_rss = flux_rss 24 | self.managerss = ManageRss() 25 | 26 | def bypass(self): 27 | """ 28 | Randomly Retweet 5 tweets & Randomly Tweet 10 things. 29 | """ 30 | try: 31 | logging.info("Bypass anti-bot protections ...") 32 | self.randomretweet() 33 | self.rss_and_tweet() 34 | logging.info("Anti-bot bypass completed !") 35 | 36 | except tweepy.TweepyException as e: 37 | if e.api_codes == 326: 38 | pass 39 | 40 | def random_retweet_calculation(self): 41 | """ 42 | Check for number of ReTweet Follow needed to bypass 43 | """ 44 | follow_count, retweet_count = 0, 0 45 | for tweet in self.api.user_timeline(count=200, tweet_mode="extended"): 46 | if tweet.retweeted: 47 | retweet_count += 1 48 | if "FOLLOW" in tweet.full_text.upper(): 49 | follow_count += 1 50 | percent_RtFol = (follow_count * 100) / retweet_count 51 | if percent_RtFol > 25: 52 | randomrt = (follow_count * 4) - retweet_count 53 | logging.info("They are " + str(round(percent_RtFol, 2)) + " % of RT related to giveaway, we will do " + str( 54 | randomrt) + " RT random") 55 | if randomrt > 15: 56 | randomrt = 15 57 | logging.info("We will only do " + str(randomrt) + " retweets for the moment.") 58 | else: 59 | logging.info("There is " + str(round(percent_RtFol, 2)) + " % retweets for the giveaways, we move on to the next step.") 60 | randomrt = 0 61 | return randomrt 62 | 63 | def random_tweet_calculation(self): 64 | """ 65 | Check for number of tweet needed to bypass 66 | """ 67 | retweet_count = 0 68 | for tweet in self.api.user_timeline(count=200, tweet_mode="extended"): 69 | if tweet.retweeted: 70 | retweet_count += 1 71 | if retweet_count > 100: 72 | randomtweet = retweet_count - 100 73 | logging.info("They are " + str(retweet_count) + " retweets for 200 tweets, we need " + str( 74 | randomtweet) + "tweets random") 75 | if randomtweet > 15: 76 | randomtweet = 15 77 | logging.info("We will only do " + str(randomtweet) + " tweets for the moment") 78 | else: 79 | logging.info("There are less than 50% ReTweets on the account we move on to the next step.") 80 | randomtweet = 0 81 | return randomtweet 82 | 83 | def randomretweet(self): 84 | """ 85 | Randomly select trending tweets and Retweet them. 86 | """ 87 | nbtweet = self.random_retweet_calculation() 88 | logging.info("Random retweet started, %s remaining", 89 | str(nbtweet)) 90 | time.sleep(5) 91 | if nbtweet > 0: 92 | # Get tweet at Trending place 610264, 93 | trend_api_response = self.api.get_place_trends(610264) 94 | # Pick Names of Trending Topics 95 | trends = list([trend['name'] for trend in trend_api_response[0]['trends']]) 96 | # Randomly Select Tweet number in trending topic 97 | random_count = random.randrange(0, len(trends)) 98 | for tweet in tweepy.Cursor(self.api.search_tweets, 99 | q=trends[random_count], 100 | result_type="recent", 101 | include_entities="True", 102 | lang="fr").items(nbtweet): 103 | try: 104 | tweet.retweet() 105 | next_retweet_sleep_count = random.randrange(10, 20) 106 | nbtweet -= 1 107 | logging.info("Random retweet done, %s remaining, sleeping for %ss...", 108 | str(nbtweet), 109 | str(next_retweet_sleep_count)) 110 | time.sleep(next_retweet_sleep_count) # Randomly stop activity for 10-20 seconds 111 | except tweepy.TweepyException as e: 112 | if e.api_codes == 185: 113 | break 114 | elif (e.api_codes == 327) or (e.api_codes == 326): 115 | pass 116 | else: 117 | logging.error(e.api_messages) 118 | except StopIteration: 119 | break 120 | 121 | def rss_and_tweet(self): 122 | """ 123 | Select articles on rss feeds and tweet them. 124 | """ 125 | nbtweet = self.random_tweet_calculation() 126 | feeds = [] # list of feed objects 127 | for url in self.flux_rss: 128 | try: 129 | feeds.append(feedparser.parse(url)) 130 | except Exception as e: 131 | logging.error("Error while appending %s feed to feeds' list.", url) 132 | 133 | random.shuffle(feeds) 134 | 135 | for feed in feeds: 136 | for post in feed.entries: 137 | if nbtweet > 0: 138 | if self.managerss.link_exist(post.link): 139 | pass 140 | else: 141 | try: 142 | mode_message = random.randrange(1, 3) 143 | if mode_message == 1: 144 | message = post.title 145 | else: 146 | message = post.title + " " + post.link 147 | self.managerss.add_link(post.link) 148 | self.api.update_status(message) 149 | next_tweet_sleep_count = random.randrange(10, 20) 150 | nbtweet -= 1 151 | logging.info("Random tweet done, %s remaining, sleeping for %ss...", 152 | str(nbtweet), 153 | str(next_tweet_sleep_count)) 154 | time.sleep(next_tweet_sleep_count) 155 | except tweepy.TweepyException as e: 156 | if e.api_codes == 185: 157 | break 158 | elif (e.api_codes == 187) or (e.api_codes == 327) or (e.api_codes == 186) or ( 159 | e.api_codes == 326): 160 | break 161 | else: 162 | logging.error(e.api_messages) 163 | -------------------------------------------------------------------------------- /BotTwitter/helper.py: -------------------------------------------------------------------------------- 1 | # Standard libraries 2 | import sys 3 | import logging 4 | 5 | # third party libraries 6 | import tweepy 7 | 8 | class Helper: 9 | def __init__(self): 10 | """ 11 | HelperFunctions class constructor 12 | """ 13 | 14 | def ask_to_exit(self): 15 | print('''[1] Next account | [2] Exit ''') 16 | user_input = input("Your choice (by default 2): ") 17 | 18 | # Continue 19 | if user_input == "1": 20 | pass 21 | # Exit 22 | else: 23 | sys.exit() 24 | pass 25 | 26 | def get_user(self, api_key, api_secret, access_token, access_secret): 27 | # Authenticate Key to use Twitter API 28 | auth = tweepy.OAuthHandler(api_key, api_secret) 29 | auth.set_access_token(access_token, access_secret) 30 | api = tweepy.API(auth) 31 | 32 | # Get Twitter User object and Create Table for each user 33 | user = api.verify_credentials() 34 | return api, user 35 | 36 | def logging_configuration(self, logging_level): 37 | logging.basicConfig(filename='logs/bot_twitter.log', 38 | level=logging.INFO, 39 | format='%(asctime)s - %(levelname)s - %(message)s') 40 | 41 | root_logger = logging.getLogger() 42 | root_logger.setLevel(logging_level) 43 | 44 | handler = logging.StreamHandler(sys.stdout) 45 | handler.setLevel(logging.INFO) 46 | formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') 47 | handler.setFormatter(formatter) 48 | root_logger.addHandler(handler) -------------------------------------------------------------------------------- /BotTwitter/manage_follow.py: -------------------------------------------------------------------------------- 1 | # Standard libraries 2 | import datetime 3 | import logging 4 | import sqlite3 5 | 6 | 7 | class ManageFollow: 8 | def __init__(self, user, api): 9 | """ 10 | ManageFollow object keeps track of user and tweets in an sqlite table 11 | 12 | :param user tweepy.API.verify_credentials() : Current user object for tweepy bot 13 | :param api tweepy.API: api object from tweepy library 14 | """ 15 | self.user = user 16 | self.api = api 17 | 18 | def update_table(self, follower): 19 | """ 20 | Add Follower entry to User table. 21 | 22 | :param follower string: Follower Name/ID to be recorded in database. 23 | """ 24 | connection = sqlite3.connect('data.db') 25 | c = connection.cursor() 26 | c.execute('''SELECT * FROM {tab} WHERE compte = ?;'''.format(tab=self.user.screen_name), (str(follower),)) 27 | data = c.fetchall() 28 | 29 | # If this id exist or not 30 | if len(data) == 0: 31 | c.execute('''INSERT INTO {tab}(compte,date) VALUES (:compte, :date);'''.format( 32 | tab=self.user.screen_name), (follower, datetime.datetime.now())) 33 | else: 34 | c.execute('''UPDATE {tab} SET date = ? WHERE compte = ?'''.format( 35 | tab=self.user.screen_name), (datetime.datetime.now(), str(follower),)) 36 | c.close() 37 | connection.commit() 38 | 39 | def unfollow(self): 40 | """ 41 | Remove past users with follow date > 2 months. 42 | 43 | """ 44 | logging.info("We check if there are accounts to unfollow ...") 45 | connection = sqlite3.connect('data.db') 46 | c = connection.cursor() 47 | c.execute("""SELECT * FROM {tab};""".format(tab=self.user.screen_name)) 48 | data = c.fetchall() 49 | unfollow_count = 0 50 | for i in data: 51 | try: 52 | date = datetime.datetime.strptime(i[2], "%Y-%m-%d %H:%M:%S.%f") 53 | 54 | # Add 2 Months into Next Year 55 | newyear = date.year + 1 if date.month > 10 else date.year 56 | newmonth = (date.month + 2) % 12 57 | date = date.replace(month=newmonth, year=newyear) 58 | 59 | if datetime.datetime.now() > date: 60 | c.execute('''DELETE FROM {tab} WHERE compte = ?;'''.format(tab=self.user.screen_name), (str(i[1]),)) 61 | self.api.destroy_friendship(i[1]) 62 | unfollow_count += 1 63 | 64 | except Exception: 65 | pass 66 | 67 | logging.info("Unfollow accounts : %s", str(unfollow_count)) 68 | c.close() 69 | connection.commit() 70 | 71 | 72 | def create_tables_follow(user): 73 | """ 74 | Create new tables for each user. 75 | 76 | :param user string: Name of new user to create. 77 | """ 78 | connection = sqlite3.connect('data.db') 79 | c = connection.cursor() 80 | c.execute('''CREATE TABLE IF NOT EXISTS {tab} 81 | (id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, compte text, date DATE);'''.format(tab=user.screen_name)) 82 | c.close() 83 | connection.commit() 84 | -------------------------------------------------------------------------------- /BotTwitter/manage_rss.py: -------------------------------------------------------------------------------- 1 | # Standard libraries 2 | import sqlite3 3 | 4 | 5 | class ManageRss: 6 | def __init__(self): 7 | """ 8 | GestionRss constructor 9 | """ 10 | 11 | def add_link(self, link): 12 | """ 13 | Add a link to the database 14 | 15 | :param link String: link of an rss article 16 | """ 17 | connection = sqlite3.connect('data.db') 18 | c = connection.cursor() 19 | c.execute('''INSERT INTO rss_links (link) VALUES (:link);''', (link,)) 20 | c.close() 21 | connection.commit() 22 | 23 | def link_exist(self, link): 24 | """ 25 | Returns true if the link exists in the database. 26 | 27 | :param link String: Link of an rss article 28 | """ 29 | connection = sqlite3.connect('data.db') 30 | c = connection.cursor() 31 | c.execute('''SELECT * FROM rss_links WHERE link = ?;''', (link,)) 32 | data = c.fetchall() 33 | # If this link exist or not 34 | if len(data) == 0: 35 | return False 36 | else: 37 | return True 38 | 39 | 40 | def create_table_rss(): 41 | """ 42 | Create new tables to save the rss links. 43 | 44 | """ 45 | connection = sqlite3.connect('data.db') 46 | c = connection.cursor() 47 | c.execute('''CREATE TABLE IF NOT EXISTS rss_links 48 | (id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, link text);''') 49 | c.close() 50 | connection.commit() 51 | -------------------------------------------------------------------------------- /BotTwitter/retweet_giveaway.py: -------------------------------------------------------------------------------- 1 | # Standard libraries 2 | import logging 3 | import random 4 | import re 5 | import time 6 | from datetime import datetime, timedelta 7 | 8 | # third party libraries 9 | import tweepy 10 | 11 | 12 | class RetweetGiveaway: 13 | def __init__(self, api, user): 14 | """ 15 | RetweetGiveaway class constructor, requires api object and user object 16 | 17 | :param api tweepy.API: api object from tweepy library 18 | :param user tweepy.API.verify_credentials() : User object for current bot 19 | """ 20 | self.user = user 21 | self.api = api 22 | self.bot_action = [] 23 | 24 | def check_retweet(self, words_to_search, accounts_to_blacklist, hashtag_to_blacklist, giveaway_to_blacklist, 25 | comment_with_hashtag, max_giveaway, nb_days_rollback): 26 | """ 27 | Check for useful tweets by filtering out blacklisted 28 | 29 | :param words_to_search list: List of Keywords to Search tweet for 30 | :param accounts_to_blacklist list: List of Blacklisted Accounts to Ignore 31 | :param hashtag_to_blacklist list: List of Blacklisted Hashtags in tweets to ignore 32 | :param giveaway_to_blacklist list: List of Blacklisted Giveaways to Ignore 33 | :param comment_with_hashtag boolean: If we comment with hashtag 34 | :param max_giveaway integer: Maximum number of giveaway retrieve for each word 35 | :param nb_days_rollback integer: Number of day back 36 | """ 37 | action = [] 38 | regex_detect_tag = [r"\b(\w*INVIT(E|É)\w*)\b", 39 | r"\b(\w*IDENTIFI(E|É)\w*)\b", 40 | r"\b(\w*TAG\w*)\b", 41 | r"\b(\w*MENTIONN(E|É)\w*)\b"] 42 | regex_detect_tag = re.compile('|'.join(regex_detect_tag), re.IGNORECASE) 43 | 44 | for word in words_to_search: 45 | 46 | logging.info("Searching giveaway with the word : %s", word) 47 | for tweet in tweepy.Cursor(self.api.search_tweets, 48 | q=word, until=(datetime.now() - timedelta(nb_days_rollback)).strftime('%Y-%m-%d'), 49 | lang="fr", tweet_mode="extended").items(max_giveaway): 50 | 51 | if tweet.retweet_count > 5: 52 | is_in_blacklist = [ele for ele in giveaway_to_blacklist if (ele in tweet.full_text.upper())] 53 | if is_in_blacklist: 54 | pass 55 | else: 56 | # Check if it's a retweet 57 | if hasattr(tweet, 'retweeted_status'): 58 | screen_name = tweet.retweeted_status.author.screen_name 59 | entities = tweet.retweeted_status.entities 60 | full_text = tweet.retweeted_status.full_text 61 | extra = 0 62 | else: 63 | screen_name = tweet.user.screen_name 64 | entities = tweet.entities 65 | full_text = tweet.full_text 66 | extra = 3 67 | 68 | # Check if Tweet Author is blacklisted or not 69 | if screen_name not in accounts_to_blacklist: 70 | 71 | # Check for INVITE/TAG/MENTIONNE in retweet text 72 | if re.search(regex_detect_tag, full_text): 73 | 74 | # Check if tweet has Hashtags 75 | if len(entities['hashtags']) > 0: 76 | # if comment with hashtag is enabled 77 | if comment_with_hashtag: 78 | # Clean Hastags 79 | h_list = self.manage_hashtag(entities['hashtags'], 80 | hashtag_to_blacklist) 81 | # If we find Hashtags -> Record the tweet 82 | if h_list: 83 | action.append(tweet) 84 | action.append(1 + extra) 85 | self.bot_action.append(action) 86 | else: 87 | action.append(tweet) 88 | action.append(2 + extra) 89 | self.bot_action.append(action) 90 | else: 91 | action.append(tweet) 92 | action.append(2 + extra) 93 | self.bot_action.append(action) 94 | # Else Select Action 2 95 | else: 96 | action.append(tweet) 97 | action.append(2 + extra) 98 | self.bot_action.append(action) 99 | 100 | # If regex-tags not found, record the tweet without action number 101 | else: 102 | action.append(tweet) 103 | self.bot_action.append(action) 104 | 105 | action = [] 106 | 107 | return self.bot_action 108 | 109 | def manage_giveaway(self, list_giveaway, sentence_for_tag, list_name, hashtag_to_blacklist, managefollow, 110 | like_giveaway, nb_account_to_tag): 111 | """ 112 | Handle Give away tweets by following/commenting/tagging depending on the giveaway levels 113 | 114 | :param list_giveaway list: List of Giveaways tweets and (optional) Giveaway levels 115 | :param sentence_for_tag list: List of Random Sentences to use for commenting 116 | :param list_name list: List of Names to Randomly Tag on giveaways 117 | :param hashtag_to_blacklist list: List of hastags to blacklist 118 | :param managefollow managefollow: Database management object from ManageFollow 119 | :param like_giveaway boolean: If we like giveaway 120 | """ 121 | for giveaway in list_giveaway: 122 | tweet = giveaway[0] 123 | 124 | try: 125 | if hasattr(tweet, 'retweeted_status'): 126 | retweeted = tweet.retweeted_status.retweeted 127 | id_ = tweet.retweeted_status.id 128 | author_id = tweet.retweeted_status.author.id 129 | entities = tweet.retweeted_status.entities 130 | screen_name = tweet.retweeted_status.user.screen_name 131 | else: 132 | retweeted = tweet.retweeted 133 | id_ = tweet.id 134 | author_id = tweet.user.id 135 | entities = tweet.entities 136 | screen_name = tweet.user.screen_name 137 | 138 | if not retweeted: 139 | self.api.retweet(id_) 140 | if like_giveaway: 141 | self.api.create_favorite(id_) 142 | 143 | self.api.create_friendship(user_id=author_id) 144 | 145 | if len(giveaway) == 2: 146 | comment_level = giveaway[1] 147 | self.comment(tweet, sentence_for_tag, comment_level, list_name, hashtag_to_blacklist, 148 | nb_account_to_tag) 149 | 150 | managefollow.update_table(author_id) 151 | 152 | if len(entities['user_mentions']) > 0: 153 | for mention in entities['user_mentions']: 154 | self.api.create_friendship(user_id=mention['id']) 155 | managefollow.update_table(mention['id']) 156 | 157 | random_sleep_time = random.randrange(10, 20) 158 | logging.info("You participated in the giveaway of : @%s. Sleeping for %ss...", 159 | screen_name, 160 | str(random_sleep_time)) 161 | 162 | time.sleep(random_sleep_time) 163 | 164 | except tweepy.TweepyException as e: 165 | if e.api_codes == 327: 166 | pass 167 | elif e.api_codes == 161: 168 | logging.warning("The account can no longer follow. We go to the next step.") 169 | break 170 | elif e.api_codes == 136: 171 | logging.info("You have been blocked by: %s", screen_name) 172 | break 173 | elif e.api_codes == 326: 174 | logging.warning("You have to do a captcha on the account: %s", self.user.screen_name) 175 | break 176 | else: 177 | logging.error(e.api_messages) 178 | 179 | def comment(self, tweet, sentence_for_tag, hashtag, list_name, hashtag_to_blacklist, nb_account_to_tag): 180 | """ 181 | Add Comment to a given tweet using some rules. 182 | 183 | :param tweet tweepy.tweet: Tweet object from tweepy library 184 | :param sentence_for_tag list: List of random sentences 185 | :param hashtag list: List of Hashtags 186 | :param list_name list: List of user names 187 | :param hashtag_to_blacklist list: List of Blacklisted Hastags to avoid 188 | """ 189 | random.shuffle(list_name) 190 | nbrandom = random.randrange(0, len(sentence_for_tag)) 191 | randomsentence = sentence_for_tag[nbrandom] 192 | 193 | # Random Sentence + Tag Comment + Hashtag Comment + Update Status 194 | if hashtag == 1: 195 | comment = "@" + tweet.retweeted_status.author.screen_name + " " + randomsentence + " " 196 | comment = self.add_tag_comment(list_name, comment, nb_account_to_tag) 197 | comment = self.add_hashtag_comment(comment, tweet.retweeted_status.entities['hashtags'], 198 | hashtag_to_blacklist) 199 | self.api.update_status(comment, in_reply_to_status_id=tweet.retweeted_status.id) 200 | 201 | # Random Sentence + Tag Comment + Update Status 202 | elif hashtag == 2: 203 | comment = "@" + tweet.retweeted_status.author.screen_name + " " + randomsentence + " " 204 | comment = self.add_tag_comment(list_name, comment, nb_account_to_tag) 205 | self.api.update_status(comment, in_reply_to_status_id=tweet.retweeted_status.id) 206 | 207 | # Hashtag Comment + Update Status 208 | elif hashtag == 3: 209 | comment = "@" + tweet.retweeted_status.author.screen_name + " " 210 | comment = self.add_hashtag_comment(comment, tweet.retweeted_status.entities['hashtags'], 211 | hashtag_to_blacklist) 212 | self.api.update_status(comment, in_reply_to_status_id=tweet.retweeted_status.id) 213 | 214 | # User - Random Sentence + Tag Comment + Hashtag Comment + Update Status 215 | elif hashtag == 4: 216 | comment = "@" + tweet.user.screen_name + " " + randomsentence + " " 217 | comment = self.add_tag_comment(list_name, comment, nb_account_to_tag) 218 | comment = self.add_hashtag_comment(comment, tweet.entities['hashtags'], 219 | hashtag_to_blacklist) 220 | self.api.update_status(comment, in_reply_to_status_id=tweet.id) 221 | 222 | # User - Random Sentence + Tag Comment + Update Status 223 | elif hashtag == 5: 224 | comment = "@" + tweet.user.screen_name + " " + randomsentence + " " 225 | comment = self.add_tag_comment(list_name, comment, nb_account_to_tag) 226 | self.api.update_status(comment, in_reply_to_status_id=tweet.id) 227 | 228 | # User - Hashtag Comment + Update Status 229 | elif hashtag == 6: 230 | comment = "@" + tweet.user.screen_name + " " 231 | comment = self.add_hashtag_comment(comment, tweet.entities['hashtags'], 232 | hashtag_to_blacklist) 233 | self.api.update_status(comment, in_reply_to_status_id=tweet.id) 234 | 235 | def manage_hashtag(self, hashtag_list, hashtag_to_blacklist): 236 | """ 237 | Filter Blacklisted Hastags 238 | 239 | :param hashtag_list list: List of Hashtags from Tweet 240 | :param hashtag_to_blacklist list: List of BlackListed Hashtags 241 | """ 242 | h_list = [] 243 | for h in hashtag_list: 244 | h_list.append(h['text'].upper()) 245 | 246 | return list(set(h_list) - set(hashtag_to_blacklist)) 247 | 248 | def add_tag_comment(self, list_name, comment, nb_account_to_tag): 249 | """ 250 | Tag other users in comment. 251 | 252 | :param list_name list: List of user names to add to comment 253 | :param comment string: Tweet/text/Comment 254 | """ 255 | nbusernotif = 0 256 | for username in list_name: 257 | if nbusernotif < nb_account_to_tag: 258 | # We don't want to tag ourselves 259 | if username == "@" + self.user.screen_name: 260 | pass 261 | else: 262 | comment = comment + username + " " 263 | nbusernotif += 1 264 | return comment 265 | 266 | def add_hashtag_comment(self, comment, hashtag_list, hashtag_to_blacklist): 267 | """ 268 | Add hashtag in Comments 269 | 270 | :param comment string: Comment to which to add hashtags 271 | :param hashtag_list list: List of Hashtags 272 | :param hashtag_to_blacklist list: List of Blacklisted Hashtags to avoid 273 | """ 274 | h_list = self.manage_hashtag(hashtag_list, hashtag_to_blacklist) 275 | for hashtag in h_list: 276 | comment = comment + "#" + hashtag + " " 277 | return comment 278 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8.7 2 | Maintainer j4rj4r 3 | 4 | COPY . /App 5 | WORKDIR /App 6 | 7 | RUN pip install -r requirements.txt 8 | 9 | CMD ["python","-u","/App/main.py"] 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jarod Cajna 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BotTwitter 2 | Un bot multi-compte Twitter simple d'utilisation pour participer aux concours (et les gagner). 3 | 4 | 5 | ### Les fonctionnalités du bot : 6 | 7 | * Multicompte 8 | * Bypass les protections de Pickaw (Twrench) 9 | * Tag des comptes quand un concours le demande 10 | * Retweet, like et follow les concours 11 | * Unfollow automatique 12 | * Possibilité de blacklist des comptes 13 | * Si le concours le demande le bot peut follow plusieurs personnes 14 | * Le bot peut répondre à un concours avec des hashtags 15 | 16 | 17 | ### Dépendance du script : 18 | 19 | Vous devez installer ces libraries python3 pour que le script fonctionne : 20 | ``` 21 | Tweepy 22 | PyYaml 23 | feedparser 24 | ``` 25 | ### Installation : 26 | 27 | * Dans un premier temps pour utiliser le script vous allez avoir besoin d'un compte développeur Twitter et de récupérer vos accès à l'API. 28 | Vous pouvez demander cet accès sur le site développeur de Twitter : [Twitter Developer](https://developer.twitter.com/) 29 | Si vous avez besoin d'un tutoriel : [Tutoriel compte développeur](https://www.extly.com/docs/autotweetng_joocial/tutorials/how-to-auto-post-from-joomla-to-twitter/apply-for-a-twitter-developer-account/#apply-for-a-developer-account) 30 | 31 | * Vous devez ensuite installer une version 3.x de Python : [Python 3.x](https://www.python.org/downloads/) 32 | 33 | * Pour finir vous devez installer les libraries Tweepy, PyYaml et feedparser: 34 | ``` 35 | python3 -m pip install tweepy PyYaml feedparser 36 | ou 37 | py -m pip install tweepy PyYaml feedparser 38 | ``` 39 | Ces commandes sont à rentrer dans votre console (cmd pour Windows) 40 | Si pip n'est pas reconnu vous devez l'installer. 41 | 42 | 43 | ### Configuration : 44 | 45 | Tous les paramètres de configurations sont dans le fichier **configuration.yml**. 46 | Copiez le fichier **configuration.yml.dist** pour créer le fichier **configuration.yml**. 47 | 48 | Une fois que vous avez téléchargé le projet, pour pouvoir lancer le script vous devez ajouter dans le fichier configuration.yml les clés de l'[API Twitter](https://developer.twitter.com/). 49 | 50 | ``` 51 | accounts: 52 | # Accounts name 53 | - "Nom du compte": 54 | # API key 55 | - "Example" 56 | # API key secret 57 | - "Example" 58 | # Access token 59 | - "Example" 60 | # Access token secret 61 | - "Example" 62 | ``` 63 | Vous pouvez rajouter autant de comptes que vous voulez. Evitez les comptes commençants par un chiffre. 64 | ``` 65 | accounts: 66 | # Accounts name 67 | - "Nom du compte": 68 | # API key 69 | - "Example" 70 | # API key secret 71 | - "Example" 72 | # Access token 73 | - "Example" 74 | # Access token secret 75 | - "Example" 76 | - "Nom du compte 2": 77 | # API key 78 | - "Example" 79 | # API key secret 80 | - "Example" 81 | # Access token 82 | - "Example" 83 | # Access token secret 84 | - "Example" 85 | ``` 86 | Pour faire fonctionner correctement la fonction de bypass antibot, vous devez renseigner des flux rss. 87 | Une liste de flux que vous pouvez ajouter est disponible ici : [Flux rss](http://atlasflux.saynete.net/index.htm) 88 | ``` 89 | # RSS 90 | flux_rss: 91 | - https://partner-feeds.20min.ch/rss/20minutes 92 | - https://www.24matins.fr/feed 93 | ``` 94 | 95 | Il est possible désactiver la fonction de bypass antibot, la fonction like des concours, la fonction commenter avec un hashtag. 96 | ``` 97 | # Use the antibot bypass feature (True/False) 98 | bypass_antibot : True 99 | 100 | # Like all giveaway (True/False) 101 | like_giveaway : False 102 | 103 | # Comment with hashtag (True/False) 104 | comment_with_hashtag : True 105 | ``` 106 | 107 | Vous pouvez ajouter des mot à rechercher pour trouver des concours 108 | ``` 109 | # Words to search for giveaway 110 | words_to_search: 111 | - "#concours" 112 | - "#JeuConcours" 113 | - "concours pour gagner" 114 | - "Gagnez rt + follow" 115 | - "RT & follow" 116 | ``` 117 | 118 | Vous pouvez définir des comptes à tag quand un concours le demande (par défaut vos bots peuvent se tag entre eux) 119 | ``` 120 | # Accounts we want to invite 121 | accounts_to_tag: 122 | - "@j4rj4r_binks" 123 | ``` 124 | 125 | En copiant des tweets, vous pouvez récupéré des tweets "sensibles". Il y a donc la possibilité de filtrer des mots à ne pas copier. 126 | ``` 127 | # Words that should not be copied with the antibot 128 | words_to_blacklist_antibot: 129 | - "pd" 130 | - "tapette" 131 | - "enculé" 132 | - "pédale" 133 | - "isis" 134 | ``` 135 | 136 | ### Lancement du bot : 137 | ``` 138 | python3 main.py 139 | ou 140 | py main.py 141 | ``` 142 | 143 | 144 | Vous avez une question ? Des idées d'ameliorations ? vous pouvez venir sur notre serveur discord : [MoneyMakers](https://discord.gg/gjNbrgwRxT) 145 | On recherche des devs pour continuer à améliorer ce bot et à commencer de nouveaux projets ! 146 | -------------------------------------------------------------------------------- /configuration.yml.dist: -------------------------------------------------------------------------------- 1 | # Define accounts and keys 2 | accounts: 3 | # Accounts name 4 | - "Example": 5 | # API key 6 | - "Example" 7 | # API key secret 8 | - "Example" 9 | # Access token 10 | - "Example" 11 | # Access token secret 12 | - "Example" 13 | - "Example2": 14 | # API key 15 | - "Example" 16 | # API key secret 17 | - "Example" 18 | # Access token 19 | - "Example" 20 | # Access token secret 21 | - "Example" 22 | 23 | # Use the antibot bypass feature (True/False) 24 | bypass_antibot : True 25 | 26 | # RSS 27 | flux_rss: 28 | - https://www.24matins.fr/feed 29 | - http://feeds.feedburner.com/actu/dDcd 30 | 31 | # Like all giveaway (True/False) 32 | like_giveaway : False 33 | 34 | # Comment with hashtag (True/False) 35 | comment_with_hashtag : True 36 | 37 | # Maximum number of giveaway retrieve for each word 38 | max_giveaway : 10 39 | 40 | # Number of accounts to tag 41 | nb_account_to_tag : 2 42 | 43 | # Minimum level wanted for script logging (DEBUG, INFO, WARNING, ERROR, CRITICAL) 44 | logging_level : "INFO" 45 | 46 | # Words to search for giveaway 47 | words_to_search: 48 | - "#concours" 49 | - "#JeuConcours" 50 | - "concours pour gagner" 51 | - "Gagnez rt + follow" 52 | - "RT & follow" 53 | 54 | # Period to check. How many days back from today 55 | #Default : 0 : today 56 | # 1 : yesterday and today 57 | # 5 : since 5 days back 58 | nb_days_rollback: 0 59 | 60 | # Accounts we want to invite 61 | accounts_to_tag: 62 | - "@j4rj4r_binks" 63 | 64 | # Accounts that we don't want to follow 65 | accounts_to_blacklist: 66 | - "@RealB0tSpotter" 67 | - "@jflessauSpam" 68 | - "@b0ttem" 69 | - "@BotSpotterBot" 70 | - "@gulzaarAvi" 71 | - "@BotFett" 72 | 73 | # Sentences you need to tweet to invite a friend 74 | sentence_for_tag: 75 | - "J'invite :" 76 | - "Merci ! je tag :" 77 | - "Je tag :" 78 | - "Hop Hop, j'invite :" 79 | - "Avec moi :" 80 | - "Help me :" 81 | - "Pour vous aussi les gars :" 82 | - "tentez votre chance !" 83 | - "Je tente ma chance ! J'espère que je vais gagner !" 84 | - "J'espère que vais gagner !" 85 | - "Merci pour le concours ! Essayez aussi :" 86 | - "Que la chance soit avec moi ! et vous" 87 | - "Merci d'organiser ce concours ! Ça peut vous intéresser" 88 | - "On croise les doigts ! vous aussi" 89 | - "C'est pour vous ça ! :" 90 | - "Celui là on le gagne" 91 | - "J'espère que vais gagner ! On participe !" 92 | - "Merci d'organiser ce concours !" 93 | - "Bonne chance à tous !" 94 | - "J'adore les concours et je sais que vous aussi" 95 | - "J'ai tellement envie de gagner, essayez vous aussi" 96 | - "Je participe et j'invite" 97 | - "Bonjour je participe avec plaisir. Merci pour ce concours et bonne journée ! " 98 | - "Merci beaucoup j'adore votre compte. Je vous souhaite une belle continuation. J'invite " 99 | - "Bonjour je participe avec plaisir à ce super concours et j'invite " 100 | - "Superbe ! Je tente ma chance bien volontiers et j'invite " 101 | - "Chouette bonjour je participe avec plaisir avec " 102 | - "Bonjour je tente ma chance avec " 103 | - "Bonjour, Je tente ma chance bien volontiers et j'invite " 104 | - "Merci beaucoup pour ce super concours, je tague " 105 | - "Bonjour génial ! Je tente ma chance et j'identifie " 106 | - "Merci ! J'invite " 107 | - "Hello !!! Merci beaucoup je tente ma chance merci et bonne journée ! Je tague " 108 | - "Bonjour je participe avec plaisir pour ce beau cadeau et j'invite " 109 | - "Bonjour et merci pour ce super concours :) j'invite " 110 | - "Bonjour, trop Sympa ce concours. Essayez-vous aussi " 111 | - "Je tag mes amis " 112 | - "On tente encore et encore " 113 | - "Je tente ma chance aux côtés de " 114 | - "Ça vous intéressent ?" 115 | - "Qu'est ce que vous en pensez ? Allez participez-vous aussi " 116 | - "Let's go, je suis sûr qu'on va gagner cette fois :) " 117 | 118 | # hashtag we don't want to tweet 119 | hashtag_to_blacklist: 120 | - "giveaway" 121 | - "concours" 122 | - "rt" 123 | - "follow" 124 | - "jeuconcours" 125 | - "jeu" 126 | - "jeux" 127 | - "cadeau" 128 | - "cadeaux" 129 | - "concour" 130 | - "giveway" 131 | - "tweet" 132 | - "commente" 133 | - "followers" 134 | - "follower" 135 | - "twitter" 136 | - "tag" 137 | - "jeuxconcours" 138 | - "giveawayalert" 139 | - "retweet" 140 | - "mentionne" 141 | - "kdo" 142 | - "gratuit" 143 | - "cadeaunoel" 144 | - "annonce" 145 | - "noel" 146 | - "joyeuxnoel" 147 | 148 | # We don't want to participate in giveaway with these words 149 | giveaway_to_blacklist: 150 | - "nude" 151 | -------------------------------------------------------------------------------- /logs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/j4rj4r/BotTwitter/d4a45b31c362685a5cd1990cf3fad2ac7e8da610/logs/.gitkeep -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # Standard libraries 2 | import logging 3 | import random 4 | import sys 5 | import time 6 | from datetime import datetime, timedelta 7 | 8 | # third party libraries 9 | import tweepy 10 | import yaml 11 | 12 | # Local libraries 13 | from BotTwitter.bypass_antibot import BypassAntiBot 14 | from BotTwitter.helper import Helper 15 | from BotTwitter.manage_follow import ManageFollow, create_tables_follow 16 | from BotTwitter.manage_rss import create_table_rss 17 | from BotTwitter.retweet_giveaway import RetweetGiveaway 18 | 19 | # Configuration 20 | VERSION = 3.1 21 | CONFIGURATION_FILE = "configuration.yml" 22 | Helper = Helper() 23 | 24 | # Load all configuration variables 25 | with open(CONFIGURATION_FILE, 'r', encoding="utf8") as stream: 26 | out = yaml.load(stream, Loader=yaml.FullLoader) 27 | words_to_search = out['words_to_search'] 28 | nb_days_rollback = out['nb_days_rollback'] 29 | accounts_to_tag = out['accounts_to_tag'] 30 | sentence_for_tag = out['sentence_for_tag'] 31 | hashtag_to_blacklist = [x.upper() for x in out['hashtag_to_blacklist']] 32 | giveaway_to_blacklist = [x.upper() for x in out['giveaway_to_blacklist']] 33 | accounts_to_blacklist = [w.replace('@', '') for w in out['accounts_to_blacklist']] 34 | list_account = out['accounts'] 35 | bypass_antibot = out['bypass_antibot'] 36 | like_giveaway = out['like_giveaway'] 37 | comment_with_hashtag = out['comment_with_hashtag'] 38 | max_giveaway = out['max_giveaway'] 39 | logging_level = out['logging_level'] 40 | flux_rss = out['flux_rss'] 41 | nb_account_to_tag = out['nb_account_to_tag'] 42 | 43 | # Configuration of the logging library 44 | Helper.logging_configuration(logging_level) 45 | 46 | # Create a table to store rss links if it does not exist. 47 | create_table_rss() 48 | 49 | # Main loop 50 | while True: 51 | list_name = [] 52 | for account in out['accounts']: 53 | for account_name, list_auth in account.items(): 54 | try: 55 | # Extract API & ACCESS credentials 56 | api_key, api_secret, access_token, access_secret = list_auth 57 | api, user = Helper.get_user(api_key, api_secret, access_token, access_secret) 58 | 59 | create_tables_follow(user) 60 | list_name.append("@" + user.screen_name) 61 | logging.info("Configuration completed for the account : %s", user.screen_name) 62 | 63 | except Exception as e: 64 | logging.error("Error with account : %s", account_name) 65 | logging.error(e) 66 | continue 67 | 68 | logging.info("-" * 40) 69 | # Add Accounts to Tag 70 | if accounts_to_tag: 71 | list_name += accounts_to_tag 72 | # We don't want a duplicate 73 | list_name = list(set(list_name)) 74 | 75 | connection = 0 76 | # Looking for an account to find giveaway 77 | for account in out['accounts']: 78 | for account_name, list_auth in account.items(): 79 | try: 80 | # If an account is available, we break the loop 81 | if connection == 1: 82 | break 83 | api_key, api_secret, access_token, access_secret = list_auth 84 | api, user = Helper.get_user(api_key, api_secret, access_token, access_secret) 85 | connection = 1 86 | except: 87 | continue 88 | 89 | # If no account is available 90 | if connection == 0: 91 | logging.error("No account available!") 92 | sys.exit() 93 | # We retrieve a list of giveaway 94 | else: 95 | rtgiveaway = RetweetGiveaway(api, user) 96 | giveaway_list = rtgiveaway.check_retweet(words_to_search, 97 | accounts_to_blacklist, 98 | hashtag_to_blacklist, 99 | giveaway_to_blacklist, 100 | comment_with_hashtag, 101 | max_giveaway, 102 | nb_days_rollback) 103 | 104 | for account in out['accounts']: 105 | for account_name, list_auth in account.items(): 106 | try: 107 | # Thread here 108 | # Extract API & ACCESS credentials 109 | api_key, api_secret, access_token, access_secret = list_auth 110 | 111 | # Authenticate Key to use Twitter API 112 | auth = tweepy.OAuthHandler(api_key, api_secret) 113 | auth.set_access_token(access_token, access_secret) 114 | api = tweepy.API(auth) 115 | 116 | user = api.verify_credentials() 117 | logging.info("-" * 40) 118 | logging.info("Launching the bot on : @%s", user.screen_name) 119 | 120 | managefollow = ManageFollow(user, api) 121 | managefollow.unfollow() 122 | 123 | rtgiveaway = RetweetGiveaway(api, user) 124 | rtgiveaway.manage_giveaway(giveaway_list, sentence_for_tag, 125 | list_name, hashtag_to_blacklist, 126 | managefollow, like_giveaway, 127 | nb_account_to_tag) 128 | 129 | # If the antibot bypass feature is activated 130 | if bypass_antibot: 131 | bypass = BypassAntiBot(api, flux_rss) 132 | bypass.bypass() 133 | 134 | logging.info("Sleeping for 30s...") 135 | time.sleep(30) 136 | 137 | except KeyboardInterrupt: 138 | Helper.ask_to_exit() 139 | 140 | except tweepy.TweepyException as error: 141 | if error.api_codes == 326 or error.api_code == 32: 142 | logging.error("Connection error : %s", account_name) 143 | continue 144 | else: 145 | continue 146 | 147 | try: 148 | now = datetime.now() 149 | current_time = now.strftime("%H:%M:%S") 150 | logging.info("Current time : %s", current_time) 151 | 152 | # Sleep if it's night time 153 | if 22 < now.hour or now.hour < 2: 154 | waiting_time = 7 * 3600 155 | else: 156 | waiting_time = random.randrange(4000, 6000) 157 | 158 | logging.info("Waiting time before restarting bots : " + str(timedelta(seconds=waiting_time))) 159 | logging.info("-" * 40) 160 | 161 | time.sleep(waiting_time) 162 | except KeyboardInterrupt: 163 | Helper.ask_to_exit() 164 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Pyyaml==6.0 2 | tweepy==4.3.0 3 | feedparser==6.0.8 4 | --------------------------------------------------------------------------------