Example #1
0
async def sync_users_(event):
    """Syncs sticker list users. (You must have emoji-council role)"""
    if not event.user.has_role(ROLE__NEKO_DUNGEON__EMOJI_MANAGER):
        abort(
            f'You must have {ROLE__NEKO_DUNGEON__EMOJI_MANAGER:m} role to invoke this command.'
        )

    async with DB_ENGINE.connect() as connector:
        response = await connector.execute(
            select([sticker_counter_model.user_id
                    ]).distinct(sticker_counter_model.user_id), )

        results = await response.fetchall()

        user_ids = [result[0] for result in results]
        guild_users = GUILD__NEKO_DUNGEON.users

        user_ids_to_remove = [
            user_id for user_id in user_ids if (user_id not in guild_users)
        ]

        if user_ids_to_remove:
            await connector.execute(STICKER_COUNTER_TABLE.delete().where(
                sticker_counter_model.user_id.in_(user_ids_to_remove)))

    return f'Unused user entries removed: {len(user_ids_to_remove)}'
Example #2
0
    async def __call__(self, command_name):
        command_source = self.sources.get(command_name, None)
        if (command_source is None):
            abort(f'Command not found: {command_name!r}')

        page = command_source[0]

        if len(command_source) > 1:
            components = Row(
                Button(
                    emoji=EMOJI_LEFT,
                    custom_id=f'source.{self.command_type}.{command_name}._',
                    enabled=False,
                ),
                Button(
                    emoji=EMOJI_RIGHT,
                    custom_id=f'source.{self.command_type}.{command_name}.1',
                    enabled=True,
                ),
            )

        else:
            components = None

        yield InteractionResponse(page, components=components)
Example #3
0
async def leave(client, event):
    """Leave from the current game, pls no."""
    game = ACTIVE_GAMES.get(event.channel.id, None)
    if game is None:
        abort('Nothing to leave from.')

    return Embed(None, game.leave_user(event.user), color=KANAKO_COLOR)
Example #4
0
async def info(client, event):
    """Shows information about the current game."""
    game = ACTIVE_GAMES.get(event.channel.id, None)
    if game is None:
        abort('There is no active game at the channel.')

    return game.get_information()
Example #5
0
async def all_users(
    client,
    event,
):
    """Shows the new users of the guild."""
    if not event.user.has_role(ROLE__NEKO_DUNGEON__MODERATOR):
        abort('Hacker trying to hack Discord.')

    users = []
    guild = event.guild
    for user in guild.users.values():
        joined_at = user.guild_profiles[guild].joined_at
        if (joined_at is not None):
            users.append((joined_at, user))

    users.sort(reverse=True)

    embeds = []

    for index, (joined_at, user) in enumerate(users, 1):
        if index % 10 == 1:
            embed = Embed('Joined users')
            embeds.append(embed)

        add_user_field(embed, index, joined_at, user)

    await Pagination(client, event, embeds)
Example #6
0
async def is_banned(client, event, user: ('user', 'Who should I check?')):
    """Checks whether the user is banned."""
    if (not event.user.has_role(ROLE__NEKO_DUNGEON__TESTER)) and (
            not event.user_permissions.can_ban_users):
        abort('You need to have `ban users` permissions to do this.')

    if not event.channel.cached_permissions_for(client).can_ban_users:
        abort('I need to have `ban users` permissions to do this.')

    yield  # acknowledge the event

    try:
        ban_entry = await client.guild_ban_get(event.guild, user)
    except DiscordException as err:
        if err.code == ERROR_CODES.unknown_ban:
            ban_entry = None
        else:
            raise

    embed = Embed(f'Ban entry for {user:f}').add_thumbnail(user.avatar_url)

    if ban_entry is None:
        embed.description = 'The user **NOT YET** banned.'

    else:
        embed.description = 'The user is banned.'

        reason = ban_entry.reason
        if reason is None:
            reason = '*No reason was specified.*'

        embed.add_field('Reason:', reason)

    yield embed
