Пример #1
0
    async def tempban(
        self, ctx: Context, target: Member, length: int, span: str, *, reason: str = None
    ):
        """Temporarily ban a member from the server.
        For timing, plural and non-plural spans are accepted (Day, days, minutes, etc).
        Use "max" as the span for pseudo-permanence (10 years).
        Ban member permission required.
        """
        sid = str(ctx.guild.id)

        if sid not in self.tempban_db:
            self.tempban_db[sid] = {}

        # Get the current UTC time, a future time from time_parser, and the difference
        now = datetime.now(tz=timezone.utc)
        future = time_parser(span, length, now)
        length = future - now

        embed = await embed_builder("Temporarily Banned", target, reason, length)

        await target.send(embed=embed)

        await target.ban(reason=reason, delete_message_days=0)

        self.tempban_db[sid][target.id] = {
            "issued_by": str(ctx.author.id),
            "reason": reason,
            "expires": str(future.timestamp()),
        }

        update_db(self.sql_db, self.tempban_db, "temp_bans")

        tag = f"{target.name}#{target.discriminator}"
        await ctx.send(f":white_check_mark: Tempbanned {tag} for {reason}")
        await self.log_to_channel(ctx, target, reason)
Пример #2
0
    async def cmd_plugins_reload(self, ctx: Context, name: str):
        """Reload plugin (Cog). Do not include file extension.
        Botmaster required.
        """
        if name not in self.bot.plugins:
            await ctx.send(f":anger: Plugin {name}.py is not loaded.")
        else:
            try:
                self.copy_plugin_if_needed(name)

                self.bot.unload_extension(f"plugins.{name}")
                self.bot.plugins.remove(name)

                update_db(self.bot.db, self.bot.plugins, "plugins")
                await ctx.send(
                    f":white_check_mark: Plugin {name}.py successfully unloaded."
                )

                self.bot.load_extension(f"plugins.{name}")
                self.bot.plugins.append(name)

                update_db(self.bot.db, self.bot.plugins, "plugins")
                await ctx.send(
                    f":white_check_mark: Plugin {name}.py successfully loaded."
                )
            except Exception as e:
                exc = f"{type(e).__name__}, {e}"
                await ctx.send(f":anger: Error reloading {name}.py:\n```py\n{exc}\n```")
Пример #3
0
    async def admin_log(self, ctx: Context, enabled: bool, channel: TextChannel = None):
        """Enable/disable to-channel logging and set the log channel.
        MUST HAVE SERVER ADMINISTRATOR PERMISSION
        """
        sid = str(ctx.guild.id)

        if sid not in self.db:
            self.db[sid] = {}

        self.db[sid]["log"] = enabled

        if channel is not None:
            self.db[sid]["log_channel"] = str(channel.id)
        else:
            if "log_channel" not in self.db[sid]:
                self.db[sid]["log_channel"] = str(ctx.message.channel.id)
            channel = ctx.message.channel

        update_db(self.sql_db, self.db, "admin")

        embed = Embed(title="Log Settings", color=0xFF0000)
        embed.add_field(name="Enabled", value=str(enabled))
        embed.add_field(name="Log Channel", value=channel.mention)

        await ctx.send(embed=embed)
Пример #4
0
    async def role_admin_add(self, ctx: Context, role_get: Role, *,
                             description: str):
        """Add or update a role on the assignable roles list.
        Server administrator permission required.
        """
        sid = str(ctx.guild.id)
        rid = str(role_get.id)
        name = role_get.name.lower()

        if sid not in self.db:
            self.db[sid] = {"roles": {}}
        elif "roles" not in self.db[sid]:
            self.db[sid]["roles"] = {}
        elif name in self.db[sid]["roles"]:
            if self.db[sid]["roles"][name]["id"] != rid:
                await ctx.send(
                    ":anger: There is already a role with the same name in the "
                    "assignable roles list.")
            else:
                await ctx.send(":anger: That roles ia already assignable.")

            return

        try:
            self.db[sid]["roles"][name] = {
                "id": rid,
                "description": description
            }

            await ctx.send(
                f":white_check_mark: Added {name} to assignable roles.")
            update_db(self.sql_db, self.db, "servers")
        except Exception as e:
            await ctx.send(f":anger: Error adding role: {e}")
