Catalog PyPI
PACKAGE EXPLORERView in registry

3tears-channels

PyPI

Unified message protocol for channel adapters in the 3tears platform

Versions collected1
Direct dependencies11From release 0.72.0
Last observed20 Oct, 15:37 UTC

README

Version 0.72.0 documentation

3tears-channels

A unified message protocol for the 3tears framework, with adapters for Slack, Discord, and WebSocket clients. Write your agent logic once and deliver it across channels.

pip install 3tears-channels

What you get

  • One message model -- ChannelMessage, ChannelResponse, ChannelDeliveryMessage, and Attachment carry a request and its reply regardless of channel.
  • Routing -- ChannelRouter and StreamingChannelRouter dispatch inbound messages and stream responses back.
  • Slack and Discord -- payload and rich-formatting builders (build_slack_blocks, build_slack_payload, build_discord_embed, build_discord_payload) plus a should_use_rich_formatting helper.
  • WebSocket -- WebSocketHandler, WebSocketProtocol, a ConnectionRegistry, and frame primitives for real-time clients.
  • Rooms and presence -- RoomFanout, RoomState, RoomIndexCollection, and a three-tier-backed PresenceCollection with a PresenceSweeper for connection liveness.
  • Email -- threetears.channels.mail: Mailer over an EmailTransport, an SMTP transport reading its settings per send, EmailTemplate substitution with an HTML alternative and List-Unsubscribe headers, send_batch for many recipients with per-recipient failure isolation, and BounceReceiver for HMAC-verified delivery reports. A sibling of the channel adapters rather than one of them -- an email recipient has no conversation and usually no account, so ChannelDeliveryMessage does not apply.

Quickstart

from threetears.channels import ChannelRouter, ChannelMessage, build_slack_blocks

router = ChannelRouter(...)
response = await router.dispatch(ChannelMessage(text="hello", channel="slack", ...))

blocks = build_slack_blocks(response)

WebSocket connection lifetime

WebSocketHandler ends a connection on its own in two cases, besides the peer disconnecting.

The peer stops answering. Every heartbeat_interval seconds (config key, default 30) each open connection is sent {"type": "ping"}. A connection that has sent nothing since the previous ping is closed with code 1011, and its registry entry, rooms and presence are cleaned up as on a disconnect. Any frame counts as an answer; an otherwise idle client answers with {"type": "pong"}. A connection busy with a frame (a turn in flight) is not judged until it is reading again.

The credential expires. If the auth_validator's claims include exp (unix seconds), the first frame at or after that time, or the first heartbeat tick of an idle connection, is answered with the same refusal an unauthenticated connection gets:

{"type": "error", "code": "UNAUTHENTICATED", "message": "access token expired"}

and the socket is closed 1008. A client handles it like any UNAUTHENTICATED refusal: obtain a fresh credential and reconnect. Leave exp out of the claims for a connection that should not expire.

A connection that is answering pings also keeps its presence row fresh, so PresenceSweeper does not evict a live member.

License

MIT. See LICENSE.

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Versions 1

Collected releases
VersionPublishedDirect dependenciesOpen version
0.72.020 Oct 202611

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Repository

ACCESSIBLE
pacepace/3tears

Shared repository data · GitHub

License detected in repository
MIT
License branch / ref
develop
Last push
20 Oct 2026
Archived
No
Contributor identities
— · partial collection

Public sponsor counts have not been collected. These signals belong to the repository owner.

Downloads & popularity

0count
Gauge · observed snapshots20 Oct 2026
10.50
2026-10-20: 0 · observed 2026-10-20T17:18:19.8928356Z
20 Oct20 Oct

github · 1 observations shown. Missing days are unknown.

View observed values
Date / source periodValueObserved at
2026-10-2002026-10-20T17:18:19.8928356Z

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 15:37 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:b54055f9c80b62f38e82e693214d3f02bfcf4e07d73cde226152c38a1d52fe79