Skip to content

Commit c94641f

Browse files
committed
fix: aguardar sync de chaves E2E no envio (v1.0.7 update)
1 parent a080019 commit c94641f

File tree

4 files changed

+47
-15
lines changed

4 files changed

+47
-15
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
## [v1.0.7] - 2026-01-19
44
### Corrigido
55
- **Race condition no pareamento**: salvamento do JID agora é síncrono, garantindo que a sessão esteja persistida antes de liberar para uso.
6-
- Mensagens ficando em "Aguardando" após conectar instância: adicionado controle `sessionReady` que indica quando a sessão está pronta para enviar mensagens (após presence enviado).
6+
- **Mensagens "Aguardando"**: A API de envio de mensagens agora aguarda inteligentemente (até 10s) que a sincronização das chaves de criptografia (prekeys) esteja concluída antes de despachar a mensagem. Isso evita que as primeiras mensagens fiquem pendentes no destinatário.
77

88
### Adicionado
9-
- Método `IsSessionReady()` no Manager para verificar se a sessão está completamente pronta para operações de envio.
9+
- Método `IsSessionReady()` no Manager para verificar se a sessão está completamente pronta.
10+
- Lógica de polling no `MessageService` para garantir estabilidade no envio inicial.
1011

1112
### Internals
1213
- Limpeza automática do estado `sessionReady` e `pairingSuccess` ao deletar sessão.

cmd/api/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ func main() {
135135
}
136136

137137
logr.Debug("inicializando serviços")
138-
messageService := message.NewServiceWithSession(repos.Message, sessionManager, repos.Instance)
138+
messageService := message.NewServiceWithSession(repos.Message, sessionManager, repos.Instance, logr)
139139
apiTokenService := api_token.NewService(repos.APIToken)
140140
userService := user.NewService(repos.User, apiTokenService, instanceService)
141141
authService := auth.NewService(cfg.JWT.Secret, cfg.JWT.ExpHours, repos.User)

internal/service/message/service.go

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"go.mau.fi/whatsmeow"
1515
"go.mau.fi/whatsmeow/proto/waE2E"
1616
"go.mau.fi/whatsmeow/types"
17+
"go.uber.org/zap"
1718
"google.golang.org/protobuf/proto"
1819

1920
"github.com/open-apime/apime/internal/storage"
@@ -76,21 +77,27 @@ type Service struct {
7677
repo storage.MessageRepository
7778
sessionMgr SessionManager
7879
instanceRepo storage.InstanceRepository
80+
log *zap.Logger
7981
}
8082

8183
type SessionManager interface {
8284
GetClient(instanceID string) (*whatsmeow.Client, error)
85+
IsSessionReady(instanceID string) bool
8386
}
8487

