コード例 #1
0
ファイル: core.py プロジェクト: kdanthony/spirit
    async def on_ready(self):
        """Display startup information"""
        print('Spirit v{}'.format(constants.VERSION))
        print('Username: {}'.format(self.bot.user.name))
        print('------')

        # Get guilds in database
        with DBase() as db:
            db = db.get_guilds()
        db_guilds = []
        to_delete = []
        for row in db:
            guild = self.bot.get_guild(row[0])
            if guild:
                db_guilds.append(guild)
            else:
                to_delete.append(row[0])

        # Add guilds
        for guild in self.bot.guilds:
            if guild not in db_guilds:
                with DBase() as db:
                    db.add_guild(guild.id)

        # Remove guilds
        for guild_id in to_delete:
            with DBase() as db:
                db.remove_guild(guild_id)
コード例 #2
0
ファイル: settings.py プロジェクト: kdanthony/spirit
    async def seteventrole(self, ctx, *, event_role):
        """Set the lowest role that is able to create events (Manage Server only)

        By default, creating events requires the user to have Administrator permissions.
        But if an event role is set, then any user that is of the event role or higher may
        create events.

        **Note:** Mentioning the role directly with this command will not work. You must provide
        only the name of the role without mentioning it. The role name is also case sensitive!
        """
        manager = MessageManager(self.bot, ctx.author, ctx.channel,
                                 [ctx.message])

        guild_event_role = None
        for role in ctx.guild.roles:
            if role.name in (event_role, "@{}".format(event_role)):
                guild_event_role = role

        if not guild_event_role:
            await manager.say(
                "I couldn't find a role called **{}** on this server.\n".
                format(event_role) +
                "Note that you must provide only the name of the role. " +
                "Mentioning it with the @ sign won't work. The role name is also "
                + "case sensitive!")
            return await manager.clear()

        with DBase() as db:
            db.set_event_role_id(ctx.guild.id, guild_event_role.id)

        await manager.say("The event role has been set to: **{}**".format(
            format_role_name(guild_event_role)))
        return await manager.clear()
コード例 #3
0
ファイル: settings.py プロジェクト: kdanthony/spirit
    async def togglecleanup(self, ctx):
        """
        Toggle command message cleanup on/off (Manage Server only)

        When enabled, command message spam will be deleted a few seconds
        after a command has been invoked. This feature is designed to
        keep bot related spam to a minimum. Only non important messages will
        be deleted if this is enabled; messages like the help message or the
        roster, for example, will not be removed.
        """
        manager = MessageManager(self.bot, ctx.author, ctx.channel,
                                 [ctx.message])

        with DBase() as db:
            db.toggle_cleanup(ctx.guild.id)
            rows = db.get_cleanup(ctx.guild.id)

        if len(rows) and len(rows[0]):
            cleanup = rows[0][0]
        else:
            raise ValueError("Could not retrieve 'cleanup' from database")

        status = 'enabled' if cleanup else 'disabled'
        await manager.say("Command message cleanup is now *{}*".format(status))
        return await manager.clear()
コード例 #4
0
ファイル: events.py プロジェクト: kdanthony/spirit
    async def set_attendance(self, member, guild, attending, title, message):
        """Send updated event attendance info to db and update the event"""
        with DBase() as db:
            db.add_user(member.id)
            db.update_attendance(member.id, guild.id, attending, title, datetime.now())

        with DBase() as db:
            rows = db.get_event(guild.id, title)
        if len(rows) and len(rows[0]):
            event = rows[0]
        else:
            raise ValueError("Could not retrieve event")
            return

        # Update event message in place for a more seamless user experience
        event_embed = self.create_event_embed(guild, event[0], event[1], event[2], event[3], event[4], event[5], event[6], event[7])
        await message.edit(embed=event_embed)
コード例 #5
0
 def __init__(self, token, bungie_api_key, bungie_client_id):
     super().__init__(command_prefix=_prefix_callable)
     self.token = token
     self.db = DBase('credentials.json')
     self.destiny = pydest.Pydest(bungie_api_key)
     self.bungie_client_id = bungie_client_id
     self.uptime = datetime.datetime.utcnow()
     self.command_count = 0
