示例#1
0
def fetch_poll_data(author, permlink):
    """
    Fetch a poll from the blockchain and return the poll metadata.
    """
    c = LightSteemClient()
    content = c.get_content(author, permlink)
    if content.get("id") == 0:
        raise ValueError("Not a valid blockchain Comment object")
    metadata = json.loads(content.get("json_metadata"))
    if not metadata:
        raise ValueError("Not a poll")
    if metadata.get("content_type") != "poll":
        raise ValueError("Not a poll")

    votes_casted = False
    comments = c.get_content_replies(author, permlink)
    for comment in comments:
        if not comment.get("json_metadata"):
            continue
        json_metadata = json.loads(comment.get("json_metadata"))
        try:
            if json_metadata and json_metadata.get(
                    "content_type") == "poll_vote":
                votes_casted = True
        except AttributeError:
            continue

    return {
        "question": metadata.get("question"),
        "description": metadata.get("description"),
        "answers": metadata.get("choices"),
        "tags": metadata.get("tags"),
        "votes_casted": votes_casted,
    }
示例#2
0
class TransferListener:
    def __init__(self,
                 account,
                 posting_key=None,
                 active_key=None,
                 nodes=None,
                 db=None,
                 probability_dimensions=None,
                 min_age_to_vote=None,
                 max_age_to_vote=None,
                 minimum_vp_to_vote=None,
                 vote_price=None):
        self.db = db
        self.nodes = nodes or ["https://api.steemit.com"]
        self.account = account
        self.client = Client(loglevel=logging.INFO)
        self.vote_client = Client(keys=[
            posting_key,
        ], nodes=nodes)
        self.refund_client = Client(keys=[
            active_key,
        ], nodes=nodes)
        self.min_age_to_vote = min_age_to_vote or 300
        self.max_age_to_vote = max_age_to_vote or 43200
        self.minimum_vp_to_vote = minimum_vp_to_vote or 80
        self.vote_percent = VotePercent(
            probability_dimensions=probability_dimensions)
        self.vote_price = Amount(vote_price) or Amount("1.000 STEEM")

    def get_incoming_transfers(self):
        stop_at = datetime.now() - timedelta(hours=3)
        acc = self.client.account(self.account)
        transfers = []
        for _, transaction in acc.history(
                filter=["transfer"],
                only_operation_data=False,
                stop_at=stop_at,
        ):
            op = transaction["op"][1]
            if op["to"] != self.account:
                continue
            transfers.append(transaction)

        return transfers

    def poll_transfers(self):
        while True:
            try:
                for transfer in self.get_incoming_transfers():
                    self.process_transfer(transfer)
            except Exception as error:
                print(error)
            time.sleep(3)

    def in_global_blacklist(self, author):
        url = "http://blacklist.usesteem.com/user/" + author
        response = requests.get(url).json()
        return bool(len(response["blacklisted"]))

    def get_content(self, author, permlink, retries=None):
        if not retries:
            retries = 0

        try:
            content = self.client.get_content(author, permlink)
        except Exception as error:
            if retries > 5:
                raise
            return self.get_content(author, permlink, retries=retries + 1)

        return content

    def get_active_votes(self, author, permlink, retries=None):
        if not retries:
            retries = 0

        try:
            active_votes = self.client.get_active_votes(author, permlink)
        except Exception as error:
            if retries > 5:
                raise
            return self.get_active_votes(author, permlink, retries=retries + 1)

        return [v["voter"] for v in active_votes]

    def refund(self, to, amount, memo, incoming_trx_id):

        if self.db.database.transfers.count({
                "incoming_trx_id":
                incoming_trx_id,
                "status":
                TransferStatus.REFUNDED.value
        }):
            logger.info("Already refunded. (TRX id: %s)", incoming_trx_id)
            return

        try:
            op = Operation('transfer', {
                "from": self.account,
                "to": to,
                "amount": amount,
                "memo": memo,
            })
            self.refund_client.broadcast(op)
            status = TransferStatus.REFUNDED.value
        except Exception as e:
            print(e)
            status = TransferStatus.REFUND_FAILED.value

        self.db.database.transfers.update_one(
            {"incoming_tx_id": incoming_trx_id},
            {"$set": {
                "status": status
            }},
        )

    def process_transfer(self, transaction_data):

        op = transaction_data["op"][1]
        amount = Amount(op["amount"])

        # check if transaction is already registered in the database
        if self.db.is_transfer_already_registered(transaction_data["trx_id"]):
            logger.info(
                "Transaction is already registered. Skipping. (TRX: %s)",
                transaction_data["trx_id"])
            return

        # register the transaction into db
        self.db.register_incoming_transaction(
            op["from"],
            op["memo"],
            transaction_data["block"],
            transaction_data["trx_id"],
        )

        # check if the asset is valid
        if amount.symbol != "STEEM":
            logger.info(
                "Invalid asset. Refunding %s with %s. (TRX: %s)",
                op["from"],
                amount,
                transaction_data["trx_id"],
            )
            self.refund(
                op["from"],
                op["amount"],
                RefundReason.INVALID_ASSET.value,
                transaction_data["trx_id"],
            )
            return

        # check if the VP is suitable
        # Check the VP
        acc = self.client.account(self.account)
        if acc.vp() < self.minimum_vp_to_vote:
            print(acc.vp(), self.minimum_vp_to_vote)
            logger.info("Rando is sleeping. Refunding. (TRX: %s)",
                        transaction_data["trx_id"])
            self.refund(
                op["from"],
                op["amount"],
                RefundReason.SLEEP_MODE.value,
                transaction_data["trx_id"],
            )
            return

        # check vote price
        if amount.amount != self.vote_price.amount:
            logger.info("Invalid amount. Refunding. (TRX: %s)",
                        transaction_data["trx_id"])
            self.refund(
                op["from"],
                op["amount"],
                "Invalid amount. You need to send %s" % self.vote_price,
                transaction_data["trx_id"],
            )
            return

        # check if the sender is in a blacklist
        if self.in_global_blacklist(op["from"]):
            logger.info("Sender is in blacklist. Refunding. (TRX: %s)",
                        transaction_data["trx_id"])

            self.refund(
                op["from"],
                op["amount"],
                RefundReason.SENDER_IN_BLACKLIST.value,
                transaction_data["trx_id"],
            )
            return

        # check if the memo is valid
        memo = op["memo"]
        try:
            author = memo.split("@")[1].split("/")[0]
            permlink = memo.split("@")[1].split("/")[1]
        except IndexError as e:
            logger.info("Invalid URL. Refunding. Memo: %s", memo)
            self.refund(
                op["from"],
                op["amount"],
                RefundReason.INVALID_URL.value,
                transaction_data["trx_id"],
            )
            return

        # check if the author is in a blacklist
        if self.in_global_blacklist(author):
            logger.info("Author is in blacklist. Refunding. (TRX: %s)",
                        transaction_data["trx_id"])

            self.refund(
                op["from"],
                op["amount"],
                RefundReason.AUTHOR_IN_BLACKLIST.value,
                transaction_data["trx_id"],
            )
            return

        # check is the Comment is a valid Comment
        comment_content = self.get_content(author, permlink)
        if comment_content["id"] == 0:
            logger.info("Invalid post. Refunding. (TRX id: %s)",
                        transaction_data["trx_id"])
            self.refund(
                op["from"],
                op["amount"],
                RefundReason.INVALID_URL.value,
                transaction_data["trx_id"],
            )
            return

        # check comment is valid
        if comment_content.get("parent_author"):
            logger.info("Not a main post. Refunding. (TRX id: %s)",
                        transaction_data["trx_id"])
            self.refund(
                op["from"],
                op["amount"],
                RefundReason.INVALID_URL.value,
                transaction_data["trx_id"],
            )
            return

        # check if we've already voted for that post
        active_voters = self.get_active_votes(author, permlink)
        if self.account in active_voters:
            logger.info("Already voted. Refunding. (TRX id: %s)",
                        transaction_data["trx_id"])
            self.refund(
                op["from"],
                op["amount"],
                RefundReason.POST_IS_ALREADY_VOTED.value,
                transaction_data["trx_id"],
            )
            return

        # check if the Comment age is suitable
        created = parse(comment_content["created"])
        comment_age = (datetime.now() - created).total_seconds()
        if not (self.min_age_to_vote < comment_age < self.max_age_to_vote):
            self.refund(
                op["from"],
                op["amount"],
                f"Post age must be between {self.min_age_to_vote} and"
                f" {self.max_age_to_vote} seconds",
                transaction_data["trx_id"],
            )
            logger.info("Post age is invalid. Refunding. (TRX id: %s)",
                        transaction_data["trx_id"])
            return

        # vote
        self.vote(author, permlink, amount, transaction_data)

    def vote(self, author, permlink, amount, transaction_data):
        random_vote_weight = self.vote_percent.pick_percent()
        logger.info("Voting for %s/%s. Random vote weight: %%%s", author,
                    permlink, random_vote_weight)
        vote_op = Operation(
            'vote', {
                "voter": self.account,
                "author": author,
                "permlink": permlink,
                "weight": random_vote_weight * 100,
            })

        burn_op = Operation(
            'transfer', {
                "from": self.account,
                "to": "null",
                "amount": str(amount),
                "memo": f"Burning STEEM for {author}/{permlink}",
            })

        # vote
        try:
            self.vote_client.broadcast(vote_op)
            time.sleep(3)
            status = TransferStatus.VOTED.value
        except Exception as e:
            print(e)
            status = TransferStatus.VOTE_FAILED.value

        # burn
        try:
            self.refund_client.broadcast(burn_op)
            time.sleep(3)
        except Exception as e:
            print(e)
            status = TransferStatus.BURN_FAILED.value

        self.db.database.transfers.update_one(
            {"incoming_tx_id": transaction_data["trx_id"]},
            {"$set": {
                "status": status
            }},
        )