Example #1
0
 def create_embed(message, title, description, timestamp):
     embed = discord.Embed(title=title,
                           description=description,
                           timestamp=timestamp)
     embed.set_author(name=str(message.author),
                      icon_url=get_avatar(message.author))
     return embed
Example #2
0
    async def _toggle_every(self, channel, winners: int, expires_in):
        guild = channel.guild
        perms = channel.permissions_for(guild.get_member(self.bot.user.id))
        if not perms.manage_roles and not perms.administrator:
            return await channel.send('Invalid server perms')

        role = guild.get_role(323098643030736919 if not self.bot.test_mode else
                              440964128178307082)
        if role is None:
            return await channel.send('Every role not found')

        sql = 'INSERT INTO `giveaways` (`guild`, `title`, `message`, `channel`, `winners`, `expires_in`) VALUES (:guild, :title, :message, :channel, :winners, :expires_in)'

        now = datetime.utcnow()
        expired_date = now + expires_in
        sql_date = datetime2sql(expired_date)

        title = 'Toggle the every role on the winner.'
        embed = discord.Embed(
            title='Giveaway: {}'.format(title),
            description=
            'React with <:GWjojoGachiGASM:363025405562585088> to enter',
            timestamp=expired_date)
        text = 'Expires at'
        if winners > 1:
            text = '{} winners | '.format(winners) + text
        embed.set_footer(text=text, icon_url=get_avatar(self.bot.user))

        message = await channel.send(embed=embed)
        try:
            await message.add_reaction('GWjojoGachiGASM:363025405562585088')
        except:
            pass

        try:
            await self.bot.dbutil.execute(sql,
                                          params={
                                              'guild': guild.id,
                                              'title': 'Toggle every',
                                              'message': message.id,
                                              'channel': channel.id,
                                              'winners': winners,
                                              'expires_in': sql_date
                                          },
                                          commit=True)
        except SQLAlchemyError:
            logger.exception('Failed to create every toggle')
            return await channel.send('SQL error')

        task = call_later(self.remove_every, self.bot.loop,
                          expires_in.total_seconds(), guild.id, channel.id,
                          message.id, title, winners)

        self.bot.every_giveaways[message.id] = task
Example #3
0
    async def played(self, ctx):
        games = await self.bot.dbutils.get_activities(ctx.author.id)
        if games is False:
            await ctx.send('Failed to execute sql')
            return
        elif not games:
            await ctx.send('No played data has been logged')
            return

        embed = discord.Embed(title='Top games played')
        embed.set_author(name=str(ctx.author), icon_url=get_avatar(ctx.author))
        for game in games:

            embed.add_field(name=game['game'],
                            value=seconds2str(seconds=game['time'],
                                              long_def=False),
                            inline=False)

        await ctx.send(embed=embed)
Example #4
0
    async def remove_every(self, guild, channel, message, title, winners):
        guild = self.bot.get_guild(guild)
        if not guild:
            await self.delete_giveaway_from_db(message)
            return

        role = guild.get_role(323098643030736919 if not self.bot.test_mode else
                              440964128178307082)
        if role is None:
            await self.delete_giveaway_from_db(message)
            return

        channel = self.bot.get_channel(channel)
        if not channel:
            await self.delete_giveaway_from_db(message)
            return

        try:
            message = await channel.get_message(message)
        except discord.NotFound:
            logger.exception('Could not find message for every toggle')
            await self.delete_giveaway_from_db(message)
            return
        except Exception:
            logger.exception('Failed to get toggle every message')

        react = None
        for reaction in message.reactions:
            emoji = reaction.emoji
            if isinstance(emoji, str):
                continue
            if emoji.id == 363025405562585088 and emoji.name == 'GWjojoGachiGASM':
                react = reaction
                break

        if react is None:
            logger.debug('react not found')
            return

        title = 'Giveaway: {}'.format(title)
        description = 'No winners'
        users = await react.users(limit=react.count).flatten()
        candidates = [
            guild.get_member(user.id) for user in users
            if user.id != self.bot.user.id and guild.get_member(user.id)
        ]
        winners = choice(candidates,
                         min(winners, len(candidates)),
                         replace=False)
        if len(winners) > 0:
            winners = sorted(winners, key=lambda u: u.name)
            description = 'Winners: {}'.format('\n'.join(
                [user.mention for user in winners]))

        added = 0
        removed = 0
        for winner in winners:
            winner = guild.get_member(winner.id)
            if not winner:
                continue
            if role in winner.roles:
                retval = await retry(winner.remove_roles,
                                     role,
                                     reason='Won every toggle giveaway')
                removed += 1

            else:
                retval = await retry(winner.add_roles,
                                     role,
                                     reason='Won every toggle giveaway')
                added += 1

            if isinstance(retval, Exception):
                logger.debug(
                    'Failed to toggle every role on {0} {0.id}\n{1}'.format(
                        winner, retval))

        embed = discord.Embed(title=title,
                              description=description,
                              timestamp=datetime.utcnow())
        embed.set_footer(text='Expired at', icon_url=get_avatar(self.bot.user))
        await message.edit(embed=embed)
        description += '\nAdded every to {} user(s) and removed it from {} user(s)'.format(
            added, removed)
        await message.channel.send(description)
        await self.delete_giveaway_from_db(message.id)
