aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--docs/_static/.$Structures.svg.dtmp4
-rw-r--r--docs/_static/Structures.svg2
-rw-r--r--examples/config.toml6
-rw-r--r--examples/rules/COC/__init__.py1
-rw-r--r--hrc/__init__.py14
-rw-r--r--hrc/config.py15
-rw-r--r--hrc/core.py135
-rw-r--r--hrc/dependencies.py11
-rw-r--r--hrc/dev/__init__.py2
-rw-r--r--hrc/dev/api/__init__.py0
-rw-r--r--hrc/dev/echo.py98
-rw-r--r--hrc/dev/grps/v1.py22
-rw-r--r--hrc/doc/__init__.py1
-rw-r--r--hrc/event.py16
-rw-r--r--hrc/log.py9
-rw-r--r--hrc/rule/__init__.py18
-rw-r--r--hrc/service/__init__.py111
-rw-r--r--hrc/service/console/__init__.py41
-rw-r--r--hrc/service/http/__init__.py33
-rw-r--r--hrc/service/utils.py256
-rw-r--r--hrc/service/websocket/__init__.py30
-rw-r--r--hrc/typing.py11
-rw-r--r--hrc/utils.py4
-rw-r--r--pdm.lock375
-rw-r--r--pyproject.toml2
-rw-r--r--tests/test_rule.py0
-rw-r--r--tests/test_service.py5
27 files changed, 1121 insertions, 101 deletions
diff --git a/docs/_static/.$Structures.svg.dtmp b/docs/_static/.$Structures.svg.dtmp
deleted file mode 100644
index acace65..0000000
--- a/docs/_static/.$Structures.svg.dtmp
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Do not edit this file with editors other than draw.io -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1px" height="1px" viewBox="-0.5 -0.5 1 1" content="&lt;mxfile host=&quot;Electron&quot; modified=&quot;2024-07-09T00:44:12.773Z&quot; agent=&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/24.6.1 Chrome/124.0.6367.207 Electron/30.0.6 Safari/537.36&quot; etag=&quot;tdeie8-FcdftL1S-8IuH&quot; version=&quot;24.6.1&quot; type=&quot;device&quot; pages=&quot;2&quot;&gt;&#10; &lt;diagram id=&quot;prtHgNgQTEPvFCAcTncT&quot; name=&quot;Page-1&quot;&gt;&#10; &lt;mxGraphModel dx=&quot;1036&quot; dy=&quot;635&quot; grid=&quot;1&quot; gridSize=&quot;10&quot; guides=&quot;1&quot; tooltips=&quot;1&quot; connect=&quot;1&quot; arrows=&quot;1&quot; fold=&quot;1&quot; page=&quot;1&quot; pageScale=&quot;1&quot; pageWidth=&quot;827&quot; pageHeight=&quot;1169&quot; math=&quot;0&quot; shadow=&quot;0&quot;&gt;&#10; &lt;root&gt;&#10; &lt;mxCell id=&quot;0&quot; /&gt;&#10; &lt;mxCell id=&quot;1&quot; parent=&quot;0&quot; /&gt;&#10; &lt;/root&gt;&#10; &lt;/mxGraphModel&gt;&#10; &lt;/diagram&gt;&#10; &lt;diagram name=&quot;Page-1 的副本&quot; id=&quot;QUBPRxTMLXw4AuhHwJvm&quot;&gt;&#10; &lt;mxGraphModel dx=&quot;840&quot; dy=&quot;519&quot; grid=&quot;1&quot; gridSize=&quot;10&quot; guides=&quot;1&quot; tooltips=&quot;1&quot; connect=&quot;1&quot; arrows=&quot;1&quot; fold=&quot;1&quot; page=&quot;1&quot; pageScale=&quot;1&quot; pageWidth=&quot;827&quot; pageHeight=&quot;1169&quot; math=&quot;0&quot; shadow=&quot;0&quot;&gt;&#10; &lt;root&gt;&#10; &lt;mxCell id=&quot;tazqR-jlQMPDV_MZ-rYC-0&quot; /&gt;&#10; &lt;mxCell id=&quot;tazqR-jlQMPDV_MZ-rYC-1&quot; parent=&quot;tazqR-jlQMPDV_MZ-rYC-0&quot; /&gt;&#10; &lt;mxCell id=&quot;VPFQTBCB0J-yRa51enEa-0&quot; value=&quot;&quot; style=&quot;rounded=0;whiteSpace=wrap;html=1;&quot; vertex=&quot;1&quot; parent=&quot;tazqR-jlQMPDV_MZ-rYC-1&quot;&gt;&#10; &lt;mxGeometry x=&quot;60&quot; y=&quot;560&quot; width=&quot;650&quot; height=&quot;400&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;VPFQTBCB0J-yRa51enEa-3&quot; value=&quot;&quot; style=&quot;shape=internalStorage;whiteSpace=wrap;html=1;backgroundOutline=1;&quot; vertex=&quot;1&quot; parent=&quot;tazqR-jlQMPDV_MZ-rYC-1&quot;&gt;&#10; &lt;mxGeometry x=&quot;80&quot; y=&quot;580&quot; width=&quot;80&quot; height=&quot;80&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;/root&gt;&#10; &lt;/mxGraphModel&gt;&#10; &lt;/diagram&gt;&#10;&lt;/mxfile&gt;&#10;"><defs/><g><g data-cell-id="0"><g data-cell-id="1"/></g></g></svg> \ No newline at end of file
diff --git a/docs/_static/Structures.svg b/docs/_static/Structures.svg
index b42566b..1a75916 100644
--- a/docs/_static/Structures.svg
+++ b/docs/_static/Structures.svg
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Do not edit this file with editors other than draw.io -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="711px" height="291px" viewBox="-0.5 -0.5 711 291" content="&lt;mxfile host=&quot;Electron&quot; modified=&quot;2024-06-26T06:58:36.732Z&quot; agent=&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/24.6.1 Chrome/124.0.6367.207 Electron/30.0.6 Safari/537.36&quot; version=&quot;24.6.1&quot; etag=&quot;jsh3je75ImQiPwudZv7c&quot; type=&quot;device&quot; pages=&quot;2&quot;&gt;&#10; &lt;diagram id=&quot;prtHgNgQTEPvFCAcTncT&quot; name=&quot;Page-1&quot;&gt;&#10; &lt;mxGraphModel dx=&quot;1877&quot; dy=&quot;649&quot; grid=&quot;1&quot; gridSize=&quot;10&quot; guides=&quot;1&quot; tooltips=&quot;1&quot; connect=&quot;1&quot; arrows=&quot;1&quot; fold=&quot;1&quot; page=&quot;1&quot; pageScale=&quot;1&quot; pageWidth=&quot;827&quot; pageHeight=&quot;1169&quot; math=&quot;0&quot; shadow=&quot;0&quot;&gt;&#10; &lt;root&gt;&#10; &lt;mxCell id=&quot;0&quot; /&gt;&#10; &lt;mxCell id=&quot;1&quot; parent=&quot;0&quot; /&gt;&#10; &lt;mxCell id=&quot;dNxyNK7c78bLwvsdeMH5-11&quot; value=&quot;Orgchart&quot; style=&quot;swimlane;html=1;startSize=20;horizontal=1;containerType=tree;glass=0;&quot; parent=&quot;1&quot; vertex=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;-40&quot; y=&quot;150&quot; width=&quot;710&quot; height=&quot;290&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;UserObject label=&quot;%name%&amp;lt;br&amp;gt;&amp;lt;i style=&amp;quot;color: gray&amp;quot;&amp;gt;%position%&amp;lt;/i&amp;gt;&amp;lt;br&amp;gt;&amp;lt;a href=&amp;quot;mailto:%email%&amp;quot;&amp;gt;Email&amp;lt;/a&amp;gt;&quot; name=&quot;Tessa Miller&quot; position=&quot;CFO&quot; location=&quot;Office 1&quot; email=&quot;me@example.com&quot; placeholders=&quot;1&quot; link=&quot;https://www.draw.io&quot; id=&quot;dNxyNK7c78bLwvsdeMH5-12&quot;&gt;&#10; &lt;mxCell style=&quot;label;image=https://cdn3.iconfinder.com/data/icons/user-avatars-1/512/users-3-128.png;whiteSpace=wrap;html=1;rounded=0;glass=0;treeMoving=1;treeFolding=1;&quot; parent=&quot;dNxyNK7c78bLwvsdeMH5-11&quot; vertex=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;260&quot; y=&quot;50&quot; width=&quot;180&quot; height=&quot;70&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;/UserObject&gt;&#10; &lt;mxCell id=&quot;dNxyNK7c78bLwvsdeMH5-13&quot; value=&quot;&quot; style=&quot;endArrow=blockThin;endFill=1;fontSize=11;edgeStyle=elbowEdgeStyle;elbow=vertical;rounded=0;&quot; parent=&quot;dNxyNK7c78bLwvsdeMH5-11&quot; source=&quot;dNxyNK7c78bLwvsdeMH5-12&quot; target=&quot;dNxyNK7c78bLwvsdeMH5-14&quot; edge=&quot;1&quot;&gt;&#10; &lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;UserObject label=&quot;%name%&amp;lt;br&amp;gt;&amp;lt;i style=&amp;quot;color: gray&amp;quot;&amp;gt;%position%&amp;lt;/i&amp;gt;&amp;lt;br&amp;gt;&amp;lt;a href=&amp;quot;mailto:%email%&amp;quot;&amp;gt;Email&amp;lt;/a&amp;gt;&quot; name=&quot;Edward Morrison&quot; position=&quot;Brand Manager&quot; location=&quot;Office 2&quot; email=&quot;me@example.com&quot; placeholders=&quot;1&quot; link=&quot;https://www.draw.io&quot; id=&quot;dNxyNK7c78bLwvsdeMH5-14&quot;&gt;&#10; &lt;mxCell style=&quot;label;image=https://cdn3.iconfinder.com/data/icons/user-avatars-1/512/users-10-3-128.png;whiteSpace=wrap;html=1;rounded=0;glass=0;treeFolding=1;treeMoving=1;&quot; parent=&quot;dNxyNK7c78bLwvsdeMH5-11&quot; vertex=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;40&quot; y=&quot;180&quot; width=&quot;180&quot; height=&quot;80&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;/UserObject&gt;&#10; &lt;mxCell id=&quot;dNxyNK7c78bLwvsdeMH5-15&quot; value=&quot;&quot; style=&quot;endArrow=blockThin;endFill=1;fontSize=11;edgeStyle=elbowEdgeStyle;elbow=vertical;rounded=0;&quot; parent=&quot;dNxyNK7c78bLwvsdeMH5-11&quot; source=&quot;dNxyNK7c78bLwvsdeMH5-12&quot; target=&quot;dNxyNK7c78bLwvsdeMH5-16&quot; edge=&quot;1&quot;&gt;&#10; &lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;UserObject label=&quot;%name%&amp;lt;br&amp;gt;&amp;lt;i style=&amp;quot;color: gray&amp;quot;&amp;gt;%position%&amp;lt;/i&amp;gt;&amp;lt;br&amp;gt;&amp;lt;a href=&amp;quot;mailto:%email%&amp;quot;&amp;gt;Email&amp;lt;/a&amp;gt;&quot; name=&quot;Evan Valet&quot; position=&quot;HR Director&quot; location=&quot;Office 4&quot; email=&quot;me@example.com&quot; placeholders=&quot;1&quot; link=&quot;https://www.draw.io&quot; id=&quot;dNxyNK7c78bLwvsdeMH5-16&quot;&gt;&#10; &lt;mxCell style=&quot;label;image=https://cdn3.iconfinder.com/data/icons/user-avatars-1/512/users-9-2-128.png;whiteSpace=wrap;html=1;rounded=0;glass=0;treeFolding=1;treeMoving=1;&quot; parent=&quot;dNxyNK7c78bLwvsdeMH5-11&quot; vertex=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;260&quot; y=&quot;180&quot; width=&quot;180&quot; height=&quot;80&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;/UserObject&gt;&#10; &lt;mxCell id=&quot;dNxyNK7c78bLwvsdeMH5-17&quot; value=&quot;&quot; style=&quot;endArrow=blockThin;endFill=1;fontSize=11;edgeStyle=elbowEdgeStyle;elbow=vertical;rounded=0;&quot; parent=&quot;dNxyNK7c78bLwvsdeMH5-11&quot; source=&quot;dNxyNK7c78bLwvsdeMH5-12&quot; target=&quot;dNxyNK7c78bLwvsdeMH5-18&quot; edge=&quot;1&quot;&gt;&#10; &lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;UserObject label=&quot;%name%&amp;lt;br&amp;gt;&amp;lt;i style=&amp;quot;color: gray&amp;quot;&amp;gt;%position%&amp;lt;/i&amp;gt;&amp;lt;br&amp;gt;&amp;lt;a href=&amp;quot;mailto:%email%&amp;quot;&amp;gt;Email&amp;lt;/a&amp;gt;&quot; name=&quot;Alison Donovan&quot; position=&quot;System Admin&quot; location=&quot;Office 3&quot; email=&quot;me@example.com&quot; placeholders=&quot;1&quot; link=&quot;https://www.draw.io&quot; id=&quot;dNxyNK7c78bLwvsdeMH5-18&quot;&gt;&#10; &lt;mxCell style=&quot;label;image=https://cdn3.iconfinder.com/data/icons/user-avatars-1/512/users-2-128.png;whiteSpace=wrap;html=1;rounded=0;glass=0;&quot; parent=&quot;dNxyNK7c78bLwvsdeMH5-11&quot; vertex=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;490&quot; y=&quot;180&quot; width=&quot;180&quot; height=&quot;80&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;/UserObject&gt;&#10; &lt;/root&gt;&#10; &lt;/mxGraphModel&gt;&#10; &lt;/diagram&gt;&#10; &lt;diagram name=&quot;Page-1 的副本&quot; id=&quot;QUBPRxTMLXw4AuhHwJvm&quot;&gt;&#10; &lt;mxGraphModel dx=&quot;840&quot; dy=&quot;519&quot; grid=&quot;1&quot; gridSize=&quot;10&quot; guides=&quot;1&quot; tooltips=&quot;1&quot; connect=&quot;1&quot; arrows=&quot;1&quot; fold=&quot;1&quot; page=&quot;1&quot; pageScale=&quot;1&quot; pageWidth=&quot;827&quot; pageHeight=&quot;1169&quot; math=&quot;0&quot; shadow=&quot;0&quot;&gt;&#10; &lt;root&gt;&#10; &lt;mxCell id=&quot;tazqR-jlQMPDV_MZ-rYC-0&quot; /&gt;&#10; &lt;mxCell id=&quot;tazqR-jlQMPDV_MZ-rYC-1&quot; parent=&quot;tazqR-jlQMPDV_MZ-rYC-0&quot; /&gt;&#10; &lt;mxCell id=&quot;VPFQTBCB0J-yRa51enEa-0&quot; value=&quot;&quot; style=&quot;rounded=0;whiteSpace=wrap;html=1;&quot; vertex=&quot;1&quot; parent=&quot;tazqR-jlQMPDV_MZ-rYC-1&quot;&gt;&#10; &lt;mxGeometry x=&quot;60&quot; y=&quot;560&quot; width=&quot;650&quot; height=&quot;400&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;VPFQTBCB0J-yRa51enEa-3&quot; value=&quot;&quot; style=&quot;shape=internalStorage;whiteSpace=wrap;html=1;backgroundOutline=1;&quot; vertex=&quot;1&quot; parent=&quot;tazqR-jlQMPDV_MZ-rYC-1&quot;&gt;&#10; &lt;mxGeometry x=&quot;80&quot; y=&quot;580&quot; width=&quot;80&quot; height=&quot;80&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;/root&gt;&#10; &lt;/mxGraphModel&gt;&#10; &lt;/diagram&gt;&#10;&lt;/mxfile&gt;&#10;"><defs/><g><g data-cell-id="0"><g data-cell-id="1"><g data-cell-id="dNxyNK7c78bLwvsdeMH5-11"><g><path d="M 0 20 L 0 0 L 710 0 L 710 20" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/><path d="M 0 20 L 0 290 L 710 290 L 710 20" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="none"/><path d="M 0 20 L 710 20" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="none"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 10px; margin-left: 355px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;">Orgchart</div></div></div></foreignObject><text x="355" y="14" fill="rgb(240, 240, 240)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle" font-weight="bold">Orgchart</text></switch></g></g><g data-cell-id="dNxyNK7c78bLwvsdeMH5-12"><a xlink:href="https://www.draw.io"><g><rect x="260" y="50" width="180" height="70" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" pointer-events="all"/><image x="266.5" y="63.5" width="42" height="42" xlink:href="https://cdn3.iconfinder.com/data/icons/user-avatars-1/512/users-3-128.png" preserveAspectRatio="none"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 126px; height: 1px; padding-top: 85px; margin-left: 314px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;">Tessa Miller<br /><i style="color: gray">CFO</i><br /><a href="mailto:me@example.com">Email</a></div></div></div></foreignObject><text x="314" y="89" fill="rgb(240, 240, 240)" font-family="&quot;Helvetica&quot;" font-size="12px" font-weight="bold">Tessa Miller...</text></switch></g></g></a></g><g data-cell-id="dNxyNK7c78bLwvsdeMH5-13"><g><path d="M 350 120 L 350 150 L 130 150 L 130 171.88" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 130 178.88 L 127.67 171.88 L 132.33 171.88 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/></g></g><g data-cell-id="dNxyNK7c78bLwvsdeMH5-14"><a xlink:href="https://www.draw.io"><g><rect x="40" y="180" width="180" height="80" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" pointer-events="all"/><image x="46.5" y="198.5" width="42" height="42" xlink:href="https://cdn3.iconfinder.com/data/icons/user-avatars-1/512/users-10-3-128.png" preserveAspectRatio="none"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 126px; height: 1px; padding-top: 220px; margin-left: 94px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;">Edward Morrison<br /><i style="color: gray">Brand Manager</i><br /><a href="mailto:me@example.com">Email</a></div></div></div></foreignObject><text x="94" y="224" fill="rgb(240, 240, 240)" font-family="&quot;Helvetica&quot;" font-size="12px" font-weight="bold">Edward Morrison...</text></switch></g></g></a></g><g data-cell-id="dNxyNK7c78bLwvsdeMH5-15"><g><path d="M 350 120 L 350 150 L 350 171.88" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 350 178.88 L 347.67 171.88 L 352.33 171.88 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/></g></g><g data-cell-id="dNxyNK7c78bLwvsdeMH5-16"><a xlink:href="https://www.draw.io"><g><rect x="260" y="180" width="180" height="80" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" pointer-events="all"/><image x="266.5" y="198.5" width="42" height="42" xlink:href="https://cdn3.iconfinder.com/data/icons/user-avatars-1/512/users-9-2-128.png" preserveAspectRatio="none"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 126px; height: 1px; padding-top: 220px; margin-left: 314px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;">Evan Valet<br /><i style="color: gray">HR Director</i><br /><a href="mailto:me@example.com">Email</a></div></div></div></foreignObject><text x="314" y="224" fill="rgb(240, 240, 240)" font-family="&quot;Helvetica&quot;" font-size="12px" font-weight="bold">Evan Valet...</text></switch></g></g></a></g><g data-cell-id="dNxyNK7c78bLwvsdeMH5-17"><g><path d="M 350 120 L 350 150 L 580 150 L 580 171.88" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 580 178.88 L 577.67 171.88 L 582.33 171.88 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/></g></g><g data-cell-id="dNxyNK7c78bLwvsdeMH5-18"><a xlink:href="https://www.draw.io"><g><rect x="490" y="180" width="180" height="80" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" pointer-events="all"/><image x="496.5" y="198.5" width="42" height="42" xlink:href="https://cdn3.iconfinder.com/data/icons/user-avatars-1/512/users-2-128.png" preserveAspectRatio="none"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 126px; height: 1px; padding-top: 220px; margin-left: 544px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;">Alison Donovan<br /><i style="color: gray">System Admin</i><br /><a href="mailto:me@example.com">Email</a></div></div></div></foreignObject><text x="544" y="224" fill="rgb(240, 240, 240)" font-family="&quot;Helvetica&quot;" font-size="12px" font-weight="bold">Alison Donovan...</text></switch></g></g></a></g></g></g></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1651px" height="1161px" viewBox="-0.5 -0.5 1651 1161" content="&lt;mxfile host=&quot;Electron&quot; modified=&quot;2024-08-25T04:25:39.444Z&quot; agent=&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/24.6.1 Chrome/124.0.6367.207 Electron/30.0.6 Safari/537.36&quot; etag=&quot;VttEO-K9n2nUjwSbsrCF&quot; version=&quot;24.6.1&quot; type=&quot;device&quot; pages=&quot;2&quot;&gt;&#10; &lt;diagram id=&quot;prtHgNgQTEPvFCAcTncT&quot; name=&quot;Page-1&quot;&gt;&#10; &lt;mxGraphModel dx=&quot;1727&quot; dy=&quot;1058&quot; grid=&quot;0&quot; gridSize=&quot;10&quot; guides=&quot;1&quot; tooltips=&quot;1&quot; connect=&quot;1&quot; arrows=&quot;1&quot; fold=&quot;1&quot; page=&quot;1&quot; pageScale=&quot;1&quot; pageWidth=&quot;827&quot; pageHeight=&quot;1169&quot; math=&quot;0&quot; shadow=&quot;0&quot;&gt;&#10; &lt;root&gt;&#10; &lt;mxCell id=&quot;0&quot; /&gt;&#10; &lt;mxCell id=&quot;1&quot; parent=&quot;0&quot; /&gt;&#10; &lt;mxCell id=&quot;MEqyB7D-bddKutFUnnWX-2&quot; value=&quot;水系核心流程图&quot; style=&quot;swimlane;horizontal=0;whiteSpace=wrap;html=1;aspect=fixed;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;4&quot; y=&quot;4&quot; width=&quot;1650&quot; height=&quot;1160&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;MEqyB7D-bddKutFUnnWX-3&quot; value=&quot;开发&quot; style=&quot;shape=umlFrame;whiteSpace=wrap;html=1;pointerEvents=0;aspect=fixed;&quot; vertex=&quot;1&quot; parent=&quot;MEqyB7D-bddKutFUnnWX-2&quot;&gt;&#10; &lt;mxGeometry x=&quot;40&quot; y=&quot;20&quot; width=&quot;1590&quot; height=&quot;520&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;MEqyB7D-bddKutFUnnWX-4&quot; value=&quot;应用&quot; style=&quot;shape=umlFrame;whiteSpace=wrap;html=1;pointerEvents=0;aspect=fixed;&quot; vertex=&quot;1&quot; parent=&quot;MEqyB7D-bddKutFUnnWX-2&quot;&gt;&#10; &lt;mxGeometry x=&quot;40&quot; y=&quot;570&quot; width=&quot;1590&quot; height=&quot;560&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;MEqyB7D-bddKutFUnnWX-11&quot; value=&quot;事件&quot; style=&quot;ellipse;html=1;shape=endState;fillColor=#000000;strokeColor=#ff0000;aspect=fixed;&quot; vertex=&quot;1&quot; parent=&quot;MEqyB7D-bddKutFUnnWX-2&quot;&gt;&#10; &lt;mxGeometry x=&quot;1520&quot; y=&quot;430&quot; width=&quot;90&quot; height=&quot;90&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;MEqyB7D-bddKutFUnnWX-12&quot; value=&quot;文档&quot; style=&quot;ellipse;html=1;shape=endState;fillColor=#000000;strokeColor=#ff0000;aspect=fixed;&quot; vertex=&quot;1&quot; parent=&quot;MEqyB7D-bddKutFUnnWX-2&quot;&gt;&#10; &lt;mxGeometry x=&quot;1526&quot; y=&quot;1026&quot; width=&quot;90&quot; height=&quot;90&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;MEqyB7D-bddKutFUnnWX-13&quot; value=&quot;事件&quot; style=&quot;ellipse;html=1;shape=startState;fillColor=#000000;strokeColor=#ff0000;aspect=fixed;&quot; vertex=&quot;1&quot; parent=&quot;MEqyB7D-bddKutFUnnWX-2&quot;&gt;&#10; &lt;mxGeometry x=&quot;66&quot; y=&quot;86&quot; width=&quot;90&quot; height=&quot;90&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;MEqyB7D-bddKutFUnnWX-17&quot; value=&quot;规则包&quot; style=&quot;ellipse;html=1;shape=startState;fillColor=#000000;strokeColor=#ff0000;aspect=fixed;&quot; vertex=&quot;1&quot; parent=&quot;MEqyB7D-bddKutFUnnWX-2&quot;&gt;&#10; &lt;mxGeometry x=&quot;66&quot; y=&quot;636&quot; width=&quot;90&quot; height=&quot;90&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;MEqyB7D-bddKutFUnnWX-19&quot; value=&quot;Core&quot; style=&quot;strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;&quot; vertex=&quot;1&quot; parent=&quot;MEqyB7D-bddKutFUnnWX-2&quot;&gt;&#10; &lt;mxGeometry x=&quot;772&quot; y=&quot;531&quot; width=&quot;100&quot; height=&quot;100&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;/root&gt;&#10; &lt;/mxGraphModel&gt;&#10; &lt;/diagram&gt;&#10; &lt;diagram name=&quot;Page-1 的副本&quot; id=&quot;QUBPRxTMLXw4AuhHwJvm&quot;&gt;&#10; &lt;mxGraphModel dx=&quot;840&quot; dy=&quot;519&quot; grid=&quot;1&quot; gridSize=&quot;10&quot; guides=&quot;1&quot; tooltips=&quot;1&quot; connect=&quot;1&quot; arrows=&quot;1&quot; fold=&quot;1&quot; page=&quot;1&quot; pageScale=&quot;1&quot; pageWidth=&quot;827&quot; pageHeight=&quot;1169&quot; math=&quot;0&quot; shadow=&quot;0&quot;&gt;&#10; &lt;root&gt;&#10; &lt;mxCell id=&quot;tazqR-jlQMPDV_MZ-rYC-0&quot; /&gt;&#10; &lt;mxCell id=&quot;tazqR-jlQMPDV_MZ-rYC-1&quot; parent=&quot;tazqR-jlQMPDV_MZ-rYC-0&quot; /&gt;&#10; &lt;mxCell id=&quot;VPFQTBCB0J-yRa51enEa-0&quot; value=&quot;&quot; style=&quot;rounded=0;whiteSpace=wrap;html=1;&quot; vertex=&quot;1&quot; parent=&quot;tazqR-jlQMPDV_MZ-rYC-1&quot;&gt;&#10; &lt;mxGeometry x=&quot;60&quot; y=&quot;560&quot; width=&quot;650&quot; height=&quot;400&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;VPFQTBCB0J-yRa51enEa-3&quot; value=&quot;&quot; style=&quot;shape=internalStorage;whiteSpace=wrap;html=1;backgroundOutline=1;&quot; vertex=&quot;1&quot; parent=&quot;tazqR-jlQMPDV_MZ-rYC-1&quot;&gt;&#10; &lt;mxGeometry x=&quot;80&quot; y=&quot;580&quot; width=&quot;80&quot; height=&quot;80&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;/root&gt;&#10; &lt;/mxGraphModel&gt;&#10; &lt;/diagram&gt;&#10;&lt;/mxfile&gt;&#10;"><defs/><g><g data-cell-id="0"><g data-cell-id="1"><g data-cell-id="MEqyB7D-bddKutFUnnWX-2"><g id="cell-MEqyB7D-bddKutFUnnWX-2"><g><path d="M 23 0 L 0 0 L 0 1160 L 23 1160" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/><path d="M 23 0 L 1650 0 L 1650 1160 L 23 1160" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="none"/><path d="M 23 0 L 23 1160" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="none"/></g><g><g transform="translate(-0.5 -0.5)rotate(-90 11.5 580)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1158px; height: 1px; padding-top: 580px; margin-left: -567px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;">水系核心流程图</div></div></div></foreignObject><text x="12" y="584" fill="rgb(240, 240, 240)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle" font-weight="bold">水系核心流程图</text></switch></g></g></g><g data-cell-id="MEqyB7D-bddKutFUnnWX-3"><g id="cell-MEqyB7D-bddKutFUnnWX-3"><g><path d="M 40 20 L 100 20 L 100 35 L 90 50 L 40 50 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/><path d="M 100 20 L 1630 20 L 1630 540 L 40 540 L 40 50" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="none"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 35px; margin-left: 41px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">开发</div></div></div></foreignObject><text x="70" y="39" fill="rgb(240, 240, 240)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">开发</text></switch></g></g></g></g><g data-cell-id="MEqyB7D-bddKutFUnnWX-4"><g id="cell-MEqyB7D-bddKutFUnnWX-4"><g><path d="M 40 570 L 100 570 L 100 585 L 90 600 L 40 600 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/><path d="M 100 570 L 1630 570 L 1630 1130 L 40 1130 L 40 600" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="none"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 585px; margin-left: 41px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">应用</div></div></div></foreignObject><text x="70" y="589" fill="rgb(240, 240, 240)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">应用</text></switch></g></g></g></g><g data-cell-id="MEqyB7D-bddKutFUnnWX-11"><g id="cell-MEqyB7D-bddKutFUnnWX-11"><g><ellipse cx="1565" cy="475" rx="41" ry="41" fill="#000000" stroke="#ff0000" pointer-events="all"/><ellipse cx="1565" cy="475" rx="45" ry="45" fill="none" stroke="#ff0000" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 475px; margin-left: 1565px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: nowrap;">事件</div></div></div></foreignObject><text x="1565" y="479" fill="rgb(240, 240, 240)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">事件</text></switch></g></g></g></g><g data-cell-id="MEqyB7D-bddKutFUnnWX-12"><g id="cell-MEqyB7D-bddKutFUnnWX-12"><g><ellipse cx="1571" cy="1071" rx="41" ry="41" fill="#000000" stroke="#ff0000" pointer-events="all"/><ellipse cx="1571" cy="1071" rx="45" ry="45" fill="none" stroke="#ff0000" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 1071px; margin-left: 1571px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: nowrap;">文档</div></div></div></foreignObject><text x="1571" y="1075" fill="rgb(240, 240, 240)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">文档</text></switch></g></g></g></g><g data-cell-id="MEqyB7D-bddKutFUnnWX-13"><g id="cell-MEqyB7D-bddKutFUnnWX-13"><g><ellipse cx="111" cy="131" rx="41" ry="41" fill="#000000" stroke="#ff0000" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 131px; margin-left: 111px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: nowrap;">事件</div></div></div></foreignObject><text x="111" y="135" fill="rgb(240, 240, 240)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">事件</text></switch></g></g></g></g><g data-cell-id="MEqyB7D-bddKutFUnnWX-17"><g id="cell-MEqyB7D-bddKutFUnnWX-17"><g><ellipse cx="111" cy="681" rx="41" ry="41" fill="#000000" stroke="#ff0000" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 681px; margin-left: 111px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: nowrap;">规则包</div></div></div></foreignObject><text x="111" y="685" fill="rgb(240, 240, 240)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">规则包</text></switch></g></g></g></g><g data-cell-id="MEqyB7D-bddKutFUnnWX-19"><g id="cell-MEqyB7D-bddKutFUnnWX-19"><g><path d="M 822 531 L 872 581 L 822 631 L 772 581 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 581px; margin-left: 773px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">Core</div></div></div></foreignObject><text x="822" y="585" fill="rgb(240, 240, 240)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">Core</text></switch></g></g></g></g></g></g></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg> \ No newline at end of file
diff --git a/examples/config.toml b/examples/config.toml
index 4e5e2fe..40eafd3 100644
--- a/examples/config.toml
+++ b/examples/config.toml
@@ -1,2 +1,6 @@
[core]
-rule_dirs = ["rules"] \ No newline at end of file
+rule_dirs = ["rules"]
+services = ['hrc.service.http']
+
+[service.http]
+port = 8080 \ No newline at end of file
diff --git a/examples/rules/COC/__init__.py b/examples/rules/COC/__init__.py
index 260bfb5..215d662 100644
--- a/examples/rules/COC/__init__.py
+++ b/examples/rules/COC/__init__.py
@@ -10,7 +10,6 @@ from .Command import Command
core = Core()
-
class COC7(Rule):
# 规则、指令、词条,必须至少实现任意一个
diff --git a/hrc/__init__.py b/hrc/__init__.py
index b607349..89bc832 100644
--- a/hrc/__init__.py
+++ b/hrc/__init__.py
@@ -1,13 +1 @@
-from .LibCore import * # noqa: F403
-
-from . import rule # noqa: F401
-from . import core # noqa: F401
-from . import log # noqa: F401
-from . import exceptions # noqa: F401
-from . import config # noqa: F401
-from . import dependencies # noqa: F401
-from . import event # noqa: F401
-from . import perf # noqa: F401
-from . import feat # noqa: F401
-from . import doc # noqa: F401
-from . import dev # noqa: F401
+from .LibCore import * # noqa: F403 \ No newline at end of file
diff --git a/hrc/config.py b/hrc/config.py
index b6458b2..d179258 100644
--- a/hrc/config.py
+++ b/hrc/config.py
@@ -1,4 +1,4 @@
-from typing import Set, Union
+from typing import Literal, Optional, Set, Union
from pydantic import BaseModel, ConfigDict, DirectoryPath, Field
@@ -14,16 +14,21 @@ class LogConfig(ConfigModel):
verbose_exception: bool = False
+class ServiceConfig(ConfigModel):
+ """Service configuration."""
+
+
class CoreConfig(ConfigModel):
rules: Set[str] = Field(default_factory=set)
rule_dirs: Set[DirectoryPath] = Field(default_factory=set)
log: LogConfig = LogConfig()
-
+ services: Set[str] = Field(default_factory=set)
class RuleConfig(ConfigModel):
- """rule configuration."""
-
-
+ """Rule configuration."""
+
+
class MainConfig(ConfigModel):
core: CoreConfig = CoreConfig()
rule: RuleConfig = RuleConfig()
+ service: ServiceConfig = ServiceConfig()
diff --git a/hrc/core.py b/hrc/core.py
index 86f8927..66ad211 100644
--- a/hrc/core.py
+++ b/hrc/core.py
@@ -25,21 +25,21 @@ from typing import (
from pydantic import ValidationError, create_model
-from .config import ConfigModel, MainConfig, RuleConfig
-from .dependencies import solve_dependencies
-from .log import logger
-from .rule import Rule, RuleLoadType
-from .event import Event
-from .typing import CoreHook, EventHook, EventT, RuleHook # noqa: F401
-from .utils import (
+from hrc.config import ConfigModel, MainConfig, RuleConfig, ServiceConfig
+from hrc.dependencies import solve_dependencies
+from hrc.log import logger, error_or_exception
+from hrc.rule import Rule, RuleLoadType
+from hrc.event import Event
+from hrc.typing import CoreHook, EventHook, EventT, RuleHook, ServiceT, ServiceHook # noqa: F401
+from hrc.utils import (
ModulePathFinder,
get_classes_from_module_name,
is_config_class,
samefile,
wrap_get_func, # noqa: F401
)
-from .exceptions import StopException, SkipException, GetEventTimeout, LoadModuleError # noqa: F401
-
+from hrc.exceptions import StopException, SkipException, GetEventTimeout, LoadModuleError # noqa: F401
+from hrc.service import Service
if sys.version_info >= (3, 11): # pragma: no cover
import tomllib
@@ -51,7 +51,6 @@ HANDLED_SIGNALS = (
signal.SIGTERM, # Unix signal 15. Sent by `kill <pid>`.
)
-
class Core:
config: MainConfig
_current_event: Optional[Event[Any]]
@@ -60,21 +59,35 @@ class Core:
# pyright: ignore[reportUninitializedInstanceVariable]
should_exit: asyncio.Event
_restart_flag: bool # Restart flag
+ _extend_services: List[
+ Union[Type[Service[Any, Any]], str]
+ ] # A list of services loaded programmatically using the ``load_service()`` method
_extend_rules: List[Union[Type[Rule[Any, Any, Any]], str, Path]]
_extend_rule_dirs: List[Path]
rules_priority_dict: Dict[int, List[Type[Rule[Any, Any, Any]]]]
_config_file: Optional[str] # Configuration file
_config_dict: Optional[Dict[str, Any]] # Configuration dictionary
-
+
+ _core_run_hooks: List[CoreHook]
+ _core_exit_hooks: List[CoreHook]
+ _service_startup_hooks: List[ServiceHook]
+ _service_run_hooks: List[ServiceHook]
+ _service_shutdown_hooks: List[ServiceHook]
+ _event_preprocessor_hooks: List[EventHook]
+ _event_postprocessor_hooks: List[EventHook]
+
+ _service_tasks: Set[
+ "asyncio.Task[None]"
+ ] # Server task collection, used to hold references to server tasks
_condition: (
asyncio.Condition
) # Condition used to handle get # pyright: ignore[reportUninitializedInstanceVariable]
_rule_tasks: Set[
"asyncio.Task[None]"
- ] # Adapter task collection, used to hold references to adapter tasks
+ ] # Server task collection, used to hold references to server tasks
_handle_event_tasks: Set[
"asyncio.Task[None]"
- ] # Event handling task, used to keep a reference to the adapter task
+ ] # Event handling task, used to keep a reference to the service task
def __init__(
self,
@@ -84,6 +97,8 @@ class Core:
hot_reload: bool = False,
) -> None:
self.config = MainConfig()
+
+ self.services = []
self._current_event = None
self._config_file = config_file
self._config_dict = config_dict
@@ -92,13 +107,18 @@ class Core:
self._module_path_finder = ModulePathFinder()
self.rules_priority_dict = defaultdict(list)
self._raw_config_dict = {}
+ self._service_tasks = set()
self._rule_tasks = set()
self._handle_event_tasks = set()
+ self._extend_services = []
self._extend_rules = []
self._extend_rule_dirs = []
self._core_run_hooks = []
self._core_exit_hooks = []
+ self._service_startup_hooks = []
+ self._service_run_hooks = []
+ self._service_shutdown_hooks = []
self._rule_enable_hooks = []
self._rule_run_hooks = []
self._rule_disable_hooks = []
@@ -119,6 +139,7 @@ class Core:
if self._restart_flag:
self._load_rules_from_dirs(*self._extend_rule_dirs)
self._load_rules(*self._extend_rules)
+ self._load_services(*self._extend_services)
def restart(self) -> None:
logger.info("Restarting...")
@@ -146,10 +167,11 @@ class Core:
self._load_rules_from_dirs(*self.config.core.rule_dirs)
self._load_rules(*self.config.core.rules)
+ self._load_services(*self.config.core.services)
self._update_config()
logger.info("Running...")
-
+
hot_reload_task = None
if self._hot_reload: # pragma: no cover
hot_reload_task = asyncio.create_task(self._run_hot_reload())
@@ -158,6 +180,21 @@ class Core:
await core_run_hook_func(self)
try:
+ for _service in self.services:
+ for _service_startup_hook_func in self._service_startup_hooks:
+ await _service_startup_hook_func(_service)
+ try:
+ await _service.startup()
+ except Exception as e:
+ self.error_or_exception(f"Start service {_service!r} failed:", e)
+
+ for _service in self.services:
+ for _service_run_hook_func in self._service_run_hooks:
+ await _service_run_hook_func(_service)
+ _service_task = asyncio.create_task(_service.safe_run())
+ self._service_tasks.add(_service_task)
+ _service_task.add_done_callback(self._service_tasks.discard)
+
# TODO(简律纯): builtin rule enable hook function in every rules packages.
# for _rule in self.rules:
# for rule_enable_hook_func in self._rule_enable_hooks:
@@ -180,11 +217,19 @@ class Core:
if hot_reload_task is not None: # pragma: no cover
await hot_reload_task
finally:
+ for _service in self.services:
+ for service_shutdown_hook_func in self._service_shutdown_hooks:
+ await service_shutdown_hook_func(_service)
+ await _service.shutdown()
+
# TODO(简律纯): builtin rule disable hook function in every rules packages.
# for _rule in self.rules:
# for rule_disable_hook_func in self._rule_disable_hooks:
# await rule_disable_hook_func(_rule)
# await _rule.disable()
+
+ while self._service_tasks:
+ await asyncio.sleep(0)
while self._rule_tasks:
await asyncio.sleep(0)
@@ -192,6 +237,7 @@ class Core:
for core_exit_hook_func in self._core_exit_hooks:
await core_exit_hook_func(self)
+ self.services.clear()
self.rules.clear()
self.rules_priority_dict.clear()
self._module_path_finder.path.clear()
@@ -295,7 +341,7 @@ class Core:
def _update_config(self) -> None:
def update_config(
- source: List[Type[Rule[Any, Any, Any]]],
+ source: Union[List[Type[Rule[Any, Any, Any]]], List[Service[Any, Any]]],
name: str,
base: Type[ConfigModel],
) -> Tuple[Type[ConfigModel], ConfigModel]:
@@ -319,6 +365,7 @@ class Core:
self.config = create_model(
"Config",
rule=update_config(self.rules, "RuleConfig", RuleConfig),
+ service=update_config(self.services, "ServiceConfig", ServiceConfig),
__base__=MainConfig,
)(**self._raw_config_dict)
# Update the level of logging
@@ -382,7 +429,7 @@ class Core:
) -> None:
if show_log:
logger.info(
- f"Rule {current_event.rule.name} received: {current_event!r}")
+ f"Service {current_event.service.name} received: {current_event!r}")
if handle_get:
_handle_event_task = asyncio.create_task(self._handle_event())
@@ -594,6 +641,58 @@ class Core:
def load_rules_from_dirs(self, *dirs: Path) -> None:
self._extend_rule_dirs.extend(dirs)
self._load_rules_from_dirs(*dirs)
+
+ def _load_services(self, *services: Union[Type[Service[Any, Any]], str]) -> None:
+ for service_ in services:
+ service_object: Service[Any, Any]
+ try:
+ if isinstance(service_, type) and issubclass(service_, Service):
+ service_object = service_(self)
+ elif isinstance(service_, str):
+ service_classes = get_classes_from_module_name(service_, Service)
+ if not service_classes:
+ raise LoadModuleError( # noqa: TRY301
+ f"Can not find Service class in the {service_} module"
+ )
+ if len(service_classes) > 1:
+ raise LoadModuleError( # noqa: TRY301
+ f"More then one Service class in the {service_} module"
+ )
+ service_object = service_classes[0][0](self) # type: ignore
+ else:
+ raise TypeError( # noqa: TRY301
+ f"{service_} can not be loaded as service"
+ )
+ except Exception as e:
+ self.error_or_exception(f'Load service "{service_}" failed:', e)
+ else:
+ self.services.append(service_object)
+ logger.info(
+ f'Succeeded to load service "{service_object.__class__.__name__}" '
+ f'from "{service_}"'
+ )
+
+ def load_services(self, *services: Union[Type[Service[Any, Any]], str]) -> None:
+ self._extend_services.extend(services)
+ self._load_services(*services)
+
+ @overload
+ def get_service(self, service: str) -> Service[Any, Any]: ...
+
+ @overload
+ def get_service(self, service: Type[ServiceT]) -> ServiceT: ...
+
+ def get_service(
+ self, service: Union[str, Type[ServiceT]]
+ ) -> Union[Service[Any, Any], ServiceT]:
+ for _service in self.services:
+ if isinstance(service, str):
+ if _service.name == service:
+ return _service
+ elif isinstance(_service, service):
+ return _service
+ raise LookupError(f'Can not find service named "{service}"')
+
def get_rule(self, name: str) -> Type[Rule[Any, Any, Any]]:
for _rule in self.rules:
@@ -605,9 +704,9 @@ class Core:
self, message: str, exception: Exception
) -> None: # pragma: no cover
if self.config.core.log.verbose_exception:
- logger.exception(message)
+ error_or_exception(message)
else:
- logger.error(f"{message} {exception!r}")
+ error_or_exception(message, exception, verbose=False)
def core_run_hook(self, func: CoreHook) -> CoreHook:
self._core_run_hooks.append(func)
diff --git a/hrc/dependencies.py b/hrc/dependencies.py
index 3a662fd..e176e14 100644
--- a/hrc/dependencies.py
+++ b/hrc/dependencies.py
@@ -15,7 +15,7 @@ from typing import (
cast,
)
-from .utils import get_annotations, sync_ctx_manager_wrapper
+from hrc.utils import get_annotations, sync_ctx_manager_wrapper
_T = TypeVar("_T")
Dependency = Union[
@@ -45,12 +45,9 @@ class InnerDepends:
attr = getattr(self.dependency, "__name__", type(self.dependency).__name__)
cache = "" if self.use_cache else ", use_cache=False"
return f"InnerDepends({attr}{cache})"
-
-
-def Depends( # noqa: N802 # pylint: disable=invalid-name
- dependency: Optional[Dependency[_T]] = None, *, use_cache: bool = True
-) -> _T:
-
+
+
+def Depends(dependency: Optional[Dependency[_T]] = None, *, use_cache: bool = True) -> _T:
return InnerDepends(dependency=dependency, use_cache=use_cache) # type: ignore
diff --git a/hrc/dev/__init__.py b/hrc/dev/__init__.py
index cf99d7f..ea8f56b 100644
--- a/hrc/dev/__init__.py
+++ b/hrc/dev/__init__.py
@@ -1 +1 @@
-from .grps import v1 \ No newline at end of file
+from hrc.dev.grps import v1 \ No newline at end of file
diff --git a/hrc/dev/api/__init__.py b/hrc/dev/api/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/hrc/dev/api/__init__.py
diff --git a/hrc/dev/echo.py b/hrc/dev/echo.py
index 7107159..5bdeab7 100644
--- a/hrc/dev/echo.py
+++ b/hrc/dev/echo.py
@@ -4,12 +4,102 @@
:ref: https://echo.hydroroll.team
"""
-class WorkFlow(object):
+class Event(object):
+ """事件基类
+ :ref: https://echo.hydroroll.team/Event/#0_event
+ """
+ def __init__(self, event_type, data, metadata):
+ self.event_type = event_type
+ self.data = data
+ self.metadata = metadata
+
+class WorkFlow(Event):
"""workflow
:ref: https://echo.hydroroll.team/Event/#1_workflow
"""
-
-class CallBack(object):
+ def __init__(self, data, metadata):
+ super().__init__('workflow', data, metadata)
+
+class CallBack(Event):
"""callback
:ref: https://echo.hydroroll.team/Event/#4_callback
- """ \ No newline at end of file
+ """
+ def __init__(self, data, metadata):
+ super().__init__('callback', data, metadata)
+
+class Message(Event):
+ """message
+ :ref: https://echo.hydroroll.team/Event/#2_message
+ """
+ def __init__(self, data, metadata):
+ super().__init__('message', data, metadata)
+
+class Reaction(Event):
+ """reaction
+ :ref: https://echo.hydroroll.team/Event/#3_reaction
+ """
+ def __init__(self, data, metadata):
+ super().__init__('reaction', data, metadata)
+
+class Typing(Event):
+ """typing
+ :ref: https://echo.hydroroll.team/Event/#5_typing
+ """
+ def __init__(self, data, metadata):
+ super().__init__('typing', data, metadata)
+
+class UserJoin(Event):
+ """user join
+ :ref: https://echo.hydroroll.team/Event/#6_user_join
+ """
+ def __init__(self, data, metadata):
+ super().__init__('user_join', data, metadata)
+
+class UserLeave(Event):
+ """user leave
+ :ref: https://echo.hydroroll.team/Event/#7_user_leave
+ """
+ def __init__(self, data, metadata):
+ super().__init__('user_leave', data, metadata)
+
+class FileShare(Event):
+ """file share
+ :ref: https://echo.hydroroll.team/Event/#8_file_share
+ """
+ def __init__(self, data, metadata):
+ super().__init__('file_share', data, metadata)
+
+class Mention(Event):
+ """mention
+ :ref: https://echo.hydroroll.team/Event/#9_mention
+ """
+ def __init__(self, data, metadata):
+ super().__init__('mention', data, metadata)
+
+class ChannelCreate(Event):
+ """channel create
+ :ref: https://echo.hydroroll.team/Event/#10_channel_create
+ """
+ def __init__(self, data, metadata):
+ super().__init__('channel_create', data, metadata)
+
+class ChannelDelete(Event):
+ """channel delete
+ :ref: https://echo.hydroroll.team/Event/#11_channel_delete
+ """
+ def __init__(self, data, metadata):
+ super().__init__('channel_delete', data, metadata)
+
+class ChannelUpdate(Event):
+ """channel update
+ :ref: https://echo.hydroroll.team/Event/#12_channel_update
+ """
+ def __init__(self, data, metadata):
+ super().__init__('channel_update', data, metadata)
+
+class UserUpdate(Event):
+ """user update
+ :ref: https://echo.hydroroll.team/Event/#13_user_update
+ """
+ def __init__(self, data, metadata):
+ super().__init__('user_update', data, metadata) \ No newline at end of file
diff --git a/hrc/dev/grps/v1.py b/hrc/dev/grps/v1.py
index 5af118c..9402c4b 100644
--- a/hrc/dev/grps/v1.py
+++ b/hrc/dev/grps/v1.py
@@ -1 +1,21 @@
-__version__ = "1.0.0-alpha.1" \ No newline at end of file
+from pydantic import BaseModel
+
+
+__version__ = "1.0.0-alpha.1"
+
+class GRPS(BaseModel):
+ def __init__(self, *args, **kwargs):
+ self.args = args
+ self.kwargs = kwargs
+
+ def run(self):
+ pass
+
+ def start(self):
+ pass
+
+ def stop(self):
+ pass
+
+ def restart(self):
+ pass
diff --git a/hrc/doc/__init__.py b/hrc/doc/__init__.py
index e69de29..998b999 100644
--- a/hrc/doc/__init__.py
+++ b/hrc/doc/__init__.py
@@ -0,0 +1 @@
+import sphinx \ No newline at end of file
diff --git a/hrc/event.py b/hrc/event.py
index afdb00c..961a356 100644
--- a/hrc/event.py
+++ b/hrc/event.py
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Any, Generic, Optional, Union
from typing_extensions import Self
from pydantic import BaseModel, ConfigDict
-from .typing import RuleT
+from hrc.typing import RuleT
class Event(ABC, BaseModel, Generic[RuleT]):
@@ -62,20 +62,6 @@ class MessageEvent(Event[RuleT], Generic[RuleT]):
max_try_times: Optional[int] = None,
timeout: Optional[Union[int, float]] = None,
) -> Self:
- """Get the user's reply message.
-
- Equivalent to `get()` of ``Bot``, the condition is that the adapter, event type and sender are the same.
-
- Args:
- max_try_times: Maximum number of events.
- timeout: timeout period.
-
- Returns:
- Message event that the user replies to.
-
- Raises:
- GetEventTimeout: Maximum number of events exceeded or timeout.
- """
return await self.rule.get(
self.is_same_sender,
diff --git a/hrc/log.py b/hrc/log.py
index dfa126c..8e476a6 100644
--- a/hrc/log.py
+++ b/hrc/log.py
@@ -1,6 +1,7 @@
import os
import sys
from datetime import datetime
+from typing import Optional
from loguru import logger as _logger
@@ -11,13 +12,9 @@ log_path = os.path.join(
current_path, "logs", datetime.now().strftime("%Y-%m-%d") + ".log"
)
-
-def error_or_exception(message: str, exception: Exception, verbose: bool):
+def error_or_exception(message: str, exception: Optional[Exception], verbose: bool = True):
logger.remove()
- logger.add(
- sys.stderr,
- format="<magenta>{time:YYYY-MM-DD HH:mm:ss.SSS}</magenta> <level>[{level}]</level> > <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
- )
+ logger.add(sys.stderr)
logger.add(sink=log_path, level="INFO", rotation="10 MB")
if verbose:
logger.exception(message)
diff --git a/hrc/rule/__init__.py b/hrc/rule/__init__.py
index e144091..282e3e2 100644
--- a/hrc/rule/__init__.py
+++ b/hrc/rule/__init__.py
@@ -3,8 +3,8 @@ from typing import Generic, Any, Type
from abc import ABC
-from . import BaseRule # noqa: F401
-from ..typing import RuleT # noqa: F401
+from hrc.rule import BaseRule # noqa: F401
+from hrc.typing import RuleT # noqa: F401
import inspect
from abc import abstractmethod # noqa: F401
@@ -20,16 +20,16 @@ from typing import (
)
from typing_extensions import Annotated, get_args, get_origin
-from ..config import ConfigModel
+from hrc.config import ConfigModel
-from ..dependencies import Depends
-from ..event import Event
-from ..exceptions import SkipException, StopException
-from ..typing import ConfigT, EventT, StateT
-from ..utils import is_config_class
+from hrc.dependencies import Depends
+from hrc.event import Event
+from hrc.exceptions import SkipException, StopException
+from hrc.typing import ConfigT, EventT, StateT
+from hrc.utils import is_config_class
if TYPE_CHECKING:
- from ..core import Core
+ from hrc.core import Core
class RuleLoadType(Enum):
diff --git a/hrc/service/__init__.py b/hrc/service/__init__.py
new file mode 100644
index 0000000..f153d5b
--- /dev/null
+++ b/hrc/service/__init__.py
@@ -0,0 +1,111 @@
+import os
+from abc import ABC, abstractmethod
+from typing import (
+ TYPE_CHECKING,
+ Any,
+ Awaitable,
+ Callable,
+ Generic,
+ Optional,
+ Type,
+ TypeVar,
+ Union,
+ final,
+ overload,
+)
+
+from hrc.event import Event
+from hrc.typing import ConfigT, EventT
+from hrc.utils import is_config_class
+
+if TYPE_CHECKING:
+ from ..core import Core
+
+__all__ = ["Server"]
+
+if os.getenv("IAMAI_DEV") == "1": # pragma: no cover
+ # 当处于开发环境时,使用 pkg_resources 风格的命名空间包
+ __import__("pkg_resources").declare_namespace(__name__)
+
+
+_EventT = TypeVar("_EventT", bound="Event[Any]")
+
+
+class Service(Generic[EventT, ConfigT], ABC):
+ name: str
+ core: "Core"
+ Config: Type[ConfigT]
+
+ def __init__(self, core: "Core") -> None:
+ if not hasattr(self, "name"):
+ self.name = self.__class__.__name__
+ self.core: Core = core
+ self.handle_event = self.core.handle_event
+
+ @property
+ def config(self) -> ConfigT:
+ default: Any = None
+ config_class = getattr(self, "Config", None)
+ if is_config_class(config_class):
+ return getattr(
+ self.core.config.service,
+ config_class.__config_name__,
+ default,
+ )
+ return default
+
+ @final
+ async def safe_run(self) -> None:
+ try:
+ await self.run()
+ except Exception as e:
+ self.core.error_or_exception(
+ f"Run service {self.__class__.__name__} failed:", e
+ )
+
+ @abstractmethod
+ async def run(self) -> None:
+ raise NotImplementedError
+
+ async def startup(self) -> None:
+ ...
+
+ async def shutdown(self) -> None:
+ ...
+
+ @overload
+ async def get(
+ self,
+ func: Optional[Callable[[EventT], Union[bool, Awaitable[bool]]]] = None,
+ *,
+ event_type: None = None,
+ max_try_times: Optional[int] = None,
+ timeout: Optional[Union[int, float]] = None,
+ ) -> EventT: ...
+
+ @overload
+ async def get(
+ self,
+ func: Optional[Callable[[_EventT], Union[bool, Awaitable[bool]]]] = None,
+ *,
+ event_type: Type[_EventT],
+ max_try_times: Optional[int] = None,
+ timeout: Optional[Union[int, float]] = None,
+ ) -> _EventT: ...
+
+ @final
+ async def get(
+ self,
+ func: Optional[Callable[[Any], Union[bool, Awaitable[bool]]]] = None,
+ *,
+ event_type: Any = None,
+ max_try_times: Optional[int] = None,
+ timeout: Optional[Union[int, float]] = None,
+ ) -> Event[Any]:
+ return await self.core.get(
+ func,
+ event_type=event_type,
+ server_type=type(self),
+ max_try_times=max_try_times,
+ timeout=timeout,
+ ) \ No newline at end of file
diff --git a/hrc/service/console/__init__.py b/hrc/service/console/__init__.py
new file mode 100644
index 0000000..e11768e
--- /dev/null
+++ b/hrc/service/console/__init__.py
@@ -0,0 +1,41 @@
+import asyncio
+import sys
+from typing_extensions import override
+
+from hrc.event import MessageEvent
+from hrc.service import Service
+
+class ConsoleServiceEvent(MessageEvent["ConsoleService"]):
+ message: str
+
+ @override
+ def get_sender_id(self) -> None:
+ return None
+
+ @override
+ def get_plain_text(self) -> str:
+ return self.message
+
+ @override
+ async def reply(self, message: str) -> None:
+ return await self.service.send(message)
+
+ async def is_same_sender(self) -> bool:
+ return True
+
+class ConsoleService(Service[ConsoleServiceEvent, None]):
+ name: str = "console"
+
+ @override
+ async def run(self) -> None:
+ while not self.core.should_exit.is_set():
+ print("Please input message: ") # noqa: T201
+ message = await asyncio.get_event_loop().run_in_executor(
+ None, sys.stdin.readline
+ )
+ await self.handle_event(
+ ConsoleServiceEvent(service=self, type="message", message=message, rule="")
+ )
+
+ async def send(self, message: str) -> None:
+ print(f"Send a message: {message}") # noqa: T201 \ No newline at end of file
diff --git a/hrc/service/http/__init__.py b/hrc/service/http/__init__.py
new file mode 100644
index 0000000..a8d938b
--- /dev/null
+++ b/hrc/service/http/__init__.py
@@ -0,0 +1,33 @@
+from typing_extensions import override
+
+from aiohttp import web
+
+from hrc.service.utils import HttpServerService
+from hrc.event import Event
+from hrc.log import logger
+from aiohttp import web
+
+class HttpServerTestEvent(Event["HttpServerTestService"]):
+ """HTTP 服务端示例适配器事件类。"""
+
+ message: str
+
+
+class HttpServerTestService(HttpServerService[HttpServerTestEvent, None]):
+ name: str = "http_server_service"
+ get_url: str = "/"
+ post_url: str = "/"
+ host: str = "127.0.0.1"
+ port: int = 8080
+
+
+ @override
+ async def handle_response(self, request: web.Request) -> web.StreamResponse:
+ event = HttpServerTestEvent(
+ service=self,
+ type="message",
+ rule="",
+ message=await request.text(),
+ )
+ await self.handle_event(event)
+ return web.Response() \ No newline at end of file
diff --git a/hrc/service/utils.py b/hrc/service/utils.py
new file mode 100644
index 0000000..4f29e0c
--- /dev/null
+++ b/hrc/service/utils.py
@@ -0,0 +1,256 @@
+import asyncio
+from abc import ABCMeta, abstractmethod
+from typing import Literal, Optional, Union
+
+import aiohttp
+from aiohttp import web
+
+from hrc.service import Service
+from hrc.log import logger
+from hrc.typing import ConfigT, EventT
+
+__all__ = [
+ "PollingService",
+ "HttpClientService",
+ "WebSocketClientService",
+ "HttpServerService",
+ "WebSocketServerService",
+ "WebSocketService",
+]
+
+
+class PollingService(Service[EventT, ConfigT], metaclass=ABCMeta):
+ """轮询式适配器示例。"""
+
+ delay: float = 0.1
+ create_task: bool = False
+ _on_tick_task: Optional["asyncio.Task[None]"] = None
+
+ async def run(self) -> None:
+ while not self.core.should_exit.is_set():
+ await asyncio.sleep(self.delay)
+ if self.create_task:
+ self._on_tick_task = asyncio.create_task(self.on_tick())
+ else:
+ await self.on_tick()
+
+ @abstractmethod
+ async def on_tick(self) -> None:
+ """当轮询发生。"""
+
+
+class HttpClientService(PollingService[EventT, ConfigT], metaclass=ABCMeta):
+ session: aiohttp.ClientSession
+
+ async def startup(self) -> None:
+ self.session = aiohttp.ClientSession()
+
+ @abstractmethod
+ async def on_tick(self) -> None:
+ ...
+ async def shutdown(self) -> None:
+ """关闭并清理连接。"""
+ await self.session.close()
+
+
+class WebSocketClientService(Service[EventT, ConfigT], metaclass=ABCMeta):
+ url: str
+
+ async def run(self) -> None:
+ async with aiohttp.ClientSession() as session, session.ws_connect(
+ self.url
+ ) as ws:
+ msg: aiohttp.WSMessage
+ async for msg in ws:
+ if self.core.should_exit.is_set():
+ break
+ if msg.type == aiohttp.WSMsgType.ERROR:
+ break
+ await self.handle_response(msg)
+
+ @abstractmethod
+ async def handle_response(self, msg: aiohttp.WSMessage) -> None:
+ """处理响应。"""
+
+
+class HttpServerService(Service[EventT, ConfigT], metaclass=ABCMeta):
+ app: web.Application
+ runner: web.AppRunner
+ site: web.TCPSite
+ host: str
+ port: int
+ get_url: str
+ post_url: str
+
+ async def startup(self) -> None:
+ """初始化适配器。"""
+ self.app = web.Application()
+ self.app.add_routes(
+ [
+ web.get(self.get_url, self.handle_response),
+ web.post(self.post_url, self.handle_response),
+ ]
+ )
+
+ async def run(self) -> None:
+ self.runner = web.AppRunner(self.app)
+ await self.runner.setup()
+ self.site = web.TCPSite(self.runner, self.host, self.port)
+ await self.site.start()
+
+ async def shutdown(self) -> None:
+ """关闭并清理连接。"""
+ await self.runner.cleanup()
+
+ @abstractmethod
+ async def handle_response(self, request: web.Request) -> web.StreamResponse:
+ """处理响应。"""
+
+
+class WebSocketServerService(Service[EventT, ConfigT], metaclass=ABCMeta):
+ app: web.Application
+ runner: web.AppRunner
+ site: web.TCPSite
+ websocket: web.WebSocketResponse
+ host: str
+ port: int
+ url: str
+
+ async def startup(self) -> None:
+ self.app = web.Application()
+ self.app.add_routes([web.get(self.url, self.handle_response)])
+
+ async def run(self) -> None:
+ self.runner = web.AppRunner(self.app)
+ await self.runner.setup()
+ self.site = web.TCPSite(self.runner, self.host, self.port)
+ await self.site.start()
+
+ async def shutdown(self) -> None:
+ """关闭并清理连接。"""
+ await self.websocket.close()
+ await self.site.stop()
+ await self.runner.cleanup()
+
+ async def handle_response(self, request: web.Request) -> web.WebSocketResponse:
+ """处理 WebSocket。"""
+ ws = web.WebSocketResponse()
+ await ws.prepare(request)
+ self.websocket = ws
+
+ msg: aiohttp.WSMessage
+ async for msg in ws:
+ if msg.type == aiohttp.WSMsgType.TEXT:
+ await self.handle_ws_response(msg)
+ elif msg.type == aiohttp.WSMsgType.ERROR:
+ break
+
+ return ws
+
+ @abstractmethod
+ async def handle_ws_response(self, msg: aiohttp.WSMessage) -> None:
+ """处理 WebSocket 响应。"""
+
+
+class WebSocketService(Service[EventT, ConfigT], metaclass=ABCMeta):
+ """
+ 同时支持 WebSocket 客户端和服务端。
+ """
+
+ websocket: Union[web.WebSocketResponse, aiohttp.ClientWebSocketResponse, None] = (
+ None
+ )
+
+ # ws
+ session: Optional[aiohttp.ClientSession]
+
+ # reverse-ws
+ app: Optional[web.Application]
+ runner: Optional[web.AppRunner]
+ site: Optional[web.TCPSite]
+
+ # config
+ service_type: Literal["ws", "reverse-ws"]
+ host: str
+ port: int
+ url: str
+ reconnect_interval: int = 3
+
+ async def startup(self) -> None:
+ if self.service_type == "ws":
+ self.session = aiohttp.ClientSession()
+ elif self.service_type == "reverse-ws":
+ self.app = web.Application()
+ self.app.add_routes([web.get(self.url, self.handle_reverse_ws_response)])
+ else:
+ logger.error(
+ 'Config "service_type" must be "ws" or "reverse-ws", not '
+ + self.service_type
+ )
+
+ async def run(self) -> None:
+ if self.service_type == "ws":
+ while True:
+ try:
+ await self.websocket_connect()
+ except aiohttp.ClientError as e:
+ self.core.error_or_exception("WebSocket connection error:", e)
+ if self.core.should_exit.is_set():
+ break
+ await asyncio.sleep(self.reconnect_interval)
+ elif self.service_type == "reverse-ws":
+ assert self.app is not None
+ self.runner = web.AppRunner(self.app)
+ await self.runner.setup()
+ self.site = web.TCPSite(self.runner, self.host, self.port)
+ await self.site.start()
+
+ async def shutdown(self) -> None:
+ """关闭并清理连接。"""
+ if self.websocket is not None:
+ await self.websocket.close()
+ if self.service_type == "ws":
+ if self.session is not None:
+ await self.session.close()
+ elif self.service_type == "reverse-ws":
+ if self.site is not None:
+ await self.site.stop()
+ if self.runner is not None:
+ await self.runner.cleanup()
+
+ async def handle_reverse_ws_response(
+ self, request: web.Request
+ ) -> web.WebSocketResponse:
+ """处理 aiohttp WebSocket 服务器的接收。"""
+ self.websocket = web.WebSocketResponse()
+ await self.websocket.prepare(request)
+ await self.reverse_ws_connection_hook()
+ await self.handle_websocket()
+ return self.websocket
+
+ async def reverse_ws_connection_hook(self) -> None:
+ """反向 WebSocket 连接建立时的钩子函数。"""
+ logger.info("WebSocket connected!")
+
+ async def websocket_connect(self) -> None:
+ """创建正向 WebSocket 连接。"""
+ assert self.session is not None
+ logger.info("Tying to connect to WebSocket server...")
+ async with self.session.ws_connect(
+ f"ws://{self.host}:{self.port}{self.url}"
+ ) as self.websocket:
+ await self.handle_websocket()
+
+ async def handle_websocket(self) -> None:
+ """处理 WebSocket。"""
+ if self.websocket is None or self.websocket.closed:
+ return
+ async for msg in self.websocket:
+ await self.handle_websocket_msg(msg)
+ if not self.core.should_exit.is_set():
+ logger.warning("WebSocket connection closed!")
+
+ @abstractmethod
+ async def handle_websocket_msg(self, msg: aiohttp.WSMessage) -> None:
+ """处理 WebSocket 消息。"""
+ raise NotImplementedError \ No newline at end of file
diff --git a/hrc/service/websocket/__init__.py b/hrc/service/websocket/__init__.py
new file mode 100644
index 0000000..3a7f089
--- /dev/null
+++ b/hrc/service/websocket/__init__.py
@@ -0,0 +1,30 @@
+from typing import Any, Coroutine
+from typing_extensions import override
+from aiohttp import web, ClientWebSocketResponse
+
+from hrc.service.utils import WebSocketService
+from hrc.event import Event
+from hrc.log import logger
+
+from aiohttp import web
+
+class WebSocketTestEvent(Event["WebSocketTestEvent"]):
+ message: str
+
+class WebSocketTestService(WebSocketService[WebSocketTestEvent, None]):
+ name: str = "websocket_test_service"
+ service_type: str = "reverse-ws"
+ host: str = "127.0.0.1"
+ port: int = 8765
+ url: str = "/"
+
+ @override
+ async def handle_reverse_ws_response(self, request: web.Request) -> Coroutine[Any, Any, ClientWebSocketResponse]:
+ event = WebSocketTestEvent(
+ service=self,
+ type="message",
+ message=await request.text()
+ )
+ logger.info(f"Receive {event}")
+ await self.handle_event(event)
+ return web.Response() \ No newline at end of file
diff --git a/hrc/typing.py b/hrc/typing.py
index d74fd26..a207c80 100644
--- a/hrc/typing.py
+++ b/hrc/typing.py
@@ -4,17 +4,20 @@ from typing import TYPE_CHECKING, Awaitable, Callable, Optional, TypeVar
if TYPE_CHECKING:
from typing import Any
- from .core import Core
- from .config import ConfigModel
- from .event import Event
- from .rule import Rule
+ from hrc.service import Service
+ from hrc.core import Core
+ from hrc.config import ConfigModel
+ from hrc.event import Event
+ from hrc.rule import Rule
StateT = TypeVar("StateT")
EventT = TypeVar("EventT", bound="Event[Any]")
RuleT = TypeVar("RuleT", bound="Rule[Any, Any, Any]")
ConfigT = TypeVar("ConfigT", bound=Optional["ConfigModel"])
+ServiceT = TypeVar("ServiceT", bound="Service[Any, Any]")
CoreHook = Callable[["Core"], Awaitable[None]]
RuleHook = Callable[["Rule"], Awaitable[None]]
+ServiceHook = Callable[["Service[Any, Any]"], Awaitable[None]]
EventHook = Callable[["Event[Any]"], Awaitable[None]]
diff --git a/hrc/utils.py b/hrc/utils.py
index e85cea4..e1399f5 100644
--- a/hrc/utils.py
+++ b/hrc/utils.py
@@ -37,8 +37,8 @@ from typing_extensions import ParamSpec, TypeAlias, TypeGuard
from pydantic import BaseModel
-from .config import ConfigModel
-from .typing import EventT
+from hrc.config import ConfigModel
+from hrc.typing import EventT
if TYPE_CHECKING:
from os import PathLike
diff --git a/pdm.lock b/pdm.lock
index dc1b868..26e9d12 100644
--- a/pdm.lock
+++ b/pdm.lock
@@ -2,10 +2,114 @@
# It is not intended for manual editing.
[metadata]
-groups = ["default", "lint", "dev", "docs"]
-strategy = ["cross_platform"]
-lock_version = "4.4.2"
-content_hash = "sha256:94e432ee5df19393cd6bc387c01bdd4e21f36dd651004d7ce0fd8aa3acbd8304"
+groups = ["default", "dev", "docs", "lint"]
+strategy = []
+lock_version = "4.5.0"
+content_hash = "sha256:3496bc42ff732a021b4bbe5b6d42cf890f241ea0c8a569da75188ee33e5ffa54"
+
+[[metadata.targets]]
+requires_python = ">=3.9"
+
+[[package]]
+name = "aiohappyeyeballs"
+version = "2.3.5"
+requires_python = ">=3.8"
+summary = "Happy Eyeballs for asyncio"
+files = [
+ {file = "aiohappyeyeballs-2.3.5-py3-none-any.whl", hash = "sha256:4d6dea59215537dbc746e93e779caea8178c866856a721c9c660d7a5a7b8be03"},
+ {file = "aiohappyeyeballs-2.3.5.tar.gz", hash = "sha256:6fa48b9f1317254f122a07a131a86b71ca6946ca989ce6326fff54a99a920105"},
+]
+
+[[package]]
+name = "aiohttp"
+version = "3.10.3"
+requires_python = ">=3.8"
+summary = "Async http client/server framework (asyncio)"
+dependencies = [
+ "aiohappyeyeballs>=2.3.0",
+ "aiosignal>=1.1.2",
+ "async-timeout<5.0,>=4.0; python_version < \"3.11\"",
+ "attrs>=17.3.0",
+ "frozenlist>=1.1.1",
+ "multidict<7.0,>=4.5",
+ "yarl<2.0,>=1.0",
+]
+files = [
+ {file = "aiohttp-3.10.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc36cbdedf6f259371dbbbcaae5bb0e95b879bc501668ab6306af867577eb5db"},
+ {file = "aiohttp-3.10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85466b5a695c2a7db13eb2c200af552d13e6a9313d7fa92e4ffe04a2c0ea74c1"},
+ {file = "aiohttp-3.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:71bb1d97bfe7e6726267cea169fdf5df7658831bb68ec02c9c6b9f3511e108bb"},
+ {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baec1eb274f78b2de54471fc4c69ecbea4275965eab4b556ef7a7698dee18bf2"},
+ {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:13031e7ec1188274bad243255c328cc3019e36a5a907978501256000d57a7201"},
+ {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2bbc55a964b8eecb341e492ae91c3bd0848324d313e1e71a27e3d96e6ee7e8e8"},
+ {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8cc0564b286b625e673a2615ede60a1704d0cbbf1b24604e28c31ed37dc62aa"},
+ {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f817a54059a4cfbc385a7f51696359c642088710e731e8df80d0607193ed2b73"},
+ {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8542c9e5bcb2bd3115acdf5adc41cda394e7360916197805e7e32b93d821ef93"},
+ {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:671efce3a4a0281060edf9a07a2f7e6230dca3a1cbc61d110eee7753d28405f7"},
+ {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0974f3b5b0132edcec92c3306f858ad4356a63d26b18021d859c9927616ebf27"},
+ {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:44bb159b55926b57812dca1b21c34528e800963ffe130d08b049b2d6b994ada7"},
+ {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6ae9ae382d1c9617a91647575255ad55a48bfdde34cc2185dd558ce476bf16e9"},
+ {file = "aiohttp-3.10.3-cp310-cp310-win32.whl", hash = "sha256:aed12a54d4e1ee647376fa541e1b7621505001f9f939debf51397b9329fd88b9"},
+ {file = "aiohttp-3.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:b51aef59370baf7444de1572f7830f59ddbabd04e5292fa4218d02f085f8d299"},
+ {file = "aiohttp-3.10.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e021c4c778644e8cdc09487d65564265e6b149896a17d7c0f52e9a088cc44e1b"},
+ {file = "aiohttp-3.10.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:24fade6dae446b183e2410a8628b80df9b7a42205c6bfc2eff783cbeedc224a2"},
+ {file = "aiohttp-3.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bc8e9f15939dacb0e1f2d15f9c41b786051c10472c7a926f5771e99b49a5957f"},
+ {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5a9ec959b5381271c8ec9310aae1713b2aec29efa32e232e5ef7dcca0df0279"},
+ {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a5d0ea8a6467b15d53b00c4e8ea8811e47c3cc1bdbc62b1aceb3076403d551f"},
+ {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9ed607dbbdd0d4d39b597e5bf6b0d40d844dfb0ac6a123ed79042ef08c1f87e"},
+ {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e66d5b506832e56add66af88c288c1d5ba0c38b535a1a59e436b300b57b23e"},
+ {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fda91ad797e4914cca0afa8b6cccd5d2b3569ccc88731be202f6adce39503189"},
+ {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:61ccb867b2f2f53df6598eb2a93329b5eee0b00646ee79ea67d68844747a418e"},
+ {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6d881353264e6156f215b3cb778c9ac3184f5465c2ece5e6fce82e68946868ef"},
+ {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b031ce229114825f49cec4434fa844ccb5225e266c3e146cb4bdd025a6da52f1"},
+ {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5337cc742a03f9e3213b097abff8781f79de7190bbfaa987bd2b7ceb5bb0bdec"},
+ {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ab3361159fd3dcd0e48bbe804006d5cfb074b382666e6c064112056eb234f1a9"},
+ {file = "aiohttp-3.10.3-cp311-cp311-win32.whl", hash = "sha256:05d66203a530209cbe40f102ebaac0b2214aba2a33c075d0bf825987c36f1f0b"},
+ {file = "aiohttp-3.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:70b4a4984a70a2322b70e088d654528129783ac1ebbf7dd76627b3bd22db2f17"},
+ {file = "aiohttp-3.10.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:166de65e2e4e63357cfa8417cf952a519ac42f1654cb2d43ed76899e2319b1ee"},
+ {file = "aiohttp-3.10.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7084876352ba3833d5d214e02b32d794e3fd9cf21fdba99cff5acabeb90d9806"},
+ {file = "aiohttp-3.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d98c604c93403288591d7d6d7d6cc8a63459168f8846aeffd5b3a7f3b3e5e09"},
+ {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d73b073a25a0bb8bf014345374fe2d0f63681ab5da4c22f9d2025ca3e3ea54fc"},
+ {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8da6b48c20ce78f5721068f383e0e113dde034e868f1b2f5ee7cb1e95f91db57"},
+ {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a9dcdccf50284b1b0dc72bc57e5bbd3cc9bf019060dfa0668f63241ccc16aa7"},
+ {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56fb94bae2be58f68d000d046172d8b8e6b1b571eb02ceee5535e9633dcd559c"},
+ {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf75716377aad2c718cdf66451c5cf02042085d84522aec1f9246d3e4b8641a6"},
+ {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6c51ed03e19c885c8e91f574e4bbe7381793f56f93229731597e4a499ffef2a5"},
+ {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b84857b66fa6510a163bb083c1199d1ee091a40163cfcbbd0642495fed096204"},
+ {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c124b9206b1befe0491f48185fd30a0dd51b0f4e0e7e43ac1236066215aff272"},
+ {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3461d9294941937f07bbbaa6227ba799bc71cc3b22c40222568dc1cca5118f68"},
+ {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:08bd0754d257b2db27d6bab208c74601df6f21bfe4cb2ec7b258ba691aac64b3"},
+ {file = "aiohttp-3.10.3-cp312-cp312-win32.whl", hash = "sha256:7f9159ae530297f61a00116771e57516f89a3de6ba33f314402e41560872b50a"},
+ {file = "aiohttp-3.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:e1128c5d3a466279cb23c4aa32a0f6cb0e7d2961e74e9e421f90e74f75ec1edf"},
+ {file = "aiohttp-3.10.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38d91b98b4320ffe66efa56cb0f614a05af53b675ce1b8607cdb2ac826a8d58e"},
+ {file = "aiohttp-3.10.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9743fa34a10a36ddd448bba8a3adc2a66a1c575c3c2940301bacd6cc896c6bf1"},
+ {file = "aiohttp-3.10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7c126f532caf238031c19d169cfae3c6a59129452c990a6e84d6e7b198a001dc"},
+ {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:926e68438f05703e500b06fe7148ef3013dd6f276de65c68558fa9974eeb59ad"},
+ {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:434b3ab75833accd0b931d11874e206e816f6e6626fd69f643d6a8269cd9166a"},
+ {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d35235a44ec38109b811c3600d15d8383297a8fab8e3dec6147477ec8636712a"},
+ {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59c489661edbd863edb30a8bd69ecb044bd381d1818022bc698ba1b6f80e5dd1"},
+ {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50544fe498c81cb98912afabfc4e4d9d85e89f86238348e3712f7ca6a2f01dab"},
+ {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:09bc79275737d4dc066e0ae2951866bb36d9c6b460cb7564f111cc0427f14844"},
+ {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:af4dbec58e37f5afff4f91cdf235e8e4b0bd0127a2a4fd1040e2cad3369d2f06"},
+ {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b22cae3c9dd55a6b4c48c63081d31c00fc11fa9db1a20c8a50ee38c1a29539d2"},
+ {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ba562736d3fbfe9241dad46c1a8994478d4a0e50796d80e29d50cabe8fbfcc3f"},
+ {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f25d6c4e82d7489be84f2b1c8212fafc021b3731abdb61a563c90e37cced3a21"},
+ {file = "aiohttp-3.10.3-cp39-cp39-win32.whl", hash = "sha256:b69d832e5f5fa15b1b6b2c8eb6a9fd2c0ec1fd7729cb4322ed27771afc9fc2ac"},
+ {file = "aiohttp-3.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:673bb6e3249dc8825df1105f6ef74e2eab779b7ff78e96c15cadb78b04a83752"},
+ {file = "aiohttp-3.10.3.tar.gz", hash = "sha256:21650e7032cc2d31fc23d353d7123e771354f2a3d5b05a5647fc30fea214e696"},
+]
+
+[[package]]
+name = "aiosignal"
+version = "1.3.1"
+requires_python = ">=3.7"
+summary = "aiosignal: a list of registered asynchronous callbacks"
+dependencies = [
+ "frozenlist>=1.1.0",
+]
+files = [
+ {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"},
+ {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"},
+]
[[package]]
name = "alabaster"
@@ -22,6 +126,9 @@ name = "annotated-types"
version = "0.7.0"
requires_python = ">=3.8"
summary = "Reusable constraint types to use with typing.Annotated"
+dependencies = [
+ "typing-extensions>=4.0.0; python_version < \"3.9\"",
+]
files = [
{file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
{file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
@@ -54,10 +161,39 @@ files = [
]
[[package]]
+name = "async-timeout"
+version = "4.0.3"
+requires_python = ">=3.7"
+summary = "Timeout context manager for asyncio programs"
+dependencies = [
+ "typing-extensions>=3.6.5; python_version < \"3.8\"",
+]
+files = [
+ {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"},
+ {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"},
+]
+
+[[package]]
+name = "attrs"
+version = "24.2.0"
+requires_python = ">=3.7"
+summary = "Classes Without Boilerplate"
+dependencies = [
+ "importlib-metadata; python_version < \"3.8\"",
+]
+files = [
+ {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"},
+ {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"},
+]
+
+[[package]]
name = "babel"
version = "2.14.0"
requires_python = ">=3.7"
summary = "Internationalization utilities"
+dependencies = [
+ "pytz>=2015.7; python_version < \"3.9\"",
+]
files = [
{file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"},
{file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"},
@@ -228,6 +364,7 @@ requires_python = ">=3.7"
summary = "Composable command line interface toolkit"
dependencies = [
"colorama; platform_system == \"Windows\"",
+ "importlib-metadata; python_version < \"3.8\"",
]
files = [
{file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
@@ -350,6 +487,76 @@ files = [
]
[[package]]
+name = "frozenlist"
+version = "1.4.1"
+requires_python = ">=3.8"
+summary = "A list-like structure which implements collections.abc.MutableSequence"
+files = [
+ {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"},
+ {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"},
+ {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"},
+ {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"},
+ {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"},
+ {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"},
+ {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"},
+ {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"},
+ {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"},
+ {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"},
+ {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"},
+ {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"},
+ {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"},
+ {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"},
+ {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"},
+ {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"},
+ {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"},
+ {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"},
+ {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"},
+ {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"},
+ {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"},
+ {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"},
+ {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"},
+ {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"},
+ {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"},
+ {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"},
+ {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"},
+ {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"},
+ {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"},
+ {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"},
+ {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"},
+ {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"},
+ {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"},
+ {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"},
+ {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"},
+ {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"},
+ {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"},
+ {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"},
+ {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"},
+ {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"},
+ {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"},
+ {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"},
+ {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"},
+ {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"},
+ {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"},
+ {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"},
+ {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"},
+ {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"},
+ {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"},
+ {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"},
+ {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"},
+ {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"},
+ {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"},
+ {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"},
+ {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"},
+ {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"},
+ {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"},
+ {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"},
+ {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"},
+ {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"},
+ {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"},
+ {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"},
+]
+
+[[package]]
name = "furo"
version = "2024.1.29"
requires_python = ">=3.8"
@@ -370,6 +577,9 @@ name = "h11"
version = "0.14.0"
requires_python = ">=3.7"
summary = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
+dependencies = [
+ "typing-extensions; python_version < \"3.8\"",
+]
files = [
{file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
@@ -401,6 +611,7 @@ version = "7.1.0"
requires_python = ">=3.8"
summary = "Read metadata from Python packages"
dependencies = [
+ "typing-extensions>=3.6.4; python_version < \"3.8\"",
"zipp>=0.5",
]
files = [
@@ -437,6 +648,7 @@ version = "0.7.2"
requires_python = ">=3.5"
summary = "Python logging made (stupidly) simple"
dependencies = [
+ "aiocontextvars>=0.2.0; python_version < \"3.7\"",
"colorama>=0.3.4; sys_platform == \"win32\"",
"win32-setctime>=1.0.0; sys_platform == \"win32\"",
]
@@ -672,6 +884,76 @@ files = [
]
[[package]]
+name = "multidict"
+version = "6.0.5"
+requires_python = ">=3.7"
+summary = "multidict implementation"
+files = [
+ {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"},
+ {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"},
+ {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"},
+ {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"},
+ {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"},
+ {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"},
+ {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"},
+ {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"},
+ {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"},
+ {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"},
+ {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"},
+ {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"},
+ {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"},
+ {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"},
+ {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"},
+ {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"},
+ {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"},
+ {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"},
+ {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"},
+ {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"},
+ {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"},
+ {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"},
+ {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"},
+ {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"},
+ {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"},
+ {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"},
+ {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"},
+ {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"},
+ {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"},
+ {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"},
+ {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"},
+ {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"},
+ {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"},
+ {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"},
+ {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"},
+ {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"},
+ {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"},
+ {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"},
+ {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"},
+ {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"},
+ {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"},
+ {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"},
+ {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"},
+ {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"},
+ {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"},
+ {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"},
+ {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"},
+ {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"},
+ {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"},
+ {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"},
+ {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"},
+ {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"},
+ {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"},
+ {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"},
+ {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"},
+ {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"},
+ {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"},
+ {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"},
+ {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"},
+ {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"},
+ {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"},
+ {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"},
+]
+
+[[package]]
name = "myst-parser"
version = "3.0.1"
requires_python = ">=3.8"
@@ -697,8 +979,10 @@ summary = "Flexible test automation."
dependencies = [
"argcomplete<4.0,>=1.9.4",
"colorlog<7.0.0,>=2.6.1",
+ "importlib-metadata; python_version < \"3.8\"",
"packaging>=20.9",
"tomli>=1; python_version < \"3.11\"",
+ "typing-extensions>=3.7.4; python_version < \"3.8\"",
"virtualenv>=20.14.1",
]
files = [
@@ -724,6 +1008,8 @@ summary = "PDF parser and analyzer"
dependencies = [
"charset-normalizer>=2.0.0",
"cryptography>=36.0.0",
+ "importlib-metadata; python_version < \"3.8\"",
+ "typing-extensions; python_version < \"3.8\"",
]
files = [
{file = "pdfminer.six-20231228-py3-none-any.whl", hash = "sha256:e8d3c3310e6fbc1fe414090123ab01351634b4ecb021232206c4c9a8ca3e3b8f"},
@@ -1257,6 +1543,7 @@ summary = "Virtual Python Environment builder"
dependencies = [
"distlib<1,>=0.3.7",
"filelock<4,>=3.12.2",
+ "importlib-metadata>=6.6; python_version < \"3.8\"",
"platformdirs<5,>=3.9.1",
]
files = [
@@ -1393,11 +1680,6 @@ files = [
{file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"},
{file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"},
{file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"},
- {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"},
- {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"},
- {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"},
- {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"},
- {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"},
{file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"},
{file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"},
{file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"},
@@ -1418,6 +1700,81 @@ files = [
]
[[package]]
+name = "yarl"
+version = "1.9.4"
+requires_python = ">=3.7"
+summary = "Yet another URL library"
+dependencies = [
+ "idna>=2.0",
+ "multidict>=4.0",
+ "typing-extensions>=3.7.4; python_version < \"3.8\"",
+]
+files = [
+ {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"},
+ {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"},
+ {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"},
+ {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"},
+ {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"},
+ {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"},
+ {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"},
+ {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"},
+ {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"},
+ {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"},
+ {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"},
+ {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"},
+ {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"},
+ {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"},
+ {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"},
+ {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"},
+ {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"},
+ {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"},
+ {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"},
+ {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"},
+ {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"},
+ {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"},
+ {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"},
+ {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"},
+ {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"},
+ {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"},
+ {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"},
+ {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"},
+ {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"},
+ {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"},
+ {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"},
+ {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"},
+ {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"},
+ {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"},
+ {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"},
+ {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"},
+ {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"},
+ {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"},
+ {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"},
+ {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"},
+ {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"},
+ {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"},
+ {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"},
+ {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"},
+ {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"},
+ {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"},
+ {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"},
+ {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"},
+ {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"},
+ {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"},
+ {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"},
+ {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"},
+ {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"},
+ {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"},
+ {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"},
+ {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"},
+ {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"},
+ {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"},
+ {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"},
+ {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"},
+ {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"},
+ {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"},
+]
+
+[[package]]
name = "zipp"
version = "3.18.1"
requires_python = ">=3.8"
diff --git a/pyproject.toml b/pyproject.toml
index bb5f601..b988f98 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -11,6 +11,8 @@ dependencies = [
"pdfquery>=0.4.3",
"pydantic>=2.7.4",
"loguru>=0.7.2",
+ "websockets>=12.0",
+ "aiohttp>=3.10.3",
]
requires-python = ">=3.9"
readme = "README.rst"
diff --git a/tests/test_rule.py b/tests/test_rule.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/test_rule.py
diff --git a/tests/test_service.py b/tests/test_service.py
new file mode 100644
index 0000000..3826081
--- /dev/null
+++ b/tests/test_service.py
@@ -0,0 +1,5 @@
+from hrc.core import Core
+
+core = Core(config_dict={'core':{'services':['hrc.service.console', 'hrc.service.http']}})
+
+core.run() \ No newline at end of file