コード例 #1
0
ファイル: objects.py プロジェクト: zephyrkul/phen-cogs
 def run(self,
         interpreter: tse.Interpreter,
         seed_variables: dict = {},
         **kwargs) -> tse.Response:
     self.uses += 1
     seed_variables.update(uses=tse.IntAdapter(self.uses))
     return interpreter.process(self.tagscript, seed_variables, **kwargs)
コード例 #2
0
ファイル: calculator.py プロジェクト: P4RZ1V4L-93/Cogs
class Calculator(commands.Cog):
    """
    Do math
    """
    def __init__(self, bot):
        self.bot = bot
        blocks = [
            block.MathBlock(),
            block.RandomBlock(),
            block.RangeBlock(),
        ]
        self.engine = Interpreter(blocks)

    async def red_delete_data_for_user(self, **kwargs):
        return

    @commands.command(aliases=["calc"])
    async def calculate(self, ctx, *, query):
        """Math"""
        query = query.replace(",", "")
        engine_input = "{m:" + query + "}"
        start = time.monotonic()
        output = self.engine.process(engine_input)
        end = time.monotonic()

        output_string = output.body.replace("{m:", "").replace("}", "")
        e = discord.Embed(
            color=await ctx.embed_color(),
            title=f"Input: `{query}`",
            description=f"Output: `{output_string}`",
        )
        e.set_footer(text=f"Calculated in {round((end - start) * 1000, 3)} ms")
        await ctx.send(embed=e)
