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.
- bTagScript/__init__.py +66 -0
- bTagScript/adapter/__init__.py +16 -0
- bTagScript/adapter/discord_adapters.py +275 -0
- bTagScript/adapter/function_adapter.py +33 -0
- bTagScript/adapter/int_adapter.py +30 -0
- bTagScript/adapter/object_adapter.py +41 -0
- bTagScript/adapter/string_adapter.py +69 -0
- bTagScript/block/__init__.py +67 -0
- bTagScript/block/break_block.py +41 -0
- bTagScript/block/case_block.py +62 -0
- bTagScript/block/comment_block.py +33 -0
- bTagScript/block/control_block.py +161 -0
- bTagScript/block/counting_blocks.py +88 -0
- bTagScript/block/digitshorthand_block.py +38 -0
- bTagScript/block/discord_blocks/__init__.py +20 -0
- bTagScript/block/discord_blocks/command_block.py +52 -0
- bTagScript/block/discord_blocks/cooldown_block.py +101 -0
- bTagScript/block/discord_blocks/delete_block.py +47 -0
- bTagScript/block/discord_blocks/embed_block.py +248 -0
- bTagScript/block/discord_blocks/override_block.py +61 -0
- bTagScript/block/discord_blocks/react_block.py +49 -0
- bTagScript/block/discord_blocks/redirect_block.py +42 -0
- bTagScript/block/discord_blocks/requirement_blocks.py +83 -0
- bTagScript/block/helpers.py +121 -0
- bTagScript/block/math_blocks.py +257 -0
- bTagScript/block/random_block.py +65 -0
- bTagScript/block/range_block.py +54 -0
- bTagScript/block/replace_block.py +110 -0
- bTagScript/block/stop_block.py +38 -0
- bTagScript/block/strf_block.py +70 -0
- bTagScript/block/url_blocks.py +76 -0
- bTagScript/block/util_blocks/__init__.py +3 -0
- bTagScript/block/util_blocks/debug_block.py +107 -0
- bTagScript/block/var_block.py +49 -0
- bTagScript/block/vargetter_blocks.py +91 -0
- bTagScript/exceptions.py +139 -0
- bTagScript/interface/__init__.py +4 -0
- bTagScript/interface/adapter.py +43 -0
- bTagScript/interface/block.py +136 -0
- bTagScript/interpreter.py +614 -0
- bTagScript/utils.py +65 -0
- bTagScript/verb.py +129 -0
- btagscript-5.0.0.dist-info/METADATA +109 -0
- btagscript-5.0.0.dist-info/RECORD +47 -0
- btagscript-5.0.0.dist-info/WHEEL +5 -0
- btagscript-5.0.0.dist-info/licenses/LICENSE +1 -0
- 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)
|