def update_tips(ctb=None): """ Update page listing all tips """ if not ctb.conf.network.stats.enabled: return None # Start building stats page tip_list = "### All Completed Tips\n\n" q = ctb.db.execute(ctb.conf.db.sql.tips.sql_set) tips = ctb.db.execute(ctb.conf.db.sql.tips.sql_list, [ctb.conf.db.sql.tips.limit]) tip_list += ("|".join(tips.keys())) + "\n" tip_list += ("|".join([":---"] * len(tips.keys()))) + "\n" # Build tips table for t in tips: values = [] for k in tips.keys(): values.append(format_value(t, k, '', ctb, compact=True)) tip_list += ("|".join(values)) + "\n" lg.debug( "update_tips(): updating subreddit '%s', page '%s'" % (ctb.conf.network.stats.subreddit, ctb.conf.network.stats.page_tips)) ctb_misc.praw_call(ctb.reddit.wikipage.edit, ctb.conf.network.stats.subreddit, ctb.conf.network.stats.page_tips, tip_list, "Update by ALTcointip bot") return True
def init_subreddits(self): """ Determine a list of subreddits and create a PRAW object """ lg.debug("> CointipBot::init_subreddits()") try: if not hasattr(self.conf.reddit, "subreddits"): my_reddits_list = None my_reddits_string = None if hasattr(self.conf.reddit.scan, "these_subreddits"): # Subreddits are specified in conf.yml my_reddits_list = list( self.conf.reddit.scan.these_subreddits) elif self.conf.reddit.scan.my_subreddits: # Subreddits are subscribed to by bot user my_reddits = ctb_misc.praw_call( self.reddit.get_my_subreddits, limit=None) my_reddits_list = [] for my_reddit in my_reddits: my_reddits_list.append(my_reddit.display_name.lower()) my_reddits_list.sort() else: # No subreddits configured lg.debug( "< CointipBot::check_subreddits() DONE (no subreddits configured to scan)" ) return False # Build subreddits string my_reddits_string = "+".join(my_reddits_list) # Get multi-reddit PRAW object lg.debug( "CointipBot::check_subreddits(): multi-reddit string: %s", my_reddits_string, ) self.conf.reddit.subreddits = ctb_misc.praw_call( self.reddit.get_subreddit, my_reddits_string) except Exception as e: lg.error( "CointipBot::check_subreddits(): coudln't get subreddits: %s", e) raise lg.debug("< CointipBot::init_subreddits() DONE") return True
def init_subreddits(self): """ Determine a list of subreddits and create a PRAW object """ lg.debug("> CointipBot::init_subreddits()") try: if not hasattr(self.conf.reddit, 'subreddits'): my_reddits_list = None my_reddits_string = None if hasattr(self.conf.reddit.scan, 'these_subreddits'): # Subreddits are specified in conf.yml my_reddits_list = list(self.conf.reddit.scan.these_subreddits) elif self.conf.reddit.scan.my_subreddits: # Subreddits are subscribed to by bot user my_reddits = ctb_misc.praw_call(self.reddit.get_my_subreddits, limit=None) my_reddits_list = [] for my_reddit in my_reddits: my_reddits_list.append(my_reddit.display_name.lower()) my_reddits_list.sort() else: # No subreddits configured lg.debug("< CointipBot::check_subreddits() DONE (no subreddits configured to scan)") return False # Build subreddits string my_reddits_string = "+".join(my_reddits_list) # Get multi-reddit PRAW object lg.debug("CointipBot::check_subreddits(): multi-reddit string: %s", my_reddits_string) self.conf.reddit.subreddits = ctb_misc.praw_call(self.reddit.get_subreddit, my_reddits_string) except Exception as e: lg.error("CointipBot::check_subreddits(): coudln't get subreddits: %s", e) raise lg.debug("< CointipBot::init_subreddits() DONE") return True
def update_stats(ctb=None): """ Update stats wiki page """ stats = "" if not ctb.conf.network.stats.enabled: return None for s in sorted(vars(ctb.conf.db.sql.globalstats)): lg.debug("update_stats(): getting stats for '%s'" % s) sql = ctb.conf.db.sql.globalstats[s].query stats += "\n\n### %s\n\n" % ctb.conf.db.sql.globalstats[s].name stats += "%s\n\n" % ctb.conf.db.sql.globalstats[s].desc sqlexec = ctb.db.execute(sql) if sqlexec.rowcount <= 0: lg.warning("update_stats(): query <%s> returned nothing" % ctb.conf.db.sql.globalstats[s].query) continue if ctb.conf.db.sql.globalstats[s].type == "line": m = sqlexec.fetchone() k = sqlexec.keys()[0] value = format_value(m, k, '', ctb) stats += "%s = **%s**\n" % (k, value) elif ctb.conf.db.sql.globalstats[s].type == "table": stats += ("|".join(sqlexec.keys())) + "\n" stats += ("|".join([":---"] * len(sqlexec.keys()))) + "\n" for m in sqlexec: values = [] for k in sqlexec.keys(): values.append(format_value(m, k, '', ctb)) stats += ("|".join(values)) + "\n" else: lg.error("update_stats(): don't know what to do with type '%s'" % ctb.conf.db.sql.globalstats[s].type) return False stats += "\n" lg.debug("update_stats(): updating subreddit '%s', page '%s'" % (ctb.conf.network.stats.subreddit, ctb.conf.network.stats.page)) return ctb_misc.praw_call(ctb.reddit.wikipage.edit, ctb.conf.network.stats.subreddit, ctb.conf.network.stats.page, stats, "Update by ALTcointip bot")
def check_subreddits(self): """ Evaluate new comments from configured subreddits """ lg.debug("> CointipBot::check_subreddits()") try: # Process comments until old comment reached # Get last_processed_comment_time if necessary if not hasattr(self.conf.reddit, 'last_processed_comment_time') or self.conf.reddit.last_processed_comment_time <= 0: self.conf.reddit.last_processed_comment_time = ctb_misc.get_value(conn=self.db, param0='last_processed_comment_time') updated_last_processed_time = 0 # Fetch comments from subreddits my_comments = ctb_misc.praw_call(self.conf.reddit.subreddits.get_comments, limit=self.conf.reddit.scan.batch_limit) # Match each comment against regex counter = 0 for c in my_comments: # Stop processing if old comment reached #lg.debug("check_subreddits(): c.id %s from %s, %s <= %s", c.id, c.subreddit.display_name, c.created_utc, self.conf.reddit.last_processed_comment_time) if c.created_utc <= self.conf.reddit.last_processed_comment_time: lg.debug("CointipBot::check_subreddits(): old comment reached") break counter += 1 if c.created_utc > updated_last_processed_time: updated_last_processed_time = c.created_utc # Ignore duplicate comments (may happen when bot is restarted) if ctb_action.check_action(msg_id=c.id, ctb=self): lg.warning("CointipBot::check_inbox(): duplicate action detected (comment.id %s), ignoring", c.id) continue # Ignore comments from banned users if c.author and self.conf.reddit.banned_users: lg.debug("CointipBot::check_subreddits(): checking whether user '%s' is banned..." % c.author) u = ctb_user.CtbUser(name = c.author.name, redditobj = c.author, ctb = self) if u.banned: lg.info("CointipBot::check_subreddits(): ignoring banned user '%s'" % c.author) continue # Attempt to evaluate comment action = ctb_action.eval_comment(c, self) # Perform action, if found if action: lg.info("CointipBot::check_subreddits(): %s from %s (%s)", action.type, action.u_from.name, c.id) lg.debug("CointipBot::check_subreddits(): comment body: <%s>", c.body) action.do() else: lg.info("CointipBot::check_subreddits(): no match") lg.debug("CointipBot::check_subreddits(): %s comments processed", counter) if counter >= self.conf.reddit.scan.batch_limit - 1: lg.warning("CointipBot::check_subreddits(): conf.reddit.scan.batch_limit (%s) was not large enough to process all comments", self.conf.reddit.scan.batch_limit) except (HTTPError, RateLimitExceeded, timeout) as e: lg.warning("CointipBot::check_subreddits(): Reddit is down (%s), sleeping", e) time.sleep(self.conf.misc.times.sleep_seconds) pass except Exception as e: lg.error("CointipBot::check_subreddits(): coudln't fetch comments: %s", e) raise # Save updated last_processed_time value if updated_last_processed_time > 0: self.conf.reddit.last_processed_comment_time = updated_last_processed_time ctb_misc.set_value(conn=self.db, param0='last_processed_comment_time', value0=self.conf.reddit.last_processed_comment_time) lg.debug("< CointipBot::check_subreddits() 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
def check_subreddits(self): """ Evaluate new comments from configured subreddits """ lg.debug("> CointipBot::check_subreddits()") try: # Process comments until old comment reached # Get last_processed_comment_time if necessary if (not hasattr(self.conf.reddit, "last_processed_comment_time") or self.conf.reddit.last_processed_comment_time <= 0): self.conf.reddit.last_processed_comment_time = ctb_misc.get_value( conn=self.db, param0="last_processed_comment_time") updated_last_processed_time = 0 # Fetch comments from subreddits my_comments = ctb_misc.praw_call( self.conf.reddit.subreddits.get_comments, limit=self.conf.reddit.scan.batch_limit, ) # Match each comment against regex counter = 0 for c in my_comments: # Stop processing if old comment reached # lg.debug("check_subreddits(): c.id %s from %s, %s <= %s", c.id, c.subreddit.display_name, c.created_utc, self.conf.reddit.last_processed_comment_time) if c.created_utc <= self.conf.reddit.last_processed_comment_time: lg.debug( "CointipBot::check_subreddits(): old comment reached") break counter += 1 if c.created_utc > updated_last_processed_time: updated_last_processed_time = c.created_utc # Ignore duplicate comments (may happen when bot is restarted) if ctb_action.check_action(msg_id=c.id, ctb=self): lg.warning( "CointipBot::check_inbox(): duplicate action detected (comment.id %s), ignoring", c.id, ) continue # Ignore comments from banned users if c.author and self.conf.reddit.banned_users: lg.debug( "CointipBot::check_subreddits(): checking whether user '%s' is banned..." % c.author) u = ctb_user.CtbUser(name=c.author.name, redditobj=c.author, ctb=self) if u.banned: lg.info( "CointipBot::check_subreddits(): ignoring banned user '%s'" % c.author) continue # Attempt to evaluate comment action = ctb_action.eval_comment(c, self) # Perform action, if found if action: lg.info( "CointipBot::check_subreddits(): %s from %s (%s)", action.type, action.u_from.name, c.id, ) lg.debug( "CointipBot::check_subreddits(): comment body: <%s>", c.body) action.do() else: lg.info("CointipBot::check_subreddits(): no match") lg.debug("CointipBot::check_subreddits(): %s comments processed", counter) if counter >= self.conf.reddit.scan.batch_limit - 1: lg.warning( "CointipBot::check_subreddits(): conf.reddit.scan.batch_limit (%s) was not large enough to process all comments", self.conf.reddit.scan.batch_limit, ) except (HTTPError, RateLimitExceeded, timeout) as e: lg.warning( "CointipBot::check_subreddits(): Reddit is down (%s), sleeping", e) time.sleep(self.conf.misc.times.sleep_seconds) pass except Exception as e: lg.error( "CointipBot::check_subreddits(): coudln't fetch comments: %s", e) raise # Save updated last_processed_time value if updated_last_processed_time > 0: self.conf.reddit.last_processed_comment_time = updated_last_processed_time ctb_misc.set_value( conn=self.db, param0="last_processed_comment_time", value0=self.conf.reddit.last_processed_comment_time, ) lg.debug("< CointipBot::check_subreddits() 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 process_transactions(self): lg.debug("< CointipBot::process_transactions() ") sql = "SELECT * FROM pending_action where state='pending' group by created_utc limit 50" for mysqlrow in self.db.execute(sql): such_fullname = mysqlrow['fullname'] #Check to see if message or comment check_fullname = such_fullname check_fullname = check_fullname.split('_') #process as a message if check_fullname[0] == 't4': #Set vars to pass into eval_db_message msg_body = mysqlrow['msg_body'] permalink = mysqlrow['msg_link'] fullname = such_fullname id = mysqlrow['msg_id'] verify = mysqlrow['verify'] fiat = mysqlrow['verify'] coin = mysqlrow['coin'] coin_val = mysqlrow['coin_val'] to_addr = mysqlrow['to_addr'] to_user = mysqlrow['to_user'] from_user = mysqlrow['from_user'] created_utc = mysqlrow['created_utc'] type = mysqlrow['type'] if not from_user: lg.warning('CointipBot::process_transactions(): no author') sql2 = "UPDATE pending_action SET state='completed' where fullname ='%s'" % such_fullname self.db.execute(sql2) continue if not msg_body: lg.warning( 'CointipBot::process_transactions(): message has no body dont process' ) sql2 = "UPDATE pending_action SET state='completed' where fullname ='%s'" % such_fullname self.db.execute(sql2) continue action = None action = ctb_action.eval_db_message(msg_body, permalink, fullname, id, verify, fiat, coin, coin_val, to_addr, to_user, from_user, created_utc, type, self) if action: lg.info( "CointipBot::processing transaction(): %s from %s (m.id %s)", action.type, action.u_from.name, action.deleted_msg_id) action.send_to_push_queue() sql2 = "UPDATE pending_action SET state='completed' where fullname ='%s'" % fullname self.db.execute(sql2) else: sql2 = "UPDATE pending_action SET state='failed' where fullname ='%s'" % fullname self.db.execute(sql2) #its a comment else: lg.debug( "< CointipBot::process_transactions() Processing %s " % such_fullname) m = ctb_misc.praw_call(self.reddit.get_info, thing_id=such_fullname) if not m: lg.warning( 'CointipBot::process_transactions(): message or post has been deleted' ) sql2 = "UPDATE pending_action SET state='completed' where fullname ='%s'" % such_fullname self.db.execute(sql2) continue if not m.author: lg.warning('CointipBot::process_transactions(): no author') sql2 = "UPDATE pending_action SET state='completed' where fullname ='%s'" % such_fullname self.db.execute(sql2) continue action = None if mysqlrow['msg_link']: action = ctb_action.eval_comment(m, self) if action: lg.info( "CointipBot::processing transaction(): %s from %s (m.id %s)", action.type, action.u_from.name, m.id) action.send_to_push_queue() sql2 = "UPDATE pending_action SET state='completed' where fullname ='%s'" % such_fullname self.db.execute(sql2) else: sql2 = "UPDATE pending_action SET state='failed' where fullname ='%s'" % such_fullname self.db.execute(sql2)
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
def update_user_stats(ctb=None, username=None): """ Update individual user stats for given username """ if not ctb.conf.network.stats.enabled: return None # List of coins coins_q = ctb.db.execute(ctb.conf.db.sql.userstats.coins) coins = [] for c in coins_q: coins.append(c['coin']) # List of fiat fiat_q = ctb.db.execute(ctb.conf.db.sql.userstats.fiat) fiat = [] for f in fiat_q: fiat.append(f['fiat']) # Start building stats page user_stats = "### Tipping Summary for /u/%s\n\n" % username page = ctb.conf.network.stats.page + '_' + username # Total Tipped user_stats += "#### Total Tipped (Fiat)\n\n" user_stats += "fiat|total\n:---|---:\n" total_tipped = [] for f in fiat: sqlexec = ctb.db.execute(ctb.conf.db.sql.userstats.total_tipped_fiat, [username, f]) total_tipped_fiat = sqlexec.fetchone() if total_tipped_fiat['total_fiat'] is not None: user_stats += "**%s**|%s %.2f\n" % ( f, ctb.conf.fiat[f].symbol, total_tipped_fiat['total_fiat']) total_tipped.append( "%s%.2f" % (ctb.conf.fiat[f].symbol, total_tipped_fiat['total_fiat'])) user_stats += "\n" user_stats += "#### Total Tipped (Coins)\n\n" user_stats += "coin|total\n:---|---:\n" for c in coins: sqlexec = ctb.db.execute(ctb.conf.db.sql.userstats.total_tipped_coin, [username, c]) total_tipped_coin = sqlexec.fetchone() if total_tipped_coin['total_coin'] is not None: user_stats += "**%s**|%s %.6f\n" % ( c, ctb.conf.coins[c].symbol, total_tipped_coin['total_coin']) user_stats += "\n" # Total received user_stats += "#### Total Received (Fiat)\n\n" user_stats += "fiat|total\n:---|---:\n" total_received = [] for f in fiat: sqlexec = ctb.db.execute(ctb.conf.db.sql.userstats.total_received_fiat, [username, f]) total_received_fiat = sqlexec.fetchone() if total_received_fiat['total_fiat'] is not None: user_stats += "**%s**|%s %.2f\n" % ( f, ctb.conf.fiat[f].symbol, total_received_fiat['total_fiat']) total_received.append( "%s%.2f" % (ctb.conf.fiat[f].symbol, total_received_fiat['total_fiat'])) user_stats += "\n" user_stats += "#### Total Received (Coins)\n\n" user_stats += "coin|total\n:---|---:\n" for c in coins: sqlexec = ctb.db.execute(ctb.conf.db.sql.userstats.total_received_coin, [username, c]) total_received_coin = sqlexec.fetchone() if total_received_coin['total_coin'] is not None: user_stats += "**%s**|%s %.6f\n" % ( c, ctb.conf.coins[c].symbol, total_received_coin['total_coin']) user_stats += "\n" # History user_stats += "#### History\n\n" history = ctb.db.execute(ctb.conf.db.sql.userstats.history, [username, username]) user_stats += ("|".join(history.keys())) + "\n" user_stats += ("|".join([":---"] * len(history.keys()))) + "\n" # Build history table num_tipped = 0 num_received = 0 for m in history: if m['state'] == 'completed': if m['from_user'].lower() == username.lower(): num_tipped += 1 elif m['to_user'].lower() == username.lower(): num_received += 1 values = [] for k in history.keys(): values.append(format_value(m, k, username, ctb)) user_stats += ("|".join(values)) + "\n" # Submit changes lg.debug("update_user_stats(): updating subreddit '%s', page '%s'" % (ctb.conf.network.stats.subreddit, page)) ctb_misc.praw_call(ctb.reddit.wikipage.edit, ctb.conf.network.stats.subreddit, page, user_stats, "Update by ALTcointip bot") # Update user flair on subreddit if ctb.conf.network.stats.userflair and (len(total_tipped) > 0 or len(total_received) > 0): flair = "" if len(total_tipped) > 0: flair += "tipped[" + '|'.join(total_tipped) + "]" flair += " (%d)" % num_tipped if len(total_received) > 0: if len(total_tipped) > 0: flair += " / " flair += "received[" + '|'.join(total_received) + "]" flair += " (%d)" % num_received lg.debug("update_user_stats(): updating flair for %s (%s)", username, flair) r = ctb_misc.praw_call(ctb.reddit.subreddit, ctb.conf.network.stats.subreddit) res = ctb_misc.praw_call(r.flair.set, username, flair, '') lg.debug(res) return True