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,33 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from ..interface import Block
|
|
4
|
+
from ..interpreter import Context
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CommentBlock(Block):
|
|
8
|
+
"""
|
|
9
|
+
The comment block is literally just for comments, it will not be
|
|
10
|
+
parsed, however it will be removed from your codes output.
|
|
11
|
+
|
|
12
|
+
**Usage:** ``{comment([other]):[text]}``
|
|
13
|
+
|
|
14
|
+
**Aliases:** /, Comment, comment, //
|
|
15
|
+
|
|
16
|
+
**Payload:** ``text``
|
|
17
|
+
|
|
18
|
+
**Parameter:** ``other``
|
|
19
|
+
|
|
20
|
+
.. tagscript::
|
|
21
|
+
|
|
22
|
+
{//:Comment!}
|
|
23
|
+
|
|
24
|
+
{Comment(Something):Comment!}
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
ACCEPTED_NAMES = ("/", "comment", "//")
|
|
28
|
+
|
|
29
|
+
def process(self, ctx: Context) -> Optional[str]:
|
|
30
|
+
"""
|
|
31
|
+
Remove the block
|
|
32
|
+
"""
|
|
33
|
+
return ""
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from ..interface import verb_required_block
|
|
4
|
+
from ..interpreter import Context
|
|
5
|
+
from . import helper_parse_if, helper_parse_list_if, helper_split
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def parse_into_output(payload: str, result: Optional[bool]) -> Optional[str]:
|
|
9
|
+
"""
|
|
10
|
+
Parse a payload into an output.
|
|
11
|
+
"""
|
|
12
|
+
if result is None:
|
|
13
|
+
return
|
|
14
|
+
try:
|
|
15
|
+
output = helper_split(payload, False)
|
|
16
|
+
if output is not None and len(output) == 2:
|
|
17
|
+
if result:
|
|
18
|
+
return output[0]
|
|
19
|
+
return output[1]
|
|
20
|
+
elif result:
|
|
21
|
+
return payload
|
|
22
|
+
else:
|
|
23
|
+
return ""
|
|
24
|
+
except: # pylint: disable=bare-except
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
ImplicitPPRBlock = verb_required_block(True, payload=True, parameter=True)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AnyBlock(ImplicitPPRBlock):
|
|
32
|
+
"""
|
|
33
|
+
The any block checks that any of the passed expressions are true.
|
|
34
|
+
Multiple expressions can be passed to the parameter by splitting them with ``|``.
|
|
35
|
+
|
|
36
|
+
The payload is a required message that must be split by ``|``.
|
|
37
|
+
If the expression evaluates true, then the message before the ``|`` is returned, else the message after is returned.
|
|
38
|
+
|
|
39
|
+
**Usage:** ``{any(<expression|expression|...>):<message>}``
|
|
40
|
+
|
|
41
|
+
**Aliases:** ``or``
|
|
42
|
+
|
|
43
|
+
**Payload:** ``message``
|
|
44
|
+
|
|
45
|
+
**Parameter:** ``expression``
|
|
46
|
+
|
|
47
|
+
**Examples:**
|
|
48
|
+
|
|
49
|
+
.. tagscript::
|
|
50
|
+
|
|
51
|
+
{any(hi=={args}|hello=={args}|heyy=={args}):Hello {user}!|How rude.}
|
|
52
|
+
If {args} is hi
|
|
53
|
+
Hello _Leg3ndary#0001!
|
|
54
|
+
|
|
55
|
+
If {args} is what's up!
|
|
56
|
+
How rude.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
ACCEPTED_NAMES = ("any", "or")
|
|
60
|
+
|
|
61
|
+
def process(self, ctx: Context) -> Optional[str]:
|
|
62
|
+
"""
|
|
63
|
+
Process the any block
|
|
64
|
+
"""
|
|
65
|
+
result = any(helper_parse_list_if(ctx.verb.parameter) or [])
|
|
66
|
+
return parse_into_output(ctx.verb.payload, result)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class AllBlock(ImplicitPPRBlock):
|
|
70
|
+
"""
|
|
71
|
+
The all block checks that all of the passed expressions are true.
|
|
72
|
+
Multiple expressions can be passed to the parameter by splitting them with ``|``.
|
|
73
|
+
|
|
74
|
+
The payload is a required message that must be split by ``|``.
|
|
75
|
+
If the expression evaluates true, then the message before the ``|`` is returned, else the message after is returned.
|
|
76
|
+
|
|
77
|
+
**Usage:** ``{all(<expression|expression|...>):<message>}``
|
|
78
|
+
|
|
79
|
+
**Aliases:** ``and``
|
|
80
|
+
|
|
81
|
+
**Payload:** ``message``
|
|
82
|
+
|
|
83
|
+
**Parameter:** ``expression``
|
|
84
|
+
|
|
85
|
+
**Examples:**
|
|
86
|
+
|
|
87
|
+
.. tagscript::
|
|
88
|
+
|
|
89
|
+
{all({args}>=100|{args}<=1000):You picked {args}.|You must provide a number between 100 and 1000.}
|
|
90
|
+
# if {args} is 52
|
|
91
|
+
You must provide a number between 100 and 1000.
|
|
92
|
+
|
|
93
|
+
# if {args} is 282
|
|
94
|
+
You picked 282.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
ACCEPTED_NAMES = ("all", "and")
|
|
98
|
+
|
|
99
|
+
def process(self, ctx: Context) -> Optional[str]:
|
|
100
|
+
"""
|
|
101
|
+
Process all the expressions
|
|
102
|
+
"""
|
|
103
|
+
result = all(helper_parse_list_if(ctx.verb.parameter) or [])
|
|
104
|
+
return parse_into_output(ctx.verb.payload, result)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class IfBlock(ImplicitPPRBlock):
|
|
108
|
+
"""
|
|
109
|
+
The if block returns a message based on the passed expression to the parameter.
|
|
110
|
+
An expression is represented by two values compared with an operator.
|
|
111
|
+
|
|
112
|
+
The payload is a required message that must be split by ``|``.
|
|
113
|
+
If the expression evaluates true, then the message before the ``|`` is returned, else the message after is returned.
|
|
114
|
+
|
|
115
|
+
**Expression Operators:**
|
|
116
|
+
|
|
117
|
+
+----------+--------------------------+---------+---------------------------------------------+
|
|
118
|
+
| Operator | Check | Example | Description |
|
|
119
|
+
+==========+==========================+=========+=============================================+
|
|
120
|
+
| ``==`` | equality | a==a | value 1 is equal to value 2 |
|
|
121
|
+
+----------+--------------------------+---------+---------------------------------------------+
|
|
122
|
+
| ``!=`` | inequality | a!=b | value 1 is not equal to value 2 |
|
|
123
|
+
+----------+--------------------------+---------+---------------------------------------------+
|
|
124
|
+
| ``>`` | greater than | 5>3 | value 1 is greater than value 2 |
|
|
125
|
+
+----------+--------------------------+---------+---------------------------------------------+
|
|
126
|
+
| ``<`` | less than | 4<8 | value 1 is less than value 2 |
|
|
127
|
+
+----------+--------------------------+---------+---------------------------------------------+
|
|
128
|
+
| ``>=`` | greater than or equality | 10>=10 | value 1 is greater than or equal to value 2 |
|
|
129
|
+
+----------+--------------------------+---------+---------------------------------------------+
|
|
130
|
+
| ``<=`` | less than or equality | 5<=6 | value 1 is less than or equal to value 2 |
|
|
131
|
+
+----------+--------------------------+---------+---------------------------------------------+
|
|
132
|
+
|
|
133
|
+
**Usage:** ``{if(<expression>):<message>]}``
|
|
134
|
+
|
|
135
|
+
**Payload:** ``message``
|
|
136
|
+
|
|
137
|
+
**Parameter:** ``expression``
|
|
138
|
+
|
|
139
|
+
**Examples:**
|
|
140
|
+
|
|
141
|
+
.. tagscript::
|
|
142
|
+
|
|
143
|
+
{if(63=={args}):You guessed it! The number I was thinking of was 63!|Too {if({args}<63):low|high}, try again.}
|
|
144
|
+
If args is 63
|
|
145
|
+
You guessed it! The number I was thinking of was 63!
|
|
146
|
+
|
|
147
|
+
If args is 73
|
|
148
|
+
Too low, try again.
|
|
149
|
+
|
|
150
|
+
If args is 14
|
|
151
|
+
Too high, try again.
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
ACCEPTED_NAMES = ("if",)
|
|
155
|
+
|
|
156
|
+
def process(self, ctx: Context) -> Optional[str]:
|
|
157
|
+
"""
|
|
158
|
+
Process the if block
|
|
159
|
+
"""
|
|
160
|
+
result = helper_parse_if(ctx.verb.parameter)
|
|
161
|
+
return parse_into_output(ctx.verb.payload, result)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from ..interface import verb_required_block
|
|
4
|
+
from ..interpreter import Context
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CountBlock(verb_required_block(True, payload=True)):
|
|
8
|
+
"""
|
|
9
|
+
The count block will count how much of text is in message.
|
|
10
|
+
This is case sensitive and will include substrings, if you
|
|
11
|
+
don't provide a parameter, it will count the spaces in the
|
|
12
|
+
message.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
**Usage:** ``{count([text]):<message>}``
|
|
16
|
+
|
|
17
|
+
**Aliases:** ``None``
|
|
18
|
+
|
|
19
|
+
**Payload:** ``message``
|
|
20
|
+
|
|
21
|
+
**Parameter:** text
|
|
22
|
+
|
|
23
|
+
.. tagscript::
|
|
24
|
+
|
|
25
|
+
{count(Tag):TagScript}
|
|
26
|
+
1
|
|
27
|
+
|
|
28
|
+
{count(Tag):Tag Script TagScript}
|
|
29
|
+
2
|
|
30
|
+
|
|
31
|
+
{count(t):Hello World, Tag, Script}
|
|
32
|
+
1 as there's only one lowercase t in the entire string
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
ACCEPTED_NAMES = ("count",)
|
|
36
|
+
|
|
37
|
+
def process(self, ctx: Context) -> Optional[str]:
|
|
38
|
+
"""
|
|
39
|
+
Check the count of a string
|
|
40
|
+
"""
|
|
41
|
+
if ctx.verb.parameter:
|
|
42
|
+
return str(ctx.verb.payload.count(ctx.verb.parameter))
|
|
43
|
+
return str(ctx.verb.payload.count(" "))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class LengthBlock(verb_required_block(True, payload=True)):
|
|
47
|
+
"""
|
|
48
|
+
The length block will check the length of the given String.
|
|
49
|
+
If a parameter is passed in, the block will check the length
|
|
50
|
+
based on what you passed in, w for word, s for spaces.
|
|
51
|
+
If you provide an invalid parameter, the block will return -1.
|
|
52
|
+
|
|
53
|
+
**Usage:** ``{length(["w", "s"]):<text>}``
|
|
54
|
+
|
|
55
|
+
**Aliases:** ``len``
|
|
56
|
+
|
|
57
|
+
**Payload:** ``text``
|
|
58
|
+
|
|
59
|
+
**Parameter:** ``"w", "s"``
|
|
60
|
+
|
|
61
|
+
.. tagscript::
|
|
62
|
+
|
|
63
|
+
{length:TagScript}
|
|
64
|
+
9
|
|
65
|
+
|
|
66
|
+
{len(w):Tag Script}
|
|
67
|
+
2
|
|
68
|
+
|
|
69
|
+
{len(s):Hello World, Tag, Script}
|
|
70
|
+
3
|
|
71
|
+
|
|
72
|
+
{len(space):Hello World, Tag, Script}
|
|
73
|
+
-1
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
ACCEPTED_NAMES = ("length", "len")
|
|
77
|
+
|
|
78
|
+
def process(self, ctx: Context) -> Optional[str]:
|
|
79
|
+
"""
|
|
80
|
+
Check the length of a string
|
|
81
|
+
"""
|
|
82
|
+
if ctx.verb.parameter:
|
|
83
|
+
if ctx.verb.parameter in ("w", "words", "word"):
|
|
84
|
+
return str(len(ctx.verb.payload.split(" ")))
|
|
85
|
+
if ctx.verb.parameter in ("s", "spaces", "space"):
|
|
86
|
+
return str(len(ctx.verb.payload.split(" ")) - 1)
|
|
87
|
+
return "-1"
|
|
88
|
+
return str(len(ctx.verb.payload))
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from ..interface import Block
|
|
4
|
+
from ..interpreter import Context
|
|
5
|
+
from ..verb import Verb
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DigitShorthandBlock(Block):
|
|
9
|
+
"""
|
|
10
|
+
DigitShorthand Blocks are used to provide a shorthand for variables.
|
|
11
|
+
This is usually used for arguments, so you can set {1} == {args(1)}
|
|
12
|
+
{2} == {args(2)} etc.
|
|
13
|
+
|
|
14
|
+
.. tagscript::
|
|
15
|
+
|
|
16
|
+
Check the description.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, var_name: str) -> None:
|
|
20
|
+
"""
|
|
21
|
+
Initialize the DigitShorthandBlock with the block you want to be digitted. If thats a word.
|
|
22
|
+
"""
|
|
23
|
+
self.redirect_name = var_name
|
|
24
|
+
|
|
25
|
+
def will_accept(self, ctx: Context) -> bool: # pylint: disable=arguments-differ
|
|
26
|
+
"""
|
|
27
|
+
Check if the declaration is a digit
|
|
28
|
+
"""
|
|
29
|
+
return ctx.verb.declaration.isdigit()
|
|
30
|
+
|
|
31
|
+
def process(self, ctx: Context) -> Optional[str]:
|
|
32
|
+
"""
|
|
33
|
+
Process the digital shorthand
|
|
34
|
+
"""
|
|
35
|
+
blank = Verb()
|
|
36
|
+
blank.declaration = self.redirect_name
|
|
37
|
+
blank.parameter = ctx.verb.declaration
|
|
38
|
+
ctx.verb = blank
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from .command_block import CommandBlock
|
|
2
|
+
from .cooldown_block import CooldownBlock
|
|
3
|
+
from .delete_block import DeleteBlock
|
|
4
|
+
from .embed_block import EmbedBlock
|
|
5
|
+
from .override_block import OverrideBlock
|
|
6
|
+
from .react_block import ReactBlock
|
|
7
|
+
from .redirect_block import RedirectBlock
|
|
8
|
+
from .requirement_blocks import BlacklistBlock, RequireBlock
|
|
9
|
+
|
|
10
|
+
__all__ = (
|
|
11
|
+
"CommandBlock",
|
|
12
|
+
"OverrideBlock",
|
|
13
|
+
"CooldownBlock",
|
|
14
|
+
"DeleteBlock",
|
|
15
|
+
"EmbedBlock",
|
|
16
|
+
"ReactBlock",
|
|
17
|
+
"RedirectBlock",
|
|
18
|
+
"BlacklistBlock",
|
|
19
|
+
"RequireBlock",
|
|
20
|
+
)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from ...interface import verb_required_block
|
|
4
|
+
from ...interpreter import Context
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CommandBlock(verb_required_block(True, payload=True)):
|
|
8
|
+
"""
|
|
9
|
+
Run a command as if the tag invoker had ran it. Only 3 command
|
|
10
|
+
blocks can be used in a tag.
|
|
11
|
+
|
|
12
|
+
**Usage:** ``{command:<command>}``
|
|
13
|
+
|
|
14
|
+
**Aliases:** ``c, com, command``
|
|
15
|
+
|
|
16
|
+
**Payload:** command
|
|
17
|
+
|
|
18
|
+
**Parameter:** None
|
|
19
|
+
|
|
20
|
+
**Examples:**
|
|
21
|
+
|
|
22
|
+
.. tagscript::
|
|
23
|
+
|
|
24
|
+
{c:ping}
|
|
25
|
+
# Invokes ping command
|
|
26
|
+
|
|
27
|
+
{c:ban {target(id)} Chatflood/spam}
|
|
28
|
+
# Invokes ban command on the pinged user with the reason as "Chatflood/spam"
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
ACCEPTED_NAMES = ("c", "com", "command")
|
|
32
|
+
|
|
33
|
+
def __init__(self, limit: int = 3) -> None:
|
|
34
|
+
"""
|
|
35
|
+
Construct the command block.
|
|
36
|
+
"""
|
|
37
|
+
self.limit = limit
|
|
38
|
+
super().__init__()
|
|
39
|
+
|
|
40
|
+
def process(self, ctx: Context) -> Optional[str]:
|
|
41
|
+
"""
|
|
42
|
+
Process the block and update response.actions
|
|
43
|
+
"""
|
|
44
|
+
command = ctx.verb.payload.strip()
|
|
45
|
+
actions = ctx.response.actions.get("commands")
|
|
46
|
+
if actions:
|
|
47
|
+
if len(actions) >= self.limit:
|
|
48
|
+
return f"<{ctx.verb.declaration} error: limit reached ({self.limit})>"
|
|
49
|
+
else:
|
|
50
|
+
ctx.response.actions["commands"] = []
|
|
51
|
+
ctx.response.actions["commands"].append(command)
|
|
52
|
+
return ""
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from typing import Any, Dict, Optional
|
|
3
|
+
|
|
4
|
+
from discord.ext.commands import CooldownMapping
|
|
5
|
+
|
|
6
|
+
from ...exceptions import CooldownExceeded
|
|
7
|
+
from ...interface import verb_required_block
|
|
8
|
+
from ...interpreter import Context
|
|
9
|
+
from ..helpers import helper_split
|
|
10
|
+
|
|
11
|
+
__all__ = ("CooldownBlock",)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CooldownBlock(verb_required_block(True, payload=True, parameter=True)):
|
|
15
|
+
"""
|
|
16
|
+
The cooldown block implements cooldowns when running a tag.
|
|
17
|
+
The parameter requires 2 values to be passed: ``rate`` and ``per`` integers.
|
|
18
|
+
The ``rate`` is the number of times the tag can be used every ``per`` seconds.
|
|
19
|
+
|
|
20
|
+
The payload requires a ``key`` value, which is the key used to store the cooldown.
|
|
21
|
+
A key should be any string that is unique. If a channel's ID is passed as a key,
|
|
22
|
+
the tag's cooldown will be enforced on that channel. Running the tag in a separate channel
|
|
23
|
+
would have a different cooldown with the same ``rate`` and ``per`` values.
|
|
24
|
+
|
|
25
|
+
The payload also has an optional ``message`` value, which is the message to be sent when the
|
|
26
|
+
cooldown is exceeded. If no message is passed, the default message will be sent instead.
|
|
27
|
+
The cooldown message supports 2 blocks: ``key`` and ``retry_after``.
|
|
28
|
+
|
|
29
|
+
**Usage:** ``{cooldown(<rate>|<per>):<key>|[message]}``
|
|
30
|
+
|
|
31
|
+
**Payload:** ``key, message``
|
|
32
|
+
|
|
33
|
+
**Parameter:** ``rate, per``
|
|
34
|
+
|
|
35
|
+
**Examples:**
|
|
36
|
+
|
|
37
|
+
.. tagscript::
|
|
38
|
+
|
|
39
|
+
{cooldown(1|10):{author(id)}}
|
|
40
|
+
the tag author used the tag more than once in 10 seconds
|
|
41
|
+
The bucket for 741074175875088424 has reached its cooldown. Retry in 3.25 seconds."
|
|
42
|
+
|
|
43
|
+
{cooldown(3|3):{channel(id)}|Slow down! This tag can only be used 3 times per 3 seconds per channel. Try again in **{retry_after}** seconds."}
|
|
44
|
+
the tag was used more than 3 times in 3 seconds in a channel
|
|
45
|
+
Slow down! This tag can only be used 3 times per 3 seconds per channel. Try again in **0.74** seconds.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
ACCEPTED_NAMES = ("cooldown",)
|
|
49
|
+
COOLDOWNS: Dict[Any, CooldownMapping] = {}
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
def create_cooldown(cls, key: Any, rate: int, per: int) -> CooldownMapping:
|
|
53
|
+
"""
|
|
54
|
+
Create a new cooldown for the given key.
|
|
55
|
+
"""
|
|
56
|
+
cooldown = CooldownMapping.from_cooldown(rate, per, lambda x: x)
|
|
57
|
+
cls.COOLDOWNS[key] = cooldown
|
|
58
|
+
return cooldown
|
|
59
|
+
|
|
60
|
+
def process(self, ctx: Context) -> Optional[str]:
|
|
61
|
+
"""
|
|
62
|
+
Process the cooldown block.
|
|
63
|
+
"""
|
|
64
|
+
verb = ctx.verb
|
|
65
|
+
try:
|
|
66
|
+
rate, per = helper_split(verb.parameter, maxsplit=1)
|
|
67
|
+
per = int(per)
|
|
68
|
+
rate = float(rate)
|
|
69
|
+
except (ValueError, TypeError):
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
if split := helper_split(verb.payload, False, maxsplit=1):
|
|
73
|
+
key, message = split
|
|
74
|
+
else:
|
|
75
|
+
key = verb.payload
|
|
76
|
+
message = None
|
|
77
|
+
|
|
78
|
+
cooldown_key = ctx.response.extras.get("cooldown_key")
|
|
79
|
+
if cooldown_key is None:
|
|
80
|
+
cooldown_key = ctx.original_message
|
|
81
|
+
try:
|
|
82
|
+
cooldown = self.COOLDOWNS[cooldown_key]
|
|
83
|
+
base = cooldown._cooldown # pylint: disable=protected-access
|
|
84
|
+
if (rate, per) != (base.rate, base.per):
|
|
85
|
+
cooldown = self.create_cooldown(cooldown_key, rate, per)
|
|
86
|
+
except KeyError:
|
|
87
|
+
cooldown = self.create_cooldown(cooldown_key, rate, per)
|
|
88
|
+
|
|
89
|
+
current = time.time()
|
|
90
|
+
bucket = cooldown.get_bucket(key, current)
|
|
91
|
+
retry_after = bucket.update_rate_limit(current)
|
|
92
|
+
if retry_after:
|
|
93
|
+
retry_after = round(retry_after, 2)
|
|
94
|
+
if message:
|
|
95
|
+
message = message.replace("{key}", str(key)).replace(
|
|
96
|
+
"{retry_after}", str(retry_after)
|
|
97
|
+
)
|
|
98
|
+
else:
|
|
99
|
+
message = f"The bucket for {key} has reached its cooldown. Retry in {retry_after} seconds."
|
|
100
|
+
raise CooldownExceeded(message, bucket, key, retry_after)
|
|
101
|
+
return ""
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from ...interface import Block
|
|
4
|
+
from ...interpreter import Context
|
|
5
|
+
from .. import helper_parse_if
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DeleteBlock(Block):
|
|
9
|
+
"""
|
|
10
|
+
The delete block will delete the message if the condition provided in
|
|
11
|
+
the parameter is met, or if just the block is added, the message will
|
|
12
|
+
be deleted. Only one delete block will be processed, the rest,
|
|
13
|
+
removed, but ignored.
|
|
14
|
+
|
|
15
|
+
.. note::
|
|
16
|
+
|
|
17
|
+
This block will only set the actions "delete" key to True/False.
|
|
18
|
+
You must set the behaviour manually.
|
|
19
|
+
|
|
20
|
+
**Usage:** ``{delete(<expression>)}``
|
|
21
|
+
|
|
22
|
+
**Aliases:** ``del``
|
|
23
|
+
|
|
24
|
+
**Payload:** ``None``
|
|
25
|
+
|
|
26
|
+
**Parameter:** ``expression``
|
|
27
|
+
|
|
28
|
+
.. tagscript::
|
|
29
|
+
|
|
30
|
+
{delete}
|
|
31
|
+
{del(true==true)}
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
ACCEPTED_NAMES = ("delete", "del")
|
|
35
|
+
|
|
36
|
+
def process(self, ctx: Context) -> Optional[str]:
|
|
37
|
+
"""
|
|
38
|
+
Process the delete
|
|
39
|
+
"""
|
|
40
|
+
if "delete" in ctx.response.actions.keys():
|
|
41
|
+
return ""
|
|
42
|
+
if ctx.verb.parameter is None:
|
|
43
|
+
value = True
|
|
44
|
+
else:
|
|
45
|
+
value = helper_parse_if(ctx.verb.parameter)
|
|
46
|
+
ctx.response.actions["delete"] = value
|
|
47
|
+
return ""
|