init v1.0.0

This commit is contained in:
ageer
2024-02-27 20:52:19 +08:00
parent 1f7f97e86a
commit a079ef44e5
602 changed files with 163057 additions and 95 deletions

View File

@@ -0,0 +1,70 @@
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.xmzs</groupId>
<artifactId>live-chat-clients</artifactId>
<version>1.0.0</version>
</parent>
<packaging>jar</packaging>
<artifactId>live-chat-client-bilibili</artifactId>
<name>live-chat-client-bilibili</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.xmzs</groupId>
<artifactId>live-chat-client-servers-netty-client</artifactId>
</dependency>
<dependency>
<groupId>com.aayushatharva.brotli4j</groupId>
<artifactId>brotli4j</artifactId>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,247 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.api;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import tech.ordinaryroad.live.chat.client.bilibili.api.request.BilibiliLikeReportV3Request;
import tech.ordinaryroad.live.chat.client.bilibili.api.request.BilibiliSendMsgRequest;
import tech.ordinaryroad.live.chat.client.commons.base.exception.BaseException;
import tech.ordinaryroad.live.chat.client.commons.util.OrLiveChatCookieUtil;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static tech.ordinaryroad.live.chat.client.commons.base.msg.BaseMsg.OBJECT_MAPPER;
/**
* B站API简易版
*
* @author mjz
* @date 2023/5/5
*/
@Slf4j
public class BilibiliApis {
public static final TimedCache<Long, String> GIFT_IMG_CACHE = new TimedCache<>(TimeUnit.DAYS.toMillis(1));
public static final String KEY_COOKIE_CSRF = "bili_jct";
public static final String KEY_UID = "DedeUserID";
@SneakyThrows
public static RoomInitResult roomInit(long roomId, String cookie) {
@Cleanup
HttpResponse response = createGetRequest("https://api.live.bilibili.com/room/v1/Room/room_init?id=" + roomId, cookie).execute();
JsonNode dataJsonNode = responseInterceptor(response.body());
return OBJECT_MAPPER.readValue(dataJsonNode.toString(), RoomInitResult.class);
}
public static JsonNode roomGiftConfig(long roomId, String cookie) {
@Cleanup
HttpResponse response = createGetRequest("https://api.live.bilibili.com/xlive/web-room/v1/giftPanel/roomGiftConfig?platform=pc&source=live&build=0&global_version=0&room_id=" + roomId, cookie).execute();
return responseInterceptor(response.body());
}
/**
* @param roomId
* @param type 直播间用0
* @return <pre>{@code
* {
* "group": "live",
* "business_id": 0,
* "refresh_row_factor": 0.125,
* "refresh_rate": 100,
* "max_delay": 5000,
* "token": "-wm5-Qo4BBAztd1qp5ZJpgyTMRBhCc7yikz5d9rAd63PV46G9BMwl0R10kMM8Ilb-UieZGjLtipPrz4Cvi0DdhGFwOi8PJpFN9K-LoXh6Z_4yjEIwgRerDiMIstHzJ80J3B7wnRisAYkWA==",
* "host_list": [{
* "host": "ali-bj-live-comet-09.chat.bilibili.com",
* "port": 2243,
* "wss_port": 443,
* "ws_port": 2244
* }, {
* "host": "ali-gz-live-comet-02.chat.bilibili.com",
* "port": 2243,
* "wss_port": 443,
* "ws_port": 2244
* }, {
* "host": "broadcastlv.chat.bilibili.com",
* "port": 2243,
* "wss_port": 443,
* "ws_port": 2244
* }]
* }
* }</pre>
*/
public static JsonNode getDanmuInfo(long roomId, int type, String cookie) {
@Cleanup
HttpResponse response = createGetRequest("https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo?id=" + roomId + "&type=" + type, cookie).execute();
return responseInterceptor(response.body());
}
public static String getGiftImgById(long giftId, long roomId) {
if (!GIFT_IMG_CACHE.containsKey(giftId)) {
ThreadUtil.execAsync(() -> {
updateGiftImgCache(roomId, null);
});
}
return GIFT_IMG_CACHE.get(giftId);
}
/**
* 更新礼物图片缓存
*/
public static void updateGiftImgCache(long roomId, String cookie) {
JsonNode jsonNode = roomGiftConfig(roomId, cookie);
for (JsonNode node : jsonNode.get("global_gift").get("list")) {
long giftId = node.get("id").asLong();
String giftImgUrl = node.get("webp").asText();
GIFT_IMG_CACHE.put(giftId, giftImgUrl);
}
}
/**
* 发送弹幕
*
* @param request {@link BilibiliSendMsgRequest}
* @param cookie Cookie
*/
public static void sendMsg(BilibiliSendMsgRequest request, String cookie) {
if (StrUtil.isBlank(cookie)) {
throw new BaseException("发送弹幕接口cookie不能为空");
}
Map<String, Object> stringObjectMap = BeanUtil.beanToMap(request);
@Cleanup HttpResponse execute = HttpUtil.createPost("https://api.live.bilibili.com/msg/send")
.cookie(cookie)
.form(stringObjectMap)
.execute();
responseInterceptor(execute.body());
}
/**
* 发送弹幕
*
* @param msg 内容
* @param realRoomId 真实房间id
* @param cookie Cookie
*/
public static void sendMsg(String msg, long realRoomId, String cookie) {
String biliJct = OrLiveChatCookieUtil.getCookieByName(cookie, KEY_COOKIE_CSRF, () -> {
throw new BaseException("cookie中缺少参数" + KEY_COOKIE_CSRF);
});
BilibiliSendMsgRequest request = new BilibiliSendMsgRequest(msg, StrUtil.toString(ZonedDateTime.now(ZoneId.of("Asia/Shanghai")).toEpochSecond()), realRoomId, biliJct, biliJct);
sendMsg(request, cookie);
}
/**
* 为主播点赞
*
* @param request {@link BilibiliLikeReportV3Request}
* @param cookie Cookie
*/
public static void likeReportV3(BilibiliLikeReportV3Request request, String cookie) {
if (StrUtil.isBlank(cookie)) {
throw new BaseException("为主播点赞接口cookie不能为空");
}
Map<String, Object> stringObjectMap = BeanUtil.beanToMap(request);
@Cleanup HttpResponse execute = HttpUtil.createPost("https://api.live.bilibili.com/xlive/app-ucenter/v1/like_info_v3/like/likeReportV3")
.cookie(cookie)
.form(stringObjectMap)
.execute();
responseInterceptor(execute.body());
}
/**
* 为主播点赞
*
* @param anchor_id 主播Uid {@link RoomInitResult#uid}
* @param realRoomId 真实房间Id {@link RoomInitResult#room_id}
* @param cookie Cookie
*/
public static void likeReportV3(long anchor_id, long realRoomId, String cookie) {
String uid = OrLiveChatCookieUtil.getCookieByName(cookie, KEY_UID, () -> {
throw new BaseException("cookie中缺少参数" + KEY_UID);
});
String biliJct = OrLiveChatCookieUtil.getCookieByName(cookie, KEY_COOKIE_CSRF, () -> {
throw new BaseException("cookie中缺少参数" + KEY_COOKIE_CSRF);
});
BilibiliLikeReportV3Request request = new BilibiliLikeReportV3Request(realRoomId, uid, anchor_id, biliJct, biliJct);
likeReportV3(request, cookie);
}
public static HttpRequest createGetRequest(String url, String cookies) {
return HttpUtil.createGet(url)
.cookie(cookies);
}
private static JsonNode responseInterceptor(String responseString) {
try {
JsonNode jsonNode = OBJECT_MAPPER.readTree(responseString);
int code = jsonNode.get("code").asInt();
if (code == 0) {
// 成功
return jsonNode.get("data");
} else {
throw new BaseException(jsonNode.get("message").asText());
}
} catch (JsonProcessingException e) {
throw new BaseException(e);
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public static class RoomInitResult {
private long room_id;
private int short_id;
private long uid;
private int need_p2p;
private boolean is_hidden;
private boolean is_locked;
private boolean is_portrait;
private int live_status;
private int hidden_till;
private int lock_till;
private boolean encrypted;
private boolean pwd_verified;
private long live_time;
private int room_shield;
private int is_sp;
private int special_type;
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.api.request;
import cn.hutool.core.util.StrUtil;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author mjz
* @date 2024/1/31
*/
@Data
@NoArgsConstructor
public class BilibiliLikeReportV3Request {
/**
* 本次点赞次数
*/
private int click_time = 1;
/**
* 房间真实ID
*/
private long room_id;
/**
* Cookie中的DedeUserID
*/
private String uid;
/**
* RoomInitResult中的uid
*/
private long anchor_id;
/**
* Cookie中的bili_jct
*/
private String csrf;
/**
* Cookie中的bili_jct
*/
private String csrf_token;
/**
* 暂时留空
*/
private String visit_id = StrUtil.EMPTY;
public BilibiliLikeReportV3Request(long room_id, String uid, long anchor_id, String csrf, String csrf_token) {
this.room_id = room_id;
this.uid = uid;
this.anchor_id = anchor_id;
this.csrf = csrf;
this.csrf_token = csrf_token;
}
}

View File

@@ -0,0 +1,77 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.api.request;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author mjz
* @date 2023/9/7
*/
@Data
@NoArgsConstructor
public class BilibiliSendMsgRequest {
private String bubble = "0";
/**
* 弹幕内容
*/
private String msg;
/**
* 弹幕颜色
*/
private String color = "16777215";
private String mode = "1";
private String room_type = "0";
private String jumpfrom = "0";
/**
* 字体大小
*/
private String fontsize = "25";
/**
* 时间戳(秒)
*/
private String rnd;
/**
* 房间真实ID
*/
private long roomid;
/**
* Cookie中的bili_jct
*/
private String csrf;
/**
* Cookie中的bili_jct
*/
private String csrf_token;
public BilibiliSendMsgRequest(String msg, String rnd, long roomid, String csrf, String csrf_token) {
this.msg = msg;
this.rnd = rnd;
this.roomid = roomid;
this.csrf = csrf;
this.csrf_token = csrf_token;
}
}

View File

@@ -0,0 +1,184 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.client;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import lombok.extern.slf4j.Slf4j;
import tech.ordinaryroad.live.chat.client.bilibili.api.BilibiliApis;
import tech.ordinaryroad.live.chat.client.bilibili.config.BilibiliLiveChatClientConfig;
import tech.ordinaryroad.live.chat.client.bilibili.constant.BilibiliCmdEnum;
import tech.ordinaryroad.live.chat.client.bilibili.listener.IBilibiliConnectionListener;
import tech.ordinaryroad.live.chat.client.bilibili.listener.IBilibiliMsgListener;
import tech.ordinaryroad.live.chat.client.bilibili.msg.base.IBilibiliMsg;
import tech.ordinaryroad.live.chat.client.bilibili.netty.handler.BilibiliBinaryFrameHandler;
import tech.ordinaryroad.live.chat.client.bilibili.netty.handler.BilibiliConnectionHandler;
import tech.ordinaryroad.live.chat.client.commons.base.exception.BaseException;
import tech.ordinaryroad.live.chat.client.commons.base.listener.IBaseConnectionListener;
import tech.ordinaryroad.live.chat.client.servers.netty.client.base.BaseNettyClient;
import java.util.List;
import java.util.function.Consumer;
/**
* B站直播间弹幕客户端
*
* @author mjz
* @date 2023/8/20
*/
@Slf4j
public class BilibiliLiveChatClient extends BaseNettyClient<
BilibiliLiveChatClientConfig,
BilibiliCmdEnum,
IBilibiliMsg,
IBilibiliMsgListener,
BilibiliConnectionHandler,
BilibiliBinaryFrameHandler
> {
private BilibiliApis.RoomInitResult roomInitResult = new BilibiliApis.RoomInitResult();
public BilibiliLiveChatClient(BilibiliLiveChatClientConfig config, List<IBilibiliMsgListener> msgListeners, IBilibiliConnectionListener connectionListener, EventLoopGroup workerGroup) {
super(config, workerGroup, connectionListener);
addMsgListeners(msgListeners);
// 初始化
this.init();
}
public BilibiliLiveChatClient(BilibiliLiveChatClientConfig config, IBilibiliMsgListener msgListener, IBilibiliConnectionListener connectionListener, EventLoopGroup workerGroup) {
super(config, workerGroup, connectionListener);
addMsgListener(msgListener);
// 初始化
this.init();
}
public BilibiliLiveChatClient(BilibiliLiveChatClientConfig config, IBilibiliMsgListener msgListener, IBilibiliConnectionListener connectionListener) {
this(config, msgListener, connectionListener, new NioEventLoopGroup());
}
public BilibiliLiveChatClient(BilibiliLiveChatClientConfig config, IBilibiliMsgListener msgListener) {
this(config, msgListener, null, new NioEventLoopGroup());
}
public BilibiliLiveChatClient(BilibiliLiveChatClientConfig config) {
this(config, null);
}
@Override
public void init() {
roomInitResult = BilibiliApis.roomInit(getConfig().getRoomId(), getConfig().getCookie());
super.init();
}
@Override
public BilibiliConnectionHandler initConnectionHandler(IBaseConnectionListener<BilibiliConnectionHandler> clientConnectionListener) {
return new BilibiliConnectionHandler(
WebSocketClientHandshakerFactory.newHandshaker(getWebsocketUri(), WebSocketVersion.V13, null, true, new DefaultHttpHeaders(), getConfig().getMaxFramePayloadLength()),
BilibiliLiveChatClient.this, clientConnectionListener
);
}
@Override
public BilibiliBinaryFrameHandler initBinaryFrameHandler() {
return new BilibiliBinaryFrameHandler(super.msgListeners, BilibiliLiveChatClient.this);
}
@Override
public void sendDanmu(Object danmu, Runnable success, Consumer<Throwable> failed) {
if (!checkCanSendDanmu(false)) {
return;
}
if (danmu instanceof String) {
String msg = (String) danmu;
try {
if (log.isDebugEnabled()) {
log.debug("{} bilibili发送弹幕 {}", getConfig().getRoomId(), danmu);
}
boolean sendSuccess = false;
try {
BilibiliApis.sendMsg(msg, roomInitResult.getRoom_id(), getConfig().getCookie());
sendSuccess = true;
} catch (Exception e) {
log.error("bilibili弹幕发送失败", e);
if (failed != null) {
failed.accept(e);
}
}
if (!sendSuccess) {
return;
}
if (log.isDebugEnabled()) {
log.debug("bilibili弹幕发送成功 {}", danmu);
}
if (success != null) {
success.run();
}
finishSendDanmu();
} catch (Exception e) {
log.error("bilibili弹幕发送失败", e);
if (failed != null) {
failed.accept(e);
}
}
} else {
super.sendDanmu(danmu, success, failed);
}
}
@Override
public void clickLike(int count, Runnable success, Consumer<Throwable> failed) {
if (count <= 0) {
throw new BaseException("点赞次数必须大于0");
}
boolean successfullyClicked = false;
try {
BilibiliApis.likeReportV3(roomInitResult.getUid(), roomInitResult.getRoom_id(), getConfig().getCookie());
successfullyClicked = true;
} catch (Exception e) {
log.error("Bilibili为直播间点赞失败", e);
if (failed != null) {
failed.accept(e);
}
}
if (!successfullyClicked) {
return;
}
if (log.isDebugEnabled()) {
log.debug("Bilibili为直播间点赞成功");
}
if (success != null) {
success.run();
}
}
}

View File

@@ -0,0 +1,67 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.config;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import tech.ordinaryroad.live.chat.client.bilibili.constant.ProtoverEnum;
import tech.ordinaryroad.live.chat.client.commons.util.OrLiveChatNumberUtil;
import tech.ordinaryroad.live.chat.client.servers.netty.client.config.BaseNettyClientConfig;
/**
* B站直播间弹幕客户端配置
*
* @author mjz
* @date 2023/8/21
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder(toBuilder = true)
public class BilibiliLiveChatClientConfig extends BaseNettyClientConfig {
/**
* @see ProtoverEnum
*/
@Builder.Default
private ProtoverEnum protover = ProtoverEnum.NORMAL_ZLIB;
@Builder.Default
private String websocketUri = "wss://broadcastlv.chat.bilibili.com:443/sub";
@Override
public Long getRoomId() {
return OrLiveChatNumberUtil.parseLong(super.getRoomId());
}
public void setProtover(ProtoverEnum protover) {
ProtoverEnum oldValue = this.protover;
this.protover = protover;
super.propertyChangeSupport.firePropertyChange("protover", oldValue, protover);
}
}

View File

@@ -0,0 +1,143 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.constant;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* @author mjz
* @date 2023/1/6
*/
@Getter
@RequiredArgsConstructor
public enum BilibiliCmdEnum {
/**
* 游客状态下5分钟后会出现登录提示弹幕中的用户名、用户id等信息将不再可见
*/
LOG_IN_NOTICE,
/**
* 收到弹幕
*/
DANMU_MSG,
/**
* 收到礼物
*/
SEND_GIFT,
/**
* 有人上舰
*/
GUARD_BUY,
/**
* 欢迎舰长
*/
WELCOME_GUARD,
WELCOME,
/**
* 礼物连击
*/
COMBO_SEND,
/**
* 欢迎高能用户、(舰长?待验证)特殊消息
*/
ENTRY_EFFECT,
HOT_RANK_CHANGED,
HOT_RANK_CHANGED_V2,
INTERACT_WORD,
/**
* 开始直播
*/
LIVE,
LIVE_INTERACTIVE_GAME,
NOTICE_MSG,
/**
* 高能榜数量更新
*/
ONLINE_RANK_COUNT,
ONLINE_RANK_TOP3,
ONLINE_RANK_V2,
PK_BATTLE_END,
PK_BATTLE_FINAL_PROCESS,
PK_BATTLE_PROCESS,
PK_BATTLE_PROCESS_NEW,
PK_BATTLE_SETTLE,
PK_BATTLE_SETTLE_USER,
PK_BATTLE_SETTLE_V2,
/**
* 主播准备中
*/
PREPARING,
ROOM_REAL_TIME_MESSAGE_UPDATE,
/**
* 停止直播的房间ID列表
*/
STOP_LIVE_ROOM_LIST,
/**
* 醒目留言
*/
SUPER_CHAT_MESSAGE,
SUPER_CHAT_MESSAGE_JPN,
/**
* 删除醒目留言
*/
SUPER_CHAT_MESSAGE_DELETE,
WIDGET_BANNER,
/**
* 点赞数更新
*/
LIKE_INFO_V3_UPDATE,
/**
* 为主播点赞
*/
LIKE_INFO_V3_CLICK,
HOT_ROOM_NOTIFY,
/**
* 观看人数变化
*/
WATCHED_CHANGE,
POPULAR_RANK_CHANGED,
COMMON_NOTICE_DANMAKU,
LIVE_MULTI_VIEW_CHANGE,
RECOMMEND_CARD,
PK_BATTLE_START_NEW,
PK_BATTLE_ENTRANCE,
AREA_RANK_CHANGED,
ROOM_BLOCK_MSG,
USER_TOAST_MSG,
PK_BATTLE_PRE_NEW,
PK_BATTLE_RANK_CHANGE,
PK_BATTLE_START,
PK_BATTLE_PRE,
PLAY_TAG,
;
public static BilibiliCmdEnum getByString(String cmd) {
try {
return BilibiliCmdEnum.valueOf(cmd);
} catch (Exception e) {
return null;
}
}
}

View File

@@ -0,0 +1,86 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.constant;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* @author mjz
* @date 2023/1/5
*/
@Getter
@RequiredArgsConstructor
public enum OperationEnum {
HANDSHAKE(0),
HANDSHAKE_REPLY(1),
/**
* 心跳包
*/
HEARTBEAT(2),
/**
* 心跳包回复(人气值)
*/
HEARTBEAT_REPLY(3),
SEND_MSG(4),
/**
* 普通包(命令)
*/
SEND_SMS_REPLY(5),
DISCONNECT_REPLY(6),
/**
* 认证包
*/
AUTH(7),
/**
* 认证包回复
*/
AUTH_REPLY(8),
RAW(9),
PROTO_READY(10),
PROTO_FINISH(11),
CHANGE_ROOM(12),
CHANGE_ROOM_REPLY(13),
REGISTER(14),
REGISTER_REPLY(15),
UNREGISTER(16),
UNREGISTER_REPLY(17),
;
private final int code;
public static OperationEnum getByCode(int code) {
for (OperationEnum value : OperationEnum.values()) {
if (value.code == code) {
return value;
}
}
return null;
}
}

View File

@@ -0,0 +1,67 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.constant;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* @author mjz
* @date 2023/1/5
*/
@Getter
@RequiredArgsConstructor
public enum ProtoverEnum {
/**
* 普通包正文不使用压缩
*/
NORMAL_NO_COMPRESSION(0),
/**
* 心跳及认证包正文不使用压缩
*/
HEARTBEAT_AUTH_NO_COMPRESSION(1),
/**
* 普通包正文使用zlib压缩
*/
NORMAL_ZLIB(2),
/**
* 普通包正文使用brotli压缩,解压为一个带头部的协议0普通包
*/
NORMAL_BROTLI(3),
;
private final int code;
public static ProtoverEnum getByCode(int code) {
for (ProtoverEnum value : ProtoverEnum.values()) {
if (value.code == code) {
return value;
}
}
return null;
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.listener;
import tech.ordinaryroad.live.chat.client.bilibili.netty.handler.BilibiliConnectionHandler;
import tech.ordinaryroad.live.chat.client.commons.base.listener.IBaseConnectionListener;
/**
* 连接回调
*
* @author mjz
* @date 2023/8/21
*/
public interface IBilibiliConnectionListener extends IBaseConnectionListener<BilibiliConnectionHandler> {
}

View File

@@ -0,0 +1,138 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.listener;
import tech.ordinaryroad.live.chat.client.bilibili.constant.BilibiliCmdEnum;
import tech.ordinaryroad.live.chat.client.bilibili.msg.*;
import tech.ordinaryroad.live.chat.client.bilibili.netty.handler.BilibiliBinaryFrameHandler;
import tech.ordinaryroad.live.chat.client.commons.base.listener.*;
/**
* @author mjz
* @date 2023/1/7
*/
public interface IBilibiliMsgListener extends IBaseMsgListener<BilibiliBinaryFrameHandler, BilibiliCmdEnum>,
IDanmuMsgListener<BilibiliBinaryFrameHandler, DanmuMsgMsg>,
IGiftMsgListener<BilibiliBinaryFrameHandler, SendGiftMsg>,
ISuperChatMsgListener<BilibiliBinaryFrameHandler, SuperChatMessageMsg>,
IEnterRoomMsgListener<BilibiliBinaryFrameHandler, InteractWordMsg>,
ILikeMsgListener<BilibiliBinaryFrameHandler, LikeInfoV3ClickMsg> {
/**
* 收到礼物
*
* @param binaryFrameHandler BilibiliBinaryFrameHandler
* @param msg SendSmsReplyMsg
* @deprecated use {@link IGiftMsgListener#onGiftMsg(Object, Object)}
*/
default void onSendGift(BilibiliBinaryFrameHandler binaryFrameHandler, SendSmsReplyMsg msg) {
this.onSendGift(msg);
}
/**
* @deprecated use {@link IGiftMsgListener#onGiftMsg(Object)}
*/
default void onSendGift(SendSmsReplyMsg msg) {
// ignore
}
/**
* 普通用户进入直播间
*
* @param binaryFrameHandler BilibiliBinaryFrameHandler
* @param msg SendSmsReplyMsg
* @deprecated use {@link IEnterRoomMsgListener#onEnterRoomMsg}
*/
default void onEnterRoom(BilibiliBinaryFrameHandler binaryFrameHandler, SendSmsReplyMsg msg) {
this.onEnterRoom(msg);
}
/**
* @deprecated use {@link IEnterRoomMsgListener#onEnterRoomMsg}
*/
default void onEnterRoom(SendSmsReplyMsg msg) {
// ignore
}
/**
* 入场效果(高能用户)
*
* @param binaryFrameHandler BilibiliBinaryFrameHandler
* @param sendSmsReplyMsg SendSmsReplyMsg
*/
default void onEntryEffect(BilibiliBinaryFrameHandler binaryFrameHandler, SendSmsReplyMsg sendSmsReplyMsg) {
this.onEntryEffect(sendSmsReplyMsg);
}
default void onEntryEffect(SendSmsReplyMsg sendSmsReplyMsg) {
// ignore
}
/**
* 观看人数变化
*
* @param binaryFrameHandler BilibiliBinaryFrameHandler
* @param msg SendSmsReplyMsg
*/
default void onWatchedChange(BilibiliBinaryFrameHandler binaryFrameHandler, SendSmsReplyMsg msg) {
this.onWatchedChange(msg);
}
default void onWatchedChange(SendSmsReplyMsg msg) {
// ignore
}
/**
* 为主播点赞
*
* @param binaryFrameHandler BilibiliBinaryFrameHandler
* @param msg SendSmsReplyMsg
* @deprecated use {@link ILikeMsgListener#onLikeMsg}
*/
default void onClickLike(BilibiliBinaryFrameHandler binaryFrameHandler, SendSmsReplyMsg msg) {
this.onClickLike(msg);
}
/**
* @deprecated use {@link ILikeMsgListener#onLikeMsg}
*/
default void onClickLike(SendSmsReplyMsg msg) {
// ignore
}
/**
* 点赞数更新
*
* @param binaryFrameHandler BilibiliBinaryFrameHandler
* @param msg SendSmsReplyMsg
*/
default void onClickUpdate(BilibiliBinaryFrameHandler binaryFrameHandler, SendSmsReplyMsg msg) {
this.onClickUpdate(msg);
}
default void onClickUpdate(SendSmsReplyMsg msg) {
// ignore
}
}

View File

@@ -0,0 +1,92 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.msg;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.bilibili.constant.OperationEnum;
import tech.ordinaryroad.live.chat.client.bilibili.constant.ProtoverEnum;
import tech.ordinaryroad.live.chat.client.bilibili.msg.base.BaseBilibiliMsg;
/**
* @author mjz
* @date 2023/1/6
*/
@Getter
@Setter
@RequiredArgsConstructor
public class AuthMsg extends BaseBilibiliMsg {
/**
* 用户uid0代表游客
*/
private long uid;
/**
* 房间id room_id不是短id short_id
* 可以通过将url参数id改为直播地址中的数字来查询房间真实id
* example: <a href="https://api.live.bilibili.com/room/v1/Room/room_init?id=6">https://api.live.bilibili.com/room/v1/Room/room_init?id=6</a>
*/
private final long roomid;
/**
* 协议版本
*
* @see ProtoverEnum#getCode()
*/
private final int protover;
/**
* 平台标识
*/
private String platform = "web";
private int type = 2;
/**
* 必须字段
*
* @since 2023-08-19
*/
private final String buvid;
/**
* 认证秘钥(必须字段)
*
* @since @since 2023-08-19
*/
private final String key;
@Override
public ProtoverEnum getProtoverEnum() {
return ProtoverEnum.getByCode(this.protover);
}
@Override
public OperationEnum getOperationEnum() {
return OperationEnum.AUTH;
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.msg;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.bilibili.constant.OperationEnum;
import tech.ordinaryroad.live.chat.client.bilibili.constant.ProtoverEnum;
import tech.ordinaryroad.live.chat.client.bilibili.msg.base.BaseBilibiliMsg;
/**
* @author mjz
* @date 2023/1/6
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class AuthReplyMsg extends BaseBilibiliMsg {
/**
* 0: OK,-101: TOKEN_ERROR
*/
private int code;
@JsonIgnore
private int protover;
@Override
public ProtoverEnum getProtoverEnum() {
return ProtoverEnum.getByCode(protover);
}
@Override
public OperationEnum getOperationEnum() {
return OperationEnum.AUTH_REPLY;
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.msg;
import cn.hutool.core.codec.Base64;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.bilibili.constant.OperationEnum;
import tech.ordinaryroad.live.chat.client.bilibili.msg.base.BaseBilibiliMsg;
import tech.ordinaryroad.live.chat.client.commons.base.msg.IDanmuMsg;
/**
* @author mjz
* @date 2023/9/8
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class DanmuMsgMsg extends BaseBilibiliMsg implements IDanmuMsg {
private JsonNode info;
private String dm_v2;
@Override
public OperationEnum getOperationEnum() {
return OperationEnum.SEND_SMS_REPLY;
}
@Override
public String getBadgeName() {
JsonNode jsonNode3 = info.get(3);
if (jsonNode3.isEmpty()) {
return "";
}
return jsonNode3.get(1).asText();
}
@Override
public byte getBadgeLevel() {
JsonNode jsonNode3 = info.get(3);
if (jsonNode3.isEmpty()) {
return 0;
}
return (byte) jsonNode3.get(0).asInt();
}
@Override
public String getUid() {
JsonNode jsonNode2 = info.get(2);
return jsonNode2.get(0).asText();
}
@Override
public String getUsername() {
JsonNode jsonNode2 = info.get(2);
return jsonNode2.get(1).asText();
}
@Override
public String getUserAvatar() {
String avatar = null;
try {
tech.ordinaryroad.live.chat.client.bilibili.protobuf.dm_v2 dmV2 = tech.ordinaryroad.live.chat.client.bilibili.protobuf.dm_v2.parseFrom(Base64.decode(dm_v2));
avatar = dmV2.getDmV220().getAvatar();
} catch (Exception e) {
// ignore
}
return avatar;
}
@Override
public String getContent() {
JsonNode jsonNode1 = info.get(1);
return jsonNode1.asText();
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.msg;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.bilibili.constant.OperationEnum;
import tech.ordinaryroad.live.chat.client.bilibili.constant.ProtoverEnum;
import tech.ordinaryroad.live.chat.client.bilibili.msg.base.BaseBilibiliMsg;
/**
* @author mjz
* @date 2023/1/6
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class HeartbeatMsg extends BaseBilibiliMsg {
@JsonIgnore
private int protover;
@Override
public ProtoverEnum getProtoverEnum() {
return ProtoverEnum.getByCode(protover);
}
@Override
public OperationEnum getOperationEnum() {
return OperationEnum.HEARTBEAT;
}
}

View File

@@ -0,0 +1,60 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.msg;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.bilibili.constant.OperationEnum;
import tech.ordinaryroad.live.chat.client.bilibili.constant.ProtoverEnum;
import tech.ordinaryroad.live.chat.client.bilibili.msg.base.BaseBilibiliMsg;
/**
* @author mjz
* @date 2023/1/6
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class HeartbeatReplyMsg extends BaseBilibiliMsg {
private int popularity;
@JsonIgnore
private int protover;
@Override
public ProtoverEnum getProtoverEnum() {
return ProtoverEnum.getByCode(protover);
}
@Override
public OperationEnum getOperationEnum() {
return OperationEnum.HEARTBEAT_REPLY;
}
}

View File

@@ -0,0 +1,396 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.msg;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.bilibili.constant.OperationEnum;
import tech.ordinaryroad.live.chat.client.bilibili.msg.base.BaseBilibiliMsg;
import tech.ordinaryroad.live.chat.client.commons.base.msg.IEnterRoomMsg;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author mjz
* @date 2023/12/26
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class InteractWordMsg extends BaseBilibiliMsg implements IEnterRoomMsg {
private Data data;
@Override
public OperationEnum getOperationEnum() {
return OperationEnum.SEND_SMS_REPLY;
}
@Override
public String getBadgeName() {
if (data == null || data.fans_medal == null) {
return null;
}
return data.fans_medal.medal_name;
}
@Override
public byte getBadgeLevel() {
if (data == null || data.fans_medal == null) {
return 0;
}
return data.fans_medal.medal_level;
}
@Override
public String getUid() {
if (data == null) {
return null;
}
return Long.toString(data.uid);
}
@Override
public String getUsername() {
if (data == null) {
return null;
}
return data.uname;
}
@Override
public String getUserAvatar() {
if (data == null || data.uinfo == null || data.uinfo.base == null) {
return null;
}
return data.uinfo.base.face;
}
@lombok.Data
public static class Data {
private Contribution contribution;
private Contribution_v2 contribution_v2;
private int core_user_type;
private int dmscore;
private Fans_medal fans_medal;
private String group_medal;
private List<Integer> identities;
private boolean is_mystery;
private int is_spread;
private int msg_type;
private int privilege_type;
private long roomid;
private long score;
private String spread_desc;
private String spread_info;
private int tail_icon;
private String tail_text;
private long timestamp;
private long trigger_time;
private long uid;
private Uinfo uinfo;
private String uname;
private String uname_color;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
@lombok.Data
public static class Contribution {
private int grade;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
@lombok.Data
public static class Contribution_v2 {
private int grade;
private String rank_type;
private String text;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
@lombok.Data
public static class Fans_medal {
private long anchor_roomid;
private int guard_level;
private int icon_id;
private int is_lighted;
private long medal_color;
private long medal_color_border;
private long medal_color_end;
private long medal_color_start;
private byte medal_level;
private String medal_name;
private long score;
private String special;
private long target_id;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
@lombok.Data
public static class Origin_info {
private String face;
private String name;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
@lombok.Data
public static class Risk_ctrl_info {
private String face;
private String name;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
@lombok.Data
public static class Official_info {
private int role;
private String title;
private String desc;
private int type;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
@lombok.Data
public static class Base {
private String face;
private boolean is_mystery;
private String name;
private int name_color;
private Origin_info origin_info;
private Risk_ctrl_info risk_ctrl_info;
private Official_info official_info;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
@lombok.Data
public static class Medal {
private String name;
private int level;
private long color_start;
private long color_end;
private long color_border;
private long color;
private int id;
private int typ;
private int is_light;
private long ruid;
private int guard_level;
private int score;
private String guard_icon;
private String honor_icon;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
@lombok.Data
public static class Guard {
private int level;
private String expired_str;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
@lombok.Data
public static class Uinfo {
private long uid;
private Base base;
private Medal medal;
private String wealth;
private String title;
private Guard guard;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
}

View File

@@ -0,0 +1,158 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.msg;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.bilibili.constant.OperationEnum;
import tech.ordinaryroad.live.chat.client.bilibili.msg.base.BaseBilibiliMsg;
import tech.ordinaryroad.live.chat.client.commons.base.msg.ILikeMsg;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author mjz
* @date 2024/1/31
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class LikeInfoV3ClickMsg extends BaseBilibiliMsg implements ILikeMsg {
private Data data;
@Override
public OperationEnum getOperationEnum() {
return OperationEnum.SEND_SMS_REPLY;
}
@Override
public String getBadgeName() {
if (this.data == null || this.data.getFans_medal() == null) {
return null;
}
return this.data.getFans_medal().getMedal_name();
}
@Override
public byte getBadgeLevel() {
if (this.data == null || this.data.getFans_medal() == null) {
return 0;
}
return this.data.getFans_medal().getMedal_level();
}
@Override
public String getUid() {
if (this.data == null) {
return null;
}
return Long.toString(this.data.getUid());
}
@Override
public String getUsername() {
if (this.data == null) {
return "";
}
return this.data.getUname();
}
@Override
public String getUserAvatar() {
if (this.data == null || this.data.getUinfo() == null || this.data.getUinfo().getBase() == null) {
return "";
}
return this.data.getUinfo().getBase().getFace();
}
@lombok.Data
public static class Data {
private int show_area;
private int msg_type;
private String like_icon;
private long uid;
private String like_text;
private String uname;
private String uname_color;
private List<Integer> identities;
private InteractWordMsg.Fans_medal fans_medal;
private Contribution_info contribution_info;
private int dmscore;
private String group_medal;
private boolean is_mystery;
private InteractWordMsg.Uinfo uinfo;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
@lombok.Data
public static class Contribution_info {
private int grade;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
}

View File

@@ -0,0 +1,351 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.msg;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.bilibili.api.BilibiliApis;
import tech.ordinaryroad.live.chat.client.bilibili.constant.OperationEnum;
import tech.ordinaryroad.live.chat.client.bilibili.msg.base.BaseBilibiliMsg;
import tech.ordinaryroad.live.chat.client.bilibili.msg.dto.MedalInfo;
import tech.ordinaryroad.live.chat.client.commons.base.msg.IGiftMsg;
import java.util.HashMap;
import java.util.Map;
/**
* @author mjz
* @date 2023/9/8
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class SendGiftMsg extends BaseBilibiliMsg implements IGiftMsg {
private Data data;
/**
* 额外属性,获取礼物图片时可能会用到
*/
private long roomId;
@Override
public String getBadgeName() {
if (data == null || data.medal_info == null) {
return IGiftMsg.super.getBadgeName();
}
return data.medal_info.getMedal_name();
}
@Override
public byte getBadgeLevel() {
if (data == null || data.medal_info == null) {
return IGiftMsg.super.getBadgeLevel();
}
return data.medal_info.getMedal_level();
}
@Override
public String getUid() {
if (this.data == null) {
return null;
}
return Long.toString(this.data.getUid());
}
@Override
public String getUsername() {
if (this.data == null) {
return "";
}
return this.data.getUname();
}
@Override
public String getUserAvatar() {
if (this.data == null) {
return "";
}
return this.data.getFace();
}
@Override
public String getGiftName() {
if (this.data == null) {
return "未知礼物";
}
return this.data.getGiftName();
}
@Override
public String getGiftImg() {
return BilibiliApis.getGiftImgById(this.data.giftId, this.roomId);
}
@Override
public String getGiftId() {
if (this.data == null) {
return null;
}
return Long.toString(data.getGiftId());
}
@Override
public int getGiftCount() {
if (this.data == null) {
return 0;
}
return data.getNum();
}
@Override
public int getGiftPrice() {
if (this.data == null) {
return -1;
}
return data.getPrice();
}
@Override
public String getReceiveUid() {
if (this.data == null || this.data.getReceive_user_info() == null) {
return null;
}
return Long.toString(data.getReceive_user_info().getUid());
}
@Override
public String getReceiveUsername() {
if (this.data == null || this.data.getReceive_user_info() == null) {
return "";
}
return data.getReceive_user_info().getUname();
}
@Override
public OperationEnum getOperationEnum() {
return OperationEnum.SEND_SMS_REPLY;
}
@lombok.Data
public static class Data {
private int draw;
private int gold;
private int silver;
private int num;
private int total_coin;
private int effect;
private int broadcast_id;
private int crit_prob;
private int guard_level;
private long rcost;
private long uid;
private long timestamp;
private int giftId;
private int giftType;
@JsonProperty("super")
private int _super;
private int super_gift_num;
private int super_batch_gift_num;
private int remain;
private int discount_price;
private int price;
private String beatId;
private String biz_source;
private String action;
private String coin_type;
private String uname;
private String face;
private String batch_combo_id;
private String rnd;
private String giftName;
private String original_gift_name;
private Combo_send combo_send;
private Batch_combo_send batch_combo_send;
private String tag_image;
private String top_list;
private String send_master;
private boolean is_first;
private int demarcation;
private int combo_stay_time;
private int combo_total_coin;
private String tid;
private int effect_block;
private int is_special_batch;
private int combo_resources_id;
private int magnification;
private String name_color;
private MedalInfo medal_info;
private int svga_block;
private JsonNode blind_gift;
private int float_sc_resource_id;
@JsonProperty("switch")
private boolean _switch;
private int face_effect_type;
private int face_effect_id;
private boolean is_naming;
private Receive_user_info receive_user_info;
private boolean is_join_receiver;
private Bag_gift bag_gift;
private int wealth_level;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
@lombok.Data
public static class Combo_send {
private long uid;
private int gift_num;
private int combo_num;
private int gift_id;
private String combo_id;
private String gift_name;
private String action;
private String uname;
private String send_master;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
@lombok.Data
public static class Receive_user_info {
private String uname;
private long uid;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
@lombok.Data
public static class Batch_combo_send {
private long uid;
private int gift_num;
private int batch_combo_num;
private int gift_id;
private String batch_combo_id;
private String gift_name;
private String action;
private String uname;
private String send_master;
private JsonNode blind_gift;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
@lombok.Data
public static class Bag_gift {
private int show_price;
private int price_for_show;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.msg;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.bilibili.constant.OperationEnum;
import tech.ordinaryroad.live.chat.client.bilibili.msg.base.BaseBilibiliCmdMsg;
/**
* @author mjz
* @date 2023/1/6
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class SendSmsReplyMsg extends BaseBilibiliCmdMsg {
private Long id;
private String name;
private JsonNode full;
private JsonNode half;
private JsonNode side;
private JsonNode data;
private JsonNode info;
private JsonNode msg_common;
private JsonNode msg_self;
private JsonNode link_url;
private JsonNode msg_type;
private JsonNode shield_uid;
private JsonNode business_id;
private JsonNode scatter;
private long roomid;
private long real_roomid;
@Override
public OperationEnum getOperationEnum() {
return OperationEnum.SEND_SMS_REPLY;
}
}

View File

@@ -0,0 +1,202 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.msg;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.bilibili.constant.OperationEnum;
import tech.ordinaryroad.live.chat.client.bilibili.msg.base.BaseBilibiliMsg;
import tech.ordinaryroad.live.chat.client.bilibili.msg.dto.MedalInfo;
import tech.ordinaryroad.live.chat.client.commons.base.msg.ISuperChatMsg;
import java.util.HashMap;
import java.util.Map;
/**
* @author mjz
* @date 2023/9/24
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class SuperChatMessageMsg extends BaseBilibiliMsg implements ISuperChatMsg {
private long roomid;
private Data data;
@Override
public OperationEnum getOperationEnum() {
return OperationEnum.SEND_SMS_REPLY;
}
@Override
public String getUid() {
if (this.data == null) {
return null;
}
return Long.toString(this.data.uid);
}
@Override
public String getUsername() {
if (this.data == null || this.data.getUser_info() == null) {
return "";
}
return this.data.user_info.uname;
}
@Override
public String getUserAvatar() {
if (this.data == null || this.data.getUser_info() == null) {
return "";
}
return this.data.user_info.face;
}
@Override
public String getContent() {
if (this.data == null) {
return "";
}
return this.data.message;
}
@Override
public int getDuration() {
if (this.data == null) {
return 0;
}
return this.data.time;
}
@lombok.Data
public static class Data {
private String background_bottom_color;
private String background_color;
private String background_color_end;
private String background_color_start;
private String background_icon;
private String background_image;
private String background_price_color;
private double color_point;
private int dmscore;
private long end_time;
private Gift gift;
private long id;
private int is_ranked;
private int is_send_audit;
private MedalInfo medal_info;
private String message;
private String message_font_color;
private String message_trans;
private int price;
private int rate;
private long start_time;
private int time;
private String token;
private int trans_mark;
private long ts;
private long uid;
private User_info user_info;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
@lombok.Data
public static class Gift {
private int gift_id;
private String gift_name;
private int num;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
@lombok.Data
public static class User_info {
private String face;
private String face_frame;
private int guard_level;
private int is_main_vip;
private int is_svip;
private int is_vip;
private String level_color;
private int manager;
private String name_color;
private String title;
private String uname;
private int user_level;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.msg.base;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.bilibili.constant.BilibiliCmdEnum;
import tech.ordinaryroad.live.chat.client.bilibili.constant.ProtoverEnum;
import tech.ordinaryroad.live.chat.client.commons.base.msg.BaseCmdMsg;
/**
* @author mjz
* @date 2023/1/6
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public abstract class BaseBilibiliCmdMsg extends BaseCmdMsg<BilibiliCmdEnum> implements IBilibiliMsg {
private int protover;
private String cmd;
@Override
public String getCmd() {
return this.cmd;
}
@Override
public void setCmd(String cmd) {
this.cmd = cmd;
}
@Override
public BilibiliCmdEnum getCmdEnum() {
return BilibiliCmdEnum.getByString(getCmd());
}
@Override
public ProtoverEnum getProtoverEnum() {
return ProtoverEnum.getByCode(this.protover);
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.msg.base;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.bilibili.constant.ProtoverEnum;
import tech.ordinaryroad.live.chat.client.commons.base.msg.BaseMsg;
/**
* @author mjz
* @date 2023/1/6
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public abstract class BaseBilibiliMsg extends BaseMsg implements IBilibiliMsg {
private int protover;
@Override
public ProtoverEnum getProtoverEnum() {
return ProtoverEnum.getByCode(protover);
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.msg.base;
import com.fasterxml.jackson.annotation.JsonIgnore;
import tech.ordinaryroad.live.chat.client.bilibili.constant.OperationEnum;
import tech.ordinaryroad.live.chat.client.bilibili.constant.ProtoverEnum;
import tech.ordinaryroad.live.chat.client.commons.base.msg.IMsg;
/**
* @author mjz
* @date 2023/8/26
*/
public interface IBilibiliMsg extends IMsg {
@JsonIgnore
ProtoverEnum getProtoverEnum();
@JsonIgnore
OperationEnum getOperationEnum();
}

View File

@@ -0,0 +1,66 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.msg.dto;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
@Data
public class MedalInfo {
private long target_id;
private String special;
private int icon_id;
private String anchor_uname;
private int anchor_roomid;
private byte medal_level;
private String medal_name;
private String medal_color;
private long medal_color_start;
private long medal_color_end;
private long medal_color_border;
private int is_lighted;
private int guard_level;
/**
* 未知属性都放在这
*/
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getUnknownProperties() {
return unknownProperties;
}
@JsonAnySetter
public void setOther(String key, JsonNode value) {
this.unknownProperties.put(key, value);
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.netty.frame;
import io.netty.buffer.ByteBuf;
import tech.ordinaryroad.live.chat.client.bilibili.netty.frame.base.BaseBilibiliWebSocketFrame;
/**
* @author mjz
* @date 2023/1/5
*/
public class AuthWebSocketFrame extends BaseBilibiliWebSocketFrame {
public AuthWebSocketFrame(ByteBuf byteBuf) {
super(byteBuf);
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.netty.frame;
import io.netty.buffer.ByteBuf;
import tech.ordinaryroad.live.chat.client.bilibili.netty.frame.base.BaseBilibiliWebSocketFrame;
/**
* @author mjz
* @date 2023/1/5
*/
public class HeartbeatWebSocketFrame extends BaseBilibiliWebSocketFrame {
public HeartbeatWebSocketFrame(ByteBuf byteBuf) {
super(byteBuf);
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.netty.frame.base;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import tech.ordinaryroad.live.chat.client.bilibili.constant.OperationEnum;
import tech.ordinaryroad.live.chat.client.bilibili.constant.ProtoverEnum;
/**
* 实现Bilibili协议的BinaryWebSocketFrame
* <a href="https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/live/message_stream.md#数据包格式">数据包格式</a>
*
* @author mjz
* @date 2023/1/5
*/
public abstract class BaseBilibiliWebSocketFrame extends BinaryWebSocketFrame {
public static int sequence = 0;
public ProtoverEnum getProtoverEnum() {
return ProtoverEnum.getByCode(super.content().getShort(6));
}
public OperationEnum getOperationEnum() {
return OperationEnum.getByCode(super.content().getInt(8));
}
public BaseBilibiliWebSocketFrame(ByteBuf byteBuf) {
super(byteBuf);
}
}

View File

@@ -0,0 +1,113 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.netty.frame.factory;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.NumberUtil;
import com.fasterxml.jackson.databind.JsonNode;
import tech.ordinaryroad.live.chat.client.bilibili.api.BilibiliApis;
import tech.ordinaryroad.live.chat.client.bilibili.constant.ProtoverEnum;
import tech.ordinaryroad.live.chat.client.bilibili.msg.AuthMsg;
import tech.ordinaryroad.live.chat.client.bilibili.msg.HeartbeatMsg;
import tech.ordinaryroad.live.chat.client.bilibili.netty.frame.AuthWebSocketFrame;
import tech.ordinaryroad.live.chat.client.bilibili.netty.frame.HeartbeatWebSocketFrame;
import tech.ordinaryroad.live.chat.client.bilibili.util.BilibiliCodecUtil;
import tech.ordinaryroad.live.chat.client.commons.base.exception.BaseException;
import tech.ordinaryroad.live.chat.client.commons.util.OrLiveChatCookieUtil;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author mjz
* @date 2023/1/5
*/
public class BilibiliWebSocketFrameFactory {
private static final ConcurrentHashMap<Long, BilibiliWebSocketFrameFactory> CACHE = new ConcurrentHashMap<>();
/**
* 浏览器地址中的房间id支持短id
*/
private final long roomId;
private volatile static HeartbeatMsg heartbeatMsg;
public BilibiliWebSocketFrameFactory(long roomId) {
this.roomId = roomId;
}
public synchronized static BilibiliWebSocketFrameFactory getInstance(long roomId) {
return CACHE.computeIfAbsent(roomId, aLong -> new BilibiliWebSocketFrameFactory(roomId));
}
/**
* 创建认证包
*
* @param protover {@link ProtoverEnum}
* @param cookie 浏览器cookie仅用来维持登录状态
* @return AuthWebSocketFrame
*/
public AuthWebSocketFrame createAuth(ProtoverEnum protover, String cookie) {
try {
Map<String, String> cookieMap = OrLiveChatCookieUtil.parseCookieString(cookie);
String buvid3 = OrLiveChatCookieUtil.getCookieByName(cookieMap, "buvid3", () -> UUID.randomUUID().toString());
String uid = OrLiveChatCookieUtil.getCookieByName(cookieMap, "DedeUserID", () -> "0");
BilibiliApis.RoomInitResult data = BilibiliApis.roomInit(roomId, cookie);
JsonNode danmuInfo = BilibiliApis.getDanmuInfo(roomId, 0, cookie);
long realRoomId = data.getRoom_id();
AuthMsg authMsg = new AuthMsg(realRoomId, protover.getCode(), buvid3, danmuInfo.get("token").asText());
authMsg.setUid(NumberUtil.parseLong(uid));
return new AuthWebSocketFrame(BilibiliCodecUtil.encode(authMsg));
} catch (Exception e) {
throw new BaseException(String.format("认证包创建失败请检查房间号是否正确。roomId: %d, msg: %s", roomId, e.getMessage()));
}
}
public AuthWebSocketFrame createAuth(ProtoverEnum protover) {
return this.createAuth(protover, null);
}
public HeartbeatWebSocketFrame createHeartbeat(ProtoverEnum protover) {
return new HeartbeatWebSocketFrame(BilibiliCodecUtil.encode(this.getHeartbeatMsg(protover)));
}
/**
* 心跳包单例模式
*
* @param protover {@link ProtoverEnum}
* @return HeartbeatWebSocketFrame
*/
public HeartbeatMsg getHeartbeatMsg(ProtoverEnum protover) {
if (heartbeatMsg == null) {
synchronized (BilibiliWebSocketFrameFactory.this) {
if (heartbeatMsg == null) {
heartbeatMsg = new HeartbeatMsg(protover.getCode());
}
}
}
return heartbeatMsg;
}
}

View File

@@ -0,0 +1,153 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.netty.handler;
import cn.hutool.core.util.StrUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import tech.ordinaryroad.live.chat.client.bilibili.client.BilibiliLiveChatClient;
import tech.ordinaryroad.live.chat.client.bilibili.constant.BilibiliCmdEnum;
import tech.ordinaryroad.live.chat.client.bilibili.listener.IBilibiliMsgListener;
import tech.ordinaryroad.live.chat.client.bilibili.msg.*;
import tech.ordinaryroad.live.chat.client.bilibili.msg.base.BaseBilibiliMsg;
import tech.ordinaryroad.live.chat.client.bilibili.msg.base.IBilibiliMsg;
import tech.ordinaryroad.live.chat.client.bilibili.util.BilibiliCodecUtil;
import tech.ordinaryroad.live.chat.client.commons.base.msg.ICmdMsg;
import tech.ordinaryroad.live.chat.client.servers.netty.client.handler.BaseNettyClientBinaryFrameHandler;
import java.util.List;
/**
* 消息处理器
*
* @author mjz
* @date 2023/1/4
*/
@Slf4j
@ChannelHandler.Sharable
public class BilibiliBinaryFrameHandler extends BaseNettyClientBinaryFrameHandler<BilibiliLiveChatClient, BilibiliBinaryFrameHandler, BilibiliCmdEnum, IBilibiliMsg, IBilibiliMsgListener> {
public BilibiliBinaryFrameHandler(List<IBilibiliMsgListener> msgListeners, BilibiliLiveChatClient client) {
super(msgListeners, client);
}
public BilibiliBinaryFrameHandler(List<IBilibiliMsgListener> msgListeners, long roomId) {
super(msgListeners, roomId);
}
@SneakyThrows
@Override
public void onCmdMsg(BilibiliCmdEnum cmd, ICmdMsg<BilibiliCmdEnum> cmdMsg) {
if (super.msgListeners.isEmpty()) {
return;
}
SendSmsReplyMsg sendSmsReplyMsg = (SendSmsReplyMsg) cmdMsg;
switch (cmd) {
case DANMU_MSG: {
DanmuMsgMsg danmuMsgMsg = new DanmuMsgMsg();
danmuMsgMsg.setProtover(sendSmsReplyMsg.getProtover());
danmuMsgMsg.setInfo(sendSmsReplyMsg.getInfo());
danmuMsgMsg.setDm_v2(StrUtil.toStringOrNull(sendSmsReplyMsg.getUnknownProperties().get("dm_v2")));
iteratorMsgListeners(msgListener -> msgListener.onDanmuMsg(BilibiliBinaryFrameHandler.this, danmuMsgMsg));
break;
}
case SEND_GIFT: {
SendGiftMsg sendGiftMsg = new SendGiftMsg();
sendGiftMsg.setRoomId(getRoomIdAsLong());
sendGiftMsg.setProtover(sendSmsReplyMsg.getProtover());
SendGiftMsg.Data data = BaseBilibiliMsg.OBJECT_MAPPER.treeToValue(sendSmsReplyMsg.getData(), SendGiftMsg.Data.class);
sendGiftMsg.setData(data);
iteratorMsgListeners(msgListener -> {
msgListener.onGiftMsg(BilibiliBinaryFrameHandler.this, sendGiftMsg);
msgListener.onSendGift(BilibiliBinaryFrameHandler.this, sendSmsReplyMsg);
});
break;
}
case SUPER_CHAT_MESSAGE: {
SuperChatMessageMsg superChatMessageMsg = new SuperChatMessageMsg();
superChatMessageMsg.setProtover(sendSmsReplyMsg.getProtover());
superChatMessageMsg.setRoomid(sendSmsReplyMsg.getRoomid());
SuperChatMessageMsg.Data data = BaseBilibiliMsg.OBJECT_MAPPER.treeToValue(sendSmsReplyMsg.getData(), SuperChatMessageMsg.Data.class);
superChatMessageMsg.setData(data);
iteratorMsgListeners(msgListener -> msgListener.onSuperChatMsg(BilibiliBinaryFrameHandler.this, superChatMessageMsg));
break;
}
case INTERACT_WORD: {
InteractWordMsg interactWordMsg = new InteractWordMsg();
interactWordMsg.setProtover(sendSmsReplyMsg.getProtover());
InteractWordMsg.Data data = BaseBilibiliMsg.OBJECT_MAPPER.treeToValue(sendSmsReplyMsg.getData(), InteractWordMsg.Data.class);
interactWordMsg.setData(data);
iteratorMsgListeners(msgListener -> {
msgListener.onEnterRoomMsg(BilibiliBinaryFrameHandler.this, interactWordMsg);
msgListener.onEnterRoom(BilibiliBinaryFrameHandler.this, sendSmsReplyMsg);
});
break;
}
case ENTRY_EFFECT: {
iteratorMsgListeners(msgListener -> msgListener.onEntryEffect(BilibiliBinaryFrameHandler.this, sendSmsReplyMsg));
break;
}
case WATCHED_CHANGE: {
iteratorMsgListeners(msgListener -> msgListener.onWatchedChange(BilibiliBinaryFrameHandler.this, sendSmsReplyMsg));
break;
}
case LIKE_INFO_V3_CLICK: {
LikeInfoV3ClickMsg likeInfoV3ClickMsg = new LikeInfoV3ClickMsg();
likeInfoV3ClickMsg.setProtover(sendSmsReplyMsg.getProtover());
LikeInfoV3ClickMsg.Data data = BaseBilibiliMsg.OBJECT_MAPPER.treeToValue(sendSmsReplyMsg.getData(), LikeInfoV3ClickMsg.Data.class);
likeInfoV3ClickMsg.setData(data);
iteratorMsgListeners(msgListener -> {
msgListener.onLikeMsg(BilibiliBinaryFrameHandler.this, likeInfoV3ClickMsg);
msgListener.onClickLike(BilibiliBinaryFrameHandler.this, sendSmsReplyMsg);
});
break;
}
case LIKE_INFO_V3_UPDATE: {
iteratorMsgListeners(msgListener -> msgListener.onClickUpdate(BilibiliBinaryFrameHandler.this, sendSmsReplyMsg));
break;
}
default: {
iteratorMsgListeners(msgListener -> msgListener.onOtherCmdMsg(BilibiliBinaryFrameHandler.this, cmd, cmdMsg));
}
}
}
@Override
protected List<IBilibiliMsg> decode(ByteBuf byteBuf) {
return BilibiliCodecUtil.decode(byteBuf);
}
}

View File

@@ -0,0 +1,154 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.netty.handler;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import lombok.extern.slf4j.Slf4j;
import tech.ordinaryroad.live.chat.client.bilibili.client.BilibiliLiveChatClient;
import tech.ordinaryroad.live.chat.client.bilibili.config.BilibiliLiveChatClientConfig;
import tech.ordinaryroad.live.chat.client.bilibili.constant.ProtoverEnum;
import tech.ordinaryroad.live.chat.client.bilibili.netty.frame.factory.BilibiliWebSocketFrameFactory;
import tech.ordinaryroad.live.chat.client.commons.base.listener.IBaseConnectionListener;
import tech.ordinaryroad.live.chat.client.servers.netty.client.handler.BaseNettyClientConnectionHandler;
/**
* 连接处理器
*
* @author mjz
* @date 2023/8/21
*/
@Slf4j
@ChannelHandler.Sharable
public class BilibiliConnectionHandler extends BaseNettyClientConnectionHandler<BilibiliLiveChatClient, BilibiliConnectionHandler> {
/**
* 以ClientConfig为主
*/
private final long roomId;
/**
* 以ClientConfig为主
*/
private final ProtoverEnum protover;
/**
* 以ClientConfig为主
*/
private String cookie;
public BilibiliConnectionHandler(WebSocketClientHandshaker handshaker, BilibiliLiveChatClient client, IBaseConnectionListener<BilibiliConnectionHandler> listener) {
super(handshaker, client, listener);
this.roomId = client.getConfig().getRoomId();
this.protover = client.getConfig().getProtover();
this.cookie = client.getConfig().getCookie();
}
public BilibiliConnectionHandler(WebSocketClientHandshaker handshaker, BilibiliLiveChatClient client) {
this(handshaker, client, null);
}
public BilibiliConnectionHandler(WebSocketClientHandshaker handshaker, long roomId, ProtoverEnum protover, IBaseConnectionListener<BilibiliConnectionHandler> listener, String cookie) {
super(handshaker, listener);
this.roomId = roomId;
this.protover = protover;
this.cookie = cookie;
}
public BilibiliConnectionHandler(WebSocketClientHandshaker handshaker, long roomId, ProtoverEnum protover, IBaseConnectionListener<BilibiliConnectionHandler> listener) {
this(handshaker, roomId, protover, listener, null);
}
public BilibiliConnectionHandler(WebSocketClientHandshaker handshaker, long roomId, ProtoverEnum protover, String cookie) {
this(handshaker, roomId, protover, null, cookie);
}
public BilibiliConnectionHandler(WebSocketClientHandshaker handshaker, long roomId, ProtoverEnum protover) {
this(handshaker, roomId, protover, null, null);
}
@Override
protected void sendHeartbeat(ChannelHandlerContext ctx) {
if (log.isDebugEnabled()) {
log.debug("发送心跳包");
}
ctx.writeAndFlush(
getWebSocketFrameFactory(getRoomId()).createHeartbeat(getProtover())
).addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
if (log.isDebugEnabled()) {
log.debug("心跳包发送完成");
}
} else {
log.error("心跳包发送失败", future.cause());
}
});
}
private static BilibiliWebSocketFrameFactory getWebSocketFrameFactory(long roomId) {
return BilibiliWebSocketFrameFactory.getInstance(roomId);
}
@Override
public void sendAuthRequest(Channel channel) {
// 5s内认证
if (log.isDebugEnabled()) {
log.debug("发送认证包");
}
channel.writeAndFlush(getWebSocketFrameFactory(getRoomId()).createAuth(getProtover(), getCookie()));
}
public long getRoomId() {
return client != null ? client.getConfig().getRoomId() : roomId;
}
private ProtoverEnum getProtover() {
return client != null ? client.getConfig().getProtover() : protover;
}
private String getCookie() {
return client != null ? client.getConfig().getCookie() : cookie;
}
@Override
protected long getHeartbeatPeriod() {
if (client == null) {
return BilibiliLiveChatClientConfig.DEFAULT_HEARTBEAT_PERIOD;
} else {
return client.getConfig().getHeartbeatPeriod();
}
}
@Override
protected long getHeartbeatInitialDelay() {
if (client == null) {
return BilibiliLiveChatClientConfig.DEFAULT_HEARTBEAT_INITIAL_DELAY;
} else {
return client.getConfig().getHeartbeatInitialDelay();
}
}
}

View File

@@ -0,0 +1,78 @@
/*
* 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.
*/
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: dm_v2.proto
package tech.ordinaryroad.live.chat.client.bilibili.protobuf;
public final class Dm_v2Proto {
private Dm_v2Proto() {}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistryLite registry) {
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistry registry) {
registerAllExtensions(
(com.google.protobuf.ExtensionRegistryLite) registry);
}
static final com.google.protobuf.Descriptors.Descriptor
internal_static_tech_ordinaryroad_live_chat_client_bilibili_protobuf_dm_v2_descriptor;
static final
com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
internal_static_tech_ordinaryroad_live_chat_client_bilibili_protobuf_dm_v2_fieldAccessorTable;
public static com.google.protobuf.Descriptors.FileDescriptor
getDescriptor() {
return descriptor;
}
private static com.google.protobuf.Descriptors.FileDescriptor
descriptor;
static {
String[] descriptorData = {
"\n\013dm_v2.proto\0224tech.ordinaryroad.live.ch" +
"at.client.bilibili.protobuf\032\016dm_v2_20.pr" +
"oto\"Y\n\005dm_v2\022P\n\010dm_v2_20\030\024 \001(\0132>.tech.or" +
"dinaryroad.live.chat.client.bilibili.pro" +
"tobuf.dm_v2_20BJ\n4tech.ordinaryroad.live" +
".chat.client.bilibili.protobufB\nDm_v2Pro" +
"toP\001\242\002\003GPBb\006proto3"
};
descriptor = com.google.protobuf.Descriptors.FileDescriptor
.internalBuildGeneratedFileFrom(descriptorData,
new com.google.protobuf.Descriptors.FileDescriptor[] {
Dm_v2_20Proto.getDescriptor(),
});
internal_static_tech_ordinaryroad_live_chat_client_bilibili_protobuf_dm_v2_descriptor =
getDescriptor().getMessageTypes().get(0);
internal_static_tech_ordinaryroad_live_chat_client_bilibili_protobuf_dm_v2_fieldAccessorTable = new
com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
internal_static_tech_ordinaryroad_live_chat_client_bilibili_protobuf_dm_v2_descriptor,
new String[] { "DmV220", });
Dm_v2_20Proto.getDescriptor();
}
// @@protoc_insertion_point(outer_class_scope)
}

View File

@@ -0,0 +1,74 @@
/*
* 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.
*/
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: dm_v2_20.proto
package tech.ordinaryroad.live.chat.client.bilibili.protobuf;
public final class Dm_v2_20Proto {
private Dm_v2_20Proto() {}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistryLite registry) {
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistry registry) {
registerAllExtensions(
(com.google.protobuf.ExtensionRegistryLite) registry);
}
static final com.google.protobuf.Descriptors.Descriptor
internal_static_tech_ordinaryroad_live_chat_client_bilibili_protobuf_dm_v2_20_descriptor;
static final
com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
internal_static_tech_ordinaryroad_live_chat_client_bilibili_protobuf_dm_v2_20_fieldAccessorTable;
public static com.google.protobuf.Descriptors.FileDescriptor
getDescriptor() {
return descriptor;
}
private static com.google.protobuf.Descriptors.FileDescriptor
descriptor;
static {
String[] descriptorData = {
"\n\016dm_v2_20.proto\0224tech.ordinaryroad.live" +
".chat.client.bilibili.protobuf\"\032\n\010dm_v2_" +
"20\022\016\n\006avatar\030\004 \001(\tBM\n4tech.ordinaryroad." +
"live.chat.client.bilibili.protobufB\rDm_v" +
"2_20ProtoP\001\242\002\003GPBb\006proto3"
};
descriptor = com.google.protobuf.Descriptors.FileDescriptor
.internalBuildGeneratedFileFrom(descriptorData,
new com.google.protobuf.Descriptors.FileDescriptor[] {
});
internal_static_tech_ordinaryroad_live_chat_client_bilibili_protobuf_dm_v2_20_descriptor =
getDescriptor().getMessageTypes().get(0);
internal_static_tech_ordinaryroad_live_chat_client_bilibili_protobuf_dm_v2_20_fieldAccessorTable = new
com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
internal_static_tech_ordinaryroad_live_chat_client_bilibili_protobuf_dm_v2_20_descriptor,
new String[] { "Avatar", });
}
// @@protoc_insertion_point(outer_class_scope)
}

View File

@@ -0,0 +1,48 @@
/*
* 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.
*/
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: dm_v2.proto
package tech.ordinaryroad.live.chat.client.bilibili.protobuf;
public interface dm_v2OrBuilder extends
// @@protoc_insertion_point(interface_extends:tech.ordinaryroad.live.chat.client.bilibili.protobuf.dm_v2)
com.google.protobuf.MessageOrBuilder {
/**
* <code>.tech.ordinaryroad.live.chat.client.bilibili.protobuf.dm_v2_20 dm_v2_20 = 20;</code>
* @return Whether the dmV220 field is set.
*/
boolean hasDmV220();
/**
* <code>.tech.ordinaryroad.live.chat.client.bilibili.protobuf.dm_v2_20 dm_v2_20 = 20;</code>
* @return The dmV220.
*/
dm_v2_20 getDmV220();
/**
* <code>.tech.ordinaryroad.live.chat.client.bilibili.protobuf.dm_v2_20 dm_v2_20 = 20;</code>
*/
dm_v2_20OrBuilder getDmV220OrBuilder();
}

View File

@@ -0,0 +1,45 @@
/*
* 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.
*/
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: dm_v2_20.proto
package tech.ordinaryroad.live.chat.client.bilibili.protobuf;
public interface dm_v2_20OrBuilder extends
// @@protoc_insertion_point(interface_extends:tech.ordinaryroad.live.chat.client.bilibili.protobuf.dm_v2_20)
com.google.protobuf.MessageOrBuilder {
/**
* <code>string avatar = 4;</code>
* @return The avatar.
*/
String getAvatar();
/**
* <code>string avatar = 4;</code>
* @return The bytes for avatar.
*/
com.google.protobuf.ByteString
getAvatarBytes();
}

View File

@@ -0,0 +1,259 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.util;
import cn.hutool.core.util.StrUtil;
import com.aayushatharva.brotli4j.Brotli4jLoader;
import com.aayushatharva.brotli4j.decoder.BrotliInputStream;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import lombok.extern.slf4j.Slf4j;
import tech.ordinaryroad.live.chat.client.bilibili.constant.OperationEnum;
import tech.ordinaryroad.live.chat.client.bilibili.constant.ProtoverEnum;
import tech.ordinaryroad.live.chat.client.bilibili.msg.AuthReplyMsg;
import tech.ordinaryroad.live.chat.client.bilibili.msg.HeartbeatMsg;
import tech.ordinaryroad.live.chat.client.bilibili.msg.HeartbeatReplyMsg;
import tech.ordinaryroad.live.chat.client.bilibili.msg.SendSmsReplyMsg;
import tech.ordinaryroad.live.chat.client.bilibili.msg.base.BaseBilibiliMsg;
import tech.ordinaryroad.live.chat.client.bilibili.msg.base.IBilibiliMsg;
import tech.ordinaryroad.live.chat.client.commons.base.exception.BaseException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
/**
* @author mjz
* @date 2023/1/6
*/
@Slf4j
public class BilibiliCodecUtil {
public static int sequence = 0;
public static final short FRAME_HEADER_LENGTH = 16;
public static ByteBuf encode(BaseBilibiliMsg msg) {
ByteBuf out = Unpooled.buffer(FRAME_HEADER_LENGTH);
String bodyJsonString = StrUtil.EMPTY;
// HeartbeatMsg不需要正文如果序列化后得到`{}`,则替换为空字符串
if (!(msg instanceof HeartbeatMsg)) {
bodyJsonString = msg.toString();
if (StrUtil.EMPTY_JSON.equals(bodyJsonString)) {
bodyJsonString = StrUtil.EMPTY;
}
}
byte[] bodyBytes = bodyJsonString.getBytes(StandardCharsets.UTF_8);
int length = bodyBytes.length + FRAME_HEADER_LENGTH;
out.writeInt(length);
out.writeShort(FRAME_HEADER_LENGTH);
out.writeShort(msg.getProtoverEnum().getCode());
out.writeInt(msg.getOperationEnum().getCode());
out.writeInt(sequence++);
out.writeBytes(bodyBytes);
return out;
}
public static List<IBilibiliMsg> decode(ByteBuf in) {
List<IBilibiliMsg> msgList = new ArrayList<>();
Queue<ByteBuf> pendingByteBuf = new LinkedList<>();
do {
Optional<IBilibiliMsg> msg = doDecode(in, pendingByteBuf);
msg.ifPresent(msgList::add);
in = pendingByteBuf.poll();
} while (in != null);
return msgList;
}
/**
* 执行解码操作,有压缩则先解压,解压后可能得到多条消息
*
* @param in handler收到的一条消息
* @param pendingByteBuf 用于存放未读取完的ByteBuf
* @return Optional<IBilibiliMsg> 何时为空值:不支持的{@link OperationEnum},不支持的{@link ProtoverEnum}{@link #parse(OperationEnum, String)}反序列化失败
* @see OperationEnum
* @see ProtoverEnum
*/
private static Optional<IBilibiliMsg> doDecode(ByteBuf in, Queue<ByteBuf> pendingByteBuf) {
int length = in.readInt();
short frameHeaderLength = in.readShort();
short protoverCode = in.readShort();
int operationCode = in.readInt();
int sequence = in.readInt();
int contentLength = length - frameHeaderLength;
byte[] inputBytes = new byte[contentLength];
in.readBytes(inputBytes);
if (in.readableBytes() != 0) {
// log.error("in.readableBytes() {}", in.readableBytes());
pendingByteBuf.offer(in);
}
OperationEnum operationEnum = OperationEnum.getByCode(operationCode);
if (operationEnum == null) {
throw new BaseException(String.format("未知operation: %d", operationCode));
}
if (protoverCode == ProtoverEnum.NORMAL_ZLIB.getCode()) {
switch (operationEnum) {
case SEND_SMS_REPLY: {
// Decompress the bytes
Inflater inflater = new Inflater();
inflater.reset();
inflater.setInput(inputBytes);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(contentLength);
try {
byte[] bytes = new byte[1024];
while (!inflater.finished()) {
int count = inflater.inflate(bytes);
byteArrayOutputStream.write(bytes, 0, count);
}
} catch (DataFormatException e) {
throw new BaseException(e);
}
inflater.end();
return doDecode(Unpooled.wrappedBuffer(byteArrayOutputStream.toByteArray()), pendingByteBuf);
}
case HEARTBEAT_REPLY: {
BigInteger bigInteger = new BigInteger(inputBytes);
return parse(operationEnum, String.format("{\"popularity\":%d}", bigInteger));
}
default: {
String s = new String(inputBytes, StandardCharsets.UTF_8);
return parse(operationEnum, s);
}
}
} else if (protoverCode == ProtoverEnum.NORMAL_NO_COMPRESSION.getCode()) {
switch (operationEnum) {
case HEARTBEAT_REPLY: {
BigInteger bigInteger = new BigInteger(inputBytes);
return parse(operationEnum, String.format("{\"popularity\":%d}", bigInteger));
}
default: {
String s = new String(inputBytes, StandardCharsets.UTF_8);
return parse(operationEnum, s);
}
}
} else if (protoverCode == ProtoverEnum.HEARTBEAT_AUTH_NO_COMPRESSION.getCode()) {
switch (operationEnum) {
case HEARTBEAT_REPLY: {
BigInteger bigInteger = new BigInteger(inputBytes);
return parse(operationEnum, String.format("{\"popularity\":%d}", bigInteger));
}
default: {
String s = new String(inputBytes, StandardCharsets.UTF_8);
return parse(operationEnum, s);
}
}
} else if (protoverCode == ProtoverEnum.NORMAL_BROTLI.getCode()) {
switch (operationEnum) {
case SEND_SMS_REPLY: {
// Load the native library
Brotli4jLoader.ensureAvailability();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(inputBytes);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(contentLength);
byte[] bytes = new byte[1024];
BrotliInputStream brotliInputStream = null;
ByteBuf wrappedBuffer = null;
try {
brotliInputStream = new BrotliInputStream(byteArrayInputStream);
int count;
while ((count = brotliInputStream.read(bytes)) > -1) {
byteArrayOutputStream.write(bytes, 0, count);
}
wrappedBuffer = Unpooled.wrappedBuffer(byteArrayOutputStream.toByteArray());
} catch (IOException e) {
throw new BaseException(e);
} finally {
try {
// Close the BrotliInputStream. This also closes the InputStream.
if (brotliInputStream != null) {
brotliInputStream.close();
}
byteArrayOutputStream.close();
} catch (IOException e) {
log.error("解压失败", e);
}
}
return doDecode(wrappedBuffer, pendingByteBuf);
}
case HEARTBEAT_REPLY: {
BigInteger bigInteger = new BigInteger(inputBytes);
return parse(operationEnum, String.format("{\"popularity\":%d}", bigInteger));
}
default: {
String s = new String(inputBytes, StandardCharsets.UTF_8);
return parse(operationEnum, s);
}
}
} else {
if (log.isWarnEnabled()) {
log.warn("暂不支持的版本:{}", protoverCode);
}
return Optional.empty();
}
}
public static Optional<IBilibiliMsg> parse(OperationEnum operation, String jsonString) {
switch (operation) {
case SEND_SMS_REPLY: {
try {
return Optional.ofNullable(BaseBilibiliMsg.OBJECT_MAPPER.readValue(jsonString, SendSmsReplyMsg.class));
} catch (JsonProcessingException e) {
throw new BaseException(e);
}
}
case AUTH_REPLY: {
try {
return Optional.ofNullable(BaseBilibiliMsg.OBJECT_MAPPER.readValue(jsonString, AuthReplyMsg.class));
} catch (JsonProcessingException e) {
throw new BaseException(e);
}
}
case HEARTBEAT_REPLY: {
try {
return Optional.ofNullable(BaseBilibiliMsg.OBJECT_MAPPER.readValue(jsonString, HeartbeatReplyMsg.class));
} catch (JsonProcessingException e) {
throw new BaseException(e);
}
}
default: {
if (log.isWarnEnabled()) {
log.warn("暂不支持 {}", operation);
}
return Optional.empty();
}
}
}
}

View File

@@ -0,0 +1,14 @@
syntax = "proto3";
package tech.ordinaryroad.live.chat.client.bilibili.protobuf;
option java_package = "tech.ordinaryroad.live.chat.client.bilibili.protobuf";
option java_outer_classname = "Dm_v2Proto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
import "dm_v2_20.proto";
message dm_v2 {
dm_v2_20 dm_v2_20 = 20;
}

View File

@@ -0,0 +1,12 @@
syntax = "proto3";
package tech.ordinaryroad.live.chat.client.bilibili.protobuf;
option java_package = "tech.ordinaryroad.live.chat.client.bilibili.protobuf";
option java_outer_classname = "Dm_v2_20Proto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
message dm_v2_20 {
string avatar = 4;
}

View File

@@ -0,0 +1,16 @@
package tech.ordinaryroad.live.chat.client.bilibili.api;
import org.junit.jupiter.api.Test;
/**
* @author mjz
* @date 2023/9/7
*/
class BilibiliApisTest {
@Test
void sendMsg() {
String cookie = System.getenv("cookie");
BilibiliApis.sendMsg("666", 545068, cookie);
}
}

View File

@@ -0,0 +1,176 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.bilibili.client;
import cn.hutool.core.thread.ThreadUtil;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import tech.ordinaryroad.live.chat.client.bilibili.config.BilibiliLiveChatClientConfig;
import tech.ordinaryroad.live.chat.client.bilibili.constant.BilibiliCmdEnum;
import tech.ordinaryroad.live.chat.client.bilibili.listener.IBilibiliMsgListener;
import tech.ordinaryroad.live.chat.client.bilibili.msg.*;
import tech.ordinaryroad.live.chat.client.bilibili.netty.handler.BilibiliBinaryFrameHandler;
import tech.ordinaryroad.live.chat.client.commons.base.msg.ICmdMsg;
import tech.ordinaryroad.live.chat.client.commons.base.msg.IMsg;
import tech.ordinaryroad.live.chat.client.commons.client.enums.ClientStatusEnums;
/**
* @author mjz
* @date 2023/8/26
*/
@Slf4j
class BilibiliLiveChatClientTest {
static Object lock = new Object();
BilibiliLiveChatClient client;
@Test
void example() throws InterruptedException {
String cookie = System.getenv("cookie");
log.error("cookie: {}", cookie);
BilibiliLiveChatClientConfig config = BilibiliLiveChatClientConfig.builder()
// TODO 浏览器Cookie
.cookie(cookie)
.roomId(7777)
.roomId(697)
.build();
client = new BilibiliLiveChatClient(config, new IBilibiliMsgListener() {
@Override
public void onDanmuMsg(BilibiliBinaryFrameHandler binaryFrameHandler, DanmuMsgMsg msg) {
IBilibiliMsgListener.super.onDanmuMsg(binaryFrameHandler, msg);
log.info("{} 收到弹幕 {} {}({}){}", 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);
log.info("{} 收到礼物 {} {}({}) {} {}({})x{}({})", 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);
log.info("{} 收到醒目留言 {}({}){}", binaryFrameHandler.getRoomId(), msg.getUsername(), msg.getUid(), msg.getContent());
}
@Override
public void onEnterRoomMsg(InteractWordMsg msg) {
log.info("{} {}({}) 进入直播间", msg.getBadgeLevel() != 0 ? msg.getBadgeLevel() + msg.getBadgeName() : "", msg.getUsername(), msg.getUid());
}
@Override
public void onLikeMsg(BilibiliBinaryFrameHandler binaryFrameHandler, LikeInfoV3ClickMsg msg) {
IBilibiliMsgListener.super.onLikeMsg(binaryFrameHandler, msg);
log.info("{} 收到点赞 {} {}({})", binaryFrameHandler.getRoomId(), msg.getBadgeLevel() != 0 ? msg.getBadgeLevel() + msg.getBadgeName() : "", msg.getUsername(), msg.getUid());
}
@Override
public void onEntryEffect(SendSmsReplyMsg msg) {
JsonNode data = msg.getData();
String copyWriting = data.get("copy_writing").asText();
log.info("入场效果 {}", copyWriting);
}
@Override
public void onWatchedChange(SendSmsReplyMsg msg) {
JsonNode data = msg.getData();
int num = data.get("num").asInt();
String textSmall = data.get("text_small").asText();
String textLarge = data.get("text_large").asText();
log.debug("观看人数变化 {} {} {}", num, textSmall, textLarge);
}
@Override
public void onClickLike(SendSmsReplyMsg msg) {
JsonNode data = msg.getData();
String uname = data.get("uname").asText();
String likeText = data.get("like_text").asText();
log.debug("为主播点赞 {} {}", uname, likeText);
}
@Override
public void onClickUpdate(SendSmsReplyMsg msg) {
JsonNode data = msg.getData();
int clickCount = data.get("click_count").asInt();
log.debug("点赞数更新 {}", clickCount);
}
@Override
public void onMsg(IMsg msg) {
log.debug("收到{}消息 {}", msg.getClass(), msg);
}
@Override
public void onCmdMsg(BilibiliCmdEnum cmd, ICmdMsg<BilibiliCmdEnum> cmdMsg) {
log.debug("收到CMD消息{} {}", cmd, cmdMsg);
}
@Override
public void onOtherCmdMsg(BilibiliCmdEnum cmd, ICmdMsg<BilibiliCmdEnum> cmdMsg) {
// log.debug("收到其他CMD消息 {}", cmd);
switch (cmd) {
case GUARD_BUY: {
// 有人上舰
SendSmsReplyMsg sendSmsReplyMsg = (SendSmsReplyMsg) cmdMsg;
break;
}
case SUPER_CHAT_MESSAGE_DELETE: {
// 删除醒目留言
SendSmsReplyMsg sendSmsReplyMsg = (SendSmsReplyMsg) cmdMsg;
break;
}
}
}
@Override
public void onUnknownCmd(String cmdString, IMsg msg) {
log.debug("收到未知CMD消息 {}", cmdString);
}
});
client.connect();
client.addStatusChangeListener(evt -> {
ClientStatusEnums newValue = (ClientStatusEnums) evt.getNewValue();
if (newValue == ClientStatusEnums.CONNECTED) {
ThreadUtil.execAsync(() -> {
ThreadUtil.sleep(5000);
client.clickLike(5, () -> {
log.warn("为主播点赞成功");
});
});
}
});
// 防止测试时直接退出
while (true) {
synchronized (lock) {
lock.wait();
}
}
}
}

View File

@@ -0,0 +1,41 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.xmzs</groupId>
<artifactId>live-chat-clients</artifactId>
<version>1.0.0</version>
</parent>
<packaging>jar</packaging>
<artifactId>live-chat-client-douyin</artifactId>
<name>live-chat-client-douyin</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.xmzs</groupId>
<artifactId>live-chat-client-servers-netty-client</artifactId>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,81 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.douyin;
import cn.hutool.core.util.RandomUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tech.ordinaryroad.live.chat.client.commons.base.msg.IMsg;
import tech.ordinaryroad.live.chat.client.commons.client.enums.ClientStatusEnums;
import tech.ordinaryroad.live.chat.client.douyin.client.DouyinLiveChatClient;
import tech.ordinaryroad.live.chat.client.douyin.config.DouyinLiveChatClientConfig;
import tech.ordinaryroad.live.chat.client.douyin.listener.IDouyinMsgListener;
import tech.ordinaryroad.live.chat.client.douyin.msg.DouyinDanmuMsg;
import tech.ordinaryroad.live.chat.client.douyin.netty.handler.DouyinBinaryFrameHandler;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class ClientModeExample {
static Logger log = LoggerFactory.getLogger(ClientModeExample.class);
public static void main(String[] args) {
// 1. 创建配置
DouyinLiveChatClientConfig config = DouyinLiveChatClientConfig.builder()
// TODO 浏览器Cookie
.cookie("did=web_6c4ac2a8ef8855d35df4d564baeaa8e8; kuaishou.live.bfb1s=7206d814e5c089a58c910ed8bf52ace5; clientid=3; did=web_6c4ac2a8ef8855d35df4d564baeaa8e8; client_key=65890b29; kpn=GAME_ZONE; userId=3941614875; kuaishou.live.web_st=ChRrdWFpc2hvdS5saXZlLndlYi5zdBKgAbRhgemDxM_Z_lBn7EZ_-5unvtslsh7ci5VY_eg80qqjxy5yb7oBFrdcWSPlz6jMIBz9h_yoLzATE3ngj2WawpxvbJhmq0EnRYIHv308kTBmg4KN2JQf7w2mfrsp1vusFbZ3NkfsEO4PrGQfX0L6mJRbgXeBqyz4tUM5O0q2Jte_NzWkaOnezvIGRAG8Y6yA1dxGUmiA9syTrPrdnSOzZvAaEoJNhwQ4OUDtgURWN6k9Xgm8PSIgAfV-ZvahtgaYhopZno6OuS2pkaFZjrz4ymoEZ1DSnj0oBTAB; kuaishou.live.web_ph=a287be6ab01dce264c0554eed94c2d6ac991; userId=3941614875")
// TODO 直播间id支持短id
.roomId("Jiazi-9931")
.build();
// 2. 创建Client并传入配置、添加消息回调
DouyinLiveChatClient client = new DouyinLiveChatClient(config, new IDouyinMsgListener() {
// @Override
// public void onMsg(IMsg msg) {
// log.debug("收到{}消息 {}", msg.getClass(), msg);
// }
// @Override
// public void onUnknownCmd(String cmdString, IMsg msg) {
// log.debug("收到未知CMD消息 {}", cmdString);
// }
@Override
public void onDanmuMsg(DouyinBinaryFrameHandler douyinBinaryFrameHandler, DouyinDanmuMsg msg) {
log.info("{} 收到弹幕 [{}] {}({}){}", douyinBinaryFrameHandler.getRoomId(), msg.getBadgeLevel() != 0 ? msg.getBadgeLevel() + msg.getBadgeName() : "", msg.getUsername(), msg.getUid(), msg.getContent());
}
});
// 3. 开始监听直播间
client.connect();
// 客户端连接状态回调
// client.addStatusChangeListener(evt -> {
// if (evt.getNewValue().equals(ClientStatusEnums.CONNECTED)) {
// // TODO 要发送的弹幕内容,请注意控制发送频率;框架内置支持设置发送弹幕的最少时间间隔,小于时将忽略该次发送
// client.sendDanmu("666666" + RandomUtil.randomNumbers(1));
// }
// });
}
}

View File

@@ -0,0 +1,108 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.douyin.api;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import cn.hutool.http.HttpUtil;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import tech.ordinaryroad.live.chat.client.commons.base.exception.BaseException;
import tech.ordinaryroad.live.chat.client.commons.util.OrLiveChatCookieUtil;
import java.util.Map;
/**
* @author mjz
* @date 2024/1/3
*/
@Slf4j
public class DouyinApis {
public static final String KEY_COOKIE_TTWID = "ttwid";
public static final String KEY_COOKIE_MS_TOKEN = "msToken";
public static final String KEY_COOKIE_AC_NONCE = "__ac_nonce";
public static final String MS_TOKEN_BASE_STRING = RandomUtil.BASE_CHAR_NUMBER_LOWER + "=_";
public static final int MS_TOKEN_LENGTH = 107;
public static final int AC_NONCE_LENGTH = 21;
public static final String PATTERN_USER_UNIQUE_ID = "\\\\\"user_unique_id\\\\\":\\\\\"(\\d+)\\\\\"";
public static final String PATTERN_ROOM_ID = "\\\\\"roomId\\\\\":\\\\\"(\\d+)\\\\\"";
public static RoomInitResult roomInit(Object roomId, String cookie) {
Map<String, String> cookieMap = OrLiveChatCookieUtil.parseCookieString(cookie);
@Cleanup
HttpResponse response1 = HttpUtil.createGet("https://live.douyin.com/").cookie(cookie).execute();
String ttwid = OrLiveChatCookieUtil.getCookieByName(cookieMap, KEY_COOKIE_TTWID, () -> response1.getCookie(KEY_COOKIE_TTWID).getValue());
String msToken = OrLiveChatCookieUtil.getCookieByName(cookieMap, KEY_COOKIE_MS_TOKEN, () -> RandomUtil.randomString(MS_TOKEN_BASE_STRING, MS_TOKEN_LENGTH));
String __ac_nonce = OrLiveChatCookieUtil.getCookieByName(cookieMap, KEY_COOKIE_AC_NONCE, () -> RandomUtil.randomString(AC_NONCE_LENGTH));
@Cleanup
HttpResponse response2 = HttpUtil.createGet("https://live.douyin.com/" + roomId)
.cookie(StrUtil.emptyToDefault(cookie, KEY_COOKIE_TTWID + "=" + ttwid + "; " + KEY_COOKIE_MS_TOKEN + "=" + msToken + "; " + KEY_COOKIE_AC_NONCE + "=" + __ac_nonce))
.execute();
if (response2.getStatus() != HttpStatus.HTTP_OK) {
throw new BaseException("获取" + roomId + "真实房间ID失败");
}
String user_unique_id = StrUtil.emptyToDefault(ReUtil.getGroup1(PATTERN_USER_UNIQUE_ID, response2.body()), RandomUtil.randomNumbers(19));
long realRoomId;
String realRoomIdString = ReUtil.getGroup1(PATTERN_ROOM_ID, response2.body());
try {
realRoomId = NumberUtil.parseLong(realRoomIdString);
} catch (Exception e) {
throw new BaseException("获取" + roomId + "真实房间ID失败");
}
return RoomInitResult.builder()
.ttwid(ttwid)
.msToken(msToken)
.acNonce(__ac_nonce)
.realRoomId(realRoomId)
.userUniqueId(user_unique_id)
.build();
}
public static RoomInitResult roomInit(Object roomId) {
return roomInit(roomId, null);
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public static class RoomInitResult {
private String ttwid;
private String msToken;
private String acNonce;
private long realRoomId;
private String userUniqueId;
}
}

View File

@@ -0,0 +1,174 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.douyin.client;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.GlobalHeaders;
import cn.hutool.http.Header;
import cn.hutool.http.HttpUtil;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import lombok.extern.slf4j.Slf4j;
import tech.ordinaryroad.live.chat.client.commons.base.listener.IBaseConnectionListener;
import tech.ordinaryroad.live.chat.client.douyin.api.DouyinApis;
import tech.ordinaryroad.live.chat.client.douyin.config.DouyinLiveChatClientConfig;
import tech.ordinaryroad.live.chat.client.douyin.constant.DouyinCmdEnum;
import tech.ordinaryroad.live.chat.client.douyin.listener.IDouyinConnectionListener;
import tech.ordinaryroad.live.chat.client.douyin.listener.IDouyinMsgListener;
import tech.ordinaryroad.live.chat.client.douyin.msg.base.IDouyinMsg;
import tech.ordinaryroad.live.chat.client.douyin.netty.handler.DouyinBinaryFrameHandler;
import tech.ordinaryroad.live.chat.client.douyin.netty.handler.DouyinConnectionHandler;
import tech.ordinaryroad.live.chat.client.servers.netty.client.base.BaseNettyClient;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
/**
* @author mjz
* @date 2024/1/2
*/
@Slf4j
public class DouyinLiveChatClient extends BaseNettyClient<
DouyinLiveChatClientConfig,
DouyinCmdEnum,
IDouyinMsg,
IDouyinMsgListener,
DouyinConnectionHandler,
DouyinBinaryFrameHandler> {
private DouyinApis.RoomInitResult roomInitResult = new DouyinApis.RoomInitResult();
public DouyinLiveChatClient(DouyinLiveChatClientConfig config, List<IDouyinMsgListener> msgListeners, IDouyinConnectionListener connectionListener, EventLoopGroup workerGroup) {
super(config, workerGroup, connectionListener);
addMsgListeners(msgListeners);
// 初始化
this.init();
}
public DouyinLiveChatClient(DouyinLiveChatClientConfig config, IDouyinMsgListener msgListener, IDouyinConnectionListener connectionListener, EventLoopGroup workerGroup) {
super(config, workerGroup, connectionListener);
addMsgListener(msgListener);
// 初始化
this.init();
}
public DouyinLiveChatClient(DouyinLiveChatClientConfig config, IDouyinMsgListener msgListener, IDouyinConnectionListener connectionListener) {
this(config, msgListener, connectionListener, new NioEventLoopGroup());
}
public DouyinLiveChatClient(DouyinLiveChatClientConfig config, IDouyinMsgListener msgListener) {
this(config, msgListener, null, new NioEventLoopGroup());
}
public DouyinLiveChatClient(DouyinLiveChatClientConfig config) {
this(config, null);
}
@Override
public void init() {
roomInitResult = DouyinApis.roomInit(getConfig().getRoomId(), getConfig().getCookie());
super.init();
}
@Override
public DouyinConnectionHandler initConnectionHandler(IBaseConnectionListener<DouyinConnectionHandler> clientConnectionListener) {
DefaultHttpHeaders headers = new DefaultHttpHeaders();
headers.add(Header.COOKIE.name(), DouyinApis.KEY_COOKIE_TTWID + "=" + roomInitResult.getTtwid());
headers.add(Header.USER_AGENT.name(), GlobalHeaders.INSTANCE.header(Header.USER_AGENT));
return new DouyinConnectionHandler(
WebSocketClientHandshakerFactory.newHandshaker(getWebsocketUri(), WebSocketVersion.V13, null, true, headers, getConfig().getMaxFramePayloadLength()),
DouyinLiveChatClient.this, clientConnectionListener
);
}
@Override
public DouyinBinaryFrameHandler initBinaryFrameHandler() {
return new DouyinBinaryFrameHandler(super.msgListeners, DouyinLiveChatClient.this);
}
@Override
protected String getWebSocketUriString() {
long realRoomId = roomInitResult.getRealRoomId();
String userUniqueId = roomInitResult.getUserUniqueId();
String webSocketUriString = super.getWebSocketUriString();
Map<String, String> queryParams = new HashMap<>();
queryParams.put("app_name", "douyin_web");
queryParams.put("version_code", getConfig().getVersionCode());
queryParams.put("webcast_sdk_version", getConfig().getWebcastSdkVersion());
queryParams.put("update_version_code", getConfig().getUpdateVersionCode());
queryParams.put("compress", "gzip");
queryParams.put("device_platform", "web");
queryParams.put("cookie_enabled", "true");
queryParams.put("screen_width", "800");
queryParams.put("screen_height", "1280");
queryParams.put("browser_language", "zh-CN");
queryParams.put("browser_platform", "MacIntel");
queryParams.put("browser_name", "Mozilla");
queryParams.put("browser_version", "5.0%20(Macintosh;%20Intel%20Mac%20OS%20X%2010_15_7)%20AppleWebKit/537.36%20(KHTML,%20like%20Gecko)%20Chrome/116.0.0.0%20Safari/537.36");
queryParams.put("browser_online", "true");
queryParams.put("tz_name", "Asia/Shanghai");
queryParams.put("host", "https://live.douyin.com");
queryParams.put("im_path", "/webcast/im/fetch/");
queryParams.put("endpoint", "live_pc");
queryParams.put("identity", "audience");
queryParams.put("support_wrds", "1");
queryParams.put("heartbeatDuration ", "0");
queryParams.put("live_id", "1");
queryParams.put("did_rule", "3");
queryParams.put("aid", "6383");
queryParams.put("room_id", Long.toString(realRoomId));
queryParams.put("user_unique_id", userUniqueId);
// TODO 生成signature
queryParams.put("signature", "00000000");
queryParams.put("cursor", "t-" + System.currentTimeMillis() + "_r-1_d-1_u-1_h-1");
queryParams.put("internal_ext", "internal_src:dim|" +
"wss_push_room_id:" + realRoomId + "|" +
"wss_push_did:" + userUniqueId + "|" +
"dim_log_id:" + DateUtil.format(new Date(), "yyyy-MM-dd") + RandomUtil.randomNumbers(6) + RandomUtil.randomString("0123456789ABCDEF", 20) + "|" +
"first_req_ms:" + System.currentTimeMillis() + "|" +
"fetch_time:" + System.currentTimeMillis() + "|" +
"seq:1|" +
"wss_info:0-" + System.currentTimeMillis() + "-0-0|" +
"wrds_kvs:WebcastRoomStatsMessage-" + System.nanoTime() + "_WebcastRoomRankMessage-" + System.nanoTime() + "_LotteryInfoSyncData-" + System.nanoTime() + "_WebcastActivityEmojiGroupsMessage-" + System.nanoTime());
return webSocketUriString + "?" + HttpUtil.toParams(queryParams);
}
public void sendDanmu(Object danmu, Runnable success, Consumer<Throwable> failed) {
super.sendDanmu(danmu, success, failed);
}
}

View File

@@ -0,0 +1,93 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.douyin.config;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import tech.ordinaryroad.live.chat.client.servers.netty.client.config.BaseNettyClientConfig;
/**
* @author mjz
* @date 2024/1/2
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder(toBuilder = true)
public class DouyinLiveChatClientConfig extends BaseNettyClientConfig {
@Builder.Default
private int aggregatorMaxContentLength = 64 * 1024 * 1024;
@Builder.Default
private int maxFramePayloadLength = 64 * 1024 * 1024;
private String versionCode = "180800";
private String webcastSdkVersion = "1.0.12";
private String updateVersionCode = "1.0.12";
/**
* 示例
* wss://webcast5-ws-web-lf.douyin.com/webcast/im/push/v2/
* ?app_name=douyin_web
* &version_code=180800
* &webcast_sdk_version=1.0.12
* &update_version_code=1.0.12
* &compress=gzip
* &device_platform=web
* &cookie_enabled=true
* &screen_width=1512
* &screen_height=982
* &browser_language=zh-CN
* &browser_platform=MacIntel
* &browser_name=Mozilla
* &browser_version=5.0%20(Macintosh;%20Intel%20Mac%20OS%20X%2010_15_7)%20AppleWebKit/537.36%20(KHTML,%20like%20Gecko)%20Chrome/118.0.0.0%20Safari/537.36
* &browser_online=true
* &tz_name=Asia/Shanghai
* &cursor=u-1_h-1_t-1704202376885_r-1_d-1
* &internal_ext=internal_src:dim|wss_push_room_id:7319486720022301449|wss_push_did:7319492411867170356|dim_log_id:20240102213256AAA5B735ADBE992BEF6A|first_req_ms:1704202376757|fetch_time:1704202376885|seq:1|wss_info:0-1704202376885-0-0|wrds_kvs:WebcastActivityEmojiGroupsMessage-1704200830782138545_WebcastRoomRankMessage-1704202270876589607_WebcastRoomStatsMessage-1704202372842388781
* &host=https://live.douyin.com
* &aid=6383
* &live_id=1
* &did_rule=3
* &endpoint=live_pc
* &support_wrds=1
* &user_unique_id=7319492411867170356
* &im_path=/webcast/im/fetch/
* &identity=audience
* &need_persist_msg_count=15
* &room_id=7319486720022301449
* &heartbeatDuration=0
* &signature=Wk407jV1/WbnoIGk
*/
@Builder.Default
private String websocketUri = "wss://webcast5-ws-web-lf.douyin.com:443/webcast/im/push/v2/";
}

View File

@@ -0,0 +1,73 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.douyin.constant;
import cn.hutool.core.util.StrUtil;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* @author mjz
* @date 2024/1/2
*/
@Getter
@RequiredArgsConstructor
public enum DouyinCmdEnum {
/**
* 弹幕
*/
WebcastChatMessage,
/**
* 礼物
*/
WebcastGiftMessage,
/**
* 点赞
*/
WebcastLikeMessage,
/**
* 入房
*/
WebcastMemberMessage,
WebcastSocialMessage,
WebcastRoomUserSeqMessage,
WebcastFansclubMessage,
WebcastControlMessage,
;
public static DouyinCmdEnum getByName(String name) {
if (StrUtil.isBlank(name)) {
return null;
}
for (DouyinCmdEnum value : values()) {
if (value.name().equals(name)) {
return value;
}
}
return null;
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.douyin.listener;
import tech.ordinaryroad.live.chat.client.commons.base.listener.IBaseConnectionListener;
import tech.ordinaryroad.live.chat.client.douyin.netty.handler.DouyinConnectionHandler;
/**
* @author mjz
* @date 2024/1/2
*/
public interface IDouyinConnectionListener extends IBaseConnectionListener<DouyinConnectionHandler> {
}

View File

@@ -0,0 +1,44 @@
/*
* 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.
*/
package tech.ordinaryroad.live.chat.client.douyin.listener;
import tech.ordinaryroad.live.chat.client.commons.base.listener.*;
import tech.ordinaryroad.live.chat.client.douyin.constant.DouyinCmdEnum;
import tech.ordinaryroad.live.chat.client.douyin.msg.DouyinDanmuMsg;
import tech.ordinaryroad.live.chat.client.douyin.msg.DouyinEnterRoomMsg;
import tech.ordinaryroad.live.chat.client.douyin.msg.DouyinGiftMsg;
import tech.ordinaryroad.live.chat.client.douyin.msg.DouyinLikeMsg;
import tech.ordinaryroad.live.chat.client.douyin.netty.handler.DouyinBinaryFrameHandler;
/**
* @author mjz
* @date 2024/1/2
*/
public interface IDouyinMsgListener extends IBaseMsgListener<DouyinBinaryFrameHandler, DouyinCmdEnum>,
IDanmuMsgListener<DouyinBinaryFrameHandler, DouyinDanmuMsg>,
IGiftMsgListener<DouyinBinaryFrameHandler, DouyinGiftMsg>,
IEnterRoomMsgListener<DouyinBinaryFrameHandler, DouyinEnterRoomMsg>,
ILikeMsgListener<DouyinBinaryFrameHandler, DouyinLikeMsg> {
}

Some files were not shown because too many files have changed in this diff Show More