Example #5
0
    async def stats(self, ctx):
        """Get stats about this bot"""
        pid = os.getpid()
        process = psutil.Process(pid)
        uptime = time.time() - process.create_time()
        d = datetime.utcfromtimestamp(uptime)
        uptime = f'{d.day-1}d {d.hour}h {d.minute}m {d.second}s'
        current_memory = round(process.memory_info().rss / 1048576, 2)
        memory_usage = f' Current: {current_memory}MB'
        if sys.platform == 'linux':
            try:
                # use pmap to find the memory usage of this process and turn it to megabytes
                # Since shlex doesn't care about pipes | I have to do this
                s1 = subprocess.Popen(shlex.split('pmap %s' % os.getpid()),
                                      stdin=subprocess.PIPE,
                                      stdout=subprocess.PIPE,
                                      stderr=subprocess.PIPE)
                s2 = subprocess.Popen(
                    shlex.split('grep -Po "total +\K([0-9])+(?=K)"'),
                    stdin=s1.stdout,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE)
                s1.stdin.close()
                memory = round(
                    int(s2.communicate()[0].decode('utf-8')) / 1024, 1)
                usable_memory = str(memory) + 'MB'
                memory_usage = f'{current_memory}MB/{usable_memory} ({(current_memory/memory*100):.1f}%)'

            except:
                logger.exception('Failed to get extended mem usage')

        users = 0
        for _ in self.bot.get_all_members():
            users += 1

        guilds = len(self.bot.guilds)

        try:
            # Get the last time the bot was updated
            last_updated = format_rfc2822(
                os.stat('.git/refs/heads/master').st_mtime, localtime=True)
        except OSError:
            logger.exception('Failed to get last updated')
            last_updated = 'N/A'

        sql = 'SELECT * FROM `command_stats` ORDER BY `uses` DESC LIMIT 3'
        try:
            rows = await self.bot.dbutil.execute(sql)
        except SQLAlchemyError:
            logger.exception('Failed to get command stats')
            top_cmd = 'Failed to get command stats'
        else:
            top_cmd = ''
            i = 1
            for row in rows:
                name = row['parent']
                cmd = row['cmd']
                if cmd:
                    name += ' ' + cmd

                top_cmd += f'{i}. `{name}` with {row["uses"]} uses\n'
                i += 1

        embed = discord.Embed(title='Stats', colour=random_color())
        embed.add_field(name='discord.py version', value=discord.__version__)
        embed.add_field(name='Uptime', value=uptime)
        embed.add_field(name='Servers', value=str(guilds))
        embed.add_field(name='Users', value=str(users))
        embed.add_field(name='Memory usage', value=memory_usage)
        embed.add_field(name='Last updated', value=last_updated)
        embed.add_field(name='Most used commands', value=top_cmd, inline=False)
        embed.set_thumbnail(url=get_avatar(self.bot.user))
        embed.set_author(name=self.bot.user.name,
                         icon_url=get_avatar(self.bot.user))

        await ctx.send(embed=embed)
