AdvancedTagScript 3.2.4__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.
- TagScriptEngine/__init__.py +227 -0
- TagScriptEngine/_warnings.py +88 -0
- TagScriptEngine/adapter/__init__.py +50 -0
- TagScriptEngine/adapter/discordadapters.py +596 -0
- TagScriptEngine/adapter/functionadapter.py +23 -0
- TagScriptEngine/adapter/intadapter.py +22 -0
- TagScriptEngine/adapter/objectadapter.py +35 -0
- TagScriptEngine/adapter/redbotadapters.py +161 -0
- TagScriptEngine/adapter/stringadapter.py +47 -0
- TagScriptEngine/block/__init__.py +130 -0
- TagScriptEngine/block/allowedmentions.py +60 -0
- TagScriptEngine/block/assign.py +43 -0
- TagScriptEngine/block/breakblock.py +41 -0
- TagScriptEngine/block/case.py +63 -0
- TagScriptEngine/block/command.py +141 -0
- TagScriptEngine/block/comment.py +29 -0
- TagScriptEngine/block/control.py +149 -0
- TagScriptEngine/block/cooldown.py +95 -0
- TagScriptEngine/block/count.py +68 -0
- TagScriptEngine/block/embedblock.py +306 -0
- TagScriptEngine/block/fiftyfifty.py +34 -0
- TagScriptEngine/block/helpers.py +164 -0
- TagScriptEngine/block/loosevariablegetter.py +40 -0
- TagScriptEngine/block/mathblock.py +164 -0
- TagScriptEngine/block/randomblock.py +51 -0
- TagScriptEngine/block/range.py +56 -0
- TagScriptEngine/block/redirect.py +42 -0
- TagScriptEngine/block/replaceblock.py +110 -0
- TagScriptEngine/block/require_blacklist.py +79 -0
- TagScriptEngine/block/shortcutredirect.py +23 -0
- TagScriptEngine/block/stopblock.py +38 -0
- TagScriptEngine/block/strf.py +70 -0
- TagScriptEngine/block/strictvariablegetter.py +38 -0
- TagScriptEngine/block/substr.py +25 -0
- TagScriptEngine/block/urlencodeblock.py +41 -0
- TagScriptEngine/exceptions.py +105 -0
- TagScriptEngine/interface/__init__.py +14 -0
- TagScriptEngine/interface/adapter.py +75 -0
- TagScriptEngine/interface/block.py +124 -0
- TagScriptEngine/interpreter.py +502 -0
- TagScriptEngine/py.typed +0 -0
- TagScriptEngine/utils.py +71 -0
- TagScriptEngine/verb.py +160 -0
- advancedtagscript-3.2.4.dist-info/METADATA +99 -0
- advancedtagscript-3.2.4.dist-info/RECORD +48 -0
- advancedtagscript-3.2.4.dist-info/WHEEL +5 -0
- advancedtagscript-3.2.4.dist-info/licenses/LICENSE +1 -0
- advancedtagscript-3.2.4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import Dict, List, Optional, Tuple
|
|
5
|
+
|
|
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] = {
|
|
18
|
+
"true": True,
|
|
19
|
+
"false": False,
|
|
20
|
+
"enable": True,
|
|
21
|
+
"disable": False,
|
|
22
|
+
"yes": True,
|
|
23
|
+
"no": False,
|
|
24
|
+
"on": True,
|
|
25
|
+
"off": False,
|
|
26
|
+
"y": True,
|
|
27
|
+
"n": False,
|
|
28
|
+
"t": True,
|
|
29
|
+
"f": False,
|
|
30
|
+
"1": True,
|
|
31
|
+
"0": False,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def implicit_bool(string: str) -> Optional[bool]:
|
|
36
|
+
"""
|
|
37
|
+
Parse a string to a boolean.
|
|
38
|
+
|
|
39
|
+
>>> implicit_bool("true")
|
|
40
|
+
True
|
|
41
|
+
>>> implicit_bool("FALSE")
|
|
42
|
+
False
|
|
43
|
+
>>> implicit_bool("abc")
|
|
44
|
+
None
|
|
45
|
+
|
|
46
|
+
Parameters
|
|
47
|
+
----------
|
|
48
|
+
string: str
|
|
49
|
+
The string to convert.
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
bool
|
|
54
|
+
The boolean value of the string.
|
|
55
|
+
None
|
|
56
|
+
The string failed to parse.
|
|
57
|
+
"""
|
|
58
|
+
return BOOL_LOOKUP.get(string.lower())
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def helper_parse_if(string: str) -> Optional[bool]:
|
|
62
|
+
"""
|
|
63
|
+
Parse an expression string to a boolean.
|
|
64
|
+
|
|
65
|
+
>>> helper_parse_if("this == this")
|
|
66
|
+
True
|
|
67
|
+
>>> helper_parse_if("2>3")
|
|
68
|
+
False
|
|
69
|
+
>>> helper_parse_if("40 >= 40")
|
|
70
|
+
True
|
|
71
|
+
>>> helper_parse_if("False")
|
|
72
|
+
False
|
|
73
|
+
>>> helper_parse_if("1")
|
|
74
|
+
None
|
|
75
|
+
|
|
76
|
+
Parameters
|
|
77
|
+
----------
|
|
78
|
+
string: str
|
|
79
|
+
The string to convert.
|
|
80
|
+
|
|
81
|
+
Returns
|
|
82
|
+
-------
|
|
83
|
+
bool
|
|
84
|
+
The boolean value of the expression.
|
|
85
|
+
None
|
|
86
|
+
The expression failed to parse.
|
|
87
|
+
"""
|
|
88
|
+
value = implicit_bool(string)
|
|
89
|
+
if value is not None:
|
|
90
|
+
return value
|
|
91
|
+
try:
|
|
92
|
+
if "!=" in string:
|
|
93
|
+
spl = string.split("!=")
|
|
94
|
+
return spl[0].strip() != spl[1].strip()
|
|
95
|
+
if "==" in string:
|
|
96
|
+
spl = string.split("==")
|
|
97
|
+
return spl[0].strip() == spl[1].strip()
|
|
98
|
+
if ">=" in string:
|
|
99
|
+
spl = string.split(">=")
|
|
100
|
+
return float(spl[0].strip()) >= float(spl[1].strip())
|
|
101
|
+
if "<=" in string:
|
|
102
|
+
spl = string.split("<=")
|
|
103
|
+
return float(spl[0].strip()) <= float(spl[1].strip())
|
|
104
|
+
if ">" in string:
|
|
105
|
+
spl = string.split(">")
|
|
106
|
+
return float(spl[0].strip()) > float(spl[1].strip())
|
|
107
|
+
if "<" in string:
|
|
108
|
+
spl = string.split("<")
|
|
109
|
+
return float(spl[0].strip()) < float(spl[1].strip())
|
|
110
|
+
except Exception:
|
|
111
|
+
pass
|
|
112
|
+
|
|
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
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def helper_parse_list_if(if_string):
|
|
161
|
+
split = helper_split(if_string, False)
|
|
162
|
+
if split is None:
|
|
163
|
+
return [helper_parse_if(if_string)]
|
|
164
|
+
return [helper_parse_if(item) for item in split]
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Optional, Tuple
|
|
4
|
+
|
|
5
|
+
from ..interface import Block
|
|
6
|
+
from ..interpreter import Context
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
__all__: Tuple[str, ...] = ("LooseVariableGetterBlock",)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class LooseVariableGetterBlock(Block):
|
|
13
|
+
"""
|
|
14
|
+
The loose variable block represents the adapters for any seeded or defined variables.
|
|
15
|
+
This variable implementation is considered "loose" since it checks whether the variable is
|
|
16
|
+
valid during :meth:`process`, rather than :meth:`will_accept`.
|
|
17
|
+
|
|
18
|
+
**Usage:** ``{<variable_name>([parameter]):[payload]}``
|
|
19
|
+
|
|
20
|
+
**Aliases:** This block is valid for any inputted declaration.
|
|
21
|
+
|
|
22
|
+
**Payload:** Depends on the variable's underlying adapter.
|
|
23
|
+
|
|
24
|
+
**Parameter:** Depends on the variable's underlying adapter.
|
|
25
|
+
|
|
26
|
+
**Examples:** ::
|
|
27
|
+
|
|
28
|
+
{=(var):This is my variable.}
|
|
29
|
+
{var}
|
|
30
|
+
# This is my variable.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def will_accept(self, ctx: Context) -> bool: # type: ignore
|
|
34
|
+
return True
|
|
35
|
+
|
|
36
|
+
def process(self, ctx: Context) -> Optional[str]:
|
|
37
|
+
if ctx.verb.declaration in ctx.response.variables:
|
|
38
|
+
return ctx.response.variables[ctx.verb.declaration].get_value(ctx.verb)
|
|
39
|
+
else:
|
|
40
|
+
return None
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
from __future__ import division, annotations
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
import operator
|
|
5
|
+
from typing import Any, Callable, Dict, List, Tuple, cast, Optional as TypingOptional
|
|
6
|
+
|
|
7
|
+
from pyparsing import (
|
|
8
|
+
CaselessLiteral,
|
|
9
|
+
Combine,
|
|
10
|
+
Forward,
|
|
11
|
+
Group,
|
|
12
|
+
Literal,
|
|
13
|
+
Optional,
|
|
14
|
+
ParserElement,
|
|
15
|
+
Word,
|
|
16
|
+
ZeroOrMore,
|
|
17
|
+
alphas,
|
|
18
|
+
nums,
|
|
19
|
+
oneOf,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
from ..interface import Block
|
|
23
|
+
from ..interpreter import Context
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
__all__: Tuple[str, ...] = ("MathBlock",)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class NumericStringParser(object):
|
|
30
|
+
"""
|
|
31
|
+
Most of this code comes from the fourFn.py pyparsing example
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def pushFirst(self, strg: Any, loc: Any, toks: Any) -> Any:
|
|
36
|
+
self.exprStack.append(toks[0])
|
|
37
|
+
|
|
38
|
+
def pushUMinus(self, strg: Any, loc: Any, toks: Any) -> Any:
|
|
39
|
+
if toks and toks[0] == "-":
|
|
40
|
+
self.exprStack.append("unary -")
|
|
41
|
+
|
|
42
|
+
def __init__(self) -> None:
|
|
43
|
+
"""
|
|
44
|
+
expop :: '^'
|
|
45
|
+
multop :: '*' | '/'
|
|
46
|
+
addop :: '+' | '-'
|
|
47
|
+
integer :: ['+' | '-'] '0'..'9'+
|
|
48
|
+
atom :: PI | E | real | fn '(' expr ')' | '(' expr ')'
|
|
49
|
+
factor :: atom [ expop factor ]*
|
|
50
|
+
term :: factor [ multop factor ]*
|
|
51
|
+
expr :: term [ addop term ]*
|
|
52
|
+
"""
|
|
53
|
+
point: Literal = Literal(".")
|
|
54
|
+
e: CaselessLiteral = CaselessLiteral("E")
|
|
55
|
+
fnumber: Combine = Combine(
|
|
56
|
+
Word("+-" + nums, nums)
|
|
57
|
+
+ Optional(point + Optional(Word(nums)))
|
|
58
|
+
+ Optional(e + Word("+-" + nums, nums))
|
|
59
|
+
)
|
|
60
|
+
ident: Word = Word(alphas, alphas + nums + "_$")
|
|
61
|
+
mod: Literal = Literal("%")
|
|
62
|
+
plus: Literal = Literal("+")
|
|
63
|
+
minus: Literal = Literal("-")
|
|
64
|
+
mult: Literal = Literal("*")
|
|
65
|
+
iadd: Literal = Literal("+=")
|
|
66
|
+
imult: Literal = Literal("*=")
|
|
67
|
+
idiv: Literal = Literal("/=")
|
|
68
|
+
isub: Literal = Literal("-=")
|
|
69
|
+
div: Literal = Literal("/")
|
|
70
|
+
lpar: ParserElement = Literal("(").suppress()
|
|
71
|
+
rpar: ParserElement = Literal(")").suppress()
|
|
72
|
+
addop: ParserElement = plus | minus
|
|
73
|
+
multop: ParserElement = mult | div | mod
|
|
74
|
+
iop: ParserElement = iadd | isub | imult | idiv
|
|
75
|
+
expop: Literal = Literal("^")
|
|
76
|
+
pi: CaselessLiteral = CaselessLiteral("PI")
|
|
77
|
+
expr: Forward = Forward()
|
|
78
|
+
atom: ParserElement = (
|
|
79
|
+
(
|
|
80
|
+
Optional(oneOf("- +"))
|
|
81
|
+
+ (ident + lpar + expr + rpar | pi | e | fnumber).setParseAction(self.pushFirst)
|
|
82
|
+
)
|
|
83
|
+
| Optional(oneOf("- +")) + Group(lpar + expr + rpar)
|
|
84
|
+
).setParseAction(self.pushUMinus)
|
|
85
|
+
# by defining exponentiation as "atom [ ^ factor ]..." instead of
|
|
86
|
+
# "atom [ ^ atom ]...", we get right-to-left exponents, instead of left-to-right
|
|
87
|
+
# that is, 2^3^2 = 2^(3^2), not (2^3)^2.
|
|
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 )
|
|
94
|
+
# general_term = term + ZeroOrMore( addop_term ) | OneOrMore( addop_term)
|
|
95
|
+
# expr << general_term
|
|
96
|
+
self.bnf: ParserElement = final
|
|
97
|
+
# map operator symbols to corresponding arithmetic operations
|
|
98
|
+
epsilon: float = 1e-12
|
|
99
|
+
self.opn: Dict[str, Callable[[Any, Any], Any]] = {
|
|
100
|
+
"+": operator.add,
|
|
101
|
+
"-": operator.sub,
|
|
102
|
+
"+=": operator.iadd,
|
|
103
|
+
"-=": operator.isub,
|
|
104
|
+
"*": operator.mul,
|
|
105
|
+
"*=": operator.imul,
|
|
106
|
+
"/": operator.truediv,
|
|
107
|
+
"/=": operator.itruediv,
|
|
108
|
+
"^": operator.pow,
|
|
109
|
+
"%": operator.mod,
|
|
110
|
+
}
|
|
111
|
+
self.fn: Dict[str, Any] = {
|
|
112
|
+
"sin": math.sin,
|
|
113
|
+
"cos": math.cos,
|
|
114
|
+
"tan": math.tan,
|
|
115
|
+
"sinh": math.sinh,
|
|
116
|
+
"cosh": math.cosh,
|
|
117
|
+
"tanh": math.tanh,
|
|
118
|
+
"exp": math.exp,
|
|
119
|
+
"abs": abs,
|
|
120
|
+
"trunc": lambda a: int(a),
|
|
121
|
+
"round": round,
|
|
122
|
+
"sgn": lambda a: abs(a) > epsilon and ((a > 0) - (a < 0)) or 0,
|
|
123
|
+
"log": lambda a: math.log(a, 10),
|
|
124
|
+
"ln": math.log,
|
|
125
|
+
"log2": math.log2,
|
|
126
|
+
"sqrt": math.sqrt,
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
def evaluateStack(self, s: List[Any]) -> Any:
|
|
130
|
+
op = s.pop()
|
|
131
|
+
if op == "unary -":
|
|
132
|
+
return -self.evaluateStack(s)
|
|
133
|
+
if op in self.opn:
|
|
134
|
+
op2 = self.evaluateStack(s)
|
|
135
|
+
op1 = self.evaluateStack(s)
|
|
136
|
+
return self.opn[op](op1, op2)
|
|
137
|
+
elif op == "PI":
|
|
138
|
+
return math.pi # 3.1415926535
|
|
139
|
+
elif op == "E":
|
|
140
|
+
return math.e # 2.718281828
|
|
141
|
+
elif op in self.fn:
|
|
142
|
+
return self.fn[op](self.evaluateStack(s))
|
|
143
|
+
elif op[0].isalpha():
|
|
144
|
+
return 0
|
|
145
|
+
else:
|
|
146
|
+
return float(op)
|
|
147
|
+
|
|
148
|
+
def eval(self, num_string: str, parseAll: bool = True) -> Any:
|
|
149
|
+
self.exprStack = []
|
|
150
|
+
results = self.bnf.parseString(num_string, parseAll) # noqa: F841
|
|
151
|
+
return self.evaluateStack(self.exprStack[:])
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
NSP: NumericStringParser = NumericStringParser()
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class MathBlock(Block):
|
|
158
|
+
ACCEPTED_NAMES: Tuple[str, ...] = ("math", "m", "+", "calc")
|
|
159
|
+
|
|
160
|
+
def process(self, ctx: Context) -> TypingOptional[str]:
|
|
161
|
+
try:
|
|
162
|
+
return str(NSP.eval(cast(str, ctx.verb.payload).strip(" ")))
|
|
163
|
+
except Exception:
|
|
164
|
+
return None
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
from typing import Optional, Tuple, cast
|
|
5
|
+
|
|
6
|
+
from ..interface import verb_required_block
|
|
7
|
+
from ..interpreter import Context
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
__all__: Tuple[str, ...] = ("RandomBlock",)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RandomBlock(verb_required_block(True, payload=True)): # type: ignore
|
|
14
|
+
"""
|
|
15
|
+
Pick a random item from a list of strings, split by either ``~``
|
|
16
|
+
or ``,``. An optional seed can be provided to the parameter to
|
|
17
|
+
always choose the same item when using that seed.
|
|
18
|
+
|
|
19
|
+
**Usage:** ``{random([seed]):<list>}``
|
|
20
|
+
|
|
21
|
+
**Aliases:** ``#, rand``
|
|
22
|
+
|
|
23
|
+
**Payload:** list
|
|
24
|
+
|
|
25
|
+
**Parameter:** seed, None
|
|
26
|
+
|
|
27
|
+
**Examples:** ::
|
|
28
|
+
|
|
29
|
+
{random:Carl,Harold,Josh} attempts to pick the lock!
|
|
30
|
+
# Possible Outputs:
|
|
31
|
+
# Josh attempts to pick the lock!
|
|
32
|
+
# Carl attempts to pick the lock!
|
|
33
|
+
# Harold attempts to pick the lock!
|
|
34
|
+
|
|
35
|
+
{=(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.}
|
|
36
|
+
{=(insult):{#:{insults}}}
|
|
37
|
+
{insult}
|
|
38
|
+
# Assigns a random insult to the insult variable
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
ACCEPTED_NAMES: Tuple[str, ...] = ("random", "#", "rand")
|
|
42
|
+
|
|
43
|
+
def process(self, ctx: Context) -> Optional[str]:
|
|
44
|
+
spl = []
|
|
45
|
+
if "~" in (payload := cast(str, ctx.verb.payload)):
|
|
46
|
+
spl = payload.split("~")
|
|
47
|
+
else:
|
|
48
|
+
spl = payload.split(",")
|
|
49
|
+
random.seed(ctx.verb.parameter)
|
|
50
|
+
|
|
51
|
+
return random.choice(spl)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
from typing import Optional, Tuple, cast
|
|
5
|
+
|
|
6
|
+
from ..interface import verb_required_block
|
|
7
|
+
from ..interpreter import Context
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
__all__: Tuple[str, ...] = ("RangeBlock",)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RangeBlock(verb_required_block(True, payload=True)): # type: ignore
|
|
14
|
+
"""
|
|
15
|
+
The range block picks a random number from a range of numbers seperated by ``-``.
|
|
16
|
+
The number range is inclusive, so it can pick the starting/ending number as well.
|
|
17
|
+
Using the rangef block will pick a number to the tenth decimal place.
|
|
18
|
+
|
|
19
|
+
An optional seed can be provided to the parameter to always choose the same item when using that seed.
|
|
20
|
+
|
|
21
|
+
**Usage:** ``{range([seed]):<lowest-highest>}``
|
|
22
|
+
|
|
23
|
+
**Aliases:** ``rangef``
|
|
24
|
+
|
|
25
|
+
**Payload:** number
|
|
26
|
+
|
|
27
|
+
**Parameter:** seed, None
|
|
28
|
+
|
|
29
|
+
**Examples:** ::
|
|
30
|
+
|
|
31
|
+
Your lucky number is {range:10-30}!
|
|
32
|
+
# Your lucky number is 14!
|
|
33
|
+
# Your lucky number is 25!
|
|
34
|
+
|
|
35
|
+
{=(height):{rangef:5-7}}
|
|
36
|
+
I am guessing your height is {height}ft.
|
|
37
|
+
# I am guessing your height is 5.3ft.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
ACCEPTED_NAMES: Tuple[str, ...] = ("rangef", "range")
|
|
41
|
+
|
|
42
|
+
def process(self, ctx: Context) -> Optional[str]:
|
|
43
|
+
try:
|
|
44
|
+
spl = cast(str, ctx.verb.payload).split("-")
|
|
45
|
+
random.seed(ctx.verb.parameter)
|
|
46
|
+
if cast(str, ctx.verb.declaration).lower() == "rangef":
|
|
47
|
+
lower: float = float(spl[0])
|
|
48
|
+
upper: float = float(spl[1])
|
|
49
|
+
base: float = random.randint(int(lower) * 10, int(upper) * 10) / 10
|
|
50
|
+
return str(base)
|
|
51
|
+
else:
|
|
52
|
+
lower = int(float(spl[0]))
|
|
53
|
+
upper = int(float(spl[1]))
|
|
54
|
+
return str(random.randint(lower, upper))
|
|
55
|
+
except Exception:
|
|
56
|
+
return None
|
|
@@ -0,0 +1,42 @@
|
|
|
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, ...] = ("RedirectBlock",)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RedirectBlock(verb_required_block(True, parameter=True)): # type: ignore
|
|
13
|
+
"""
|
|
14
|
+
Redirects the tag response to either the given channel, the author's DMs,
|
|
15
|
+
or uses a reply based on what is passed to the parameter.
|
|
16
|
+
|
|
17
|
+
**Usage:** ``{redirect(<"dm"|"reply"|channel>)}``
|
|
18
|
+
|
|
19
|
+
**Payload:** None
|
|
20
|
+
|
|
21
|
+
**Parameter:** "dm", "reply", channel
|
|
22
|
+
|
|
23
|
+
**Examples:** ::
|
|
24
|
+
|
|
25
|
+
{redirect(dm)}
|
|
26
|
+
{redirect(reply)}
|
|
27
|
+
{redirect(#general)}
|
|
28
|
+
{redirect(626861902521434160)}
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
ACCEPTED_NAMES = ("redirect",)
|
|
32
|
+
|
|
33
|
+
def process(self, ctx: Context) -> Optional[str]:
|
|
34
|
+
param: str = cast(str, ctx.verb.parameter).strip()
|
|
35
|
+
if param.lower() == "dm":
|
|
36
|
+
target: str = "dm"
|
|
37
|
+
elif param.lower() == "reply":
|
|
38
|
+
target: str = "reply"
|
|
39
|
+
else:
|
|
40
|
+
target = param
|
|
41
|
+
ctx.response.actions["target"] = target
|
|
42
|
+
return ""
|
|
@@ -0,0 +1,110 @@
|
|
|
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, ...] = ("ReplaceBlock", "PythonBlock")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ReplaceBlock(verb_required_block(True, payload=True, parameter=True)): # type: ignore
|
|
13
|
+
"""
|
|
14
|
+
The replace block will replace specific characters in a string.
|
|
15
|
+
The parameter should split by a ``,``, containing the characters to find
|
|
16
|
+
before the command and the replacements after.
|
|
17
|
+
|
|
18
|
+
**Usage:** ``{replace(<original,new>):<message>}``
|
|
19
|
+
|
|
20
|
+
**Aliases:** ``None``
|
|
21
|
+
|
|
22
|
+
**Payload:** message
|
|
23
|
+
|
|
24
|
+
**Parameter:** original, new
|
|
25
|
+
|
|
26
|
+
**Examples:** ::
|
|
27
|
+
|
|
28
|
+
{replace(o,i):welcome to the server}
|
|
29
|
+
# welcime ti the server
|
|
30
|
+
|
|
31
|
+
{replace(1,6):{args}}
|
|
32
|
+
# if {args} is 1637812
|
|
33
|
+
# 6637862
|
|
34
|
+
|
|
35
|
+
{replace(, ):Test}
|
|
36
|
+
# T e s t
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
ACCEPTED_NAMES: Tuple[str, ...] = ("replace",)
|
|
40
|
+
|
|
41
|
+
def process(self, ctx: Context) -> Optional[str]:
|
|
42
|
+
try:
|
|
43
|
+
before, after = cast(str, ctx.verb.parameter).split(",", 1)
|
|
44
|
+
except ValueError:
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
return cast(str, ctx.verb.payload).replace(before, after)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class PythonBlock(verb_required_block(True, payload=True, parameter=True)): # type: ignore
|
|
51
|
+
"""
|
|
52
|
+
The in block serves three different purposes depending on the alias that is used.
|
|
53
|
+
|
|
54
|
+
The ``in`` alias checks if the parameter is anywhere in the payload.
|
|
55
|
+
|
|
56
|
+
``contain`` strictly checks if the parameter is the payload, split by whitespace.
|
|
57
|
+
|
|
58
|
+
``index`` finds the location of the parameter in the payload, split by whitespace.
|
|
59
|
+
If the parameter string is not found in the payload, it returns 1.
|
|
60
|
+
|
|
61
|
+
index is used to return the value of the string form the given list of
|
|
62
|
+
|
|
63
|
+
**Usage:** ``{in(<string>):<payload>}``
|
|
64
|
+
|
|
65
|
+
**Aliases:** ``index``, ``contains``
|
|
66
|
+
|
|
67
|
+
**Payload:** payload
|
|
68
|
+
|
|
69
|
+
**Parameter:** string
|
|
70
|
+
|
|
71
|
+
**Examples:** ::
|
|
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: # type: ignore
|
|
92
|
+
dec = cast(str, ctx.verb.declaration).lower()
|
|
93
|
+
return dec in ("contains", "in", "index")
|
|
94
|
+
|
|
95
|
+
def process(self, ctx: Context) -> str:
|
|
96
|
+
dec: str = cast(str, ctx.verb.declaration).lower()
|
|
97
|
+
if dec == "contains":
|
|
98
|
+
return str(bool(ctx.verb.parameter in cast(str, ctx.verb.payload).split())).lower()
|
|
99
|
+
elif dec == "in":
|
|
100
|
+
return str(bool(cast(str, ctx.verb.parameter) in cast(str, ctx.verb.payload))).lower()
|
|
101
|
+
else:
|
|
102
|
+
try:
|
|
103
|
+
return str(
|
|
104
|
+
cast(str, ctx.verb.payload)
|
|
105
|
+
.strip()
|
|
106
|
+
.split()
|
|
107
|
+
.index(cast(str, ctx.verb.parameter))
|
|
108
|
+
)
|
|
109
|
+
except ValueError:
|
|
110
|
+
return "-1"
|