微信iLink Bot Java SDK 2.1.0开箱即用:二维码登录+全类型消息收发+媒体上传下载

0 阅读14分钟

微信iLink Bot Java SDK 2.1.0开箱即用:二维码登录+全类型消息收发+媒体上传下载

前言:前段时间微信官方开放了iLink协议,并推出了一套标准化接口,支持开发者通过API完成机器人登录、消息收发(支持markdown格式)、媒体上传下载等核心能力。但原生接口存在调用流程繁琐、参数结构复杂、加解密逻辑需要手动处理、上下文管理等问题,直接接入成本高、开发效率低,很多开发者花费大量时间在协议对接上,反而无暇聚焦业务逻辑。

为此,我基于微信iLink官方协议,全新升级打造了这套Java SDK(当前版本2.1.0),对底层接口进行全面封装,简化了调用逻辑与参数处理,内置异常体系、状态管理、资源释放等核心能力,让Java开发者无需关心协议细节、无需处理加解密与上下文缓存,只需几行代码即可快速搭建稳定、合规的微信机器人。

在我看来,iLink协议真正的核心价值,并非只是做一个闲聊机器人,而是为开发者提供了第一条微信官方认可、合规安全的消息接入通道。它让服务端可以正规接收微信消息,告别以往通过Hook、逆向协议等非正规方式带来的封号风险与稳定性问题。基于这套官方通道,开发者可以自由扩展业务逻辑:消息触发、后台任务、系统通知、数据处理,甚至可以与大模型AI结合,构建智能客服、自动化工具、企业内部系统等复杂业务场景,真正实现安全、稳定、可持续的微信生态开发。

项目开源地址:github.com/lith0924/we…,欢迎Star、Fork,一起完善迭代~

一、SDK核心特性一览(2.1.0版本升级亮点)

相较于1.0版本,2.1.0版本进一步优化封装逻辑,完善功能覆盖,兼顾易用性与灵活性,新手可快速上手,老手可灵活扩展:

  • **全功能覆盖,无死角**:支持二维码登录、登录状态轮询、文本/图片/视频/文件/语音消息收发、输入态控制、媒体下载与AES解密、会话上下文管理,覆盖iLink Bot开发全链路。

  • **异常友好,精准排查**:自定义专属异常类,精准区分会话过期、业务异常、网络异常、上下文缺失等场景,方便开发者针对性处理,减少调试成本。

  • **内置优化,稳定可靠**:会话过期自动检测、网络波动指数退避重试、HTTP资源自动管理、SLF4J日志适配,同时内置心跳探测、线程池管理,保障生产环境稳定运行。

  • **易集成,零冗余依赖**:支持Maven、Gradle快速引入,无额外第三方冗余依赖,兼容主流Java版本,无需手动下载jar包,开箱即用。

  • **结构清晰,易于扩展**:采用分层设计,核心门面、业务服务、数据层、支撑层分离,配套专属DTO和实体类,避免参数混乱,便于后续功能扩展与维护。

二、快速引入依赖(2.1.0最新版本)

SDK已发布至Maven中央仓库,直接复制对应依赖即可集成,无需手动下载jar包,当前最新稳定版本为2.1.0

2.1 Maven依赖

<dependency>
    <groupId>io.github.lith0924</groupId>
    <artifactId>wechat-ilink-sdk</artifactId>
    <version>2.1.0</version>
</dependency>

2.2 Gradle依赖

implementation 'io.github.lith0924:wechat-ilink-sdk:2.1.0'

三、项目核心结构说明(分层设计,一目了然)

SDK做了清晰的分层设计,将底层原始接口和上层封装接口分离,同时配套专属DTO和实体类,避免参数混乱,开发者可按需选择调用,后续扩展更便捷:

类型说明示例
核心门面(入口层)SDK统一入口,封装所有对外暴露的API,简化调用ILinkClient、ILinkClientBuilder
核心服务(业务层)封装底层业务逻辑,区分原始API与封装APILoginService、MessageService、MediaService
DTO/实体类(数据层)友好型DTO屏蔽冗余字段,实体类保留官方完整结构LoginContext、QrCodeInfo、WeixinMessage
基础设施层(支撑层)提供通用能力支撑,降低业务层耦合ILinkConfig、RetryPolicy、HttpClientFacade

