Beispiel #1
0
async def quote_delete(bot, channel_id, cmd, args, **kwargs):
    if len(args) != 1:
        raise Send_error(f'Syntax error, use !{cmd} <number>')

    r = await bot.db.fetchone(
        'SELECT enabled FROM twitch_quotes WHERE channel_id=%s and number=%s',
        (
            channel_id,
            args[0],
        ))

    if not r:
        raise Send_error('Unknown quote')

    if r['enabled'] == 0:
        raise Send_error('This quote is deleted')

    await bot.db.execute(
        'UPDATE twitch_quotes SET '
        'enabled=0 '
        'WHERE channel_id=%s AND number=%s', (
            channel_id,
            args[0],
        ))

    raise Send_error('Quote deleted')
async def set_game(bot, channel_id, args, var_args, **kwargs):
    game = ' '.join(args) or ' '.join(var_args['set_game'])
    if not game:
        raise Send_error('You must specify a game or a category')
    game_id=''
    if game != '<unset>':
        data = await utils.twitch_channel_token_request(
            bot, channel_id,
            'https://api.twitch.tv/helix/search/categories',
            params={'query': game},
        )
        if 'data' in data and data['data']:
            game_id = data['data'][0]['id']
            game = data['data'][0]['name']
        else:
            raise Send_error(f'No game or category found for {game}')
    try:
        await utils.twitch_channel_token_request(
            bot, channel_id,
            f'https://api.twitch.tv/helix/channels?broadcaster_id={channel_id}',
            method='PATCH',
            json={
                'game_id': game_id,
            },
        )
    except utils.Twitch_request_error as e:
        raise Send_error(f'Unable to change game/category. Error: {str(e)}')
    raise Send_error(f'Game/category changed to: {game}')
Beispiel #3
0
async def time(bot, var_args, **kwargs):
    if 'time' not in var_args or not var_args['time']:
        raise Send_error('A timezone must be specified with time. Example {time Europe/Copenhagen}')
    if var_args['time'][0] not in pytz.all_timezones:
        raise Send_error('Invalid timezone. Valid list: https://docs.botashell.com/vars/time')
    dt = pytz.utc.localize(
        datetime.utcnow(),
    ).astimezone(pytz.timezone(var_args['time'][0]))
    return {
        'time': dt.strftime('%H:%M')
    }
Beispiel #4
0
async def quote_add(bot, channel_id, cmd, user, user_id, args, **kwargs):
    if len(args) == 0:
        raise Send_error(f'Syntax error, use !{cmd} your quote')

    c = await bot.db.execute(
        'INSERT INTO twitch_quotes '
        '(channel_id, created_by_user_id, created_by_user, '
        'message, enabled, created_at, updated_at) '
        'VALUES (%s, %s, %s, %s, %s, %s, %s)',
        (channel_id, user_id, user, ' '.join(args), 1, datetime.utcnow(),
         datetime.utcnow()))
    r = await bot.db.fetchone('SELECT number FROM twitch_quotes WHERE id=%s',
                              (c.lastrowid, ))
    raise Send_error(f'Quote created with number: {r["number"]}')
Beispiel #5
0
async def lol(bot, channel, channel_id, args, var_args, **kwargs):
    if not var_args or not var_args.get('tft.summoner') or \
        len(var_args['tft.summoner']) != 2:
        raise Send_error('{tft.summoner "<name>" "<region>"} is missing')

    headers = {
        'X-Riot-Token': config['tft_apikey'],
    }
    summoner = var_args['tft.summoner'][0]
    region = var_args['tft.summoner'][1].lower()
    if region not in regions:
        raise Send_error(
            f'Invalid region "{region}". Valid regions: {", ".join(regions)}')

    url = f'https://{region.lower()}.api.riotgames.com/tft/summoner/v1/summoners/by-name/{summoner}'
    async with bot.ahttp.get(url, headers=headers) as r:
        if r.status >= 400:
            error = await r.text()
            raise Send_error(f'Riot error: {error}')
        d = await r.json()
        encrypted_id = d['id']
        account_id = d['accountId']

    result = {
        'tft.summoner': '',
        'tft.rank': 'Unranked',
        'tft.tier': '',
        'tft.lp': 0,
        'tft.wins': 0,
        'tft.losses': 0,
    }

    url = f'https://{region}.api.riotgames.com/tft/league/v1/entries/by-summoner/{encrypted_id}'

    async with bot.ahttp.get(url, headers=headers) as r:
        if r.status >= 400:
            error = await r.text()
            raise Send_error(f'Riot error: {error}')
        d = await r.json()

    for a in d:
        if a['queueType'] == 'RANKED_TFT':
            result['tft.wins'] = a['wins']
            result['tft.losses'] = a['losses']
            result['tft.rank'] = a['rank']
            result['tft.tier'] = a['tier'].title()
            result['tft.lp'] = a['leaguePoints']
            break

    return result
