AdvancedTagscript 3.2.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. TagScriptEngine/__init__.py +227 -0
  2. TagScriptEngine/_warnings.py +88 -0
  3. TagScriptEngine/adapter/__init__.py +50 -0
  4. TagScriptEngine/adapter/discordadapters.py +596 -0
  5. TagScriptEngine/adapter/functionadapter.py +23 -0
  6. TagScriptEngine/adapter/intadapter.py +22 -0
  7. TagScriptEngine/adapter/objectadapter.py +35 -0
  8. TagScriptEngine/adapter/redbotadapters.py +161 -0
  9. TagScriptEngine/adapter/stringadapter.py +47 -0
  10. TagScriptEngine/block/__init__.py +130 -0
  11. TagScriptEngine/block/allowedmentions.py +60 -0
  12. TagScriptEngine/block/assign.py +43 -0
  13. TagScriptEngine/block/breakblock.py +41 -0
  14. TagScriptEngine/block/case.py +63 -0
  15. TagScriptEngine/block/command.py +141 -0
  16. TagScriptEngine/block/comment.py +29 -0
  17. TagScriptEngine/block/control.py +149 -0
  18. TagScriptEngine/block/cooldown.py +95 -0
  19. TagScriptEngine/block/count.py +68 -0
  20. TagScriptEngine/block/embedblock.py +306 -0
  21. TagScriptEngine/block/fiftyfifty.py +34 -0
  22. TagScriptEngine/block/helpers.py +164 -0
  23. TagScriptEngine/block/loosevariablegetter.py +40 -0
  24. TagScriptEngine/block/mathblock.py +164 -0
  25. TagScriptEngine/block/randomblock.py +51 -0
  26. TagScriptEngine/block/range.py +56 -0
  27. TagScriptEngine/block/redirect.py +42 -0
  28. TagScriptEngine/block/replaceblock.py +110 -0
  29. TagScriptEngine/block/require_blacklist.py +79 -0
  30. TagScriptEngine/block/shortcutredirect.py +23 -0
  31. TagScriptEngine/block/stopblock.py +38 -0
  32. TagScriptEngine/block/strf.py +70 -0
  33. TagScriptEngine/block/strictvariablegetter.py +38 -0
  34. TagScriptEngine/block/substr.py +25 -0
  35. TagScriptEngine/block/urlencodeblock.py +41 -0
  36. TagScriptEngine/exceptions.py +105 -0
  37. TagScriptEngine/interface/__init__.py +14 -0
  38. TagScriptEngine/interface/adapter.py +75 -0
  39. TagScriptEngine/interface/block.py +124 -0
  40. TagScriptEngine/interpreter.py +502 -0
  41. TagScriptEngine/py.typed +0 -0
  42. TagScriptEngine/utils.py +71 -0
  43. TagScriptEngine/verb.py +160 -0
  44. advancedtagscript-3.2.3.dist-info/METADATA +99 -0
  45. advancedtagscript-3.2.3.dist-info/RECORD +48 -0
  46. advancedtagscript-3.2.3.dist-info/WHEEL +5 -0
  47. advancedtagscript-3.2.3.dist-info/licenses/LICENSE +1 -0
  48. advancedtagscript-3.2.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,161 @@