四、极速上手:10分钟搭建iLink机器人

下面通过完整代码示例,演示从创建客户端、扫码登录、消息接发到各类媒体消息发送的全流程,复制即可运行测试,新手也能快速上手。

package com.lth.wechat.ilink.example;

import com.github.wechat.ilink.sdk.ILinkClient;
import com.github.wechat.ilink.sdk.core.config.ILinkConfig;
import com.github.wechat.ilink.sdk.core.listener.OnLoginListener;
import com.github.wechat.ilink.sdk.core.listener.OnMessageListener;
import com.github.wechat.ilink.sdk.core.login.LoginContext;
import com.github.wechat.ilink.sdk.core.model.MessageItem;
import com.github.wechat.ilink.sdk.core.model.WeixinMessage;

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

/**
 * 微信 iLink Bot SDK 2.1.0 完整示例(扫码登录+消息收发+媒体操作)
 */
public class ILinkFullDemo {
    // 初始化客户端(可自定义配置)
    private static ILinkClient client = ILinkClient.builder()
            .config(ILinkConfig.builder()
                    .connectTimeoutMs(35000)
                    .readTimeoutMs(35000)
                    .httpMaxRetries(3)
                    .heartbeatEnabled(true)
                    .build())
            .onLogin(new OnLoginListener() {
                @Override
                public void onLoginSuccess(LoginContext context) {
                    System.out.println("登录成功,botId = " + context.getBotId());
                }

                @Override
                public void onLoginFailure(Throwable throwable) {
                    System.err.println("登录失败: " + throwable.getMessage());
                }
            })
            .onMessage(new OnMessageListener() {
                @Override
                public void onMessages(List<WeixinMessage> messages) {
                    System.out.println("收到" + messages.size() + "条消息");
                }
            })
            .build();
    private static String targetUserId, contextToken, cursor = "";

    public static void main(String[] args) throws Exception {
        login();                    // 1. 二维码登录
        receiveMessage();           // 2. 接收消息(获取目标用户ID和contextToken)
        sendTextWithTyping();       // 3. 带输入态发送文本
        sendImage();                // 4. 发送图片
        sendFile();                 // 5. 发送文件
        sendVideo();                // 6. 发送视频
        sendVoice();                // 7. 发送语音
        downloadMedia();            // 8. 下载媒体消息
        client.close();             // 9. 释放资源(必做)
    }

    /**
     * 1. 二维码登录(自动轮询登录状态)
     */
    private static void login() throws Exception {
        // 获取二维码内容(自行渲染为二维码)
        String qrCodeContent = client.executeLogin();
        System.out.println("请将以下内容渲染为二维码后扫码登录:");
        System.out.println(qrCodeContent);
        // 阻塞等待登录完成,获取登录凭证
        LoginContext context = client.getLoginFuture().get();
        System.out.println("登录成功,botId = " + context.getBotId());
    }

    /**
     * 2. 接收消息(获取目标用户ID和contextToken,用于后续发送消息)
     */
    private static void receiveMessage() throws Exception {
        while (targetUserId == null) {
            // 拉取消息(SDK自动管理cursor游标)
            List<WeixinMessage> messages = client.getUpdates();
            if (!messages.isEmpty()) {
                WeixinMessage msg = messages.get(0);
                targetUserId = msg.getFrom_user_id();
                contextToken = msg.getContext_token();
                System.out.println("获取目标用户ID:" + targetUserId);
                System.out.println("获取上下文标识:" + contextToken);
            }
            Thread.sleep(3000);
        }
    }

    /**
     * 3. 带输入态发送文本消息(模拟原生输入效果)
     */
    private static void sendTextWithTyping() throws Exception {
        // 开启输入态
        client.startTyping(targetUserId);
        // 模拟输入延迟
        Thread.sleep(1500);
        // 发送文本消息(SDK自动使用缓存的contextToken)
        client.sendText(targetUserId, "微信iLink Bot SDK 2.0.0 测试消息");
        // 停止输入态
        client.stopTyping(targetUserId);
    }