Пример #5
0
 async def on_raw_message_delete(self, payload):
     try:
         del self.db[str(payload.guild_id)]["reacts"][str(
             payload.message_id)]
         update_db(self.sql_db, self.db, "servers")
     except KeyError:
         pass
Пример #6
0
    async def unmute(self, ctx: Context, target: Member):
        """Unmute a member early.
        Kick member permission required.
        """
        sid = str(ctx.guild.id)
        uid = str(target.id)
        mute_role = None

        try:
            mute_role = ctx.guild.get_role(int(self.db[sid]["mute_role"]))
        except KeyError:
            await ctx.send(":anger: This server has no mute role set.")
            return

        if sid not in self.mute_db:
            await ctx.send(":anger: This server has no mutes.")
            return

        if uid not in self.mute_db[sid]:
            await ctx.send(":anger: This member is not muted.")
            return

        await target.send(f":speaking_head: You have been unmuted in {ctx.guild.name}.")
        await ctx.send(f":speaking_head: Unmuted {target.name}.")

        await target.remove_roles(mute_role)
        del self.mute_db[sid][uid]

        update_db(self.sql_db, self.mute_db, "mutes")
Пример #7
0
    async def groups_create(self, ctx: Context, name: str, *,
                            description: str):
        """Create a group for you and your friends!
        Name must NOT include spaces and is CaSe SeNsItIvE!
        """
        if len(name) > 48:
            await ctx.send(
                ":anger: Please use a name shorter than 48 characters.")
            return

        sid = str(ctx.guild.id)

        if sid not in self.db:
            self.db[sid] = {}

        # Try to make a role, text, and voice channel for the group
        try:
            role = await ctx.guild.create_role(name=name,
                                               reason="Groups plugin")
            ow = {
                ctx.guild.default_role:
                PermissionOverwrite(read_messages=False),
                role: PermissionOverwrite(read_messages=True),
            }
            category = await ctx.guild.create_category(name=name,
                                                       reason="Groups plugin",
                                                       overwrites=ow)
            text = await ctx.guild.create_text_channel(
                name=name.lower(),
                reason="Groups plugin",
                category=category,
                topic=description,
            )
            voice = await ctx.guild.create_voice_channel(
                name=name, reason="Groups plugin", category=category)

            self.db[sid][name] = {
                "info": {
                    "leader": str(ctx.author.id),
                    "description": description,
                    "category": str(category.id),
                    "text_channel": str(text.id),
                    "voice_channel": str(voice.id),
                    "role": str(role.id),
                }
            }

            update_db(self.sql_db, self.db, "servers")

            await ctx.author.add_roles(role, reason="Group created.")

        except Exception as e:
            await ctx.send(f":anger: Something went wrong: `{e}`")
            return

        await ctx.send(":white_check_mark: Group created!")
        await text.send(
            f"Welcome to your group {ctx.author.mention}! Try the `group invite` command!"
        )
Пример #8
0
    async def on_guild_join(self, guild: Guild):
        sid = str(guild.id)

        self.bot.log.info(f"[JOIN] {guild.name}")

        if sid not in self.bot.servers:
            self.bot.servers[sid] = {}
            update_db(self.bot.db, self.bot.servers, "servers")
Пример #9
0
    async def on_guild_remove(self, guild: Guild):
        sid = str(guild.id)

        self.bot.log.info(f"[LEAVE] {guild.name}")

        if sid in self.bot.servers:
            self.bot.servers.pop(sid)
            update_db(self.bot.db, self.bot.servers, "servers")
