mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-15 04:18:44 +00:00
fix(zalo): correct json field mappings (#924)
Fixes #922 - Zalo OA bot was silently dropping all incoming messages due to JSON field mapping mismatches. - Align zaloBotInfo/zaloFrom/zaloChat JSON tags with live API (display_name, chat_type) - Change getUpdates to decode single-object response (API returns one update, not an array) - Add dedicated pollClient with Timeout=0 + per-call context timeout for long-polling - Update tests accordingly
This commit is contained in:
1 parent
8873548102
commit
12cfcec59a
2 files changed
+26
-14
No files matched your search
@@ -44,6 +44,7 @@ type Channel struct {
|
||||
blockReply *bool
|
||||
stopCh chan struct{}
|
||||
client *http.Client
|
||||
pollClient *http.Client
|
||||
// pairingService, pairingDebounce are inherited from channels.BaseChannel.
|
||||
}
|
||||
|
||||
@@ -74,6 +75,7 @@ func New(cfg config.ZaloConfig, msgBus *bus.MessageBus, pairingSvc store.Pairing
|
||||
blockReply: cfg.BlockReply,
|
||||
stopCh: make(chan struct{}),
|
||||
client: &http.Client{Timeout: 60 * time.Second},
|
||||
pollClient: &http.Client{Timeout: 0},
|
||||
}
|
||||
ch.SetPairingService(pairingSvc)
|
||||
return ch, nil
|
||||
@@ -398,7 +400,7 @@ type zaloAPIResponse struct {
|
||||
|
||||
type zaloBotInfo struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Name string `json:"display_name"`
|
||||
}
|
||||
|
||||
type zaloMessage struct {
|
||||
@@ -414,12 +416,12 @@ type zaloMessage struct {
|
||||
|
||||
type zaloFrom struct {
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Username string `json:"display_name"`
|
||||
}
|
||||
|
||||
type zaloChat struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Type string `json:"chat_type"`
|
||||
}
|
||||
|
||||
type zaloUpdate struct {
|
||||
@@ -428,6 +430,10 @@ type zaloUpdate struct {
|
||||
}
|
||||
|
||||
func (c *Channel) callAPI(method string, body any) (json.RawMessage, error) {
|
||||
return c.callAPIWith(context.Background(), c.client, method, body)
|
||||
}
|
||||
|
||||
func (c *Channel) callAPIWith(ctx context.Context, client *http.Client, method string, body any) (json.RawMessage, error) {
|
||||
url := fmt.Sprintf("%s/bot%s/%s", apiBase, c.token, method)
|
||||
|
||||
var reqBody io.Reader
|
||||
@@ -439,7 +445,7 @@ func (c *Channel) callAPI(method string, body any) (json.RawMessage, error) {
|
||||
reqBody = bytes.NewReader(data)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", url, reqBody)
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", url, reqBody)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create request: %w", err)
|
||||
}
|
||||
@@ -447,7 +453,7 @@ func (c *Channel) callAPI(method string, body any) (json.RawMessage, error) {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("api call %s: %w", method, err)
|
||||
}
|
||||
@@ -488,16 +494,22 @@ func (c *Channel) getUpdates(timeout int) ([]zaloUpdate, error) {
|
||||
"timeout": timeout,
|
||||
}
|
||||
|
||||
result, err := c.callAPI("getUpdates", params)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout+7)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result, err := c.callAPIWith(ctx, c.pollClient, "getUpdates", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var updates []zaloUpdate
|
||||
if err := json.Unmarshal(result, &updates); err != nil {
|
||||
var update zaloUpdate
|
||||
if err := json.Unmarshal(result, &update); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal updates: %w", err)
|
||||
}
|
||||
return updates, nil
|
||||
if update.EventName == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return []zaloUpdate{update}, nil
|
||||
}
|
||||
|
||||
func (c *Channel) sendMessage(chatID, text string) error {
|
||||
|
||||
@@ -115,7 +115,7 @@ func TestCallAPI_MalformedJSONReturnsError(t *testing.T) {
|
||||
// TestGetMe_ParsesBotInfo verifies getMe returns the zaloBotInfo struct.
|
||||
func TestGetMe_ParsesBotInfo(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write([]byte(`{"ok":true,"result":{"id":"bot-xyz","name":"TestBot"}}`))
|
||||
_, _ = w.Write([]byte(`{"ok":true,"result":{"id":"bot-xyz","display_name":"TestBot"}}`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
@@ -142,10 +142,10 @@ func TestGetMe_UnmarshalError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetUpdates_ParsesUpdateArray verifies getUpdates decodes the updates array.
|
||||
func TestGetUpdates_ParsesUpdateArray(t *testing.T) {
|
||||
// TestGetUpdates_ParsesSingleUpdate verifies getUpdates decodes the single-object API response.
|
||||
func TestGetUpdates_ParsesSingleUpdate(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write([]byte(`{"ok":true,"result":[{"event_name":"message.text.received","message":{"message_id":"m1","text":"hi","from":{"id":"user1"},"chat":{"id":"user1"}}}]}`))
|
||||
_, _ = w.Write([]byte(`{"ok":true,"result":{"event_name":"message.text.received","message":{"message_id":"m1","text":"hi","from":{"id":"user1"},"chat":{"id":"user1"}}}}`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
@@ -168,7 +168,7 @@ func TestGetUpdates_ParsesUpdateArray(t *testing.T) {
|
||||
// TestGetUpdates_UnmarshalError verifies a malformed result surfaces an error.
|
||||
func TestGetUpdates_UnmarshalError(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write([]byte(`{"ok":true,"result":"string-instead-of-array"}`))
|
||||
_, _ = w.Write([]byte(`{"ok":true,"result":"not-an-object"}`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
|
||||
Reference in new issue
Block a user