Metadata-Version: 2.5
Name: render-lab-tasks-slack
Version: 0.1.1
Summary: Slack messaging tasks for Render Workflows
Project-URL: Repository, https://github.com/render-lab/render-tasks-python
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.12
Requires-Dist: httpx<0.29,>=0.28
Requires-Dist: render-lab-tasks-core<0.2,>=0.1.1
Requires-Dist: render-lab-triggers<0.2,>=0.1.0
Requires-Dist: render==1.0.1
Description-Content-Type: text/markdown

# render-lab-tasks-slack

Slack messaging for Render Workflows. Version 0.1.1 implements all ten
registered tasks and the registration-free webhook adapter. Version 0.1.0
contained only `slack.postMessage`.

Install from [PyPI](https://pypi.org/project/render-lab-tasks-slack/) with Python 3.12+:

```sh
pip install render-lab-tasks-slack==0.1.1
```

```python
from render import TaskContext, Workflows
from render_lab_tasks_slack.tasks import app as slack_app, post_message

app = Workflows.from_workflows(slack_app)


@app.task(name="example.notify")
async def notify(ctx: TaskContext):
    return await ctx.run(post_message, {"text": "Hello from Python"})
```

Call `app.start()` from the workflow entry point. `.tasks` also exports
`post_message_impl(ctx, input, deps=...)` for testing and re-registration under
your own task name. Inject `SlackDeps` with fake `SlackPort`/`SlackWebPort` objects.

## Contract

Input requires `text` and supports `title`, `context`, `markdown`, `blocks`,
`attachments`, `channel`, and `username`. Explicit `blocks` (including an empty
list) override generated blocks. Titles, context, and markdown generate Block Kit;
double-star markdown bold becomes Slack single-star bold.

Delivery matches the TypeScript task:

1. A channel plus `SLACK_BOT_TOKEN` uses `chat.postMessage` and returns
   `{delivered: true, channel, ts}`. This route sends text and blocks; legacy
   attachments and username are webhook-only, matching the TS implementation.
2. Otherwise `SLACK_WEBHOOK_URL` is used. Its destination is fixed by Slack;
   the input channel is informational and is not sent in the webhook payload.
3. Without a usable webhook, print the fallback text and return `delivered: false`.
   A bot token without a channel does not select the Web API route.

Absent optional result fields are omitted rather than serialized as null.

## Environment

| Variable | Required | Purpose |
| --- | --- | --- |
| SLACK_WEBHOOK_URL | No | Incoming webhook destination |
| SLACK_BOT_TOKEN | No | Bot token for posting to an explicit channel |

Credentials are read at execution time. HTTPX has a 30-second timeout and no
automatic retries. Render retries failures up to four times with a 1000 ms base
delay and 2x backoff. Slack Web API `ok: false` responses raise even with HTTP 200.
Slack posts are not exactly-once: a retry after an uncertain delivery can duplicate
a message. Webhook exceptions omit the credential-bearing URL.

## Full Web API surface

`open_thread`, `update_message`, `await_reaction`, `await_reply`, `add_reaction`,
`schedule_message`, `list_channel_history`, `get_permalink`, and `upload_file`
are available from `.tasks` with raw `*_impl` exports. They require
`SLACK_BOT_TOKEN` or an injected `SlackDeps.web`. Await operations make one read
per durable attempt and throw while the requested reaction/reply is absent.
The upload port preserves the pinned TS `files.upload` endpoint; its current
vendor availability remains a live-verification backlog item in both repos.

## Webhook adapter

`render_lab_tasks_slack.webhooks.slack_adapter(on_event, ...)` returns a
registration-free adapter for `render_lab_triggers`. The callback receives the
verified event and returns `{"task": "namespace.task", "args": [event]}` or `None`.
The signing credential `SLACK_SIGNING_SECRET` is read inside verification unless
provided explicitly. Importing this module does not register tasks. Signature
checks use the raw body; live delivery remains in the testing backlog.
