takopi-slack-plugin 0.0.15__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.
- takopi_slack_plugin/__init__.py +1 -0
- takopi_slack_plugin/backend.py +193 -0
- takopi_slack_plugin/bridge.py +1380 -0
- takopi_slack_plugin/client.py +254 -0
- takopi_slack_plugin/commands/__init__.py +3 -0
- takopi_slack_plugin/commands/dispatch.py +114 -0
- takopi_slack_plugin/commands/executor.py +192 -0
- takopi_slack_plugin/config.py +60 -0
- takopi_slack_plugin/engine.py +142 -0
- takopi_slack_plugin/onboarding.py +58 -0
- takopi_slack_plugin/outbox.py +165 -0
- takopi_slack_plugin/overrides.py +20 -0
- takopi_slack_plugin/thread_sessions.py +289 -0
- takopi_slack_plugin-0.0.15.dist-info/METADATA +151 -0
- takopi_slack_plugin-0.0.15.dist-info/RECORD +17 -0
- takopi_slack_plugin-0.0.15.dist-info/WHEEL +4 -0
- takopi_slack_plugin-0.0.15.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Slack transport plugin for Takopi."""
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import anyio
|
|
8
|
+
|
|
9
|
+
from takopi.api import (
|
|
10
|
+
EngineBackend,
|
|
11
|
+
SetupIssue,
|
|
12
|
+
SetupResult,
|
|
13
|
+
TransportBackend,
|
|
14
|
+
TransportRuntime,
|
|
15
|
+
ExecBridgeConfig,
|
|
16
|
+
ConfigError,
|
|
17
|
+
HOME_CONFIG_PATH,
|
|
18
|
+
install_issue,
|
|
19
|
+
load_settings,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
from .bridge import SlackBridgeConfig, SlackPresenter, SlackTransport, run_main_loop
|
|
23
|
+
from .client import SlackClient
|
|
24
|
+
from .config import SlackTransportSettings
|
|
25
|
+
from .onboarding import interactive_setup
|
|
26
|
+
from .thread_sessions import SlackThreadSessionStore, resolve_sessions_path
|
|
27
|
+
|
|
28
|
+
_CREATE_CONFIG_TITLE = "create a config"
|
|
29
|
+
_CONFIGURE_SLACK_TITLE = "configure slack"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _config_issue(path: Path, *, title: str) -> SetupIssue:
|
|
33
|
+
display = _display_path(path)
|
|
34
|
+
return SetupIssue(title, (f" {display}",))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _display_path(path: Path) -> str:
|
|
38
|
+
home = Path.home()
|
|
39
|
+
try:
|
|
40
|
+
return f"~/{path.relative_to(home)}"
|
|
41
|
+
except ValueError:
|
|
42
|
+
return str(path)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _expect_transport_settings(
|
|
46
|
+
transport_config: object, *, config_path: Path
|
|
47
|
+
) -> SlackTransportSettings:
|
|
48
|
+
return SlackTransportSettings.from_config(
|
|
49
|
+
transport_config, config_path=config_path
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def check_setup(
|
|
54
|
+
backend: EngineBackend,
|
|
55
|
+
*,
|
|
56
|
+
transport_override: str | None = None,
|
|
57
|
+
) -> SetupResult:
|
|
58
|
+
issues: list[SetupIssue] = []
|
|
59
|
+
config_path = HOME_CONFIG_PATH
|
|
60
|
+
cmd = backend.cli_cmd or backend.id
|
|
61
|
+
backend_issues: list[SetupIssue] = []
|
|
62
|
+
if shutil.which(cmd) is None:
|
|
63
|
+
backend_issues.append(install_issue(cmd, backend.install_cmd))
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
settings, config_path = load_settings()
|
|
67
|
+
if transport_override:
|
|
68
|
+
settings = settings.model_copy(update={"transport": transport_override})
|
|
69
|
+
if settings.transport != "slack":
|
|
70
|
+
issues.append(_config_issue(config_path, title=_CONFIGURE_SLACK_TITLE))
|
|
71
|
+
else:
|
|
72
|
+
try:
|
|
73
|
+
transport_config = settings.transport_config(
|
|
74
|
+
"slack", config_path=config_path
|
|
75
|
+
)
|
|
76
|
+
_expect_transport_settings(
|
|
77
|
+
transport_config, config_path=config_path
|
|
78
|
+
)
|
|
79
|
+
except ConfigError:
|
|
80
|
+
issues.append(_config_issue(config_path, title=_CONFIGURE_SLACK_TITLE))
|
|
81
|
+
except ConfigError:
|
|
82
|
+
issues.extend(backend_issues)
|
|
83
|
+
title = (
|
|
84
|
+
_CONFIGURE_SLACK_TITLE
|
|
85
|
+
if config_path.exists() and config_path.is_file()
|
|
86
|
+
else _CREATE_CONFIG_TITLE
|
|
87
|
+
)
|
|
88
|
+
issues.append(_config_issue(config_path, title=title))
|
|
89
|
+
return SetupResult(issues=issues, config_path=config_path)
|
|
90
|
+
|
|
91
|
+
issues.extend(backend_issues)
|
|
92
|
+
return SetupResult(issues=issues, config_path=config_path)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class SlackBackend(TransportBackend):
|
|
96
|
+
id = "slack"
|
|
97
|
+
description = "Slack bot"
|
|
98
|
+
|
|
99
|
+
def check_setup(
|
|
100
|
+
self,
|
|
101
|
+
engine_backend: EngineBackend,
|
|
102
|
+
*,
|
|
103
|
+
transport_override: str | None = None,
|
|
104
|
+
) -> SetupResult:
|
|
105
|
+
return check_setup(engine_backend, transport_override=transport_override)
|
|
106
|
+
|
|
107
|
+
async def interactive_setup(self, *, force: bool) -> bool:
|
|
108
|
+
return await interactive_setup(force=force)
|
|
109
|
+
|
|
110
|
+
def lock_token(self, *, transport_config: object, _config_path: Path) -> str | None:
|
|
111
|
+
settings = _expect_transport_settings(
|
|
112
|
+
transport_config, config_path=_config_path
|
|
113
|
+
)
|
|
114
|
+
return settings.bot_token
|
|
115
|
+
|
|
116
|
+
def build_and_run(
|
|
117
|
+
self,
|
|
118
|
+
*,
|
|
119
|
+
transport_config: object,
|
|
120
|
+
config_path: Path,
|
|
121
|
+
runtime: TransportRuntime,
|
|
122
|
+
final_notify: bool,
|
|
123
|
+
default_engine_override: str | None,
|
|
124
|
+
) -> None:
|
|
125
|
+
settings = _expect_transport_settings(
|
|
126
|
+
transport_config, config_path=config_path
|
|
127
|
+
)
|
|
128
|
+
startup_msg = _build_startup_message(runtime, startup_pwd=os.getcwd())
|
|
129
|
+
client = SlackClient(settings.bot_token)
|
|
130
|
+
transport = SlackTransport(client)
|
|
131
|
+
presenter = SlackPresenter(message_overflow=settings.message_overflow)
|
|
132
|
+
exec_cfg = ExecBridgeConfig(
|
|
133
|
+
transport=transport,
|
|
134
|
+
presenter=presenter,
|
|
135
|
+
final_notify=final_notify,
|
|
136
|
+
)
|
|
137
|
+
thread_store = SlackThreadSessionStore(
|
|
138
|
+
resolve_sessions_path(config_path)
|
|
139
|
+
)
|
|
140
|
+
cfg = SlackBridgeConfig(
|
|
141
|
+
client=client,
|
|
142
|
+
runtime=runtime,
|
|
143
|
+
channel_id=settings.channel_id,
|
|
144
|
+
app_token=settings.app_token,
|
|
145
|
+
startup_msg=startup_msg,
|
|
146
|
+
exec_cfg=exec_cfg,
|
|
147
|
+
thread_store=thread_store,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
async def run_loop() -> None:
|
|
151
|
+
await run_main_loop(
|
|
152
|
+
cfg,
|
|
153
|
+
watch_config=runtime.watch_config,
|
|
154
|
+
default_engine_override=default_engine_override,
|
|
155
|
+
transport_id=self.id,
|
|
156
|
+
transport_config=settings,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
anyio.run(run_loop)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _build_startup_message(runtime: TransportRuntime, *, startup_pwd: str) -> str:
|
|
163
|
+
available_engines = list(runtime.available_engine_ids())
|
|
164
|
+
missing_engines = list(runtime.missing_engine_ids())
|
|
165
|
+
misconfigured_engines = list(runtime.engine_ids_with_status("bad_config"))
|
|
166
|
+
failed_engines = list(runtime.engine_ids_with_status("load_error"))
|
|
167
|
+
|
|
168
|
+
engine_list = ", ".join(available_engines) if available_engines else "none"
|
|
169
|
+
|
|
170
|
+
notes: list[str] = []
|
|
171
|
+
if missing_engines:
|
|
172
|
+
notes.append(f"not installed: {', '.join(missing_engines)}")
|
|
173
|
+
if misconfigured_engines:
|
|
174
|
+
notes.append(f"misconfigured: {', '.join(misconfigured_engines)}")
|
|
175
|
+
if failed_engines:
|
|
176
|
+
notes.append(f"failed to load: {', '.join(failed_engines)}")
|
|
177
|
+
if notes:
|
|
178
|
+
engine_list = f"{engine_list} ({'; '.join(notes)})"
|
|
179
|
+
|
|
180
|
+
project_aliases = sorted(set(runtime.project_aliases()), key=str.lower)
|
|
181
|
+
project_list = ", ".join(project_aliases) if project_aliases else "none"
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
"takopi is ready\n\n"
|
|
185
|
+
f"default: `{runtime.default_engine}`\n"
|
|
186
|
+
f"agents: `{engine_list}`\n"
|
|
187
|
+
f"projects: `{project_list}`\n"
|
|
188
|
+
f"working in: `{startup_pwd}`"
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
slack_backend = SlackBackend()
|
|
193
|
+
BACKEND = slack_backend
|