Example #7
0
async def latest_users(
    client,
    event,
):
    """Shows the new users of the guild."""
    if not event.user.has_role(ROLE__NEKO_DUNGEON__MODERATOR):
        abort('Hacker trying to hack Discord.')

    date_limit = datetime.now() - timedelta(days=7)

    users = []
    guild = event.guild
    for user in guild.users.values():
        # Use created at and not `joined_at`, we can ignore lurkers.
        created_at = user.guild_profiles[guild].created_at
        if created_at > date_limit:
            users.append((created_at, user))

    users.sort(reverse=True)
    del users[10:]

    embed = Embed('Recently joined users')
    if users:
        for index, (joined_at, user) in enumerate(users, 1):
            add_user_field(embed, index, joined_at, user)

    else:
        embed.description = '*none*'

    return InteractionResponse(embed=embed, allowed_mentions=None)
Example #8
0
async def sync_emojis_(event):
    """Syncs emoji list emojis. (You must have emoji-council role)"""
    if not event.user.has_role(ROLE__NEKO_DUNGEON__EMOJI_MANAGER):
        abort(f'You must have {ROLE__NEKO_DUNGEON__EMOJI_MANAGER:m} role to invoke this command.')
    
    async with DB_ENGINE.connect() as connector:
        response = await connector.execute(
            select([emoji_counter_model.emoji_id]).distinct(emoji_counter_model.emoji_id),
        )
        
        results = await response.fetchall()
        
        emoji_ids = [result[0] for result in results]
        guild_emojis = GUILD__NEKO_DUNGEON.emojis
        
        emoji_ids_to_remove = [emoji_id for emoji_id in emoji_ids if (emoji_id not in guild_emojis)]
        
        if emoji_ids_to_remove:
            await connector.execute(
                EMOJI_COUNTER_TABLE.delete().where(
                    emoji_counter_model.emoji_id.in_(emoji_ids_to_remove)
                )
            )
    
    return f'Unused emoji entries removed: {len(emoji_ids_to_remove)}'
Example #9
0
def get_player(client, event):
    player = client.solarlink.get_player(event.guild_id)

    if player is None:
        abort('No player in this server!')

    return player
Example #10
0
async def safe_booru(client, event,
        tags: ('str', 'Some tags to spice it up?') = '',
            ):
    """Some safe images?"""
    guild = event.guild
    if (guild is None) or guild.partial:
        abort(f'Please invite me, {client:f} first!')
    
    return answer_booru(client, event, tags, SAFE_BOORU, SAFE_BANNED)
Example #11
0
def get_extension_by_name(name):
    extension = EXTENSION_LOADER.get_extension(name)
    if (extension is None):
        abort('There is no extension with the specified name.')

    if extension.locked:
        abort('The extension is locked, probably for reason.')

    return extension
Example #12
0
async def role_claimer(event):
    """Role claimer message. (Owner only)"""

    # Double check.
    if not event.user.has_role(ROLE_OWNER):
        abort('Owner only')

    return InteractionResponse('Claim role by clicking on it',
                               components=ROLE_CLAIMER_COMPONENTS)
Example #13
0
async def join(client, event):
    """Join to the currently active game inside of the channel!"""
    game = ACTIVE_GAMES.get(event.channel.id, None)
    if game is None:
        abort('There is nothing to join into at the channel.')

    if isinstance(game, KanakoRunner):
        abort('The game is already started, oof.')

    return Embed(None, game.join_user(event.user), color=KANAKO_COLOR)
Example #14
0
async def start_(client, event):
    """Starts the current game."""
    game = ACTIVE_GAMES.get(event.channel.id, None)
    if game is None:
        abort('There is no active game at the channel.')

    if isinstance(game, KanakoRunner):
        abort('The game is already started, oof.')

    return game.start_user(event.user)
