Feishu OpenPlatform Server SDK for Go

repository·v3_main·Indexed 20 days ago

https://github.com/larksuite/oapi-sdk-go

A high-level, type-safe Go interface for interacting with Feishu/Lark APIs. The SDK automates token management, signature verification, and event handling. It includes a Channel module for WebSocket-based event listening, messaging, and media uploads, as well as support for one-click app registration using OAuth 2.0 Device Authorization Grant via registration.RegisterApp.

Tokens
13.6K
Snippets
28
Records
43
Agent score
69%

What's inside oapi-sdk-go

  1. What is the Channel module and when to use it

    v3_main

    The Channel module is a high-level abstraction built on top of ws.Client, event.EventDispatcher, and lark.Client. It encapsulates complex tasks such as transport, message normalization, security policies, outbound sending, streaming replies, media uploading, card interactions, and lifecycle callbacks.

    Use Channel when: You are building conversational bots that require features like AI dialogue, streaming replies, interactive cards, media uploads, or @all mention strategies.

    Use ws.Client + event.EventDispatcher when: You only need to receive and process a small number of simple events without advanced conversational features.

  2. Select the base template using `AppAddons.Preset`

    v3_main

    The AppAddons.Preset field determines which base template is used during app creation. This is distinct from Options.AppPreset (which handles metadata like name and avatar).

    ValueBase templateBehavior
    unset (nil)Platform defaultUses the standard platform template.
    falseMinimal base templateThe confirmation page shows only the scopes, events, and callbacks declared in your Addons object.
    truePlatform defaultExplicitly requests the default base template.

    If you set Preset to false, you can create an app with only minimal capabilities by providing an Addons object without any scopes, events, or callbacks.

    // Requesting a minimal base template
    preset := false
    _, err := registration.RegisterApp(ctx, &registration.Options{
    	Addons: &registration.AppAddons{
    		Preset: &preset,
    	},
    	OnQRCode: func(info *registration.QRCodeInfo) {
    		fmt.Println(info.URL)
    	},
    })
  3. Use the Channel module for conversational bots

    v3_main

    The Channel module is a high-level abstraction built on top of ws.Client, event.EventDispatcher, and lark.Client. It is designed specifically for building conversational bots (AI chat, streaming replies, interactive cards, media handling, etc.).

    Key features include:

    • Transport management and message normalization.
    • Safety policy control (e.g., mention requirements).
    • Outbound message sending (text, markdown, cards, media).
    • Streaming replies (markdown or card-based).
    • Media upload and card interactions.
    • Lifecycle hooks (ready, reconnect, disconnect, error).

    When to use:

    • Use Channel if you need a full-featured bot experience.
    • Use ws.Client + event.EventDispatcher only if you need to receive a few simple events without the extra overhead.
    // Minimal setup pattern
    client := lark.NewClient(appID, appSecret)
    wsClient := larkws.NewClient(appID, appSecret)
    ch := channel.NewChannel(client, wsClient)
    
    // Always start via the channel to ensure lifecycle hooks are wired
    if err := ch.Start(ctx); err != nil {
        panic(err)
    }
  4. Use the Channel module for high-level bot operations

    v3_main

    The SDK provides a Channel module that wraps WebSocket and API Client functionality. It is designed to simplify common bot integration tasks, allowing developers to focus on business logic rather than low-level communication.

    Key capabilities include:

    • Event listening
    • Message normalization
    • Sending streaming replies
    • Uploading media

    Refer to the Channel Module Documentation for detailed usage.

  5. Use the Channel Module for event listening and messaging

    v3_main

    The SDK provides a Channel module built on top of WebSocket and the API Client. It encapsulates several complex processes, allowing you to focus on business logic instead of infrastructure. Key capabilities include:

    • Event listening
    • Message normalization
    • Streaming replies
    • Media uploads

    Refer to the doc/channel.md file for detailed documentation on implementing these features.

  6. Implement streaming replies (Markdown vs Card)

    v3_main

    Use ch.Stream(ctx, input) to get a types.StreamController for real-time updates. There are two distinct modes:

    1. Markdown Stream

    Use this for text-based AI responses. Use the Append() method on the controller.

    • If Card is empty, the SDK sends an initial message first.
    • Append() performs throttled updates.
    • Flush() pushes buffered content immediately.
    • If content exceeds TextChunkLimit, the SDK continues via subsequent reply messages.

    2. Card Stream

    Use this if your initial SendInput contains a Card. You must use UpdateCard() instead of Append().

    Note: Markdown streams do not support UpdateCard(), and Card streams do not support Append().

    // --- Markdown Stream Example ---
    streamCtrl, err := ch.Stream(ctx, &types.SendInput{
        ChatID:         msg.ChatID,
        ReplyMessageID: msg.MessageID,
        Title:          "Assistant",
    })
    if err != nil {
        return err
    }
    
    for chunk := range llmStream(msg.Content) {
        if err := streamCtrl.Append(ctx, chunk); err != nil {
            return err
        }
    }
    return streamCtrl.Close(ctx)
    
    
    // --- Card Stream Example ---
    streamCtrl, err := ch.Stream(ctx, &types.SendInput{
        ChatID: chatID,
        Card:   initialCardJSON,
    })
    if err != nil {
        return err
    }
    
    if err := streamCtrl.UpdateCard(ctx, nextCardJSON); err != nil {
        return err
    }
    return streamCtrl.Close(ctx)
  7. Configure scopes, events, and callbacks during registration

    v3_main

    You can incrementally request permissions using Options.Addons. These values are pre-filled into the user's confirmation page.

    • Options.CreateOnly=true: Forces the flow to only allow creating a new app.
    • Options.AppID: Initiates an update flow for an existing app instead of creating a new one.
    • Addons behavior: These are additive only; you cannot use them to remove configurations from the platform's base template.

    Note: The SDK validates the structure and non-empty strings, but it does not verify if the specific scope, event, or callback names are valid on the platform side.

    // Example: Creating a new app with specific scopes and events
    _, err := registration.RegisterApp(ctx, &registration.Options{
    	Addons: &registration.AppAddons{
    		Scopes: registration.AppAddonsScopes{
    			Tenant: []string{"im:message:send_as_bot"},
    			User:   []string{"calendar:calendar:read"},
    		},
    		Events: registration.AppAddonsEvents{
    			Items: registration.AppAddonsEventItems{
    				Tenant: []string{"im.message.receive_v1"},
    			},
    		},
    		Callbacks: registration.AppAddonsCallbacks{
    			Items: []string{"card.action.trigger"},
    		},
    	},
    	CreateOnly: true,
    	OnQRCode: func(info *registration.QRCodeInfo) {
    		fmt.Println(info.URL)
    	},
    })
  8. One-Click App Registration with `registration.RegisterApp`

    v3_main

    The SDK supports one-click app creation using the OAuth 2.0 Device Authorization Grant (RFC 8628). By calling registration.RegisterApp, the SDK generates a verification URL (or QR code) for the user. Once the user authorizes the request in Feishu/Lark, the app is automatically created, and the SDK returns the App ID and App Secret.

    package main
    
    import (
    	"context"
    	"errors"
    	"fmt"
    	"time"
    
    	lark "github.com/larksuite/oapi-sdk-go/v3"
    	"github.com/larksuite/oapi-sdk-go/v3/scene/registration"
    )
    
    func main() {
    	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
    	defer cancel()
    
    	result, err := registration.RegisterApp(ctx, &registration.Options{
    		OnQRCode: func(info *registration.QRCodeInfo) {
    			fmt.Printf("open or scan this url: %s\n", info.URL)
    			fmt.Printf("the link expires in %d seconds\n", info.ExpireIn)
    		},
    		OnStatusChange: func(info *registration.StatusChangeInfo) {
    			// status: polling | slow_down | domain_switched
    			fmt.Printf("registration status: %s", info.Status)
    			if info.Interval > 0 {
    				fmt.Printf(", next poll after %d seconds", info.Interval)
    			}
    			fmt.Println()
    		},
    	})
    	if err != nil {
    		var regErr *registration.RegisterAppError
    		if errors.As(err, &regErr) {
    			fmt.Printf("register app failed: code=%s, description=%s\n", regErr.Code, regErr.Description)
    			return
    		}
    		panic(err)
    	}
    
    	fmt.Println("App ID:", result.ClientID)
    	fmt.Println("App Secret:", result.ClientSecret)
    
    	client := lark.NewClient(result.ClientID, result.ClientSecret)
    	_ = client
    }
  9. Manual Debugging with sample/channel/main.go

    v3_main

    Use sample/channel/main.go for manual debugging in a real Feishu environment. This is ideal for verifying event reception, policy interception, card interactions, and streaming updates.

    Run Command:

    go run sample/channel/main.go

    Explicit Parameters:

    go run sample/channel/main.go \
      -app_id="$APP_ID" \
      -app_secret="$APP_SECRET" \
      -dm_mode=open \
      -respond_all=true

    Supported Flags:

    • -app_id / -app_secret: Feishu application credentials.
    • -dm_mode: Single-chat policy. Supports open, disabled, or allowlist.
    • -respond_all: Whether to respond to @all in group chats.
  10. Customize permissions, events, and callbacks during app registration

    v3_main

    When using registration.RegisterApp, you can incrementally request permissions, event subscriptions, and callbacks using Options.Addons. These configurations will be pre-filled on the user's confirmation page after they scan the QR code.

    Key Behaviors

    • Incremental Only: Addons only supports adding configurations; it cannot remove configurations from the platform's base template.
    • Validation: The SDK validates the data shape and non-empty strings but does not verify if the permission, event, or callback names actually exist.
    • Create vs Update:
      • Set Options.CreateOnly = true to allow only new app creation.
      • Provide Options.AppID to enter the flow for updating an existing application's configuration.

    Example: Creating a new app with specific scopes and events

    _, err := registration.RegisterApp(ctx, &registration.Options{
    	Addons: &registration.AppAddons{
    		Scopes: registration.AppAddonsScopes{
    			Tenant: []string{"im:message:send_as_bot"},
    			User:   []string{"calendar:calendar:read"},
    		},
    		Events: registration.AppAddonsEvents{
    			Items: registration.AppAddonsEventItems{
    				Tenant: []string{"im.message.receive_v1"},
    			},
    		},
    		Callbacks: registration.AppAddonsCallbacks{
    			Items: []string{"card.action.trigger"},
    		},
    	},
    	CreateOnly: true,
    	OnQRCode: func(info *registration.QRCodeInfo) {
    		fmt.Println(info.URL)
    	},
    })
    if err != nil {
    	panic(err)
    }
  11. Automated Regression Testing with sample/channel_test_cases/main.go

    v3_main

    Use sample/channel_test_cases/main.go for automated or semi-automated regression testing. This is designed for batch verification of sending, updating, reverting, downloading, and policy capabilities.

    Run All Tests:

    go run sample/channel_test_cases/main.go

    Run with Explicit Credentials:

    go run sample/channel_test_cases/main.go \
      -app_id="$APP_ID" \
      -app_secret="$APP_SECRET" \
      -receive_id="$RECEIVE_ID"

    Run via Email (if no receive_id):

    go run sample/channel_test_cases/main.go \
      -app_id="$APP_ID" \
      -app_secret="$APP_SECRET" \
      -email="$EMAIL"

    Run Specific Test Cases:

    • Run a single case: -case=TC-001
    • Run a group of cases by prefix: -case=TC-10 (runs TC-101 through TC-110 etc.)
    • Run retry/revert cases: -case=TC-70
    go run sample/channel_test_cases/main.go -case=TC-001
  12. Prerequisites for Channel Testing

    v3_main

    Before running the channel testing samples, ensure the following requirements are met:

    1. Feishu App Credentials: A Feishu app must be created and enabled with an APP_ID and APP_SECRET.
    2. Event Subscription: Long connection event subscription must be enabled in the Feishu Developer Console.
    3. Permissions: The app must have permissions for message sending, message receiving, file upload/download, and business card sharing.
    4. Environment Setup: The bot must be added to the target group chat, or you must have a single-chat user ready to receive messages.
    5. Contact Permissions: If testing email-to-user conversion or business card sharing, the app needs contact reading permissions.

    Recommended Environment Variables:

    export APP_ID=cli_xxx
    export APP_SECRET=xxx

    For Automated Testing (requires one of these):

    export RECEIVE_ID=ou_xxx_or_oc_xxx
    # OR
    export EMAIL=someone@example.com