コード例 #6
0
def get_event_role(guild):
    """Return the event role, if it exists"""
    with DBase() as db:
        rows = db.get_event_role_id(guild.id)

    if len(rows) and len(rows[0]):
        for role in guild.roles:
            if role.id == rows[0][0]:
                return role
コード例 #7
0
ファイル: roster.py プロジェクト: kdanthony/spirit
    async def show(self, ctx):
        """
        Display the roster

        The roster includes the name, Destiny 2 class,
        and timezone of server members. Note that only
        users who have set a role or timezone will be
        displayed on the roster.
        """
        manager = MessageManager(self.bot, ctx.author, ctx.channel, [ctx.message])
        roster_groups = []

        with DBase() as db:
            roster = db.get_roster(ctx.guild.id)

        if len(roster) != 0:

            text = "```\n"
            for row in roster:

                # Add a single entry to the roster message
                member = ctx.guild.get_member(row[0])
                if member:
                    name = member.display_name
                    formatted_name = (name[:16] + '..') if len(name) > 16 else name
                    role = row[1] if row[1] else "---"
                    time_zone = row[2] if row[2] else "---"
                    text += '{:18} {:6} {:7}\n'.format(formatted_name, time_zone, role)

                # If the message is too big, place it into a group
                if len(text) > 2000:
                    text += "```"
                    roster_groups.append(text)
                    text = "```\n"

            # Add any remaining entries into a roster group
            if len(text) > 5:
                text += "```"
                roster_groups.append(text)

            # Send the initial roster message
            embed_msg = discord.Embed(color=constants.BLUE)
            embed_msg.title="{} Roster".format(ctx.guild.name)
            embed_msg.description = roster_groups[0]
            await manager.say(embed_msg, embed=True, delete=False)

            # Send additional roster messages if the roster is too long
            for group in roster_groups[1:]:
                embed_msg = discord.Embed(color=constants.BLUE)
                embed_msg.title="{} Roster (continued)".format(ctx.guild.name)
                embed_msg.description = group
                await manager.say(embed_msg, embed=True, delete=False)

        else:
            await manager.say("No roster exists yet. Use '{}roster settimezone' or '{}roster ".format(ctx.prefix, ctx.prefix)
                            + "setclass' to add the first entry!")
        await manager.clear()
コード例 #8
0
ファイル: spirit.py プロジェクト: kdanthony/spirit
async def _prefix_callable(bot, message):
    """Get current command prefix"""
    base = ['<@{}> '.format(bot.user.id)]
    if isinstance(message.channel, discord.abc.PrivateChannel):
        base.append('!')
    else:
        with DBase() as db:
            custom_prefix = db.get_prefix(message.guild.id)
            if len(custom_prefix) > 0 and len(custom_prefix[0]) > 0:
                base.append(custom_prefix[0][0])
    return base
コード例 #9
0
ファイル: events.py プロジェクト: kdanthony/spirit
    async def delete_event(self, guild, title, member, channel):
        """Delete an event and update the events channel on success"""
        event_delete_role = get_event_delete_role(guild)
        with DBase() as db:
            rows = db.get_event_creator(guild.id, title)

        if len(rows) and len(rows[0]):
            creator_id = rows[0][0]

        if member.permissions_in(channel).manage_guild or (member.id == creator_id) or (event_delete_role and member.top_role >= event_delete_role):
            with DBase() as db:
                deleted = db.delete_event(guild.id, title)
            if deleted:
                await self.list_events(guild)
                return True
        else:
            try:
                await member.send("You don't have permission to delete that event.")
            except:
                pass
コード例 #10
0
ファイル: events.py プロジェクト: kdanthony/spirit
 async def list_events(self, guild):
     """Clear the event channel and display all upcoming events"""
     events_channel = await self.get_events_channel(guild)
     await events_channel.purge(limit=999, check=delete_all)
     with DBase() as db:
         events = db.get_events(guild.id)
     if len(events) > 0:
         for row in events:
             event_embed = self.create_event_embed(guild, row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7])
             msg = await events_channel.send(embed=event_embed)
             await msg.add_reaction("\N{WHITE HEAVY CHECK MARK}")
             await msg.add_reaction("\N{CROSS MARK}")
     else:
         await events_channel.send("There are no upcoming events.")
