2 Star 12 Fork 2

OrdinaryRoad / ordinaryroad-live-chat-client

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
MIT

ordinaryroad-live-chat-client

license release Maven Central

This project is in progress... 👨‍💻,有问题欢迎提交issuse, 觉得有用的话可以点个小星星⭐️鼓励一下,感谢

如果对项目感兴趣也欢迎加入QQ频道交流讨论, 提交PR

ToDo List: https://github.com/orgs/OrdinaryRoad-Project/projects/1

更新日志:https://github.com/OrdinaryRoad-Project/ordinaryroad-live-chat-client/releases


Live room WebSocket chat client

  • Feature 0: Netty
  • Feature 1: 消息中的未知属性统一放到单独的MAP中(JSON格式消息)
  • Feature 2: 支持自动重连
  • Feature 3: 支持同时监听多个直播间
  • Feature 4: 支持短直播间id
  • Feature 5*: 支持弹幕发送、为主播点赞
  • Feature 6*: 内置收到弹幕、收到礼物、收到醒目留言、用户入房、收到点赞、状态变化、统计信息、社交消息回调
  • Feature 7: 支持消息转发
  • Feature 8: 支持单独引入编解码模块
  • Feature 9: 支持网络代理

*存在平台差异

  • ✅: 平台支持且已完成
  • ☑️️: 平台支持但未实现
  • ❌: 平台网页端暂不支持

平台适配情况表

平台 LiveChatClient Cookie 短直播间id 发送弹幕 为主播点赞
Bilibili B站
Douyu 斗鱼
Huya 虎牙
Douyin 抖音* ☑️️ ☑️ ☑️️
Kuaishou 快手

*未实现抖音平台的签名算法,相当于半成品

平台直播间消息适配情况表

平台 弹幕 礼物 醒目留言 进入房间 点赞 状态变化 统计信息 社交消息
Bilibili B站 ✅(点赞数、当前人数、累计观看人数)
Douyu 斗鱼 ☑️ ☑️
Huya 虎牙 ✅(高级用户) ☑️
Douyin 抖音 ✅(点赞个数) ✅(点赞数、当前人数)
Kuaishou 快手 ✅(礼物信息不全) ✅(首次点赞) ✅(点赞数、当前人数)

消息接口内置的方法见 https://github.com/OrdinaryRoad-Project/ordinaryroad-live-chat-client/tree/main/live-chat-client-commons/live-chat-client-commons-base/src/main/java/tech/ordinaryroad/live/chat/client/commons/base/msg

运行效果图 运行效果


⭐BarrageFly——让弹幕飞,基于该项目的一个弹幕转发、过滤、处理平台

0 原理

抓取浏览器的WebSocket二进制流,然后分析模拟浏览器的行为;这种方式的优点是不需要开发者认证,缺点是没有官方文档,分析过程比较费时费力,并且需要适配不同平台的流程变化(不会经常变化)

以后可能会考虑支持平台的开放协议

1 安装

JDK ≥ 8

B站


<dependency>
    <groupId>tech.ordinaryroad</groupId>
    <artifactId>live-chat-client-bilibili</artifactId>
    <!-- 参考github release版本,不需要前缀`v` -->
    <version>${ordinaryroad-live-chat-client.version}</version>
</dependency>

Gradle用户注意:自从0.3.2版本开始,B站Client配置中,压缩方式默认为NORMAL_BROTLI,当使用Gradle引入时,还需要引入操作系统对应的brotli4j native包,详见:https://github.com/hyperxpro/Brotli4j?tab=readme-ov-file#gradle

例如:

val liveChatClientBrotliVersion = "1.16.0"
// Windows
implementation("com.aayushatharva.brotli4j:native-windows-x86_64:$liveChatClientBrotliVersion")
implementation("com.aayushatharva.brotli4j:native-windows-aarch64:$liveChatClientBrotliVersion")
// Linux
implementation("com.aayushatharva.brotli4j:native-linux-armv7:$liveChatClientBrotliVersion")
implementation("com.aayushatharva.brotli4j:native-linux-aarch64:$liveChatClientBrotliVersion")
implementation("com.aayushatharva.brotli4j:native-linux-x86_64:$liveChatClientBrotliVersion")
implementation("com.aayushatharva.brotli4j:native-linux-s390x:$liveChatClientBrotliVersion")
implementation("com.aayushatharva.brotli4j:native-linux-riscv64:$liveChatClientBrotliVersion")
implementation("com.aayushatharva.brotli4j:native-linux-ppc64le:$liveChatClientBrotliVersion")
// Mac
implementation("com.aayushatharva.brotli4j:native-osx-aarch64:$liveChatClientBrotliVersion")
implementation("com.aayushatharva.brotli4j:native-osx-x86_64:$liveChatClientBrotliVersion")