async def set_language(bot, channel_id, args, var_args, **kwargs):
    language = ' '.join(args) or ' '.join(var_args['set_language'])
    if not language:
        raise Send_error('You must specify a language')
    try:
        await utils.twitch_channel_token_request(
            bot, channel_id,
            f'https://api.twitch.tv/helix/channels?broadcaster_id={channel_id}',
            method='PATCH',
            json={
                'broadcaster_language': language,
            },
        )
    except utils.Twitch_request_error as e:
        raise Send_error(f'Unable to change language. Error: {str(e)}')
    raise Send_error(f'Language changed to: {language}')
Beispiel #7
0
async def followage(bot, user_id, display_name, channel_id, channel, args,
                    **kwargs):
    uid = user_id
    user = display_name
    if len(args) > 0:
        user = utils.safe_username(args[0])
        uid = await utils.twitch_lookup_user_id(bot.ahttp, bot.db, user)
        if not uid:
            uid = user_id
            user = display_name
    data = await utils.twitch_request(
        bot.ahttp,
        'https://api.twitch.tv/helix/users/follows',
        params={
            'from_id': uid,
            'to_id': channel_id,
        })
    if not data['data']:
        raise Send_error('{} does not follow {}'.format(user, channel))
    followed_at = parse(data['data'][0]['followed_at']).replace(tzinfo=None)

    return {
        'followage':
        utils.seconds_to_pretty(dt1=datetime.utcnow(), dt2=followed_at),
        'followage_date':
        followed_at.strftime('%Y-%m-%d'),
        'followage_datetime':
        followed_at.strftime('%Y-%m-%d %H:%M:%S UTC'),
    }
Beispiel #8
0
async def countdown(bot, var_args, args, **kwargs):
    from_ = 1
    to = 100
    try:
        if 'randint' in var_args:
            if len(var_args['randint']) == 1:
                to = int(var_args['randint'][0])
            elif len(var_args['randint']) > 1:
                from_ = int(var_args['randint'][0])
                to = int(var_args['randint'][1])
    except ValueError:
        pass

    try:
        if len(args) == 1:
            to = int(args[0])
        elif len(args) > 1:
            from_ = int(args[0])
            to = int(args[1])
    except ValueError:
        pass

    if to <= from_:
        raise Send_error('First argument must be lower than the second')

    return {
        'randint': randint(from_, to),
    }
Beispiel #9
0
async def accountage(bot, user_id, display_name, args, **kwargs):
    uid = user_id
    user = display_name
    if len(args) > 0:
        user = utils.safe_username(args[0])
        uid = await utils.twitch_lookup_user_id(bot.ahttp, bot.db, user)
        if not uid:
            uid = user_id
            user = display_name
    data = await utils.twitch_request(
        bot.ahttp,
        'https://api.twitch.tv/helix/users',
        params={'id': uid},
    )
    if not data or not data['data']:
        raise Send_error('Found no data on {}'.format(user, ))
    created_at = parse(data['data'][0]['created_at']).replace(tzinfo=None)
    return {
        'accountage':
        utils.seconds_to_pretty(dt1=datetime.utcnow(), dt2=created_at),
        'accountage_date':
        created_at.strftime('%Y-%m-%d'),
        'accountage_datetime':
        created_at.strftime('%Y-%m-%d %H:%M:%S UTC'),
    }
