fix: 保留Bilibili链接HTML实体边界
This commit is contained in:
parent
0782b1d639
commit
ecea754cac
@ -4,7 +4,8 @@ import {
|
|||||||
isAllowedBilibiliUrl,
|
isAllowedBilibiliUrl,
|
||||||
} from './bilibili-url-parser';
|
} from './bilibili-url-parser';
|
||||||
|
|
||||||
const URL_PATTERN = /https?:\/\/[^\s<>"',;:!,。!?;、\]\)}]+/giu;
|
const URL_PATTERN = /https?:\/\/[^\s<>"',:!,。!?;、\]\)}]+/giu;
|
||||||
|
const HTML_ENTITY_BEFORE_SEMICOLON = /&(?:amp|quot|lt|gt|#34|#60|#62)$/iu;
|
||||||
const MAX_DEPTH = 7;
|
const MAX_DEPTH = 7;
|
||||||
const MAX_NODES = 200;
|
const MAX_NODES = 200;
|
||||||
const MAX_STRINGS = 200;
|
const MAX_STRINGS = 200;
|
||||||
@ -31,7 +32,9 @@ export function extractBilibiliUrls(input: BilibiliUrlExtractionInput) {
|
|||||||
|
|
||||||
for (const text of candidates) {
|
for (const text of candidates) {
|
||||||
for (const rawUrl of text.match(URL_PATTERN) || []) {
|
for (const rawUrl of text.match(URL_PATTERN) || []) {
|
||||||
const cleaned = cleanBilibiliUrlCandidate(rawUrl);
|
const cleaned = cleanBilibiliUrlCandidate(
|
||||||
|
trimNonEntitySemicolonTail(rawUrl),
|
||||||
|
);
|
||||||
if (!isAllowedBilibiliUrl(cleaned) || seen.has(cleaned)) continue;
|
if (!isAllowedBilibiliUrl(cleaned) || seen.has(cleaned)) continue;
|
||||||
seen.add(cleaned);
|
seen.add(cleaned);
|
||||||
output.push(cleaned);
|
output.push(cleaned);
|
||||||
@ -42,6 +45,20 @@ export function extractBilibiliUrls(input: BilibiliUrlExtractionInput) {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes semicolon-delimited CQ tail text while keeping common HTML entities intact.
|
||||||
|
* @param rawUrl - Raw URL token collected by the extractor regex.
|
||||||
|
* @returns URL token that can still be decoded by `cleanBilibiliUrlCandidate`.
|
||||||
|
*/
|
||||||
|
function trimNonEntitySemicolonTail(rawUrl: string) {
|
||||||
|
for (let index = 0; index < rawUrl.length; index += 1) {
|
||||||
|
if (rawUrl[index] !== ';') continue;
|
||||||
|
if (HTML_ENTITY_BEFORE_SEMICOLON.test(rawUrl.slice(0, index))) continue;
|
||||||
|
return rawUrl.slice(0, index);
|
||||||
|
}
|
||||||
|
return rawUrl;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collects string values that may contain links from text fields and nested QQ card objects.
|
* Collects string values that may contain links from text fields and nested QQ card objects.
|
||||||
* @param input - Extraction input built from normalized message state.
|
* @param input - Extraction input built from normalized message state.
|
||||||
|
|||||||
@ -135,4 +135,36 @@ describe('Bilibili URL extractor', () => {
|
|||||||
}),
|
}),
|
||||||
).toEqual(['https://b23.tv/abc123']);
|
).toEqual(['https://b23.tv/abc123']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('keeps html entity wrappers intact until URL cleanup runs', () => {
|
||||||
|
expect(
|
||||||
|
extractBilibiliUrls({
|
||||||
|
messageText: '',
|
||||||
|
rawEvent: {
|
||||||
|
message: [
|
||||||
|
{
|
||||||
|
data: {
|
||||||
|
data: '<【https://www.bilibili.com/video/BV1xx411c7mD】>',
|
||||||
|
},
|
||||||
|
type: 'xml',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
rawMessage: '',
|
||||||
|
}),
|
||||||
|
).toEqual(['https://www.bilibili.com/video/BV1xx411c7mD']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preserves ampersand query entities while removing CQ separators', () => {
|
||||||
|
expect(
|
||||||
|
extractBilibiliUrls({
|
||||||
|
messageText:
|
||||||
|
'[CQ:share,url=https://www.bilibili.com/video/BV1xx411c7mD?foo=1&bar=2,title=视频]',
|
||||||
|
rawEvent: {},
|
||||||
|
rawMessage: '',
|
||||||
|
}),
|
||||||
|
).toEqual([
|
||||||
|
'https://www.bilibili.com/video/BV1xx411c7mD?foo=1&bar=2',
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user