Example #15
0
async def role_(
        client,
        event,
        role: ('role', 'Select the role to show information of.'),
):
    """Shows the information about a role."""
    if role.partial:
        abort('I must be in the guild, where the role is.')

    embed = Embed(f'Role information for: {role.name}', color=role.color)
    embed.add_field('Position', str(role.position), inline=True)
    embed.add_field('Id', str(role.id), inline=True)

    embed.add_field('Separated',
                    'true' if role.separated else 'false',
                    inline=True)
    embed.add_field('Mentionable',
                    'true' if role.mentionable else 'false',
                    inline=True)

    manager_type = role.manager_type
    if manager_type is RoleManagerType.none:
        managed_description = 'false'
    else:
        if manager_type is RoleManagerType.unset:
            await client.sync_roles(role.guild)
            manager_type = role.manager_type

        if manager_type is RoleManagerType.bot:
            managed_description = f'Special role for bot: {role.manager:f}'
        elif manager_type is RoleManagerType.booster:
            managed_description = 'Role for the boosters of the guild.'
        elif manager_type is RoleManagerType.integration:
            managed_description = f'Special role for integration: {role.manager.name}'
        elif manager_type is RoleManagerType.unknown:
            managed_description = 'Some new things.. Never heard of them.'
        else:
            managed_description = 'I have no clue.'

    embed.add_field('Managed', managed_description, inline=True)

    color = role.color
    embed.add_field('color', f'html: {color.as_html}\n'
                    f'rgb: {color.as_rgb}\n'
                    f'int: {color:d}',
                    inline=True)

    created_at = role.created_at
    embed.add_field('Created at', f'{created_at:{DATETIME_FORMAT_CODE}}\n'
                    f'{elapsed_time(created_at)} ago',
                    inline=True)

    return embed
Example #16
0
async def show_map(client, event, map_: ([('hiragana', 'hiragana'),
                                          ('katakana', 'katakana')],
                                         'Choose a map to display!')):
    """Shows the selected map!"""
    permissions = event.channel.cached_permissions_for(client)
    if (not permissions.can_send_messages) or (
            not permissions.can_add_reactions):
        abort(
            'I need `send messages` and `add reactions` permission to execute this command.'
        )

    pages = MAP_SHOWCASES[map_]
    await KanakoPagination(client, event, pages)
Example #17
0
async def add_emoji(client,
                    event,
                    emoji: ('str', 'The emoji to add.'),
                    name: ('str',
                           'Custom name to add the emoji with.') = None):
    """Adds an emoji to the guild."""
    if not client.is_owner(event.user):
        abort('Owner only!')

    emoji = parse_emoji(emoji)
    if emoji is None:
        abort('That\'s not an emoji.')

    if emoji.is_unicode_emoji():
        abort('Cannot add unicode emojis')

    if name is None:
        name = emoji.name
    else:
        if len(name) > 32:
            abort('Name length can be max 32.')

    embed = Embed('Are you sure to add this emoji?').add_field(
        'Name:', name).add_image(emoji.url)

    message = yield InteractionResponse(embed=embed,
                                        components=ADD_EMOJI_COMPONENTS)

    try:
        component_interaction = await wait_for_component_interaction(
            message,
            timeout=300.0,
            check=functools.partial(check_is_user_same, event.user))

    except TimeoutError:
        component_interaction = None
        cancelled = True
    else:
        if component_interaction.interaction == ADD_EMOJI_BUTTON_CANCEL:
            cancelled = True
        else:
            cancelled = False

    if cancelled:
        embed.title = 'Adding emoji has been cancelled.'
    else:
        embed.title = 'Emoji has been added!'

        async with client.http.get(emoji.url) as response:
            emoji_data = await response.read()

        await client.emoji_create(event.guild, name, emoji_data)

    yield InteractionResponse(embed=embed,
                              components=None,
                              message=message,
                              event=component_interaction)
Example #18
0
async def next_(
    client,
    event,
):
    """Plays the next song."""
    player = get_player(client, event)

    track = player.get_current()
    if track is None:
        abort('Nothing to skip.')

    if track.user is not event.user:
        abort(
            'Sorry, the track was added by {event.user:m}, so only they can skip.'
        )

    await player.skip()
    return f'Track skipped: {create_track_repr(track, None)}'
Example #19
0
async def nsfw_booru(client, event,
        tags: ('str', 'Some tags to spice it up?') = '',
            ):
    """Some not so safe images? You perv!"""
    guild = event.guild
    if (guild is None) or guild.partial:
        abort(f'Please invite me, {client:f} first!')
    
    channel = event.channel
    if not channel.nsfw:
        if 'koishi' in tags.lower():
            description = 'I love you too\~,\nbut this is not the right place to lewd.'
        else:
            description = 'Onii chaan\~,\nthis is not the right place to lewd.'
        
        abort(description)
    
    return answer_booru(client, event, tags, NSFW_BOORU, NSFW_BANNED)