Пример #10
0
    async def warn(
        self, ctx: Context, target: Member, length: int, span: str, *, reason: str
    ):
        """Warn a member.
        For timing, plural and non-plural spans are accepted (Day, days, minutes, etc).
        Use "max" as the span for pseudo-permanence (10 years).
        Kick member permission required.
        """
        sid = str(ctx.guild.id)
        uid = str(target.id)
        warn_count = 1

        if sid not in self.warn_db:
            self.warn_db[sid] = {}

        if uid in self.warn_db[sid]:
            for _ in self.warn_db[sid][uid]:
                warn_count += 1

        # Get the current UTC time, a future time from time_parser, and the difference
        now = datetime.now(tz=timezone.utc)
        future = time_parser(span, length, now)
        length = future - now

        embed = await embed_builder("Warned", target, reason, length)

        await target.send(embed=embed)
        await target.send(f":warning: This is warning #{warn_count}.")
        await ctx.send(
            f":warning: Warning {warn_count} issued to {target.name} for {reason}"
        )

        if uid not in self.warn_db[sid]:
            self.warn_db[sid][uid] = {}

        def db_check(count: int) -> int:
            if str(count) in self.warn_db[sid][uid]:
                count += 1
                return db_check(count)
            else:
                return count

        warn_count = db_check(warn_count)

        warning = {
            "issued_by": str(ctx.author.id),
            "reason": reason,
            "expires": str(future.timestamp()),
        }
        self.warn_db[sid][uid][str(warn_count)] = warning

        update_db(self.sql_db, self.warn_db, "warns")
        await self.log_to_channel(ctx, target, reason)
Пример #11
0
    async def cmd_plugins_disable(self, ctx: Context, name: str):
        """Disable a loaded plugin (Cog) on the current server.
        Server administrator permission required.
        """
        if name not in self.bot.plugins:
            await ctx.send(f":anger: No plugin {name} is loaded.")
            return
        else:
            sid = str(ctx.guild.id)

            self.bot.servers[sid][name] = False

            update_db(self.bot.db, self.bot.servers, "servers")
            await ctx.send(f":white_check_mark: Plugin {name} disabled on your server.")
Пример #12
0
    async def cmd_logs_deletes(self, ctx: Context, enabled: bool):
        """Set logging of deleted messages to the server's log channel.
        MUST HAVE SERVER ADMINISTRATOR PERMISSION
        """
        sid = str(ctx.guild.id)

        if sid not in self.bot.servers:
            self.bot.servers[sid] = {}

        self.bot.servers[sid]["log_deletes"] = enabled
        update_db(self.bot.db, self.bot.servers, "servers")

        await ctx.send(
            f":white_check_mark: Logging message deletes set to {enabled}.")
Пример #13
0
    async def admin_role(self, ctx: Context, role: Role):
        """Set the mute role for the server.
        MUST HAVE SERVER ADMINISTRATOR PERMISSION
        """
        sid = str(ctx.guild.id)

        if sid not in self.db:
            self.db[sid] = {}

        self.db[sid]["mute_role"] = str(role.id)

        update_db(self.sql_db, self.db, "admin")

        await ctx.send(f":white_check_mark: Mute role set to: {role.name}.")
Пример #14
0
    async def cmd_logs_channel(self, ctx: Context, channel: TextChannel):
        """Set the server's message logging channel.
        MUST HAVE SERVER ADMINISTRATOR PERMISSION
        """
        sid = str(ctx.guild.id)

        if sid not in self.bot.servers:
            self.bot.servers[sid] = {}

        self.bot.servers[sid]["log_channel"] = str(channel.id)
        update_db(self.bot.db, self.bot.servers, "servers")

        await ctx.send(
            f":white_check_mark: Logging channel set to {channel.mention}.")
Пример #15
0
    async def script_remove(self, ctx: Context, prefix: str):
        """Remove a custom response.
        Manage messages permission required.
        """
        sid = str(ctx.guild.id)

        try:
            del self.db[sid]["complex"][prefix]
            update_db(self.sql_db, self.db, "servers")
            await ctx.send(
                f":white_check_mark: Script response `{prefix}` removed.")
        except KeyError:
            await ctx.send(f":anger: There is no script response `{prefix}` "
                           "registered on this server.")
Пример #16
0
    async def text_remove(self, ctx: Context, name: str):
        """Remove a custom text command.
        Manage messages permission required.
        """
        sid = str(ctx.guild.id)

        try:
            del self.db[sid]["text"][name]
            update_db(self.sql_db, self.db, "servers")
            await ctx.send(f":white_check_mark: Command `{name}` removed.")
        except KeyError:
            await ctx.send(
                f":anger: There is no command `{name}` registered on this server."
            )