Beispiel #10
0
async def permit_manager(bot, cmd, args, channel, channel_id, var_args, **kwargs):
    if len(args) < 1:
        raise Send_error('Invalid syntax, use: !{} <user>'.format(cmd))
    user = utils.safe_username(args[0])
    user_id = await utils.twitch_lookup_user_id(bot.ahttp, bot.db, user)
    if not user_id:
        raise Send_error('Unknown user')    
    time = int(var_args['permit'][0]) if 'permit' in var_args and var_args['permit'] else 60
    key = 'tbot:filter:permit:{}:{}'.format(
        channel_id, user_id,
    )
    permit = await bot.redis.setex(key, time, '1')
    bot.send("PRIVMSG", target='#'+channel, message='@{}, you will not receive a timeout for the next {}'.format(
        user, 
        utils.seconds_to_pretty(time) if time > 60 else '{} seconds'.format(time),
    ))
    raise Send_break()
Beispiel #11
0
async def playlist(bot, channel_id, **kwargs):
    data = await request(
        bot, channel_id,
        'https://api.spotify.com/v1/me/player/currently-playing')
    if data == None:
        raise Send_error('ERROR: Spotify failed to load')
    if not data or not data['is_playing']:
        raise Send_error('Spotify is not playing')

    playlist = await request(bot, channel_id, data['context']['href'])
    playlistname = ''
    if playlist:
        playlistname = playlist['name']

    return {
        'spotify.playlist_name': playlistname,
        'spotify.playlist_url': data['context']['external_urls']['spotify'],
    }
async def title_game_views_followers(bot, channel_id, **kwargs):
    data = await utils.twitch_request(bot.ahttp, 
        f'https://api.twitch.tv/helix/users/follows?to_id={channel_id}&first=1',
    )
    if not data['data']:
        raise Send_error('No data')
    return {
        'followers': str(data['total']),
    }
async def set_delay(bot, channel_id, args, var_args, **kwargs):
    delay = ' '.join(args) or ' '.join(var_args['set_delay'])
    try:
        delay = int(delay)
    except ValueError:        
        raise Send_error('Delay must be a number')
    try:
        await utils.twitch_channel_token_request(
            bot, channel_id,
            f'https://api.twitch.tv/helix/channels?broadcaster_id={channel_id}',
            method='PATCH',
            json={
                'delay': delay,
            },
        )
    except utils.Twitch_request_error as e:
        raise Send_error(f'Unable to change delay. Error: {str(e)}')
    raise Send_error(f'Delay changed to: {delay}')
Beispiel #14
0
async def lol(bot, channel, channel_id, args, var_args, **kwargs):   
    if not var_args or not var_args.get('lol.summoner') or \
        len(var_args['lol.summoner']) != 2:
        raise Send_error('{lol.summoner "<name>" "<region>"} is missing')


    headers = {
        'X-Riot-Token': config['lol_apikey'],
    }

    summoner = var_args['lol.summoner'][0]
    region = var_args['lol.summoner'][1].lower()
    if region not in regions:
        raise Send_error(f'Invalid region "{region}". Valid regions: {", ".join(regions)}')

    url = f'https://{region.lower()}.api.riotgames.com/lol/summoner/v4/summoners/by-name/{summoner}'
    async with bot.ahttp.get(url, headers=headers) as r:
        if r.status >= 400:
            error = await r.text()
            raise Send_error(f'Riot error: {error}')
        d = await r.json()
        encrypted_id = d['id']
        puuid = d['puuid']

    r = {
        'lol.summoner': '',
        'lol.rank': 'Unranked',
        'lol.tier': '',
        'lol.lp': 0,
        'lol.wins': 0,
        'lol.losses': 0,
        'lol.live_wins': 0,
        'lol.live_losses': 0,
    }

    rank_vars = ['lol.rank', 'lol.tier', 'lol.lp', 'lol.wins', 'lol.losses']
    if any(a in var_args for a in rank_vars):
        await get_rank(bot, headers, region, encrypted_id, r)

    live_vars = ['lol.live_wins', 'lol.live_losses']
    if any(a in var_args for a in live_vars):
        await get_live(channel_id, bot, headers, region, puuid, r)

    return r
