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,107 @@
1
+ from typing import Optional
2
+
3
+ from ...interface import Block
4
+ from ...interpreter import Context
5
+
6
+
7
+ class DebugBlock(Block):
8
+ """
9
+ The debug block allows you to debug your tagscript quickly and easily,
10
+ it will save the output to the debug_var key in the response dict.
11
+ Separate the variables you want to include or exclude with a comma or
12
+ a tilde.
13
+
14
+ If no parameters are provided in addition to no payload, all variables
15
+ will be included. If no parameters are provided and a payload is
16
+ provided, it will assume you want to include those variables.
17
+
18
+ **Usage:** ``{debug(["i", "include", "e", "exclude"]):<variables>}``
19
+
20
+ **Aliases:** ``None``
21
+
22
+ **Payload:** ``variables``
23
+
24
+ **Parameter:** ``"i", "include", "e", "exclude"``
25
+
26
+ .. note::
27
+
28
+ {debug} is the same as {debug(exclude):}
29
+
30
+ {debug:somevar~anothervar} is the same as {debug(include):somevar~anothervar}
31
+
32
+ **Examples:**
33
+
34
+ .. note::
35
+
36
+ THIS SHOULD ALWAYS BE PLACED AT THE VERY BOTTOM, IT WILL NOT RETURN ANYTHING UNDER IT.
37
+
38
+ .. tagscript::
39
+
40
+ Assuming we have the following tagscript, we first set the var something, then set
41
+ parsed (using the dollar sign method), to Hello|World, (assume we actually wanted just the Hello
42
+ but we forgot)
43
+
44
+ {=(something):Hello/World}
45
+ {$parsed:{something(1)}}
46
+ {if({parsed}==Hello):Hello|Bye}
47
+
48
+ Running this would provided the output Bye, using the debug block below:
49
+ {debug}
50
+ We'll get all the variables at their, "final state"
51
+ This will be provided in a dict, which you can further parse and output to your liking.
52
+
53
+ EG, in YAML format:
54
+ something: Hello/World
55
+ parsed: Hello/World
56
+
57
+ This allow's you to see that you forgot to parse with a delimiter which will lead to easy fixing.
58
+ """
59
+
60
+ ACCEPTED_NAMES = ("debug",)
61
+
62
+ def process(self, ctx: Context) -> Optional[str]: # pylint: disable=too-many-branches
63
+ """
64
+ Debug the tagscript!
65
+ """
66
+ debug = {}
67
+
68
+ if ctx.verb.parameter:
69
+ if ctx.verb.parameter in ("e", "exc", "exclude"):
70
+ if ctx.verb.payload:
71
+ if "~" in ctx.verb.payload:
72
+ exclude = ctx.verb.payload.split("~")
73
+ else:
74
+ exclude = ctx.verb.payload.split(",")
75
+ for k, v in ctx.response.variables.items():
76
+ if k not in exclude:
77
+ debug[k] = v.get_value(ctx.verb)
78
+ else:
79
+ return None
80
+
81
+ elif ctx.verb.parameter in ("i", "inc", "include"):
82
+ if ctx.verb.payload:
83
+ if "~" in ctx.verb.payload:
84
+ include = ctx.verb.payload.split("~")
85
+ else:
86
+ include = ctx.verb.payload.split(",")
87
+
88
+ for k, v in ctx.response.variables.items():
89
+ if k in include:
90
+ debug[k] = v.get_value(ctx.verb)
91
+ else:
92
+ return None
93
+
94
+ elif ctx.verb.payload:
95
+ if "~" in ctx.verb.payload:
96
+ include = ctx.verb.payload.split("~")
97
+ else:
98
+ include = ctx.verb.payload.split(",")
99
+
100
+ for k, v in ctx.response.variables.items():
101
+ if k in include:
102
+ debug[k] = v.get_value(ctx.verb)
103
+ else:
104
+ for k, v in ctx.response.variables.items():
105
+ debug[k] = v.get_value(ctx.verb)
106
+ ctx.response.extras["debug"] = debug
107
+ return ""
@@ -0,0 +1,49 @@
1
+ from typing import Optional
2
+
3
+ from ..adapter import StringAdapter
4
+ from ..interface import Block
5
+ from ..interpreter import Context
6
+
7
+
8
+ class VarBlock(Block):
9
+ """
10
+ Variables are useful for choosing a value and referencing it later in a tag.
11
+ Variables can be referenced using brackets as any other block.
12
+ Note that if the variable's name is being "used" by any other block the variable
13
+ will be ignored.
14
+
15
+ **Usage:** ``{=(<name>):<value>}``
16
+
17
+ **Aliases:** ``assign, let, var, =``
18
+
19
+ **Payload:** ``value``
20
+
21
+ **Parameter:** ``name``
22
+
23
+ **Examples:**
24
+
25
+ .. tagscript::
26
+
27
+ {=(prefix):!}
28
+ The prefix here is `{prefix}`.
29
+ The prefix here is `!`.
30
+
31
+ {let(day):Monday}
32
+ {if({day}==Wednesday):It's Wednesday my dudes!|The day is {day}.}
33
+ The day is Monday.
34
+
35
+ Variables can also be created like so if the interpreter uses loose variables
36
+ {$<name>:<value>}
37
+ {$day:Monday} == {=(day):Monday}
38
+ """
39
+
40
+ ACCEPTED_NAMES = ("=", "assign", "let", "var")
41
+
42
+ def process(self, ctx: Context) -> Optional[str]:
43
+ """
44
+ Process the block and assign the variable.
45
+ """
46
+ if ctx.verb.parameter in ctx.interpreter._blocknames: # pylint: disable=protected-access
47
+ return None
48
+ ctx.response.variables[ctx.verb.parameter] = StringAdapter(str(ctx.verb.payload))
49
+ return ""
@@ -0,0 +1,91 @@
1
+ from typing import Optional
2
+
3
+ from ..adapter import StringAdapter
4
+ from ..interface import Block
5
+ from ..interpreter import Context
6
+
7
+
8
+ class LooseVariableGetterBlock(Block):
9
+ """
10
+ The loose variable block represents the adapters for any seeded or defined variables.
11
+ This variable implementation is considered "loose" since it checks whether the variable is
12
+ valid during :meth:`process`, rather than :meth:`will_accept`.
13
+ You may also define variables here with {$<variable name>:<value>}, note that this is not
14
+ available using the StrictVariableGetterBlock class.
15
+
16
+ **Usage:** ``{<variable_name>([parameter]):[payload]}``
17
+
18
+ **Aliases:** This block is valid for any inputted declaration.
19
+
20
+ **Payload:** Depends on the variable's underlying adapter.
21
+
22
+ **Parameter:** Depends on the variable's underlying adapter.
23
+
24
+ **Examples:**
25
+
26
+ .. tagscript::
27
+
28
+ {=(example):This is my variable.}
29
+ {example}
30
+ This is my variable.
31
+
32
+ {$variablename:This is another variable.}
33
+ {variablename}
34
+ This is another variable.
35
+ """
36
+
37
+ def will_accept(self, ctx: Context) -> bool: # pylint: disable=arguments-differ
38
+ """
39
+ This block will accept any declaration.
40
+ """
41
+ return True
42
+
43
+ def process(self, ctx: Context) -> Optional[str]:
44
+ """
45
+ This block will check whether the variable is valid.
46
+ """
47
+ if ctx.verb.declaration.startswith("$"):
48
+ varname = ctx.verb.declaration.split("$", 1)[1]
49
+ ctx.response.variables[varname] = StringAdapter(str(ctx.verb.payload))
50
+ return ""
51
+ if ctx.verb.declaration in ctx.response.variables:
52
+ return ctx.response.variables[ctx.verb.declaration].get_value(ctx.verb)
53
+ return None
54
+
55
+
56
+ class StrictVariableGetterBlock(Block):
57
+ """
58
+ The strict variable block represents the adapters for any seeded or defined variables.
59
+ This variable implementation is considered "strict" since it checks whether the variable is
60
+ valid during :meth:`will_accept` and is only processed if the declaration refers to a valid
61
+ variable. The main difference between this and the LooseVariableGetterBlock is that this
62
+ block will only attempt to process if the variable's already been defined.
63
+
64
+ **Usage:** ``{<variable_name>([parameter]):[payload]}``
65
+
66
+ **Aliases:** This block is valid for any variable name in `Response.variables`.
67
+
68
+ **Payload:** Depends on the variable's underlying adapter.
69
+
70
+ **Parameter:** Depends on the variable's underlying adapter.
71
+
72
+ **Examples:**
73
+
74
+ .. tagscript::
75
+
76
+ {=(example):This is my variable.}
77
+ {example}
78
+ This is my variable.
79
+ """
80
+
81
+ def will_accept(self, ctx: Context) -> bool: # pylint: disable=arguments-differ
82
+ """
83
+ Check if the declaration is in the response variables
84
+ """
85
+ return ctx.verb.declaration in ctx.response.variables
86
+
87
+ def process(self, ctx: Context) -> Optional[str]:
88
+ """
89
+ Process the strict variable block
90
+ """
91
+ return ctx.response.variables[ctx.verb.declaration].get_value(ctx.verb)
@@ -0,0 +1,139 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from discord.ext.commands import Cooldown
6
+
7
+ if TYPE_CHECKING:
8
+ from .interpreter import Interpreter, Response
9
+
10
+
11
+ __all__ = (
12
+ "TagScriptError",
13
+ "WorkloadExceededError",
14
+ "ProcessError",
15
+ "EmbedParseError",
16
+ "BadColourArgument",
17
+ "StopError",
18
+ "CooldownExceeded",
19
+ "BlocknameDuplicateError",
20
+ )
21
+
22
+
23
+ class TagScriptError(Exception):
24
+ """
25
+ Base class for all module errors.
26
+ """
27
+
28
+
29
+ class WorkloadExceededError(TagScriptError):
30
+ """
31
+ Raised when the interpreter goes over its passed character limit.
32
+ """
33
+
34
+
35
+ class ProcessError(TagScriptError):
36
+ """
37
+ Raised when an exception occurs during interpreter processing.
38
+
39
+ Attributes
40
+ ----------
41
+ original: Exception
42
+ The original exception that occurred during processing.
43
+ response: Response
44
+ The incomplete response that was being processed when the exception occurred.
45
+ interpreter: Interpreter
46
+ The interpreter used for processing.
47
+ """
48
+
49
+ def __init__(self, error: Exception, response: Response, interpreter: Interpreter) -> None:
50
+ """
51
+ Construct the error
52
+ """
53
+ self.original: Exception = error
54
+ self.response: Response = response
55
+ self.interpreter: Interpreter = interpreter
56
+ super().__init__(error)
57
+
58
+
59
+ class EmbedParseError(TagScriptError):
60
+ """
61
+ Raised if an exception occurs while attempting to parse an embed.
62
+ """
63
+
64
+
65
+ class BadColourArgument(EmbedParseError):
66
+ """
67
+ Raised when the passed input fails to convert to `discord.Colour`.
68
+
69
+ Attributes
70
+ ----------
71
+ argument: str
72
+ The invalid input.
73
+ """
74
+
75
+ def __init__(self, argument: str) -> None:
76
+ """
77
+ Init the error
78
+ """
79
+ self.argument = argument
80
+ super().__init__(f'Colour "{argument}" is invalid.')
81
+
82
+
83
+ class StopError(TagScriptError):
84
+ """
85
+ Raised by the StopBlock to stop processing.
86
+
87
+ Attributes
88
+ ----------
89
+ message: str
90
+ The stop error message.
91
+ """
92
+
93
+ def __init__(self, message: str):
94
+ self.message = message
95
+ super().__init__(message)
96
+
97
+
98
+ class CooldownExceeded(StopError):
99
+ """
100
+ Raised by the cooldown block when a cooldown is exceeded.
101
+
102
+ Attributes
103
+ ----------
104
+ message: str
105
+ The cooldown error message.
106
+ cooldown: discord.ext.commands.Cooldown
107
+ The cooldown bucket with information on the cooldown.
108
+ key: str
109
+ The cooldown key that reached its cooldown.
110
+ retry_after: float
111
+ The seconds left til the cooldown ends.
112
+ """
113
+
114
+ def __init__(self, message: str, cooldown: Cooldown, key: str, retry_after: float) -> None:
115
+ """
116
+ Construct the error
117
+ """
118
+ self.cooldown = cooldown
119
+ self.key = key
120
+ self.retry_after = retry_after
121
+ super().__init__(message)
122
+
123
+
124
+ class BlocknameDuplicateError(TagScriptError):
125
+ """
126
+ Raised when a a duplicate block name is passed to the interpreter
127
+
128
+ Attributes
129
+ ----------
130
+ blockname: str
131
+ The blockname that was duplicated
132
+ """
133
+
134
+ def __init__(self, blockname: str) -> None:
135
+ """
136
+ Init
137
+ """
138
+ self.blockname: str = blockname
139
+ super().__init__()
@@ -0,0 +1,4 @@
1
+ from .adapter import Adapter
2
+ from .block import Block, verb_required_block
3
+
4
+ __all__ = ("Adapter", "Block", "verb_required_block")
@@ -0,0 +1,43 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Optional
4
+
5
+ if TYPE_CHECKING:
6
+ from ..interpreter import Context
7
+
8
+
9
+ class Adapter:
10
+ """
11
+ The base class for TagScript adapters.
12
+
13
+ Implementations must subclass this to create adapters.
14
+ """
15
+
16
+ def __repr__(self) -> str:
17
+ """
18
+ String repr
19
+ """
20
+ return f"<{type(self).__qualname__} at {hex(id(self))}>"
21
+
22
+ def get_value(self, ctx: Context) -> Optional[str]:
23
+ """
24
+ Processes the adapter's actions for a given :class:`~bTagScript.interpreter.Context`.
25
+
26
+ Subclasses must implement this.
27
+
28
+ Parameters
29
+ ----------
30
+ ctx: Context
31
+ The context object containing the TagScript :class:`~bTagScript.verb.Verb`.
32
+
33
+ Returns
34
+ -------
35
+ Optional[str]
36
+ The adapters's processed value.
37
+
38
+ Raises
39
+ ------
40
+ NotImplementedError
41
+ The subclass did not implement this required method.
42
+ """
43
+ raise NotImplementedError
@@ -0,0 +1,136 @@
1
+ from __future__ import annotations
2
+
3
+ from functools import lru_cache
4
+ from typing import TYPE_CHECKING, Optional
5
+
6
+ if TYPE_CHECKING:
7
+ from ..interpreter import Context
8
+
9
+
10
+ __all__ = ("Block", "verb_required_block")
11
+
12
+
13
+ class Block:
14
+ """
15
+ The base class for TagScript blocks.
16
+
17
+ Implementations must subclass this to create new blocks.
18
+
19
+ Attributes
20
+ ----------
21
+ ACCEPTED_NAMES: Tuple[str, ...]
22
+ The accepted names for this block. This ideally should be set as a class attribute.
23
+ """
24
+
25
+ ACCEPTED_NAMES = ()
26
+
27
+ def __repr__(self) -> str:
28
+ """
29
+ String repr
30
+ """
31
+ return f"<{type(self).__qualname__} at {hex(id(self))}>"
32
+
33
+ @classmethod
34
+ def will_accept(cls, ctx: Context) -> bool:
35
+ """
36
+ Describes whether the block is valid for the given :class:`~bTagScript.interpreter.Context`.
37
+
38
+ Parameters
39
+ ----------
40
+ ctx: Context
41
+ The context object containing the TagScript :class:`~bTagScript.verb.Verb`.
42
+
43
+ Returns
44
+ -------
45
+ bool
46
+ Whether the block should be processed for this :class:`~bTagScript.interpreter.Context`.
47
+ """
48
+ dec = ctx.verb.declaration.lower()
49
+ return dec in cls.ACCEPTED_NAMES
50
+
51
+ def pre_process(self, ctx: Context) -> Optional[str]: # pylint: disable=unused-argument
52
+ """
53
+ Any pre processing that needs to be done before the block is processed.
54
+ """
55
+ return None
56
+
57
+ def process(self, ctx: Context) -> Optional[str]:
58
+ """
59
+ Processes the block's actions for a given :class:`~bTagScript.interpreter.Context`.
60
+
61
+ Subclasses must implement this.
62
+
63
+ Parameters
64
+ ----------
65
+ ctx: Context
66
+ The context object containing the TagScript :class:`~bTagScript.verb.Verb`.
67
+
68
+ Returns
69
+ -------
70
+ Optional[str]
71
+ The block's processed value.
72
+
73
+ Raises
74
+ ------
75
+ NotImplementedError
76
+ The subclass did not implement this required method.
77
+ """
78
+ raise NotImplementedError
79
+
80
+ def post_process(self, ctx: Context) -> Optional[str]: # pylint: disable=unused-argument
81
+ """
82
+ Any post processing that needs to be done after the block is processed.
83
+ """
84
+ return None
85
+
86
+
87
+ @lru_cache(maxsize=None)
88
+ def verb_required_block(
89
+ implicit: bool,
90
+ *,
91
+ parameter: bool = False,
92
+ payload: bool = False,
93
+ ) -> Block:
94
+ """
95
+ Get a Block subclass that requires a verb to implicitly or explicitly have a parameter or payload passed.
96
+
97
+ Parameters
98
+ ----------
99
+ implicit: bool
100
+ Specifies whether the value is required to be passed implicitly or explicitly.
101
+ ``{block()}`` would be allowed if implicit is False.
102
+ parameter: bool
103
+ Passing True will cause the block to require a parameter to be passed.
104
+ payload: bool
105
+ Passing True will cause the block to require the payload to be passed.
106
+ """
107
+ check = (lambda x: x) if implicit else (lambda x: x is not None)
108
+
109
+ class RequireMeta(type):
110
+ """
111
+ Require a verb to have a parameter or payload if added.
112
+ """
113
+
114
+ def __repr__(cls) -> str:
115
+ """
116
+ String repr
117
+ """
118
+ return f"VerbRequiredBlock(implicit={implicit!r}, payload={payload!r}, parameter={parameter!r})"
119
+
120
+ class VerbRequiredBlock(Block, metaclass=RequireMeta): # pylint: disable=abstract-method
121
+ """
122
+ The required block.
123
+ """
124
+
125
+ def will_accept(self, ctx: Context) -> bool: # pylint: disable=arguments-differ
126
+ """
127
+ Describes whether the block is valid for the given :class:`~bTagScript.interpreter.Context`.
128
+ """
129
+ verb = ctx.verb
130
+ if payload and not check(verb.payload):
131
+ return False
132
+ if parameter and not check(verb.parameter):
133
+ return False
134
+ return super().will_accept(ctx)
135
+
136
+ return VerbRequiredBlock