def check_muted(self, muted_accounts):
     for acc in muted_accounts:
         if acc not in self.accounts:
             continue
         if not self.accounts[acc]["muted"]:
             self.accounts[acc]["muted"] = True
             store_data(self.data_file, "accounts", self.accounts)
             if self.accounts[acc]["delegated_hp"] > 0 and not self.accounts[
                     acc]["delegation_revoked"]:
                 self.remove_delegation(acc)
                 self.notify_account(acc, self.config["delegationMuteMsg"])
 def check_for_sufficient_hp(self):
     data_db = read_data(self.data_file)
     if "hp_warning_send" in data_db:
         hp_warning_send = data_db["hp_warning_send"]
     else:
         hp_warning_send = False
     hp = self.delegation_acc.get_token_power(only_own_vests=True)
     if hp_warning_send and hp > self.config["hpWarning"]:
         hp_warning_send = False
     elif not hp_warning_send and hp < self.config["hpWarning"]:
         if not self.config["no_broadcast"]:
             hp_warning_send = True
         self.notify_admin(
             "Warning: HIVE POWER of @%s is below %.3f HP" %
             (self.config["delegationAccount"], self.config["hpWarning"]))
     store_data(self.data_file, "hp_warning_send", hp_warning_send)
 def check_account_on_activity(self, account, timestamp):
     if account not in self.accounts:
         return
     acc = Account(account, blockchain_instance=self.hive)
     self.accounts[account]["rc"] = acc.get_rc_manabar()["current_mana"]
     self.accounts[account]["hp"] = acc.get_token_power(only_own_vests=True)
     self.accounts[account]["rc_comments"] = self.accounts[account][
         "rc"] / self.comment_rc_costs
     store_data(self.data_file, "accounts", self.accounts)
     if self.accounts[account]["delegated_hp"] > 0:
         return
     if self.accounts[account]["delegation_revoked"]:
         return
     if self.accounts[account]["hp"] > self.config["maxUserHP"]:
         return
     if self.accounts[account]["rc_comments"] < self.config["minPostRC"]:
         ok = self.add_delegation(account, timestamp)
         if ok:
             self.notify_account(account, self.config["delegationMsg"])
    def remove_delegation(self, account):

        if self.config["no_broadcast"]:
            logger.info(
                "no_broadcast = True, Would remove delegation from %s" %
                (account))
            return False
        if self.delegation_acc.blockchain.wallet.locked():
            self.delegation_acc.blockchain.wallet.unlock(
                self.config["wallet_password"])
        logger.info("remove delegation from %s" % (account))
        try:
            self.delegation_acc.delegate_vesting_shares(account, 0)
        except Exception as e:
            logger.warn(str(e))
            self.notify_admin("Could not undelegate HP from %s" % (account))
            return False
        self.accounts[account]["delegation_revoked"] = True
        store_data(self.data_file, "accounts", self.accounts)
        return True
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("config", help="Config file in JSON format")
    parser.add_argument("--logconfig",
                        help="Logger Config file in JSON format",
                        default='logger.json')
    parser.add_argument("--datadir", help="Data storage dir", default='.')
    parser.add_argument('--list-accounts', action='store_true')
    args = parser.parse_args()

    setup_logging(default_path=args.logconfig)

    logger.info("Loading config: %s" % str(args.config))

    config = json.loads(open(os.path.abspath(args.config)).read())
    datadir = args.datadir

    nodelist = NodeList()
    nodelist.update_nodes()
    hive = Hive(node=nodelist.get_hive_nodes(),
                num_retries=5,
                call_num_retries=3,
                timeout=15)
    blockchain = Blockchain(blockchain_instance=hive)
    logger.info(str(hive))
    data_file = os.path.join(datadir, 'data.db')
    bot = DelegationOnboardBot(config, data_file, hive)

    data_db = read_data(data_file)
    if "last_block_num" in data_db:
        last_block_num = data_db["last_block_num"]
    else:
        last_block_num = 0

    if "last_block_num" in data_db:
        start_block = data_db["last_block_num"] + 1
        if start_block == 35922615:
            start_block += 1
        logger.info("Start block_num: %d" % start_block)

        stop_block = start_block + 100
        if stop_block > blockchain.get_current_block_num():
            stop_block = blockchain.get_current_block_num()
    else:
        start_block = None
        stop_block = None

    if args.list_accounts:
        t = PrettyTable([
            "account", "timestamp", "muted", "hp", "del. hp", "del. timestamp",
            "rc_comments", "del revoked"
        ])
        t.align = "l"
        for acc_name in data_db["accounts"]:
            acc = data_db["accounts"][acc_name]
            if acc["timestamp"] is None:
                timestamp = ""
            else:
                timestamp = formatTimeString(acc["timestamp"])
            if acc["delegation_timestamp"] is None:
                del_timestamp = ""
            else:
                del_timestamp = formatTimeString(acc["delegation_timestamp"])
            t.add_row([
                acc_name, timestamp, acc["muted"], acc["hp"],
                acc["delegated_hp"], del_timestamp,
                round(acc["rc_comments"], 3), acc["delegation_revoked"]
            ])
        print(t)
        return

    logger.info("starting delegation manager for onboarding..")
    block_counter = None
    last_print_stop_block = stop_block
    while True:
        if start_block is not None and stop_block is not None:
            if last_print_stop_block is not None and stop_block - last_print_stop_block > 1:
                last_print_stop_block = stop_block
        last_block_num = bot.run(start_block, stop_block)
        # Update nodes once a day
        if block_counter is None:
            block_counter = last_block_num
        elif last_block_num - block_counter > 20 * 60 * 24:
            nodelist.update_nodes()
            hive = Hive(node=nodelist.get_hive_nodes(),
                        num_retries=5,
                        call_num_retries=3,
                        timeout=15)

            bot.hive = hive

        start_block = last_block_num + 1

        stop_block = start_block + 100
        if stop_block > blockchain.get_current_block_num():
            stop_block = blockchain.get_current_block_num()

        store_data(data_file, "last_block_num", last_block_num)
        time.sleep(3)
    def __init__(self, config, data_file, hived_instance):
        self.config = config
        self.data_file = data_file
        data_db = read_data(data_file)
        if "accounts" in data_db:
            accounts = data_db["accounts"]
        else:
            accounts = {}
        self.hive = hived_instance

        # add log stats
        self.log_data = {
            "start_time": 0,
            "last_block_num": None,
            "new_commands": 0,
            "stop_block_num": 0,
            "stop_block_num": 0,
            "time_for_blocks": 0
        }
        config_cnt = 0
        necessary_fields = [
            "delegationAccount", "referrerAccount", "adminAccount",
            "delegationAmount", "delegationLength", "beneficiaryRemoval",
            "minPostRC", "muteAccount", "hpWarning", "maxUserHP", "notifyUser",
            "delegationMsg", "delegationLengthMsg", "delegationMuteMsg",
            "delegationBeneficiaryMsg", "delegationMaxMsg"
        ]

        check_config(self.config, necessary_fields, self.hive)
        self.hive.wallet.unlock(self.config["wallet_password"])
        self.onboard_api = "https://hiveonboard.com/api/referrer/%s" % self.config[
            "referrerAccount"]
        self.blockchain = Blockchain(mode='head',
                                     blockchain_instance=self.hive)
        self.muted_acc = Account(self.config["muteAccount"],
                                 blockchain_instance=self.hive)
        self.delegation_acc = Account(self.config["delegationAccount"],
                                      blockchain_instance=self.hive)
        self.muted_accounts = self.muted_acc.get_mutings(limit=1000)

        active_key = False
        for key in self.delegation_acc["active"]["key_auths"]:
            if key[0] in self.hive.wallet.getPublicKeys(current=True):
                active_key = True
        for key in self.delegation_acc["owner"]["key_auths"]:
            if key[0] in self.hive.wallet.getPublicKeys(current=True):
                active_key = True
        if not active_key:
            logger.warn(
                "Active key from %s is not stored into the beempy wallet." %
                self.delegation_acc["name"])

        rc = RC(blockchain_instance=self.hive)
        self.comment_rc_costs = rc.comment(tx_size=4000,
                                           permlink_length=40,
                                           parent_permlink_length=0)
        self.accounts = self.get_referrer(accounts)
        self.update_delegations()
        self.check_muted(self.muted_accounts)
        self.check_delegation_age()
        self.check_max_hp()
        self.print_account_info()
        store_data(self.data_file, "accounts", self.accounts)
    def run(self, start_block, stop_block):
        if self.hive.wallet.locked():
            self.hive.wallet.unlock(self.config["wallet_password"])
        if self.hive.wallet.locked():
            logger.error(
                "Could not unlock wallet. Please check wallet_passowrd in config"
            )
            return

        current_block = self.blockchain.get_current_block_num()
        if stop_block is None or stop_block > current_block:
            stop_block = current_block

        if start_block is None:
            start_block = current_block
            last_block_num = current_block - 1
        else:
            last_block_num = start_block - 1

        self.check_delegation_age()
        self.check_max_hp()
        self.check_for_sufficient_hp()
        store_data(self.data_file, "accounts", self.accounts)

        self.log_data["start_block_num"] = start_block
        for op in self.blockchain.stream(start=start_block, stop=stop_block):
            self.log_data = print_block_log(self.log_data, op,
                                            self.config["print_log_at_block"])
            last_block_num = op["block_num"]
            timestamp = op["timestamp"].replace(tzinfo=None)

            if op["type"] == "comment":
                account = op["author"]
                if account not in list(self.accounts.keys()):
                    continue
                self.check_account_on_activity(account, timestamp)
                if op["parent_author"] == "":
                    self.check_beneficiaries(op["author"], op["permlink"])
            elif op["type"] == "vote":
                account = op["voter"]
                if account not in list(self.accounts.keys()):
                    continue
                self.check_account_on_activity(account, timestamp)
            elif op["type"] == "transfer":
                account = op["from"]
                if account not in list(self.accounts.keys()):
                    continue
                self.check_account_on_activity(account, timestamp)
            elif op["type"] == "custom_json":

                if len(op["required_posting_auths"]) > 0:
                    account = op["required_posting_auths"][0]
                elif len(op["required_auths"]) > 0:
                    account = op["required_auths"][0]
                if op["id"] == "follow":
                    if op["json"] == "":
                        continue
                    json_data = json.loads(op["json"])
                    if "what" not in json_data:
                        continue
                    if len(json_data["what"]) == 0:
                        continue
                    if json_data["what"][0] != "ignore":
                        continue
                    if account == self.config["muteAccount"] and json_data[
                            "following"] in self.accounts:
                        self.check_muted([json_data["following"]])

                if account not in list(self.accounts.keys()):
                    continue

                self.check_account_on_activity(account, timestamp)
            elif op["type"] == "delegate_vesting_shares":
                if op["delegator"] != self.config["delegationAccount"]:
                    continue
                account = op["delegatee"]
                if account not in list(self.accounts.keys()):
                    continue
                delegated_hp = self.hive.vests_to_hp(
                    float(
                        Amount(op["vesting_shares"],
                               blockchain_instance=self.hive)))
                self.accounts[account]["delegated_hp"] = delegated_hp
                self.accounts[account]["delegation_timestamp"] = timestamp
                if delegated_hp > 0 and self.accounts[account][
                        "delegation_revoked"]:
                    self.accounts[account]["delegation_revoked"] = False
                elif delegated_hp == 0 and not self.accounts[account][
                        "delegation_revoked"]:
                    self.accounts[account]["delegation_revoked"] = True
                store_data(self.data_file, "accounts", self.accounts)

            elif op["type"] == "create_claimed_account":
                if op["json_metadata"] == "":
                    continue
                meta_data = json.loads(op["json_metadata"])
                if "beneficiaries" not in meta_data:
                    continue
                for entry in meta_data["beneficiaries"]:
                    if entry["label"] == "referrer" and entry[
                            "name"] == self.config["referrerAccount"]:
                        self.accounts[op["new_account_name"]] = {
                            "timestamp": None,
                            "weight": None,
                            "muted": False,
                            "rc": 0,
                            "hp": 0,
                            "delegated_hp": 0,
                            "delegation_timestamp": None,
                            "rc_comments": 0,
                            "delegation_revoked": False
                        }
                        self.accounts[
                            op["new_account_name"]]["weight"] = entry["weight"]
                        self.accounts[op["new_account_name"]][
                            "timestamp"] = op["timestamp"].replace(tzinfo=None)
                        store_data(self.data_file, "accounts", self.accounts)

        return last_block_num