增加直播弹幕推送流中的 GUARD_MSG, ROOM_BLOCK_MSG, ROOM_SILENT_OFF, WISH_BOTTLE 数据包的支持

This commit is contained in:
czp 2018-02-10 01:36:36 +08:00
parent 803290932d
commit 33c8c8ad6f
29 changed files with 490 additions and 120 deletions

View File

@ -217,7 +217,7 @@ API 文档
@Subscribe
public void onDanMuMsg(DanMuMsgPackageEvent danMuMsgPackageEvent) {
DanMuMsgEntity danMuMsgEntity = danMuMsgPackageEvent.getDanMuMsgEntity();
DanMuMsgEntity danMuMsgEntity = danMuMsgPackageEvent.getEntity();
System.out.pintf("%s: %s\n", danMuMsgEntity.getUsername(), danMuMsgEntity.getMessage());
}
}
@ -236,19 +236,35 @@ API 文档
| ConnectionCloseEvent | 连接断开(主动或被动) |
| ConnectSucceedEvent | 进房成功 |
| DanMuMsgPackageEvent | 收到 DANMU_MSG 数据包 |
| GuardMsgPackageEvent | 收到 GUARD_MSG 数据包 |
| LivePackageEvent | 收到 LIVE 数据包 |
| PreparingPackageEvent | 收到 PREPARING 数据包 |
| RoomBlockMsgPackageEvent | 收到 ROOM_BLOCK_MSG 数据包 |
| RoomSilentOffPackageEvent | 收到 ROOM_SILENT_OFF 数据包 |
| SendGiftPackageEvent | 收到 SEND_GIFT 数据包 |
| SendHeartBeatPackageEvent | 每次发送心跳包后触发一次 |
| SysGiftPackageEvent | 收到 SYS_GIFT 数据包 |
| SysMsgPackageEvent | 收到 SYS_MSG 数据包 |
| UnknownPackageEvent | B站新增了新种类的数据包, 出现此情况请提交 issue |
| ViewerCountPackageEvent | 收到 房间人数 数据包(不是 Json) |
| WelcomeGuardPackageEvent | 收到 WELCOME_GUARD 数据包 |
| WelcomePackageEvent | 收到 WELCOME 数据包 |
| WishBottlePackageEvent | 收到 WISH_BOTTLE 数据包 |
事件里面可以取到解析好的 POJO, 然后可以从里面取数据, 见上面的监听器示例.
# 特别说明
## DANMU_MSG 中的各个字段含义
在直播间实时弹幕推送流中, 存在一种类型为 DANMU_MSG 的数据包, 它里面存储的 JSON, 全部都是 JsonArray, 并且每个元素类型不一样, 含义不一样.
简单地说, 这个 JSON 完全无法自描述而且很多字段猜不到是什么含义, 它的示例见 /record 文件夹(还有一份带备注的版本, 里面记录了已经猜出的字段含义).
已经猜出的字段, 可以直接从 DanMuMsgEntity 里面用对应的方法取得, 对于没有猜出的字段, 需要类似这样来获取:
int something = danMuMsgEntity.getInfo().get(0).getAsJsonArray().get(2).getAsInt();
如果你可以明确其中的字段含义, 欢迎提交 issue.
## 直播间 ID 问题
一个直播间, 我们用浏览器去访问它, 他可能是这样的

View File

@ -0,0 +1,4 @@
{
"cmd": "GUARD_MSG",
"msg": "乘客 :?想不想joice:? 成功购买1313366房间总督船票1张欢迎登船"
}

View File

@ -0,0 +1,6 @@
{
"cmd": "ROOM_BLOCK_MSG",
"uid": "60244207",
"uname": "承包rose",
"roomid": 5279
}

View File

@ -0,0 +1,5 @@
{
"cmd": "ROOM_SILENT_OFF",
"data": [],
"roomid": "29434"
}

View File

@ -0,0 +1,23 @@
{
"cmd": "WISH_BOTTLE",
"data": {
"action": "update",
"id": 1832,
"wish": {
"id": 1832,
"uid": 110631,
"type": 1,
"type_id": 7,
"wish_limit": 99999,
"wish_progress": 14381,
"status": 1,
"content": "女装直播",
"ctime": "2018-01-12 17:25:58",
"count_map": [
1,
3,
5
]
}
}
}