    /**
     * 4. 发送图片消息
     */
    private static void sendImage() throws Exception {
        // 读取本地图片字节
        byte[] imageBytes = Files.readAllBytes(Paths.get("demo.png"));
        // 发送图片(自动上传媒体,无需手动调用上传接口)
        client.sendImage(targetUserId, imageBytes, "demo.png", "测试图片");
    }

    /**
     * 5. 发送文件消息
     */
    private static void sendFile() throws Exception {
        byte[] fileBytes = Files.readAllBytes(Paths.get("demo.pdf"));
        client.sendFile(targetUserId, fileBytes, "demo.pdf", "测试文件");
    }

    /**
     * 6. 发送视频消息
     */
    private static void sendVideo() throws Exception {
        byte[] videoBytes = Files.readAllBytes(Paths.get("demo.mp4"));
        // 参数:目标用户ID、视频字节、文件名、视频时长(ms)、视频描述
        client.sendVideo(targetUserId, videoBytes, "demo.mp4", 5000, "测试视频");
    }

    /**
     * 7. 发送语音消息(silk格式)
     */
    private static void sendVoice() throws Exception {
        byte[] voiceBytes = Files.readAllBytes(Paths.get("demo.silk"));
        // 参数:目标用户ID、语音字节、文件名、语音时长(ms)、采样率
        client.sendVoice(targetUserId, voiceBytes, "demo.silk", 3000, 16000);
    }

    /**
     * 8. 下载媒体消息(从接收的消息中下载)
     */
    private static void downloadMedia() throws Exception {
        List<WeixinMessage> messages = client.getUpdates();
        for (WeixinMessage msg : messages) {
            if (msg.getItem_list() == null) continue;
            for (MessageItem item : msg.getItem_list()) {
                // 下载图片(自动AES解密)
                if (item.getImage_item() != null) {
                    byte[] imageBytes = client.downloadImageFromMessageItem(item);
                    Files.write(Paths.get("download.png"), imageBytes);
                    System.out.println("图片下载完成");
                }
            }
        }
    }
}

效果演示

SDK 2.1.0已完整支持各类媒体消息发送与下载,包括语音、图片、视频、文件等常见类型,支持markdown格式转化,同时可模拟微信原生输入状态展示,交互体验更贴近官方客户端,无需额外开发适配。

1.接收消息,可以接收普通消息,媒体类型消息,可以将媒体类型数据下载到本地处理,后端可做任意处理 请添加图片描述

2.发送消息,普通消息以及媒体类型消息,可以动态控制输入状态(正在输入中),普通消息支持markdown格式转换,此处不做演示了,感兴趣的朋友可以自己测试一下

请添加图片描述

五、核心API详解(分类整理,方便查阅)

SDK核心API分为登录、消息收发、媒体操作、配置四大类,所有封装API均简化了参数传递,无需拼接复杂请求体,直接调用即可实现对应功能。

5.1 登录相关API(最常用)

方法名称参数返回值接口类型核心说明
executeLogin()String(二维码内容)封装API一键获取二维码内容,新手首选,简化登录流程
getLoginFuture()Future&lt;LoginContext&gt;封装API获取登录结果,阻塞等待登录完成,返回登录凭证
getBotQrCode()QrCodeResp原始API原生获取二维码响应,保留官方完整字段,适合二次开发

5.2 消息收发API(全覆盖)

支持文本、图片、视频、文件、语音、输入态控制等各类消息,封装API直接传参即可调用,无需关心底层协议细节:

  • sendText(String targetUserId, String content):发送文本消息,最常用

  • sendTextWithTyping(String targetUserId, String content, long typingDurationMs):带输入态发送文本,一键实现输入态控制

  • sendImage(String targetUserId, byte[] imageBytes, String fileName, String desc):发送图片消息,自动上传媒体

  • sendVideo(String targetUserId, byte[] videoBytes, String fileName, long durationMs, String desc):发送视频消息

  • sendFile(String targetUserId, byte[] fileBytes, String fileName, String desc):发送文件消息

  • sendVoice(String targetUserId, byte[] voiceBytes, String fileName, long durationMs, int sampleRate):发送语音消息

  • getUpdates():接收消息,SDK自动管理cursor游标,无需手动维护

  • startTyping(String targetUserId) / stopTyping(String targetUserId):手动开启/停止输入态

