|
@@ -1,5 +1,4 @@
|
|
|
-"""
|
|
|
|
|
-Chatto Platform Adapter for Hermes Agent.
|
|
|
|
|
|
|
+"""Chatto Platform Adapter for Hermes Agent.
|
|
|
|
|
|
|
|
A plugin-based gateway adapter that connects to a Chatto server
|
|
A plugin-based gateway adapter that connects to a Chatto server
|
|
|
(self-hosted team chat) and relays messages to/from the Hermes agent.
|
|
(self-hosted team chat) and relays messages to/from the Hermes agent.
|
|
@@ -12,7 +11,6 @@ including both outbound messaging and realtime WebSocket connections.
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
-from builtins import ImportError
|
|
|
|
|
import random
|
|
import random
|
|
|
|
|
|
|
|
from gateway.platforms.helpers import MessageDeduplicator
|
|
from gateway.platforms.helpers import MessageDeduplicator
|
|
@@ -34,22 +32,22 @@ import mimetypes
|
|
|
import os
|
|
import os
|
|
|
from datetime import datetime, timezone
|
|
from datetime import datetime, timezone
|
|
|
from enum import StrEnum
|
|
from enum import StrEnum
|
|
|
-from typing import Any, Dict, List, Literal, Optional, Tuple, cast
|
|
|
|
|
|
|
+from typing import Any, Dict, List, Optional, Tuple, cast
|
|
|
from urllib.parse import urlsplit
|
|
from urllib.parse import urlsplit
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
+from gateway.config import Platform, PlatformConfig
|
|
|
from gateway.platforms.base import (
|
|
from gateway.platforms.base import (
|
|
|
BasePlatformAdapter,
|
|
BasePlatformAdapter,
|
|
|
- SendResult,
|
|
|
|
|
MessageEvent,
|
|
MessageEvent,
|
|
|
MessageType,
|
|
MessageType,
|
|
|
ProcessingOutcome,
|
|
ProcessingOutcome,
|
|
|
|
|
+ SendResult,
|
|
|
cache_media_bytes,
|
|
cache_media_bytes,
|
|
|
get_inbound_media_max_bytes,
|
|
get_inbound_media_max_bytes,
|
|
|
validate_inbound_media_size,
|
|
validate_inbound_media_size,
|
|
|
)
|
|
)
|
|
|
-from gateway.config import Platform, PlatformConfig
|
|
|
|
|
|
|
|
|
|
# Chattolib imports (vendored)
|
|
# Chattolib imports (vendored)
|
|
|
# Using vendored chattolib from vendor/chattolib/
|
|
# Using vendored chattolib from vendor/chattolib/
|
|
@@ -68,17 +66,16 @@ try:
|
|
|
ChattoError,
|
|
ChattoError,
|
|
|
)
|
|
)
|
|
|
from chattolib.realtime import (
|
|
from chattolib.realtime import (
|
|
|
|
|
+ ChattoRealtimeCloseError,
|
|
|
ChattoRealtimeError,
|
|
ChattoRealtimeError,
|
|
|
- ChattoRealtimeCloseError, RealtimeEvent,
|
|
|
|
|
- stream_events
|
|
|
|
|
|
|
+ RealtimeEvent,
|
|
|
|
|
+ stream_events,
|
|
|
)
|
|
)
|
|
|
from chattolib.realtime_types import (
|
|
from chattolib.realtime_types import (
|
|
|
MessagePostedPayload,
|
|
MessagePostedPayload,
|
|
|
ReactionPayload,
|
|
ReactionPayload,
|
|
|
)
|
|
)
|
|
|
- from chattolib.types import (
|
|
|
|
|
- PresenceStatus, RoomKind, User
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ from chattolib.types import PresenceStatus, RoomKind, User
|
|
|
|
|
|
|
|
except ImportError as e:
|
|
except ImportError as e:
|
|
|
# Fail loudly: continuing here only defers the failure to a confusing
|
|
# Fail loudly: continuing here only defers the failure to a confusing
|
|
@@ -89,11 +86,13 @@ except ImportError as e:
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
|
from .platform_config import (
|
|
from .platform_config import (
|
|
|
- ChattoConfiguration, ChattoConstants,
|
|
|
|
|
|
|
+ ChattoConfiguration,
|
|
|
|
|
+ ChattoConstants,
|
|
|
)
|
|
)
|
|
|
except ImportError: # pragma: no cover - loaded as a top-level module (tests)
|
|
except ImportError: # pragma: no cover - loaded as a top-level module (tests)
|
|
|
from platform_config import (
|
|
from platform_config import (
|
|
|
- ChattoConfiguration, ChattoConstants,
|
|
|
|
|
|
|
+ ChattoConfiguration,
|
|
|
|
|
+ ChattoConstants,
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@@ -194,15 +193,16 @@ def chat_type_for_room_kind(kind: Optional[RoomKind]) -> HermesChatType:
|
|
|
# --------------------------------------------------------------------------- #
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
|
|
|
|
def hermes_adapter_factory(config: PlatformConfig):
|
|
def hermes_adapter_factory(config: PlatformConfig):
|
|
|
- """Factory wrapper that constructs ChattoAdapter from a PlatformConfig."""
|
|
|
|
|
|
|
+ """Construct a ChattoAdapter from a PlatformConfig."""
|
|
|
return ChattoAdapter(config)
|
|
return ChattoAdapter(config)
|
|
|
|
|
|
|
|
|
|
|
|
|
class ChattoAdapter(BasePlatformAdapter):
|
|
class ChattoAdapter(BasePlatformAdapter):
|
|
|
- """Chatto platform adapter — receives messages via WebSocket realtime,
|
|
|
|
|
- sends via ConnectRPC."""
|
|
|
|
|
|
|
+ """Chatto platform adapter.
|
|
|
|
|
+
|
|
|
|
|
+ Receives messages via WebSocket realtime, sends via ConnectRPC.
|
|
|
|
|
+ """
|
|
|
|
|
|
|
|
-
|
|
|
|
|
_SPLIT_THRESHOLD = 9900
|
|
_SPLIT_THRESHOLD = 9900
|
|
|
# Read by BasePlatformAdapter.max_message_length_for_chat(), which the
|
|
# Read by BasePlatformAdapter.max_message_length_for_chat(), which the
|
|
|
# gateway and the stream consumer use to chunk outgoing messages. Without
|
|
# gateway and the stream consumer use to chunk outgoing messages. Without
|
|
@@ -214,7 +214,7 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
supports_status_text: bool = True # client.update_custom_status
|
|
supports_status_text: bool = True # client.update_custom_status
|
|
|
|
|
|
|
|
def __init__(self, pconfig: PlatformConfig):
|
|
def __init__(self, pconfig: PlatformConfig):
|
|
|
- """Signature needs to be compatible with BasePlatformAdapter.__init__ """
|
|
|
|
|
|
|
+ """Signature needs to be compatible with BasePlatformAdapter.__init__."""
|
|
|
super().__init__(config=pconfig, platform=Platform(ChattoConstants.PLATFORM_NAME))
|
|
super().__init__(config=pconfig, platform=Platform(ChattoConstants.PLATFORM_NAME))
|
|
|
# "extra" has been pre-processed by Hermes-Framework to be a dict of extra config values insode PlatformConfig.
|
|
# "extra" has been pre-processed by Hermes-Framework to be a dict of extra config values insode PlatformConfig.
|
|
|
|
|
|
|
@@ -267,7 +267,6 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
|
|
|
|
|
async def _get_chatto_client(self: ChattoAdapter) -> Optional[ChattoClient]:
|
|
async def _get_chatto_client(self: ChattoAdapter) -> Optional[ChattoClient]:
|
|
|
"""Get or create a ChattoClient instance."""
|
|
"""Get or create a ChattoClient instance."""
|
|
|
-
|
|
|
|
|
if self._chatto_client is not None:
|
|
if self._chatto_client is not None:
|
|
|
return self._chatto_client
|
|
return self._chatto_client
|
|
|
|
|
|
|
@@ -328,11 +327,10 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
token: Optional[str] = None,
|
|
token: Optional[str] = None,
|
|
|
) -> ChattoClient:
|
|
) -> ChattoClient:
|
|
|
"""Return a connected ``ChattoClient`` using token or login/password."""
|
|
"""Return a connected ``ChattoClient`` using token or login/password."""
|
|
|
-
|
|
|
|
|
if token:
|
|
if token:
|
|
|
return ChattoClient(token=token, base_url=base_url)
|
|
return ChattoClient(token=token, base_url=base_url)
|
|
|
return await ChattoClient.login(login, password, base_url=base_url)
|
|
return await ChattoClient.login(login, password, base_url=base_url)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
async def connect(self, *, is_reconnect: bool = False) -> bool:
|
|
async def connect(self, *, is_reconnect: bool = False) -> bool:
|
|
|
"""Connect to Chatto and start the realtime event stream.
|
|
"""Connect to Chatto and start the realtime event stream.
|
|
|
|
|
|
|
@@ -340,7 +338,7 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
"""
|
|
"""
|
|
|
logger.info("Chatto: connecting...")
|
|
logger.info("Chatto: connecting...")
|
|
|
|
|
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if not await self._ensure_token():
|
|
if not await self._ensure_token():
|
|
|
return False
|
|
return False
|
|
|
|
|
|
|
@@ -386,8 +384,8 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
logger.info(
|
|
logger.info(
|
|
|
"Chatto: connected and authentiated to %s using login:'%s' (display_name:'%s' id: '%s')",
|
|
"Chatto: connected and authentiated to %s using login:'%s' (display_name:'%s' id: '%s')",
|
|
|
self.chatto_config.base_url.value,
|
|
self.chatto_config.base_url.value,
|
|
|
- self.me.login, self.me.display_name, self.me.id
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ self.me.login, self.me.display_name, self.me.id,
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
return True
|
|
return True
|
|
|
|
|
|
|
@@ -470,7 +468,7 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
except RuntimeError:
|
|
except RuntimeError:
|
|
|
logger.debug("Chatto: _seed_room aborted - no client available for %s", room_id)
|
|
logger.debug("Chatto: _seed_room aborted - no client available for %s", room_id)
|
|
|
return
|
|
return
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
for ev in timeline_page.events:
|
|
for ev in timeline_page.events:
|
|
|
if ev.id:
|
|
if ev.id:
|
|
|
self._mark_seen(ev.id)
|
|
self._mark_seen(ev.id)
|
|
@@ -555,7 +553,8 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
|
|
|
|
|
def _check_auth(self, user: User) -> bool:
|
|
def _check_auth(self, user: User) -> bool:
|
|
|
"""We roll our own auth-systen on the against the chatto_config'ured allowed_users etc.
|
|
"""We roll our own auth-systen on the against the chatto_config'ured allowed_users etc.
|
|
|
- because.. Hermes authz_mixin.py IS NOT SANE.
|
|
|
|
|
|
|
+
|
|
|
|
|
+ because.. Hermes authz_mixin.py IS NOT SANE.
|
|
|
"""
|
|
"""
|
|
|
if self.chatto_config.allow_all_users.value:
|
|
if self.chatto_config.allow_all_users.value:
|
|
|
return True
|
|
return True
|
|
@@ -691,7 +690,7 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
if message.actor_id in self._user_cache:
|
|
if message.actor_id in self._user_cache:
|
|
|
# try the user cache.
|
|
# try the user cache.
|
|
|
user = self._user_cache.get(message.actor_id)
|
|
user = self._user_cache.get(message.actor_id)
|
|
|
- else:
|
|
|
|
|
|
|
+ else:
|
|
|
# get the user and update cache.
|
|
# get the user and update cache.
|
|
|
directory_member = await client.get_user(user_id=message.actor_id)
|
|
directory_member = await client.get_user(user_id=message.actor_id)
|
|
|
if directory_member is None:
|
|
if directory_member is None:
|
|
@@ -706,10 +705,10 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
|
|
|
|
|
if not self._check_auth(user):
|
|
if not self._check_auth(user):
|
|
|
return
|
|
return
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
# Todo: use a function that either reads from cache or gets room kind again.
|
|
# Todo: use a function that either reads from cache or gets room kind again.
|
|
|
if self._room_kinds.get(message.room_id) is None:
|
|
if self._room_kinds.get(message.room_id) is None:
|
|
|
- room_viewer_state = await client.get_room(message.room_id)
|
|
|
|
|
|
|
+ room_viewer_state = await client.get_room(message.room_id)
|
|
|
if room_viewer_state is None:
|
|
if room_viewer_state is None:
|
|
|
return
|
|
return
|
|
|
if room_viewer_state.room is None:
|
|
if room_viewer_state.room is None:
|
|
@@ -732,7 +731,7 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
if mentioned is False:
|
|
if mentioned is False:
|
|
|
logger.debug("Discarding message. Bot was not mentionend but require_mention is '%s'.", self.chatto_config.require_mention.value)
|
|
logger.debug("Discarding message. Bot was not mentionend but require_mention is '%s'.", self.chatto_config.require_mention.value)
|
|
|
return
|
|
return
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
logger.info("mentioned: %s", mentioned)
|
|
logger.info("mentioned: %s", mentioned)
|
|
|
|
|
|
|
|
# With require_mention off we see every message in the channel, including
|
|
# With require_mention off we see every message in the channel, including
|
|
@@ -835,7 +834,7 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
"message_ts": payload.message_event_id,
|
|
"message_ts": payload.message_event_id,
|
|
|
"event_ts": event.id,
|
|
"event_ts": event.id,
|
|
|
"raw_event": event,
|
|
"raw_event": event,
|
|
|
- }
|
|
|
|
|
|
|
+ },
|
|
|
)
|
|
)
|
|
|
except Exception: # pragma: no cover - the hook contract is non-blocking
|
|
except Exception: # pragma: no cover - the hook contract is non-blocking
|
|
|
logger.debug("Chatto: reaction hook forwarding failed", exc_info=True)
|
|
logger.debug("Chatto: reaction hook forwarding failed", exc_info=True)
|
|
@@ -879,11 +878,10 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
|
|
|
|
|
async def _chattolib_event_loop(self) -> None:
|
|
async def _chattolib_event_loop(self) -> None:
|
|
|
"""Event loop using chattolib's stream_events.
|
|
"""Event loop using chattolib's stream_events.
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
This replaces the manual WebSocket loop with chattolib's high-level
|
|
This replaces the manual WebSocket loop with chattolib's high-level
|
|
|
stream_events() which provides pre-decoded RealtimeEvent objects.
|
|
stream_events() which provides pre-decoded RealtimeEvent objects.
|
|
|
"""
|
|
"""
|
|
|
-
|
|
|
|
|
delay = ChattoConstants.WS_RECONNECT_INITIAL_BACKOFF
|
|
delay = ChattoConstants.WS_RECONNECT_INITIAL_BACKOFF
|
|
|
while not self._closing:
|
|
while not self._closing:
|
|
|
try:
|
|
try:
|
|
@@ -963,11 +961,11 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
for room_with_state in rooms_list:
|
|
for room_with_state in rooms_list:
|
|
|
if not room_with_state:
|
|
if not room_with_state:
|
|
|
continue
|
|
continue
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
room_obj = room_with_state.room or None
|
|
room_obj = room_with_state.room or None
|
|
|
if not room_obj:
|
|
if not room_obj:
|
|
|
continue
|
|
continue
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
self._room_names[room_obj.id] = room_obj.name
|
|
self._room_names[room_obj.id] = room_obj.name
|
|
|
self._room_kinds[room_obj.id] = room_obj.kind
|
|
self._room_kinds[room_obj.id] = room_obj.kind
|
|
|
|
|
|
|
@@ -1512,7 +1510,7 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
await self.add_reaction(chat_id, message_id, "👀")
|
|
await self.add_reaction(chat_id, message_id, "👀")
|
|
|
|
|
|
|
|
async def on_processing_complete(
|
|
async def on_processing_complete(
|
|
|
- self, event: MessageEvent, outcome: ProcessingOutcome
|
|
|
|
|
|
|
+ self, event: MessageEvent, outcome: ProcessingOutcome,
|
|
|
) -> None:
|
|
) -> None:
|
|
|
"""Swap the 👀 reaction for ✅ (success) or ❌ (failure).
|
|
"""Swap the 👀 reaction for ✅ (success) or ❌ (failure).
|
|
|
|
|
|
|
@@ -1530,6 +1528,8 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
await self.add_reaction(chat_id, message_id, "✅")
|
|
await self.add_reaction(chat_id, message_id, "✅")
|
|
|
elif outcome == ProcessingOutcome.FAILURE:
|
|
elif outcome == ProcessingOutcome.FAILURE:
|
|
|
await self.add_reaction(chat_id, message_id, "❌")
|
|
await self.add_reaction(chat_id, message_id, "❌")
|
|
|
|
|
+ elif outcome == ProcessingOutcome.C:
|
|
|
|
|
+ await self.add_reaction(chat_id, message_id, "❌")
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------ #
|
|
# ------------------------------------------------------------------ #
|
|
|
# Asset upload (chunked)
|
|
# Asset upload (chunked)
|
|
@@ -1735,8 +1735,9 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
metadata: Optional[Dict[str, Any]] = None,
|
|
metadata: Optional[Dict[str, Any]] = None,
|
|
|
**kwargs,
|
|
**kwargs,
|
|
|
) -> SendResult:
|
|
) -> SendResult:
|
|
|
- """Send a local video as a native Chatto attachment (Chatto transcodes
|
|
|
|
|
- and plays it inline).
|
|
|
|
|
|
|
+ """Send a local video as a native Chatto attachment.
|
|
|
|
|
+
|
|
|
|
|
+ Chatto transcodes and plays it inline.
|
|
|
|
|
|
|
|
BasePlatformAdapter override
|
|
BasePlatformAdapter override
|
|
|
"""
|
|
"""
|
|
@@ -1941,17 +1942,17 @@ async def hermes_standalone_sender_fn(
|
|
|
force_document=False,
|
|
force_document=False,
|
|
|
) -> SendResult:
|
|
) -> SendResult:
|
|
|
"""Deliver a message to Chatto without a running gateway adapter. Do not modify signature.
|
|
"""Deliver a message to Chatto without a running gateway adapter. Do not modify signature.
|
|
|
|
|
+
|
|
|
Used by cron / scheduled routines that run out-of-process. Creates a
|
|
Used by cron / scheduled routines that run out-of-process. Creates a
|
|
|
short-lived chattolib client, posts, and closes.
|
|
short-lived chattolib client, posts, and closes.
|
|
|
"""
|
|
"""
|
|
|
-
|
|
|
|
|
chatto_config: ChattoConfiguration = ChattoConfiguration(pconfig=pconfig)
|
|
chatto_config: ChattoConfiguration = ChattoConfiguration(pconfig=pconfig)
|
|
|
|
|
|
|
|
# Create a temporary client for standalone sending — we need a base URL plus
|
|
# Create a temporary client for standalone sending — we need a base URL plus
|
|
|
# either a token or a full login/password pair.
|
|
# either a token or a full login/password pair.
|
|
|
has_credentials = bool(
|
|
has_credentials = bool(
|
|
|
chatto_config.token.value
|
|
chatto_config.token.value
|
|
|
- or (chatto_config.login.value and chatto_config.password.value)
|
|
|
|
|
|
|
+ or (chatto_config.login.value and chatto_config.password.value),
|
|
|
)
|
|
)
|
|
|
if not chatto_config.base_url.value or not has_credentials:
|
|
if not chatto_config.base_url.value or not has_credentials:
|
|
|
return SendResult(success=False, error="Chatto: base URL or credentials missing")
|
|
return SendResult(success=False, error="Chatto: base URL or credentials missing")
|
|
@@ -1990,12 +1991,12 @@ async def hermes_standalone_sender_fn(
|
|
|
|
|
|
|
|
|
|
|
|
|
def hermes_validate_config(config: PlatformConfig) -> bool:
|
|
def hermes_validate_config(config: PlatformConfig) -> bool:
|
|
|
- """"
|
|
|
|
|
|
|
+ """Check whether Chatto Plugin is configured.
|
|
|
|
|
+
|
|
|
Function name should be the same as register argument name with "hermes_" prefix, so we
|
|
Function name should be the same as register argument name with "hermes_" prefix, so we
|
|
|
know that it is needed for plugin register(). Do not change signature.
|
|
know that it is needed for plugin register(). Do not change signature.
|
|
|
- - config
|
|
|
|
|
- Check whether Chatto Plugin is configured. Compare to hermes_is_connected()."""
|
|
|
|
|
-
|
|
|
|
|
|
|
+ Takes ``config``. Compare to hermes_is_connected().
|
|
|
|
|
+ """
|
|
|
chatto_config = ChattoConfiguration(pconfig=config)
|
|
chatto_config = ChattoConfiguration(pconfig=config)
|
|
|
|
|
|
|
|
if len(chatto_config.allowed_users.value) > 0 and chatto_config.allow_all_users.value:
|
|
if len(chatto_config.allowed_users.value) > 0 and chatto_config.allow_all_users.value:
|
|
@@ -2015,9 +2016,13 @@ def hermes_validate_config(config: PlatformConfig) -> bool:
|
|
|
|
|
|
|
|
def hermes_check_fn() -> bool:
|
|
def hermes_check_fn() -> bool:
|
|
|
"""Check if Chatto is configured and dependencies are available.
|
|
"""Check if Chatto is configured and dependencies are available.
|
|
|
- Add real logic?! Or just .. there are no dependencies.. always return true. Really. Docs suck."""
|
|
|
|
|
|
|
+
|
|
|
|
|
+ Add real logic?! Or just .. there are no dependencies.. always return true. Really. Docs suck.
|
|
|
|
|
+ """
|
|
|
try:
|
|
try:
|
|
|
- from .vendor.common.chattolib import client # noqa: F401 — vendored dependency probe
|
|
|
|
|
|
|
+ from .vendor.common.chattolib import (
|
|
|
|
|
+ client, # noqa: F401 — vendored dependency probe
|
|
|
|
|
+ )
|
|
|
return True
|
|
return True
|
|
|
except ImportError:
|
|
except ImportError:
|
|
|
return False
|
|
return False
|
|
@@ -2028,26 +2033,25 @@ def hermes_check_fn() -> bool:
|
|
|
# ---------------------------------------------------------------------------
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
def hermes_is_connected(config: PlatformConfig) -> bool:
|
|
def hermes_is_connected(config: PlatformConfig) -> bool:
|
|
|
- """Check whether Chatto Plugin is connected. But where to? To the Hermes Agent? To Chatto Server?
|
|
|
|
|
- The Hermes Agent plugin docs suck and it seems there are many functions to do the same."""
|
|
|
|
|
|
|
+ """Check whether Chatto Plugin is connected. But where to: the Hermes Agent or the Chatto server.
|
|
|
|
|
+
|
|
|
|
|
+ The Hermes Agent plugin docs suck and it seems there are many functions to do the same.
|
|
|
|
|
+ """
|
|
|
return bool(hermes_validate_config(config) and config.enabled)
|
|
return bool(hermes_validate_config(config) and config.enabled)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def hermes_setup_fn() -> None:
|
|
def hermes_setup_fn() -> None:
|
|
|
"""Interactive setup wizard for Chatto. Is called by and only works in Hermes CLI context.
|
|
"""Interactive setup wizard for Chatto. Is called by and only works in Hermes CLI context.
|
|
|
|
|
+
|
|
|
Function name should be the same as register argument name with "hermes_" prefix, so we
|
|
Function name should be the same as register argument name with "hermes_" prefix, so we
|
|
|
know that it is needed for plugin register().
|
|
know that it is needed for plugin register().
|
|
|
"""
|
|
"""
|
|
|
from hermes_cli.setup import (
|
|
from hermes_cli.setup import (
|
|
|
|
|
+ print_success,
|
|
|
prompt,
|
|
prompt,
|
|
|
prompt_yes_no,
|
|
prompt_yes_no,
|
|
|
save_env_value,
|
|
save_env_value,
|
|
|
- get_env_value,
|
|
|
|
|
- print_header,
|
|
|
|
|
- print_info,
|
|
|
|
|
- print_warning,
|
|
|
|
|
- print_success,
|
|
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
url = prompt(
|
|
url = prompt(
|
|
@@ -2094,7 +2098,6 @@ def hermes_env_enablement_fn() -> Optional[dict]:
|
|
|
Function name should be the same as register argument name with "hermes_" prefix, so we
|
|
Function name should be the same as register argument name with "hermes_" prefix, so we
|
|
|
know that it is needed for plugin register().
|
|
know that it is needed for plugin register().
|
|
|
"""
|
|
"""
|
|
|
-
|
|
|
|
|
# Seed keys must be the config.yaml "extra" keys (config_key), NOT the env
|
|
# Seed keys must be the config.yaml "extra" keys (config_key), NOT the env
|
|
|
# var names — ChattoConfiguration reads extra[config_key].
|
|
# var names — ChattoConfiguration reads extra[config_key].
|
|
|
seed: Dict[str, Any] = {
|
|
seed: Dict[str, Any] = {
|