ya-tagscript 1.0.0__tar.gz

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 (186) hide show
  1. ya_tagscript-1.0.0/.github/workflows/black.yml +22 -0
  2. ya_tagscript-1.0.0/.github/workflows/publish.yml +121 -0
  3. ya_tagscript-1.0.0/.github/workflows/test.yml +32 -0
  4. ya_tagscript-1.0.0/.gitignore +27 -0
  5. ya_tagscript-1.0.0/.pre-commit-config.yaml +33 -0
  6. ya_tagscript-1.0.0/.python-version +1 -0
  7. ya_tagscript-1.0.0/.readthedocs.yaml +14 -0
  8. ya_tagscript-1.0.0/LICENSE.md +1 -0
  9. ya_tagscript-1.0.0/PKG-INFO +104 -0
  10. ya_tagscript-1.0.0/README.md +82 -0
  11. ya_tagscript-1.0.0/docs/Makefile +23 -0
  12. ya_tagscript-1.0.0/docs/_ext/glossary_backlink_checker.py +173 -0
  13. ya_tagscript-1.0.0/docs/_ext/styled_list_directive.py +33 -0
  14. ya_tagscript-1.0.0/docs/adapters.rst +67 -0
  15. ya_tagscript-1.0.0/docs/blocks.rst +151 -0
  16. ya_tagscript-1.0.0/docs/code_examples/adapters/int_adapter_example.py +15 -0
  17. ya_tagscript-1.0.0/docs/code_examples/adapters/member_adapter_example.py +32 -0
  18. ya_tagscript-1.0.0/docs/code_examples/nested_blocks_example.py +55 -0
  19. ya_tagscript-1.0.0/docs/code_examples/quickstart_example.py +51 -0
  20. ya_tagscript-1.0.0/docs/conf.py +93 -0
  21. ya_tagscript-1.0.0/docs/credits.rst +43 -0
  22. ya_tagscript-1.0.0/docs/exceptions.rst +7 -0
  23. ya_tagscript-1.0.0/docs/glossary.rst +124 -0
  24. ya_tagscript-1.0.0/docs/index.rst +112 -0
  25. ya_tagscript-1.0.0/docs/interfaces.rst +37 -0
  26. ya_tagscript-1.0.0/docs/interpreter.rst +21 -0
  27. ya_tagscript-1.0.0/docs/intro.rst +110 -0
  28. ya_tagscript-1.0.0/docs/make.bat +35 -0
  29. ya_tagscript-1.0.0/docs/numeric_parser.rst +14 -0
  30. ya_tagscript-1.0.0/docs/quickstart.rst +7 -0
  31. ya_tagscript-1.0.0/docs/requirements.txt +5 -0
  32. ya_tagscript-1.0.0/pyproject.toml +69 -0
  33. ya_tagscript-1.0.0/src/__init__.py +0 -0
  34. ya_tagscript-1.0.0/src/ya_tagscript/__init__.py +6 -0
  35. ya_tagscript-1.0.0/src/ya_tagscript/adapters/__init__.py +21 -0
  36. ya_tagscript-1.0.0/src/ya_tagscript/adapters/discord_adapters/__init__.py +11 -0
  37. ya_tagscript-1.0.0/src/ya_tagscript/adapters/discord_adapters/attribute_adapter.py +62 -0
  38. ya_tagscript-1.0.0/src/ya_tagscript/adapters/discord_adapters/channel_adapter.py +54 -0
  39. ya_tagscript-1.0.0/src/ya_tagscript/adapters/discord_adapters/guild_adapter.py +63 -0
  40. ya_tagscript-1.0.0/src/ya_tagscript/adapters/discord_adapters/member_adapter.py +60 -0
  41. ya_tagscript-1.0.0/src/ya_tagscript/adapters/function_adapter.py +24 -0
  42. ya_tagscript-1.0.0/src/ya_tagscript/adapters/int_adapter.py +17 -0
  43. ya_tagscript-1.0.0/src/ya_tagscript/adapters/object_adapter.py +49 -0
  44. ya_tagscript-1.0.0/src/ya_tagscript/adapters/string_adapter.py +93 -0
  45. ya_tagscript-1.0.0/src/ya_tagscript/blocks/__init__.py +70 -0
  46. ya_tagscript-1.0.0/src/ya_tagscript/blocks/actions/__init__.py +15 -0
  47. ya_tagscript-1.0.0/src/ya_tagscript/blocks/actions/command_block.py +66 -0
  48. ya_tagscript-1.0.0/src/ya_tagscript/blocks/actions/delete_block.py +68 -0
  49. ya_tagscript-1.0.0/src/ya_tagscript/blocks/actions/override_block.py +81 -0
  50. ya_tagscript-1.0.0/src/ya_tagscript/blocks/actions/react_block.py +106 -0
  51. ya_tagscript-1.0.0/src/ya_tagscript/blocks/actions/redirect_block.py +79 -0
  52. ya_tagscript-1.0.0/src/ya_tagscript/blocks/actions/silence_block.py +53 -0
  53. ya_tagscript-1.0.0/src/ya_tagscript/blocks/conditional/__init__.py +11 -0
  54. ya_tagscript-1.0.0/src/ya_tagscript/blocks/conditional/all_block.py +95 -0
  55. ya_tagscript-1.0.0/src/ya_tagscript/blocks/conditional/any_block.py +95 -0
  56. ya_tagscript-1.0.0/src/ya_tagscript/blocks/conditional/if_block.py +114 -0
  57. ya_tagscript-1.0.0/src/ya_tagscript/blocks/conditional/python_block.py +81 -0
  58. ya_tagscript-1.0.0/src/ya_tagscript/blocks/discord/__init__.py +7 -0
  59. ya_tagscript-1.0.0/src/ya_tagscript/blocks/discord/cooldown_block.py +122 -0
  60. ya_tagscript-1.0.0/src/ya_tagscript/blocks/discord/embed_block.py +358 -0
  61. ya_tagscript-1.0.0/src/ya_tagscript/blocks/flow/__init__.py +9 -0
  62. ya_tagscript-1.0.0/src/ya_tagscript/blocks/flow/break_block.py +54 -0
  63. ya_tagscript-1.0.0/src/ya_tagscript/blocks/flow/shortcutredirect_block.py +64 -0
  64. ya_tagscript-1.0.0/src/ya_tagscript/blocks/flow/stop_block.py +56 -0
  65. ya_tagscript-1.0.0/src/ya_tagscript/blocks/limiters/__init__.py +7 -0
  66. ya_tagscript-1.0.0/src/ya_tagscript/blocks/limiters/blacklist_block.py +84 -0
  67. ya_tagscript-1.0.0/src/ya_tagscript/blocks/limiters/require_block.py +86 -0
  68. ya_tagscript-1.0.0/src/ya_tagscript/blocks/lists/__init__.py +7 -0
  69. ya_tagscript-1.0.0/src/ya_tagscript/blocks/lists/cycle_block.py +65 -0
  70. ya_tagscript-1.0.0/src/ya_tagscript/blocks/lists/list_block.py +63 -0
  71. ya_tagscript-1.0.0/src/ya_tagscript/blocks/math/__init__.py +7 -0
  72. ya_tagscript-1.0.0/src/ya_tagscript/blocks/math/math_block.py +350 -0
  73. ya_tagscript-1.0.0/src/ya_tagscript/blocks/math/ordinal_block.py +79 -0
  74. ya_tagscript-1.0.0/src/ya_tagscript/blocks/meta/__init__.py +7 -0
  75. ya_tagscript-1.0.0/src/ya_tagscript/blocks/meta/comment_block.py +47 -0
  76. ya_tagscript-1.0.0/src/ya_tagscript/blocks/meta/debug_block.py +140 -0
  77. ya_tagscript-1.0.0/src/ya_tagscript/blocks/rng/__init__.py +9 -0
  78. ya_tagscript-1.0.0/src/ya_tagscript/blocks/rng/fiftyfifty_block.py +36 -0
  79. ya_tagscript-1.0.0/src/ya_tagscript/blocks/rng/random_block.py +96 -0
  80. ya_tagscript-1.0.0/src/ya_tagscript/blocks/rng/range_block.py +95 -0
  81. ya_tagscript-1.0.0/src/ya_tagscript/blocks/strings/__init__.py +15 -0
  82. ya_tagscript-1.0.0/src/ya_tagscript/blocks/strings/case_block.py +55 -0
  83. ya_tagscript-1.0.0/src/ya_tagscript/blocks/strings/join_block.py +47 -0
  84. ya_tagscript-1.0.0/src/ya_tagscript/blocks/strings/replace_block.py +64 -0
  85. ya_tagscript-1.0.0/src/ya_tagscript/blocks/strings/substring_block.py +78 -0
  86. ya_tagscript-1.0.0/src/ya_tagscript/blocks/strings/urldecode_block.py +54 -0
  87. ya_tagscript-1.0.0/src/ya_tagscript/blocks/strings/urlencode_block.py +52 -0
  88. ya_tagscript-1.0.0/src/ya_tagscript/blocks/time/__init__.py +7 -0
  89. ya_tagscript-1.0.0/src/ya_tagscript/blocks/time/strf_block.py +102 -0
  90. ya_tagscript-1.0.0/src/ya_tagscript/blocks/time/timedelta_block.py +171 -0
  91. ya_tagscript-1.0.0/src/ya_tagscript/blocks/variables/__init__.py +9 -0
  92. ya_tagscript-1.0.0/src/ya_tagscript/blocks/variables/assign_block.py +48 -0
  93. ya_tagscript-1.0.0/src/ya_tagscript/blocks/variables/loose_variable_getter_block.py +80 -0
  94. ya_tagscript-1.0.0/src/ya_tagscript/blocks/variables/strict_variable_getter_block.py +80 -0
  95. ya_tagscript-1.0.0/src/ya_tagscript/exceptions/__init__.py +19 -0
  96. ya_tagscript-1.0.0/src/ya_tagscript/exceptions/exceptions.py +111 -0
  97. ya_tagscript-1.0.0/src/ya_tagscript/interfaces/__init__.py +12 -0
  98. ya_tagscript-1.0.0/src/ya_tagscript/interfaces/adapterabc.py +28 -0
  99. ya_tagscript-1.0.0/src/ya_tagscript/interfaces/blockabc.py +151 -0
  100. ya_tagscript-1.0.0/src/ya_tagscript/interfaces/interpreterabc.py +107 -0
  101. ya_tagscript-1.0.0/src/ya_tagscript/interfaces/nodeabc.py +124 -0
  102. ya_tagscript-1.0.0/src/ya_tagscript/interpreter/__init__.py +11 -0
  103. ya_tagscript-1.0.0/src/ya_tagscript/interpreter/context.py +47 -0
  104. ya_tagscript-1.0.0/src/ya_tagscript/interpreter/interpreter.py +196 -0
  105. ya_tagscript-1.0.0/src/ya_tagscript/interpreter/node.py +75 -0
  106. ya_tagscript-1.0.0/src/ya_tagscript/interpreter/parse_state.py +96 -0
  107. ya_tagscript-1.0.0/src/ya_tagscript/interpreter/response.py +75 -0
  108. ya_tagscript-1.0.0/src/ya_tagscript/interpreter/ts_parser.py +364 -0
  109. ya_tagscript-1.0.0/src/ya_tagscript/py.typed +0 -0
  110. ya_tagscript-1.0.0/src/ya_tagscript/util/__init__.py +9 -0
  111. ya_tagscript-1.0.0/src/ya_tagscript/util/conditionals.py +83 -0
  112. ya_tagscript-1.0.0/src/ya_tagscript/util/escaping.py +13 -0
  113. ya_tagscript-1.0.0/src/ya_tagscript/util/splitter.py +40 -0
  114. ya_tagscript-1.0.0/tests/.coveragerc +10 -0
  115. ya_tagscript-1.0.0/tests/__init__.py +0 -0
  116. ya_tagscript-1.0.0/tests/ya_tagscript/__init__.py +0 -0
  117. ya_tagscript-1.0.0/tests/ya_tagscript/adapters/__init__.py +0 -0
  118. ya_tagscript-1.0.0/tests/ya_tagscript/adapters/discord_adapters/__init__.py +0 -0
  119. ya_tagscript-1.0.0/tests/ya_tagscript/adapters/discord_adapters/test_attribute_adapter.py +104 -0
  120. ya_tagscript-1.0.0/tests/ya_tagscript/adapters/discord_adapters/test_channel_adapter.py +131 -0
  121. ya_tagscript-1.0.0/tests/ya_tagscript/adapters/discord_adapters/test_guild_adapter.py +234 -0
  122. ya_tagscript-1.0.0/tests/ya_tagscript/adapters/discord_adapters/test_member_adapter.py +247 -0
  123. ya_tagscript-1.0.0/tests/ya_tagscript/adapters/test_function_adapter.py +47 -0
  124. ya_tagscript-1.0.0/tests/ya_tagscript/adapters/test_int_adapter.py +41 -0
  125. ya_tagscript-1.0.0/tests/ya_tagscript/adapters/test_object_adapter.py +103 -0
  126. ya_tagscript-1.0.0/tests/ya_tagscript/adapters/test_string_adapter.py +194 -0
  127. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/__init__.py +0 -0
  128. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/actions/__init__.py +0 -0
  129. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/actions/test_command_block.py +105 -0
  130. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/actions/test_delete_block.py +73 -0
  131. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/actions/test_override_block.py +100 -0
  132. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/actions/test_react_block.py +301 -0
  133. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/actions/test_redirect_block.py +104 -0
  134. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/actions/test_silence_block.py +25 -0
  135. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/conditional/__init__.py +0 -0
  136. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/conditional/test_all_block.py +228 -0
  137. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/conditional/test_any_block.py +234 -0
  138. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/conditional/test_if_block.py +297 -0
  139. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/conditional/test_python_block.py +174 -0
  140. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/discord/__init__.py +0 -0
  141. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/discord/test_cooldown_block.py +286 -0
  142. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/discord/test_embed_block.py +2129 -0
  143. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/flow/__init__.py +0 -0
  144. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/flow/test_break_block.py +105 -0
  145. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/flow/test_shortcutredirect_block.py +57 -0
  146. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/flow/test_stop_block.py +151 -0
  147. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/limiters/__init__.py +0 -0
  148. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/limiters/test_blacklist_block.py +156 -0
  149. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/limiters/test_require_block.py +156 -0
  150. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/lists/__init__.py +0 -0
  151. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/lists/test_cycle_block.py +152 -0
  152. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/lists/test_list_block.py +151 -0
  153. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/math/__init__.py +0 -0
  154. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/math/test_math_block.py +1350 -0
  155. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/math/test_ordinal_block.py +227 -0
  156. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/meta/__init__.py +0 -0
  157. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/meta/test_comment_block.py +60 -0
  158. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/meta/test_debug_block.py +304 -0
  159. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/rng/__init__.py +0 -0
  160. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/rng/test_fiftyfifty_block.py +70 -0
  161. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/rng/test_random_block.py +193 -0
  162. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/rng/test_range_block.py +336 -0
  163. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/strings/__init__.py +0 -0
  164. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/strings/test_case_block.py +121 -0
  165. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/strings/test_join_block.py +82 -0
  166. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/strings/test_replace_block.py +166 -0
  167. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/strings/test_substring_block.py +222 -0
  168. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/strings/test_urldecode_block.py +79 -0
  169. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/strings/test_urlencode_block.py +75 -0
  170. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/time/__init__.py +0 -0
  171. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/time/test_strf_block.py +236 -0
  172. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/time/test_timedelta_block.py +598 -0
  173. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/variables/__init__.py +0 -0
  174. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/variables/test_assign_block.py +103 -0
  175. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/variables/test_loose_variable_getter_block.py +99 -0
  176. ya_tagscript-1.0.0/tests/ya_tagscript/blocks/variables/test_strict_variable_getter_block.py +100 -0
  177. ya_tagscript-1.0.0/tests/ya_tagscript/interpreter/__init__.py +0 -0
  178. ya_tagscript-1.0.0/tests/ya_tagscript/interpreter/test_interpreter.py +212 -0
  179. ya_tagscript-1.0.0/tests/ya_tagscript/interpreter/test_node.py +72 -0
  180. ya_tagscript-1.0.0/tests/ya_tagscript/interpreter/test_parse_state.py +56 -0
  181. ya_tagscript-1.0.0/tests/ya_tagscript/interpreter/test_ts_parser.py +263 -0
  182. ya_tagscript-1.0.0/tests/ya_tagscript/util/__init__.py +0 -0
  183. ya_tagscript-1.0.0/tests/ya_tagscript/util/test_conditionals.py +213 -0
  184. ya_tagscript-1.0.0/tests/ya_tagscript/util/test_escaping.py +29 -0
  185. ya_tagscript-1.0.0/tests/ya_tagscript/util/test_splitter.py +46 -0
  186. ya_tagscript-1.0.0/uv.lock +1745 -0