Example #20
0
async def guild_(
    event,
    field: ([(name, name) for name in GUILD_FIELDS],
            'Which fields should I show?') = DEFAULT_GUILD_FILED,
):
    """Shows some information about the guild."""
    guild = event.guild
    if guild.partial:
        abort('I must be in the guild to execute this command.')

    embed = Embed(guild.name,
                  color=(guild.icon_hash & 0xFFFFFF if
                         (guild.icon_type is ICON_TYPE_NONE) else
                         (guild.id >> 22) & 0xFFFFFF)).add_thumbnail(
                             guild.icon_url_as(size=128))

    GUILD_FIELDS[field](guild, embed, True)

    return embed
Example #21
0
async def seek(
        client,
        event,
        seconds: (float, 'Where to seek?'),
):
    """Seek the track."""
    player = get_player(client, event)

    track = player.get_current()
    if track is None:
        abort('No songs are being played right now!')

    duration = track.duration
    if (seconds < 0.0) or (seconds > duration):
        abort(
            f'Cannot seek to {seconds:.2f} seconds. Please define a value between `0` and {duration:.0f}.'
        )

    await player.seek(seconds)
    return 'Seeked the current track!'
Example #22
0
async def enable_ping(
    client,
    event,
    allow: ('bool', 'Enable?') = True,
):
    """Enables the ping command in your guild."""
    guild = event.guild
    if guild is None:
        abort('Guild only command.')

    if not event.user_permissions.can_administrator:
        abort('You must have administrator permission to use this command.')

    application_commands = await client.application_command_guild_get_all(guild
                                                                          )
    for application_command in application_commands:
        # If you are not working with overlapping names, a name check should be enough.
        if application_command.name == ping.name:
            command_present = True
            break
    else:
        command_present = False

    if allow:
        if command_present:
            content = 'The command is already present.'
        else:
            await client.application_command_guild_create(
                guild, ping.get_schema())
            content = 'The command has been added.'
    else:
        if command_present:
            await client.application_command_guild_delete(
                guild, application_command)
            content = 'The command has been disabled.'
        else:
            content = 'The command is not present.'

    return Embed('Success', content)
Example #23
0
async def debug_command(client, event,
        command_name: (str, 'The command\'s name.')
            ):
    """Gets debug information about the given command."""
    if not client.is_owner(event.user):
        abort('Owner only.')
    
    if not command_name:
        abort('Empty command name.')
    
    application_commands = await client.application_command_guild_get_all(GUILD__NEKO_DUNGEON)
    for application_command in application_commands:
        if application_command.name == command_name:
            break
    else:
        abort('Command could not be found.')
    
    try:
        permission = await client.application_command_permission_get(GUILD__NEKO_DUNGEON, application_command)
    except DiscordException as err:
        if err.code == ERROR_CODES.unknown_application_command_permissions:
            permission = None
        else:
            raise
    
    text_parts = [
        '**Application command**:\n'
        'Name : `', application_command.name, '`\n'
        'Id : `', repr(application_command.id), '`\n'
        'Allow by default : `', repr(application_command.allow_by_default), '`\n'
        '**Permission overwrites**:\n'
    ]
    
    if permission is None:
        overwrites = None
    else:
        overwrites = permission.overwrites
    
    if overwrites is None:
        text_parts.append('*none*')
    else:
        for index, overwrite in enumerate(overwrites):
            text_parts.append(repr(index))
            text_parts.append('.: type: `')
            text_parts.append(overwrite.type.name)
            text_parts.append('`; id: `')
            text_parts.append(repr(overwrite.target_id))
            
            target_name = overwrite.target.name
            if target_name:
                text_parts.append('`; name: `')
                text_parts.append(target_name)
            
            text_parts.append('`; allowed: `')
            text_parts.append(repr(overwrite.allow))
            text_parts.append('`\n')
    
    return ''.join(text_parts)
