mirror of
https://github.com/dalbodeule/chibot-chzzk-bot.git
synced 2025-06-09 07:18:22 +00:00
some debugs on Chisu playlist
This commit is contained in:
parent
f7953778e1
commit
590c1203bd
@ -9,9 +9,7 @@ import org.slf4j.Logger
|
|||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import space.mori.chzzk_bot.chatbot.chzzk.Connector.chzzk
|
import space.mori.chzzk_bot.chatbot.chzzk.Connector.chzzk
|
||||||
import space.mori.chzzk_bot.chatbot.discord.Discord
|
import space.mori.chzzk_bot.chatbot.discord.Discord
|
||||||
import space.mori.chzzk_bot.common.events.CoroutinesEventBus
|
import space.mori.chzzk_bot.common.events.*
|
||||||
import space.mori.chzzk_bot.common.events.TimerEvent
|
|
||||||
import space.mori.chzzk_bot.common.events.TimerType
|
|
||||||
import space.mori.chzzk_bot.common.models.User
|
import space.mori.chzzk_bot.common.models.User
|
||||||
import space.mori.chzzk_bot.common.services.LiveStatusService
|
import space.mori.chzzk_bot.common.services.LiveStatusService
|
||||||
import space.mori.chzzk_bot.common.services.TimerConfigService
|
import space.mori.chzzk_bot.common.services.TimerConfigService
|
||||||
@ -39,6 +37,11 @@ object ChzzkHandler {
|
|||||||
UserService.getAllUsers().map {
|
UserService.getAllUsers().map {
|
||||||
chzzk.getChannel(it.token)?.let { token -> addUser(token, it) }
|
chzzk.getChannel(it.token)?.let { token -> addUser(token, it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handlers.forEach { handler ->
|
||||||
|
val streamInfo = getStreamInfo(handler.listener.channelId)
|
||||||
|
if (streamInfo.content.status == "OPEN") handler.isActive(true, streamInfo)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun disable() {
|
fun disable() {
|
||||||
@ -148,7 +151,6 @@ class UserHandler(
|
|||||||
get() = _isActive
|
get() = _isActive
|
||||||
|
|
||||||
internal fun isActive(value: Boolean, status: IData<IStreamInfo>) {
|
internal fun isActive(value: Boolean, status: IData<IStreamInfo>) {
|
||||||
_isActive = value
|
|
||||||
if(value) {
|
if(value) {
|
||||||
logger.info("${user.username} is live.")
|
logger.info("${user.username} is live.")
|
||||||
|
|
||||||
@ -170,10 +172,11 @@ class UserHandler(
|
|||||||
""
|
""
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
if(!_isActive) {
|
||||||
delay(5000L)
|
delay(5000L)
|
||||||
listener.sendChat("${user.username} 님! 오늘도 열심히 방송하세요!")
|
listener.sendChat("${user.username} 님! 오늘도 열심히 방송하세요!")
|
||||||
Discord.sendDiscord(user, status)
|
Discord.sendDiscord(user, status)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.info("${user.username} is offline.")
|
logger.info("${user.username} is offline.")
|
||||||
@ -181,12 +184,25 @@ class UserHandler(
|
|||||||
listener.closeAsync()
|
listener.closeAsync()
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Default).launch {
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
dispatcher.post(TimerEvent(
|
val events = listOf(
|
||||||
channel.channelId,
|
TimerEvent(
|
||||||
TimerType.STREAM_OFF,
|
channel.channelId,
|
||||||
""
|
TimerType.STREAM_OFF,
|
||||||
))
|
null
|
||||||
|
),
|
||||||
|
SongEvent(
|
||||||
|
channel.channelId,
|
||||||
|
SongType.STREAM_OFF,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
events.forEach { dispatcher.post(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_isActive = value
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -36,6 +36,14 @@ class MessageHandler(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
reloadCommand()
|
reloadCommand()
|
||||||
|
dispatcher.subscribe(SongEvent::class) {
|
||||||
|
if(it.type == SongType.STREAM_OFF) {
|
||||||
|
val user = UserService.getUser(channel.channelId)
|
||||||
|
if(! user?.let { usr -> SongListService.getSong(usr) }.isNullOrEmpty()) {
|
||||||
|
SongListService.deleteUser(user!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun reloadCommand() {
|
internal fun reloadCommand() {
|
||||||
@ -199,33 +207,35 @@ class MessageHandler(
|
|||||||
val url = parts[1]
|
val url = parts[1]
|
||||||
val songs = SongListService.getSong(user)
|
val songs = SongListService.getSong(user)
|
||||||
|
|
||||||
if (songs.any { it.url == url }) {
|
|
||||||
listener.sendChat("같은 노래가 이미 신청되어 있습니다.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val video = getYoutubeVideo(url)
|
val video = getYoutubeVideo(url)
|
||||||
if (video == null) {
|
if (video == null) {
|
||||||
listener.sendChat("유튜브에서 찾을 수 없어요!")
|
listener.sendChat("유튜브에서 찾을 수 없어요!")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (songs.any { it.url == video.url }) {
|
||||||
|
listener.sendChat("같은 노래가 이미 신청되어 있습니다.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
SongListService.saveSong(
|
SongListService.saveSong(
|
||||||
user,
|
user,
|
||||||
msg.userId,
|
msg.userId,
|
||||||
video.url,
|
video.url,
|
||||||
video.name,
|
video.name,
|
||||||
video.author,
|
video.author,
|
||||||
video.length
|
video.length,
|
||||||
|
msg.profile?.nickname ?: ""
|
||||||
)
|
)
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
dispatcher.post(SongEvent(
|
dispatcher.post(SongEvent(
|
||||||
user.token,
|
user.token,
|
||||||
SongType.ADD,
|
SongType.ADD,
|
||||||
msg.userId,
|
msg.userId,
|
||||||
|
msg.profile?.nickname ?: "",
|
||||||
video.name,
|
video.name,
|
||||||
video.author,
|
video.author,
|
||||||
video.length
|
video.length,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,8 @@ enum class SongType(var value: Int) {
|
|||||||
class SongEvent(
|
class SongEvent(
|
||||||
val uid: String,
|
val uid: String,
|
||||||
val type: SongType,
|
val type: SongType,
|
||||||
val req_uid: String?,
|
val reqUid: String?,
|
||||||
|
val reqName: String?,
|
||||||
val name: String?,
|
val name: String?,
|
||||||
val author: String?,
|
val author: String?,
|
||||||
val time: Int?,
|
val time: Int?,
|
||||||
|
@ -5,15 +5,17 @@ import org.jetbrains.exposed.dao.IntEntityClass
|
|||||||
import org.jetbrains.exposed.dao.id.EntityID
|
import org.jetbrains.exposed.dao.id.EntityID
|
||||||
import org.jetbrains.exposed.dao.id.IntIdTable
|
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||||
import org.jetbrains.exposed.sql.javatime.datetime
|
import org.jetbrains.exposed.sql.javatime.datetime
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
object SongLists: IntIdTable("song_list") {
|
object SongLists: IntIdTable("song_list") {
|
||||||
val user = reference("user", Users)
|
val user = reference("user", Users)
|
||||||
val uid = varchar("uid", 64)
|
val uid = varchar("uid", 64)
|
||||||
val url = varchar("url", 128)
|
val url = varchar("url", 128)
|
||||||
val name = text("name")
|
val name = text("name")
|
||||||
|
val reqName = varchar("req_name", 20)
|
||||||
val author = text("author")
|
val author = text("author")
|
||||||
val time = integer("time")
|
val time = integer("time")
|
||||||
val created_at = datetime("created_at")
|
val created_at = datetime("created_at").default(LocalDateTime.now())
|
||||||
}
|
}
|
||||||
|
|
||||||
class SongList(id: EntityID<Int>) : IntEntity(id) {
|
class SongList(id: EntityID<Int>) : IntEntity(id) {
|
||||||
@ -27,4 +29,5 @@ class SongList(id: EntityID<Int>) : IntEntity(id) {
|
|||||||
|
|
||||||
var user by User referencedOn SongLists.user
|
var user by User referencedOn SongLists.user
|
||||||
var uid by SongLists.uid
|
var uid by SongLists.uid
|
||||||
|
var reqName by SongLists.reqName
|
||||||
}
|
}
|
@ -8,7 +8,7 @@ import space.mori.chzzk_bot.common.models.SongLists
|
|||||||
import space.mori.chzzk_bot.common.models.User
|
import space.mori.chzzk_bot.common.models.User
|
||||||
|
|
||||||
object SongListService {
|
object SongListService {
|
||||||
fun saveSong(user: User, uid: String, url: String, name: String, author: String, time: Int) {
|
fun saveSong(user: User, uid: String, url: String, name: String, author: String, time: Int, reqName: String) {
|
||||||
return transaction {
|
return transaction {
|
||||||
SongList.new {
|
SongList.new {
|
||||||
this.user = user
|
this.user = user
|
||||||
@ -17,6 +17,7 @@ object SongListService {
|
|||||||
this.name = name
|
this.name = name
|
||||||
this.author = author
|
this.author = author
|
||||||
this.time = time
|
this.time = time
|
||||||
|
this.reqName = reqName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -32,7 +33,7 @@ object SongListService {
|
|||||||
|
|
||||||
fun getSong(user: User): List<SongList> {
|
fun getSong(user: User): List<SongList> {
|
||||||
return transaction {
|
return transaction {
|
||||||
SongList.find(SongLists.user eq user.id).toList()
|
SongList.find(SongLists.user eq user.id).toList().sortedBy { it.created_at }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,4 +51,13 @@ object SongListService {
|
|||||||
songRow
|
songRow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteUser(user: User): Boolean {
|
||||||
|
return transaction {
|
||||||
|
val songRow = SongList.find(SongLists.user eq user.id).toList()
|
||||||
|
|
||||||
|
songRow.forEach { it.delete() }
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -16,7 +16,7 @@ data class YoutubeVideo(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val regex = ".*(?:youtu.be/|v/|u/\\w/|embed/|watch\\?v=|&v=)([^#&?]*).*".toRegex()
|
val regex = ".*(?:youtu.be/|v/|u/\\w/|embed/|watch\\?v=|&v=)([^#&?]*).*".toRegex()
|
||||||
val durationRegex = """PT(\d+H)?(\d+m)?(\d+S)?""".toRegex()
|
val durationRegex = """PT(\d+H)?(\d+M)?(\d+S)?""".toRegex()
|
||||||
|
|
||||||
val client = OkHttpClient()
|
val client = OkHttpClient()
|
||||||
val gson = Gson()
|
val gson = Gson()
|
||||||
@ -33,6 +33,7 @@ fun getYoutubeVideoId(url: String): String? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun parseDuration(duration: String): Int {
|
fun parseDuration(duration: String): Int {
|
||||||
|
println(duration)
|
||||||
val matchResult = durationRegex.find(duration)
|
val matchResult = durationRegex.find(duration)
|
||||||
val (hours, minutes, seconds) = matchResult?.destructured ?: return 0
|
val (hours, minutes, seconds) = matchResult?.destructured ?: return 0
|
||||||
|
|
||||||
@ -54,7 +55,7 @@ fun getYoutubeVideo(url: String): YoutubeVideo? {
|
|||||||
.addPathSegment("videos")
|
.addPathSegment("videos")
|
||||||
.addQueryParameter("id", videoId)
|
.addQueryParameter("id", videoId)
|
||||||
.addQueryParameter("key", dotenv["YOUTUBE_API_KEY"])
|
.addQueryParameter("key", dotenv["YOUTUBE_API_KEY"])
|
||||||
.addQueryParameter("part", "snippet")
|
.addQueryParameter("part", "snippet,contentDetails,status")
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
|
||||||
@ -71,10 +72,12 @@ fun getYoutubeVideo(url: String): YoutubeVideo? {
|
|||||||
|
|
||||||
if (items == null || items.size() == 0) return null
|
if (items == null || items.size() == 0) return null
|
||||||
|
|
||||||
|
println(json)
|
||||||
|
|
||||||
val item = items[0].asJsonObject
|
val item = items[0].asJsonObject
|
||||||
val snippet = item.getAsJsonObject("snippet")
|
val snippet = item.getAsJsonObject("snippet")
|
||||||
val contentDetail = item.asJsonObject.getAsJsonObject("contentDetail")
|
val contentDetail = item.getAsJsonObject("contentDetails")
|
||||||
val status = contentDetail.getAsJsonObject("status")
|
val status = item.getAsJsonObject("status")
|
||||||
|
|
||||||
if (!status.get("embeddable").asBoolean) return null
|
if (!status.get("embeddable").asBoolean) return null
|
||||||
|
|
||||||
|
1
inc.env
1
inc.env
@ -4,5 +4,6 @@ DB_URL=jdbc:mariadb://localhost:3306/chzzk
|
|||||||
DB_USER=chzzk
|
DB_USER=chzzk
|
||||||
DB_PASS=chzzk
|
DB_PASS=chzzk
|
||||||
RUN_AGENT=false
|
RUN_AGENT=false
|
||||||
|
YOUTUBE_API_KEY=
|
||||||
NID_AUT=
|
NID_AUT=
|
||||||
NID_SES=
|
NID_SES=
|
@ -13,6 +13,7 @@ import io.ktor.server.routing.*
|
|||||||
import io.ktor.server.websocket.*
|
import io.ktor.server.websocket.*
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import space.mori.chzzk_bot.webserver.routes.apiRoutes
|
import space.mori.chzzk_bot.webserver.routes.apiRoutes
|
||||||
|
import space.mori.chzzk_bot.webserver.routes.apiSongRoutes
|
||||||
import space.mori.chzzk_bot.webserver.routes.wsSongRoutes
|
import space.mori.chzzk_bot.webserver.routes.wsSongRoutes
|
||||||
import space.mori.chzzk_bot.webserver.routes.wsTimerRoutes
|
import space.mori.chzzk_bot.webserver.routes.wsTimerRoutes
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
@ -38,6 +39,7 @@ val server = embeddedServer(Netty, port = 8080) {
|
|||||||
}
|
}
|
||||||
routing {
|
routing {
|
||||||
apiRoutes()
|
apiRoutes()
|
||||||
|
apiSongRoutes()
|
||||||
wsTimerRoutes()
|
wsTimerRoutes()
|
||||||
wsSongRoutes()
|
wsSongRoutes()
|
||||||
swaggerUI("swagger-ui/index.html", "openapi/documentation.yaml") {
|
swaggerUI("swagger-ui/index.html", "openapi/documentation.yaml") {
|
||||||
|
@ -4,10 +4,29 @@ import io.ktor.http.*
|
|||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.response.*
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import space.mori.chzzk_bot.common.models.SongList
|
||||||
import space.mori.chzzk_bot.common.services.SongListService
|
import space.mori.chzzk_bot.common.services.SongListService
|
||||||
import space.mori.chzzk_bot.common.services.UserService
|
import space.mori.chzzk_bot.common.services.UserService
|
||||||
|
|
||||||
fun Routing.songRoutes() {
|
@Serializable
|
||||||
|
data class SongsDTO(
|
||||||
|
val url: String,
|
||||||
|
val name: String,
|
||||||
|
val author: String,
|
||||||
|
val time: Int,
|
||||||
|
val reqName: String
|
||||||
|
)
|
||||||
|
|
||||||
|
fun SongList.toDTO(): SongsDTO = SongsDTO(
|
||||||
|
this.url,
|
||||||
|
this.name,
|
||||||
|
this.author,
|
||||||
|
this.time,
|
||||||
|
this.reqName
|
||||||
|
)
|
||||||
|
|
||||||
|
fun Routing.apiSongRoutes() {
|
||||||
route("/songs/{uid}") {
|
route("/songs/{uid}") {
|
||||||
get {
|
get {
|
||||||
val uid = call.parameters["uid"]
|
val uid = call.parameters["uid"]
|
||||||
@ -18,7 +37,7 @@ fun Routing.songRoutes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val songs = SongListService.getSong(user)
|
val songs = SongListService.getSong(user)
|
||||||
call.respond(songs)
|
call.respond(HttpStatusCode.OK, songs.map { it.toDTO() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
route("/songs") {
|
route("/songs") {
|
||||||
|
@ -86,7 +86,7 @@ fun Routing.wsSongRoutes() {
|
|||||||
ws.sendSerialized(SongResponse(
|
ws.sendSerialized(SongResponse(
|
||||||
it.type.value,
|
it.type.value,
|
||||||
it.uid,
|
it.uid,
|
||||||
it.req_uid,
|
it.reqUid,
|
||||||
it.name,
|
it.name,
|
||||||
it.author,
|
it.author,
|
||||||
it.time
|
it.time
|
||||||
|
@ -8,8 +8,7 @@ servers:
|
|||||||
paths:
|
paths:
|
||||||
/:
|
/:
|
||||||
get:
|
get:
|
||||||
summary: "Webroot"
|
description: ""
|
||||||
description: "Main page of this api"
|
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: "OK"
|
description: "OK"
|
||||||
@ -22,7 +21,7 @@ paths:
|
|||||||
value: "Hello World!"
|
value: "Hello World!"
|
||||||
/health:
|
/health:
|
||||||
get:
|
get:
|
||||||
description: "Health Check endpoint"
|
description: ""
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: "OK"
|
description: "OK"
|
||||||
@ -32,4 +31,152 @@ paths:
|
|||||||
type: "string"
|
type: "string"
|
||||||
examples:
|
examples:
|
||||||
Example#1:
|
Example#1:
|
||||||
value: "OK"
|
value: "OK"
|
||||||
|
/song/{uid}:
|
||||||
|
get:
|
||||||
|
description: ""
|
||||||
|
parameters:
|
||||||
|
- name: "uid"
|
||||||
|
in: "path"
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
- name: "Connection"
|
||||||
|
in: "header"
|
||||||
|
required: true
|
||||||
|
description: "Websocket Connection parameter"
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
- name: "Upgrade"
|
||||||
|
in: "header"
|
||||||
|
required: true
|
||||||
|
description: "Websocket Upgrade parameter"
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
- name: "Sec-WebSocket-Key"
|
||||||
|
in: "header"
|
||||||
|
required: true
|
||||||
|
description: "Websocket Sec-WebSocket-Key parameter"
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
responses:
|
||||||
|
"101":
|
||||||
|
description: "Switching Protocols"
|
||||||
|
headers:
|
||||||
|
Connection:
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
Upgrade:
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
Sec-WebSocket-Accept:
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
/songs:
|
||||||
|
get:
|
||||||
|
description: ""
|
||||||
|
responses:
|
||||||
|
"400":
|
||||||
|
description: "Bad Request"
|
||||||
|
content:
|
||||||
|
text/plain:
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
examples:
|
||||||
|
Example#1:
|
||||||
|
value: "Require UID"
|
||||||
|
/songs/{uid}:
|
||||||
|
get:
|
||||||
|
description: ""
|
||||||
|
parameters:
|
||||||
|
- name: "uid"
|
||||||
|
in: "path"
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
responses:
|
||||||
|
"404":
|
||||||
|
description: "Not Found"
|
||||||
|
content:
|
||||||
|
text/plain:
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
examples:
|
||||||
|
Example#1:
|
||||||
|
value: "No user found"
|
||||||
|
"200":
|
||||||
|
description: "OK"
|
||||||
|
content:
|
||||||
|
'*/*':
|
||||||
|
schema:
|
||||||
|
type: "array"
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/SongList"
|
||||||
|
/timer/{uid}:
|
||||||
|
get:
|
||||||
|
description: ""
|
||||||
|
parameters:
|
||||||
|
- name: "uid"
|
||||||
|
in: "path"
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
- name: "Connection"
|
||||||
|
in: "header"
|
||||||
|
required: true
|
||||||
|
description: "Websocket Connection parameter"
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
- name: "Upgrade"
|
||||||
|
in: "header"
|
||||||
|
required: true
|
||||||
|
description: "Websocket Upgrade parameter"
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
- name: "Sec-WebSocket-Key"
|
||||||
|
in: "header"
|
||||||
|
required: true
|
||||||
|
description: "Websocket Sec-WebSocket-Key parameter"
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
responses:
|
||||||
|
"101":
|
||||||
|
description: "Switching Protocols"
|
||||||
|
headers:
|
||||||
|
Connection:
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
Upgrade:
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
Sec-WebSocket-Accept:
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
Object:
|
||||||
|
type: "object"
|
||||||
|
properties: {}
|
||||||
|
ResultRow:
|
||||||
|
type: "object"
|
||||||
|
properties:
|
||||||
|
fieldIndex:
|
||||||
|
type: "object"
|
||||||
|
required:
|
||||||
|
- "fieldIndex"
|
||||||
|
SongList:
|
||||||
|
type: "object"
|
||||||
|
properties:
|
||||||
|
writeValues:
|
||||||
|
$ref: "#/components/schemas/Object"
|
||||||
|
_readValues:
|
||||||
|
$ref: "#/components/schemas/ResultRow"
|
||||||
|
required:
|
||||||
|
- "id"
|
||||||
|
- "writeValues"
|
Loading…
x
Reference in New Issue
Block a user