5.3 媒体操作API

发送图片、视频等媒体消息前,SDK会自动完成媒体上传,无需手动调用上传接口;下载媒体时,自动完成AES解密,简化开发流程:

// 1. 发送媒体(自动上传)
byte[] imageBytes = Files.readAllBytes(Paths.get("test.jpg"));
client.sendImage("user@im.wechat", imageBytes, "test.jpg", "测试图片");

// 2. 下载媒体(自动解密)
List<WeixinMessage> messages = client.getUpdates();
for (WeixinMessage msg : messages) {
    for (MessageItem item : msg.getItem_list()) {
        if (item.getImage_item() != null) {
            byte[] imageBytes = client.downloadImageFromMessageItem(item);
            Files.write(Paths.get("download.jpg"), imageBytes);
        }
    }
}

5.4 配置与上下文API

  • ILinkConfig.builder():自定义客户端配置(超时时间、重试次数、心跳等)

  • clearContext(String targetUserId):清理单个用户会话上下文

  • clearAllContexts():清理所有用户会话上下文

  • close():关闭客户端,释放HTTP资源、线程池等(必做)

六、关键参数与规则详解(避坑必备)

对接iLink协议时,很多开发者会因参数格式、规则不熟悉踩坑,这里整理核心要点,帮大家快速避坑:

6.1 用户ID格式规范(必看)

注意:发送消息时,目标用户ID格式错误会直接导致发送失败,需严格遵循上述格式。

6.2 游标Cursor机制(核心)

Cursor是消息分页与增量拉取的关键,必须遵循以下规则,否则会出现消息重复或丢失:

  • 首次接收消息:cursor传空字符串(SDK内部已默认处理,无需手动传入)

  • 后续接收:SDK自动用上一次返回的nextCursor,无需手动管理

  • 旧游标:会返回该游标及之后所有历史数据

  • 新游标:仅返回游标之后的新消息

6.3 登录状态枚举

  • WAIT / WAITING:等待扫码

  • SCANED / SCANNED:已扫码,待用户确认

  • CONFIRMED / LOGGED_IN:已确认,登录成功

  • EXPIRED:二维码已过期,需重新获取二维码

6.4 媒体类型对应值

类型值媒体类型说明
1图片支持png、jpg等常见格式
2视频需传入视频时长(ms)
3文件无格式限制,需传入文件名
4语音仅支持silk格式,需传入时长和采样率

6.5 contextToken(会话上下文核心)

contextToken是消息上下文标识,用于关联发送消息与对应会话,必须满足以下条件才能正常使用:

  • 来源:从接收的消息(getUpdates())中获取,无法自定义

  • 缓存:SDK会自动按用户ID缓存最新的contextToken,无需手动传入

  • 异常:若提示“missing latest context token”,需先让目标用户发消息,再调用getUpdates()拉取

七、异常处理最佳实践

SDK自定义了专属异常,重点关注会话过期异常,出现该异常必须重新扫码登录,其他异常可统一捕获处理,降低调试成本:

try {
    // 调用消息接收或发送接口
    List<WeixinMessage> messages = client.getUpdates();
    client.sendText(targetUserId, "测试消息");
} catch (NotLoginException e) {
    // 未登录异常,需先执行登录
    System.err.println("请先扫码登录:" + e.getMessage());
} catch (ILinkSessionExpiredException e) {
    // 会话过期(错误码ret=-14),必须重新扫码登录
    System.err.println("会话已过期,请重新启动程序扫码登录");
} catch (ILinkException e) {
    // 其他业务异常,参数错误、网络失败等
    System.err.println("SDK调用异常:" + e.getMessage());
} catch (IOException e) {
    // 网络或IO异常,如文件读取失败、HTTP请求异常
    System.err.println("IO/网络异常:" + e.getMessage());
} catch (Exception e) {
    // 通用异常捕获
    e.printStackTrace();
}

八、自定义客户端配置

默认客户端已满足绝大多数场景,如需自定义超时时间、HTTP客户端配置、重试策略、心跳参数等,可手动创建配置对象传入,灵活性拉满:

import com.github.wechat.ilink.sdk.core.config.ILinkConfig;
import java.time.Duration;
import java.net.http.HttpClient;

// 1. 自定义HTTP客户端
HttpClient httpClient = HttpClient.newBuilder()
        .connectTimeout(Duration.ofSeconds(10))  // 连接超时10秒
        .build();

// 2. 自定义SDK配置
ILinkConfig config = ILinkConfig.builder()
        .connectTimeoutMs(15000)                // 连接超时15秒
        .readTimeoutMs(15000)                   // 读取超时15秒
        .writeTimeoutMs(15000)                  // 写入超时15秒
        .httpMaxRetries(5)                      // HTTP最大重试次数5次
        .retryBaseDelayMs(1000)                 // 重试基础退避时间1秒
        .retryMaxDelayMs(10000)                 // 最大退避时间10秒
        .heartbeatEnabled(true)                 // 启用心跳探测
        .heartbeatIntervalMs(30000)             // 心跳间隔30秒
        .channelVersion("1.0.0")                // 渠道版本
        .build();

// 3. 初始化自定义配置的客户端
ILinkClient client = ILinkClient.builder()
        .config(config)
        .httpClient(httpClient)
        .build();

九、SDK内置亮点特性(2.1.0升级重点)

  • **会话过期自动检测**:精准捕获ret=-14异常,抛出专属会话过期异常,无需手动判断错误码,简化异常处理逻辑。

  • **指数退避重试**:网络波动、接口超时场景自动重试,避免单次请求失败导致业务中断,保障服务稳定性。

  • **资源自动管理**:内置HTTP连接池、线程池管理,实现AutoCloseable接口,调用close()即可释放所有资源,防止内存泄漏。

  • **日志适配**:兼容SLF4J日志框架,可接入项目现有日志体系,方便调试和线上监控,日志粒度可灵活控制。

  • **上下文自动缓存**:拉取消息后自动缓存最新contextToken,发送消息时无需手动传入,降低调用难度。

  • **Builder模式优化**:客户端、配置均支持Builder模式,代码更简洁,配置更灵活,易于扩展。

十、常见问题与避坑指南(高频问题汇总)

  • Q:发送消息提示“missing latest context token”? A:原因:目标用户未给Bot发过消息,或消息未被getUpdates()拉取到,导致SDK无法获取contextToken。

解决:让目标用户先发一条消息 → 调用getUpdates()拉取消息 → 再执行发送操作。

  • Q:登录失败或登录结果为空? A:优先检查:网络是否正常、二维码是否过期、登录轮询是否完整、是否成功获取LoginContext。

  • Q:接收不到新消息? A:检查cursor是否由SDK自动管理,无需手动传入;若手动维护cursor,需确保每次使用上一次返回的nextCursor。

  • Q:媒体消息发送失败? A:先确认文件能正常读取、媒体类型值匹配;发送图片/视频/文件/语音时,无需手动上传媒体,SDK会自动处理。

  • Q:媒体下载失败? A:优先检查:消息项是否包含媒体、CDNMedia的encrypt_query_param和aes_key是否存在、下载后文件是否正确保存。

  • Q:登录掉线后无法恢复? A:当前产品模型下,登录状态失效后需手动重新登录,不支持自动重新拉起二维码流程,可捕获会话过期异常触发重新登录。

十一、项目总结与后续规划

微信iLink Bot Java SDK 2.1.0 核心目标是“让iLink Bot开发更简单、更稳定”,无需关注底层协议细节,无需重复开发通用能力,一键集成登录、消息、媒体等核心功能,适配企业级开发场景,稳定、易用、可扩展。

相较于1.0版本,2.1.0进一步优化了封装逻辑,完善了功能覆盖,新增了上下文自动缓存、Builder模式、心跳探测等特性,解决了开发者在对接过程中遇到的各类痛点,让开发者能将更多精力聚焦在业务逻辑上。

后续将随微信官方接口开放持续迭代扩展

目前我也正在基于该协议做一些有趣的项目产品开发,大家感兴趣的可以持续关注一下。

再次附上项目开源地址:github.com/lith0924/we…,欢迎大家Star支持,提交Issue反馈问题、提交PR贡献代码,一起完善这个工具,让更多开发者受益!