async def title_game_views_followers(bot, channel_id, **kwargs):
    data = await utils.twitch_request(bot.ahttp, 
        f'https://api.twitch.tv/helix/channels?broadcaster_id={channel_id}',
    )
    if not data['data']:
        raise Send_error('No data')
    return {
        'title': data['data'][0]['title'] or '<No title>',
        'game': data['data'][0]['game_name'] or '<Not in any game category>',
    }
Beispiel #16
0
async def countdown(bot, var_args, **kwargs):
    d = ' '.join(var_args['countdown'])
    try:
        dt = parse(d).astimezone(tz.UTC).replace(tzinfo=None)
    except ValueError:
        raise Send_error(f'Invalid date format: "{d}". Use ISO 8601 format.')

    return {
        'countdown': utils.seconds_to_pretty(dt1=datetime.utcnow(), dt2=dt),
    }
Beispiel #17
0
async def song(bot, channel_id, **kwargs):
    data = await request(
        bot, channel_id,
        'https://api.spotify.com/v1/me/player/currently-playing')
    if data == None:
        raise Send_error('ERROR: Spotify failed to load')
    if not data or not data['is_playing']:
        raise Send_error('Spotify is not playing')
    artists = [r['name'] for r in data['item']['artists']]
    duration = '{}:{:02d}'.format(
        *divmod(round(data['item']['duration_ms'] / 1000), 60))
    progress = '{}:{:02d}'.format(*divmod(round(data['progress_ms'] /
                                                1000), 60))
    return {
        'spotify.song_name': data['item']['name'],
        'spotify.song_artists': ', '.join(artists),
        'spotify.song_progress': progress,
        'spotify.song_duration': duration,
    }
Beispiel #18
0
async def prev_song(bot, channel_id, args, **kwargs):
    num = utils.find_int(args) or 1
    if num > 10:
        raise Send_error('Only allowed to go back 10 songs')
    data = await request(
        bot, channel_id,
        'https://api.spotify.com/v1/me/player/recently-played?limit={}'.format(
            num))
    if data == None:
        raise Send_error('ERROR: Spotify failed to load')
    if 'items' not in data or not data['items']:
        raise Send_error('No previous song found')
    if num > len(data['items']):
        raise Send_error('No previous song found')
    data = data['items'][num - 1]
    artists = [r['name'] for r in data['track']['artists']]
    return {
        'spotify.prev_song_name': data['track']['name'],
        'spotify.prev_song_artists': ', '.join(artists),
    }
Beispiel #19
0
async def count_match(bot, game_id, region, headers, puuid):
    url = f'https://{region}.api.riotgames.com/lol/match/v5/matches/{game_id}'
    async with bot.ahttp.get(url, headers=headers) as r:
        if r.status >= 400:
            error = await r.text()
            raise Send_error(f'Riot error: {error}')
        match = await r.json()
        if match['info']['gameDuration'] < 60*10:
            return
        for p in match['info']['participants']:
            if p['puuid'] == puuid:
                return p['win']
Beispiel #20
0
async def request(bot, channel_id, url, params=None, headers={}):
    t = await bot.db.fetchone(
        'SELECT * FROM twitch_spotify WHERE channel_id=%s;', (channel_id))
    if not t:
        raise Send_error(
            'ERROR: Spotify has not been configured for this channel')
    headers.update({'Authorization': 'Bearer {}'.format(t['token'])})
    async with bot.ahttp.get(url, params=params, headers=headers) as r:
        if r.status == 200:
            data = await r.json()
            return data
        elif r.status == 204:
            return {}
        elif r.status == 401:
            await refresh_token(bot, channel_id, t['refresh_token'])
            return await request(bot, channel_id, url, params, headers)
        error = await r.text()
        raise Send_error('ERROR: Spotify request error {}: {}'.format(
            r.status,
            error,
        ))
Beispiel #21
0
async def subs(bot, channel_id, **kwargs):
    try:
        data = await utils.twitch_channel_token_request(
            bot,
            channel_id,
            'https://api.twitch.tv/subscriptions',
            params={'broadcaster_id': channel_id})
        return {
            'subs': data['total'],
        }
    except utils.Twitch_request_error as e:
        raise Send_error('Unable to get sub count. Error: {}'.format(str(e)))