1
+ import datetime
2
+ from typing import Any, Dict, Optional, Tuple, cast
3
+
4
+ import discord
5
+
6
+ from ..verb import Verb
7
+ from ..interface import SimpleAdapter
8
+ from ..utils import escape_content
9
+
10
+ try:
11
+ import redbot # noqa: F401
12
+ except ModuleNotFoundError:
13
+ _has_redbot = False
14
+ else:
15
+ _has_redbot = True
16
+
17
+ from redbot.core.bot import Red
18
+ from redbot.core.commands import Command
19
+ from redbot.core.utils.chat_formatting import humanize_number, humanize_list
20
+
21
+
22
+ __all__: Tuple[str, ...] = ("RedCommandAdapter", "RedBotAdapter")
23
+
24
+
25
+ class RedCommandAdapter(SimpleAdapter["Command"]):
26
+ if not _has_redbot:
27
+ raise ImportError("A Red-DiscordBot instance is required to use this.", name="redbot")
28
+
29
+ def __init__(self, base: Command, *, signature: Optional[str] = None) -> None:
30
+ super().__init__(base=base)
31
+ self.signature: Optional[str] = signature
32
+
33
+ def update_attributes(self) -> None:
34
+ command: Command = self.object
35
+ self._attributes.update(
36
+ {
37
+ "name": command.name,
38
+ "cog_name": getattr(command, "cog_name", None),
39
+ "description": getattr(command, "description", None),
40
+ "aliases": humanize_list(list(getattr(command, "aliases", []))) or "None",
41
+ "qualified_name": command.qualified_name,
42
+ "signature": self.signature,
43
+ }
44
+ )
45
+
46
+ def get_value(self, ctx: Verb) -> str:
47
+ should_escape: bool = False
48
+ if ctx.parameter is None:
49
+ return_value: str = self.object.qualified_name
50
+ else:
51
+ try:
52
+ value: Any = self._attributes[ctx.parameter]
53
+ except KeyError:
54
+ return # type: ignore
55
+ if isinstance(value, tuple):
56
+ value, should_escape = value
57
+ return_value: str = str(value) if value is not None else None # type: ignore
58
+ return escape_content(return_value) if should_escape else return_value
59
+
60
+
61
+ class RedBotAdapter(SimpleAdapter["Red"]):
62
+ """
63
+ The ``{bot}`` block with no parameters returns the bot's name & discriminator,
64
+ but passing the attributes listed below to the block payload will return that attribute instead.
65
+
66
+ **Usage:** ``{bot([attribute])}``
67
+
68
+ **Payload:** None
69
+
70
+ **Parameter:** attribute, None
71
+
72
+ Attributes
73
+ ----------
74
+ id
75
+ The bot's Discord ID.
76
+ name
77
+ The bot's username.
78
+ discriminator
79
+ The bot's discriminator.
80
+ nick
81
+ The bot's nickname, if they have one, else their username.
82
+ created_at
83
+ The bot's creation date.
84
+ timestamp
85
+ The bot's creation date as a UTC timestamp.
86
+ mention
87
+ A formatted text that pings the bot.
88
+ verified
89
+ If the bot is verified or not.
90
+ shard_count (*)
91
+ The bot's total shard count.
92
+ servers (*)
93
+ Total server/guild count of the bot.
94
+ channels (*)
95
+ Total number of channels visible to the bot.
96
+ visible_users (*)
97
+ Total number of users visible to the bot.
98
+ total_users (*)
99
+ The bot's total user count.
100
+ unique_users (*)
101
+ The bot's unique user count.
102
+ percentage_chunked (*)
103
+ Percentage of chunked guilds the bot has.
104
+
105
+ .. warning::
106
+ Attributes denoting `(*)` can only be used by the bot owner.
107
+ """
108
+
109
+ if not _has_redbot:
110
+ raise ImportError("A Red-DiscordBot instance is required to use this.")
111
+
112
+ def __init__(self, base: Red, *, owner: bool = True) -> None:
113
+ super().__init__(base=base)
114
+ self.is_owner: bool = owner
115
+
116
+ def update_attributes(self) -> None:
117
+ self.user: discord.ClientUser = cast(discord.ClientUser, self.object.user)
118
+ created_at: datetime.datetime = getattr(
119
+ self.user, "created_at", None
120
+ ) or discord.utils.snowflake_time(self.user.id)
121
+ self._attributes.update(
122
+ {
123
+ "id": self.user.id,
124
+ "name": self.user.name,
125
+ "discriminator": self.user.discriminator,
126
+ "nick": self.user.display_name,
127
+ "mention": self.user.display_avatar.url,
128
+ "created_at": created_at,
129
+ "timestamp": int(created_at.timestamp()),
130
+ "verified": self.user.verified,
131
+ }
132
+ )
133
+ if self.is_owner:
134
+ visible_users: int = sum(len(g.members) for g in self.object.guilds)
135
+ total_users: int = sum(
136
+ g.member_count if g.member_count else 0 for g in self.object.guilds
137
+ )
138
+ owner_attributes: Dict[str, Any] = {
139
+ "shard_count": humanize_number(self.object.bot.shard_count),
140
+ "servers": humanize_number(len(self.object.guilds)),
141
+ "channels": humanize_number(sum(len(g.channels) for g in self.object.guilds)),
142
+ "visible_users": humanize_number(visible_users),
143
+ "total_users": humanize_number(total_users),
144
+ "unique_users": humanize_number(len(self.object.users)),
145
+ "percentage_chunked": visible_users / total_users * 100,
146
+ }
147
+ self._attributes.update(owner_attributes)
148
+
149
+ def get_value(self, ctx: Verb) -> str:
150
+ should_escape: bool = False
151
+ if ctx.parameter is None:
152
+ return_value: str = "{0.name}#{0.discriminator}".format(self.user)
153
+ else:
154
+ try:
155
+ value: Any = self._attributes[ctx.parameter]
156
+ except KeyError:
157
+ return # type: ignore
158
+ if isinstance(value, tuple):
159
+ value, should_escape = value
160
+ return_value: str = str(value) if value is not None else None # type: ignore
161
+ return escape_content(return_value) if should_escape else return_value
@@ -0,0 +1,47 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Tuple
4
+
5
+ from ..interface import Adapter
6
+ from ..utils import escape_content
7
+ from ..verb import Verb
8
+
9
+
10
+ __all__: Tuple[str, ...] = ("StringAdapter",)
11
+
12
+
13
+ class StringAdapter(Adapter):
14
+ __slots__: Tuple[str, ...] = ("string", "escape_content")
15
+
16
+ def __init__(self, string: str, *, escape: bool = False) -> None:
17
+ self.string: str = str(string)
18
+ self.escape_content = escape
19
+
20
+ def __repr__(self) -> str:
21
+ return f"<{type(self).__qualname__} string={repr(self.string)}>"
22
+
23
+ def get_value(self, ctx: Verb) -> str:
24
+ return self.return_value(self.handle_ctx(ctx))
25
+
26
+ def handle_ctx(self, ctx: Verb) -> str:
27
+ if ctx.parameter is None:
28
+ return self.string
29
+ try:
30
+ if "+" not in ctx.parameter:
31
+ index = int(ctx.parameter) - 1
32
+ splitter = " " if ctx.payload is None else ctx.payload
33
+ return self.string.split(splitter)[index]
34
+ else:
35
+ index = int(ctx.parameter.replace("+", "")) - 1
36
+ splitter = " " if ctx.payload is None else ctx.payload
37
+ if ctx.parameter.startswith("+"):
38
+ return splitter.join(self.string.split(splitter)[: index + 1])
39
+ elif ctx.parameter.endswith("+"):
40
+ return splitter.join(self.string.split(splitter)[index:])
41
+ else:
42
+ return self.string.split(splitter)[index]
43
+ except Exception:
44
+ return self.string
45
+
46
+ def return_value(self, string: str) -> str:
47
+ return escape_content(string) if self.escape_content else string
@@ -0,0 +1,130 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Tuple
4
+
5
+ # isort: off
6
+ from .helpers import (
7
+ implicit_bool as implicit_bool,
8
+ helper_parse_if as helper_parse_if,
9
+ helper_parse_list_if as helper_parse_list_if,
10
+ helper_split as helper_split,
11
+ easier_helper_split as easier_helper_split,
12
+ )
13
+
14
+ # isort: on
15
+ from .allowedmentions import (
16
+ AllowedMentionsBlock as AllowedMentionsBlock,
17
+ )
18
+ from .assign import (
19
+ AssignmentBlock as AssignmentBlock,
20
+ )
21
+ from .breakblock import (
22
+ BreakBlock as BreakBlock,
23
+ )
24
+ from .command import (
25
+ CommandBlock as CommandBlock,
26
+ OverrideBlock as OverrideBlock,
27
+ SequentialGather as SequentialGather,
28
+ )
29
+ from .control import (
30
+ AllBlock as AllBlock,
31
+ AnyBlock as AnyBlock,
32
+ IfBlock as IfBlock,
33
+ )
34
+ from .cooldown import (
35
+ CooldownBlock as CooldownBlock,
36
+ )
37
+ from .embedblock import (
38
+ EmbedBlock as EmbedBlock,
39
+ )
40
+ from .fiftyfifty import (
41
+ FiftyFiftyBlock as FiftyFiftyBlock,
42
+ )
43
+ from .loosevariablegetter import (
44
+ LooseVariableGetterBlock as LooseVariableGetterBlock,
45
+ )
46
+ from .mathblock import (
47
+ MathBlock as MathBlock,
48
+ )
49
+ from .randomblock import (
50
+ RandomBlock as RandomBlock,
51
+ )
52
+ from .range import (
53
+ RangeBlock as RangeBlock,
54
+ )
55
+ from .redirect import (
56
+ RedirectBlock as RedirectBlock,
57
+ )
58
+ from .replaceblock import (
59
+ PythonBlock as PythonBlock,
60
+ ReplaceBlock as ReplaceBlock,
61
+ )
62
+ from .require_blacklist import (
63
+ BlacklistBlock as BlacklistBlock,
64
+ RequireBlock as RequireBlock,
65
+ )
66
+ from .shortcutredirect import (
67
+ ShortCutRedirectBlock as ShortCutRedirectBlock,
68
+ )
69
+ from .stopblock import (
70
+ StopBlock as StopBlock,
71
+ )
72
+ from .strf import (
73
+ StrfBlock as StrfBlock,
74
+ )
75
+ from .strictvariablegetter import (
76
+ StrictVariableGetterBlock as StrictVariableGetterBlock,
77
+ )
78
+ from .substr import (
79
+ SubstringBlock as SubstringBlock,
80
+ )
81
+ from .urlencodeblock import (
82
+ URLEncodeBlock as URLEncodeBlock,
83
+ )
84
+ from .case import (
85
+ UpperBlock as UpperBlock,
86
+ LowerBlock as LowerBlock,
87
+ )
88
+ from .count import (
89
+ CountBlock as CountBlock,
90
+ LengthBlock as LengthBlock,
91
+ )
92
+
93
+ __all__: Tuple[str, ...] = (
94
+ "implicit_bool",
95
+ "helper_parse_if",
96
+ "helper_parse_list_if",
97
+ "helper_split",
98
+ "easier_helper_split",
99
+ "AllowedMentionsBlock",
100
+ "AllBlock",
101
+ "AnyBlock",
102
+ "AssignmentBlock",
103
+ "BlacklistBlock",
104
+ "BreakBlock",
105
+ "SequentialGather",
106
+ "CommandBlock",
107
+ "CooldownBlock",
108
+ "EmbedBlock",
109
+ "FiftyFiftyBlock",
110
+ "IfBlock",
111
+ "LooseVariableGetterBlock",
112
+ "MathBlock",
113
+ "OverrideBlock",
114
+ "PythonBlock",
115
+ "RandomBlock",
116
+ "RangeBlock",
117
+ "RedirectBlock",
118
+ "ReplaceBlock",
119
+ "RequireBlock",
120
+ "ShortCutRedirectBlock",
121
+ "StopBlock",
122
+ "StrfBlock",
123
+ "StrictVariableGetterBlock",
124
+ "SubstringBlock",
125
+ "URLEncodeBlock",
126
+ "UpperBlock",
127
+ "LowerBlock",
128
+ "CountBlock",
129
+ "LengthBlock",
130
+ )
@@ -0,0 +1,60 @@
1
+ from typing import Dict, List, Optional, Tuple, Union
2
+
3
+ from ..interface import Block
4
+ from ..interpreter import Context
5
+
6
+
7
+ class AllowedMentionsBlock(Block):
8
+ """
9
+ The ``{allowedmentions}`` block attempts to enable mentioning of roles.
10
+ Passing no parameter enables mentioning of all roles within the message
11
+ content. However passing a role name or ID to the block parameter allows
12
+ mentioning of that specific role only. Multiple role name or IDs can be
13
+ included, separated by a comma ",". By default, mentioning is only
14
+ triggered if the execution author has "manage server" permissions. However,
15
+ using the "override" keyword as a payload allows mentioning to be triggered
16
+ by anyone.
17
+
18
+ **Usage:** ``{allowedmentions(<role, None>):["override", None]}``
19
+
20
+ **Aliases:** ``mentions``
21
+
22
+ **Payload:** "override", None
23
+
24
+ **Parameter:** role, None
25
+
26
+ **Examples:** ::
27
+
28
+ {allowedmentions}
29
+ {allowedmentions:override}
30
+ {allowedmentions(@Admin, Moderator):override}
31
+ {allowedmentions(763522431151112265, 812949167190048769)}
32
+ {mentions(763522431151112265, 812949167190048769):override}
33
+ """
34
+
35
+ ACCEPTED_NAMES: Tuple[str, ...] = ("allowedmentions", "mentions")
36
+ PAYLOADS: Tuple[str, ...] = ("override",)
37
+
38
+ @classmethod
39
+ def will_accept(cls, ctx: Context) -> bool:
40
+ if ctx.verb.payload and ctx.verb.payload not in cls.PAYLOADS:
41
+ return False
42
+ return super().will_accept(ctx)
43
+
44
+ def process(self, ctx: Context) -> Optional[str]:
45
+ actions: Optional[Dict[str, Union[bool, List[str]]]] = ctx.response.actions.get(
46
+ "allowed_mentions", None
47
+ )
48
+ if actions:
49
+ return None
50
+ if not (param := ctx.verb.parameter):
51
+ ctx.response.actions["allowed_mentions"] = {
52
+ "mentions": True,
53
+ "override": True if ctx.verb.payload else False,
54
+ }
55
+ return ""
56
+ ctx.response.actions["allowed_mentions"] = {
57
+ "mentions": [r.strip() for r in param.split(",")],
58
+ "override": True if ctx.verb.payload else False,
59
+ }
60
+ return ""
@@ -0,0 +1,43 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional, Tuple
4
+
5
+ from ..adapter import StringAdapter
6
+ from ..interface import verb_required_block
7
+ from ..interpreter import Context
8
+
9
+
10
+ __all__: Tuple[str, ...] = ("AssignmentBlock",)
11
+
12
+
13
+ class AssignmentBlock(verb_required_block(False, parameter=True)): # type: ignore
14
+ """
15
+ Variables are useful for choosing a value and referencing it later in a tag.
16
+ Variables can be referenced using brackets as any other block.
17
+
18
+ **Usage:** ``{=(<name>):<value>}``
19
+
20
+ **Aliases:** ``assign, let, var``
21
+
22
+ **Payload:** value
23
+
24
+ **Parameter:** name
25
+
26
+ **Examples:** ::
27
+
28
+ {=(prefix):!}
29
+ The prefix here is `{prefix}`.
30
+ # The prefix here is `!`.
31
+
32
+ {assign(day):Monday}
33
+ {if({day}==Wednesday):It's Wednesday my dudes!|The day is {day}.}
34
+ # The day is Monday.
35
+ """
36
+
37
+ ACCEPTED_NAMES: Tuple[str, ...] = ("=", "assign", "let", "var")
38
+
39
+ def process(self, ctx: Context) -> Optional[str]:
40
+ if ctx.verb.parameter is None:
41
+ return None
42
+ ctx.response.variables[ctx.verb.parameter] = StringAdapter(str(ctx.verb.payload))
43
+ return ""
@@ -0,0 +1,41 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional, Tuple, cast
4
+
5
+ from ..interface import Block
6
+ from ..interpreter import Context
7
+ from . import helper_parse_if
8
+
9
+
10
+ __all__: Tuple[str, ...] = ("BreakBlock",)
11
+
12
+
13
+ class BreakBlock(Block):
14
+ """
15
+ The break block will force the tag output to only be the payload of this block, if the passed
16
+ expresssion evaluates true.
17
+ If no message is provided to the payload, the tag output will be empty.
18
+
19
+ This differs from the `StopBlock` as the stop block stops all tagscript processing and returns
20
+ its message while the break block continues to process blocks. If command blocks exist after
21
+ the break block, they will still execute.
22
+
23
+ **Usage:** ``{break(<expression>):[message]}``
24
+
25
+ **Aliases:** ``short, shortcircuit``
26
+
27
+ **Payload:** message
28
+
29
+ **Parameter:** expression
30
+
31
+ **Examples:** ::
32
+
33
+ {break({args}==):You did not provide any input.}
34
+ """
35
+
36
+ ACCEPTED_NAMES: Tuple[str, ...] = ("break", "shortcircuit", "short")
37
+
38
+ def process(self, ctx: Context) -> Optional[str]:
39
+ if helper_parse_if(cast(str, ctx.verb.parameter)):
40
+ ctx.response.body = ctx.verb.payload if ctx.verb.payload != None else "" # noqa: E711
41
+ return ""
@@ -0,0 +1,63 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Tuple
4
+
5
+ from ..interface import Block
6
+ from ..interpreter import Context
7
+
8
+
9
+ __all__: Tuple[str, ...] = ("UpperBlock", "LowerBlock")
10
+
11
+
12
+ class UpperBlock(Block):
13
+ """Converts the given text to uppercase.
14
+
15
+ **Usage:** ``{upper([text]))}``
16
+
17
+ **Aliases:** ``uppercase, upper``
18
+
19
+ **Payload:** None
20
+
21
+ **Parameter:** text
22
+
23
+ **Examples:** ::
24
+
25
+ The text is {lower(ThIs Is A TeXt)}!
26
+ # The text is THIS IS A TEXT!
27
+
28
+ You have entered {lower({args})}!
29
+ # You have entered HELLO WORLD!
30
+ """
31
+
32
+ ACCEPTED_NAMES: Tuple[str, ...] = ("upper", "uppercase")
33
+
34
+ def process(self, ctx: Context) -> str:
35
+ text = str(ctx.verb.parameter).upper()
36
+ return "" if text == "NONE" else text
37
+
38
+
39
+ class LowerBlock(Block):
40
+ """Converts the given text to lowercase.
41
+
42
+ **Usage:** ``{lower([text])}``
43
+
44
+ **Aliases:** ``lowercase, lower``
45
+
46
+ **Payload:** None
47
+
48
+ **Parameter:** text
49
+
50
+ **Examples:** ::
51
+
52
+ The text is {lower(ThIs Is A TeXt)}!
53
+ # The text is this is a text!
54
+
55
+ You have entered {lower({args})}!
56
+ # You have entered hello world!
57
+ """
58
+
59
+ ACCEPTED_NAMES: Tuple[str, ...] = ("lower", "lowercase")
60
+
61
+ def process(self, ctx: Context) -> str:
62
+ text = str(ctx.verb.parameter).lower()
63
+ return "" if text == "none" else text
@@ -0,0 +1,141 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ from types import TracebackType
5
+ from typing import Any, Awaitable, Generator, Iterator, List, Optional, Tuple, Type, TypeVar
6
+
7
+ from ..interface import Block, verb_required_block
8
+ from ..interpreter import Context
9
+
10
+ T = TypeVar("T")
11
+
12
+
13
+ __all__: Tuple[str, ...] = ("CommandBlock", "OverrideBlock", "SequentialGather")
14
+
15
+
16
+ class CommandBlock(verb_required_block(True, payload=True)): # type: ignore
17
+ """
18
+ Run a command as if the tag invoker had ran it. Only 3 command
19
+ blocks can be used in a tag.
20
+
21
+ **Usage:** ``{command:<command>}``
22
+
23
+ **Aliases:** ``c, com, command``
24
+
25
+ **Payload:** command
26
+
27
+ **Parameter:** None
28
+
29
+ **Examples:** ::
30
+
31
+ {c:ping}
32
+ # invokes ping command
33
+
34
+ {c:ban {target(id)} Chatflood/spam}
35
+ # invokes ban command on the pinged user with the reason as "Chatflood/spam"
36
+ """
37
+
38
+ ACCEPTED_NAMES: Tuple[str, ...] = ("c", "com", "command")
39
+
40
+ def __init__(self, limit: int = 3):
41
+ self.limit = limit
42
+ super().__init__()
43
+
44
+ def process(self, ctx: Context) -> Optional[str]:
45
+ command = ctx.verb.payload.strip() # type: ignore
46
+ actions = ctx.response.actions.get("commands")
47
+ if actions:
48
+ if len(actions) >= self.limit:
49
+ return f"`COMMAND LIMIT REACHED ({self.limit})`"
50
+ else:
51
+ ctx.response.actions["commands"] = []
52
+ ctx.response.actions["commands"].append(command)
53
+ return ""
54
+
55
+
56
+ class OverrideBlock(Block):
57
+ """
58
+ Override a command's permission requirements. This can override
59
+ mod, admin, or general user permission requirements when running commands
60
+ with the :ref:`Command Block`. Passing no parameter will default to overriding
61
+ all permissions.
62
+
63
+ In order to add a tag with the override block, the tag author must have ``Manage
64
+ Server`` permissions.
65
+
66
+ This will not override bot owner commands or command checks.
67
+
68
+ **Usage:** ``{override(["admin"|"mod"|"permissions"]):[command]}``
69
+
70
+ **Payload:** command
71
+
72
+ **Parameter:** "admin", "mod", "permissions"
73
+
74
+ **Examples:** ::
75
+
76
+ {override}
77
+ # overrides all commands and permissions
78
+
79
+ {override(admin)}
80
+ # overrides commands that require the admin role
81
+
82
+ {override(permissions)}
83
+ {override(mod)}
84
+ # overrides commands that require the mod role or have user permission requirements
85
+ """
86
+
87
+ ACCEPTED_NAMES: Tuple[str, ...] = ("override",)
88
+
89
+ def process(self, ctx: Context) -> Optional[str]:
90
+ param = ctx.verb.parameter
91
+ if not param:
92
+ ctx.response.actions["overrides"] = {"admin": True, "mod": True, "permissions": True}
93
+ return ""
94
+
95
+ param = param.strip().lower()
96
+ if param not in ("admin", "mod", "permissions"):
97
+ return None
98
+ overrides = ctx.response.actions.get(
99
+ "overrides", {"admin": False, "mod": False, "permissions": False}
100
+ )
101
+ overrides[param] = True
102
+ ctx.response.actions["overrides"] = overrides
103
+ return ""
104
+
105
+
106
+ class SequentialGather(Awaitable[T]):
107
+ """
108
+ Use this to run commands sequentially.
109
+
110
+ Parameters
111
+ ----------
112
+ awaitables : Tuple[Awaitable[T]]
113
+ the awaitables to be run sequentially.
114
+
115
+ Returns
116
+ -------
117
+ `List[T]`
118
+ the result object.
119
+ """
120
+
121
+ def __init__(self, *awaitables: Awaitable[T]) -> None:
122
+ self.__awaitables: Tuple[Awaitable[T], ...] = awaitables
123
+ self.__iterator: Iterator[Awaitable[T]] = iter(self.__awaitables)
124
+ self.__results: List[T] = []
125
+ self.__lock: asyncio.Lock = asyncio.Lock()
126
+
127
+ def __await__(self) -> Generator[Any, None, List[T]]:
128
+ return self.__aenter__().__await__()
129
+
130
+ async def __aenter__(self) -> List[T]:
131
+ async with self.__lock:
132
+ for coro in self.__iterator:
133
+ await asyncio.sleep(0.10)
134
+ result: T = await coro
135
+ self.__results.append(result)
136
+ return self.__results
137
+
138
+ async def __aexit__(
139
+ self, exc_type: Type[BaseException], exc_value: BaseException, traceback: TracebackType
140
+ ) -> None:
141
+ pass