// the client of a device with its event handler.
func openWhatsApp(ctx context.Context, dir string, clock func() time.Time, logger *slog.Logger, newClient func(device *store.Device, handle func(event any) bool) WhatsAppClient) (*WhatsApp, error) {
w := &WhatsApp{
+ ctx: ctx,
dir: filepath.Join(dir, whatsappDirName),
clock: clock,
logger: logger,
// creation, close, removal, and snapshots. It is held before mutex, and stays
// held when removal releases mutex for client callbacks.
//
+// ctx is the caller context until Run sets its context for library callbacks.
+// The mutex protects it, so callbacks follow daemon cancellation.
+//
// The inbox puts messages into batches and logs them, and groups holds the
// names of the groups. Both get a WhatsAppLink from link for each call, so they
// use the current device and client.
// log, and those of private chats hold phone numbers, because the agent needs
// them to send.
type WhatsApp struct {
+ ctx context.Context
dir string
clock func() time.Time
logger *slog.Logger
func (w *WhatsApp) Run(ctx context.Context, l *TurnLoop, notify func(Event)) error {
w.mutex.Lock()
+ w.ctx = ctx
w.notify = notify
linked := w.linked
w.retrying = linked
// connected again with the length of the outage. The retries of the library and
// Run between them log nothing.
func (w *WhatsApp) handle(event any) bool {
+ w.mutex.Lock()
+
+ ctx := w.ctx
+ w.mutex.Unlock()
+
switch event := event.(type) {
case *events.Message:
- return w.inbox.receive(w.link(), event)
+ return w.inbox.receive(ctx, w.link(), event)
case *events.GroupInfo:
if event.Name != nil {
w.groups.set(event.JID.String(), event.Name.Name)
w.mutex.Unlock()
if line != "" {
- w.logger.Log(context.Background(), level, line, attrs...)
+ w.logger.Log(ctx, level, line, attrs...)
}
if notice != "" {