Exemplo n.º 1
0
    async def pve(self, ctx, username=None, platform=None):
        """Display PvE stats across all characters on an account

        In order to use this command for your own account, you must first register your Destiny 2
        account with the bot via the register command.

        `stats pve` - Display your PvE stats (preferred platform)
        \$`stats pve Asal#1502 bnet` - Display Asal's PvE stats on Battle.net
        \$`stats pve @user` - Display a registered user's PvE stats (preferred platform)
        \$`stats pve @user bnet` - Display a registered user's PvE stats on Battle.net
        """
        manager = MessageManager(ctx)
        await ctx.channel.trigger_typing()

        # Get membership details. This depends on whether or not a platform or username were given.
        membership_details = await helpers.get_membership_details(self.bot, ctx, username, platform)

        # If there was an error getting membership details, display it
        if isinstance(membership_details, str):
            await manager.send_message(membership_details)
            return await manager.clean_messages()
        else:
            platform_id, membership_id, display_name = membership_details

        pve_stats_json = await self.get_stats(platform_id, membership_id, [7,4,16,17,18,46,47])
        if not pve_stats_json:
            await manager.send_message("Sorry, I can't seem to retrieve those stats right now")
            return await manager.clean_messages()

        pve_stats = PvEStats(pve_stats_json) 
        await manager.send_embed(pve_stats_embed(pve_stats, display_name, platform_id))
        await manager.clean_messages()
Exemplo n.º 2
0
    async def pm(self, ctx, user_id: int, *message):
        """Send a PM via the bot to a user given their ID"""
        manager = MessageManager(ctx)
        user = self.bot.get_user(user_id)

        if ctx.author.id not in constants.MODS:
            return

        if len(message) == 0:
            await manager.send_message("You forgot to include your message!")
            return await manager.clean_messages()

        response = "You have received a message from my developer:\n\n**"
        for word in message:
            response += "{} ".format(word)
        response += (
            "**\n\nYour response will not be tracked here. If you wish " +
            "to speak with him further, join the official **{} Support** " +
            "server - https://discord.gg/GXCFpkr").format(self.bot.user.name)

        try:
            await user.send(response)
        except:
            await manager.send_message(
                'Could not PM user with ID {}'.format(user_id))
        else:
            await manager.send_message('PM successfully sent.')
        await manager.clean_messages()