Пример #17
0
    async def role_react_add(self, ctx: Context, message: Message,
                             role_get: Role, *, description: str):
        """Add a new reaction-based role to a message in your server.
        This will start a very quick interactive process for you to select the reaction.
        Server administrator permission required.
        """
        sid = str(ctx.guild.id)

        if sid not in self.db:
            self.db[sid] = {"reacts": {}}
        elif "reacts" not in self.db[sid]:
            self.db[sid]["reacts"] = {}

        prompt = await ctx.send("React to this message to set your emoji.")

        try:
            reaction, _ = await ctx.bot.wait_for(
                "reaction_add",
                timeout=20.0,
                check=lambda r, m: m == ctx.message.author and r.message.id ==
                prompt.id,
            )
        except asyncio.TimeoutError:
            await ctx.send(":anger: You took too long to react!")
            return

        mid = str(message.id)

        if mid not in self.db[sid]["reacts"]:
            self.db[sid]["reacts"][mid] = {}

        # Convert the reaction emoji to a string if needed
        if isinstance(reaction.emoji, (Emoji, PartialEmoji)):
            reaction = reaction.emoji.name
        else:
            reaction = reaction.emoji

        self.db[sid]["reacts"][mid][role_get.name] = {
            "description": description,
            "id": role_get.id,
            "reaction": reaction,
            "channel": message.channel.id,
            "message": message.id,
        }

        update_db(self.sql_db, self.db, "servers")

        await ctx.send(
            f":white_check_mark: Role {role_get.name} added with {reaction}.")
Пример #18
0
    async def ghosts(self, ctx: Context, enabled: bool = True):
        """Set per-server reporting of deleted messages contianing mentions (pings).
        This causes a 'Ghost' notification on the client of the user who was mentioned.

        If enabled, the bot will post a message showing all users mentioned.
        """
        sid = str(ctx.guild.id)

        if sid not in self.bot.servers:
            self.bot.servers[sid] = {}

        self.bot.servers[sid]["report_ghosts"] = enabled
        update_db(self.bot.db, self.bot.servers, "servers")

        await ctx.send(f":white_check_mark: Ghost reporting set to {enabled}.")
Пример #19
0
    async def group_check(self):
        if len(self.db) <= 0:
            return

        for sid in self.db:
            if len(self.db[sid]) <= 0:
                del self.db[sid]
                continue

            for group, data in self.db[sid].items():
                info = data["info"]
                guild = self.bot.get_guild(int(sid))

                # Get the related channels and roles
                category = guild.get_channel(int(info["category"]))
                text_channel = guild.get_channel(int(info["text_channel"]))
                voice_channel = guild.get_channel(int(info["voice_channel"]))
                role = guild.get_role(int(info["role"]))

                # Try to get the latest message from the text channel
                try:
                    last_message = await text_channel.fetch_message(
                        text_channel.last_message_id)
                except Exception as e:
                    # No message found, or there was some other error
                    self.bot.log.error(f"[ERROR][GROUPS]\n    - {e}")
                    last_message = None

                try:
                    last_datetime = last_message.created_at.replace(
                        tzinfo=timezone.utc)
                    delta = datetime.now(tz=timezone.utc) - last_datetime
                    since_sent = int(delta.total_seconds())
                except Exception as e:
                    self.bot.log.error(f"[ERROR][GROUPS]\n    - {e}")
                    since_sent = 1801

                if not voice_channel.members and since_sent >= 1800:
                    try:
                        for chan in (text_channel, voice_channel, category,
                                     role):
                            await chan.delete(
                                reason="Groups Plugin (Inactivity)")

                        del self.db[sid][group]
                        update_db(self.sql_db, self.db, "servers")
                    except Exception as e:
                        self.bot.log.error(f"[ERROR][GROUPS]:\n    - {e}")
Пример #20
0
    async def cmd_plugins_enable(self, ctx: Context, name: str):
        """Enable a loaded plugin (Cog) on the current server.
        Server administrator permission required.
        """
        if name not in self.bot.plugins:
            # There is a distinction between server-loaded and bot-loaded plugins
            # therefore I do not include the .py extension here purposefully
            await ctx.send(f":anger: No plugin {name} is loaded.")
            return
        else:
            sid = str(ctx.guild.id)

            self.bot.servers[sid][name] = True

            update_db(self.bot.db, self.bot.servers, "servers")
            await ctx.send(f":white_check_mark: Plugin {name} enabled on your server.")
