Files
cg33andClaude 00b719d215 feat: 微信个人号(ilink)平台、扫码 setup 与 CDN 媒体 (#257)
* feat(platform): add Weixin personal (ilink) long-poll channel

Implement weixin platform against ilink bot HTTP API (getUpdates +
sendMessage), matching the OpenClaw openclaw-weixin backend.

- Text, quoted text, and voice messages with ASR text; media-only inbound
  gets a placeholder until CDN decrypt is added
- Persist get_updates_buf and per-peer context_token under data_dir
- allow_from, optional proxy/SKRouteTag, session pause on errcode -14
- ReplyContextReconstructor for cron; FormattingInstructionProvider
- Inject cc_data_dir/cc_project into all platform options for state paths
- Build tag no_weixin; Makefile + docs + config.example.toml

* feat(weixin): ilink QR setup CLI, CDN encrypt/decrypt, and Chinese docs

- Add cc-connect weixin setup/bind/new with get_bot_qrcode + get_qrcode_status flow
- Config helpers EnsureProjectWithWeixinPlatform / SaveWeixinPlatformCredentials
- Inbound CDN decrypt and outbound ImageSender/FileSender via getuploadurl
- Document weixin.md (ZH), usage guides, README/INSTALL links

Made-with: Cursor

* fix(weixin): remove unused netError type for golangci-lint

Made-with: Cursor

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-22 13:01:28 +08:00

58 lines
1.2 KiB
Go

package weixin
import (
"fmt"
"strings"
)
func isMediaItemType(t int) bool {
switch t {
case messageItemImage, messageItemVoice, messageItemFile, messageItemVideo:
return true
default:
return false
}
}
// bodyFromItemList extracts user-visible text from Weixin item_list (text, quotes, voice ASR).
func bodyFromItemList(items []messageItem) string {
if len(items) == 0 {
return ""
}
for _, item := range items {
switch item.Type {
case messageItemText:
if item.TextItem == nil {
continue
}
text := strings.TrimSpace(item.TextItem.Text)
ref := item.RefMsg
if ref == nil {
return text
}
if ref.MessageItem != nil && isMediaItemType(ref.MessageItem.Type) {
return text
}
var parts []string
if ref.Title != "" {
parts = append(parts, ref.Title)
}
if ref.MessageItem != nil {
refBody := bodyFromItemList([]messageItem{*ref.MessageItem})
if refBody != "" {
parts = append(parts, refBody)
}
}
if len(parts) == 0 {
return text
}
return fmt.Sprintf("[引用: %s]\n%s", strings.Join(parts, " | "), text)
case messageItemVoice:
if item.VoiceItem != nil && strings.TrimSpace(item.VoiceItem.Text) != "" {
return strings.TrimSpace(item.VoiceItem.Text)
}
}
}
return ""
}