Example #24
0
async def character(client, event,
        name: ('str', 'Who\'s?'),
            ):
    """Shows you the given Touhou character's portrait."""
    name_length = len(name)
    if name_length == 0:
        abort('Empty name was given.')
    
    if name_length > 10:
        name_length = 10
    
    diversity = 0.2+(10-name_length)*0.02
    
    matcheds = get_close_matches(name, TOUHOU_NAMES, n=1, cutoff=1.0-diversity)
    if matcheds:
        return TOUHOU_NAME_RELATIONS[matcheds[0]](client, event)
    else:
        embed = Embed('No match', color=BOORU_COLOR)
        matcheds = get_close_matches(name, TOUHOU_NAMES, n=10, cutoff=0.8-diversity)
        if matcheds:
            field_value_parts = []
            for index, matched in enumerate(matcheds, 1):
                field_value_parts.append(str(index))
                field_value_parts.append('.: **')
                field_value_parts.append(matched)
                field_value_parts.append('**')
                name = TOUHOU_NAME_RELATIONS[matched].title
                if matched != name:
                    field_value_parts.append(' [')
                    field_value_parts.append(name)
                    field_value_parts.append(']')
                
                field_value_parts.append('\n')
            
            del field_value_parts[-1]
            
            embed.add_field('Close matches:', ''.join(field_value_parts))
        return embed
Example #25
0
async def register(
        event,
        name: ('str', 'Please provide a name to register.'),
):
    """Registers the specified extension by it's name."""
    check_permission(event)

    extension = EXTENSION_LOADER.get_extension(name)
    if (extension is not None):
        abort(
            f'There is already an extension added with the given name: `{extension.name}`.'
        )

    try:
        EXTENSION_LOADER.add(name)
    except TypeError:
        title = f'Registering {name!r} extension failed.'
        description = 'There is no such extension.'
    else:
        title = f'Registering {name!r} was successful.'
        description = None

    return Embed(title, description)
Example #26
0
async def welcome_screen_(client, event):
    """Shows the guild's welcome screen."""
    guild = event.guild
    if guild is None:
        abort('Guild only command.')

    if guild not in client.guild_profiles:
        abort('I must be in the guild to execute this command')

    yield

    welcome_screen = await client.welcome_screen_get(guild)
    if welcome_screen is None:
        yield Embed(
            description=f'**{guild.name}** *has no welcome screen enabled*.')
        return

    description = welcome_screen.description
    if (description is None):
        description = '*TOP THINGS TO DO HERE*'
    else:
        description = f'{welcome_screen.description}\n\n*TOP THINGS TO DO HERE*'

    embed = Embed(f'Welcome to **{guild.name}**', description)

    icon_url = guild.icon_url
    if (icon_url is not None):
        embed.add_thumbnail(icon_url)

    welcome_channels = welcome_screen.welcome_channels
    if (welcome_channels is not None):
        for welcome_channel in welcome_channels:
            embed.add_field(
                f'{welcome_channel.emoji:e} {welcome_channel.description}',
                f'#{welcome_channel.channel:d}')

    yield embed
Example #27
0
async def guild_icon(
    event,
    choice: (GUILD_ICON_CHOICES, 'Which icon of the guild?') = 'icon',
):
    """Shows the guild's icon."""
    guild = event.guild
    if (guild is None) or guild.partial:
        abort(
            'The command unavailable in guilds, where the application\'s bot is not in.'
        )

    if choice == 'icon':
        name = 'icon'
        url = guild.icon_url_as(size=4096)
        hash_value = guild.icon_hash
        components = GUILD_ICON_ICON_COMPONENTS

    elif choice == 'banner':
        name = 'banner'
        url = guild.banner_url_as(size=4096)
        hash_value = guild.banner_hash
        components = GUILD_ICON_BANNER_COMPONENTS

    elif choice == 'discovery_splash':
        name = 'discovery splash'
        url = guild.discovery_splash_url_as(size=4096)
        hash_value = guild.discovery_splash_hash
        components = GUILD_ICON_ICON_COMPONENTS

    else:
        name = 'invite splash'
        url = guild.invite_splash_url_as(size=4096)
        hash_value = guild.invite_splash_hash
        components = GUILD_ICON_DISCOVERY_SPLASH_COMPONENTS

    embed = create_embed(guild, name, url, hash_value)
    return InteractionResponse(embed=embed, components=components)