Пример #21
0
    async def text_create(self, ctx: Context, name: str, *, text: str):
        """Create or update a new custom text command.
        Manage messages permission required.
        """
        sid = str(ctx.guild.id)

        if sid not in self.db:
            self.db[sid] = {"prefix": "_", "text": {}}
        elif "text" not in self.db[sid]:
            self.db[sid]["text"] = {}

        try:
            self.db[sid]["text"][name] = text
            update_db(self.sql_db, self.db, "servers")
            await ctx.send(f":white_check_mark: Command {name} added!")
        except Exception as e:
            await ctx.send(f":anger: Something went wrong: {e}")
Пример #22
0
    async def mute(
        self, ctx: Context, target: Member, length: int, span: str, *, reason: str
    ):
        """Set a member to the mute role.
        For timing, plural and non-plural spans are accepted (Day, days, minutes, etc).
        Use "max" as the span for pseudo-permanence (10 years).
        Kick member permission required.
        """
        sid = str(ctx.guild.id)
        uid = str(target.id)
        mute_role = None

        try:
            mute_role = ctx.guild.get_role(int(self.db[sid]["mute_role"]))
        except KeyError:
            await ctx.send(":anger: Server has no mute role set.")
            return

        if sid not in self.mute_db:
            self.mute_db[sid] = {}

        # Get the current UTC time, a future time from time_parser, and the difference
        now = datetime.now(tz=timezone.utc)
        future = time_parser(span, length, now)
        length = future - now
        time = pretty_timedelta(length)

        embed = await embed_builder("Muted", target, reason, length)

        await target.send(embed=embed)
        await ctx.send(
            f":white_check_mark: {target.name} muted for {reason}, expires in {time}"
        )

        await target.add_roles(mute_role)

        mute = {
            "issued_by": str(ctx.author.id),
            "reason": reason,
            "expires": str(future.timestamp()),
        }

        self.mute_db[sid][uid] = mute

        update_db(self.sql_db, self.mute_db, "mutes")
        await self.log_to_channel(ctx, target, reason)
Пример #23
0
    async def script_create(self, ctx: Context, prefix: str, *, text: str):
        """Create or update a new script response.
        Manage messages permission required.
        """
        sid = str(ctx.guild.id)

        if sid not in self.db:
            self.db[sid] = {"complex": {}}
        elif "complex" not in self.db[sid]:
            self.db[sid]["complex"] = {}

        try:
            self.db[sid]["complex"][prefix] = text
            update_db(self.sql_db, self.db, "servers")
            await ctx.send(
                f":white_check_mark: Script response {prefix} added!")
        except Exception as e:
            await ctx.send(f":anger: Something went wrong: {e}")
Пример #24
0
    async def role_admin_invokes(self, ctx: Context, remove: bool = None):
        """Manage role commands and confirmation messages being deleted on your server.
        If DeleteCommands is set to True in the bot's config, this will only affect
        the confirmation messages.

        Running the command without arguments will display the current setting.
        Server administrator permission required.
        """
        sid = str(ctx.guild.id)

        if sid not in self.db:
            self.db[sid] = {"remove": False}

        if remove is not None:
            self.db[sid]["remove"] = remove

        update_db(self.sql_db, self.db, "servers")
        await ctx.send(f"Remove role invokes: `{self.db[sid]['remove']}`")
Пример #25
0
    async def tempban_check(self):
        ts = datetime.now(tz=timezone.utc).timestamp()

        if len(self.tempban_db) <= 0:
            return

        for sid in self.tempban_db:
            if len(self.tempban_db[sid]) <= 0:
                continue

            for uid in self.tempban_db[sid]:
                info = self.tempban_db[sid][uid]

                if ts >= float(info["expires"]):
                    guild = self.bot.get_guild(int(sid))
                    await guild.unban(self.bot.get_user(uid))

                    del self.tempban_db[sid][uid]
                    update_db(self.sql_db, self.tempban_db, "temp_bans")
                    self.bot.log.info(f"[ADMIN][TEMPBAN][REMOVE] {uid} in <{guild.name}>")
