xyberos-slack 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- xyberos_slack-0.1.0/PKG-INFO +56 -0
- xyberos_slack-0.1.0/README.md +46 -0
- xyberos_slack-0.1.0/pyproject.toml +23 -0
- xyberos_slack-0.1.0/setup.cfg +4 -0
- xyberos_slack-0.1.0/tests/test_client.py +49 -0
- xyberos_slack-0.1.0/tests/test_plugin.py +28 -0
- xyberos_slack-0.1.0/xyberos_slack/__init__.py +6 -0
- xyberos_slack-0.1.0/xyberos_slack/client.py +68 -0
- xyberos_slack-0.1.0/xyberos_slack/http.py +57 -0
- xyberos_slack-0.1.0/xyberos_slack/plugin.py +60 -0
- xyberos_slack-0.1.0/xyberos_slack.egg-info/PKG-INFO +56 -0
- xyberos_slack-0.1.0/xyberos_slack.egg-info/SOURCES.txt +14 -0
- xyberos_slack-0.1.0/xyberos_slack.egg-info/dependency_links.txt +1 -0
- xyberos_slack-0.1.0/xyberos_slack.egg-info/entry_points.txt +2 -0
- xyberos_slack-0.1.0/xyberos_slack.egg-info/requires.txt +1 -0
- xyberos_slack-0.1.0/xyberos_slack.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: xyberos-slack
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Slack Web API plugin (RFC-0019, M7): post messages, list channels
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Keywords: xyberos,plugin,slack,tool
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: xyberos>=1.0
|
|
10
|
+
|
|
11
|
+
# xyberos-slack
|
|
12
|
+
|
|
13
|
+
**Slack Web API plugin — RFC-0019, M7 (community wave).** Post messages and
|
|
14
|
+
list channels from Xyberos agents via the Slack Web API. Stdlib-only with an
|
|
15
|
+
injectable transport for tests.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install -e ./slack
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from xyberos import create_app
|
|
27
|
+
from xyberos_slack import SlackPlugin
|
|
28
|
+
|
|
29
|
+
app = create_app()
|
|
30
|
+
app.load_plugin(SlackPlugin()) # token from SLACK_TOKEN
|
|
31
|
+
|
|
32
|
+
app.tools.execute("slack_post_message", None, channel="general", text="hello")
|
|
33
|
+
app.tools.execute("slack_list_channels", None, limit=100)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Tools
|
|
37
|
+
|
|
38
|
+
| Tool | Notes |
|
|
39
|
+
| ---- | ----- |
|
|
40
|
+
| `slack_post_message(channel, text)` | post to a channel |
|
|
41
|
+
| `slack_list_channels(limit=100)` | public channels |
|
|
42
|
+
|
|
43
|
+
Requires `SLACK_TOKEN` (a bot token with `chat:write` / `channels:read`).
|
|
44
|
+
|
|
45
|
+
## Tests
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install pytest
|
|
49
|
+
pytest tests/
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Canned responses via an injectable transport — no network.
|
|
53
|
+
|
|
54
|
+
## Ship location
|
|
55
|
+
|
|
56
|
+
Plugin (`xyberos.plugins` entry point) — community wave (M7).
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# xyberos-slack
|
|
2
|
+
|
|
3
|
+
**Slack Web API plugin — RFC-0019, M7 (community wave).** Post messages and
|
|
4
|
+
list channels from Xyberos agents via the Slack Web API. Stdlib-only with an
|
|
5
|
+
injectable transport for tests.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install -e ./slack
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from xyberos import create_app
|
|
17
|
+
from xyberos_slack import SlackPlugin
|
|
18
|
+
|
|
19
|
+
app = create_app()
|
|
20
|
+
app.load_plugin(SlackPlugin()) # token from SLACK_TOKEN
|
|
21
|
+
|
|
22
|
+
app.tools.execute("slack_post_message", None, channel="general", text="hello")
|
|
23
|
+
app.tools.execute("slack_list_channels", None, limit=100)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Tools
|
|
27
|
+
|
|
28
|
+
| Tool | Notes |
|
|
29
|
+
| ---- | ----- |
|
|
30
|
+
| `slack_post_message(channel, text)` | post to a channel |
|
|
31
|
+
| `slack_list_channels(limit=100)` | public channels |
|
|
32
|
+
|
|
33
|
+
Requires `SLACK_TOKEN` (a bot token with `chat:write` / `channels:read`).
|
|
34
|
+
|
|
35
|
+
## Tests
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install pytest
|
|
39
|
+
pytest tests/
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Canned responses via an injectable transport — no network.
|
|
43
|
+
|
|
44
|
+
## Ship location
|
|
45
|
+
|
|
46
|
+
Plugin (`xyberos.plugins` entry point) — community wave (M7).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "xyberos-slack"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Slack Web API plugin (RFC-0019, M7): post messages, list channels"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = {text = "Apache-2.0"}
|
|
12
|
+
dependencies = ["xyberos>=1.0"]
|
|
13
|
+
keywords = ["xyberos", "plugin", "slack", "tool"]
|
|
14
|
+
|
|
15
|
+
[project.entry-points."xyberos.plugins"]
|
|
16
|
+
slack = "xyberos_slack.plugin:plugin"
|
|
17
|
+
|
|
18
|
+
[tool.setuptools]
|
|
19
|
+
packages = ["xyberos_slack"]
|
|
20
|
+
|
|
21
|
+
[tool.pytest.ini_options]
|
|
22
|
+
testpaths = ["tests"]
|
|
23
|
+
pythonpath = ["."]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Tests for the Slack Web API client (injectable transport, no network)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from xyberos.exceptions.provider import ProviderError
|
|
7
|
+
|
|
8
|
+
from xyberos_slack import SlackClient
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _fake_request():
|
|
12
|
+
def request(method, url, **kwargs):
|
|
13
|
+
if url.endswith("/chat.postMessage"):
|
|
14
|
+
return 200, {"ok": True, "channel": "C123", "ts": "1700000000.000001"}
|
|
15
|
+
if url.endswith("/conversations.list"):
|
|
16
|
+
return 200, {"ok": True, "channels": [{"id": "C123", "name": "general"}]}
|
|
17
|
+
return 200, {"ok": False, "error": "not_authed"}
|
|
18
|
+
|
|
19
|
+
return request
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_post_message():
|
|
23
|
+
request = _fake_request()
|
|
24
|
+
client = SlackClient(token="t", request=request)
|
|
25
|
+
result = client.post_message("general", "hello")
|
|
26
|
+
assert result == {"ok": True, "channel": "C123", "ts": "1700000000.000001"}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_list_channels():
|
|
30
|
+
request = _fake_request()
|
|
31
|
+
client = SlackClient(token="t", request=request)
|
|
32
|
+
channels = client.list_channels(limit=50)
|
|
33
|
+
assert channels == [{"id": "C123", "name": "general"}]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_slack_ok_false_raises():
|
|
37
|
+
def request(method, url, **kwargs):
|
|
38
|
+
return 200, {"ok": False, "error": "not_authed"}
|
|
39
|
+
|
|
40
|
+
client = SlackClient(token="t", request=request)
|
|
41
|
+
with pytest.raises(ProviderError, match="not_authed"):
|
|
42
|
+
client.list_channels()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_requires_token():
|
|
46
|
+
request = _fake_request()
|
|
47
|
+
client = SlackClient(token=None, request=request)
|
|
48
|
+
with pytest.raises(ProviderError, match="token"):
|
|
49
|
+
client.post_message("general", "hi")
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Tests for loading the Slack plugin into a Xyberos app."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from xyberos import create_app
|
|
6
|
+
|
|
7
|
+
from xyberos_slack import SlackPlugin
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _fake_request():
|
|
11
|
+
def request(method, url, **kwargs):
|
|
12
|
+
if url.endswith("/chat.postMessage"):
|
|
13
|
+
return 200, {"ok": True, "channel": "C123", "ts": "1"}
|
|
14
|
+
return 200, {"ok": True, "channels": [{"id": "C123", "name": "general"}]}
|
|
15
|
+
|
|
16
|
+
return request
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_plugin_registers_and_executes():
|
|
20
|
+
app = create_app()
|
|
21
|
+
app.load_plugin(SlackPlugin(token="t", request=_fake_request()))
|
|
22
|
+
assert "slack_post_message" in app.tools.names
|
|
23
|
+
assert "slack_list_channels" in app.tools.names
|
|
24
|
+
|
|
25
|
+
result = app.tools.execute("slack_post_message", None, channel="general", text="hi")
|
|
26
|
+
assert result["ok"] is True
|
|
27
|
+
|
|
28
|
+
app.unload_plugin("slack")
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""A minimal Slack Web API client (stdlib ``urllib``, injectable transport)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from xyberos.exceptions.provider import ProviderError
|
|
9
|
+
|
|
10
|
+
from .http import RequestTransport, default_request
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _raise_for_status(status: int, body: Any) -> None:
|
|
14
|
+
if 200 <= status < 300:
|
|
15
|
+
return
|
|
16
|
+
message = body if isinstance(body, str) else str(body)
|
|
17
|
+
raise ProviderError(f"Slack API returned HTTP {status}: {message[:200]}")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SlackClient:
|
|
21
|
+
"""Thin client for the Slack Web API."""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
token: str | None = None,
|
|
26
|
+
*,
|
|
27
|
+
base_url: str = "https://slack.com/api",
|
|
28
|
+
request: RequestTransport | None = None,
|
|
29
|
+
timeout: float = 30.0,
|
|
30
|
+
) -> None:
|
|
31
|
+
self._token = token if token is not None else os.getenv("SLACK_TOKEN")
|
|
32
|
+
self._base_url = base_url.rstrip("/")
|
|
33
|
+
self._request = request or default_request
|
|
34
|
+
self._timeout = timeout
|
|
35
|
+
|
|
36
|
+
def post_message(self, channel: str, text: str) -> dict[str, Any]:
|
|
37
|
+
status, body = self._request(
|
|
38
|
+
"POST",
|
|
39
|
+
f"{self._base_url}/chat.postMessage",
|
|
40
|
+
json_body={"channel": channel, "text": text},
|
|
41
|
+
headers=self._headers(),
|
|
42
|
+
timeout=self._timeout,
|
|
43
|
+
)
|
|
44
|
+
_raise_for_status(status, body)
|
|
45
|
+
self._check_ok(body, "post_message")
|
|
46
|
+
return {"ok": True, "channel": body.get("channel"), "ts": body.get("ts")}
|
|
47
|
+
|
|
48
|
+
def list_channels(self, limit: int = 100) -> list[dict[str, Any]]:
|
|
49
|
+
status, body = self._request(
|
|
50
|
+
"GET",
|
|
51
|
+
f"{self._base_url}/conversations.list",
|
|
52
|
+
query={"types": "public_channel", "limit": limit},
|
|
53
|
+
headers=self._headers(),
|
|
54
|
+
timeout=self._timeout,
|
|
55
|
+
)
|
|
56
|
+
_raise_for_status(status, body)
|
|
57
|
+
self._check_ok(body, "list_channels")
|
|
58
|
+
return [{"id": channel.get("id"), "name": channel.get("name")} for channel in body.get("channels", [])]
|
|
59
|
+
|
|
60
|
+
def _headers(self) -> dict[str, str]:
|
|
61
|
+
if not self._token:
|
|
62
|
+
raise ProviderError("Slack requires a token (set SLACK_TOKEN)")
|
|
63
|
+
return {"Authorization": f"Bearer {self._token}"}
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def _check_ok(body: Any, action: str) -> None:
|
|
67
|
+
if isinstance(body, dict) and body.get("ok") is False:
|
|
68
|
+
raise ProviderError(f"Slack '{action}' failed: {body.get('error')}")
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""A tiny stdlib HTTP helper (no third-party deps).
|
|
2
|
+
|
|
3
|
+
``default_request`` performs one HTTP request with ``urllib`` and returns
|
|
4
|
+
``(status, body)`` where ``body`` is parsed JSON when the response is JSON,
|
|
5
|
+
otherwise raw text. Injectable so tests run without a network.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import urllib.error
|
|
12
|
+
import urllib.parse
|
|
13
|
+
import urllib.request
|
|
14
|
+
from typing import Any, Callable
|
|
15
|
+
|
|
16
|
+
#: (method, url, *, json_body, headers, query, timeout) -> (status, body)
|
|
17
|
+
RequestTransport = Callable[..., tuple[int, Any]]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def default_request(
|
|
21
|
+
method: str,
|
|
22
|
+
url: str,
|
|
23
|
+
*,
|
|
24
|
+
json_body: Any = None,
|
|
25
|
+
headers: dict[str, str] | None = None,
|
|
26
|
+
query: dict[str, Any] | None = None,
|
|
27
|
+
timeout: float = 30.0,
|
|
28
|
+
) -> tuple[int, Any]:
|
|
29
|
+
"""Send one request and return ``(status, parsed_json_or_text)``."""
|
|
30
|
+
final_url = url
|
|
31
|
+
if query:
|
|
32
|
+
separator = "&" if "?" in url else "?"
|
|
33
|
+
final_url = url + separator + urllib.parse.urlencode(query)
|
|
34
|
+
|
|
35
|
+
data: bytes | None = None
|
|
36
|
+
request_headers = dict(headers or {})
|
|
37
|
+
if json_body is not None:
|
|
38
|
+
data = json.dumps(json_body).encode("utf-8")
|
|
39
|
+
request_headers.setdefault("Content-Type", "application/json")
|
|
40
|
+
|
|
41
|
+
request = urllib.request.Request(
|
|
42
|
+
final_url, data=data, headers=request_headers, method=method
|
|
43
|
+
)
|
|
44
|
+
try:
|
|
45
|
+
with urllib.request.urlopen(request, timeout=timeout) as response:
|
|
46
|
+
raw = response.read()
|
|
47
|
+
content_type = response.headers.get("Content-Type", "")
|
|
48
|
+
except urllib.error.HTTPError as exc:
|
|
49
|
+
return exc.code, exc.read().decode("utf-8", errors="replace")
|
|
50
|
+
|
|
51
|
+
text = raw.decode("utf-8", errors="replace")
|
|
52
|
+
if "application/json" in content_type or text.lstrip().startswith("{"):
|
|
53
|
+
try:
|
|
54
|
+
return 200, json.loads(text)
|
|
55
|
+
except json.JSONDecodeError:
|
|
56
|
+
return 200, text
|
|
57
|
+
return 200, text
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Slack plugin entry point (RFC-0019, M7)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, cast
|
|
6
|
+
|
|
7
|
+
from xyberos.contracts import Plugin, Tool
|
|
8
|
+
from xyberos.tools import FunctionTool
|
|
9
|
+
|
|
10
|
+
from .client import SlackClient
|
|
11
|
+
from .http import RequestTransport
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _pop_tool(registry: Any, name: str) -> None:
|
|
15
|
+
unregister = getattr(registry, "unregister", None)
|
|
16
|
+
if callable(unregister):
|
|
17
|
+
unregister(name)
|
|
18
|
+
return
|
|
19
|
+
store = getattr(registry, "_tools", None)
|
|
20
|
+
if isinstance(store, dict):
|
|
21
|
+
cast(dict[str, Any], store).pop(name, None)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SlackPlugin(Plugin):
|
|
25
|
+
"""Registers Slack tools (post messages / list channels)."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, token: str | None = None, *, request: RequestTransport | None = None) -> None:
|
|
28
|
+
self._client = SlackClient(token, request=request)
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def name(self) -> str:
|
|
32
|
+
return "slack"
|
|
33
|
+
|
|
34
|
+
def tools(self) -> list[Tool]:
|
|
35
|
+
client = self._client
|
|
36
|
+
|
|
37
|
+
def _post_message(channel: str, text: str) -> dict[str, Any]:
|
|
38
|
+
return client.post_message(channel, text)
|
|
39
|
+
|
|
40
|
+
def _list_channels(limit: int = 100) -> list[dict[str, Any]]:
|
|
41
|
+
return client.list_channels(limit)
|
|
42
|
+
|
|
43
|
+
return [
|
|
44
|
+
FunctionTool("slack_post_message", _post_message, description="Post a message to a Slack channel."),
|
|
45
|
+
FunctionTool("slack_list_channels", _list_channels, description="List public Slack channels."),
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
def register(self, kernel: object) -> None:
|
|
49
|
+
registry = kernel.resolve("tools")
|
|
50
|
+
for tool in self.tools():
|
|
51
|
+
registry.register(tool)
|
|
52
|
+
|
|
53
|
+
def unregister(self, kernel: object) -> None:
|
|
54
|
+
registry = kernel.resolve("tools")
|
|
55
|
+
for tool in self.tools():
|
|
56
|
+
_pop_tool(registry, tool.name)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
#: Auto-discovered by ``app.load_entry_points()``.
|
|
60
|
+
plugin = SlackPlugin()
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: xyberos-slack
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Slack Web API plugin (RFC-0019, M7): post messages, list channels
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Keywords: xyberos,plugin,slack,tool
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: xyberos>=1.0
|
|
10
|
+
|
|
11
|
+
# xyberos-slack
|
|
12
|
+
|
|
13
|
+
**Slack Web API plugin — RFC-0019, M7 (community wave).** Post messages and
|
|
14
|
+
list channels from Xyberos agents via the Slack Web API. Stdlib-only with an
|
|
15
|
+
injectable transport for tests.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install -e ./slack
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from xyberos import create_app
|
|
27
|
+
from xyberos_slack import SlackPlugin
|
|
28
|
+
|
|
29
|
+
app = create_app()
|
|
30
|
+
app.load_plugin(SlackPlugin()) # token from SLACK_TOKEN
|
|
31
|
+
|
|
32
|
+
app.tools.execute("slack_post_message", None, channel="general", text="hello")
|
|
33
|
+
app.tools.execute("slack_list_channels", None, limit=100)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Tools
|
|
37
|
+
|
|
38
|
+
| Tool | Notes |
|
|
39
|
+
| ---- | ----- |
|
|
40
|
+
| `slack_post_message(channel, text)` | post to a channel |
|
|
41
|
+
| `slack_list_channels(limit=100)` | public channels |
|
|
42
|
+
|
|
43
|
+
Requires `SLACK_TOKEN` (a bot token with `chat:write` / `channels:read`).
|
|
44
|
+
|
|
45
|
+
## Tests
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install pytest
|
|
49
|
+
pytest tests/
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Canned responses via an injectable transport — no network.
|
|
53
|
+
|
|
54
|
+
## Ship location
|
|
55
|
+
|
|
56
|
+
Plugin (`xyberos.plugins` entry point) — community wave (M7).
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
tests/test_client.py
|
|
4
|
+
tests/test_plugin.py
|
|
5
|
+
xyberos_slack/__init__.py
|
|
6
|
+
xyberos_slack/client.py
|
|
7
|
+
xyberos_slack/http.py
|
|
8
|
+
xyberos_slack/plugin.py
|
|
9
|
+
xyberos_slack.egg-info/PKG-INFO
|
|
10
|
+
xyberos_slack.egg-info/SOURCES.txt
|
|
11
|
+
xyberos_slack.egg-info/dependency_links.txt
|
|
12
|
+
xyberos_slack.egg-info/entry_points.txt
|
|
13
|
+
xyberos_slack.egg-info/requires.txt
|
|
14
|
+
xyberos_slack.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
xyberos>=1.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
xyberos_slack
|