コード例 #11
0
ファイル: settings.py プロジェクト: kdanthony/spirit
    async def setprefix(self, ctx, new_prefix):
        """
        Change the server's command prefix (Manage Server only)
        """
        manager = MessageManager(self.bot, ctx.author, ctx.channel,
                                 [ctx.message])

        if len(new_prefix) > 5:
            await manager.say("Prefix must be less than 6 characters.")
            return await manager.clear()

        with DBase() as db:
            db.set_prefix(ctx.guild.id, new_prefix)
        await manager.say("Command prefix has been changed to " + new_prefix)
        return await manager.clear()
コード例 #12
0
ファイル: roster.py プロジェクト: kdanthony/spirit
    async def setclass(self, ctx, role):
        """
        Add your Destiny 2 class to the roster

        Class must be one of Titan, Warlock, or Hunter
        """
        manager = MessageManager(self.bot, ctx.author, ctx.channel, [ctx.message])

        role = role.lower().title()
        if role == "Titan" or role == "Warlock" or role == "Hunter":
            with DBase() as db:
                db.add_user(ctx.author.id)
                db.update_role(ctx.author.id, role, ctx.guild.id)
            await manager.say("Your class has been updated!")
        else:
            await manager.say("Class must be one of: Titan, Hunter, Warlock")
        await manager.clear()
コード例 #13
0
ファイル: messages.py プロジェクト: kdanthony/spirit
    async def clear(self):
        """Delete messages marked for deletion"""
        def check(message):
            if (message.author in (self.user, self.bot.user)
                and message.id in [m.id for m in self.messages]):
                return True

        if not isinstance(self.channel, discord.abc.PrivateChannel):
            with DBase() as db:
                rows = db.get_cleanup(self.channel.guild.id)

            if len(rows) and len(rows[0]):
                cleanup = rows[0][0]
            else:
                raise ValueError("Could not retrieve 'cleanup' from database")

            if cleanup:
                await asyncio.sleep(constants.SPAM_DELAY)
                await self.channel.purge(limit=999, check=check)
コード例 #14
0
ファイル: roster.py プロジェクト: kdanthony/spirit
    async def settimezone(self, ctx, *, time_zone):
        """
        Add your timezone to the roster

        For a full list of supported timezones, check out the bot's support server
        """
        manager = MessageManager(self.bot, ctx.author, ctx.channel, [ctx.message])
        time_zone = time_zone.upper()
        time_zone = "".join(time_zone.split())

        if time_zone in constants.TIME_ZONES:
            with DBase() as db:
                db.add_user(ctx.author.id)
                db.update_timezone(ctx.author.id, time_zone, ctx.guild.id)
            await manager.say("Your time zone has been updated!")
        else:
            await manager.say("Unsupported time zone. For a list of supported timezones, "
                            + "check out the bot's support server.")
        await manager.clear()
