diff --git a/examples/template/mytwitterbot.py b/examples/template/mytwitterbot.py index bd77ac9..d3cc6f0 100644 --- a/examples/template/mytwitterbot.py +++ b/examples/template/mytwitterbot.py @@ -111,6 +111,16 @@ def on_mention(self, tweet, prefix): raise NotImplementedError("You need to implement this to reply to/fav mentions (or pass if you don't want to)!") + def on_direct_message(self, dm): + """ + Defines actions to take when a direct message is received. + + dm - a tweepy.DirectMessage object. You can access the text with + dm.text and the sender (a tweepy.User object) with dm.sender + """ + raise NotImplementedError("You need to implement this to reply to DMs (or pass if you don't want to)!") + + def on_timeline(self, tweet, prefix): """ Defines actions to take on a timeline tweet. diff --git a/twitterbot/bot.py b/twitterbot/bot.py index 3c49414..f1ae557 100644 --- a/twitterbot/bot.py +++ b/twitterbot/bot.py @@ -90,8 +90,12 @@ def __init__(self): self.state['last_reply_id'] = 0 self.state['last_reply_time'] = 0 + self.state['last_dm_id'] = 1 + self.state['last_dm_time'] = 0 + self.state['recent_timeline'] = [] self.state['mention_queue'] = [] + self.state['dm_queue'] = [] self.state['friends'] = self.api.friends_ids(self.id) self.state['followers'] = self.api.followers_ids(self.id) @@ -176,6 +180,10 @@ def on_follow(self, f_id): self.state['followers'].append(f_id) + def on_direct_message(self): + raise NotImplementedError("You need to implement this to reply to direct messages (or pass if you don't want to)!") + + def post_tweet(self, text, reply_to=None, media=None): kwargs = {} args = [text] @@ -211,6 +219,26 @@ def favorite_tweet(self, tweet): self._log_tweepy_error('Can\'t fav status', e) + def send_direct_message(self, recipient, text): + """ + Send a DM. + + recipient - the tweepy.User, user ID, or screen name to send the DM to + text - the content of the DM + """ + tweepy_recipient = recipient.id if hasattr(recipient, 'id') else recipient + + logging.info('Sending DM to {}: {}'.format(tweepy_recipient, text)) + + try: + self.api.send_direct_message(user=tweepy_recipient, text=text) + return True + + except tweepy.TweepError as e: + self._log_tweepy_error('Can\'t send DM', e) + return False + + def _ignore_method(self, method): return hasattr(method, 'not_implemented') and method.not_implemented @@ -247,6 +275,15 @@ def _handle_mentions(self): #time.sleep(self.config['reply_interval']) + def _handle_direct_messages(self): + """ + Performs some action on the DMs in self.dm_queue + """ + for dm in iter(self.state['dm_queue']): + self.on_direct_message(dm) + self.state['dm_queue'].remove(dm) + + def get_mention_prefix(self, tweet): """ Returns a string of users to @-mention when responding to a tweet. @@ -345,7 +382,34 @@ def _check_followers(self): except IncompleteRead as e: self.log('Incomplete read error -- skipping followers update') - + + def _check_direct_messages(self): + """ + Checks for DMs. + """ + if self._ignore_method(self.on_direct_message): + logging.debug("Ignoring direct messages") + return + + try: + current_dms = self.api.direct_messages(since_id=self.state['last_dm_id'], count=200) + + if len(current_dms) != 0: + self.state['last_dm_id'] = current_dms[0].id + + self.state['last_dm_time'] = time.time() + + self.state['dm_queue'] += reversed(current_dms) + + logging.info('DMs updated ({} retrieved, {} total in queue)'.format(len(current_dms), len(self.state['dm_queue']))) + + except tweepy.TweepError as e: + self._log_tweepy_error('Can\'t retrieve direct messages', e) + + except IncompleteRead as e: + self.log('Incomplete read error -- skipping DM update') + + def _handle_followers(self): """ Handles new followers. @@ -383,6 +447,11 @@ def run(self): self._check_mentions() self._handle_mentions() + # check DMs every minute + if (time.time() - self.state['last_dm_time']) > 60: + self._check_direct_messages() + self._handle_direct_messages() + # tweet to timeline #if self.reply_to_timeline and (time.time() - self.last_mention_time) > 60: if (time.time() - self.state['last_timeline_time']) > 60: