Files
social-mcp/apps/xhs-mcp/src/platforms/xiaohongshu/target-resolver.ts
T

96 lines
2.4 KiB
TypeScript

interface FeedTargetInput {
feed_id?: string;
xsec_token?: string;
url?: string;
}
interface UserTargetInput {
user_id?: string;
xsec_token?: string;
url?: string;
}
interface FeedTargetResolved {
feedId: string;
xsecToken: string;
}
interface UserTargetResolved {
userId: string;
xsecToken: string;
}
function parseXhsUrl(rawUrl: string): URL {
const trimmed = rawUrl.trim();
if (!trimmed) {
throw new Error('url cannot be empty');
}
if (/^https?:\/\//i.test(trimmed)) {
return new URL(trimmed);
}
if (trimmed.startsWith('/')) {
return new URL(`https://www.xiaohongshu.com${trimmed}`);
}
return new URL(`https://${trimmed}`);
}
function extractFeedIdFromPath(pathname: string): string | undefined {
const patterns = [
/\/explore\/([a-zA-Z0-9_-]+)/,
/\/discovery\/item\/([a-zA-Z0-9_-]+)/,
/\/note\/([a-zA-Z0-9_-]+)/,
];
for (const pattern of patterns) {
const match = pathname.match(pattern);
if (match?.[1]) return match[1];
}
return undefined;
}
function extractUserIdFromPath(pathname: string): string | undefined {
const match = pathname.match(/\/user\/profile\/([a-zA-Z0-9_-]+)/);
return match?.[1];
}
function extractXsecToken(url: URL): string | undefined {
return url.searchParams.get('xsec_token') ?? url.searchParams.get('xsecToken') ?? undefined;
}
export function resolveFeedTarget(input: FeedTargetInput): FeedTargetResolved {
let feedId = input.feed_id?.trim();
let xsecToken = input.xsec_token?.trim();
if (input.url) {
const parsed = parseXhsUrl(input.url);
feedId = feedId || extractFeedIdFromPath(parsed.pathname);
xsecToken = xsecToken || extractXsecToken(parsed);
}
if (!feedId || !xsecToken) {
throw new Error('xhs_get_feed_detail requires either url with feed_id/xsec_token, or both feed_id and xsec_token');
}
return { feedId, xsecToken };
}
export function resolveUserTarget(input: UserTargetInput): UserTargetResolved {
let userId = input.user_id?.trim();
let xsecToken = input.xsec_token?.trim();
if (input.url) {
const parsed = parseXhsUrl(input.url);
userId = userId || extractUserIdFromPath(parsed.pathname);
xsecToken = xsecToken || extractXsecToken(parsed);
}
if (!userId || !xsecToken) {
throw new Error('xhs_get_user_profile requires either url with user_id/xsec_token, or both user_id and xsec_token');
}
return { userId, xsecToken };
}