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
Exemple #2
0
    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
Exemple #3
0
    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")
Exemple #5
0
    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
Exemple #6
0
    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
Exemple #7
0
    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
Exemple #8
0
    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
Exemple #9
0
    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)
Exemple #10
0
    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
Exemple #11
0
    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