Example #28
0
async def invite_create(
    client,
    event,
    permanent: ('bool', 'Create permanent?') = False,
):
    """I create an invite for you!"""
    guild = event.guild
    if guild is None:
        abort('Guild only command')

    if guild not in client.guild_profiles:
        abort('I must be in the guild to execute the command.')

    if not event.user_permissions.can_create_instant_invite:
        abort(
            'You must have `create instant invite` permission to invoke this command.'
        )

    if not guild.cached_permissions_for(client).can_create_instant_invite:
        abort(
            'I must have `create instant invite` permission invite to execute this command.'
        )

    yield

    if permanent:
        invite = await client.vanity_invite_get(guild)
        if invite is None:
            max_age = 0
            max_uses = 0
    else:
        invite = None
        max_age = 21600
        max_uses = 1

    if invite is None:
        yield
        invite = await client.invite_create_preferred(guild,
                                                      max_age=max_age,
                                                      max_uses=max_uses)

    if invite is None:
        content = '**Error**\nI do not have enough permission to create invite from the guild\'s preferred channel.'
    else:
        content = invite.url

    yield content
Example #29
0
async def play(
    client,
    event,
    song_name: ('str', 'The name of the song to play'),
):
    """Play a song."""
    guild = event.guild
    if (guild is None):
        abort('You need to be in a voice channel!')
    
    user = event.user
    state = guild.voice_states.get(event.user.id, None)
    if state is None:
        abort('You need to be in a voice channel!')
    
    if not is_url(song_name):
        song_name = f'ytsearch:{song_name}'
    
    yield
    
    result = await client.solarlink.get_tracks(song_name)
    
    # No result?
    if result is None:
        track = None
    
    else:
        tracks = result.tracks
        
        # If we received a playlist, it can be empty as well
        if tracks:
            selected_track_index = result.selected_track_index
            if selected_track_index == -1:
                selected_track_index = 0
            
            track = tracks[selected_track_index]
        
        else:
            # It is empty
            track = None
    
    if track is None:
        abort('No songs found. Please try again!')
    
    player = client.solarlink.get_player(event.guild_id)
    if player is None:
        player = await client.solarlink.join_voice(state.channel)
    
    await player.append(track, user=user)
    
    yield (
        f'Track added to queue!\n'
        f'- Name: [{track.title}]({track.url})\n'
        f'- Duration: {duration_to_string(track.duration)}'
    )
Example #30
0
async def in_role(
    client,
    event,
    role_1: ('role', 'Select a role.'),
    role_2: ('role', 'Double role!') = None,
    role_3: ('role', 'Triple role!') = None,
    role_4: ('role', 'Quadra role!') = None,
    role_5: ('role', 'Penta role!') = None,
    role_6: ('role', 'Epic!') = None,
    role_7: ('role', 'Legendary!') = None,
    role_8: ('role', 'Mythical!') = None,
    role_9: ('role', 'Lunatic!') = None,
):
    """Shows the users with the given roles."""
    guild = event.guild
    if guild is None:
        abort('Guild only command.')

    if guild not in client.guild_profiles:
        abort('I must be in the guild to do this.')

    roles = set()
    for role in role_1, role_2, role_3, role_4, role_5, role_6, role_7, role_8, role_9:
        if role is None:
            continue

        if role.guild is guild:
            roles.add(role)
            continue

        abort(f'Role {role.name}, [{role.id}] is bound to an other guild.')

    users = []
    for user in guild.users.values():
        try:
            guild_profile = user.guild_profiles[guild]
        except KeyError:
            continue

        guild_profile_roles = guild_profile.roles
        if guild_profile_roles is None:
            continue

        if not roles.issubset(guild_profile_roles):
            continue

        users.append(user)

    pages = InRolePageGetter(users, guild, roles)

    await Pagination(client,
                     event,
                     pages,
                     check=partial_func(in_role_pagination_check, event.user))