コード例 #15
0
ファイル: destiny.py プロジェクト: kdanthony/spirit
    async def loadout(self, ctx):
        """Display your last played character's loadout

        In order to use this command, you must first register your Destiny 2 account with the bot
        via the register command.
        """
        manager = MessageManager(self.bot, ctx.author, ctx.channel,
                                 [ctx.message])
        await ctx.channel.trigger_typing()

        # Check if user has registered their D2 account with the bot
        with DBase() as db:
            entries = db.get_d2_info(ctx.author.id)
        if len(entries
               ) > 0 and entries[0][0] != None and entries[0][1] != None:
            platform = entries[0][0]
            membership_id = entries[0][1]
        else:
            await manager.say(
                "You must first register your Destiny 2 account with the " +
                "`{}register` command.".format(ctx.prefix))
            return await manager.clear()

        res = await self.destiny.api.get_profile(
            platform, membership_id,
            ['characters', 'characterEquipment', 'profiles'])

        if res['ErrorCode'] != 1:
            await manager.say(
                "Sorry, I can't seem to retrive your Guardian right now.")
            return await manager.clear()

        # Determine which character was last played
        chars_last_played = []
        for character_id in res['Response']['characters']['data']:
            last_played_str = res['Response']['characters']['data'][
                character_id]['dateLastPlayed']
            date_format = '%Y-%m-%dT%H:%M:%SZ'
            last_played = datetime.strptime(last_played_str, date_format)
            chars_last_played.append((character_id, last_played))
        last_played_char_id = max(chars_last_played, key=lambda t: t[1])[0]
        last_played_char = res['Response']['characters']['data'].get(
            last_played_char_id)

        #######################################
        # ------ Decode Character Info ------ #
        #######################################

        role_dict = await self.destiny.decode_hash(
            last_played_char['classHash'], 'DestinyClassDefinition')
        role = role_dict['displayProperties']['name']

        gender_dict = await self.destiny.decode_hash(
            last_played_char['genderHash'], 'DestinyGenderDefinition')
        gender = gender_dict['displayProperties']['name']

        race_dict = await self.destiny.decode_hash(
            last_played_char['raceHash'], 'DestinyRaceDefinition')
        race = race_dict['displayProperties']['name']

        char_name = res['Response']['profile']['data']['userInfo'][
            'displayName']
        level = last_played_char['levelProgression']['level']
        light = last_played_char['light']
        emblem_url = 'https://www.bungie.net' + last_played_char['emblemPath']

        stats = []
        for stat_hash in ('2996146975', '392767087', '1943323491'):
            stat_dict = await self.destiny.decode_hash(
                stat_hash, 'DestinyStatDefinition')
            stat_name = stat_dict['displayProperties']['name']
            if stat_hash in last_played_char['stats'].keys():
                stats.append(
                    (stat_name, last_played_char['stats'].get(stat_hash)))
            else:
                stats.append((stat_name, 0))

        #######################################
        # ------ Decode Equipment Info ------ #
        #######################################

        weapons = [['Kinetic', '-'], ['Energy', '-'], ['Power', '-']]
        weapons_index = 0

        armor = [['Helmet', '-'], ['Gauntlets', '-'], ['Chest', '-'],
                 ['Legs', '-'], ['Class Item', '-']]
        armor_index = 0

        equipped_items = res['Response']['characterEquipment']['data'][
            last_played_char_id]['items']
        for item in equipped_items:

            item_dict = await self.destiny.decode_hash(
                item['itemHash'], 'DestinyInventoryItemDefinition')
            item_name = "{}".format(item_dict['displayProperties']['name'])

            if weapons_index < 3:
                weapons[weapons_index][1] = item_name
                weapons_index += 1

            elif armor_index < 5:
                armor[armor_index][1] = item_name
                armor_index += 1

        #################################
        # ------ Formulate Embed ------ #
        #################################

        char_info = "Level {} {} {} {}  |\N{SMALL BLUE DIAMOND}{}\n".format(
            level, race, gender, role, light)
        char_info += "{} {}  • ".format(stats[0][1], stats[0][0])
        char_info += "{} {}  • ".format(stats[1][1], stats[1][0])
        char_info += "{} {}".format(stats[2][1], stats[2][0])

        weapons_info = ""
        for weapon in weapons:
            weapons_info += '**{}:** {}  \n'.format(weapon[0], weapon[1])

        armor_info = ""
        for item in armor:
            armor_info += '**{}:** {}\n'.format(item[0], item[1])

        e = discord.Embed(colour=constants.BLUE)
        e.set_author(name=char_name,
                     icon_url=constants.PLATFORM_URLS.get(platform))
        e.description = char_info
        e.set_thumbnail(url=emblem_url)
        e.add_field(name='Weapons', value=weapons_info, inline=True)
        e.add_field(name='Armor', value=armor_info, inline=True)

        await manager.say(e, embed=True, delete=False)
        await manager.clear()