Beispiel #22
0
async def weather(bot, var_args, args, cmd, **kwargs):
    if not config['openweathermap_apikey']:
        raise Send_error('`openweathermap_apikey` is missing in the config')
    url = 'https://api.openweathermap.org/data/2.5/weather'
    city = None
    if len(args) > 0:
        city = ' '.join(args)
    if not city:
        city = ' '.join(var_args.get('weather.lookup_city', ['']))

    if not city:
        raise Send_error(f'Use !{cmd} <city>')

    units = var_args.get('weather.units', ['metric'])[0]

    params = {
        'q': city,
        'APPID': config['openweathermap_apikey'],
        'units': units,
    }
    async with bot.ahttp.get(url, params=params) as r:
        d = await r.json()
        if r.status == 200:
            return {
                'weather.description': d['weather'][0]['description']\
                    if d['weather'] else 'Unknown',
                'weather.temp': d['main']['temp'],
                'weather.temp_min': d['main']['temp_min'],
                'weather.temp_max': d['main']['temp_max'],
                'weather.humidity': d['main']['humidity'],
                'weather.city': d['name'],
                'weather.wind_speed': d['wind']['speed'],
                'weather.lookup_city': '',
                'weather.units': '',
            }
        else:
            raise Send_error(d['message'])
Beispiel #23
0
async def quote_get(bot, args, channel_id, cmd, **kwargs):
    if (len(args) == 0) or (args[0].lower() == 'random'):
        r = await bot.db.fetchone(
            'SELECT * FROM twitch_quotes WHERE channel_id=%s and enabled=1 ORDER BY RAND()',
            (channel_id, ))
    else:
        r = await bot.db.fetchone(
            'SELECT * FROM twitch_quotes WHERE channel_id=%s and number=%s', (
                channel_id,
                args[0],
            ))

    if not r:
        raise Send_error('Unknown quote')

    if r['enabled'] == 0:
        raise Send_error('This quote is deleted')

    return {
        'quote.message': r['message'],
        'quote.number': r['number'],
        'quote.user': r['created_by_user'],
        'quote.date': r['created_at'].strftime('%Y-%m-%d'),
    }
Beispiel #24
0
async def quote_edit(bot, channel_id, cmd, args, **kwargs):
    if len(args) < 2:
        raise Send_error(f'Syntax error, use !{cmd} <number> <new text>')

    n = args.pop(0)

    r = await bot.db.fetchone(
        'SELECT enabled FROM twitch_quotes WHERE channel_id=%s and number=%s',
        (
            channel_id,
            n,
        ))
    if not r:
        raise Send_error('Unknown quote')

    if r['enabled'] == 0:
        raise Send_error('This quote is deleted')

    await bot.db.execute(
        'UPDATE twitch_quotes SET '
        'message=%s, updated_at=%s '
        'WHERE channel_id=%s and number=%s',
        (' '.join(args), datetime.utcnow(), channel_id, n))
    raise Send_error(f'Quote updated')
Beispiel #25
0
async def get_rank(bot, headers, region, encrypted_id, result):
    url = f'https://{region}.api.riotgames.com/lol/league/v4/entries/by-summoner/{encrypted_id}'
    async with bot.ahttp.get(url, headers=headers) as r:
        if r.status >= 400:
            error = await r.text()
            raise Send_error(f'Riot error: {error}')
        d = await r.json()

    for a in d:
        if a['queueType'] == 'RANKED_SOLO_5x5':
            result['lol.wins'] = a['wins']
            result['lol.losses'] = a['losses']
            result['lol.rank'] = a['rank']
            result['lol.tier'] = a['tier'].title()
            result['lol.lp'] = a['leaguePoints']
            break
