matrix-agent 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ from .agent import MatrixAgent, AgentConfig
2
+ from .core import BaseChain, BaseFactory
3
+ from .handlers import MatrixHandler
4
+
5
+ __all__ = ["MatrixAgent", "AgentConfig", "BaseChain", "BaseFactory", "MatrixHandler"]
matrix_agent/agent.py ADDED
@@ -0,0 +1,71 @@
1
+ import asyncio
2
+ import logging
3
+ from dataclasses import dataclass
4
+ from typing import Dict, Any, Optional
5
+ from nio import AsyncClient, AsyncClientConfig
6
+ from nio.crypto import TrustState
7
+
8
+ from .core import BaseChain
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ @dataclass
13
+ class AgentConfig:
14
+ homeserver: str
15
+ user_id: str
16
+ device_id: Optional[str] = None
17
+ access_token: Optional[str] = None
18
+ store_path: str = "/tmp"
19
+ store_sync_tokens: bool = True
20
+ encryption_enabled: bool = True
21
+
22
+ class MatrixAgent:
23
+ def __init__(self, config: AgentConfig, chain: BaseChain):
24
+ self.config = config
25
+ self.chain = chain
26
+ self._client: Optional[AsyncClient] = None
27
+
28
+ async def login(self):
29
+ client_config = AsyncClientConfig(
30
+ store_sync_tokens=self.config.store_sync_tokens,
31
+ encryption_enabled=self.config.encryption_enabled,
32
+ )
33
+ self._client = AsyncClient(
34
+ self.config.homeserver,
35
+ self.config.user_id,
36
+ device_id=self.config.device_id,
37
+ store_path=self.config.store_path,
38
+ config=client_config,
39
+ )
40
+
41
+ if not self.config.access_token:
42
+ raise ValueError("Access token is required for automated login.")
43
+
44
+ self._client.restore_login(
45
+ user_id=self.config.user_id,
46
+ device_id=self.config.device_id,
47
+ access_token=self.config.access_token,
48
+ )
49
+
50
+ def _callback_wrapper(self, event):
51
+ asyncio.create_task(self._dispatch(event))
52
+
53
+ async def _dispatch(self, event):
54
+ request_context = {"event": event, "client": self._client}
55
+ await self.chain.execute(request_context)
56
+
57
+ async def start(self):
58
+ await self._client.sync(full_state=True)
59
+ for device in self._client.device_store:
60
+ if device.trust_state != TrustState.verified:
61
+ self._client.verify_device(device)
62
+
63
+ # Register both Room and ToDevice event paths
64
+ self._client.add_event_callback(self._callback_wrapper)
65
+ self._client.add_to_device_callback(self._callback_wrapper)
66
+
67
+ await self._client.sync_forever(timeout=30_000, full_state=True)
68
+
69
+ async def close(self):
70
+ if self._client:
71
+ await self._client.close()
matrix_agent/core.py ADDED
@@ -0,0 +1,67 @@
1
+ import importlib
2
+ import logging
3
+ from typing import Dict, Any, Optional, Type, List
4
+
5
+ logger = logging.getLogger(__name__)
6
+
7
+ class FactoryMeta(type):
8
+ def __call__(cls, *args, **kwargs):
9
+ name = args[0] if args else kwargs.get("name")
10
+ if cls.__name__ == "BaseFactory" and isinstance(name, str):
11
+ if name in cls._registry:
12
+ concrete_class = cls._registry[name]
13
+ else:
14
+ concrete_class = cls._lazy_load(name)
15
+ return super(FactoryMeta, cls).__call__(concrete_class, *args, **kwargs)
16
+ return super().__call__(*args, **kwargs)
17
+
18
+ class BaseFactory(metaclass=FactoryMeta):
19
+ plugin_dir: Optional[str] = None
20
+ _registry: Dict[str, Type["BaseFactory"]] = {}
21
+
22
+ def __init_subclass__(cls, name: Optional[str] = None, **kwargs):
23
+ super().__init_subclass__(**kwargs)
24
+ if name is None:
25
+ name = cls.__module__.split(".")[-1]
26
+
27
+ # Handle registry isolation for different factory types
28
+ if BaseFactory not in cls.__bases__:
29
+ factory_cls = cls.__bases__[0]
30
+ if not hasattr(factory_cls, "_registry") or factory_cls._registry is BaseFactory._registry:
31
+ setattr(factory_cls, "_registry", {})
32
+ factory_cls._registry[name] = cls
33
+
34
+ @classmethod
35
+ def _lazy_load(cls, name: str) -> Type["BaseFactory"]:
36
+ if not cls.plugin_dir:
37
+ raise RuntimeError(f"Factory {cls.__name__} has no 'plugin_dir' defined.")
38
+ try:
39
+ importlib.import_module(f"{cls.plugin_dir}.{name}")
40
+ return cls._registry[name]
41
+ except (ImportError, KeyError):
42
+ raise KeyError(f"Could not find registered implementation '{name}' in {cls.plugin_dir}")
43
+
44
+ class BaseHandler:
45
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
46
+ self.config = config or {}
47
+
48
+ def can_handle(self, request: Dict[str, Any]) -> bool:
49
+ raise NotImplementedError
50
+
51
+ async def handle(self, request: Dict[str, Any]) -> bool:
52
+ raise NotImplementedError
53
+
54
+ class BaseChain:
55
+ def __init__(self, factory: Type[BaseFactory], handler_names: List[str], global_config: Dict[str, Any]):
56
+ self.handlers = []
57
+ for name in handler_names:
58
+ config = global_config.get(name, {})
59
+ handler_cls = factory(name)
60
+ self.handlers.append(handler_cls(config))
61
+
62
+ async def execute(self, request: Dict[str, Any]):
63
+ for handler in self.handlers:
64
+ if handler.can_handle(request):
65
+ consumed = await handler.handle(request)
66
+ if consumed:
67
+ break
@@ -0,0 +1,16 @@
1
+ from typing import Any
2
+ from .core import BaseHandler
3
+
4
+ class MatrixHandler(BaseHandler):
5
+ """
6
+ Abstract Base Class for Matrix Handlers.
7
+ The 'request' object passed to these methods always contains:
8
+ {'event': nio_event, 'client': nio_client}
9
+ """
10
+ def can_handle(self, request: dict) -> bool:
11
+ # To be overridden by concrete handlers
12
+ return False
13
+
14
+ async def handle(self, request: dict) -> bool:
15
+ # To be overridden by concrete handlers
16
+ return False
@@ -0,0 +1,25 @@
1
+ Metadata-Version: 2.4
2
+ Name: matrix-agent
3
+ Version: 0.1.1
4
+ Summary: Plugin-driven matrix client framework.
5
+ Author-email: Peach <james@christhall.us>
6
+ License: MIT
7
+ Project-URL: Homepage, https://gitlab.com/girrasol/matrix-bot
8
+ Keywords: matrix,agent,bot
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Requires-Python: >=3.12
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: matrix-nio
16
+ Requires-Dist: pyyaml
17
+ Provides-Extra: dev
18
+ Requires-Dist: pytest>=7.0; extra == "dev"
19
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
20
+ Requires-Dist: black>=23.0; extra == "dev"
21
+ Requires-Dist: mypy>=1.0; extra == "dev"
22
+
23
+ # Matrix Bot
24
+
25
+
@@ -0,0 +1,8 @@
1
+ matrix_agent/__init__.py,sha256=9h0iYvIae-zddLFp2PYgEDC5oW743glYQRAeSotFvyY,208
2
+ matrix_agent/agent.py,sha256=EFEz4jTTg8fd2SAEyD3-F28dkJtVpZ9D0clxnfURw1A,2270
3
+ matrix_agent/core.py,sha256=8TmWZVcmMIrpcd_uE5hFV43-fBK1GeQ6VYWFwXWCWuA,2627
4
+ matrix_agent/handlers.py,sha256=W17sQzVjUnlvFZ8ODxoMIMBToK0VhkAiptF46Hbh_vg,501
5
+ matrix_agent-0.1.1.dist-info/METADATA,sha256=P_caHUBquxz0u8Q-qOSwk1lzEUHvxiXiz3hRO2HNvj8,757
6
+ matrix_agent-0.1.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
7
+ matrix_agent-0.1.1.dist-info/top_level.txt,sha256=AbvkOHrhUIWo2DQLcZnQ-Lmi5tzaDUgC3HVPXuiLcto,13
8
+ matrix_agent-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ matrix_agent