Exemple #1
0
    def __init__(self, author, mode="stream.comment", config={}):
        self.author = author
        self.writer = Writer(author=self.author)
        self.uploader = Uploader(author=self.author)
        self.mode = mode
        self.config = config

        # the configuration functions for a info bot
        self.data = lambda : []
        self.title = lambda : None
        self.body = lambda : None
        self.tags = lambda: None
        self.ready = lambda: True
Exemple #2
0
 def __init__(self, tag="cn", days=CN_HELLO_REVIEW_DURACTION):
     SteemReader.__init__(self, tag=tag, days=days)
     self.attributes = [
         u'title', u'pending_payout_value', u'author', u'net_votes',
         u'created', u'url'
         # u'permlink', u'authorperm', u'body', u'community', u'category',
     ]
     self.author = CN_HELLO_ACCOUNT
     self.writer = Writer(author=self.author)
     self.voter = Voter(author=self.author)
     self.uploader = Uploader(author=self.author)
     self.newbies = Newbies()
     self.db = settings.get_db()
     self.comments = []
Exemple #3
0
    def __init__(self, author, mode="stream.comment", config={}):
        self.author = author
        self.writer = Writer(author=self.author)
        self.voter = Voter(author=self.author)
        self.uploader = Uploader(author=self.author)
        self.mode = mode
        self.config = config

        # the configuration functions for a vote bot
        self.what_to_vote = None
        self.who_to_vote = lambda: True
        self.when_to_vote = lambda: 15
        self.how_to_vote = lambda: 50
        self.is_ready = lambda: True
        self.after_success = lambda: True

        self.last_vote_timestamp = -1