Example #6
0
    async def poll(self, ctx, *, message):
        """
        Creates a poll that expires by default in 60 seconds
        Examples of use: {prefix}{name} title -d description -e <:GWjojoGappyGoodShit:364223863888019468> 👌 -a
        available arguments
        `-d` `-description` Description for the poll
        `-t` `-time` Time after which the poll is expired. Maximum time is 1 week
        `-e` `-emotes` Optional emotes that are automatically added to the poll

        These options require no arguments. Default values that are used when they aren't specified are marked in square brackets []
        `-m` `-max_winners` [1] Maximum amount of winners. It might be more in case of a draw
        `-s` `-strict` [false] Only count emotes specified in the -emotes argument
        `-n` `-no_duplicate_votes` [false] Ignores users who react to more than one emote
        `-a` `-allow_multiple_entries` [false] Count all reactions from the user. Even if that user reacted with multiple emotes.
        """

        # Add -header if it's not present so argparser can recognise the argument
        message = '-header ' + message if not message.startswith('-h') else message
        try:
            parsed = self.parser.parse_args(message.split(' '))
        except:
            return await ctx.send('Failed to parse arguments')

        if parsed.strict and not parsed.emotes:
            return await ctx.send('Cannot set strict mode without specifying any emotes')

        if parsed.no_duplicate_votes and parsed.allow_multiple_entries:
            return await ctx.send('Cannot have -n and -a specified at the same time. That would be dumb')

        if parsed.max_winners < 1:
            return await ctx.send('Max winners needs to be an integer bigger than 0')

        if parsed.max_winners > 20:
            return await ctx.send('Max winners cannot be bigger than 20')

        title = ' '.join(parsed.header)
        expires_in = parse_time(' '.join(parsed.time))
        if expires_in.total_seconds() == 0:
            await ctx.send('No time specified or time given is 0 seconds. Using default value of 60s')
            expires_in = timedelta(seconds=60)
        if expires_in.days > 14:
            return await ctx.send('Maximum time is 14 days')

        now = datetime.utcnow()
        expired_date = now + expires_in
        sql_date = datetime2sql(expired_date)
        parsed.time = sql_date

        emotes = []
        failed = []
        if parsed.emotes:
            for emote in parsed.emotes:
                if not emote.strip():
                    continue

                animated, name, emote_id = get_emote_name_id(emote)
                if name is None:
                    if len(emote) > 2:
                        # If length is more than 2 it's most likely not an unicode char
                        continue

                    emotes.append(emote)
                else:
                    emotes.append((name, emote_id))

        if parsed.description:
            description = ' '.join(parsed.description)
        else:
            description = discord.Embed.Empty

        embed = discord.Embed(title=title, description=description, timestamp=expired_date)
        if parsed.time:
            embed.add_field(name='Valid for',
                            value='%s' % str(expires_in))
        embed.set_footer(text='Expires at', icon_url=get_avatar(ctx.author))

        options = ''
        if parsed.strict:
            options += 'Strict mode on. Only specified emotes are counted\n'

        if parsed.no_duplicate_votes:
            options += 'Voting for more than one valid option will invalidate your vote\n'
        elif not parsed.allow_multiple_entries:
            options += 'If user votes multiple times only 1 reaction is counted'

        if parsed.allow_multiple_entries:
            options += 'All all valid votes are counted from a user\n'

        if parsed.max_winners > 1:
            options += 'Max amount of winners %s (might be more in case of a tie)' % parsed.max_winners

        if options:
            embed.add_field(name='Modifiers', value=options)

        msg = await ctx.send(embed=embed)

        # add reactions to message
        for emote in emotes:
            try:
                emote = '{}:{}'.format(*emote) if isinstance(emote, tuple) else emote
                await msg.add_reaction(emote)
            except discord.DiscordException:
                failed.append(emote)
        if failed:
            await ctx.send('Failed to get emotes `{}`'.format('` `'.join(failed)),
                           delete_after=60)

        def do_sql():
            sql = 'INSERT INTO `polls` (`guild`, `title`, `strict`, `message`, `channel`, `expires_in`, `ignore_on_dupe`, `multiple_votes`, `max_winners`) ' \
                  'VALUES (:guild, :title, :strict, :message, :channel, :expires_in, :ignore_on_dupe, :multiple_votes, :max_winners)'
            d = {'guild': ctx.guild.id, 'title': title,
                 'strict': parsed.strict, 'message': msg.id,
                 'channel': ctx.channel.id,
                 'expires_in': parsed.time,
                 'ignore_on_dupe': parsed.no_duplicate_votes,
                 'multiple_votes': parsed.allow_multiple_entries,
                 'max_winners': parsed.max_winners}

            session = self.bot.get_session
            try:
                session.execute(sql, params=d)

                emotes_list = []
                if emotes:
                    sql = 'INSERT INTO `emotes` (`name`, `emote`, `guild`) VALUES (:name, :emote, :guild) '
                    values = []
                    # We add all successfully parsed emotes even if the bot failed to
                    # add them so strict mode will count them in too
                    for emote in emotes:
                        if not isinstance(emote, tuple):
                            name, id = emote, emote
                            emotes_list.append(id)
                            guild = None
                        else:
                            name, id = emote
                            emotes_list.append(id)
                            guild = ctx.guild.id
                        values.append({'name': name, 'emote': str(id), 'guild': guild})

                    # If emote is already in the table update its name
                    sql += ' ON DUPLICATE KEY UPDATE name=VALUES(name)'
                    session.execute(sql, values)

                    sql = 'INSERT IGNORE INTO `pollEmotes` (`poll_id`, `emote_id`) VALUES (:poll, :emote)'
                    values = []
                    for id in emotes_list:
                        values.append({'poll': msg.id, 'emote': str(id)})

                        session.execute(sql, values)

                session.commit()
                return emotes_list
            except SQLAlchemyError:
                session.rollback()
                logger.exception('Failed sql query')
                return 'Failed to save poll. Exception has been logged'

        emotes_list = await self.bot.loop.run_in_executor(self.bot.threadpool, do_sql)
        if isinstance(emotes_list, str):
            # Error happened when this is a string. Otherwise it's a list
            return await ctx.send(emotes_list)

        poll = Poll(self.bot, msg.id, msg.channel.id, title, expires_at=expired_date, strict=parsed.strict,
                    emotes=emotes_list, no_duplicate_votes=parsed.no_duplicate_votes,
                    multiple_votes=parsed.allow_multiple_entries, max_winners=parsed.max_winners,
                    after=lambda f: self.polls.pop(msg.id, None))
        poll.start()
        self.polls[msg.id] = poll