コード例 #3
0
class Utilities(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        blocks = [
            block.MathBlock(),
            block.RandomBlock(),
            block.RangeBlock(),
        ]
        self.engine = Interpreter(blocks)

    @commands.command(name="iplookup", aliases=["ip", "ipinfo"])
    async def iplookup(self, ctx, arg):
        suffix = (arg)
        lookup = ("http://ip-api.com/json/" + suffix + "?fields=66846719")
        values = requests.get(lookup).json()
        if values['status'] == "fail":
            return await ctx.send("Please provide a valid argument")
        embed = discord.Embed(
            colour=await self.bot.get_embed_color(ctx.channel))
        embed.set_author(name=f"🌐 IP Lookup Details: {arg}")
        embed.add_field(name="IP",
                        value=values['query'] or "N/A",
                        inline=False)
        embed.add_field(name="Mobile",
                        value=values['mobile'] or "N/A",
                        inline=True)
        embed.add_field(name="Proxy",
                        value=values['proxy'] or "N/A",
                        inline=True)
        embed.add_field(name="Hosting",
                        value=values['hosting'] or "N/A",
                        inline=True)
        embed.add_field(name="Continent",
                        value=values['continent'] or "N/A",
                        inline=True)
        embed.add_field(name="Country",
                        value=values['country'] or "N/A",
                        inline=True)
        embed.add_field(name="Region",
                        value=values['regionName'] or "N/A",
                        inline=True)
        embed.add_field(name="City",
                        value=values['city'] or "N/A",
                        inline=True)
        embed.add_field(name="District",
                        value=values['district'] or "N/A",
                        inline=True)
        embed.add_field(name="Zip", value=values['zip'] or "N/A", inline=True)
        embed.add_field(name="Latitude",
                        value=values['lat'] or "N/A",
                        inline=True)
        embed.add_field(name="Longitude",
                        value=values['lon'] or "N/A",
                        inline=True)
        embed.add_field(name="💸 Currency",
                        value=values['currency'] or "N/A",
                        inline=True)
        embed.add_field(name="⏲️ Timezone",
                        value=values['timezone'] or "N/A",
                        inline=True)
        embed.add_field(name="🌐 ISP/Organization",
                        value=values['isp'] or values['org']
                        or "Not available",
                        inline=False)
        embed.set_footer(text=f"Requested by: {ctx.message.author}",
                         icon_url=ctx.message.author.avatar_url)
        await ctx.send(embed=embed)

    @commands.command(aliases=["calc"])
    async def calculate(self, ctx, *, query):
        """Math"""
        query = query.replace(",", "")
        engine_input = "{m:" + query + "}"
        start = time.monotonic()
        output = self.engine.process(engine_input)
        end = time.monotonic()

        output_string = output.body.replace("{m:", "").replace("}", "")
        e = discord.Embed(
            color=await ctx.embed_color(),
            title=f"Input: `{query}`",
            description=f"Output: `{output_string}`",
        )
        e.set_footer(text=f"Calculated in {round((end - start) * 1000, 3)} ms")
        await ctx.send(embed=e)
コード例 #4
0
ファイル: tags.py プロジェクト: SergeTFM/phen-cogs
class Tags(commands.Cog):
    """
    Create and use tags.
    """
    __version__ = "0.1.1"

    def format_help_for_context(self, ctx):
        pre_processed = super().format_help_for_context(ctx)
        n = "\n" if "\n\n" not in pre_processed else ""
        return f"{pre_processed}{n}\nCog Version: {self.__version__}"

    def __init__(self, bot: Red) -> None:
        self.bot = bot
        self.config = Config.get_conf(
            self,
            identifier=567234895692346562369,
            force_registration=True,
        )
        default_guild = {"tags": {}}
        self.config.register_guild(**default_guild)
        blocks = [
            block.MathBlock(),
            block.RandomBlock(),
            block.RangeBlock(),
            block.AnyBlock(),
            block.IfBlock(),
            block.AllBlock(),
            block.BreakBlock(),
            block.StrfBlock(),
            block.StopBlock(),
            block.AssignmentBlock(),
            block.FiftyFiftyBlock(),
            block.ShortCutRedirectBlock("message"),
            block.LooseVariableGetterBlock(),
            block.SubstringBlock(),
        ]
        self.engine = Interpreter(blocks)

    async def red_delete_data_for_user(self, *, requester: str, user_id: int):
        guilds_data = await self.config.all_guilds()
        for guild_id, data in guilds_data.items():
            guild = self.bot.get_guild(guild_id)
            if guild and data["tags"]:
                for name, tag in data["tags"].items():
                    if str(user_id) in str(tag["author"]):
                        async with self.config.guild(guild).tags() as t:
                            del t[name]

    @commands.guild_only()
    @commands.group(invoke_without_command=True, usage="<tag_name> [args]")
    async def tag(self,
                  ctx,
                  response: Optional[bool],
                  tag_name: tag_name,
                  *,
                  args: str = ""):
        """Tag management with TagScript.

        These commands use TagScriptEngine. [This site](https://github.com/JonSnowbd/TagScript/blob/v2/Documentation/Using%20TSE.md) has documentation on how to use TagScript blocks."""
        if response is None:
            response = True
        tag_data = await self.get_stored_tag(ctx, tag_name, response)
        if tag_data:
            tag = tag_data["tag"]
            async with self.config.guild(ctx.guild).tags() as t:
                t[tag_name]["uses"] += 1
            await self.process_tag(ctx, tag, args)

    @commands.mod_or_permissions(manage_guild=True)
    @tag.command()
    async def add(self, ctx, tag_name: tag_name, *, tagscript):
        """Add a tag with TagScript."""
        command = self.bot.get_command(tag_name)
        if command:
            await ctx.send(f"`{tag_name}` is already a registered command.")
            return

        tag = await self.get_stored_tag(ctx, tag_name, False)
        if tag:
            msg = await ctx.send(
                f"`{tag_name}` is already registered tag. Would you like to overwrite it?"
            )
            start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
            pred = ReactionPredicate.yes_or_no(msg, ctx.author)
            await ctx.bot.wait_for("reaction_add", check=pred)

            if pred.result is False:
                await ctx.send("Action cancelled.")
                return

        await self.store_tag(ctx, tag_name, tagscript)

    @commands.mod_or_permissions(manage_guild=True)
    @tag.command()
    async def edit(self, ctx, tag_name: tag_name, *, tagscript):
        """Edit a tag with TagScript."""
        tag = await self.get_stored_tag(ctx, tag_name, False)
        if not tag:
            return

        async with self.config.guild(ctx.guild).tags() as t:
            t[tag_name]["tag"] = tagscript
        await ctx.send(f"Tag `{tag_name}` edited.")

    @commands.mod_or_permissions(manage_guild=True)
    @tag.command(aliases=["delete"])
    async def remove(self, ctx, tag_name: tag_name):
        """Delete a tag."""
        tag = await self.get_stored_tag(ctx, tag_name)
        if tag:
            async with self.config.guild(ctx.guild).tags() as e:
                del e[tag_name]
            await ctx.send("Tag deleted.")

    @tag.command(name="info")
    async def tag_info(self, ctx, name: str):
        """Get info about an tag that is stored on this server."""
        tag = await self.get_stored_tag(ctx, name)
        if tag:
            e = discord.Embed(
                color=await ctx.embed_color(),
                title=f"`{name}` Info",
                description=
                f"Author: <@!{tag['author']}>\nUses: {tag['uses']}\nLength: {len(tag['tag'])}",
            )
            e.add_field(name="TagScript", value=box(tag["tag"]))
            e.set_author(name=ctx.guild, icon_url=ctx.guild.icon_url)
            await ctx.send(embed=e)

    @tag.command(name="raw")
    async def tag_raw(self, ctx, name: str):
        """Get a tag's raw content."""
        tag = await self.get_stored_tag(ctx, name)
        if tag:
            await ctx.send(
                escape_markdown(tag["tag"]),
                allowed_mentions=discord.AllowedMentions(everyone=False,
                                                         roles=False,
                                                         users=False),
            )

    @tag.command(name="list")
    async def tag_list(self, ctx):
        """View stored tags."""
        tags = await self.config.guild(ctx.guild).tags()
        description = []

        for name, tag in tags.items():
            description.append(f"`{name}` - Created by <@!{tag['author']}>")
        description = "\n".join(description)

        color = await self.bot.get_embed_colour(ctx)
        e = discord.Embed(color=color,
                          title=f"Stored Tags",
                          description=description)
        e.set_author(name=ctx.guild, icon_url=ctx.guild.icon_url)
        await ctx.send(embed=e)

    @commands.is_owner()
    @commands.mod_or_permissions(manage_guild=True)
    @tag.command(aliases=["execute"])
    async def run(self, ctx, *, tagscript):
        """Execute TagScript without storing."""
        start = time.monotonic()
        output = self.engine.process(tagscript)
        end = time.monotonic()

        e = discord.Embed(
            color=await ctx.embed_color(),
            title="TagScriptEngine",
            description=f"Executed in **{round((end - start) * 1000, 3)}** ms",
        )
        e.add_field(name="Input", value=tagscript, inline=False)
        if output.actions:
            e.add_field(name="Actions", value=output.actions)
        if output.variables:
            e.add_field(name="Variables", value=output.variables)
        e.add_field(name="Output",
                    value=output.body or discord.Embed.Empty,
                    inline=False)

        m = await ctx.send(embed=e)

    @commands.is_owner()
    @tag.command()
    async def process(self, ctx: commands.Context, *, tagscript: str):
        """Process TagScript without storing."""
        await self.process_tag(ctx, tagscript, "")

    async def store_tag(self, ctx: commands.Context, name: str,
                        tagscript: str):
        async with self.config.guild(ctx.guild).tags() as t:
            t[name] = {"author": ctx.author.id, "uses": 0, "tag": tagscript}
        await ctx.send(f"Tag stored under the name `{name}`.")

    async def get_stored_tag(self,
                             ctx: commands.Context,
                             name: tag_name,
                             response: bool = True):
        tags = await self.config.guild(ctx.guild).tags()
        try:
            tag = tags[name]
        except KeyError:
            if response:
                await ctx.send(f'Tag "{name}" not found.')
            return
        return tag

    @commands.Cog.listener()
    async def on_message_without_command(self, message: discord.Message):
        if message.author.bot or not (
                message.guild
                and await self.bot.message_eligible_as_command(message)):
            return
        ctx = await self.bot.get_context(message)
        if ctx.prefix is None:
            return

        tag_command = message.content[len(ctx.prefix):]
        tag_split = tag_command.split(" ")
        if not tag_split:
            return
        tag_name = tag_split[0]
        tag = await self.get_stored_tag(ctx, tag_name, False)
        if tag:
            new_message = copy(message)
            new_message.content = f"{ctx.prefix}tag False {tag_command}"
            await self.bot.process_commands(new_message)

    async def process_tag(self, ctx: commands.Context, tagscript: str,
                          args: str) -> str:
        output = self.engine.process(tagscript)
        if output.body:
            o = output.body.replace("{args}", args)
            commands = COM_RE.findall(o)
            to_process = []
            if commands:
                o = re.sub(COM_RE, "", o)
                for index, command in enumerate(commands):
                    if index > 2:
                        break
                    if command.startswith("tag"):
                        await ctx.send("Looping isn't allowed.")
                        return
                    new = copy(ctx.message)
                    new.content = ctx.prefix + command
                    to_process.append(self.bot.process_commands(new))
            if "".join(o.strip()):
                await ctx.send(o)
            if to_process:
                await asyncio.gather(*to_process)
コード例 #5
0
class Math(commands.Cog):
    """Do math"""
    def __init__(self, bot):
        self.bot = bot
        blocks = [
            block.MathBlock(),
            block.RandomBlock(),
            block.RangeBlock(),
        ]
        self.engine = Interpreter(blocks)

        # Initialize a session
        self.session = aiohttp.ClientSession()

    @commands.command()
    async def math(self, ctx, *expression):
        """ Solve a math expression. Supports very complex problems too.
			For eg. `luci math sin(pi/4)`
		"""
        log = logging.getLogger("math")

        if (expression == ""):
            embed = discord.Embed(
                color=0xf34949,  # Red							
                title="Input A Expression")
            await ctx.send(embed=embed)
            return

        start = time.monotonic()

        api = "http://api.mathjs.org/v4/"
        params = {"expr": "".join(expression)}

        async with self.session.get(api, params=params) as response:
            end = time.monotonic()

            if (response.status != 200):
                log.info(expression)
                log.error(await response.text())
                return

            embed = discord.Embed(
                color=0xf34949,  # Red					
                title=await response.text())
            embed.add_field(name="Your Input:",
                            value=f'`{"".join(expression)}`',
                            inline=True)
            embed.add_field(name="Answer:",
                            value=f"`{await response.text()}`",
                            inline=True)
            embed.set_footer(
                text=f"Calculated in {round((end - start) * 1000, 3)} ms")
            await ctx.send(embed=embed)

    @commands.command(aliases=["calc"])
    async def calculate(self, ctx, *, query):
        """ Faster but sometimes does not work."""

        query = query.replace(",", "")
        engine_input = "{m:" + query + "}"
        start = time.monotonic()
        output = self.engine.process(engine_input)
        end = time.monotonic()

        output_string = output.body.replace("{m:", "").replace("}", "")
        embed = discord.Embed(
            color=0xf34949,
            title=f"Input: `{query}`",
            description=f"Output: `{output_string}`",
        )
        embed.set_footer(
            text=f"Calculated in {round((end - start) * 1000, 3)} ms")
        await ctx.send(embed=embed)

    async def get_result(self, ctx, operation, expression):
        # First properly encode expression
        # Change "/" to (over) because the api says so
        if ("/" in expression):
            expression = expression.replace("/", "(over)")

        # Encode the url now
        encoded_expression = parse.quote(expression)
        api = "https://newton.now.sh/api/v2"

        start = time.monotonic()
        async with self.session.get(
                f"{api}/{operation}/{encoded_expression}") as response:
            data = await response.json()

            if ("error" in data.keys()):
                # Get coolcry emoji
                coolcry = self.bot.get_emoji(780445565476798475)

                embed = discord.Embed(
                    title=
                    f"Sorry! But the server was unable to solve the expression {coolcry}",
                    color=0xf34949)
                return embed

            result = data["result"]
            end = time.monotonic()

            embed = discord.Embed(
                color=0xf34949,  # Red					
                title=result)
            embed.add_field(name="Your Input:",
                            value=f'`{expression.replace("(over)", "/")}`',
                            inline=True)
            embed.add_field(name="Answer:", value=f"`{result}`", inline=True)
            embed.set_footer(
                text=f"Calculated in {round((end - start) * 1000, 3)} ms")
            return embed

    @commands.command(aliases=["factor"])
    async def factorise(self, ctx, *expression):
        """Factorise a polynomial equation. Please put tan(x) instead of tanx and so on for all trigonametric functions.
		Usage: `luci factorise x^2 + 2x`"""
        expression = "".join(expression)

        loading = await ctx.send("Calculating...")
        await ctx.trigger_typing()

        embed = await self.get_result(ctx=ctx,
                                      operation="factor",
                                      expression=expression)

        # First delete the calculating message
        await loading.delete()
        await ctx.send(embed=embed)

    @commands.command(aliases=["derivation", "differentiate"])
    async def derive(self, ctx, *expression):
        """Differentiate a polynomial. Please put tan(x) instead of tanx and so on for all trigonametric functions.
		Usage: `luci derive x^2 + 2x`"""
        expression = "".join(expression)

        loading = await ctx.send("Calculating...")
        await ctx.trigger_typing()

        embed = await self.get_result(ctx=ctx,
                                      operation="derive",
                                      expression=expression)

        # First delete the calculating message
        await loading.delete()
        await ctx.send(embed=embed)

    @commands.command(aliases=["integration"])
    async def integrate(self, ctx, *expression):
        """Integrate a polynomial. Please put tan(x) instead of tanx and so on for all trigonametric functions.
		Usage: `luci integrate x^2 + 2x`"""
        expression = "".join(expression)

        loading = await ctx.send("Calculating...")
        await ctx.trigger_typing()

        embed = await self.get_result(ctx=ctx,
                                      operation="integrate",
                                      expression=expression)

        # First delete the calculating message
        await loading.delete()
        await ctx.send(embed=embed)

    @commands.command(aliases=["solution", "zeroes", "roots"])
    async def solve(self, ctx, *expression):
        """Find roots of a polynomial. Please put tan(x) instead of tanx and so on for all trigonametric functions.
		Usage: `luci roots x^2 + 2x`"""
        expression = "".join(expression)

        loading = await ctx.send("Calculating...")
        await ctx.trigger_typing()

        embed = await self.get_result(ctx=ctx,
                                      operation="zeroes",
                                      expression=expression)

        # First delete the calculating message
        await loading.delete()
        await ctx.send(embed=embed)

    @commands.command()
    async def tangent(self, ctx, *expression):
        """Find tangent of a curve at a point [Eg: `luci tangent 2|x^3`. See `luci help tangent`]. Please put tan(x) instead of tanx and so on for all trigonametric functions.
		Usage: `luci tangent 2|x^3` # Here 2 is the point where tangent is to be find"""
        expression = "".join(expression)

        loading = await ctx.send("Calculating...")
        await ctx.trigger_typing()

        embed = await self.get_result(ctx=ctx,
                                      operation="tangent",
                                      expression=expression)

        # First delete the calculating message
        await loading.delete()
        await ctx.send(embed=embed)

    @commands.command(name="area",
                      aliases=["area under the curve", "definite integration"])
    async def definite_integral(self, ctx, *expression):
        """Do definite integration on a polynomial [Eg: `luci area 2:4|x^3`. See `luci help area`]. Please put tan(x) instead of tanx and so on for all trigonametric functions.
		Usage: `luci area 2:4|x^3` # Here area is to be calulated from 2 to 4"""
        expression = "".join(expression)

        loading = await ctx.send("Calculating...")
        await ctx.trigger_typing()

        embed = await self.get_result(ctx=ctx,
                                      operation="area",
                                      expression=expression)

        # First delete the calculating message
        await loading.delete()
        await ctx.send(embed=embed)
コード例 #6
0
class Tags(commands.Cog):
    """
    Create and use tags.

    The TagScript documentation can be found [here](https://phen-cogs.readthedocs.io/en/latest/index.html).
    """

    __version__ = "2.0.5"

    def format_help_for_context(self, ctx: commands.Context):
        pre_processed = super().format_help_for_context(ctx)
        n = "\n" if "\n\n" not in pre_processed else ""
        return f"{pre_processed}{n}\nCog Version: {self.__version__}"

    def __init__(self, bot: Red) -> None:
        self.bot = bot
        self.config = Config.get_conf(
            self,
            identifier=567234895692346562369,
            force_registration=True,
        )
        default_guild = {"tags": {}}
        default_global = {"tags": {}}
        self.config.register_guild(**default_guild)
        self.config.register_global(**default_global)

        blocks = stable_blocks + [
            block.MathBlock(),
            block.RandomBlock(),
            block.RangeBlock(),
            block.AnyBlock(),
            block.IfBlock(),
            block.AllBlock(),
            block.BreakBlock(),
            block.StrfBlock(),
            block.StopBlock(),
            block.AssignmentBlock(),
            block.FiftyFiftyBlock(),
            block.ShortCutRedirectBlock("args"),
            block.LooseVariableGetterBlock(),
            block.SubstringBlock(),
        ]
        self.engine = Interpreter(blocks)
        self.role_converter = commands.RoleConverter()
        self.channel_converter = commands.TextChannelConverter()
        self.member_converter = commands.MemberConverter()
        self.emoji_converter = commands.EmojiConverter()

        self.guild_tag_cache = defaultdict(dict)
        self.global_tag_cache = {}
        self.task = asyncio.create_task(self.cache_tags())

    def cog_unload(self):
        if self.task:
            self.task.cancel()

    async def red_delete_data_for_user(self, *, requester: str, user_id: int):
        if requester not in ("discord_deleted_user", "user"):
            return
        guilds_data = await self.config.all_guilds()
        for guild_id, data in guilds_data.items():
            guild = self.bot.get_guild(guild_id)
            if guild and data["tags"]:
                for name, tag in data["tags"].items():
                    if str(user_id) in str(tag["author"]):
                        async with self.config.guild(guild).tags() as t:
                            del t[name]

    async def cache_tags(self):
        guilds_data = await self.config.all_guilds()
        async for guild_id, guild_data in AsyncIter(guilds_data.items(),
                                                    steps=100):
            async for tag_name, tag_data in AsyncIter(
                    guild_data["tags"].items(), steps=50):
                tag_object = Tag.from_dict(self,
                                           tag_name,
                                           tag_data,
                                           guild_id=guild_id)
                self.guild_tag_cache[guild_id][tag_name] = tag_object

        global_tags = await self.config.tags()
        async for global_tag_name, global_tag_data in AsyncIter(
                global_tags.items(), steps=50):
            global_tag = Tag.from_dict(self, global_tag_name, global_tag_data)
            self.global_tag_cache[global_tag_name] = global_tag
        log.debug("tag cache built")

    def get_tag(
        self,
        guild: Optional[discord.Guild],
        tag_name: str,
        *,
        check_global: bool = True,
        global_priority: bool = False,
    ) -> Optional[Tag]:
        tag = None
        if global_priority is True and check_global is True:
            return self.global_tag_cache.get(tag_name)
        if guild is not None:
            tag = self.guild_tag_cache[guild.id].get(tag_name)
        if tag is None and check_global is True:
            tag = self.global_tag_cache.get(tag_name)
        return tag

    async def validate_tagscript(self, ctx: commands.Context, tagscript: str):
        output = self.engine.process(tagscript)
        is_owner = await self.bot.is_owner(ctx.author)
        if is_owner:
            return True
        author_perms = ctx.channel.permissions_for(ctx.author)
        if output.actions.get("overrides"):
            if not author_perms.manage_guild:
                raise MissingTagPermissions(
                    "You must have **Manage Server** permissions to use the `override` block."
                )
        if output.actions.get("allowed_mentions"):
            # if not author_perms.mention_everyone:
            if not is_owner:
                raise MissingTagPermissions(
                    "You must have **Mention Everyone** permissions to use the `allowedmentions` block."
                )
        return True

    @commands.command(usage="<tag_name> [args]")
    async def invoketag(self,
                        ctx,
                        response: Optional[bool],
                        tag_name: str,
                        *,
                        args: Optional[str] = ""):
        """
        Manually invoke a tag with its name and arguments.

        Restricting this command with permissions in servers will restrict all members from invoking tags.
        """
        response = response or True
        try:
            _tag = await TagConverter(check_global=True).convert(ctx, tag_name)
        except commands.BadArgument as e:
            if response is True:
                await ctx.send(e)
        else:
            seed = {"args": adapter.StringAdapter(args)}
            await self.process_tag(ctx, _tag, seed_variables=seed)

    @commands.guild_only()
    @commands.group(aliases=["customcom"])
    async def tag(self, ctx: commands.Context):
        """
        Tag management with TagScript.

        These commands use TagScriptEngine. [This site](https://phen-cogs.readthedocs.io/en/latest/index.html) has documentation on how to use TagScript blocks.
        """

    @commands.mod_or_permissions(manage_guild=True)
    @tag.command(name="add", aliases=["create", "+"])
    async def tag_add(self, ctx: commands.Context, tag_name: TagName, *,
                      tagscript: TagScriptConverter):
        """
        Add a tag with TagScript.

        [Tag usage guide](https://phen-cogs.readthedocs.io/en/latest/blocks.html#usage)
        """
        tag = self.get_tag(ctx.guild, tag_name)
        if tag:
            msg = await ctx.send(
                f"`{tag_name}` is already registered tag. Would you like to overwrite it?"
            )
            start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
            pred = ReactionPredicate.yes_or_no(msg, ctx.author)
            try:
                await ctx.bot.wait_for("reaction_add", check=pred, timeout=30)
            except asyncio.TimeoutError:
                return await ctx.send("Tag edit cancelled.")

            if pred.result is False:
                return await ctx.send("Tag edit cancelled.")
            tag.tagscript = tagscript
            await tag.update_config()
            await ctx.send(f"Tag `{tag}` edited.")
            return

        tag = Tag(self,
                  tag_name,
                  tagscript,
                  author_id=ctx.author.id,
                  guild_id=ctx.guild.id)
        self.guild_tag_cache[ctx.guild.id][tag_name] = tag
        await tag.update_config()
        await ctx.send(f"Tag `{tag}` added.")

    @commands.mod_or_permissions(manage_guild=True)
    @tag.command(name="edit", aliases=["e"])
    async def tag_edit(self, ctx: commands.Context, tag: TagConverter, *,
                       tagscript: TagScriptConverter):
        """Edit a tag with TagScript."""
        tag.tagscript = tagscript
        await tag.update_config()
        await ctx.send(f"Tag `{tag}` edited.")

    @commands.mod_or_permissions(manage_guild=True)
    @tag.command(name="remove", aliases=["delete", "-"])
    async def tag_remove(self, ctx: commands.Context, tag: TagConverter):
        """Delete a tag."""
        await tag.delete()
        await ctx.send(f"Tag `{tag}` deleted.")

    @tag.command(name="info")
    async def tag_info(self, ctx: commands.Context, tag: TagConverter):
        """Get info about an tag that is stored on this server."""
        desc = [
            f"Author: {tag.author.mention if tag.author else tag.author_id}",
            f"Uses: {tag.uses}",
            f"Length: {len(tag)}",
        ]
        e = discord.Embed(
            color=await ctx.embed_color(),
            title=f"Tag `{tag}` Info",
            description="\n".join(desc),
        )
        e.set_author(name=ctx.guild, icon_url=ctx.guild.icon_url)
        await ctx.send(embed=e)

    @tag.command(name="raw")
    async def tag_raw(self, ctx: commands.Context, tag: TagConverter):
        """Get a tag's raw content."""
        for page in pagify(tag.tagscript, shorten_by=100):
            await ctx.send(
                escape_markdown(page),
                allowed_mentions=discord.AllowedMentions.none(),
            )

    @tag.command(name="list")
    async def tag_list(self, ctx: commands.Context):
        """View stored tags."""
        tags = self.guild_tag_cache[ctx.guild.id]
        if not tags:
            return await ctx.send("There are no stored tags on this server.")
        description = []

        for name, tag in tags.items():
            tagscript = tag.tagscript
            if len(tagscript) > 23:
                tagscript = tagscript[:20] + "..."
            tagscript = tagscript.replace("\n", " ")
            description.append(f"`{name}` - {escape_markdown(tagscript)}")
        description = "\n".join(description)

        e = discord.Embed(color=await ctx.embed_color())
        e.set_author(name="Stored Tags", icon_url=ctx.guild.icon_url)

        embeds = []
        pages = list(pagify(description))
        for index, page in enumerate(pages, 1):
            embed = e.copy()
            embed.description = page
            embed.set_footer(text=f"{index}/{len(pages)} | {len(tags)} tags")
            embeds.append(embed)
        await menu(ctx, embeds, DEFAULT_CONTROLS)

    @commands.is_owner()
    @tag.command(name="run", aliases=["execute"])
    async def tag_run(self, ctx: commands.Context, *, tagscript: str):
        """Execute TagScript without storing."""
        start = time.monotonic()
        author = MemberAdapter(ctx.author)
        target = MemberAdapter(
            ctx.message.mentions[0]) if ctx.message.mentions else author
        channel = ChannelAdapter(ctx.channel)
        guild = GuildAdapter(ctx.guild)
        seed = {
            "author": author,
            "user": author,
            "target": target,
            "member": target,
            "channel": channel,
            "guild": guild,
            "server": guild,
        }
        output = self.engine.process(tagscript, seed_variables=seed)
        end = time.monotonic()

        e = discord.Embed(
            color=await ctx.embed_color(),
            title="TagScriptEngine",
            description=f"Executed in **{round((end - start) * 1000, 3)}** ms",
        )
        e.add_field(name="Input", value=tagscript, inline=False)
        if output.actions:
            e.add_field(name="Actions", value=output.actions, inline=False)
        if output.variables:
            vars = "\n".join([
                f"{name}: {type(obj).__name__}"
                for name, obj in output.variables.items()
            ])
            e.add_field(name="Variables", value=vars, inline=False)
        e.add_field(name="Output",
                    value=output.body or "NO OUTPUT",
                    inline=False)

        await ctx.send(embed=e)

    @commands.is_owner()
    @tag.command(name="process")
    async def tag_process(self, ctx: commands.Context, *, tagscript: str):
        """Process TagScript without storing."""
        tag = Tag(
            self,
            "processed_tag",
            tagscript,
            author_id=ctx.author.id,
            real=False,
        )
        await self.process_tag(ctx, tag)
        await ctx.tick()

    @commands.is_owner()
    @tag.group(name="global")
    async def tag_global(self, ctx: commands.Context):
        """Manage global tags."""

    @tag_global.command(name="add", aliases=["create", "+"])
    async def tag_global_add(self, ctx: commands.Context, tag_name: TagName, *,
                             tagscript: TagScriptConverter):
        """
        Add a global tag with TagScript.

        [Tag usage guide](https://phen-cogs.readthedocs.io/en/latest/blocks.html#usage)
        """
        tag = self.get_tag(None, tag_name, check_global=True)
        if tag:
            msg = await ctx.send(
                f"`{tag_name}` is already registered global tag. Would you like to overwrite it?"
            )
            start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
            pred = ReactionPredicate.yes_or_no(msg, ctx.author)
            try:
                await ctx.bot.wait_for("reaction_add", check=pred, timeout=30)
            except asyncio.TimeoutError:
                return await ctx.send("Global tag edit cancelled.")

            if pred.result is False:
                return await ctx.send("Global tag edit cancelled.")
            tag.tagscript = tagscript
            await tag.update_config()
            await ctx.send(f"Global tag `{tag}` edited.")
            return

        tag = Tag(self, tag_name, tagscript, author_id=ctx.author.id)
        self.global_tag_cache[tag_name] = tag
        await tag.update_config()
        await ctx.send(f"Global tag `{tag}` added.")

    @tag_global.command(name="edit", aliases=["e"])
    async def tag_global_edit(
        self,
        ctx: commands.Context,
        tag: TagConverter(check_global=True, global_priority=True),
        *,
        tagscript: TagScriptConverter,
    ):
        """Edit a global tag with TagScript."""
        tag.tagscript = tagscript
        await tag.update_config()
        await ctx.send(f"Global tag `{tag}` edited.")

    @tag_global.command(name="remove", aliases=["delete", "-"])
    async def tag_global_remove(self, ctx: commands.Context,
                                tag: TagConverter(check_global=True,
                                                  global_priority=True)):
        """Delete a global tag."""
        await tag.delete()
        await ctx.send(f"Global tag `{tag}` deleted.")

    @tag_global.command(name="info")
    async def tag_global_info(self, ctx: commands.Context,
                              tag: TagConverter(check_global=True,
                                                global_priority=True)):
        """Get info about a global tag."""
        desc = [
            f"Author: {tag.author.mention if tag.author else tag.author_id}",
            f"Uses: {tag.uses}",
            f"Length: {len(tag)}",
        ]
        e = discord.Embed(
            color=await ctx.embed_color(),
            title=f"Tag `{tag}` Info",
            description="\n".join(desc),
        )
        e.set_author(name=ctx.me, icon_url=ctx.me.avatar_url)
        await ctx.send(embed=e)

    @tag_global.command(name="raw")
    async def tag_global_raw(self, ctx: commands.Context,
                             tag: TagConverter(check_global=True,
                                               global_priority=True)):
        """Get a tag's raw content."""
        for page in pagify(tag.tagscript, shorten_by=100):
            await ctx.send(
                escape_markdown(page),
                allowed_mentions=discord.AllowedMentions.none(),
            )

    @tag_global.command(name="list")
    async def tag_global_list(self, ctx: commands.Context):
        """View stored tags."""
        tags = self.global_tag_cache
        if not tags:
            return await ctx.send("There are no global tags.")
        description = []

        for name, tag in tags.items():
            tagscript = tag.tagscript
            if len(tagscript) > 23:
                tagscript = tagscript[:20] + "..."
            tagscript = tagscript.replace("\n", " ")
            description.append(f"`{name}` - {escape_markdown(tagscript)}")
        description = "\n".join(description)

        e = discord.Embed(color=await ctx.embed_color())
        e.set_author(name="Global Tags", icon_url=ctx.me.avatar_url)

        embeds = []
        pages = list(pagify(description))
        for index, page in enumerate(pages, 1):
            embed = e.copy()
            embed.description = page
            embed.set_footer(text=f"{index}/{len(pages)} | {len(tags)} tags")
            embeds.append(embed)
        await menu(ctx, embeds, DEFAULT_CONTROLS)

    @commands.is_owner()
    @commands.command()
    async def migratealias(self, ctx: commands.Context):
        """Migrate alias global and guild configs to tags."""
        alias_cog = self.bot.get_cog("Alias")
        if not alias_cog:
            return await ctx.send("Alias cog must be loaded to migrate data.")

        query = await ctx.send(
            f"Are you sure you want to migrate alias data to tags? (Y/n)")
        pred = MessagePredicate.yes_or_no(ctx)
        try:
            response = await self.bot.wait_for("message",
                                               check=pred,
                                               timeout=30)
        except asyncio.TimeoutError:
            return await ctx.send(
                "Query timed out, not migrating alias to tags.")

        if pred.result is False:
            return await ctx.send("Migration cancelled.")

        migrated_guilds = 0
        migrated_guild_alias = 0
        all_guild_data: dict = await alias_cog.config.all_guilds()

        async for guild_id, guild_data in AsyncIter(all_guild_data.items(),
                                                    steps=100):
            if not guild_data["entries"]:
                continue
            migrated_guilds += 1
            for alias in guild_data["entries"]:
                tagscript = "{c:" + alias["command"] + " {args}}"
                tag = Tag(
                    self,
                    alias["name"],
                    tagscript,
                    author_id=alias["creator"],
                    guild_id=alias["guild"],
                    uses=alias["uses"],
                )
                self.guild_tag_cache[guild_id][alias["name"]] = tag
                await tag.update_config()
                migrated_guild_alias += 1
        await ctx.send(
            f"Migrated {migrated_guild_alias} aliases from {migrated_guilds} "
            "servers to tags. Moving on to global aliases..")

        migrated_global_alias = 0
        async for entry in AsyncIter(await alias_cog.config.entries(),
                                     steps=50):
            tagscript = "{c:" + entry["command"] + " {args}}"
            global_tag = Tag(
                self,
                entry["name"],
                tagscript,
                author_id=entry["creator"],
                uses=entry["uses"],
            )
            self.global_tag_cache[entry["name"]] = global_tag
            await global_tag.update_config()
            migrated_global_alias += 1
        await ctx.send(
            f"Migrated {migrated_global_alias} global aliases to tags. "
            "Migration completed, unload the alias cog to prevent command "
            f"duplication with `{ctx.clean_prefix}unload alias`.")

    @commands.Cog.listener()
    async def on_message_without_command(self, message: discord.Message):
        if message.author.bot:
            return
        if message.guild:
            if not isinstance(message.author, discord.Member):
                return
            if not await self.bot.message_eligible_as_command(message):
                return
        else:
            if not (await self.bot.allowed_by_whitelist_blacklist(
                    message.author)):
                return
        await self.handle_message(message)

    async def handle_message(self, message: discord.Message):
        try:
            prefix = await Alias.get_prefix(self, message)
        except ValueError:
            return
        tag_command = message.content[len(prefix):]
        tag_split = tag_command.split(" ", 1)
        if self.get_tag(message.guild, tag_split[0], check_global=True):
            await self.invoke_tag_message(message, prefix, tag_command)

    async def invoke_tag_message(self, message: discord.Message, prefix: str,
                                 tag_command: str):
        new_message = copy(message)
        new_message.content = f"{prefix}invoketag False {tag_command}"
        ctx = await self.bot.get_context(new_message)
        await self.bot.invoke(ctx)

    async def process_tag(self,
                          ctx: commands.Context,
                          tag: Tag,
                          *,
                          seed_variables: dict = {},
                          **kwargs) -> str:
        author = MemberAdapter(ctx.author)
        target = MemberAdapter(
            ctx.message.mentions[0]) if ctx.message.mentions else author
        channel = ChannelAdapter(ctx.channel)
        seed = {
            "author": author,
            "user": author,
            "target": target,
            "member": target,
            "channel": channel,
        }
        if ctx.guild:
            guild = GuildAdapter(ctx.guild)
            seed.update(guild=guild, server=guild)
        seed_variables.update(seed)

        output = tag.run(self.engine, seed_variables=seed_variables, **kwargs)
        await tag.update_config()
        to_gather = []
        command_messages = []
        content = output.body[:2000] if output.body else None
        actions = output.actions
        embed = actions.get("embed")
        destination = ctx.channel
        replying = False

        if actions:
            try:
                await self.validate_checks(ctx, actions)
            except RequireCheckFailure as error:
                response = error.response
                if response is not None:
                    if response.strip():
                        await ctx.send(response[:2000])
                else:
                    start_adding_reactions(ctx.message, ["❌"])
                return

            if delete := actions.get("delete", False):
                to_gather.append(self.delete_quietly(ctx))

            if delete is False and (reactu := actions.get("reactu")):
                to_gather.append(self.do_reactu(ctx, reactu))

            if actions.get("commands"):
                for command in actions["commands"]:
                    if command.startswith("tag") or command == "invoketag":
                        await ctx.send("Tag looping isn't allowed.")
                        return
                    new = copy(ctx.message)
                    new.content = ctx.prefix + command
                    command_messages.append(new)

            if target := actions.get("target"):
                if target == "dm":
                    destination = await ctx.author.create_dm()
                elif target == "reply":
                    replying = True
                else:
                    try:
                        chan = await self.channel_converter.convert(
                            ctx, target)
                    except commands.BadArgument:
                        pass
                    else:
                        if chan.permissions_for(ctx.me).send_messages:
                            destination = chan
コード例 #7
0
class Tags(commands.Cog):
    """
    Create and use tags.

    The TagScript documentation can be found [here](https://phen-cogs.readthedocs.io/en/latest/index.html).
    """

    __version__ = "1.5.0"

    def format_help_for_context(self, ctx: commands.Context):
        pre_processed = super().format_help_for_context(ctx)
        n = "\n" if "\n\n" not in pre_processed else ""
        return f"{pre_processed}{n}\nCog Version: {self.__version__}"

    def __init__(self, bot: Red) -> None:
        self.bot = bot
        self.config = Config.get_conf(
            self,
            identifier=567234895692346562369,
            force_registration=True,
        )
        default_guild = {"tags": {}}
        self.config.register_guild(**default_guild)

        blocks = stable_blocks + [
            block.MathBlock(),
            block.RandomBlock(),
            block.RangeBlock(),
            block.AnyBlock(),
            block.IfBlock(),
            block.AllBlock(),
            block.BreakBlock(),
            block.StrfBlock(),
            block.StopBlock(),
            block.AssignmentBlock(),
            block.FiftyFiftyBlock(),
            block.ShortCutRedirectBlock("args"),
            block.LooseVariableGetterBlock(),
            block.SubstringBlock(),
        ]
        self.engine = Interpreter(blocks)
        self.role_converter = commands.RoleConverter()
        self.channel_converter = commands.TextChannelConverter()
        self.member_converter = commands.MemberConverter()
        self.emoji_converter = commands.EmojiConverter()

        self.guild_tag_cache = defaultdict(dict)
        self.task = asyncio.create_task(self.cache_tags())

    def cog_unload(self):
        if self.task:
            self.task.cancel()

    async def red_delete_data_for_user(self, *, requester: str, user_id: int):
        if requester not in ("discord_deleted_user", "user"):
            return
        guilds_data = await self.config.all_guilds()
        for guild_id, data in guilds_data.items():
            guild = self.bot.get_guild(guild_id)
            if guild and data["tags"]:
                for name, tag in data["tags"].items():
                    if str(user_id) in str(tag["author"]):
                        async with self.config.guild(guild).tags() as t:
                            del t[name]

    async def cache_tags(self):
        await self.bot.wait_until_ready()
        guilds_data = await self.config.all_guilds()
        for guild_id, data in guilds_data.items():
            tags = data.get("tags", {})
            guild = self.bot.get_guild(guild_id) or discord.Object(guild_id)
            for tag_name, tag_data in tags.items():
                tag_object = Tag.from_dict(self, guild, tag_name, tag_data)
                self.guild_tag_cache[guild_id][tag_name] = tag_object
        log.debug("tag cache built")

    def get_tag(self, guild: discord.Guild, tag_name: str):
        return self.guild_tag_cache[guild.id].get(tag_name)

    async def validate_tagscript(self, ctx: commands.Context, tagscript: str):
        output = self.engine.process(tagscript)
        is_owner = await self.bot.is_owner(ctx.author)
        if is_owner:
            return True
        author_perms = ctx.channel.permissions_for(ctx.author)
        if output.actions.get("overrides"):
            if not author_perms.manage_guild:
                raise MissingTagPermissions(
                    "You must have **Manage Server** permissions to use the `override` block."
                )
        if output.actions.get("allowed_mentions"):
            # if not author_perms.mention_everyone:
            if not is_owner:
                raise MissingTagPermissions(
                    "You must have **Mention Everyone** permissions to use the `allowedmentions` block."
                )
        return True

    @commands.guild_only()
    @commands.group(invoke_without_command=True,
                    usage="<tag_name> [args]",
                    aliases=["customcom"])
    async def tag(self,
                  ctx,
                  response: Optional[bool],
                  tag_name: str,
                  *,
                  args: Optional[str] = ""):
        """
        Tag management with TagScript.

        These commands use TagScriptEngine. [This site](https://phen-cogs.readthedocs.io/en/latest/index.html) has documentation on how to use TagScript blocks.
        """
        if response is None:
            response = True
        try:
            _tag = await TagConverter().convert(ctx, tag_name)
        except commands.BadArgument as e:
            if response:
                await ctx.send(e)
            return
        seed = {"args": adapter.StringAdapter(args)}
        log.info(
            f"Processing tag for {tag_name} on {ctx.guild} ({ctx.guild.id})")
        await self.process_tag(ctx, _tag, seed_variables=seed)

    @commands.mod_or_permissions(manage_guild=True)
    @tag.command(aliases=["create", "+"])
    async def add(self, ctx: commands.Context, tag_name: TagName, *,
                  tagscript: TagScriptConverter):
        """
        Add a tag with TagScript.

        [Tag usage guide](https://phen-cogs.readthedocs.io/en/latest/blocks.html#usage)
        """
        tag = self.get_tag(ctx.guild, tag_name)
        if tag:
            msg = await ctx.send(
                f"`{tag_name}` is already registered tag. Would you like to overwrite it?"
            )
            start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
            pred = ReactionPredicate.yes_or_no(msg, ctx.author)
            try:
                await ctx.bot.wait_for("reaction_add", check=pred, timeout=30)
            except asyncio.TimeoutError:
                return await ctx.send("Tag edit cancelled.")

            if pred.result is False:
                return await ctx.send("Tag edit cancelled.")
            tag.tagscript = tagscript
            await tag.update_config()
            await ctx.send(f"Tag `{tag}` edited.")
            return

        tag = Tag(self, ctx.guild, tag_name, tagscript, author=ctx.author)
        self.guild_tag_cache[ctx.guild.id][tag_name] = tag
        async with self.config.guild(ctx.guild).tags() as t:
            t[tag_name] = tag.to_dict()
        await ctx.send(f"Tag `{tag}` added.")

    @commands.mod_or_permissions(manage_guild=True)
    @tag.command(aliases=["e"])
    async def edit(self, ctx: commands.Context, tag: TagConverter, *,
                   tagscript: TagScriptConverter):
        """Edit a tag with TagScript."""
        tag.tagscript = tagscript
        await tag.update_config()
        await ctx.send(f"Tag `{tag}` edited.")
        # await self.cache_tags()

    @commands.mod_or_permissions(manage_guild=True)
    @tag.command(aliases=["delete", "-"])
    async def remove(self, ctx: commands.Context, tag: TagConverter):
        """Delete a tag."""
        async with self.config.guild(ctx.guild).tags() as e:
            del e[str(tag)]
        del self.guild_tag_cache[ctx.guild.id][str(tag)]
        await ctx.send("Tag deleted.")
        # await self.cache_tags()

    @tag.command(name="info")
    async def tag_info(self, ctx: commands.Context, tag: TagConverter):
        """Get info about an tag that is stored on this server."""
        desc = [
            f"Author: {tag.author.mention if tag.author else tag.author_id}",
            f"Uses: {tag.uses}",
            f"Length: {len(tag)}",
        ]
        e = discord.Embed(
            color=await ctx.embed_color(),
            title=f"Tag `{tag}` Info",
            description="\n".join(desc),
        )
        e.set_author(name=ctx.guild, icon_url=ctx.guild.icon_url)
        await ctx.send(embed=e)

    @tag.command(name="raw")
    async def tag_raw(self, ctx: commands.Context, tag: TagConverter):
        """Get a tag's raw content."""
        await ctx.send(
            escape_markdown(tag.tagscript[:2000]),
            allowed_mentions=discord.AllowedMentions.none(),
        )

    @tag.command(name="list")
    async def tag_list(self, ctx: commands.Context):
        """View stored tags."""
        tags = self.guild_tag_cache[ctx.guild.id]
        if not tags:
            return await ctx.send("There are no stored tags on this server.")
        description = []

        for name, tag in tags.items():
            tagscript = tag.tagscript
            if len(tagscript) > 23:
                tagscript = tagscript[:20] + "..."
            tagscript = tagscript.replace("\n", " ")
            description.append(f"`{name}` - {escape_markdown(tagscript)}")
        description = "\n".join(description)

        e = discord.Embed(color=await ctx.embed_color(), title=f"Stored Tags")
        e.set_author(name=ctx.guild, icon_url=ctx.guild.icon_url)

        embeds = []
        pages = list(pagify(description))
        for index, page in enumerate(pages, 1):
            embed = e.copy()
            embed.description = page
            embed.set_footer(text=f"{index}/{len(pages)}")
            embeds.append(embed)
        await menu(ctx, embeds, DEFAULT_CONTROLS)

    @commands.is_owner()
    @commands.mod_or_permissions(manage_guild=True)
    @tag.command(aliases=["execute"])
    async def run(self, ctx: commands.Context, *, tagscript: str):
        """Execute TagScript without storing."""
        start = time.monotonic()
        author = MemberAdapter(ctx.author)
        target = MemberAdapter(
            ctx.message.mentions[0]) if ctx.message.mentions else author
        channel = TextChannelAdapter(ctx.channel)
        guild = GuildAdapter(ctx.guild)
        seed = {
            "author": author,
            "user": author,
            "target": target,
            "member": target,
            "channel": channel,
            "guild": guild,
            "server": guild,
        }
        output = self.engine.process(tagscript, seed_variables=seed)
        end = time.monotonic()

        e = discord.Embed(
            color=await ctx.embed_color(),
            title="TagScriptEngine",
            description=f"Executed in **{round((end - start) * 1000, 3)}** ms",
        )
        e.add_field(name="Input", value=tagscript, inline=False)
        if output.actions:
            e.add_field(name="Actions", value=output.actions, inline=False)
        if output.variables:
            vars = "\n".join([
                f"{name}: {type(obj).__name__}"
                for name, obj in output.variables.items()
            ])
            e.add_field(name="Variables", value=vars, inline=False)
        e.add_field(name="Output",
                    value=output.body or "NO OUTPUT",
                    inline=False)

        await ctx.send(embed=e)

    @commands.is_owner()
    @tag.command()
    async def process(self, ctx: commands.Context, *, tagscript: str):
        """Process TagScript without storing."""
        tag = Tag(
            self,
            ctx.guild,
            "processed_tag",
            tagscript,
            author=ctx.author,
            real=False,
        )
        await self.process_tag(ctx, tag)
        await ctx.tick()

    @commands.Cog.listener()
    async def on_message_without_command(self, message: discord.Message):
        if (message.author.bot
                or not isinstance(message.author, discord.Member)
                or not message.guild):
            return
        if not self.guild_tag_cache[message.guild.id]:
            return
        if not await self.bot.message_eligible_as_command(message):
            return
        ctx = await self.bot.get_context(message)
        if ctx.prefix is None:
            return

        tag_command = message.content[len(ctx.prefix):]
        tag_split = tag_command.split(" ", 1)
        if self.get_tag(message.guild, tag_split[0]):
            new_message = copy(message)
            new_message.content = f"{ctx.prefix}tag False {tag_command}"
            ctx = await self.bot.get_context(new_message)
            await self.bot.invoke(ctx)

    async def process_tag(self,
                          ctx: commands.Context,
                          tag: Tag,
                          *,
                          seed_variables: dict = {},
                          **kwargs) -> str:
        author = MemberAdapter(ctx.author)
        target = MemberAdapter(
            ctx.message.mentions[0]) if ctx.message.mentions else author
        channel = TextChannelAdapter(ctx.channel)
        guild = GuildAdapter(ctx.guild)
        seed = {
            "author": author,
            "user": author,
            "target": target,
            "member": target,
            "channel": channel,
            "guild": guild,
            "server": guild,
        }
        seed_variables.update(seed)

        output = tag.run(self.engine, seed_variables=seed_variables, **kwargs)
        await tag.update_config()
        to_gather = []
        command_messages = []
        content = output.body[:2000] if output.body else None
        actions = output.actions
        embed = actions.get("embed")
        destination = ctx.channel
        replying = False

        if actions:
            try:
                await self.validate_checks(ctx, actions)
            except RequireCheckFailure as error:
                response = error.response
                if response is not None:
                    if response:
                        await ctx.send(response[:2000])
                else:
                    start_adding_reactions(ctx.message, ["❌"])
                return

            if delete := actions.get("delete", False):
                to_gather.append(self.delete_quietly(ctx))

            if delete is False and (reactu := actions.get("reactu")):
                to_gather.append(self.do_reactu(ctx, reactu))

            if actions.get("commands"):
                for command in actions["commands"]:
                    if command.startswith("tag"):
                        await ctx.send("Looping isn't allowed.")
                        return
                    new = copy(ctx.message)
                    new.content = ctx.prefix + command
                    command_messages.append(new)

            if target := actions.get("target"):
                if target == "dm":
                    destination = await ctx.author.create_dm()
                elif target == "reply":
                    replying = True
                else:
                    try:
                        chan = await self.channel_converter.convert(
                            ctx, target)
                    except commands.BadArgument:
                        pass
                    else:
                        if chan.permissions_for(ctx.me).send_messages:
                            destination = chan
コード例 #8
0
ファイル: roles.py プロジェクト: kablekompany/phen-cogs
class Roles(MixinMeta):
    """
    Useful role commands.
    """
    def __init__(self):
        self.interpreter = Interpreter([LooseVariableGetterBlock()])
        super().__init__()

    async def initialize(self):
        log.debug("Roles Initialize")
        await super().initialize()

    @commands.guild_only()
    @commands.group(invoke_without_command=True)
    async def role(self, ctx: commands.Context, member: TouchableMember(False),
                   *, role: StrictRole(False)):
        """Base command for modifying roles.

        Invoking this command will add or remove the given role from the member, depending on whether they already had it."""
        if role in member.roles and await can_run_command(ctx, "role remove"):
            com = self.bot.get_command("role remove")
            await ctx.invoke(
                com,
                member=member,
                role=role,
            )
        elif role not in member.roles and await can_run_command(
                ctx, "role add"):
            com = self.bot.get_command("role add")
            await ctx.invoke(
                com,
                member=member,
                role=role,
            )
        else:
            await ctx.send_help()

    @commands.bot_has_permissions(embed_links=True)
    @role.command("info")
    async def role_info(self, ctx: commands.Context, *, role: FuzzyRole):
        """Get information about a role."""
        await ctx.send(embed=await self.get_info(role))

    async def get_info(self, role: discord.Role) -> discord.Embed:
        if guild_roughly_chunked(
                role.guild) is False and self.bot.intents.members:
            await role.guild.chunk()
        description = [
            f"{role.mention}",
            f"Members: {len(role.members)} | Position: {role.position}",
            f"Color: {role.color}",
            f"Hoisted: {role.hoist}",
            f"Mentionable: {role.mentionable}",
        ]
        if role.managed:
            description.append(f"Managed: {role.managed}")
        if role in await self.bot.get_mod_roles(role.guild):
            description.append(f"Mod Role: True")
        if role in await self.bot.get_admin_roles(role.guild):
            description.append(f"Admin Role: True")
        e = discord.Embed(
            color=role.color,
            title=role.name,
            description="\n".join(description),
            timestamp=role.created_at,
        )
        e.set_footer(text=role.id)
        return e

    def format_member(self, member: discord.Member, formatting: str) -> str:
        output = self.interpreter.process(formatting,
                                          {"member": MemberAdapter(member)})
        return output.body

    @commands.bot_has_permissions(attach_files=True)
    @commands.admin_or_permissions(manage_roles=True)
    @role.command("members", aliases=["dump"])
    async def role_members(
        self,
        ctx: commands.Context,
        role: FuzzyRole,
        *,
        formatting: str = "{member} - {member(id)}",
    ):
        """
        Sends a list of members in a role.

        You can supply a custom formatting tagscript for each member.
        The [member](https://phen-cogs.readthedocs.io/en/latest/tags/default_variables.html#author-block) block is available to use, found on the [TagScript documentation](https://phen-cogs.readthedocs.io/en/latest/index.html).

        **Example:**
        `[p]role dump @admin <t:{member(timestamp)}> - {member(mention)}`
        """
        if guild_roughly_chunked(
                ctx.guild) is False and self.bot.intents.members:
            await ctx.guild.chunk()
        if not role.members:
            return await ctx.send(f"**{role}** has no members.")
        members = "\n".join(
            self.format_member(member, formatting) for member in role.members)
        if len(members) > 2000:
            await ctx.send(file=text_to_file(members, f"members.txt"))
        else:
            await ctx.send(members,
                           allowed_mentions=discord.AllowedMentions.none())

    @staticmethod
    def get_hsv(role: discord.Role):
        return rgb_to_hsv(*role.color.to_rgb())

    @commands.bot_has_permissions(embed_links=True)
    @commands.admin_or_permissions(manage_roles=True)
    @role.command("colors")
    async def role_colors(self, ctx: commands.Context):
        """Sends the server's roles, ordered by color."""
        roles = defaultdict(list)
        for r in ctx.guild.roles:
            roles[str(r.color)].append(r)
        roles = dict(sorted(roles.items(),
                            key=lambda v: self.get_hsv(v[1][0])))

        lines = [
            f"**{color}**\n{' '.join(r.mention for r in rs)}"
            for color, rs in roles.items()
        ]
        for page in pagify("\n".join(lines)):
            e = discord.Embed(description=page)
            await ctx.send(embed=e)

    @commands.bot_has_permissions(manage_roles=True)
    @commands.admin_or_permissions(manage_roles=True)
    @role.command("create")
    async def role_create(
        self,
        ctx: commands.Context,
        color: Optional[discord.Color] = discord.Color.default(),
        hoist: Optional[bool] = False,
        *,
        name: str = None,
    ):
        """
        Creates a role.

        Color and whether it is hoisted can be specified.
        """
        if len(ctx.guild.roles) >= 250:
            return await ctx.send(
                "This server has reached the maximum role limit (250).")

        role = await ctx.guild.create_role(name=name,
                                           colour=color,
                                           hoist=hoist)
        await ctx.send(f"**{role}** created!", embed=await self.get_info(role))

    @commands.admin_or_permissions(manage_roles=True)
    @commands.bot_has_permissions(manage_roles=True)
    @role.command("color", aliases=["colour"])
    async def role_color(self, ctx: commands.Context,
                         role: StrictRole(check_integrated=False),
                         color: discord.Color):
        """Change a role's color."""
        await role.edit(color=color)
        await ctx.send(f"**{role}** color changed to **{color}**.",
                       embed=await self.get_info(role))

    @commands.admin_or_permissions(manage_roles=True)
    @commands.bot_has_permissions(manage_roles=True)
    @role.command("hoist")
    async def role_hoist(
            self,
            ctx: commands.Context,
            role: StrictRole(check_integrated=False),
            hoisted: bool = None,
    ):
        """Toggle whether a role should appear seperate from other roles."""
        hoisted = hoisted if hoisted is not None else not role.hoist
        await role.edit(hoist=hoisted)
        now = "now" if hoisted else "no longer"
        await ctx.send(f"**{role}** is {now} hoisted.",
                       embed=await self.get_info(role))

    @commands.admin_or_permissions(manage_roles=True)
    @commands.bot_has_permissions(manage_roles=True)
    @role.command("name")
    async def role_name(self, ctx: commands.Context,
                        role: StrictRole(check_integrated=False), *,
                        name: str):
        """Change a role's name."""
        old_name = role.name
        await role.edit(name=name)
        await ctx.send(f"Changed **{old_name}** to **{name}**.",
                       embed=await self.get_info(role))

    @commands.admin_or_permissions(manage_roles=True)
    @commands.bot_has_permissions(manage_roles=True)
    @role.command("add")
    async def role_add(self, ctx: commands.Context, member: TouchableMember, *,
                       role: StrictRole):
        """Add a role to a member."""
        if role in member.roles:
            await ctx.send(
                f"**{member}** already has the role **{role}**. Maybe try removing it instead."
            )
            return
        reason = get_audit_reason(ctx.author)
        await member.add_roles(role, reason=reason)
        await ctx.send(f"Added **{role.name}** to **{member}**.")

    @commands.admin_or_permissions(manage_roles=True)
    @commands.bot_has_permissions(manage_roles=True)
    @role.command("remove")
    async def role_remove(self, ctx: commands.Context, member: TouchableMember,
                          *, role: StrictRole):
        """Remove a role from a member."""
        if role not in member.roles:
            await ctx.send(
                f"**{member}** doesn't have the role **{role}**. Maybe try adding it instead."
            )
            return
        reason = get_audit_reason(ctx.author)
        await member.remove_roles(role, reason=reason)
        await ctx.send(f"Removed **{role.name}** from **{member}**.")

    @commands.admin_or_permissions(manage_roles=True)
    @commands.bot_has_permissions(manage_roles=True)
    @role.command(require_var_positional=True)
    async def addmulti(self, ctx: commands.Context, role: StrictRole,
                       *members: TouchableMember):
        """Add a role to multiple members."""
        reason = get_audit_reason(ctx.author)
        already_members = []
        success_members = []
        for member in members:
            if role not in member.roles:
                await member.add_roles(role, reason=reason)
                success_members.append(member)
            else:
                already_members.append(member)
        msg = []
        if success_members:
            msg.append(
                f"Added **{role}** to {humanize_roles(success_members)}.")
        if already_members:
            msg.append(
                f"{humanize_roles(already_members)} already had **{role}**.")
        await ctx.send("\n".join(msg))

    @commands.admin_or_permissions(manage_roles=True)
    @commands.bot_has_permissions(manage_roles=True)
    @role.command(require_var_positional=True)
    async def removemulti(self, ctx: commands.Context, role: StrictRole,
                          *members: TouchableMember):
        """Remove a role from multiple members."""
        reason = get_audit_reason(ctx.author)
        already_members = []
        success_members = []
        for member in members:
            if role in member.roles:
                await member.remove_roles(role, reason=reason)
                success_members.append(member)
            else:
                already_members.append(member)
        msg = []
        if success_members:
            msg.append(
                f"Removed **{role}** from {humanize_roles(success_members)}.")
        if already_members:
            msg.append(
                f"{humanize_roles(already_members)} didn't have **{role}**.")
        await ctx.send("\n".join(msg))

    @commands.admin_or_permissions(manage_roles=True)
    @commands.bot_has_permissions(manage_roles=True)
    @commands.group(invoke_without_command=True, require_var_positional=True)
    async def multirole(self, ctx: commands.Context, member: TouchableMember,
                        *roles: StrictRole):
        """Add multiple roles to a member."""
        not_allowed = []
        already_added = []
        to_add = []
        for role in roles:
            allowed = await is_allowed_by_role_hierarchy(
                self.bot, ctx.me, ctx.author, role)
            if not allowed[0]:
                not_allowed.append(role)
            elif role in member.roles:
                already_added.append(role)
            else:
                to_add.append(role)
        reason = get_audit_reason(ctx.author)
        msg = []
        if to_add:
            await member.add_roles(*to_add, reason=reason)
            msg.append(f"Added {humanize_roles(to_add)} to **{member}**.")
        if already_added:
            msg.append(
                f"**{member}** already had {humanize_roles(already_added)}.")
        if not_allowed:
            msg.append(
                f"You do not have permission to assign the roles {humanize_roles(not_allowed)}."
            )
        await ctx.send("\n".join(msg))

    @commands.admin_or_permissions(manage_roles=True)
    @commands.bot_has_permissions(manage_roles=True)
    @multirole.command("remove", require_var_positional=True)
    async def multirole_remove(self, ctx: commands.Context,
                               member: TouchableMember, *roles: StrictRole):
        """Remove multiple roles from a member."""
        not_allowed = []
        not_added = []
        to_rm = []
        for role in roles:
            allowed = await is_allowed_by_role_hierarchy(
                self.bot, ctx.me, ctx.author, role)
            if not allowed[0]:
                not_allowed.append(role)
            elif role not in member.roles:
                not_added.append(role)
            else:
                to_rm.append(role)
        reason = get_audit_reason(ctx.author)
        msg = []
        if to_rm:
            await member.remove_roles(*to_rm, reason=reason)
            msg.append(f"Removed {humanize_roles(to_rm)} from **{member}**.")
        if not_added:
            msg.append(
                f"**{member}** didn't have {humanize_roles(not_added)}.")
        if not_allowed:
            msg.append(
                f"You do not have permission to assign the roles {humanize_roles(not_allowed)}."
            )
        await ctx.send("\n".join(msg))

    @commands.admin_or_permissions(manage_roles=True)
    @commands.bot_has_permissions(manage_roles=True)
    @role.command()
    async def all(self, ctx: commands.Context, *, role: StrictRole):
        """Add a role to all members of the server."""
        await self.super_massrole(ctx, ctx.guild.members, role)

    @commands.admin_or_permissions(manage_roles=True)
    @commands.bot_has_permissions(manage_roles=True)
    @role.command(aliases=["removeall"])
    async def rall(self, ctx: commands.Context, *, role: StrictRole):
        """Remove a role from all members of the server."""
        member_list = self.get_member_list(ctx.guild.members, role, False)
        await self.super_massrole(ctx, member_list, role,
                                  "No one on the server has this role.", False)

    @commands.admin_or_permissions(manage_roles=True)
    @commands.bot_has_permissions(manage_roles=True)
    @role.command()
    async def humans(self, ctx: commands.Context, *, role: StrictRole):
        """Add a role to all humans (non-bots) in the server."""
        await self.super_massrole(
            ctx,
            [member for member in ctx.guild.members if not member.bot],
            role,
            "Every human in the server has this role.",
        )

    @commands.admin_or_permissions(manage_roles=True)
    @commands.bot_has_permissions(manage_roles=True)
    @role.command()
    async def rhumans(self, ctx: commands.Context, *, role: StrictRole):
        """Remove a role from all humans (non-bots) in the server."""
        await self.super_massrole(
            ctx,
            [member for member in ctx.guild.members if not member.bot],
            role,
            "None of the humans in the server have this role.",
            False,
        )

    @commands.admin_or_permissions(manage_roles=True)
    @commands.bot_has_permissions(manage_roles=True)
    @role.command()
    async def bots(self, ctx: commands.Context, *, role: StrictRole):
        """Add a role to all bots in the server."""
        await self.super_massrole(
            ctx,
            [member for member in ctx.guild.members if member.bot],
            role,
            "Every bot in the server has this role.",
        )

    @commands.admin_or_permissions(manage_roles=True)
    @commands.bot_has_permissions(manage_roles=True)
    @role.command()
    async def rbots(self, ctx: commands.Context, *, role: StrictRole):
        """Remove a role from all bots in the server."""
        await self.super_massrole(
            ctx,
            [member for member in ctx.guild.members if member.bot],
            role,
            "None of the bots in the server have this role.",
            False,
        )

    @commands.admin_or_permissions(manage_roles=True)
    @commands.bot_has_permissions(manage_roles=True)
    @role.command("in")
    async def role_in(self, ctx: commands.Context, target_role: FuzzyRole, *,
                      add_role: StrictRole):
        """Add a role to all members of a another role."""
        await self.super_massrole(
            ctx,
            [member for member in target_role.members],
            add_role,
            f"Every member of **{target_role}** has this role.",
        )

    @commands.admin_or_permissions(manage_roles=True)
    @commands.bot_has_permissions(manage_roles=True)
    @role.command("rin")
    async def role_rin(self, ctx: commands.Context, target_role: FuzzyRole, *,
                       remove_role: StrictRole):
        """Remove a role from all members of a another role."""
        await self.super_massrole(
            ctx,
            [member for member in target_role.members],
            remove_role,
            f"No one in **{target_role}** has this role.",
            False,
        )

    @commands.check(targeter_cog)
    @commands.admin_or_permissions(manage_roles=True)
    @commands.bot_has_permissions(manage_roles=True)
    @role.group()
    async def target(self, ctx: commands.Context):
        """
        Modify roles using 'targeting' args.

        An explanation of Targeter and test commands to preview the members affected can be found with `[p]target`.
        """

    @target.command("add")
    async def target_add(self, ctx: commands.Context, role: StrictRole, *,
                         args: TargeterArgs):
        """
        Add a role to members using targeting args.

        An explanation of Targeter and test commands to preview the members affected can be found with `[p]target`.
        """
        await self.super_massrole(
            ctx,
            args,
            role,
            f"No one was found with the given args that was eligible to recieve **{role}**.",
        )

    @target.command("remove")
    async def target_remove(self, ctx: commands.Context, role: StrictRole, *,
                            args: TargeterArgs):
        """
        Remove a role from members using targeting args.

        An explanation of Targeter and test commands to preview the members affected can be found with `[p]target`.
        """
        await self.super_massrole(
            ctx,
            args,
            role,
            f"No one was found with the given args that was eligible have **{role}** removed from them.",
            False,
        )

    async def super_massrole(
        self,
        ctx: commands.Context,
        members: list,
        role: discord.Role,
        fail_message: str = "Everyone in the server has this role.",
        adding: bool = True,
    ):
        if guild_roughly_chunked(
                ctx.guild) is False and self.bot.intents.members:
            await ctx.guild.chunk()
        member_list = self.get_member_list(members, role, adding)
        if not member_list:
            await ctx.send(fail_message)
            return
        verb = "add" if adding else "remove"
        word = "to" if adding else "from"
        await ctx.send(
            f"Beginning to {verb} **{role.name}** {word} **{len(member_list)}** members."
        )
        async with ctx.typing():
            result = await self.massrole(member_list, [role],
                                         get_audit_reason(ctx.author), adding)
            result_text = f"{verb.title()[:5]}ed **{role.name}** {word} **{len(result['completed'])}** members."
            if result["skipped"]:
                result_text += (
                    f"\nSkipped {verb[:5]}ing roles for **{len(result['skipped'])}** members."
                )
            if result["failed"]:
                result_text += (
                    f"\nFailed {verb[:5]}ing roles for **{len(result['failed'])}** members."
                )
        await ctx.send(result_text)

    def get_member_list(self,
                        members: list,
                        role: discord.Role,
                        adding: bool = True):
        if adding:
            members = [
                member for member in members if role not in member.roles
            ]
        else:
            members = [member for member in members if role in member.roles]
        return members

    async def massrole(self,
                       members: list,
                       roles: list,
                       reason: str,
                       adding: bool = True):
        completed = []
        skipped = []
        failed = []
        for member in members:
            if adding:
                to_add = [role for role in roles if role not in member.roles]
                if to_add:
                    try:
                        await member.add_roles(*to_add, reason=reason)
                    except Exception as e:
                        failed.append(member)
                        log.exception(f"Failed to add roles to {member}",
                                      exc_info=e)
                    else:
                        completed.append(member)
                else:
                    skipped.append(member)
            else:
                to_remove = [role for role in roles if role in member.roles]
                if to_remove:
                    try:
                        await member.remove_roles(*to_remove, reason=reason)
                    except Exception as e:
                        failed.append(member)
                        log.exception(f"Failed to remove roles from {member}",
                                      exc_info=e)
                    else:
                        completed.append(member)
                else:
                    skipped.append(member)
        return {"completed": completed, "skipped": skipped, "failed": failed}

    @staticmethod
    def format_members(members: List[discord.Member]):
        length = len(members)
        s = "" if length == 1 else "s"
        return f"**{hn(length)}** member{s}"

    @role.command("uniquemembers", aliases=["um"], require_var_positional=True)
    async def role_uniquemembers(self, ctx: commands.Context,
                                 *roles: FuzzyRole):
        """
        View the total unique members between multiple roles.
        """
        roles_length = len(roles)
        if roles_length == 1:
            raise commands.UserFeedbackCheckFailure(
                "You must provide at least 2 roles.")
        if not ctx.guild.chunked:
            await ctx.guild.chunk()
        color = roles[0].color
        unique_members = set()
        description = []
        for role in roles:
            unique_members.update(role.members)
            description.append(
                f"{role.mention}: {self.format_members(role.members)}")
        description.insert(
            0, f"**Unique members**: {self.format_members(unique_members)}")
        e = discord.Embed(
            color=color,
            title=f"Unique members between {roles_length} roles",
            description="\n".join(description),
        )
        ref = ctx.message.to_reference(fail_if_not_exists=False)
        await ctx.send(embed=e, reference=ref)
コード例 #9
0
ファイル: objects.py プロジェクト: proguy914629bot/phen-cogs
 def run(self, interpreter: Interpreter, **kwargs) -> Interpreter.Response:
     return interpreter.process(self.tagscript, **kwargs)
コード例 #10
0
class Tags(commands.Cog):
    """
    Create and use tags.

    The TagScript documentation can be found [here](https://github.com/phenom4n4n/phen-cogs/blob/master/tags/README.md).
    """

    __version__ = "1.2.13"

    def format_help_for_context(self, ctx):
        pre_processed = super().format_help_for_context(ctx)
        n = "\n" if "\n\n" not in pre_processed else ""
        return f"{pre_processed}{n}\nCog Version: {self.__version__}"

    def __init__(self, bot: Red) -> None:
        self.bot = bot
        self.config = Config.get_conf(
            self,
            identifier=567234895692346562369,
            force_registration=True,
        )
        default_guild = {"tags": {}}
        self.config.register_guild(**default_guild)

        blocks = stable_blocks + [
            block.MathBlock(),
            block.RandomBlock(),
            block.RangeBlock(),
            block.AnyBlock(),
            block.IfBlock(),
            block.AllBlock(),
            block.BreakBlock(),
            block.StrfBlock(),
            block.StopBlock(),
            block.AssignmentBlock(),
            block.FiftyFiftyBlock(),
            block.ShortCutRedirectBlock("message"),
            block.LooseVariableGetterBlock(),
            block.SubstringBlock(),
        ]
        self.engine = Interpreter(blocks)
        self.role_converter = commands.RoleConverter()
        self.channel_converter = commands.TextChannelConverter()
        self.member_converter = commands.MemberConverter()
        self.emoji_converter = commands.EmojiConverter()

        self.tag_cache = {}
        self.guild_data_cache = {}
        self.task = asyncio.create_task(self.cache_tags())

    def cog_unload(self):
        if self.task:
            self.task.cancel()

    async def red_delete_data_for_user(self, *, requester: str, user_id: int):
        if requester not in ("discord_deleted_user", "user"):
            return
        guilds_data = await self.config.all_guilds()
        for guild_id, data in guilds_data.items():
            guild = self.bot.get_guild(guild_id)
            if guild and data["tags"]:
                for name, tag in data["tags"].items():
                    if str(user_id) in str(tag["author"]):
                        async with self.config.guild(guild).tags() as t:
                            del t[name]

    async def cache_tags(self):
        guilds_data = await self.config.all_guilds()
        self.guild_data_cache = guilds_data
        for guild_id, data in guilds_data.items():
            self.tag_cache[guild_id] = list(data.get("tags", {}).keys())

    @commands.guild_only()
    @commands.group(invoke_without_command=True,
                    usage="<tag_name> [args]",
                    aliases=["customcom"])
    async def tag(self,
                  ctx,
                  response: Optional[bool],
                  tag_name: str,
                  *,
                  args: Optional[str] = ""):
        """Tag management with TagScript.

        These commands use TagScriptEngine. [This site](https://github.com/phenom4n4n/phen-cogs/blob/master/tags/README.md) has documentation on how to use TagScript blocks."""
        if response is None:
            response = True
        try:
            _tag = await TagConverter().convert(ctx, tag_name)
        except commands.BadArgument as e:
            if response:
                await ctx.send(e)
            return
        async with self.config.guild(ctx.guild).tags() as t:
            t[tag_name]["uses"] += 1
        seed = {"args": adapter.StringAdapter(args)}
        log.info(
            f"Processing tag for {tag_name} on {ctx.guild} ({ctx.guild.id})")
        await self.process_tag(ctx, _tag, seed_variables=seed)

    @commands.mod_or_permissions(manage_guild=True)
    @tag.command(aliases=["create", "+"])
    async def add(self, ctx, tag_name: TagName, *, tagscript):
        """Add a tag with TagScript."""
        tag = await self.get_stored_tag(ctx, tag_name, False)
        if tag:
            msg = await ctx.send(
                f"`{tag_name}` is already registered tag. Would you like to overwrite it?"
            )
            start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
            pred = ReactionPredicate.yes_or_no(msg, ctx.author)
            await ctx.bot.wait_for("reaction_add", check=pred)

            if pred.result is False:
                await ctx.send("Action cancelled.")
                return

        await self.store_tag(ctx, tag_name, tagscript)
        await self.cache_tags()

    @commands.mod_or_permissions(manage_guild=True)
    @tag.command(aliases=["e"])
    async def edit(self, ctx, tag: TagConverter, *, tagscript):
        """Edit a tag with TagScript."""
        async with self.config.guild(ctx.guild).tags() as t:
            t[str(tag)]["tag"] = tagscript
        await ctx.send(f"Tag `{tag}` edited.")

    @commands.mod_or_permissions(manage_guild=True)
    @tag.command(aliases=["delete", "-"])
    async def remove(self, ctx, tag: TagConverter):
        """Delete a tag."""
        async with self.config.guild(ctx.guild).tags() as e:
            del e[str(tag)]
        await ctx.send("Tag deleted.")
        await self.cache_tags()

    @tag.command(name="info")
    async def tag_info(self, ctx, tag: TagConverter):
        """Get info about an tag that is stored on this server."""
        e = discord.Embed(
            color=await ctx.embed_color(),
            title=f"`{tag}` Info",
            description=
            f"Author: {tag.author.mention if tag.author else tag.author_id}\nUses: {tag.uses}\nLength: {len(tag)}",
        )
        e.add_field(name="TagScript", value=box(str(tag)))
        e.set_author(name=ctx.guild, icon_url=ctx.guild.icon_url)
        await ctx.send(embed=e)

    @tag.command(name="raw")
    async def tag_raw(self, ctx, tag: TagConverter):
        """Get a tag's raw content."""
        await ctx.send(
            escape_markdown(tag.tagscript[:2000]),
            allowed_mentions=discord.AllowedMentions(everyone=False,
                                                     roles=False,
                                                     users=False),
        )

    @tag.command(name="list")
    async def tag_list(self, ctx):
        """View stored tags."""
        tags = await self.config.guild(ctx.guild).tags()
        if not tags:
            return await ctx.send("There are no stored tags on this server.")
        description = []

        for name, tag in tags.items():
            description.append(f"`{name}` - Created by <@!{tag['author']}>")
        description = "\n".join(description)

        color = await self.bot.get_embed_colour(ctx)
        e = discord.Embed(color=color, title=f"Stored Tags")
        e.set_author(name=ctx.guild, icon_url=ctx.guild.icon_url)

        if len(description) > 2048:
            embeds = []
            pages = list(pagify(description, page_length=1024))
            for index, page in enumerate(pages, start=1):
                embed = e.copy()
                embed.description = page
                embed.set_footer(text=f"{index}/{len(pages)}")
                embeds.append(embed)
            await menu(ctx, embeds, DEFAULT_CONTROLS)
        else:
            e.description = description
            emoji = self.bot.get_emoji(736038541364297738) or "❌"
            controls = {emoji: close_menu}
            await menu(ctx, [e], controls)

    @commands.is_owner()
    @commands.mod_or_permissions(manage_guild=True)
    @tag.command(aliases=["execute"])
    async def run(self, ctx: commands.Context, *, tagscript: str):
        """Execute TagScript without storing."""
        start = time.monotonic()
        author = MemberAdapter(ctx.author)
        target = MemberAdapter(
            ctx.message.mentions[0]) if ctx.message.mentions else author
        channel = TextChannelAdapter(ctx.channel)
        guild = GuildAdapter(ctx.guild)
        seed = {
            "author": author,
            "user": author,
            "target": target,
            "member": target,
            "channel": channel,
            "guild": guild,
            "server": guild,
        }
        output = self.engine.process(tagscript, seed_variables=seed)
        end = time.monotonic()

        e = discord.Embed(
            color=await ctx.embed_color(),
            title="TagScriptEngine",
            description=f"Executed in **{round((end - start) * 1000, 3)}** ms",
        )
        e.add_field(name="Input", value=tagscript, inline=False)
        if output.actions:
            e.add_field(name="Actions", value=output.actions, inline=False)
        if output.variables:
            vars = "\n".join([
                f"{name}: {type(obj).__name__}"
                for name, obj in output.variables.items()
            ])
            e.add_field(name="Variables", value=vars, inline=False)
        e.add_field(name="Output",
                    value=output.body or "NO OUTPUT",
                    inline=False)

        await ctx.send(embed=e)

    @commands.is_owner()
    @tag.command()
    async def process(self, ctx: commands.Context, *, tagscript: str):
        """Process TagScript without storing."""
        tag = Tag(
            "processed_tag",
            tagscript,
            invoker=ctx.author,
            author=ctx.author,
            author_id=ctx.author.id,
            uses=1,
            ctx=ctx,
        )
        await self.process_tag(ctx, tag)
        await ctx.tick()

    async def store_tag(self, ctx: commands.Context, name: str,
                        tagscript: str):
        async with self.config.guild(ctx.guild).tags() as t:
            t[name] = {"author": ctx.author.id, "uses": 0, "tag": tagscript}
        await ctx.send(f"Tag stored under the name `{name}`.")

    # thanks trusty, https://github.com/TrustyJAID/Trusty-cogs/blob/master/retrigger/retrigger.py#L1065
    @tag.command()
    async def explain(self, ctx: commands.Context):
        """View Tag block documentation."""
        with open(Path(__file__).parent / "README.md", "r",
                  encoding="utf8") as infile:
            data = infile.read()
        pages = list(
            pagify(data, ["\n\n\n", "\n\n"], page_length=500, priority=True))
        embeds = []
        e = discord.Embed(
            title="Tags",
            color=await ctx.embed_color(),
            url=
            "https://github.com/phenom4n4n/phen-cogs/blob/master/tags/README.md",
        )
        for index, page in enumerate(pages, start=1):
            embed = e.copy()
            embed.description = page
            embed.set_footer(text=f"{index}/{len(pages)}")
            embeds.append(embed)
        await menu(ctx, embeds, DEFAULT_CONTROLS)

    async def get_stored_tag(self,
                             ctx: commands.Context,
                             name: TagName,
                             response: bool = True) -> Optional[Tag]:
        tags = await self.config.guild(ctx.guild).tags()
        tag = tags.get(name)
        if tag:
            tag = Tag.from_dict(name, tag, ctx=ctx)
            return tag
        return None

    @commands.Cog.listener()
    async def on_message_without_command(self, message: discord.Message):
        if (message.author.bot
                or not isinstance(message.author, discord.Member)
                or not message.guild):
            return
        if message.guild.id not in self.tag_cache.keys():
            return
        if not await self.bot.message_eligible_as_command(message):
            return
        ctx = await self.bot.get_context(message)
        if ctx.prefix is None:
            return

        tag_command = message.content[len(ctx.prefix):]
        tag_split = tag_command.split(" ")
        if not tag_split:
            return
        tag_name = tag_split[0]
        if tag_name in self.tag_cache.get(message.guild.id, []):
            new_message = copy(message)
            new_message.content = f"{ctx.prefix}tag False {tag_command}"
            ctx = await self.bot.get_context(new_message)
            await self.bot.invoke(ctx)

    async def process_tag(self,
                          ctx: commands.Context,
                          tag: Tag,
                          *,
                          seed_variables: dict = {},
                          **kwargs) -> str:
        author = MemberAdapter(ctx.author)
        target = MemberAdapter(
            ctx.message.mentions[0]) if ctx.message.mentions else author
        channel = TextChannelAdapter(ctx.channel)
        guild = GuildAdapter(ctx.guild)
        destination = ctx.channel
        seed = {
            "author": author,
            "user": author,
            "target": target,
            "member": target,
            "channel": channel,
            "guild": guild,
            "server": guild,
        }
        seed_variables.update(seed)

        output = tag.run(self.engine, seed_variables=seed_variables, **kwargs)
        to_gather = []
        command_messages = []
        content = output.body[:2000] if output.body else None
        actions = output.actions
        embed = actions.get("embed")

        if actions:
            if actions.get("requires") or actions.get("blacklist"):
                check, response = await self.validate_checks(ctx, actions)
                if not check:
                    if response:
                        await ctx.send(response[:2000])
                    else:
                        start_adding_reactions(ctx.message, ["❌"])
                    return
            if delete := actions.get("delete"):
                if ctx.channel.permissions_for(ctx.me).manage_messages:
                    to_gather.append(delete_quietly(ctx.message))
            if not delete and (reactu := actions.get("reactu")):
                to_gather.append(self.do_reactu(ctx, reactu))
            if actions.get("commands"):
                for command in actions["commands"]:
                    if command.startswith("tag"):
                        await ctx.send("Looping isn't allowed.")
                        return
                    new = copy(ctx.message)
                    new.content = ctx.prefix + command
                    command_messages.append(new)
            if target := actions.get("target"):
                if target == "dm":
                    destination = await ctx.author.create_dm()
                else:
                    try:
                        chan = await self.channel_converter.convert(
                            ctx, target)
                    except commands.BadArgument:
                        pass
                    else:
                        if chan.permissions_for(ctx.me).send_messages:
                            destination = chan
コード例 #11
0
ファイル: tags.py プロジェクト: design4webdev/phen-cogs
class Tags(commands.Cog):
    """
    Create and use tags.

    The TagScript documentation can be found [here](https://github.com/phenom4n4n/phen-cogs/blob/master/tags/README.md).
    """

    __version__ = "1.2.1"

    def format_help_for_context(self, ctx):
        pre_processed = super().format_help_for_context(ctx)
        n = "\n" if "\n\n" not in pre_processed else ""
        return f"{pre_processed}{n}\nCog Version: {self.__version__}"

    def __init__(self, bot: Red) -> None:
        self.bot = bot
        cog = self.bot.get_cog("CustomCommands")
        if cog:
            raise RuntimeError(
                "This cog conflicts with CustomCommands and cannot be loaded with both at the same time."
            )
        self.config = Config.get_conf(
            self,
            identifier=567234895692346562369,
            force_registration=True,
        )
        default_guild = {"tags": {}}
        self.config.register_guild(**default_guild)
        blocks = stable_blocks + [
            block.MathBlock(),
            block.RandomBlock(),
            block.RangeBlock(),
            block.AnyBlock(),
            block.IfBlock(),
            block.AllBlock(),
            block.BreakBlock(),
            block.StrfBlock(),
            block.StopBlock(),
            block.AssignmentBlock(),
            block.FiftyFiftyBlock(),
            block.ShortCutRedirectBlock("message"),
            block.LooseVariableGetterBlock(),
            block.SubstringBlock(),
        ]
        self.engine = Interpreter(blocks)

    async def red_delete_data_for_user(self, *, requester: str, user_id: int):
        guilds_data = await self.config.all_guilds()
        for guild_id, data in guilds_data.items():
            guild = self.bot.get_guild(guild_id)
            if guild and data["tags"]:
                for name, tag in data["tags"].items():
                    if str(user_id) in str(tag["author"]):
                        async with self.config.guild(guild).tags() as t:
                            del t[name]

    @commands.guild_only()
    @commands.group(invoke_without_command=True, usage="<tag_name> [args]")
    async def tag(self,
                  ctx,
                  response: Optional[bool],
                  tag_name: str,
                  *,
                  args: Optional[str] = ""):
        """Tag management with TagScript.

        These commands use TagScriptEngine. [This site](https://github.com/phenom4n4n/phen-cogs/blob/master/tags/README.md) has documentation on how to use TagScript blocks."""
        if response is None:
            response = True
        try:
            tag = await TagConverter().convert(ctx, tag_name)
        except commands.BadArgument as e:
            if response:
                await ctx.send(e)
                return
        async with self.config.guild(ctx.guild).tags() as t:
            t[tag_name]["uses"] += 1
        seed = {"args": adapter.StringAdapter(args)}
        await self.process_tag(ctx, tag, seed_variables=seed)

    @commands.mod_or_permissions(manage_guild=True)
    @tag.command()
    async def add(self, ctx, tag_name: TagName, *, tagscript):
        """Add a tag with TagScript."""
        tag = await self.get_stored_tag(ctx, tag_name, False)
        if tag:
            msg = await ctx.send(
                f"`{tag_name}` is already registered tag. Would you like to overwrite it?"
            )
            start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
            pred = ReactionPredicate.yes_or_no(msg, ctx.author)
            await ctx.bot.wait_for("reaction_add", check=pred)

            if pred.result is False:
                await ctx.send("Action cancelled.")
                return

        await self.store_tag(ctx, tag_name, tagscript)

    @commands.mod_or_permissions(manage_guild=True)
    @tag.command(aliases=["e"])
    async def edit(self, ctx, tag: TagConverter, *, tagscript):
        """Edit a tag with TagScript."""
        async with self.config.guild(ctx.guild).tags() as t:
            t[str(tag)]["tag"] = tagscript
        await ctx.send(f"Tag `{tag}` edited.")

    @commands.mod_or_permissions(manage_guild=True)
    @tag.command(aliases=["delete"])
    async def remove(self, ctx, tag: TagConverter):
        """Delete a tag."""
        async with self.config.guild(ctx.guild).tags() as e:
            del e[str(tag)]
        await ctx.send("Tag deleted.")

    @tag.command(name="info")
    async def tag_info(self, ctx, tag: TagConverter):
        """Get info about an tag that is stored on this server."""
        e = discord.Embed(
            color=await ctx.embed_color(),
            title=f"`{tag}` Info",
            description=
            f"Author: {tag.author.mention if tag.author else tag.author_id}\nUses: {tag.uses}\nLength: {len(tag)}",
        )
        e.add_field(name="TagScript", value=box(str(tag)))
        e.set_author(name=ctx.guild, icon_url=ctx.guild.icon_url)
        await ctx.send(embed=e)

    @tag.command(name="raw")
    async def tag_raw(self, ctx, tag: TagConverter):
        """Get a tag's raw content."""
        await ctx.send(
            escape_markdown(tag.tagscript[:2000]),
            allowed_mentions=discord.AllowedMentions(everyone=False,
                                                     roles=False,
                                                     users=False),
        )

    @tag.command(name="list")
    async def tag_list(self, ctx):
        """View stored tags."""
        tags = await self.config.guild(ctx.guild).tags()
        description = []

        for name, tag in tags.items():
            description.append(f"`{name}` - Created by <@!{tag['author']}>")
        description = "\n".join(description)

        color = await self.bot.get_embed_colour(ctx)
        e = discord.Embed(color=color,
                          title=f"Stored Tags",
                          description=description)
        e.set_author(name=ctx.guild, icon_url=ctx.guild.icon_url)
        await ctx.send(embed=e)

    @commands.is_owner()
    @commands.mod_or_permissions(manage_guild=True)
    @tag.command(aliases=["execute"])
    async def run(self, ctx: commands.Context, *, tagscript: str):
        """Execute TagScript without storing."""
        start = time.monotonic()
        author = MemberAdapter(ctx.author)
        target = MemberAdapter(
            ctx.message.mentions[0]) if ctx.message.mentions else author
        channel = TextChannelAdapter(ctx.channel)
        guild = GuildAdapter(ctx.guild)
        seed = {
            "author": author,
            "user": author,
            "target": target,
            "member": target,
            "channel": channel,
            "guild": guild,
            "server": guild,
        }
        output = self.engine.process(tagscript, seed_variables=seed)
        end = time.monotonic()

        e = discord.Embed(
            color=await ctx.embed_color(),
            title="TagScriptEngine",
            description=f"Executed in **{round((end - start) * 1000, 3)}** ms",
        )
        e.add_field(name="Input", value=tagscript, inline=False)
        if output.actions:
            e.add_field(name="Actions", value=output.actions, inline=False)
        if output.variables:
            vars = "\n".join([
                f"{name}: {type(obj).__name__}"
                for name, obj in output.variables.items()
            ])
            e.add_field(name="Variables", value=vars, inline=False)
        e.add_field(name="Output",
                    value=output.body or "NO OUTPUT",
                    inline=False)

        await ctx.send(embed=e)

    @commands.is_owner()
    @tag.command()
    async def process(self, ctx: commands.Context, *, tagscript: str):
        """Process TagScript without storing."""
        tag = Tag(
            "processed_tag",
            tagscript,
            invoker=ctx.author,
            author=ctx.author,
            author_id=ctx.author.id,
            uses=1,
            ctx=ctx,
        )
        await self.process_tag(ctx, tag)

    async def store_tag(self, ctx: commands.Context, name: str,
                        tagscript: str):
        async with self.config.guild(ctx.guild).tags() as t:
            t[name] = {"author": ctx.author.id, "uses": 0, "tag": tagscript}
        await ctx.send(f"Tag stored under the name `{name}`.")

    async def get_stored_tag(self,
                             ctx: commands.Context,
                             name: TagName,
                             response: bool = True):
        tags = await self.config.guild(ctx.guild).tags()
        tag = tags.get(name)
        if tag:
            tag = Tag.from_dict(name, tag, ctx=ctx)
            return tag
        return None

    @commands.Cog.listener()
    async def on_message_without_command(self, message: discord.Message):
        if message.author.bot or not (
                message.guild
                and await self.bot.message_eligible_as_command(message)):
            return
        ctx = await self.bot.get_context(message)
        if ctx.prefix is None:
            return

        tag_command = message.content[len(ctx.prefix):]
        tag_split = tag_command.split(" ")
        if not tag_split:
            return
        tag_name = tag_split[0]
        tag = await self.get_stored_tag(ctx, tag_name, False)
        if tag:
            new_message = copy(message)
            new_message.content = f"{ctx.prefix}tag False {tag_command}"
            await self.bot.process_commands(new_message)

    async def process_tag(self,
                          ctx: commands.Context,
                          tag: Tag,
                          *,
                          seed_variables: dict = {},
                          **kwargs) -> str:
        author = MemberAdapter(ctx.author)
        target = MemberAdapter(
            ctx.message.mentions[0]) if ctx.message.mentions else author
        channel = TextChannelAdapter(ctx.channel)
        guild = GuildAdapter(ctx.guild)
        seed = {
            "author": author,
            "user": author,
            "target": target,
            "member": target,
            "channel": channel,
            "guild": guild,
            "server": guild,
        }
        seed_variables.update(seed)

        output = tag.run(self.engine, seed_variables=seed_variables, **kwargs)
        to_gather = []
        commands_to_process = []
        content = output.body[:2000] if output.body else None
        actions = output.actions
        embed = actions.get("embed")

        if actions:
            if actions.get("delete"):
                if ctx.channel.permissions_for(ctx.me).manage_messages:
                    await delete_quietly(ctx.message)
            if actions.get("commands"):
                for command in actions["commands"]:
                    if command.startswith("tag"):
                        await ctx.send("Looping isn't allowed.")
                        return
                    new = copy(ctx.message)
                    new.content = ctx.prefix + command
                    commands_to_process.append(self.bot.process_commands(new))

        if content or embed:
            try:
                await ctx.send(content, embed=embed)
            except discord.HTTPException:
                return await ctx.send(
                    "I failed to send that embed. The tag has stopped processing."
                )

        if to_gather:
            await asyncio.gather(*to_gather)
        if commands_to_process:
            await asyncio.gather(*commands_to_process)
コード例 #12
0
ファイル: math.py プロジェクト: luciferchase/cogsbylucifer
class Math(BaseCog):
    def __init__(self, bot):
        self.bot = bot
        blocks = [
            block.MathBlock(),
            block.RandomBlock(),
            block.RangeBlock(),
        ]
        self.engine = Interpreter(blocks)

    async def red_delete_data_for_user(self, **kwargs):
        return

    @commands.command()
    async def math(self, ctx, *expression):
        """ Solve a math expression. Supports very complex problems too.
			For eg. `luci math sin(pi/4)`
		"""
        log = logging.getLogger("red.cogsbylucifer.math")

        if (expression == ""):
            embed = discord.Embed(color=0xea1010, title="Input A Expression")
            await ctx.send(embed=embed)
            return

        start = time.monotonic()

        api = "http://api.mathjs.org/v4/"
        params = {"expr": "".join(expression)}
        response = requests.get(url=api, params=params)
        end = time.monotonic()

        if (str(response.status_code)[:2] == "40"):
            log.info(expression)
            log.error(response.text)

        embed = discord.Embed(color=await ctx.embed_color(),
                              title=response.text)
        embed.add_field(name="Your Input:",
                        value=f'`{"".join(expression)}`',
                        inline=True)
        embed.add_field(name="Answer:", value=response.text, inline=True)
        embed.set_footer(
            text=f"Calculated in {round((end - start) * 1000, 3)} ms")
        await ctx.send(embed=embed)

    @commands.command(aliases=["calc"])
    async def calculate(self, ctx, *, query):
        """ Faster but sometimes does not work.
		"""

        query = query.replace(",", "")
        engine_input = "{m:" + query + "}"
        start = time.monotonic()
        output = self.engine.process(engine_input)
        end = time.monotonic()

        output_string = output.body.replace("{m:", "").replace("}", "")
        embed = discord.Embed(
            color=await ctx.embed_color(),
            title=f"Input: `{query}`",
            description=f"Output: `{output_string}`",
        )
        embed.set_footer(
            text=f"Calculated in {round((end - start) * 1000, 3)} ms")
        await ctx.send(embed=embed)