Exemplo n.º 3
0
    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 Manage Server 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(ctx)

        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.send_message(
                "I couldn't find a role called **{}** on this server.\n\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.clean_messages()

        self.bot.db.set_event_role_id(ctx.guild.id, guild_event_role.id)
        await manager.send_message(
            "The event role has been set to: **{}**".format(
                format_role_name(guild_event_role)))
        return await manager.clean_messages()
Exemplo n.º 4
0
    async def trials(self, ctx, username=None, platform=None):
        """Display Trials stats across all characters on an account

        In order to use this command for your own account, you must first register your Destiny 2
        account with the bot via the register command.

        `stats trials` - Display your Trials stats (preferred platform)
        \$`stats trials Asal#1502 bnet` - Display Asal's Trials stats on Battle.net
        \$`stats trials @user` - Display a registered user's Trials stats (preferred platform)
        \$`stats trials @user bnet` - Display a registered user's Trials stats on Battle.net
        """
        manager = MessageManager(ctx)
        await ctx.channel.trigger_typing()

        # Get membership details. This depends on whether or not a platform or username were given.
        membership_details = await helpers.get_membership_details(self.bot, ctx, username, platform)

        # If there was an error getting membership details, display it
        if isinstance(membership_details, str):
            await manager.send_message(membership_details)
            return await manager.clean_messages()
        else:
            platform_id, membership_id, display_name = membership_details

        trials_stats_json = (await self.get_stats(platform_id, membership_id, [39]))['trialsofthenine'].get('allTime')
        if not trials_stats_json:
            await manager.send_message("Sorry, I can't seem to retrieve those stats right now")
            return await manager.clean_messages()

        trials_stats = PvPStats(trials_stats_json)
        await manager.send_embed(pvp_stats_embed(trials_stats, "Trials of the Nine Stats", display_name, platform_id))
        await manager.clean_messages()
Exemplo n.º 5
0
    async def donate(self, ctx):
        """Support the continued development of Spirit!"""
        manager = MessageManager(ctx)
        e = discord.Embed(colour=constants.BLUE)

        text = (
            "Spirit is a work of love that has taken countless hours to develop. Your donation "
            +
            "will go towards server hosting costs, development tools, and if you donate "
            +
            "monthly, will also earn you some special privelges on the Spirit Discord server!\n\n"
            + "Donate once: https://www.paypal.me/spiritbot\n" +
            "Donate monthly: https://www.patreon.com/spiritbot")
        reward_1 = "- Colored name on the Spirit Discord server"
        reward_2 = (
            "- Patron role and colored name on the Spirit Discord server\n" +
            "- Access to the developer blog on Patreon and the Spirit Discord server\n"
            +
            "- Access to a patron only channel on the Spirit Discord server which includes sneak peeks of new features!"
        )
        reward_3 = ("- All rewards from the previous tier\n" +
                    "- Your own personalized message built right into Spirit!")

        e.description = text
        e.add_field(name="$1/Month", value=reward_1)
        e.add_field(name="$5/Month", value=reward_2)
        e.add_field(name="$10/Month", value=reward_3)

        await manager.send_embed(e)
        await manager.clean_messages()
Exemplo n.º 6
0
    async def on_command_error(self, ctx, error):
        """Command error handler"""
        manager = MessageManager(ctx)

        if isinstance(error, commands.CommandNotFound):
            pass

        elif isinstance(error, commands.MissingRequiredArgument):
            pass

        elif isinstance(error, commands.NotOwner):
            pass

        elif isinstance(error, commands.NoPrivateMessage):
            await manager.send_message("You can't use that command in a private message")

        elif isinstance(error, commands.CheckFailure):
            await manager.send_message("You don't have the required permissions to do that")

        elif isinstance(error, commands.CommandOnCooldown):
            await manager.send_message(error)

        # Non Discord.py errors
        elif isinstance(error, commands.CommandInvokeError):
            if isinstance(error.original, discord.errors.Forbidden):
                pass
            elif isinstance(error.original, asyncio.TimeoutError):
                await manager.send_private_message("I'm not sure where you went. We can try this again later.")
            else:
                raise error

        else:
            raise error

        await manager.clean_messages()
Exemplo n.º 7
0
    async def about(self, ctx):
        """Display information about the bot itself

        This command was adapted from RoboDanny by Rapptz - https://www.github.com/Rapptz/RoboDanny
        """
        manager = MessageManager(ctx)
        e = discord.Embed(title='Spirit v{}'.format(constants.VERSION),
                          colour=constants.BLUE)

        e.description = (
            "[Invite Spirit](https://discordapp.com/oauth2/authorize?client_id=335084645743984641&scope=bot&permissions=523344)\n"
            + "[Spirit Support Server](https://discord.gg/GXCFpkr)")

        owner = self.bot.get_user(118926942404608003)
        e.set_author(name=str(owner), icon_url=owner.avatar_url)

        # statistics
        total_members = sum(1 for _ in self.bot.get_all_members())
        total_online = len({
            m.id
            for m in self.bot.get_all_members()
            if m.status is discord.Status.online
        })
        total_unique = len(self.bot.users)

        voice_channels = []
        text_channels = []
        for guild in self.bot.guilds:
            voice_channels.extend(guild.voice_channels)
            text_channels.extend(guild.text_channels)

        text = len(text_channels)
        voice = len(voice_channels)

        e.add_field(name='Members',
                    value='{} total\n{} unique\n{} unique online'.format(
                        total_members, total_unique, total_online))
        e.add_field(name='Channels',
                    value='{} total\n{} text\n{} voice'.format(
                        text + voice, text, voice))

        memory_usage = "%0.2f" % (self.process.memory_full_info().uss /
                                  1024**2)
        cpu_usage = "%0.2f" % (self.process.cpu_percent() / psutil.cpu_count())
        e.add_field(name='Process',
                    value='{} MiB\n{}% CPU'.format(memory_usage, cpu_usage))

        e.add_field(name='Guilds', value=len(self.bot.guilds))
        e.add_field(name='Commands Run', value=self.bot.command_count)
        e.add_field(name='Uptime', value=self.get_bot_uptime(brief=True))

        e.set_footer(text='Made with discord.py',
                     icon_url='http://i.imgur.com/5BFecvA.png')
        await manager.send_embed(e)
        await manager.clean_messages()
Exemplo n.º 8
0
    async def seteventrole_error(self, ctx, error):
        if isinstance(error, commands.MissingRequiredArgument):
            manager = MessageManager(ctx)
            event_role = get_event_role(ctx)

            if not event_role:
                role_display = 'None (anyone can make events)'
            else:
                role_display = format_role_name(event_role)

            await manager.send_message(
                "The current event role is: **{}**\n\n".format(role_display) +
                "To change the event role, use '{}settings seteventrole <role_name>'"
                .format(ctx.prefix))
            await manager.clean_messages()
Exemplo n.º 9
0
    async def setprefix(self, ctx, new_prefix):
        """
        Change the server's command prefix (Manage Server only)
        """
        manager = MessageManager(ctx)

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

        self.bot.db.set_prefix(ctx.guild.id, new_prefix)
        await manager.send_message("Command prefix has been changed to " +
                                   new_prefix)
        return await manager.clean_messages()
Exemplo n.º 10
0
    async def seteventdeleterole_error(self, ctx, error):
        if isinstance(error, commands.MissingRequiredArgument):
            manager = MessageManager(ctx)
            event_role = get_event_delete_role(ctx)

            if not event_role:
                role_display = '**None** (only Manage Sever members can delete events)'
            else:
                role_display = format_role_name(event_role)

            await manager.send_message(
                "The current event delete role is: {}\n\n".format(
                    role_display) +
                "To change the event delete role, use '{}settings seteventdeleterole <role_name>'"
                .format(ctx.prefix))
            await manager.clean_messages()
Exemplo n.º 11
0
    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(ctx)

        role = role.lower().title()
        if role == "Titan" or role == "Warlock" or role == "Hunter":
            self.bot.db.add_user(ctx.author.id)
            self.bot.db.update_role(ctx.author.id, role, ctx.guild.id)
            await manager.send_message("Your class has been updated!")
        else:
            await manager.send_message(
                "Class must be one of: Titan, Hunter, Warlock")
        await manager.clean_messages()
Exemplo n.º 12
0
    async def broadcast(self, ctx, *, message):
        """Send a message to the owner of every server the bot belongs to"""
        manager = MessageManager(ctx)

        if ctx.author.id not in constants.OWNERS:
            return

        count = 0
        for guild in self.bot.guilds:
            try:
                await guild.owner.send(message)
            except:
                pass
            else:
                count += 1

        await manager.send_message(
            "Broadcast message sent to **{}** users".format(count))
        await manager.clean_messages()
Exemplo n.º 13
0
    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(ctx)
        time_zone = time_zone.upper()
        time_zone = "".join(time_zone.split())

        if time_zone in constants.TIME_ZONES:
            self.bot.db.add_user(ctx.author.id)
            self.bot.db.update_timezone(ctx.author.id, time_zone, ctx.guild.id)
            await manager.send_message("Your time zone has been updated!")
        else:
            await manager.send_message(
                "Unsupported time zone. For a list of supported timezones, " +
                "check out the bot's support server.")
        await manager.clean_messages()
Exemplo n.º 14
0
    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(ctx)

        self.bot.db.toggle_cleanup(ctx.guild.id)
        cleanup_status_text = 'enabled' if cleanup_is_enabled(
            ctx) else 'disabled'

        await manager.send_message(
            "Command message cleanup is now *{}*".format(cleanup_status_text))
        return await manager.clean_messages()
Exemplo n.º 15
0
    async def countdown(self, ctx):
        """Show time until upcoming Destiny 2 releases"""
        manager = MessageManager(ctx)
        pst_now = datetime.now(tz=pytz.timezone('US/Pacific'))
        text = ""

        for name, date in constants.RELEASE_DATES:
            diff = date - pst_now
            days = diff.days + 1
            if days == 0:
                text += "{}: Today!\n".format(name)
            elif days == 1:
                text += "{}: Tomorrow!\n".format(name)
            elif days > 1:
                text += "{}: {} days\n".format(name, days)

        if not text:
            text = "There are no concrete dates for our next adventure..."

        countdown = discord.Embed(title="Destiny 2 Countdown",
                                  color=constants.BLUE)
        countdown.description = text
        await manager.send_embed(countdown)
        await manager.clean_messages()
Exemplo n.º 16
0
    async def feedback(self, ctx, *, message):
        """
        Send a message to the bot's developer

        Ex. '!feedback Your bot is awesome!'

        This command was adapted from RoboDanny by Rapptz - https://www.github.com/Rapptz/RoboDanny
        """
        manager = MessageManager(ctx)

        e = discord.Embed(title='Feedback', colour=constants.BLUE)
        e.set_author(name=str(ctx.author), icon_url=ctx.author.avatar_url)
        e.description = message
        e.timestamp = ctx.message.created_at

        if ctx.guild is not None:
            e.add_field(name='Server',
                        value='{} (ID: {})'.format(ctx.guild.name,
                                                   ctx.guild.id),
                        inline=False)

        e.add_field(name='Channel',
                    value='{} (ID: {})'.format(ctx.channel, ctx.channel.id),
                    inline=False)
        e.set_footer(text='Author ID: {}'.format(ctx.author.id))

        feedback_channel = self.bot.get_channel(359848505654771715)
        if feedback_channel:
            await feedback_channel.send(embed=e)
        else:
            asal = await self.bot.get_user_info("118926942404608003")
            await asal.send(embed=e)

        await manager.send_message(
            "Your feedback has been sent to the developer!")
        await manager.clean_messages()
Exemplo n.º 17
0
 async def setprefix_error(self, ctx, error):
     if isinstance(error, commands.MissingRequiredArgument):
         manager = MessageManager(ctx)
         await manager.send_message("Oops! You didn't provide a new prefix."
                                    )
         await manager.clean_messages()
Exemplo n.º 18
0
    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(ctx)
        roster_groups = []
        roster = self.bot.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.get('user_id'))
                role = row.get('role')
                timezone = row.get('timezone')
                if member:
                    name = member.display_name
                    formatted_name = (name[:16] +
                                      '..') if len(name) > 16 else name
                    role = role if role else "---"
                    timezone = timezone if timezone else "---"
                    text += '{:18} {:6} {:7}\n'.format(formatted_name,
                                                       timezone, 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.send_embed(embed_msg)

            # 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.send_embed(embed_msg)

        else:
            await manager.send_message(
                "No roster exists yet. Use '{}roster settimezone' or '{}roster "
                .format(ctx.prefix, ctx.prefix) +
                "setclass' to add the first entry!")
        await manager.clean_messages()
Exemplo n.º 19
0
    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(ctx)
        event_role = get_event_role(ctx)
        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.send_message(
                    "You must be of role **{}** or higher to do that.".format(
                        event_role))
                return await manager.clean_messages()

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

        # Title
        await manager.send_private_message("Enter event title:")
        res = await manager.get_next_private_message()
        if not res:
            return await manager.clean_messages()
        title = res.content

        # Description
        await manager.send_private_message(
            "Enter event description (type 'none' for no description):")
        res = await manager.get_next_private_message()
        if not res:
            return await manager.clean_messages()
        if res.content.upper() != 'NONE':
            description = res.content
        else:
            description = ""

        # Number of attendees
        max_members = 0
        while not max_members:
            await manager.send_private_message(
                "Enter the maximum numbers of attendees (type 'none' for no maximum):"
            )
            res = await manager.get_next_private_message()
            if not res:
                return await manager.clean_messages()
            if res.content.upper() == 'NONE':
                break
            elif is_int(res.content) and int(res.content) in range(1, 10000):
                max_members = int(res.content)
            else:
                await manager.send_private_message(
                    "Invalid entry. Must be a number between 1 and 9999.")

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

        # Time zone
        time_zone = None
        while not time_zone:
            await manager.send_private_message(
                "Enter the time zone (PST, EST, etc):")
            res = await manager.get_next_private_message()
            if not res:
                return await manager.clean_messages()
            user_timezone = "".join(res.content.upper().split())
            if user_timezone not in constants.TIME_ZONES:
                await manager.send_private_message("Unsupported time zone")
            else:
                time_zone = user_timezone

        affected_rows = self.bot.db.create_event(title, start_time, time_zone,
                                                 ctx.guild.id, description,
                                                 max_members, ctx.author.id)
        if affected_rows == 0:
            await manager.send_private_message(
                "An event with that name already exists!")
            return await manager.clean_messages()

        event_channel = await self.get_events_channel(ctx.guild)
        await manager.send_private_message(
            "Event created! The " + event_channel.mention +
            " channel will be updated momentarily.")
        await self.list_events(ctx.guild)
        await manager.clean_messages()
Exemplo n.º 20
0
    async def help(self, ctx, str_cmd=None, str_subcmd=None):
        """Display command information"""
        manager = MessageManager(ctx)

        # 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:
                custom_prefix = self.bot.db.get_prefix(ctx.guild.id)
                if len(custom_prefix):
                    prefix = custom_prefix.get('prefix')
                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.send_message(
                    "There are no commands called '{}'".format(str_cmd))
                return await manager.clean_messages()

            # Check for subcommand
            if hasattr(cmd, 'commands'):
                for sub_cmd in cmd.commands:
                    if sub_cmd.name == str_subcmd:
                        help_embed = self.help_embed_single(prefix, sub_cmd)
                        await manager.send_embed(help_embed)
                        break
                else:
                    await manager.send_message(
                        "'{}' doesn't have a subcommand called '{}'".format(
                            str_cmd, str_subcmd))
            else:
                await manager.send_message(
                    "'{}' 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.send_message(
                    "There are no commands called '{}'".format(str_cmd))
                return await manager.clean_messages()

            # Check if command has subcommands
            if hasattr(cmd, 'commands'):
                sub_cmds = []
                for sub_cmd in cmd.commands:
                    sub_cmds.append(sub_cmd)
                help_embed = self.help_embed_group(prefix, cmd, sub_cmds)
            else:
                help_embed = self.help_embed_single(prefix, cmd)
            await manager.send_embed(help_embed)

        # No command passed, print help for all commands
        else:
            help_embed = self.help_embed_all(prefix, self.bot.commands)
            await manager.send_embed(help_embed)

        await manager.clean_messages()
Exemplo n.º 21
0
    async def pvp(self, ctx, username=None, platform=None):
        """Display PvP stats across all characters on an account

        In order to use this command for your own account, you must first register your Destiny 2
        account with the bot via the register command.

        `stats pvp` - Display your PvP stats (preferred platform)
        \$`stats pvp Asal#1502 bnet` - Display Asal's PvP stats on Battle.net
        \$`stats pvp @user` - Display a registered user's PvP stats (preferred platform)
        \$`stats pvp @user bnet` - Display a registered user's PvP stats on Battle.net
        """
        manager = MessageManager(ctx)
        await ctx.channel.trigger_typing()

        # Get membership details. This depends on whether or not a platform or username were given.
        membership_details = await helpers.get_membership_details(self.bot, ctx, username, platform)

        # If there was an error getting membership details, display it
        if isinstance(membership_details, str):
            await manager.send_message(membership_details)
            return await manager.clean_messages()
        else:
            platform_id, membership_id, display_name = membership_details

        # Get PvP stats
        try:
            res = await self.bot.destiny.api.get_historical_stats(platform_id, membership_id, groups=['general'], modes=[5])
        except:
            await manager.send_message("Sorry, I can't seem to retrieve those stats right now")
            return await manager.clean_messages()

        if res['ErrorCode'] != 1:
            await manager.send_message("Sorry, I can't seem to retrieve those stats right now")
            return await manager.clean_messages()

        pvp_stats = res['Response']['allPvP'].get('allTime')

        if not pvp_stats:
            await manager.send_message("Sorry, I can't seem to retrieve those stats right now")
            return await manager.clean_messages()

        time_played = pvp_stats['secondsPlayed']['basic']['displayValue']
        kdr = pvp_stats['killsDeathsRatio']['basic']['displayValue']
        best_weapon = pvp_stats['weaponBestType']['basic']['displayValue']
        games_played = pvp_stats['activitiesEntered']['basic']['displayValue']
        best_kills = pvp_stats['bestSingleGameKills']['basic']['displayValue']
        best_spree = pvp_stats['longestKillSpree']['basic']['displayValue']
        combat_rating = pvp_stats['combatRating']['basic']['displayValue']
        kills = pvp_stats['kills']['basic']['displayValue']
        assists = pvp_stats['assists']['basic']['displayValue']
        deaths = pvp_stats['deaths']['basic']['displayValue']
        kda = str(round((int(kills) + int(assists)) / int(deaths), 2))

        # Can't convert a string of '-' to a float!
        win_ratio = pvp_stats['winLossRatio']['basic']['displayValue']
        if win_ratio != '-':
            win_ratio = float(win_ratio)
            win_rate = str(round(win_ratio / (win_ratio + 1) * 100, 1)) + "%"
        else:
            win_rate = win_ratio

        e = discord.Embed(colour=constants.BLUE)
        e.set_author(name="{} | Crucible Stats".format(display_name), icon_url=constants.PLATFORM_URLS.get(platform_id))
        e.add_field(name='Kills', value=kills, inline=True)
        e.add_field(name='Assists', value=assists, inline=True)
        e.add_field(name='Deaths', value=deaths, inline=True)
        e.add_field(name='KD', value=kdr, inline=True)
        e.add_field(name='KA/D', value=kda, inline=True)
        e.add_field(name='Win Rate', value=win_rate, inline=True)
        e.add_field(name='Best Spree', value=best_spree, inline=True)
        e.add_field(name='Most Kills in a Game', value=best_kills, inline=True)
        e.add_field(name='Favorite Weapon', value=best_weapon, inline=True)
        e.add_field(name='Combat Rating', value=combat_rating, inline=True)
        e.add_field(name='Games Played', value=games_played, inline=True)
        e.add_field(name='Time Played', value=time_played, inline=True)

        await manager.send_embed(e)
        await manager.clean_messages()
Exemplo n.º 22
0
    async def trials(self, ctx, username=None, platform=None):
        """Display Trials stats across all characters on an account

        In order to use this command for your own account, you must first register your Destiny 2
        account with the bot via the register command.

        `stats trials` - Display your Trials stats (preferred platform)
        \$`stats trials Asal#1502 bnet` - Display Asal's Trials stats on Battle.net
        \$`stats trials @user` - Display a registered user's Trials stats (preferred platform)
        \$`stats trials @user bnet` - Display a registered user's Trials stats on Battle.net
        """
        manager = MessageManager(ctx)
        await ctx.channel.trigger_typing()

        membership_details = await helpers.get_membership_details(self.bot, ctx, username, platform)

        if isinstance(membership_details, str):
            await manager.send_message(membership_details)
            return await manager.clean_messages()

        platform_id, membership_id, display_name = membership_details

        try:
            res = await self.bot.destiny.api.get_historical_stats(platform_id, membership_id, groups=['general'], modes=[39])

            if res['ErrorCode'] != 1:
                await manager.send_message("Sorry, I can't seem to retrieve those stats right now")
                return await manager.clean_messages()

            trials_stats = res['Response']['trialsofthenine'].get('allTime')

            #| time played | KDR | best weapon | games played | most kills in sg | longest spree | combar rating | kills | assists | deaths | kda

            time_played = trials_stats['secondsPlayed']['basic']['displayValue']
            kdr = trials_stats['killsDeathsRatio']['basic']['displayValue']
            best_weapon = trials_stats['weaponBestType']['basic']['displayValue']
            games_played = trials_stats['activitiesEntered']['basic']['displayValue']
            best_kills = trials_stats['bestSingleGameKills']['basic']['displayValue']
            best_spree = trials_stats['longestKillSpree']['basic']['displayValue']
            combat_rating = trials_stats['combatRating']['basic']['displayValue']
            kills = trials_stats['kills']['basic']['displayValue']
            assists = trials_stats['assists']['basic']['displayValue']
            deaths = trials_stats['deaths']['basic']['displayValue']
            kda = str(round((int(kills) + int(assists)) /int(deaths), 2))
            win_ratio = trials_stats['winLossRatio']['basic']['displayValue']
            if win_ratio != '-':
                win_ratio = float(win_ratio)
                win_rate = str(round(win_ratio / (win_ratio + 1) * 100, 1)) + "%"
            else:
                win_rate = win_ratio

            e = discord.Embed(color=constants.BLUE)
            e.set_author(name="{} | Trials of the Nine stats".format(display_name),
            icon_url=constants.PLATFORM_URLS.get(platform_id))
            e.add_field(name='Kills', value=kills, inline=True)
            e.add_field(name='Assists', value=assists, inline=True)
            e.add_field(name='Deaths', value=deaths, inline=True)
            e.add_field(name='KD', value=kdr, inline=True)
            e.add_field(name='KA/D', value=kda, inline=True)
            e.add_field(name='Win Rate', value=win_rate, inline=True)
            e.add_field(name='Best Spree', value=best_spree, inline=True)
            e.add_field(name='Most kills in a Game', value=best_kills, inline=True)
            e.add_field(name='Favorite Weapon', value=best_weapon, inline=True)
            e.add_field(name='Combat Rating', value=combat_rating, inline=True)
            e.add_field(name='Games Played', value=games_played, inline=True)
            e.add_field(name='Time Played', value=time_played, inline=True)

            await manager.send_embed(e)
            await manager.clean_messages()

        except:
            await manager.send_message("Sorry, I can't seem to retrieve those stats right now")
            return await manager.clean_messages()
Exemplo n.º 23
0
    async def pve(self, ctx, username=None, platform=None):
        """Display PvE stats across all characters on an account

        In order to use this command for your own account, you must first register your Destiny 2
        account with the bot via the register command.

        `stats pve` - Display your PvE stats (preferred platform)
        \$`stats pve Asal#1502 bnet` - Display Asal's PvE stats on Battle.net
        \$`stats pve @user` - Display a registered user's PvE stats (preferred platform)
        \$`stats pve @user bnet` - Display a registered user's PvE stats on Battle.net
        """
        manager = MessageManager(ctx)
        await ctx.channel.trigger_typing()

        # Get membership details. This depends on whether or not a platform or username were given.
        membership_details = await helpers.get_membership_details(self.bot, ctx, username, platform)

        # If there was an error getting membership details, display it
        if isinstance(membership_details, str):
            await manager.send_message(membership_details)
            return await manager.clean_messages()
        else:
            platform_id, membership_id, display_name = membership_details

        # Get PvE stats
        try:
            res = await self.bot.destiny.api.get_historical_stats(platform_id, membership_id, groups=['general'], modes=[7,4,16,18])
        except pydest.PydestException as e:
            await manager.send_message("Sorry, I can't seem to retrieve those stats right now")
            return await manager.clean_messages()
        if res['ErrorCode'] != 1:
            await manager.send_message("Sorry, I can't seem to retrieve those stats right now")
            return await manager.clean_messages()
        pve_stats = res['Response']

        if not pve_stats:
            await manager.send_message("Sorry, I can't seem to retrieve those stats right now")
            return await manager.clean_messages()

        time_played = pve_stats['allPvE']['allTime']['totalActivityDurationSeconds']['basic']['displayValue'] if len(pve_stats['allPvE']) else 0
        best_weapon = pve_stats['allPvE']['allTime']['weaponBestType']['basic']['displayValue'] if len(pve_stats['allPvE']) else 0
        num_heroic_events = pve_stats['allPvE']['allTime']['heroicPublicEventsCompleted']['basic']['displayValue'] if len(pve_stats['allPvE']) else 0
        num_events = pve_stats['allPvE']['allTime']['publicEventsCompleted']['basic']['displayValue'] if len(pve_stats['allPvE']) else 0
        num_raids = pve_stats['raid']['allTime']['activitiesCleared']['basic']['displayValue'] if len(pve_stats['raid']) else 0
        raid_time = pve_stats['raid']['allTime']['totalActivityDurationSeconds']['basic']['displayValue'] if len(pve_stats['raid']) else 0
        num_nightfall = pve_stats['nightfall']['allTime']['activitiesCleared']['basic']['displayValue'] if len(pve_stats['nightfall']) else 0
        num_strikes = pve_stats['allStrikes']['allTime']['activitiesCleared']['basic']['displayValue'] if len(pve_stats['allStrikes']) else 0
        fastest_nightfall = pve_stats['nightfall']['allTime']['fastestCompletionMs']['basic']['displayValue'] if len(pve_stats['nightfall']) else 0
        kills = pve_stats['allPvE']['allTime']['kills']['basic']['displayValue'] if len(pve_stats['allPvE']) else 0
        assists = pve_stats['allPvE']['allTime']['assists']['basic']['displayValue'] if len(pve_stats['allPvE']) else 0
        deaths = pve_stats['allPvE']['allTime']['deaths']['basic']['displayValue'] if len(pve_stats['allPvE']) else 0

        e = discord.Embed(colour=constants.BLUE)
        e.set_author(name="{} | PvE Stats".format(display_name), icon_url=constants.PLATFORM_URLS.get(platform_id))
        e.add_field(name='Kills', value=kills, inline=True)
        e.add_field(name='Assists', value=assists, inline=True)
        e.add_field(name='Deaths', value=deaths, inline=True)
        e.add_field(name='Strikes', value=num_strikes, inline=True)
        e.add_field(name='Nightfalls', value=num_nightfall, inline=True)
        e.add_field(name='Fastest Nightfall', value=fastest_nightfall, inline=True)
        e.add_field(name='Public Events', value=num_events, inline=True)
        e.add_field(name='Heroic Public Events', value=num_heroic_events, inline=True)
        e.add_field(name='Favorite Weapon', value=best_weapon, inline=True)
        e.add_field(name='Total Raid Time', value=raid_time, inline=True)
        e.add_field(name='Raids', value=num_raids, inline=True)
        e.add_field(name='Time Played', value=time_played, inline=True)

        await manager.send_embed(e)
        await manager.clean_messages()
Exemplo n.º 24
0
 async def settimezone_error(self, ctx, error):
     if isinstance(error, commands.MissingRequiredArgument):
         manager = MessageManager(ctx)
         await manager.send_message(
             "Oops! You didn't include your timezone.")
         await manager.clean_messages()
Exemplo n.º 25
0
    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 that require knowledge of your
        Destiny 2 profile.
        """
        manager = MessageManager(ctx)
        template = "https://www.bungie.net/en/OAuth/Authorize?client_id={}&response_type=code&state={}"
        auth_url = template.format(self.bot.bungie_client_id, ctx.author.id)
        bliz_name, xbox_name, psn_name, bliz_id, xbox_id, psn_id = (None, ) * 6

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

        # Prompt user with link to Bungie.net OAuth authentication
        e = discord.Embed(colour=constants.BLUE)
        e.title = "Click Here to Register"
        e.url = auth_url
        e.description = (
            "Click the above link to register your Bungie.net account with Spirit. "
            +
            "Registering will allow Spirit to access your connected Destiny " +
            "2 accounts.")
        registration_msg = await manager.send_private_embed(e)

        # Wait for user info from the web server via Redis
        res = await self.redis.subscribe(ctx.author.id)
        tsk = asyncio.ensure_future(self.wait_for_msg(res[0]))
        try:
            user_info = await asyncio.wait_for(tsk, timeout=120)
        except asyncio.TimeoutError:
            await manager.send_private_message(
                "I'm not sure where you went. We can try this again later.")
            await registration_msg.delete()
            return await manager.clean_messages()
        await ctx.author.dm_channel.trigger_typing()

        # Save OAuth credentials and bungie ID
        bungie_id = user_info.get('membership_id')
        access_token = user_info.get('access_token')
        refresh_token = user_info.get('refresh_token')
        self.bot.db.update_registration(bungie_id, access_token, refresh_token,
                                        ctx.author.id)

        # Fetch platform specific display names and membership IDs
        try:
            res = await self.bot.destiny.api.get_membership_data_by_id(
                bungie_id)
        except:
            await manager.send_private_message(
                "I can't seem to connect to Bungie right now. Try again later."
            )
            await registration_msg.delete()
            return await manager.clean_messages()

        if res['ErrorCode'] != 1:
            await manager.send_private_message(
                "Oops, something went wrong during registration. Please try again."
            )
            await registration_msg.delete()
            return await manager.clean_messages()

        if not self.user_has_connected_accounts(res):
            await manager.send_private_message(
                "Oops, you don't have any public accounts attached to your Bungie.net profile."
            )
            await registration_msg.delete()
            return await manager.clean_messages()

        for entry in res['Response']['destinyMemberships']:
            if entry['membershipType'] == 4:
                bliz_name = entry['displayName']
                bliz_id = entry['membershipId']
            elif entry['membershipType'] == 1:
                xbox_name = entry['displayName']
                xbox_id = entry['membershipId']
            elif entry['membershipType'] == 2:
                psn_name = entry['displayName']
                psn_id = entry['membershipId']

        bungie_name = res['Response']['bungieNetUser']['displayName']
        self.bot.db.update_display_names(ctx.author.id, bungie_name, bliz_name,
                                         xbox_name, psn_name)
        self.bot.db.update_membership_ids(ctx.author.id, bliz_id, xbox_id,
                                          psn_id)

        # Get references to platform emojis from Spirit Support server
        platform_reactions = []
        if bliz_name:
            platform_reactions.append(self.bot.get_emoji(constants.BNET_ICON))
        if xbox_name:
            platform_reactions.append(self.bot.get_emoji(constants.XBOX_ICON))
        if psn_name:
            platform_reactions.append(self.bot.get_emoji(constants.PS_ICON))

        # Display message with prompts to select a preferred platform
        e = self.registered_embed(bungie_name, bliz_name, xbox_name, psn_name)
        platform_msg = await manager.send_private_embed(e)
        await registration_msg.delete()

        # If only one account is connected, set it as preferred (don't display reactions)
        platform_names = (bliz_name, xbox_name, psn_name)
        if self.num_non_null_entries(platform_names) == 1:

            if bliz_name:
                platform_id = 4
            elif xbox_name:
                platform_id = 1
            else:
                platform_id = 2

            self.bot.db.update_platform(ctx.author.id, platform_id)
            return await manager.clean_messages()

        func = self.add_reactions(platform_msg, platform_reactions)
        self.bot.loop.create_task(func)

        def check_reaction(reaction, user):
            if reaction.message.id == platform_msg.id and user == ctx.author:
                for emoji in platform_reactions:
                    if reaction.emoji == emoji:
                        return True

        # Wait for platform reaction from user
        try:
            reaction, user = await self.bot.wait_for('reaction_add',
                                                     timeout=120.0,
                                                     check=check_reaction)
        except asyncio.TimeoutError:
            await platform_msg.delete()
            await manager.send_private_message(
                "I'm not sure where you went. We can try this again later.")
            return await manager.clean_messages()

        # Save preferred platform
        platform = constants.PLATFORMS.get(reaction.emoji.name)
        self.bot.db.update_platform(ctx.author.id, platform)

        # Update message with preferred platform
        e = self.registered_embed(bungie_name,
                                  bliz_name,
                                  xbox_name,
                                  psn_name,
                                  footer=True,
                                  platform=platform)
        await platform_msg.edit(embed=e)

        return await manager.clean_messages()
Exemplo n.º 26
0
 async def feedback_error(self, ctx, error):
     if isinstance(error, commands.MissingRequiredArgument):
         manager = MessageManager(ctx)
         await manager.send_message("You forgot to include your feedback!")
         await manager.clean_messages()
Exemplo n.º 27
0
    async def item(self, ctx, *, search_term):
        """Search for a Destiny 2 item

        This command searches for and displays Destiny 2 items. The results are
        limited to weapons and armour. If multiple results are found, they will
        be displayed in a paginated format. The user who invoked the command
        will be able to page through the results.

        Note: The search term must contain a minimum of three characters.
        """
        manager = MessageManager(ctx)
        paginator = Paginator(self.bot, ctx)
        await ctx.channel.trigger_typing()

        try:
            res = await self.bot.destiny.api.search_destiny_entities(
                'DestinyInventoryItemDefinition', search_term)
        except pydest.PydestException as e:
            await manager.send_message(
                "Sorry, I can't seem to search for items right now.")
            return await manager.clean_messages()
        except ValueError as f:
            await manager.send_message(
                "Your search term contains unsupported characters.")
            return await manager.clean_messages()

        if res['ErrorCode'] != 1:
            await manager.send_message(
                "Sorry, I can't seem to search for items right now")
            return await manager.clean_messages()

        # Check how many results were found - we need at least one
        num_results = res['Response']['results']['totalResults']
        if num_results == 0:
            await manager.send_message(
                "I didn't find any items that match your search.")
            return await manager.clean_messages()

        # Iterate through each result, and add them to the paginator if valid type
        for i, entry in enumerate(res['Response']['results']['results']):

            item_hash = entry['hash']
            item = await self.bot.destiny.decode_hash(
                item_hash, 'DestinyInventoryItemDefinition')

            # If item isn't a weapon or armor piece, skip to the next one
            if item['itemType'] not in (2, 3):
                continue

            e = discord.Embed()
            e.set_author(name=item['displayProperties']['name'])
            e.description = "*{}*".format(
                item['displayProperties']['description'])
            e.set_thumbnail(url=BASE_URL + item['displayProperties']['icon'])

            # Set embed color based on item rarity
            item_rarity = int(item['inventory']['tierType'])
            if item_rarity == 2:
                e.color = discord.Color.light_grey()
            elif item_rarity == 3:
                e.color = discord.Color.dark_green()
            elif item_rarity == 4:
                e.color = discord.Color.blue()
            elif item_rarity == 5:
                e.color = discord.Color.dark_purple()
            elif item_rarity == 6:
                e.color = discord.Color.gold()
            else:
                e.color = constants.BLUE

            # Add armor/weapon specific information
            if item['itemType'] == 3:
                e = self.embed_weapon(e, item)
                e = await self.embed_perks(e, item, 4241085061)
            else:
                e = self.embed_armor(e, item)
                e = await self.embed_perks(e, item, 2518356196)

            paginator.add_embed(e)

        if not paginator.length:
            await manager.send_message(
                "I didn't find any items that match your search.")
            return await manager.clean_messages()

        func = manager.clean_messages()
        self.bot.loop.create_task(func)
        await paginator.paginate()
Exemplo n.º 28
0
    async def loadout(self, ctx, username=None, platform=None):
        """Display a Guardian's loadout

        In order to use this command for your own Guardian, you must first register your Destiny 2
        account with the bot via the register command.

        `loadout` - Display your Guardian's loadout (preferred platform)
        \$`loadout Asal#1502 bnet` - Display Asal's Guardian's loadout on Battle.net
        \$`loadout @user` - Display a registered user's Guardian (preferred platform)
        \$`loadout @user bnet` - Display a registered user's Guardian on Battle.net
        """
        manager = MessageManager(ctx)
        await ctx.channel.trigger_typing()

        # Get membership details. This depends on whether or not a platform or username were given.
        membership_details = await helpers.get_membership_details(
            self.bot, ctx, username, platform)

        # If there was an error getting membership details, display it
        if isinstance(membership_details, str):
            await manager.send_message(membership_details)
            return await manager.clean_messages()
        else:
            platform_id, membership_id, _ = membership_details

        # Attempt to fetch character information from Bungie.net
        try:
            res = await self.bot.destiny.api.get_profile(
                platform_id, membership_id,
                ['characters', 'characterEquipment', 'profiles'])
        except pydest.PydestException as e:
            await manager.send_message(
                "Sorry, I can't seem to retrieve that Guardian right now.")
            return await manager.clean_messages()

        if res['ErrorCode'] != 1:
            await manager.send_message(
                "Sorry, I can't seem to retrieve that Guardian right now.")
            return await manager.clean_messages()

        # 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.bot.destiny.decode_hash(
            last_played_char['classHash'], 'DestinyClassDefinition')
        role = role_dict['displayProperties']['name']

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

        race_dict = await self.bot.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.bot.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.bot.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_id))
        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.send_embed(e)
        await manager.clean_messages()
Exemplo n.º 29
0
 async def broadcast_error(self, ctx, error):
     manager = MessageManager(ctx)
     await manager.send_message("You didn't include a broadcast message")
     return await manager.clean_messages()
Exemplo n.º 30
0
 async def item_error(self, ctx, error):
     if isinstance(error, commands.MissingRequiredArgument):
         manager = MessageManager(ctx)
         await manager.send_message(
             "Oops! You didn't specify a search term.")
         await manager.clean_messages()