Compare commits

..

11 Commits

Author SHA1 Message Date
John Smith
b483c6beab 更新版本号v1.6.2 2023-07-08 11:42:21 +08:00
John Smith
f748d35fe4 修复前端页面不活动时心跳超时的问题 2023-07-06 23:26:44 +08:00
John Smith
b98a8f6680 修复cookie导致的获取头像失败 2023-07-06 23:25:07 +08:00
John Smith
914a41700e Squashed commit of the following:
commit 414258724b456628a2e361a11de102dbdc5a2fe4
Author: John Smith <xfgryujk@126.com>
Date:   Tue Jul 4 23:58:59 2023 +0800

    修正代码风格和wbi签名实现

commit 34a6546bd6ac00a893d36a65c20d479b113e3f53
Author: Bryan不可思议 <programripper@foxmail.com>
Date:   Thu Jun 22 15:52:51 2023 +0800

    Update services/avatar.py

    Co-authored-by: Hao Guan <10684225+hguandl@users.noreply.github.com>

commit f9dd56a13c296ff938560bcd161570f000855b35
Author: ProgramRipper <programripper@foxmail.com>
Date:   Tue Jun 13 03:33:15 2023 +0800

    fix: syntax error

commit a0354f2480a7c561e6dcc223fa9d4834310540b5
Author: ProgramRipper <programripper@foxmail.com>
Date:   Tue Jun 13 03:08:55 2023 +0800

    fix: get_avatar_url
