ilink

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 9, 2026 License: MIT Imports: 16 Imported by: 0

README

Go Reference Go Report Card

Go SDK for the Tencent WeChat iLink Bot API — the official, legally-backed WeChat personal account bot interface released in 2026 through the OpenClaw platform.

Overview

iLink (ilinkai.weixin.qq.com) is Tencent's official HTTP/JSON API that lets developers receive and send WeChat messages from a personal account. This package provides an idiomatic Go client for it.

WeChat User ──▶ iLink API ──▶ Client.ListenAndServe ──▶ your Handler
your Handler ──▶ Client.Reply ──▶ iLink API ──▶ WeChat User

Requirements

  • Go 1.22 or later
  • A WeChat account eligible for the iLink Bot feature (via the OpenClaw platform)

Installation

go get github.com/lib-x/ilink

Quick start

1. Log in

Scan a QR code once to obtain a Token. Persist it so you don't need to scan again on restart.

ctx := context.Background()

login, err := ilink.StartLogin(ctx)
if err != nil {
    log.Fatal(err)
}
fmt.Println("Scan this URL with WeChat:", login.URL)

token, err := login.Wait(ctx)
if err != nil {
    log.Fatal(err)
}

// Save token.BotToken to disk for reuse.
data, _ := json.Marshal(token)
os.WriteFile("token.json", data, 0600)
2. Receive and reply to messages
client := ilink.NewClient(token)

err := client.ListenAndServe(ctx, ilink.HandlerFunc(func(ctx context.Context, msg *ilink.Message) error {
    log.Printf("message from %s: %s", msg.From, msg.Text())
    return client.Reply(ctx, msg, ilink.TextMessage("pong"))
}))

ListenAndServe blocks until ctx is cancelled or the server signals a session timeout (ErrSessionTimeout). Use signal.NotifyContext for graceful shutdown.

3. Send a message
err := client.Send(ctx, &ilink.OutboundMessage{
    To:           "o9cq800kum_xxx@im.wechat",
    ContextToken: msg.ContextToken, // required for correct routing
    Items:        []ilink.Item{ilink.TextMessage("Hello!")},
})
4. Send an image
data, _ := os.ReadFile("photo.jpg")

media, err := client.UploadMedia(ctx, data, &ilink.UploadOptions{
    FileName:  "photo.jpg",
    MediaType: ilink.UploadMediaTypeImage,
    ToUserID:  msg.From,
})
if err != nil {
    log.Fatal(err)
}

err = client.Reply(ctx, msg, ilink.Item{
    Type:  ilink.ItemTypeImage,
    Image: &ilink.ImageItem{Media: media},
})

Media files are AES-128-ECB encrypted before upload to Tencent CDN. UploadMedia handles key generation, encryption, and the CDN PUT automatically.

5. Download and decrypt a received image
for _, item := range msg.Items {
    if item.Type != ilink.ItemTypeImage || item.Image == nil {
        continue
    }
    img := item.Image
    cdnURL := img.Media.FullURL
    if cdnURL == "" {
        cdnURL = "https://novac2c.cdn.weixin.qq.com/c2c?" + img.Media.EncryptQueryParam
    }
    plain, err := ilink.DownloadAndDecrypt(ctx, cdnURL, img.Media)
    if err != nil {
        log.Fatal(err)
    }
    os.WriteFile("received.jpg", plain, 0644)
}
6. Recall a message
err := client.RecallMessage(ctx, sentMsgID)
7. Handle session timeout

When the bot session expires (server errcode=-14), ListenAndServe returns ErrSessionTimeout. Re-scan the QR code to recover:

err := client.ListenAndServe(ctx, handler)
if errors.Is(err, ilink.ErrSessionTimeout) {
    log.Println("session expired — please re-login")
}

API reference