Пример #26
0
    async def warn_check(self):
        ts = datetime.now(tz=timezone.utc).timestamp()

        if len(self.warn_db) <= 0:
            return

        for sid in self.warn_db:
            if len(self.warn_db[sid]) <= 0:
                continue

            guild = self.bot.get_guild(int(sid))

            for uid in self.warn_db[sid]:
                for i, w in self.warn_db[sid][uid].items():
                    if ts >= float(w["expires"]):
                        del self.warn_db[sid][uid][i]
                        update_db(self.sql_db, self.warn_db, "warns")
                        self.bot.log.info(
                            f"[ADMIN][WARN][REMOVE] {uid}.{i} in <{guild.name}>"
                        )
Пример #27
0
    async def role_admin_remove(self, ctx: Context, *, role_get: Role):
        """Remove a role from the assignable roles list.
        Server administrator permission required.
        """
        sid = str(ctx.guild.id)
        name = role_get.name.lower()

        if not await self.roles_check(ctx):
            return

        if name not in self.db[sid]["roles"]:
            await ctx.send(
                ":anger: That is not an assignable role on this server.")
        else:
            try:
                del self.db[sid]["roles"][name]

                await ctx.send(
                    f":white_check_mark: Removed {role_get.name} from assignable roles."
                )
                update_db(self.sql_db, self.db, "servers")
            except Exception as e:
                await ctx.send(f":anger: Error removing role: {e}")
Пример #28
0
    async def mute_check(self):
        ts = datetime.now(tz=timezone.utc).timestamp()

        if len(self.mute_db) <= 0:
            return

        for sid in self.mute_db:
            if len(self.mute_db[sid]) <= 0:
                continue

            for uid, info in self.mute_db[sid].items():
                if ts >= float(info["expires"]):
                    guild = self.bot.get_guild(int(sid))

                    try:
                        role = guild.get_role(int(self.db[sid]["mute_role"]))
                    # Delete the mute from the database if we're unable to get the role
                    except KeyError:
                        del self.mute_db[sid][uid]
                        break

                    target = guild.get_member(int(uid))

                    if role in target.roles:
                        await target.remove_roles(role, reason="Auto mute remove.")
                        await target.send(
                            f":speaking_head: Your mute in {guild.name} has expired."
                        )
                    else:
                        del self.mute_db[sid][uid]
                        break

                    del self.mute_db[sid][uid]
                    update_db(self.sql_db, self.mute_db, "mutes")
                    self.bot.log.info(
                        f"[ADMIN][MUTE][REMOVE] {target.id} in <{guild.name}>"
                    )
Пример #29
0
    async def cmd_plugins_load(self, ctx: Context, name: str):
        """Load plugin (Cog). Do not include file extension.
        Botmaster required.
        """
        if name in self.bot.plugins:
            await ctx.send(f":anger: Plugin {name}.py already loaded.")
            return

        if not os.path.isfile(f"plugins/{name}.py"):
            await ctx.send(f":anger: Cannot find plugins/{name}.py")
        else:
            try:
                self.copy_plugin_if_needed(name)

                self.bot.load_extension(f"plugins.{name}")
                self.bot.plugins.append(name)

                update_db(self.bot.db, self.bot.plugins, "plugins")
                await ctx.send(
                    f":white_check_mark: Plugin {name}.py successfully loaded."
                )
            except Exception as e:
                exc = f"{type(e).__name__}, {e}"
                await ctx.send(f":anger: Error loading {name}.py:\n```py\n{exc}\n```")
Пример #30
0
    async def block(self, ctx: Context, target: User, block: bool = True):
        """Add or remove a user from the block list.
        Botmaster required.
        """
        uid = str(target.id)

        if uid in self.bot.blocklist:
            # Trying to block a user who is already blocked
            if block:
                await ctx.send(f":anger: {target.name} is already blocked.")
            # Unblock a user.
            else:
                self.bot.blocklist.remove(uid)
                await ctx.send(f":white_check_mark: {target.name} unblocked.")
        else:
            # Add a user to the blocklist
            if block:
                self.bot.blocklist.append(uid)
                await ctx.send(f":white_check_mark: {target.name} blocked.")
            # Trying to remove a user who is not blocklisted
            else:
                await ctx.send(f":anger: {target.name} is not blocked.")

        update_db(self.bot.db, self.bot.blocklist, "blocklist")