?前言
就在前天,微信官方終于推出了小龍蝦接入微信的插件「微信 ClawBot」:
![]()
那作為一個程序員,我就想了,微信能把小龍蝦接進來,那不就能把滿足協議要求的任何機器人接進來了么?二開一個官方支持的微信機器人不再是夢想!
![]()
這篇文章就來講一下怎么根據微信官方支持的渠道,實現一個自己高度自定義的機器人,而不是局限于小龍蝦這個框架。
背景
根據官方的插件接入指南可以看到,微信機器人主要是靠一個叫@tencent-weixin/openclaw-weixin-cli的 npm 包:
![]()
那我們來到這個包在 npm 上的對應頁面 https://www.npmjs.com/package/@tencent-weixin/openclaw-weixin-cli,可以看到有非常清晰的說明:![]()
從介紹里我們也能看到,這個包只是一個安裝程序,并沒有程序的實際內容,實際的邏輯都在@tencent/openclaw-weixin里:
![]()
所以我們需要找到這個包對應的 npm 頁面 https://www.npmjs.com/package/@tencent-weixin/openclaw-weixin,核心的內容就是下圖中的這段話:
![]()
就是說,想把自己的程序接入到微信機器人里,只需要實現這5個 Http 接口就好了。
接口
路徑
作用
getUpdates
ilink/bot/getupdates
長輪詢拉取新消息
sendMessage
ilink/bot/sendmessage
發送消息給用戶
getUploadUrl
ilink/bot/getuploadurl
獲取媒體文件上傳地址
getConfig
ilink/bot/getconfig
獲取賬號配置
sendTyping
ilink/bot/sendtyping
發送「正在輸入」狀態
文檔的旁邊還有源碼,這也是我們二開的重要參考:
![]()
? 整體架構
理清楚上面的背景之后,我們要做的這個項目的數據流向就非常清晰了:收消息 → 問 AI → 發回復。
![]()
核心實現 一、掃碼登錄
掃碼登錄是整個流程的第一步。微信的登錄協議是一個典型的「生成二維碼 → 輪詢狀態 → 獲取 Token」流程:
![]()
首先通過get_bot_qrcode接口拿到二維碼:
async function fetchQRCode(): Promise {
const url = `${BASE_URL}/ilink/bot/get_bot_qrcode?bot_type=${BOT_TYPE}`;
const res = await fetch(url);
if (!res.ok) throw new Error(`獲取二維碼失敗: ${res.status}`);
return (await res.json()) as QRCodeResponse;
}
這里參考的是插件源碼中的src/auth/login-qr.ts文件:
![]()
然后用qrcode-terminal把二維碼渲染到終端里,用微信掃碼即可:
![]()
掃碼后,通過長輪詢get_qrcode_status接口來追蹤狀態變化。這里的狀態機有四種狀態:
wait—— 等待掃碼,繼續輪詢
scaned—— 用戶已掃碼,等待手機確認
expired—— 二維碼過期,自動刷新(最多 3 次)
confirmed—— 用戶確認,返回
bot_token和ilink_bot_id
case "confirmed": {
if (!status.bot_token || !status.ilink_bot_id) {
thrownewError("登錄確認但未返回 token 或 bot_id");
}
const creds: LoginCredentials = {
token: status.bot_token,
baseUrl: status.baseurl || BASE_URL,
accountId: status.ilink_bot_id,
userId: status.ilink_user_id,
};
saveCredentials(creds);
console.log(`[auth] ? 登錄成功! accountId=${creds.accountId}`);
return creds;
}
拿到憑證后保存到data/credentials.json,文件權限設為0o600(僅當前用戶可讀寫)。下次啟動時如果憑證還在,就跳過掃碼直接復用。
我們只需要點擊連接,就登錄成功可以對話啦:
![]()
二、微信 API 層
和微信通信的核心就是 HTTP POST,但有幾個協議細節需要注意。
1)每個請求都需要攜帶特定的 Header:
function buildHeaders(token?: string): Record {
const headers: Record = {
"Content-Type": "application/json",
AuthorizationType: "ilink_bot_token",
"X-WECHAT-UIN": randomWechatUin(),
};
if (token) {
headers.Authorization = `Bearer ${token}`;
}
return headers;
}
X-WECHAT-UIN是一個隨機的 uint32 經過 base64 編碼,每次請求都重新生成。AuthorizationType固定為ilink_bot_token。
2)getUpdates 長輪詢:
這是整個機器人的「耳朵」。它通過一個游標get_updates_buf實現增量同步,服務端會 hold 住請求直到有新消息或者超時(默認 35 秒):
export asyncfunction getUpdates(
baseUrl: string,
token: string,
buf: string,
timeoutMs = DEFAULT_LONG_POLL_TIMEOUT_MS,
): Promise {
try {
returnawait apiPost
(
baseUrl,
"ilink/bot/getupdates",
{ get_updates_buf: buf },
token,
timeoutMs,
);
} catch (err) {
if (err instanceofError && err.name === "AbortError") {
return { ret: 0, msgs: [], get_updates_buf: buf };
}
throw err;
}
}
注意這里對AbortError(超時)的處理——長輪詢超時是正常現象,直接返回空響應讓調用方繼續下一輪即可。
3)sendMessage:
發消息需要構造一個符合微信協議的WeixinMessage結構,里面的關鍵字段是context_token,這是微信用來標識會話上下文的令牌,必須從收到的消息里提取出來回傳:
await apiPost(
baseUrl,
"ilink/bot/sendmessage",
{
msg: {
from_user_id: "",
to_user_id: to,
client_id: clientId,
message_type: MessageType.BOT,
message_state: MessageState.FINISH,
item_list: [{ type: MessageItemType.TEXT, text_item: { text } }],
context_token: contextToken,
},
},
token,
);
三、AI 對話層AI 層使用 OpenAI SDK,但通過baseURL參數實現了對任意兼容接口的支持。所以不管你用 GPT、DeepSeek 還是智譜 GLM,改個環境變量就行。
核心設計是按用戶維度維護獨立的對話上下文:
export class AIChat {
private sessions = new Map();
async chat(userId: string, userMessage: string): Promise {
const session = this.getSession(userId);
session.history.push({ role: "user", content: userMessage });
// 滑動窗口,防止上下文過長
if (session.history.length > this.maxHistory) {
session.history = session.history.slice(-this.maxHistory);
}
const messages: ChatCompletionMessageParam[] = [
{ role: "system", content: this.systemPrompt },
...session.history,
];
const completion = awaitthis.client.chat.completions.create({
model: this.model,
messages,
});const reply = completion.choices[0]?.message?.content || "(AI 未返回內容)";
session.history.push({ role: "assistant", content: reply });
return reply;
}
}
每個微信用戶 ID 對應一個ChatSession,里面存著這個用戶的對話歷史。通過滑動窗口來控制上下文長度,避免 token 超限。
用戶可以發送/clear指令來清空對話上下文,重新開始。
四、Bot 主循環
最后就是把所有模塊串起來的 Bot 主循環。它的核心就是一個while循環:
while (this.running) {
try {
const resp = await getUpdates(
this.credentials.baseUrl,
this.credentials.token,
this.getUpdatesBuf,
);
// 更新游標
if (resp.get_updates_buf) {
this.getUpdatesBuf = resp.get_updates_buf;
}// 處理每條消息
for (const msg of resp.msgs ?? []) {
awaitthis.handleMessage(msg);
}
} catch (err) {
// 重試 + 退避策略
}
}
消息處理的邏輯:
過濾非用戶消息,只處理
message_type === USER的消息緩存
context_token提取文本內容
通過
sendMessage發回去
處理失敗還有重試策略:連續失敗不超過 5 次時,每次等 2 秒后重試;超過 5 次則退避 30 秒,避免頻繁請求被限流。
使用方式 環境要求
Node.js >= 22
# 1. 克隆并安裝
git clone https://github.com/user/wx-robot-ilink.git
cd wx-robot-ilink
npm install
# 2. 配置 AI 模型
cp .env.example .env
# 編輯 .env 填入你的 API Key# 3. 啟動
npm run dev
首次啟動會在終端顯示二維碼,微信掃碼后在手機確認,就能開始使用了。
結語
以上就是wx-robot-ilink的完整實現啦。回顧一下,整個項目做了這幾件事:
從
@tencent-weixin/openclaw-weixin源碼里提取了微信 iLink HTTP API 協議實現了掃碼登錄方案是 QR 狀態機 + 憑證持久化
實現了長輪詢收消息 + 發消息的通信層
接入 OpenAI 兼容 AI 模型,支持多輪對話
用一個 while 循環把所有東西串起來
整體不到 300 行 TypeScript,沒有任何重型框架依賴,就實現了一個微信 AI 機器人。
當然,目前這個版本還是比較基礎的,后續可以擴展的方向有很多:
圖片/語音支持 —— 協議本身支持 IMAGE、VOICE、VIDEO、FILE 類型,可以實現多模態對話
持久化對話歷史 —— 目前對話在內存中,重啟會丟失,可以接 SQLite 或 Redis
定時消息 —— 結合 cron 實現早報、天氣推送等
更多功能還是要等微信官方支持~
項目已開源:https://github.com/co-pine/wx-robot-ilink,歡迎大家 Star、Fork、提 Issue!
這篇文章是我們編程導航團隊「松柏」同學的實戰,如果覺得寫得不錯,可以給個點贊和關注支持一下哦~
有問題歡迎在評論區交流,下期再見!
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.