-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathrelay.js
167 lines (149 loc) · 4.82 KB
/
relay.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
const EventEmitter = require('events')
const { KeepLiveWS } = require('bilibili-live-ws')
const getConfW = async (roomid, customFetch) => {
const { data: { token: key, host_list: hosts } } = await customFetch(`https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo?id=${roomid}&type=0`).then(w => w.json())
const { host } = hosts[Math.floor(Math.random() * hosts.length)]
const address = `wss://${host}/sub`
return { key, host, address }
}
const wait = ms => new Promise(resolve => setTimeout(resolve, ms))
module.exports = (home, customFetch, getBUVID, uid) => {
const emitter = new EventEmitter().setMaxListeners(Infinity)
let start = false
emitter.on('start', () => {
start = true
})
emitter.on('stop', () => {
start = false
})
const log = (...msg) => {
home.emit('log', 'relay', ...msg)
home.emit('relay', ...msg)
}
const send = relay => home.secureSend(JSON.stringify({ relay }))
const waiting = []
const rooms = new Set()
const lived = new Set()
const printStatus = () => {
log(`living/opening/limit: ${lived.size} / ${rooms.size} / ${home.wsLimit}`)
home.emit('relayStatus', { lived: lived.size, rooms: rooms.size })
}
const processWaiting = async () => {
while (waiting.length) {
const { f, resolve, roomid } = waiting.shift()
f().then(resolve).catch(() => {
log('redo', roomid)
waiting.push({ f, resolve, roomid })
if (waiting.length === 1) {
processWaiting()
}
})
await wait(1800)
}
}
const getConf = roomid => {
const p = new Promise(resolve => waiting.push({ resolve, f: () => getConfW(roomid, customFetch), roomid }))
if (waiting.length === 1) {
processWaiting()
}
return p
}
const openRoom = async roomid => {
const { address, key } = await getConf(roomid)
log(`OPEN: ${roomid}`)
printStatus()
const opts = { address, key, protover: 3 }
if (getBUVID) {
opts.buvid = await getBUVID()
}
if (uid) {
opts.uid = uid
}
const live = new KeepLiveWS(roomid, opts)
live.interval = 60 * 1000
live.on('live', () => {
log(`LIVE: ${roomid}`)
lived.add(roomid)
printStatus()
})
live.on('error', () => {
log(`ERROR: ${roomid}`)
lived.delete(roomid)
printStatus()
})
live.on('close', async () => {
log(`CLOSE: ${roomid}`)
lived.delete(roomid)
printStatus()
const { address, key } = await getConf(roomid)
live.params[2] = { key, address }
})
live.on('LIVE', () => send({ roomid, e: 'LIVE' }))
live.on('PREPARING', () => send({ roomid, e: 'PREPARING' }))
live.on('ROUND', () => send({ roomid, e: 'ROUND' }))
live.on('heartbeat', online => send({ roomid, e: 'heartbeat', data: online }))
live.on('ROOM_CHANGE', ({ data: { title } }) => send({ roomid, e: 'ROOM_CHANGE', data: title, token: `${roomid}_ROOM_CHANGE_${title}` }))
live.on('DANMU_MSG', async ({ info }) => {
if (!info[0][9]) {
const message = info[1]
const mid = info[2][0]
const uname = info[2][1]
const timestamp = info[0][4]
const token = `${roomid}_DANMU_MSG_${mid}_${timestamp}`
send({ roomid, e: 'DANMU_MSG', data: { message, uname, timestamp, mid }, token })
}
})
live.on('SEND_GIFT', ({ data }) => {
const coinType = data.coin_type
const mid = data.uid
const giftId = data.giftId
const totalCoin = data.total_coin
const uname = data.uname
const tid = data.tid
const token = `${roomid}_SEND_GIFT_${mid}_${tid}`
send({ roomid, e: 'SEND_GIFT', data: { coinType, giftId, totalCoin, uname, mid }, token })
})
live.on('GUARD_BUY', ({ data }) => {
const mid = data.uid
const uname = data.username
const num = data.num
const price = data.price
const giftId = data.gift_id
const level = data.guard_level
const time = data.start_time
const token = `${roomid}_GUARD_BUY_${mid}_${time}`
send({ roomid, e: 'GUARD_BUY', data: { mid, uname, num, price, giftId, level }, token })
})
if (start) {
emitter.once('stop', () => {
lived.delete(roomid)
live.close()
})
} else {
lived.delete(roomid)
live.close()
}
}
const watch = roomid => {
if (roomid && !rooms.has(roomid)) {
rooms.add(roomid)
log(`WATCH: ${roomid}`)
openRoom(roomid)
emitter.once('stop', () => {
rooms.delete(roomid)
})
}
}
home.once('open', () => {
setInterval(() => {
if (rooms.size === lived.size && rooms.size < home.wsLimit && start) {
home.ask({ type: 'pickRoom' })
.then(roomid => watch(roomid))
.catch(e => {
console.error('ask pickRoom error', e)
})
}
}, home.liveInterval)
})
return emitter
}