class TweepyAPITests(unittest.TestCase): def setUp(self): auth = OAuthHandler(oauth_consumer_key, oauth_consumer_secret) auth.set_access_token(oauth_token, oauth_token_secret) self.api = API(auth) self.api.retry_count = 2 self.api.retry_delay = 5 def testhometimeline(self): self.api.home_timeline() def testfriendstimeline(self): self.api.friends_timeline() def testusertimeline(self): self.api.user_timeline() self.api.user_timeline('twitter') def testmentions(self): self.api.mentions() def testretweetedbyme(self): self.api.retweeted_by_me() def testretweetedbyuser(self): self.api.retweeted_by_user('twitter') def testretweetedtome(self): self.api.retweeted_to_me() def testretweetsofme(self): self.api.retweets_of_me() def testretweet(self): s = self.api.retweet(test_tweet_id) s.destroy() def testretweets(self): self.api.retweets(test_tweet_id) def testgetstatus(self): self.api.get_status(id=test_tweet_id) def testupdateanddestroystatus(self): # test update text = 'testing %i' % random.randint(0, 1000) update = self.api.update_status(status=text) self.assertEqual(update.text, text) # test destroy deleted = self.api.destroy_status(id=update.id) self.assertEqual(deleted.id, update.id) def testgetuser(self): u = self.api.get_user('twitter') self.assertEqual(u.screen_name, 'twitter') u = self.api.get_user(783214) self.assertEqual(u.screen_name, 'twitter') def testsearchusers(self): self.api.search_users('twitter') def testme(self): me = self.api.me() self.assertEqual(me.screen_name, username) def testfriends(self): self.api.friends() def testfollowers(self): self.api.followers() def testdirectmessages(self): self.api.direct_messages() def testsentdirectmessages(self): self.api.sent_direct_messages() def testsendanddestroydirectmessage(self): # send sent_dm = self.api.send_direct_message(username, text='test message') self.assertEqual(sent_dm.text, 'test message') self.assertEqual(sent_dm.sender.screen_name, username) self.assertEqual(sent_dm.recipient.screen_name, username) # destroy destroyed_dm = self.api.destroy_direct_message(sent_dm.id) self.assertEqual(destroyed_dm.text, sent_dm.text) self.assertEqual(destroyed_dm.id, sent_dm.id) self.assertEqual(destroyed_dm.sender.screen_name, username) self.assertEqual(destroyed_dm.recipient.screen_name, username) def testcreatedestroyfriendship(self): enemy = self.api.destroy_friendship('twitter') self.assertEqual(enemy.screen_name, 'twitter') # Wait 5 seconds to allow Twitter time # to process the friendship destroy request. sleep(5) friend = self.api.create_friendship('twitter') self.assertEqual(friend.screen_name, 'twitter') def testshowfriendship(self): source, target = self.api.show_friendship(target_screen_name='twtiter') self.assert_(isinstance(source, Friendship)) self.assert_(isinstance(target, Friendship)) def testfriendsids(self): self.api.friends_ids(username) def testfollowersids(self): self.api.followers_ids(username) def testverifycredentials(self): self.assertNotEqual(self.api.verify_credentials(), False) # make sure that `me.status.entities` is not an empty dict me = self.api.verify_credentials(include_entities=True) self.assertTrue(me.status.entities) # `status` shouldn't be included me = self.api.verify_credentials(skip_status=True) self.assertFalse(hasattr(me, 'status')) def testratelimitstatus(self): self.api.rate_limit_status() """ TODO(josh): Remove once this deprecated API is gone. def testsetdeliverydevice(self): self.api.set_delivery_device('im') self.api.set_delivery_device('none') """ def testupdateprofilecolors(self): original = self.api.me() updated = self.api.update_profile_colors('000', '000', '000', '000', '000') # restore colors self.api.update_profile_colors( original.profile_background_color, original.profile_text_color, original.profile_link_color, original.profile_sidebar_fill_color, original.profile_sidebar_border_color ) self.assertEqual(updated.profile_background_color, '000') self.assertEqual(updated.profile_text_color, '000') self.assertEqual(updated.profile_link_color, '000') self.assertEqual(updated.profile_sidebar_fill_color, '000') self.assertEqual(updated.profile_sidebar_border_color, '000') """ def testupateprofileimage(self): self.api.update_profile_image('examples/profile.png') def testupdateprofilebg(self): self.api.update_profile_background_image('examples/bg.png') """ def testupdateprofile(self): original = self.api.me() profile = { 'name': 'Tweepy test 123', 'url': 'http://www.example.com', 'location': 'pytopia', 'description': 'just testing things out' } updated = self.api.update_profile(**profile) self.api.update_profile( name = original.name, url = original.url, location = original.location, description = original.description ) for k,v in profile.items(): if k == 'email': continue self.assertEqual(getattr(updated, k), v) def testfavorites(self): self.api.favorites() def testcreatedestroyfavorite(self): self.api.create_favorite(4901062372) self.api.destroy_favorite(4901062372) def testenabledisablenotifications(self): self.api.enable_notifications('twitter') self.api.disable_notifications('twitter') def testcreatedestroyblock(self): self.api.create_block('twitter') self.assertEqual(self.api.exists_block('twitter'), True) self.api.destroy_block('twitter') self.assertEqual(self.api.exists_block('twitter'), False) self.api.create_friendship('twitter') # restore def testblocks(self): self.api.blocks() def testblocksids(self): self.api.blocks_ids() def testcreateupdatedestroylist(self): self.api.create_list('tweeps') # XXX: right now twitter throws a 500 here, issue is being looked into by twitter. #self.api.update_list('tweeps', mode='private') self.api.destroy_list('tweeps') def testlists(self): self.api.lists() def testlistsmemberships(self): self.api.lists_memberships() def testlistssubscriptions(self): self.api.lists_subscriptions() def testlisttimeline(self): self.api.list_timeline('applepie', 'stars') def testgetlist(self): self.api.get_list('applepie', 'stars') def testaddremovelistmember(self): uid = self.api.get_user('twitter').id self.api.add_list_member('test', uid) self.api.remove_list_member('test', uid) def testlistmembers(self): self.api.list_members('applepie', 'stars') def testislistmember(self): uid = self.api.get_user('applepie').id self.api.is_list_member('applepie', 'stars', uid) def testsubscribeunsubscribelist(self): self.api.subscribe_list('applepie', 'stars') self.api.unsubscribe_list('applepie', 'stars') def testlistsubscribers(self): self.api.list_subscribers('applepie', 'stars') def testissubscribedlist(self): uid = self.api.get_user('applepie').id self.api.is_subscribed_list('applepie', 'stars', uid) def testsavedsearches(self): s = self.api.create_saved_search('test') self.api.saved_searches() self.assertEqual(self.api.get_saved_search(s.id).query, 'test') self.api.destroy_saved_search(s.id) def testsearch(self): self.api.search('tweepy') def testtrends(self): self.api.trends_daily() self.api.trends_weekly() def testgeoapis(self): def place_name_in_list(place_name, place_list): """Return True if a given place_name is in place_list.""" return any([x.full_name.lower() == place_name.lower() for x in place_list]) twitter_hq = self.api.geo_similar_places(lat=37, long= -122, name='Twitter HQ') # Assumes that twitter_hq is first Place returned... self.assertEqual(twitter_hq[0].id, '3bdf30ed8b201f31') # Test various API functions using Austin, TX, USA self.assertEqual(self.api.geo_id(id='c3f37afa9efcf94b').full_name, 'Austin, TX') self.assertTrue(place_name_in_list('Austin, TX', self.api.nearby_places(lat=30.267370168467806, long= -97.74261474609375))) # Austin, TX, USA self.assertTrue(place_name_in_list('Austin, TX', self.api.reverse_geocode(lat=30.267370168467806, long= -97.74261474609375))) # Austin, TX, USA
class TweepyApi(BaseTweepyApi, ApiAdapter): """ A `ApiAdapter` implementation using `tweepy` library. http://github.com/tweepy/tweepy/ """ def __init__(self, *args, **kwargs): ApiAdapter.__init__(self, *args, **kwargs) # from `turses.api.base.ApiAdapter` def init_api(self): oauth_handler = TweepyOAuthHandler(self._consumer_key, self._consumer_secret) oauth_handler.set_access_token(self._access_token_key, self._access_token_secret) self._api = BaseTweepyApi(oauth_handler) @to_user def verify_credentials(self): return self._api.me() @to_user @include_entities def get_user(self, screen_name, **kwargs): return self._api.get_user(screen_name=screen_name, **kwargs) # timelines @to_status @include_entities def get_home_timeline(self, **kwargs): tweets = self._api.home_timeline(**kwargs) retweets = self._api.retweeted_to_me(**kwargs) tweets.extend(retweets) return tweets @to_status @include_entities def get_user_timeline(self, screen_name, **kwargs): return self._api.user_timeline(screen_name, **kwargs) @to_status @include_entities def get_own_timeline(self, **kwargs): me = self.verify_credentials() tweets = self._api.user_timeline(screen_name=me.screen_name, **kwargs) retweets = self._api.retweeted_by_me(**kwargs) tweets.extend(retweets) return tweets @to_status @include_entities def get_mentions(self, **kwargs): return self._api.mentions(**kwargs) @to_status @include_entities def get_favorites(self, **kwargs): return self._api.favorites(**kwargs) @to_direct_message @include_entities def get_direct_messages(self, **kwargs): dms = self._api.direct_messages(**kwargs) sent = self._api.sent_direct_messages(**kwargs) dms.extend(sent) return dms # NOTE: # `get_thread` is not decorated with `to_status` because # it uses `TweepyApi.get_user_timeline` which is already # decorated @include_entities def get_thread(self, status, **kwargs): """ Get the conversation to which `status` belongs. It filters the last tweets by the participanting users and based on mentions to each other. """ author = status.authors_username mentioned = status.mentioned_usernames if author not in mentioned: mentioned.append(author) tweets = [] for username in mentioned: tweets.extend(self.get_user_timeline(username, **kwargs)) def belongs_to_conversation(status): for username in mentioned: if username in status.text: return True return filter(belongs_to_conversation, tweets) @to_status_from_search @include_entities def search(self, text, **kwargs): return self._api.search(text, **kwargs) def update(self, text): self._api.update_status(text) def destroy_status(self, status): self._api.destroy_status(status.id) def retweet(self, status): self._api.retweet(status.id) def direct_message(self, username, text): self._api.send_direct_message(user=username, text=text) def destroy_direct_message(self, dm): self._api.destroy_direct_message(dm.id) def create_friendship(self, screen_name): self._api.create_friendship(screen_name=screen_name) def destroy_friendship(self, screen_name): self._api.destroy_friendship(screen_name=screen_name) def create_favorite(self, status): self._api.create_favorite(status.id) def destroy_favorite(self, status): self._api.destroy_favorite(status.id) # list methods def get_lists(self, screen_name): raise NotImplementedError def get_own_lists(self): raise NotImplementedError def get_list_memberships(self): raise NotImplementedError def get_list_subscriptions(self): raise NotImplementedError def get_list_timeline(self, list): raise NotImplementedError def get_list_members(self, list): raise NotImplementedError def is_list_member(self, user, list): raise NotImplementedError def subscribe_to_list(self, list): raise NotImplementedError def get_list_subscribers(self, list): raise NotImplementedError def is_list_subscriber(self, user, list): raise NotImplementedError
class TweepyApi(BaseTweepyApi, ApiAdapter): """ A :class:`turses.api.ApiAdapter` implementation using `tweepy` library. http://github.com/tweepy/tweepy/ """ def __init__(self, *args, **kwargs): ApiAdapter.__init__(self, *args, **kwargs) # from `turses.api.base.ApiAdapter` def init_api(self): oauth_handler = TweepyOAuthHandler(self._consumer_key, self._consumer_secret) oauth_handler.set_access_token(self._access_token_key, self._access_token_secret) self._api = BaseTweepyApi(oauth_handler) @to_user def verify_credentials(self): return self._api.me() @to_user @include_entities def get_user(self, screen_name, **kwargs): return self._api.get_user(screen_name=screen_name, **kwargs) # timelines @to_status @include_entities def get_status(self, status_id, **kwargs): return self._api.get_status(status_id, **kwargs) @to_status @include_entities def get_home_timeline(self, **kwargs): tweets = self._api.home_timeline(**kwargs) retweets = self._api.retweeted_to_me(**kwargs) tweets.extend(retweets) return tweets @to_status @include_entities def get_user_timeline(self, screen_name, **kwargs): return self._api.user_timeline(screen_name, **kwargs) @to_status @include_entities def get_own_timeline(self, **kwargs): me = self.verify_credentials() tweets = self._api.user_timeline(screen_name=me.screen_name, **kwargs) retweets = self._api.retweeted_by_me(**kwargs) tweets.extend(retweets) return tweets @to_status @include_entities def get_mentions(self, **kwargs): return self._api.mentions(**kwargs) @to_status @include_entities def get_favorites(self, **kwargs): return self._api.favorites(**kwargs) @to_direct_message @include_entities def get_direct_messages(self, **kwargs): dms = self._api.direct_messages(**kwargs) sent = self._api.sent_direct_messages(**kwargs) dms.extend(sent) return dms @include_entities def get_thread(self, status, **kwargs): """ Get the conversation to which `status` belongs. """ users_in_conversation = [status.authors_username] # Save the users that are mentioned for user in status.mentioned_usernames: if user not in users_in_conversation: users_in_conversation.append(user) # Fetch the tweets from participants before and after `status` # was published tweets_from_participants = [] for user in users_in_conversation: user_tweets = self._get_older_and_newer_tweets(user, status.id) tweets_from_participants.extend(user_tweets) def belongs_to_conversation(tweet): for user in users_in_conversation: if user in tweet.text: return True return filter(belongs_to_conversation, tweets_from_participants) def _get_older_and_newer_tweets(self, screen_name, tweet_id, count=20): """ Get tweets from the user with `screen_name` username that are older and newer than `tweet_id`. By default, 20 tweets are fetched. If provided, `count` controls how many tweets are requested. """ older = self.get_user_timeline(screen_name, max_id=tweet_id, count=count / 2) newer = self.get_user_timeline(screen_name, since_id=tweet_id, count=count / 2) return older + newer def get_message_thread(self, dm, **kwargs): messages = self.get_direct_messages(**kwargs) me = self.verify_credentials() if dm.sender_screen_name == me.screen_name: with_user = dm.recipient_screen_name else: with_user = dm.sender_screen_name def belongs_to_conversation(message): return (message.sender_screen_name == with_user or message.recipient_screen_name == with_user) return filter(belongs_to_conversation, messages) @to_status_from_search @include_entities def search(self, text, **kwargs): return self._api.search(text, **kwargs) @to_status @include_entities def get_retweets_of_me(self, **kwargs): return self._api.retweets_of_me(**kwargs) def update(self, text): self._api.update_status(text) def destroy_status(self, status): self._api.destroy_status(status.id) def retweet(self, status): self._api.retweet(status.id) def direct_message(self, username, text): self._api.send_direct_message(user=username, text=text) def destroy_direct_message(self, dm): self._api.destroy_direct_message(dm.id) def create_friendship(self, screen_name): self._api.create_friendship(screen_name=screen_name) def destroy_friendship(self, screen_name): self._api.destroy_friendship(screen_name=screen_name) def create_favorite(self, status): self._api.create_favorite(status.id) def destroy_favorite(self, status): self._api.destroy_favorite(status.id) # list methods @to_list def get_lists(self, screen_name): return self._api.lists(screen_name) @to_list def get_own_lists(self): return self._api.lists() @to_list def get_list_memberships(self): return self._api.lists_memberships() @to_list def get_list_subscriptions(self): return self._api.lists_subscriptions() @to_status def get_list_timeline(self, a_list): owner = a_list.owner.screen_name return self._api.list_timeline(owner=owner, slug=a_list.slug) @to_user def get_list_members(self, a_list): owner = a_list.owner.screen_name return self._api.list_members(owner=owner, slug=a_list.slug) def is_list_member(self, user, a_list): return bool( self._api.is_list_member( owner=user.screen_name, slug=a_list.slug, user_id=user.id, )) @to_list def subscribe_to_list(self, a_list): owner = a_list.owner return self._api.subscribe_list(owner=owner.screen_name, slug=a_list.slug) @to_user def get_list_subscribers(self, a_list): owner = a_list.owner return self._api.list_subscribers( owner=owner.screen_name, slug=a_list.slug, ) def is_list_subscriber(self, user, a_list): return bool( self._api.is_subscribed_list( owner=user.screen_name, slug=a_list.slug, user_id=user.id, ))
class TweepyApi(BaseTweepyApi, ApiAdapter): """ A :class:`turses.api.ApiAdapter` implementation using `tweepy` library. http://github.com/tweepy/tweepy/ """ def __init__(self, *args, **kwargs): ApiAdapter.__init__(self, *args, **kwargs) # from `turses.api.base.ApiAdapter` def init_api(self): oauth_handler = TweepyOAuthHandler(self._consumer_key, self._consumer_secret, secure=configuration.twitter['use_https']) oauth_handler.set_access_token(self._access_token_key, self._access_token_secret) self._api = BaseTweepyApi(oauth_handler, secure=configuration.twitter['use_https']) @to_user def verify_credentials(self): return self._api.me() @to_user @include_entities def get_user(self, screen_name, **kwargs): return self._api.get_user(screen_name=screen_name, **kwargs) # timelines @to_status @include_entities def get_status(self, status_id, **kwargs): return self._api.get_status(status_id, **kwargs) @to_status @include_entities def get_home_timeline(self, **kwargs): tweets = self._api.home_timeline(**kwargs) retweets = self._api.retweeted_to_me(**kwargs) tweets.extend(retweets) return tweets @to_status @include_entities def get_user_timeline(self, screen_name, **kwargs): return self._api.user_timeline(screen_name, **kwargs) @to_status @include_entities def get_own_timeline(self, **kwargs): me = self.verify_credentials() tweets = self._api.user_timeline(screen_name=me.screen_name, **kwargs) retweets = self._api.retweeted_by_me(**kwargs) tweets.extend(retweets) return tweets @to_status @include_entities def get_mentions(self, **kwargs): return self._api.mentions(**kwargs) @to_status @include_entities def get_favorites(self, **kwargs): return self._api.favorites(**kwargs) @to_direct_message @include_entities def get_direct_messages(self, **kwargs): dms = self._api.direct_messages(**kwargs) sent = self._api.sent_direct_messages(**kwargs) dms.extend(sent) return dms @include_entities def get_thread(self, status, **kwargs): """ Get the conversation to which `status` belongs. """ users_in_conversation = [status.authors_username] # Save the users that are mentioned for user in status.mentioned_usernames: if user not in users_in_conversation: users_in_conversation.append(user) # Fetch the tweets from participants before and after `status` # was published tweets_from_participants = [] for user in users_in_conversation: user_tweets = self._get_older_and_newer_tweets(user, status.id) tweets_from_participants.extend(user_tweets) def belongs_to_conversation(tweet): for user in users_in_conversation: if user in tweet.text: return True return filter(belongs_to_conversation, tweets_from_participants) def _get_older_and_newer_tweets(self, screen_name, tweet_id, count=20): """ Get tweets from the user with `screen_name` username that are older and newer than `tweet_id`. By default, 20 tweets are fetched. If provided, `count` controls how many tweets are requested. """ older = self.get_user_timeline(screen_name, max_id=tweet_id, count=count/2) newer = self.get_user_timeline(screen_name, since_id=tweet_id, count=count/2) return older + newer def get_message_thread(self, dm, **kwargs): messages = self.get_direct_messages(**kwargs) me = self.verify_credentials() if dm.sender_screen_name == me.screen_name: with_user = dm.recipient_screen_name else: with_user = dm.sender_screen_name def belongs_to_conversation(message): return (message.sender_screen_name == with_user or message.recipient_screen_name == with_user) return filter(belongs_to_conversation, messages) @to_status_from_search @include_entities def search(self, text, **kwargs): return self._api.search(text, **kwargs) @to_status @include_entities def get_retweets_of_me(self, **kwargs): return self._api.retweets_of_me(**kwargs) def update(self, text): self._api.update_status(text) def reply(self, status, text): self._api.update_status(text, in_reply_to_status_id=status.id) def destroy_status(self, status): self._api.destroy_status(status.id) def retweet(self, status): self._api.retweet(status.id) def direct_message(self, username, text): self._api.send_direct_message(user=username, text=text) def destroy_direct_message(self, dm): self._api.destroy_direct_message(dm.id) def create_friendship(self, screen_name): self._api.create_friendship(screen_name=screen_name) def destroy_friendship(self, screen_name): self._api.destroy_friendship(screen_name=screen_name) def create_favorite(self, status): self._api.create_favorite(status.id) def destroy_favorite(self, status): self._api.destroy_favorite(status.id) # list methods @to_list def get_lists(self, screen_name): return self._api.lists(screen_name) @to_list def get_own_lists(self): return self._api.lists() @to_list def get_list_memberships(self): return self._api.lists_memberships() @to_list def get_list_subscriptions(self): return self._api.lists_subscriptions() @to_status def get_list_timeline(self, a_list): owner = a_list.owner.screen_name return self._api.list_timeline(owner=owner, slug=a_list.slug) @to_user def get_list_members(self, a_list): owner = a_list.owner.screen_name return self._api.list_members(owner=owner, slug=a_list.slug) def is_list_member(self, user, a_list): return bool(self._api.is_list_member(owner=user.screen_name, slug=a_list.slug, user_id=user.id,)) @to_list def subscribe_to_list(self, a_list): owner = a_list.owner return self._api.subscribe_list(owner=owner.screen_name, slug=a_list.slug) @to_user def get_list_subscribers(self, a_list): owner = a_list.owner return self._api.list_subscribers(owner=owner.screen_name, slug=a_list.slug,) def is_list_subscriber(self, user, a_list): return bool(self._api.is_subscribed_list(owner=user.screen_name, slug=a_list.slug, user_id=user.id,))
user = "******" sent_direct_message_to = api.send_direct_message(user, text="Hello..") write_into_file_func("send_direct_message") myfile.write("Message Send to User.. Message Info : \n") write_into_file_DirectMessage(sent_direct_message_to) print("Successfully implemented send_direct_message") ###############---------destroy_direct_message-------##################### #Destroy a direct message. Authenticating user must be the recipient of the direct message. #Enter specific message id here...... message_id = '' destroy_direct_message_to = api.destroy_direct_message(message_id) write_into_file_func("destroy_direct_message") myfile.write("Message Sent to User.. Message Info : \n") write_into_file_DirectMessage(destroy_direct_message_to) print("Successfully implemented destroy_direct_message") myfile.exit() except Exception as e: print("Error Occured. " + str(e) + " Try Again. ")
class Twitter: def __init__(self): ''' initialize tweepy objects: - api - follower - bot_id - day ''' print("Initializing twitter...") self.auth = OAuthHandler(constants.CONSUMER_KEY, constants.CONSUMER_SECRET) self.auth.set_access_token(constants.ACCESS_KEY, constants.ACCESS_SECRET) self.api = API(self.auth, wait_on_rate_limit=True, wait_on_rate_limit_notify=True) self.follower = list() self.bot_id = int() self.message_db = tuple() self.day = (datetime.now(timezone.utc) + timedelta(hours=constants.Timezone)).day def read_dm(self): ''' read and filter DMs filters: - set from DM - check follower (exception for admin) - muted words (exception for admin) - similiarity checker - primary keywords - attachment - attachment_url - photo - video - animated_gif :returns: list of filtered DMs ''' print("Get direct messages...") dms = list() try: api = self.api dm = api.list_direct_messages() for x in range(len(dm)): sender_id = dm[x].message_create['sender_id'] message = dm[x].message_create['message_data']['text'] message_data = dm[x].message_create['message_data'] id = dm[x].id self.delete_dm(id) # Avoid keyword error by skipping bot messages if int(sender_id) == self.bot_id: continue # set from DM if constants.Set_keyword in message: print("command in progress...") try: message = message.split() command, *content = message[1:] if type(content) == list: pass else: content = content.split() notif = "commands:" if command in constants.Dict_set_keyword.keys(): command1 = constants.Dict_set_keyword[command] if len(content) != 0: for data in content: try: command1 = command1.format( f"\"{data}\"") notif += f"\nprocessing {command} {data}" exec(command1) except Exception as ex: notif += f"\nexcept: {ex}" pass else: try: notif += f"\nprocessing {command}" exec(command1) except Exception as ex: notif += f"\nexcept: {ex}" pass except Exception as ex: notif = "some commands failed" + \ f"\n{ex}" + f"\n{notif}" print(ex) pass finally: sent = api.send_direct_message(recipient_id=sender_id, text=notif).id self.delete_dm(sent) continue # check follower (Disable it when your followers are more than 5K) #elif int(sender_id) not in self.follower and sender_id != constants.Admin_id: # print("sender not in follower") # try: # notif = "Hmm kayaknya kamu belum follow base ini. Follow dulu ya biar bisa ngirim menfess" # sent = api.send_direct_message( # recipient_id=sender_id, text=notif).id # self.delete_dm(sent) # except Exception as ex: # print(ex) # sleep(30) # pass # continue # muted words list_muted = [i.lower() for i in constants.Muted_words] if any(i in message.lower() for i in list_muted) and sender_id != constants.Admin_id: try: print("deleting muted menfess") notif = "Menfess kamu mengandung muted words, jangan lupa baca peraturan base yaa!" sent = api.send_direct_message(recipient_id=sender_id, text=notif).id self.delete_dm(sent) except Exception as ex: sleep(60) print(ex) pass continue # Message filter # Based on Twitter rules https://help.twitter.com/en/rules-and-policies/twitter-search-policies # Similiarity checker notif_temp = 0 date_now = (datetime.now(timezone.utc) + timedelta(hours=constants.Timezone)).day if date_now != self.day: self.day = date_now self.message_db = tuple() for i in self.message_db: similiarity = SequenceMatcher(None, message, i).ratio() if similiarity == 1: print("Message similiarity is duplicate") sent = api.send_direct_message( recipient_id=sender_id, text= "Menfess kamu sama dengan menfess lain (hari ini). Coba gunakan pilihan kata yang lain!\nNote: Abaikan jika mendapat pesan ini setelah menfess terkirim." ) self.delete_dm(sent.id) notif_temp = 1 break elif similiarity > 0.9: print("Message similiarity is more than 0.9") sent = api.send_direct_message( recipient_id=sender_id, text= "Menfess kamu mirip dengan menfess lain (hari ini). Coba gunakan pilihan kata yang lain!" ) self.delete_dm(sent.id) notif_temp = 1 break if notif_temp == 0: self.message_db += (message, ) else: continue # primary keywords if constants.First_Keyword.lower() in message.lower(): print("Getting message -> by sender id " + str(sender_id)) # attachment_url (retweet) url = None urls = message_data['entities']['urls'] if len(urls) != 0: for i in urls: if "https://twitter.com/" in i['expanded_url']: if "video" not in i['expanded_url']: if "photo" not in i['expanded_url']: if "media" not in i['expanded_url']: url = (i['url'], i['expanded_url']) # attachment if 'attachment' not in message_data: print("DM does not have any media..") d = dict(message=message, sender_id=sender_id, id=id, media=None, url=url) dms.append(d) else: print("DM have an attachment") media = message_data['attachment']['media'] media_type = media['type'] # photo if media_type == 'photo': photo_url = media['media_url'] d = dict(message=message, sender_id=sender_id, id=dm[x].id, media=photo_url, type=media_type, url=url) dms.append(d) # video elif media_type == 'video': media_url = media['video_info']['variants'] temp_bitrate = list() for varian in media_url: if varian['content_type'] == "video/mp4": temp_bitrate.append( (varian['bitrate'], varian['url'])) temp_bitrate.sort() temp_bitrate.reverse() video_url = temp_bitrate[0][1] d = dict(message=message, sender_id=sender_id, id=dm[x].id, media=video_url, type=media_type, url=url) dms.append(d) # animation_gif elif media_type == 'animated_gif': media_url = media['video_info']['variants'][0] video_url = media_url['url'] d = dict(message=message, sender_id=sender_id, id=dm[x].id, media=video_url, type=media_type, url=url) dms.append(d) else: try: print("deleting message (keyword not in message)") notif = "Keyword yang kamu kirim salah!" sent = api.send_direct_message(recipient_id=sender_id, text=notif).id self.delete_dm(sent) except Exception as ex: sleep(60) print(ex) pass print(str(len(dms)) + " collected") if len(dms) > 1: dms.reverse() self.random_time = randrange(0, 5) x = 0 y = 0 time = datetime.now( timezone.utc) + timedelta(hours=constants.Timezone) for i in dms: y += 1 x += (len(i['message']) // 272) + 1 if i['media'] != None: x += 0.2 sent_time = time + timedelta(minutes=1, seconds=self.random_time + x * (25 + self.random_time)) hour = sent_time.hour minute = sent_time.minute if hour < 10: hour = f"0{hour}" if minute < 10: minute = f"0{minute}" sent_time = f"{str(hour)}:{str(minute)}" notif = f"Menfess kamu berada pada urutan ke-{str(y)}, akan terkirim sekitar pukul {sent_time}" sent = api.send_direct_message(recipient_id=i['sender_id'], text=notif) self.delete_dm(sent.id) sleep(60 + self.random_time) return dms except Exception as ex: pass print(ex) sleep(60) return dms def delete_dm(self, id): ''' delete a DM :param id: message id -> int or str ''' print("Deleting dm with id = " + str(id)) try: self.api.destroy_direct_message(id) except Exception as ex: print(ex) sleep(60) pass def get_user_screen_name(self, id): ''' get username :param id: account id -> int :returns: username -> str ''' try: print("Getting username") user = self.api.get_user(id) return user.screen_name except Exception as ex: pass print(ex) user = "******" sleep(60) return user def Thread(self, name, file_type, tweet, media_ids=None, attachment_url=None): ''' tweet a thread :param name: filename of the file -> str :param file_type: ('photo', 'video', 'animated_gif', 'normal' or 'retweet') -> str :param tweet: -> str :param media_ids: media id -> list :param attachment_url: url -> str :returns: tweet id -> str ''' print("Tweeting a Thread") try: left = 0 right = 272 check = tweet[:right].split() separator = len(check[-1]) if tweet[right - 1] == " ": separator += 1 tweet1 = unescape(tweet[left:right - separator]) + '(cont)' if file_type == 'photo' or file_type == 'animated_gif' or file_type == 'video': media_id = self.media_upload_chunk(name) media_ids = list() media_ids.append(media_id) complete = self.api.update_status( tweet1, media_ids=media_ids, attachment_url=attachment_url).id elif file_type == 'normal': complete = self.api.update_status( tweet1, attachment_url=attachment_url).id elif file_type == "retweet": complete = self.api.update_status( tweet1, media_ids=media_ids, attachment_url=attachment_url).id sleep(25 + self.random_time) postid = str(complete) tweet2 = tweet[right - separator:] while len(tweet2) > 280: left += 272 - separator right += 272 - separator check = tweet[:right].split() separator = len(check[-1]) if tweet[right - 1] == " ": separator += 1 tweet2 = unescape(tweet[left:right - separator]) + '(cont)' complete = self.api.update_status( tweet2, in_reply_to_status_id=complete, auto_populate_reply_metadata=True).id sleep(25 + self.random_time) tweet2 = tweet[right - separator:] tweet2 = unescape(tweet2) self.api.update_status(tweet2, in_reply_to_status_id=complete, auto_populate_reply_metadata=True) sleep(25 + self.random_time) return postid except Exception as ex: pass print(ex) sleep(30) return None def post_tweet(self, tweet, attachment_url=None): ''' tweet a normal tweet :param tweet: -> str :param attachment_url: url -> str :returns: tweet id -> str ''' try: max_char = len(tweet) if max_char <= 280: postid = self.api.update_status( unescape(tweet), attachment_url=attachment_url).id sleep(25 + self.random_time) elif max_char > 280: postid = self.Thread(None, "normal", tweet, None, attachment_url) return postid except Exception as ex: pass sleep(60) print(ex) return None def download_media(self, media_url, filename=None): ''' download media from url save the filename :param media_url: url -> string :param filename: None (default) or filename --> str :returns: file name (when filename=None) -> str ''' try: print("Downloading media...") oauth = OAuth1(client_key=constants.CONSUMER_KEY, client_secret=constants.CONSUMER_SECRET, resource_owner_key=constants.ACCESS_KEY, resource_owner_secret=constants.ACCESS_SECRET) r = get(media_url, auth=oauth) if filename == None: filename = media_url.replace('/', ' ') filename = filename.replace('?', ' ') filename = filename.split() filename1 = filename[-1] if "?" in media_url: filename1 = filename[-2] filename = str(filename1) with open(filename, 'wb') as f: f.write(r.content) f.close() if exists(filename) == False: sleep(3) print("Download media successfully") return filename except Exception as ex: print(ex) pass def media_upload_chunk(self, filename, media_category=True): ''' upload media with chunk :param filename: -> str :param media_category: True for tweet, False for DM :returns: media id -> str ''' try: mediaupload = MediaUpload(filename, media_category) media_id = mediaupload.upload_init() mediaupload.upload_append() mediaupload.upload_finalize() return str(media_id) except Exception as ex: print(ex) pass def post_tweet_with_media(self, tweet, media_url, file_type, attachment_url=None): ''' tweet a tweet with media :param tweet: -> str :param media_url: url -> str :param file_type: ('photo', 'video', or 'animated_gif') -> str :returns: tweet id -> str ''' try: tweet = tweet.split() tweet = " ".join(tweet[:-1]) max_char = len(tweet) if max_char <= 280: filename = self.download_media(media_url) media_id = self.media_upload_chunk(filename) media_ids = [media_id] postid = self.api.update_status( unescape(tweet), media_ids=media_ids, attachment_url=attachment_url).id sleep(25 + self.random_time) elif max_char > 280: postid = self.Thread(filename, file_type, tweet, None, attachment_url) remove(filename) print("Upload with media success!") return postid except Exception as ex: pass sleep(60) print(ex) return None
class TweepyApi(BaseTweepyApi, Api): """ A `Api` implementation using `tweepy` library. http://github.com/tweepy/tweepy/ """ def __init__(self, *args, **kwargs): Api.__init__(self, *args, **kwargs) # conversion to `turses.models` def _to_status(self, statuses): def to_status(status): text = status.text is_reply = False in_reply_to_user = '' is_retweet = False retweet_count = 0 is_favorite = False author = '' if getattr(status, 'retweeted_status', False): is_retweet = True text = status.retweeted_status.text retweet_count = status.retweet_count author = status.retweeted_status.author.screen_name if status.in_reply_to_screen_name: is_reply = True in_reply_to_user = status.in_reply_to_screen_name if status.favorited: is_favorite = True kwargs = { 'id': status.id, 'created_at': status.created_at, 'user': status.user.screen_name, 'text': text, 'is_retweet': is_retweet, 'is_reply': is_reply, 'is_favorite': is_favorite, 'in_reply_to_user': in_reply_to_user, 'retweet_count': retweet_count, 'author': author, } return Status(**kwargs) if isinstance(statuses, list): return [to_status(status) for status in statuses] else: return to_status(statuses) def _to_direct_message(self, dms): def to_direct_message(dm): kwargs = { 'id': dm.id, 'created_at': dm.created_at, 'sender_screen_name': dm.sender_screen_name, 'recipient_screen_name': dm.recipient_screen_name, 'text': dm.text, } return DirectMessage(**kwargs) if isinstance(dms, list): return [to_direct_message(dm) for dm in dms] else: return to_direct_message(dms) def _to_list(self, lists): def to_list(l): created_at = datetime_from_twitter_datestring(l.created_at) if l.mode == u'private': private = True else: private = False kwargs = { 'id': l.id, 'owner': l.user.screen_name, 'created_at': created_at, 'name': l.name, 'description': l.description, 'member_count': l.member_count, 'subscriber_count': l.subscriber_count, 'private': private, } return List(**kwargs) if isinstance(lists, list): return [to_list(l) for l in lists] else: return to_list(lists) # from `turses.api.base.Api` def init_api(self): oauth_handler = TweepyOAuthHandler(self._consumer_key, self._consumer_secret) oauth_handler.set_access_token(self._access_token_key, self._access_token_secret) self._api = BaseTweepyApi(oauth_handler) def verify_credentials(self): def to_user(user): kwargs = { 'screen_name': user.screen_name, } return User(**kwargs) return to_user(self._api.me()) # timelines def get_home_timeline(self, **kwargs): tweets = self._api.home_timeline(**kwargs) retweets = self._api.retweeted_to_me(**kwargs) tweets.extend(retweets) return self._to_status(tweets) def get_user_timeline(self, screen_name, **kwargs): return self._to_status(self._api.user_timeline(screen_name, **kwargs)) def get_own_timeline(self, **kwargs): me = self.verify_credentials() tweets = self._api.user_timeline(screen_name=me.screen_name, **kwargs) retweets = self._api.retweeted_by_me(**kwargs) tweets.extend(retweets) return self._to_status(tweets) def get_mentions(self, **kwargs): return self._to_status(self._api.mentions(**kwargs)) def get_favorites(self, **kwargs): return self._to_status(self._api.favorites(**kwargs)) def get_direct_messages(self, **kwargs): dms = self._api.direct_messages(**kwargs) sent = self._api.sent_direct_messages(**kwargs) dms.extend(sent) return self._to_direct_message(dms) def get_thread(self, status, **kwargs): author = get_authors_username(status) mentioned = get_mentioned_usernames(status) if author not in mentioned: mentioned.append(author) tweets = [] for username in mentioned: tweets.extend(self.get_user_timeline(username, **kwargs)) def belongs_to_conversation(status): for username in mentioned: if username in status.text: return True return filter(belongs_to_conversation, tweets) def get_search(self, text, **kwargs): # `tweepy.API.search` returns `tweepy.models.SearchResult` objects instead # `tweepy.models.Status` so we have to convert them differently def to_status(status): kwargs = { 'id': status.id, 'created_at': status.created_at, 'user': status.from_user, 'text': status.text, } return Status(**kwargs) results = self._api.search(text, **kwargs) return [to_status(result) for result in results] def update(self, text): return self._api.update_status(text) def destroy_status(self, status): return self._to_status(self._api.destroy_status(status.id)) def retweet(self, status): return self._to_status(self._api.retweet(status.id)) def direct_message(self, username, text): return self._to_direct_message(self._api.send_direct_message(user=username, text=text)) def destroy_direct_message(self, dm): return self._to_direct_message(self._api.destroy_direct_message(dm.id)) # TODO: convert to `turses.models.User` def create_friendship(self, screen_name): self._api.create_friendship(screen_name=screen_name) def destroy_friendship(self, screen_name): self._api.destroy_friendship(screen_name=screen_name) def create_favorite(self, status): self._to_status(self._api.create_favorite(status.id)) def destroy_favorite(self, status): self._to_status(self._api.destroy_favorite(status.id)) # list methods def get_lists(self, screen_name): raise NotImplementedError def get_own_lists(self): raise NotImplementedError def get_list_memberships(self): raise NotImplementedError def get_list_subscriptions(self): raise NotImplementedError def get_list_timeline(self, list): raise NotImplementedError def get_list_members(self, list): raise NotImplementedError def is_list_member(self, user, list): raise NotImplementedError def subscribe_to_list(self, list): raise NotImplementedError def get_list_subscribers(self, list): raise NotImplementedError def is_list_subscriber(self, user, list): raise NotImplementedError
class TweepyAPITests(unittest.TestCase): def setUp(self): auth = OAuthHandler(oauth_consumer_key, oauth_consumer_secret) auth.set_access_token(oauth_token, oauth_token_secret) self.api = API(auth) self.api.retry_count = 2 self.api.retry_delay = 5 def testhometimeline(self): self.api.home_timeline() def testfriendstimeline(self): self.api.friends_timeline() def testusertimeline(self): self.api.user_timeline() self.api.user_timeline('twitter') def testmentions(self): self.api.mentions() def testretweetedbyme(self): self.api.retweeted_by_me() def testretweetedbyuser(self): self.api.retweeted_by_user('twitter') def testretweetedtome(self): self.api.retweeted_to_me() def testretweetsofme(self): self.api.retweets_of_me() def testretweet(self): s = self.api.retweet(123) s.destroy() def testretweets(self): self.api.retweets(123) def testgetstatus(self): self.api.get_status(id=123) def testupdateanddestroystatus(self): # test update text = 'testing %i' % random.randint(0, 1000) update = self.api.update_status(status=text) self.assertEqual(update.text, text) # test destroy deleted = self.api.destroy_status(id=update.id) self.assertEqual(deleted.id, update.id) def testgetuser(self): u = self.api.get_user('twitter') self.assertEqual(u.screen_name, 'twitter') u = self.api.get_user(783214) self.assertEqual(u.screen_name, 'twitter') def testsearchusers(self): self.api.search_users('twitter') def testme(self): me = self.api.me() self.assertEqual(me.screen_name, username) def testfriends(self): self.api.friends() def testfollowers(self): self.api.followers() def testdirectmessages(self): self.api.direct_messages() def testsentdirectmessages(self): self.api.sent_direct_messages() def testsendanddestroydirectmessage(self): # send sent_dm = self.api.send_direct_message(username, text='test message') self.assertEqual(sent_dm.text, 'test message') self.assertEqual(sent_dm.sender.screen_name, username) self.assertEqual(sent_dm.recipient.screen_name, username) # destroy destroyed_dm = self.api.destroy_direct_message(sent_dm.id) self.assertEqual(destroyed_dm.text, sent_dm.text) self.assertEqual(destroyed_dm.id, sent_dm.id) self.assertEqual(destroyed_dm.sender.screen_name, username) self.assertEqual(destroyed_dm.recipient.screen_name, username) def testcreatedestroyfriendship(self): enemy = self.api.destroy_friendship('twitter') self.assertEqual(enemy.screen_name, 'twitter') self.assertFalse(self.api.exists_friendship(username, 'twitter')) friend = self.api.create_friendship('twitter') self.assertEqual(friend.screen_name, 'twitter') self.assertTrue(self.api.exists_friendship(username, 'twitter')) def testshowfriendship(self): source, target = self.api.show_friendship(target_screen_name='twtiter') self.assert_(isinstance(source, Friendship)) self.assert_(isinstance(target, Friendship)) def testfriendsids(self): self.api.friends_ids(username) def testfollowersids(self): self.api.followers_ids(username) def testverifycredentials(self): self.assertNotEqual(self.api.verify_credentials(), False) # make sure that `me.status.entities` is not an empty dict me = self.api.verify_credentials(include_entities=True) self.assertTrue(me.status.entities) # `status` shouldn't be included me = self.api.verify_credentials(skip_status=True) self.assertFalse(hasattr(me, 'status')) api = API(BasicAuthHandler('bad', 'password')) self.assertEqual(api.verify_credentials(), False) def testratelimitstatus(self): self.api.rate_limit_status() def testsetdeliverydevice(self): self.api.set_delivery_device('im') self.api.set_delivery_device('none') def testupdateprofilecolors(self): original = self.api.me() updated = self.api.update_profile_colors('000', '000', '000', '000', '000') # restore colors self.api.update_profile_colors(original.profile_background_color, original.profile_text_color, original.profile_link_color, original.profile_sidebar_fill_color, original.profile_sidebar_border_color) self.assertEqual(updated.profile_background_color, '000') self.assertEqual(updated.profile_text_color, '000') self.assertEqual(updated.profile_link_color, '000') self.assertEqual(updated.profile_sidebar_fill_color, '000') self.assertEqual(updated.profile_sidebar_border_color, '000') """ def testupateprofileimage(self): self.api.update_profile_image('examples/profile.png') def testupdateprofilebg(self): self.api.update_profile_background_image('examples/bg.png') """ def testupdateprofile(self): original = self.api.me() profile = { 'name': 'Tweepy test 123', 'url': 'http://www.example.com', 'location': 'pytopia', 'description': 'just testing things out' } updated = self.api.update_profile(**profile) self.api.update_profile(name=original.name, url=original.url, location=original.location, description=original.description) for k, v in profile.items(): if k == 'email': continue self.assertEqual(getattr(updated, k), v) def testfavorites(self): self.api.favorites() def testcreatedestroyfavorite(self): self.api.create_favorite(4901062372) self.api.destroy_favorite(4901062372) def testenabledisablenotifications(self): self.api.enable_notifications('twitter') self.api.disable_notifications('twitter') def testcreatedestroyblock(self): self.api.create_block('twitter') self.assertEqual(self.api.exists_block('twitter'), True) self.api.destroy_block('twitter') self.assertEqual(self.api.exists_block('twitter'), False) self.api.create_friendship('twitter') # restore def testblocks(self): self.api.blocks() def testblocksids(self): self.api.blocks_ids() def testcreateupdatedestroylist(self): self.api.create_list('tweeps') # XXX: right now twitter throws a 500 here, issue is being looked into by twitter. #self.api.update_list('tweeps', mode='private') self.api.destroy_list('tweeps') def testlists(self): self.api.lists() def testlistsmemberships(self): self.api.lists_memberships() def testlistssubscriptions(self): self.api.lists_subscriptions() def testlisttimeline(self): self.api.list_timeline('applepie', 'stars') def testgetlist(self): self.api.get_list('applepie', 'stars') def testaddremovelistmember(self): uid = self.api.get_user('twitter').id self.api.add_list_member('test', uid) self.api.remove_list_member('test', uid) def testlistmembers(self): self.api.list_members('applepie', 'stars') def testislistmember(self): uid = self.api.get_user('applepie').id self.api.is_list_member('applepie', 'stars', uid) def testsubscribeunsubscribelist(self): self.api.subscribe_list('applepie', 'stars') self.api.unsubscribe_list('applepie', 'stars') def testlistsubscribers(self): self.api.list_subscribers('applepie', 'stars') def testissubscribedlist(self): uid = self.api.get_user('applepie').id self.api.is_subscribed_list('applepie', 'stars', uid) def testsavedsearches(self): s = self.api.create_saved_search('test') self.api.saved_searches() self.assertEqual(self.api.get_saved_search(s.id).query, 'test') self.api.destroy_saved_search(s.id) def testsearch(self): self.api.search('tweepy') def testtrends(self): self.api.trends() self.api.trends_current() self.api.trends_daily() self.api.trends_weekly() def testgeoapis(self): self.api.geo_id(id='c3f37afa9efcf94b') # Austin, TX, USA self.api.nearby_places(lat=30.267370168467806, long=-97.74261474609375) # Austin, TX, USA self.api.reverse_geocode(lat=30.267370168467806, long=-97.74261474609375) # Austin, TX, USA
class TweepyAPITests(unittest.TestCase): def setUp(self): auth = OAuthHandler(oauth_consumer_key, oauth_consumer_secret) auth.set_access_token(oauth_token, oauth_token_secret) self.api = API(auth) self.api.retry_count = 2 self.api.retry_delay = 5 # TODO: Actually have some sort of better assertion def testgetoembed(self): data = self.api.get_oembed(test_tweet_id) self.assertEqual(data['author_name'], "Twitter") def testhometimeline(self): self.api.home_timeline() def testusertimeline(self): self.api.user_timeline() self.api.user_timeline('twitter') def testmentionstimeline(self): self.api.mentions_timeline() def testretweetsofme(self): self.api.retweets_of_me() def testretweet(self): # TODO(josh): Need a way to get random tweets to retweet. raise SkipTest() def testretweets(self): self.api.retweets(test_tweet_id) def testgetstatus(self): self.api.get_status(id=test_tweet_id) def testupdateanddestroystatus(self): # test update text = 'testing %i' % random.randint(0, 1000) update = self.api.update_status(status=text) self.assertEqual(update.text, text) # test destroy deleted = self.api.destroy_status(id=update.id) self.assertEqual(deleted.id, update.id) def testgetuser(self): u = self.api.get_user('twitter') self.assertEqual(u.screen_name, 'twitter') u = self.api.get_user(783214) self.assertEqual(u.screen_name, 'twitter') def testsearchusers(self): self.api.search_users('twitter') def testsuggestedcategories(self): self.api.suggested_categories() def testsuggestedusers(self): categories = self.api.suggested_categories() if len(categories) != 0: self.api.suggested_users(categories[0].slug) def testsuggesteduserstweets(self): categories = self.api.suggested_categories() if len(categories) != 0: self.api.suggested_users_tweets(categories[0].slug) def testme(self): me = self.api.me() self.assertEqual(me.screen_name, username) def testdirectmessages(self): self.api.direct_messages() def testsentdirectmessages(self): self.api.sent_direct_messages() def testsendanddestroydirectmessage(self): # send sent_dm = self.api.send_direct_message(username, text='test message') self.assertEqual(sent_dm.text, 'test message') self.assertEqual(sent_dm.sender.screen_name, username) self.assertEqual(sent_dm.recipient.screen_name, username) # destroy destroyed_dm = self.api.destroy_direct_message(sent_dm.id) self.assertEqual(destroyed_dm.text, sent_dm.text) self.assertEqual(destroyed_dm.id, sent_dm.id) self.assertEqual(destroyed_dm.sender.screen_name, username) self.assertEqual(destroyed_dm.recipient.screen_name, username) def testcreatedestroyfriendship(self): enemy = self.api.destroy_friendship('twitter') self.assertEqual(enemy.screen_name, 'twitter') # Wait 5 seconds to allow Twitter time # to process the friendship destroy request. sleep(5) friend = self.api.create_friendship('twitter') self.assertEqual(friend.screen_name, 'twitter') def testshowfriendship(self): source, target = self.api.show_friendship(target_screen_name='twtiter') self.assert_(isinstance(source, Friendship)) self.assert_(isinstance(target, Friendship)) def testfriendsids(self): self.api.friends_ids(username) def testfollowersids(self): self.api.followers_ids(username) def testfriends(self): self.api.friends(username) def testfollowers(self): self.api.followers(username) def testverifycredentials(self): self.assertNotEqual(self.api.verify_credentials(), False) # make sure that `me.status.entities` is not an empty dict me = self.api.verify_credentials(include_entities=True) self.assertTrue(me.status.entities) # `status` shouldn't be included me = self.api.verify_credentials(skip_status=True) self.assertFalse(hasattr(me, 'status')) def testratelimitstatus(self): self.api.rate_limit_status() """ TODO(josh): Remove once this deprecated API is gone. def testsetdeliverydevice(self): self.api.set_delivery_device('im') self.api.set_delivery_device('none') """ def testupdateprofilecolors(self): original = self.api.me() updated = self.api.update_profile_colors('000', '000', '000', '000', '000') # restore colors self.api.update_profile_colors(original.profile_background_color, original.profile_text_color, original.profile_link_color, original.profile_sidebar_fill_color, original.profile_sidebar_border_color) self.assertEqual(updated.profile_background_color, '000000') self.assertEqual(updated.profile_text_color, '000000') self.assertEqual(updated.profile_link_color, '000000') self.assertEqual(updated.profile_sidebar_fill_color, '000000') self.assertEqual(updated.profile_sidebar_border_color, '000000') """ def testupateprofileimage(self): self.api.update_profile_image('examples/profile.png') def testupdateprofilebg(self): self.api.update_profile_background_image('examples/bg.png') """ def testupdateprofile(self): original = self.api.me() profile = { 'name': 'Tweepy test 123', 'location': 'pytopia', 'description': 'just testing things out' } updated = self.api.update_profile(**profile) self.api.update_profile(name=original.name, url=original.url, location=original.location, description=original.description) for k, v in profile.items(): if k == 'email': continue self.assertEqual(getattr(updated, k), v) def testfavorites(self): self.api.favorites() def testcreatedestroyfavorite(self): self.api.create_favorite(4901062372) self.api.destroy_favorite(4901062372) def testcreatedestroyblock(self): self.api.create_block('twitter') self.api.destroy_block('twitter') self.api.create_friendship('twitter') # restore def testblocks(self): self.api.blocks() def testblocksids(self): self.api.blocks_ids() def testcreateupdatedestroylist(self): params = {'owner_screen_name': username, 'slug': 'tweeps'} l = self.api.create_list(name=params['slug'], **params) l = self.api.update_list(list_id=l.id, description='updated!') self.assertEqual(l.description, 'updated!') self.api.destroy_list(list_id=l.id) def testlistsall(self): self.api.lists_all() def testlistsmemberships(self): self.api.lists_memberships() def testlistssubscriptions(self): self.api.lists_subscriptions() def testlisttimeline(self): self.api.list_timeline('applepie', 'stars') def testgetlist(self): self.api.get_list(owner_screen_name='applepie', slug='stars') def testaddremovelistmember(self): params = { 'slug': 'test', 'owner_screen_name': username, 'screen_name': 'twitter' } def assert_list(l): self.assertEqual(l.name, params['slug']) assert_list(self.api.add_list_member(**params)) assert_list(self.api.remove_list_member(**params)) def testlistmembers(self): self.api.list_members('applepie', 'stars') def testshowlistmember(self): self.assertTrue( self.api.show_list_member(owner_screen_name='applepie', slug='stars', screen_name='NathanFillion')) def testsubscribeunsubscribelist(self): params = {'owner_screen_name': 'applepie', 'slug': 'stars'} self.api.subscribe_list(**params) self.api.unsubscribe_list(**params) def testlistsubscribers(self): self.api.list_subscribers('applepie', 'stars') def testshowlistsubscriber(self): self.assertTrue( self.api.show_list_subscriber('twitter', 'team', username)) def testsavedsearches(self): s = self.api.create_saved_search('test') self.api.saved_searches() self.assertEqual(self.api.get_saved_search(s.id).query, 'test') self.api.destroy_saved_search(s.id) def testsearch(self): self.api.search('tweepy') def testgeoapis(self): def place_name_in_list(place_name, place_list): """Return True if a given place_name is in place_list.""" return any([ x.full_name.lower() == place_name.lower() for x in place_list ]) twitter_hq = self.api.geo_similar_places(lat=37, long=-122, name='Twitter HQ') # Assumes that twitter_hq is first Place returned... self.assertEqual(twitter_hq[0].id, '3bdf30ed8b201f31') # Test various API functions using Austin, TX, USA self.assertEqual( self.api.geo_id(id='c3f37afa9efcf94b').full_name, 'Austin, TX') self.assertTrue( place_name_in_list( 'Austin, TX', self.api.reverse_geocode( lat=30.267370168467806, long=-97.74261474609375))) # Austin, TX, USA
class TweepyApi(BaseTweepyApi, ApiAdapter): """ A `ApiAdapter` implementation using `tweepy` library. http://github.com/tweepy/tweepy/ """ def __init__(self, *args, **kwargs): ApiAdapter.__init__(self, *args, **kwargs) # from `turses.api.base.ApiAdapter` def init_api(self): oauth_handler = TweepyOAuthHandler(self._consumer_key, self._consumer_secret) oauth_handler.set_access_token(self._access_token_key, self._access_token_secret) self._api = BaseTweepyApi(oauth_handler) @to_user def verify_credentials(self): return self._api.me() @to_user @include_entities def get_user(self, screen_name, **kwargs): return self._api.get_user(screen_name=screen_name, **kwargs) # timelines @to_status @include_entities def get_home_timeline(self, **kwargs): tweets = self._api.home_timeline(**kwargs) retweets = self._api.retweeted_to_me(**kwargs) tweets.extend(retweets) return tweets @to_status @include_entities def get_user_timeline(self, screen_name, **kwargs): return self._api.user_timeline(screen_name, **kwargs) @to_status @include_entities def get_own_timeline(self, **kwargs): me = self.verify_credentials() tweets = self._api.user_timeline(screen_name=me.screen_name, **kwargs) retweets = self._api.retweeted_by_me(**kwargs) tweets.extend(retweets) return tweets @to_status @include_entities def get_mentions(self, **kwargs): return self._api.mentions(**kwargs) @to_status @include_entities def get_favorites(self, **kwargs): return self._api.favorites(**kwargs) @to_direct_message @include_entities def get_direct_messages(self, **kwargs): dms = self._api.direct_messages(**kwargs) sent = self._api.sent_direct_messages(**kwargs) dms.extend(sent) return dms # NOTE: # `get_thread` is not decorated with `to_status` because # it uses `TweepyApi.get_user_timeline` which is already # decorated @include_entities def get_thread(self, status, **kwargs): """ Get the conversation to which `status` belongs. It filters the last tweets by the participanting users and based on mentions to each other. """ author = status.authors_username mentioned = status.mentioned_usernames if author not in mentioned: mentioned.append(author) tweets = [] for username in mentioned: tweets.extend(self.get_user_timeline(username, **kwargs)) def belongs_to_conversation(status): for username in mentioned: if username in status.text: return True return filter(belongs_to_conversation, tweets) @to_status_from_search @include_entities def search(self, text, **kwargs): return self._api.search(text, **kwargs) @to_status @include_entities def get_retweets_of_me(self, **kwargs): return self._api.retweets_of_me(**kwargs) def update(self, text): self._api.update_status(text) def destroy_status(self, status): self._api.destroy_status(status.id) def retweet(self, status): self._api.retweet(status.id) def direct_message(self, username, text): self._api.send_direct_message(user=username, text=text) def destroy_direct_message(self, dm): self._api.destroy_direct_message(dm.id) def create_friendship(self, screen_name): self._api.create_friendship(screen_name=screen_name) def destroy_friendship(self, screen_name): self._api.destroy_friendship(screen_name=screen_name) def create_favorite(self, status): self._api.create_favorite(status.id) def destroy_favorite(self, status): self._api.destroy_favorite(status.id) # list methods @to_list def get_lists(self, screen_name): return self._api.lists(screen_name) @to_list def get_own_lists(self): return self._api.lists() @to_list def get_list_memberships(self): return self._api.lists_memberships() @to_list def get_list_subscriptions(self): return self._api.lists_subscriptions() @to_status def get_list_timeline(self, a_list): owner = a_list.owner.screen_name return self._api.list_timeline(owner=owner, slug=a_list.slug) @to_user def get_list_members(self, a_list): owner = a_list.owner.screen_name return self._api.list_members(owner=owner, slug=a_list.slug) def is_list_member(self, user, a_list): return bool(self._api.is_list_member(owner=user.screen_name, slug=a_list.slug, user_id=user.id,)) @to_list def subscribe_to_list(self, a_list): owner = a_list.owner return self._api.subscribe_list(owner=owner.screen_name, slug=a_list.slug) @to_user def get_list_subscribers(self, a_list): owner = a_list.owner return self._api.list_subscribers(owner=owner.screen_name, slug=a_list.slug,) def is_list_subscriber(self, user, a_list): return bool(self._api.is_subscribed_list(owner=user.screen_name, slug=a_list.slug, user_id=user.id,))
class TweepyApi(BaseTweepyApi, Api): """ A `Api` implementation using `tweepy` library. http://github.com/tweepy/tweepy/ """ def __init__(self, *args, **kwargs): Api.__init__(self, *args, **kwargs) # conversion to `turses.models` def _to_status(self, statuses): def to_status(status): kwargs = { 'id': status.id, 'created_at': status.created_at, 'user': status.user.screen_name, 'text': status.text, } return Status(**kwargs) if isinstance(statuses, list): return [to_status(status) for status in statuses] else: return to_status(statuses) def _to_direct_message(self, dms): def to_direct_message(dm): kwargs = { 'id': dm.id, 'created_at': dm.created_at, 'sender_screen_name': dm.sender_screen_name, 'recipient_screen_name': dm.recipient_screen_name, 'text': dm.text, } return DirectMessage(**kwargs) if isinstance(dms, list): return [to_direct_message(dm) for dm in dms] else: return to_direct_message(dms) def _to_list(self, lists): def to_list(l): created_at = datetime_from_twitter_datestring(l.created_at) if l.mode == u'private': private = True else: private = False kwargs = { 'id': l.id, 'owner': l.user.screen_name, 'created_at': created_at, 'name': l.name, 'description': l.description, 'member_count': l.member_count, 'subscriber_count': l.subscriber_count, 'private': private, } return List(**kwargs) if isinstance(lists, list): return [to_list(l) for l in lists] else: return to_list(lists) # from `turses.api.base.Api` def init_api(self): oauth_handler = TweepyOAuthHandler(self._consumer_key, self._consumer_secret) oauth_handler.set_access_token(self._access_token_key, self._access_token_secret) self._api = BaseTweepyApi(oauth_handler) def verify_credentials(self): def to_user(user): kwargs = { 'screen_name': user.screen_name, } return User(**kwargs) return to_user(self._api.me()) # timelines def get_home_timeline(self, **kwargs): tweets = self._api.home_timeline(**kwargs) retweets = self._api.retweeted_to_me(**kwargs) tweets.extend(retweets) return self._to_status(tweets) def get_user_timeline(self, screen_name, **kwargs): return self._to_status(self._api.user_timeline(screen_name, **kwargs)) def get_own_timeline(self, **kwargs): me = self.verify_credentials() tweets = self._api.user_timeline(screen_name=me.screen_name, **kwargs) retweets = self._api.retweeted_by_me(**kwargs) tweets.extend(retweets) return self._to_status(tweets) def get_mentions(self, **kwargs): return self._to_status(self._api.mentions(**kwargs)) def get_favorites(self, **kwargs): return self._to_status(self._api.favorites(**kwargs)) def get_direct_messages(self, **kwargs): dms = self._api.direct_messages(**kwargs) sent = self._api.sent_direct_messages(**kwargs) dms.extend(sent) return self._to_direct_message(dms) def get_thread(self, status, **kwargs): author = get_authors_username(status) mentioned = get_mentioned_usernames(status) if author not in mentioned: mentioned.append(author) tweets = [] for username in mentioned: tweets.extend(self.get_user_timeline(username, **kwargs)) def belongs_to_conversation(status): for username in mentioned: if username in status.text: return True return filter(belongs_to_conversation, tweets) def get_search(self, text, **kwargs): # `tweepy.API.search` returns `tweepy.models.SearchResult` objects instead # `tweepy.models.Status` so we have to convert them differently def to_status(status): kwargs = { 'id': status.id, 'created_at': status.created_at, 'user': status.from_user, 'text': status.text, } return Status(**kwargs) results = self._api.search(text, **kwargs) return [to_status(result) for result in results] def update(self, text): return self._api.update_status(text) def destroy_status(self, status): return self._to_status(self._api.destroy_status(status.id)) def retweet(self, status): return self._to_status(self._api.retweet(status.id)) def direct_message(self, username, text): return self._to_direct_message( self._api.send_direct_message(user=username, text=text)) def destroy_direct_message(self, dm): return self._to_direct_message(self._api.destroy_direct_message(dm.id)) # TODO: convert to `turses.models.User` def create_friendship(self, screen_name): self._api.create_friendship(screen_name=screen_name) def destroy_friendship(self, screen_name): self._api.destroy_friendship(screen_name=screen_name) def create_favorite(self, status): self._to_status(self._api.create_favorite(status.id)) def destroy_favorite(self, status): self._to_status(self._api.destroy_favorite(status.id)) # list methods def get_lists(self, screen_name): raise NotImplementedError def get_own_lists(self): raise NotImplementedError def get_list_memberships(self): raise NotImplementedError def get_list_subscriptions(self): raise NotImplementedError def get_list_timeline(self, list): raise NotImplementedError def get_list_members(self, list): raise NotImplementedError def is_list_member(self, user, list): raise NotImplementedError def subscribe_to_list(self, list): raise NotImplementedError def get_list_subscribers(self, list): raise NotImplementedError def is_list_subscriber(self, user, list): raise NotImplementedError
class Twitter: ''' Control twitter account Attributes: - credential - api - me :param credential: object that contains attributes like config ''' def __init__(self, credential: object): ''' initialize twitter with tweepy :param credential: object that contains attributes like config ''' self.credential = credential print("Initializing twitter...") auth = OAuthHandler(credential.CONSUMER_KEY, credential.CONSUMER_SECRET) auth.set_access_token(credential.ACCESS_KEY, credential.ACCESS_SECRET) self.api = API(auth, wait_on_rate_limit=True, wait_on_rate_limit_notify=True) self.api.verify_credentials() self.me = self.api.me() def get_all_followers(self, user_id: str, first_delay: bool = True) -> list: '''Get all followers ids, Twitter API limits to get 5000 followers/minute :param first_delay: False: delete delay for the first get request :return: list of followers ids integer ''' try: print("Getting all followers ids...") ids = list() for page in Cursor(self.api.followers_ids, user_id=user_id).pages(): ids.extend(page) if first_delay is False: first_delay = True continue sleep(60) return ids except Exception as ex: pass print(ex) sleep(60) return list() def get_all_followed(self, user_id: str, first_delay: bool = True) -> list: '''Get all user ids that followed by bot, Twitter api limits to get 5000 followed/minute :param first_delay: False: delete delay for the first get :return: list of followers ids integer ''' try: print("Getting all friends ids...") ids = list() for page in Cursor(self.api.friends_ids, user_id=user_id).pages(): ids.extend(page) if first_delay is False: first_delay = True continue sleep(60) return ids except Exception as ex: pass print(ex) sleep(60) return list() def delete_dm(self, id: str) -> NoReturn: ''' :param id: message id ''' try: self.api.destroy_direct_message(id) except Exception as ex: print(ex) sleep(60) pass def send_dm(self, recipient_id: str, text: str) -> NoReturn: ''' :param recipient_id: account target ''' try: self.api.send_direct_message(recipient_id=recipient_id, text=text) except Exception as ex: pass print(ex) sleep(60) def get_user_screen_name(self, id: str) -> str: ''' :param id: account id :return: username ''' try: user = self.api.get_user(id) return user.screen_name except Exception as ex: pass print(ex) sleep(60) return "Exception" def download_media(self, media_url: str, filename: str = None) -> str: '''Download media from url :param media_url: url :param filename: None (default) or filename :return: file name (if filename==None) ''' print("Downloading media...") oauth = OAuth1(client_key=self.credential.CONSUMER_KEY, client_secret=self.credential.CONSUMER_SECRET, resource_owner_key=self.credential.ACCESS_KEY, resource_owner_secret=self.credential.ACCESS_SECRET) r = requests.get(media_url, auth=oauth) if filename == None: for i in re.sub("[/?=]", " ", media_url).split(): if re.search(r"\.mp4$|\.gif$|\.jpg$|\.jpeg$|\.png$|\.webp$", i): filename = i break if filename == None: raise Exception( "filename is not supported, please check the link") with open(filename, 'wb') as f: f.write(r.content) f.close() print("Download media successfully") return filename def add_watermark(self, filename: str, output: str = None) -> str: '''Add watermark to photo, then save as output. Only support photo type :returns: output file name ''' try: if output == None: output = filename file_type = filename.split('.')[-1] if file_type in "jpg jpeg png webp": print("Adding watermark...") adm = self.credential watermark.watermark_text_image( filename, text=adm.Watermark_text, font=adm.Watermark_font, ratio=adm.Watermark_ratio, pos=adm.Watermark_position, output=output, color=adm.Watermark_textColor, stroke_color=adm.Watermark_textStroke, watermark=adm.Watermark_image) return output except Exception as ex: pass print(ex) return filename def upload_media_tweet(self, media_tweet_url: str) -> list: '''Upload media from media tweet url Usually when sender wants to post more than one media, he will attachs media tweet url. But the sender's username is mentioned on the bottom of the media. This method intended to make sender anonym. you can add media_ids to other method. This method contains watermark module :param media_tweet_url: media tweet url i.e. https://twitter.com/username/status/123/photo/1 :return: [(media_id, media_type),] a.k.a media_idsAndTypes ''' try: postid = re.sub(r"[/\.:]", " ", media_tweet_url).split()[-3] status = self.api.get_status(postid) media_idsAndTypes = list() if 'extended_entities' not in status._json: return list() print("Uploading media tweet...") for media in status._json['extended_entities']['media']: media_type = media['type'] if media_type == 'photo': media_url = media['media_url'] elif media_type == 'video': media_urls = media['video_info']['variants'] temp_bitrate = list() for varian in media_urls: if varian['content_type'] == "video/mp4": temp_bitrate.append( (varian['bitrate'], varian['url'])) # sort to choose the highest bitrate temp_bitrate.sort() media_url = temp_bitrate[-1][1] elif media_type == 'animated_gif': media_url = media['video_info']['variants'][0]['url'] filename = self.download_media(media_url) # Add watermark if self.credential.Watermark is True: self.add_watermark(filename) media_id, media_type = self.upload_media(filename) remove(filename) media_idsAndTypes.append((media_id, media_type)) return media_idsAndTypes # e.g [(media_id, media_type), (media_id, media_type), ] except Exception as ex: pass print(ex) sleep(60) return list() def upload_media(self, filename: str, media_category: str = 'tweet') -> tuple: '''Upload media using twitter api v1.1 This method are needed when you want to use media to do something on twitter :param media_category: 'tweet' or 'dm'. default to 'tweet' :return: media id, media_type ''' mediaupload = MediaUpload(self.credential, filename, media_category) media_id, media_type = mediaupload.upload_init() mediaupload.upload_append() mediaupload.upload_finalize() return media_id, media_type def post_tweet(self, tweet: str, sender_id: str, media_url: str = None, attachment_url: str = None, media_idsAndTypes: list = list(), possibly_sensitive: bool = False) -> dict: '''Post a tweet, contains watermark module Per tweet delay is 36s + self.random_time, but the last delay is deleted :param tweet: message :param media_url: url of the media that sent from dm :param attachment_url: url that will be attached to twett (retweet) :param media_idsAndTypes: [(media_ids, media_type),] :param possibly_sensitive: True if menfess contains sensitive contents :return: {'postid': '', 'error_code': ''} -> dict ''' try: #### ADD MEDIA_ID AND MEDIA_TYPE TO LIST_MEDIA_IDS #### # media_idsAndTypes e.g. [(media_id, media_type), (media_id, media_type), ] if media_url != None: tweet = tweet.split(" ") tweet = " ".join(tweet[:-1]) filename = self.download_media(media_url) # Add watermark if self.credential.Watermark: self.add_watermark(filename) media_id, media_type = self.upload_media(filename) # Add attachment media from DM to the first order media_idsAndTypes.insert(0, (media_id, media_type)) remove(filename) list_media_ids = search_list_media_ids( media_idsAndTypes) #[[media_ids],[media_ids],[media_ids]] #### POST TWEET #### postid = 0 list_postid_thread = list() # used for #delete command # postid is the first tweet of the tweets thread while len(tweet) + count_emoji(tweet) > 280: # Making a Thread. limit = 272 # some emoticons count as 2 char limit -= count_emoji(tweet[:limit]) check = tweet[:limit].split(" ") if len(check) == 1: # avoid error when user send 272 char in one word separator = 0 else: separator = len(check[-1]) tweet_thread = unescape(tweet[:limit - separator]) + '-cont-' if postid == 0: print("Making a thread...") # postid is static after first update. postid = self.api.update_status( tweet_thread, attachment_url=attachment_url, media_ids=list_media_ids[:1][0], possibly_sensitive=possibly_sensitive).id postid_thread = str(postid) else: postid_thread = self.api.update_status( tweet_thread, in_reply_to_status_id=postid_thread, auto_populate_reply_metadata=True, media_ids=list_media_ids[:1][0], possibly_sensitive=possibly_sensitive).id list_postid_thread.append(postid_thread) list_media_ids = list_media_ids[1:] + [[]] sleep(36 + self.credential.Delay_time) # tweet are dynamic here tweet = tweet[limit - separator:] # Above and below operation differences are on tweet_thread and unescape(tweet), also tweet[limit-separator:] # It's possible to change it to be one function if postid == 0: # postid is static after first update. postid = self.api.update_status( unescape(tweet), attachment_url=attachment_url, media_ids=list_media_ids[:1][0], possibly_sensitive=possibly_sensitive).id postid_thread = str(postid) else: postid_thread = self.api.update_status( unescape(tweet), in_reply_to_status_id=postid_thread, auto_populate_reply_metadata=True, media_ids=list_media_ids[:1][0], possibly_sensitive=possibly_sensitive).id list_postid_thread.append(postid_thread) list_media_ids = list_media_ids[1:] + [[]] # When media_ids still exists, It will be attached to the subsequent tweets while len(list_media_ids[0] ) != 0: # Pay attention to the list format, [[]] sleep(36 + self.credential.Delay_time) print("Posting the rest of media...") postid_thread = self.api.update_status( in_reply_to_status_id=postid_thread, auto_populate_reply_metadata=True, media_ids=list_media_ids[:1][0], possibly_sensitive=possibly_sensitive).id list_postid_thread.append(postid_thread) list_media_ids = list_media_ids[1:] + [[]] print('Menfess is posted -> postid:', str(postid)) return { 'postid': str(postid), 'list_postid_thread': list_postid_thread } except Exception as ex: pass print(ex) return {'postid': None, 'error_code': 'post_tweet, ' + str(ex)}
class Twitter: ''' :param credential: class that contains objects like administrator_data -> object ''' def __init__(self, credential): ''' initialize twitter with tweepy Attributes: - credential - api - AdminCmd - UserCmd - me - follower - followed - random_time - db_sent - day - db_received - indicator_start - db_intervalTime :param credential: class that contains objects like administrator_data -> object ''' self.credential = credential print("Initializing twitter...") auth = OAuthHandler( credential.CONSUMER_KEY, credential.CONSUMER_SECRET) auth.set_access_token( credential.ACCESS_KEY, credential.ACCESS_SECRET) self.api = API( auth, wait_on_rate_limit=True, wait_on_rate_limit_notify=True) self.AdminCmd = AdminCommand(self.api, credential) self.UserCmd = UserCommand(self.api, credential) self.me = self.api.me() self.follower = list() # list of integer self.followed = list() # list of integer self.random_time = credential.Delay_time self.db_sent = dict() # dict of sender and his postid, update every midnight with self.day self.day = (datetime.now(timezone.utc) + timedelta(hours=credential.Timezone)).day self.db_received = list() # list of 55 received menfess's message id self.indicator_start = False self.db_intervalTime = dict() def get_all_followers(self, user_id, first_delay=True): '''Return all followers ids Twitter API limiting to get 5000 followers/minute :param user_id: User id -> int or str :param first_delay: False: delete delay for first operation -> bool :returns: list of followers ids integer ''' try: print("Getting all followers ids...") ids = list() for page in Cursor(self.api.followers_ids, user_id=user_id).pages(): ids.extend(page) if first_delay is False: first_delay = True continue sleep(60) return ids except Exception as ex: pass print(ex) sleep(60) return list() def get_all_followed(self, user_id, first_delay=True): '''Get all account ids that followed by screen_name Twitter api limiting to get 5000 followed/minute :param user_id: user id -> str or int :param first_delay: False: delete delay for first operation -> bool :returns: list of followers ids integer ''' try: print("Getting all friends ids...") ids = list() for page in Cursor(self.api.friends_ids, user_id=user_id).pages(): ids.extend(page) if first_delay is False: first_delay = True continue sleep(60) return ids except Exception as ex: pass print(ex) sleep(60) return list() def db_sent_updater(self, action, sender_id=None, postid=None): '''Update self.db_sent :param action: 'update','add' or 'delete' -> str :param sender_id: sender id who has sent the menfess -> str :param postid: tweet id or (sender_id, tweet id) -> str or tuple ''' try: if action == 'update': day = (datetime.now(timezone.utc) + timedelta(hours=self.credential.Timezone)).day if day != self.day: self.day = day self.db_sent.clear() elif action == 'add': if sender_id not in self.db_sent: self.db_sent[sender_id] = [postid] else: self.db_sent[sender_id] += [postid] elif action == 'delete': self.db_sent[sender_id].remove(postid) if len(self.db_sent[sender_id]) == 0: del self.db_sent[sender_id] except Exception as ex: pass print(ex) def read_dm(self): '''Read and filter DMs This method contains AdminCmd and UserCmd that can do exec and self.db_sent updater. Filters: - received dm - admin & user command - interval per sender - account status - blacklist words - only followed - sender requirements - menfess trigger - attachment_url - photo - video - animated_gif :returns: list of dict filtered DMs ''' # Update db_sent self.db_sent_updater('update') print("Getting direct messages...") dms = list() try: api = self.api dm = api.list_direct_messages(count=50) # FILL DB_RECEIVED WHEN BOT WAS JUST STARTED (Keep_DM) if self.indicator_start is False: self.indicator_start = True if self.credential.Keep_DM is True: for x in dm: self.db_received.append(x.id) # Ignore messages that received before bot started return dms # Check Account_status to make process (after Turned off) faster if self.credential.Account_status is True: dm.reverse() else: for x in range(len(dm)): message = dm[x].message_create['message_data']['text'].lower() if "#switch on" in message: self.credential.Account_status = True self.delete_dm(dm[x].id) self.send_dm(dm[x].message_create['sender_id'],"processed: #switch on") del dm[x] dm.reverse() break elif any(i in message for i in self.credential.Admin_cmd): dm.reverse() break else: print("Account status: False; AdminCmd not found") return dms for x in range(len(dm)): sender_id = dm[x].message_create['sender_id'] # str message_data = dm[x].message_create['message_data'] message = message_data['text'] id = dm[x].id # Message id is already stored on db_received, the message will be skipped (Keep_DM) if id in self.db_received: continue # Avoid keyword error by skipping bot messages if sender_id == str(self.me.id): self.delete_dm(id) continue # ADMIN & USER COMMAND list_command = list(self.credential.Admin_cmd) + list(self.credential.User_cmd) command = message.split()[0].lower() if any(i == command for i in list_command): # delete DM to avoid repeated command self.delete_dm(id) AdminCmd = self.AdminCmd #pylint: disable=unused-variable UserCmd = self.UserCmd #pylint: disable=unused-variable command, *contents = message.split() notif = str() if command.lower() in self.credential.Admin_cmd: # Manage admin access if sender_id not in self.credential.Admin_id: notif = self.credential.Notify_wrongTrigger self.send_dm(recipient_id=sender_id, text=notif) continue else: pass print(f"command {command} {str(contents)} in progress...") dict_command = self.credential.Admin_cmd.copy() dict_command.update(self.credential.User_cmd) if len(contents) != 0: urls = message_data["entities"]["urls"] for arg in contents: try: notif += f"\nprocessed: {command} {arg}" fix_command = dict_command[command.lower()] exec(fix_command) if "urls" in fix_command: break except Exception as ex: pass print(ex) notif += f"\nException: {ex}" else: try: notif += f"\nprocessed: {command}" exec(dict_command[command.lower()]) except Exception as ex: pass print(ex) notif += f"\nException: {ex}" # Skip notif if '#no_notif' in command's comment if "#no_notif" in dict_command[command.lower()]: if "Exception" not in notif: continue # Manage notification for user if sender_id not in self.credential.Admin_id: if "Exception" not in notif: notif = self.credential.Notify_userCmdDelete else: notif = self.credential.Notify_userCmdDeleteFail self.send_dm(sender_id, notif) continue # ACCOUNT STATUS if self.credential.Account_status is False: continue # Interval time per sender if self.credential.Interval_perSender is True and sender_id not in self.credential.Admin_id: date_now = datetime.now(timezone.utc) + timedelta(hours=self.credential.Timezone) for i in list(self.db_intervalTime): # cleaning self.db_intervalTime if self.db_intervalTime[i] < date_now: del self.db_intervalTime[i] if sender_id in self.db_intervalTime: continue else: self.db_intervalTime[sender_id] = date_now + timedelta(seconds=self.credential.Interval_time) # Delete or store message id to avoid repeated menfess (Keep_DM) if self.credential.Keep_DM is True: if len(self.db_received) > 55: self.db_received.pop(0) self.db_received.append(id) else: self.delete_dm(id) # ONLY FOLLOWED if self.credential.Only_followed is True and sender_id not in self.credential.Admin_id: if int(sender_id) not in self.followed: self.send_dm(sender_id, self.credential.Notify_notFollowed) continue # Minimum lenMenfess if len(message) < self.credential.Minimum_lenMenfess and sender_id not in self.credential.Admin_id: self.send_dm(sender_id, self.credential.Notify_senderRequirements) continue # SENDER REQUIREMENTS if self.credential.Sender_requirements is True and sender_id not in self.credential.Admin_id: indicator = 0 user = (api.get_user(sender_id))._json # minimum followers if user['followers_count'] < self.credential.Minimum_followers: indicator = 1 # minimum age created_at = datetime.strptime(user['created_at'], '%a %b %d %H:%M:%S +0000 %Y') now = (datetime.now(timezone.utc) + timedelta(hours=self.credential.Timezone)).replace(tzinfo=None) if (now-created_at).days < self.credential.Minimum_day: indicator = 1 if indicator == 1: self.send_dm(sender_id, self.credential.Notify_senderRequirements) continue # BLACKLIST WORDS list_blacklist = [i.lower() for i in self.credential.Blacklist_words] if any(i in message.lower() for i in list_blacklist) and sender_id not in self.credential.Admin_id: try: print("Skipping blacklist menfess") notif = self.credential.Notify_blacklistWords self.send_dm(recipient_id=sender_id, text=notif) except Exception as ex: sleep(60) print(ex) pass continue # MENFESS TRIGGER if any(j.lower() in message.lower() for j in self.credential.Trigger_word): print("Getting message -> sender_id: " + str(sender_id)) dict_dms = dict(message=message, sender_id=sender_id, media_url=None, attachment_urls={'tweet':(None, None), 'media':list()}) # attachment url urls = message_data['entities']['urls'] for i in urls: if "twitter.com/" in i['expanded_url'] and "/status/" in i['expanded_url']: # i['url]: url in text message # Media if any(j in i['expanded_url'] for j in ['/video/', '/photo/', '/media/']): dict_dms['attachment_urls']['media'].append((i['url'], i['expanded_url'])) #i['expanded_url'] e.g https://twitter.com/username/status/123/photo/1 # Tweet else: dict_dms['attachment_urls']['tweet'] = (i['url'], i['expanded_url']) #i['expanded_url'] e.g https://twitter.com/username/status/123?s=19 # attachment media if 'attachment' in message_data: media = message_data['attachment']['media'] media_type = media['type'] if media_type == 'photo': media_url = media['media_url'] elif media_type == 'video': media_urls = media['video_info']['variants'] temp_bitrate = list() for varian in media_urls: if varian['content_type'] == "video/mp4": temp_bitrate.append((varian['bitrate'], varian['url'])) # sort to choose the highest bitrate temp_bitrate.sort() media_url = temp_bitrate[-1][1] elif media_type == 'animated_gif': media_url = media['video_info']['variants'][0]['url'] dict_dms['media_url'] = media_url dms.append(dict_dms) # WRONG TRIGGER else: notif = self.credential.Notify_wrongTrigger self.send_dm(recipient_id=sender_id, text=notif) # Send wrong menfess to admin username = self.get_user_screen_name(sender_id) notif = message + f"\nstatus: wrong trigger\nfrom: @{username}\nid: {sender_id}" for admin in self.credential.Admin_id: self.send_dm(recipient_id=admin, text=notif) print(str(len(dms)) + " messages collected") return dms except Exception as ex: pass print(ex) sleep(60) return dms def notify_queue(self, dms): """Notify the menfess queue to sender :param dms: dms that returned from self.read_dm -> list of dict """ try: print("Notifying the queue to sender") x, y, z = -1, 0, 0 # x is primary time (30 sec); y is queue; z is addition time for media time = datetime.now(timezone.utc) + timedelta(hours=self.credential.Timezone) for i in dms: y += 1 x += (len(i['message']) // 272) + 1 if i['media_url'] != None: z += 3 if self.credential.Private_mediaTweet is True: z += len(i['attachment_urls']['media']) * 4 # Delay for the first sender is very quick, so, it won't be notified if x == 0: continue sent_time = time + timedelta(seconds= x*(32+self.random_time) + z) sent_time = datetime.strftime(sent_time, '%H:%M') notif = self.credential.Notify_queueMessage.format(str(y), sent_time) self.send_dm(recipient_id=i['sender_id'], text=notif) except Exception as ex: pass print(ex) sleep(60) def delete_dm(self, id): '''Delete a DM :param id: message id -> int or str ''' try: self.api.destroy_direct_message(id) except Exception as ex: print(ex) sleep(60) pass def send_dm(self, recipient_id, text): '''Send DM and automatically delete the sent DM :param recipient_id: -> str or int :param text: -> str ''' try: sent = self.api.send_direct_message(recipient_id=recipient_id, text=text) self.delete_dm(sent.id) except Exception as ex: pass print(ex) sleep(60) def get_user_screen_name(self, id): '''Get username :param id: account id -> int :returns: username -> str ''' try: user = self.api.get_user(id) return user.screen_name except Exception as ex: pass print(ex) sleep(60) return "Exception" def download_media(self, media_url, filename=None): '''Download media from url :param media_url: url -> string :param filename: None (default) or filename --> str :returns: file name (when filename=None) -> str ''' print("Downloading media...") oauth = OAuth1(client_key=self.credential.CONSUMER_KEY, client_secret=self.credential.CONSUMER_SECRET, resource_owner_key=self.credential.ACCESS_KEY, resource_owner_secret=self.credential.ACCESS_SECRET) r = get(media_url, auth=oauth) if filename == None: for i in sub("[/?=]", " ", media_url).split(): if search(r"\.mp4$|\.gif$|\.jpg$|\.jpeg$|\.png$|\.webp$", i): filename = i break if filename == None: raise Exception("filename is not supported, please check the link") with open(filename, 'wb') as f: f.write(r.content) f.close() print("Download media successfully") return filename def add_watermark(self, filename, output=None): '''Add watermark to photo, then save as output Only support photo, if other, nothing will happen :param filename: file name -> str :param output: output name -> str :returns: output name -> str ''' try: if output == None: output = filename file_type = filename.split('.')[-1] if file_type in "jpg jpeg png webp": print("Adding watermark...") adm = self.credential wm.watermark_text_image(filename, text=adm.Watermark_text, ratio=adm.Watermark_ratio, pos=adm.Watermark_position, output=output, color=adm.Watermark_textColor, stroke_color=adm.Watermark_textStroke, watermark=adm.Watermark_image) return output except Exception as ex: pass print(ex) return filename def upload_media_tweet(self, media_tweet_url): '''Upload media with (from) media tweet url Usually when sender want to post more than one media, he will attachs media tweet url. But the sender's username is mentioned on the bottom of the media. This method intended to make sender anonym. This return list of media_ids, then you can add media_ids to other method. Contains watermark module :param media_tweet_url: media tweet url e.g https://twitter.com/username/status/123/photo/1 -> str :returns: [(media_id, media_type),] a.k.a media_idsAndTypes -> list ''' try: postid = sub(r"[/\.:]", " ", media_tweet_url).split()[-3] status = self.api.get_status(postid) media_idsAndTypes = list() if 'extended_entities' not in status._json: return list() print("Uploading media tweet...") for media in status._json['extended_entities']['media']: media_type = media['type'] if media_type == 'photo': media_url = media['media_url'] elif media_type == 'video': media_urls = media['video_info']['variants'] temp_bitrate = list() for varian in media_urls: if varian['content_type'] == "video/mp4": temp_bitrate.append((varian['bitrate'], varian['url'])) # sort to choose the highest bitrate temp_bitrate.sort() media_url = temp_bitrate[-1][1] elif media_type == 'animated_gif': media_url = media['video_info']['variants'][0]['url'] filename = self.download_media(media_url) # Add watermark if self.credential.Watermark is True: self.add_watermark(filename) media_id, media_type = self.upload_media(filename) remove(filename) media_idsAndTypes.append((media_id, media_type)) return media_idsAndTypes # e.g [(media_id, media_type), (media_id, media_type), ] except Exception as ex: pass print(ex) sleep(60) return list() def upload_media(self, filename, media_category='tweet'): '''Upload media with chunk This method are needed when you want to use media to do something on twitter. This returns list of media_id, you can attach it to other method that require media id. :param filename: -> str :param media_category: 'tweet' or 'dm'. default to 'tweet' :returns: media id, media_type -> tuple ''' mediaupload = MediaUpload(self.credential, filename, media_category) media_id, media_type = mediaupload.upload_init() mediaupload.upload_append() mediaupload.upload_finalize() return media_id, media_type def post_tweet(self, tweet, sender_id, media_url=None, attachment_url=None, media_idsAndTypes=list(), possibly_sensitive=False): '''Post a tweet, contains watermark module and self.db_sent updater Per tweet delay is 30s + self.random_time, but the last delay is deleted :param tweet: -> str :param sender_id: -> str or int :param media_url: media url that will be posted -> str :param attachment_url: url -> str :param media_idsAndTypes: [(media_ids, media_type),] -> list :param possibly_sensitive: True when menfess contains sensitive contents -> bool :returns: tweet id -> str ''' try: #### ADD MEDIA_ID AND MEDIA_TYPE TO LIST_MEDIA_IDS #### # mediaIdsAndTypes e.g. [(media_id, media_type), (media_id, media_type), ] if media_url != None: tweet = tweet.split() tweet = " ".join(tweet[:-1]) filename = self.download_media(media_url) # Add watermark if self.credential.Watermark is True: self.add_watermark(filename) media_id, media_type = self.upload_media(filename) # Add attachment media from DM to the first order media_idsAndTypes.insert(0, (media_id, media_type)) remove(filename) list_media_ids = [[]] # e.g. [[media_ids],[media_ids],[media_ids]] temp = 0 while len(media_idsAndTypes) != 0: if temp == 0: temp = 1 list_media_ids = list() media_ids = list() added = 0 for media_id, media_type in media_idsAndTypes[:4]: if media_type == 'video' or media_type == 'animated_gif': if added == 0: media_ids.append(media_id) added += 1 break media_ids.append(media_id) added += 1 list_media_ids.append(media_ids) # media_idsAndTypes are dynamic here media_idsAndTypes = media_idsAndTypes[added:] #### POST TWEET #### postid = 0 # postid is the first tweet of the tweets thread while len(tweet) > 280: # Making a Thread. limit = 272 check = tweet[:limit].split() separator = len(check[-1]) if tweet[limit-1] == " ": separator += 1 tweet1 = unescape(tweet[:limit-separator]) + '-cont-' if postid == 0: print("Making a thread...") # postid is static after first update. postid = self.api.update_status( tweet1, attachment_url=attachment_url, media_ids=list_media_ids[:1][0], possibly_sensitive=possibly_sensitive).id postid1 = postid else: postid1 = self.api.update_status( tweet1, in_reply_to_status_id=postid1, auto_populate_reply_metadata=True, media_ids=list_media_ids[:1][0], possibly_sensitive=possibly_sensitive).id list_media_ids = list_media_ids[1:] + [[]] sleep(30+self.random_time) # tweet are dynamic here tweet = tweet[limit-separator:] # Above and below operation differences are on tweet1 and unescape(tweet), also tweet[limit-separator:] # It's possible to change it to be one function if postid == 0: # postid is static after first update. postid = self.api.update_status( unescape(tweet), attachment_url=attachment_url, media_ids=list_media_ids[:1][0], possibly_sensitive=possibly_sensitive).id postid1 = postid else: postid1 = self.api.update_status( unescape(tweet), in_reply_to_status_id=postid1, auto_populate_reply_metadata=True, media_ids=list_media_ids[:1][0], possibly_sensitive=possibly_sensitive).id list_media_ids = list_media_ids[1:] + [[]] # When media_ids still exists, It will be attached to the subsequent tweets while len(list_media_ids[0]) != 0: # Pay attention to the list format, [[]] sleep(30+self.random_time) print("Posting the rest of media...") postid1 = self.api.update_status( in_reply_to_status_id=postid1, auto_populate_reply_metadata=True, media_ids=list_media_ids[:1][0], possibly_sensitive=possibly_sensitive).id list_media_ids = list_media_ids[1:] + [[]] print('Menfess is posted -> postid:', str(postid)) # ADD TO DB SENT self.db_sent_updater('add', sender_id, str(postid)) return postid except Exception as ex: pass print(ex) sleep(60) return None
class TweepyAPITests(unittest.TestCase): def setUp(self): auths = [] for consumer_key, consumer_secret, access_key, access_secret in oauth_keys: auth = OAuthHandler(consumer_key, consumer_secret) auth.set_access_token(access_key, access_secret) auths.append(auth) self.api = API(auths) self.api.retry_count = 2 self.api.retry_delay = 5 # TODO: Actually have some sort of better assertion def testgetoembed(self): data = self.api.get_oembed(test_tweet_id) self.assertEqual(data["author_name"], "Twitter") def testhometimeline(self): self.api.home_timeline() def testusertimeline(self): self.api.user_timeline() self.api.user_timeline("twitter") def testmentionstimeline(self): self.api.mentions_timeline() def testretweetsofme(self): self.api.retweets_of_me() def testretweet(self): # TODO(josh): Need a way to get random tweets to retweet. raise SkipTest() def testretweets(self): self.api.retweets(test_tweet_id) def testgetstatus(self): self.api.get_status(id=test_tweet_id) def testupdateanddestroystatus(self): # test update text = "testing %i" % random.randint(0, 1000) update = self.api.update_status(status=text) self.assertEqual(update.text, text) # test destroy deleted = self.api.destroy_status(id=update.id) self.assertEqual(deleted.id, update.id) def testgetuser(self): u = self.api.get_user("twitter") self.assertEqual(u.screen_name, "twitter") u = self.api.get_user(783214) self.assertEqual(u.screen_name, "twitter") def testsearchusers(self): self.api.search_users("twitter") def testsuggestedcategories(self): self.api.suggested_categories() def testsuggestedusers(self): categories = self.api.suggested_categories() if len(categories) != 0: self.api.suggested_users(categories[0].slug) def testsuggesteduserstweets(self): categories = self.api.suggested_categories() if len(categories) != 0: self.api.suggested_users_tweets(categories[0].slug) def testme(self): me = self.api.me() self.assertEqual(me.screen_name, username) def testdirectmessages(self): self.api.direct_messages() def testsentdirectmessages(self): self.api.sent_direct_messages() def testsendanddestroydirectmessage(self): # send sent_dm = self.api.send_direct_message(username, text="test message") self.assertEqual(sent_dm.text, "test message") self.assertEqual(sent_dm.sender.screen_name, username) self.assertEqual(sent_dm.recipient.screen_name, username) # destroy destroyed_dm = self.api.destroy_direct_message(sent_dm.id) self.assertEqual(destroyed_dm.text, sent_dm.text) self.assertEqual(destroyed_dm.id, sent_dm.id) self.assertEqual(destroyed_dm.sender.screen_name, username) self.assertEqual(destroyed_dm.recipient.screen_name, username) def testcreatedestroyfriendship(self): enemy = self.api.destroy_friendship("twitter") self.assertEqual(enemy.screen_name, "twitter") # Wait 5 seconds to allow Twitter time # to process the friendship destroy request. sleep(5) friend = self.api.create_friendship("twitter") self.assertEqual(friend.screen_name, "twitter") def testshowfriendship(self): source, target = self.api.show_friendship(target_screen_name="twtiter") self.assert_(isinstance(source, Friendship)) self.assert_(isinstance(target, Friendship)) def testfriendsids(self): self.api.friends_ids(username) def testfollowersids(self): self.api.followers_ids(username) def testfriends(self): self.api.friends(username) def testfollowers(self): self.api.followers(username) def testverifycredentials(self): self.assertNotEqual(self.api.verify_credentials(), False) # make sure that `me.status.entities` is not an empty dict me = self.api.verify_credentials(include_entities=True) self.assertTrue(me.status.entities) # `status` shouldn't be included me = self.api.verify_credentials(skip_status=True) self.assertFalse(hasattr(me, "status")) def testratelimitstatus(self): self.api.rate_limit_status() """ TODO(josh): Remove once this deprecated API is gone. def testsetdeliverydevice(self): self.api.set_delivery_device('im') self.api.set_delivery_device('none') """ def testupdateprofilecolors(self): original = self.api.me() updated = self.api.update_profile_colors("000", "000", "000", "000", "000") # restore colors self.api.update_profile_colors( original.profile_background_color, original.profile_text_color, original.profile_link_color, original.profile_sidebar_fill_color, original.profile_sidebar_border_color, ) self.assertEqual(updated.profile_background_color, "000000") self.assertEqual(updated.profile_text_color, "000000") self.assertEqual(updated.profile_link_color, "000000") self.assertEqual(updated.profile_sidebar_fill_color, "000000") self.assertEqual(updated.profile_sidebar_border_color, "000000") """ def testupateprofileimage(self): self.api.update_profile_image('examples/profile.png') def testupdateprofilebg(self): self.api.update_profile_background_image('examples/bg.png') """ def testupdateprofile(self): original = self.api.me() profile = { "name": "Tweepy test 123", "url": "http://www.example.com", "location": "pytopia", "description": "just testing things out", } updated = self.api.update_profile(**profile) self.api.update_profile( name=original.name, url=original.url, location=original.location, description=original.description ) for k, v in profile.items(): if k == "email": continue self.assertEqual(getattr(updated, k), v) def testfavorites(self): self.api.favorites() def testcreatedestroyfavorite(self): self.api.create_favorite(4901062372) self.api.destroy_favorite(4901062372) def testcreatedestroyblock(self): self.api.create_block("twitter") self.api.destroy_block("twitter") self.api.create_friendship("twitter") # restore def testblocks(self): self.api.blocks() def testblocksids(self): self.api.blocks_ids() def testcreateupdatedestroylist(self): params = {"owner_screen_name": username, "slug": "tweeps"} l = self.api.create_list(name=params["slug"], **params) l = self.api.update_list(list_id=l.id, description="updated!") self.assertEqual(l.description, "updated!") self.api.destroy_list(list_id=l.id) def testlistsall(self): self.api.lists_all() def testlistsmemberships(self): self.api.lists_memberships() def testlistssubscriptions(self): self.api.lists_subscriptions() def testlisttimeline(self): self.api.list_timeline("applepie", "stars") def testgetlist(self): self.api.get_list(owner_screen_name="applepie", slug="stars") def testaddremovelistmember(self): params = {"slug": "test", "owner_screen_name": username, "screen_name": "twitter"} def assert_list(l): self.assertEqual(l.name, params["slug"]) assert_list(self.api.add_list_member(**params)) assert_list(self.api.remove_list_member(**params)) def testlistmembers(self): self.api.list_members("applepie", "stars") def testshowlistmember(self): self.assertTrue( self.api.show_list_member(owner_screen_name="applepie", slug="stars", screen_name="NathanFillion") ) def testsubscribeunsubscribelist(self): params = {"owner_screen_name": "applepie", "slug": "stars"} self.api.subscribe_list(**params) self.api.unsubscribe_list(**params) def testlistsubscribers(self): self.api.list_subscribers("applepie", "stars") def testshowlistsubscriber(self): self.assertTrue(self.api.show_list_subscriber("applepie", "stars", username)) def testsavedsearches(self): s = self.api.create_saved_search("test") self.api.saved_searches() self.assertEqual(self.api.get_saved_search(s.id).query, "test") self.api.destroy_saved_search(s.id) def testsearch(self): self.api.search("tweepy") def testgeoapis(self): def place_name_in_list(place_name, place_list): """Return True if a given place_name is in place_list.""" return any([x.full_name.lower() == place_name.lower() for x in place_list]) twitter_hq = self.api.geo_similar_places(lat=37, long=-122, name="Twitter HQ") # Assumes that twitter_hq is first Place returned... self.assertEqual(twitter_hq[0].id, "3bdf30ed8b201f31") # Test various API functions using Austin, TX, USA self.assertEqual(self.api.geo_id(id="c3f37afa9efcf94b").full_name, "Austin, TX") self.assertTrue( place_name_in_list("Austin, TX", self.api.reverse_geocode(lat=30.267370168467806, long=-97.74261474609375)) ) # Austin, TX, USA