Authentication
Symbol Description
StartLogin(ctx) Begin a QR code login session
Login.URL Pre-rendered QR code image URL
Login.QRCode Raw QR code string (encode yourself if needed)
Login.Wait(ctx) Block until the user scans; returns a Token
Token Persistent credentials — save BotToken to disk
Client
Symbol Description
NewClient(token, ...Option) Create a client from a saved Token
WithHTTPClient(hc) Replace the default http.Client
WithLogger(l) Set a log/slog.Logger for polling errors
WithBaseURL(u) Override the API base URL (useful for tests)
Messaging
Symbol Description
Client.ListenAndServe(ctx, Handler) Long-poll loop; calls Handler per message
Client.Send(ctx, *OutboundMessage) Send a message to any user
Client.Reply(ctx, *Message, ...Item) Reply to an inbound message (copies ContextToken)
Client.SendTyping(ctx, userID, bool) Show/hide the "typing…" indicator
Client.RecallMessage(ctx, msgID) Withdraw a previously sent message
TextMessage(text) Construct a plain-text Item
Media
Symbol Description
Client.UploadMedia(ctx, data, *UploadOptions) Encrypt and upload; returns a *CDNMedia
DecryptMedia(item, ciphertext) Decrypt a CDN-downloaded blob
DownloadAndDecrypt(ctx, cdnURL, item) Fetch and decrypt in one call
UploadOptions
Field Description
FileName Original file name (embedded in CDN reference)
MediaType UploadMediaTypeImage / Video / File / Voice
ToUserID Target recipient's user ID (required by the server)
ThumbData Raw thumbnail bytes for IMAGE / VIDEO uploads
NoThumb Suppress thumbnail CDN slot allocation
Handler interface
type Handler interface {
    ServeMessage(ctx context.Context, msg *Message) error
}

// HandlerFunc adapts an ordinary function to Handler, like net/http.HandlerFunc.
type HandlerFunc func(ctx context.Context, msg *Message) error
Message
Field Type Description
Seq int64 Message sequence number
MessageID int64 Unique message ID
From string Sender user ID (xxx@im.wechat)
To string Bot's own user ID (xxx@im.bot)
GroupID string Set for group chat messages
SessionID string Conversation session identifier
CreateTimeMs int64 Creation timestamp (milliseconds)
UpdateTimeMs int64 Last-update timestamp (milliseconds)
ContextToken string Must be echoed back when replying
Items []Item Message content elements
Message types
ItemType Constant Populated field
Text ItemTypeText Item.Text *TextItem
Image ItemTypeImage Item.Image *ImageItem
Voice ItemTypeVoice Item.Voice *VoiceItem
File ItemTypeFile Item.File *FileItem
Video ItemTypeVideo Item.Video *VideoItem

Each Item also carries MsgID, CreateTimeMs, UpdateTimeMs, IsCompleted, and RefMsg *RefMessage (for quoted messages).

CDNMedia

All media types reference CDN-hosted, AES-128-ECB-encrypted content via *CDNMedia:

Field Description
EncryptQueryParam Encrypted CDN parameter string for download/upload
AESKey Base64-encoded 128-bit AES key
EncryptType 0 = encrypt fileid only; 1 = pack thumbnail info
FullURL Complete download URL when provided by the server
Media item types

ImageItem

Field Description
Media *CDNMedia Full-size image CDN reference
ThumbMedia *CDNMedia Thumbnail CDN reference
AESKey string Raw hex AES-128 key (preferred for inbound decryption)
URL string Direct image URL (when provided)
ThumbWidth/Height/Size Thumbnail dimensions and byte size
MidSize, HDSize Mid-resolution and HD byte sizes

VoiceItem

Field Description
Media *CDNMedia Voice file CDN reference
EncodeType VoiceEncodeType Audio encoding (e.g. VoiceEncodeSilk)
SampleRate int Sample rate in Hz
PlaytimeMs int Duration in milliseconds
RecognText string ASR transcript (inbound only, may be empty)

FileItem

Field Description
Media *CDNMedia File CDN reference
FileName string Original file name
MD5 string Plaintext file MD5 (hex)
Size int64 Plaintext file size in bytes

VideoItem

Field Description
Media *CDNMedia Video CDN reference
ThumbMedia *CDNMedia Thumbnail CDN reference
PlayLengthMs int Duration in milliseconds
VideoSize int Plaintext video size in bytes
VideoMD5 string Plaintext video MD5
Errors
Symbol Meaning
ErrQRCodeExpired QR code expired before the user scanned it
ErrQRCodeCancelled User cancelled the scan on the phone
ErrLoginTimeout ctx deadline exceeded during Login.Wait
ErrSessionTimeout Server errcode=-14: bot session expired, re-login required
*APIError Non-zero ret code returned by the iLink API

Design notes

The package follows standard library conventions:

  • Handler / HandlerFunc mirror net/http.Handler / HandlerFunc.
  • ListenAndServe blocks and returns on ctx cancellation or ErrSessionTimeout, matching the net/http pattern.
  • Functional options (WithLogger, WithHTTPClient, …) keep NewClient forward-compatible.
  • context.Context is the first argument of every I/O call; all network operations are cancellable.
  • Wire types are unexported. Callers only interact with Message, Item, CDNMedia, and the per-type media structs; internal JSON shapes live in wire.go.
  • Dynamic long-poll timeout. The server's longpolling_timeout_ms hint is respected automatically.

iLink is an official Tencent product governed by the WeChat ClawBot Feature Terms of Service. Tencent acts solely as a message conduit and does not store message content. Refer to the terms for permitted use cases and prohibited actions.

This SDK is an independent open-source client and is not affiliated with or endorsed by Tencent.

Keeping the SDK up to date

When @tencent-weixin/openclaw-weixin publishes a new version, follow the step-by-step process in AGENTS.md — written for both human maintainers and AI agents.

License

MIT

Documentation

Overview

Package ilink provides a client for the Tencent WeChat iLink Bot API.

iLink is Tencent's official WeChat personal account Bot API, exposed through the OpenClaw platform. It allows developers to receive and send WeChat messages over a standard HTTP/JSON long-poll interface.

Authentication

Login is performed by scanning a QR code with the WeChat app. The resulting token must be persisted by the caller and supplied when constructing a Client.

login, err := ilink.StartLogin(ctx)
if err != nil { ... }
fmt.Println("Scan:", login.URL)

token, err := login.Wait(ctx)
if err != nil { ... }
// persist token.BotToken for reuse

Receiving messages

Implement the Handler interface and call Client.ListenAndServe:

client := ilink.NewClient(token)
err := client.ListenAndServe(ctx, ilink.HandlerFunc(func(ctx context.Context, msg *ilink.Message) error {
    fmt.Println(msg.From, ":", msg.Text())
    return client.Reply(ctx, msg, ilink.TextMessage("你好!"))
}))

Sending messages

Use Client.Send to send a message to any user:

err := client.Send(ctx, &ilink.OutboundMessage{
    To:           "o9cq800kum_xxx@im.wechat",
    ContextToken: msg.ContextToken,
    Items:        []ilink.Item{ilink.TextMessage("Hello")},
})

Media

Images, files, voices and videos are uploaded to Tencent CDN with AES-128-ECB encryption before sending. Use Client.UploadMedia to prepare a MediaItem.

Index

Examples

Constants

View Source
const (
	UploadMediaTypeImage = 1
	UploadMediaTypeVideo = 2
	UploadMediaTypeFile  = 3
	UploadMediaTypeVoice = 4
)

UploadMediaType constants mirror UploadMediaType from upstream types.ts.

Variables

View Source
var (
	// ErrQRCodeExpired is returned by Login.Wait when the QR code expires
	// before the user scans it.
	ErrQRCodeExpired = errors.New("ilink: QR code expired")

	// ErrQRCodeCancelled is returned by Login.Wait when the user cancels
	// the scan on the phone.
	ErrQRCodeCancelled = errors.New("ilink: QR code scan cancelled")

	// ErrLoginTimeout is returned by Login.Wait when the context deadline is
	// exceeded before the user confirms the scan.
	ErrLoginTimeout = errors.New("ilink: timed out waiting for QR code confirmation")

	// ErrSessionTimeout is returned by ListenAndServe when the server reports
	// errcode=-14, indicating the bot session has expired and re-login is
	// required.
	ErrSessionTimeout = errors.New("ilink: session timeout (re-login required)")
)

Sentinel errors returned by the package.

Functions

func DecryptMedia

func DecryptMedia(item *CDNMedia, ciphertext []byte) ([]byte, error)

DecryptMedia decrypts a CDN-downloaded encrypted blob using the AES key stored in the CDNMedia. Use this to read images/files received from users.

data, _ := ilink.DownloadAndDecrypt(ctx, cdnURL, msg.Items[0].Image.Media)
// or, if you downloaded the bytes yourself:
plain, err := ilink.DecryptMedia(msg.Items[0].Image.Media, ciphertext)
Example

ExampleDecryptMedia shows how to decrypt a received media file.

package main

import (
	"context"
	"log"
	"os"

	"github.com/lib-x/ilink"
)

