mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
fix(line): log loading refresh errors, skip typing without recorder
Address review feedback from @alexhoshina and Codex: - Log sendLoading errors in ticker goroutine instead of discarding - Only start typing indicator when PlaceholderRecorder is available to avoid wasted API calls and unnecessary goroutine creation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -368,9 +368,6 @@ func (c *LINEChannel) processEvent(event lineEvent) {
|
||||
"preview": utils.Truncate(content, 50),
|
||||
})
|
||||
|
||||
// Show typing/loading indicator (requires user ID, not group ID)
|
||||
c.sendLoading(senderID)
|
||||
|
||||
sender := bus.SenderInfo{
|
||||
Platform: "line",
|
||||
PlatformID: senderID,
|
||||
@@ -381,6 +378,28 @@ func (c *LINEChannel) processEvent(event lineEvent) {
|
||||
return
|
||||
}
|
||||
|
||||
// Thinking indicator (LINE loading animation is 1:1 only).
|
||||
// For group/room chats, LINE provides no equivalent API.
|
||||
// Only start if PlaceholderRecorder is available to avoid wasted API calls.
|
||||
if !isGroup {
|
||||
if rec := c.GetPlaceholderRecorder(); rec != nil {
|
||||
typingCtx, typingCancel := context.WithTimeout(c.ctx, 5*time.Minute)
|
||||
stop, err := c.StartTyping(typingCtx, chatID)
|
||||
if err == nil {
|
||||
var stopOnce sync.Once
|
||||
stopFn := func() {
|
||||
stopOnce.Do(func() {
|
||||
stop()
|
||||
typingCancel()
|
||||
})
|
||||
}
|
||||
rec.RecordTypingStop("line", chatID, stopFn)
|
||||
} else {
|
||||
typingCancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.HandleMessage(c.ctx, peer, msg.ID, senderID, chatID, content, mediaPaths, metadata, sender)
|
||||
}
|
||||
|
||||
@@ -577,17 +596,54 @@ func (c *LINEChannel) sendPush(ctx context.Context, to, content, quoteToken stri
|
||||
return c.callAPI(ctx, linePushEndpoint, payload)
|
||||
}
|
||||
|
||||
// StartTyping implements channels.TypingCapable using LINE's loading animation.
|
||||
//
|
||||
// NOTE: The LINE loading animation API only works for 1:1 chats. Callers must ensure
|
||||
// the provided chatID is a user chat ID (not a group/room ID).
|
||||
// There is no explicit "stop" API; we periodically re-send start requests to keep
|
||||
// the indicator alive, and stop by canceling the context.
|
||||
func (c *LINEChannel) StartTyping(ctx context.Context, chatID string) (func(), error) {
|
||||
if chatID == "" {
|
||||
return func() {}, nil
|
||||
}
|
||||
|
||||
typingCtx, cancel := context.WithCancel(ctx)
|
||||
var once sync.Once
|
||||
stop := func() { once.Do(cancel) }
|
||||
|
||||
// Send immediately, then refresh periodically for long-running tasks.
|
||||
if err := c.sendLoading(typingCtx, chatID); err != nil {
|
||||
stop()
|
||||
return stop, err
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(50 * time.Second)
|
||||
go func() {
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-typingCtx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
if err := c.sendLoading(typingCtx, chatID); err != nil {
|
||||
logger.DebugCF("line", "Failed to refresh loading indicator", map[string]any{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return stop, nil
|
||||
}
|
||||
|
||||
// sendLoading sends a loading animation indicator to the chat.
|
||||
func (c *LINEChannel) sendLoading(chatID string) {
|
||||
func (c *LINEChannel) sendLoading(ctx context.Context, chatID string) error {
|
||||
payload := map[string]any{
|
||||
"chatId": chatID,
|
||||
"loadingSeconds": 60,
|
||||
}
|
||||
if err := c.callAPI(c.ctx, lineLoadingEndpoint, payload); err != nil {
|
||||
logger.DebugCF("line", "Failed to send loading indicator", map[string]any{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
return c.callAPI(ctx, lineLoadingEndpoint, payload)
|
||||
}
|
||||
|
||||
// callAPI makes an authenticated POST request to the LINE API.
|
||||
|
||||
Reference in New Issue
Block a user