diff --git a/api/chat.py b/api/chat.py index 647b579..06a53c0 100644 --- a/api/chat.py +++ b/api/chat.py @@ -31,6 +31,7 @@ class Command(enum.IntEnum): ADD_SUPER_CHAT = 5 DEL_SUPER_CHAT = 6 UPDATE_TRANSLATION = 7 + FATAL_ERROR = 8 class ContentType(enum.IntEnum): @@ -38,6 +39,10 @@ class ContentType(enum.IntEnum): EMOTICON = 1 +class FatalErrorType(enum.IntEnum): + AUTH_CODE_ERROR = 1 + + def make_message_body(cmd, data): return json.dumps( { diff --git a/api/open_live.py b/api/open_live.py index 803c0ae..325cd2a 100644 --- a/api/open_live.py +++ b/api/open_live.py @@ -35,7 +35,7 @@ class TransportError(Exception): class BusinessError(Exception): """业务返回码错误""" def __init__(self, data: dict): - super().__init__(f"message={data['message']}, request_id={data['request_id']}") + super().__init__(f"code={data['code']}, message={data['message']}, request_id={data['request_id']}") self.data = data @property @@ -115,19 +115,22 @@ async def _read_response(req_ctx_mgr: AsyncContextManager[aiohttp.ClientResponse def _validate_auth_code(auth_code): - if re.fullmatch(r'[0-9A-Z]{11,16}', auth_code): + # 我也不知道是不是一定是这个格式,先临时这么处理 + if re.fullmatch(r'[0-9A-Z]{12,14}', auth_code): return - - logger.warning('Auth code error! auth_code=%s', auth_code) raise BusinessError({ 'code': 7007, 'message': '身份码错误', - 'data': {}, - 'request_id': '0' + 'request_id': '0', + 'data': None }) class _OpenLiveHandlerBase(api.base.ApiHandler): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.res: Optional[dict] = None + def prepare(self): super().prepare() if not isinstance(self.json_args, dict): @@ -147,14 +150,14 @@ class _PublicHandlerBase(_OpenLiveHandlerBase): async def post(self): try: - res = await request_open_live_or_common_server( + self.res = await request_open_live_or_common_server( self._OPEN_LIVE_URL, self._COMMON_SERVER_URL, self.json_args ) except TransportError: raise tornado.web.HTTPError(500) except BusinessError as e: - res = e.data - self.write(res) + self.res = e.data + self.write(self.res) class _PrivateHandlerBase(_OpenLiveHandlerBase): @@ -167,21 +170,41 @@ class _PrivateHandlerBase(_OpenLiveHandlerBase): raise tornado.web.HTTPError(501) try: - res = await _request_open_live(self._OPEN_LIVE_URL, self.json_args) + self.res = await _request_open_live(self._OPEN_LIVE_URL, self.json_args) except TransportError: raise tornado.web.HTTPError(500) except BusinessError as e: - res = e.data - self.write(res) + self.res = e.data + self.write(self.res) -class StartGamePublicHandler(_PublicHandlerBase): +class _StartGameMixin(_OpenLiveHandlerBase): _OPEN_LIVE_URL = START_GAME_OPEN_LIVE_URL _COMMON_SERVER_URL = START_GAME_COMMON_SERVER_URL + async def post(self): + await super().post() # noqa + if self.res is None: + return -class StartGamePrivateHandler(_PrivateHandlerBase): - _OPEN_LIVE_URL = START_GAME_OPEN_LIVE_URL + try: + room_id = self.res['data']['anchor_info']['room_id'] + except (TypeError, KeyError): + room_id = None + code = self.res['code'] + logger.info('room_id=%s start game res: %s %s', room_id, code, self.res['message']) + if code == 7007: + # 身份码错误 + # 让我看看是哪个混蛋把房间ID、UID当做身份码 + logger.warning('Auth code error! auth_code=%s', self.json_args.get('code', None)) + + +class StartGamePublicHandler(_StartGameMixin, _PublicHandlerBase): + pass + + +class StartGamePrivateHandler(_StartGameMixin, _PrivateHandlerBase): + pass class EndGamePublicHandler(_PublicHandlerBase): diff --git a/frontend/src/api/chat/ChatClientDirectOpenLive.js b/frontend/src/api/chat/ChatClientDirectOpenLive.js index b14625f..dd722a3 100644 --- a/frontend/src/api/chat/ChatClientDirectOpenLive.js +++ b/frontend/src/api/chat/ChatClientDirectOpenLive.js @@ -51,10 +51,18 @@ export default class ChatClientDirectOpenLive extends ChatClientOfficialBase { app_id: 0 })).data if (res.code !== 0) { - throw Error(`code=${res.code}, message=${res.message}, request_id=${res.request_id}`) + let msg = `code=${res.code}, message=${res.message}, request_id=${res.request_id}` + if (res.code === 7007) { + // 身份码错误 + throw new chat.ChatClientFatalError(chat.FATAL_ERROR_TYPE_AUTH_CODE_ERROR, msg) + } + throw Error(msg) } } catch (e) { console.error('startGame failed:', e) + if (e instanceof chat.ChatClientFatalError) { + throw e + } return false } diff --git a/frontend/src/api/chat/ChatClientOfficialBase/index.js b/frontend/src/api/chat/ChatClientOfficialBase/index.js index 6a00ed8..1905747 100644 --- a/frontend/src/api/chat/ChatClientOfficialBase/index.js +++ b/frontend/src/api/chat/ChatClientOfficialBase/index.js @@ -1,6 +1,8 @@ import { BrotliDecode } from './brotli_decode' import { inflate } from 'pako' +import * as chat from '..' + const HEADER_SIZE = 16 export const WS_BODY_PROTOCOL_VERSION_NORMAL = 0 @@ -50,6 +52,8 @@ export default class ChatClientOfficialBase { this.onDelSuperChat = null this.onUpdateTranslation = null + this.onFatalError = null + this.needInitRoom = true this.websocket = null this.retryCount = 0 @@ -116,7 +120,18 @@ export default class ChatClientOfficialBase { return } - if (!await this.initRoom()) { + let res + try { + res = await this.initRoom() + } catch (e) { + res = false + console.error('initRoom exception:', e) + if (e instanceof chat.ChatClientFatalError && this.onFatalError) { + this.onFatalError(e) + } + } + + if (!res) { this.onWsClose() throw Error('initRoom failed') } diff --git a/frontend/src/api/chat/ChatClientRelay.js b/frontend/src/api/chat/ChatClientRelay.js index a570204..22c0927 100644 --- a/frontend/src/api/chat/ChatClientRelay.js +++ b/frontend/src/api/chat/ChatClientRelay.js @@ -1,3 +1,5 @@ +import * as chat from '.' + const COMMAND_HEARTBEAT = 0 const COMMAND_JOIN_ROOM = 1 const COMMAND_ADD_TEXT = 2 @@ -6,6 +8,7 @@ const COMMAND_ADD_MEMBER = 4 const COMMAND_ADD_SUPER_CHAT = 5 const COMMAND_DEL_SUPER_CHAT = 6 const COMMAND_UPDATE_TRANSLATION = 7 +const COMMAND_FATAL_ERROR = 8 // const CONTENT_TYPE_TEXT = 0 const CONTENT_TYPE_EMOTICON = 1 @@ -24,6 +27,8 @@ export default class ChatClientRelay { this.onDelSuperChat = null this.onUpdateTranslation = null + this.onFatalError = null + this.websocket = null this.retryCount = 0 this.isDestroying = false @@ -176,6 +181,14 @@ export default class ChatClientRelay { this.onUpdateTranslation(data) break } + case COMMAND_FATAL_ERROR: { + if (!this.onFatalError) { + break + } + let error = new chat.ChatClientFatalError(data.type, data.msg) + this.onFatalError(error) + break + } } } } diff --git a/frontend/src/api/chat/ChatClientTest.js b/frontend/src/api/chat/ChatClientTest.js index 057d4a7..d98730c 100644 --- a/frontend/src/api/chat/ChatClientTest.js +++ b/frontend/src/api/chat/ChatClientTest.js @@ -238,6 +238,8 @@ export default class ChatClientTest { this.onDelSuperChat = null this.onUpdateTranslation = null + this.onFatalError = null + this.timerId = null } diff --git a/frontend/src/api/chat/index.js b/frontend/src/api/chat/index.js index 628d4a0..b26d864 100644 --- a/frontend/src/api/chat/index.js +++ b/frontend/src/api/chat/index.js @@ -1,5 +1,14 @@ import axios from 'axios' +export const FATAL_ERROR_TYPE_AUTH_CODE_ERROR = 1 + +export class ChatClientFatalError extends Error { + constructor(type, message) { + super(message) + this.type = type + } +} + export const DEFAULT_AVATAR_URL = '//static.hdslb.com/images/member/noface.gif' export function processAvatarUrl(avatarUrl) { diff --git a/frontend/src/views/Room.vue b/frontend/src/views/Room.vue index f9f649c..fbf576b 100644 --- a/frontend/src/views/Room.vue +++ b/frontend/src/views/Room.vue @@ -88,7 +88,7 @@ export default { // 提示用户已加载 this.$message({ message: 'Loaded', - duration: '500' + duration: 500 }) }, beforeDestroy() { @@ -165,6 +165,7 @@ export default { this.chatClient.onAddSuperChat = this.onAddSuperChat this.chatClient.onDelSuperChat = this.onDelSuperChat this.chatClient.onUpdateTranslation = this.onUpdateTranslation + this.chatClient.onFatalError = this.onFatalError this.chatClient.start() }, async initTextEmoticons() { @@ -266,6 +267,18 @@ export default { } this.$refs.renderer.updateMessage(data.id, { translation: data.translation }) }, + onFatalError(error) { + this.$message.error({ + message: error.toString(), + duration: 10 * 1000 + }) + this.chatClient.stop() + + if (error.type === chat.FATAL_ERROR_TYPE_AUTH_CODE_ERROR) { + // Read The Fucking Manual + this.$router.push({ name: 'help' }) + } + }, filterTextMessage(data) { if (this.config.blockGiftDanmaku && data.isGiftDanmaku) { diff --git a/services/chat.py b/services/chat.py index 77625ab..855602e 100644 --- a/services/chat.py +++ b/services/chat.py @@ -176,8 +176,20 @@ class OpenLiveClient(blivedm.OpenLiveClient): except api_open_live.TransportError: logger.error('_start_game() failed') return False - except api_open_live.BusinessError: + except api_open_live.BusinessError as e: logger.warning('_start_game() failed') + + if e.code == 7007: + # 身份码错误 + # 让我看看是哪个混蛋把房间ID、UID当做身份码 + logger.warning('Auth code error! auth_code=%s', self._room_owner_auth_code) + room = client_room_manager.get_room(self.room_key) + if room is not None: + room.send_cmd_data(api.chat.Command.FATAL_ERROR, { + 'type': api.chat.FatalErrorType.AUTH_CODE_ERROR, + 'msg': str(e) + }) + return False return self._parse_start_game(data['data'])