func main() {
	// Assume msg is an inbound *ilink.Message containing an image.
	var msg *ilink.Message

	for _, item := range msg.Items {
		if item.Type != ilink.ItemTypeImage || item.Image == nil {
			continue
		}
		img := item.Image
		if img.Media == nil {
			continue
		}
		// Use FullURL if available, otherwise construct from EncryptQueryParam.
		cdnURL := img.Media.FullURL
		if cdnURL == "" {
			cdnURL = "https://novac2c.cdn.weixin.qq.com/c2c?" + img.Media.EncryptQueryParam
		}
		plain, err := ilink.DownloadAndDecrypt(context.Background(), cdnURL, img.Media)
		if err != nil {
			log.Fatal(err)
		}
		os.WriteFile("received.jpg", plain, 0o644)
	}
}

func DownloadAndDecrypt

func DownloadAndDecrypt(ctx context.Context, cdnURL string, item *CDNMedia) ([]byte, error)

DownloadAndDecrypt is a convenience function that fetches ciphertext from a CDN URL and decrypts it using the key stored in item.

When item.FullURL is populated the caller may use it directly; otherwise construct the URL from item.EncryptQueryParam and the CDN base:

https://novac2c.cdn.weixin.qq.com/c2c?<EncryptQueryParam>

Types

type APIError

type APIError struct {
	// Ret is the error code returned by the server.
	Ret int
	// Op is the API endpoint that returned the error.
	Op string
}

APIError represents a non-zero ret code returned by the iLink API.

func (*APIError) Error

func (e *APIError) Error() string

type CDNMedia added in v0.1.1

type CDNMedia struct {
	// EncryptQueryParam is the encrypted CDN parameter string used to
	// download or reference the file.
	EncryptQueryParam string
	// AESKey is the base64-encoded 128-bit AES key.
	AESKey string
	// EncryptType: 0 = encrypt fileid only; 1 = pack thumbnail/mid-size info.
	EncryptType int
	// FullURL is the complete download URL returned by the server (when present).
	// If empty, construct the URL from EncryptQueryParam and the CDN base.
	FullURL string
}

CDNMedia is a CDN reference to an AES-128-ECB-encrypted media file. It is embedded in ImageItem, VoiceItem, FileItem, and VideoItem.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a WeChat iLink bot client. Create one with NewClient.

A Client is safe for concurrent use by multiple goroutines.

func NewClient

func NewClient(token Token, opts ...Option) *Client

NewClient creates a new iLink Client authenticated with the given Token.

token, _ := login.Wait(ctx)
client := ilink.NewClient(token)

func (*Client) ListenAndServe

func (c *Client) ListenAndServe(ctx context.Context, h Handler) error

ListenAndServe starts the long-poll loop, calling h.ServeMessage for every inbound message. It blocks until ctx is cancelled or a non-recoverable error occurs, and returns ctx.Err() on clean shutdown.

If the server signals errcode=-14 (session timeout), ListenAndServe returns ErrSessionTimeout so the caller can re-authenticate.

Handler errors are logged via the client's logger and do not stop the loop. Use ctx cancellation to shut down gracefully.

err := client.ListenAndServe(ctx, ilink.HandlerFunc(func(ctx context.Context, msg *ilink.Message) error {
    return client.Reply(ctx, msg, ilink.TextMessage("pong"))
}))
Example

ExampleClient_ListenAndServe shows the minimal echo-bot pattern.

package main

import (
	"context"
	"encoding/json"
	"log"
	"os"
	"os/signal"

	"github.com/lib-x/ilink"
)

func main() {
	// Load a previously persisted token.
	data, err := os.ReadFile("token.json")
	if err != nil {
		log.Fatal(err)
	}
	var token ilink.Token
	if err := json.Unmarshal(data, &token); err != nil {
		log.Fatal(err)
	}

	client := ilink.NewClient(token)

	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
	defer stop()

	err = client.ListenAndServe(ctx, ilink.HandlerFunc(func(ctx context.Context, msg *ilink.Message) error {
		log.Printf("message from %s: %s", msg.From, msg.Text())
		return client.Reply(ctx, msg, ilink.TextMessage("pong"))
	}))
	if err != nil {
		log.Fatal(err)
	}
}

func (*Client) RecallMessage added in v0.1.1

func (c *Client) RecallMessage(ctx context.Context, msgID int64) error

RecallMessage withdraws a previously sent message. msgID is the message_id field from an outbound message's server response.

Note: message recall may have a time limit enforced by WeChat.

func (*Client) Reply