如果引入后仍无法使用,请修改protover配置项,例如改为NORMAL_ZLIBBilibiliLiveChatClientConfig.builder().protover(ProtoverEnum.NORMAL_ZLIB).roomId("xxx").build()

斗鱼


<dependency>
    <groupId>tech.ordinaryroad</groupId>
    <artifactId>live-chat-client-douyu</artifactId>
    <!-- 参考github release版本,不需要前缀`v` -->
    <version>${ordinaryroad-live-chat-client.version}</version>
</dependency>

虎牙


<dependency>
    <groupId>tech.ordinaryroad</groupId>
    <artifactId>live-chat-client-huya</artifactId>
    <!-- 参考github release版本,不需要前缀`v` -->
    <version>${ordinaryroad-live-chat-client.version}</version>
</dependency>

抖音


<dependency>
    <groupId>tech.ordinaryroad</groupId>
    <artifactId>live-chat-client-douyin</artifactId>
    <!-- 参考github release版本,不需要前缀`v` -->
    <version>${ordinaryroad-live-chat-client.version}</version>
</dependency>

快手


<dependency>
    <groupId>tech.ordinaryroad</groupId>
    <artifactId>live-chat-client-kuaishou</artifactId>
    <!-- 参考github release版本,不需要前缀`v` -->
    <version>${ordinaryroad-live-chat-client.version}</version>
</dependency>

2 使用

测试类包含了多种样例,可供参考

2.0 不同平台的CMD定义

可以重写onCmdMsg(收到的所有CMD消息)onOtherCmdMsg(框架未处理的CMD消息)回调方法,判断CMD来监听框架已经定义的CMD类型

如果要监听的消息枚举类中未定义,可以考虑重写onUnknownCmdMsg(未知CMD消息)方法

ICmdMsg类型转换对应关系

  • B站:MessageMsg
  • 斗鱼:DouyuCmdMsg
  • 虎牙:WSPushMessage 或 WSMsgItem
  • 抖音:DouyinCmdMsg
  • 快手:KuaishouCmdMsg
@Override
public void onOtherCmdMsg(BilibiliCmdEnum cmd, ICmdMsg<BilibiliCmdEnum> cmdMsg) {
        switch (cmd) {
            case GUARD_BUY: {
                // 有人上舰
                MessageMsg messageMsg = (MessageMsg) cmdMsg;
                ...
                break;
            }
            case SUPER_CHAT_MESSAGE_DELETE: {
                // 删除醒目留言
                MessageMsg messageMsg = (MessageMsg) cmdMsg;
                ...
                break;
            }
            default: {
                // ignore
            }
        }
}

2.1 Client模式

Spring Boot 示例 client-example

  1. 创建配置
  2. 创建Client并传入配置、添加消息回调
  3. 开始监听直播间

如果需要查看其他平台的效果,请将Bilibili改为其他平台对应的英文,并修改消息回调接口的函数签名

