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,52 @@
### 序列化
#### 序列化基本数据类型
1. escape(key)
2. escape(value)
3. key@=value/
#### 序列化Map
1. escape(key)
2. value for each
1. escape(key)
2. escape(value)
3. escape(key@=value/)
3. escape(value)
4. key@=value/
### 反序列化
#### 反序列化基本数据类型
1. spilt("/"):
type@=chatmsg
2. split("@="):
1. key: unescape(key) = el
2. unescape(value).endsWith("/") = false
2.1 value = chatmsg
#### 反序列化Map
1. spilt("/"):
el@=eid@AA=1@ASetp@AA=1@ASsc@AA=1@AS
2. split("@="):
1. key: unescape = el
2. unescape(value).endsWith("/") = true
eid@A=1/etp@A=1/sc@A=1/
1. split("/")
eid@A=1
etp@A=1
sc@A=1
2. unescape:
eid@=1
etp@=1
sc@=1
3. for each
1. split("@=")
2. unescape(key)
3. unescape(value)
4. value: map

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.
-->
<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-douyu</artifactId>
<name>live-chat-client-douyu</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>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,196 @@
/*
* 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.douyu.api;
import cn.hutool.core.collection.CollUtil;
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.crypto.digest.MD5;
import cn.hutool.http.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Cleanup;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import tech.ordinaryroad.live.chat.client.commons.base.exception.BaseException;
import tech.ordinaryroad.live.chat.client.commons.base.msg.BaseMsg;
import tech.ordinaryroad.live.chat.client.commons.util.OrLocalDateTimeUtil;
import tech.ordinaryroad.live.chat.client.douyu.msg.dto.GiftPropSingle;
import java.util.List;
import java.util.Map;
/**
* API简易版
*
* @author mjz
* @date 2023/5/5
*/
@Slf4j
public class DouyuApis {
public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
public static final String PATTERN_BODY_ROOM_ID = "\\$ROOM\\.room_id\\D+(\\d+)";
public static final String KEY_REDIRECT_LOCATION_RID = "rid";
public static final String KEY_COOKIE_DY_DID = "dy_did";
public static final String KEY_COOKIE_ACF_UID = "acf_uid";
public static final String KEY_COOKIE_ACF_STK = "acf_stk";
public static final String KEY_COOKIE_ACF_LTKID = "acf_ltkid";
public static final String API_AVATAR = "https://apic.douyucdn.cn/upload/";
// https://webconf.douyucdn.cn/resource/common/gift/flash/gift_effect.json
// https://webconf.douyucdn.cn/resource/common/gift/common_config_v2.json
// https://webconf.douyucdn.cn/resource/common/prop_gift_list/prop_gift_config.json
// 用PID查询礼物信息https://gift.douyucdn.cn/api/prop/v1/web/single?pid=
// 查询房间礼物列表https://gift.douyucdn.cn/api/gift/v3/web/list?rid=
public static final String API_GIFT_LIST = "https://gift.douyucdn.cn/api/gift/v3/web/list?rid=";
public static final String API_PROP_SINGLE = "https://gift.douyucdn.cn/api/prop/v1/web/single?pid=";
public static final String API_AVATAR_PREFIX_SMALL = "_small.jpg";
public static final String API_AVATAR_PREFIX_MIDDLE = "_middle.jpg";
public static final String API_AVATAR_PREFIX_BIG = "_big.jpg";
public static String getAvatarUrl(List<String> list, String prefix) {
if (CollUtil.isEmpty(list) || list.size() < 3) {
return StrUtil.EMPTY;
}
return API_AVATAR + CollUtil.join(list, "/") + prefix;
}
public static String getSmallAvatarUrl(List<String> list) {
return getAvatarUrl(list, API_AVATAR_PREFIX_SMALL);
}
public static long getRealRoomId(long roomId, String cookie) {
String realRoomIdString = null;
@Cleanup
HttpResponse execute = createGetRequest("https://www.douyu.com/" + roomId, cookie).execute();
if (execute.getStatus() == HttpStatus.HTTP_NOT_FOUND) {
throw new BaseException("获取" + roomId + "真实房间ID失败");
}
if (HttpStatus.isRedirected(execute.getStatus())) {
String location = execute.header(Header.LOCATION);
Map<String, String> paramMap = HttpUtil.decodeParamMap(location, null);
if (paramMap.containsKey(KEY_REDIRECT_LOCATION_RID)) {
realRoomIdString = paramMap.get(KEY_REDIRECT_LOCATION_RID);
}
}
if (StrUtil.isBlank(realRoomIdString)) {
String body = execute.body();
String matchString = ReUtil.get(PATTERN_BODY_ROOM_ID, body, 1);
if (StrUtil.isNotBlank(matchString)) {
realRoomIdString = matchString;
}
}
long realRoomId = roomId;
if (!StrUtil.isBlank(realRoomIdString)) {
try {
realRoomId = NumberUtil.parseLong(realRoomIdString);
} catch (Exception e) {
throw new BaseException("获取" + roomId + "真实房间ID失败");
}
}
return realRoomId;
}
public static long getRealRoomId(long roomId) {
return getRealRoomId(roomId, null);
}
public static JsonNode getServerInfo(long roomId, String cookie) {
@Cleanup
HttpResponse execute = createPostRequest("https://www.douyu.com/lapi/live/gateway/web/" + roomId + "?isH5=1", cookie).execute();
return responseInterceptor(execute.body());
}
public static JsonNode getServerInfo(long roomId) {
return getServerInfo(roomId, null);
}
public static String getRandomWssUri(long roomId) {
JsonNode serverInfo = getServerInfo(roomId);
JsonNode wss = serverInfo.get("wss");
JsonNode jsonNode = wss.get(RandomUtil.randomInt(0, wss.size()));
return "wss://" + jsonNode.get("domain").asText() + ":" + jsonNode.get("port").asInt();
}
public static JsonNode getGiftList(long roomId) {
@Cleanup
HttpResponse execute = createGetRequest(API_GIFT_LIST + roomId, null).execute();
return responseInterceptor(execute.body());
}
@SneakyThrows
public static GiftPropSingle getGiftPropSingleByPid(String pid) {
@Cleanup
HttpResponse execute = createGetRequest(API_PROP_SINGLE + pid, null).execute();
JsonNode jsonNode = responseInterceptor(execute.body());
return BaseMsg.OBJECT_MAPPER.readValue(jsonNode.toString(), GiftPropSingle.class);
}
public static final String vk_secret = "r5*^5;}2#${XF[h+;'./.Q'1;,-]f'p[";
/**
* 参考: <a href="https://cjting.me/2020/07/01/douyu-crawler-and-font-anti-crawling">斗鱼关注人数爬取 ── 字体反爬的攻与防</a>
*
* @param currentTimeSecs
* @param did
* @return
*/
public static String generateVk(long currentTimeSecs, String did) {
return MD5.create().digestHex(currentTimeSecs + vk_secret + did);
}
public static String generateVk(String did) {
return generateVk(OrLocalDateTimeUtil.zonedCurrentTimeSecs(), did);
}
public static HttpRequest createGetRequest(String url, String cookie) {
return HttpUtil.createGet(url)
.cookie(cookie);
}
public static HttpRequest createPostRequest(String url, String cookie) {
return HttpUtil.createPost(url)
.cookie(cookie);
}
private static JsonNode responseInterceptor(String responseString) {
try {
JsonNode jsonNode = OBJECT_MAPPER.readTree(responseString);
int code = jsonNode.get("error").asInt();
if (code == 0) {
// 成功
return jsonNode.get("data");
} else {
throw new BaseException(jsonNode.get("msg").asText());
}
} catch (JsonProcessingException e) {
throw new BaseException(e);
}
}
}

View File

@@ -0,0 +1,117 @@
/*
* 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.douyu.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.commons.base.listener.IBaseConnectionListener;
import tech.ordinaryroad.live.chat.client.commons.base.msg.IMsg;
import tech.ordinaryroad.live.chat.client.douyu.client.base.BaseDouyuLiveChatClient;
import tech.ordinaryroad.live.chat.client.douyu.config.DouyuLiveChatClientConfig;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuClientModeEnum;
import tech.ordinaryroad.live.chat.client.douyu.listener.IDouyuConnectionListener;
import tech.ordinaryroad.live.chat.client.douyu.listener.IDouyuMsgListener;
import tech.ordinaryroad.live.chat.client.douyu.msg.LoginresMsg;
import tech.ordinaryroad.live.chat.client.douyu.netty.handler.DouyuBinaryFrameHandler;
import tech.ordinaryroad.live.chat.client.douyu.netty.handler.DouyuConnectionHandler;
import java.util.List;
/**
* 直播间弹幕客户端{@link DouyuClientModeEnum#DANMU}
*
* @author mjz
* @date 2023/8/20
*/
@Slf4j
public class DouyuDanmuLiveChatClient extends BaseDouyuLiveChatClient implements IDouyuMsgListener {
public DouyuDanmuLiveChatClient(DouyuLiveChatClientConfig config, List<IDouyuMsgListener> msgListeners, IDouyuConnectionListener connectionListener, EventLoopGroup workerGroup) {
super(DouyuClientModeEnum.DANMU, config, msgListeners, connectionListener, workerGroup);
addMsgListener(DouyuDanmuLiveChatClient.this);
}
public DouyuDanmuLiveChatClient(DouyuLiveChatClientConfig config, IDouyuMsgListener msgListener, IDouyuConnectionListener connectionListener, EventLoopGroup workerGroup) {
super(DouyuClientModeEnum.DANMU, config, msgListener, connectionListener, workerGroup);
addMsgListener(DouyuDanmuLiveChatClient.this);
}
public DouyuDanmuLiveChatClient(DouyuLiveChatClientConfig config, IDouyuMsgListener msgListener, IDouyuConnectionListener connectionListener) {
this(config, msgListener, connectionListener, new NioEventLoopGroup());
}
public DouyuDanmuLiveChatClient(DouyuLiveChatClientConfig config, IDouyuMsgListener msgListener) {
this(config, msgListener, null);
}
public DouyuDanmuLiveChatClient(DouyuLiveChatClientConfig config) {
this(config, null);
}
@Override
public DouyuConnectionHandler initConnectionHandler(IBaseConnectionListener<DouyuConnectionHandler> clientConnectionListener) {
return new DouyuConnectionHandler(DouyuClientModeEnum.DANMU,
WebSocketClientHandshakerFactory.newHandshaker(getWebsocketUri(), WebSocketVersion.V13, null, true, new DefaultHttpHeaders(), getConfig().getMaxFramePayloadLength()),
DouyuDanmuLiveChatClient.this, clientConnectionListener
);
}
@Override
public DouyuBinaryFrameHandler initBinaryFrameHandler() {
return new DouyuBinaryFrameHandler(msgListeners, DouyuDanmuLiveChatClient.this);
}
@Override
public void onMsg(DouyuBinaryFrameHandler binaryFrameHandler, IMsg msg) {
if (msg instanceof LoginresMsg) {
// 1 type@=joingroup/rid@=4615502/gid@=1/
send(getWebSocketFrameFactory(getConfig().getRoomId()).createJoingroup(), () -> {
// 2 type@=mrkl/
send(getWebSocketFrameFactory(getConfig().getRoomId()).createHeartbeat(), () -> {
// 3 type@=sub/mt@=dayrk/
send(getWebSocketFrameFactory(getConfig().getRoomId()).createSub());
});
});
}
}
@Override
public boolean removeMsgListener(IDouyuMsgListener msgListener) {
if (msgListener == DouyuDanmuLiveChatClient.this) {
return false;
}
return super.removeMsgListener(msgListener);
}
@Override
public void removeAllMsgListeners() {
super.removeAllMsgListeners();
addMsgListener(DouyuDanmuLiveChatClient.this);
}
}

View File