@@ -0,0 +1,22 @@
1
+ name: Lint
2
+
3
+ on: [ push, pull_request ]
4
+
5
+ jobs:
6
+ lint:
7
+
8
+ runs-on: ubuntu-latest
9
+ strategy:
10
+ fail-fast: false
11
+ matrix:
12
+ python-version: [ "3.11", "3.12", "3.13" ]
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - name: Lint with black
18
+ uses: psf/black@stable
19
+ with:
20
+ options: '--check --diff'
21
+ src: '.'
22
+ version: '~= 25.0'
@@ -0,0 +1,121 @@
1
+ name: Publish Python 🐍 distribution 📦 to PyPI and TestPyPI
2
+
3
+ on: push
4
+
5
+ jobs:
6
+ build:
7
+ name: Build distribution 📦
8
+ runs-on: ubuntu-latest
9
+
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+
13
+ - name: Install uv
14
+ uses: astral-sh/setup-uv@v5
15
+
16
+ - name: Set up Python
17
+ uses: actions/setup-python@v5
18
+ with:
19
+ python-version-file: ".python-version"
20
+
21
+ - name: Install the project
22
+ run: uv sync --all-extras --dev
23
+
24
+ - name: Build a binary wheel and a source tarball
25
+ run: uv build
26
+
27
+ - name: Store the distribution packages
28
+ uses: actions/upload-artifact@v4
29
+ with:
30
+ name: python-package-distributions
31
+ path: dist/
32
+
33
+ publish-to-pypi:
34
+ name: Publish Python 🐍 distribution 📦 to PyPI
35
+ if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes
36
+ needs:
37
+ - build
38
+ runs-on: ubuntu-latest
39
+ environment:
40
+ name: pypi
41
+ url: https://pypi.org/p/ya_tagscript
42
+ permissions:
43
+ id-token: write
44
+
45
+ steps:
46
+ - name: Download all the dists
47
+ uses: actions/download-artifact@v4
48
+ with:
49
+ name: python-package-distributions
50
+ path: dist/
51
+
52
+ - name: Publish distribution 📦 to PyPI
53
+ uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc
54
+
55
+ github-release:
56
+ name: >-
57
+ Sign the Python 🐍 distribution 📦 with Sigstore
58
+ and upload them to GitHub Release
59
+ needs:
60
+ - publish-to-pypi
61
+ runs-on: ubuntu-latest
62
+
63
+ permissions:
64
+ contents: write
65
+ id-token: write
66
+
67
+ steps:
68
+ - name: Download all the dists
69
+ uses: actions/download-artifact@v4
70
+ with:
71
+ name: python-package-distributions
72
+ path: dist/
73
+
74
+ - name: Sign the dists with Sigstore
75
+ uses: sigstore/gh-action-sigstore-python@f514d46b907ebcd5bedc05145c03b69c1edd8b46
76
+ with:
77
+ inputs: >-
78
+ ./dist/*.tar.gz
79
+ ./dist/*.whl
80
+
81
+ - name: Create GitHub Release
82
+ env:
83
+ GITHUB_TOKEN: ${{ github.token }}
84
+ run: >-
85
+ gh release create
86
+ "$GITHUB_REF_NAME"
87
+ --repo "$GITHUB_REPOSITORY"
88
+ --notes ""
89
+
90
+ - name: Upload artifact signatures to GitHub Release
91
+ env:
92
+ GITHUB_TOKEN: ${{ github.token }}
93
+ run: >-
94
+ gh release upload
95
+ "$GITHUB_REF_NAME" dist/**
96
+ --repo "$GITHUB_REPOSITORY"
97
+
98
+ publish-to-testpypi:
99
+ name: Publish Python 🐍 distribution 📦 to TestPyPI
100
+ needs:
101
+ - build
102
+ runs-on: ubuntu-latest
103
+
104
+ environment:
105
+ name: testpypi
106
+ url: https://test.pypi.org/p/ya_tagscript
107
+
108
+ permissions:
109
+ id-token: write
110
+
111
+ steps:
112
+ - name: Download all the dists
113
+ uses: actions/download-artifact@v4
114
+ with:
115
+ name: python-package-distributions
116
+ path: dist/
117
+
118
+ - name: Publish distribution 📦 to TestPyPI
119
+ uses: pypa/gh-action-pypi-publish@release/v1
120
+ with:
121
+ repository-url: https://test.pypi.org/legacy/
@@ -0,0 +1,32 @@
1
+ name: Test
2
+
3
+ on: [ push, pull_request ]
4
+
5
+ jobs:
6
+ test:
7
+
8
+ runs-on: ubuntu-latest
9
+ strategy:
10
+ fail-fast: false
11
+ matrix:
12
+ python-version: [ "3.11", "3.12", "3.13" ]
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - name: Install uv
18
+ uses: astral-sh/setup-uv@v5
19
+ with:
20
+ enable-cache: true
21
+ cache-dependency-glob: "uv.lock"
22
+
23
+ - name: Set up Python ${{ matrix.python-version }}
24
+ uses: actions/setup-python@v5
25
+ with:
26
+ python-version: ${{ matrix.python-version }}
27
+
28
+ - name: Install dependencies
29
+ run: uv sync --all-extras --dev
30
+
31
+ - name: Run tests
32
+ run: uv run pytest tests
@@ -0,0 +1,27 @@
1
+ __pycache__/
2
+
3
+ # Distribution / packaging
4
+ dist/
5
+
6
+ # Unit test / coverage reports
7
+ htmlcov/
8
+ .coverage
9
+ .coverage.*
10
+ coverage.xml
11
+
12
+ # Sphinx documentation
13
+ docs/_build/
14
+ # Environments
15
+ .env
16
+ .venv
17
+ env/
18
+ venv/
19
+ ENV/
20
+ env.bak/
21
+ venv.bak/
22
+
23
+
24
+ .DS_Store
25
+
26
+ .idea/
27
+ .local/
@@ -0,0 +1,33 @@
1
+ default_language_version:
2
+ python: python3.11
3
+ fail_fast: false
4
+ repos:
5
+ - repo: https://github.com/pre-commit/pre-commit-hooks
6
+ rev: v5.0.0
7
+ hooks:
8
+ - id: check-toml
9
+ - id: check-yaml
10
+ - id: end-of-file-fixer
11
+ - id: trailing-whitespace
12
+ - id: check-builtin-literals
13
+ - id: check-ast
14
+ - id: check-json
15
+ - id: detect-private-key
16
+ - id: requirements-txt-fixer
17
+ - id: trailing-whitespace
18
+ args: [ --markdown-linebreak-ext=md ]
19
+
20
+ - repo: https://github.com/psf/black
21
+ rev: 25.1.0
22
+ hooks:
23
+ - id: black
24
+
25
+ - repo: https://github.com/pycqa/isort
26
+ rev: 6.0.1
27
+ hooks:
28
+ - id: isort
29
+
30
+ - repo: https://github.com/astral-sh/uv-pre-commit
31
+ rev: 0.7.3
32
+ hooks:
33
+ - id: uv-lock
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,14 @@
1
+ version: 2
2
+
3
+ build:
4
+ os: ubuntu-24.04
5
+ tools:
6
+ python: "3.13"
7
+
8
+ sphinx:
9
+ configuration: docs/conf.py
10
+ fail_on_warning: true
11
+
12
+ python:
13
+ install:
14
+ - requirements: docs/requirements.txt
@@ -0,0 +1 @@
1
+ This work is licensed under the Creative Commons Attribution 4.0 International License. To view a copy of this license, visit https://creativecommons.org/licenses/by/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
@@ -0,0 +1,104 @@
1
+ Metadata-Version: 2.4
2
+ Name: ya_tagscript
3
+ Version: 1.0.0
4
+ Summary: Yet Another TagScript fork. An easy drop in user-provided Templating system.
5
+ Project-URL: GitHub, https://github.com/MajorTanya/ya_tagscript
6
+ Project-URL: Issues, https://github.com/MajorTanya/ya_tagscript/issues
7
+ Author-email: MajorTanya <tanya@majortanya.eu>
8
+ License-Expression: CC-BY-4.0
9
+ License-File: LICENSE.md
10
+ Keywords: tagscript
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Requires-Python: <3.14,>=3.11
18
+ Requires-Dist: discord-py>=2.5.2
19
+ Requires-Dist: pyparsing>=3.2.3
20
+ Requires-Dist: python-dateutil>=2.9.0.post0
21
+ Description-Content-Type: text/markdown
22
+
23
+ # ya_tagscript - Yet Another TagScript fork
24
+
25
+ ## Information
26
+
27
+ This is a fork of PhenoM4n4n's [TagScript](https://github.com/phenom4n4n/TagScript),
28
+ which itself is a fork of JonSnowbd's
29
+ [TagScript](https://github.com/JonSnowbd/TagScript), a string templating language.
30
+
31
+ The main purpose of this fork is to align the Discord-specific blocks with the most
32
+ recent Discord and discord.py changes (new username system, etc.).
33
+
34
+ ## What?
35
+
36
+ TagScript is a drop-in, easy-to-use string interpreter that lets you provide users with
37
+ ways of customizing their profiles or chat rooms with interactive text.
38
+
39
+ For example TagScript comes out of the box with a random block that would let users
40
+ provide a template that produces a new result each time its ran, or assign math and
41
+ variables for later use.
42
+
43
+ ## Changes in v1
44
+
45
+ The newly released v1 includes a complete rewrite of the entire parsing and
46
+ interpretation performed by the TagScriptInterpreter class. This comes with some subtle
47
+ behavioural changes but great care was taken to keep v1 behaviourally similar to v0 and
48
+ PhenoM4n4n's fork. Most simple uses *should* behave the same but especially deeply
49
+ nested scripts or dynamically assembled ones will most likely require adjustments.
50
+ Some documentation examples were also outright wrong, so those were also adjusted to
51
+ reflect the actual behaviour of the pre-existing interpreter.
52
+
53
+ ### Oh, no! Behaviour changes?!
54
+
55
+ To remove the need for dynamically assembled blocks (e.g. command blocks that only
56
+ happen under some circumstances), v1 only performs interpretation of branches that were
57
+ actually hit (to the best of its ability).
58
+
59
+ Take this (very bad) password check script:
60
+
61
+ ```tagscript
62
+ {if({args}==123):{command:echo password accepted!}|something else}
63
+ ```
64
+
65
+ If `{args}` is not in fact equal to `123`, the interpreter WILL NOT interpret the
66
+ command block AT ALL. It jumps straight to the `else` section after the `|` and only
67
+ interprets that. This means that for any incorrect values of `{args}`, the `echo`
68
+ command is NOT included in the list of commands in `response.actions.get("commands")`.
69
+
70
+ Other blocks with some conditional executions (like `all` or `any`) do the same.
71
+
72
+ ## Installation
73
+
74
+ Download the latest version through pip:
75
+
76
+ ```
77
+ pip3 install ya_tagscript
78
+ ```
79
+
80
+ or
81
+
82
+ ```
83
+ pip3 install git+https://github.com/MajorTanya/ya_tagscript.git@v1.0.0.alpha03
84
+ ```
85
+
86
+ Download from a commit:
87
+
88
+ ```
89
+ pip3 install git+https://github.com/MajorTanya/ya_tagscript.git@<COMMIT_HASH>
90
+ ```
91
+
92
+ Install for editing/development:
93
+
94
+ ```
95
+ git clone https://github.com/MajorTanya/ya_tagscript.git
96
+ pip3 install -e ./TagScript
97
+ ```
98
+
99
+ ## Dependencies
100
+
101
+ - `Python>=3.11`
102
+ - `discord.py>=2.5.2`
103
+ - `pyparsing>=3.2.3`
104
+ - `python-dateutil>=2.9.0.post0`
@@ -0,0 +1,82 @@
1
+ # ya_tagscript - Yet Another TagScript fork
2
+
3
+ ## Information
4
+
5
+ This is a fork of PhenoM4n4n's [TagScript](https://github.com/phenom4n4n/TagScript),
6
+ which itself is a fork of JonSnowbd's
7
+ [TagScript](https://github.com/JonSnowbd/TagScript), a string templating language.
8
+
9
+ The main purpose of this fork is to align the Discord-specific blocks with the most
10
+ recent Discord and discord.py changes (new username system, etc.).
11
+
12
+ ## What?
13
+
14
+ TagScript is a drop-in, easy-to-use string interpreter that lets you provide users with
15
+ ways of customizing their profiles or chat rooms with interactive text.
16
+
17
+ For example TagScript comes out of the box with a random block that would let users
18
+ provide a template that produces a new result each time its ran, or assign math and
19
+ variables for later use.
20
+
21
+ ## Changes in v1
22
+
23
+ The newly released v1 includes a complete rewrite of the entire parsing and
24
+ interpretation performed by the TagScriptInterpreter class. This comes with some subtle
25
+ behavioural changes but great care was taken to keep v1 behaviourally similar to v0 and
26
+ PhenoM4n4n's fork. Most simple uses *should* behave the same but especially deeply
27
+ nested scripts or dynamically assembled ones will most likely require adjustments.
28
+ Some documentation examples were also outright wrong, so those were also adjusted to
29
+ reflect the actual behaviour of the pre-existing interpreter.
30
+
31
+ ### Oh, no! Behaviour changes?!
32
+
33
+ To remove the need for dynamically assembled blocks (e.g. command blocks that only
34
+ happen under some circumstances), v1 only performs interpretation of branches that were
35
+ actually hit (to the best of its ability).
36
+
37
+ Take this (very bad) password check script:
38
+
39
+ ```tagscript
40
+ {if({args}==123):{command:echo password accepted!}|something else}
41
+ ```
42
+
43
+ If `{args}` is not in fact equal to `123`, the interpreter WILL NOT interpret the
44
+ command block AT ALL. It jumps straight to the `else` section after the `|` and only
45
+ interprets that. This means that for any incorrect values of `{args}`, the `echo`
46
+ command is NOT included in the list of commands in `response.actions.get("commands")`.
47
+
48
+ Other blocks with some conditional executions (like `all` or `any`) do the same.
49
+
50
+ ## Installation
51
+
52
+ Download the latest version through pip:
53
+
54
+ ```
55
+ pip3 install ya_tagscript
56
+ ```
57
+
58
+ or
59
+
60
+ ```
61
+ pip3 install git+https://github.com/MajorTanya/ya_tagscript.git@v1.0.0.alpha03
62
+ ```
63
+
64
+ Download from a commit:
65
+
66
+ ```
67
+ pip3 install git+https://github.com/MajorTanya/ya_tagscript.git@<COMMIT_HASH>
68
+ ```
69
+
70
+ Install for editing/development:
71
+
72
+ ```
73
+ git clone https://github.com/MajorTanya/ya_tagscript.git
74
+ pip3 install -e ./TagScript
75
+ ```
76
+
77
+ ## Dependencies
78
+
79
+ - `Python>=3.11`
80
+ - `discord.py>=2.5.2`
81
+ - `pyparsing>=3.2.3`
82
+ - `python-dateutil>=2.9.0.post0`
@@ -0,0 +1,23 @@
1
+ # Minimal makefile for Sphinx documentation
2
+ #
3
+
4
+ # You can set these variables from the command line, and also
5
+ # from the environment for the first two.
6
+ SPHINXOPTS ?=
7
+ SPHINXBUILD ?= sphinx-build
8
+ SOURCEDIR = .
9
+ BUILDDIR = _build
10
+
11
+ # Put it first so that "make" without argument is like "make help".
12
+ help:
13
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14
+
15
+ .PHONY: help Makefile
16
+
17
+ livehtml:
18
+ sphinx-autobuild --watch ../src --re-ignore "\.py(?:c|c\.*)|__pycache__" "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
19
+
20
+ # Catch-all target: route all unknown targets to Sphinx using the new
21
+ # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
22
+ %: Makefile
23
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@@ -0,0 +1,173 @@
1
+ import dataclasses
2
+ from typing import Any
3
+
4
+ from docutils import nodes
5
+ from sphinx.addnodes import desc, desc_signature, pending_xref
6
+ from sphinx.application import Sphinx
7
+ from sphinx.util import logging as sphinx_logging
8
+
9
+ logger = sphinx_logging.getLogger(__name__)
10
+
11
+
12
+ def setup(app: Sphinx) -> dict[str, Any]:
13
+ collector = GlossaryRefChecker()
14
+ # Capture pending cross-refs before resolution
15
+ app.connect("doctree-read", collector.process_doc_read)
16
+ app.connect("doctree-resolved", collector.check_glossary_refs)
17
+ return {
18
+ "version": "1.0",
19
+ "parallel_read_safe": True,
20
+ "parallel_write_safe": True,
21
+ }
22
+
23
+
24
+ @dataclasses.dataclass
25
+ class MismatchedTermData:
26
+ found_undeclared: set[str]
27
+ declared_not_found: set[str]
28
+ ref_col_seen: bool
29
+
30
+
31
+ class GlossaryRefChecker:
32
+ """
33
+ A Sphinx extension to validate glossary term references and their back-references.
34
+
35
+ This extension ensures that all glossary terms referenced in the documentation
36
+ are properly declared and that their "Referenced by" sections are consistent.
37
+ It helps maintain the integrity of cross-references in the documentation,
38
+ improving its accuracy and reliability for users.
39
+
40
+ This validation approach allows for all references to be resolved fully before
41
+ checking their validity without a need to modify the documents in progress, which
42
+ would spawn further reference objects.
43
+ """
44
+
45
+ def process_doc_read(self, app: Sphinx, doctree: nodes.document) -> None:
46
+ docname = app.env.docname
47
+ if not hasattr(app.env, "glossary_term_refs"):
48
+ app.env.glossary_term_refs = {} # type: ignore
49
+
50
+ for node in doctree.findall(
51
+ lambda n: isinstance(n, (pending_xref, nodes.reference))
52
+ ):
53
+ if node.get("reftype") != "term":
54
+ continue
55
+
56
+ term = node.get("reftarget", "")
57
+ anchor = None
58
+
59
+ # Check if inside an autodoc class directive
60
+ parent = node
61
+ while parent and not (
62
+ isinstance(parent, desc) and parent.get("objtype") == "class"
63
+ ):
64
+ parent = parent.parent
65
+
66
+ if parent:
67
+ # It's inside a class doc
68
+ sig_node = parent.next_node(desc_signature)
69
+ if sig_node and sig_node.get("ids"):
70
+ anchor = sig_node["ids"][0]
71
+ else:
72
+ # fallback: use nearest parent with an ID (like a section)
73
+ parent = node
74
+ while parent:
75
+ if parent.get("ids"):
76
+ anchor = parent["ids"][0]
77
+ break
78
+ parent = parent.parent
79
+
80
+ if not anchor:
81
+ # fallback to synthetic anchor if no ID found (should be rare)
82
+ anchor = f"auto-ref-{hash(node.astext()) % 100000}"
83
+
84
+ logger.verbose(
85
+ f"[GlossaryRefCheck] Glossary term reference found: term=%r, doc=%r, anchor=%r",
86
+ term,
87
+ docname,
88
+ anchor,
89
+ )
90
+
91
+ app.env.glossary_term_refs.setdefault(term, set()).add((docname, anchor)) # type: ignore
92
+
93
+ def check_glossary_refs(self, app: Sphinx, doctree: nodes.document, docname: str):
94
+ if docname != "glossary":
95
+ return
96
+
97
+ mismatched_terms: dict[str, MismatchedTermData] = {}
98
+
99
+ for node in doctree.findall(nodes.definition_list_item):
100
+ term_definitions: set[str] = set()
101
+ for child in node.findall(nodes.term):
102
+ child_text = child.astext()
103
+ term_definitions.add(child_text)
104
+ mismatched_terms[child_text] = MismatchedTermData(
105
+ set(),
106
+ set(),
107
+ ref_col_seen=False,
108
+ )
109
+
110
+ refs_map: dict[str, set[tuple[str, str]]] = getattr(
111
+ app.env,
112
+ "glossary_term_refs",
113
+ {},
114
+ )
115
+
116
+ for ul in node.findall(nodes.bullet_list):
117
+ if "x-ref-col" not in ul.get("classes", []):
118
+ continue
119
+
120
+ listed_refs: set[str] = set()
121
+ for ul_li in ul.findall(nodes.list_item):
122
+ for para in ul_li.findall(nodes.paragraph):
123
+ for ref in para.findall(nodes.reference):
124
+ refuri = ref.attributes.get("refuri")
125
+ listed_refs.add(str(refuri).split("#")[-1])
126
+
127
+ combined_found_refs = set()
128
+ for t_def in term_definitions:
129
+ if (refs := refs_map.get(t_def)) is None:
130
+ continue
131
+ combined_found_refs.update({rm_entry[1] for rm_entry in refs})
132
+
133
+ found_but_undeclared = combined_found_refs.difference(listed_refs)
134
+ declared_but_not_found = listed_refs.difference(combined_found_refs)
135
+ for t_def in term_definitions:
136
+ mismatched_terms[t_def] = MismatchedTermData(
137
+ found_but_undeclared,
138
+ declared_but_not_found,
139
+ ref_col_seen=True,
140
+ )
141
+
142
+ if any(
143
+ (
144
+ not term_data.ref_col_seen
145
+ or len(term_data.found_undeclared) != 0
146
+ or len(term_data.declared_not_found) != 0
147
+ )
148
+ for term_data in mismatched_terms.values()
149
+ ):
150
+ problem_terms: dict[str, MismatchedTermData] = {}
151
+ for term, term_data in mismatched_terms.items():
152
+ if not term_data.ref_col_seen:
153
+ problem_terms[term] = term_data
154
+ logger.error(
155
+ "[GlossaryRefCheck] Could not find a 'Referenced by' "
156
+ "section with an 'x-ref-col' class for term %r",
157
+ term,
158
+ )
159
+ elif (
160
+ len(term_data.found_undeclared) != 0
161
+ or len(term_data.declared_not_found) != 0
162
+ ):
163
+ problem_terms[term] = term_data
164
+ logger.error(
165
+ "[GlossaryRefCheck] 'Referenced by' section mismatch for "
166
+ "term %r: %r",
167
+ term,
168
+ term_data,
169
+ )
170
+ else:
171
+ logger.info(
172
+ "[GlossaryRefCheck] All glossary term references back-referenced correctly!"
173
+ )
@@ -0,0 +1,33 @@
1
+ from docutils import nodes
2
+ from docutils.parsers.rst import Directive
3
+ from docutils.parsers.rst.directives import class_option
4
+
5
+
6
+ class StyledList(Directive):
7
+ has_content = True
8
+ option_spec = {
9
+ "class": class_option,
10
+ }
11
+
12
+ def run(self) -> list[nodes.Node]:
13
+ node = nodes.Element()
14
+ self.state.nested_parse(self.content, self.content_offset, node)
15
+
16
+ list_node = next(
17
+ (
18
+ child
19
+ for child in node
20
+ if isinstance(child, (nodes.bullet_list, nodes.enumerated_list))
21
+ ),
22
+ None,
23
+ )
24
+ if list_node is None:
25
+ error = self.state_machine.reporter.error(
26
+ "Expected a bullet list or enumerated list in 'styled-list' content.",
27
+ nodes.literal_block(self.block_text, self.block_text),
28
+ line=self.lineno,
29
+ )
30
+ return [error]
31
+
32
+ list_node["classes"] += self.options.get("class", [])
33
+ return [list_node]