public class ClientModeExample {
    public static void main(String[] args) {
        String cookie = System.getenv("cookie");
        // 1. 创建配置
        BilibiliLiveChatClientConfig config = BilibiliLiveChatClientConfig.builder()
                // TODO 消息转发地址
                .forwardWebsocketUri("")
                // TODO 浏览器Cookie
                .cookie(cookie)
                // TODO 直播间id(支持短id)
                .roomId(7777)
                .build();

        // 2. 创建Client并传入配置、添加消息回调
        BilibiliLiveChatClient client = new BilibiliLiveChatClient(config, new IBilibiliMsgListener() {
            @Override
            public void onDanmuMsg(BilibiliBinaryFrameHandler binaryFrameHandler, DanmuMsgMsg msg) {
                IBilibiliMsgListener.super.onDanmuMsg(binaryFrameHandler, msg);
                System.out.printf("%s 收到弹幕 %s %s(%s):%s\n", binaryFrameHandler.getRoomId(), msg.getBadgeLevel() != 0 ? msg.getBadgeLevel() + msg.getBadgeName() : "", msg.getUsername(), msg.getUid(), msg.getContent());
            }

            @Override
            public void onGiftMsg(BilibiliBinaryFrameHandler binaryFrameHandler, SendGiftMsg msg) {
                IBilibiliMsgListener.super.onGiftMsg(binaryFrameHandler, msg);
                System.out.printf("%s 收到礼物 %s %s(%s) %s %s(%s)x%s(%s)\n", binaryFrameHandler.getRoomId(), msg.getBadgeLevel() != 0 ? msg.getBadgeLevel() + msg.getBadgeName() : "", msg.getUsername(), msg.getUid(), msg.getData().getAction(), msg.getGiftName(), msg.getGiftId(), msg.getGiftCount(), msg.getGiftPrice());
            }

            @Override
            public void onSuperChatMsg(BilibiliBinaryFrameHandler binaryFrameHandler, SuperChatMessageMsg msg) {
                IBilibiliMsgListener.super.onSuperChatMsg(binaryFrameHandler, msg);
                System.out.printf("%s 收到醒目留言 %s(%s):%s\n", binaryFrameHandler.getRoomId(), msg.getUsername(), msg.getUid(), msg.getContent());
            }

            @Override
            public void onEnterRoomMsg(InteractWordMsg msg) {
                System.out.printf("%s %s(%s) 进入直播间\n", msg.getBadgeLevel() != 0 ? msg.getBadgeLevel() + msg.getBadgeName() : "", msg.getUsername(), msg.getUid());
            }

            @Override
            public void onLikeMsg(BilibiliBinaryFrameHandler binaryFrameHandler, LikeInfoV3ClickMsg msg) {
                IBilibiliMsgListener.super.onLikeMsg(binaryFrameHandler, msg);
                System.out.printf("%s 收到点赞 %s %s(%s)\n", binaryFrameHandler.getRoomId(), msg.getBadgeLevel() != 0 ? msg.getBadgeLevel() + msg.getBadgeName() : "", msg.getUsername(), msg.getUid());
            }

            @Override
            public void onLiveStatusMsg(BilibiliBinaryFrameHandler binaryFrameHandler, BilibiliLiveStatusChangeMsg msg) {
                IBilibiliMsgListener.super.onLiveStatusMsg(binaryFrameHandler, msg);
                System.out.printf("%s 状态变化 %s\n", binaryFrameHandler.getRoomId(), msg.getLiveStatusAction());
            }

            @Override
            public void onRoomStatsMsg(BilibiliBinaryFrameHandler binaryFrameHandler, BilibiliRoomStatsMsg msg) {
                IBilibiliMsgListener.super.onRoomStatsMsg(binaryFrameHandler, msg);
                System.out.printf("%s 统计信息 累计点赞数: %s, 当前观看人数: %s, 累计观看人数: %s\n", binaryFrameHandler.getRoomId(), msg.getLikedCount(), msg.getWatchingCount(), msg.getWatchedCount());
            }
        });

        // 添加客户端连接状态回调
        client.addStatusChangeListener((evt, oldStatus, newStatus) -> {
            if (newStatus == ClientStatusEnums.CONNECTED) {
                // TODO 要发送的弹幕内容,请注意控制发送频率;框架内置支持设置发送弹幕的最少时间间隔,小于时将忽略该次发送
                client.sendDanmu("666666" + RandomUtil.randomNumbers(1));
            }
        });

        // 3. 开始监听直播间
        client.connect();
    }
}

