예제 #1
0
    def __init__(self, **kwargs):
        if kwargs['is_pm'] and len(kwargs['full_command']) > 1:
            kwargs['room'] = find_true_name(kwargs['full_command'][1])

        super().__init__(**kwargs)

        if self.is_pm:
            if self.command in ['mal', 'anime', 'manga']:
                self.room = const.ANIME_ROOM
            else:
                self.min_args += 1
                self.usage_msg += '[ROOM] '

        if self.command in ['anime', 'manga']:
            self.usage_msg += 'SERIES NAME'

        if self.command in ['randanime', 'randmanga']:
            self.usage_msg += '[GENRES]'

        if self.command == 'mal':
            self.req_rank_pm = ' '

            self.usage_msg += '[USERNAME] [-r CATEGORIES]'
            self.mal_args = mal_arg_parser(' '.join(self.args),
                                           self.true_caller)

            if self.mal_args.roll is not None:
                self.pm_response = self.is_pm
예제 #2
0
    def update_info(self, info_line):
        parts = info_line.split('|')

        if len(parts) <= 2:
            return

        if parts[1] == 'player':
            self.players.append(find_true_name(parts[3]))
예제 #3
0
def check_answer(guess, answers, exact=False):
    '''
    Checks if a guess is correct for a trivia question.

    Args:
        guess (str): The raw guess
        answers (str list): Base list of acceptable answers 
    
    Returns:
        An empty string if the guess is incorrect, else the matching answer
        from answers.
    '''
    t_guess = find_true_name(guess)

    for answer in answers:
        t_answer = find_true_name(answer)
        if (t_guess == t_answer):
            return answer
        elif exact:
            continue
        elif t_answer in t_guess:
            return answer

        # The heuristic for generating aliases for the answers is as follows -
        # given an answer, valid prefixes consist of whole alphanumeric chunks
        # (separated by non-alphanumeric chars), starting from the beginning of
        # the answer. If the guess matches any of these prefixes, and is at least
        # 8 characters long, it is counted as correct.
        answer_parts = re.findall('([a-zA-Z0-9]+)', answer)
        acceptable = []
        total = ''
        for part in answer_parts:
            total += part.lower()
            if len(total) >= 8:
                acceptable.append(total)

        if ":" in answer:
            prefix = answer.split(':')[0]
            acceptable.append(find_true_name(prefix))

        if t_guess in acceptable:
            return answer

    return ''
예제 #4
0
async def steam_user_rand_series(putter, id64, username, caller, ctx):
    true_caller = find_true_name(caller)

    prefix = f'{ctx}|'
    if ctx == 'pm':
        prefix = f'|/w {true_caller},'

    games = []
    async with aiohttp.ClientSession(trust_env=True) as session:
        steam_key = os.getenv('STEAM_KEY')
        async with session.get(
                f'{STEAM_API}IPlayerService/GetOwnedGames/v0001/?key={steam_key}&steamid={id64}'
        ) as r:
            resp = await r.text()

            try:
                games = json.loads(resp)['response']['games']
            except:
                await putter(
                    f'{prefix} No games found for {username} with the given specifications.'
                )
                return

    game_info = None
    while True:
        if len(games) == 0:
            await putter(
                f'{prefix} No games found for {username} with the given specifications.'
            )
            return

        rand_game = random.choice(games)

        game_id = rand_game['appid']
        game_info = await steam_game_info(game_id)

        if not game_info:
            games.remove(rand_game)
            await asyncio.sleep(1)
            continue
        elif game_info['type'] != 'game':
            games.remove(rand_game)
            await asyncio.sleep(1)
            continue
        else:
            break

    rand_title = game_info['name']
    msg = f'{prefix}{caller} rolled {rand_title}'
    await putter(msg)
