def _parse_direct_message(self, event_data): recipient_id = event_data['message_create']['target']['recipient_id'] sender_id = event_data['message_create']['sender_id'] sender_screen_name = self.get_user_name(sender_id) message_text = event_data['message_create']['message_data']['text'] msg = { 'created_utc': self._timestamp_utc_now(), 'author': { 'name': sender_screen_name }, 'recipient_id': sender_id, 'type': 'direct_message' } msg['id'] = event_data['id'] + str( msg['created_utc'])[(30 - len(event_data['id'])):] text = event_data['message_create']['message_data']['text'] msg['body'] = text.replace('@' + self.user, '').strip() lg.debug(msg) action = ctb_action.eval_message(ctb_misc.DotDict(msg), self.ctb) return action
def on_pubmsg(self, c, e): ch = e.target nick = e.source.nick text = ' '.join(e.arguments) lg.debug('IRCChatBot::on_pubmsg(): %s on %s: %s', nick, ch, text) # ignore my own message if nick == self.username: return None # commands must start with + if text[0] != '+': return None now = datetime.utcnow().replace(tzinfo=pytz.utc) msg = { 'created_utc': calendar.timegm(now.utctimetuple()), 'author': { 'name': nick } } msg['id'] = str(msg['created_utc']) msg['body'] = text print(msg) action = ctb_action.eval_message(ctb_misc.DotDict(msg), self.ctb) if action: lg.info("IRCChatBot::on_pubmsg(): %s from %s", action.type, action.u_from.name) lg.debug("IRCChatBot::on_pubmsg(): comment body: <%s>", action.msg.body) action.do()
def _parse_mention(self, data): # ignore retweets if 'retweeted_status' in data: return None author_name = data['user']['screen_name'] if author_name == self.user or '@' + self.user not in data['text']: return None # we do allow the bot to issue commands msg = { 'created_utc': self._timestamp_utc_now(), 'author': { 'name': author_name }, 'type': 'mention' } msg['id'] = data['id_str'] + str( msg['created_utc'])[(30 - len(data['id_str'])):] text = data['text'] msg['body'] = text.replace('@' + self.user, '').strip() print(msg) action = ctb_action.eval_message(ctb_misc.DotDict(msg), self.ctb) return action
def follow_followers(self): lg.debug('TwitterStreamer::follow_followers(): started') # only get the latest followers but this relies on Twitter returns the latest followers first followers = [] for fid in self.conn.cursor(self.conn.get_followers_ids, count=5000): followers.append(fid) friends = [] for fid in self.conn.cursor(self.conn.get_friends_ids, count=5000): friends.append(fid) pending = [] for fid in self.conn.cursor(self.conn.get_outgoing_friendship_ids): pending.append(fid) lg.debug( 'TwitterStreamer::follow_followers(): looking for new followers') to_follow = [ f for f in followers[:100] if f not in friends and f not in pending ] lg.debug( 'TwitterStreamer::follow_followers(): about to send follow request' ) # only follow 10 at a time actions = [] for user_id in to_follow[:10]: try: resp = self.conn.create_friendship(user_id=user_id) time.sleep(1) except TwythonError as e: # either really failed (e.g. sent request before) or already friends lg.warning( "TwitterStreamer::follow_followers: failed to follow user %s: %s", user_id, e.msg) continue else: lg.debug( 'TwitterStreamer::follow_followers(): just sent request to follow user %s', user_id) msg = { 'id': str(user_id), 'created_utc': self._timestamp_utc_now(), 'author': { 'name': resp['screen_name'] }, 'body': '+register', 'type': 'mention' } # make the msg id unique msg['id'] += ('@' + str(msg['created_utc'])) action = ctb_action.eval_message(ctb_misc.DotDict(msg), self.ctb) actions.append(action) return actions
def _parse_follow(self, event_data): event = event_data['event'] source_id = event['source']['id'] source_name = event['source']['screen_name'] msg = {'id': source_id, 'created_utc': self._timestamp_utc_now(), 'author': {'name': source_name}, 'body': '+register', 'type': 'direct_message'} # make the msg id unique msg['id'] += ('@' + str(msg['created_utc'])) lg.debug(msg) action = ctb_action.eval_message(ctb_misc.DotDict(msg), self.ctb) return action
def _parse_mention(self, event_data): event = event_data['event'] author_name = event['user']['screen_name'] # we do allow the bot to issue commands msg = {'created_utc': self._timestamp_utc_now(), 'author': {'name': author_name}, 'type': 'mention'} msg['id'] = event['id_str'] + str(msg['created_utc'])[(30-len(event['id_str'])):] text = event['text'] msg['body'] = text.replace('@' + self.username, '').strip() lg.debug(msg) action = ctb_action.eval_message(ctb_misc.DotDict(msg), self.ctb) return action
def _parse_direct_msg(self, data): # ignore direct message from the bot itself author_name = data['sender']['screen_name'] if author_name == self.username: return None msg = { 'created_utc': self._timestamp_utc_now(), 'author': { 'name': author_name }, 'type': 'direct_message' } msg['id'] = data['id_str'] + str( msg['created_utc'])[(30 - len(data['id_str'])):] text = data['text'] msg['body'] = text.replace('@' + self.username, '').strip() print(msg) action = ctb_action.eval_message(ctb_misc.DotDict(msg), self.ctb) return action
def check_inbox(self): """ Evaluate new messages in inbox """ lg.debug('> CointipBot::check_inbox()') try: # Try to fetch some messages messages = list(ctb_misc.praw_call(self.reddit.get_unread, limit=self.conf.reddit.scan.batch_limit)) messages.reverse() # Process messages for m in messages: # Sometimes messages don't have an author (such as 'you are banned from' message) if not m.author: lg.info("CointipBot::check_inbox(): ignoring msg with no author") ctb_misc.praw_call(m.mark_as_read) continue lg.info("CointipBot::check_inbox(): %s from %s", "comment" if m.was_comment else "message", m.author.name) # Ignore duplicate messages (sometimes Reddit fails to mark messages as read) if ctb_action.check_action(msg_id=m.id, ctb=self): lg.warning("CointipBot::check_inbox(): duplicate action detected (msg.id %s), ignoring", m.id) ctb_misc.praw_call(m.mark_as_read) continue # Ignore self messages if m.author and m.author.name.lower() == self.conf.reddit.auth.user.lower(): lg.debug("CointipBot::check_inbox(): ignoring message from self") ctb_misc.praw_call(m.mark_as_read) continue # Ignore messages from banned users if m.author and self.conf.reddit.banned_users: lg.debug("CointipBot::check_inbox(): checking whether user '%s' is banned..." % m.author) u = ctb_user.CtbUser(name = m.author.name, redditobj = m.author, ctb = self) if u.banned: lg.info("CointipBot::check_inbox(): ignoring banned user '%s'" % m.author) ctb_misc.praw_call(m.mark_as_read) continue action = None if m.was_comment: # Attempt to evaluate as comment / mention action = ctb_action.eval_comment(m, self) else: # Attempt to evaluate as inbox message action = ctb_action.eval_message(m, self) # Perform action, if found if action: lg.info("CointipBot::check_inbox(): %s from %s (m.id %s)", action.type, action.u_from.name, m.id) lg.debug("CointipBot::check_inbox(): message body: <%s>", m.body) action.do() else: lg.info("CointipBot::check_inbox(): no match") if self.conf.reddit.messages.sorry and not m.subject in ['post reply', 'comment reply']: user = ctb_user.CtbUser(name=m.author.name, redditobj=m.author, ctb=self) tpl = self.jenv.get_template('didnt-understand.tpl') msg = tpl.render(user_from=user.name, what='comment' if m.was_comment else 'message', source_link=m.permalink if hasattr(m, 'permalink') else None, ctb=self) lg.debug("CointipBot::check_inbox(): %s", msg) user.tell(subj='What?', msg=msg, msgobj=m if not m.was_comment else None) # Mark message as read ctb_misc.praw_call(m.mark_as_read) except (HTTPError, ConnectionError, Timeout, timeout) as e: lg.warning("CointipBot::check_inbox(): Reddit is down (%s), sleeping", e) time.sleep(self.conf.misc.times.sleep_seconds) pass except RateLimitExceeded as e: lg.warning("CointipBot::check_inbox(): rate limit exceeded, sleeping for %s seconds", e.sleep_time) time.sleep(e.sleep_time) time.sleep(1) pass except Exception as e: lg.error("CointipBot::check_inbox(): %s", e) raise lg.debug("< CointipBot::check_inbox() DONE") return True
def check_inbox(self): """ Evaluate new messages in inbox """ lg.debug("> CointipBot::check_inbox()") try: # Try to fetch some messages messages = list( ctb_misc.praw_call(self.reddit.get_unread, limit=self.conf.reddit.scan.batch_limit)) messages.reverse() # Process messages for m in messages: # Sometimes messages don't have an author (such as 'you are banned from' message) if not m.author: lg.info( "CointipBot::check_inbox(): ignoring msg with no author" ) ctb_misc.praw_call(m.mark_as_read) continue lg.info( "CointipBot::check_inbox(): %s from %s", "comment" if m.was_comment else "message", m.author.name, ) # Ignore duplicate messages (sometimes Reddit fails to mark messages as read) if ctb_action.check_action(msg_id=m.id, ctb=self): lg.warning( "CointipBot::check_inbox(): duplicate action detected (msg.id %s), ignoring", m.id, ) ctb_misc.praw_call(m.mark_as_read) continue # Ignore self messages if (m.author and m.author.name.lower() == self.conf.reddit.auth.user.lower()): lg.debug( "CointipBot::check_inbox(): ignoring message from self" ) ctb_misc.praw_call(m.mark_as_read) continue # Ignore messages from banned users if m.author and self.conf.reddit.banned_users: lg.debug( "CointipBot::check_inbox(): checking whether user '%s' is banned..." % m.author) u = ctb_user.CtbUser(name=m.author.name, redditobj=m.author, ctb=self) if u.banned: lg.info( "CointipBot::check_inbox(): ignoring banned user '%s'" % m.author) ctb_misc.praw_call(m.mark_as_read) continue action = None if m.was_comment: # Attempt to evaluate as comment / mention action = ctb_action.eval_comment(m, self) else: # Attempt to evaluate as inbox message action = ctb_action.eval_message(m, self) # Perform action, if found if action: lg.info( "CointipBot::check_inbox(): %s from %s (m.id %s)", action.type, action.u_from.name, m.id, ) lg.debug("CointipBot::check_inbox(): message body: <%s>", m.body) action.do() else: lg.info("CointipBot::check_inbox(): no match") if self.conf.reddit.messages.sorry and m.subject not in [ "post reply", "comment reply", ]: user = ctb_user.CtbUser(name=m.author.name, redditobj=m.author, ctb=self) tpl = self.jenv.get_template("didnt-understand.tpl") msg = tpl.render( user_from=user.name, what="comment" if m.was_comment else "message", source_link=ctb_misc.permalink(m), ctb=self, ) lg.debug("CointipBot::check_inbox(): %s", msg) user.tell( subj="What?", msg=msg, msgobj=m if not m.was_comment else None, ) # Mark message as read ctb_misc.praw_call(m.mark_as_read) except (HTTPError, ConnectionError, Timeout, RateLimitExceeded, timeout) as e: lg.warning( "CointipBot::check_inbox(): Reddit is down (%s), sleeping", e) time.sleep(self.conf.misc.times.sleep_seconds) pass except Exception as e: lg.exception("CointipBot::check_inbox(): %s", e) # raise # ^ what do we say to death? # lg.error("^not today (^skipped raise)") # raise #not sure if right number of spaces; try to quit on error # for now, quitting on error because of dealing with on-going issues; switch # back when stable lg.debug("< CointipBot::check_inbox() DONE") return True
def check_inbox(self): """ Evaluate new messages until checkpoint is reached """ lg.debug('> CointipBot::check_inbox()') last_read = None running = True while running: try: # fetch all the messages since last read lg.debug("querying all messages since %s", last_read) my_params = {'after': last_read} messages = list( ctb_misc.praw_call(self.reddit.get_inbox, params=my_params)) # Process messages for m in messages: last_read = m.fullname lg.debug("Checking %s", last_read) #check for checkpoint if self.check_id(thingid=last_read): lg.debug("Hit checkpoint") running = False break # Sometimes messages don't have an author (such as 'you are banned from' message) if not m.author: lg.info( "CointipBot::check_inbox(): ignoring msg with no author" ) self.mark_as_processed(thingid=m.fullname) continue lg.info("CointipBot::check_inbox(): %s from %s", "comment" if m.was_comment else "message", m.author.name) # Ignore self messages if m.author and m.author.name.lower( ) == self.conf.reddit.auth.user.lower(): lg.debug( "CointipBot::check_inbox(): ignoring message from self" ) self.mark_as_processed(thingid=m.fullname) continue # Ignore messages from banned users if m.author and self.conf.reddit.banned_users: lg.debug( "CointipBot::check_inbox(): checking whether user '%s' is banned..." % m.author) u = ctb_user.CtbUser(name=m.author.name, redditobj=m.author, ctb=self) if u.banned: lg.info( "CointipBot::check_inbox(): ignoring banned user '%s'" % m.author) self.mark_as_processed(thingid=m.fullname) continue action = None if m.was_comment: # Attempt to evaluate as comment / mention action = ctb_action.eval_comment(m, self) else: # Attempt to evaluate as inbox message action = ctb_action.eval_message(m, self) # load into database if action: lg.info( "CointipBot::check_inbox(): %s from %s (m.id %s)", action.type, action.u_from.name, m.id) lg.debug( "CointipBot::check_inbox(): message body: <%s>", m.body) action.save_pending(state="pending") else: lg.info( "CointipBot::check_inbox(): no match ... message body: <%s>", m.body) if self.conf.reddit.messages.sorry and not m.subject in [ 'post reply', 'comment reply' ]: user = ctb_user.CtbUser(name=m.author.name, redditobj=m.author, ctb=self) tpl = self.jenv.get_template( 'didnt-understand.tpl') msg = tpl.render( user_from=user.name, what='comment' if m.was_comment else 'message', source_link=m.permalink if hasattr( m, 'permalink') else None, ctb=self) lg.debug("CointipBot::check_inbox(): %s", msg) user.tell(subj='What?', msg=msg, msgobj=m if not m.was_comment else None) # Mark message as read self.mark_as_processed(thingid=m.fullname) except (HTTPError, ConnectionError, Timeout, RateLimitExceeded, timeout) as e: lg.warning( "CointipBot::check_inbox(): Reddit is down (%s), sleeping", e) time.sleep(self.conf.misc.times.sleep_seconds) pass except Exception as e: lg.error("CointipBot::check_inbox(): %s", e) raise lg.debug("< CointipBot::check_inbox() DONE") return True
def check_inbox(self): """ Evaluate new messages in inbox """ lg.debug('> CointipBot::check_inbox()') try: # Try to fetch some messages messages = list( ctb_misc.praw_call(self.reddit.get_unread, limit=self.conf.reddit.scan.batch_limit)) messages.reverse() # Process messages for m in messages: # Sometimes messages don't have an author (such as 'you are banned from' message) if not m.author: lg.info( "CointipBot::check_inbox(): ignoring msg with no author" ) ctb_misc.praw_call(m.mark_as_read) continue lg.info("CointipBot::check_inbox(): %s from %s", "comment" if m.was_comment else "message", m.author.name) # Ignore duplicate messages (sometimes Reddit fails to mark messages as read) if ctb_action.check_action(msg_id=m.id, ctb=self): lg.warning( "CointipBot::check_inbox(): duplicate action detected (msg.id %s), ignoring", m.id) ctb_misc.praw_call(m.mark_as_read) continue # Ignore self messages if m.author and m.author.name.lower( ) == self.conf.reddit.auth.user.lower(): lg.debug( "CointipBot::check_inbox(): ignoring message from self" ) ctb_misc.praw_call(m.mark_as_read) continue # Ignore messages from banned users if m.author and self.conf.reddit.banned_users: lg.debug( "CointipBot::check_inbox(): checking whether user '%s' is banned..." % m.author) u = ctb_user.CtbUser(name=m.author.name, redditobj=m.author, ctb=self) if u.banned: lg.info( "CointipBot::check_inbox(): ignoring banned user '%s'" % m.author) ctb_misc.praw_call(m.mark_as_read) continue action = None if m.was_comment: # Attempt to evaluate as comment / mention action = ctb_action.eval_comment(m, self) else: # Attempt to evaluate as inbox message action = ctb_action.eval_message(m, self) # Perform action, if found if action: lg.info("CointipBot::check_inbox(): %s from %s (m.id %s)", action.type, action.u_from.name, m.id) lg.debug("CointipBot::check_inbox(): message body: <%s>", m.body) action.do() else: lg.info("CointipBot::check_inbox(): no match") if self.conf.reddit.messages.sorry and not m.subject in [ 'post reply', 'comment reply' ]: user = ctb_user.CtbUser(name=m.author.name, redditobj=m.author, ctb=self) tpl = self.jenv.get_template('didnt-understand.tpl') msg = tpl.render( user_from=user.name, what='comment' if m.was_comment else 'message', source_link=m.permalink if hasattr(m, 'permalink') else None, ctb=self) lg.debug("CointipBot::check_inbox(): %s", msg) user.tell(subj='What?', msg=msg, msgobj=m if not m.was_comment else None) # Mark message as read ctb_misc.praw_call(m.mark_as_read) except (HTTPError, ConnectionError, Timeout, timeout) as e: lg.warning( "CointipBot::check_inbox(): Reddit is down (%s), sleeping", e) time.sleep(self.conf.misc.times.sleep_seconds) pass except RateLimitExceeded as e: lg.warning( "CointipBot::check_inbox(): rate limit exceeded, sleeping for %s seconds", e.sleep_time) time.sleep(e.sleep_time) time.sleep(1) pass except Exception as e: lg.error("CointipBot::check_inbox(): %s", e) raise lg.debug("< CointipBot::check_inbox() DONE") return True