2023-07-05 00:04:00 +08:00
John Smith
085765b1a2 修复SC消息创建出错的问题 2023-07-04 21:04:36 +08:00
John Smith
14c2fd48df 临时修复连接5分钟后用户名变星号的问题 2023-07-03 23:03:14 +08:00
John Smith
8683c0bad7 Merge pull request #105 from xfgryujk/dependabot/pip/tornado-6.3.2
Bump tornado from 6.1.0 to 6.3.2
2023-05-26 09:27:44 +08:00
dependabot[bot]
a1189aea69 Bump tornado from 6.1.0 to 6.3.2
Bumps [tornado](https://github.com/tornadoweb/tornado) from 6.1.0 to 6.3.2.
- [Changelog](https://github.com/tornadoweb/tornado/blob/master/docs/releases.rst)
- [Commits](https://github.com/tornadoweb/tornado/compare/v6.1.0...v6.3.2)

---
updated-dependencies:
- dependency-name: tornado
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-25 18:33:21 +00:00
John Smith
9ce1dd21aa 更新教程截图 2022-10-09 19:11:21 +08:00
John Smith
1f89d0d1cf 添加cookie提高获取头像成功率 2022-10-09 18:53:42 +08:00
John Smith
062e7ed1aa 临时修复获取头像请求被屏蔽的问题 2022-09-28 00:11:29 +08:00
16 changed files with 114 additions and 30 deletions

View File

@@ -55,7 +55,7 @@
npm i
npm run build
```
2. 运行服务器需要Python3.6以上版本):
2. 运行服务器需要Python3.8以上版本):
```sh
pip3 install -r requirements.txt
python3 main.py

View File

@@ -148,7 +148,7 @@ class ChatHandler(tornado.websocket.WebSocketHandler): # noqa
)
def _on_receive_timeout(self):
logger.warning('client=%s timed out', self.request.remote_ip)
logger.info('client=%s timed out', self.request.remote_ip)
self._receive_timeout_timer_handle = None
self.close()
@@ -308,8 +308,9 @@ class RoomInfoHandler(api.base.ApiHandler): # noqa
async with utils.request.http_session.get(
blivedm_client.ROOM_INIT_URL,
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)'
' Chrome/102.0.0.0 Safari/537.36'
**utils.request.BILIBILI_COMMON_HEADERS,
'Origin': 'https://live.bilibili.com',
'Referer': f'https://live.bilibili.com/{room_id}'
},
params={
'room_id': room_id

Submodule blivedm updated: 2cca7354b1...9d92fcdc91

View File

@@ -1,6 +1,6 @@
{
"name": "blivechat",
"version": "1.6.1",
"version": "1.6.2",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 301 KiB

After

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -107,7 +107,7 @@ export default class ChatClientDirect {
sendAuth() {
let authParams = {
uid: 0,
uid: this.roomOwnerUid,
roomid: this.roomId,
protover: 3,
platform: 'web',

View File

@@ -10,8 +10,7 @@ const COMMAND_UPDATE_TRANSLATION = 7
// const CONTENT_TYPE_TEXT = 0
const CONTENT_TYPE_EMOTICON = 1
const HEARTBEAT_INTERVAL = 10 * 1000
const RECEIVE_TIMEOUT = HEARTBEAT_INTERVAL + (5 * 1000)
const RECEIVE_TIMEOUT = 15 * 1000
export default class ChatClientRelay {
constructor(roomId, autoTranslate) {
@@ -28,7 +27,6 @@ export default class ChatClientRelay {
this.websocket = null
this.retryCount = 0
this.isDestroying = false
this.heartbeatTimerId = null
this.receiveTimeoutTimerId = null
}
@@ -66,16 +64,9 @@ export default class ChatClientRelay {
}
}
}))
this.heartbeatTimerId = window.setInterval(this.sendHeartbeat.bind(this), HEARTBEAT_INTERVAL)
this.refreshReceiveTimeoutTimer()
}
sendHeartbeat() {
this.websocket.send(JSON.stringify({
cmd: COMMAND_HEARTBEAT
}))
}
refreshReceiveTimeoutTimer() {
if (this.receiveTimeoutTimerId) {
window.clearTimeout(this.receiveTimeoutTimerId)
@@ -95,10 +86,6 @@ export default class ChatClientRelay {
onWsClose() {
this.websocket = null
if (this.heartbeatTimerId) {
window.clearInterval(this.heartbeatTimerId)
this.heartbeatTimerId = null
}
if (this.receiveTimeoutTimerId) {
window.clearTimeout(this.receiveTimeoutTimerId)
this.receiveTimeoutTimerId = null
@@ -117,6 +104,10 @@ export default class ChatClientRelay {
let { cmd, data } = JSON.parse(event.data)
switch (cmd) {
case COMMAND_HEARTBEAT: {
// 不能由定时器触发发心跳包因为浏览器会把不活动页面的定时器调到1分钟以上
this.websocket.send(JSON.stringify({
cmd: COMMAND_HEARTBEAT
}))
break
}
case COMMAND_ADD_TEXT: {

View File

@@ -2,4 +2,4 @@ aiohttp==3.7.4
Brotli==1.0.9
pycryptodome==3.10.1
sqlalchemy==1.4.31
tornado==6.1.0
tornado==6.3.2

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 97 KiB

View File

@@ -1,8 +1,10 @@
# -*- coding: utf-8 -*-
import asyncio
import datetime
import hashlib
import logging
import re
import urllib.parse
from typing import *
import aiohttp
@@ -29,6 +31,14 @@ _uid_queue_to_fetch: Optional[asyncio.Queue] = None
# 上次被B站ban时间
_last_fetch_banned_time: Optional[datetime.datetime] = None
# wbi密码表
WBI_KEY_INDEX_TABLE = [
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35,
27, 43, 5, 49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13
]
# wbi鉴权口令
_wbi_key = ''
def init():
cfg = config.get_config()
@@ -45,6 +55,9 @@ async def get_avatar_url(user_id):
async def get_avatar_url_or_none(user_id):
if user_id == 0:
return None
avatar_url = get_avatar_url_from_memory(user_id)
if avatar_url is not None:
return avatar_url
@@ -146,16 +159,20 @@ async def _get_avatar_url_from_web_coroutine(user_id, future):
async def _do_get_avatar_url_from_web(user_id):
global _wbi_key
if _wbi_key == '':
# TODO 判断一下是否正在获取
_wbi_key = await _get_wbi_key()
try:
async with utils.request.http_session.get(
'https://api.bilibili.com/x/space/acc/info',
'https://api.bilibili.com/x/space/wbi/acc/info',
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)'
' Chrome/102.0.0.0 Safari/537.36'
**utils.request.BILIBILI_COMMON_HEADERS,
'Origin': 'https://space.bilibili.com',
'Referer': f'https://space.bilibili.com/{user_id}/'
},
params={
'mid': user_id
}
params=_add_wbi_sign({'mid': user_id}),
) as r:
if r.status != 200:
logger.warning('Failed to fetch avatar: status=%d %s uid=%d', r.status, r.reason, user_id)
@@ -168,11 +185,78 @@ async def _do_get_avatar_url_from_web(user_id):
except (aiohttp.ClientConnectionError, asyncio.TimeoutError):
return None
if data['code'] != 0:
# 这里虽然失败但不会被ban一段时间
logger.info('Failed to fetch avatar: code=%d %s uid=%d', data['code'], data['message'], user_id)
if data['code'] == -403:
_wbi_key = ''
return None
avatar_url = process_avatar_url(data['data']['face'])
update_avatar_cache(user_id, avatar_url)
return avatar_url
async def _get_wbi_key():
try:
async with utils.request.http_session.get(
'https://api.bilibili.com/nav',
headers=utils.request.BILIBILI_COMMON_HEADERS,
) as r:
if r.status != 200:
logger.warning('Failed to get wbi key: status=%d %s', r.status, r.reason)
return ''
data = await r.json()
except (aiohttp.ClientConnectionError, asyncio.TimeoutError):
logger.exception('Failed to get wbi key:')
return ''
try:
wbi_img = data['data']['wbi_img']
img_key = wbi_img['img_url'].rpartition('/')[2].partition('.')[0]
sub_key = wbi_img['sub_url'].rpartition('/')[2].partition('.')[0]
except KeyError:
logger.warning('Failed to get wbi key: data=%s', data)
return ''
shuffled_key = img_key + sub_key
wbi_key = []
for index in WBI_KEY_INDEX_TABLE:
if index < len(shuffled_key):
wbi_key.append(shuffled_key[index])
return ''.join(wbi_key)
def _add_wbi_sign(params: dict):
if _wbi_key == '':
return params
wts = str(int(datetime.datetime.now().timestamp()))
params_to_sign = {**params, 'wts': wts}
# 按key字典序排序
params_to_sign = {
key: params_to_sign[key]
for key in sorted(params_to_sign.keys())
}
# 过滤一些字符
for key, value in params_to_sign.items():
value = ''.join(
ch
for ch in str(value)
if ch not in "!'()*"
)
params_to_sign[key] = value
str_to_sign = urllib.parse.urlencode(params_to_sign) + _wbi_key
w_rid = hashlib.md5(str_to_sign.encode('utf-8')).hexdigest()
return {
**params,
'wts': wts,
'w_rid': w_rid
}
def process_avatar_url(avatar_url):
# 去掉协议兼容HTTP、HTTPS
m = re.fullmatch(r'(?:https?:)?(.*)', avatar_url)

View File

@@ -263,7 +263,7 @@ class LiveMsgHandler(blivedm.BaseHandler):
price=data['price'],
message=data['message'],
start_time=data['start_time'],
id_=data['id'],
id=data['id'],
uid=data['uid'],
uname=data['user_info']['uname'],
face=data['user_info']['face'],

View File

@@ -5,7 +5,7 @@ import aiohttp
import utils.request
VERSION = 'v1.6.1'
VERSION = 'v1.6.2'
def check_update():

View File

@@ -1,4 +1,12 @@
# -*- coding: utf-8 -*-
import aiohttp
# 不带这堆头部有时候也能成功请求,但是带上后成功的概率更高
BILIBILI_COMMON_HEADERS = {
'Origin': 'https://www.bilibili.com',
'Referer': 'https://www.bilibili.com/',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)'
' Chrome/114.0.0.0 Safari/537.36'
}
http_session = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10))