async def cmd_give(msg: Message, *args):
    if len(args) != 2:
        raise InvalidArgumentsError(reason='missing required arguments', cmd=cmd_give)

    if not msg.mentions or msg.mentions[0] not in msg.channel.chatters:
        raise InvalidArgumentsError(reason=f'no viewer found by the name "{(msg.mentions or args)[0]}"')

    caller = get_balance_from_msg(msg)
    target = get_balance(msg.channel_name, msg.mentions[0])

    try:
        give = int(args[1])
    except ValueError:
        raise InvalidArgumentsError(reason='give amount must be a integer, example: 100', cmd=cmd_give)

    if give <= 0:
        raise InvalidArgumentsError(reason='give amount must be 1 or higher', cmd=cmd_give)

    cur_name = get_currency_name(msg.channel_name).name

    if caller.balance < give:
        raise InvalidArgumentsError(reason=f"{msg.mention} you don't have enough {cur_name}", cmd=cmd_give)

    caller.balance -= give
    target.balance += give

    session.commit()

    await msg.reply(
        f"@{msg.author} you gave @{args[0]} {give} {cur_name}, @{args[0]}'s balance is now {target.balance}")
async def cmd_give(msg: Message, *args):
    if len(args) != 2:
        raise InvalidArgumentsException()

    if args[0] not in msg.channel.chatters:
        await msg.reply(msg=f'no viewer found by the name "{args[0]}"')
        return

    caller = get_balance_from_msg(msg)
    target = get_balance(msg.channel_name, args[0])

    try:
        give = int(args[1])
    except ValueError:
        await msg.reply('invalid give amount')
        return

    cur_name = get_currency_name(msg.channel_name).name

    if caller.balance < give:
        await msg.reply(f"@{msg.author} you don't have enough {cur_name}")
        return

    caller.balance -= give
    target.balance += give

    session.commit()

    await msg.reply(
        f"@{msg.author} you gave @{args[0]} {give} {cur_name}, @{args[0]}'s balance is now {target.balance}"
    )
async def cmd_upd_sound(msg: Message, *args):
    # this largely follows the same steps as addsound
    snd = get_sound(msg.channel_name, args[0])
    if snd is None:
        raise InvalidArgumentsError(reason='no sound found with this name',
                                    cmd=cmd_upd_sound)

    optionals = ' '.join(args[2:])

    if 'name' in optionals:
        m = re.search(r'name=(\w+)', msg.content)
        if m:
            snd.sndid = m
        else:
            raise InvalidArgumentsError(reason='invalid new name for name=',
                                        cmd=cmd_upd_sound)

    if 'price=' in optionals and 'pricemult=' in optionals:
        raise InvalidArgumentsError(
            reason='specify price or pricemult, not both!', cmd=cmd_upd_sound)

    if 'price=' in optionals:
        m = re.search(r'price=(\d+)', msg.content)
        if m:
            snd.price = int(m.group(1))
        else:
            raise InvalidArgumentsError(
                reason='invalid price for price=, must be an INT',
                cmd=cmd_upd_sound)

    if 'pricemult=' in optionals:
        m = re.search(r'pricemult=(x?)(\d+.\d*)', msg.content)
        if m and float(m.group(2)) >= 0:
            snd.pricemult = float(m.group(2))
        else:
            raise InvalidArgumentsError(
                reason=
                'invalid argument for pricemult=, must be a non-negative ' +
                'FLOAT or xFLOAT, e.g., 0.7 or x1.4',
                cmd=cmd_upd_sound)

    if 'gain=' in optionals:
        m = re.search(r'gain=(-?\d+.\d*)', msg.content)
        if m:
            snd.gain = float(m.group(1))
        else:
            raise InvalidArgumentsError(
                reason='invalid gain for gain=, must be a FLOAT, e.g., -1.4',
                cmd=cmd_add_sound)

    session.commit()
    await msg.reply(f'successfully updated sound {snd.sndid}')
async def cmd_mine(msg: Message, *args):
    key = (msg.author, msg.channel_name)
    diff = (datetime.now() - last_mine_time.get(key, datetime.now())).total_seconds()

    if key not in last_mine_time or diff >= 0:
        bal = get_balance_from_msg(msg)
        bal.balance += mine_gain
        session.commit()
        last_mine_time[key] = datetime.now() + timedelta(minutes=5)

        await msg.reply(
            f'@{msg.author} you went to work at the mines and came out with '
            f'{mine_gain} {get_currency_name(msg.channel_name).name} worth of gold',
            whisper=True)
    else:
        await msg.reply(f'you cannot mine again for {int(abs(diff))} seconds', whisper=True)