func (c *Client) Reply(ctx context.Context, to *Message, items ...Item) error

Reply is a convenience wrapper around Client.Send that copies the ContextToken from an inbound message so WeChat routes the reply correctly.

return client.Reply(ctx, msg, ilink.TextMessage("pong"))

func (*Client) Send

func (c *Client) Send(ctx context.Context, msg *OutboundMessage) error

Send delivers an OutboundMessage to a WeChat user.

Example

ExampleClient_Send shows how to proactively send a message to a user.

package main

import (
	"context"
	"log"

	"github.com/lib-x/ilink"
)

func main() {
	client := ilink.NewClient(ilink.Token{BotToken: "…"})

	err := client.Send(context.Background(), &ilink.OutboundMessage{
		To:           "o9cq800kum_xxx@im.wechat",
		ContextToken: "AARzJWAF…", // from a previous inbound message
		Items:        []ilink.Item{ilink.TextMessage("Hello!")},
	})
	if err != nil {
		log.Fatal(err)
	}
}

func (*Client) SendTyping

func (c *Client) SendTyping(ctx context.Context, userID string, status bool) error

SendTyping sends a typing status indicator to the given user. status=true starts the "typing…" indicator; status=false cancels it.

func (*Client) UploadMedia

func (c *Client) UploadMedia(ctx context.Context, data []byte, opts *UploadOptions) (*CDNMedia, error)

UploadMedia encrypts data with a fresh AES-128 key, uploads the ciphertext to Tencent CDN, and returns a CDNMedia ready to embed in an Item.

data, _ := os.ReadFile("photo.jpg")
media, err := client.UploadMedia(ctx, data, &ilink.UploadOptions{
    FileName:  "photo.jpg",
    MediaType: ilink.UploadMediaTypeImage,
    ToUserID:  msg.From,
})
if err != nil { ... }
err = client.Reply(ctx, msg, ilink.Item{
    Type:  ilink.ItemTypeImage,
    Image: &ilink.ImageItem{Media: media},
})
Example

ExampleClient_UploadMedia shows how to upload an image and send it.

package main

import (
	"context"
	"log"
	"os"

	"github.com/lib-x/ilink"
)

func main() {
	client := ilink.NewClient(ilink.Token{BotToken: "…"})
	ctx := context.Background()

	data, err := os.ReadFile("photo.jpg")
	if err != nil {
		log.Fatal(err)
	}

	media, err := client.UploadMedia(ctx, data, &ilink.UploadOptions{
		FileName:  "photo.jpg",
		MediaType: ilink.UploadMediaTypeImage,
		ToUserID:  "o9cq800kum_xxx@im.wechat",
	})
	if err != nil {
		log.Fatal(err)
	}

	// Send the uploaded image in a message.
	err = client.Send(ctx, &ilink.OutboundMessage{
		To:           "o9cq800kum_xxx@im.wechat",
		ContextToken: "AARzJWAF…",
		Items: []ilink.Item{
			{
				Type:  ilink.ItemTypeImage,
				Image: &ilink.ImageItem{Media: media},
			},
		},
	})
	if err != nil {
		log.Fatal(err)
	}
}

type FileItem added in v0.1.1

type FileItem struct {
	// Media is the CDN reference to the file.
	Media *CDNMedia

	// FileName is the original file name.
	FileName string
	// MD5 is the plaintext file MD5 (hex).
	MD5 string
	// Size is the plaintext file size in bytes.
	Size int64
}

FileItem is a file attachment.

type Handler

type Handler interface {
	ServeMessage(ctx context.Context, msg *Message) error
}

Handler is implemented by any value that can handle an inbound Message. It mirrors the net/http.Handler contract: return nil on success; return a non-nil error to signal that processing failed (the polling loop logs the error and continues).

type HandlerFunc

type HandlerFunc func(ctx context.Context, msg *Message) error

HandlerFunc is an adapter to allow the use of ordinary functions as [Handler]s, analogous to net/http.HandlerFunc.

func (HandlerFunc) ServeMessage

func (f HandlerFunc) ServeMessage(ctx context.Context, msg *Message) error

ServeMessage calls f(ctx, msg).

type ImageItem added in v0.1.1