예제 #5
0
    async def gen_mangadex_question(self, session):
        series_id = ''
        cover_id = ''
        answers = []
        while not series_id:
            await asyncio.sleep(0.2)
            async with session.get(f'{const.DEX_API}manga/random') as r:
                rand_series = await r.json()

                if r.status != 200:
                    await asyncio.sleep(3)
                    continue

                series_info = rand_series['data']
                if series_info['attributes']['contentRating'] != 'safe':
                    continue

                skip = False
                for tag in series_info['attributes']['tags']:
                    if tag['attributes']['name']['en'] in [
                            'Doujinshi', 'Oneshot'
                    ]:
                        skip = True
                        break
                if skip:
                    continue

                if not self.duplicate_check(series_info['id']):
                    series_id = series_info['id']

                    answers = [series_info['attributes']['title']['en']]
                    for title in series_info['attributes']['altTitles']:
                        if len(find_true_name(title['en'])) > 0:
                            answers.append(title['en'])

        async with session.get(f'{const.DEX_API}cover',
                               params={'manga[]': [series_id]}) as r:
            covers = await r.json()

            cover_info = random.choice(covers['data'])
            cover_id = cover_info['attributes']['fileName']

        img_url = f'https://uploads.mangadex.org/covers/{series_id}/{cover_id}.256.jpg'
        img_uhtml = gen_uhtml_img_code(img_url, height_resize=PIC_SIZE)
        await self.questions.put(
            ['/adduhtml {}, {}'.format(UHTML_NAME, img_uhtml), answers])
예제 #6
0
    async def evaluate(self):
        if self.check_eligible():
            await self.pm_msg(self.msg)
            return ''

        arg_offset = 1 if self.is_pm else 0

        if self.command == 'song_add':
            title = ' '.join(self.args[arg_offset:-1])
            url = self.args[-1]

            song_exists = await self.db_man.execute(
                "SELECT * FROM songs WHERE "
                f"room='{self.room}' AND url='{url}'")

            if song_exists:
                self.msg = f'This url already exists in the song pool for {self.room}.'
            else:
                # Sqlite escape
                title = title.replace("'", "''")
                await self.db_man.execute(
                    "INSERT INTO songs (room, title, url) "
                    f"VALUES ('{self.room}', '{title}', '{url}')")
                self.msg = f'Added {title} to {self.room} song pool.'

        elif self.command == 'song_rm':
            title = ' '.join(self.args[arg_offset:])
            self.msg = f'{title} not found in song pool.'

            room_songs = await self.db_man.execute("SELECT title FROM songs "
                                                   f"WHERE room='{self.room}'")

            for s in list(sum(room_songs, ())):
                if find_true_name(s) == find_true_name(title):
                    escaped_s = s.replace("'", "''")
                    await self.db_man.execute(
                        "DELETE FROM songs WHERE "
                        f"room='{self.room}' AND title='{escaped_s}'")

            self.msg = f'Deleted all songs called {title} from song pool.'

        elif self.command == 'song_list':
            self.msg = f'No songs found for {self.room}.'

            song_exists = await self.db_man.execute(
                "SELECT title, url FROM songs "
                f"WHERE room='{self.room}'")

            if song_exists:
                room_songs = {}
                for song in song_exists:
                    room_songs[song[0]] = song[1]

                header_text = monospace_table_row([('Song Title', 100),
                                                   ('Link', 25)])
                header_text += '\n' + '-' * 146
                box_text = ''
                for s in sorted(room_songs.keys()):
                    box_text += monospace_table_row([(s, 100),
                                                     (room_songs[s], 25)])
                    box_text += '\n'

                r = requests.post(
                    const.PASTIE_API,
                    data=f'{header_text}\n{box_text}'.encode('utf-8'))

                if r.status_code == 200:
                    self.msg = f"""https://pastie.io/raw/{r.json()['key']}"""
                else:
                    self.msg = 'Unable to generate song list at this time.'

        elif self.command == 'randsong':
            song_exists = await self.db_man.execute(
                "SELECT title, url FROM songs "
                f"WHERE room='{self.room}'")

            if not song_exists:
                self.msg = f'There are no songs for {self.room}!'
            else:
                rand_song = random.choice(song_exists)
                # Decode the URL because PS re-encodes it
                self.msg = f'[[{rand_song[0]}<{urllib.parse.unquote(rand_song[1])}>]]'

        return self.msg
