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
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 __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
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)
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
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)