bTagScript 5.0.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.
Files changed (47) hide show
  1. bTagScript/__init__.py +66 -0
  2. bTagScript/adapter/__init__.py +16 -0
  3. bTagScript/adapter/discord_adapters.py +275 -0
  4. bTagScript/adapter/function_adapter.py +33 -0
  5. bTagScript/adapter/int_adapter.py +30 -0
  6. bTagScript/adapter/object_adapter.py +41 -0
  7. bTagScript/adapter/string_adapter.py +69 -0
  8. bTagScript/block/__init__.py +67 -0
  9. bTagScript/block/break_block.py +41 -0
  10. bTagScript/block/case_block.py +62 -0
  11. bTagScript/block/comment_block.py +33 -0
  12. bTagScript/block/control_block.py +161 -0
  13. bTagScript/block/counting_blocks.py +88 -0
  14. bTagScript/block/digitshorthand_block.py +38 -0
  15. bTagScript/block/discord_blocks/__init__.py +20 -0
  16. bTagScript/block/discord_blocks/command_block.py +52 -0
  17. bTagScript/block/discord_blocks/cooldown_block.py +101 -0
  18. bTagScript/block/discord_blocks/delete_block.py +47 -0
  19. bTagScript/block/discord_blocks/embed_block.py +248 -0
  20. bTagScript/block/discord_blocks/override_block.py +61 -0
  21. bTagScript/block/discord_blocks/react_block.py +49 -0
  22. bTagScript/block/discord_blocks/redirect_block.py +42 -0
  23. bTagScript/block/discord_blocks/requirement_blocks.py +83 -0
  24. bTagScript/block/helpers.py +121 -0
  25. bTagScript/block/math_blocks.py +257 -0
  26. bTagScript/block/random_block.py +65 -0
  27. bTagScript/block/range_block.py +54 -0
  28. bTagScript/block/replace_block.py +110 -0
  29. bTagScript/block/stop_block.py +38 -0
  30. bTagScript/block/strf_block.py +70 -0
  31. bTagScript/block/url_blocks.py +76 -0
  32. bTagScript/block/util_blocks/__init__.py +3 -0
  33. bTagScript/block/util_blocks/debug_block.py +107 -0
  34. bTagScript/block/var_block.py +49 -0
  35. bTagScript/block/vargetter_blocks.py +91 -0
  36. bTagScript/exceptions.py +139 -0
  37. bTagScript/interface/__init__.py +4 -0
  38. bTagScript/interface/adapter.py +43 -0
  39. bTagScript/interface/block.py +136 -0
  40. bTagScript/interpreter.py +614 -0
  41. bTagScript/utils.py +65 -0
  42. bTagScript/verb.py +129 -0
  43. btagscript-5.0.0.dist-info/METADATA +109 -0
  44. btagscript-5.0.0.dist-info/RECORD +47 -0
  45. btagscript-5.0.0.dist-info/WHEEL +5 -0
  46. btagscript-5.0.0.dist-info/licenses/LICENSE +1 -0
  47. btagscript-5.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,257 @@