2.1.1 Client相关API

  • 连接
    • void connect(Runnable success, Consumer<Throwable> failed)
    • void connect(Runnable success)
    • void connect()
  • 断开连接
    • void disconnect(boolean cancelReconnect)
    • void disconnect()
  • 销毁
    • void destroy()
  • 发送消息
    • void send(Object msg, Runnable success, Consumer<Throwable> failed)
    • void send(Object msg, Runnable success)
    • void send(Object msg, Consumer<Throwable> failed)
    • void send(Object msg)
  • 发送弹幕
    • void sendDanmu(Object danmu, Runnable success, Consumer<Throwable> failed)
    • void sendDanmu(Object danmu, Runnable success)
    • void sendDanmu(Object danmu, Consumer<Throwable> failed)
    • void sendDanmu(Object danmu)
  • 为主播点赞
    • void clickLike(int count, Runnable success, Consumer<Throwable> failed)
    • void clickLike(int count, Runnable success)
    • void clickLike(int count, Consumer<Throwable> failed)
    • void clickLike(int count)
  • 添加消息监听器
    • boolean addMsgListener(MsgListener msgListener)
    • boolean addMsgListeners(List<MsgListener> msgListeners)
  • 移除消息监听器
    • boolean removeMsgListener(MsgListener msgListener)
    • boolean removeMsgListeners(List<MsgListener> msgListeners)
  • 获取当前状态
    • ClientStatusEnums getStatus()
  • 添加状态变化监听器
    • void addStatusChangeListener(IClientStatusChangeListener listener)
  • 移除状态变化监听器
    • void removeStatusChangeListener(IClientStatusChangeListener listener)

2.2 高级模式

参考 BilibiliHandlerModeExample

2.3 单独引入编解码模块

参考 codec-example

B站示例,其他平台只需修改bilibili即可

使用Gradle引入B站编解码模块时,参考#B站


<dependency>
    <groupId>tech.ordinaryroad</groupId>
    <artifactId>live-chat-client-codec-bilibili</artifactId>
    <!-- 参考github release版本,不需要前缀`v` -->
    <version>${ordinaryroad-live-chat-client.version}</version>
</dependency>

2.4 设置代理

生效范围:仅项目自身,不会影响引用该项目的父项目

在代码中修改Configsocks5ProxyHost("127.0.0.1"), socks5ProxyPort("1080")

身份认证(暂未测试)

  • socks5ProxyUsername("username"), socks5ProxyPassword("password")

image【极光HTTP代理】企业级代理IP云服务商,48小时线下技术支持,仅0.006元/IP- 无效IP不计费

3 项目说明

3.1 commons模块

主要是抽象接口、抽象类的定义

3.1.1 commons-base

定义了一些基础的接口、抽象类:消息、消息监听器、连接连监听器

  • 消息接口
    • IMsg:所有msg都应该实现该接口
      • ICmdMsg:有些平台的一些消息正文中没有消息类型cmd字段,例如B站的心跳包,因此再细分为cmdMsg
      • IDanmuMsg: 内置获取用户ID、用户名、用户头像、粉丝牌名称、粉丝牌等级、弹幕内容等方法
        • ISuperChatMsg:醒目留言,内置获取持续时间方法
      • IGiftMsg: 内置获取发送方ID、发送方用户名、发送方头像、接收方ID、接收方用户名、礼物名称、礼物图片、礼物ID、礼物个数、礼物单价等方法
      • IEnterRoomMsg: 内置获取用户ID、用户名、用户头像、粉丝牌名称、粉丝牌等级方法
      • ILikeMsg: 内置获取用户ID、用户名、用户头像、粉丝牌名称、粉丝牌等级、点赞数方法
      • ILiveStatusChangeMsg: 内置获取状态变化方法
  • 消息监听器
    • IBaseMsgListener(所有平台都支持,其他消息监听器存在平台差异)
      • onMsg:所有消息(不管消息内容)都会调用,不包括由该消息的某个字段派生出的消息,例如快手的弹幕礼物等消息是SC_FEED_PUSH中的字段,因此onMsg中不会出现处理后的弹幕、礼物消息,而是包含弹幕、礼物等的SCWebFeedPushCMD消息
      • onCmdMsg:cmd消息(消息体中有表示消息类型的字段时),并且该类型需要处理(例如心跳回复包不需要处理)时调用
      • onOtherCmdMsg:该消息类型不需要处理(例如PK、点赞数更新等类型)时调用
      • onUnknownCmd:该消息类型未知(没有对应的枚举类)时调用
    • IDanmuMsgListener(所有平台)
      • onDanmuMsg:收到弹幕消息
    • IGiftMsgListener(所有平台,快手礼物消息不全,缺少礼物单价、接收方信息)
      • onGiftMsg:收到礼物消息(抖音、快手平台需要判断礼物个数是否大于0)
    • ISuperChatMsgListener(B站)
      • onSuperChatMsg:收到醒目留言
    • IEnterRoomMsgListener(B站、斗鱼、抖音,虎牙只能接收到高级用户的入房回调)
      • onEnterRoomMsg:进入房间消息回调
    • ILikeMsgListener(B站、快手、抖音支持获取点赞的个数)
      • onLikeMsg:收到点赞消息
    • ILiveStatusChangeListener(B站、抖音测试只有下播消息)
      • onLiveStatusMsg:收到状态变化消息
    • IRoomStatsMsgListener(B站,抖音和快手没有累计观看人数信息)
      • onRoomStatsMsg:收到信息统计消息

