def _list_apps(config): """ Lists all apps that have been published to the 21 marketplace Args: config (Config): config object used for getting .two1 information. """ click.secho(UxString.my_apps.format(config.username), fg="green") client = rest_client.TwentyOneRestClient(TWO1_HOST, config.machine_auth, config.username) current_page = 0 total_pages = get_search_results(config, client, current_page) if total_pages < 1: return while 0 <= current_page < total_pages: try: prompt_resp = click.prompt(UxString.pagination, type=str) next_page = get_next_page(prompt_resp, current_page) if next_page == -1: model_id = prompt_resp display_app_info(config, client, model_id) elif next_page >= total_pages or next_page < 0: continue else: get_search_results(config, client, next_page) current_page = next_page except click.exceptions.Abort: return
def _flush(config): client = rest_client.TwentyOneRestClient(TWO1_HOST, config.machine_auth, config.username) flush_earnings(config, client) config.log("")
def _status(config, detail): """ Reports two1 stataus including balances, username, and mining hashrate Args: config (Config): config object used for getting .two1 information detail (bool): Lists all balance details in status report Returns: dict: a dictionary of 'account', 'mining', and 'wallet' items with formatted strings for each value """ client = rest_client.TwentyOneRestClient(TWO1_HOST, config.machine_auth, config.username) status = { "account": status_account(config), "mining": status_mining(config, client), "wallet": status_wallet(config, client, detail) } config.log("") # status_endpoints(config) # status_bought_endpoints(config) return status
def _join(config, network): """Perform the rest_client join""" client = rest_client.TwentyOneRestClient(TWO1_HOST, config.machine_auth, config.username) try: config.log(UxString.update_superuser) start_zerotier_command = ["sudo", "service", "zerotier-one", "start"] subprocess.check_output(start_zerotier_command) zt_device_address = zerotier.device_address() response = client.join(network, zt_device_address) if response.ok: join_command = [ "sudo", "zerotier-cli", "join", response.json().get("networkid") ] subprocess.check_output(join_command) config.log( UxString.successful_join.format( click.style(network, fg="magenta"))) except ServerRequestError as e: if e.status_code == 401: config.log(UxString.invalid_network) else: raise e except subprocess.CalledProcessError as e: config.log(str(e))
def start_cpu_mining(config): """Mine at the CPU. >>> from two1.commands.config import Config >>> config = Config() >>> start_cpu_mining(config) """ client = rest_client.TwentyOneRestClient(cmd_config.TWO1_HOST, config.machine_auth, config.username) enonce1, enonce2_size, reward = set_payout_address(config, client) start_time = time.time() config.log(UxString.mining_start.format(config.username, reward)) work = get_work(config, client) found_share = mine_work(work, enonce1=enonce1, enonce2_size=enonce2_size) paid_satoshis = save_work(client, found_share, config.username) end_time = time.time() duration = end_time - start_time config.log(UxString.mining_success.format(config.username, paid_satoshis, duration), fg="magenta") click.echo(UxString.mining_status) status.status_wallet(config, client) click.echo( UxString.mining_finish.format(click.style("21 status", bold=True), click.style("21 buy", bold=True)))
def _inbox(config): """ Shows a list of notifications on a click pager Args: config (Config): config object used for getting .two1 information Returns: list: list of notifications in users inbox """ client = rest_client.TwentyOneRestClient(TWO1_HOST, config.machine_auth, config.username) prints = [] notifications, has_unreads = get_notifications(config, client) if len(notifications) > 0: prints.append(UxString.notification_intro) prints.extend(notifications) output = "\n".join(prints) config.echo_via_pager(output) if has_unreads: client.mark_notifications_read(config.username) return notifications
def _log(config, debug): client = rest_client.TwentyOneRestClient(TWO1_HOST, config.machine_auth, config.username) response = client.get_earning_logs() logs = response["logs"] prints = [UxString.log_intro] if not debug: logs = filter_rollbacks(logs) for entry in logs: prints.append(get_headline(entry)) # reason prints.append(get_description(entry)) prints.append("\n") # transaction details if entry["amount"] < 0 and "paid_to" in entry and "txns" in entry: prints.append(get_txn_details(entry)) prints.append("\n") if len(prints) == 1: prints.append(UxString.empty_logs) output = "\n".join(prints) config.echo_via_pager(output) return logs
def _search(config, search_string): """ Searches the marketplace for apps by the given search_string Apps are then displayed in a pager in which the user can get more info on a selected app. Args: config (Config): config object used for getting .two1 information search_string (str): string used to search for apps """ client = rest_client.TwentyOneRestClient(TWO1_HOST, config.machine_auth, config.username) if search_string is None: click.secho(UxString.list_all, fg="green") current_page = 0 total_pages = get_search_results(client, search_string, current_page) if total_pages < 1: return while 0 <= current_page < total_pages: try: prompt_resp = click.prompt(UxString.pagination, type=str) next_page = get_next_page(prompt_resp, current_page) if next_page == -1: model_id = prompt_resp display_search_info(config, client, model_id) elif next_page >= total_pages or next_page < 0: continue else: get_search_results(client, search_string, next_page) current_page = next_page except click.exceptions.Abort: return
def _rate(config, app_id, rating): """ Rate an app listed in the marketplace Args: config (Config): Config object used for user specific information app_id (str): Unique app id used to identify which app to rate rating (int): rating number (1-5) Raises: ServerRequestError: If any other server error occurs other than a 404 """ if rating < 1 or rating > 5: click.secho(UxString.bad_rating, fg="red") return client = rest_client.TwentyOneRestClient(TWO1_HOST, config.machine_auth, config.username) try: client.rate_app(app_id, rating) except ServerRequestError as e: if e.status_code == 404: click.secho(UxString.rating_app_not_found.format(app_id)) return raise e click.secho(UxString.rating_success.format(rating, app_id))
def _log(config, debug): client = rest_client.TwentyOneRestClient(TWO1_HOST, config.machine_auth, config.username) prints = [] logs = get_bc_logs(client, debug) prints.extend(logs) output = "\n".join(prints) config.echo_via_pager(output) return logs
def _publish(config, manifest_path, marketplace, skip, overrides): """ Publishes application by uploading the manifest to the given marketplace Args: config (Config): config object used for getting .two1 information manifest_path (str): the path to the manifest file marketplace (str): the zerotier marketplace name skip (bool): skips strict checking of manifest file overrides (dict): Dictionary containing the key/value pairs will be overridden in the manifest. """ try: manifest_json = check_app_manifest(manifest_path, overrides, marketplace) app_url = urlparse(manifest_json["host"]) app_ip = app_url.path.split(":")[0] if not skip: address = get_zerotier_address(marketplace) if address != app_ip: if not click.confirm( UxString.wrong_ip.format(app_ip, address, app_ip)): click.secho( UxString.switch_host.format(manifest_path, app_ip, address)) return except ValueError: return except ValidationError as e: click.secho(UxString.bad_manifest.format(manifest_path, e.args[0], UxString.publish_docs_url), fg="red") return app_name = manifest_json["info"]["title"] app_endpoint = "{}://{}{}".format(manifest_json["schemes"][0], manifest_json["host"], manifest_json["basePath"]) click.secho( UxString.publish_start.format(app_name, app_endpoint, marketplace)) client = rest_client.TwentyOneRestClient(TWO1_HOST, config.machine_auth, config.username) payload = {"manifest": manifest_json, "marketplace": marketplace} response = client.publish(payload) if response.status_code == 201: click.secho(UxString.publish_success.format(app_name, marketplace))
def _status(config, detail): client = rest_client.TwentyOneRestClient(TWO1_HOST, config.machine_auth, config.username) status = { "account": status_account(config), "mining": status_mining(config, client), "wallet": status_wallet(config, client, detail) } config.log("") # status_endpoints(config) # status_bought_endpoints(config) return status
def _check_notifications(config, *args, **kwargs): res = func(config, *args, **kwargs) try: client = rest_client.TwentyOneRestClient(TWO1_HOST, config.machine_auth, config.username) notifications = client.get_notifications(config.username) notification_json = notifications.json() urgent_notifications = notification_json["urgent_count"] if urgent_notifications > 0: click.secho( UxString.unread_notifications.format(urgent_notifications)) except: pass return res
def start_cpu_mining(config): """ Mines bitcoin on the command line by using the CPU of the system CPU mining, or foreground mining, is when the pool sets the difficulty very low and the CPU finds a valid solution. Args: config (Config): config object used for getting .two1 information """ client = rest_client.TwentyOneRestClient(cmd_config.TWO1_HOST, config.machine_auth, config.username) enonce1, enonce2_size, reward = set_payout_address(config, client) start_time = time.time() config.log(UxString.mining_start.format(config.username, reward)) work = get_work(config, client) found_share = mine_work(work, enonce1=enonce1, enonce2_size=enonce2_size) paid_satoshis = save_work(client, found_share, config.username) end_time = time.time() duration = end_time - start_time config.log( UxString.mining_success.format(config.username, paid_satoshis, duration), fg="magenta") click.echo(UxString.mining_status) status.status_wallet(config, client) click.echo(UxString.mining_finish.format( click.style("21 status", bold=True), click.style("21 buy", bold=True)))
def _join(config, network): """ Joins the given zerotier network Args: config (Config): config object used for getting .two1 information network (str): the name of the network being joined. Defaults to 21market Raises: ServerRequestError: if server returns an error code other than 401 """ client = rest_client.TwentyOneRestClient(TWO1_HOST, config.machine_auth, config.username) try: config.log(UxString.update_superuser) user_platform = platform.system() if user_platform != "Darwin": start_zerotier_command = [ "sudo", "service", "zerotier-one", "start" ] subprocess.check_output(start_zerotier_command) zt_device_address = zerotier.device_address() response = client.join(network, zt_device_address) if response.ok: join_command = [ "sudo", "zerotier-cli", "join", response.json().get("networkid") ] subprocess.check_output(join_command) config.log(UxString.successful_join.format(click.style(network, fg="magenta"))) except ServerRequestError as e: if e.status_code == 401: config.log(UxString.invalid_network) else: raise e except subprocess.CalledProcessError as e: config.log(str(e))
def _delete_app(config, app_id): """ Deletes an app that has been published to the 21 marketplace Args: config (Config): config object used for getting .two1 information. app_id (str): a unique string that identifies the application. """ if click.confirm(UxString.delete_confirmation.format(app_id)): client = rest_client.TwentyOneRestClient(TWO1_HOST, config.machine_auth, config.username) try: resp = client.delete_app(config.username, app_id) resp_json = resp.json() deleted_title = resp_json["deleted_title"] click.secho(UxString.delete_success.format(app_id, deleted_title)) except ServerRequestError as e: if e.status_code == 404: click.secho(UxString.delete_app_not_exist.format(app_id), fg="red") elif e.status_code == 403: click.secho(UxString.delete_app_no_permissions.format(app_id), fg="red")
def _list(config): """ Lists all of the apps that the user has rated If no apps have been rated, then an empty formatted list is printed Args: config (Config): Config object used for user specific information Raises: ServerRequestError: If server error occurs other than a 404 """ click.secho(UxString.rating_list) client = rest_client.TwentyOneRestClient(TWO1_HOST, config.machine_auth, config.username) try: ratings = client.get_ratings() headers = ["id", "App title", "Creator", "Rating", "Rating Date"] ratings = ratings.json()["ratings"] rows = [] for rating in ratings: rating_date = datetime.datetime.fromtimestamp( rating["rating_date"]).strftime("%Y-%m-%d %H:%M") rating_score = "{}/5".format(rating["rating"]) rows.append([rating["app_id"], rating["app_title"], rating["app_creator"], rating_score, rating_date]) click.echo(tabulate(rows, headers, tablefmt="grid")) except ServerRequestError as e: if e.status_code == 404: click.secho(UxString.no_ratings) return else: raise e
def _buy(config, resource, data, method, data_file, output_file, payment_method, max_price, info_only): """ Buys bitcoin payable content over http Todo: reduce number of input args Exception is too general, raise a different exception when user cannot pay Args: config (Config): config object used for getting .two1 information resource (str): resource or content to purchase method (str): HTTP request method, defaults to GET data_file (str): name of the data file to send in HTTP body output_file (str): Output file name payment_method (str): Type of payment used in the purchase: offchain, onchain, channel max_price (int): Max price of resource info_only (bool): Flag which will only get info and not purcahase the resource Raises: NotImplementedError: if endpoint or resource is not valid ResourcePriceGreaterThanMaxPriceError: If the resource price is greater than the max price """ # If resource is a URL string, then bypass seller search if URL_REGEXP.match(resource): target_url = resource seller = target_url elif re.match(r'^(((\w*)(\/){0,1})(\w*)){0,2}(\/){0,1}$', resource) and resource not in DEMOS: target_url = 'https://mkt.21.co/' + resource seller = target_url elif resource in DEMOS: target_url = TWO1_MERCHANT_HOST + DEMOS[resource]["path"] data = json.dumps(data) else: # If we can't figure out the resource type, attempt to use `http` target_url = 'http://' + resource # Change default HTTP method from "GET" to "POST", if we have data if method == "GET" and (data or data_file): method = "POST" # Set default headers for making bitrequests with JSON-like data headers = {'Content-Type': 'application/json'} try: # Find the correct payment method if payment_method == 'offchain': bit_req = BitTransferRequests(config.machine_auth, config.username) elif payment_method == 'onchain': bit_req = OnChainRequests(config.wallet) elif payment_method == 'channel': bit_req = ChannelRequests(config.wallet) channel_list = bit_req._channelclient.list() if not channel_list: confirmed = click.confirm(UxString.buy_channel_warning.format( bit_req.DEFAULT_DEPOSIT_AMOUNT, PaymentChannelStateMachine.PAYMENT_TX_MIN_OUTPUT_AMOUNT), default=True) if not confirmed: raise Exception(UxString.buy_channel_aborted) else: raise Exception('Payment method does not exist.') # Make the request if info_only: res = bit_req.get_402_info(target_url) else: res = bit_req.request(method.lower(), target_url, max_price=max_price, data=data or data_file, headers=headers) except ResourcePriceGreaterThanMaxPriceError as e: config.log( UxString.Error.resource_price_greater_than_max_price.format(e)) return except Exception as e: f = get_fees() buy_fee = 2 * f['per_input'] + f['per_output'] if 'Insufficient funds.' in str(e): config.log( UxString.Error.insufficient_funds_mine_more.format(buy_fee)) else: config.log(str(e), fg="red") return # Output results to user if output_file: # Write response output file output_file.write(res.content) elif info_only: # Print headers that are related to 402 payment required for key, val in res.items(): config.log('{}: {}'.format(key, val)) elif resource in DEMOS: config.log(DEMOS[resource]["formatter"](res)) else: # Write response to console config.log(res.text) # Write the amount paid out if something was truly paid if not info_only and hasattr(res, 'amount_paid'): client = rest_client.TwentyOneRestClient(TWO1_HOST, config.machine_auth, config.username) user_balances = _get_balances(config, client) if payment_method == 'offchain': balance_amount = user_balances.twentyone balance_type = '21.co' elif payment_method == 'onchain': balance_amount = user_balances.onchain balance_type = 'blockchain' elif payment_method == 'channel': balance_amount = user_balances.channels balance_type = 'payment channels' config.log( "You spent: %s Satoshis. Remaining %s balance: %s Satoshis." % (res.amount_paid, balance_type, balance_amount)) # Record the transaction if it was a payable request if hasattr(res, 'paid_amount'): config.log_purchase(s=seller, r=resource, p=res.paid_amount, d=str(datetime.datetime.today()))
from flask.ext.admin.contrib.sqla import ModelView from flask.ext.login import LoginManager from flask.ext.admin.contrib.fileadmin import FileAdmin # Setup the dashboard from two1.commands import status from two1.commands import log from two1.commands import flush from two1.commands import mine from two1.lib.server import rest_client from two1.commands.config import Config from two1.commands.config import TWO1_HOST conf = Config() host = TWO1_HOST client = rest_client.TwentyOneRestClient(host, conf.machine_auth, conf.username) admin = Admin(app, name='Admin', template_mode='bootstrap3') class DashboardView(BaseView): @expose('/', methods=('GET', 'POST')) def dashboard(self): flush_message = "" status_mining = status.status_mining(conf, client) if request.method == 'POST': print(request.form) if request.form['submit'] == 'Flush Earnings': flush_message = self.doFlush() else: if status_mining[
import json from flask import Flask from flask import request from two1.lib.server import rest_client from two1.commands.config import TWO1_HOST from two1.commands.config import Config from two1.lib.wallet import Wallet from two1.lib.bitserv.flask import Payment app = Flask(__name__) wallet = Wallet() config = Config() payment = Payment(app, wallet) client = rest_client.TwentyOneRestClient(TWO1_HOST, config.machine_auth, config.username) class Charity(object): name = "" address = "" # The class "constructor" - It's actually an initializer def __init__(self, name, address): self.name = name self.address = address bitGive = Charity("Bit Give", "1PEoUKNxTZsc5rFSQvQjeTVwDE9vEDCRWm") charities = [bitGive] # endpoint to look up avaialable charities to donate to @app.route('/charities', methods=['GET'])