fix: 保持Bilibili卡片事件异常不外抛
This commit is contained in:
parent
c14bcea2dc
commit
98e26616bb
@ -122,14 +122,19 @@ export class BilibiliCardApplication {
|
|||||||
if (cached && cached.expiresAt > current) return cached.value;
|
if (cached && cached.expiresAt > current) return cached.value;
|
||||||
|
|
||||||
const config = readBilibiliCardRuntimeConfig(this.host);
|
const config = readBilibiliCardRuntimeConfig(this.host);
|
||||||
const value = (
|
try {
|
||||||
await this.host.getBoundEventPluginKeys(normalizedSelfId)
|
const value = (
|
||||||
).includes(this.manifest.pluginKey);
|
await this.host.getBoundEventPluginKeys(normalizedSelfId)
|
||||||
this.boundCache.set(normalizedSelfId, {
|
).includes(this.manifest.pluginKey);
|
||||||
expiresAt: current + Math.min(config.dedupeTtlMs, 60000),
|
this.boundCache.set(normalizedSelfId, {
|
||||||
value,
|
expiresAt: current + Math.min(config.dedupeTtlMs, 60000),
|
||||||
});
|
value,
|
||||||
return value;
|
});
|
||||||
|
return value;
|
||||||
|
} catch (error) {
|
||||||
|
this.warn(`Bilibili 事件绑定查询失败: ${normalizeError(error)}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -147,7 +152,14 @@ export class BilibiliCardApplication {
|
|||||||
* @param message - Warning message safe for platform logs.
|
* @param message - Warning message safe for platform logs.
|
||||||
*/
|
*/
|
||||||
private warn(message: string) {
|
private warn(message: string) {
|
||||||
this.host.warn?.(message);
|
try {
|
||||||
|
const result = this.host.warn?.(message) as unknown;
|
||||||
|
if (isThenable(result)) {
|
||||||
|
result.catch(() => undefined);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,6 +194,22 @@ function isB23ShortLink(url: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects promise-like warning results so rejected async loggers cannot escape later.
|
||||||
|
* @param value - Return value from the optional host warning hook.
|
||||||
|
* @returns `true` when the value exposes a callable `catch` method.
|
||||||
|
*/
|
||||||
|
function isThenable(
|
||||||
|
value: unknown,
|
||||||
|
): value is { catch: (handler: () => void) => unknown } {
|
||||||
|
return (
|
||||||
|
typeof value === 'object' &&
|
||||||
|
value !== null &&
|
||||||
|
'catch' in value &&
|
||||||
|
typeof value.catch === 'function'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts thrown values to stable warning text.
|
* Converts thrown values to stable warning text.
|
||||||
* @param error - Error or arbitrary thrown value from host or domain code.
|
* @param error - Error or arbitrary thrown value from host or domain code.
|
||||||
|
|||||||
@ -25,6 +25,29 @@ describe('Bilibili card application', () => {
|
|||||||
expect(host.sendText).not.toHaveBeenCalled();
|
expect(host.sendText).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('warns and returns false when binding lookup fails', async () => {
|
||||||
|
const host = createHost({
|
||||||
|
getBoundEventPluginKeys: jest
|
||||||
|
.fn()
|
||||||
|
.mockRejectedValue(new Error('binding down')),
|
||||||
|
});
|
||||||
|
const application = new BilibiliCardApplication(host, createManifest());
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
application.handleMessage(
|
||||||
|
createMessage({
|
||||||
|
messageText: 'https://www.bilibili.com/video/BV1xx411c7mD',
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
).resolves.toBe(false);
|
||||||
|
|
||||||
|
expect(host.warn).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining('binding down'),
|
||||||
|
);
|
||||||
|
expect(host.requestJson).not.toHaveBeenCalled();
|
||||||
|
expect(host.sendText).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('ignores messages sent by the bot itself', async () => {
|
it('ignores messages sent by the bot itself', async () => {
|
||||||
const host = createHost({
|
const host = createHost({
|
||||||
getBoundEventPluginKeys: jest.fn().mockResolvedValue(['bilibili-card']),
|
getBoundEventPluginKeys: jest.fn().mockResolvedValue(['bilibili-card']),
|
||||||
@ -169,6 +192,70 @@ describe('Bilibili card application', () => {
|
|||||||
expect(host.sendText).not.toHaveBeenCalled();
|
expect(host.sendText).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns false when warning throws during binding lookup failure', async () => {
|
||||||
|
const host = createHost({
|
||||||
|
getBoundEventPluginKeys: jest
|
||||||
|
.fn()
|
||||||
|
.mockRejectedValue(new Error('binding down')),
|
||||||
|
warn: jest.fn(() => {
|
||||||
|
throw new Error('logger down');
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const application = new BilibiliCardApplication(host, createManifest());
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
application.handleMessage(
|
||||||
|
createMessage({
|
||||||
|
messageText: 'https://www.bilibili.com/video/BV1xx411c7mD',
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
).resolves.toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false when warning throws during short-link failure', async () => {
|
||||||
|
const host = createHost({
|
||||||
|
getBoundEventPluginKeys: jest.fn().mockResolvedValue(['bilibili-card']),
|
||||||
|
resolveRedirect: jest.fn().mockRejectedValue(new Error('redirect down')),
|
||||||
|
warn: jest.fn(() => {
|
||||||
|
throw new Error('logger down');
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const application = new BilibiliCardApplication(host, createManifest());
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
application.handleMessage(
|
||||||
|
createMessage({
|
||||||
|
messageText: 'https://b23.tv/abc123',
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
).resolves.toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('attaches a rejection handler when warning returns a promise', async () => {
|
||||||
|
const catchSpy = jest.fn();
|
||||||
|
const host = createHost({
|
||||||
|
getBoundEventPluginKeys: jest.fn().mockResolvedValue(['bilibili-card']),
|
||||||
|
resolveRedirect: jest.fn().mockRejectedValue(new Error('redirect down')),
|
||||||
|
warn: jest.fn(
|
||||||
|
() =>
|
||||||
|
({
|
||||||
|
catch: catchSpy,
|
||||||
|
}) as unknown as void,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
const application = new BilibiliCardApplication(host, createManifest());
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
application.handleMessage(
|
||||||
|
createMessage({
|
||||||
|
messageText: 'https://b23.tv/abc123',
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
).resolves.toBe(false);
|
||||||
|
|
||||||
|
expect(catchSpy).toHaveBeenCalledWith(expect.any(Function));
|
||||||
|
});
|
||||||
|
|
||||||
it('warns and returns false when short-link resolution fails', async () => {
|
it('warns and returns false when short-link resolution fails', async () => {
|
||||||
const host = createHost({
|
const host = createHost({
|
||||||
getBoundEventPluginKeys: jest.fn().mockResolvedValue(['bilibili-card']),
|
getBoundEventPluginKeys: jest.fn().mockResolvedValue(['bilibili-card']),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user