85-
func NewService(repo storage.MessageRepository) *Service {
86-
return &Service{repo: repo}
88+
func NewService(repo storage.MessageRepository, log *zap.Logger) *Service {
89+
return &Service{
90+
repo: repo,
91+
log: log,
92+
}
8793
}
8894

89-
func NewServiceWithSession(repo storage.MessageRepository, sessionMgr SessionManager, instanceRepo storage.InstanceRepository) *Service {
95+
func NewServiceWithSession(repo storage.MessageRepository, sessionMgr SessionManager, instanceRepo storage.InstanceRepository, log *zap.Logger) *Service {
9096
return &Service{
9197
repo: repo,
9298
sessionMgr: sessionMgr,
9399
instanceRepo: instanceRepo,
100+
log: log,
94101
}
95102
}
96103

@@ -166,6 +173,27 @@ func (s *Service) Send(ctx context.Context, input SendInput) (model.Message, err
166173
return model.Message{}, ErrInstanceNotConnected
167174
}
168175

176+
// Aguardar sessão ficar pronta (sincronização de chaves E2E)
177+
// Timeout de 10 segundos
178+
readyStart := time.Now()
179+
isReady := false
180+
for time.Since(readyStart) < 10*time.Second {
181+
if s.sessionMgr.IsSessionReady(input.InstanceID) {
182+
isReady = true
183+
break
184+
}
185+
time.Sleep(500 * time.Millisecond)
186+
}
187+
188+
if !isReady {
189+
// Logar warning mas tentar enviar mesmo assim (melhor que falhar)
190+
// Em alguns casos o evento critical_block pode não vir ou vir diferente
191+
s.log.Warn("Sessão não reportou pronta após timeout, enviando mensagem mesmo assim",
192+
zap.String("instance_id", input.InstanceID),
193+
zap.Duration("timeout", 10*time.Second),
194+
)
195+
}
196+
169197
normalizedTo := normalizeJID(input.To)
170198

171199
toJID, err := types.ParseJID(normalizedTo)

internal/session/whatsmeow/manager.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ func (m *Manager) GetSessionStorageInfo(instanceID string) SessionStorageInfo {
129129
}
130130
}
131131

132-
133132
func (m *Manager) IsSessionReady(instanceID string) bool {
134133
m.mu.RLock()
135134
defer m.mu.RUnlock()
@@ -340,7 +339,6 @@ func (m *Manager) monitorQRChannel(instanceID string, client *whatsmeow.Client,
340339
m.pairingSuccess[instanceID] = time.Now()
341340
m.mu.Unlock()
342341

343-
344342
if client != nil && client.Store != nil && client.Store.ID != nil {
345343
if m.instanceRepo != nil {
346344
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
@@ -388,15 +386,15 @@ func (m *Manager) monitorQRChannel(instanceID string, client *whatsmeow.Client,
388386
}
389387
} else {
390388
m.log.Info("presence enviado com sucesso", zap.String("instance_id", instanceID))
391-
389+
392390
m.mu.Lock()
393391
m.sessionReady[instanceID] = true
394392
m.mu.Unlock()
395393
m.log.Info("sessão marcada como pronta para mensagens", zap.String("instance_id", instanceID))
396394
return
397395
}
398396
}
399-
397+
400398
m.mu.Lock()
401399
m.sessionReady[instanceID] = true
402400
m.mu.Unlock()
@@ -980,7 +978,7 @@ func (m *Manager) restoreSessionIfExists(ctx context.Context, instanceID string)
980978
return
981979
}
982980
}
983-
981+
984982
m.mu.Lock()
985983
m.sessionReady[instanceID] = true
986984
m.mu.Unlock()
@@ -1321,14 +1319,14 @@ func (m *Manager) handleEvent(instanceID string, evt any) {
13211319
}
13221320
} else {
13231321
m.log.Info("presence enviado - instância totalmente ativa", zap.String("instance_id", instanceID))
1324-
1322+
13251323
m.mu.Lock()
13261324
m.sessionReady[instanceID] = true
13271325
m.mu.Unlock()
13281326
return
13291327
}
13301328
}
1331-
1329+
13321330
m.mu.Lock()
13331331
m.sessionReady[instanceID] = true
13341332
m.mu.Unlock()
@@ -1472,9 +1470,14 @@ func (m *Manager) handleEvent(instanceID string, evt any) {
14721470
zap.String("name", string(v.Name)),
14731471
)
14741472

1475-
// Quando critical_block sync completa, a instância está pronta para uso
14761473
if v.Name == "critical_block" {
1477-
m.log.Info("sincronização crítica concluída - instância pronta para receber mensagens",
1474+
m.log.Info("sincronização crítica concluída - prekeys E2E prontas",
1475+
zap.String("instance_id", instanceID))
1476+
1477+
m.mu.Lock()
1478+
m.sessionReady[instanceID] = true
1479+
m.mu.Unlock()
1480+
m.log.Info("sessão pronta para enviar mensagens (critical_block sync completo)",
14781481
zap.String("instance_id", instanceID))
14791482
}
14801483
default:

0 commit comments

Comments
 (0)