Exemple #4
0
class VoteBot:
    def __init__(self, author, mode="stream.comment", config={}):
        self.author = author
        self.writer = Writer(author=self.author)
        self.voter = Voter(author=self.author)
        self.uploader = Uploader(author=self.author)
        self.mode = mode
        self.config = config

        # the configuration functions for a vote bot
        self.what_to_vote = None
        self.who_to_vote = lambda: True
        self.when_to_vote = lambda: 15
        self.how_to_vote = lambda: 50
        self.is_ready = lambda: True
        self.after_success = lambda: True

        self.last_vote_timestamp = -1
        self._vote_queue = []

    def _has_reply_comment(self, receiver, message_id):
        comments = self._read_comments()
        for c in comments:
            # check the receiver and the message_id fingerprint
            if c.parent_author == receiver and verify_message(
                    message_id, c.body):
                logger.info(
                    "I found I replied to @{} with message [{}] by searching comment history"
                    .format(receiver, message_id))
                return (True, c)
        return (False, None)

    def _has_replied(self, receiver, message_id):
        # has reply records in DB, or has replied by checking steem APIs
        if self._has_reply_record(receiver, message_id):
            return True
        (replied, comment) = self._has_reply_comment(receiver, message_id)
        if replied:
            c = SteemComment(comment=comment.get_parent())
            # cache the record into database since not found
            self._add_reply_record(receiver, message_id, c, comment["created"])
            return True
        return False

    def _get_reply_body(self, message_id, author):
        account = SteemAccount(author)
        comments_num = account.remaining_comments() or ''
        daily_comments_num = round(account.daily_recovery_comments(), 1) or ''
        return get_message(message_id).format(
            name=author,
            comments_num=comments_num,
            daily_comments_num=daily_comments_num)

    def reply(self, message_id, post=None, url=None):
        """ reply to the newbies' post """
        c = SteemComment(comment=post, url=url)
        receiver = c.get_comment().author
        if not self._has_replied(receiver, message_id):
            title = c.get_comment().title
            message = self._get_reply_body(message_id, receiver)
            self.writer.reply(c.get_comment(), message)
            self._add_reply_record(receiver, message_id, c)
            logger.info("Replied to @{}'s post [{}] with [{}] message".format(
                receiver, title, message_id))
            return True
        else:
            logger.info(
                "Skip reply account @{} with [{}] message, because we already reliped before"
                .format(receiver, message_id))
            return False

    def vote(self, post=None, url=None, weight=None, retries=VOTE_RETRIES):
        c = SteemComment(comment=post, url=url)
        if retries <= 0:
            logger.error("Vote {} failed after retries for {} times".format(
                c.get_url(), VOTE_RETRIES))
            return False

        while time.time() - self.last_vote_timestamp < MINIMUM_VOTE_INTERVAL:
            wait_time = round(
                MINIMUM_VOTE_INTERVAL +
                random.random() * MINIMUM_VOTE_INTERVAL * 0.2, 2)
            logger.info(
                "Sleep {} seconds to avoid voting too frequently.".format(
                    wait_time))
            time.sleep(wait_time)
            if time.time() - self.last_vote_timestamp >= MINIMUM_VOTE_INTERVAL:
                return self.vote(post, url, weight, retries - 1)

        success = False
        try:
            weight = weight or self.weight(c)
            success = self.voter.vote(c.get_comment(), weight=weight)
            self.last_vote_timestamp = time.time()
        except:
            logger.error(
                "Failed when voting {} with error: {} . {} retry times left.".
                format(c.get_url(), traceback.format_exc(), retries - 1))
            return self.vote(post, url, weight, retries - 1)

        self.after_success(success)
        return success

    def start_vote_queue(self):
        logger.info("Start Vote Queue...")

        def wait_for_vote():
            while True:
                while (len(self._vote_queue) > 0):
                    post = self._vote_queue.pop(0)
                    self.vote(post)
                time.sleep(1)
            logger.info("Vote Queue Stopped.")

        t = Thread(target=wait_for_vote)
        # t.setDaemon(True)
        t.start()

    def append_to_vote_queue(self, post):
        self._vote_queue.append(post)

    def what(self, what_to_vote):
        """ define the condition of vote for a post """
        self.what_to_vote = what_to_vote
        return self

    def when(self, when_to_vote):
        """ define the timing of vote for a post """
        self.when_to_vote = when_to_vote
        return self

    def who(self, who_to_vote):
        """ define when to vote the post """
        self.who_to_vote = who_to_vote
        return self

    def how(self, how_to_vote):
        """ define the weight of vote the post """
        self.how_to_vote = how_to_vote
        return self

    def ready(self, is_ready):
        """ define voter has met energy or other requirements """
        self.is_ready = is_ready
        return self

    def done(self, after_success):
        """ define the callback after vote is completed successfully """
        self.after_success = after_success
        return self

    def context(self, ctx):
        self.ctx = ctx
        return self

    def weight(self, post):
        return self.how_to_vote(post)

    def watch(self, ops):
        author = ops['author']

        def perform_vote():
            if isinstance(ops, Comment):
                c = SteemComment(comment=ops)
            else:
                c = SteemComment(ops=ops)
            self.append_to_vote_queue(post=c.get_comment())

        self.ctx(ops)
        if self.what_to_vote(ops) and self.who_to_vote(
                author) and self.is_ready():
            delay = self.when_to_vote(ops)  # mins
            if delay is not None and delay > 0:
                secs = 60.0 * delay
                logger.info("I'll vote after {} seconds".format(secs))
                t = Timer(secs, perform_vote)
                t.start()
            else:
                logger.info("I'll vote immediately")
                perform_vote()

    def run(self):
        self.start_vote_queue()
        if self.mode.startswith("stream."):
            if self.mode == "stream.comment":
                stream = SteemStream(operations=["comment"])
            elif self.mode == "stream.vote":
                stream = SteemStream(operations=["vote"])
            stream.run(callback=self.watch)
        elif self.mode.startswith("query."):
            if self.mode == "query.comment.post":
                self.config['mode'] = "post"
            elif self.mode == "query.comment.comment":
                self.config['mode'] = "comment"
            elif self.mode == "query.comment.all":
                self.config['mode'] = "post+comment"
            for c in query(self.config):
                self.watch(c)