async def cmd_gamble(msg: Message, *args):
    if not len(args):
        raise InvalidArgumentsError(reason='missing required arguments', cmd=cmd_gamble)

    try:
        bet = int(args[0])
    except ValueError:
        raise InvalidArgumentsError(reason='invalid value for bet', cmd=cmd_gamble)

    sides = 6
    if len(args) == 2:
        if not args[1].isdigit():
            raise InvalidArgumentsError(reason='invalid value for dice_sides', cmd=cmd_gamble)
        sides = int(args[1])

    if bet < 10:
        raise InvalidArgumentsError(reason='bet cannot be less then 10', cmd=cmd_gamble)

    elif sides < 2:
        raise InvalidArgumentsError(reason='sides cannot be less than 2', cmd=cmd_gamble)

    bal = get_balance_from_msg(msg)

    cur_name = get_currency_name(msg.channel_name).name
    if bal.balance < bet:
        raise InvalidArgumentsError(reason=f"{msg.mention} you don't have enough {cur_name}", cmd=cmd_gamble)

    def _calc_winnings(sides, roll, bet):
        if roll == sides:
            return int(bet * ((sides / 6) + 1.5))
        return int(bet * (roll / sides))

    roll = randbelow(sides) + 1
    winnings = _calc_winnings(sides=sides, roll=roll, bet=bet)

    if winnings > bet:
        gain = winnings - bet
        bal.balance += gain
        await msg.reply(f'you rolled {roll} and won {gain} {cur_name}')
    else:
        loss = bet - winnings
        bal.balance -= loss
        await msg.reply(f'you rolled {roll} and lost {loss} {cur_name}')

    session.commit()
async def cmd_gamble(msg: Message, *args):
    if len(args) != 2:
        raise InvalidArgumentsError(reason='missing required arguments',
                                    cmd=cmd_gamble)

    try:
        sides = int(args[0])
        bet = int(args[1])
    except ValueError:
        raise InvalidArgumentsError(reason='invalid value for sides or bet',
                                    cmd=cmd_gamble)

    if bet < 10:
        raise InvalidArgumentsError(reason='bet cannot be less then 10',
                                    cmd=cmd_gamble)

    elif sides < 2:
        raise InvalidArgumentsError(reason='sides cannot be less than 2',
                                    cmd=cmd_gamble)

    bal = get_balance_from_msg(msg)
    cur_name = get_currency_name(msg.channel_name).name

    if bal.balance < bet:
        raise InvalidArgumentsError(
            reason=f"{msg.mention} you don't have enough {cur_name}",
            cmd=cmd_gamble)

    n = randbelow(sides) + 1

    if n == 1:
        if sides >= 6:
            bet *= 2
        gain = bet + int(bet * (sides / 6))
        bal.balance += gain
        await msg.reply(f'you rolled {n} and won {gain} {cur_name}')

    else:
        bal.balance -= bet
        await msg.reply(f'you rolled {n} and lost your bet of {bet} {cur_name}'
                        )

    session.commit()
async def cmd_gamble(msg: Message, *args):
    if len(args) != 2:
        raise InvalidArgumentsException()

    try:
        sides = int(args[0])
        bet = int(args[1])
    except ValueError:
        await msg.reply(f'invalid value for sides or bet')
        return

    if bet < 10:
        await msg.reply('bet cannot be less then 10')
        return

    elif sides < 2:
        await msg.reply('sides cannot be less than 2')
        return

    bal = get_balance_from_msg(msg)
    if bal.balance < bet:
        await msg.reply(
            f"you don't have enough {get_currency_name(msg.channel_name).name}"
        )
        return

    n = randbelow(sides) + 1
    cur_name = get_currency_name(msg.channel_name).name

    if n == 1:
        if sides >= 6:
            bet *= 2
        gain = bet + int(bet * (sides / 6))
        bal.balance += gain
        await msg.reply(f'you rolled {n} and won {gain} {cur_name}')

    else:
        bal.balance -= bet
        await msg.reply(f'you rolled {n} and lost your bet of {bet} {cur_name}'
                        )

    session.commit()
async def cmd_update_custom_command(msg: Message, *args):
    if len(args) < 2:
        raise InvalidArgumentsException()

    name, resp = args[0], ' '.join(args[1:])
    name = name.lower()

    if not await _verify_resp_text(msg, resp):
        return

    cmd = get_custom_command(msg.channel_name, name)

    if cmd is None:
        await msg.reply(f'custom command {name} does not exist')
        return

    cmd.response = resp
    session.commit()

    await msg.reply(f'successfully updated {cmd.name}')
async def cmd_update_custom_command(msg: Message, *args):
    if len(args) < 2:
        raise InvalidArgumentsError(reason='missing required arguments',
                                    cmd=cmd_update_custom_command)

    name, resp = args[0], ' '.join(args[1:])
    name = name.lower()

    if not _verify_resp_is_valid(resp):
        raise InvalidArgumentsError(
            reason='response cannot have . or / as the starting character',
            cmd=cmd_update_custom_command)

    cmd = get_custom_command(msg.channel_name, name)
    if cmd is None:
        raise InvalidArgumentsError(
            reason=f'custom command "{name}" does not exist',
            cmd=cmd_update_custom_command)

    cmd.response = resp
    session.commit()

    await msg.reply(f'successfully updated {cmd.name}')