@@ -0,0 +1,268 @@
/*
* 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.douyu.client;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.RandomUtil;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import lombok.extern.slf4j.Slf4j;
import tech.ordinaryroad.live.chat.client.commons.base.listener.IBaseConnectionListener;
import tech.ordinaryroad.live.chat.client.commons.base.msg.BaseCmdMsg;
import tech.ordinaryroad.live.chat.client.commons.base.msg.BaseMsg;
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;
import tech.ordinaryroad.live.chat.client.douyu.api.DouyuApis;
import tech.ordinaryroad.live.chat.client.douyu.config.DouyuLiveChatClientConfig;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.listener.IDouyuConnectionListener;
import tech.ordinaryroad.live.chat.client.douyu.listener.IDouyuMsgListener;
import tech.ordinaryroad.live.chat.client.douyu.msg.ChatmsgMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.DgbMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.MsgrepeaterproxylistMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.UenterMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.dto.GiftListInfo;
import tech.ordinaryroad.live.chat.client.douyu.msg.dto.GiftPropSingle;
import tech.ordinaryroad.live.chat.client.douyu.netty.handler.DouyuBinaryFrameHandler;
import tech.ordinaryroad.live.chat.client.douyu.netty.handler.DouyuConnectionHandler;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 直播间弹幕客户端
*
* @author mjz
* @date 2023/8/20
*/
@Slf4j
public class DouyuLiveChatClient extends DouyuWsLiveChatClient implements IDouyuMsgListener {
/**
* 通用礼物缓存过期时间1天
* pid,Info
*/
public static final TimedCache<String, GiftPropSingle> giftMap = new TimedCache<>(TimeUnit.DAYS.toMillis(1));
/**
* 房间礼物缓存过期时间1天
* realRoomId,(giftId,Info)
*/
public static final TimedCache<String, Map<String, GiftListInfo>> roomGiftMap = new TimedCache<>(TimeUnit.DAYS.toMillis(1), new HashMap<>());
private final DouyuWsLiveChatClient proxyClient = this;
private DouyuDanmuLiveChatClient danmuClient = null;
private DouyuConnectionHandler connectionHandler;
/**
* 统一管理Ws、Danmu的连接状态
*/
private final IDouyuConnectionListener connectionListener;
public DouyuLiveChatClient(DouyuLiveChatClientConfig config, List<IDouyuMsgListener> msgListeners, IDouyuConnectionListener connectionListener, EventLoopGroup workerGroup) {
super(config, msgListeners, null, workerGroup);
this.connectionListener = connectionListener;
}
public DouyuLiveChatClient(DouyuLiveChatClientConfig config, IDouyuMsgListener msgListener, IDouyuConnectionListener connectionListener, EventLoopGroup workerGroup) {
super(config, msgListener, null, workerGroup);
this.connectionListener = connectionListener;
}
public DouyuLiveChatClient(DouyuLiveChatClientConfig config, IDouyuMsgListener msgListener, IDouyuConnectionListener connectionListener) {
this(config, msgListener, connectionListener, new NioEventLoopGroup());
}
public DouyuLiveChatClient(DouyuLiveChatClientConfig config, IDouyuMsgListener msgListener) {
this(config, msgListener, null);
}
public DouyuLiveChatClient(DouyuLiveChatClientConfig config) {
this(config, null);
}
@Override
public void init() {
super.init();
// 初始化房间礼物列表
Map<String, GiftListInfo> map = new HashMap<>();
DouyuApis.getGiftList(getConfig().getRoomId())
.get("giftList")
.forEach(jsonNode -> {
try {
GiftListInfo giftListInfo = BaseMsg.OBJECT_MAPPER.readValue(jsonNode.toString(), GiftListInfo.class);
map.put(String.valueOf(giftListInfo.getId()), giftListInfo);
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug("获取房间礼物列表异常", e);
}
// ignore
}
});
roomGiftMap.put(String.valueOf(DouyuApis.getRealRoomId(getConfig().getRoomId())), map);
}
@Override
public DouyuConnectionHandler initConnectionHandler(IBaseConnectionListener<DouyuConnectionHandler> clientConnectionListener) {
this.connectionHandler = super.initConnectionHandler(super.clientConnectionListener);
return connectionHandler;
}
@Override
public void onMsg(DouyuBinaryFrameHandler binaryFrameHandler, IMsg msg) {
super.onMsg(binaryFrameHandler, msg);
if (msg instanceof MsgrepeaterproxylistMsg) {
MsgrepeaterproxylistMsg msgrepeaterproxylistMsg = (MsgrepeaterproxylistMsg) msg;
List<Map<String, String>> list = msgrepeaterproxylistMsg.getList();
if (list.isEmpty()) {
log.error("弹幕服务器列表为空");
if (connectionListener != null) {
connectionListener.onConnectFailed(connectionHandler);
}
disconnect();
} else {
// 初始化danmuClient
int randomIndex = RandomUtil.randomInt(0, list.size());
Map<String, String> randomMap = list.get(randomIndex);
DouyuLiveChatClientConfig danmuClientConfig = BeanUtil.toBean(getConfig(), DouyuLiveChatClientConfig.class, CopyOptions.create().ignoreNullValue());
danmuClientConfig.setWebsocketUri(String.format("wss://%s:%s/", randomMap.get("ip"), randomMap.get("port")));
destroyDanmuClient();
this.danmuClient = new DouyuDanmuLiveChatClient(danmuClientConfig, new IDouyuMsgListener() {
@Override
public void onMsg(DouyuBinaryFrameHandler binaryFrameHandler, IMsg msg) {
proxyClient.iteratorMsgListeners(msgListener -> msgListener.onMsg(binaryFrameHandler, msg));
}
@Override
public void onDanmuMsg(DouyuBinaryFrameHandler binaryFrameHandler, ChatmsgMsg msg) {
proxyClient.iteratorMsgListeners(msgListener -> msgListener.onDanmuMsg(binaryFrameHandler, msg));
}
@Override
public void onGiftMsg(DouyuBinaryFrameHandler binaryFrameHandler, DgbMsg msg) {
proxyClient.iteratorMsgListeners(msgListener -> msgListener.onGiftMsg(binaryFrameHandler, msg));
}
@Override
public void onEnterRoomMsg(DouyuBinaryFrameHandler binaryFrameHandler, UenterMsg uenterMsg) {
proxyClient.iteratorMsgListeners(msgListener -> msgListener.onEnterRoomMsg(binaryFrameHandler, uenterMsg));
}
@Override
public void onCmdMsg(DouyuBinaryFrameHandler binaryFrameHandler, DouyuCmdEnum cmd, ICmdMsg<DouyuCmdEnum> cmdMsg) {
proxyClient.iteratorMsgListeners(msgListener -> msgListener.onCmdMsg(binaryFrameHandler, cmd, cmdMsg));
}
@Override
public void onOtherCmdMsg(DouyuBinaryFrameHandler binaryFrameHandler, DouyuCmdEnum cmd, ICmdMsg<DouyuCmdEnum> cmdMsg) {
proxyClient.iteratorMsgListeners(msgListener -> msgListener.onOtherCmdMsg(binaryFrameHandler, cmd, cmdMsg));
}
@Override
public void onUnknownCmd(DouyuBinaryFrameHandler binaryFrameHandler, String cmdString, IMsg msg) {
proxyClient.iteratorMsgListeners(msgListener -> msgListener.onUnknownCmd(binaryFrameHandler, cmdString, msg));
}
@Override
public void onCmdMsg(DouyuBinaryFrameHandler binaryFrameHandler, DouyuCmdEnum cmd, BaseCmdMsg<DouyuCmdEnum> cmdMsg) {
proxyClient.iteratorMsgListeners(msgListener -> msgListener.onCmdMsg(binaryFrameHandler, cmd, cmdMsg));
}
@Override
public void onOtherCmdMsg(DouyuBinaryFrameHandler binaryFrameHandler, DouyuCmdEnum cmd, BaseCmdMsg<DouyuCmdEnum> cmdMsg) {
proxyClient.iteratorMsgListeners(msgListener -> msgListener.onOtherCmdMsg(binaryFrameHandler, cmd, cmdMsg));
}
@Override
public void onUnknownCmd(DouyuBinaryFrameHandler binaryFrameHandler, String cmdString, BaseMsg msg) {
proxyClient.iteratorMsgListeners(msgListener -> msgListener.onUnknownCmd(binaryFrameHandler, cmdString, msg));
}
}, new IDouyuConnectionListener() {
@Override
public void onConnected(DouyuConnectionHandler connectionHandler) {
if (connectionListener != null) {
connectionListener.onConnected(connectionHandler);
}
}
@Override
public void onConnectFailed(DouyuConnectionHandler connectionHandler) {
if (connectionListener != null) {
connectionListener.onConnectFailed(connectionHandler);
}
}
@Override
public void onDisconnected(DouyuConnectionHandler connectionHandler) {
if (connectionListener != null) {
connectionListener.onDisconnected(connectionHandler);
}
}
});
this.danmuClient.addStatusChangeListener(evt -> {
ClientStatusEnums newStatus = (ClientStatusEnums) evt.getNewValue();
switch (newStatus) {
case CONNECTED:
case RECONNECTING:
case CONNECT_FAILED:
case DISCONNECTED:
case CONNECTING: {
super.setStatus(newStatus);
break;
}
default: {
// ignore
}
}
});
this.danmuClient.connect();
}
}
}
@Override
protected void setStatus(ClientStatusEnums status) {
if (status == ClientStatusEnums.CONNECTED) {
return;
}
super.setStatus(status);
}
@Override
public void destroy() {
destroyDanmuClient();
super.destroy();
}
private void destroyDanmuClient() {
if (danmuClient != null) {
danmuClient.destroy();
}
}
}

View File

@@ -0,0 +1,116 @@
/*
* 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.douyu.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.commons.base.listener.IBaseConnectionListener;
import tech.ordinaryroad.live.chat.client.commons.base.msg.IMsg;
import tech.ordinaryroad.live.chat.client.douyu.api.DouyuApis;
import tech.ordinaryroad.live.chat.client.douyu.client.base.BaseDouyuLiveChatClient;
import tech.ordinaryroad.live.chat.client.douyu.config.DouyuLiveChatClientConfig;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuClientModeEnum;
import tech.ordinaryroad.live.chat.client.douyu.listener.IDouyuConnectionListener;
import tech.ordinaryroad.live.chat.client.douyu.listener.IDouyuMsgListener;
import tech.ordinaryroad.live.chat.client.douyu.msg.LoginresMsg;
import tech.ordinaryroad.live.chat.client.douyu.netty.handler.DouyuBinaryFrameHandler;
import tech.ordinaryroad.live.chat.client.douyu.netty.handler.DouyuConnectionHandler;
import java.util.List;
/**
* 直播间弹幕客户端{@link DouyuClientModeEnum#WS}
*
* @author mjz
* @date 2023/8/20
*/
@Slf4j
public class DouyuWsLiveChatClient extends BaseDouyuLiveChatClient implements IDouyuMsgListener {
public DouyuWsLiveChatClient(DouyuLiveChatClientConfig config, List<IDouyuMsgListener> msgListeners, IDouyuConnectionListener connectionListener, EventLoopGroup workerGroup) {
super(DouyuClientModeEnum.WS, config, msgListeners, connectionListener, workerGroup);
addMsgListener(DouyuWsLiveChatClient.this);
}
public DouyuWsLiveChatClient(DouyuLiveChatClientConfig config, IDouyuMsgListener msgListener, IDouyuConnectionListener connectionListener, EventLoopGroup workerGroup) {
super(DouyuClientModeEnum.WS, config, msgListener, connectionListener, workerGroup);
addMsgListener(DouyuWsLiveChatClient.this);
}
public DouyuWsLiveChatClient(DouyuLiveChatClientConfig config, IDouyuMsgListener msgListener, IDouyuConnectionListener connectionListener) {
this(config, msgListener, connectionListener, new NioEventLoopGroup());
}
public DouyuWsLiveChatClient(DouyuLiveChatClientConfig config, IDouyuMsgListener msgListener) {
this(config, msgListener, null);
}
public DouyuWsLiveChatClient(DouyuLiveChatClientConfig config) {
this(config, null);
}
@Override
protected String getWebSocketUriString() {
return DouyuApis.getRandomWssUri(getConfig().getRoomId());
}
@Override
public DouyuConnectionHandler initConnectionHandler(IBaseConnectionListener<DouyuConnectionHandler> clientConnectionListener) {
return new DouyuConnectionHandler(DouyuClientModeEnum.WS,
WebSocketClientHandshakerFactory.newHandshaker(getWebsocketUri(), WebSocketVersion.V13, null, true, new DefaultHttpHeaders(), getConfig().getMaxFramePayloadLength()),
DouyuWsLiveChatClient.this, clientConnectionListener
);
}
@Override
public DouyuBinaryFrameHandler initBinaryFrameHandler() {
return new DouyuBinaryFrameHandler(msgListeners, DouyuWsLiveChatClient.this);
}
@Override
public void onMsg(DouyuBinaryFrameHandler binaryFrameHandler, IMsg msg) {
if (msg instanceof LoginresMsg) {
send(getWebSocketFrameFactory(getConfig().getRoomId()).createKeeplive(getConfig().getCookie()));
}
}
@Override
public boolean removeMsgListener(IDouyuMsgListener msgListener) {
if (msgListener == DouyuWsLiveChatClient.this) {
return false;
}
return super.removeMsgListener(msgListener);
}
@Override
public void removeAllMsgListeners() {
super.removeAllMsgListeners();
addMsgListener(DouyuWsLiveChatClient.this);
}
}

View File