type ImageItem struct {
	// Media is the CDN reference to the full-size image.
	Media *CDNMedia
	// ThumbMedia is the CDN reference to the thumbnail, if present.
	ThumbMedia *CDNMedia

	// AESKey is the raw hex AES-128 key (16 bytes) preferred for inbound
	// decryption over Media.AESKey.
	AESKey string
	// URL is a direct image URL (when provided by the server).
	URL string

	// Dimensions of the thumbnail and high-def variants (pixels / bytes).
	ThumbWidth  int
	ThumbHeight int
	ThumbSize   int
	MidSize     int
	HDSize      int
}

ImageItem is an image message with optional thumbnail.

type Item

type Item struct {
	Type  ItemType
	Text  *TextItem
	Image *ImageItem
	Voice *VoiceItem
	File  *FileItem
	Video *VideoItem

	// RefMsg is set when this item quotes a previously sent message.
	RefMsg *RefMessage

	// MsgID is the per-item message ID, set on inbound items.
	MsgID string

	// CreateTimeMs is the item creation timestamp in milliseconds (inbound only).
	CreateTimeMs int64
	// UpdateTimeMs is the item last-update timestamp in milliseconds (inbound only).
	UpdateTimeMs int64
	// IsCompleted indicates the item content is fully delivered (inbound only).
	IsCompleted bool
}

Item is a single content element inside a message. Exactly one of the item fields will be non-nil, determined by Type.

func TextMessage

func TextMessage(text string) Item

TextMessage returns an Item containing a plain text payload.

type ItemType

type ItemType int

ItemType identifies the kind of content in an Item.

const (
	ItemTypeText  ItemType = 1
	ItemTypeImage ItemType = 2
	ItemTypeVoice ItemType = 3
	ItemTypeFile  ItemType = 4
	ItemTypeVideo ItemType = 5
)

type Login

type Login struct {
	// QRCode is the raw QR code string that can be encoded as an image.
	QRCode string
	// URL is a pre-rendered QR code image URL ready for display in a browser.
	URL string
	// contains filtered or unexported fields
}

Login represents an in-progress QR code login session. Call StartLogin to begin, then Login.Wait to block until the user scans.

func StartLogin

func StartLogin(ctx context.Context) (*Login, error)

StartLogin initiates a new QR code login session and returns a Login value. Display Login.URL or encode Login.QRCode as an image, then call Login.Wait.

Example

ExampleStartLogin shows how to perform a QR code login and persist the token.

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"os"

	"github.com/lib-x/ilink"
)

func main() {
	ctx := context.Background()

	login, err := ilink.StartLogin(ctx)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Scan this URL with WeChat:", login.URL)

	token, err := login.Wait(ctx)
	if err != nil {
		log.Fatal(err)
	}

	// Persist token.BotToken for reuse across restarts.
	data, _ := json.Marshal(token)
	os.WriteFile("token.json", data, 0o600)
}

func (*Login) Wait

func (l *Login) Wait(ctx context.Context) (*Token, error)

Wait polls until the user scans and confirms the QR code, then returns a Token. It respects ctx cancellation and the QR code's own expiry.

Typical polling cadence is one request per second; the server responds immediately when the status changes.

type MediaItem deprecated

type MediaItem = CDNMedia

MediaItem is the legacy unified CDN reference kept for backwards compatibility with Client.UploadMedia. New inbound messages use the richer per-type structs.

Deprecated: use CDNMedia embedded in ImageItem, VoiceItem, etc.

type Message

type Message struct {
	// Seq is the message sequence number.
	Seq int64
	// MessageID is the unique message ID.
	MessageID int64

	// From is the sender's user ID (format: "xxx@im.wechat").
	From string
	// To is the bot's own user ID (format: "xxx@im.bot").
	To string
	// GroupID is set when the message was sent in a group chat.
	GroupID string
	// SessionID is the conversation session identifier.
	SessionID string

	// CreateTimeMs is the message creation timestamp in milliseconds.
	CreateTimeMs int64
	// UpdateTimeMs is the message last-update timestamp in milliseconds.
	UpdateTimeMs int64

	// ContextToken must be echoed back verbatim when replying, so WeChat
	// associates the reply with the correct conversation window.
	ContextToken string

	// Items holds the message content elements in order.
	Items []Item
}

Message is an inbound message received from a WeChat user.

func (*Message) IsGroup

func (m *Message) IsGroup() bool

IsGroup reports whether this message originated in a group chat.

func (*Message) Text

func (m *Message) Text() string

Text returns the concatenated text of all ItemTypeText items in the message, or an empty string if there are none.

type Option

