def _notifs(cls, post, pid, level, payout): # pylint: disable=too-many-locals,too-many-branches author = post['author'] author_id = Accounts.get_id(author) parent_author = post['parent_author'] date = post['last_update'] # reply notif if level == 'insert' and parent_author and parent_author != author: irredeemable = parent_author in Mutes.all() parent_author_id = Accounts.get_id(parent_author) if not irredeemable and not cls._muted(parent_author_id, author_id): ntype = 'reply' if post['depth'] == 1 else 'reply_comment' Notify(ntype, src_id=author_id, dst_id=parent_author_id, score=Accounts.default_score(author), post_id=pid, when=date).write() # mentions notif if level in ('insert', 'update'): accounts = set(filter(Accounts.exists, mentions(post['body']))) accounts -= {author, parent_author} score = Accounts.default_score(author) if score < 30: max_mentions = 5 elif score < 60: max_mentions = 10 else: max_mentions = 25 if len(accounts) <= max_mentions: penalty = min([score, 2 * (len(accounts) - 1)]) for mention in accounts: mention_id = Accounts.get_id(mention) if (not cls._mentioned(pid, mention_id) and not cls._muted(mention_id, author_id)): Notify('mention', src_id=author_id, dst_id=mention_id, post_id=pid, when=date, score=(score - penalty)).write() else: url = '@%s/%s' % (author, post['permlink']) log.info("skip %d mentions in %s", len(accounts), url) # votes notif url = post['author'] + '/' + post['permlink'] if url in cls._votes: voters = cls._votes[url] del cls._votes[url] net = float(post['net_rshares']) ratio = float(payout) / net if net else 0 for vote in post['active_votes']: rshares = int(vote['rshares']) if vote['voter'] not in voters or rshares < 10e9: continue contrib = int(1000 * ratio * rshares) if contrib < 20: continue # < $0.020 voter_id = Accounts.get_id(vote['voter']) if not cls._voted(pid, author_id, voter_id): score = min(100, (len(str(contrib)) - 1) * 25) # $1 = 75 payload = "$%.3f" % (contrib / 1000) Notify('vote', src_id=voter_id, dst_id=author_id, when=vote['time'], post_id=pid, score=score, payload=payload).write()
async def _load_discussion(db, author, permlink): """Load a full discussion thread.""" root_id = await get_post_id(db, author, permlink) if not root_id: return {} # build `ids` list and `tree` map ids = [] tree = {} todo = [root_id] while todo: ids.extend(todo) rows = await _child_ids(db, todo) todo = [] for pid, cids in rows: tree[pid] = cids todo.extend(cids) # load all post objects, build ref-map posts = await load_posts_keyed(db, ids) # remove posts/comments from muted accounts muted_accounts = Mutes.all() rem_pids = [] for pid, post in posts.items(): if post['author'] in muted_accounts: rem_pids.append(pid) for pid in rem_pids: if pid in posts: del posts[pid] if pid in tree: rem_pids.extend(tree[pid]) refs = {pid: _ref(post) for pid, post in posts.items()} # add child refs to parent posts for pid, post in posts.items(): if pid in tree: post['replies'] = [refs[cid] for cid in tree[pid] if cid in refs] # return all nodes keyed by ref return {refs[pid]: post for pid, post in posts.items()}
async def load_posts_keyed(db, ids, truncate_body=0): """Given an array of post ids, returns full posts objects keyed by id.""" assert ids, 'no ids passed to load_posts_keyed' # fetch posts and associated author reps sql = """SELECT post_id, author, permlink, title, body, category, depth, promoted, payout, payout_at, is_paidout, children, votes, created_at, updated_at, rshares, raw_json, json FROM hive_posts_cache WHERE post_id IN :ids""" result = await db.query_all(sql, ids=tuple(ids)) author_reps = await _query_author_rep_map(db, result) muted_accounts = Mutes.all() posts_by_id = {} for row in result: row = dict(row) row['author_rep'] = author_reps[row['author']] post = _condenser_post_object(row, truncate_body=truncate_body) post['active_votes'] = _mute_votes(post['active_votes'], muted_accounts) posts_by_id[row['post_id']] = post return posts_by_id