AdvancedTagScript 3.2.5__py3-none-any.whl → 3.3.0__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.
@@ -24,7 +24,6 @@ from .block import (
24
24
  helper_parse_if as helper_parse_if,
25
25
  helper_parse_list_if as helper_parse_list_if,
26
26
  helper_split as helper_split,
27
- easier_helper_split as easier_helper_split,
28
27
  AllowedMentionsBlock as AllowedMentionsBlock,
29
28
  AllBlock as AllBlock,
30
29
  AnyBlock as AnyBlock,
@@ -57,6 +56,9 @@ from .block import (
57
56
  LengthBlock as LengthBlock,
58
57
  CooldownBlock as CooldownBlock,
59
58
  JoinBlock as JoinBlock,
59
+ ListBlock as ListBlock,
60
+ CycleBlock as CycleBlock,
61
+ OrdinalBlock as OrdinalBlock,
60
62
 
61
63
  )
62
64
  from .interface import (
@@ -100,7 +102,6 @@ __all__: Tuple[str, ...] = (
100
102
  "helper_parse_if",
101
103
  "helper_parse_list_if",
102
104
  "helper_split",
103
- "easier_helper_split",
104
105
  "AllowedMentionsBlock",
105
106
  "AllBlock",
106
107
  "AnyBlock",
@@ -133,6 +134,9 @@ __all__: Tuple[str, ...] = (
133
134
  "CountBlock",
134
135
  "LengthBlock",
135
136
  "JoinBlock",
137
+ "ListBlock",
138
+ "CycleBlock",
139
+ "OrdinalBlock",
136
140
 
137
141
  "SafeObjectAdapter",
138
142
  "StringAdapter",
@@ -177,7 +181,7 @@ __all__: Tuple[str, ...] = (
177
181
  )
178
182
 
179
183
 
180
- __version__: Final[str] = "3.2.5"
184
+ __version__: Final[str] = "3.3.0"
181
185
 
182
186
 
183
187
  class VersionNamedTuple(NamedTuple):
@@ -103,7 +103,7 @@ class RedBotAdapter(SimpleAdapter["Red"]):
103
103
  Percentage of chunked guilds the bot has.
104
104
 
105
105
  .. warning::
106
- Attributes denoting `(*)` can only be used by the bot owner.
106
+ Attributes denoting ``(*)`` can only be used by the bot owner.
107
107
  """
108
108
 
109
109
  if not _has_redbot:
@@ -3,13 +3,12 @@ from __future__ import annotations
3
3
  from typing import Tuple
4
4
 
5
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
- )
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
+ )
13
12
 
14
13
  # isort: on
15
14
  from .allowedmentions import (
@@ -92,14 +91,22 @@ from .count import (
92
91
  from .joinblock import (
93
92
  JoinBlock as JoinBlock,
94
93
  )
94
+ from .listblock import (
95
+ ListBlock as ListBlock,
96
+ )
97
+ from .cycleblock import (
98
+ CycleBlock as CycleBlock,
99
+ )
100
+ from .ordblock import (
101
+ OrdinalBlock as OrdinalBlock,
102
+ )
95
103
 
96
104
  __all__: Tuple[str, ...] = (
97
105
  "implicit_bool",
98
- "helper_parse_if",
99
- "helper_parse_list_if",
100
- "helper_split",
101
- "easier_helper_split",
102
- "AllowedMentionsBlock",
106
+ "helper_parse_if",
107
+ "helper_parse_list_if",
108
+ "helper_split",
109
+ "AllowedMentionsBlock",
103
110
  "AllBlock",
104
111
  "AnyBlock",
105
112
  "AssignmentBlock",
@@ -131,4 +138,7 @@ __all__: Tuple[str, ...] = (
131
138
  "CountBlock",
132
139
  "LengthBlock",
133
140
  "JoinBlock",
141
+ "ListBlock",
142
+ "CycleBlock",
143
+ "OrdinalBlock",
134
144
  )
@@ -12,9 +12,15 @@ __all__: Tuple[str, ...] = ("AssignmentBlock",)
12
12
 
13
13
  class AssignmentBlock(verb_required_block(False, parameter=True)): # type: ignore
14
14
  """
15
- Variables are useful for choosing a value and referencing it later in a tag.
15
+ Variables are useful for storing a value and referencing it later in a tag.
16
16
  Variables can be referenced using brackets as any other block.
17
17
 
18
+ .. note::
19
+ - Variables are not parsed by default. You must use the ``parse`` method to parse a variable.
20
+ - They are one of the most versatile and powerful features of TagScript.
21
+ - By default, a tag cannot store data between invocations. This is where ``variables`` come in.
22
+ - They are the only way to **store** data. And later **retrieve** it within the same invocation.
23
+
18
24
  **Usage:** ``{=(<name>):<value>}``
19
25
 
20
26
  **Aliases:** ``assign, let, var``
@@ -23,8 +29,20 @@ class AssignmentBlock(verb_required_block(False, parameter=True)): # type: igno
23
29
 
24
30
  **Parameter:** name
25
31
 
26
- **Examples:** ::
32
+ **Examples:**
33
+
34
+ .. code-block:: yaml
35
+
36
+ {=(message1):Hi there! How are you?}
37
+ {=(message2):It's a beautiful day today!}
38
+ {=(message3):Did you know that TagScript is a powerful tool?}
27
39
 
40
+ Now, call the variables by their names:
41
+ {message1} # Hi there! How are you?
42
+ {message2} # It's a beautiful day today!
43
+ {message3} # Did you know that TagScript is a powerful tool?
44
+
45
+ More example:
28
46
  {=(prefix):!}
29
47
  The prefix here is `{prefix}`.
30
48
  # The prefix here is `!`.
@@ -32,6 +50,162 @@ class AssignmentBlock(verb_required_block(False, parameter=True)): # type: igno
32
50
  {assign(day):Monday}
33
51
  {if({day}==Wednesday):It's Wednesday my dudes!|The day is {day}.}
34
52
  # The day is Monday.
53
+
54
+ .. caution::
55
+ - You can name variables with **anything** ``except`` existing block names or aliases.
56
+ - They will ``not`` reference the value in payload, if the name is same as an existing block name or alias.
57
+
58
+ ----
59
+
60
+ .. important:: How Argument Parsing Works - In Detail
61
+
62
+ - A variable is essentially a string that can be treated as a sequence of elements (words, numbers, etc.) when accessed.
63
+ These **elements** are split using ``delimiters`` (spaces by default) and are indexed sequentially starting from ``1``.
64
+ - A delimiter is a sequence of one or more characters that are used to split a string into a sequence of elements.
65
+ - Once a variable is assigned, its value can be referenced and ``parsed`` (split and indexed)
66
+ to extract ``specific`` parts. Let's take a look at **how** it works.
67
+ - Parsing out of bounds index will return the whole string.
68
+
69
+ ----
70
+
71
+ .. rubric:: **Basic Argument Parsing**
72
+
73
+ Example
74
+ - "Coolaid is setting up the table. So, he grabbed - a cordless drill, some screws, a spirit level, and a pair of work gloves"
75
+
76
+ - So, our ``argument`` or ``args`` in short, would be:
77
+
78
+ .. code-block:: yaml
79
+
80
+ {=(args):Coolaid is setting up the table. So, he grabbed - a cordless drill, some screws, a spirit level, and a pair of work gloves}
81
+
82
+ .. note:: ``args`` is just a variable name, perhaps the most common name, but you can name it anything.
83
+
84
+ - Since, the default delimiter is ``space``, so you can access the ``each element`` as follows:
85
+
86
+ .. code-block:: yaml
87
+
88
+ {args(1)} -> Coolaid
89
+ {args(2)} -> is
90
+ {args(3)} -> setting
91
+ {args(4)} -> up
92
+ {args(5)} -> the
93
+ {args(6)} -> table.
94
+
95
+ {args(31)} -> Would return the whole string since it doesn't exist (indexing 31st element is out of bounds).
96
+
97
+ - ``0`` is special and returns the ``last`` element:
98
+
99
+ .. code-block:: yaml
100
+
101
+ {args(0)} -> gloves
102
+
103
+ - Negative indices allow you to access elements from the end of the sequence:
104
+
105
+ .. code-block:: yaml
106
+
107
+ {args(-1)} -> work
108
+ {args(-2)} -> of
109
+ {args(-3)} -> pair
110
+ {args(-4)} -> a
111
+ {args(-5)} -> and
112
+ {args(-6)} -> level,
113
+
114
+ .. rubric:: Prefix Range Access (``+n``)
115
+
116
+ - Prefixing an index with ``+`` returns all elements from the start up to and including that position:
117
+
118
+ .. code-block:: yaml
119
+
120
+ {args(+3)} -> Coolaid is setting
121
+ {args(+7)} -> Coolaid is setting up the table. So,
122
+ {args(+13)} -> Coolaid is setting up the table. So, he grabbed - a cordless drill,
123
+
124
+ .. rubric:: Suffix Range Access (``n+``)
125
+
126
+ - Suffixing an index with ``+`` returns all elements from that position (counting from the start) to the end:
127
+
128
+ .. code-block:: yaml
129
+
130
+ {args(3+)} -> setting up the table. So, he grabbed - a cordless drill, some screws, a spirit level, and a pair of work gloves
131
+ {args(7+)} -> So, he grabbed - a cordless drill, some screws, a spirit level, and a pair of work gloves
132
+ {args(13+)} -> drill, some screws, a spirit level, and a pair of work gloves
133
+
134
+ .. rubric:: Negative Range Access (``-n+``)
135
+
136
+ - Appending ``+`` to an negative-index returns a range — all elements from that position to the end:
137
+ - Negative indices are **first** resolved from the end of the sequence,
138
+ then range access continues forward to the end.
139
+
140
+ .. code-block:: yaml
141
+
142
+ {args(-1+)} -> work gloves # Since {args(0)} == gloves
143
+ {args(-11+)} -> drill, some screws, a spirit level, and a pair of work gloves
144
+
145
+ .. tip::
146
+ - ``+n`` → from start → n
147
+ - ``n+`` → from n → end (index resolved first)
148
+ - ``-n`` → nth element from end
149
+ - ``-n+`` → nth element from end → then forward to end (index resolved first)
150
+
151
+ -----
152
+
153
+ .. rubric:: **Advanced Argument Parsing**
154
+
155
+ A **custom delimiter** can be passed as the payload to change how
156
+ the value is split. The syntax is ``{variable(index):delimiter}``:
157
+
158
+ .. code-block:: yaml
159
+
160
+ # Using the same argument as before.
161
+ {=(args):Coolaid is setting up the table. So, he grabbed - a cordless drill, some screws, a spirit level, and a pair of work gloves}
162
+
163
+ 1st Example:
164
+ {args(1):.} -> Coolaid is setting up the table
165
+ {args(2):.} -> So, he grabbed - a cordless drill, some screws, a spirit level, and a pair of work gloves
166
+ {args(3):.} -> Would return the entire string since there is no 3rd element.
167
+
168
+ 2nd Example:
169
+ {args(1):-} -> Coolaid is setting up the table. So, he grabbed
170
+ {args(2):-} -> a cordless drill, some screws, a spirit level, and a pair of work gloves
171
+
172
+ .. note::
173
+ - In the 1st example, the custom delimiter is ``.``, hence the string is split by ``.`` leaving 2 elements.
174
+ - In the 2nd example, the custom delimiter is ``-``, hence the string is split by ``-`` leaving 2 elements.
175
+ - Since in both the examples, there are ``2 elements``, so ``args(3)`` would return the entire string.
176
+ Because the index ``3rd`` element is out of bounds.
177
+
178
+ .. rubric:: **Nested Variables**
179
+
180
+ - Variables can be **nested** to perform multi-level parsing:
181
+
182
+ .. code-block:: yaml
183
+
184
+ {=(raw):A - B, C, D}
185
+ {=(part):{raw(2):-}}
186
+
187
+ # "{raw(2):-}" splits "raw" by "-" and returns the 2nd element -> "B, C, D" (1st element is "A")
188
+ # Therefore, "part" == "B, C, D"
189
+
190
+ {part(1):,} -> B
191
+ {part(2):,} -> C
192
+
193
+ Another Example:
194
+ # What if you want to parse through the things that Coolaid grabbed?
195
+ # If you look closely the "-" delimiter is placed conveniently to separate the items. So, we'll use it:
196
+
197
+ {=(args):Coolaid is setting up the table. So, he grabbed - a cordless drill, some screws, a spirit level, and a pair of work gloves}
198
+ {=(items):{args(2):-}}
199
+
200
+ # "{args(2):-}" splits "args" by "-" and returns the 2nd element -> "a cordless drill, ... and a pair of work gloves"
201
+ # Therefore, "items" == "a cordless drill, some screws, a spirit level, and a pair of work gloves"
202
+
203
+ Items:
204
+ {items(1):,} -> a cordless drill
205
+ {items(2):,} -> some screws
206
+ {items(3):,} -> a spirit level
207
+ {items(4):,} -> and a pair of work gloves
208
+
35
209
  """
36
210
 
37
211
  ACCEPTED_NAMES: Tuple[str, ...] = ("=", "assign", "let", "var")
@@ -25,6 +25,7 @@ class UpperBlock(Block):
25
25
  The text is {upper(ThIs Is A TeXt)}!
26
26
  # The text is THIS IS A TEXT!
27
27
 
28
+ {=(args):Hello World}
28
29
  You have entered {upper({args})}!
29
30
  # You have entered HELLO WORLD!
30
31
  """
@@ -52,6 +53,7 @@ class LowerBlock(Block):
52
53
  The text is {lower(ThIs Is A TeXt)}!
53
54
  # The text is this is a text!
54
55
 
56
+ {=(args):HELLO WORLD}
55
57
  You have entered {lower({args})}!
56
58
  # You have entered hello world!
57
59
  """
@@ -114,7 +114,7 @@ class SequentialGather(Awaitable[T]):
114
114
 
115
115
  Returns
116
116
  -------
117
- `List[T]`
117
+ ``List[T]``
118
118
  the result object.
119
119
  """
120
120
 
@@ -28,6 +28,9 @@ class CooldownBlock(verb_required_block(True, payload=True, parameter=True)): #
28
28
  cooldown is exceeded. If no message is passed, the default message will be sent instead.
29
29
  The cooldown message supports 2 blocks: ``key`` and ``retry_after``.
30
30
 
31
+ .. note::
32
+ - Delimiter for the parameter (``<rate>`` and ``<per>``): ``|`` or ``~``. Where ``|`` takes priority over ``~``.
33
+
31
34
  **Usage:** ``{cooldown(<rate>|<per>):<key>|[message]}``
32
35
 
33
36
  **Payload:** key, message
@@ -0,0 +1,74 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional, Tuple, cast
4
+
5
+ from ..interface import verb_required_block
6
+ from ..interpreter import Context
7
+
8
+
9
+ __all__: Tuple[str, ...] = ("CycleBlock",)
10
+
11
+
12
+ class CycleBlock(verb_required_block(True, payload=True, parameter=True)): # type: ignore
13
+ """
14
+ The cycle block returns the element in the payload that corresponds to the
15
+ index value in the parameter. If the index is out of bounds, it loops around
16
+ using modulo (``index % list_length``).
17
+
18
+ List and Cycle blocks are another way to parse through a list of values in
19
+ TagScript. They both strictly use either commas ``,`` or tildes ``~`` as the
20
+ delimiters for the list placed in the block's payload. Use tildes when elements
21
+ contain commas. These blocks only function in Tags.
22
+
23
+ Cycles use ``0`` as the index for the first element. Negative values allow
24
+ for backward parsing. The block will return an error message if the value in
25
+ the parameter is not a number.
26
+
27
+ **Usage:** ``{cycle(<index>):<elem>,<elem2>,...}``
28
+
29
+ **Aliases:** ``None``
30
+
31
+ **Payload:** list of elements (comma or tilde separated)
32
+
33
+ **Parameter:** index
34
+
35
+ **Examples:**
36
+
37
+ .. code-block:: yaml
38
+
39
+ {cycle(1):Cake,Candy,Chips,Cookies,Donut}
40
+ # Candy
41
+
42
+ {cycle(13):Cake,Candy,Chips,Cookies,Donut}
43
+ # Cookies
44
+ # (The list has 5 elements. 13 modulo 5 = 3. Index 3 is "Cookies".)
45
+
46
+ {cycle(3):0,1,2}
47
+ # 0
48
+ # (3 modulo 3 = 0. Index 0 is "0".)
49
+
50
+ Negative indices:
51
+ {cycle(-1):Apple,Banana,Cherry}
52
+ # Cherry
53
+
54
+ {cycle(-69):Charlie,Aid,Bob,Dave,Eve,Phen,Steve,Tom,Wendy,Xavier}
55
+ # Aid
56
+ # (-69 modulo 10 = 1. Index 1 is "Aid".)
57
+
58
+ """
59
+
60
+ ACCEPTED_NAMES: Tuple[str, ...] = ("cycle",)
61
+
62
+ def process(self, ctx: Context) -> Optional[str]:
63
+ try:
64
+ index = int(cast(str, ctx.verb.parameter))
65
+ except (ValueError, TypeError):
66
+ return "Invalid index: parameter must be a number."
67
+
68
+ payload = cast(str, ctx.verb.payload)
69
+ if "~" in payload:
70
+ items = payload.split("~")
71
+ else:
72
+ items = payload.split(",")
73
+
74
+ return items[index % len(items)]
@@ -8,10 +8,9 @@ from discord import Colour, Embed
8
8
 
9
9
  from ..interface import Block
10
10
  from ..interpreter import Context
11
- from .helpers import helper_split, easier_helper_split, implicit_bool
11
+ from .helpers import helper_split, implicit_bool
12
12
  from ..utils import truncate
13
13
  from ..exceptions import BadColourArgument, EmbedParseError
14
- from .._warnings import removal
15
14
 
16
15
  try:
17
16
  import orjson # noqa: F401
@@ -59,8 +58,10 @@ def set_dynamic_url(embed: Embed, attribute: str, value: str) -> None:
59
58
 
60
59
 
61
60
  def add_field(embed: Embed, _: str, payload: str) -> None:
62
- if (data := easier_helper_split(payload, maxsplit=3)) is None: # type: ignore
63
- raise EmbedParseError("`add_field` payload was not split by |")
61
+ if (data := helper_split(payload, maxsplit=2, double_semicolon=True)) is None: # type: ignore
62
+ raise EmbedParseError(
63
+ "`add_field` payload was not split by `;;` (double semicolon), `|` (pipe), or `~` (tilde)"
64
+ )
64
65
  try:
65
66
  name, value, _inline = data
66
67
  inline = implicit_bool(_inline)
@@ -69,7 +70,10 @@ def add_field(embed: Embed, _: str, payload: str) -> None:
69
70
  "`inline` argument for `add_field` is not a boolean value (_inline)"
70
71
  )
71
72
  except ValueError:
72
- name, value = cast(List[str], helper_split(payload, 2)) # type: ignore
73
+ name, value = cast(
74
+ List[str],
75
+ helper_split(payload, maxsplit=1, double_semicolon=True),
76
+ )
73
77
  inline = False
74
78
  name = truncate(name, max=FIELD_LIMITS["field.name"])
75
79
  value = truncate(value, max=FIELD_LIMITS["field.value"])
@@ -77,7 +81,7 @@ def add_field(embed: Embed, _: str, payload: str) -> None:
77
81
 
78
82
 
79
83
  def set_footer(embed: Embed, _: str, payload: str) -> None:
80
- data = easier_helper_split(payload, maxsplit=2) # type: ignore
84
+ data = helper_split(payload, maxsplit=1, double_semicolon=True) # type: ignore
81
85
  if data is None:
82
86
  embed.set_footer(text=truncate(payload, max=FIELD_LIMITS["footer.text"]))
83
87
  else:
@@ -85,6 +89,15 @@ def set_footer(embed: Embed, _: str, payload: str) -> None:
85
89
  embed.set_footer(text=truncate(text, max=FIELD_LIMITS["footer.text"]), icon_url=icon_url)
86
90
 
87
91
 
92
+ def set_author(embed: Embed, _: str, payload: str) -> None:
93
+ data = helper_split(payload, maxsplit=1, double_semicolon=True) # type: ignore
94
+ if data is None:
95
+ embed.set_author(name=truncate(payload, max=FIELD_LIMITS["author.name"]))
96
+ else:
97
+ name, icon_url = data
98
+ embed.set_author(name=truncate(name, max=FIELD_LIMITS["author.name"]), icon_url=icon_url)
99
+
100
+
88
101
  # Discord embed field character limits
89
102
  # https://docs.discord.com/developers/resources/message#embed-object-embed-limits
90
103
  FIELD_LIMITS: Dict[str, int] = {
@@ -110,19 +123,41 @@ class EmbedBlock(Block):
110
123
  Multiple embed generators are available online to visualize and generate
111
124
  embed JSON.
112
125
 
126
+ .. important::
127
+ - `Embed Limits are: <https://docs.discord.com/developers/resources/message#embed-object-embed-limits>`_
128
+ - Title: 256 characters
129
+ - Description: 4096 characters
130
+ - Footer: 2048 characters
131
+ - Author: 256 characters
132
+ - Field name: 256 characters
133
+ - Field value: 1024 characters
134
+ - The total length of the embed must not exceed 6000 characters.
135
+
113
136
  **Usage:** ``{embed(<json>)}``
114
137
 
115
138
  **Payload:** None
116
139
 
117
140
  **Parameter:** json
118
141
 
119
- **Examples:** ::
142
+ **Examples:**
143
+
144
+ .. code-block:: yaml
120
145
 
146
+ Example 1:
121
147
  {embed({"title":"Hello!", "description":"This is a test embed."})}
148
+
149
+ Example 2:
122
150
  {embed({
123
151
  "title":"Here's a random duck!",
124
152
  "image":{"url":"https://random-d.uk/api/randomimg"},
125
- "color":15194415
153
+ "color":15194415,
154
+ "fields": [
155
+ {
156
+ "name": "Fun Fact",
157
+ "value": "Ducks are birds that are well-adapted to life in and around water.",
158
+ "inline": false
159
+ }
160
+ ]
126
161
  })}
127
162
 
128
163
  **Manual**
@@ -135,11 +170,12 @@ class EmbedBlock(Block):
135
170
  * ``url``
136
171
  * ``thumbnail``
137
172
  * ``image``
173
+ * ``author``
138
174
  * ``footer``
139
175
  * ``field`` - (See below)
140
176
 
141
- Adding a field to an embed requires the payload to be split by ``|``,
142
- ``;`` or ``,`` into either 2 or 3 parts. The first part is the name
177
+ Adding a field to an embed requires the payload to be split by ``;;``,
178
+ ``|`` or ``~`` into either 2 or 3 parts. The first part is the name
143
179
  of the field, the second is the text of the field, and the third
144
180
  optionally specifies whether the field should be inline.
145
181
 
@@ -155,16 +191,22 @@ class EmbedBlock(Block):
155
191
  {embed(title):Rules}
156
192
  {embed(description):Follow these rules to ensure a good experience in our server!}
157
193
  {embed(field):Rule 1|Respect everyone you speak to.|false}
194
+ {embed(author):Mod Team|{author(avatar)}}
158
195
  {embed(footer):Thanks for reading!|{guild(icon)}}
159
196
 
160
197
  Both methods can be combined to create an embed in a tag.
161
198
  The following tagscript uses JSON to create an embed with fields and later
162
199
  set the embed title.
163
200
 
164
- ::
201
+ .. caution::
202
+ - The ``JSON`` block acts as a base for the embed.
203
+ - Since blocks are processed in **order**, the ``JSON`` block **must** come **before** any manual attribute blocks.
204
+ - The manual attributes are used to ``modify`` or ``add`` to the embed created by the ``JSON`` block.
165
205
 
166
- {embed(title):my embed title}
167
- {embed({{
206
+ .. code-block:: yaml
207
+
208
+ {embed({
209
+ "description": "This is a test description.",
168
210
  "fields": [
169
211
  {
170
212
  "name": "Field 1",
@@ -173,6 +215,8 @@ class EmbedBlock(Block):
173
215
  }
174
216
  ]
175
217
  })}
218
+ {embed(title):My embed title}
219
+
176
220
  """
177
221
 
178
222
  ACCEPTED_NAMES: Tuple[str, ...] = ("embed",)
@@ -186,23 +230,10 @@ class EmbedBlock(Block):
186
230
  "thumbnail": set_dynamic_url,
187
231
  "image": set_dynamic_url,
188
232
  "field": add_field,
233
+ "author": set_author,
189
234
  "footer": set_footer,
190
235
  }
191
236
 
192
- @removal(
193
- name="EmbedBlock",
194
- reason=(
195
- "One of EmbedBlock's trait is scheduled to be removed in the next minor release, "
196
- "A minor exception handling which would restrict the embed from getting sent and "
197
- "would raise TagScriptEngine.exceptions.EmbedParseError incase it had more than "
198
- "6000 characters, to know more about the limitations of discord embeds refer to the "
199
- "[Official Discord API Docs](https://discord.com/developers/docs/resources/channel#embed-object-embed-limits)."
200
- ),
201
- version="3.2.0",
202
- )
203
- def __init__(self) -> None:
204
- super().__init__()
205
-
206
237
  @staticmethod
207
238
  def get_embed(ctx: Context) -> Embed:
208
239
  return ctx.response.actions.get("embed", Embed())
@@ -4,17 +4,15 @@ import re
4
4
  from typing import Dict, List, Optional, Tuple
5
5
 
6
6
 
7
- __all__: Tuple[str, ...] = (
8
- "implicit_bool",
9
- "helper_parse_if",
10
- "helper_split",
11
- "easier_helper_split",
12
- "helper_parse_list_if",
13
- )
14
-
15
- SPLIT_REGEX: re.Pattern[str] = re.compile(r"(?<!\\)\|")
16
- EASIER_SPLIT_REGEX: re.Pattern[str] = re.compile(r"/[\~;]/")
17
- BOOL_LOOKUP: Dict[str, bool] = {
7
+ __all__: Tuple[str, ...] = (
8
+ "implicit_bool",
9
+ "helper_parse_if",
10
+ "helper_split",
11
+ "helper_parse_list_if",
12
+ )
13
+
14
+ SPLIT_REGEX: re.Pattern[str] = re.compile(r"(?<!\\)\|")
15
+ BOOL_LOOKUP: Dict[str, bool] = {
18
16
  "true": True,
19
17
  "false": False,
20
18
  "enable": True,
@@ -110,51 +108,32 @@ def helper_parse_if(string: str) -> Optional[bool]:
110
108
  except Exception:
111
109
  pass
112
110
 
113
-
114
- def helper_split(
115
- split_string: str, easy: bool = True, *, maxsplit: Optional[int] = None
116
- ) -> Optional[List[str]]:
117
- """
118
- A helper method to universalize the splitting logic used in multiple
119
- blocks and adapters. Please use this wherever a verb needs content to
120
- be chopped at | , or ~!
121
-
122
- >>> helper_split("this, should|work")
123
- ["this, should", "work"]
124
- """
125
- args = (maxsplit,) if maxsplit is not None else ()
126
- if "|" in split_string:
127
- return SPLIT_REGEX.split(split_string, *args)
128
- if easy:
129
- if "~" in split_string:
130
- return split_string.split("~", *args)
131
- if "," in split_string:
132
- return split_string.split(",", *args)
133
- return
134
-
135
-
136
- def easier_helper_split(
137
- split_string: str, *, maxsplit: Optional[int] = None
138
- ) -> Optional[List[str]]:
139
- """
140
- A helper method to universalize the splitting logic used in blocks
141
- and adapters. Please use this wherever a verb needs content to be
142
- chopped at `|`, `,` or `;`.
143
-
144
- >>> easier_helper_split("this, should|work")
145
- ["this, should", "work"]
146
-
147
- >>> easier_helper_split("this, should;work~as well")
148
- ["this, should", "work", "as well"]
149
- """
150
- args = (maxsplit,) if maxsplit is not None else ()
151
- if "|" in split_string:
152
- return SPLIT_REGEX.split(split_string, *args)
153
- if "~" in split_string:
154
- return EASIER_SPLIT_REGEX.split(split_string, *args)
155
- if ";" in split_string:
156
- return EASIER_SPLIT_REGEX.split(split_string, *args)
157
- return
111
+
112
+ def helper_split(
113
+ split_string: str,
114
+ easy: bool = True,
115
+ *,
116
+ maxsplit: Optional[int] = None,
117
+ double_semicolon: bool = False,
118
+ ) -> Optional[List[str]]:
119
+ """
120
+ A helper method to universalize the splitting logic used in multiple
121
+ blocks and adapters. Please use this wherever a verb needs content to
122
+ be chopped at ``|`` or ``~``. Embed parsing can also opt into ``;;``
123
+ taking priority over the other delimiters.
124
+
125
+ >>> helper_split("this, should|work")
126
+ ["this, should", "work"]
127
+ """
128
+ args = (maxsplit,) if maxsplit is not None else ()
129
+ if double_semicolon and ";;" in split_string:
130
+ return split_string.split(";;", *args)
131
+ if "|" in split_string:
132
+ return SPLIT_REGEX.split(split_string, *args)
133
+ if easy:
134
+ if "~" in split_string:
135
+ return split_string.split("~", *args)
136
+ return
158
137
 
159
138
 
160
139
  def helper_parse_list_if(if_string):
@@ -33,8 +33,8 @@ class JoinBlock(verb_required_block(False, payload=True, parameter=True)): # ty
33
33
  {join():an example sentence}
34
34
  # anexamplesentence
35
35
 
36
- {join(-):one two three}
37
- # one-two-three
36
+ {join(-):cool aid man}
37
+ # cool-aid-man
38
38
  """
39
39
 
40
40
  ACCEPTED_NAMES: Tuple[str, ...] = ("join",)
@@ -0,0 +1,66 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional, Tuple, cast
4
+
5
+ from ..interface import verb_required_block
6
+ from ..interpreter import Context
7
+
8
+
9
+ __all__: Tuple[str, ...] = ("ListBlock",)
10
+
11
+
12
+ class ListBlock(verb_required_block(True, payload=True, parameter=True)): # type: ignore
13
+ """
14
+ The list block returns the element in the payload that corresponds to the
15
+ index value in the parameter. The block returns null if the index is out of bounds.
16
+
17
+ List and Cycle blocks are another way to parse through a list of values in
18
+ TagScript. They both strictly use either commas ``,`` or tildes ``~`` as the
19
+ delimiters for the list placed in the block's payload. Use tildes when elements
20
+ contain commas. These blocks only function in Tags.
21
+
22
+ Lists use ``0`` as the index for the first element. Negative values allow
23
+ for backward parsing. The block will return an error message if the value in
24
+ the parameter is not a number.
25
+
26
+ **Usage:** ``{list(<index>):<elem>,<elem2>,...}``
27
+
28
+ **Aliases:** ``None``
29
+
30
+ **Payload:** list of elements (comma or tilde separated)
31
+
32
+ **Parameter:** index
33
+
34
+ **Examples:** ::
35
+
36
+ {list(0):Pizza~Burger~Pie~Chips~Lasagna}
37
+ # Pizza
38
+
39
+ {list(3):Pizza~Burger~Pie~Chips~Lasagna}
40
+ # Chips
41
+
42
+ {list(-1):Apple,Banana,Cherry}
43
+ # Cherry
44
+
45
+ {list(10):Apple,Banana,Cherry}
46
+ # (returns null — index out of bounds)
47
+ """
48
+
49
+ ACCEPTED_NAMES: Tuple[str, ...] = ("list",)
50
+
51
+ def process(self, ctx: Context) -> Optional[str]:
52
+ try:
53
+ index = int(cast(str, ctx.verb.parameter))
54
+ except (ValueError, TypeError):
55
+ return "Invalid index: parameter must be a number."
56
+
57
+ payload = cast(str, ctx.verb.payload)
58
+ if "~" in payload:
59
+ items = payload.split("~")
60
+ else:
61
+ items = payload.split(",")
62
+
63
+ try:
64
+ return items[index]
65
+ except IndexError:
66
+ return None
@@ -16,7 +16,7 @@ from pyparsing import (
16
16
  ZeroOrMore,
17
17
  alphas,
18
18
  nums,
19
- oneOf,
19
+ one_of,
20
20
  )
21
21
 
22
22
  from ..interface import Block
@@ -77,20 +77,20 @@ class NumericStringParser(object):
77
77
  expr: Forward = Forward()
78
78
  atom: ParserElement = (
79
79
  (
80
- Optional(oneOf("- +"))
81
- + (ident + lpar + expr + rpar | pi | e | fnumber).setParseAction(self.pushFirst)
80
+ Optional(one_of("- +"))
81
+ + (ident + lpar + expr + rpar | pi | e | fnumber).set_parse_action(self.pushFirst)
82
82
  )
83
- | Optional(oneOf("- +")) + Group(lpar + expr + rpar)
84
- ).setParseAction(self.pushUMinus)
83
+ | Optional(one_of("- +")) + Group(lpar + expr + rpar)
84
+ ).set_parse_action(self.pushUMinus)
85
85
  # by defining exponentiation as "atom [ ^ factor ]..." instead of
86
86
  # "atom [ ^ atom ]...", we get right-to-left exponents, instead of left-to-right
87
87
  # that is, 2^3^2 = 2^(3^2), not (2^3)^2.
88
88
  factor: Forward = Forward()
89
- factor << atom + ZeroOrMore((expop + factor).setParseAction(self.pushFirst)) # type: ignore
90
- term: ParserElement = factor + ZeroOrMore((multop + factor).setParseAction(self.pushFirst))
91
- expr << term + ZeroOrMore((addop + term).setParseAction(self.pushFirst)) # type: ignore
92
- final: ParserElement = expr + ZeroOrMore((iop + expr).setParseAction(self.pushFirst))
93
- # addop_term = ( addop + term ).setParseAction( self.pushFirst )
89
+ factor << atom + ZeroOrMore((expop + factor).set_parse_action(self.pushFirst)) # type: ignore
90
+ term: ParserElement = factor + ZeroOrMore((multop + factor).set_parse_action(self.pushFirst))
91
+ expr << term + ZeroOrMore((addop + term).set_parse_action(self.pushFirst)) # type: ignore
92
+ final: ParserElement = expr + ZeroOrMore((iop + expr).set_parse_action(self.pushFirst))
93
+ # addop_term = ( addop + term ).set_parse_action( self.pushFirst )
94
94
  # general_term = term + ZeroOrMore( addop_term ) | OneOrMore( addop_term)
95
95
  # expr << general_term
96
96
  self.bnf: ParserElement = final
@@ -145,9 +145,9 @@ class NumericStringParser(object):
145
145
  else:
146
146
  return float(op)
147
147
 
148
- def eval(self, num_string: str, parseAll: bool = True) -> Any:
148
+ def eval(self, num_string: str, parse_all: bool = True) -> Any:
149
149
  self.exprStack = []
150
- results = self.bnf.parseString(num_string, parseAll) # noqa: F841
150
+ results = self.bnf.parse_string(num_string, parse_all) # noqa: F841
151
151
  return self.evaluateStack(self.exprStack[:])
152
152
 
153
153
 
@@ -155,6 +155,71 @@ NSP: NumericStringParser = NumericStringParser()
155
155
 
156
156
 
157
157
  class MathBlock(Block):
158
+ """
159
+ The math block performs mathematical calculations from the given payload expression.
160
+
161
+ Supports standard arithmetic operators, exponentiation, modulo, in-place operators,
162
+ mathematical functions, and constants.
163
+
164
+ **Supported Operators:**
165
+
166
+ +----------+---------------------+
167
+ | Operator | Description |
168
+ +==========+=====================+
169
+ | ``+`` | Addition |
170
+ +----------+---------------------+
171
+ | ``-`` | Subtraction |
172
+ +----------+---------------------+
173
+ | ``*`` | Multiplication |
174
+ +----------+---------------------+
175
+ | ``/`` | Division |
176
+ +----------+---------------------+
177
+ | ``^`` | Exponentiation |
178
+ +----------+---------------------+
179
+ | ``%`` | Modulo |
180
+ +----------+---------------------+
181
+ | ``+=`` | In-place addition |
182
+ +----------+---------------------+
183
+ | ``-=`` | In-place subtraction|
184
+ +----------+---------------------+
185
+ | ``*=`` | In-place multiply |
186
+ +----------+---------------------+
187
+ | ``/=`` | In-place division |
188
+ +----------+---------------------+
189
+
190
+ **Supported Functions:**
191
+ ``sin``, ``cos``, ``tan``, ``sinh``, ``cosh``, ``tanh``,
192
+ ``exp``, ``abs``, ``trunc``, ``round``, ``sgn``,
193
+ ``log`` (base 10), ``ln`` (natural), ``log2``, ``sqrt``
194
+
195
+ **Constants:** ``PI``, ``E``
196
+
197
+ **Usage:** ``{math:<expression>}``
198
+
199
+ **Aliases:** ``m, +, calc``
200
+
201
+ **Payload:** expression
202
+
203
+ **Parameter:** None
204
+
205
+ **Examples:** ::
206
+
207
+ {math:2+3}
208
+ # 5
209
+
210
+ {m:round(7/3)}
211
+ # 2
212
+
213
+ {calc:sin(PI/2)}
214
+ # 1.0
215
+
216
+ {+:7*6}
217
+ # 42
218
+
219
+ {m:sqrt(144)}
220
+ # 12.0
221
+ """
222
+
158
223
  ACCEPTED_NAMES: Tuple[str, ...] = ("math", "m", "+", "calc")
159
224
 
160
225
  def process(self, ctx: Context) -> TypingOptional[str]:
@@ -0,0 +1,62 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional, Tuple, cast
4
+
5
+ from ..interface import verb_required_block
6
+ from ..interpreter import Context
7
+
8
+
9
+ __all__: Tuple[str, ...] = ("OrdBlock",)
10
+
11
+
12
+ def _ordinal(n: int) -> str:
13
+ """Return the ordinal string for an integer (e.g. 1 -> '1st')."""
14
+ if 11 <= abs(n) % 100 <= 13:
15
+ suffix = "th"
16
+ else:
17
+ suffix = {1: "st", 2: "nd", 3: "rd"}.get(abs(n) % 10, "th")
18
+ return f"{n}{suffix}"
19
+
20
+
21
+ class OrdinalBlock(verb_required_block(True, payload=True)): # type: ignore
22
+ """
23
+ The ordinal block returns the number in the payload with the correct ordinal
24
+ abbreviation following it.
25
+
26
+ **Usage:** ``{ordinal:<number>}``
27
+
28
+ **Aliases:** ``ord``
29
+
30
+ **Payload:** number
31
+
32
+ **Parameter:** None
33
+
34
+ **Examples:** ::
35
+
36
+ {ordinal:101}
37
+ # 101st
38
+
39
+ {ord:22}
40
+ # 22nd
41
+
42
+ {ordinal:3}
43
+ # 3rd
44
+
45
+ {ord:456}
46
+ # 456th
47
+
48
+ {ordinal:11}
49
+ # 11th
50
+
51
+ {ord:12}
52
+ # 12th
53
+ """
54
+
55
+ ACCEPTED_NAMES: Tuple[str, ...] = ("ordinal", "ord")
56
+
57
+ def process(self, ctx: Context) -> Optional[str]:
58
+ try:
59
+ number = int(cast(str, ctx.verb.payload).strip())
60
+ except (ValueError, TypeError):
61
+ return None
62
+ return _ordinal(number)
@@ -16,7 +16,7 @@ class StrfBlock(Block):
16
16
  Two types of timestamps are supported: ISO and epoch.
17
17
  If a timestamp isn't passed, the current UTC time is used.
18
18
 
19
- Invoking this block with ``unix`` will return the current Unix timestamp.
19
+ Invoking this block with :ref:`unix` will return the current Unix timestamp.
20
20
 
21
21
  **Usage:** ``{strf([timestamp]):<format>}``
22
22
 
@@ -10,6 +10,61 @@ __all__: Tuple[str, ...] = ("SubstringBlock",)
10
10
 
11
11
 
12
12
  class SubstringBlock(verb_required_block(True, parameter=True)): # type: ignore
13
+ """
14
+ The substring block extracts a specific portion of the payload text using ``0``-based indexing.
15
+ It supports both a single starting index and a specific range of characters.
16
+
17
+ .. important::
18
+
19
+ **Behavior:**
20
+
21
+ - If a single ``index`` is provided, it returns all characters from that position to the end
22
+ (counting from the start).
23
+ - From ``nth`` position (index) to end.
24
+
25
+ - If a range is provided using ``-`` (e.g., ``0-5``), it returns the characters from
26
+ the ``start`` index to the ``end`` index but **without** including the ``end`` th index.
27
+
28
+ **Usage:** ``{substr(<start[-end]>):<text>}``
29
+
30
+ **Aliases:** ``substring``
31
+
32
+ **Payload:** text (to extract from)
33
+
34
+ **Parameter:** A single integer for start, or a hyphenated range (``start-end``).
35
+
36
+ **Examples:**
37
+
38
+ .. code-block:: yaml
39
+
40
+ {substr(7):Hello, World!}
41
+ # World!
42
+ Explanation:
43
+ # - Skips up to index 7 ("Hello, ") and starts at "W" (index 7). As 7th index is inclusive.
44
+
45
+ {substr(1-4):Hello}
46
+ # ell
47
+ Explanation:
48
+ # - Skips the first character ("H" at index 0) and starts at "e" (index 1).
49
+ # - Stops at index 4 (exclusive), so it doesn't include "o" (index 4).
50
+
51
+ {substr(7-12):Hello, World!}
52
+ # World
53
+
54
+ {substr(7):TagScript is powerful}
55
+ # pt is powerful
56
+ Explanation:
57
+ # - Skips up to index 7 ("TagScri") and starts at "pt" (index 7).
58
+ # - Indexing: T(0)a(1)g(2)S(3)c(4)r(5)i(6)p(7). So at 7 it starts at pt.
59
+
60
+ .. note::
61
+
62
+ - For Single Index: Starting index is ``inclusive`` to the end of the string.
63
+ - For Range: Starting index is ``inclusive``, while the ending index is ``exclusive``.
64
+ - Negative indices are not supported.
65
+
66
+ """
67
+
13
68
  ACCEPTED_NAMES: Tuple[str, ...] = ("substr", "substring")
14
69
 
15
70
  def process(self, ctx: Context) -> Optional[str]:
@@ -416,11 +416,11 @@ class Interpreter(_Interpreter):
416
416
 
417
417
  class AsyncInterpreter(Interpreter):
418
418
  """
419
- An asynchronous subclass of `Interpreter` that allows blocks to implement asynchronous methods.
419
+ An asynchronous subclass of :class:`Interpreter` that allows blocks to implement asynchronous methods.
420
420
  Synchronous blocks are still supported.
421
421
 
422
- This subclass has no additional attributes from the `Interpreter` class.
423
- See `Interpreter` for full documentation.
422
+ This subclass has no additional attributes from the :class:`Interpreter` class.
423
+ See :class:`Interpreter` for full documentation.
424
424
  """
425
425
 
426
426
  async def _get_acceptors(self, ctx: Context) -> List[Block]: # type: ignore
@@ -482,8 +482,8 @@ class AsyncInterpreter(Interpreter):
482
482
  """
483
483
  Asynchronously process a given TagScript string.
484
484
 
485
- This method has no additional attributes from the `Interpreter` class.
486
- See `Interpreter.process` for full documentation.
485
+ This method has no additional attributes from the :class:`Interpreter` class.
486
+ See :meth:`Interpreter.process` for full documentation.
487
487
  """
488
488
  response = Response(variables=seed_variables, extra_kwargs=kwargs)
489
489
  node_ordered_list = build_node_tree(message)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AdvancedTagScript
3
- Version: 3.2.5
3
+ Version: 3.3.0
4
4
  Summary: An easy drop in user-provided Templating system.
5
5
  Home-page: https://github.com/cool-aid-man/TagScriptEngine
6
6
  Author: cool-aid-man, inthedark.org, PhenoM4n4n
@@ -1,7 +1,7 @@
1
- TagScriptEngine/__init__.py,sha256=X27OiTa2SvbM_SmT0rETVxgXJD_baJ6HLqE6X9AnKeE,6068
1
+ TagScriptEngine/__init__.py,sha256=v1jE2WhSkANRnPWLj4UieHunkkCnO6aNEaACln7u2dI,6143
2
2
  TagScriptEngine/_warnings.py,sha256=pfMXaEVGpIue0eqgzZ6HkLXEka1UGl61-yuH9uEnVe4,2662
3
3
  TagScriptEngine/exceptions.py,sha256=lhvskKi2AySE5pul_iAA251jdMv-92QkjodR4X-G27Y,2833
4
- TagScriptEngine/interpreter.py,sha256=oiyKNXgZCsMmcCTkRnszVIqG_to7PmQKomCn_pX5sb8,16604
4
+ TagScriptEngine/interpreter.py,sha256=5kF0kEqaZf5Hyu8tkmDDLEy7y-qvd6xiUJ3GwpDDh44,16638
5
5
  TagScriptEngine/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  TagScriptEngine/utils.py,sha256=Kq7_w6nuJizPYZDJIzXZ51b1ea92_7Vm_YDKbQci7F4,1697
7
7
  TagScriptEngine/verb.py,sha256=nk5IenRRGuwzIY7cMyGQn7aVYvmGcNTszztAQTSHP54,5047
@@ -10,23 +10,26 @@ TagScriptEngine/adapter/discordadapters.py,sha256=l_6HijmmGDRX7lhahskH6mTteJFpS7
10
10
  TagScriptEngine/adapter/functionadapter.py,sha256=lQlWm0dO-41bTQ2etFpWRaGnG22V1HMy9aiB8FnFZOU,591
11
11
  TagScriptEngine/adapter/intadapter.py,sha256=jENx5ah8k0U1PjJccV0NiYFsWFU9ZXTFgz3f-qx30SA,548
12
12
  TagScriptEngine/adapter/objectadapter.py,sha256=7bW-Qd6gujq2ALdK3p0ONoCSPWCTv6srkkDNjzkvDEU,1017
13
- TagScriptEngine/adapter/redbotadapters.py,sha256=AFCu4QZGSHAMwyDFHBPT9XS5aaWVzj9Hu-pwI87DXi8,5934
13
+ TagScriptEngine/adapter/redbotadapters.py,sha256=9a0EDSmelI-RJF6Mgpti1fogsNIAvNoC3F40aTKGKgk,5936
14
14
  TagScriptEngine/adapter/stringadapter.py,sha256=VaJzkm1b8b7w1T90DjR3gOH0DIsZlWtcFe1YG7DOqBQ,1710
15
- TagScriptEngine/block/__init__.py,sha256=JPyfutqST14cpSE8YgeY9PsM9cY2s5CkoNBMf3h5bmE,3070
15
+ TagScriptEngine/block/__init__.py,sha256=zfoPTCTBDIuOkcjKnNL8gYIRKOi4c4gkYNwNJPvYctQ,3223
16
16
  TagScriptEngine/block/allowedmentions.py,sha256=WMGWi1Fe0U_vUBsc4uyiX1XfQjGkhpdlyvq3HhCCLd4,2219
17
- TagScriptEngine/block/assign.py,sha256=l3DO41BVoXZbGSHsscSdNiSLddT7ggt1bPQoJdM1Iuo,1202
17
+ TagScriptEngine/block/assign.py,sha256=lgJY_hUIjrKPm4N9vWr_rxhrKejGrMRbBG3oasb0hAI,8657
18
18
  TagScriptEngine/block/breakblock.py,sha256=nt3T7fDhTxH0jpO8lu0_3oC4XwVuKKg9cmkdLsZwzL8,1288
19
- TagScriptEngine/block/case.py,sha256=KAayT5RGLOMV7g6glu9AV9DijErdT-2ph7yIleGLjZw,1441
20
- TagScriptEngine/block/command.py,sha256=1ljbZd_Mgg6Woqi6giz26coCRej-jyh4ENY_cdTwjK8,4307
19
+ TagScriptEngine/block/case.py,sha256=SF4uaAWyC9AiBzoI5SPDCaRx6wrY-_0IQDI1T_mZFvk,1503
20
+ TagScriptEngine/block/command.py,sha256=3xKo5H3K8F7BpxeHJp9I80n0QWvk_GHQCQpKnvQ7ZxQ,4309
21
21
  TagScriptEngine/block/control.py,sha256=tALrHN0vbEXheMhoQOI_C0X_WjL3c-Qqd2zEd0rAZrE,5699
22
- TagScriptEngine/block/cooldown.py,sha256=F4HmFoIlxIhDWw8Z8PFZqP1FoEEBmAW4aipByZKZocY,4022
22
+ TagScriptEngine/block/cooldown.py,sha256=7s7oXy2euN_w7TcfytHdNSt4duYowrqNcWuTJC3oxL4,4161
23
23
  TagScriptEngine/block/count.py,sha256=URa5xadQ56X7uaBkhMew6KyMdlZDwuj3xKjIH_PK37U,2135
24
- TagScriptEngine/block/embedblock.py,sha256=NbudZCln0lEKO8WD5Zmh3W56E5jzmwBMezEaIbGcyqU,11008
24
+ TagScriptEngine/block/cycleblock.py,sha256=61AhV3aNMVFGgymOuoWm79WzKjPvu_S8MU9f172lb9U,2308
25
+ TagScriptEngine/block/embedblock.py,sha256=57DUs_DXHygvWflh-ReqvyrujQGQdzv5rXtkCSn4KGk,12110
25
26
  TagScriptEngine/block/fiftyfifty.py,sha256=QjQP96vpQPRVpuVcF5iI1kTCkM54vUBpKgK2TEZU6vQ,827
26
- TagScriptEngine/block/helpers.py,sha256=Zevy0tWzDAUKSD9hc6-nW7WTreRhqENgalfySwqhF7U,4386
27
- TagScriptEngine/block/joinblock.py,sha256=-TlxO-q8clBZIu0TJg7qT2i2ti6nLAVsev_zz0lGrI4,1096
27
+ TagScriptEngine/block/helpers.py,sha256=DD71FBptjtSTlt-mHd4EqnLNn8x7gS3jCpqRudVIbdI,3596
28
+ TagScriptEngine/block/joinblock.py,sha256=gD4NdvJJ7YN8SIu3QrOw6hABAt-cHDQNNQkmiLMKqAg,1094
29
+ TagScriptEngine/block/listblock.py,sha256=r90w1oeJjH_t39cKBT96xW8mnRXLfvyLe8KOz2p6bHw,2045
28
30
  TagScriptEngine/block/loosevariablegetter.py,sha256=ym6u9HTWKN-Cgk6OQH5FVIiGnxsND2g5o4r3kRBGH9M,1231
29
- TagScriptEngine/block/mathblock.py,sha256=r69oCNUAjcGNO0QFBG7w9XCv7mvMrq__GoyhR7pW6fA,5802
31
+ TagScriptEngine/block/mathblock.py,sha256=g15LG_KEh9w8BiXNRkWcx-8K73Ic2pbiZEVZ7WrHyms,7616
32
+ TagScriptEngine/block/ordblock.py,sha256=_FOe4eI5gBm_8Ek1ZTW25lO-2lasOh8oT4Temp4WmiI,1361
30
33
  TagScriptEngine/block/randomblock.py,sha256=udC7mCGYrtN_Exg1XZZvqvvi8KkGNnRyTkuh-6rbeSo,1594
31
34
  TagScriptEngine/block/range.py,sha256=3dz6jgTIBloZyXgN7eXGsB7SBjK3Y-OJROmHOeOaJT4,1836
32
35
  TagScriptEngine/block/redirect.py,sha256=BobWl6xKqVV4FZlihmJoLPwI_i0P6Sn6G6fWFf9d8CE,1140
@@ -34,15 +37,15 @@ TagScriptEngine/block/replaceblock.py,sha256=NXx6wdgI4r3MOw-E5oIbtrMEc9BdxpM9Rip
34
37
  TagScriptEngine/block/require_blacklist.py,sha256=3jFK8Ap_ataHZrqF3MWrmXrBI5CkIXq8G6QMEq0QQXY,2810
35
38
  TagScriptEngine/block/shortcutredirect.py,sha256=2F20L_7hYMxJY9s02XwxJQIWXaBQM_dwBNZKsOJQsk8,683
36
39
  TagScriptEngine/block/stopblock.py,sha256=C10JCaajiVetEwnP_1T7SwhT8UmBQzWCPpdtDF16nZY,1091
37
- TagScriptEngine/block/strf.py,sha256=54oVyt8zVlnBWa9iSXtV7szc4eFcY-hUpjRSewzkm7M,2050
40
+ TagScriptEngine/block/strf.py,sha256=VfSqKYzXA6zdpX1hOncqpFOBaSi5Tq4emF_rsLj7AxQ,2053
38
41
  TagScriptEngine/block/strictvariablegetter.py,sha256=XMwqGad3iy5esB2aTiD5v1EaCLGDFMvB1RF11G0-l2M,1251
39
- TagScriptEngine/block/substr.py,sha256=QNtoOIBBC-tD2v4vT1NzmrSA5THJ_pOqQLezxWED8ek,835
42
+ TagScriptEngine/block/substr.py,sha256=50bRdll_7WTlCgmLlo9pr6nyvsqNq0xZyc3dhLCXKVg,2724
40
43
  TagScriptEngine/block/urlencodeblock.py,sha256=sWqdSBk-uTZZjebKhAFmlROvnQvLcjvyISPTiWb4nwM,1396
41
44
  TagScriptEngine/interface/__init__.py,sha256=37lAOMONKmWDLiC5Ox9Y3TNQUQbzW54oUugbQe0e9gc,341
42
45
  TagScriptEngine/interface/adapter.py,sha256=4jBBz7hc-8fsRSZ5DWOrUxrhXqS0SvJCdW57TZl8sQo,1967
43
46
  TagScriptEngine/interface/block.py,sha256=S0hWRH6uqBz_cYRIh3LGMyo63UK6gNidtf6EiLUKL0c,3653
44
- advancedtagscript-3.2.5.dist-info/licenses/LICENSE,sha256=NvdimESU3jrNgd_8E4i-eTecQ_jGegRrrWk1v2gOnHY,252
45
- advancedtagscript-3.2.5.dist-info/METADATA,sha256=oAjLywIMfGdHeRzKxJJkHQn-I11q8ya5haXYGylIe_Y,3565
46
- advancedtagscript-3.2.5.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
47
- advancedtagscript-3.2.5.dist-info/top_level.txt,sha256=6lY4lZDvWxraERrit1lvDsCCDdfo9e83kk_tBemAJCo,16
48
- advancedtagscript-3.2.5.dist-info/RECORD,,
47
+ advancedtagscript-3.3.0.dist-info/licenses/LICENSE,sha256=NvdimESU3jrNgd_8E4i-eTecQ_jGegRrrWk1v2gOnHY,252
48
+ advancedtagscript-3.3.0.dist-info/METADATA,sha256=Jk7KqEwJRIWH9XZrCbZCELDLF2vl121n3jMSaRc4-J0,3565
49
+ advancedtagscript-3.3.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
50
+ advancedtagscript-3.3.0.dist-info/top_level.txt,sha256=6lY4lZDvWxraERrit1lvDsCCDdfo9e83kk_tBemAJCo,16
51
+ advancedtagscript-3.3.0.dist-info/RECORD,,