async def on_call(self, ctx, args, **flags): user = None if len(args) > 1: user = await find_user(args[1:], ctx.message, global_search=False) if user is None: return '{warning} User not found' roles = user.roles else: roles = ctx.guild.roles lines = [f'{r.id:<19}| {r.name}' for r in roles[::-1]] lines_per_chunk = 30 chunks = [ f'```{"id":<19}| name\n{"-" * 53}\n' + '\n'.join(lines[i:i + lines_per_chunk]) + '```' for i in range(0, len(lines), lines_per_chunk) ] p = Paginator(self.bot) for i, chunk in enumerate(chunks): e = Embed( title= f'''{'Guild' if user is None else str(user) + "'s"} roles ({len(lines)})''', colour=Colour.gold(), description=chunk) e.set_footer(text=f'Page {i + 1} / {len(chunks)}') p.add_page(embed=e) await p.run(ctx)
async def on_call(self, ctx, args, **flags): if len(args) == 1: discrim = ctx.author.discriminator else: if args[1].isdigit() and len(args[1]) == 4: discrim = args[1] else: return '{warning} Invalid discriminator given' matched = [] for user in self.bot.users: if user.discriminator == discrim and not user.bot and user != ctx.author: matched.append(user) if not matched: return '{warning} Users with discriminator **%s** not found' % discrim lines = sorted([str(u) for u in matched], key=lambda s: (len(s), s)) users_per_chunk = 30 chunks = [ lines[i:i + users_per_chunk] for i in range(0, len(lines), users_per_chunk) ] p = Paginator(self.bot) for i, chunk in enumerate(chunks): e = Embed(description=f'```\n' + '\n'.join(chunk) + '```', colour=Colour.gold()) e.set_footer( text=f'Page {i + 1}/{len(chunks)} ({len(lines)}) results') p.add_page(embed=e) await p.run(ctx)
async def on_call(self, ctx, args, **flags): commands = [] if len(args) > 1: keys = [ m.name for m in [self.bot.mm.get_module(n) for n in set(args.args[1:])] if m is not None ] if keys: usage = await self.bot.redis.mget( *[f'command_usage:{k}' for k in keys]) commands = tuple(zip(keys, [int(u) for u in usage])) else: keys = await self.bot.redis.keys('command_usage:*') usage = await self.bot.redis.mget(*keys) for k, u in zip(keys, usage): name = k[14:] module = self.bot.mm.get_module(name) if module: if module.disabled: if not (flags.get('show-disabled', False)): continue if module.hidden: if not (flags.get('show-hidden', False)): continue if not (module.disabled or module.hidden) and flags.get( 'hide-normal', False): continue commands.append((name, int(u))) if not commands: return '{error} No commands found' total_usage = sum(u for c, u in commands) lines = [ f'{c:<20}{u}' for c, u in sorted(commands, key=lambda x: int(x[1]), reverse=True) ] lines_per_chunk = 30 chunks = [ lines[i:i + lines_per_chunk] for i in range(0, len(lines), lines_per_chunk) ] def make_embed(chunk): e = Embed(colour=Colour.gold(), title='Command usage:', description='```\n' + "\n".join(chunk) + '```') e.set_footer(text=f'Commands used in total: {total_usage}', icon_url=self.bot.user.avatar_url) return e p = Paginator(self.bot) for i, chunk in enumerate(chunks): p.add_page(embed=make_embed(chunk), content=f'Page **{i + 1}/{len(chunks)}**') await p.run(ctx)
async def on_call(self, ctx, args, **flags): users = await find_user(args[1:], ctx.message, max_count=-1) if not users: return '{warning} Users not found' lines = [f'{u.id:<19}| {u}' for u in users] lines_per_chunk = 30 chunks = [f'```{"id":<19}| name\n{"-" * 53}\n' + '\n'.join(lines[i:i + lines_per_chunk]) + '```' for i in range(0, len(lines), lines_per_chunk)] p = Paginator(self.bot) for i, chunk in enumerate(chunks): e = Embed( title=f'Matching users ({len(lines)})', colour=Colour.gold(), description=chunk ) e.set_footer(text=f'Page {i + 1} / {len(chunks)}') p.add_page(embed=e) await p.run(ctx)
async def on_call(self, ctx, args, **flags): guilds = sorted(self.bot.guilds, reverse=True, key=lambda g: (g.member_count, g.name)) lines = [f'{g.id:<19}| {g.name}' for g in guilds] lines_per_chunk = 30 chunks = [ f'```{"id":<19}| name\n{"-" * 53}\n' + '\n'.join(lines[i:i + lines_per_chunk]) + '```' for i in range(0, len(lines), lines_per_chunk) ] p = Paginator(self.bot) for i, chunk in enumerate(chunks): e = Embed(title=f'{len(self.bot.guilds)} guilds', colour=Colour.gold(), description=chunk) e.set_footer(text=f'Page {i + 1} / {len(chunks)}') p.add_page(embed=e) await p.run(ctx)
async def on_call(self, ctx, args, **flags): if len(args) == 1: user = ctx.author else: user = await find_user(args[1:], ctx.message) if not user: return '{warning} User not found' guilds = sorted( [g for g in self.bot.guilds if g.get_member(user.id) is not None], key=lambda g: (g.member_count, g.name), reverse=True) if not guilds: return '{warning} No common guilds' lines = [f'{g.id:<19}| {g.name}' for g in guilds] lines_per_chunk = 30 chunks = [ f'```{"id":<19}| name\n{"-" * 53}\n' + '\n'.join(lines[i:i + lines_per_chunk]) + '```' for i in range(0, len(lines), lines_per_chunk) ] def make_embed(chunk, page=None): e = Embed(title=f'{len(guilds)} common guilds', colour=Colour.gold(), description=chunk) e.set_author(name=user, icon_url=user.avatar_url) if page is not None: e.set_footer(text=f'Page {i + 1} / {len(chunks)}') return e p = Paginator(self.bot) for i, chunk in enumerate(chunks): p.add_page(embed=make_embed(chunk, page=i)) await p.run(ctx)
async def on_call(self, ctx, args, **flags): params = {'q': args[1:], 'o': 'json', 'no_html': 1} async with self.bot.sess.get(API_URL, params=params) as r: if r.status != 200: return '{error} request failed: ' + str(r.status) try: r_json = await r.json(content_type='application/x-javascript') except aiohttp.ContentTypeError: # (text/html; charset=utf-8) with query "osu!", ??? return '{error} Failed to read response' def make_embed(page): e = Embed(colour=Colour.gold(), title='DuckDuckGo') e.set_footer(text=f'Page {page}', icon_url='https://duckduckgo.com/favicon.png') return e abstract_text = r_json['AbstractText'] related = r_json['RelatedTopics'] p = Paginator(self.bot) if abstract_text: e = make_embed(1) e.description = abstract_text e.set_image(url=r_json['Image']) p.add_page(embed=e) elif not related: return '{warning} Nothing found' topics = [] for topic in related: if topic.get('Name'): for subtopic in topic['Topics']: subtopic['Text'] = f'[{topic["Name"]}] {subtopic["Text"]}' topics.append(subtopic) else: topics.append(topic) topics_per_page = 5 chunks = [ topics[i:i + topics_per_page] for i in range(0, len(topics), topics_per_page) ] for i, group in enumerate(chunks): e = make_embed(len(p._pages) + 1) for topic in group: e.add_field(name=topic['Text'][:255], value=topic['FirstURL']) p.add_page(embed=e) await p.run(ctx)
async def on_call(self, ctx, args, **flags): interval = flags.get('interval', None) if interval is not None: if not interval.isdigit(): return '{warning} Interval is not integer' interval = int(interval) if interval < 20: return '{warning} Minimum allowed interval is 20 seconds' self.interval = interval await self.bot.redis.set('activity_interval', interval) if len(args) == 1: return 'Interval updated' status = flags.get('status', 'online') subcommand = args[1].lower() if subcommand == 'list': items = await self.bot.redis.smembers('activity') if not items: return 'Nothing to show' lines = [f'{i + 1}) {p}' for i, p in enumerate(items)] lines_per_chunk = 4 chunks = [ lines[i:i + lines_per_chunk] for i in range(0, len(lines), lines_per_chunk) ] p = Paginator(self.bot) for i, chunk in enumerate(chunks): p.add_page(content='List of activities:```\n' + '\n'.join(chunk) + '```') return await p.run(ctx) elif subcommand == 'remove': items = await self.bot.redis.smembers('activity') if not items: return 'Nothing to remove' if len(items) == 1: await self.bot.redis.srem('activity', items[0]) await self._change_precense(0, '', 'online') return f'Deleted activity `{items[0]}`' lines = [f'{i + 1}) {p}' for i, p in enumerate(items)] lines_per_chunk = 4 chunks = [ lines[i:i + lines_per_chunk] for i in range(0, len(lines), lines_per_chunk) ] p = SelectionPaginator(self.bot) for i, chunk in enumerate(chunks): p.add_page(content='Please, type index to remove```\n' + '\n'.join(chunk) + '```') await p.run(ctx, len(lines)) if p.choice is not None: await self.bot.redis.srem('activity', items[p.choice - 1]) return f'Deleted activity `{items[p.choice - 1]}`' elif subcommand == 'playing': a_type = 0 a_name = args[2:] elif subcommand == 'streaming': a_type = 1 a_name = args[2:] elif subcommand == 'listening': a_type = 2 a_name = args[2:] elif subcommand == 'watching': a_type = 3 a_name = args[2:] else: return await self.on_doc_request(ctx) if a_name: await self.bot.redis.sadd('activity', f'{a_type}:{status}:{a_name}') await self._change_precense(a_type, a_name, status) return f'Status switched to `{a_name}`' if a_name else 'Status removed'
async def on_call(self, ctx, args, **flags): if args[1:].lower() == 'list': lines = [f'{k:<15}| {v}' for k, v in LANG_LIST.items()] lines_per_chunk = 30 chunks = [ f'```{"code":<15}| name\n{"-" * 45}\n' + '\n'.join(lines[i:i + lines_per_chunk]) + '```' for i in range(0, len(lines), lines_per_chunk) ] p = Paginator(self.bot) for i, chunk in enumerate(chunks): e = Embed(title=f'Supported languages ({len(lines)})', colour=Colour.gold(), description=chunk) e.set_footer(text=f'Page {i + 1} / {len(chunks)}') p.add_page(embed=e) return await p.run(ctx) text = args[1:] voice_flag = not flags.get('no-voice', isinstance(ctx.channel, DMChannel)) if voice_flag: if not ctx.author.voice: return '{warning} Please, join voice channel first' if not ctx.author.voice.channel.permissions_for(ctx.author).speak: return '{error} You\'re muted!' if not ctx.author.voice.channel.permissions_for( ctx.guild.me).connect: return '{error} I don\'t have permission to connect to the voice channel' if ctx.guild.voice_client is None: # not connected to voice channel try: vc = await ctx.author.voice.channel.connect() except Exception: return '{warning} Failed to connect to voice channel' elif ctx.author not in ctx.guild.voice_client.channel.members: # connected to a different voice channel await ctx.guild.voice_client.move_to(ctx.author.voice.channel) vc = ctx.guild.voice_client else: # already connected and is in the right place vc = ctx.guild.voice_client try: volume = float(flags.get('volume', 100)) / 100 except ValueError: return '{error} Invalid volume value' program = ['espeak-ng', text, '--stdout'] speed_flag = flags.get('speed') if speed_flag is not None: try: speed = int(speed_flag) + 80 except ValueError: return '{error} Invalid speed value' program.extend(('-s', str(speed))) language_flag = flags.get('language', 'en-us') if language_flag not in LANG_LIST: return '{warning} Language not found. Use `list` subcommand to get list of voices' language = language_flag woman_flag = flags.get('woman', False) quiet_flag = flags.get('quiet', False) if woman_flag: if quiet_flag: return '{error} Can\'t apply both woman and quiet flags' language += f'+f{random.randrange(1, 5)}' elif quiet_flag: language += '+whisper' else: language += f'+m{random.randrange(1, 8)}' program.extend(('-v', language)) process, pid = await create_subprocess_exec(*program) stdout, stderr = await execute_process(process) with TemporaryFile() as tmp: tmp.write(stdout) tmp.seek(0) audio = PCMVolumeTransformer(FFmpegPCMAudio(tmp, **ffmpeg_options), volume) if flags.get('file', not voice_flag): try: await ctx.send(file=File(stdout, filename='tts.wav')) except Exception: await ctx.send('Failed to send file') if voice_flag: if vc.is_playing(): vc.stop() vc.play(audio) await ctx.react('✅')
async def on_call(self, ctx, args, **flags): hidden_flag = flags.get('show-hidden', False) hide_normal_flag = flags.get('hide-normal', False) if len(args) == 1: module_list = [] for module in self.bot.mm.get_all_modules(hidden=hidden_flag): if not module.hidden and hide_normal_flag: continue module_list.append(module) if not module_list: return '{error} No commands found' modules_by_category = {} for module in module_list: category = module.category or 'Uncategorized' if category not in modules_by_category: modules_by_category[category] = [ module, ] else: modules_by_category[category].append(module) chunks_by_category = {} for category, modules in sorted(modules_by_category.items(), key=lambda x: x[0]): lines = sorted([f'{m.name:<20}{m.short_doc}' for m in modules]) lines_per_chunk = 30 chunks = [ lines[i:i + lines_per_chunk] for i in range(0, len(lines), lines_per_chunk) ] chunks_by_category[category] = chunks local_prefix = await get_local_prefix(ctx) total_pages = sum( len(chunks) for chunks in chunks_by_category.values()) + 1 def make_page(title, chunk, page): e = Embed(colour=Colour.gold(), title=title, description='```\n' + '\n'.join(chunk) + '```') e.set_footer(text=f'Current prefix: {local_prefix}') return { 'embed': e, 'content': f'Page **{page}/{total_pages}** ({len(module_list)}) commands' } p = Paginator(self.bot) p.add_page(**make_page('Categories', [], 1)) page = 2 p._pages[0]['embed'].description = f'```\n{"Category":<19} Pages\n' for category, chunks in chunks_by_category.items(): p._pages[0][ 'embed'].description += f'{category:.<19} {page:<2}- {page + len(chunks) - 1}\n' for chunk in chunks: p.add_page(**make_page(category, chunk, page)) page += 1 p._pages[0]['embed'].description += '```' return await p.run(ctx) if len(args) > 2: return '{warning} help for subcommands is not supported yet' if args[1].lower() == 'all': category = 'All commands' modules = self.bot.mm.get_all_modules(hidden=hidden_flag) elif args[1].lower() == 'random': return await random.choice( self.bot.mm.get_all_modules(hidden=hidden_flag) ).on_doc_request(ctx) else: category = args[1] modules = self.bot.mm.get_modules_by_category(category) module_list = [] for module in modules: if module.hidden: if not (flags.get('show-hidden', False)): continue if not module.hidden and flags.get('hide-normal', False): continue module_list.append(module) if module_list: lines = sorted([f'{m.name:<20}{m.short_doc}' for m in module_list]) lines_per_chunk = 30 chunks = [ lines[i:i + lines_per_chunk] for i in range(0, len(lines), lines_per_chunk) ] p = Paginator(self.bot) for i, chunk in enumerate(chunks): p.add_page( embed=Embed(colour=Colour.gold(), title=category, description=f'```' + '\n'.join(chunk) + '```'), content= f'Page **{i + 1}/{len(chunks)}** ({len(modules)}) commands' ) await p.run(ctx) else: module = self.bot.mm.get_module(args[1]) if not module: return '{warning} Unknown command or category' return await module.on_doc_request(ctx)
async def on_call(self, ctx, args, **flags): subcommand = args[1].lower() if subcommand in ('say', 'send'): room_id = await self.bot.redis.get(f'room_id_by_channel_and_user:{ctx.channel.id}:{ctx.author.id}') if room_id is None: return '{warning} Your message wasn\'t delivered. Please, connect to chat room first' u1_target_channel, u2_target_channel = await self.bot.redis.smembers(f'chat_room:{room_id}') user2_id = (u2_target_channel if u1_target_channel.endswith(str(ctx.author.id)) else u1_target_channel).split(':')[1] target_channel_id = (u1_target_channel if u1_target_channel.endswith(user2_id) else u2_target_channel).split(':')[0] user2 = self.bot.get_user(int(user2_id)) target = self.bot.get_channel(int(target_channel_id)) if target is None: target = user2 if target is None: return '{error} 2nd user not found! Please, try again' e = Embed(title='KiwiBot anonymous chat', description=args[2:], colour=Colour.gold()) e.set_author(name=user2, icon_url=user2.avatar_url) e.set_footer(text=f'Room id: {room_id}') try: await target.send(embed=e) except Exception: return await ctx.send('Failed to deliver message') else: return await ctx.react('✅') elif subcommand in ('connect', 'set'): if len(args) < 3: return await self.on_doc_request(ctx) try: room_id = int(args[2]) except ValueError: return '{error} Chat id is not digit' room = await self.bot.redis.smembers(f'chat_room:{room_id}') if room: u1_target_channel, u2_target_channel = room if u1_target_channel.endswith(str(ctx.author.id)) or u2_target_channel.endswith(str(ctx.author.id)): if u1_target_channel.endswith(str(ctx.author.id)): old_member = u1_target_channel user2_id = u2_target_channel.split(':')[1] else: old_member = u2_target_channel user2_id = u1_target_channel.split(':')[1] new_user1_channel = f'{ctx.channel.id}:{ctx.author.id}' await self.bot.redis.delete(f'room_id_by_channel_and_user:{old_member}') await self.bot.redis.set(f'room_id_by_channel_and_user:{new_user1_channel}', room_id) await self.bot.redis.srem(f'chat_room:{room_id}', old_member) await self.bot.redis.sadd(f'chat_room:{room_id}', new_user1_channel) return await ctx.send(f'Connected to room #{room_id}') return '{warning} You\'re not a room member or room does not exist' elif subcommand in ('new', 'create'): waiting_user = await self.bot.redis.get('waiting_chat_user') if waiting_user is None: await self.bot.redis.set('waiting_chat_user', f'{ctx.channel.id}:{ctx.author.id}') return await ctx.send('Please, wait for 2nd user to connect. This might take a while') channel_id, user_id = waiting_user.split(':') if int(user_id) == ctx.author.id: return '{warning} You\'re already queued. Please, wait for the 2nd user to connect' await self.bot.redis.delete('waiting_chat_user') user2 = self.bot.get_user(int(user_id)) target = self.bot.get_channel(int(channel_id)) if target is None: target = user2 if target is None: return '{error} 2nd user not found! Please, try again' new_room_id = int(await self.bot.redis.incr('last_chat_room_id')) user1_channel = f'{ctx.channel.id}:{ctx.author.id}' user2_channel = f'{channel_id}:{user_id}' await self.bot.redis.sadd(f'chat_room:{new_room_id}',user1_channel, user2_channel) await self.bot.redis.set(f'room_id_by_channel_and_user:{user1_channel}', new_room_id) await self.bot.redis.set(f'room_id_by_channel_and_user:{user2_channel}', new_room_id) e = Embed(title=f'Created chat room #{new_room_id}', colour=Colour.gold()) e.set_footer(text=user2, icon_url=user2.avatar_url) e.description = 'Now you can send messages with `chat say`' failed_to_notify = False try: await target.send(embed=e) except Exception: if not target == user2: try: await self.bot.get_user(int(user_id)).send(embed=e) except Exception: failed_to_notify = True else: failed_to_notify = True if failed_to_notify: e.description += 'Warning: failed to notify 2nd user about chat creation' e.set_footer(text=ctx.author, icon_url=ctx.author.avatar_url) return await ctx.send(embed=e) elif subcommand in ('leave', 'close'): if len(args) < 3: return await self.on_doc_request(ctx) try: room_id = int(args[2]) except ValueError: return '{error} Chat id is not digit' room = await self.bot.redis.smembers(f'chat_room:{room_id}') if room: u1_target_channel, u2_target_channel = room if u1_target_channel.endswith(str(ctx.author.id)) or u2_target_channel.endswith(str(ctx.author.id)): confirm_msg = await ctx.send( ( f'Are you sure you want to close chat **#{room_id}** ?\n' f'**This action cannot be undone**\n' f'If you want to move chat to different channel, use `connect` subcommand instead\n' f'React with ✅ to continue' ) ) if not await request_reaction_confirmation(confirm_msg, ctx.author): return await self.bot.edit_message(confirm_msg, f'Cancelled closing of chat **#{room_id}**') await self.bot.redis.delete( f'chat_room:{room_id}', f'room_id_by_channel_and_user:{u1_target_channel}', f'room_id_by_channel_and_user:{u2_target_channel}' ) return await self.bot.edit_message(confirm_msg, f'**{ctx.author}** closed chat **#{room_id}**') return '{warning} You\'re not a room member or room does not exist' elif subcommand == 'list': keys = await self.bot.redis.keys('chat_room:*') lines = [] for k in keys: u1_target_channel, u2_target_channel = await self.bot.redis.smembers(k) if u1_target_channel.endswith(str(ctx.author.id)) or u2_target_channel.endswith(str(ctx.author.id)): lines.append(f'#{k[10:]}' ) if not lines: return 'No active chats found' lines_per_chunk = 30 chunks = ['```\n' + '\n'.join(lines[i:i + lines_per_chunk]) + '```' for i in range(0, len(lines), lines_per_chunk)] p = Paginator(self.bot) for i, chunk in enumerate(chunks): e = Embed( title='Active chats', colour=Colour.gold(), description=chunk ) e.set_footer(text=f'Page {i + 1} / {len(chunks)}') p.add_page(embed=e) return await p.run(ctx) else: return '{warning} Unknown subcommand'
async def on_call(self, ctx, args, **flags): if args[1:].lower() == 'list': lines = [f'{k:<10}| {v}' for k, v in self.langs.items()] lines_per_chunk = 30 chunks = [ f'```{"code":<10}| name\n{"-" * 35}\n' + '\n'.join(lines[i:i + lines_per_chunk]) + '```' for i in range(0, len(lines), lines_per_chunk) ] p = Paginator(self.bot) for i, chunk in enumerate(chunks): e = Embed(title=f'Supported languages ({len(lines)})', colour=Colour.gold(), description=chunk) e.set_footer(text=f'Page {i + 1} / {len(chunks)}') p.add_page(embed=e) return await p.run(ctx) voice_flag = not flags.get('no-voice', isinstance(ctx.channel, DMChannel)) if voice_flag: if not ctx.author.voice: return '{warning} Please, join voice channel first' if not ctx.author.voice.channel.permissions_for(ctx.author).speak: return '{error} You\'re muted!' if not ctx.author.voice.channel.permissions_for( ctx.guild.me).connect: return '{error} I don\'t have permission to connect to the voice channel' if ctx.guild.voice_client is None: # not connected to voice channel try: vc = await ctx.author.voice.channel.connect() except Exception: return '{warning} Failed to connect to voice channel' elif ctx.author not in ctx.guild.voice_client.channel.members: # connected to a different voice channel await ctx.guild.voice_client.move_to(ctx.author.voice.channel) vc = ctx.guild.voice_client else: # already connected and is in the right place vc = ctx.guild.voice_client try: volume = float(flags.get('volume', 100)) / 100 except ValueError: return '{error} Invalid volume value' language_flag = flags.get('language') if language_flag: if language_flag not in self.langs: return '{warning} language not found. Use `list` subcommand to get list of voices' tts = gtts.gTTS(args[1:], lang=language_flag or 'en', slow=flags.get('slow', False), lang_check=False) with TemporaryFile() as tts_file: partial_tts = partial(tts.write_to_fp, tts_file) try: await self.bot.loop.run_in_executor(None, partial_tts) except Exception: return '{error} Problem with api response. Please, try again later' tts_file.seek(0) audio = PCMVolumeTransformer( FFmpegPCMAudio(tts_file, **ffmpeg_options), volume) if voice_flag: if vc.is_playing(): vc.stop() vc.play(audio) await ctx.react('✅') if flags.get('file', not voice_flag): try: tts_file.seek(0) await ctx.send( file=File(tts_file.read(), filename='tts.mp3')) except Exception: await ctx.send('Failed to send file')