diff --git a/build.gradle b/build.gradle index dae305c..9a6c082 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ buildscript { ext { kotlin_version = '1.3.21' kotlin_coroutines_version = '1.1.1' + ktor_version = '1.1.3' jvm_target = JavaVersion.VERSION_1_8 } @@ -66,6 +67,14 @@ dependencies { compile group: 'com.squareup.okhttp3', name: 'logging-interceptor', version: '3.13.1' } +//ktor +dependencies { + // https://mvnrepository.com/artifact/io.ktor/ktor-client-websocket + compile group: 'io.ktor', name: 'ktor-client-websocket', version: ktor_version + // https://mvnrepository.com/artifact/io.ktor/ktor-client-cio + compile group: 'io.ktor', name: 'ktor-client-cio', version: ktor_version +} + //utils dependencies { // https://mvnrepository.com/artifact/commons-io/commons-io diff --git a/src/main/kotlin/com/hiczp/bilibili/api/danmaku/Danmaku.kt b/src/main/kotlin/com/hiczp/bilibili/api/danmaku/Danmaku.kt index cf7a925..5222052 100644 --- a/src/main/kotlin/com/hiczp/bilibili/api/danmaku/Danmaku.kt +++ b/src/main/kotlin/com/hiczp/bilibili/api/danmaku/Danmaku.kt @@ -9,7 +9,7 @@ data class Danmaku( val id: Long, /** - * 下标 1, 不明属性 + * TODO 下标 1, 不明属性 */ val unknownAttribute1: String, @@ -40,7 +40,7 @@ data class Danmaku( val timestamp: Long, /** - * 下标 7, 不明属性 + * TODO 下标 7, 不明属性 */ val unknownAttribute7: String, diff --git a/src/main/kotlin/com/hiczp/bilibili/api/live/LiveAPI.kt b/src/main/kotlin/com/hiczp/bilibili/api/live/LiveAPI.kt index a2e27ce..c9d951b 100644 --- a/src/main/kotlin/com/hiczp/bilibili/api/live/LiveAPI.kt +++ b/src/main/kotlin/com/hiczp/bilibili/api/live/LiveAPI.kt @@ -60,8 +60,32 @@ interface LiveAPI { fun getAnchorInRoom(@Query("roomid") roomId: Long): Deferred /** - * 获取自己在直播间的基本信息, 包括自己的直播间号, 银瓜子, 金瓜子数量等 + * 获取自己在直播站的基本信息, 包括自己的直播间号, 银瓜子, 金瓜子数量等 */ @GET("/mobile/getUser") fun getUser(): Deferred + + /** + * 获取自己在当前直播间的信息, 包括自己的权限以及是否是管理员等 + * + * @param roomId 房间号 + */ + @Suppress("SpellCheckingInspection") + @GET("/live_user/v1/UserInfo/get_info_in_room") + fun getUserInfoInRoom(@Query("roomid") roomId: Long): Deferred + + /** + * 获取所有头衔 + * + * @param scale 屏幕尺寸 + */ + @GET("/appUser/getTitle") + fun getTitle(@Query("scale") scale: String = "xxhdpi"): Deferred + + /** + * 查询是否关注了当前主播 + */ + @POST("/relation/v1/Feed/isFollowed") + @FormUrlEncoded + fun isFollowed(@Field("follow") follow: Long): Deferred<Follow> } diff --git a/src/main/kotlin/com/hiczp/bilibili/api/live/model/AnchorInRoom.kt b/src/main/kotlin/com/hiczp/bilibili/api/live/model/AnchorInRoom.kt index f4a3643..67fcefb 100644 --- a/src/main/kotlin/com/hiczp/bilibili/api/live/model/AnchorInRoom.kt +++ b/src/main/kotlin/com/hiczp/bilibili/api/live/model/AnchorInRoom.kt @@ -18,29 +18,29 @@ data class AnchorInRoom( @SerializedName("level") var level: Level, @SerializedName("san") - var san: String // 12 + var san: Int // 12 ) { data class Info( @SerializedName("face") - var face: String, // https://i1.hdslb.com/bfs/face/4f65e79399ad5a1bf3f877851b2f819d5870b494.jpg + var face: String, // https://i0.hdslb.com/bfs/face/0434dccc0ec4de223e8ca374dea06a6e1e8eb471.jpg @SerializedName("gender") var gender: Int, // 0 @SerializedName("identification") - var identification: Int, // 1 + var identification: Int? = null, // 1 @SerializedName("mobile_verify") - var mobileVerify: Int, // 1 + var mobileVerify: Int, // 0 @SerializedName("official_verify") var officialVerify: OfficialVerify, @SerializedName("platform_user_level") - var platformUserLevel: Int, // 4 + var platformUserLevel: Int, // 5 @SerializedName("rank") var rank: String, // 10000 @SerializedName("uid") - var uid: Long, // 20293030 + var uid: Long, // 2866663 @SerializedName("uname") - var uname: String, // czp3009 + var uname: String, // hyx5020 @SerializedName("vip_type") - var vipType: Int // 0 + var vipType: Int // 2 ) { data class OfficialVerify( @SerializedName("desc") @@ -54,35 +54,35 @@ data class AnchorInRoom( data class Level( @SerializedName("anchor_score") - var anchorScore: Int, // 27376 + var anchorScore: Int, // 36685 @SerializedName("color") var color: Int, // 5805790 @SerializedName("cost") - var cost: Int, // 52187800 + var cost: Int, // 29431298 @SerializedName("master_level") var masterLevel: MasterLevel, @SerializedName("rcost") - var rcost: Int, // 2737665 + var rcost: Int, // 3668592 @SerializedName("svip") var svip: Int, // 0 @SerializedName("svip_time") - var svipTime: String, // 0000-00-00 00:00:00 + var svipTime: String, // 2019-02-09 11:03:54 @SerializedName("uid") - var uid: Int, // 20293030 + var uid: Int, // 2866663 @SerializedName("update_time") - var updateTime: String, // 2019-03-12 16:00:48 + var updateTime: String, // 2019-03-12 23:00:42 @SerializedName("user_level") - var userLevel: Int, // 25 + var userLevel: Int, // 22 @SerializedName("user_score") var userScore: String, // 0 @SerializedName("vip") var vip: Int, // 0 @SerializedName("vip_time") - var vipTime: String // 2018-05-15 12:00:50 + var vipTime: String // 2019-02-09 11:03:54 ) { data class MasterLevel( @SerializedName("anchor_score") - var anchorScore: Int, // 27376 + var anchorScore: Int, // 36685 @SerializedName("color") var color: Int, // 5805790 @SerializedName("current") @@ -96,7 +96,7 @@ data class AnchorInRoom( @SerializedName("sort") var sort: String, // >10000 @SerializedName("upgrade_score") - var upgradeScore: Int // 12234 + var upgradeScore: Int // 2925 ) } } diff --git a/src/main/kotlin/com/hiczp/bilibili/api/live/model/DanmakuConfig.kt b/src/main/kotlin/com/hiczp/bilibili/api/live/model/DanmakuConfig.kt index bf734a1..3105c25 100644 --- a/src/main/kotlin/com/hiczp/bilibili/api/live/model/DanmakuConfig.kt +++ b/src/main/kotlin/com/hiczp/bilibili/api/live/model/DanmakuConfig.kt @@ -13,18 +13,30 @@ data class DanmakuConfig( var msg: String // ok ) { data class Data( + /** + * 推荐服务器 + */ @SerializedName("host") var host: String, // broadcastlv.chat.bilibili.com + /** + * 服务器列表 + */ @SerializedName("host_server_list") var hostServerList: List<HostServer>, @SerializedName("max_delay") var maxDelay: Int, // 5000 + /** + * 这里的端口是非 TSL 的 websocket 端口, 需要到 host_server_list 中寻找推荐服务器对应的 websocket TSL 端口(通常是 443) + */ @SerializedName("port") var port: Int, // 2243 @SerializedName("refresh_rate") var refreshRate: Int, // 100 @SerializedName("refresh_row_factor") var refreshRowFactor: Double, // 0.125 + /** + * 如果 DNS 失效可以使用该列表中的 IP + */ @SerializedName("server_list") var serverList: List<Server> ) { @@ -40,8 +52,14 @@ data class DanmakuConfig( var host: String, // broadcastlv.chat.bilibili.com @SerializedName("port") var port: Int, // 2243 + /** + * websocket 端口 + */ @SerializedName("ws_port") var wsPort: Int, // 2244 + /** + * websocket TSL 端口 + */ @SerializedName("wss_port") var wssPort: Int // 443 ) diff --git a/src/main/kotlin/com/hiczp/bilibili/api/live/model/Follow.kt b/src/main/kotlin/com/hiczp/bilibili/api/live/model/Follow.kt new file mode 100644 index 0000000..c4f16e4 --- /dev/null +++ b/src/main/kotlin/com/hiczp/bilibili/api/live/model/Follow.kt @@ -0,0 +1,19 @@ +package com.hiczp.bilibili.api.live.model + +import com.google.gson.annotations.SerializedName + +data class Follow( + @SerializedName("code") + var code: Int, // 0 + @SerializedName("data") + var `data`: Data, + @SerializedName("message") + var message: String, // success + @SerializedName("msg") + var msg: String // success +) { + data class Data( + @SerializedName("follow") + var follow: Int // 0 + ) +} diff --git a/src/main/kotlin/com/hiczp/bilibili/api/live/model/MobileTab.kt b/src/main/kotlin/com/hiczp/bilibili/api/live/model/MobileTab.kt new file mode 100644 index 0000000..5fea665 --- /dev/null +++ b/src/main/kotlin/com/hiczp/bilibili/api/live/model/MobileTab.kt @@ -0,0 +1,36 @@ +package com.hiczp.bilibili.api.live.model + +import com.google.gson.annotations.SerializedName + +data class MobileTab( + @SerializedName("code") + var code: Int, // 0 + @SerializedName("data") + var `data`: List<Tab>, + @SerializedName("message") + var message: String, + @SerializedName("msg") + var msg: String +) { + data class Tab( + /** + * 如果是非顶层 Tab 则 default 为 null + */ + @SerializedName("default") + var default: Int? = null, // 0 + @SerializedName("default_sub_tab") + var defaultSubTab: String, + @SerializedName("desc") + var desc: String, // 友爱社 + @SerializedName("order") + var order: Int, // 600 + @SerializedName("status") + var status: Int, // 0 + @SerializedName("sub_tab") + var subTab: List<Tab>, + @SerializedName("type") + var type: String, // love-club + @SerializedName("url") + var url: String + ) +} diff --git a/src/main/kotlin/com/hiczp/bilibili/api/live/model/RoomInfo.kt b/src/main/kotlin/com/hiczp/bilibili/api/live/model/RoomInfo.kt index 3beb1cd..b6873ad 100644 --- a/src/main/kotlin/com/hiczp/bilibili/api/live/model/RoomInfo.kt +++ b/src/main/kotlin/com/hiczp/bilibili/api/live/model/RoomInfo.kt @@ -24,6 +24,9 @@ data class RoomInfo( var areaName: String, // 其他游戏 @SerializedName("area_pendants") var areaPendants: String, + /** + * 粉丝数 + */ @SerializedName("attention") var attention: Int, // 62 @SerializedName("background") diff --git a/src/main/kotlin/com/hiczp/bilibili/api/live/model/Title.kt b/src/main/kotlin/com/hiczp/bilibili/api/live/model/Title.kt new file mode 100644 index 0000000..6938158 --- /dev/null +++ b/src/main/kotlin/com/hiczp/bilibili/api/live/model/Title.kt @@ -0,0 +1,37 @@ +package com.hiczp.bilibili.api.live.model + +import com.google.gson.annotations.SerializedName + +data class Title( + @SerializedName("code") + var code: Int, // 0 + @SerializedName("data") + var `data`: List<Title>, + @SerializedName("message") + var message: String, // success + @SerializedName("msg") + var msg: String // success +) { + data class Title( + @SerializedName("colorful") + var colorful: Int, // 0 + @SerializedName("height") + var height: Int, // 20 + @SerializedName("id") + var id: String, // cake-flour + @SerializedName("img") + var img: String, // https://s1.hdslb.com/bfs/static/blive/live-assets/mobile/titles/title/3/cake-flour.png?20180726173300 + @SerializedName("is_lihui") + var isLihui: Int, // 0 + @SerializedName("lihui_height") + var lihuiHeight: Int, // 0 + @SerializedName("lihui_img") + var lihuiImg: String, + @SerializedName("lihui_width") + var lihuiWidth: Int, // 0 + @SerializedName("title") + var title: String, // 2016 新春活动 + @SerializedName("width") + var width: Int // 68 + ) +} diff --git a/src/main/kotlin/com/hiczp/bilibili/api/live/model/User.kt b/src/main/kotlin/com/hiczp/bilibili/api/live/model/User.kt index d0df28b..7ee5e46 100644 --- a/src/main/kotlin/com/hiczp/bilibili/api/live/model/User.kt +++ b/src/main/kotlin/com/hiczp/bilibili/api/live/model/User.kt @@ -1,5 +1,6 @@ package com.hiczp.bilibili.api.live.model +import com.google.gson.JsonElement import com.google.gson.annotations.SerializedName data class User( @@ -18,7 +19,7 @@ data class User( @SerializedName("isSign") var isSign: Int, // 0 @SerializedName("medal") - var medal: Any?, // null + var medal: JsonElement?, // null @SerializedName("new") var new: Int, // 1 @SerializedName("room_id") diff --git a/src/main/kotlin/com/hiczp/bilibili/api/live/model/UserInfoInRoom.kt b/src/main/kotlin/com/hiczp/bilibili/api/live/model/UserInfoInRoom.kt new file mode 100644 index 0000000..e4af65b --- /dev/null +++ b/src/main/kotlin/com/hiczp/bilibili/api/live/model/UserInfoInRoom.kt @@ -0,0 +1,181 @@ +package com.hiczp.bilibili.api.live.model + +import com.google.gson.JsonElement +import com.google.gson.annotations.SerializedName + +data class UserInfoInRoom( + @SerializedName("code") + var code: Int, // 0 + @SerializedName("data") + var `data`: Data, + @SerializedName("message") + var message: String, // success + @SerializedName("msg") + var msg: String // success +) { + data class Data( + @SerializedName("entry_effect") + var entryEffect: EntryEffect, + @SerializedName("gift") + var gift: Gift, + @SerializedName("info") + var info: Info, + @SerializedName("level") + var level: Level, + @SerializedName("new") + var new: Int, // 1 + @SerializedName("privilege") + var privilege: Privilege, + @SerializedName("role") + var role: Role, + @SerializedName("room_admin") + var roomAdmin: RoomAdmin, + @SerializedName("wallet") + var wallet: Wallet + ) { + data class Wallet( + @SerializedName("gold") + var gold: String, // 0 + @SerializedName("silver") + var silver: String // 140258 + ) + + data class Info( + @SerializedName("bili_vip") + var biliVip: Int, // 0 + @SerializedName("face") + var face: String, // https://i1.hdslb.com/bfs/face/4f65e79399ad5a1bf3f877851b2f819d5870b494.jpg + @SerializedName("gender") + var gender: Int, // 0 + @SerializedName("identification") + var identification: Int, // 1 + @SerializedName("mobile_verify") + var mobileVerify: Int, // 1 + @SerializedName("mobile_virtual") + var mobileVirtual: Int, // 0 + @SerializedName("official_verify") + var officialVerify: OfficialVerify, + @SerializedName("platform_user_level") + var platformUserLevel: Int, // 4 + @SerializedName("rank") + var rank: String, // 10000 + @SerializedName("uid") + var uid: Long, // 20293030 + @SerializedName("uname") + var uname: String, // czp3009 + @SerializedName("vip_type") + var vipType: Int // 0 + ) { + data class OfficialVerify( + @SerializedName("desc") + var desc: String, + @SerializedName("role") + var role: Int, // 0 + @SerializedName("type") + var type: Int // -1 + ) + } + + data class Level( + @SerializedName("color") + var color: Int, // 5805790 + @SerializedName("cost") + var cost: Int, // 52187800 + @SerializedName("is_show_vip_broadcast") + var isShowVipBroadcast: Int, // 0 + @SerializedName("master_level") + var masterLevel: MasterLevel, + @SerializedName("rcost") + var rcost: Int, // 2737665 + @SerializedName("svip") + var svip: Int, // 0 + @SerializedName("svip_time") + var svipTime: String, // 0000-00-00 00:00:00 + @SerializedName("uid") + var uid: Int, // 20293030 + @SerializedName("update_time") + var updateTime: String, // 0000-00-00 00:00:00 + @SerializedName("user_level") + var userLevel: Int, // 25 + @SerializedName("user_level_rank") + var userLevelRank: String, // >50000 + @SerializedName("user_score") + var userScore: String, // 0 + @SerializedName("vip") + var vip: Int, // 0 + @SerializedName("vip_time") + var vipTime: String // 2018-05-15 12:00:50 + ) { + data class MasterLevel( + @SerializedName("color") + var color: Int, // 5805790 + @SerializedName("current") + var current: List<Int>, + @SerializedName("level") + var level: Int, // 11 + @SerializedName("next") + var next: List<Int> + ) + } + + data class RoomAdmin( + @SerializedName("is_admin") + var isAdmin: Int // 0 + ) + + data class Role( + @SerializedName("info") + var info: Info, + @SerializedName("role_id") + var roleId: Int, // 2 + @SerializedName("role_name") + var roleName: String // 播主 + ) { + data class Info( + @SerializedName("roomid") + var roomid: Long // 1110317 + ) + } + + data class EntryEffect( + @SerializedName("basemap_url") + var basemapUrl: String, + @SerializedName("copy_writing") + var copyWriting: String, + @SerializedName("effective_time") + var effectiveTime: Int, // 0 + @SerializedName("face") + var face: String, // https://i1.hdslb.com/bfs/face/4f65e79399ad5a1bf3f877851b2f819d5870b494.jpg + @SerializedName("highlight_color") + var highlightColor: String, + @SerializedName("id") + var id: Int, // 0 + @SerializedName("mock_effect") + var mockEffect: Int, // 0 + @SerializedName("priority") + var priority: Int, // 0 + @SerializedName("privilege_type") + var privilegeType: Int, // 0 + @SerializedName("show_avatar") + var showAvatar: Int, // 0 + @SerializedName("target_id") + var targetId: Long, // 2866663 + @SerializedName("uid") + var uid: Long // 20293030 + ) + + data class Privilege( + @SerializedName("broadcast") + var broadcast: JsonElement?, // null + @SerializedName("notice_status") + var noticeStatus: Int // 1 + ) + + data class Gift( + @SerializedName("is_show") + var isShow: String, // 1 + @SerializedName("uid") + var uid: Long // 20293030 + ) + } +} diff --git a/src/test/kotlin/com/hiczp/bilibili/api/test/IsFollowedTest.kt b/src/test/kotlin/com/hiczp/bilibili/api/test/IsFollowedTest.kt new file mode 100644 index 0000000..173146c --- /dev/null +++ b/src/test/kotlin/com/hiczp/bilibili/api/test/IsFollowedTest.kt @@ -0,0 +1,13 @@ +package com.hiczp.bilibili.api.test + +import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.Test + +class IsFollowedTest { + @Test + fun followLive() { + runBlocking { + bilibiliClient.liveAPI.isFollowed(20293030).await() + } + } +}