class Bot: """ The twitter bot class """ def __init__(self, bot): self.twython_obj = Twython(app_key=bot.consumer_key, app_secret=bot.consumer_secret, oauth_token=bot.access_token, oauth_token_secret=bot.access_token_secret) self.keywords = bot.keywords self.source_user_ids = bot.source_user_ids self.source_followers_ids = bot.source_followers_ids self.followers_ids = bot.followers_ids def get_source_user_ids(self): """ Get some users to initiate the twitter bot for the given keywords""" source_user_ids = [] for keyword in self.keywords: # get a list of user objects by searching for the keyword userlist = self.twython_obj.searchUsers(q=keyword) # select 3 random user objects random_list = random.sample(userlist, 3) # store the screen_name of the 5 users in a list ids = [user['id'] for user in random_list] #add screen_names to the list of our tweet sources source_user_ids = source_user_ids + ids for keyword in self.keywords: # get a list of recent tweets tweetlist = self.twython_obj.search(q=keyword, result_type="recent", lang='en')['results'] # select 7 random tweets random_list = random.sample(tweetlist, 7) # store screen_names of users who made the tweets ids = [tweet['from_user_id'] for tweet in random_list] #add screen_names to the list of our tweet sources source_user_ids = source_user_ids + ids self.source_user_ids = source_user_ids return source_user_ids def get_source_followers_ids(self): """ Returns a list of all followers of the users in source_users""" source_followers_ids = [] for source_user_id in self.source_user_ids: follower_ids = self.twython_obj.getFollowersIDs(user_id=source_user_id)['ids'] source_followers_ids = source_followers_ids + follower_ids self.source_followers_ids = source_followers_ids return source_followers_ids def find_and_follow(self): """ Find users to follow and follow them """ # Randomly select one keyword from the list of keywords keyword = random.choice(self.keywords) # Seacrh for tweets with that keyword tweets = self.twython_obj.search(q=keyword, result_type="recent", lang='en')['results'] done_following = False while not done_following: # Randomly select a tweet tweet = random.choice(tweets) user_id = tweet['from_user_id'] if user_id not in self.source_user_ids and user_id not in self.source_followers_ids: self.twython_obj.createFriendship(user_id=user_id) done_following = True return done_following def get_follwers(self): """ Returns a list of ids of all followers of the bot """ followers_ids = self.twython_obj.getFollowersIDs(user_id=user_id)['ids'] self.followers_ids = followers return followers_ids def copy_and_tweet(self): """ Copy a tweet and tweet it """
class TwitterServer: def __init__(self): # You can choose to latch tweets topics latch = False if rospy.has_param('latch'): latch = rospy.get_param('latch') # In case you can't direct message a user, replace DM with a public # '@user text' tweet. self.replace_dm = False if rospy.has_param('replace_dm'): self.replace_dm = rospy.get_param('replace_dm') # Publish mentions, home timeline and direct messages self.pub_home = rospy.Publisher('home_timeline', Tweets, latch = latch) self.pub_mentions = rospy.Publisher('mentions', Tweets, latch = latch) self.pub_dm = rospy.Publisher('direct_messages', Tweets, latch = latch) # Create a bridge for images conversions self.bridge = CvBridge() # Last Tweets (init values are twitter API default) self.last_mention = 12345 self.last_timeline = 12345 self.last_dm = 12345 oauth_token = None oauth_token_secret = None # Get OAuth info through parameter server if rospy.has_param('~token'): oauth_token = rospy.get_param('~token') if rospy.has_param('~token_secret'): oauth_token_secret = rospy.get_param('~token_secret') # OAuth token creation (see get_access_token.py from python-twitter) if oauth_token is None or oauth_token_secret is None: rospy.loginfo("No OAuth information given, trying to create...") t = Twython( app_key = 'HbAfkrfiw0s7Es4TVrpSuw', app_secret = 'oIjEOsEbHprUa7EOi3Mo8rNBdQlHjTGPEpGrItZj8c') # Get AUth URL. Use for login for security, url = t.get_authentication_tokens() t = Twython(app_key = 'HbAfkrfiw0s7Es4TVrpSuw', app_secret = 'oIjEOsEbHprUa7EOi3Mo8rNBdQlHjTGPEpGrItZj8c', oauth_token = url['oauth_token'], oauth_token_secret = url['oauth_token_secret']) # Open web browser on given url import webbrowser webbrowser.open( url['auth_url'] ) rospy.logwarn("Log your twitter, allow TwitROS and copy pincode.") # Wait to avoid webbrowser to corrupt raw_input rospy.sleep( rospy.Duration( 5 ) ) # Enter pincode pincode = raw_input('Pincode: ').strip() auth_props = t.get_authorized_tokens(oauth_verifier = pincode) oauth_token = auth_props['oauth_token'] oauth_token_secret = auth_props['oauth_token_secret'] rospy.loginfo("Using the following parameters for oauth: " + 'key: [{key}], '.format(key = oauth_token) + 'secret: [{secret}]'.format(secret = oauth_token_secret)) # Consumer key and secret are specific to this App. # Access token are given through OAuth for those consumer params rospy.loginfo('Trying to log into Twitter API...') # Twython self.t = Twython(app_key = 'HbAfkrfiw0s7Es4TVrpSuw', app_secret = 'oIjEOsEbHprUa7EOi3Mo8rNBdQlHjTGPEpGrItZj8c', oauth_token = oauth_token, oauth_token_secret = oauth_token_secret) result = self.t.verifyCredentials(); rospy.loginfo("Twitter connected as {name} (@{user})!" .format(name = result['name'], user = result['screen_name'])) # Stock screen name (used to show friendships) self.name = result['screen_name'] # Advertise services self.post = rospy.Service('post_tweet', Post, self.post_cb) self.retweet = rospy.Service('retweet', Id, self.retweet_cb) self.follow = rospy.Service('follow', User, self.follow_cb) self.unfollow = rospy.Service('unfollow', User, self.unfollow_cb) self.post_dm = rospy.Service('post_dm', DirectMessage, self.post_dm_cb) self.destroy = rospy.Service('destroy_dm', Id, self.destroy_cb) self.timeline = rospy.Service( 'user_timeline', Timeline, self.user_timeline_cb) # Create timers for tweet retrieval. Use oneshot and retrigger timer_home = rospy.Timer( rospy.Duration(1), self.timer_home_cb, oneshot = True ) timer_mentions = rospy.Timer( rospy.Duration(2), self.timer_mentions_cb, oneshot = True ) timer_dm = rospy.Timer( rospy.Duration(3), self.timer_dm_cb, oneshot = True ) # Tweet callback def post_cb(self, req): txt = req.text rospy.logdebug("Received a tweet: " + txt) # If only one picture, use twitter upload if len(req.images) == 1: path = self.save_image( req.images[0] ) first = True for tweet in self.split_tweet( txt ): if (req.reply_id == 0): if first: result = self.t.updateStatusWithMedia( file_ = path, status = tweet ) first = False else: result = self.t.updateStatus( status = tweet ) else: if first: result = self.t.updateStatusWithMedia( file_ = path, status = tweet, in_reply_status_id = req.reply_id ) first = False else: result = self.t.updateStatus( status = tweet, in_reply_to_status_id = req.reply_id ) # Check response for each update. if self.t.get_lastfunction_header('status') != '200 OK': return None os.system('rm -f ' + path) elif len(req.images) != 0: txt += upload( req.images ) # Publish after splitting. for tweet in self.split_tweet( txt ): if (req.reply_id == 0): result = self.t.updateStatus( status = tweet ) else: result = self.t.updateStatus( status = tweet, in_reply_to_status_id = req.reply_id ) if self.t.get_lastfunction_header('status') != '200 OK': return None return PostResponse(id = result['id']) def retweet_cb(self, req): result = self.t.retweet( id = req.id ) if self.t.get_lastfunction_header('status') != '200 OK': return None else: return IdResponse() # Does not raise an error if you are already following the user def follow_cb(self, req): rospy.logdebug("Asked to follow:" + req.user) result = self.t.createFriendship( screen_name = req.user ) if self.t.get_lastfunction_header('status') != '200 OK': return None else: return UserResponse() # Does not raise an error if you are not following the user def unfollow_cb(self, req): rospy.logdebug("Asked to unfollow:" + req.user) result = self.t.destroyFriendship( screen_name = req.user ) if self.t.get_lastfunction_header('status') != '200 OK': return None else: return UserResponse() # Send direct message. def post_dm_cb(self, req): rospy.logdebug("Received DM to " + req.user + ": " + req.text) # First, check if you can dm the user relation = self.t.showFriendship( source_screen_name = self.name, target_screen_name = req.user ) if self.t.get_lastfunction_header('status') != '200 OK': rospy.logerr("Failed to get friendship information.") return None # CASE 1: If can, send a direct message if relation['relationship']['source']['can_dm']: txt = req.text # Upload image to postimage.org using requests if len(req.images): txt += self.upload( req.images ) for dm in self.split_tweet( txt ): result = self.t.sendDirectMessage( screen_name = req.user, text = dm ) if self.t.get_lastfunction_header('status') != '200 OK': return None # Return the id of the last DM posted. return DirectMessageResponse(id = result['id']) # CASE 2: If Cant dm but allowed to tweet instead, tweet with mention elif self.replace_dm: rospy.logwarn("You can't send a direct message to " + req.user + ". Sending a public tweet instead...") # One image ---> Twitter if len(req.images) == 1 : path = self.save_image( req.images[0] ) first = True for tweet in self.split_tweet( req.text ): if first: result = self.t.updateStatusWithMedia( file_ = path, status = tweet ) first = False else: result = self.t.updateStatus( status = tweet ) if self.t.get_lastfunction_header('status') != '200 OK': return None os.system('rm -rf ' + path) # Return the id of the last DM posted. return DirectMessageResponse(id = result['id']) else: status = '@' + req.user + ' ' + req.text # Many images ---> postimage.org if len(req.images) != 0: status += upload( req.images ) for tweet in self.split_tweet( status ): result = self.t.updateStatus( status = tweet ) if self.t.get_lastfunction_header('status') != '200 OK': return None # Return the id of the last DM posted. return DirectMessageResponse(id = result['id']) # CASE 3: If can't. else: rospy.logwarn("You can't send a direct message to " + req.user) return None def destroy_cb(self, req): result = self.t.destroyDirectMessage( id = req.id ) if self.t.get_lastfunction_header('status') != '200 OK': return None else: return IdResponse() def user_timeline_cb(self, req): result = self.t.getUserTimeline( screen_name = req.user ) if self.t.get_lastfunction_header('status') != '200 OK': return None else: msg = self.process_tweets( result ) if msg: return TimelineResponse( tweets = msg ) else: rospy.logwarn(req.user + " has no tweets in its timeline.") return TimelineResponse( ) # Split a long text into 140 chars tweets def split_tweet(self, text): tweet = "" tweets = [] words = text.split(' ') for word in words: if len(tweet + word + " ") > 137: tweets.append(tweet.strip() + "...") # If tweets is intended to a user, begin all tweets with @user if text.startswith('@'): tweet = words[0] + " " + word + " " else: tweet = "..." else: tweet = tweet + word + " " tweets.append( tweet.strip() ) return tweets # Upload array of sensor_msgs/Image to postimage.org and return link. # Link is shortened if possible. def upload(self, images): files = {} paths = [] # Keep paths stored to remove files later # Construct files dict i = 0 for image in images: paths.append( self.save_image( image ) ) files['upload[{count}]'.format(count = i)] = open(paths[i], 'rb') i += 1 # Post using requests request = requests.post('http://postimage.org/index.php', files = files, params = {'optsize': 0, 'adult': 'no'}) # Cleanup saved files for path in paths: os.system('rm -rf ' + path) if not request.status_code in [301, 201, 200]: return " [FAILED UPLOAD]" # Parse HTML page returned using beautifulsoup soup = BeautifulSoup(request.text, 'html.parser') # Find the image link: Hacked that by looking at raw html file url = "" if len(images) == 1: # 1 image : find first solo link containg 'postimg' for link in soup.find_all('a'): if link.get('href').find('postimg') != -1: url = link.get('href') break else: # Many images: find the option field for gallery url for option in soup.find_all('option'): if option.get('value').find('gallery') != -1: url = option.get('value') break if not len(url): return " [FAILED PARSING OR UPLOAD]" # Shorten URL request = requests.get( 'http://is.gd/create.php', params = {'format': 'simple', 'url': url} ) if request.status_code in [301, 201, 200]: return " [" + request.text + "]" else: return " [" + url + "]" # Save a sensor_msgs/Image on /tmp and return the path def save_image(self, image): # Convert from ROS message using cv_bridge. try: cv_image = self.bridge.imgmsg_to_cv( image, desired_encoding = 'passthrough') except CvBridgeError, e: rospy.logerr(e) # Write to JPG with OpenCV path = "/tmp/pic_ul_{time}.png".format( time = time() ) cv.SaveImage( path, cv_image ) return path