예제 #7
0
    async def evaluate(self):
        if self.check_eligible():
            await self.pm_msg(self.msg)
            return ''

        arg_offset = 1 if self.is_pm else 0

        if self.command == 'emote_add':
            emote = self.args[arg_offset].lower()
            if emote.endswith(','):
                emote = emote[:-1]

            if find_true_name(emote) != emote:
                await self.pm_msg('Emotes must be only letters and/or numbers.'
                                  )
                return

            emote_url = self.args[arg_offset + 1]
            if 'discordapp' in emote_url:
                await self.pm_msg('Discord URLs do not work as emotes.')
                return

            emote_exists = await self.db_man.execute(
                "SELECT * FROM emotes WHERE "
                f"room='{self.room} AND name={emote}'")
            if emote_exists:
                await self.db_man.execute(
                    f"UPDATE emotes SET url='{emote_url}' "
                    f"WHERE room='{self.room}' AND name='{emote}'")
            else:
                await self.db_man.execute(
                    "INSERT INTO emotes (room, name, url) "
                    f"VALUES ('{self.room}', '{emote}', '{emote_url}')")

            self.msg = f'Set :{emote}: to show {emote_url}.'

        elif self.command == 'emote_rm':
            emote = find_true_name(self.args[arg_offset])

            emote_exists = await self.db_man.execute(
                "SELECT * FROM emotes WHERE "
                f"room='{self.room}' AND name='{emote}'")

            if not emote_exists:
                await self.pm_msg(f'{self.room} does not have emote {emote}.')
                return

            await self.db_man.execute("DELETE FROM emotes WHERE "
                                      f"room='{self.room}' AND name='{emote}'")
            self.msg = f'Removed {emote} from {self.room}.'

        elif self.command == 'emote_list':
            self.msg = 'No emotes found.'

            emote_list = await self.db_man.execute("SELECT name FROM emotes "
                                                   f"WHERE room='{self.room}'")

            if emote_list:
                # Flatten
                emote_list = list(sum(emote_list, ()))
                self.msg = f'!code {self.room} emotes: ' + ', '.join(
                    emote_list)

        elif self.command == 'emote_stats':
            self.msg = f'No emotes found for {self.room}.'

            emote_list = await self.db_man.execute(
                "SELECT name, times_used FROM emotes "
                f"WHERE room='{self.room}'")

            if emote_list:
                header_text = monospace_table_row([('Emote', 30),
                                                   ('Times Used', 12)])
                header_text += '\n' + '-' * 44
                box_text = ''
                for e in sorted(emote_list, key=lambda x: x[1], reverse=True):
                    box_text += monospace_table_row([(e[0], 30), (e[1], 12)])
                    box_text += '\n'

                r = requests.post(const.PASTIE_API,
                                  data=f'{header_text}\n{box_text}')

                if r.status_code == 200:
                    self.msg = f"""https://pastie.io/raw/{r.json()['key']}"""
                else:
                    self.msg = 'Unable to generate emote stats at this time.'

        return self.msg
