async def owo(self, ctx: commands.Context): """ Small. """ async for msg in ctx.history(limit=1, before=ctx.message): await ctx.message.delete() owomsg = owo.owoify(msg.content) if len(msg.embeds) > 0: embeds = msg.embeds for embed in embeds: if embed.title: embed.title = owo.owoify(embed.title) if embed.description: embed.description = owo.owoify(embed.description) if embed.footer: embed.set_footer(text=owo.owoify(embed.footer.text), icon_url=embed.footer.icon_url) if embed.author: embed.set_author(name=owo.owoify(embed.author.name), icon_url=embed.footer.icon_url) for f in range(len(embed.fields)): embed.set_field_at( f, name=owo.owoify(embed.fields[f].name), value=owo.owoify(embed.fields[f].value)) await ctx.send(owomsg, embed=embeds[0], allowed_mentions=AllowedMentions.none()) if len(embeds) > 1: for e in embeds[1:]: await ctx.send(embed=e, allowed_mentions=AllowedMentions.none()) elif owomsg != msg.content: await ctx.send(owomsg, allowed_mentions=AllowedMentions.none())
async def team_mischief(self, ctx): if ctx.guild and not Utils.can_mod_official(ctx): return embed = discord.Embed( timestamp=ctx.message.created_at, color=0xFFBD1C, title="Mischief!") guild = Utils.get_home_guild() for role_name, role_id in self.role_map.items(): this_role: discord.role = guild.get_role(role_id) if this_role is None: continue member_count = self.role_counts[str(role_id)] embed.add_field(name=this_role.name, value=str(member_count), inline=True) if len(embed.fields) == 25: await ctx.send(embed=embed, allowed_mentions=AllowedMentions.none()) embed = discord.Embed( timestamp=ctx.message.created_at, color=0xFFBD1C, title="mischief continued...") await ctx.send(embed=embed, allowed_mentions=AllowedMentions.none())
async def search_user(self, ctx, args=None): if not args: return await ctx.reply('ユーザーIDを指定してください', allowed_mentions=AllowedMentions.none()) fetched_user = await self.bot.fetch_user(int(args)) if not fetched_user: return await ctx.reply('ユーザーが見つかりませんでした', allowed_mentions=AllowedMentions.none()) user_info = { 'user_id': fetched_user.id, 'user_name': fetched_user.display_name, 'user_avatar': fetched_user.avatar_url, 'user_created_at': fetched_user.created_at, 'user_guilds': fetched_user.mutual_guilds } created_at_jst = user_info["user_created_at"].astimezone( timezone("Asia/Tokyo")).strftime("%Y/%m/%d %H:%M:%S") info_msg = Embed() info_msg.set_author(name=f'{fetched_user}') info_msg.set_thumbnail(url=user_info['user_avatar']) info_msg.add_field(name='ユーザーID', value=f'{user_info["user_id"]}', inline=False) info_msg.add_field(name='ユーザー名', value=f'{user_info["user_name"]}', inline=False) info_msg.add_field(name='アカウント作成日時', value=f'{created_at_jst}') info_msg.add_field(name='共通サーバー数', value=f'{user_info["user_guilds"]}') return await ctx.reply(embed=info_msg, allowed_mentions=AllowedMentions.none())
async def do_power_kick(self, ctx, protected_members): the_saved = [] for member in ctx.guild.members: if member not in protected_members and \ not member.bot and \ not member.guild_permissions.ban_members and \ not member.guild_permissions.manage_channels: await ctx.send(f"kicking {Utils.get_member_log_name(member)}", allowed_mentions=AllowedMentions.none()) try: await ctx.guild.kick(member) except Forbidden: await ctx.send( f"I'm not allowed to kick {Utils.get_member_log_name(member)} (forbidden)", allowed_mentions=AllowedMentions.none()) except HTTPException: await ctx.send( f"I failed to kick {Utils.get_member_log_name(member)} (http exception)", allowed_mentions=AllowedMentions.none()) else: the_saved.append(Utils.get_member_log_name(member)) # list count of members who will remain the_saved_description = '\n'.join(the_saved) the_saved_description = Utils.paginate(the_saved_description) await ctx.send("`These members were not kicked:`") for page in the_saved_description: await ctx.send(page, allowed_mentions=AllowedMentions.none()) # TODO: ping on task completion? del self.power_task[ctx.guild.id]
async def _eval(self, ctx, *, body: str): """Evaluates a code""" env = { 'bot': self.bot, 'ctx': ctx, 'channel': ctx.channel, 'author': ctx.author, 'guild': ctx.guild, 'message': ctx.message, '_': self._last_result } env.update(globals()) body = cogs.cleanup_code(body) stdout = io.StringIO() to_compile = f'async def func():\n{textwrap.indent(body, " ")}' try: exec(to_compile, env) except Exception as e: e_msg1 = Embed( title='Error', description=f'```py\n{e.__class__.__name__}: {e}\n```') return await ctx.reply(embed=e_msg1, allowed_mentions=AllowedMentions.none()) func = env['func'] try: with redirect_stdout(stdout): ret = await func() except Exception as e: value = stdout.getvalue() e_msg2 = Embed( title='Error', description=f'```py\n{value}{traceback.format_exc()}\n```') await ctx.reply(embed=e_msg2, allowed_mentions=AllowedMentions.none()) else: value = stdout.getvalue() try: await ctx.message.add_reaction('\u2705') except: pass if ret is None: if value: s_msg1 = Embed(title='Success', description=f'```py\n{value}\n```') await ctx.reply(embed=s_msg1, allowed_mentions=AllowedMentions.none()) else: self._last_result = ret s_msg2 = Embed(title='Success', description=f'```py\n{value}{ret}\n```') await ctx.reply(embed=s_msg2, allowed_mentions=AllowedMentions.none())
async def search_server(self, ctx, args=None): if not args: return await ctx.reply('サーバーIDを指定してください', allowed_mentions=AllowedMentions.none()) fetched_guild = self.bot.get_guild(int(args)) if not fetched_guild: return await ctx.reply('ユーザーが見つかりませんでした', allowed_mentions=AllowedMentions.none()) guild = fetched_guild server_name = guild.name server_id = guild.id server_icon = guild.icon_url server_owner = guild.owner server_created = guild.created_at server_region = guild.region server_all_ch_count = len(guild.channels) server_t_ch_count = len(guild.text_channels) server_v_ch_count = len(guild.voice_channels) server_c_ch_count = len(guild.categories) server_all_member_count = len(guild.members) server_m_count = len([m for m in guild.members if not m.bot]) server_b_count = len([b for b in guild.members if b.bot]) server_ban_m_count = len(await guild.bans()) server_e_count = len([e for e in guild.emojis if not e.animated]) server_ani_e_count = len([ae for ae in guild.emojis if ae.animated]) server_e_limit = guild.emoji_limit embed = Embed(title=server_name, description=f'ID: `{server_id}`') embed.add_field(name='オーナー', value=f'{server_owner} ({server_owner.id})', inline=False) embed.add_field( name='作成日時', value= f'{server_created.astimezone(timezone("Asia/Tokyo")).strftime("%Y/%m/%d %H:%M:%S")}' ) embed.add_field(name='地域', value=server_region) embed.add_field( name=f'チャンネル - {server_all_ch_count}/500', value= f'```diff\n+ カテゴリーチャンネル: {server_c_ch_count}\n+ テキストチャンネル: {server_t_ch_count}' f'\n+ ボイスチャンネル: {server_v_ch_count}\n```', inline=False) embed.add_field( name=f'メンバー - {server_all_member_count}', value=f'```diff\n+ メンバー: {server_m_count}\n+ BOT: {server_b_count}' f'\n+ Banされた人数: {server_ban_m_count}\n```', inline=False) embed.add_field( name=f'絵文字', value=f'```diff\n+ 通常: {server_e_count}/{server_e_limit}' f'\n+ アニメーション: {server_ani_e_count}/{server_e_limit}\n```', inline=False) embed.set_thumbnail(url=server_icon) await ctx.send(embed=embed)
async def leave(self, ctx, args=None): if args is None: return await ctx.reply('サーバーIDを指定してください', allowed_mentions=AllowedMentions.none()) i_get_guild = self.bot.get_guild(int(args)) if i_get_guild is None: return await ctx.reply('サーバーが見つかりませんでした', allowed_mentions=AllowedMentions.none()) await i_get_guild.leave() return await ctx.reply('サーバーから退出しました', allowed_mentions=AllowedMentions.none())
async def list(self, ctx: commands.Context): """ See all your reminders. """ out = "" your_reminders = sorted(filter( lambda reminder: reminder["meta"]["member"] == ctx.author.id, self._reminders.all()), key=remsort(datetime.datetime.utcnow())) if len(your_reminders) == 0: await ctx.reply("You don't have any reminders set.", mention_author=False) else: for reminder in your_reminders: then = datetime.datetime.strptime(reminder["datetime"], TIME_FORMAT) msg = remshort(reminder) out += fmt_list_item( f"{fmt_code(str(reminder.doc_id).rjust(3))} - " + f"{fmt_code(reminder['datetime'])}, " + f"{readable_delta(datetime.datetime.utcnow() - then)}:\n" + f" {msg}") await ctx.reply(out, allowed_mentions=AllowedMentions.none(), mention_author=False)
async def send(self, ctx, filter=None, delete=False): reference = ctx.message.reference if reference: message = await ctx.fetch_message(id=reference.message_id) text = message.content else: text = ctx.message.content prefix = fn.getprefix(self.bot, ctx.message) command = ctx.command.name if not text.lower().startswith(prefix + command): for alias in ctx.command.aliases: if text.lower().startswith(prefix + alias): command = alias break text = text[len(prefix) + len(command) + 1:] if text: if filter: text = sanitise_text(text) text = filter(text) try: await ctx.send(text, allowed_mentions=AllowedMentions.none()) except: text = "Text is too long to send" if filter: text = filter(text) await ctx.send(text) else: text = "You didn't include or reference any text to say!" if filter: text = filter(text) await ctx.reply(text)
async def db_set_thread_id( self, ctx: Context, member: Member, channel: TextChannel = None ): if channel is None: channel = ctx.channel # データベースからデータをもらう data_ch = await self.bot.database.fetch_row( constant.TABLE_NAME, channel_id=channel.id ) # データがなければ新規作成 if data_ch is None: await self.bot.database.insert( constant.TABLE_NAME, channel_id=channel.id, author_id=member.id, channel_type="thread", ) # データの上書き else: await self.bot.database.update( constant.TABLE_NAME, {"author_id": member.id}, channel_id=channel.id, channel_type="thread", ) await ctx.send( f"{channel.mention}の所有者は{member.mention}にセットされました。", allowed_mentions=AllowedMentions.none(), )
async def listall(self, ctx: commands.Context): """ (Restricted to moderators) List all reminders. """ if len(self._reminders.all()) == 0: await ctx.reply("There are no reminders.", delete_after=10, mention_author=False) return out = "" for reminder in sorted(self._reminders.all(), key=remsort(datetime.datetime.utcnow())): then = datetime.datetime.strptime(reminder["datetime"], TIME_FORMAT) msg = remshort(reminder) out += fmt_list_item( f"{fmt_code(str(reminder.doc_id).rjust(3))} - " + f"{fmt_code(reminder['datetime'])}, " + f"{readable_delta(datetime.datetime.utcnow() - then)} " + f"(<@{reminder['meta']['member']}>):\n" f" {msg}") await ctx.reply(out, allowed_mentions=AllowedMentions.none(), mention_author=False)
def __init__( self, *, prefix: str, admin_user_ids: Set[int], redis: Redis, ): super().__init__( command_prefix=prefix, owner_ids=admin_user_ids, case_insensitive=True, allowed_mentions=AllowedMentions.none(), activity=Activity( name=f"everyone ({prefix}help)", type=ActivityType.listening, ), ) self.redis = redis self.http_session = aiohttp.ClientSession() self.registered_users = RedisSet(self.redis, "registered_users") self.learning_channels = RedisSet(self.redis, "learning_channels") self.speaking_channels = RedisSet(self.redis, "speaking_channels") self.corpora = CorpusManager(self) self.avatars = AvatarManager(self) self.load_extension("jishaku") self._load_folder("events") self._load_folder("commands")
async def on_member_update(self, before: Member, after: Member): if after.activities is None: after.activities = ("dummy", ) if before.activities is None: before.activities = ("dummy", ) ch_twitch = self.bot.get_channel(const.CH_TWITCH) # 後が配信中 if any([ isinstance(after_activity, Streaming) for after_activity in after.activities ]): if any([ isinstance(before_activity, Streaming) for before_activity in before.activities ]): return # 前が配信中でない for after_activity in after.activities: if not isinstance(after_activity, Streaming): continue if after_activity.platform != "Twitch": continue await ch_twitch.send( f"{after.mention} が配信を始めました!", embed=self.to_embed(after_activity, after), allowed_mentions=AllowedMentions.none(), ) return
async def echo(self, ctx, *, text: str): '''Echo a message.\n **Example:```yml\n♤echo ECHO!\n♤say hello!```** ''' await ctx.message.delete() await ctx.send(text, allowed_mentions=AllowedMentions.none())
async def tr(self, ctx: Context, dest: str, *, text: str): """!tr <翻訳先の言語コード> <翻訳するテキスト>""" translator = Translator() translated = translator.translate(text, dest=dest) reply_message = translated.text if translated.pronunciation is not None: reply_message += f"\n🗣 {translated.pronunciation}" await ctx.reply(reply_message, allowed_mentions=AllowedMentions.none())
async def list(self, ctx: commands.Context): """ List all the self-assignable roles """ roles = \ map(lambda r: ctx.bot._guild.get_role(r["id"]).mention, self._roles) await ctx.send( "**List of self-assignable roles:**\n" + fmt_list(roles) + "\n" + "If you'd like a role added (especially pronouns), ask a moderator!", allowed_mentions=AllowedMentions.none())
async def really_imitate(self, ctx: commands.Context, user: User, intimidate: bool = False) -> None: # Parrot can't imitate itself! if user == self.bot.user: # Send the funny XOK message instead, that'll show 'em. embed = ParrotEmbed( title="Error", color_name="red", ) embed.set_thumbnail(url="https://i.imgur.com/zREuVTW.png" ) # Windows 7 close button embed.set_image(url="https://i.imgur.com/JAQ7pjz.png") # Xok sent_message = await ctx.send(embed=embed) await sent_message.add_reaction("🆗") return # Fetch this user's model. # May throw a NotRegistered or NoData error, which we'll just let the # error handler deal with. model = self.bot.get_model(user) sentence = model.make_short_sentence(500) or "Error" name = f"Not {user.display_name}" if intimidate: sentence = "**" + self.discord_caps(sentence) + "**" name = name.upper() # Prepare to send this sentence through a webhook. # Discord lets you change the name and avatar of a webhook account much # faster than those of a bot/user account, which is crucial for # imitating lots of users quickly. try: avatar_url = await self.bot.avatars.fetch(user) except Exception as error: logging.error("\n".join( traceback.format_exception(None, error, error.__traceback__))) avatar_url = user.avatar_url webhook = await fetch_webhook(ctx) if webhook is None: # Fall back to using an embed if Parrot doesn't have manage_webhooks # permission in this channel. await ctx.send(embed=ParrotEmbed(description=sentence, ). set_author(name=name, icon_url=avatar_url)) else: # Send the sentence through the webhook. await webhook.send( content=sentence, username=name, avatar_url=avatar_url, allowed_mentions=AllowedMentions.none(), )
async def on_raw_reaction_add(self, reaction: RawReactionActionEvent): if reaction.emoji.name != constant.EMOJI_PUSHPIN: return message = await self.fetch_message_from_reaction(reaction) if message.pinned: return await message.pin() await message.reply( f"{reaction.member.mention} がピン留めしました。", allowed_mentions=AllowedMentions.none(), )
async def on_raw_reaction_remove(self, reaction: RawReactionActionEvent): if reaction.emoji.name != constant.EMOJI_PUSHPIN: return message = await self.fetch_message_from_reaction(reaction) if not message.pinned: return if utils.get(message.reactions, emoji=constant.EMOJI_PUSHPIN): return await message.unpin() await message.reply("'📌' のリアクション数が 0 になったため、ピン留めが解除されました。", allowed_mentions=AllowedMentions.none())
async def on_raw_reaction_add(self, reaction): if reaction.emoji.name != constant.PIN_EMOJI: return channel = self.bot.get_channel(reaction.channel_id) message = await channel.fetch_message(reaction.message_id) if message.pinned: return await message.pin() await channel.send( f"{reaction.member.mention}がピン留めしました。", allowed_mentions=AllowedMentions.none(), )
async def mischief(self, ctx): if ctx.guild and not Utils.can_mod_official(ctx): return member_counts = Configuration.get_persistent_var(f"mischief_usage", dict()) max_member_id = max(member_counts, key=member_counts.get) wishes_granted = sum(member_counts.values()) guild = Utils.get_home_guild() max_user: discord.Member = guild.get_member(int(max_member_id)) await ctx.send(f"{len(member_counts)} people have gotten mischief roles.\n" f"I have granted {wishes_granted} wishes.\n" f"{max_user.mention} has wished the most, with {member_counts[max_member_id]} wishes granted.", allowed_mentions=AllowedMentions.none())
def __init__(self): super().__init__( command_prefix=when_mentioned_or(const.BOT_PREFIX), allowed_mentions=AllowedMentions.none(), ) print(f"{const.BOT_NAME} を起動します。") self.remove_command("help") for cog in Path("cogs/").glob("*.py"): try: self.load_extension("cogs." + cog.stem) print(f"{cog.stem}.pyは正常にロードされました。") except Exception: print_exc()
async def send_channel_message(self, channel_id: int, text: str) -> int: logger.debug( f"send_channel_message, channel_id={channel_id}, text='{text}'") async with self._discord_client() as client: logger.info("Posting a new channel message") logger.debug( f"Creating a new channel message, channel_id={channel_id}, text={text}" ) text_channel = await client.fetch_channel(channel_id) channel_message = await text_channel.send( content=text, allowed_mentions=AllowedMentions.none()) logger.debug(f"channel message id = {channel_message.id}") return channel_message.id
async def on_member_update(self, before: Member, after: Member): if after.activities is None: after.activities = ("dummy",) if before.activities is None: before.activities = ("dummy",) guild = self.bot.get_guild(constant.GUILD_ID) role_streaming = guild.get_role(constant.ROLE_STREAMING) ch_twitch = self.bot.get_channel(constant.CH_TWITCH) # 後が配信中 if any( [ isinstance(after_activity, Streaming) for after_activity in after.activities ] ): if any( [ isinstance(before_activity, Streaming) for before_activity in before.activities ] ): return # 前が配信中でない for after_activity in after.activities: if not isinstance(after_activity, Streaming): continue await ch_twitch.send( f"{after.mention} が配信を始めました!", embed=self.to_embed(after_activity, after), allowed_mentions=AllowedMentions.none(), ) await after.add_roles(role_streaming) return # 後が配信中じゃない else: if not any( [ isinstance(before_activity, Streaming) for before_activity in before.activities ] ): return # 前が配信中 await after.remove_roles(role_streaming) return
async def send_raw_content(self, ctx: Context, message: Message, json: bool = False) -> None: """ Send information about the raw API response for a `discord.Message`. If `json` is True, send the information in a copy-pasteable Python format. """ if ctx.author not in message.channel.members: await ctx.send( ":x: You do not have permissions to see the channel this message is in." ) return # I *guess* it could be deleted right as the command is invoked but I felt like it wasn't worth handling # doing this extra request is also much easier than trying to convert everything back into a dictionary again raw_data = await ctx.bot.http.get_message(message.channel.id, message.id) paginator = Paginator() def add_content(title: str, content: str) -> None: paginator.add_line(f'== {title} ==\n') # Replace backticks as it breaks out of code blocks. # An invisible character seemed to be the most reasonable solution. We hope it's not close to 2000. paginator.add_line(content.replace('`', '`\u200b')) paginator.close_page() if message.content: add_content('Raw message', message.content) transformer = pprint.pformat if json else self.format_fields for field_name in ('embeds', 'attachments'): data = raw_data[field_name] if not data: continue total = len(data) for current, item in enumerate(data, start=1): title = f'Raw {field_name} ({current}/{total})' add_content(title, transformer(item)) for page in paginator.pages: await ctx.send(page, allowed_mentions=AllowedMentions.none())
async def update_channel_message(self, channel_id: int, message_id: int, text: str): logger.debug( f"update_channel_message, channel_id={channel_id}, message_id={message_id}, text='{text}'" ) async with self._discord_client() as client: logger.info("Posting a new channel message") logger.debug( f"Creating a new channel message, channel_id={channel_id}, message_id={message_id}, text={text}" ) text_channel = await client.fetch_channel(channel_id) message = await text_channel.fetch_message(message_id) await message.edit(content=text, allowed_mentions=AllowedMentions.none()) logger.debug("updated channel message")
async def on_raw_reaction_add(self, reaction): if reaction.channel_id != constant.CH_REGISTER: return user = reaction.member guild = self.bot.get_guild(reaction.guild_id) role_member = guild.get_role(constant.ROLE_MEMBER) ch_register = self.bot.get_channel(constant.CH_REGISTER) message = await ch_register.fetch_message(reaction.message_id) if role_member in user.roles: return await user.add_roles(role_member) ch_notify = self.bot.get_channel(constant.CH_JOIN) await ch_notify.send( f"{user.mention}が参加しました。", allowed_mentions=AllowedMentions.none(), ) await message.remove_reaction(reaction.emoji, user)
async def archive_message(channel: TextChannel, executor_id: int): """ Create the archive message :return: """ guild = channel.guild # Get the channel archive_channel_id = await CONFIG.archive_channel() archive_channel = guild.get_channel(archive_channel_id) if archive_channel is None: return async with db_context() as db: # Get the ticket from the database result = await db.execute( select(Ticket).where(Ticket.channel_id == channel.id)) ticket = result.scalars().first() if ticket is None: return # Create the embed embed = embeds.default(guild.me, has_footer=False) embed.title = "Ticket Closed" embed.add_field(name="Ticket ID", value=str(ticket.id), inline=True) embed.add_field(name="Opened By", value=f"<@{ticket.creator_id}>", inline=True) embed.add_field(name="Closed By", value=f"<@{executor_id}>", inline=True) # TODO: implement ticket log embed.add_field(name="Ticket Log", value="Click here", inline=True) embed.add_field( name="Opened At", value=ticket.created_at.strftime("%H:%M:%S %m/%d/%Y (UTC)"), inline=False, ) embed.add_field( name="Closed At", value=datetime.utcnow().strftime("%H:%M:%S %m/%d/%Y (UTC)"), inline=False, ) await archive_channel.send(embed=embed, allowed_mentions=AllowedMentions.none())
def __init__(self, **kwargs): self.cluster_name = kwargs.pop("cluster_name") self.cluster_id = kwargs.pop("cluster_id") self.config = ConfigLoader("config.toml") mentions = AllowedMentions.none() mentions.users = True super().__init__( allowed_mentions=mentions, command_prefix=self.config.bot.global_prefix, **kwargs, ) # we overwrite the prefix when it is connected # setup stuff self.queue = asyncio.Queue() # global queue for ordered tasks self.schedule_manager = TimedScheduler() self.version = self.config.bot.version self.paginator = paginator self.BASE_URL = self.config.external.base_url self.bans = set(self.config.game.bans) self.support_server_id = self.config.game.support_server_id self.linecount = 0 self.make_linecount() self.all_prefixes = {} self.activity = discord.Game(name=f"IdleRPG v{self.version}" if self. config.bot.is_beta else self.BASE_URL) self.logger = logging.getLogger() # global cooldown self.add_check(self.global_cooldown, call_once=True) # we assume the bot is created for use right now self.launch_time = datetime.datetime.now() self.eligible_for_cooldown_reduce = set() # caching self.not_eligible_for_cooldown_reduce = set() # caching self.normal_cooldown = commands.CooldownMapping.from_cooldown( 1, self.config.bot.global_cooldown, commands.BucketType.user, ) self.donator_cooldown = commands.CooldownMapping.from_cooldown( 1, self.config.bot.donator_cooldown, commands.BucketType.user, )
async def on_reaction_add(self, reaction, user): if not self.bot.ready(): return message = reaction.message channel = message.channel if user.bot: return if reaction.emoji != F: return if message.id not in ongoing_respects: return respects = ongoing_respects[message.id] if user.id in respects[1]: return respects[1].add(user.id) await channel.send( f"{user.mention} payed respects to **{respects[0]}**", allowed_mentions=AllowedMentions.none())