Beispiel #26
0
async def get_live(channel_id, bot, headers, region, puuid, result):
    if not bot.channels_check[channel_id]['went_live_at']:
        return
    new_region = new_regions[region]    
    url = f'https://{new_region}.api.riotgames.com/lol/match/v5/matches/by-puuid/{puuid}/ids?start=0&count=100&startTime={int(bot.channels_check[channel_id]["went_live_at"].timestamp())}'
    async with bot.ahttp.get(url, headers=headers) as r:
        if r.status >= 400:
            error = await r.text()
            raise Send_error(f'Riot error: {error}')
        matches = await r.json()

    w = []
    for m in matches:
        w.append(count_match(bot, m, new_region, headers, puuid))
    r = await asyncio.gather(*w)
    for t in r:
        if t:
            result['lol.live_wins'] += 1
        else:
            result['lol.live_losses'] += 1
Beispiel #27
0
async def refresh_token(bot, channel_id, refresh_token):
    url = 'https://accounts.spotify.com/api/token'
    body = {
        'grant_type': 'refresh_token',
        'refresh_token': refresh_token,
        'client_id': config['spotify']['client_id'],
        'client_secret': config['spotify']['client_secret'],
    }
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded',
    }
    async with bot.ahttp.post(url, params=body, headers=headers) as r:
        if r.status == 200:
            data = await r.json()
            await bot.db.execute(
                'UPDATE twitch_spotify SET token=%s WHERE channel_id=%s;',
                (data['access_token'], channel_id))
            if 'refresh_token' in data:
                await bot.db.execute(
                    'UPDATE twitch_spotify SET refresh_token=%s WHERE channel_id=%s;',
                    (data['refresh_token'], channel_id))
        else:
            raise Send_error('ERROR: Soptify needs to be reauthorized')
Beispiel #28
0
async def cmd_manager(bot, display_name, args, cmd, channel_id, **kwargs):
    if len(args) < 2:
        raise Send_error(
            'Syntax error, use: !{} add/edit/get/delete [cmd] <response>'.
            format(cmd))

    method = args.pop(0).lower()
    cmd_name = args.pop(0).lower().strip('!')

    if method == 'add' or method == 'edit':
        if len(args) == 0:
            raise Send_error('Syntax error, use: !{} {} {} <reponse>'.format(
                cmd, method, cmd_name))
        try:
            utils.validate_cmd(cmd_name)
            utils.validate_cmd_response(' '.join(args))
        except Exception as e:
            raise Send_error(str(e))

    if method == 'add':
        r = await get_cmd(bot, channel_id, cmd_name)
        if r:
            raise Send_error(
                'Cmd "{0}" already exists, use: !{1} edit {0} <response>'.
                format(cmd_name, cmd))
        await bot.db.execute(
            'INSERT INTO twitch_commands (channel_id, cmd, response, created_at, updated_at) '
            'VALUES (%s, %s, %s, %s, %s)',
            (channel_id, cmd_name, ' '.join(args), datetime.utcnow(),
             datetime.utcnow()))
        raise Send_error('!{} successfully saved'.format(cmd_name))

    elif method == 'edit':
        r = await get_cmd(bot, channel_id, cmd_name)
        if not r:
            raise Send_error(
                'Cmd "{0}" does not exist, use !{1} add {0} <response>'.format(
                    cmd_name, cmd))

        await bot.db.execute(
            'UPDATE twitch_commands SET response=%s, updated_at=%s WHERE channel_id=%s and cmd=%s',
            (
                ' '.join(args),
                datetime.utcnow(),
                channel_id,
                cmd_name,
            ))
        raise Send_error('!{} successfully saved'.format(cmd_name))

    elif method == 'delete':
        r = await get_cmd(bot, channel_id, cmd_name)
        if not r:
            raise Send_error('Cmd "{0}" does not exist'.format(cmd_name))
        await bot.db.execute(
            'DELETE FROM twitch_commands WHERE channel_id=%s and cmd=%s', (
                channel_id,
                cmd_name,
            ))
        raise Send_error('!{} successfully deleted'.format(cmd_name))

    elif method == 'get':
        r = await get_cmd(bot, channel_id, cmd_name)
        if not r:
            raise Send_error('Cmd "{0}" does not exist'.format(cmd_name))
        raise Send_error('{}'.format(r['response']))

    else:
        raise Send_error(
            'Syntax error, use: !{} add/edit/get/delete [cmd] <response>'.
            format(cmd))