Slack¶
rivers.integrations.slack posts to Slack when assets or runs fail. SlackResource holds the bot token. Its methods build the hooks and the sensor that post through it.
Setup¶
Create a Slack app with a bot token that has the chat:write scope, and invite the bot to the channel. SlackResource reads the token from SLACK_TOKEN:
from rivers.integrations.slack import SlackResource
slack = SlackResource() # token from SLACK_TOKEN
slack = SlackResource(token="xoxb-...") # or explicit
The token is a secret, a SecretStr: reprs and logs show **********, and the UI's launch dialog does not show it. slack.token.get_secret_value() returns it.
Alert once per failed run¶
run_failure_sensor returns a run-status sensor that posts one message per failed run of the code location:
import rivers as rs
repo = rs.CodeRepository(
assets=[orders, revenue],
sensors=[
slack.run_failure_sensor(
"#data-alerts",
ui_location_url="https://rivers.example.com/locations/prod/etl",
default_status=rs.SensorStatus.Running,
)
],
)
The daemon evaluates the sensor only with default_status=rs.SensorStatus.Running.
The default message names the run and its job, and lists each failed step with its error. For a run that failed to launch, it shows the launch error instead:
| Parameter | Default | Description |
|---|---|---|
channel |
required | Channel name or id, e.g. "#data-alerts". |
text_fn |
built-in | (RunStatusSensorContext) -> str. Builds the message text. |
blocks_fn |
None |
(RunStatusSensorContext) -> list[dict]. Builds Block Kit blocks; the text is then the notification fallback. |
ui_location_url |
None |
The code location's UI URL. When set, the message links to the run at {ui_location_url}/runs/{run_id}. |
name |
"slack_run_failure_sensor" |
Sensor name. Two Slack sensors in one repository need distinct names. |
minimum_interval |
"30s" |
Minimum interval between ticks. |
monitored_jobs |
None |
Only report runs of these jobs. None reports every failed run. |
default_status |
Stopped |
Whether the daemon evaluates the sensor. |
Each tick posts for at most 5 runs; later failures go out on the next ticks. See how runs are picked.
Alert per asset¶
failure_hook and success_hook return hooks that post when an asset step fails or succeeds:
@rs.Asset(hooks=[slack.failure_hook("#data-alerts")])
def orders() -> int: ...
@rs.Asset(hooks=[slack.success_hook("#data-releases", text_fn=lambda c: f"`{c.asset_name}` is fresh")])
def revenue() -> int: ...
| Parameter | Default | Description |
|---|---|---|
channel |
required | Channel name or id. |
text_fn |
built-in | (HookContext) -> str. The default names the asset and run, and quotes the error on failure. |
ui_location_url |
None |
The code location's UI URL. When set, the message links to the run. |
name |
"slack_failure_hook" / "slack_success_hook" |
Hook name. |
A hook fires once per failed step, so a run with three failed assets sends three messages. Use run_failure_sensor for one message per run. A Slack error in a hook is logged and does not fail the step.
Post from your own code¶
Register SlackResource as a resource to post from assets or sensors:
@rs.Asset
def report(slack: SlackResource) -> None:
slack.get_client().chat_postMessage(channel="#reports", text="Report ready")
repo = rs.CodeRepository(assets=[report], resources={"slack": SlackResource()})
get_client() returns a slack_sdk.WebClient.