Compare commits

...

10 Commits
0.1.0 ... main

Author SHA1 Message Date
xys20071111
c810083b74
添加多房间支持 2023-02-18 10:37:32 +08:00
xys20071111
be192c8585
修复cookie少了分号的问题 2023-01-08 19:05:16 +08:00
xys20071111
c066a5a8c4
更新B站的api接口 2022-12-16 21:33:21 +08:00
xys20071111
7a5475b93c
修改代码, 添加新的日志输出 2022-12-06 20:33:48 +08:00
xys20071111
f39c3b628f
添加退出时杀死插件的功能 2022-12-06 11:18:30 +08:00
xys20071111
e70174f4a4
添加关闭付费响应的开关,删除分号 2022-11-10 12:53:53 +08:00
xys20071111
aa6e39ffe4
完成插件自动启动功能 2022-11-03 20:12:03 +08:00
xys20071111
dcb617f43d
添加免费礼物感谢开关 2022-10-10 16:22:18 +08:00
xys20071111
7cf982baec
修复一处问题 2022-06-20 21:11:38 +08:00
xys20071111
186324bfb8
统一源码文件名格式 2022-06-20 15:25:08 +08:00
17 changed files with 248 additions and 184 deletions

2
.gitignore vendored
View File

@ -1,5 +1,7 @@
config/* config/*
log/* log/*
plugins/*
!plugins/.gitkeep
!config/.gitkeep !config/.gitkeep
!log/.gitkeep !log/.gitkeep
dist dist

0
plugins/.gitkeep Normal file
View File

View File

@ -1,15 +1,15 @@
import { EventEmitter, WebSocketClient, WebSocketServer } from "./deps.ts"; import { EventEmitter, WebSocketClient, WebSocketServer } from "./Deps.ts"
import { sendDanmaku } from './SendDanmaku.ts' import { sendDanmaku } from './SendDanmaku.ts'
import config from "./config.ts"; import config from "./Config.ts"
interface Message { interface Message {
cmd: string; cmd: string
// deno-lint-ignore no-explicit-any // deno-lint-ignore no-explicit-any
data: any; data: any
} }
const server = new WebSocketServer(config.api.port); const server: WebSocketServer = new WebSocketServer(config.api.port)
const authedClientSet: Set<WebSocketClient> = new Set(); const authedClientSet: Set<WebSocketClient> = new Set()
class APIMsgHandler extends EventEmitter { class APIMsgHandler extends EventEmitter {
emit( emit(
@ -19,42 +19,38 @@ class APIMsgHandler extends EventEmitter {
): boolean { ): boolean {
if (eventName === "AUTH") { if (eventName === "AUTH") {
if (args[0] === config.api.token) { if (args[0] === config.api.token) {
authedClientSet.add(socket); authedClientSet.add(socket)
socket.send(JSON.stringify({ cmd: "AUTH", data: "AUTHED" })); socket.send(JSON.stringify({ cmd: "AUTH", data: "AUTHED" }))
return true; return true
} else { } else {
socket.send(JSON.stringify({ cmd: "AUTH", data: "FAILED" })); socket.send(JSON.stringify({ cmd: "AUTH", data: "FAILED" }))
return true; return true
} }
} }
if (authedClientSet.has(socket)) { if (authedClientSet.has(socket)) {
super.emit.apply(this, [eventName, socket, args]); return super.emit.apply(this, [eventName, socket, args])
return true;
} }
return false; return false
} }
} }
const serverEventEmitter = new APIMsgHandler(); const serverEventEmitter = new APIMsgHandler()
serverEventEmitter.on("SEND", (_socket: WebSocketClient, data: string) => { serverEventEmitter.on("SEND", (_socket: WebSocketClient, dataJson: string) => {
sendDanmaku({ const data: {
msg: data, msg: string
}); roomId: number
}); } = JSON.parse(dataJson)
sendDanmaku(data.roomId, {
serverEventEmitter.on("ROOMID", (socket: WebSocket) => { msg: data.msg
socket.send(JSON.stringify({ })
cmd: "ROOMID", })
data: config.room_id,
}));
});
server.on("connection", (client: WebSocketClient) => { server.on("connection", (client: WebSocketClient) => {
client.on("message", (data: string) => { client.on("message", (data: string) => {
const msg: Message = JSON.parse(data); const msg: Message = JSON.parse(data)
serverEventEmitter.emit(msg.cmd, client, msg.data); serverEventEmitter.emit(msg.cmd, client, msg.data)
}); })
}); })
export { server }; export { server }

View File

@ -21,15 +21,19 @@ interface api_config {
} }
interface ConfigStruct { interface ConfigStruct {
room_id: number room_id: Array<number>
verify: Credential verify: Credential
danmakus: DanmakuTemplate danmakus: DanmakuTemplate
cold_down_time: number cold_down_time: number
advertiseing_cold_down: number advertiseing_cold_down: number
api: api_config api: api_config
free_gift_action: boolean
disable_gift_action: boolean
disable_super_chat_action: boolean
disable_graud_action: boolean
} }
const decoder = new TextDecoder('utf-8') const decoder = new TextDecoder('utf-8')
const config: ConfigStruct = JSON.parse(decoder.decode(Deno.readFileSync(`config/${Deno.args[0]}`))); const config: ConfigStruct = JSON.parse(decoder.decode(Deno.readFileSync(Deno.args[0])))
export default config; export default config

View File

@ -1,6 +1,6 @@
// deno-lint-ignore-file // deno-lint-ignore-file
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import config from './config.ts' import config from './Config.ts'
import { sendDanmaku } from './SendDanmaku.ts' import { sendDanmaku } from './SendDanmaku.ts'
import { Encoding } from './Text.ts' import { Encoding } from './Text.ts'
import { getTimeString, FormatString } from './utils/mod.ts' import { getTimeString, FormatString } from './utils/mod.ts'
@ -12,58 +12,60 @@ let logFile = Deno.openSync(`./log/${getTimeString()}-${config.room_id}.log`, {
const thanksColdDownSet = new Set<string>() const thanksColdDownSet = new Set<string>()
export function receiveGift(data: any) { export function receiveGift(roomId: number, data: any) {
if(thanksColdDownSet.has(data.uname)){ if(thanksColdDownSet.has(data.uname)){
return return
} }
logFile.writeSync(Encoding.UTF8.getBytes(`${getTimeString()} ${data.uname} 投喂了${data.super_gift_num}${data.giftName} 价值${data.price / 1000 * data.super_gift_num}\n`)) logFile.writeSync(Encoding.UTF8.getBytes(`${getTimeString()} ${data.uname} 投喂了${data.super_gift_num}${data.giftName} 价值${data.price / 1000 * data.super_gift_num}\n`))
sendDanmaku({ if (config.free_gift_action || data.super_gift_num > 0) {
sendDanmaku(roomId, {
msg: FormatString(config.danmakus.gift, { name: data.uname, gift: data.giftName }) msg: FormatString(config.danmakus.gift, { name: data.uname, gift: data.giftName })
}) })
thanksColdDownSet.add(data.uname) thanksColdDownSet.add(data.uname)
setTimeout(() => {thanksColdDownSet.delete(data.uname)}, config.cold_down_time) setTimeout(() => { thanksColdDownSet.delete(data.uname) }, config.cold_down_time)
}
} }
export function onTotalGift(data: any) { export function onTotalGift(roomId: number, data: any) {
logFile.writeSync(Encoding.UTF8.getBytes(`${getTimeString()} ${data.uname}投喂了${data.total_num}${data.gift_name}\n`)) logFile.writeSync(Encoding.UTF8.getBytes(`${getTimeString()} ${data.uname}投喂了${data.total_num}${data.gift_name}\n`))
sendDanmaku({ sendDanmaku(roomId, {
msg: FormatString(config.danmakus.gift_total, { name: data.uname, gift: data.gift_name, count: data.total_num }) msg: FormatString(config.danmakus.gift_total, { name: data.uname, gift: data.gift_name, count: data.total_num })
}) })
} }
export function receiveDanmaku(data: any) { export function receiveDanmaku(roomId:number, data: any) {
logFile.writeSync(Encoding.UTF8.getBytes(`${getTimeString()} ${data[2][1]}:${data[2][0]} ${data[1]}\n`)) logFile.writeSync(Encoding.UTF8.getBytes(`${getTimeString()} ${roomId} ${data[2][1]}:${data[2][0]} ${data[1]}\n`))
} }
export function onLiveStart() { export function onLiveStart(roomId: number) {
if(skipCount != 1){ if(skipCount != 1){
skipCount ++ skipCount ++
return return
} }
skipCount = 0 skipCount = 0
logFile.close() logFile.close()
sendDanmaku({ msg: config.danmakus.live_start }) sendDanmaku(roomId, { msg: config.danmakus.live_start })
logFile = Deno.openSync(`./log/${getTimeString()}-${config.room_id}.log`, { logFile = Deno.openSync(`./log/${getTimeString()}-${config.room_id}.log`, {
create: true create: true
}) })
logFile.writeSync(Encoding.UTF8.getBytes(`${getTimeString()} 直播开始\n`)) logFile.writeSync(Encoding.UTF8.getBytes(`${getTimeString()} 直播开始\n`))
} }
export function onLiveEnd() { export function onLiveEnd(roomId: number) {
logFile.writeSync(Encoding.UTF8.getBytes(`${getTimeString()} 直播结束\n`)) logFile.writeSync(Encoding.UTF8.getBytes(`${getTimeString()} 直播结束\n`))
sendDanmaku({ msg: config.danmakus.live_end }) sendDanmaku(roomId, { msg: config.danmakus.live_end })
} }
export function onGraud(data: any) { export function onGraud(roomId: number, data: any) {
logFile.writeSync(Encoding.UTF8.getBytes(`${getTimeString()} ${data.username}:${data.uid} 购买了 ${data.gift_name}\n`)) logFile.writeSync(Encoding.UTF8.getBytes(`${getTimeString()} ${data.username}:${data.uid} 购买了 ${data.gift_name}\n`))
sendDanmaku({ sendDanmaku(roomId, {
msg: FormatString(config.danmakus.guard, { type: data.gift_name, name: data.username }) msg: FormatString(config.danmakus.guard, { type: data.gift_name, name: data.username })
}) })
} }
export function onSuperChat(data: any) { export function onSuperChat(roomId: number, data: any) {
logFile.writeSync(Encoding.UTF8.getBytes(`${getTimeString()} ${data.user_info.uname}发送了SC 价格${data.price}\n`)) logFile.writeSync(Encoding.UTF8.getBytes(`${getTimeString()} ${data.user_info.uname}发送了SC 价格${data.price}\n`))
sendDanmaku({ sendDanmaku(roomId, {
msg: FormatString(config.danmakus.sc, { name: data.user_info.uname }) msg: FormatString(config.danmakus.sc, { name: data.user_info.uname })
}) })
} }

View File

@ -1,6 +1,6 @@
import { brotli, EventEmitter } from "./deps.ts"; import { brotli, EventEmitter, WebSocketClient } from "./Deps.ts"
import config from "./config.ts"; import config from "./Config.ts"
import { printLog } from "./utils/printLog.ts"; import { printLog } from "./utils/mod.ts"
import { server as apiServer } from './APIServer.ts' import { server as apiServer } from './APIServer.ts'
enum DANMAKU_PROTOCOL { enum DANMAKU_PROTOCOL {
@ -19,34 +19,36 @@ enum DANMAKU_TYPE {
} }
const cookie = const cookie =
`buvid3=${config.verify.buvid3};SESSDATA=${config.verify.sessdata};bili_jct=${config.verify.csrf}`; `buvid3=${config.verify.buvid3}SESSDATA=${config.verify.sessdata}bili_jct=${config.verify.csrf}`
const encoder = new TextEncoder(); const encoder = new TextEncoder()
const decoder = new TextDecoder("utf-8"); const decoder = new TextDecoder("utf-8")
export class DanmakuReceiver extends EventEmitter { export class DanmakuReceiver extends EventEmitter {
private roomId: number; private roomId: number
private ws: WebSocket | null = null; private ws: WebSocket | null = null
constructor(roomId: number) { constructor(roomId: number) {
super(); super()
this.roomId = roomId; this.roomId = roomId
} }
public async connect() { public async connect() {
const roomConfig = await (await fetch( const roomConfig = await (await fetch(
`https://api.live.bilibili.com/room/v1/Danmu/getConf?room_id=${this.roomId}&platform=pc&player=web`, `https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo?id=${this.roomId}&type=0`,
{ {
headers: { headers: {
cookie, Cookie: cookie,
"user-agent": "User-Agent":
"Mozilla/5.0 (X11 Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36", "Mozilla/5.0 (X11 Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36",
host: "api.live.bilibili.com", Host: "api.live.bilibili.com",
Origin: 'https://live.bilibili.com',
Referer: `https://live.bilibili.com/${this.roomId}?broadcast_type=0`
}, },
}, },
)).json(); )).json()
this.ws = new WebSocket( this.ws = new WebSocket(
`wss://${roomConfig.data.host_server_list[0].host}:${ `wss://${roomConfig.data.host_list[0].host}:${
roomConfig.data.host_server_list[0].wss_port roomConfig.data.host_list[0].wss_port
}/sub`, }/sub`,
); )
this.ws.onopen = () => { this.ws.onopen = () => {
const payload = JSON.stringify({ const payload = JSON.stringify({
roomid: this.roomId, roomid: this.roomId,
@ -54,87 +56,88 @@ export class DanmakuReceiver extends EventEmitter {
platform: "web", platform: "web",
uid: config.verify.uid, uid: config.verify.uid,
key: roomConfig.data.token, key: roomConfig.data.token,
}); type: 2
})
this.ws!.send(this.generatePacket( this.ws!.send(this.generatePacket(
1, 1,
7, 7,
payload, payload,
)); ))
this.ws!.onmessage = this.danmakuProcesser.bind(this); this.ws!.onmessage = this.danmakuProcesser.bind(this)
}; }
this.ws.onclose = () => { this.ws.onclose = () => {
this.emit("closed"); this.emit("closed")
}; }
} }
private generatePacket( private generatePacket(
protocol: number, protocol: number,
type: number, type: number,
payload: string, payload: string,
): ArrayBuffer { ): ArrayBuffer {
const payloadEncoded = encoder.encode(payload); const payloadEncoded = encoder.encode(payload)
const packetLength = 16 + payloadEncoded.length; const packetLength = 16 + payloadEncoded.length
const packet = new ArrayBuffer(packetLength); const packet = new ArrayBuffer(packetLength)
const packetArray = new Uint8Array(packet); const packetArray = new Uint8Array(packet)
const packetView = new DataView(packet); const packetView = new DataView(packet)
packetView.setInt32(0, packetLength); // 总长度 packetView.setInt32(0, packetLength) // 总长度
packetView.setInt16(4, 16); // 头长度 packetView.setInt16(4, 16) // 头长度
packetView.setUint16(6, protocol); // 协议类型 packetView.setUint16(6, protocol) // 协议类型
packetView.setUint32(8, type); // 包类型 packetView.setUint32(8, type) // 包类型
packetView.setUint32(12, 1); // 一个常数 packetView.setUint32(12, 1) // 一个常数
packetArray.set(payloadEncoded, 16); //写入负载 packetArray.set(payloadEncoded, 16) //写入负载
return packet; return packet
} }
private async danmakuProcesser(ev: MessageEvent<Blob>) { private async danmakuProcesser(ev: MessageEvent<Blob>) {
// 弹幕事件处理 // 弹幕事件处理
const msgPacket = await ev.data.arrayBuffer() const msgPacket = await ev.data.arrayBuffer()
const msgArray = new Uint8Array(msgPacket); const msgArray = new Uint8Array(msgPacket)
const msg = new DataView(msgPacket); const msg = new DataView(msgPacket)
const packetProtocol = msg.getInt16(6); const packetProtocol = msg.getInt16(6)
const packetType = msg.getInt32(8); const packetType = msg.getInt32(8)
const packetPayload: Uint8Array = msgArray.slice(16); const packetPayload: Uint8Array = msgArray.slice(16)
let jsonData; let jsonData
switch (packetType) { switch (packetType) {
case DANMAKU_TYPE.HEARTBEAT_REPLY: case DANMAKU_TYPE.HEARTBEAT_REPLY:
// 心跳包,不做处理 // 心跳包,不做处理
break; break
case DANMAKU_TYPE.AUTH_REPLY: case DANMAKU_TYPE.AUTH_REPLY:
printLog("通过认证"); printLog('弹幕接收器' ,"通过认证")
// 认证通过每30秒发一次心跳包 // 认证通过每30秒发一次心跳包
setInterval(() => { setInterval(() => {
const heartbeatPayload = "陈睿你妈死了"; const heartbeatPayload = "陈睿你妈死了"
if (this.ws) { if (this.ws) {
this.ws.send(this.generatePacket(1, 2, heartbeatPayload)); this.ws.send(this.generatePacket(1, 2, heartbeatPayload))
} }
}, 30000); }, 30000)
this.emit("connected"); this.emit("connected")
break; break
case DANMAKU_TYPE.DATA: case DANMAKU_TYPE.DATA:
switch (packetProtocol) { switch (packetProtocol) {
case DANMAKU_PROTOCOL.JSON: case DANMAKU_PROTOCOL.JSON:
// 这些数据大都没用,但还是留着吧 // 这些数据大都没用,但还是留着吧
jsonData = JSON.parse(decoder.decode(packetPayload)); jsonData = JSON.parse(decoder.decode(packetPayload))
this.emit(jsonData.cmd, jsonData.data); this.emit(jsonData.cmd, jsonData.data)
break; break
case DANMAKU_PROTOCOL.BROTLI: { case DANMAKU_PROTOCOL.BROTLI: {
const resultRaw = brotli.decompress(packetPayload); const resultRaw = brotli.decompress(packetPayload)
const result = new DataView(resultRaw.buffer); const result = new DataView(resultRaw.buffer)
let offset = 0; let offset = 0
while (offset < resultRaw.length) { while (offset < resultRaw.length) {
const length = result.getUint32(offset); const length = result.getUint32(offset)
const packetData = resultRaw.slice(offset + 16, offset + length); const packetData = resultRaw.slice(offset + 16, offset + length)
const data = JSON.parse(decoder.decode(packetData)); const data = JSON.parse(decoder.decode(packetData))
const cmd = data.cmd.split(":")[0]; const cmd = data.cmd.split(":")[0]
this.emit(cmd, data.info || data.data); this.emit(cmd, this.roomId, data.info || data.data)
apiServer.clients.forEach((client) => { apiServer.clients.forEach((client: WebSocketClient) => {
client.send(JSON.stringify({ cmd, data: data.info || data.data })) client.send(JSON.stringify({ cmd, room: this.roomId, data: data.info || data.data }))
}) })
offset += length; offset += length
} }
} }
} }
break; break
default: default:
printLog("什么鬼,没见过这种包"); printLog('弹幕接收器', `未知的弹幕数据包种类 ${packetType}`)
} }
} }
} }

6
src/Deps.ts Normal file
View File

@ -0,0 +1,6 @@
import * as events from 'https://deno.land/x/events@v1.0.0/mod.ts'
const EventEmitter = events.default
export { EventEmitter }
export * as brotli from 'https://deno.land/x/brotli@0.1.7/mod.ts'
export { WebSocketServer, type WebSocketClient } from 'https://deno.land/x/websocket@v0.1.4/mod.ts'

38
src/Main.ts Normal file
View File

@ -0,0 +1,38 @@
import config from './Config.ts'
import { DanmakuReceiver } from './DanmakuReceiver.ts'
import { onGraud, onLiveEnd, onLiveStart, onSuperChat, onTotalGift, receiveDanmaku, receiveGift } from './DanmakuCallbacks.ts'
import { printLog } from './utils/mod.ts'
import { launchAllPlugins } from './Plugins.ts'
const roomReceiverMap: Map<number, DanmakuReceiver> = new Map()
for(const room of config.room_id) {
const danmakuReceiver = new DanmakuReceiver(room)
danmakuReceiver.on('connected', () => {
printLog('主程序', '连接成功')
})
if (!config.disable_gift_action) {
danmakuReceiver.on('COMBO_SEND', onTotalGift)
danmakuReceiver.on('SEND_GIFT', receiveGift)
}
if (!config.disable_super_chat_action) {
danmakuReceiver.on('GUARD_BUY', onGraud)
}
if (!config.disable_super_chat_action) {
danmakuReceiver.on('SUPER_CHAT_MESSAGE', onSuperChat)
}
globalThis.onunload = () => {
printLog('主程序', '退出')
}
danmakuReceiver.on('closed', () => {
printLog('主程序', '掉线了')
danmakuReceiver.connect()
})
danmakuReceiver.on('LIVE', onLiveStart)
danmakuReceiver.on('PREPARING', onLiveEnd)
danmakuReceiver.on('DANMU_MSG', receiveDanmaku)
danmakuReceiver.connect()
roomReceiverMap.set(room, danmakuReceiver)
}
launchAllPlugins()

37
src/Plugins.ts Normal file
View File

@ -0,0 +1,37 @@
import { printErr } from "./utils/PrintLog.ts"
const pluginSet: Set<Deno.Process> = new Set()
export async function launchAllPlugins() {
const pluginsList = await Deno.readDir('./plugins')
for await (const plugin of pluginsList) {
if (plugin.name === '.gitkeep') {
continue
}
try {
const pluginProcess = Deno.run({
cmd: [`./plugins/${plugin.name}/main`, `./plugins/${plugin.name}/config.json`]
})
pluginSet.add(pluginProcess)
} catch {
printErr('主程序', `启动插件${plugin.name}失败`)
}
}
}
function stopAllPlugins() {
for (const pluginProcess of pluginSet) {
pluginProcess.kill()
}
Deno.exit()
}
Deno.addSignalListener('SIGTERM', () => {
stopAllPlugins()
Deno.exit()
})
Deno.addSignalListener('SIGINT', () => {
stopAllPlugins()
Deno.exit()
})

View File

@ -1,50 +1,50 @@
import config from "./config.ts"; import config from "./Config.ts"
const cookie = const cookie =
`buvid3=${config.verify.buvid3}; SESSDATA=${config.verify.sessdata}; bili_jct=${config.verify.csrf};`; `buvid3=${config.verify.buvid3}; SESSDATA=${config.verify.sessdata}; bili_jct=${config.verify.csrf}`
export interface DanmakuStruct { export interface DanmakuStruct {
color?: number; color?: number
bubble?: number; bubble?: number
msg: string; msg: string
mode?: number; mode?: number
fontsize?: number; fontsize?: number
rnd?: number; rnd?: number
roomid?: number; roomid?: number
csrf?: string; csrf?: string
csrf_token?: string; csrf_token?: string
} }
export function sendDanmaku(danmaku: DanmakuStruct) { export function sendDanmaku(roomId: number, danmaku: DanmakuStruct) {
if (danmaku.msg.length > 19) { if (danmaku.msg.length > 19) {
sendDanmaku({ sendDanmaku(roomId, {
msg: danmaku.msg.slice(0, 15), msg: danmaku.msg.slice(0, 15),
}); })
setTimeout(() => { setTimeout(() => {
sendDanmaku({ sendDanmaku(roomId, {
msg: danmaku.msg.slice(15, danmaku.msg.length), msg: danmaku.msg.slice(15, danmaku.msg.length),
}); })
}, 2000); }, 2000)
return; return
} }
danmaku.rnd = new Date().getTime(); danmaku.rnd = new Date().getTime()
if (!danmaku.color) { if (!danmaku.color) {
danmaku.color = 5816798; danmaku.color = 5816798
} }
if (!danmaku.bubble) { if (!danmaku.bubble) {
danmaku.bubble = 0; danmaku.bubble = 0
} }
if (!danmaku.mode) { if (!danmaku.mode) {
danmaku.mode = 1; danmaku.mode = 1
} }
if (!danmaku.fontsize) { if (!danmaku.fontsize) {
danmaku.fontsize = 24; danmaku.fontsize = 24
} }
danmaku.roomid = config.room_id as number; danmaku.roomid = roomId
danmaku.csrf = danmaku.csrf_token = config.verify.csrf; danmaku.csrf = danmaku.csrf_token = config.verify.csrf
const data = new FormData(); const data = new FormData()
for (const k in danmaku) { for (const k in danmaku) {
data.append(k, danmaku[k as keyof DanmakuStruct]!.toString()); data.append(k, danmaku[k as keyof DanmakuStruct]!.toString())
} }
fetch("https://api.live.bilibili.com/msg/send", { fetch("https://api.live.bilibili.com/msg/send", {
method: 'POST', method: 'POST',
@ -52,9 +52,9 @@ export function sendDanmaku(danmaku: DanmakuStruct) {
headers: { headers: {
cookie: cookie, cookie: cookie,
"user-agent": "user-agent":
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36", "Mozilla/5.0 (X11 Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36",
host: "api.live.bilibili.com", host: "api.live.bilibili.com",
"Referer": "https://live.bilibili.com", "Referer": "https://live.bilibili.com",
} }
}); })
} }

View File

@ -1,7 +0,0 @@
import * as events from 'https://deno.land/x/events@v1.0.0/mod.ts'
const EventEmitter = events.default
export { EventEmitter }
export * as brotli from 'https://deno.land/x/brotli@v0.1.4/mod.ts'
export { WebSocketServer } from "https://deno.land/x/websocket@v0.1.4/mod.ts";
export type { WebSocketClient } from "https://deno.land/x/websocket@v0.1.4/mod.ts";

View File

@ -1,20 +0,0 @@
import config from './config.ts';
import { DanmakuReceiver } from './DanmakuReceiver.ts';
import { onGraud, onLiveEnd, onLiveStart, onSuperChat, onTotalGift, receiveDanmaku, receiveGift } from './danmakuCallbacks.ts';
import { printLog } from './utils/mod.ts';
const danmakuReceiver = new DanmakuReceiver(config.room_id);
danmakuReceiver.on('connected', () => {
printLog('连接成功');
});
danmakuReceiver.on('closed', () => danmakuReceiver.connect());
danmakuReceiver.on('SEND_GIFT', receiveGift);
danmakuReceiver.on('LIVE', onLiveStart);
danmakuReceiver.on('PREPARING', onLiveEnd);
danmakuReceiver.on('DANMU_MSG', receiveDanmaku);
danmakuReceiver.on('COMBO_SEND', onTotalGift);
danmakuReceiver.on('GUARD_BUY', onGraud);
danmakuReceiver.on('SUPER_CHAT_MESSAGE', onSuperChat);
danmakuReceiver.connect();

View File

@ -1,8 +1,8 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any // deno-lint-ignore no-explicit-any
export function FormatString(str: string, args: any): string { export function FormatString(str: string, args: any): string {
let result: string = str; let result: string = str
for(const arg in args) { for(const arg in args) {
result = result.replace(new RegExp(`{${arg}}`, 'g'), args[arg]) result = result.replace(new RegExp(`{${arg}}`, 'g'), args[arg])
} }
return result; return result
} }

7
src/utils/PrintLog.ts Normal file
View File

@ -0,0 +1,7 @@
import { getTimeString } from './GetTimeString.ts'
export function printLog(source: string, msg: unknown) {
console.log(`[log][${source}][${getTimeString()}] ${msg}`)
}
export function printErr(source: string, msg: unknown) {
console.error(`[err][${source}][${getTimeString()}] ${msg}`)
}

View File

@ -1,3 +1,3 @@
export { getTimeString } from './getTimeString.ts' export { getTimeString } from './GetTimeString.ts'
export { printLog } from './printLog.ts' export { printLog } from './PrintLog.ts'
export { FormatString } from './FormatString.ts' export { FormatString } from './FormatString.ts'

View File

@ -1,4 +0,0 @@
import { getTimeString } from './getTimeString.ts'
export function printLog(msg: unknown) {
console.log(`[${getTimeString()}] ${msg}`)
}