예제 #8
0
    async def evaluate(self):
        self.msg = '/adduhtml '

        eligibility = self.check_eligible()

        if eligibility and eligibility != 2:
            await self.pm_msg(self.msg)
            return ''
        elif eligibility:
            if self.is_pm and not self.room:
                await self.pm_msg(self.usage_with_error(''))
                return ''
            elif self.is_pm and self.bot.roomlist[self.room].get_user(
                    self.caller):
                self.msg = f'/sendprivateuhtml {self.true_caller}, '
            else:
                await self.pm_msg(f'You can only use {self.command} in PMs. '
                                  'Make sure you\'re in the specified room.')
                return ''
        elif self.is_pm:
            self.msg = f'/sendprivateuhtml {self.true_caller}, '

        if self.command == 'plebs':
            uhtml = gen_uhtml_img_code(const.PLEB_URL, height_resize=250)
            self.msg += f'hippo-pleb, {uhtml}'

        elif self.command == 'calendar':
            curr_day_str = curr_cal_date()
            calendar = json.load(open(const.CALENDARFILE))

            if self.room not in calendar:
                calendar[self.room] = {curr_day_str: []}
                json.dump(calendar, open(const.CALENDARFILE, 'w', indent=4))
            if not calendar[self.room][curr_day_str]:
                return 'No images found for this date.'

            date_imgs = calendar[self.room][curr_day_str]
            uhtml = gen_uhtml_img_code(random.choice(date_imgs),
                                       height_resize=200)
            self.msg += f'hippo-calendar, {uhtml}'

        elif self.command == 'birthday':
            await self.bot.send_birthday_text(automatic=False)

        elif self.command == 'anime':
            query = ' '.join(self.args)
            self.msg += await anilist_search('anime', query,
                                             self.bot.anilist_man)

        elif self.command == 'manga':
            query = ' '.join(self.args)
            self.msg += await anilist_search('manga', query,
                                             self.bot.anilist_man)

        elif self.command == 'randanime':
            genres = []
            tags = []

            true_args = list(map(find_true_name, self.args))

            for g in const.ANILIST_GENRES:
                if find_true_name(g) in true_args:
                    genres.append(g)
            for t in list(const.ANILIST_TAGS):
                if find_true_name(t) in true_args:
                    tags.append(t)

            self.msg += await anilist_rand_series('anime',
                                                  self.bot.anilist_man,
                                                  genres=genres,
                                                  tags=tags)

        elif self.command == 'randmanga':
            genres = []
            tags = []

            true_args = list(map(find_true_name, self.args))

            for g in const.ANILIST_GENRES:
                if find_true_name(g) in true_args:
                    genres.append(g)
            for t in list(const.ANILIST_TAGS):
                if find_true_name(t) in true_args:
                    tags.append(t)

            self.msg += await anilist_rand_series('manga',
                                                  self.bot.anilist_man,
                                                  genres=genres,
                                                  tags=tags)

        elif self.command == 'mal':
            true_mal_user = find_true_name(''.join(self.mal_args.username))

            if self.mal_args.roll is not None:
                media = ['anime', 'manga']
                if 'anime' not in self.mal_args.roll:
                    media.remove('anime')
                if 'manga' not in self.mal_args.roll:
                    media.remove('manga')

                return_msg = await self.bot.mal_man.user_rand_series(
                    true_mal_user, media, anotd=self.is_anotd)

                if return_msg.startswith('rolled'):
                    self.msg = f'{self.caller} {return_msg}'
                else:
                    self.msg = return_msg

            else:
                return_msg = await self.bot.mal_man.show_user(
                    true_mal_user, self.bot.jikan_man)

                if is_uhtml(return_msg):
                    self.msg += f'hippo-{true_mal_user}mal, {return_msg}'
                elif self.is_pm:
                    self.pm_response = self.is_pm
                    self.msg = return_msg
                else:
                    self.msg = return_msg

        return self.msg