type Option func(*Client)

Option configures a Client. Pass options to NewClient.

func WithBaseURL

func WithBaseURL(u string) Option

WithBaseURL overrides the iLink API base URL. Useful for testing against a mock server. The Token.BaseURL returned during login takes precedence.

func WithHTTPClient

func WithHTTPClient(hc *http.Client) Option

WithHTTPClient replaces the default HTTP client used for all API calls.

func WithLogger

func WithLogger(l *slog.Logger) Option

WithLogger sets the structured logger used by Client.ListenAndServe to report polling errors and handler panics. The default is slog.Default.

type OutboundMessage

type OutboundMessage struct {
	// To is the recipient user ID (format: "xxx@im.wechat").
	To string
	// GroupID optionally targets a group chat.
	GroupID string
	// ContextToken must match the context_token from the inbound message
	// that triggered this reply, so WeChat routes it to the right chat window.
	// Use [Client.Reply] to populate this automatically.
	ContextToken string
	// Items contains the content to send. At least one item is required.
	Items []Item
}

OutboundMessage is a message to be sent to a WeChat user.

type RefMessage added in v0.1.1

type RefMessage struct {
	// Item is the referenced message item.
	Item *Item
	// Title is a plaintext summary of the referenced content.
	Title string
}

RefMessage represents a quoted / referenced message.

type TextItem

type TextItem struct {
	Text string
}

TextItem holds a plain-text payload.

type Token

type Token struct {
	// BotToken is the Bearer token used to authenticate all API requests.
	BotToken string `json:"bot_token"`
	// BaseURL is the API base URL returned by the server for this account.
	// If empty, the global default is used.
	BaseURL string `json:"baseurl,omitempty"`
}

Token holds the credentials obtained after a successful QR code login. Persist this value; supply it to NewClient to avoid re-scanning on restart.

type UploadOptions

type UploadOptions struct {
	// FileName is embedded in the CDN reference for file-type attachments.
	FileName string

	// MediaType selects the upload slot: use the UploadMediaType* constants.
	// Defaults to UploadMediaTypeImage when zero.
	MediaType int

	// ToUserID is the target recipient's user ID. Required for routing when
	// the server needs it to resolve the CDN bucket.
	ToUserID string

	// ThumbData is raw thumbnail bytes for IMAGE or VIDEO uploads.
	// When set, a separate CDN slot is allocated for the thumbnail.
	ThumbData []byte

	// NoThumb suppresses thumbnail allocation even when ThumbData is empty.
	NoThumb bool
}

UploadOptions configures a Client.UploadMedia call.

type VideoItem

type VideoItem struct {
	// Media is the CDN reference to the video.
	Media *CDNMedia
	// ThumbMedia is the CDN reference to the thumbnail.
	ThumbMedia *CDNMedia

	// VideoSize is the plaintext video size in bytes.
	VideoSize int
	// PlayLengthMs is the video duration in milliseconds.
	PlayLengthMs int
	// VideoMD5 is the plaintext video MD5.
	VideoMD5 string

	ThumbSize   int
	ThumbWidth  int
	ThumbHeight int
}

VideoItem is a video message with an optional thumbnail.

type VoiceEncodeType added in v0.1.1

type VoiceEncodeType int

VoiceEncodeType indicates the audio encoding of a voice message.

const (
	VoiceEncodePCM      VoiceEncodeType = 1
	VoiceEncodeADPCM    VoiceEncodeType = 2
	VoiceEncodeFeature  VoiceEncodeType = 3
	VoiceEncodeSpeex    VoiceEncodeType = 4
	VoiceEncodeAMR      VoiceEncodeType = 5
	VoiceEncodeSilk     VoiceEncodeType = 6
	VoiceEncodeMP3      VoiceEncodeType = 7
	VoiceEncodeOGGSpeex VoiceEncodeType = 8
)

type VoiceItem

type VoiceItem struct {
	// Media is the CDN reference to the voice file.
	Media *CDNMedia

	// EncodeType is the audio encoding; [VoiceEncodeSilk] is the most common.
	EncodeType VoiceEncodeType
	// BitsPerSample and SampleRate describe the audio format.
	BitsPerSample int
	SampleRate    int
	// PlaytimeMs is the audio duration in milliseconds.
	PlaytimeMs int
	// RecognText is the ASR transcript (inbound only, may be empty).
	RecognText string
}

VoiceItem is a voice message.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL