예제 #1
0
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)
예제 #2
0
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)
예제 #3
0
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)
예제 #4
0
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)
예제 #5
0
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"
예제 #6
0
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)
예제 #7
0
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)
예제 #8
0
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.'
예제 #9
0
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
예제 #10
0
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))
예제 #11
0
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"
예제 #12
0
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
예제 #13
0
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)
예제 #14
0
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()
예제 #15
0
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
예제 #16
0
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'
예제 #17
0
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.'
예제 #18
0
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.')
예제 #19
0
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)
예제 #20
0
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))
예제 #21
0
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
예제 #22
0
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()
예제 #23
0
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()
예제 #24
0
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)
예제 #25
0
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)
예제 #26
0
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)
예제 #27
0
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)
예제 #28
0
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]
예제 #29
0
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())
예제 #30
0
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()