Webhooks: post each new incoming message (closes #7)
check / check (push) Successful in 2m3s
check / check (push) Successful in 2m3s
Each message a contact sends in a direct chat is posted to each webhook registered on that chat: `POST`, `Content-Type: application/json`, body `{"chat_id":N,"message":{...}}` with the same record the messages endpoint returns. The event handler only queues the message; four goroutines post it, one attempt each with a 10-second timeout and no retries, and with 100 already waiting a message is dropped and logged, so a slow webhook never delays the bot's replies. Posting stops last, after the chat client.
Disclosures: redirects are not followed; a logged failure can name the webhook's host but never its path, query or credentials; posts still in flight at shutdown are abandoned and logged.
Model: opus-5-5
This commit was merged in pull request #19.
This commit is contained in:
+20
-9
@@ -56,10 +56,10 @@ var errExited = errors.New("simplex-chat exited")
|
||||
|
||||
// Run reads the webhooks kept in cfg.DataDir, starts the chat client with
|
||||
// its database there, serving its API on localhost at chatPort, connects
|
||||
// to it, sets up the bot's address, then answers messages and serves the
|
||||
// bot's API until ctx is cancelled — which is a clean stop and returns
|
||||
// nil — or until the chat client, the connection to it or the API's
|
||||
// listener fails, which returns the error.
|
||||
// to it, sets up the bot's address, then answers messages, posts them to
|
||||
// the webhooks and serves the bot's API until ctx is cancelled — which is
|
||||
// a clean stop and returns nil — or until the chat client, the connection
|
||||
// to it or the API's listener fails, which returns the error.
|
||||
func Run(
|
||||
ctx context.Context, log *slog.Logger, cfg *config.Config, chatPort int,
|
||||
) error {
|
||||
@@ -75,6 +75,12 @@ func Run(
|
||||
return err
|
||||
}
|
||||
|
||||
// Posts the messages the event handler hands it to the webhooks. Its
|
||||
// stop, deferred first, runs last, once the chat client has exited
|
||||
// and can send no more messages; cancelling ctx does not reach it.
|
||||
deliveries := api.StartDeliveries(context.WithoutCancel(ctx), log, webhooks)
|
||||
defer deliveries.Stop()
|
||||
|
||||
// Cancelling this stops the chat client; the deferred wait makes
|
||||
// Run return only once it has exited, whatever path Run takes.
|
||||
// Cancelling ctx does not reach it, so that the API, stopped first,
|
||||
@@ -94,7 +100,7 @@ func Run(
|
||||
<-cli.Done()
|
||||
}()
|
||||
|
||||
client, err := connect(ctx, log, cli, chatPort)
|
||||
client, err := connect(ctx, log, cli, chatPort, handle(log, deliveries))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -155,9 +161,10 @@ func stopAPI(ctx context.Context, log *slog.Logger, srv *http.Server) {
|
||||
}
|
||||
|
||||
// connect waits for the chat client to open its API on chatPort and
|
||||
// connects to it.
|
||||
// connects to it, passing its events to onEvent.
|
||||
func connect(
|
||||
ctx context.Context, log *slog.Logger, cli *simplex.CLI, chatPort int,
|
||||
onEvent simplex.EventHandler,
|
||||
) (*simplex.Client, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, connectTimeout)
|
||||
defer cancel()
|
||||
@@ -165,7 +172,7 @@ func connect(
|
||||
url := "ws://127.0.0.1:" + strconv.Itoa(chatPort)
|
||||
|
||||
for {
|
||||
client, err := simplex.Dial(ctx, url, log, handle(log))
|
||||
client, err := simplex.Dial(ctx, url, log, onEvent)
|
||||
if err == nil {
|
||||
return client, nil
|
||||
}
|
||||
@@ -228,8 +235,9 @@ func setUp(
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// handle answers each text message a contact sends.
|
||||
func handle(log *slog.Logger) simplex.EventHandler {
|
||||
// handle answers each text message a contact sends, and hands every
|
||||
// message a contact sends to deliveries, for the webhooks of its chat.
|
||||
func handle(log *slog.Logger, deliveries *api.Deliveries) simplex.EventHandler {
|
||||
return func(c *simplex.Client, ev simplex.Event) {
|
||||
switch ev.Type {
|
||||
case simplex.TypeNewChatItems:
|
||||
@@ -243,6 +251,9 @@ func handle(log *slog.Logger) simplex.EventHandler {
|
||||
}
|
||||
|
||||
for _, item := range r.ChatItems {
|
||||
// Never waits, so no webhook holds up the replies.
|
||||
deliveries.Add(item)
|
||||
|
||||
msg, ok := item.Message()
|
||||
if !ok {
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user