feat: add view count on bv parse card
This commit is contained in:
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
|
||||||
|
)
|
||||||
|
}
|
||||||
+2
-1
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user