1
+ from __future__ import division
2
+
3
+ import math
4
+ import operator
5
+ from typing import Optional as Optional_
6
+
7
+ from pyparsing import (
8
+ CaselessLiteral,
9
+ Combine,
10
+ Forward,
11
+ Group,
12
+ Literal,
13
+ Optional,
14
+ Word,
15
+ ZeroOrMore,
16
+ alphas,
17
+ nums,
18
+ oneOf,
19
+ )
20
+
21
+ from ..interface import Block
22
+ from ..interpreter import Context
23
+
24
+
25
+ class NumericStringParser(object):
26
+ """
27
+ Most of this code comes from the fourFn.py pyparsing example
28
+
29
+ """
30
+
31
+ def pushFirst(self, strg: str, loc, toks) -> None: # pylint: disable=unused-argument
32
+ """
33
+ Parse actions that push the first element of the matched tokens
34
+ """
35
+ self.exprStack.append(toks[0])
36
+
37
+ def pushUMinus(self, strg: str, loc, toks) -> None: # pylint: disable=unused-argument
38
+ """
39
+ Parse actions that push the last element of the matched tokens??
40
+ """
41
+ if toks and toks[0] == "-":
42
+ self.exprStack.append("unary -")
43
+
44
+ def __init__(self) -> None:
45
+ """
46
+ expop :: '^'
47
+ multop :: '*' | '/'
48
+ addop :: '+' | '-'
49
+ integer :: ['+' | '-'] '0'..'9'+
50
+ atom :: PI | E | real | fn '(' expr ')' | '(' expr ')'
51
+ factor :: atom [ expop factor ]*
52
+ term :: factor [ multop factor ]*
53
+ expr :: term [ addop term ]*
54
+ """
55
+ self.exprStack = []
56
+ point = Literal(".")
57
+ e = CaselessLiteral("E")
58
+ fnumber = Combine(
59
+ Word("+-" + nums, nums)
60
+ + Optional(point + Optional(Word(nums)))
61
+ + Optional(e + Word("+-" + nums, nums))
62
+ )
63
+ ident = Word(alphas, alphas + nums + "_$")
64
+ mod = Literal("%")
65
+ plus = Literal("+")
66
+ minus = Literal("-")
67
+ mult = Literal("*")
68
+ iadd = Literal("+=")
69
+ imult = Literal("*=")
70
+ idiv = Literal("/=")
71
+ isub = Literal("-=")
72
+ div = Literal("/")
73
+ lpar = Literal("(").suppress()
74
+ rpar = Literal(")").suppress()
75
+ addop = plus | minus
76
+ multop = mult | div | mod
77
+ iop = iadd | isub | imult | idiv
78
+ expop = Literal("^")
79
+ pi = CaselessLiteral("PI")
80
+ expr = Forward()
81
+ atom = (
82
+ (
83
+ Optional(oneOf("- +"))
84
+ + (ident + lpar + expr + rpar | pi | e | fnumber).setParseAction(self.pushFirst)
85
+ )
86
+ | Optional(oneOf("- +")) + Group(lpar + expr + rpar)
87
+ ).setParseAction(self.pushUMinus)
88
+ # by defining exponentiation as "atom [ ^ factor ]..." instead of
89
+ # "atom [ ^ atom ]...", we get right-to-left exponents, instead of left-to-right
90
+ # that is, 2^3^2 = 2^(3^2), not (2^3)^2.
91
+ factor = Forward()
92
+ factor << atom + ZeroOrMore( # pylint: disable=expression-not-assigned
93
+ (expop + factor).setParseAction(self.pushFirst)
94
+ )
95
+ term = factor + ZeroOrMore((multop + factor).setParseAction(self.pushFirst))
96
+ expr << term + ZeroOrMore( # pylint: disable=expression-not-assigned
97
+ (addop + term).setParseAction(self.pushFirst)
98
+ )
99
+ final = expr + ZeroOrMore((iop + expr).setParseAction(self.pushFirst))
100
+ # addop_term = ( addop + term ).setParseAction( self.pushFirst )
101
+ # general_term = term + ZeroOrMore( addop_term ) | OneOrMore( addop_term)
102
+ # expr << general_term
103
+ self.bnf = final
104
+ # map operator symbols to corresponding arithmetic operations
105
+ epsilon = 1e-12
106
+ self.opn = {
107
+ "+": operator.add,
108
+ "-": operator.sub,
109
+ "+=": operator.iadd,
110
+ "-=": operator.isub,
111
+ "*": operator.mul,
112
+ "*=": operator.imul,
113
+ "/": operator.truediv,
114
+ "/=": operator.itruediv,
115
+ "^": operator.pow,
116
+ "%": operator.mod,
117
+ }
118
+ self.fn = {
119
+ "sin": math.sin,
120
+ "cos": math.cos,
121
+ "tan": math.tan,
122
+ "exp": math.exp,
123
+ "abs": abs,
124
+ "trunc": lambda a: int(a), # pylint: disable=unnecessary-lambda
125
+ "round": round,
126
+ "sgn": lambda a: abs(a) > epsilon and ((a > 0) - (a < 0)) or 0,
127
+ "log": lambda a: math.log(a, 10),
128
+ "ln": math.log,
129
+ "log2": math.log2,
130
+ "sqrt": math.sqrt,
131
+ }
132
+
133
+ def evaluateStack(self, s):
134
+ """
135
+ Evaluate the expression on the input data in the stack.
136
+ """
137
+ op = s.pop()
138
+ final = None
139
+ if op == "unary -":
140
+ final = -self.evaluateStack(s) # pylint: disable=invalid-unary-operand-type
141
+ elif op in self.opn:
142
+ op2 = self.evaluateStack(s)
143
+ op1 = self.evaluateStack(s)
144
+ final = self.opn[op](op1, op2)
145
+ elif op == "PI":
146
+ final = math.pi # 3.1415926535
147
+ elif op == "E":
148
+ final = math.e # 2.718281828
149
+ elif op in self.fn:
150
+ final = self.fn[op](self.evaluateStack(s))
151
+ elif op[0].isalpha():
152
+ final = 0
153
+ if final is not None:
154
+ return final
155
+ return float(op)
156
+
157
+ def eval(self, num_string: str, parseAll: bool = True) -> float:
158
+ """
159
+ Evaluate the expression on the input data.
160
+ """
161
+ self.exprStack = []
162
+ self.bnf.parseString(num_string, parseAll)
163
+ return self.evaluateStack(self.exprStack[:])
164
+
165
+
166
+ NSP = NumericStringParser()
167
+
168
+
169
+ class MathBlock(Block):
170
+ """
171
+ A math block is a block that contains a math expression.
172
+ If the block fails to parse if will return the declaration
173
+ plus error like so: `<math error>`, <+ error> etc.
174
+
175
+ **Usage:** ``{math:<expression>}``
176
+
177
+ **Aliases:** ``math, m, +, calc``
178
+
179
+ **Payload:** ``expression``
180
+
181
+ **Parameter:** None
182
+
183
+ **Examples:**
184
+
185
+ .. tagscript::
186
+
187
+ {m:2+3}
188
+ 5.0
189
+
190
+ {math:7(2+3)}
191
+ 42.0
192
+
193
+ {math:trunc(7(2+3))}
194
+ 42
195
+ """
196
+
197
+ ACCEPTED_NAMES = ("math", "m", "+", "calc")
198
+
199
+ def process(self, ctx: Context) -> Optional_[str]:
200
+ """
201
+ Try and process the block into a float
202
+ """
203
+ try:
204
+ return str(NSP.eval(ctx.verb.payload.strip(" ")))
205
+ except: # pylint: disable=bare-except
206
+ return f"<{ctx.verb.declaration} error>"
207
+
208
+
209
+ class OrdinalAbbreviationBlock(Block):
210
+ """
211
+ The ordinalabbreviation block returns the ordinal abbreviation of a number.
212
+ If a parameter is provided, it must be, one of, c, comma, indicator, i
213
+ Comma being adding commas every 3 digits, indicator, meaning the ordinal indicator.
214
+ (The st of 1st, nd of 2nd, etc.)
215
+
216
+ The number may be positive or negative, if the payload is invalid, the
217
+ declaration plus error is returned.
218
+
219
+ **Usage:** ``{ord(["c", "comma", "i", "indicator"]):<number>}``
220
+
221
+ **Aliases:** ``None``
222
+
223
+ **Payload:** ``number``
224
+
225
+ **Parameter:** ``"c", "comma", "i", "indicator"``
226
+
227
+ .. tagscript::
228
+
229
+ {ord:1000}
230
+ 1,000th
231
+
232
+ {ord(c):1213123}
233
+ 1,213,123
234
+
235
+ {ord(i):2022}
236
+ 2022nd
237
+ """
238
+
239
+ ACCEPTED_NAMES = ("ord",)
240
+
241
+ def process(self, ctx: Context) -> str:
242
+ """
243
+ Process the ordinal abbreviation block
244
+ """
245
+ num = ctx.verb.payload.split("-", 1)[-1]
246
+ if num.isdigit():
247
+ comma = f"{int(num):,}"
248
+ if ctx.verb.parameter in ["c", "comma"]:
249
+ return comma
250
+ i = int(ctx.verb.payload.split("-", 1)[-1])
251
+ indicator = "tsnrhtdd"[
252
+ (i // 10 % 10 != 1) * (i % 10 < 4) * i % 10 :: 4
253
+ ] # I stole this from stack overflow
254
+ if ctx.verb.parameter in ["i", "indicator"]:
255
+ return f"{ctx.verb.payload}{indicator}" # concatenation is slower?
256
+ return f"{comma}{indicator}"
257
+ return f"<{ctx.verb.declaration} error>"
@@ -0,0 +1,65 @@
1
+ import random
2
+ from typing import Optional
3
+
4
+ from ..interface import verb_required_block
5
+ from ..interpreter import Context
6
+
7
+
8
+ class RandomBlock(verb_required_block(True, payload=True)):
9
+ """
10
+ Pick a random item from a list of strings, split by either ``~``
11
+ or ``,``. An optional seed can be provided to the parameter to
12
+ always choose the same item when using that seed.
13
+ You can weight options differently by adding a weight and | before
14
+ the item.
15
+
16
+ **Usage:** ``{random([seed]):<list>}``
17
+
18
+ **Aliases:** ``#, rand``
19
+
20
+ **Payload:** ``list``
21
+
22
+ **Parameter:** ``seed``
23
+
24
+ **Examples:**
25
+
26
+ .. tagscript::
27
+
28
+ {random:Carl,Harold,Josh} attempts to pick the lock!
29
+ Possible Outputs:
30
+ Josh attempts to pick the lock!
31
+ Carl attempts to pick the lock!
32
+ Harold attempts to pick the lock!
33
+
34
+ {=(insults):You're so ugly that you went to the salon and it took 3 hours just to get an estimate.~I'll never forget the first time we met, although I'll keep trying.~You look like a before picture.}
35
+ {=(insult):{#:{insults}}}
36
+ {insult}
37
+ Assigns a random insult to the insult variable
38
+
39
+ {#:5|Cool,3|Lame}
40
+ 5 to 3 chances of being cool vs lame
41
+ """
42
+
43
+ ACCEPTED_NAMES = ("random", "#", "rand")
44
+
45
+ def process(self, ctx: Context) -> Optional[str]:
46
+ """
47
+ Process the randomness woo
48
+ """
49
+ if "~" in ctx.verb.payload:
50
+ spl = ctx.verb.payload.split("~")
51
+ else:
52
+ spl = ctx.verb.payload.split(",")
53
+
54
+ choices = []
55
+ weights = []
56
+ for choice in spl:
57
+ weight, sep, remainder = choice.partition("|")
58
+ if sep and weight.isdigit():
59
+ choices.append(remainder)
60
+ weights.append(int(weight))
61
+ else:
62
+ choices.append(choice)
63
+ weights.append(1)
64
+ rng = random.Random(ctx.verb.parameter) if ctx.verb.parameter is not None else random
65
+ return rng.choices(choices, weights, k=1)[0]
@@ -0,0 +1,54 @@
1
+ import random
2
+ from typing import Optional
3
+
4
+ from ..interface import verb_required_block
5
+ from ..interpreter import Context
6
+
7
+
8
+ class RangeBlock(verb_required_block(True, payload=True)):
9
+ """
10
+ The range block picks a random number from a range of numbers seperated by ``-``.
11
+ The number range is inclusive, so it can pick the starting/ending number as well.
12
+ Using the rangef block will pick a number to the tenth decimal place.
13
+
14
+ An optional seed can be provided to the parameter to always choose the same item when using that seed.
15
+
16
+ **Usage:** ``{range([seed]):<lowest-highest>}``
17
+
18
+ **Aliases:** ``rangef``
19
+
20
+ **Payload:** ``number``
21
+
22
+ **Parameter:** ``seed``
23
+
24
+ **Examples:**
25
+
26
+ .. tagscript::
27
+
28
+ Your lucky number is {range:10-30}!
29
+ Your lucky number is 14!
30
+ Your lucky number is 25!
31
+
32
+ {=(height):{rangef:5-7}}
33
+ I am guessing your height is {height}ft.
34
+ I am guessing your height is 5.3ft.
35
+ """
36
+
37
+ ACCEPTED_NAMES = ("rangef", "range")
38
+
39
+ def process(self, ctx: Context) -> Optional[str]:
40
+ """
41
+ Process the range block
42
+ """
43
+ try:
44
+ spl = ctx.verb.payload.split("-")
45
+ rng = random.Random(ctx.verb.parameter) if ctx.verb.parameter is not None else random
46
+ if ctx.verb.declaration.lower() == "rangef":
47
+ lower = int(float(spl[0]) * 10)
48
+ upper = int(float(spl[1]) * 10)
49
+ return str(rng.randint(lower, upper) / 10)
50
+ lower = int(float(spl[0]))
51
+ upper = int(float(spl[1]))
52
+ return str(rng.randint(lower, upper))
53
+ except: # pylint: disable=bare-except
54
+ return None
@@ -0,0 +1,110 @@
1
+ from typing import Optional
2
+
3
+ from ..interface import verb_required_block
4
+ from ..interpreter import Context
5
+
6
+
7
+ class ReplaceBlock(verb_required_block(True, payload=True, parameter=True)):
8
+ """
9
+ The replace block will replace specific characters in a string.
10
+ The parameter should split by a ``,``, containing the characters to find
11
+ before the command and the replacements after.
12
+
13
+ **Usage:** ``{replace(<original,new>):<message>}``
14
+
15
+ **Aliases:** ``sub``
16
+
17
+ **Payload:** message
18
+
19
+ **Parameter:** original, new
20
+
21
+ .. tagscript::
22
+
23
+ {replace(o,i):welcome to the server}
24
+ welcime ti the server
25
+
26
+ {replace(1,6):{args}}
27
+ if {args} is 1637812
28
+ 6637862
29
+
30
+ {replace(, ):Test}
31
+ T e s t
32
+ """
33
+
34
+ ACCEPTED_NAMES = ("replace", "sub")
35
+
36
+ def process(self, ctx: Context) -> Optional[str]:
37
+ """
38
+ Replace the characters in the payload
39
+ """
40
+ try:
41
+ before, after = ctx.verb.parameter.split(",", 1)
42
+ except ValueError:
43
+ return None
44
+
45
+ return ctx.verb.payload.replace(before, after)
46
+
47
+
48
+ class PythonBlock(verb_required_block(True, payload=True, parameter=True)):
49
+ """
50
+ The in block serves three different purposes depending on the alias that is used.
51
+
52
+ The ``in`` alias checks if the parameter is anywhere in the payload.
53
+
54
+ ``contain`` strictly checks if the parameter is the payload, split by whitespace.
55
+
56
+ ``index`` finds the location of the parameter in the payload, split by whitespace.
57
+ If the parameter string is not found in the payload, it returns 1.
58
+
59
+ index is used to return the value of the string form the given list of
60
+
61
+ **Usage:** ``{in(<string>):<payload>}``
62
+
63
+ **Aliases:** ``index, contains``
64
+
65
+ **Payload:** ``payload``
66
+
67
+ **Parameter:** ``string``
68
+
69
+ **Examples:**
70
+
71
+ .. tagscript::
72
+
73
+ {in(apple pie):banana pie apple pie and other pie}
74
+ true
75
+ {in(mute):How does it feel to be muted?}
76
+ true
77
+ {in(a):How does it feel to be muted?}
78
+ false
79
+
80
+ {contains(mute):How does it feel to be muted?}
81
+ false
82
+ {contains(muted?):How does it feel to be muted?}
83
+ false
84
+
85
+ {index(food):I love to eat food. everyone does.}
86
+ 4
87
+ {index(pie):I love to eat food. everyone does.}
88
+ -1
89
+ """
90
+
91
+ def will_accept(self, ctx: Context) -> bool:
92
+ """
93
+ Check if we can accept
94
+ """
95
+ dec = ctx.verb.declaration.lower()
96
+ return dec in ("contains", "in", "index")
97
+
98
+ def process(self, ctx: Context) -> Optional[str]:
99
+ """
100
+ Process the block
101
+ """
102
+ dec = ctx.verb.declaration.lower()
103
+ if dec == "contains":
104
+ return str(bool(ctx.verb.parameter in ctx.verb.payload.split())).lower()
105
+ if dec == "in":
106
+ return str(bool(ctx.verb.parameter in ctx.verb.payload)).lower()
107
+ try:
108
+ return str(ctx.verb.payload.strip().split().index(ctx.verb.parameter))
109
+ except ValueError:
110
+ return "-1"
@@ -0,0 +1,38 @@
1
+ from typing import Optional
2
+
3
+ from ..exceptions import StopError
4
+ from ..interface import verb_required_block
5
+ from ..interpreter import Context
6
+ from . import helper_parse_if
7
+
8
+
9
+ class StopBlock(verb_required_block(True, parameter=True)):
10
+ """
11
+ The stop block stops tag processing if the given parameter is true.
12
+ If a message is passed to the payload it will return that message.
13
+
14
+ **Usage:** ``{stop(<bool>):[string]}``
15
+
16
+ **Aliases:** ``halt``
17
+
18
+ **Payload:** ``string``
19
+
20
+ **Parameter:** ``bool``
21
+
22
+ **Example:**
23
+
24
+ .. tagscript::
25
+
26
+ {stop(=={args}):You must provide arguments for this tag.}
27
+ enforces providing arguments for a tag
28
+ """
29
+
30
+ ACCEPTED_NAMES = ("stop", "halt")
31
+
32
+ def process(self, ctx: Context) -> Optional[str]:
33
+ """
34
+ Process the stop block
35
+ """
36
+ if helper_parse_if(ctx.verb.parameter):
37
+ raise StopError("" if ctx.verb.payload is None else ctx.verb.payload)
38
+ return ""
@@ -0,0 +1,70 @@
1
+ from datetime import datetime, timezone
2
+ from typing import Optional
3
+
4
+ from ..interface import Block
5
+ from ..interpreter import Context
6
+
7
+
8
+ class StrfBlock(Block):
9
+ """
10
+ The strf block converts and formats timestamps based on `strftime formatting spec <https://strftime.org/>`_.
11
+ Two types of timestamps are supported: ISO and epoch.
12
+ If a timestamp isn't passed, the current UTC time is used.
13
+
14
+ Invoking this block with `unix` will return the current Unix timestamp.
15
+
16
+ **Usage:** ``{strf([timestamp]):<format>}``
17
+
18
+ **Aliases:** ``unix``
19
+
20
+ **Payload:** ``format``
21
+
22
+ **Parameter:** ``timestamp``
23
+
24
+ **Example:**
25
+
26
+ .. tagscript::
27
+
28
+ {strf:%Y-%m-%d}
29
+ 2021-07-11
30
+
31
+ {strf({user(timestamp)}):%c}
32
+ Fri Jun 29 21:10:28 2018
33
+
34
+ {strf(1420070400):%A %d, %B %Y}
35
+ Thursday 01, January 2015
36
+
37
+ {strf(2019-10-09T01:45:00.805000):%H:%M %d-%B-%Y}
38
+ 01:45 09-October-2019
39
+
40
+ {unix}
41
+ 1629182008
42
+ """
43
+
44
+ ACCEPTED_NAMES = ("strf", "unix")
45
+
46
+ def process(self, ctx: Context) -> Optional[str]:
47
+ """
48
+ Process the strf block
49
+ """
50
+ if ctx.verb.declaration.lower() == "unix":
51
+ return str(int(datetime.now(timezone.utc).timestamp()))
52
+ if not ctx.verb.payload:
53
+ return None
54
+ if ctx.verb.parameter:
55
+ if ctx.verb.parameter.isdigit():
56
+ try:
57
+ t = datetime.fromtimestamp(int(ctx.verb.parameter))
58
+ except: # pylint: disable=bare-except
59
+ return None
60
+ else:
61
+ try:
62
+ t = datetime.fromisoformat(ctx.verb.parameter)
63
+ # converts datetime.__str__ to datetime
64
+ except ValueError:
65
+ return None
66
+ else:
67
+ t = datetime.now()
68
+ if not t.tzinfo:
69
+ t = t.replace(tzinfo=timezone.utc)
70
+ return t.strftime(ctx.verb.payload)
@@ -0,0 +1,76 @@
1
+ from typing import Optional
2
+ from urllib.parse import quote, quote_plus, unquote, unquote_plus
3
+
4
+ from ..interface import verb_required_block
5
+ from ..interpreter import Context
6
+
7
+
8
+ class URLDecodeBlock(verb_required_block(True, payload=True)):
9
+ """
10
+ This block will decode a given url into a string
11
+ with non-url compliant characters replaced. Using ``+`` as the parameter
12
+ will replace spaces with ``+`` rather than ``%20``.
13
+
14
+ **Usage:** ``{urldecode(["+"]):<string>}``
15
+
16
+ **Payload:** string
17
+
18
+ **Parameter:** "+", None
19
+
20
+ **Examples:**
21
+
22
+ .. tagscript::
23
+
24
+ {urldecode:covid-19%20sucks}
25
+ covid-19 sucks
26
+
27
+ {urldecode(+):im+stuck+at+home+writing+docs}
28
+ im stuck at home writing docs
29
+
30
+ This block is just the reverse of the urlencode block
31
+ """
32
+
33
+ ACCEPTED_NAMES = ("urldecode",)
34
+
35
+ def process(self, ctx: Context) -> Optional[str]:
36
+ """
37
+ Process the block
38
+ """
39
+ method = unquote_plus if ctx.verb.parameter == "+" else unquote
40
+ return method(ctx.verb.payload)
41
+
42
+
43
+ class URLEncodeBlock(verb_required_block(True, payload=True)):
44
+ """
45
+ This block will encode a given string into a properly formatted url
46
+ with non-url compliant characters replaced. Using ``+`` as the parameter
47
+ will replace spaces with ``+`` rather than ``%20``.
48
+
49
+ **Usage:** ``{urlencode(["+"]):<string>}``
50
+
51
+ **Payload:** string
52
+
53
+ **Parameter:** "+", None
54
+
55
+ **Example:**
56
+
57
+ .. tagscript::
58
+
59
+ {urlencode:covid-19 sucks}
60
+ covid-19%20sucks
61
+
62
+ {urlencode(+):im stuck at home writing docs}
63
+ im+stuck+at+home+writing+docs
64
+
65
+ You can use this to search up blocks
66
+ Eg if {args} is command block
67
+
68
+ <https://btagscript.readthedocs.io/en/latest/search.html?q={urlencode(+):{args}}&check_keywords=yes&area=default>
69
+ <https://btagscript.readthedocs.io/en/latest/search.html?q=command+block&check_keywords=yes&area=default>
70
+ """
71
+
72
+ ACCEPTED_NAMES = ("urlencode",)
73
+
74
+ def process(self, ctx: Context) -> str:
75
+ method = quote_plus if ctx.verb.parameter == "+" else quote
76
+ return method(ctx.verb.payload)
@@ -0,0 +1,3 @@
1
+ from .debug_block import DebugBlock
2
+
3
+ __all__ = ("DebugBlock",)