correspond.channels.signal#

Signal through signal-cli-rest-api (JSON-RPC mode): listen, read what was logged, send.

References

signal: (the linked account’s receive stream, for listen), signal:+<number> (a person, by international phone number or Signal uuid) and signal:group.<id> (a group, by the REST API’s group id).

Signal has no bot API. signal-cli runs as a linked device (or a registered number) of an account you control, and the REST wrapper exposes it: POST /v2/send to send, GET /v1/receive/<number> (a WebSocket in JSON-RPC mode) to receive. The WebSocket client is the optional websocket-client (extra signal); tests and other transports pass ws=.

Three facts shape the adapter:

  • There is no history before linking: read returns only what listen and send logged on this machine (history_depth = since_link).

  • signal-cli releases older than about three months stop working against Signal’s servers, so a silent channel may just be an old release (requirements signal says so).

  • Whether to trust a changed safety number is an authenticity policy of the server (--trust-new-identities). listen compares the account’s known identities on every poll and reports a changed safety number as an identity.changed event.

>>> Signal().parse_ref("group.ZXhhbXBsZQ==").kind
'group'

Functions

requirements_problems()

What stops Signal working from this configuration; the signal-cli age caveat is a note, not a problem.

websocket_frames(url, *, timeout)

Text frames of the WebSocket at url until it has been quiet for timeout seconds (the default ws=).

Classes

Signal(*[, http, ws, log])

A Signal account linked to a signal-cli-rest-api server.

class correspond.channels.signal.Signal(*, http=<function urllib_http>, ws=<function websocket_frames>, log=None)[source]#

Bases: object

A Signal account linked to a signal-cli-rest-api server.

audience(ref, *, draft=None)[source]#

Who reads a conversation: a person’s is named; a group’s members are listed by the REST API.

Return type:

Audience

property capabilities: Capabilities#

A linked device’s view of Signal.

property log: MutableMapping[str, Any]#

Every message received or sent, as JSON, keyed <conversation id>/<message id>.json; identities seen sit under identities/.

parse_ref(id)[source]#

"" (the account’s stream), a phone number or uuid, or group.<id>.

Return type:

ConversationRef

poll(ref, *, cursor=None, limit=None)[source]#

What the WebSocket delivered within listen_wait_s, then any safety-number changes.

Signal hands each message to the receiver once, so the cursor (the last envelope timestamp) only marks progress; delivery_id is what dedupes.

read(ref, *, since=None, limit=None)[source]#

What the log holds for a conversation, oldest first.

Return type:

list[Message]

send(ref, draft, *, dry_run=False)[source]#

POST /v2/send to a person or group, the title (if any) prepended; a reply quotes a logged message.

Return type:

SendResult

correspond.channels.signal.requirements_problems()[source]#

What stops Signal working from this configuration; the signal-cli age caveat is a note, not a problem.

Return type:

list[str]

correspond.channels.signal.websocket_frames(url, *, timeout)[source]#

Text frames of the WebSocket at url until it has been quiet for timeout seconds (the default ws=).

Return type:

Iterable[str]