def self_checks(self):
        """
    Run self-checks before starting the bot
    """

        # Ensure bot is a registered user
        b = ctb_user.CtbUser(name=self.conf.reddit.auth.user.lower(), ctb=self)
        if not b.is_registered():
            b.register()

        #self.bot = b

        # Ensure (total pending tips) < (CointipBot's balance)
        for c in self.coins:
            ctb_balance = b.get_balance(coin=c, kind='givetip')
            pending_tips = float(0)
            actions = ctb_action.get_actions(atype='givetip',
                                             state='pending',
                                             coin=c,
                                             ctb=self)
            for a in actions:
                pending_tips += a.coinval
            if (ctb_balance - pending_tips) < -0.000001:
                raise Exception(
                    "CointipBot::self_checks(): CointipBot's %s balance (%s) < total pending tips (%s)"
                    % (c.upper(), ctb_balance, pending_tips))

        # Ensure coin balances are positive
        for c in self.coins:
            b = float(self.coins[c].conn.getbalance())
            if b < 0:
                raise Exception(
                    "CointipBot::self_checks(): negative balance of %s: %s" %
                    (c, b))

        # Ensure user accounts are intact and balances are not negative
        sql = "SELECT username FROM t_users ORDER BY username"
        for mysqlrow in self.db.execute(sql):
            u = ctb_user.CtbUser(name=mysqlrow['username'], ctb=self)
            if not u.is_registered():
                raise Exception(
                    "CointipBot::self_checks(): user %s is_registered() failed"
                    % mysqlrow['username'])
        #  for c in vars(self.coins):
        #    if u.get_balance(coin=c, kind='givetip') < 0:
        #      raise Exception("CointipBot::self_checks(): user %s %s balance is negative" % (mysqlrow['username'], c))

        return True
Exemple #2
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 #3
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 #4
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 #5
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