View File

@ -20,6 +20,7 @@ public interface LiveService {
@GET("AppRoom/msg")
Call<LiveHistoryBulletScreensEntity> getHistoryBulletScreens(@Query("room_id") long roomId);
//登录后访问该 API 将在服务器新增一条直播间观看历史
@GET("AppRoom/index")
Call<LiveRoomInfoEntity> getRoomInfo(@Query("room_id") long roomId);

View File

@ -13,7 +13,7 @@ public class PackageHelper {
);
}
public static Package createHeatBeatPackage() {
public static Package createHeartBeatPackage() {
return new Package(
Package.PackageType.HEART_BEAT,
new byte[0]

View File

@ -0,0 +1,31 @@
package com.hiczp.bilibili.api.live.socket.entity;
import com.google.gson.annotations.SerializedName;
public class GuardMsgEntity {
/**
* cmd : GUARD_MSG
* msg : 乘客 :?想不想joice:? 成功购买1313366房间总督船票1张欢迎登船
*/
@SerializedName("cmd")
private String cmd;
@SerializedName("msg")
private String msg;
public String getCmd() {
return cmd;
}
public void setCmd(String cmd) {
this.cmd = cmd;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}

View File

@ -0,0 +1,53 @@
package com.hiczp.bilibili.api.live.socket.entity;
import com.google.gson.annotations.SerializedName;
public class RoomBlockMsgEntity {
/**
* cmd : ROOM_BLOCK_MSG
* uid : 60244207
* uname : 承包rose
* roomid : 5279
*/
@SerializedName("cmd")
private String cmd;
@SerializedName("uid")
private String uid;
@SerializedName("uname")
private String uname;
@SerializedName("roomid")
private long roomid;
public String getCmd() {
return cmd;
}
public void setCmd(String cmd) {
this.cmd = cmd;
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public long getRoomid() {
return roomid;
}
public void setRoomid(long roomid) {
this.roomid = roomid;
}
}

View File

@ -0,0 +1,44 @@
package com.hiczp.bilibili.api.live.socket.entity;
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class RoomSilentOffEntity {
/**
* cmd : ROOM_SILENT_OFF
* data : []
* roomid : 29434
*/
@SerializedName("cmd")
private String cmd;
@SerializedName("roomid")
private String roomid;
@SerializedName("data")
private List<?> data;
public String getCmd() {
return cmd;
}
public void setCmd(String cmd) {
this.cmd = cmd;
}
public String getRoomid() {
return roomid;
}
public void setRoomid(String roomid) {
this.roomid = roomid;
}
public List<?> getData() {
return data;
}
public void setData(List<?> data) {
this.data = data;
}
}

View File

@ -0,0 +1,188 @@
package com.hiczp.bilibili.api.live.socket.entity;
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class WishBottleEntity {
/**
* cmd : WISH_BOTTLE
* data : {"action":"update","id":1832,"wish":{"id":1832,"uid":110631,"type":1,"type_id":7,"wish_limit":99999,"wish_progress":14381,"status":1,"content":"女装直播","ctime":"2018-01-12 17:25:58","count_map":[1,3,5]}}
*/
@SerializedName("cmd")
private String cmd;
@SerializedName("data")
private Data data;
public String getCmd() {
return cmd;
}
public void setCmd(String cmd) {
this.cmd = cmd;
}
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
public static class Data {
/**
* action : update
* id : 1832
* wish : {"id":1832,"uid":110631,"type":1,"type_id":7,"wish_limit":99999,"wish_progress":14381,"status":1,"content":"女装直播","ctime":"2018-01-12 17:25:58","count_map":[1,3,5]}
*/
@SerializedName("action")
private String action;
@SerializedName("id")
private long id;
@SerializedName("wish")
private Wish wish;
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Wish getWish() {
return wish;
}
public void setWish(Wish wish) {
this.wish = wish;
}
public static class Wish {
/**
* id : 1832
* uid : 110631
* type : 1
* type_id : 7
* wish_limit : 99999
* wish_progress : 14381
* status : 1
* content : 女装直播
* ctime : 2018-01-12 17:25:58
* count_map : [1,3,5]
*/
@SerializedName("id")
private long id;
@SerializedName("uid")
private long uid;
@SerializedName("type")
private int type;
@SerializedName("type_id")
private int typeId;
@SerializedName("wish_limit")
private int wishLimit;
@SerializedName("wish_progress")
private int wishProgress;
@SerializedName("status")
private int status;
@SerializedName("content")
private String content;
@SerializedName("ctime")
private String ctime;
@SerializedName("count_map")
private List<Integer> countMap;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public long getUid() {
return uid;
}
public void setUid(long uid) {
this.uid = uid;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public int getTypeId() {
return typeId;
}
public void setTypeId(int typeId) {
this.typeId = typeId;
}
public int getWishLimit() {
return wishLimit;
}
public void setWishLimit(int wishLimit) {
this.wishLimit = wishLimit;
}
public int getWishProgress() {
return wishProgress;
}
public void setWishProgress(int wishProgress) {
this.wishProgress = wishProgress;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getCtime() {
return ctime;
}
public void setCtime(String ctime) {
this.ctime = ctime;
}
public List<Integer> getCountMap() {
return countMap;
}
public void setCountMap(List<Integer> countMap) {
this.countMap = countMap;
}
}
}
}

View File

@ -2,17 +2,8 @@ package com.hiczp.bilibili.api.live.socket.event;
import com.hiczp.bilibili.api.live.socket.entity.ActivityEventEntity;
import java.util.EventObject;
public class ActivityEventPackageEvent extends EventObject {
private ActivityEventEntity activityEventEntity;
public ActivityEventPackageEvent(Object source, ActivityEventEntity activityEventEntity) {
super(source);
this.activityEventEntity = activityEventEntity;
}
public ActivityEventEntity getActivityEventEntity() {
return activityEventEntity;
public class ActivityEventPackageEvent extends ReceivePackageEvent<ActivityEventEntity> {
public ActivityEventPackageEvent(Object source, ActivityEventEntity entity) {
super(source, entity);
}
}

View File

@ -2,17 +2,8 @@ package com.hiczp.bilibili.api.live.socket.event;
import com.hiczp.bilibili.api.live.socket.entity.DanMuMsgEntity;
import java.util.EventObject;
public class DanMuMsgPackageEvent extends EventObject {
private DanMuMsgEntity danMuMsgEntity;
public DanMuMsgPackageEvent(Object source, DanMuMsgEntity danMuMsgEntity) {
super(source);
this.danMuMsgEntity = danMuMsgEntity;
}
public DanMuMsgEntity getDanMuMsgEntity() {
return danMuMsgEntity;
public class DanMuMsgPackageEvent extends ReceivePackageEvent<DanMuMsgEntity> {
public DanMuMsgPackageEvent(Object source, DanMuMsgEntity entity) {
super(source, entity);
}
}

View File

@ -0,0 +1,9 @@
package com.hiczp.bilibili.api.live.socket.event;
import com.hiczp.bilibili.api.live.socket.entity.GuardMsgEntity;
public class GuardMsgPackageEvent extends ReceivePackageEvent<GuardMsgEntity> {
public GuardMsgPackageEvent(Object source, GuardMsgEntity entity) {
super(source, entity);
}
}

View File

@ -2,17 +2,8 @@ package com.hiczp.bilibili.api.live.socket.event;
import com.hiczp.bilibili.api.live.socket.entity.LiveEntity;
import java.util.EventObject;
public class LivePackageEvent extends EventObject {
private LiveEntity liveEntity;
public LivePackageEvent(Object source, LiveEntity liveEntity) {
super(source);
this.liveEntity = liveEntity;
}
public LiveEntity getLiveEntity() {
return liveEntity;
public class LivePackageEvent extends ReceivePackageEvent<LiveEntity> {
public LivePackageEvent(Object source, LiveEntity entity) {
super(source, entity);
}
}

View File

@ -2,17 +2,8 @@ package com.hiczp.bilibili.api.live.socket.event;
import com.hiczp.bilibili.api.live.socket.entity.PreparingEntity;
import java.util.EventObject;
public class PreparingPackageEvent extends EventObject {
private PreparingEntity preparingEntity;
public PreparingPackageEvent(Object source, PreparingEntity preparingEntity) {
super(source);
this.preparingEntity = preparingEntity;
}
public PreparingEntity getPreparingEntity() {
return preparingEntity;
public class PreparingPackageEvent extends ReceivePackageEvent<PreparingEntity> {
public PreparingPackageEvent(Object source, PreparingEntity entity) {
super(source, entity);
}
}

View File

@ -0,0 +1,16 @@
package com.hiczp.bilibili.api.live.socket.event;
import java.util.EventObject;
public abstract class ReceivePackageEvent<T> extends EventObject {
private T entity;
ReceivePackageEvent(Object source, T entity) {
super(source);
this.entity = entity;
}
public T getEntity() {
return entity;
}
}

View File

@ -0,0 +1,9 @@
package com.hiczp.bilibili.api.live.socket.event;
import com.hiczp.bilibili.api.live.socket.entity.RoomBlockMsgEntity;
public class RoomBlockMsgPackageEvent extends ReceivePackageEvent<RoomBlockMsgEntity> {
public RoomBlockMsgPackageEvent(Object source, RoomBlockMsgEntity entity) {
super(source, entity);
}
}

View File

@ -0,0 +1,9 @@
package com.hiczp.bilibili.api.live.socket.event;
import com.hiczp.bilibili.api.live.socket.entity.RoomSilentOffEntity;
public class RoomSilentOffPackageEvent extends ReceivePackageEvent<RoomSilentOffEntity> {
public RoomSilentOffPackageEvent(Object source, RoomSilentOffEntity entity) {
super(source, entity);
}
}

View File

@ -2,17 +2,8 @@ package com.hiczp.bilibili.api.live.socket.event;
import com.hiczp.bilibili.api.live.socket.entity.SendGiftEntity;
import java.util.EventObject;
public class SendGiftPackageEvent extends EventObject {
private SendGiftEntity sendGiftEntity;
public SendGiftPackageEvent(Object source, SendGiftEntity sendGiftEntity) {
super(source);
this.sendGiftEntity = sendGiftEntity;
}
public SendGiftEntity getSendGiftEntity() {
return sendGiftEntity;
public class SendGiftPackageEvent extends ReceivePackageEvent<SendGiftEntity> {
public SendGiftPackageEvent(Object source, SendGiftEntity entity) {
super(source, entity);
}
}

View File

@ -0,0 +1,9 @@
package com.hiczp.bilibili.api.live.socket.event;
import java.util.EventObject;
public class SendHeartBeatPackageEvent extends EventObject {
public SendHeartBeatPackageEvent(Object source) {
super(source);
}
}

View File

@ -2,17 +2,8 @@ package com.hiczp.bilibili.api.live.socket.event;
import com.hiczp.bilibili.api.live.socket.entity.SysGiftEntity;
import java.util.EventObject;
public class SysGiftPackageEvent extends EventObject {
private SysGiftEntity sysGiftEntity;
public SysGiftPackageEvent(Object source, SysGiftEntity sysGiftEntity) {
super(source);
this.sysGiftEntity = sysGiftEntity;
}
public SysGiftEntity getSysGiftEntity() {
return sysGiftEntity;
public class SysGiftPackageEvent extends ReceivePackageEvent<SysGiftEntity> {
public SysGiftPackageEvent(Object source, SysGiftEntity entity) {
super(source, entity);
}
}

View File

@ -2,17 +2,8 @@ package com.hiczp.bilibili.api.live.socket.event;
import com.hiczp.bilibili.api.live.socket.entity.SysMsgEntity;
import java.util.EventObject;
public class SysMsgPackageEvent extends EventObject {
private SysMsgEntity sysMsgEntity;
public SysMsgPackageEvent(Object source, SysMsgEntity sysMsgEntity) {
super(source);
this.sysMsgEntity = sysMsgEntity;
}
public SysMsgEntity getSysMsgEntity() {
return sysMsgEntity;
public class SysMsgPackageEvent extends ReceivePackageEvent<SysMsgEntity> {
public SysMsgPackageEvent(Object source, SysMsgEntity entity) {
super(source, entity);
}
}

View File

@ -2,17 +2,8 @@ package com.hiczp.bilibili.api.live.socket.event;
import com.hiczp.bilibili.api.live.socket.entity.WelcomeGuardEntity;
import java.util.EventObject;
public class WelcomeGuardPackageEvent extends EventObject {
private WelcomeGuardEntity welcomeGuardEntity;
public WelcomeGuardPackageEvent(Object source, WelcomeGuardEntity welcomeGuardEntity) {
super(source);
this.welcomeGuardEntity = welcomeGuardEntity;
}
public WelcomeGuardEntity getWelcomeGuardEntity() {
return welcomeGuardEntity;
public class WelcomeGuardPackageEvent extends ReceivePackageEvent<WelcomeGuardEntity> {
public WelcomeGuardPackageEvent(Object source, WelcomeGuardEntity entity) {
super(source, entity);
}
}

View File

@ -2,17 +2,8 @@ package com.hiczp.bilibili.api.live.socket.event;
import com.hiczp.bilibili.api.live.socket.entity.WelcomeEntity;
import java.util.EventObject;
public class WelcomePackageEvent extends EventObject {
private WelcomeEntity welcomeEntity;
public WelcomePackageEvent(Object source, WelcomeEntity welcomeEntity) {
super(source);
this.welcomeEntity = welcomeEntity;
}
public WelcomeEntity getWelcomeEntity() {
return welcomeEntity;
public class WelcomePackageEvent extends ReceivePackageEvent<WelcomeEntity> {
public WelcomePackageEvent(Object source, WelcomeEntity entity) {
super(source, entity);
}
}

View File

@ -0,0 +1,9 @@
package com.hiczp.bilibili.api.live.socket.event;
import com.hiczp.bilibili.api.live.socket.entity.WishBottleEntity;
public class WishBottlePackageEvent extends ReceivePackageEvent<WishBottleEntity> {
public WishBottlePackageEvent(Object source, WishBottleEntity entity) {
super(source, entity);
}
}

View File

@ -69,7 +69,10 @@ public class LiveClientHandler extends SimpleChannelInboundHandler<Package> {
case ENTER_ROOM_SUCCESS: {
eventBus.post(new ConnectSucceedEvent(this));
ctx.executor().scheduleAtFixedRate(
() -> ctx.writeAndFlush(PackageHelper.createHeatBeatPackage()),
() -> {
ctx.writeAndFlush(PackageHelper.createHeartBeatPackage());
eventBus.post(new SendHeartBeatPackageEvent(this));
},
0L,
30L,
TimeUnit.SECONDS
@ -120,6 +123,22 @@ public class LiveClientHandler extends SimpleChannelInboundHandler<Package> {
eventCreationExpression = () -> new ActivityEventPackageEvent(this, GSON.fromJson(content, ActivityEventEntity.class));
}
break;
case "WISH_BOTTLE": {
eventCreationExpression = () -> new WishBottlePackageEvent(this, GSON.fromJson(content, WishBottleEntity.class));
}
break;
case "ROOM_BLOCK_MSG": {
eventCreationExpression = () -> new RoomBlockMsgPackageEvent(this, GSON.fromJson(content, RoomBlockMsgEntity.class));
}
break;
case "ROOM_SILENT_OFF": {
eventCreationExpression = () -> new RoomSilentOffPackageEvent(this, GSON.fromJson(content, RoomSilentOffEntity.class));
}
break;
case "GUARD_MSG": {
eventCreationExpression = () -> new GuardMsgPackageEvent(this, GSON.fromJson(content, GuardMsgEntity.class));
}
break;
default: {
LOGGER.error("Received unknown json below: \n{}", formatJson(content));
eventCreationExpression = () -> new UnknownPackageEvent(this, content);

View File

@ -66,7 +66,7 @@ public class LiveClientTest {
@Subscribe
public void activityEvent(ActivityEventPackageEvent activityEventPackageEvent) {
ActivityEventEntity.Data data = activityEventPackageEvent.getActivityEventEntity().getData();
ActivityEventEntity.Data data = activityEventPackageEvent.getEntity().getData();
LOGGER.info("[ActivityEvent] keyword: {}, type: {}, progress: {}%",
data.getKeyword(),
data.getType(),
@ -86,7 +86,7 @@ public class LiveClientTest {
@Subscribe
public void danMuMsg(DanMuMsgPackageEvent danMuMsgPackageEvent) {
DanMuMsgEntity danMuMsgEntity = danMuMsgPackageEvent.getDanMuMsgEntity();
DanMuMsgEntity danMuMsgEntity = danMuMsgPackageEvent.getEntity();
StringBuilder stringBuilder = new StringBuilder("[DanMuMsg] ");
danMuMsgEntity.getFansMedalName().ifPresent(fansMedalName ->
@ -110,17 +110,17 @@ public class LiveClientTest {
@Subscribe
public void live(LivePackageEvent livePackageEvent) {
LOGGER.info("[Live] Room {} start live", livePackageEvent.getLiveEntity().getRoomId());
LOGGER.info("[Live] Room {} start live", livePackageEvent.getEntity().getRoomId());
}
@Subscribe
public void preparing(PreparingPackageEvent preparingPackageEvent) {
LOGGER.info("[Preparing] Room {} stop live", preparingPackageEvent.getPreparingEntity().getRoomId());
LOGGER.info("[Preparing] Room {} stop live", preparingPackageEvent.getEntity().getRoomId());
}
@Subscribe
public void sendGift(SendGiftPackageEvent sendGiftPackageEvent) {
SendGiftEntity.DataEntity dataEntity = sendGiftPackageEvent.getSendGiftEntity().getData();
SendGiftEntity.DataEntity dataEntity = sendGiftPackageEvent.getEntity().getData();
LOGGER.info("[SendGift] {} give {}*{}",
dataEntity.getUserName(),
dataEntity.getGiftName(),
@ -130,7 +130,7 @@ public class LiveClientTest {
@Subscribe
public void SysGift(SysGiftPackageEvent sysGiftPackageEvent) {
SysGiftEntity sysGiftEntity = sysGiftPackageEvent.getSysGiftEntity();
SysGiftEntity sysGiftEntity = sysGiftPackageEvent.getEntity();
LOGGER.info("[SysGift] {}: {}",
sysGiftEntity.getMsg(),
sysGiftEntity.getUrl()
@ -139,7 +139,7 @@ public class LiveClientTest {
@Subscribe
public void SysMsg(SysMsgPackageEvent sysMsgPackageEvent) {
SysMsgEntity sysMsgEntity = sysMsgPackageEvent.getSysMsgEntity();
SysMsgEntity sysMsgEntity = sysMsgPackageEvent.getEntity();
LOGGER.info("[SysMsg] {}: {}",
sysMsgEntity.getMsg(),
sysMsgEntity.getUrl()
@ -153,7 +153,7 @@ public class LiveClientTest {
@Subscribe
public void WelcomeGuard(WelcomeGuardPackageEvent welcomeGuardPackageEvent) {
WelcomeGuardEntity.DataEntity dataEntity = welcomeGuardPackageEvent.getWelcomeGuardEntity().getData();
WelcomeGuardEntity.DataEntity dataEntity = welcomeGuardPackageEvent.getEntity().getData();
LOGGER.info("[WelcomeGuard] [GL {}] {}",
dataEntity.getGuardLevel(),
dataEntity.getUsername()
@ -162,7 +162,7 @@ public class LiveClientTest {
@Subscribe
public void Welcome(WelcomePackageEvent welcomePackageEvent) {
WelcomeEntity.DataEntity dataEntity = welcomePackageEvent.getWelcomeEntity().getData();
WelcomeEntity.DataEntity dataEntity = welcomePackageEvent.getEntity().getData();
StringBuilder stringBuilder = new StringBuilder("[Welcome] ");
if (dataEntity.isAdmin()) {
stringBuilder.append("[ADMIN] ");