async def rolemenu_detach( self, ctx: core.Context, name: commands.clean_content(), message: discord.Message, ): _("""Stops tracking reactions on the selected message""") result = await ctx.db.role_menus.update_one( {"guild_id": ctx.guild.id, "name": name}, {"$pull": {"messages": message.id}}, ) if result.matched_count == 0: return await ctx.inform( _("No role menu found named `{name}`").format(name=name) ) if result.modified_count == 0: return await ctx.inform( _("Role menu named `{name}` not attached to this message.").format( name=name ) ) await ctx.inform( _("Role menu `{name}` detached from message").format(name=name) )
async def 슈프림(self, ctx, *, text: commands.clean_content(fix_channel_mentions=True)): parser = argparser.Arguments() parser.add_argument('input', nargs="+", default=None) parser.add_argument('-d', '--dark', action='store_true') parser.add_argument('-l', '--light', action='store_true') args, valid_check = parser.parse_args(text) if not valid_check: return await ctx.send(args) inputText = urllib.parse.quote(' '.join(args.input)) if len(inputText) > 500: return await ctx.send(f"**{ctx.author.name}**, 500자 미만으로만 가능해요.") darkorlight = "" if args.dark: darkorlight = "dark=true" if args.light: darkorlight = "light=true" if args.dark and args.light: return await ctx.send( f"**{ctx.author.name}**, 동시에 --dark 와 --light를 지정할 수 없어요..") await self.api_img_creator_( ctx, f"https://api.alexflipnote.dev/supreme?text={inputText}&{darkorlight}", "supreme.png")
async def rolemenu_create( self, ctx: core.Context, name: commands.clean_content(), *roles: ManageableRole(), ): _("""Creates a new role menu""") if len(roles) < 1: return await ctx.inform(_("You must specify at least 1 role")) msg = None role_emoji = [] def format_role_emoji(): return "\n".join( f"**{discord.utils.get(roles, id=_re.role)}**: " + str( ctx.bot.emoji(_re.emoji) if isinstance(_re.emoji, int) else _re.emoji ) for _re in role_emoji ) for role in roles: content = _( "Specify a reaction for the `{role}` role to this message\n\n{role_emoji}" ).format(role=role, role_emoji=format_role_emoji(),) if msg is None: msg = await ctx.send(content) else: await msg.edit(content=content) try: reaction, user = await ctx.bot.wait_for( "reaction_add", timeout=60.0, check=lambda r, u: u == ctx.author and r.message.id == msg.id, ) except asyncio.TimeoutError: if msg is not None: await msg.delete() return await ctx.inform(_("Role menu creation canceled")) else: emoji = ( reaction.emoji.id if not isinstance(reaction.emoji, str) else str(reaction) ) role_emoji.append(RoleEmoji(emoji=emoji, role=role.id)) await msg.edit(content=format_role_emoji()) role_menu = RoleMenu(guild_id=ctx.guild.id, name=name, roles=role_emoji) try: await ctx.db.role_menus.insert_one(role_menu.dict()) except DuplicateKeyError: return await ctx.inform( _("A role menu named `{name}` already exists").format(name=name) ) await ctx.inform(_("Role menu named `{name}` saved").format(name=name))
async def youtube( self, ctx, *, content: commands.clean_content(use_nicknames=True, fix_channel_mentions=True, remove_markdown=True) = None, ): """[text1], [text2]|||What?, How?""" if content is None: content = (" ".join( random.choice(self.Hamood.RANDOMWORDS) for _ in range(random.randint(3, 5))) + f" {random.choice(self.Hamood.RANDOMEMOJIS)}") im = await self.search_for_image(ctx, 50) av = await self.fetch_av(ctx.author) if im: img, dt = await self.Hamood.run_async_t( self.YOUTUBE.generate, video=im, title=content, username=str(ctx.author), views=f"{random.randint(1, 10000000):,} views", pfp=av, ) await self.Hamood.quick_embed(ctx, pil_image=img, stats=dt)
async def meme( self, ctx, *, content: commands.clean_content(use_nicknames=True, fix_channel_mentions=True, remove_markdown=True) = None, ): """[toptext], [bottomtext]|||Your standard meme format.""" if content is None: content = "TOPTEXT, BOTTOMTEXT" if "," not in content: content += ", " im = await self.search_for_image(ctx, 50) if im: img, dt = await self.Hamood.run_async_t(standardmeme, *(im, *content.split(","))) if img: await self.Hamood.quick_embed(ctx, pil_image=img, stats=dt) else: await self.Hamood.quick_embed( ctx, title="Could Not Generate Meme :(", description="Image dimensions could have been too weird.", )
async def create_github_issue( ctx, *args: commands.clean_content(fix_channel_mentions=True) ): issue = " ".join(list(args)) print(f"dev command invocation: {issue}") answer = create_github_issue_helper(ctx, issue) await ctx.send(answer)
async def help( self, ctx, *, command: commands.clean_content(escape_markdown=True) = None): _("""Shows help about the bot.""") if command: command = self.bot.get_command(command.lower()) if not command: return await ctx.send(_("Sorry, that command does not exist.")) sig = self.make_signature(command) subcommands = getattr(command, "commands", None) if subcommands: clean_subcommands = "\n".join([ f" {c.name.ljust(15, ' ')} {_(getattr(c.callback, '__doc__'))}" for c in subcommands ]) fmt = f"```\n{ctx.prefix}{sig}\n\n{_(getattr(command.callback, '__doc__'))}\n\nCommands:\n{clean_subcommands}\n```" else: fmt = f"```\n{ctx.prefix}{sig}\n\n{_(getattr(command.callback, '__doc__'))}\n```" return await ctx.send(fmt) await self.bot.paginator.Paginator(extras=self.make_pages() ).paginate(ctx)
async def history(self, ctx, *, text: commands.clean_content(fix_channel_mentions = True)): """ Sample text """ if len(text) > 15: return await ctx.send('You need to specify text less than 15 characters long.') async with ctx.channel.typing(): with BytesIO() as b: final_text = text.replace('\n', ' ').upper() left_adjust = 150 if len(final_text) > 5: left_adjust = left_adjust - (15 * (len(final_text) - 5)) base = Image.open("assets/generators/history/template.png").convert("RGBA") txtO = Image.new("RGBA", base.size, (255, 255, 255, 0)) font = ImageFont.truetype("assets/generators/history/impact.ttf", 50) canv = ImageDraw.Draw(txtO) canv.text((left_adjust, 302), final_text, font=font, fill="White") out = Image.alpha_composite(base, txtO) out.save(b, "PNG") b.seek(0) await ctx.send(content=ctx.author.mention, file=discord.File(b, filename="mock.png"))
async def decide(self, ctx, *, text: commands.clean_content(fix_channel_mentions = True)): """ Decisions, decisions, decisions... """ decisions = list(filter(None, text.strip().split('|'))) if len(decisions) < 2: return await ctx.send('You need to give two decisions split by `|`') if len(decisions[0]) > 30 or len(decisions[1]) > 30: return await ctx.send('Decisions cannot be longer than 20 characters.') async with ctx.channel.typing(): with BytesIO() as b: decisions[0] = textwrap.fill(decisions[0], 11).strip() decisions[1] = textwrap.fill(decisions[1], 10).strip() base = Image.open("assets/generators/decide/template.png").convert("RGBA") dec1 = ImageFont.truetype("assets/generators/decide/verdana.ttf", 20) dec2 = ImageFont.truetype("assets/generators/decide/verdana.ttf", 18) txt1 = Image.new("RGBA", base.size, (255, 255, 255, 0)) txt2 = Image.new("RGBA", base.size, (255, 255, 255, 0)) canv1 = ImageDraw.Draw(txt1) canv2 = ImageDraw.Draw(txt2) canv1.text((115, 65), decisions[0], font=dec1, fill="Black") canv2.text((300, 75), decisions[1], font=dec2, fill="Black") txt1 = txt1.rotate(13, resample=Image.BICUBIC) txt2 = txt2.rotate(12, resample=Image.BICUBIC) comp1 = Image.alpha_composite(base, txt1) out = Image.alpha_composite(comp1, txt2) out.save(b, "PNG") b.seek(0) await ctx.send(content=ctx.author.mention, file=discord.File(b, filename="decide.png"))
async def sign(self, ctx, *, text: commands.clean_content(fix_channel_mentions = True)): """ making creative command descriptions is hard tbh """ if len(text) > 120: return await ctx.send(f'Sorry! Text needs to be less than 120 characters long. **({len(text)}/120)**') async with ctx.channel.typing(): with BytesIO() as b: font = ImageFont.truetype("assets/generators/sign/verdana.ttf", 30) final_text = textutils.wrap(font, text, 330) lines = final_text.split('\n') l_adjust = 200 if len(lines) < 4 else 228 base = Image.open("assets/generators/sign/template.png").convert("RGBA") txtO = Image.new("RGBA", base.size, (255, 255, 255, 0)) canv = ImageDraw.Draw(txtO) canv.text((l_adjust, 200), final_text, font=font, fill="Black") txtO = txtO.rotate(12.5, resample=Image.BICUBIC) out = Image.alpha_composite(base, txtO) out.save(b, "PNG") b.seek(0) await ctx.send(file=discord.File(b, filename="sign.png"))
async def say(self, ctx, *, message: commands.clean_content()): """Says the message that you give.""" try: await ctx.message.delete() except discord.Forbidden: pass await ctx.send(f"{message}")
async def choose(self, ctx: core.Context, *options: commands.clean_content()): _("""Selects one of the options""") if not options: return await ctx.inform(_("I have nothing to choose from")) choice = random.choice(options) await ctx.send(f"{ctx.author.mention}, {choice.strip()}")
async def vote(self, ctx: Context, title: clean_content(fix_channel_mentions=True), *options: str) -> None: """ Build a quick voting poll with matching reactions with the provided options. A maximum of 20 options can be provided, as Discord supports a max of 20 reactions on a single message. """ if len(title) > 256: raise BadArgument( "The title cannot be longer than 256 characters.") if len(options) < 2: raise BadArgument("Please provide at least 2 options.") if len(options) > 20: raise BadArgument("I can only handle 20 options!") codepoint_start = 127462 # represents "regional_indicator_a" unicode value options = { chr(i): f"{chr(i)} - {v}" for i, v in enumerate(options, start=codepoint_start) } embed = Embed(title=title, description="\n".join(options.values())) message = await ctx.send(embed=embed) for reaction in options: await message.add_reaction(reaction)
async def create_github_issue( ctx, *args: commands.clean_content(fix_channel_mentions=True)): """Creates a Github issue (for bug reports and feature requests)""" issue = " ".join(list(args)) logging.info("dev command invocation: %s", issue) answer = create_github_issue_helper(ctx, issue) await ctx.send(answer)
async def mute_temp( self, ctx: core.Context, member: ModeratedMember(), delta_seconds: int, *, reason: clean_content() = None, ): _("""Temporarily mutes member in the guild""") with ctx.typing(): aws = [ self.mute_channel(channel, member, reason) for channel in ctx.guild.channels ] await asyncio.gather(*aws) try: await ctx.message.delete() except discord.Forbidden: await ctx.send(_("Member muted: {0}").format(str(member))) await self.create_modlog_case( ctx, member, discord.AuditLogAction.overwrite_create, reason, expires_at=ctx.message.created_at + timedelta(seconds=delta_seconds), )
async def supreme(self, ctx, *, text: commands.clean_content(fix_channel_mentions=True)): """ Make a fake Supreme logo Arguments: --dark | Make the background to dark colour --light | Make background to light and text to dark colour """ parser = argparser.Arguments() parser.add_argument('input', nargs="+", default=None) parser.add_argument('-d', '--dark', action='store_true') parser.add_argument('-l', '--light', action='store_true') args, valid_check = parser.parse_args(text) if not valid_check: return await ctx.send(args) inputText = urllib.parse.quote(' '.join(args.input)) if len(inputText) > 500: return await ctx.send(f"**{ctx.author.name}**, the Supreme API is limited to 500 characters, sorry.") darkorlight = "" if args.dark: darkorlight = "dark=true" if args.light: darkorlight = "light=true" if args.dark and args.light: return await ctx.send(f"**{ctx.author.name}**, you can't define both --dark and --light, sorry..") await self.api_img_creator(ctx, f"https://api.alexflipnote.dev/supreme?text={inputText}&{darkorlight}", "supreme.png", token=self.alex_api_token)
async def safe_send( self, content: typing.Optional[str] = None, *, tts: bool = False, embed: embeds.Embed = None, file: _discord.File = None, files: typing.List[_discord.File] = None, delete_after: typing.Optional[float] = None, nonce: typing.Optional[int] = None, **cleaner_opts, ) -> _discord.Message: """ This is the same as the discord.py :class:`discord.ext.commands.Context` object, except for the fact that it also has a :meth:`safe_send` method that you can call instead of :meth:`send` if you so choose. The safe_send method takes any additional keyword arguments to the :class:`libneko.clean_content` class. These are used to scrub any response that is being sent. """ if content is not None: content = str(content) cc = _commands.clean_content(**cleaner_opts) content = await cc.convert(self, content) return await super().send( content=content, tts=tts, embed=embed, file=file, files=files, delete_after=delete_after, nonce=nonce, )
async def ban_temp( self, ctx: core.Context, member: ModeratedMember(), delta_seconds: int, delete_days: Optional[delete_message_days] = 0, *, reason: clean_content() = None, ): _( """Temporarily ban user in the guild You can specify number of days worth of messages to delete from the user in the \ guild. The minimum is 0 and the maximum is 7. Defaults to 0.""" ) await ctx.guild.ban(member, delete_message_days=delete_days, reason=reason) try: await ctx.message.delete() except discord.Forbidden: await ctx.send(_("User banned: {0}").format(str(member))) await self.create_modlog_case( ctx, member, discord.AuditLogAction.ban, reason, expires_at=ctx.message.created_at + timedelta(seconds=delta_seconds), )
def __init__(self, **kwargs): super().__init__(command_prefix=kwargs.pop('command_prefix', ('t.', 'T.', 'tim.')), case_insensitive=True, **kwargs) self.session = ClientSession(loop=self.loop) self.start_time = datetime.datetime.utcnow() self.clean_text = commands.clean_content(escape_markdown=True, fix_channel_mentions=True)
async def unmute(self, ctx, member: discord.Member, *, reason: commands.clean_content(fix_channel_mentions=True)='None specified'): """ Unmutes the specified user """ interaction._check_hierarchy(ctx.author, member, False) config = await self.bot.db.get_config(ctx.guild.id) if not config['mutedRole']: return await ctx.send('A muted role hasn\'t been configured for this server. ' 'You can change the role by using the `config` command.') role = discord.utils.get(ctx.guild.roles, id=int(config['mutedRole'])) if not role: return await ctx.send('A muted role was configured for this server but no longer exists. ' 'You can change the role by using the `config` command.') if role.position > ctx.me.top_role.position: return await ctx.send('The muted role\'s position is higher than my top role. Unable to unassign the role') if role not in member.roles: return await ctx.send('That user is not currently muted.') await member.remove_roles(role, reason=f'[ {ctx.author} ] {reason}') await self.safe_react(ctx.message, '🔈') await self.helpers.post_modlog_entry(ctx.guild.id, 'Unmuted', member, ctx.author, reason, '', 0x53dc39) await self.bot.db.remove_timed_entry(ctx.guild.id, member.id, 'mutes')
async def ban(self, ctx, member: IDConverter, *, reason: commands.clean_content(fix_channel_mentions=True)='None specified'): """ Bans a user from the server Timed bans: For the reason parameter, specify a time, unit and then your reason. E.g: 5s Spamming Where 5s means 5 seconds. Supported units: seconds, minutes, hours, days, weeks. When using a unit, specify the first letter (seconds -> s, minutes -> m etc...) """ user = await self.helpers.resolve_user(member) if not user: raise commands.BadArgument('member is not a valid user/id') member = ctx.guild.get_member(user.id) if member: interaction.check_hierarchy(ctx, member) else: member = user time, reason = timeparser.convert(reason) await ctx.guild.ban(member, reason=f'[ {ctx.author} ] {reason}', delete_message_days=7) await self.safe_react(ctx.message, '🔨') await self.bot.db.remove_timed_entry(ctx.guild.id, member.id, 'mutes') # Delete any timed mutes this user has if time: await self.helpers.post_modlog_entry(ctx.guild.id, 'Banned', member, ctx.author, reason, str(time)) await self.helpers.create_timed_ban(ctx.guild.id, member.id, time.absolute) else: await self.helpers.post_modlog_entry(ctx.guild.id, 'Banned', member, ctx.author, reason)
async def kick(self, ctx, member: discord.Member, *, reason: commands.clean_content(fix_channel_mentions=True)='None specified'): """ Kicks a user from the server """ interaction.check_hierarchy(ctx, member) await member.kick(reason=f'[ {ctx.author} ] {reason}') await self.safe_react(ctx.message, '👢') await self.helpers.post_modlog_entry(ctx.guild.id, 'Kicked', member, ctx.author, reason)
async def help( self, ctx, *, command: commands.clean_content(escape_markdown=True) = None): """Get usage information for commands.""" if command: command = self.bot.get_command(command.lower()) if not command: return await ctx.send("Sorry, typed command doesn't exist.") sig = self.help_signature(command) subcommands = getattr(command, "commands", None) form = discord.Embed(color=self.bot.color) if subcommands: clean_subcommands = "\n".join([ f"{c.name} - {getattr(c.callback, '__doc__')}" for c in subcommands ]) form.title = f"{ctx.prefix}{sig}" form.description = getattr(command.callback, '__doc__') form.add_field(name="Commands", value=clean_subcommands, inline=False) else: form.title = f"{ctx.prefix}{sig}" form.description = getattr(command.callback, '__doc__') return await ctx.send(embed=form, delete_after=60) await self.bot.paginator.Paginator(extras=self.make_pages() ).paginate(ctx)
async def update_playbar(self): try: if not self._running: self.update_playbar.stop() position = lavalink.utils.format_time(self.player.position) if self.player.current.stream: duration = "🔴 LIVE" else: duration = lavalink.utils.format_time( self.player.current.duration) cur = None dur = None if not self.player.current.stream: cur = int(self.player.position / 1000) dur = int(self.player.current.duration / 1000) bar = "\n" + self.get_bar(cur, dur) if (cur and dur) else "" other = f"{(REPEAT_EMOJI if self.player.repeat else 'Repeat OFF')} | {(SHUFFLE_EMOJI if self.player.shuffle else 'Shuffle OFF')}" cleaned_title = await (commands.clean_content( escape_markdown=True)).convert(self.ctx, self.player.current.title) song = f"**[{cleaned_title}]({self.player.current.uri})**\n({position}/{duration}){bar}\n{other}" # actually format the new embed from the old one emb = self.msg.embeds[0] emb.description = song await self.msg.edit(embed=emb) except (AttributeError, discord.errors.NotFound): self.update_playbar.stop() # pylint: disable=no-member
async def remind( self, ctx, execute_at: interpret_str_as_datetime, *, content: commands.clean_content(fix_channel_mentions=True) ): if len(content) > Reminder.MAX_CONTENT_LENGTH: raise commands.BadArgument description = ( f'**Przypomnę ci tutaj "{content}" {human_datetime(execute_at)}.**\n*Przypomnienie zostanie anulowane ' 'jeśli usuniesz tę wiadomość. Możesz to zrobić przy użyciu komendy `nie`.*' ) embed = self.bot.generate_embed('🍅', 'Ustawiono przypomnienie', description) confirmation_message = await self.bot.send(ctx, embed=embed) if confirmation_message is None: return try: details = { 'confirmation_message_id': confirmation_message.id, 'channel_id': ctx.channel.id, 'content': content, 'user_id': ctx.author.id, 'requested_at': utc_to_naive_local(ctx.message.created_at), 'execute_at': execute_at } with data.session(commit=True) as session: reminder = Reminder(**details) session.add(reminder) self.bot.loop.create_task(self.set_off_reminder(**details)) except: await confirmation_message.delete() raise
async def say(self, ctx, *, content: commands.clean_content(use_nicknames=False, fix_channel_mentions=True)): embed = embeds.twoembed(f"Message from {ctx.author}!", discord.utils.escape_mentions(content)) embed.set_thumbnail(url=ctx.author.avatar_url) await ctx.send(embed=embed)
def __init__(self, bot): self.bot = bot self.bot.source_url = "https://github.com/crrapi/chr1sBot" self.bot.command_usage = 0 self.valid = ("py",) self._clean_chat = commands.clean_content(fix_channel_mentions=True, escape_markdown=True) self._update_presence.start()
async def roll_dice(ctx, arg: commands.clean_content(fix_channel_mentions=True)): """Rolls #d# dice""" roll = arg.split("d") logging.info("dice command invocation: %s", roll) answer = roll_dice_helper(roll) await ctx.send(answer)
async def warn(self, ctx, member: discord.Member, *, reason: commands.clean_content(fix_channel_mentions=True)='None specified'): """ Issues a warning to the given user """ interaction._check_hierarchy(ctx.author, member, False) threshold = (await self.bot.db.get_config(ctx.guild.id))['warnThreshold'] current_warns = await self.helpers.get_warns(member.id, ctx.guild.id) + 1 append_reason = '' if reason == 'None specified' else f'for **{reason}**' if threshold == 0: await ctx.send(f'Warned **{member}** {append_reason} (Warnings: {current_warns})') else: amount = current_warns % threshold if amount == 0: try: await member.ban(reason=f'[ {ctx.author} ] Too many warnings', delete_message_days=7) except discord.Forbidden: await ctx.send(f'Unable to ban **{member.name}** for hitting the warning limit') else: await ctx.send(f'Banned **{member.name}** for hitting the warning limit ({threshold}/{threshold})') else: await ctx.send(f'Warned **{member}** {append_reason} (Warnings: {amount}/{threshold})') await self.helpers.set_warns(member.id, ctx.guild.id, current_warns) await self.helpers.post_modlog_entry(ctx.guild.id, 'Warned', member, ctx.author, reason, '', 0xEFD344)
async def twitch_set_message( self, ctx: core.Context, user: TwitchSubscriptionConverter, *, message: commands.clean_content(), ): _("""Sets alert message for specified user Markdown is allowed. Replacements `{{link}}` - stream link `{{name}}` - streamer name `{{title}}` - stream title `{{game}}` - game name `{{viewers}}` - viewers count `{{views}}` - user's views count""") await ctx.db.twitch_subs.update_one( { "topic": str(user.topic), "guilds.id": ctx.guild.id }, {"$set": { "guilds.$.message": message }}, ) return await ctx.inform( _("Alert message set for {0}.").format(user.user.display_name))
async def say(self, ctx, *, message: commands.clean_content()): '''Say something as the bot''' voted = await self.upvoted(ctx.author.id) if not voted: return await ctx.send(f'To use this command, you must upvote RemixBot here: https://discordbots.org/bot/{self.bot.user.id}') try: await ctx.message.delete() except discord.Forbidden: pass await ctx.send(message)