コード例 #16
0
ファイル: events.py プロジェクト: kdanthony/spirit
    async def event(self, ctx):
        """
        Create an event in the events channel

        After invoking the event command, the bot will ask
        you to enter the event details. Once the event
        is created, it will appear in the upcoming-events
        channel. The upcoming-events channel is designed with
        the assumption that it isn't used for anything but
        displaying events; non event messages will be deleted.

        Users will be able to accept and decline the
        event by adding reactions. If a maximum number
        of attendees is set and the event is full,
        additional attendees will be placed in a standby
        section. If a spot opens up, the user at the
        top of the standby section will be automatically
        moved into the event.

        By default, everyone can make events. However, a minimum role
        requirement to create events can be defined in the settings.
        See `help settings seteventrole` for more information.

        The event creator and those with the Manage Sever permission
        can delete events by reacting to the event message with \U0001f480.
        """
        manager = MessageManager(self.bot, ctx.author, ctx.channel, [ctx.message])
        event_role = get_event_role(ctx.guild)
        member_permissions = ctx.author.permissions_in(ctx.channel)

        if event_role:
            if ctx.author.top_role < event_role:
                event_role_str = format_role_name(event_role)
                await manager.say("You must be of role **{}** or higher to do that.".format(event_role))
                return await manager.clear()

        await manager.say('Event creation instructions have been messaged to you')

        res = await manager.say_and_wait("Enter event title:", dm=True)
        if not res:
            return await manager.clear()
        title = res.content

        description = ""
        res = await manager.say_and_wait("Enter event description (type 'none' for no description):", dm=True)
        if not res:
            return await manager.clear()
        if res.content.upper() != 'NONE':
            description = res.content

        max_members = 0
        while not max_members:
            res = await manager.say_and_wait("Enter the maximum numbers of attendees (type 'none' for no maximum):", dm=True)
            if not res:
                return await manager.clear()
            if res.content.upper() == 'NONE':
                break
            elif is_int(res.content) and int(res.content) > 0:
                max_members = int(res.content)
            else:
                await manager.say("That is not a a valid entry.", dm=True)

        start_time = None
        while not start_time:
            res = await manager.say_and_wait("Enter event time (YYYY-MM-DD HH:MM AM/PM):", dm=True)
            if not res:
                return await manager.clear()
            start_time_format = '%Y-%m-%d %I:%M %p'
            try:
                start_time = datetime.strptime(res.content, start_time_format)
            except ValueError:
                await manager.say("Invalid event time!", dm=True)

        time_zone = None
        while not time_zone:
            res = await manager.say_and_wait("Enter the time zone (PST, EST, etc):", dm=True)
            if not res:
                return await manager.clear()
            user_timezone = "".join(res.content.upper().split())
            if user_timezone not in constants.TIME_ZONES:
                await manager.say("Unsupported time zone", dm=True)
            else:
                time_zone = user_timezone

        with DBase() as db:
            affected_rows = db.create_event(title, start_time, time_zone, ctx.guild.id, description, max_members, ctx.author.id)
        if affected_rows == 0:
            await manager.say("An event with that name already exists!", dm=True)
            return await manager.clear()

        event_channel = await self.get_events_channel(ctx.guild)
        await manager.say("Event created! The " + event_channel.mention + " channel will be updated momentarily.", dm=True)
        await self.list_events(ctx.guild)
        await manager.clear()
コード例 #17
0
ファイル: core.py プロジェクト: kdanthony/spirit
 async def on_guild_join(self, guild):
     """Add guild and it's members to database"""
     with DBase() as db:
         db.add_guild(guild.id)
コード例 #18
0
ファイル: core.py プロジェクト: kdanthony/spirit
 async def on_guild_remove(self, guild):
     """Remove guild from database"""
     with DBase() as db:
         db.remove_guild(guild.id)
コード例 #19
0
ファイル: core.py プロジェクト: kdanthony/spirit
 async def on_member_remove(self, user):
     """Remove user from database when they leave the guild"""
     with DBase() as db:
         db.remove_user(user.id)
コード例 #20
0
 def __init__(self, token):
     super().__init__(command_prefix=_prefix_callable)
     self.token = token
     self.db = DBase('credentials.json')
     self.uptime = datetime.datetime.utcnow()
     self.command_count = 0
