def open_account(author: Union[AccountId, str], account: Union[AccountId, str], server: Server): """Open account through authorization of author on server. Author can be the same account as account""" if server.has_account(account): raise ValueCommandException(account) if server.has_account(author): _assert_authorized(_get_account(author, server), None) server.open_account(account)
def _get_account(account_id: Union[AccountId, str], server: Server) -> Account: """Get account from server, unless it doesn't exist, in which case raise""" account_id = parse_account_id(account_id) if not server.has_account(account_id): raise AccountCommandException(account_id) return server.get_account(account_id)
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_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_admin_open_account(author: AccountId, message: str, server: Server, **kwargs): """Processes a message that tries to open a new account.""" assert_authorized(author, server, Authorization.ADMIN) account_name = parse_account_name_command(message) if server.has_account(account_name): raise CommandException('Account `%s` already exists.' % account_name) server.open_account(account_name) return 'Account `%s` has been opened successfully.' % account_name
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.' % account.get_balance() return 'Hi there %s %s. %s Have a great day.' % (account.get_authorization().name.lower(), author.readable(), main_response)
def assert_is_account(account_name: Union[str, AccountId], server: Server) -> Account: """Asserts that a particular account exists. Returns the account.""" if isinstance(account_name, str): account_name = parse_account_id(account_name) if not server.has_account(account_name): raise CommandException( ('Sorry, I can\'t process your request because %s does not have an account yet. ' 'Accounts can be opened using the `open` command.') % account_name.readable()) return server.get_account(account_name)
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 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_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 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 get_authorization_or_citizen(author: AccountId, server: Server): """Gets an account's authorization if it exists and the default citizen authorization otherwise.""" return server.get_account(author).get_authorization() \ if server.has_account(author) \ else Authorization.CITIZEN