def add_account(whiptail, bitshares_instance): """ "Add account" dialog :param whiptail.Whiptail whiptail: instance of Whiptail or NoWhiptail :param bitshares.BitShares bitshares_instance: an instance of BitShares class :return str: user-supplied account name """ validator = ConfigValidator(bitshares_instance) account = whiptail.prompt("Your Account Name") private_key = whiptail.prompt("Your Private Key", password=True) if not validator.validate_account_name(account): whiptail.alert("Account name does not exist.") return False if not validator.validate_private_key(account, private_key): whiptail.alert("Private key is invalid") return False if private_key and not validator.validate_private_key_type( account, private_key): whiptail.alert("Please use active private key.") return False # User can supply empty private key if it was added earlier if private_key: validator.add_private_key(private_key) whiptail.alert("Private Key added successfully.") return account
def __init__(self, view, bitshares_instance, mode): self.view = view self.mode = mode self.validator = ConfigValidator(Config(), bitshares_instance or shared_bitshares_instance())
def configure_dexbot(config, ctx): """ Main `cli configure` entrypoint :param dexbot.config.Config config: dexbot config """ whiptail = get_whiptail('DEXBot configure') workers = config.get('workers', {}) bitshares_instance = ctx.bitshares validator = ConfigValidator(bitshares_instance) if not workers: while True: txt = whiptail.prompt("Your name for the worker") if len(txt) == 0: whiptail.alert("Worker name cannot be blank. ") else: config['workers'] = { txt: configure_worker(whiptail, {}, bitshares_instance) } if not whiptail.confirm( "Set up another worker?\n(DEXBot can run multiple workers in one instance)" ): break setup_systemd(whiptail, config) else: while True: action = whiptail.menu( "You have an existing configuration.\nSelect an action:", [('LIST', 'List your workers'), ('NEW', 'Create a new worker'), ('EDIT', 'Edit a worker'), ('DEL_WORKER', 'Delete a worker'), ('ADD', 'Add a bitshares account'), ('DEL_ACCOUNT', 'Delete a bitshares account'), ('SHOW', 'Show bitshares accounts'), ('NODES', 'Edit Node Selection'), ('ADD_NODE', 'Add Your Node'), ('HELP', 'Where to get help'), ('EXIT', 'Quit this application')]) my_workers = [(index, index) for index in workers] if action == 'EXIT': # Cancel will also exit the application. but this is a clearer label # Todo: modify cancel to be "Quit" or "Exit" for the whiptail menu item. break elif action == 'LIST': if len(my_workers): # List workers, then provide option to list config of workers worker_name = whiptail.menu( "List of Your Workers. Select to view Configuration.", my_workers) content = config['workers'][worker_name] text = '\n' for key, value in content.items(): text += '{}: {}\n'.format(key, value) whiptail.view_text(text, pager=False) else: whiptail.alert('No workers to view.') elif action == 'EDIT': if len(my_workers): worker_name = whiptail.menu("Select worker to edit", my_workers) config['workers'][worker_name] = configure_worker( whiptail, config['workers'][worker_name], bitshares_instance) else: whiptail.alert('No workers to edit.') elif action == 'DEL_WORKER': if len(my_workers): worker_name = whiptail.menu("Select worker to delete", my_workers) del config['workers'][worker_name] # Pass ctx.config which is a loaded config (see ui.py configfile()), while `config` in a Config() # instance, which is empty dict, but capable of returning keys via __getitem__(). We need to pass # loaded config into StrategyBase to avoid loading a default config and preserve `--configfile` # option strategy = StrategyBase( worker_name, bitshares_instance=bitshares_instance, config=ctx.config) strategy.clear_all_worker_data() else: whiptail.alert('No workers to delete.') elif action == 'NEW': worker_name = whiptail.prompt("Your name for the new worker. ") if not worker_name: whiptail.alert("Worker name cannot be blank. ") elif not validator.validate_worker_name(worker_name): whiptail.alert( 'Worker name needs to be unique. "{}" is already in use.' .format(worker_name)) else: config['workers'][worker_name] = configure_worker( whiptail, {}, bitshares_instance) elif action == 'ADD': add_account(whiptail, bitshares_instance) elif action == 'DEL_ACCOUNT': del_account(whiptail, bitshares_instance) elif action == 'SHOW': account_list = list_accounts(bitshares_instance) if account_list: action = whiptail.menu( "Bitshares Account List (Name - Type)", account_list) else: whiptail.alert( 'You do not have any bitshares accounts in the wallet') elif action == 'ADD_NODE': txt = whiptail.prompt( "Your name for the new node: e.g. wss://dexnode.net/ws") # Insert new node on top of the list config['node'].insert(0, txt) elif action == 'NODES': choice = whiptail.node_radiolist( msg="Choose your preferred node", items=select_choice(config['node'][0], [(index, index) for index in config['node']])) # Move selected node as first item in the config file's node list config['node'].remove(choice) config['node'].insert(0, choice) setup_systemd(whiptail, config) elif action == 'HELP': whiptail.alert( "Please see https://github.com/Codaone/DEXBot/wiki") whiptail.clear() return config
class WorkerController: def __init__(self, view, bitshares_instance, mode): self.view = view self.mode = mode self.validator = ConfigValidator(Config(), bitshares_instance or shared_bitshares_instance()) @property def strategies(self): """ Defines strategies that are configurable from the GUI. key: Strategy location in the project name: The name that is shown in the GUI for user form_module: If there is custom form module created with QTDesigner :return: List of strategies """ strategies = collections.OrderedDict() strategies['dexbot.strategies.relative_orders'] = { 'name': 'Relative Orders', 'form_module': 'dexbot.views.ui.forms.relative_orders_widget_ui', } strategies['dexbot.strategies.staggered_orders'] = {'name': 'Staggered Orders', 'form_module': ''} strategies['dexbot.strategies.king_of_the_hill'] = {'name': 'King of the Hill', 'form_module': ''} for desc, module in find_external_strategies(): strategies[module] = {'name': desc, 'form_module': module} # if there is no UI form in the module then GUI will gracefully revert to auto-ui return strategies @classmethod def get_strategies(cls): """Class method for getting the strategies.""" return cls(None, None, None).strategies @staticmethod def get_unique_worker_name(): """ Returns unique worker name "Worker %n". %n is the next available index """ index = 1 workers = Config().workers_data.keys() worker_name = "Worker {0}".format(index) while worker_name in workers: worker_name = "Worker {0}".format(index) index += 1 return worker_name @staticmethod def get_strategy_module(worker_data): return worker_data['module'] @staticmethod def get_strategy_mode(worker_data): return worker_data['mode'] @staticmethod def get_allow_instant_fill(worker_data): return worker_data['allow_instant_fill'] @staticmethod def get_assets(worker_data): return re.split("[/:]", worker_data['market']) def get_base_asset(self, worker_data): return self.get_assets(worker_data)[1] def get_quote_asset(self, worker_data): return self.get_assets(worker_data)[0] @staticmethod def get_account(worker_data): return worker_data['account'] @staticmethod def handle_save_dialog(): dialog = ConfirmationDialog( 'Saving the worker will cancel all the current orders.\n' 'Are you sure you want to do this?' ) return dialog.exec_() @gui_error def change_strategy_form(self, worker_data=None): # Make sure the container is empty for index in reversed(range(self.view.strategy_container.count())): self.view.strategy_container.itemAt(index).widget().setParent(None) strategy_module = self.view.strategy_input.currentData() self.view.strategy_widget = StrategyFormWidget(self, strategy_module, worker_data) self.view.strategy_container.addWidget(self.view.strategy_widget) # Resize the dialog to be minimum possible height width = self.view.geometry().width() self.view.setMinimumHeight(0) self.view.resize(width, 1) @gui_error def validate_form(self): error_texts = [] base_asset = self.view.base_asset_input.text() quote_asset = self.view.quote_asset_input.text() fee_asset = self.view.fee_asset_input.text() worker_name = self.view.worker_name_input.text() old_worker_name = None if self.mode == 'add' else self.view.worker_name if not self.validator.validate_worker_name(worker_name, old_worker_name): error_texts.append('Worker name needs to be unique. "{}" is already in use.'.format(worker_name)) if not self.validator.validate_asset(base_asset): error_texts.append('Field "Base Asset" does not have a valid asset.') if not self.validator.validate_asset(quote_asset): error_texts.append('Field "Quote Asset" does not have a valid asset.') if not self.validator.validate_asset(fee_asset): error_texts.append('Field "Fee Asset" does not have a valid asset.') if not self.validator.validate_market(base_asset, quote_asset): error_texts.append("Market {}/{} doesn't exist.".format(base_asset, quote_asset)) if self.mode == 'add': account = self.view.account_input.text() private_key = self.view.private_key_input.text() if not self.validator.validate_account_name(account): error_texts.append("Account doesn't exist.") if not self.validator.validate_private_key(account, private_key): error_texts.append('Private key is invalid.') elif private_key and not self.validator.validate_private_key_type(account, private_key): error_texts.append('Please use active private key.') error_texts.extend(self.view.strategy_widget.strategy_controller.validation_errors()) error_text = '\n'.join(error_texts) if error_text: dialog = NoticeDialog(error_text) dialog.exec_() return False else: return True @gui_error def handle_save(self): if not self.validate_form(): return if self.mode == 'add': # Add the private key to the database private_key = self.view.private_key_input.text() if private_key: self.validator.add_private_key(private_key) account = self.view.account_input.text() else: # Edit account = self.view.account_name.text() base_asset = self.view.base_asset_input.text() quote_asset = self.view.quote_asset_input.text() fee_asset = self.view.fee_asset_input.text() operational_percent_quote = self.view.operational_percent_quote_input.value() operational_percent_base = self.view.operational_percent_base_input.value() strategy_module = self.view.strategy_input.currentData() self.view.worker_data = { 'account': account, 'market': '{}/{}'.format(quote_asset, base_asset), 'module': strategy_module, 'fee_asset': fee_asset, 'operational_percent_quote': operational_percent_quote, 'operational_percent_base': operational_percent_base, **self.view.strategy_widget.values, } self.view.worker_name = self.view.worker_name_input.text() self.view.accept()