コード例 #21
0
    async def help(self, ctx, str_cmd=None, str_subcmd=None):
        """Display command information"""
        manager = MessageManager(self.bot, ctx.author, ctx.channel,
                                 [ctx.message])

        # Determine which prefix to display in the help message
        if isinstance(ctx.channel, discord.abc.PrivateChannel):
            prefix = '!'
        else:
            # Don't want to display @botname as the prefix in the help message
            # It's way too long!
            if ctx.prefix != '<@{}> '.format(self.bot.user.id):
                prefix = ctx.prefix
            else:
                with DBase() as db:
                    custom_prefix = db.get_prefix(ctx.guild.id)
                    if len(custom_prefix) > 0 and len(custom_prefix[0]) > 0:
                        prefix = custom_prefix[0][0]
                    else:
                        raise AttributeError(
                            "Could not retrieve command prefix")

        # User passed a command and a subcommand
        if str_cmd and str_subcmd:
            cmd = self.bot.get_command(str_cmd)
            if cmd is None:
                await manager.say(
                    "There are no commands called '{}'".format(str_cmd))
                return await manager.clear()

            # Check for subcommand
            if hasattr(cmd, 'commands'):
                for sub_cmd in cmd.commands:
                    if sub_cmd.name == str_subcmd:
                        help = self.help_embed_single(prefix, sub_cmd)
                        await manager.say(help, embed=True, delete=False)
                        break
                else:
                    await manager.say(
                        "'{}' doesn't have a subcommand called '{}'".format(
                            str_cmd, str_subcmd))
            else:
                await manager.say(
                    "'{}' does not have any subcommands".format(str_cmd))

        # User passed in a single command
        elif str_cmd:
            cmd = self.bot.get_command(str_cmd)
            if cmd is None:
                await manager.say(
                    "There are no commands called '{}'".format(str_cmd))
                return await manager.clear()

            # Check if command has subcommands
            if hasattr(cmd, 'commands'):
                sub_cmds = []
                for sub_cmd in cmd.commands:
                    sub_cmds.append(sub_cmd)
                help = self.help_embed_group(prefix, cmd, sub_cmds)
            else:
                help = self.help_embed_single(prefix, cmd)
            await manager.say(help, embed=True, delete=False)

        # No command passed, print help for all commands
        else:
            help = self.help_embed_all(prefix, self.bot.commands)
            await manager.say(help, embed=True, delete=False)

        await manager.clear()
コード例 #22
0
ファイル: destiny.py プロジェクト: kdanthony/spirit
    async def register(self, ctx):
        """Register your Destiny 2 account with the bot

        This command will let the bot know which Destiny 2 profile to associate with your Discord
        profile. Registering is a prerequisite to using any commands to require knowledge of your
        public Destiny 2 profile.
        """
        manager = MessageManager(self.bot, ctx.author, ctx.channel,
                                 [ctx.message])

        if not isinstance(ctx.channel, discord.abc.PrivateChannel):
            await manager.say(
                "Registration instructions have been messaged to you")

        await manager.say(
            "Registering your Destiny 2 account with me will allow " +
            "you to invoke commands that use information from your " +
            "public Destiny 2 profile.",
            dm=True)

        platform = None
        while not platform:
            res = await manager.say_and_wait(
                "Enter your platform (**xbox** or **playstation**):", dm=True)
            if not res:
                return await manager.clear()
            platforms = {'PC': 4, 'XBOX': 1, 'PLAYSTATION': 2}
            platform = platforms.get(res.content.upper())
            if not platform:
                await manager.say("Invalid platform. Try again.", dm=True)

        act = await manager.say_and_wait("Enter your exact **account name**:",
                                         dm=True)
        if not act:
            return await manager.clear()

        try:
            res = await self.destiny.api.search_destiny_player(
                platform, act.content)
        except ValueError as e:
            await manager.say(
                "Invalid account name. If this seems wrong, please contact the developer."
            )
            return await manager.clear()

        act_exists = False
        if res['ErrorCode'] == 1 and len(res['Response']) == 1:
            act_exists = True
            membership_id = res['Response'][0]['membershipId']
        elif res['ErrorCode'] == 1 and len(res['Response']) > 1:
            for entry in res['Response']:
                if act.content == entry['displayName']:
                    act_exists = True
                    membership_id = entry['membershipId']
                    break

        if not act_exists:
            await manager.say(
                "An account with that name doesn't seem to exist.", dm=True)
        else:
            await manager.say("Account successfully registered!", dm=True)
            with DBase() as db:
                db.add_user(ctx.author.id)
                db.update_registration(platform, membership_id, ctx.author.id)

        return await manager.clear()