Exemple #5
0
class CnHelloBot(SteemReader):
    def __init__(self, tag="cn", days=CN_HELLO_REVIEW_DURACTION):
        SteemReader.__init__(self, tag=tag, days=days)
        self.attributes = [
            u'title', u'pending_payout_value', u'author', u'net_votes',
            u'created', u'url'
            # u'permlink', u'authorperm', u'body', u'community', u'category',
        ]
        self.author = CN_HELLO_ACCOUNT
        self.writer = Writer(author=self.author)
        self.voter = Voter(author=self.author)
        self.uploader = Uploader(author=self.author)
        self.newbies = Newbies()
        self.db = settings.get_db()
        self.comments = []

    def get_name(self):
        name = "cn-hello"
        return "{}-{}".format(name, self._get_time_str())

    def is_qualified(self, post):
        # c = SteemComment(comment=post)
        author = post.author
        return self.is_recent(
            post=post, days=CN_HELLO_REVIEW_DURACTION) and self.newbies.verify(
                author, post)

    def _get_newbie_list(self):
        return self.newbies.get(local=True)

    def list_newbies(self):
        # show newbies
        newbies = self._get_newbie_list()
        print("{} newbies are found".format(len(newbies)), newbies)

    def _read_comments(self):
        if len(self.comments) == 0:
            # settings.set_steem_node(STEEM_API_NODES[1], overwrite=True)
            self.comments = get_comments(self.author)
            settings.set_steem_node(CURRENT_API_NODE,
                                    overwrite=True,
                                    condenser=False)
        return self.comments

    def _add_reply_record(self, receiver, message_id, post, timestamp=None):
        if receiver and message_id and post:
            if not self._has_reply_record(receiver, message_id):
                timestamp = timestamp or datetime.now(pytz.utc)
                reply = {
                    "receiver": receiver,
                    "reputation": SteemAccount(receiver).reputation(),
                    "message_id": message_id,
                    "url": post.get_url(),
                    "title": post.get_comment().title,
                    "updated": timestamp
                }
                self.db.insert_reply(reply)
                logger.info(
                    "Add reply to author @{} with message [{}] into database".
                    format(receiver, message_id))
                return True
        return False

    def _has_reply_record(self, receiver, message_id):
        return self.db.has_reply(receiver, message_id)

    def _has_reply_comment(self, receiver, message_id):
        comments = self._read_comments()
        for c in comments:
            # check the receiver and the message_id fingerprint
            if c.parent_author == receiver and verify_message(
                    message_id, c.body):
                logger.info(
                    "I found I replied to @{} with message [{}] by searching comment history"
                    .format(receiver, message_id))
                return (True, c)
        return (False, None)

    def _has_replied(self, receiver, message_id):
        # has reply records in DB, or has replied by checking steem APIs
        if self._has_reply_record(receiver, message_id):
            return True
        (replied, comment) = self._has_reply_comment(receiver, message_id)
        if replied:
            c = SteemComment(comment=comment.get_parent())
            # cache the record into database since not found
            self._add_reply_record(receiver, message_id, c, comment["created"])
            return True
        return False

    def _get_reply_body(self, message_id, author):
        account = SteemAccount(author)
        comments_num = account.remaining_comments() or ''
        daily_comments_num = round(account.daily_recovery_comments(), 1) or ''
        return get_message(message_id).format(
            name=author,
            comments_num=comments_num,
            daily_comments_num=daily_comments_num)

    def reply(self, message_id, post=None, url=None):
        """ reply to the newbies' post """
        c = SteemComment(comment=post, url=url)
        receiver = c.get_comment().author
        if not self._has_replied(receiver, message_id):
            title = c.get_comment().title
            message = self._get_reply_body(message_id, receiver)
            self.writer.reply(c.get_comment(), message)
            self._add_reply_record(receiver, message_id, c)
            logger.info("Replied to @{}'s post [{}] with [{}] message".format(
                receiver, title, message_id))
            return True
        else:
            logger.info(
                "Skip reply account @{} with [{}] message, because we already reliped before"
                .format(receiver, message_id))
            return False

    def vote(self, post=None, url=None):
        c = SteemComment(comment=post, url=url)
        receiver = c.get_comment().author
        title = c.get_comment().title

        if not c.is_upvoted_by(self.author):
            self.voter.upvote(c.get_comment(), weight=float(VOTE_WEIGHT))
            logger.info("I have upvoted @{}'s post [{}] successfully".format(
                receiver, title))
            return True
        else:
            logger.info(
                "Skip upvote @{} because I already upvoted his/her post [{}]".
                format(receiver, title))
            return False

    def _pause(self):
        # You may only comment once every 3 seconds due to HF20
        time.sleep(5)

    def welcome_all(self, message_id="welcome"):
        if len(self.posts) == 0:
            self.get_latest_posts()

        if len(self.posts) > 0:
            # welcome newbies by replying and voting
            replied_posts = []
            for post in self.posts:
                replied = self.reply(post=post, message_id=message_id)
                if replied:
                    self.vote(post=post)
                    replied_posts.append(post)
                    self._pause()
            self.posts = replied_posts
            return self.posts
        return []

    def _get_replies(self, message_id, days):
        return list(self.db.get_replies(message_id, days))

    def _has_published(self, title, keyword):
        settings.set_steem_node(CURRENT_API_NODE,
                                overwrite=True,
                                condenser=True)
        posts = get_posts(account=self.author, keyword=keyword, limit=10)
        settings.set_steem_node(CURRENT_API_NODE,
                                overwrite=True,
                                condenser=False)
        if len(posts) > 0:
            for post in posts:
                if post.title == title:
                    return True
        return False

    def _get_accessible_url(self, url):
        if url and len(url) > 0:
            return url.replace(STEEM_HOST, ACCESSIBLE_STEEM_HOST)
        return ""

    def _get_daily_blog_body(self, message_id):
        delta = 0.05
        daily_replies = self._get_replies("welcome", 1.0 - delta)  # 1 days
        weekly_replies = self._get_replies("welcome", 7.0 - delta)  # 7 days

        daily_total = len(daily_replies)
        weekly_total = len(weekly_replies)

        articles = [
            ("@{}".format(r['receiver']),
             "<a href=\"{}\">{}</a>".format(self._get_accessible_url(r['url']),
                                            r['title'])) for r in daily_replies
        ]
        articles_table = build_table(["作者", "文章"], articles)

        stats = []
        for r in weekly_replies:
            author = r['receiver']
            account = SteemAccount(author)
            steemd_link = "{}/@{}".format(STEEMD_HOST, author)
            row = ("@{}".format(author), round(account.age_in_days(), 1),
                   round(account.reputation(),
                         1), "<a href=\"{}\">{}%</a>".format(
                             steemd_link, round(account.rc_percentage(), 1)),
                   round(account.steem_power(),
                         1), account.follower_count(), account.post_count())
            stats.append(row)
        stats = sorted(stats, key=(lambda row: row[1]), reverse=False)
        stats_table = build_table(
            ["新人", "天数", "声望", "活动能量", "Steem Power", "粉丝数", "发帖数"], stats)

        return get_message(message_id).format(daily_total=daily_total,
                                              weekly_total=weekly_total,
                                              articles_table=articles_table,
                                              stats_table=stats_table)

    def publish_daily_stats(self):
        """ publish the daily statistics post about newbies """
        title = "{} {}".format(DAILY_SUMMARY_PREFIX, self._get_time_str())
        if not self._has_published(title, DAILY_SUMMARY_PREFIX):
            body = self._get_daily_blog_body("daily_summary")
            self.writer.post(title, body, SUMMARY_POST_TAGS)
            logger.info(
                "I have published today's post [{}] successfully".format(
                    title))
            return True
        else:
            logger.info(
                "Skip this post [{}], because I already published the post with the same title"
                .format(title))
            return False

    def _get_img_md(self, path):
        if path and len(path) > 0:
            url = self.uploader.upload(path)
            if url and len(url) > 0:
                name = os.path.split(path)[-1]
                return "![{}]({})".format(name, url)
        return ""

    def _get_weekly_blog_body(self, message_id):
        delta = 0.05
        weekly_replies = self._get_replies("welcome", 7.0 - delta)  # 7 days
        quarterly_replies = self._get_replies("welcome",
                                              90.0 - delta)  # 90 days
        all_replies = self._get_replies("welcome", 365.0 - delta)  # 365 days

        weekly_total = len(weekly_replies)
        quarterly_total = len(quarterly_replies)
        total = len(all_replies)

        print(weekly_total, quarterly_total, total)

        pic_weekly = draw_weekly_data(weekly_replies,
                                      os.path.join(self.folder, "weekly.png"))
        pic_quarterly = draw_quarterly_data(
            quarterly_replies, os.path.join(self.folder, "quarterly.png"))
        pic_all = draw_all_data(all_replies,
                                os.path.join(self.folder, "all.png"))

        weekly_graph = self._get_img_md(pic_weekly)
        quarterly_graph = self._get_img_md(pic_quarterly)
        all_graph = self._get_img_md(pic_all)

        return get_message(message_id).format(total=total,
                                              weekly_total=weekly_total,
                                              quarterly_total=quarterly_total,
                                              weekly_graph=weekly_graph,
                                              quarterly_graph=quarterly_graph,
                                              all_graph=all_graph)

    def publish_weekly_stats(self):
        """ publish the weekly statistics post about newbies """
        title = "{} {}".format(WEEKLY_SUMMARY_PREFIX, self._get_time_str())
        if not self._has_published(title, WEEKLY_SUMMARY_PREFIX):
            body = self._get_weekly_blog_body("weekly_summary")
            self.writer.post(title, body, SUMMARY_POST_TAGS)
            logger.info(
                "I have published this week's post [{}] successfully".format(
                    title))
            return True
        else:
            logger.info(
                "Skip this post [{}], because I already published the post with the same title"
                .format(title))
            return False
