feat: add view count on bv parse card

This commit is contained in:
2024-09-14 22:00:18 +08:00
parent eb65a89425
commit 78487514ce
6 files changed
+115 -58

No files matched your search

@@ -83,7 +83,7 @@ class FancyBot : OBMessage {
} }
BVParseCommand.getShortUrlBVID(shortUrl) BVParseCommand.getShortUrlBVID(shortUrl)
} else { } else {
message.rawMessage.split("/")[4] message.rawMessage.split("?").first().split("/").filter { it.isNotEmpty() && it.isNotBlank() }.last()
} }
BVParseCommand.parse(this, bvid, message) BVParseCommand.parse(this, bvid, message)
} }
@@ -7,8 +7,10 @@
package cn.rtast.fancybot.commands.parse package cn.rtast.fancybot.commands.parse
import cn.rtast.fancybot.entity.bili.BVID import cn.rtast.fancybot.entity.bili.ShortUrl
import cn.rtast.fancybot.entity.bili.UserStat import cn.rtast.fancybot.entity.bili.UserStat
import cn.rtast.fancybot.entity.bili.VideoStat
import cn.rtast.fancybot.entity.bili.ViewCount
import cn.rtast.fancybot.util.Http import cn.rtast.fancybot.util.Http
import cn.rtast.fancybot.util.Resources import cn.rtast.fancybot.util.Resources
import cn.rtast.fancybot.util.drawCustomImage import cn.rtast.fancybot.util.drawCustomImage
@@ -30,8 +32,10 @@ import javax.imageio.ImageIO
object BVParseCommand { object BVParseCommand {
private const val CID_URL = "https://api.bilibili.com/x/web-interface/view" private const val VIDEO_STAT_URL = "https://api.bilibili.com/x/web-interface/view"
private const val USER_STAT_URL = "https://api.bilibili.com/x/relation/stat" private const val USER_STAT_URL = "https://api.bilibili.com/x/relation/stat"
private const val SHORT_URL_API_URL = "https://api.bilibili.com/x/share/click"
private const val VIEW_COUNT_URL = "https://api.bilibili.com/x/player/online/total"
private const val CANVAS_WIDTH = 1000 private const val CANVAS_WIDTH = 1000
private const val CANVAS_HEIGHT = 600 private const val CANVAS_HEIGHT = 600
private val titleFont = Font("Serif", Font.ITALIC, 40) private val titleFont = Font("Serif", Font.ITALIC, 40)
@@ -46,12 +50,32 @@ object BVParseCommand {
private val tempOkHttpClient = OkHttpClient() private val tempOkHttpClient = OkHttpClient()
fun getShortUrlBVID(shortUrl: String): String { private fun generateShortUrl(bvid: String, oid: Long): String {
val request = Request.Builder().url(shortUrl.split(" ").last()).build() val result = Http.post<ShortUrl>(
val redirectedUrl = tempOkHttpClient.newCall(request).execute() SHORT_URL_API_URL, mapOf(
redirectedUrl.use { "buvid" to bvid,
return it.request.url.toString().split("/")[4].split("?").first() "build" to 6114514,
"platform" to "unix",
"share_channel" to "COPY",
"share_mode" to 2,
"share_id" to "main.ugc-video-detail.0.0.pv",
"oid" to oid
)
)
return result.data.content.split(" ").last()
} }
private fun getViewCount(bvid: String, cid: Long): String {
return Http.get<ViewCount>(
VIEW_COUNT_URL, mapOf(
"bvid" to bvid,
"cid" to cid
)
).data.total
}
private fun getVideoStat(bvid: String): VideoStat {
return Http.get<VideoStat>(VIDEO_STAT_URL, mapOf("bvid" to bvid))
} }
private fun createResponseImage( private fun createResponseImage(
@@ -66,6 +90,7 @@ object BVParseCommand {
like: String, like: String,
favorite: String, favorite: String,
fans: Int, fans: Int,
viewCount: String,
): String { ): String {
val coverImage = ImageIO.read(URI(picUrl).toURL()) val coverImage = ImageIO.read(URI(picUrl).toURL())
val faceImage = ImageIO.read(URI(authorFace).toURL()) val faceImage = ImageIO.read(URI(authorFace).toURL())
@@ -104,6 +129,8 @@ object BVParseCommand {
// draw fans count // draw fans count
g2d.font = numberFont g2d.font = numberFont
g2d.drawString(fans.formatNumber() + "粉丝", 120, 520) g2d.drawString(fans.formatNumber() + "粉丝", 120, 520)
// draw viewing count
g2d.drawString("${viewCount}人正在观看", 40, 460)
// draw author face // draw author face
g2d.drawCustomImage(faceImage, 40, 480, 60.0, 60.0, true) g2d.drawCustomImage(faceImage, 40, 480, 60.0, 60.0, true)
// draw cover image // draw cover image
@@ -112,8 +139,18 @@ object BVParseCommand {
return canvas.toByteArray().encodeToBase64() return canvas.toByteArray().encodeToBase64()
} }
fun getShortUrlBVID(shortUrl: String): String {
val request = Request.Builder().url(shortUrl.split(" ").last()).build()
val redirectedUrl = tempOkHttpClient.newCall(request).execute()
redirectedUrl.use {
return it.request.url.toString().split("/")[4].split("?").first()
}
}
suspend fun parse(listener: OBMessage, bvid: String, message: GroupMessage) { suspend fun parse(listener: OBMessage, bvid: String, message: GroupMessage) {
val videoInfo = Http.get<BVID>(CID_URL, mapOf("bvid" to bvid)) val videoInfo = this.getVideoStat(bvid)
val viewCount = this.getViewCount(bvid, videoInfo.data.cid)
val shortUrl = this.generateShortUrl(bvid, videoInfo.data.aid)
val fans = Http.get<UserStat>(USER_STAT_URL, mapOf("vmid" to videoInfo.data.owner.mid)).data.follower val fans = Http.get<UserStat>(USER_STAT_URL, mapOf("vmid" to videoInfo.data.owner.mid)).data.follower
val authorFace = videoInfo.data.owner.face val authorFace = videoInfo.data.owner.face
val title = videoInfo.data.title val title = videoInfo.data.title
@@ -128,11 +165,12 @@ object BVParseCommand {
val image = this.createResponseImage( val image = this.createResponseImage(
title, author, authorFace, title, author, authorFace,
coverPicUrl, reply, view, coin, coverPicUrl, reply, view, coin,
share, like, favorite, fans share, like, favorite, fans, viewCount
) )
val msg = MessageChain.Builder() val msg = MessageChain.Builder()
.addReply(message.messageId) .addReply(message.messageId)
.addImage(image, true) .addImage(image, true)
.addText(shortUrl)
.build() .build()
listener.sendGroupMessage(message.groupId, msg) listener.sendGroupMessage(message.groupId, msg)
} }
@@ -0,0 +1,16 @@
/*
* Copyright © 2024 RTAkland
* Author: RTAkland
* Date: 2024/9/14
*/
package cn.rtast.fancybot.entity.bili
data class ShortUrl(
val data: Data
) {
data class Data(
val content: String
)
}
@@ -7,10 +7,11 @@
package cn.rtast.fancybot.entity.bili package cn.rtast.fancybot.entity.bili
data class BVID( data class VideoStat(
val data: Data val data: Data
) { ) {
data class Data( data class Data(
val aid: Long,
val pic: String, val pic: String,
val title: String, val title: String,
val owner: Owner, val owner: Owner,
@@ -0,0 +1,16 @@
/*
* Copyright © 2024 RTAkland
* Author: RTAkland
* Date: 2024/9/14
*/
package cn.rtast.fancybot.entity.bili
data class ViewCount(
val data: Data
) {
data class Data(
val total: String
)
}
+32 -46
View File
@@ -100,7 +100,7 @@ object Http {
@JvmOverloads @JvmOverloads
inline fun <reified T> post( inline fun <reified T> post(
url: String, url: String,
formBody: Map<String, String>? = null, formBody: Map<String, Any>? = null,
headers: Map<String, String>? = null, headers: Map<String, String>? = null,
params: Map<String, Any>? = null params: Map<String, Any>? = null
): T { ): T {
@@ -108,7 +108,7 @@ object Http {
val paramsUrl = buildParams(url, params) val paramsUrl = buildParams(url, params)
formBody?.let { formBody?.let {
it.forEach { (key, value) -> it.forEach { (key, value) ->
body.add(key, value) body.add(key, value.toString())
} }
} }
val request = Request.Builder() val request = Request.Builder()
@@ -146,48 +146,34 @@ object Http {
return this.executeRequest(headerRequest.build()) return this.executeRequest(headerRequest.build())
} }
inline fun <reified T> getAsync( // @JvmOverloads
url: String, // fun post(
params: Map<String, Any>? = null, // url: String,
headers: Map<String, String>? = null, // form: Map<String, Any>,
noinline callback: (T?) -> Unit // headers: Map<String, String>? = null,
) { // params: Map<String, Any>? = null
val paramsUrl = buildParams(url, params) // ): String {
val request = Request.Builder() // val formBody = FormBody.Builder()
.url(paramsUrl) // form.forEach {
.get() // formBody.add(it.key, it.value.toString())
// }
executeRequestAsync(request, headers) { response -> // val paramsUrl = buildParams(url, params)
try { // val request = Request.Builder()
callback(response.fromJson<T>()) // .post(formBody.build())
} catch (e: Exception) { // .url(paramsUrl)
e.printStackTrace() // this.addHeaders(request, jsonHeader)
callback(null) // val headerRequest = addHeaders(request, headers)
} // return this.executeRequest(headerRequest.build())
} // }
} //
// @JvmOverloads
inline fun <reified T> postAsync( // inline fun <reified T> post(
url: String, // url: String,
jsonBody: String, // form: Map<String, Any>,
headers: Map<String, String>? = null, // headers: Map<String, String>? = null,
params: Map<String, Any>? = null, // params: Map<String, Any>? = null
noinline callback: (T?) -> Unit // ): T {
) { // val result = post(url, form, headers, params)
val body = jsonBody.toRequestBody(jsonMediaType) // return result.fromJson<T>()
val paramsUrl = buildParams(url, params) // }
val request = Request.Builder()
.post(body)
.url(paramsUrl)
this.addHeaders(request, jsonHeader)
executeRequestAsync(request, headers) { response ->
try {
callback(response.fromJson<T>())
} catch (e: Exception) {
e.printStackTrace()
callback(null)
}
}
}
} }