3.1.2 commons-client

  • 定义了Client的配置:连接地址、房间id、Cookie、心跳、自动重连等相关参数
  • 定义了Client的一些方法:初始化、销毁、连接、断开、添加消息回调、移除消息回调、发送弹幕、为主播点赞等
  • 定义了Client的生命周期

3.1.3 commons-util

  • 一些工具类:时间、反射、Cookie

3.2 servers模块

对所使用的连接工具的抽象

3.2.1 servers-netty

  • 定义了连接处理Handler
  • 定义了数据处理Handler

3.2.2 servers-netty-client

基于Netty实现的Client

  • 扩展了Client、ClientConfig
  • 扩展Handler增加了Client成员变量

3.3 clients模块

对使用Netty作为连接工具的servers-netty-client的具体实现

  • client-bilibili
  • client-douyu
  • client-huya
  • client-douyin
  • client-kuaishou
  • client-websocket

3.4 codec模块

  1. 解码decode:根据平台协议,对收到的二进制流进行解码
  2. 编码encode:根据平台协议,将消息编码为二进制流
  • codec-bilibili
    • BilibiliCodecUtil
    • BilibiliMsgFactory
  • codec-douyu
    • DouyuCodecUtil
    • DouyuMsgFactory
  • codec-huya
    • HuyaCodecUtil
    • HuyaMsgFactory
  • codec-douyin
  • codec-kuaishou

由于抖音和快手使用的都是Protobuf协议,目前版本暂未实现CodecUtil工具类,可以参考DouyinCodecHandlerKuaishouCodecHandler中的编解码实现

交流讨论

扫描二维码 或点击链接加入QQ频道【OrdinaryRoad】:https://pd.qq.com/s/3id0n7fvs

捐赠

开源不易,您的认可与支持是我不断更新的最大动力!

扫码或者访问https://dwz.tax/ucWb进行捐赠

日期 捐赠人 金额 留言 渠道
2024-03-06 **睿 88.88 佬 加油 ZFB
2024-03-10 **豪 88.8 大佬加油 ZFB
2024-03-25 **波 188.8 / ZFB
2024-04-02 **豪 30 / ZFB
2024-04-30 *h 100 大佬牛逼 WX
... ... ... ... ...

Star History

Star History Chart

感谢

免责声明

免责声明:仅供学术研究使用。对于违反相关法律、造成危害的滥用行为,开发者不负任何责任。

另附各个平台的开放平台地址

MIT License Copyright (c) 2023 OrdinaryRoad Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

简介

(2024最新版) 一个基于Netty的通用直播间弹幕客户端,支持网络代理,支持弹幕发送*、为主播点赞*,已支持B站、斗鱼、虎牙、抖音、快手;BarrageFly——让弹幕飞,基于该项目的一个弹幕转发、过滤、处理平台;支持多平台直播间弹幕监听 展开 收起
Java
MIT
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Java
1
https://gitee.com/or-mjz/ordinaryroad-live-chat-client.git
git@gitee.com:or-mjz/ordinaryroad-live-chat-client.git
or-mjz
ordinaryroad-live-chat-client
ordinaryroad-live-chat-client
main

搜索帮助