Exemple #6
0
class InfoBot:
    def __init__(self, author, mode="stream.comment", config={}):
        self.author = author
        self.writer = Writer(author=self.author)
        self.uploader = Uploader(author=self.author)
        self.mode = mode
        self.config = config

        # the configuration functions for a info bot
        self.data = lambda: []
        self.title = lambda: None
        self.body = lambda: None
        self.tags = lambda: None
        self.ready = lambda: True
        self.self_vote = lambda: False

    def _has_reply_comment(self, receiver, message_id):
        comments = self._read_comments()
        for c in comments:
            # check the receiver and the message_id fingerprint
            if c.parent_author == receiver and verify_message(
                    message_id, c.body):
                logger.info(
                    "I found I replied to @{} with message [{}] by searching comment history"
                    .format(receiver, message_id))
                return (True, c)
        return (False, None)

    def _has_replied(self, receiver, message_id):
        # has reply records in DB, or has replied by checking steem APIs
        if self._has_reply_record(receiver, message_id):
            return True
        (replied, comment) = self._has_reply_comment(receiver, message_id)
        if replied:
            c = SteemComment(comment=comment.get_parent())
            # cache the record into database since not found
            self._add_reply_record(receiver, message_id, c, comment["created"])
            return True
        return False

    def _get_reply_body(self, message_id, author):
        account = SteemAccount(author)
        comments_num = account.remaining_comments() or ''
        daily_comments_num = round(account.daily_recovery_comments(), 1) or ''
        return get_message(message_id).format(
            name=author,
            comments_num=comments_num,
            daily_comments_num=daily_comments_num)

    def reply(self, message_id=None, post=None, url=None):
        """ reply to the' post """
        c = SteemComment(comment=post, url=url)
        receiver = c.get_comment().author
        if not self._has_replied(receiver, message_id):
            title = c.get_comment().title
            message = self._get_reply_body(message_id, receiver)
            self.writer.reply(c.get_comment(), message)
            self._add_reply_record(receiver, message_id, c)
            logger.info("Replied to @{}'s post [{}] with [{}] message".format(
                receiver, title, message_id))
            return True
        else:
            logger.info(
                "Skip reply account @{} with [{}] message, because we already reliped before"
                .format(receiver, message_id))
            return False

    def _has_published(self, title):
        posts = get_posts(account=self.author, limit=50)
        if len(posts) > 0:
            for post in posts:
                if post.title == title:
                    return True
        return False

    def _get_time_str(self):
        return get_utc_date_str()

    def _get_accessible_url(self, url):
        if url and len(url) > 0:
            return url.replace(STEEM_HOST, ACCESSIBLE_STEEM_HOST)
        return ""

    def publish(self, title, body, tags):
        """ publish the post """
        if not self._has_published(title):
            self_vote = self.self_vote() or False
            self.writer.post(title, body, tags, self_vote=self_vote)
            logger.info(
                "I have published the post [{}] successfully".format(title))
            return True
        else:
            logger.info(
                "Skip this post [{}], because I already published the post with the same title"
                .format(title))
            return False

    def get_votes(self, authors, up=None):
        votes = []
        for author in authors:
            acc = SteemAccount(author=author)
            my_votes = acc.get_votes(up)
            for v in my_votes:
                v['voter'] = author
            votes += my_votes
        return votes

    def get_data(self, data):
        self.data = data
        return self

    def get_title(self, title):
        self.title = title
        return self

    def get_body(self, body):
        self.body = body
        return self

    def get_tags(self, tags):
        self.tags = tags
        return self

    def is_ready(self, ready):
        self.ready = ready
        return self

    def is_self_vote(self, self_vote):
        self.self_vote = self_vote
        return self

    def run(self):
        data = self.data()
        title = self.title(data)
        body = self.body(data)
        tags = self.tags(data)
        if self.ready(data):
            self.publish(title, body, tags)