@@ -0,0 +1,139 @@
/*
* 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.douyu.client.base;
import cn.hutool.core.collection.CollUtil;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import tech.ordinaryroad.live.chat.client.douyu.config.DouyuLiveChatClientConfig;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuClientModeEnum;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.listener.IDouyuConnectionListener;
import tech.ordinaryroad.live.chat.client.douyu.listener.IDouyuMsgListener;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.IDouyuMsg;
import tech.ordinaryroad.live.chat.client.douyu.netty.frame.factory.DouyuWebSocketFrameFactory;
import tech.ordinaryroad.live.chat.client.douyu.netty.handler.DouyuBinaryFrameHandler;
import tech.ordinaryroad.live.chat.client.douyu.netty.handler.DouyuConnectionHandler;
import tech.ordinaryroad.live.chat.client.servers.netty.client.base.BaseNettyClient;
import java.util.List;
import java.util.function.Consumer;
/**
* @author mjz
* @date 2023/9/15
*/
@Slf4j
public abstract class BaseDouyuLiveChatClient extends BaseNettyClient<
DouyuLiveChatClientConfig,
DouyuCmdEnum,
IDouyuMsg,
IDouyuMsgListener,
DouyuConnectionHandler,
DouyuBinaryFrameHandler
> {
private final DouyuClientModeEnum mode;
public BaseDouyuLiveChatClient(DouyuClientModeEnum mode, DouyuLiveChatClientConfig config, List<IDouyuMsgListener> msgListeners, IDouyuConnectionListener connectionListener, EventLoopGroup workerGroup) {
super(config, workerGroup, connectionListener);
this.mode = mode;
addMsgListeners(msgListeners);
// 初始化
this.init();
}
public BaseDouyuLiveChatClient(DouyuClientModeEnum mode, DouyuLiveChatClientConfig config, IDouyuMsgListener msgListener, IDouyuConnectionListener connectionListener, EventLoopGroup workerGroup) {
super(config, workerGroup, connectionListener);
this.mode = mode;
addMsgListener(msgListener);
// 初始化
this.init();
}
public BaseDouyuLiveChatClient(DouyuClientModeEnum mode, DouyuLiveChatClientConfig config, IDouyuMsgListener msgListener, IDouyuConnectionListener connectionListener) {
this(mode, config, msgListener, connectionListener, new NioEventLoopGroup());
}
public BaseDouyuLiveChatClient(DouyuClientModeEnum mode, DouyuLiveChatClientConfig config, IDouyuMsgListener msgListener) {
this(mode, config, msgListener, null, new NioEventLoopGroup());
}
public BaseDouyuLiveChatClient(DouyuClientModeEnum mode, DouyuLiveChatClientConfig config) {
this(mode, config, CollUtil.newArrayList(), null, new NioEventLoopGroup());
}
@Override
public void sendDanmu(Object danmu, Runnable success, Consumer<Throwable> failed) {
if (!checkCanSendDanmu()) {
return;
}
if (mode == DouyuClientModeEnum.WS && danmu instanceof String) {
String msg = (String) danmu;
if (log.isDebugEnabled()) {
log.debug("{} douyu发送弹幕 {}", getConfig().getRoomId(), danmu);
}
WebSocketFrame webSocketFrame = null;
try {
webSocketFrame = getWebSocketFrameFactory(getConfig().getRoomId()).createDanmu(msg, getConfig().getCookie());
} catch (Exception e) {
log.error("douyu弹幕包创建失败", e);
if (failed != null) {
failed.accept(e);
}
}
if (webSocketFrame == null) {
return;
}
send(webSocketFrame, () -> {
if (log.isDebugEnabled()) {
log.debug("douyu弹幕发送成功 {}", danmu);
}
if (success != null) {
success.run();
}
finishSendDanmu();
}, throwable -> {
log.error("douyu弹幕发送失败", throwable);
if (failed != null) {
failed.accept(throwable);
}
});
} else {
super.sendDanmu(danmu);
}
}
protected static DouyuWebSocketFrameFactory getWebSocketFrameFactory(long roomId) {
return DouyuWebSocketFrameFactory.getInstance(roomId);
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.douyu.config;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import tech.ordinaryroad.live.chat.client.commons.util.OrLiveChatNumberUtil;
import tech.ordinaryroad.live.chat.client.servers.netty.client.config.BaseNettyClientConfig;
/**
* Douyu直播间弹幕客户端配置
*
* @author mjz
* @date 2023/8/21
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder(toBuilder = true)
public class DouyuLiveChatClientConfig extends BaseNettyClientConfig {
@Builder.Default
private String ver = "20220825";
@Builder.Default
private String aver = "218101901";
@Override
public Long getRoomId() {
return OrLiveChatNumberUtil.parseLong(super.getRoomId());
}
public void setVer(String ver) {
String oldValue = this.ver;
this.ver = ver;
super.propertyChangeSupport.firePropertyChange("ver", oldValue, ver);
}
public void setAver(String aver) {
String oldValue = this.aver;
this.aver = aver;
super.propertyChangeSupport.firePropertyChange("aver", oldValue, aver);
}
}

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.douyu.constant;
/**
* @author mjz
* @date 2023/9/15
*/
public enum DouyuClientModeEnum {
/**
* 发送弹幕获取可用的Danmu服务器链接
*/
WS,
/**
* 接收直播间弹幕
*/
DANMU,
}

View File

@@ -0,0 +1,199 @@
/*
* 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.douyu.constant;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import tech.ordinaryroad.live.chat.client.douyu.msg.*;
/**
* <a href='https://open.douyu.com/source/api/63'>文档</a>
*
* @author mjz
* @date 2023/1/6
*/
@Getter
@AllArgsConstructor
@RequiredArgsConstructor
public enum DouyuCmdEnum {
/**
* 登录请求消息
* <pre>{@code
* type@=loginreq/roomid@=7750753/dfl@=/username@=visitor10424697/uid@=1168052601/ver@=20220825/aver@=218101901/ct@=0/
* type@=loginreq/roomid@=58839/aid@=yihanTest/token@=4c8421535f9639d8c1ad35d1fa421f36/time@=1574850339/auth@=45619bb990e6b76db06a66d5a8a446d7/
* }</pre>
*/
loginreq(LoginreqMsg.class),
/**
* 登录响应消息
* <pre>{@code
* type@=loginres/userid@=1168052601/roomgroup@=0/pg@=0/sessionid@=0/username@=/nickname@=/live_stat@=0/is_illegal@=0/ill_ct@=/ill_ts@=0/now@=0/ps@=0/es@=0/it@=0/its@=0/npv@=0/best_dlev@=0/cur_lev@=0/nrc@=964715377/ih@=0/sid@=76494/sahf@=0/sceneid@=0/newrg@=0/regts@=0/ip@=112.43.93.229/rn@=0/rct@=0/
* type@=loginresp/msg@=ok/rid@=77614265/
* }</pre>
*/
loginres(LoginresMsg.class),
/**
* 入组
* type@=joingroup/rid@=59872/aid@=yourapplicaitonID/token@=4c8421535f9639d8c1ad35d1fa421f36/time@=1574850339/auth@=xxxxxxxxxxxx/
* Auth 生成方式为 md5({secret}_{aid}_{time}_{token}), secret为aid对应的秘钥
*/
joingroup(JoingroupMsg.class),
/**
* 客户端旧版心跳、心跳回复
* <pre>{@code
* type@=keeplive/tick@=1439802131/
* }</pre>
*/
keeplive(KeepliveMsg.class),
/**
* 客户端新版心跳、心跳回复
* <pre>{@code
* type@=mrkl/
* }</pre>
*/
mrkl,
/**
* 订阅贵族排行变动
* type@=sub/mt@=online_vip_list/
*/
sub(SubMsg.class),
subres,
noble_num_info,
oni,
postLogin,
/**
* 弹幕
* type@=chatmsg/rid@=7750753/uid@=549977/nn@=一闪一闪亮品品/txt@=冷门歌手哎,声音独特哎/cid@=044c3bf3074b483275a44e0000000000/ic@=avatar@Sdefault@S08/level@=29/sahf@=0/nl@=7/cst@=1693107871294/bnn@=/bl@=0/brid@=0/hc@=/lk@=/dms@=8/pdg@=26/pdk@=86/ext@=/
* type@=chatmsg/rid@=58839/ct@=8/hashid@=9LA18ePx4dqW/nn@=test/txt@=666/cid@=1111/ic@=icon/sahf@=0/level@=1/nl@=0/nc@=0/cmt@=0/gt@=0/col@=0/rg@=0/pg@=0/dlv@=0/dc@=0/bdlv@=0/gatin@=0/ chtin@=0/repin@=0/bnn@=test/bl@=0/brid@=58839/hc@=0/ol@=0/rev@=0/hl@=0/ifs@=0/p2p@=0/el@=eid@AA=1@ASetp@AA=1@ASsc@AA=1@AS/
*/
chatmsg(ChatmsgMsg.class),
pingreq,
/**
* 登出
* type@=logout/
*/
logout,
/**
* 登录响应
* type@=loginresp/msg@=ok/rid@=77614265/
*/
loginresp,
/**
* 礼物
* type@=dgb/gfid@=1/gs@=59872/gfcnt@=1/hashid@=1/rid@=1/nn@=someone/level@=1/dw@=1/
*/
dgb(DgbMsg.class),
/**
* 用户进房通知消息
* type@=uenter/rid@=1/ uid@=1/nn@=someone/str@=1/level@=1/el@=eid@AA=1@ASetp@AA=1@ASsc@AA=1@AS@S/
*/
uenter(UenterMsg.class),
/**
* 房间开关播提醒
* type@=rss/rid@=1/ss@=1/code@=1/rt@=0/notify@=1/endtime@=1/
*/
rss,
/**
* 超级弹幕消息
* type@=ssd/rid@=1/trid@=1/content@=test/cli tp@=1/url@=test_url/jmptp@=1/
*/
ssd,
/**
* 房间内礼物广播
* type@=spbc/rid@=1/gfid@=1/sn@=name/dn@=name/gn@=1/gc@=1/gb@=1/es@=1/ eid@=1/
*/
spbc,
/**
* 房间宝箱消息
* type@=tsgs/rid@=1/gid@=1/gfid@=1/sn@=name/dn@=name/gn@=1/gc@=1/gb@=1/es@=1/gfid@=1/eid@=1/
*/
tsgs,
/**
* 房间内 top10 变化消息
* type@=rankup/uid@=1/rn@=3/rid@=1/rkt@=1/gid@=-9999/rt@=0/ nk@=test/sz@=3/drid@=1/bt@=1/
*/
rankup,
/**
* 主播离开提醒
* type@=al/rid@=10111/aid@=3044114/
*/
al,
/**
* 主播回来继续直播提醒
* type@=ab/rid@=10111/gid@=-9999/aid@=3044114/
*/
ab,
/**
* 用户等级提升消息
* type@=upgrade/rid@=1/gid@=-9999/uid@=12001/nn@=test/level@=3/ic@=icon/
*/
upgrade,
/**
* 主播等级提升广播
* type@=upbc/rid@=1/gid@=-9999/lev@=20/pu@=0/
*/
upbc,
/**
* 禁言操作结果
* type@=newblackres/rid@=1/gid@=-9999/ret@=0/otype@=2/sid@=10002/did@=10003/snic@=stest/dnic@=dtest/endtime@=1501920157/
*/
newblackres,
/**
* 徽章等级提升通知
* type@=blab/rid@=1/gid@=-9999/uid@=10002/nn@=test/lbl@=2/bl@=3/ba@=1/bnn@=ttt/
*/
blab,
/**
* 用户分享了直播间通知
* type@=srres/rid@=1/gid@=-9999/uid@=12001/nickname@=test/exp@=3/
*/
srres,
/**
* 栏目排行榜变更通知
* type@=rri/rid@=1/rn@=cate_rank/cate_id@=5/uid@=10005/sc@=100 00/idx@=10/bcr@=1/ibc@=1/an@=test/rktype@=1/tag_id@=1200/gif t_id@=100/
*/
rri,
mapkb(MapkbMsg.class),
/**
* 发送弹幕
*/
chatmessage(ChatmessageMsg.class),
h5ckreq(H5ckreqMsg.class),
h5gkcreq(H5gkcreqMsg.class),
h5cs(H5csMsg.class),
msgrepeaterproxylist(MsgrepeaterproxylistMsg.class),
;
private Class<?> tClass;
public static DouyuCmdEnum getByString(String cmd) {
try {
return DouyuCmdEnum.valueOf(cmd);
} catch (Exception e) {
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.douyu.listener;
import tech.ordinaryroad.live.chat.client.commons.base.listener.IBaseConnectionListener;
import tech.ordinaryroad.live.chat.client.douyu.netty.handler.DouyuConnectionHandler;
/**
* 连接回调
*
* @author mjz
* @date 2023/8/21
*/
public interface IDouyuConnectionListener extends IBaseConnectionListener<DouyuConnectionHandler> {
}

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.
*/
package tech.ordinaryroad.live.chat.client.douyu.listener;
import tech.ordinaryroad.live.chat.client.commons.base.listener.IBaseMsgListener;
import tech.ordinaryroad.live.chat.client.commons.base.listener.IDanmuMsgListener;
import tech.ordinaryroad.live.chat.client.commons.base.listener.IEnterRoomMsgListener;
import tech.ordinaryroad.live.chat.client.commons.base.listener.IGiftMsgListener;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.msg.ChatmsgMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.DgbMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.UenterMsg;
import tech.ordinaryroad.live.chat.client.douyu.netty.handler.DouyuBinaryFrameHandler;
/**
* @author mjz
* @date 2023/1/7
*/
public interface IDouyuMsgListener extends IBaseMsgListener<DouyuBinaryFrameHandler, DouyuCmdEnum>,
IDanmuMsgListener<DouyuBinaryFrameHandler, ChatmsgMsg>,
IGiftMsgListener<DouyuBinaryFrameHandler, DgbMsg>,
IEnterRoomMsgListener<DouyuBinaryFrameHandler, UenterMsg> {
}

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.douyu.msg;
import cn.hutool.core.util.RandomUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.commons.util.OrLocalDateTimeUtil;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
/**
* @author mjz
* @date 2023/9/7
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class ChatmessageMsg extends BaseDouyuCmdMsg {
private String pe = "0";
/**
* 弹幕内容
*/
private String content;
private String col = "0";
/**
* Cookie中的dy_did
*/
private String dy;
/**
* Cookie中的acf_uid
*/
private String sender;
private String ifs = "0";
private String nc = "0";
private String dat = "0";
private String rev = "0";
/**
* 当前时间戳(秒)
*/
private String tts;
private String admzq = "0";
/**
* tts往后加几秒的时间戳毫秒
*/
private String cst;
public ChatmessageMsg(String content, String dy, String sender) {
this.content = content;
this.dy = dy;
this.sender = sender;
this.tts = String.valueOf(OrLocalDateTimeUtil.zonedCurrentTimeSecs());
this.cst = String.valueOf(OrLocalDateTimeUtil.zonedCurrentTimeMillis() + RandomUtil.randomLong(8000, 10000));
}
@Override
public String getType() {
return DouyuCmdEnum.chatmessage.name();
}
}

View File

@@ -0,0 +1,125 @@
/*
* 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.douyu.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.commons.base.msg.IDanmuMsg;
import tech.ordinaryroad.live.chat.client.douyu.api.DouyuApis;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
import java.util.List;
/**
* <pre>{@code
* {
* "type": "chatmsg",
* "cmdEnum": "chatmsg",
* "cmd": "chatmsg",
* "nn": "宋老二929",
* "ext": null,
* "bnn": null,
* "level": "1",
* "cst": "1693213418102",
* "brid": "0",
* "bl": "0",
* "dms": "5",
* "rid": "3168536",
* "uid": "396023456",
* "txt": "666",
* "pdg": "47",
* "pdk": "89",
* "sahf": "0",
* "ic": ["avatar_v3", "202101", "45daf5ceb475414293e3da4559552655"],
* "hb": ["2719"],
* "hc": null,
* "cid": "0b37e26cccd54f7c4d73590000000000",
* "lk": null
* }
* }</pre>
*
* @author mjz
* @date 2023/8/28
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class ChatmsgMsg extends BaseDouyuCmdMsg implements IDanmuMsg {
private String nn;
private String ext;
private String bnn;
private String level;
private String cst;
private String brid;
private byte bl;
private String dms;
private long rid;
private String uid;
private String txt;
private String pdg;
private String pdk;
private String sahf;
private List<String> ic;
private List<String> hb;
private String hc;
private String cid;
private JsonNode lk;
@Override
public String getType() {
return DouyuCmdEnum.chatmsg.name();
}
@Override
public String getBadgeName() {
return this.bnn;
}
@Override
public byte getBadgeLevel() {
return this.bl;
}
@Override
public String getUsername() {
return this.nn;
}
@Override
public String getUserAvatar() {
return DouyuApis.getSmallAvatarUrl(ic);
}
@Override
public String getContent() {
return this.txt;
}
}

View File

@@ -0,0 +1,233 @@
/*
* 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.douyu.msg;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.commons.base.msg.IGiftMsg;
import tech.ordinaryroad.live.chat.client.douyu.api.DouyuApis;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.dto.GiftListInfo;
import tech.ordinaryroad.live.chat.client.douyu.msg.dto.GiftPropSingle;
import java.util.List;
/**
* 收到礼物消息
* <pre>{@code
* {
* "type": "dgb",
* "nn": "用户名",
* "eid": "0",
* "bst": "32",
* "brid": "0",
* "pma": "186963537",
* "bnid": "1",
* "gfid": "824",
* "bl": "0",
* "pid": "268",
* "rid": "290935",
* "mss": "186963457",
* "bcst": "2",
* "uid": "用户id",
* "receive_uid": "接受用户id",
* "ic": ["avatar_v3", "201912", "d031765fbe764a49887083bcf200da0b"],
* "from": "2",
* "gpf": "1",
* "bnl": "1",
* "ce": "1",
* "bnn": null,
* "receive_nn": "接受用户用户名",
* "level": "43",
* "bcnt": "1",
* "gs": "0",
* "hits": "249",
* "gfcnt": "249",
* "ct": "0",
* "pfm": "27585",
* "sahf": "0",
* "hc": null,
* "fc": "0",
* "eic": "0"
* }
* }</pre>
*
* @author mjz
* @date 2023/9/8
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class DgbMsg extends BaseDouyuCmdMsg implements IGiftMsg {
/**
* 用户名
*/
private String nn;
private String eid;
private String bst;
private String brid;
private String pma;
private String bnid;
private long gfid;
private byte bl;
private String pid;
private String rid;
private String mss;
private String bcst;
/**
* 用户id
*/
private String uid;
/**
* 收礼物用户id
*/
private long receive_uid;
private List<String> ic;
private String from;
private String gpf;
private String bnl;
private String ce;
private String bnn;
/**
* 收礼物用户名
*/
private String receive_nn;
private String level;
private String bcnt;
private String gs;
/**
* 连击
*/
private String hits;
/**
* 礼物个数
*/
private int gfcnt;
private String ct;
private String pfm;
private String sahf;
private String hc;
private String fc;
private String eic;
private String bsfl;
private String skinid;
// region 额外属性
private GiftPropSingle giftInfo = GiftPropSingle.DEFAULT_GIFT;
private GiftListInfo roomGiftInfo = GiftListInfo.DEFAULT_GIFT;
// endregion
@Override
public String getType() {
return DouyuCmdEnum.dgb.name();
}
@Override
public String getBadgeName() {
return this.bnn;
}
@Override
public byte getBadgeLevel() {
return this.bl;
}
@Override
public String getUsername() {
return this.nn;
}
@Override
public String getUserAvatar() {
return DouyuApis.getSmallAvatarUrl(ic);
}
@Override
public String getGiftName() {
if (this.roomGiftInfo != null && this.roomGiftInfo != GiftListInfo.DEFAULT_GIFT) {
return this.roomGiftInfo.getName();
}
if (this.giftInfo == null) {
return "未知礼物";
}
return this.giftInfo.getName();
}
@Override
public String getGiftImg() {
if (this.roomGiftInfo != null && this.roomGiftInfo != GiftListInfo.DEFAULT_GIFT && this.roomGiftInfo.getBasicInfo() != null) {
return this.roomGiftInfo.getPicUrlPrefix() + this.roomGiftInfo.getBasicInfo().getChatPic();
}
if (this.giftInfo == null) {
return "";
}
return this.giftInfo.getPicUrlPrefix() + this.giftInfo.getChatPic();
}
@Override
public String getGiftId() {
return Long.toString(this.gfid);
}
@Override
public int getGiftCount() {
return this.gfcnt;
}
/**
* 100 => 1鱼翅
*/
@Override
public int getGiftPrice() {
if (this.roomGiftInfo != null && this.roomGiftInfo != GiftListInfo.DEFAULT_GIFT && this.roomGiftInfo.getPriceInfo() != null) {
return this.roomGiftInfo.getPriceInfo().getPrice();
}
if (this.giftInfo == null) {
return -1;
}
return this.giftInfo.getPrice();
}
@Override
public String getReceiveUid() {
return Long.toString(this.receive_uid);
}
@Override
public String getReceiveUsername() {
return this.receive_nn;
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.douyu.msg;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
/**
* type@=chatmsg/rid@=7750753/uid@=549977/nn@=一闪一闪亮品品/txt@=冷门歌手哎,声音独特哎/cid@=044c3bf3074b483275a44e0000000000/ic@=avatar@Sdefault@S08/level@=29/sahf@=0/nl@=7/cst@=1693107871294/bnn@=/bl@=0/brid@=0/hc@=/lk@=/dms@=8/pdg@=26/pdk@=86/ext@=/
*
* @see tech.ordinaryroad.live.chat.client.douyu.util.DouyuCodecUtil#getDouyuMsgClassByType(DouyuCmdEnum, short)
* @author mjz
* @date 2023/1/6
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class DouyuCmdMsg extends BaseDouyuCmdMsg {
private String tick;
private String res;
}

View File

@@ -0,0 +1,51 @@
/*
* 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.douyu.msg;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
/**
* @author mjz
* @date 2023/9/8
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class H5ckreqMsg extends BaseDouyuCmdMsg {
private String rid;
private String ti;
@Override
public String getType() {
return DouyuCmdEnum.h5ckreq.name();
}
}

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.douyu.msg;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
/**
* @author mjz
* @date 2023/9/8
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class H5csMsg extends BaseDouyuCmdMsg {
private String result;
@Override
public String getType() {
return DouyuCmdEnum.h5cs.name();
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.douyu.msg;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
/**
* @author mjz
* @date 2023/9/8
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class H5gkcreqMsg extends BaseDouyuCmdMsg {
private String rid;
private String ti;
@Override
public String getType() {
return DouyuCmdEnum.h5gkcreq.name();
}
}

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.
*/
package tech.ordinaryroad.live.chat.client.douyu.msg;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
/**
* @author mjz
* @date 2023/1/6
*/
public class HeartbeatMsg extends BaseDouyuCmdMsg {
@Override
public String getType() {
return DouyuCmdEnum.mrkl.name();
}
}

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.
*/
package tech.ordinaryroad.live.chat.client.douyu.msg;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
/**
* @author mjz
* @date 2023/1/6
*/
public class HeartbeatReplyMsg extends BaseDouyuCmdMsg {
@Override
public String getType() {
return DouyuCmdEnum.mrkl.name();
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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.douyu.msg;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
/**
* @author mjz
* @date 2023/8/27
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class JoingroupMsg extends BaseDouyuCmdMsg {
private long rid;
private long gid = -9999;
@Override
public String getType() {
return DouyuCmdEnum.joingroup.name();
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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.douyu.msg;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.commons.util.OrLocalDateTimeUtil;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
/**
* <pre>{@code
* type@=keeplive/vbw@=0/cdn@=hs-h5/tick@=1694099389/kd@=da9c384371b4552ac94e1237d2596262/
* }</pre>
*
* @author mjz
* @date 2023/9/7
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class KeepliveMsg extends BaseDouyuCmdMsg {
private String vbw = "0";
private String cnd;
private long tick = OrLocalDateTimeUtil.zonedCurrentTimeSecs();
private String kd = "";
public KeepliveMsg(String cnd) {
this.cnd = cnd;
}
@Override
public String getType() {
return DouyuCmdEnum.keeplive.name();
}
}

View File

@@ -0,0 +1,107 @@
/*
* 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.douyu.msg;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.commons.util.OrLocalDateTimeUtil;
import tech.ordinaryroad.live.chat.client.douyu.api.DouyuApis;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
import java.util.Arrays;
import java.util.List;
/**
* @author mjz
* @date 2023/8/27
*/
@Getter
@Setter
@RequiredArgsConstructor
public class LoginreqMsg extends BaseDouyuCmdMsg {
public static List<String> SHOULD_IGNORE_NEW_LOGIN_PROPERTIES = Arrays.asList("roomid", "dfl", "username", "uid", "ver", "aver", "ct", "type");
public static List<String> SHOULD_IGNORE_OLD_LOGIN_PROPERTIES = Arrays.asList("type", "roomid", "dfl", "username", "password", "ltkid", "biz", "stk", "devid", "ct", "pt", "cvr", "tvr", "apd", "rt", "vk", "ver", "aver", "dmbt", "dmbv");
private long roomid;
private String dfl;
private String username;
private long uid;
private String ver;
private String aver;
private int ct = 0;
private String password;
/**
* Cookie中的acf_ltkid
*/
private String ltkid;
private String biz;
/**
* Cookie中的acf_stk
*/
private String stk;
/**
* Cookie中的dy_did
*/
private String devid;
private String pt = "2";
private String cvr = "0";
private String tvr = "7";
private String apd = "";
private long rt = OrLocalDateTimeUtil.zonedCurrentTimeSecs();
private String vk;
private String dmbt = "chrome";
private String dmbv = "116";
public LoginreqMsg(long roomid, String dfl, String username, long uid, String ver, String aver) {
this.roomid = roomid;
this.dfl = dfl;
this.username = username;
this.uid = uid;
this.ver = ver;
this.aver = aver;
}
public LoginreqMsg(long roomid, String dfl, String username, String ver, String aver, String ltkid, String biz, String stk, String devid) {
this.roomid = roomid;
this.dfl = dfl;
this.username = username;
this.ver = ver;
this.aver = aver;
this.ltkid = ltkid;
this.biz = biz;
this.stk = stk;
this.devid = devid;
this.vk = DouyuApis.generateVk(devid);
}
@Override
public String getType() {
return DouyuCmdEnum.loginreq.name();
}
}

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.douyu.msg;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
/**
* @author mjz
* @date 2023/8/27
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class LoginresMsg extends BaseDouyuCmdMsg {
private long userid;
private int roomgroup;
private int pg;
private long sessionid;
private String username;
private String nickname;
private int live_stat;
private boolean is_illegal;
private String ill_ct;
private String ill_ts;
private String now;
private String ps;
private String es;
private String it;
private String its;
private String npv;
private String best_dlev;
private String cur_lev;
private String nrc;
private String ih;
private String sid;
private String sahf;
private String sceneid;
private String newrg;
private String regts;
private String ip;
private String rn;
private String rct;
@Override
public String getType() {
return DouyuCmdEnum.loginres.name();
}
}

View File

@@ -0,0 +1,53 @@
/*
* 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.douyu.msg;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
import java.util.List;
/**
* @author mjz
* @date 2023/8/28
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class MapkbMsg extends BaseDouyuCmdMsg {
private long pk_time;
private List<Object> teams;
@Override
public String getType() {
return DouyuCmdEnum.mapkb.name();
}
}

View File

@@ -0,0 +1,96 @@
/*
* 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.douyu.msg;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
import java.util.List;
import java.util.Map;
/**
* @author mjz
* @date 2023/9/8
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class MsgrepeaterproxylistMsg extends BaseDouyuCmdMsg {
private String rid;
/**
* <pre>{@code
* [{
* "nr": "0",
* "port": "8501",
* "ip": "danmuproxy.douyu.com",
* "id": "0",
* "ml": "10000"
* }, {
* "nr": "0",
* "port": "8502",
* "ip": "danmuproxy.douyu.com",
* "id": "0",
* "ml": "10000"
* }, {
* "nr": "0",
* "port": "8503",
* "ip": "danmuproxy.douyu.com",
* "id": "0",
* "ml": "10000"
* }, {
* "nr": "0",
* "port": "8504",
* "ip": "danmuproxy.douyu.com",
* "id": "0",
* "ml": "10000"
* }, {
* "nr": "0",
* "port": "8505",
* "ip": "danmuproxy.douyu.com",
* "id": "0",
* "ml": "10000"
* }, {
* "nr": "0",
* "port": "8506",
* "ip": "danmuproxy.douyu.com",
* "id": "0",
* "ml": "10000"
* }]
* }</pre>
*/
private List<Map<String, String>> list;
@Override
public String getType() {
return DouyuCmdEnum.msgrepeaterproxylist.name();
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.douyu.msg;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
/**
* @author mjz
* @date 2023/8/27
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class SubMsg extends BaseDouyuCmdMsg {
private String mt = "dayrk";
@Override
public String getType() {
return DouyuCmdEnum.sub.name();
}
}

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.
*/
package tech.ordinaryroad.live.chat.client.douyu.msg;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.commons.base.msg.IEnterRoomMsg;
import tech.ordinaryroad.live.chat.client.douyu.api.DouyuApis;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
import java.util.List;
/**
* @author mjz
* @date 2023/12/27
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class UenterMsg extends BaseDouyuCmdMsg implements IEnterRoomMsg {
private String nn;
private long uid;
private int level;
private String sahf;
private List<String> ic;
private String rid;
@Override
public String getBadgeName() {
return null;
}
@Override
public byte getBadgeLevel() {
return 0;
}
@Override
public String getUid() {
return Long.toString(uid);
}
@Override
public String getUsername() {
return nn;
}
@Override
public String getUserAvatar() {
return DouyuApis.getSmallAvatarUrl(ic);
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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.douyu.msg.base;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import tech.ordinaryroad.live.chat.client.commons.base.msg.BaseCmdMsg;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
/**
* @author mjz
* @date 2023/1/6
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public abstract class BaseDouyuCmdMsg extends BaseCmdMsg<DouyuCmdEnum> implements IDouyuMsg {
private String type;
@Override
public String getCmd() {
return type;
}
@Override
public void setCmd(String cmd) {
type = cmd;
}
@Override
public DouyuCmdEnum getCmdEnum() {
return DouyuCmdEnum.getByString(type);
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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.douyu.msg.base;
import tech.ordinaryroad.live.chat.client.commons.base.msg.IMsg;
/**
* @author mjz
* @date 2023/8/26
*/
public interface IDouyuMsg extends IMsg {
}

View File

@@ -0,0 +1,136 @@
/*
* 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.douyu.msg.dto;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class GiftListInfo {
public static final GiftListInfo DEFAULT_GIFT = new GiftListInfo();
private BasicInfo basicInfo;
private JsonNode batchInfo;
private int batchInfoFlag;
private int countLimit;
private int defaultSkinId;
private int donateStatus;
private JsonNode effectInfo;
private int effectStatus;
private int endTime;
private JsonNode growthInfo;
private long hitInterval;
private int id;
private int isBatchLimited;
private int isFace;
private String name = "未知礼物";
private String picUrlPrefix;
private PriceInfo priceInfo = new PriceInfo();
private int showStatus;
private List<String> skinIds;
private int startTime;
private List<Integer> tabIds;
/**
* 未知属性都放在这
*/
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);
}
@Data
public static class BasicInfo {
private String bizTag;
private String borderColor;
private String chatPic;
private String culture;
private String desc1;
private String desc2;
private String focusPic;
private String giftPic;
private int giftType;
private int guardLevel;
private String sendPic;
private String subscriptColor;
private String subscriptText;
private String webSubscriptBigPic;
private String webSubscriptSmallPic;
private String webSubscriptText;
/**
* 未知属性都放在这
*/
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);
}
}
@Data
public static class PriceInfo {
private int price = -1;
private String priceType;
private int returnNum;
/**
* 未知属性都放在这
*/
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,99 @@
/*
* 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.douyu.msg.dto;
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 java.util.HashMap;
import java.util.List;
import java.util.Map;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class GiftPropInfo {
public static final GiftPropInfo DEFAULT_GIFT = new GiftPropInfo();
private String name = "未知礼物";
private String bimg;
private String himg;
private String cimg;
private int stay_time;
private int show_combo;
private String batch_preset;
private List<String> hit;
private int type;
private int style;
private int pc;
private int ry;
private int ef;
private int effect;
private int is_syn_fs;
private int is_stay;
private String drgb;
private String urgb;
private String grgb;
private String brgb;
private String small_effect_icon;
private String big_effect_icon;
private String effect_icon;
private String gift_icon;
private String gift_open_icon;
private int bonus;
private String web_flash;
private String hit_interval;
private int devote;
private int attack;
private int exp;
private int active_type;
private String pc_full_icon;
private String pc_full_bg_icon;
private int pc_show_combo;
private String pc_urgb;
private String pc_grgb;
private int index;
/**
* 未知属性都放在这
*/
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,97 @@
/*
* 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.douyu.msg.dto;
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 java.util.HashMap;
import java.util.Map;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class GiftPropSingle {
public static final GiftPropSingle DEFAULT_GIFT = new GiftPropSingle();
// key,value
private JsonNode batchInfo;
private String bizTag;
private String borderColor;
private String chatPic;
private int count;
private String description;
private String description2;
private int devote;
// key,value
private JsonNode effectInfo;
private int exp;
private int expiry;
private String focusPic;
private int hitInterval;
private long id;
private int intimate;
private String intro;
private int isClick;
private int isFace;
private int isValuable;
private int level;
private int levelTime;
private int met;
private String name = "未知礼物";
private String picUrlPrefix;
private int price = -1;
private int priceType;
private String propPic;
private int propType;
private int returnNum;
private String sendPic;
private String subscriptColor;
private String subscriptText;
private String webSubscriptBigPic;
private String webSubscriptSmallPic;
private String webSubscriptText;
/**
* 未知属性都放在这
*/
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.douyu.netty.frame;
import io.netty.buffer.ByteBuf;
import tech.ordinaryroad.live.chat.client.douyu.netty.frame.base.BaseDouyuWebSocketFrame;
/**
* @author mjz
* @date 2023/1/5
*/
public class AuthWebSocketFrame extends BaseDouyuWebSocketFrame {
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.douyu.netty.frame;
import io.netty.buffer.ByteBuf;
import tech.ordinaryroad.live.chat.client.douyu.netty.frame.base.BaseDouyuWebSocketFrame;
/**
* @author mjz
* @date 2023/1/5
*/
public class HeartbeatWebSocketFrame extends BaseDouyuWebSocketFrame {
public HeartbeatWebSocketFrame(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.douyu.netty.frame;
import io.netty.buffer.ByteBuf;
import tech.ordinaryroad.live.chat.client.douyu.netty.frame.base.BaseDouyuWebSocketFrame;
/**
* @author mjz
* @date 2023/9/8
*/
public class KeepliveWebSocketFrame extends BaseDouyuWebSocketFrame {
public KeepliveWebSocketFrame(ByteBuf byteBuf) {
super(byteBuf);
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.douyu.netty.frame.base;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
/**
* @author mjz
* @date 2023/8/27
*/
public abstract class BaseDouyuWebSocketFrame extends BinaryWebSocketFrame {
public BaseDouyuWebSocketFrame(ByteBuf byteBuf) {
super(byteBuf);
}
}

View File

@@ -0,0 +1,181 @@
/*
* 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.douyu.netty.frame.factory;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import tech.ordinaryroad.live.chat.client.commons.base.exception.BaseException;
import tech.ordinaryroad.live.chat.client.commons.util.OrLiveChatCookieUtil;
import tech.ordinaryroad.live.chat.client.douyu.api.DouyuApis;
import tech.ordinaryroad.live.chat.client.douyu.config.DouyuLiveChatClientConfig;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuClientModeEnum;
import tech.ordinaryroad.live.chat.client.douyu.msg.*;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
import tech.ordinaryroad.live.chat.client.douyu.netty.frame.AuthWebSocketFrame;
import tech.ordinaryroad.live.chat.client.douyu.netty.frame.HeartbeatWebSocketFrame;
import tech.ordinaryroad.live.chat.client.douyu.netty.frame.KeepliveWebSocketFrame;
import tech.ordinaryroad.live.chat.client.douyu.util.DouyuCodecUtil;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author mjz
* @date 2023/1/5
*/
public class DouyuWebSocketFrameFactory {
private static final ConcurrentHashMap<Long, DouyuWebSocketFrameFactory> CACHE = new ConcurrentHashMap<>();
/**
* 浏览器地址中的房间id暂不支持短id
*/
private final long roomId;
private volatile static HeartbeatMsg heartbeatMsg;
public DouyuWebSocketFrameFactory(long roomId) {
this.roomId = roomId;
}
public synchronized static DouyuWebSocketFrameFactory getInstance(long roomId) {
return CACHE.computeIfAbsent(roomId, aLong -> new DouyuWebSocketFrameFactory(roomId));
}
/**
* 创建认证包
*
* @param mode {@link DouyuClientModeEnum#DANMU}, {@link DouyuClientModeEnum#WS}
* @param ver {@link DouyuLiveChatClientConfig#getVer()}
* @param aver {@link DouyuLiveChatClientConfig#getAver()}
* @param cookie 浏览器Cookie发送弹幕时必传
* @return AuthWebSocketFrame
*/
public AuthWebSocketFrame createAuth(DouyuClientModeEnum mode, String ver, String aver, String cookie) {
try {
// type@=loginreq/roomid@=7750753/dfl@=/username@=visitor10424697/uid@=1168052601/ver@=20220825/aver@=218101901/ct@=0/
LoginreqMsg loginreqMsg;
long realRoomId = DouyuApis.getRealRoomId(roomId);
long uid;
String username;
Map<String, String> cookieMap = OrLiveChatCookieUtil.parseCookieString(cookie);
if (cookieMap.isEmpty()) {
// 视为未登录
if (mode == DouyuClientModeEnum.DANMU) {
uid = RandomUtil.randomLong(10000000, 19999999);
username = "visitor" + RandomUtil.randomLong(10000000, 19999999);
loginreqMsg = new LoginreqMsg(realRoomId, "", username, uid, ver, aver);
return new AuthWebSocketFrame(DouyuCodecUtil.encode(loginreqMsg, LoginreqMsg.SHOULD_IGNORE_NEW_LOGIN_PROPERTIES));
} else {
loginreqMsg = new LoginreqMsg(realRoomId, "", "", ver, aver, "", "", "", UUID.fastUUID().toString(true));
return new AuthWebSocketFrame(DouyuCodecUtil.encode(loginreqMsg, LoginreqMsg.SHOULD_IGNORE_OLD_LOGIN_PROPERTIES));
}
}
// 视为登录
else {
String acfUid = OrLiveChatCookieUtil.getCookieByName(cookieMap, DouyuApis.KEY_COOKIE_ACF_UID, () -> {
throw new BaseException("Cookie中缺少字段" + DouyuApis.KEY_COOKIE_ACF_UID);
});
uid = NumberUtil.parseLong(acfUid);
username = acfUid;
String dfl = "sn@A=105@Sss@A=1";
if (mode == DouyuClientModeEnum.DANMU) {
loginreqMsg = new LoginreqMsg(realRoomId, dfl, username, uid, ver, aver);
return new AuthWebSocketFrame(DouyuCodecUtil.encode(loginreqMsg, LoginreqMsg.SHOULD_IGNORE_NEW_LOGIN_PROPERTIES));
} else {
String acfLtkid = OrLiveChatCookieUtil.getCookieByName(cookieMap, DouyuApis.KEY_COOKIE_ACF_LTKID, () -> {
throw new BaseException("Cookie中缺少字段" + DouyuApis.KEY_COOKIE_ACF_LTKID);
});
String acfStk = OrLiveChatCookieUtil.getCookieByName(cookieMap, DouyuApis.KEY_COOKIE_ACF_STK, () -> {
throw new BaseException("Cookie中缺少字段" + DouyuApis.KEY_COOKIE_ACF_STK);
});
String dyDid = OrLiveChatCookieUtil.getCookieByName(cookieMap, DouyuApis.KEY_COOKIE_DY_DID, () -> {
throw new BaseException("Cookie中缺少字段" + DouyuApis.KEY_COOKIE_DY_DID);
});
loginreqMsg = new LoginreqMsg(realRoomId, dfl, username, ver, aver, acfLtkid, "1", acfStk, dyDid);
return new AuthWebSocketFrame(DouyuCodecUtil.encode(loginreqMsg, LoginreqMsg.SHOULD_IGNORE_OLD_LOGIN_PROPERTIES));
}
}
} catch (Exception e) {
throw new BaseException(String.format("认证包创建失败请检查房间号是否正确。roomId: %d, msg: %s", roomId, e.getMessage()));
}
}
public AuthWebSocketFrame createAuth(DouyuClientModeEnum mode, String ver, String aver) {
return this.createAuth(mode, ver, aver, null);
}
public HeartbeatWebSocketFrame createHeartbeat() {
return new HeartbeatWebSocketFrame(DouyuCodecUtil.encode(this.getHeartbeatMsg()));
}
public KeepliveWebSocketFrame createKeeplive(String cookie) {
return new KeepliveWebSocketFrame(DouyuCodecUtil.encode(this.getKeepliveMsg(StrUtil.isNotBlank(cookie) ? "hs-h5" : "")));
}
/**
* 心跳包单例模式
*
* @return HeartbeatWebSocketFrame
*/
public HeartbeatMsg getHeartbeatMsg() {
if (heartbeatMsg == null) {
synchronized (DouyuWebSocketFrameFactory.this) {
if (heartbeatMsg == null) {
heartbeatMsg = new HeartbeatMsg();
}
}
}
return heartbeatMsg;
}
private BaseDouyuCmdMsg getKeepliveMsg(String cnd) {
return new KeepliveMsg(cnd);
}
public WebSocketFrame createJoingroup() {
JoingroupMsg joingroupMsg = new JoingroupMsg();
joingroupMsg.setRid(roomId);
return new BinaryWebSocketFrame(DouyuCodecUtil.encode(joingroupMsg));
}
public WebSocketFrame createSub() {
return new BinaryWebSocketFrame(DouyuCodecUtil.encode(new SubMsg()));
}
public WebSocketFrame createDanmu(String msg, String cookie) {
String dyDid = OrLiveChatCookieUtil.getCookieByName(cookie, DouyuApis.KEY_COOKIE_DY_DID, () -> {
throw new BaseException("cookie中缺少参数" + DouyuApis.KEY_COOKIE_DY_DID);
});
String acfUid = OrLiveChatCookieUtil.getCookieByName(cookie, DouyuApis.KEY_COOKIE_ACF_UID, () -> {
throw new BaseException("cookie中缺少参数" + DouyuApis.KEY_COOKIE_ACF_UID);
});
ChatmessageMsg chatmessageMsg = new ChatmessageMsg(msg, dyDid, acfUid);
return new BinaryWebSocketFrame(DouyuCodecUtil.encode(chatmessageMsg));
}
}

View File

@@ -0,0 +1,98 @@
/*
* 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.douyu.netty.handler;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import lombok.extern.slf4j.Slf4j;
import tech.ordinaryroad.live.chat.client.commons.base.msg.ICmdMsg;
import tech.ordinaryroad.live.chat.client.douyu.client.base.BaseDouyuLiveChatClient;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.listener.IDouyuMsgListener;
import tech.ordinaryroad.live.chat.client.douyu.msg.ChatmsgMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.DgbMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.DouyuCmdMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.UenterMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.IDouyuMsg;
import tech.ordinaryroad.live.chat.client.douyu.util.DouyuCodecUtil;
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 DouyuBinaryFrameHandler extends BaseNettyClientBinaryFrameHandler<BaseDouyuLiveChatClient, DouyuBinaryFrameHandler, DouyuCmdEnum, IDouyuMsg, IDouyuMsgListener> {
public DouyuBinaryFrameHandler(List<IDouyuMsgListener> msgListeners, BaseDouyuLiveChatClient client) {
super(msgListeners, client);
}
public DouyuBinaryFrameHandler(List<IDouyuMsgListener> msgListeners, long roomId) {
super(msgListeners, roomId);
}
@Override
public void onCmdMsg(DouyuCmdEnum cmd, ICmdMsg<DouyuCmdEnum> cmdMsg) {
if (super.msgListeners.isEmpty()) {
return;
}
switch (cmd) {
case chatmsg: {
iteratorMsgListeners(msgListener -> msgListener.onDanmuMsg(DouyuBinaryFrameHandler.this, (ChatmsgMsg) cmdMsg));
break;
}
case dgb: {
iteratorMsgListeners(msgListener -> msgListener.onGiftMsg(DouyuBinaryFrameHandler.this, (DgbMsg) cmdMsg));
break;
}
case uenter: {
iteratorMsgListeners(msgListener -> msgListener.onEnterRoomMsg(DouyuBinaryFrameHandler.this, (UenterMsg) cmdMsg));
break;
}
default: {
if (!(cmdMsg instanceof DouyuCmdMsg)) {
if (log.isDebugEnabled()) {
log.debug("非DouyuCmdMsg {}", cmdMsg.getClass());
}
return;
}
iteratorMsgListeners(msgListener -> msgListener.onOtherCmdMsg(DouyuBinaryFrameHandler.this, cmd, cmdMsg));
}
}
}
@Override
protected List<IDouyuMsg> decode(ByteBuf byteBuf) {
return DouyuCodecUtil.decode(byteBuf);
}
}

View File

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

View File

@@ -0,0 +1,329 @@
/*
* 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.douyu.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import lombok.extern.slf4j.Slf4j;
import tech.ordinaryroad.live.chat.client.commons.util.OrLiveChatReflectUtil;
import tech.ordinaryroad.live.chat.client.douyu.api.DouyuApis;
import tech.ordinaryroad.live.chat.client.douyu.client.DouyuLiveChatClient;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.msg.DgbMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.DouyuCmdMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.HeartbeatMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.HeartbeatReplyMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.IDouyuMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.dto.GiftListInfo;
import tech.ordinaryroad.live.chat.client.douyu.msg.dto.GiftPropSingle;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* 参考https://open.douyu.com/source/api/63
*
* @author mjz
* @date 2023/1/6
*/
@Slf4j
public class DouyuCodecUtil {
public static final String[] IGNORE_PROPERTIES = {"OBJECT_MAPPER", "unknownProperties"};
/**
* 以SHOULD_IGNORE开头的成员变量将不会序列化
*/
public static final String SHOULD_IGNORE_PROPERTIES_PREFIX = "SHOULD_IGNORE";
public static final short MSG_TYPE_SEND = 689;
public static final short MSG_TYPE_RECEIVE = 690;
public static final short FRAME_HEADER_LENGTH = 8;
public static ByteBuf encode(BaseDouyuCmdMsg msg, List<String> containProperties) {
ByteBuf out = Unpooled.buffer(FRAME_HEADER_LENGTH);
String bodyDouyuSttString = StrUtil.nullToEmpty(toDouyuSttString(msg, containProperties)) + SUFFIX;
byte[] bodyBytes = bodyDouyuSttString.getBytes(StandardCharsets.UTF_8);
int length = bodyBytes.length + FRAME_HEADER_LENGTH;
out.writeIntLE(length);
out.writeIntLE(length);
out.writeShortLE(MSG_TYPE_SEND);
out.writeByte(0);
out.writeByte(0);
out.writeBytes(bodyBytes);
return out;
}
public static ByteBuf encode(BaseDouyuCmdMsg msg) {
return encode(msg, null);
}
public static List<IDouyuMsg> decode(ByteBuf in) {
List<IDouyuMsg> msgList = new ArrayList<>();
Queue<ByteBuf> pendingByteBuf = new LinkedList<>();
do {
Optional<IDouyuMsg> msg = doDecode(in, pendingByteBuf);
msg.ifPresent(msgList::add);
in = pendingByteBuf.poll();
} while (in != null);
return msgList;
}
/**
* 执行解码操作
*
* @param in handler收到的一条消息
* @param pendingByteBuf 用于存放未读取完的ByteBuf
* @return Optional<IDouyuMsg> 何时为空值:不支持的{@link DouyuCmdEnum}{@link #parseDouyuSttString(String, short)}反序列化失败
*/
private static Optional<IDouyuMsg> doDecode(ByteBuf in, Queue<ByteBuf> pendingByteBuf) {
int length = in.readIntLE();
in.readIntLE();
// MSG_TYPE_RECEIVE
short msgType = in.readShortLE();
if (msgType != MSG_TYPE_RECEIVE) {
log.error("decode消息类型 非 收到的消息");
}
in.readByte();
in.readByte();
int contentLength = length - FRAME_HEADER_LENGTH;
byte[] inputBytes = new byte[contentLength];
in.readBytes(inputBytes);
if (in.readableBytes() != 0) {
// log.error("in.readableBytes() {}", in.readableBytes());
pendingByteBuf.offer(in);
}
String bodyDouyuSttString = new String(inputBytes, 0, inputBytes.length - 1);
return Optional.ofNullable(parseDouyuSttString(bodyDouyuSttString, msgType));
}
public static final String SPLITTER = "@=";
public static final String END = "/";
public static final String SUFFIX = "\0";
/**
* <pre>{@code @S/ -> @AS@S}</pre>
*
* @param string
* @return
*/
public static String escape(String string) {
// return string == null ? StrUtil.EMPTY : (string.replaceAll("/", "@S").replaceAll("@", "@A"));
return string == null ? StrUtil.EMPTY : (string.replaceAll("@", "@A").replaceAll("/", "@S"));
}
/**
* <pre>{@code @AS@S -> @S/}</pre>
*
* @param string
* @return
*/
public static String unescape(String string) {
return string == null ? StrUtil.EMPTY : (string.replaceAll("@S", "/").replaceAll("@A", "@"));
}
public static String toDouyuSttString(Object object, List<String> containProperties) {
StringBuffer sb = new StringBuffer();
if (object instanceof IDouyuMsg) {
Class<?> objectClass = object.getClass();
Field[] fields = ReflectUtil.getFields(objectClass, field -> {
String name = field.getName();
if (CollUtil.isNotEmpty(containProperties)) {
return containProperties.contains(name);
} else {
return !name.startsWith(SHOULD_IGNORE_PROPERTIES_PREFIX) && !ArrayUtil.contains(IGNORE_PROPERTIES, name);
}
});
for (Field field : fields) {
String key = field.getName();
Method method = OrLiveChatReflectUtil.getGetterMethod(objectClass, key);
Object value = ReflectUtil.invoke(object, method);
String douyuSttString = toDouyuSttString(value, containProperties);
String escape = escape(douyuSttString);
sb.append(escape(key))
.append(SPLITTER)
.append(escape)
.append(END);
}
} else {
if (object instanceof Iterable<?>) {
Iterable<?> iterable = (Iterable<?>) object;
StringBuffer iterableStringBuffer = new StringBuffer();
for (Object o : iterable) {
String douyuSttString = toDouyuSttString(o, containProperties);
String escape = escape(douyuSttString);
iterableStringBuffer.append(escape)
.append(END);
}
sb.append((iterableStringBuffer.toString()));
} else if (object instanceof Map<?, ?>) {
Map<?, ?> map = (Map<?, ?>) object;
StringBuffer mapStringBuffer = new StringBuffer();
map.forEach((mapKey, mapValue) -> {
mapStringBuffer.append(escape(StrUtil.toStringOrNull(mapKey)))
.append(SPLITTER)
.append(escape(toDouyuSttString(mapValue, containProperties)))
.append(END);
});
sb.append((mapStringBuffer.toString()));
} else {
sb.append((StrUtil.nullToEmpty(StrUtil.toStringOrNull(object))));
}
}
return sb.toString();
}
public static String toDouyuSttString(Object object) {
return toDouyuSttString(object, null);
}
public static IDouyuMsg parseDouyuSttString(String string, short msgType) {
Map<String, Object> stringObjectMap = parseDouyuSttStringToMap(string);
String type = (String) stringObjectMap.get("type");
DouyuCmdEnum cmdEnum = DouyuCmdEnum.getByString(type);
Class<IDouyuMsg> msgClass = getDouyuMsgClassByType(cmdEnum, msgType);
if (msgClass == null) {
// TODO 不支持的cmdEnum
if (log.isWarnEnabled()) {
log.warn("暂不支持 cmdEnum {}, msgType {}", cmdEnum, msgType);
}
return null;
}
IDouyuMsg t = ReflectUtil.newInstance(msgClass);
stringObjectMap.forEach((key, value) -> {
Field field = ReflectUtil.getField(t.getClass(), key);
// 未知key
if (field == null) {
// Object -> JsonNode
((BaseDouyuCmdMsg) t).getUnknownProperties().put(key, BaseDouyuCmdMsg.OBJECT_MAPPER.valueToTree(value));
// log.debug("未知key {} {}已存放于unknownProperties中", msgClass, key);
} else {
ReflectUtil.setFieldValue(t, field, value);
}
});
// 礼物消息设置礼物信息字段
if (t instanceof DgbMsg) {
DgbMsg msg = (DgbMsg) t;
String pid = msg.getPid();
// 通用礼物
if (StrUtil.isNotBlank(pid)) {
GiftPropSingle giftSingle = DouyuLiveChatClient.giftMap.get(pid, () -> {
GiftPropSingle gift = GiftPropSingle.DEFAULT_GIFT;
try {
gift = DouyuApis.getGiftPropSingleByPid(pid);
} catch (Exception e) {
log.error("礼物信息获取失败, pid=" + pid, e);
}
return gift;
});
msg.setGiftInfo(giftSingle);
}
// 房间礼物
else {
String realRoomId = msg.getRid();
if (DouyuLiveChatClient.roomGiftMap.containsKey(realRoomId)) {
Map<String, GiftListInfo> stringGiftListInfoMap = DouyuLiveChatClient.roomGiftMap.get(realRoomId);
msg.setRoomGiftInfo(stringGiftListInfoMap.getOrDefault(String.valueOf(msg.getGfid()), GiftListInfo.DEFAULT_GIFT));
}
}
}
return t;
}
public static Object parseDouyuSttStringToObject(String value) {
Object valueObject;
if (StrUtil.isBlank(value)) {
return null;
}
if (value.contains(SPLITTER) && value.contains(END)) {
// log.debug("map valueObject {}", value);
valueObject = parseDouyuSttStringToMap(value);
}
// List<Object>
else if (!value.contains(SPLITTER) && value.contains(END)) {
// log.debug("list valueObject {}", value);
List<Object> list = new ArrayList<>();
for (String s : value.split(END)) {
list.add(parseDouyuSttStringToObject(unescape(s)));
}
valueObject = list;
}
// String
else {
valueObject = value;
}
return valueObject;
}
public static Map<String, Object> parseDouyuSttStringToMap(String string) {
Map<String, Object> stringObjectMap = new HashMap<>();
for (String s : string.split(END)) {
String[] entry = s.split(SPLITTER);
String key = unescape(entry[0]);
String value = unescape(ArrayUtil.get(entry, 1));
Object valueObject = parseDouyuSttStringToObject(value);
stringObjectMap.put(key, valueObject);
}
return stringObjectMap;
}
public static <T extends IDouyuMsg> Class<T> getDouyuMsgClassByType(DouyuCmdEnum douyuCmdEnum, short msgType) {
if (douyuCmdEnum == null) {
return (Class<T>) DouyuCmdMsg.class;
}
Class<?> msgClass;
Class<?> tClass = douyuCmdEnum.getTClass();
if (tClass == null) {
if (douyuCmdEnum == DouyuCmdEnum.mrkl) {
if (msgType == MSG_TYPE_RECEIVE) {
msgClass = HeartbeatReplyMsg.class;
} else if (msgType == MSG_TYPE_SEND) {
msgClass = HeartbeatMsg.class;
} else {
msgClass = null;
}
} else {
msgClass = DouyuCmdMsg.class;
}
} else {
msgClass = tClass;
}
return (Class<T>) msgClass;
}
}

View File

@@ -0,0 +1,30 @@
package tech.ordinaryroad.live.chat.client.douyu.api;
import org.junit.jupiter.api.Test;
import tech.ordinaryroad.live.chat.client.douyu.msg.dto.GiftPropSingle;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* @author mjz
* @date 2023/8/30
*/
class DouyuApisTest {
@Test
void getRealRoomId() {
assertEquals(2947432, DouyuApis.getRealRoomId(92000));
assertEquals(3168536, DouyuApis.getRealRoomId(3168536));
assertEquals(290935, DouyuApis.getRealRoomId(22222));
assertEquals(290935, DouyuApis.getRealRoomId(290935));
assertEquals(520, DouyuApis.getRealRoomId(520));
assertThrows(RuntimeException.class, () -> DouyuApis.getRealRoomId(-1));
}
@Test
void getGiftInfo() {
GiftPropSingle giftByPid = DouyuApis.getGiftPropSingleByPid("4");
assertEquals("", giftByPid.getName());
}
}

View File

@@ -0,0 +1,31 @@
package tech.ordinaryroad.live.chat.client.douyu.client;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 描述:
*
* @author https:www.unfbx.com
* @since 2023-03-02
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class ChatChoice implements Serializable {
private long index;
/**
* 请求参数stream为true返回是delta
*/
@JsonProperty("delta")
private Message delta;
/**
* 请求参数stream为false返回是message
*/
@JsonProperty("message")
private Message message;
@JsonProperty("finish_reason")
private String finishReason;
}

View File

@@ -0,0 +1,24 @@
package tech.ordinaryroad.live.chat.client.douyu.client;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 描述: chat答案类
*
* @author https:www.unfbx.com
* 2023-03-02
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class ChatCompletionResponse implements Serializable {
private String id;
private String object;
private long created;
private String model;
private List<ChatChoice> choices;
private Usage usage;
}

View File

@@ -0,0 +1,410 @@
/*
* 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.douyu.client;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.junit.jupiter.api.Test;
import tech.ordinaryroad.live.chat.client.commons.base.msg.BaseCmdMsg;
import tech.ordinaryroad.live.chat.client.commons.base.msg.BaseMsg;
import tech.ordinaryroad.live.chat.client.commons.base.msg.IMsg;
import tech.ordinaryroad.live.chat.client.douyu.config.DouyuLiveChatClientConfig;
import tech.ordinaryroad.live.chat.client.douyu.constant.DouyuCmdEnum;
import tech.ordinaryroad.live.chat.client.douyu.listener.IDouyuConnectionListener;
import tech.ordinaryroad.live.chat.client.douyu.listener.IDouyuMsgListener;
import tech.ordinaryroad.live.chat.client.douyu.msg.ChatmsgMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.DgbMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.UenterMsg;
import tech.ordinaryroad.live.chat.client.douyu.netty.handler.DouyuBinaryFrameHandler;
import tech.ordinaryroad.live.chat.client.douyu.netty.handler.DouyuConnectionHandler;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author mjz
* @date 2023/8/26
*/
@Slf4j
class DouyuLiveChatClientTest implements IDouyuConnectionListener, IDouyuMsgListener {
static Object lock = new Object();
DouyuLiveChatClient client;
private final OkHttpClient okClient = new OkHttpClient();
private static final String API_KEY = "sk-xxxxxx";
private static final String URL = "https://api.pandarobot.chat/v1/chat/completions";
private final Lock reentrantLock = new ReentrantLock();
private boolean isProcessing = false;
public String getChatGptResponse(String prompt){
if (!reentrantLock.tryLock()) {
// 如果无法立即获得锁,直接返回空字符串
log.info("自动回复:我还没准备好");
return "";
}
try {
if (isProcessing) {
log.info("自动回复:我还没准备好");
// 如果已经在处理中,直接返回
return "";
}
isProcessing = true;
// 你的原始代码逻辑
RequestBody body = RequestBody.create(MediaType.get("application/json; charset=utf-8"),
"{\"model\": \"gpt-4-0125-preview\", \"messages\": [{\"role\": \"system\", \"content\": \"扮演一个充满机智和幽默感的斗鱼直播弹幕助手你的任务是用不超过30字的诙谐小段子来回复直播间用户的评论。你的回复应该机智幽默不夸张能引发笑声同时也要和谐适应直播间的气氛。当你收到用户的评论时迅速给出一个既幽默又贴切的回复。例如评论: 今天的直播怎么样?回复: 好看得让人忘记摸鱼,专心变成鱼粉!\"}, {\"role\": \"user\", \"content\": \"" + prompt + "\"}]}");
Request request = new Request.Builder()
.url(URL)
.post(body)
.addHeader("Authorization", "Bearer " + API_KEY)
.build();
long startTime = System.currentTimeMillis(); // 获取开始时间的毫秒数
System.out.println("执行前.......");
Thread.sleep(3000);
System.out.println("执行后.......");
long endTime = System.currentTimeMillis(); // 获取结束时间的毫秒数
long timeDiff = endTime - startTime; // 计算时间差
System.out.println("执行时间 " + timeDiff / 1000 + " 秒。");
try (Response response = okClient.newCall(request).execute()) {
if (response.body() != null) {
ObjectMapper mapper = new ObjectMapper();
ChatCompletionResponse completionResponse = mapper.readValue(response.body().string(), ChatCompletionResponse.class);
return completionResponse.getChoices().get(0).getMessage().getContent();
}
} catch (Exception e) {
log.info("调用出错了{}",e.getMessage());
}
return "";
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
isProcessing = false;
reentrantLock.unlock();
}
}
@Test
void example() throws InterruptedException {
String cookie = "dy_did=621fbe7a636002f6e6cc42eb00091701; acf_did=621fbe7a636002f6e6cc42eb00091701; dy_teen_mode=%7B%22uid%22%3A%22145703733%22%2C%22status%22%3A0%2C%22birthday%22%3A%22%22%2C%22password%22%3A%22%22%7D; dy_did=621fbe7a636002f6e6cc42eb00091701; Hm_lvt_e99aee90ec1b2106afe7ec3b199020a7=1706978160,1707033428,1707109487; PHPSESSID=1orrfq0m0gfrbd58slgh6shqc7; acf_auth=c22dT%2BYJG1xQ3ZuNrfxYNS06lnIjzNl%2F58xziUwyAsNedpJWvbEtIBh%2Bq%2F%2FYxo%2FFYAp9aQr4i4iYqTp2kYdb7n2JNUFBwGEBgZ8GiGS6O%2FR0CNLy8kQ2iVw; dy_auth=d0d0Ps%2FEOPWjXoPGMenITKuiBIC7wTua3heP19LVKzOZ8qibQpmTA9Hpyx%2FdxtDei1Db6KUWkzq1fEC0BLSZVSUbDkuv%2FX%2F%2B0pMu1kgXnk%2FUzQ%2BhV3EiRf8; wan_auth37wan=f0a9656c2e00K9uFDlpgnTfrv3m8aZoYB0WQTWxGSrSccCMd9LUiEUD2oYpJ%2F5Oe5uD2tVpaUA69sGzlTAtl9p3uLSSqgrvH%2F8sy1gWyyrS4fxotQSU; acf_uid=145703733; acf_username=145703733; acf_nickname=%E5%9B%BE%E5%85%94%E5%85%94%E7%9A%84%E5%85%94%E5%85%94%E5%9B%BE; acf_own_room=0; acf_groupid=1; acf_phonestatus=1; acf_avatar=https%3A%2F%2Fapic.douyucdn.cn%2Fupload%2Favanew%2Fface%2F201706%2F25%2F21%2F82973545fd6b770fe03175783ffbc3d5_; acf_ct=0; acf_ltkid=28814433; acf_biz=1; acf_stk=1213d18dc8e6b5b1; Hm_lpvt_e99aee90ec1b2106afe7ec3b199020a7=1707141953";
DouyuLiveChatClientConfig config = DouyuLiveChatClientConfig.builder()
// TODO 修改房间id支持短id
.roomId(7828414)
.cookie(cookie)
.build();
client = new DouyuLiveChatClient(config, new IDouyuMsgListener() {
@Override
public void onMsg(DouyuBinaryFrameHandler binaryFrameHandler, IMsg msg) {
IDouyuMsgListener.super.onMsg(binaryFrameHandler, msg);
// log.debug("{} 收到{}消息 {}", binaryFrameHandler.getRoomId(), msg.getClass(), msg);
}
@Override
public void onDanmuMsg(DouyuBinaryFrameHandler binaryFrameHandler, ChatmsgMsg msg) {
if(Integer.parseInt(msg.getLevel())>5){
String chatGptResponse = getChatGptResponse(msg.getContent());
log.info("自动回复:"+ "@"+msg.getUsername()+": "+chatGptResponse);
if(!Objects.equals(chatGptResponse, "")){
client.sendDanmu("@"+msg.getUsername()+": "+chatGptResponse);
}
}
log.info("{} 收到弹幕 {} {}({}){}", binaryFrameHandler.getRoomId(), msg.getBadgeLevel() != 0 ? msg.getBadgeLevel() + msg.getBadgeName() : "", msg.getUsername(), msg.getUid(), msg.getContent());
}
@Override
public void onGiftMsg(DouyuBinaryFrameHandler binaryFrameHandler, DgbMsg msg) {
IDouyuMsgListener.super.onGiftMsg(binaryFrameHandler, msg);
// log.info("{} 收到礼物 {} {}({}) {} {}({})x{}({})", binaryFrameHandler.getRoomId(), msg.getBadgeLevel() != 0 ? msg.getBadgeLevel() + msg.getBadgeName() : "", msg.getUsername(), msg.getUid(), "赠送", msg.getGiftName(), msg.getGiftId(), msg.getGiftCount(), msg.getGiftPrice());
}
@Override
public void onEnterRoomMsg(UenterMsg msg) {
// if(msg.getLevel()>20){
// String chatGptResponse = getChatGptResponse(msg.getUsername()+"加入了直播间,请你帮忙编写简短有创意的欢迎语");
// if(!Objects.equals(chatGptResponse, "")){
// client.sendDanmu(chatGptResponse);
// }
// }
log.info("{} {}({}) 进入直播间", msg.getBadgeLevel() != 0 ? msg.getBadgeLevel() + msg.getBadgeName() : "", msg.getUsername(), msg.getUid());
}
@Override
public void onCmdMsg(DouyuBinaryFrameHandler binaryFrameHandler, DouyuCmdEnum cmd, BaseCmdMsg<DouyuCmdEnum> cmdMsg) {
IDouyuMsgListener.super.onCmdMsg(binaryFrameHandler, cmd, cmdMsg);
//log.info("{} 收到CMD消息{} {}", binaryFrameHandler.getRoomId(), cmd, cmdMsg);
}
@Override
public void onOtherCmdMsg(DouyuBinaryFrameHandler binaryFrameHandler, DouyuCmdEnum cmd, BaseCmdMsg<DouyuCmdEnum> cmdMsg) {
IDouyuMsgListener.super.onOtherCmdMsg(binaryFrameHandler, cmd, cmdMsg);
// log.debug("{} 收到其他CMD消息 {}", binaryFrameHandler.getRoomId(), cmd);
}
@Override
public void onUnknownCmd(DouyuBinaryFrameHandler binaryFrameHandler, String cmdString, BaseMsg msg) {
IDouyuMsgListener.super.onUnknownCmd(binaryFrameHandler, cmdString, msg);
// log.debug("{} 收到未知CMD消息 {}", binaryFrameHandler.getRoomId(), cmdString);
}
}, new IDouyuConnectionListener() {
@Override
public void onConnected(DouyuConnectionHandler connectionHandler) {
log.info("{} onConnected", connectionHandler.getRoomId());
}
@Override
public void onConnectFailed(DouyuConnectionHandler connectionHandler) {
log.info("{} onConnectFailed", connectionHandler.getRoomId());
}
@Override
public void onDisconnected(DouyuConnectionHandler connectionHandler) {
log.info("{} onDisconnected", connectionHandler.getRoomId());
}
});
client.connect();
// 防止测试时直接退出
while (true) {
synchronized (lock) {
lock.wait();
}
}
}
@Test
void multipyListeners() throws InterruptedException {
DouyuLiveChatClientConfig config = DouyuLiveChatClientConfig.builder()
// TODO 修改房间id支持短id
.roomId(74751)
.build();
client = new DouyuLiveChatClient(config, null, this);
client.addMsgListener(new IDouyuMsgListener() {
@Override
public void onMsg(DouyuBinaryFrameHandler binaryFrameHandler, IMsg msg) {
log.info("listener 1 onMsg {}", msg);
}
});
IDouyuMsgListener msgListener2 = new IDouyuMsgListener() {
@Override
public void onMsg(DouyuBinaryFrameHandler binaryFrameHandler, IMsg msg) {
log.info("listener 2 onMsg {}", msg);
}
};
client.addMsgListener(msgListener2);
AtomicBoolean removed = new AtomicBoolean(false);
client.addMsgListener(new IDouyuMsgListener() {
@Override
public void onMsg(DouyuBinaryFrameHandler binaryFrameHandler, IMsg msg) {
log.info("listener 3 onMsg {}", msg);
if (!removed.get()) {
log.warn("remove listener 2 by listener 3");
removed.set(client.removeMsgListener(msgListener2));
}
}
});
client.connect();
// 防止测试时直接退出
while (true) {
synchronized (lock) {
lock.wait();
}
}
}
@Test
void multiplyClient() throws InterruptedException {
DouyuLiveChatClientConfig config1 = DouyuLiveChatClientConfig.builder().roomId(890074).build();
DouyuLiveChatClient client1 = new DouyuLiveChatClient(config1, DouyuLiveChatClientTest.this, DouyuLiveChatClientTest.this);
DouyuLiveChatClientConfig config2 = DouyuLiveChatClientConfig.builder().roomId(718133).build();
DouyuLiveChatClient client2 = new DouyuLiveChatClient(config2, DouyuLiveChatClientTest.this, DouyuLiveChatClientTest.this);
client1.connect(() -> {
log.warn("client1 connect successfully, start connecting client2");
client2.connect(() -> {
log.warn("client2 connect successfully");
});
});
// 防止测试时直接退出
while (true) {
synchronized (lock) {
lock.wait();
}
}
}
@Test
void sendDanmu() throws InterruptedException {
String cookie = "dy_did=621fbe7a636002f6e6cc42eb00091701; acf_did=621fbe7a636002f6e6cc42eb00091701; Hm_lvt_e99aee90ec1b2106afe7ec3b199020a7=1706978160; PHPSESSID=5mg98bkoens5bsg7da6ooj74b7; acf_auth=2a246MCoU1hPn940VxZrs0wQVc8tY96s6rHocobTmUJF8XKjoxWmv51PLf1V4T5g8QiEAyy6u%2BHQfC%2FTNkJudUVdEowNqAF81eaUzHvo5i1MIXBRbDXYXwY; dy_auth=d066mz7%2BE83QjmEJfujfi%2FyOuib9Y2qfBcK7pINX6Gde4HGRaiWZxhNXkqdbLU1aV%2FXaQ0wbkyxV0DddiF9jBc4tMpCMjvi4a1C6EJTfpLHvMmaGBKlI%2FlQ; wan_auth37wan=b747c445309bcOqiaMF72xt346irN4v%2Fhi%2BgabHCNxe812jm9rR8kGws8cdJlsn0C6jxsVKY6We5xWlrjHKesoD2%2B0Av52%2FWzIxaZKT0f30GASOJz58; acf_uid=145703733; acf_username=145703733; acf_nickname=%E5%9B%BE%E5%85%94%E5%85%94%E7%9A%84%E5%85%94%E5%85%94%E5%9B%BE; acf_own_room=0; acf_groupid=1; acf_phonestatus=1; acf_avatar=https%3A%2F%2Fapic.douyucdn.cn%2Fupload%2Favanew%2Fface%2F201706%2F25%2F21%2F82973545fd6b770fe03175783ffbc3d5_; acf_ct=0; acf_ltkid=28814429; acf_biz=1; acf_stk=0fad5f2454193f86; dy_teen_mode=%7B%22uid%22%3A%22145703733%22%2C%22status%22%3A0%2C%22birthday%22%3A%22%22%2C%22password%22%3A%22%22%7D; dy_did=621fbe7a636002f6e6cc42eb00091701; acf_ccn=5ba506efe2d8a939cf09f8597c7d8030; Hm_lpvt_e99aee90ec1b2106afe7ec3b199020a7=1706978671";
// log.error("cookie: {}", cookie);
DouyuLiveChatClientConfig config = DouyuLiveChatClientConfig.builder()
.cookie(cookie)
// TODO 修改弹幕发送最短时间间隔默认3s
.minSendDanmuPeriod(10 * 1000)
// TODO 修改房间id支持短id
.roomId(4624967)
.build();
DouyuWsLiveChatClient client = new DouyuWsLiveChatClient(config, new IDouyuMsgListener() {
@Override
public void onMsg(IMsg msg) {
IDouyuMsgListener.super.onMsg(msg);
// log.debug("收到消息 {}", msg.getClass());
}
@Override
public void onCmdMsg(DouyuCmdEnum cmd, BaseCmdMsg<DouyuCmdEnum> cmdMsg) {
log.debug("收到CMD消息 {} {}", cmd, cmdMsg);
}
@Override
public void onEnterRoomMsg(UenterMsg msg) {
log.info("{} {}({}) 进入直播间", msg.getBadgeLevel() != 0 ? msg.getBadgeLevel() + msg.getBadgeName() : "", msg.getUsername(), msg.getUid());
}
@Override
public void onUnknownCmd(String cmdString, BaseMsg msg) {
IDouyuMsgListener.super.onUnknownCmd(cmdString, msg);
log.debug("收到未知CMD消息 {} {}", cmdString, msg);
}
}, new IDouyuConnectionListener() {
@Override
public void onConnected(DouyuConnectionHandler connectionHandler) {
log.error("{} onConnected", connectionHandler.getRoomId());
}
@Override
public void onConnectFailed(DouyuConnectionHandler connectionHandler) {
log.error("{} onConnectFailed", connectionHandler.getRoomId());
}
@Override
public void onDisconnected(DouyuConnectionHandler connectionHandler) {
log.error("{} onDisconnected", connectionHandler.getRoomId());
}
}, new NioEventLoopGroup());
client.connect(() -> {
client.sendDanmu("主播真好看");
});
// 防止测试时直接退出
while (true) {
synchronized (lock) {
lock.wait();
}
}
}
@Test
void createAuthFrameFailedAndDisconnect() throws InterruptedException {
DouyuLiveChatClientConfig config = DouyuLiveChatClientConfig.builder()
// TODO 修改房间id支持短id
.autoReconnect(false)
.cookie("12323232'123'213'2'13'2")
.roomId(22222)
.build();
client = new DouyuLiveChatClient(config, this, this);
client.connect();
// 防止测试时直接退出
while (true) {
synchronized (lock) {
lock.wait();
}
}
}
@Test
void autoReconnect() throws InterruptedException {
DouyuLiveChatClientConfig config = DouyuLiveChatClientConfig.builder()
// TODO 修改房间id支持短id
.autoReconnect(true)
.cookie("12323232'123'213'2'13'2")
// .websocketUri("wss://sa.asd.asd:12")
.roomId(22222)
.build();
client = new DouyuLiveChatClient(config, this, this);
client.addStatusChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
Object oldStatus = evt.getOldValue();
Object newStatus = evt.getNewValue();
log.error("{} => {}", oldStatus, newStatus);
}
});
client.connect();
// 防止测试时直接退出
while (true) {
synchronized (lock) {
lock.wait();
}
}
}
@Override
public void onConnected(DouyuConnectionHandler connectionHandler) {
log.info("{} onConnected", connectionHandler.getRoomId());
}
@Override
public void onConnectFailed(DouyuConnectionHandler connectionHandler) {
log.info("{} onConnectFailed", connectionHandler.getRoomId());
}
@Override
public void onDisconnected(DouyuConnectionHandler connectionHandler) {
log.info("{} onDisconnected", connectionHandler.getRoomId());
}
@Override
public void onDanmuMsg(DouyuBinaryFrameHandler binaryFrameHandler, ChatmsgMsg msg) {
IDouyuMsgListener.super.onDanmuMsg(binaryFrameHandler, msg);
log.info("{} 收到弹幕 {}({}){}", binaryFrameHandler.getRoomId(), msg.getNn(), msg.getUid(), msg.getTxt());
}
}

View File

@@ -0,0 +1,104 @@
package tech.ordinaryroad.live.chat.client.douyu.client;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import java.io.Serializable;
/**
* 描述:
*
* @author https:www.unfbx.com
* @since 2023-03-02
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class Message implements Serializable {
/**
* 目前支持四个中角色参考官网,进行情景输入:
* https://platform.openai.com/docs/guides/chat/introduction
*/
private String role;
private String content;
private String name;
public static Builder builder() {
return new Builder();
}
/**
* 构造函数
*
* @param role 角色
* @param content 描述主题信息
* @param name name
*/
public Message(String role, String content, String name) {
this.role = role;
this.content = content;
this.name = name;
}
public Message() {
}
private Message(Builder builder) {
setRole(builder.role);
setContent(builder.content);
setName(builder.name);
}
@Getter
@AllArgsConstructor
public enum Role {
SYSTEM("system"),
USER("user"),
ASSISTANT("assistant"),
FUNCTION("function"),
;
private String name;
}
public static final class Builder {
private String role;
private String content;
private String name;
public Builder() {
}
public Builder role(Role role) {
this.role = role.getName();
return this;
}
public Builder role(String role) {
this.role = role;
return this;
}
public Builder content(String content) {
this.content = content;
return this;
}
public Builder name(String name) {
this.name = name;
return this;
}
public Message build() {
return new Message(this);
}
}
}

View File

@@ -0,0 +1,24 @@
package tech.ordinaryroad.live.chat.client.douyu.client;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 描述:
*
* @author https:www.unfbx.com
* 2023-02-15
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class Usage implements Serializable {
@JsonProperty("prompt_tokens")
private long promptTokens;
@JsonProperty("completion_tokens")
private long completionTokens;
@JsonProperty("total_tokens")
private long totalTokens;
}

View File

@@ -0,0 +1,102 @@
package tech.ordinaryroad.live.chat.client.douyu.util;
import org.junit.jupiter.api.Test;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.BaseDouyuCmdMsg;
import tech.ordinaryroad.live.chat.client.douyu.msg.base.IDouyuMsg;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
* @author mjz
* @date 2023/8/27
*/
class DouyuCodecUtilTest {
@Test
void test() {
String loginres = "type@=loginres/userid@=1235403446/roomgroup@=0/pg@=0/sessionid@=0/username@=/nickname@=/live_stat@=0/is_illegal@=0/ill_ct@=/ill_ts@=0/now@=0/ps@=0/es@=0/it@=0/its@=0/npv@=0/best_dlev@=0/cur_lev@=0/nrc@=2785047409/ih@=0/sid@=72256/sahf@=0/sceneid@=0/newrg@=0/regts@=0/ip@=112.43.93.229/rn@=0/rct@=0/";
String pingreq = "type@=pingreq/tick@=1693128084471/";
String mrkl = "type@=mrkl/";
String subres = "type@=subres/res@=0/";
String mapkb = "type@=mapkb/cmd@=3/rid@=5515841/pk_time@=600/lt@=156/teams@=team@AA=5@ASres@AA=1@ASsc@AA=660000@ASbf@AA=0@AS@Steam@AA=6@ASres@AA=2@ASsc@AA=600000@ASbf@AA=0@AS@Steam@AA=4@ASres@AA=3@ASsc@AA=456000@ASbf@AA=0@AS@Steam@AA=2@ASres@AA=4@ASsc@AA=302000@ASbf@AA=0@AS@Steam@AA=3@ASres@AA=5@ASsc@AA=100000@ASbf@AA=0@AS@Steam@AA=1@ASres@AA=6@ASsc@AA=200@ASbf@AA=0@AS@S/members@=rid@AA=5515841@ASuid@AA=219585944@ASnick@AA=喵小莎@ASicon@AA=avatar_v3@AAS202303@AAS4cab38d935004acfab53f77a730c9d42@ASstatus@AA=0@ASsc@AA=100000@AStime@AA=1693127363@ASteam@AA=3@ASgroup_no@AA=0@ASmd@AA=0@AShvsc@AA=0@ASct@AA=0@AS@Srid@AA=8733064@ASuid@AA=364378905@ASnick@AA=阿允唱情歌@ASicon@AA=avatar_v3@AAS202008@AAS31b239d8af174462b5e0a2990c70b818@ASstatus@AA=0@ASsc@AA=600000@AStime@AA=1693127527@ASteam@AA=6@ASgroup_no@AA=0@ASmd@AA=0@AShvsc@AA=0@ASct@AA=0@AS@Srid@AA=10673032@ASuid@AA=442218984@ASnick@AA=果酱Broly@ASicon@AA=avatar_v3@AAS202306@AASf7d61a0ea3bb4c179af7b5e24f94953e@ASstatus@AA=0@ASsc@AA=200@AStime@AA=1693127347@ASteam@AA=1@ASgroup_no@AA=0@ASmd@AA=0@AShvsc@AA=0@ASct@AA=0@AS@Srid@AA=533813@ASuid@AA=36922190@ASnick@AA=正直博@ASicon@AA=avatar_v3@AAS202212@AAS4e340983996f43b991ffa50af7b956f6@ASstatus@AA=0@ASsc@AA=302000@AStime@AA=1693127348@ASteam@AA=2@ASgroup_no@AA=0@ASmd@AA=0@AShvsc@AA=0@ASct@AA=0@AS@Srid@AA=8031896@ASuid@AA=243111494@ASnick@AA=青榎@ASicon@AA=avatar_v3@AAS202305@AAS8bd800d3bb8d4e16884dcb3e3f77b038@ASstatus@AA=0@ASsc@AA=456000@AStime@AA=1693127393@ASteam@AA=4@ASgroup_no@AA=0@ASmd@AA=0@AShvsc@AA=0@ASct@AA=0@AS@Srid@AA=7009686@ASuid@AA=299482114@ASnick@AA=紫薇同学@ASicon@AA=avatar_v3@AAS202302@AASb67044b78b494b0896fd6738bc2d5b7b@ASstatus@AA=0@ASsc@AA=660000@AStime@AA=1693127424@ASteam@AA=5@ASgroup_no@AA=0@ASmd@AA=0@AShvsc@AA=0@ASct@AA=0@AS@S/subcmd@=0/pkm@=0/rlt@=0/";
String pdrinfo = "giftId@=23518/total@=total@A=3@Snum@A=0@Sfin@A=0@S/actStatus@=1/time@=1693158017596/type@=pdrinfo/dayFin@=0/list@=1@A=total@AAA=6@AASnum@AAA=6@AASpid@AAA=2682@AASfin@AAA=1@AAS@AStotal@AAA=600@AASnum@AAA=336@AASpid@AAA=2680@AASfin@AAA=0@AAS@AS@S100@A=total@AAA=2@AASnum@AAA=0@AASpid@AAA=2685@AASfin@AAA=0@AAS@AStotal@AAA=100@AASnum@AAA=5@AASpid@AAA=2682@AASfin@AAA=0@AAS@AS@S10@A=total@AAA=2@AASnum@AAA=0@AASpid@AAA=2683@AASfin@AAA=0@AAS@AStotal@AAA=80@AASnum@AAA=35@AASpid@AAA=2681@AASfin@AAA=0@AAS@AS@S/";
Map<String, String> map = new HashMap<>();
map.put("loginres", loginres);
map.put("pingreq", pingreq);
map.put("mrkl", mrkl);
map.put("subres", subres);
map.put("mapkb", mapkb);
map.put("pdrinfo", pdrinfo);
map.forEach((string, string2) -> {
BaseDouyuCmdMsg baseDouyuCmdMsg = (BaseDouyuCmdMsg) DouyuCodecUtil.parseDouyuSttString(string2, DouyuCodecUtil.MSG_TYPE_RECEIVE);
assertNotNull(baseDouyuCmdMsg);
String cmd = baseDouyuCmdMsg.getCmd();
assertEquals(string, cmd);
});
}
@Test
void decodeTest() {
String mapkb = "type@=mapkb/cmd@=3/rid@=5515841/pk_time@=600/lt@=156/teams@=team@AA=5@ASres@AA=1@ASsc@AA=660000@ASbf@AA=0@AS@Steam@AA=6@ASres@AA=2@ASsc@AA=600000@ASbf@AA=0@AS@Steam@AA=4@ASres@AA=3@ASsc@AA=456000@ASbf@AA=0@AS@Steam@AA=2@ASres@AA=4@ASsc@AA=302000@ASbf@AA=0@AS@Steam@AA=3@ASres@AA=5@ASsc@AA=100000@ASbf@AA=0@AS@Steam@AA=1@ASres@AA=6@ASsc@AA=200@ASbf@AA=0@AS@S/members@=rid@AA=5515841@ASuid@AA=219585944@ASnick@AA=喵小莎@ASicon@AA=avatar_v3@AAS202303@AAS4cab38d935004acfab53f77a730c9d42@ASstatus@AA=0@ASsc@AA=100000@AStime@AA=1693127363@ASteam@AA=3@ASgroup_no@AA=0@ASmd@AA=0@AShvsc@AA=0@ASct@AA=0@AS@Srid@AA=8733064@ASuid@AA=364378905@ASnick@AA=阿允唱情歌@ASicon@AA=avatar_v3@AAS202008@AAS31b239d8af174462b5e0a2990c70b818@ASstatus@AA=0@ASsc@AA=600000@AStime@AA=1693127527@ASteam@AA=6@ASgroup_no@AA=0@ASmd@AA=0@AShvsc@AA=0@ASct@AA=0@AS@Srid@AA=10673032@ASuid@AA=442218984@ASnick@AA=果酱Broly@ASicon@AA=avatar_v3@AAS202306@AASf7d61a0ea3bb4c179af7b5e24f94953e@ASstatus@AA=0@ASsc@AA=200@AStime@AA=1693127347@ASteam@AA=1@ASgroup_no@AA=0@ASmd@AA=0@AShvsc@AA=0@ASct@AA=0@AS@Srid@AA=533813@ASuid@AA=36922190@ASnick@AA=正直博@ASicon@AA=avatar_v3@AAS202212@AAS4e340983996f43b991ffa50af7b956f6@ASstatus@AA=0@ASsc@AA=302000@AStime@AA=1693127348@ASteam@AA=2@ASgroup_no@AA=0@ASmd@AA=0@AShvsc@AA=0@ASct@AA=0@AS@Srid@AA=8031896@ASuid@AA=243111494@ASnick@AA=青榎@ASicon@AA=avatar_v3@AAS202305@AAS8bd800d3bb8d4e16884dcb3e3f77b038@ASstatus@AA=0@ASsc@AA=456000@AStime@AA=1693127393@ASteam@AA=4@ASgroup_no@AA=0@ASmd@AA=0@AShvsc@AA=0@ASct@AA=0@AS@Srid@AA=7009686@ASuid@AA=299482114@ASnick@AA=紫薇同学@ASicon@AA=avatar_v3@AAS202302@AASb67044b78b494b0896fd6738bc2d5b7b@ASstatus@AA=0@ASsc@AA=660000@AStime@AA=1693127424@ASteam@AA=5@ASgroup_no@AA=0@ASmd@AA=0@AShvsc@AA=0@ASct@AA=0@AS@S/subcmd@=0/pkm@=0/rlt@=0/";
Map<String, Object> stringObjectMap = DouyuCodecUtil.parseDouyuSttStringToMap(mapkb);
assertNotNull(stringObjectMap);
stringObjectMap.forEach((string, o) -> {
System.out.println(string);
});
}
@Test
void decodeTest2() {
String pdrinfo = "giftId@=23518/total@=total@A=3@Snum@A=0@Sfin@A=0@S/actStatus@=1/time@=1693158017596/type@=pdrinfo/dayFin@=0/list@=1@A=total@AAA=6@AASnum@AAA=6@AASpid@AAA=2682@AASfin@AAA=1@AAS@AStotal@AAA=600@AASnum@AAA=336@AASpid@AAA=2680@AASfin@AAA=0@AAS@AS@S100@A=total@AAA=2@AASnum@AAA=0@AASpid@AAA=2685@AASfin@AAA=0@AAS@AStotal@AAA=100@AASnum@AAA=5@AASpid@AAA=2682@AASfin@AAA=0@AAS@AS@S10@A=total@AAA=2@AASnum@AAA=0@AASpid@AAA=2683@AASfin@AAA=0@AAS@AStotal@AAA=80@AASnum@AAA=35@AASpid@AAA=2681@AASfin@AAA=0@AAS@AS@S/";
Map<String, Object> stringObjectMap = DouyuCodecUtil.parseDouyuSttStringToMap(pdrinfo);
assertNotNull(stringObjectMap);
stringObjectMap.forEach((string, o) -> {
System.out.println(string);
});
}
@Test
void toDouyuSttStringTest() {
String douyuSttString = DouyuCodecUtil.toDouyuSttString(new HashMap<String, Object>() {{
put("key1", "value1");
put("key2", 2);
put("key3", new ArrayList<Map<String, Object>>() {{
add(new HashMap<String, Object>() {{
put("11", 11);
put("12", 12);
}});
add(new HashMap<String, Object>() {{
put("21", 21);
put("22", 22);
}});
}});
}});
System.out.println(douyuSttString);
assertEquals("key1@=value1/key2@=2/key3@=11@AA=11@AS12@AA=12@AS@S22@AA=22@AS21@AA=21@AS@S/".length(), douyuSttString.length());
}
@Test
void codecTest() {
String mapkb = "type@=mapkb/pk_time@=600/teams@=team@AA=5@ASres@AA=1@ASsc@AA=660000@ASbf@AA=0@AS@Steam@AA=6@ASres@AA=2@ASsc@AA=600000@ASbf@AA=0@AS@Steam@AA=4@ASres@AA=3@ASsc@AA=456000@ASbf@AA=0@AS@Steam@AA=2@ASres@AA=4@ASsc@AA=302000@ASbf@AA=0@AS@Steam@AA=3@ASres@AA=5@ASsc@AA=100000@ASbf@AA=0@AS@Steam@AA=1@ASres@AA=6@ASsc@AA=200@ASbf@AA=0@AS@S/";
IDouyuMsg iDouyuMsg = DouyuCodecUtil.parseDouyuSttString(mapkb, DouyuCodecUtil.MSG_TYPE_RECEIVE);
String douyuSttString = DouyuCodecUtil.toDouyuSttString(iDouyuMsg);
System.out.println(mapkb);
System.out.println(douyuSttString);
assertEquals(mapkb.length(), douyuSttString.length());
IDouyuMsg douyuSttStringMsg = DouyuCodecUtil.parseDouyuSttString(douyuSttString, DouyuCodecUtil.MSG_TYPE_RECEIVE);
assertNotNull(douyuSttStringMsg);
}
@Test
void unescape() {
String unescape = DouyuCodecUtil.unescape("team@AA=5@ASres@AA=1@ASsc@AA=660000@ASbf@AA=0@AS@Steam@AA=6@ASres@AA=2@ASsc@AA=600000@ASbf@AA=0@AS@Steam@AA=4@ASres@AA=3@ASsc@AA=456000@ASbf@AA=0@AS@Steam@AA=2@ASres@AA=4@ASsc@AA=302000@ASbf@AA=0@AS@Steam@AA=3@ASres@AA=5@ASsc@AA=100000@ASbf@AA=0@AS@Steam@AA=1@ASres@AA=6@ASsc@AA=200@ASbf@AA=0@AS@S");
System.out.println(unescape);
}
}