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
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
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)
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)
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)
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