def process_balance(author: AccountId, message: str, server: Server, **kwargs): """Processes a message requesting the balance on an account.""" if not server.has_account(author): return 'Hi there %s. I can\'t tell you what the balance on your account is because you don\'t have an account yet. ' \ 'You can open one with the `open` command.' % author.readable() account = server.get_account(author) main_response = 'The balance on your account is %s.' % fraction_to_str( account.get_balance()) return 'Hi there %s %s. %s Have a great day.' % (account.get_authorization( ).name.lower(), author.readable(), main_response)
def add_alias(author: Union[AccountId, str], account: Union[AccountId, str], signature: str, server: Server): """Alias author to account using an alias code (signature)""" if server.has_account(author): raise AccountCommandException(account) account = _get_account(account, server) if _is_signed_by(account, str(author), signature): server.add_account_alias(account, author) else: raise ValueCommandException(signature)
def authorize(author_id: Union[AccountId, str], account_id: Union[AccountId, str], auth_level: Authorization, server: Server): """Changes an account's authorization level to `auth_level`.""" author = _get_account(author_id, server) account = _get_account(account_id, server) required = max(Authorization.ADMIN, auth_level, account.get_authorization()) _assert_authorized(author, account, admin_level=required, min_level=required) server.authorize(author_id, account, auth_level)
def add_public_key(author: Union[AccountId, str], account: Union[AccountId, str], key: Union[str, ECC.EccKey], server: Server): """Add public key to account, with authorization from author""" author = _get_account(author, server) account = _get_account(account, server) _assert_authorized(author, account) if not isinstance(key, ECC.EccKey): try: key = ECC.import_key(key) except: raise ValueCommandException(key) server.add_public_key(account, key)
def process_remove_funds(author: AccountId, message: str, server: Server, **kwargs): author_account = assert_authorized(author, server, Authorization.ADMIN) parsed = parse_remove_funds(message) if parsed is None: raise CommandException( 'Bad formatting; Expected `remove-funds` `AMOUNT` `BENEFICIARY`') amount, beneficiary = parsed if amount < 0: raise CommandException('I can\'t remove negative amounts of funds') beneficiary_account = assert_is_account(beneficiary, server) if amount != 0: server.remove_funds(author_account, beneficiary_account, amount) return "Success"
def remove_funds(author_id: Union[AccountId, str], account_id: Union[AccountId, str], amount: Fraction, server: Server): """Remove funds from an account. Rules applying to print_money apply. """ if amount <= 0: raise ValueCommandException(amount) author = _get_account(author_id, server) account = _get_account(account_id, server) _assert_authorized(author, None) server.remove_funds(author_id, account, amount)
def print_money(author_id: Union[AccountId, str], account_id: Union[AccountId, str], amount: Fraction, server: Server): """Print an amount of money into an account, with the authorization of author, on server """ if amount <= 0: raise ValueCommandException(amount) author = _get_account(author_id, server) account = _get_account(account_id, server) _assert_authorized(author, None) server.print_money(author_id, account, amount)
def process_add_public_key(author: AccountId, message: str, server: Server, **kwargs): """Processes a message that requests for a public key to be associated with an account.""" account = assert_is_account(author, server) pem = '\n'.join(line for line in message.split('\n')[1:] if line != '' and not line.isspace()) try: key = ECC.import_key(pem) except Exception as e: raise CommandException( "Incorrectly formatted key. Inner error message: %s." % str(e)) server.add_public_key(account, key) return 'Public key added successfully.'
def request_alias(author: Union[AccountId, str], account: Union[AccountId, str], server: Server) -> str: """Generates an alias code for linking accounts together""" if server.has_account(account): raise AccountCommandException(account) author = _get_account(author, server) key = ECC.generate(curve='P-256') signer = DSS.new(key, 'fips-186-3') signature = base64.b64encode( signer.sign(SHA3_512.new( str(account).encode('utf-8')))).decode('utf-8') server.add_public_key(author, key.public_key()) return signature
def process_open_account(author: AccountId, message: str, server: Server, prefix='', platform_name='Reddit', **kwargs): """Processes a message that tries to open a new account.""" if server.has_account(author): return 'Hi there %s. Looks like you already have an account. No need to open another one.' % author.readable( ) server.open_account(author) return 'Hi there %s. Your account has been opened successfully. Thank you for your business. %s' % ( author.readable(), get_generic_help_message(author, prefix, platform_name))
def process_force_tick(author: AccountId, message: str, server: Server, **kwargs): assert_authorized(author, server, Authorization.DEVELOPER) parsed = parse_force_tick(message) if parsed is None: raise CommandException("Learn to Format SMH") amount = parsed i = 0 while i < amount: i += 1 print(i) server.notify_tick_elapsed(time.time()) return f"successfully forced {amount} ticks"
def transfer(author_id: Union[AccountId, str], source_id: Union[AccountId, str], destination_id: Union[AccountId, str], amount: Fraction, server: Server): """Transfer amount ¤ from source to destination with authorization from author on server""" author = _get_account(author_id, server) source = _get_account(source_id, server) destination = _get_account(destination_id, server) _assert_authorized(author, source) if not server.can_transfer(source, destination, amount): raise ValueCommandException(amount) proof = server.transfer(author_id, source, destination, amount) return proof
def process_admin_remove_proxy(author: AccountId, message: str, server: Server, **kwargs): """Processes an admin proxy addition command.""" assert_authorized(author, server, Authorization.ADMIN) parsed = parse_admin_add_proxy_command(message) if parsed is None: raise CommandException( 'Incorrect formatting. Expected format `admin-remove-proxy ACCOUNT_NAME PROXIED_ACCOUNT_NAME`.' ) account_name, proxied_account_name = parsed account = assert_is_account(account_name, server) proxied_account = assert_is_account(proxied_account_name, server) server.remove_proxy(author, account, proxied_account) return 'Account %s can no longer act as a proxy for account %s.' % ( account_name, proxied_account_name)
def delete_account(author: Union[AccountId, str], account: Union[AccountId, str], server: Server): """Delete account with authorization from account on server.""" author_acc = _get_account(author, server) _assert_authorized(author_acc, None) if not server.delete_account(author, account): raise ProcessCommandException()
def create_test_bot_and_server(): try: remove('./test.db') except FileNotFoundError: pass bot = TestBot() server = Server('sqlite:///test.db', bot) register_commands(bot, server) return bot, server
def process_delete_account(author: AccountId, message: str, server: Server, **kwargs): assert_authorized(author, server, Authorization.ADMIN) parsed = parse_delete_account(message) if parsed is None: raise CommandException('Incorrect formatting; **SMH**') account = parsed return "Success" if server.delete_account( author, account) else 'dunno Something broke'
def process_print_money(author: AccountId, message: str, server: Server, **kwargs): """Processes a request to print a batch of money and deposit it in an account.""" assert_authorized(author, server, Authorization.ADMIN) parsed = parse_print_money(message) if parsed is None: raise CommandException( 'Command formatted incorrectly. Expected format `print-money AMOUNT BENEFICIARY`.' ) amount, beneficiary = parsed if amount < 0: raise CommandException('Cannot print negative amounts of money.') beneficiary_account = assert_is_account(beneficiary, server) if amount != 0: server.print_money(author, beneficiary_account, amount) return 'Money printed successfully.'
def process_add_alias(author: AccountId, message: str, server: Server, **kwargs): """Processes a request to add `author` as an alias to an account.""" if server.has_account(author): raise CommandException( 'An account has already been associated with %s, so it cannot be an alias for another account.' % author.readable()) split_msg = message.split() if len(split_msg) != 3: raise CommandException('Incorrect formatting. Expected `add-alias ALIASED_ACCOUNT ALIAS_REQUEST_CODE`.') _, aliased_account_name, signature = split_msg aliased_account = assert_is_account(aliased_account_name, server) if is_signed_by(aliased_account, str(author), signature): server.add_account_alias(aliased_account, author) return 'Alias set up successfully. %s and %s now refer to the same account.' % ( aliased_account_name, author.readable()) else: raise CommandException('Cannot set up alias because the signature is invalid.')
def process_request_alias(author: AccountId, message: str, server: Server, **kwargs): """Processes a request for an alias code.""" account = assert_is_account(author, server) split_msg = message.split() if len(split_msg) != 2: raise CommandException( 'Incorrect formatting. Expected `request-alias ALIAS_ACCOUNT_NAME`.' ) _, alias_name = split_msg alias_id = parse_account_id(alias_name) if server.has_account(alias_id): raise CommandException( 'An account has already been associated with %s, so it cannot be an alias for this account.' % alias_id.readable()) # To generate an alias code, we generate an ECC public/private key pair, use the # private key to generate a signed version of the aliased account name and associate # the public key with the account. key = ECC.generate(curve='P-256') signature = sign_message(str(alias_id), key) server.add_public_key(account, key.public_key()) # At this point we will allow the private key to be forgotten. # Compose a helpful message for as to how the bot can be contacted to link accounts. if isinstance(alias_id, RedditAccountId): contact_message = 'Send me that exact command as a Reddit Private Message (not a direct chat) from %s.' % alias_id.readable( ) elif isinstance(alias_id, DiscordAccountId): contact_message = 'Send me that exact command prefixed with a mention of my name via Discord from account %s.' % alias_id else: contact_message = '' return ( 'I created an alias request code for you. ' 'Make {0} an alias for this account ({2}) by sending me the following message from {3}.\n\n```\nadd-alias {4} {1}\n```\n\n{5}' ).format(str(alias_id), signature, author.readable(), alias_id.readable(), str(author), contact_message)
def process_help(author: AccountId, message: str, server: Server, prefix='', platform_name='Reddit', **kwargs): """Gets the help message for the economy bot.""" if server.has_account(author): return '''Howdy partner! It's always a pleasure to meet an account holder. %s %s''' % (get_how_to_reach_message(platform_name), get_generic_help_message(author, prefix, platform_name)) else: return '''Hi! You look new. {1} If you don't have an account with me yet, you can open one using this command: > {0}open Alternatively, if you already have an account then please don't create a new one here. Instead, link your existing account to {2} by running the following command from a username that's already associated with the account. > request-alias {3} (If you're sending me that over Discord you'll also have to ping me at the start of that command.)'''.format( prefix, get_how_to_reach_message(platform_name), author.readable(), str(author))
def create_recurring_transfer(author_id: Union[AccountId, str], sender_id: Union[AccountId, str], destination_id: Union[AccountId, str], amount: Fraction, tick_count: int, server: Server): """Create a recurring transfer.""" author = _get_account(author_id, server) sender = _get_account(sender_id, server) destination = _get_account(destination_id, server) _assert_authorized(author, sender) transfer = server.create_recurring_transfer(author_id, sender, destination, amount * tick_count, tick_count) return transfer
def process_create_recurring_transfer(author: AccountId, message: str, server: Server, **kwargs): """Processes a request to set up a recurring transfer.""" parse_result = parse_create_recurring_transfer(message) if parse_result is None: return 'Request formatted incorrectly. Expected `create-recurring-transfer AMOUNT_PER_TICK BENEFICIARY TICK_COUNT`.' amount, destination_name, tick_count = parse_result author_account = assert_is_account(author, server) dest_account = assert_is_account(destination_name, server) transfer = server.create_recurring_transfer(author, author_account, dest_account, amount * tick_count, tick_count) return 'Recurring transfer set up with ID `%s`.' % transfer.get_id()
def process_admin_create_recurring_transfer(author: AccountId, message: str, server: Server, **kwargs): """Processes a request to set up an arbitrary recurring transfer.""" assert_authorized(author, server, Authorization.ADMIN) parse_result = parse_admin_create_recurring_transfer(message) if parse_result is None: return 'Request formatted incorrectly. Expected `admin-create-recurring-transfer AMOUNT_PER_TICK SENDER BENEFICIARY TICK_COUNT`.' amount, sender_name, destination_name, tick_count = parse_result assert_is_account(author, server) sender_account = assert_is_account(sender_name, server) dest_account = assert_is_account(destination_name, server) transfer = server.create_recurring_transfer(author, sender_account, dest_account, amount * tick_count, tick_count) return 'Recurring transfer set up with ID `%s`.' % transfer.get_id()
def force_tax(author: Union[AccountId, str], server: Server): """Manually trigger taxation""" author = _get_account(author, server) _assert_authorized(author, None) server.force_tax(author)
def remove_tax_bracket(author: Union[AccountId, str], name: str, server: Server): """Remove tax bracket by name with authorization from author""" author = _get_account(author, server) _assert_authorized(author, None) server.remove_tax_bracket(author, name)
def auto_tax(author: Union[AccountId, str], server: Server) -> bool: """Toggle automatic taxation""" author = _get_account(author, server) _assert_authorized(author, None) return server.toggle_auto_tax(author)
def add_tax_bracket(author: Union[AccountId, str], start: Fraction, end: Fraction, rate: Fraction, name: str, server: Server): """Add a tax bracket to a server with authorization from author""" author = _get_account(author, server) _assert_authorized(author, None) server.add_tax_bracket(author, start, end, rate, name)
def list_public_accounts(author: Union[AccountId, str], server: Server) -> List[Account]: """returns a list of all accounts marked as public""" return [account for account in server.list_accounts() if account.public]
def force_ticks(author: Union[AccountId, str], amount: int, server: Server): """Forcibly run multiple ticks""" author = _get_account(author, server) _assert_authorized(author, None) for _ in range(amount): server.notify_tick_elapsed(time.time())
def list_accounts(author: Union[AccountId, str], server: Server) -> List[Account]: """List all accounts""" author = _get_account(author, server) _assert_authorized(author, None) return server.list_accounts()