예제 #9
0
    async def gen_am_base(self, session, anilist_man):
        query = '''
        query ($page: Int, $perpage: Int) {
            Page (page: $page, perPage: $perpage) {
                pageInfo {
                    total
                }
                media (CATEGORIES_PLACEHOLDER minimumTagRank: 50, isAdult: false, sort: SORT_PLACEHOLDER) {
                    id
                    idMal
                    type
                    description
                    title {
                        english
                        userPreferred
                        romaji
                    }
                    coverImage {
                        large
                    }
                    bannerImage
                }
            }
        }
        '''

        if not self.category_params:
            if 'all' not in self.categories:
                media = []
                genres = []
                tags = []
                for c in self.categories:
                    true_c = find_true_name(c)

                    for m in const.ANILIST_MEDIA:
                        if true_c == find_true_name(m):
                            media.append(m)
                            break

                    for g in const.ANILIST_GENRES:
                        if true_c == find_true_name(g):
                            genres.append(g)
                            break

                    for t in const.ANILIST_TAGS:
                        if true_c == find_true_name(t):
                            tags.append(t)
                            break

                if media:
                    self.category_params.append(
                        f'format_in: {", ".join(media)}')
                if genres:
                    self.category_params.append(
                        f'genre_in: {json.dumps(genres)}')
                if tags:
                    self.category_params.append(f'tag_in: {json.dumps(tags)}')

                if self.excludecats:
                    media = []
                    genres = []
                    tags = []
                    for c in self.excludecats:
                        true_c = find_true_name(c)

                        for m in const.ANILIST_MEDIA:
                            if true_c == find_true_name(m):
                                media.append(m)
                                break

                        for g in const.ANILIST_GENRES:
                            if true_c == find_true_name(g):
                                genres.append(g)
                                break

                        for t in const.ANILIST_TAGS:
                            if true_c == find_true_name(t):
                                tags.append(t)
                                break

                    if media:
                        self.category_params.append(
                            f'format_not_in: {json.dumps(media)}')
                    if genres:
                        self.category_params.append(
                            f'genre_not_in: {json.dumps(genres)}')
                    if tags:
                        self.category_params.append(
                            f'tag_not_in: {json.dumps(tags)}')

        category_params_str = ','.join(self.category_params)
        if category_params_str:
            category_params_str += ','
        query = query.replace('CATEGORIES_PLACEHOLDER', category_params_str)

        sort = 'SCORE_DESC' if self.by_rating else 'POPULARITY_DESC'
        query = query.replace('SORT_PLACEHOLDER', sort)

        # Get max_rank
        if not self.max_rank:
            query_vars = {'page': 1, 'perpage': 1}

            async with anilist_man.lock():
                self.max_rank = await anilist_num_entries(
                    query, query_vars, session)

            if not self.max_rank:
                for task in asyncio.all_tasks():
                    if task.get_name() == 'trivia-{}'.format(self.room):
                        task.cancel()
                        return

        rank = 0
        if self.max_rank < self.num_qs:
            self.series_exist = False
            for task in asyncio.all_tasks():
                if task.get_name() == 'trivia-{}'.format(self.room):
                    task.cancel()
                    return

        diff_scale = max(1.1, math.log(self.max_rank, 10) / 1.5)
        std_dev_scale = max(10, diff_scale**2)

        while rank < 1 or rank > self.max_rank:
            rank = int(
                random.gauss(self.max_rank // ((diff_scale)**(10 - self.diff)),
                             (std_dev_scale * self.diff / 2)))

        all_series = []
        roll_query_vars = {'page': rank, 'perpage': 1}
        # Anilist pageInfo is flaky, resulting in the possibility of overshooting the upper bound at higher diffs
        while not all_series:
            async with anilist_man.lock():
                async with session.post(const.ANILIST_API,
                                        json={
                                            'query': query,
                                            'variables': roll_query_vars
                                        }) as r:
                    resp = await r.json()

                    if r.status != 200:
                        for task in asyncio.all_tasks():
                            if task.get_name() == 'trivia-{}'.format(
                                    self.room):
                                task.cancel()
                                return

                    all_series = resp['data']['Page']['media']

            roll_query_vars['page'] = math.floor(roll_query_vars['page'] * 0.8)

        series_data = all_series[0]

        aliases = []
        for title in series_data['title'].values():
            if title:
                aliases.append(title)

        slug = {
            'img_url': series_data['coverImage']['large'],
            'description': series_data['description'],
            'answers': aliases,
            'rank': rank,
            'id': series_data['id']
        }

        is_nsfw = await self.bot.mal_man.is_nsfw(series_data['type'].lower(),
                                                 series_data['idMal'])
        if is_nsfw:
            slug = slug.fromkeys(slug, None)
        return slug
예제 #10
0
    def get_user(self, username):
        for u in self.users:
            if u.true_name == find_true_name(username):
                return u

        return None
예제 #11
0
 def remove_user(self, username):
     for u in self.users:
         if u.true_name == find_true_name(username):
             self.users.remove(u)
예제 #12
0
 def __init__(self, username, rank=' '):
     self.name = username
     self.true_name = find_true_name(self.name)
     self.rank = rank