fix(channels): preserve original http client timeouts for LINE and WeCom

Split LINE single 60s client into infoClient (10s) for bot info lookups
and apiClient (30s) for messaging API calls. Lower WeCom cached client
base timeout from 60s to 30s (matching uploadMedia), and ensure it is
always >= the configured ReplyTimeout so the per-request context
deadline remains the effective limit.
This commit is contained in:
ItsT0ng 2026-03-01 15:06:50 +11:00
parent dd9d6e40cf
commit ad8abe6c3d
3 changed files with 27 additions and 11 deletions

View file

@ -45,7 +45,8 @@ type replyTokenEntry struct {
type LINEChannel struct { type LINEChannel struct {
*channels.BaseChannel *channels.BaseChannel
config config.LINEConfig config config.LINEConfig
client *http.Client infoClient *http.Client // for bot info lookups (short timeout)
apiClient *http.Client // for messaging API calls
botUserID string // Bot's user ID botUserID string // Bot's user ID
botBasicID string // Bot's basic ID (e.g. @216ru...) botBasicID string // Bot's basic ID (e.g. @216ru...)
botDisplayName string // Bot's display name for text-based mention detection botDisplayName string // Bot's display name for text-based mention detection
@ -70,7 +71,8 @@ func NewLINEChannel(cfg config.LINEConfig, messageBus *bus.MessageBus) (*LINECha
return &LINEChannel{ return &LINEChannel{
BaseChannel: base, BaseChannel: base,
config: cfg, config: cfg,
client: &http.Client{Timeout: 60 * time.Second}, infoClient: &http.Client{Timeout: 10 * time.Second},
apiClient: &http.Client{Timeout: 30 * time.Second},
}, nil }, nil
} }
@ -106,7 +108,7 @@ func (c *LINEChannel) fetchBotInfo() error {
} }
req.Header.Set("Authorization", "Bearer "+c.config.ChannelAccessToken) req.Header.Set("Authorization", "Bearer "+c.config.ChannelAccessToken)
resp, err := c.client.Do(req) resp, err := c.infoClient.Do(req)
if err != nil { if err != nil {
return err return err
} }
@ -645,7 +647,7 @@ func (c *LINEChannel) callAPI(ctx context.Context, endpoint string, payload any)
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+c.config.ChannelAccessToken) req.Header.Set("Authorization", "Bearer "+c.config.ChannelAccessToken)
resp, err := c.client.Do(req) resp, err := c.apiClient.Do(req)
if err != nil { if err != nil {
return channels.ClassifyNetError(err) return channels.ClassifyNetError(err)
} }

View file

@ -130,11 +130,18 @@ func NewWeComAppChannel(cfg config.WeComAppConfig, messageBus *bus.MessageBus) (
channels.WithReasoningChannelID(cfg.ReasoningChannelID), channels.WithReasoningChannelID(cfg.ReasoningChannelID),
) )
// Client timeout must be >= the configured ReplyTimeout so the
// per-request context deadline is always the effective limit.
clientTimeout := 30 * time.Second
if d := time.Duration(cfg.ReplyTimeout) * time.Second; d > clientTimeout {
clientTimeout = d
}
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
return &WeComAppChannel{ return &WeComAppChannel{
BaseChannel: base, BaseChannel: base,
config: cfg, config: cfg,
client: &http.Client{Timeout: 60 * time.Second}, client: &http.Client{Timeout: clientTimeout},
ctx: ctx, ctx: ctx,
cancel: cancel, cancel: cancel,
processedMsgs: make(map[string]bool), processedMsgs: make(map[string]bool),

View file

@ -94,11 +94,18 @@ func NewWeComBotChannel(cfg config.WeComConfig, messageBus *bus.MessageBus) (*We
channels.WithReasoningChannelID(cfg.ReasoningChannelID), channels.WithReasoningChannelID(cfg.ReasoningChannelID),
) )
// Client timeout must be >= the configured ReplyTimeout so the
// per-request context deadline is always the effective limit.
clientTimeout := 30 * time.Second
if d := time.Duration(cfg.ReplyTimeout) * time.Second; d > clientTimeout {
clientTimeout = d
}
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
return &WeComBotChannel{ return &WeComBotChannel{
BaseChannel: base, BaseChannel: base,
config: cfg, config: cfg,
client: &http.Client{Timeout: 60 * time.Second}, client: &http.Client{Timeout: clientTimeout},
ctx: ctx, ctx: ctx,
cancel: cancel, cancel: cancel,
processedMsgs: make(map[string]bool), processedMsgs: make(map[string]bool),