fix(qq): resolve golangci-lint formatting issues

- Fix interface{} → any in sync.Map Range callbacks
- Wrap long method signatures in mockQQAPI
- Remove blank lines between }) and following statements per gofumpt
This commit is contained in:
kent.liu 2026-03-19 18:18:44 +08:00
parent d25abb59a4
commit d3de49f3a3
5 changed files with 1153 additions and 46 deletions

@ -0,0 +1 @@
Subproject commit 75ba9e83883d143fff60d511885e2b14b9cab597

File diff suppressed because it is too large Load diff

View file

@ -43,9 +43,9 @@ const (
bytesPerMiB = 1024 * 1024
// Reconnection constants (used as defaults when config values are 0)
reconnectInitialDefault = 5 * time.Second
reconnectMaxDefault = 5 * time.Minute
reconnectMultiplier = 2.0
reconnectInitialDefault = 5 * time.Second
reconnectMaxDefault = 5 * time.Minute
reconnectMultiplier = 2.0
// Retry constants (used as defaults when config values are 0)
maxRetriesDefault = 3
@ -53,8 +53,8 @@ const (
retryMaxDelayDefault = 10 * time.Second
// Rate limit defaults
rateLimitGroupDefault = 500 * time.Millisecond
rateLimitDirectDefault = 200 * time.Millisecond
rateLimitGroupDefault = 500 * time.Millisecond
rateLimitDirectDefault = 200 * time.Millisecond
)
type qqAPI interface {
@ -96,9 +96,9 @@ type QQChannel struct {
stopOnce sync.Once
// Reconnection state
reconnectMu sync.Mutex
reconnecting bool
stopReconnect chan struct{}
reconnectMu sync.Mutex
reconnecting bool
stopReconnect chan struct{}
// Rate limiting
groupRateLimiter *rateLimiter
@ -270,9 +270,9 @@ func (c *QQChannel) Send(ctx context.Context, msg bus.OutboundMessage) error {
if attempt > 0 {
backoff := c.calculateBackoff(attempt - 1)
logger.InfoCF("qq", "Retrying send", map[string]any{
"attempt": attempt,
"chat_id": msg.ChatID,
"backoff": backoff.String(),
"attempt": attempt,
"chat_id": msg.ChatID,
"backoff": backoff.String(),
})
select {
@ -1093,15 +1093,15 @@ func (c *QQChannel) reconnect() {
// resetSessionState clears transient state that may be invalid after reconnection.
func (c *QQChannel) resetSessionState() {
// Clear transient state that may be invalid after reconnection
c.chatType.Range(func(key, value interface{}) bool {
c.chatType.Range(func(key, value any) bool {
c.chatType.Delete(key)
return true
})
c.lastMsgID.Range(func(key, value interface{}) bool {
c.lastMsgID.Range(func(key, value any) bool {
c.lastMsgID.Delete(key)
return true
})
c.msgSeqCounters.Range(func(key, value interface{}) bool {
c.msgSeqCounters.Range(func(key, value any) bool {
c.msgSeqCounters.Delete(key)
return true
})

View file

@ -18,14 +18,14 @@ import (
// mockQQAPI implements qqAPI for testing
type mockQQAPI struct {
callCount atomic.Int32
wsCalled atomic.Bool
postCalled atomic.Bool
lastChatID string
lastChatKind string
shouldFail bool
failErr error
failRemaining atomic.Int32
callCount atomic.Int32
wsCalled atomic.Bool
postCalled atomic.Bool
lastChatID string
lastChatKind string
shouldFail bool
failErr error
failRemaining atomic.Int32
}
func (m *mockQQAPI) WS(ctx context.Context, params map[string]string, body string) (*dto.WebsocketAP, error) {
@ -35,7 +35,9 @@ func (m *mockQQAPI) WS(ctx context.Context, params map[string]string, body strin
}, nil
}
func (m *mockQQAPI) PostGroupMessage(ctx context.Context, groupID string, msg dto.APIMessage, opt ...options.Option) (*dto.Message, error) {
func (m *mockQQAPI) PostGroupMessage(
ctx context.Context, groupID string, msg dto.APIMessage, opt ...options.Option,
) (*dto.Message, error) {
m.postCalled.Store(true)
m.lastChatID = groupID
m.lastChatKind = "group"
@ -51,7 +53,9 @@ func (m *mockQQAPI) PostGroupMessage(ctx context.Context, groupID string, msg dt
return &dto.Message{ID: "msg-123"}, nil
}
func (m *mockQQAPI) PostC2CMessage(ctx context.Context, userID string, msg dto.APIMessage, opt ...options.Option) (*dto.Message, error) {
func (m *mockQQAPI) PostC2CMessage(
ctx context.Context, userID string, msg dto.APIMessage, opt ...options.Option,
) (*dto.Message, error) {
m.postCalled.Store(true)
m.lastChatID = userID
m.lastChatKind = "direct"
@ -78,12 +82,12 @@ func newTestQQChannel(t *testing.T, api *mockQQAPI) *QQChannel {
}
ch := &QQChannel{
BaseChannel: channels.NewBaseChannel("qq", cfg, nil, config.FlexibleStringSlice{"*"}),
config: cfg,
api: api,
dedup: make(map[string]time.Time),
done: make(chan struct{}),
groupRateLimiter: newRateLimiter(500 * time.Millisecond),
BaseChannel: channels.NewBaseChannel("qq", cfg, nil, config.FlexibleStringSlice{"*"}),
config: cfg,
api: api,
dedup: make(map[string]time.Time),
done: make(chan struct{}),
groupRateLimiter: newRateLimiter(500 * time.Millisecond),
directRateLimiter: newRateLimiter(200 * time.Millisecond),
}
ch.SetRunning(true)
@ -102,7 +106,6 @@ func TestSend_Success(t *testing.T) {
ChatID: "group-1",
Content: "Hello",
})
if err != nil {
t.Errorf("expected no error, got %v", err)
}
@ -114,7 +117,7 @@ func TestSend_Success(t *testing.T) {
// TestSend_WithRetry tests that retries happen on transient errors
func TestSend_WithRetry(t *testing.T) {
api := &mockQQAPI{
shouldFail: true,
shouldFail: true,
failErr: errors.New("connection reset"),
failRemaining: atomic.Int32{},
}
@ -127,7 +130,6 @@ func TestSend_WithRetry(t *testing.T) {
ChatID: "group-1",
Content: "Hello",
})
// Should succeed after retries
if err != nil {
t.Errorf("expected success after retry, got %v", err)
@ -140,7 +142,7 @@ func TestSend_WithRetry(t *testing.T) {
// TestSend_NonRetryableError tests that non-retryable errors don't retry
func TestSend_NonRetryableError(t *testing.T) {
api := &mockQQAPI{
shouldFail: true,
shouldFail: true,
failErr: errors.New("unauthorized"),
failRemaining: atomic.Int32{},
}
@ -166,7 +168,7 @@ func TestSend_NonRetryableError(t *testing.T) {
// TestSend_ContextCancellation tests that retries stop on context cancellation
func TestSend_ContextCancellation(t *testing.T) {
api := &mockQQAPI{
shouldFail: true,
shouldFail: true,
failErr: errors.New("timeout"),
failRemaining: atomic.Int32{},
}
@ -414,9 +416,9 @@ func TestQQChannel_ResetSessionState(t *testing.T) {
// TestQQChannel_ResetSessionState_ReasoningChannel tests that reasoning channel is re-registered
func TestQQChannel_ResetSessionState_ReasoningChannel(t *testing.T) {
ch := &QQChannel{
BaseChannel: channels.NewBaseChannel("qq", config.QQConfig{}, nil, config.FlexibleStringSlice{"*"}),
config: config.QQConfig{ReasoningChannelID: "reasoning-group"},
dedup: make(map[string]time.Time),
BaseChannel: channels.NewBaseChannel("qq", config.QQConfig{}, nil, config.FlexibleStringSlice{"*"}),
config: config.QQConfig{ReasoningChannelID: "reasoning-group"},
dedup: make(map[string]time.Time),
groupRateLimiter: newRateLimiter(500 * time.Millisecond),
directRateLimiter: newRateLimiter(200 * time.Millisecond),
}
@ -431,9 +433,9 @@ func TestQQChannel_ResetSessionState_ReasoningChannel(t *testing.T) {
// TestQQChannel_ConfigDrivenParams tests that config values override defaults.
func TestQQChannel_ConfigDrivenParams(t *testing.T) {
tests := []struct {
name string
setup func(*config.QQConfig)
check func(*testing.T, *QQChannel)
name string
setup func(*config.QQConfig)
check func(*testing.T, *QQChannel)
}{
{
name: "reconnect initial uses config when set",
@ -490,7 +492,7 @@ func TestQQChannel_ConfigDrivenParams(t *testing.T) {
},
},
{
name: "rate limiters use config values",
name: "rate limiters use config values",
setup: func(cfg *config.QQConfig) {
cfg.RateLimitGroupMs = 1000
cfg.RateLimitDirectMs = 500

View file

@ -360,11 +360,11 @@ type QQConfig struct {
ReasoningChannelID string `json:"reasoning_channel_id" env:"PICOCLAW_CHANNELS_QQ_REASONING_CHANNEL_ID"`
// Stability config (all optional with sensible defaults)
ReconnectInitialMs int `json:"reconnect_initial_ms,omitempty" env:"PICOCLAW_CHANNELS_QQ_RECONNECT_INITIAL_MS"`
ReconnectMaxMs int `json:"reconnect_max_ms,omitempty" env:"PICOCLAW_CHANNELS_QQ_RECONNECT_MAX_MS"`
MaxRetries int `json:"max_retries,omitempty" env:"PICOCLAW_CHANNELS_QQ_MAX_RETRIES"`
ReconnectInitialMs int `json:"reconnect_initial_ms,omitempty" env:"PICOCLAW_CHANNELS_QQ_RECONNECT_INITIAL_MS"`
ReconnectMaxMs int `json:"reconnect_max_ms,omitempty" env:"PICOCLAW_CHANNELS_QQ_RECONNECT_MAX_MS"`
MaxRetries int `json:"max_retries,omitempty" env:"PICOCLAW_CHANNELS_QQ_MAX_RETRIES"`
RetryInitialDelayMs int `json:"retry_initial_delay_ms,omitempty" env:"PICOCLAW_CHANNELS_QQ_RETRY_INITIAL_DELAY_MS"`
RetryMaxDelayMs int `json:"retry_max_delay_ms,omitempty" env:"PICOCLAW_CHANNELS_QQ_RETRY_MAX_DELAY_MS"`
RetryMaxDelayMs int `json:"retry_max_delay_ms,omitempty" env:"PICOCLAW_CHANNELS_QQ_RETRY_MAX_DELAY_MS"`
RateLimitGroupMs int `json:"rate_limit_group_ms,omitempty" env:"PICOCLAW_CHANNELS_QQ_RATE_LIMIT_GROUP_MS"`
RateLimitDirectMs int `json:"rate_limit_direct_ms,omitempty" env:"PICOCLAW_CHANNELS_QQ_RATE_LIMIT_DIRECT_MS"`
}