feat: 扩展 QQBot FF14 查价区服解析
This commit is contained in:
parent
31119bbc93
commit
92462ae736
@ -1,6 +1,13 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import type { QqbotCommand } from './qqbot-command.entity';
|
||||
import type { QqbotNormalizedMessage } from '../qqbot.types';
|
||||
import {
|
||||
isQqbotFf14DataCenterName,
|
||||
isQqbotFf14LocationName,
|
||||
isQqbotFf14RegionName,
|
||||
isQqbotFf14WorldName,
|
||||
splitQqbotFf14WorldPath,
|
||||
} from '../plugins/ff14Market/qqbot-ff14-worlds';
|
||||
|
||||
export type QqbotCommandMatchResult = {
|
||||
alias: string;
|
||||
@ -68,9 +75,9 @@ export class QqbotCommandParserService {
|
||||
const positional: string[] = [];
|
||||
|
||||
for (const token of tokens) {
|
||||
if (/^(hq|HQ)$/.test(token)) {
|
||||
if (/^hq$/i.test(token)) {
|
||||
flags.set('hq', true);
|
||||
} else if (/^(nq|NQ)$/.test(token)) {
|
||||
} else if (/^nq$/i.test(token)) {
|
||||
flags.set('hq', 'false');
|
||||
} else if (token.includes('=')) {
|
||||
const [key, ...rest] = token.split('=');
|
||||
@ -80,27 +87,105 @@ export class QqbotCommandParserService {
|
||||
}
|
||||
}
|
||||
|
||||
let world = this.normalizeString(flags.get('world') || flags.get('server'));
|
||||
let region = this.normalizeString(flags.get('region') || flags.get('地区'));
|
||||
let dataCenter = this.normalizeString(
|
||||
flags.get('dataCenter') ||
|
||||
flags.get('datacenter') ||
|
||||
flags.get('dc') ||
|
||||
flags.get('大区'),
|
||||
);
|
||||
let world = this.normalizeString(
|
||||
flags.get('world') ||
|
||||
flags.get('server') ||
|
||||
flags.get('服务器') ||
|
||||
flags.get('小区'),
|
||||
);
|
||||
let item = positional.join(' ');
|
||||
if (!world && positional.length > 1) {
|
||||
world = positional[positional.length - 1];
|
||||
item = positional.slice(0, -1).join(' ');
|
||||
|
||||
const worldPath = splitQqbotFf14WorldPath(world);
|
||||
if (worldPath.dataCenter && worldPath.world) {
|
||||
dataCenter = dataCenter || worldPath.dataCenter;
|
||||
region = region || worldPath.region || '';
|
||||
world = worldPath.world;
|
||||
}
|
||||
|
||||
if (!world && !dataCenter && positional.length > 1) {
|
||||
const picked = this.pickTrailingFf14Location(positional);
|
||||
if (picked) {
|
||||
dataCenter = picked.dataCenter || dataCenter;
|
||||
item = picked.item;
|
||||
region = picked.region || region;
|
||||
world = picked.world || world;
|
||||
}
|
||||
}
|
||||
if (item.includes('@')) {
|
||||
const [itemName, worldName] = item.split('@');
|
||||
const itemWorldPath = splitQqbotFf14WorldPath(worldName);
|
||||
item = itemName.trim();
|
||||
world = world || worldName?.trim();
|
||||
dataCenter = dataCenter || itemWorldPath.dataCenter || '';
|
||||
region = region || itemWorldPath.region || '';
|
||||
world = world || itemWorldPath.world || worldName?.trim();
|
||||
}
|
||||
|
||||
return {
|
||||
dataCenter,
|
||||
hq: this.normalizeHq(flags.get('hq')),
|
||||
item,
|
||||
language: this.normalizeString(flags.get('lang')) || 'zh',
|
||||
raw: rawArgs,
|
||||
region,
|
||||
world,
|
||||
};
|
||||
}
|
||||
|
||||
private pickTrailingFf14Location(positional: string[]) {
|
||||
const last = positional[positional.length - 1];
|
||||
if (!isQqbotFf14LocationName(last)) return null;
|
||||
|
||||
const path = splitQqbotFf14WorldPath(last);
|
||||
if (path.dataCenter && path.world) {
|
||||
return {
|
||||
dataCenter: path.dataCenter,
|
||||
item: positional.slice(0, -1).join(' '),
|
||||
region: path.region,
|
||||
world: path.world,
|
||||
};
|
||||
}
|
||||
|
||||
const previous = positional[positional.length - 2];
|
||||
const beforePrevious = positional[positional.length - 3];
|
||||
if (
|
||||
previous &&
|
||||
isQqbotFf14DataCenterName(previous) &&
|
||||
isQqbotFf14WorldName(last)
|
||||
) {
|
||||
const hasRegion = beforePrevious && isQqbotFf14RegionName(beforePrevious);
|
||||
return {
|
||||
dataCenter: previous,
|
||||
item: positional.slice(0, hasRegion ? -3 : -2).join(' '),
|
||||
region: hasRegion ? beforePrevious : undefined,
|
||||
world: last,
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
previous &&
|
||||
isQqbotFf14RegionName(previous) &&
|
||||
isQqbotFf14DataCenterName(last)
|
||||
) {
|
||||
return {
|
||||
dataCenter: last,
|
||||
item: positional.slice(0, -2).join(' '),
|
||||
region: previous,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
item: positional.slice(0, -1).join(' '),
|
||||
world: last,
|
||||
};
|
||||
}
|
||||
|
||||
private normalizeHq(value?: string | true) {
|
||||
if (value === undefined) return undefined;
|
||||
if (value === true) return true;
|
||||
|
||||
@ -2,12 +2,14 @@ import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import * as http from 'node:http';
|
||||
import * as https from 'node:https';
|
||||
import { resolveQqbotFf14MarketTarget } from './qqbot-ff14-worlds';
|
||||
|
||||
type HttpMethod = 'GET';
|
||||
|
||||
type XivapiSearchItem = {
|
||||
fields?: {
|
||||
Icon?: string;
|
||||
IsUntradable?: boolean;
|
||||
LevelItem?: number;
|
||||
Name?: string;
|
||||
};
|
||||
@ -40,6 +42,7 @@ type UniversalisMarketResponse = {
|
||||
|
||||
export type QqbotFf14ResolvedItem = {
|
||||
icon?: string;
|
||||
isUntradable?: boolean;
|
||||
itemId: number;
|
||||
itemLevel?: number;
|
||||
name: string;
|
||||
@ -60,6 +63,13 @@ export type QqbotFf14PriceResult = {
|
||||
export class QqbotFf14ClientService {
|
||||
private readonly xivapiBaseUrl: string;
|
||||
private readonly universalisBaseUrl: string;
|
||||
private readonly cnItemAliases = new Map<
|
||||
string,
|
||||
{ itemId: number; name: string }
|
||||
>([
|
||||
['小鸣鼠', { itemId: 43590, name: '小鸣鼠角笛' }],
|
||||
['小鸣鼠角笛', { itemId: 43590, name: '小鸣鼠角笛' }],
|
||||
]);
|
||||
|
||||
constructor(private readonly configService: ConfigService) {
|
||||
this.xivapiBaseUrl =
|
||||
@ -75,22 +85,31 @@ export class QqbotFf14ClientService {
|
||||
itemId?: number | string;
|
||||
language?: string;
|
||||
}): Promise<QqbotFf14ResolvedItem> {
|
||||
const language = this.normalizeXivapiLanguage(params.language);
|
||||
const itemId = Number(params.itemId || params.item);
|
||||
if (Number.isInteger(itemId) && itemId > 0) {
|
||||
return this.getItemById(itemId, params.language);
|
||||
return this.getItemById(itemId, language);
|
||||
}
|
||||
|
||||
const keyword = `${params.item || ''}`.trim();
|
||||
if (!keyword) throw new Error('请提供 FF14 物品名称或物品 ID');
|
||||
|
||||
const alias = this.resolveCnItemAlias(keyword);
|
||||
if (alias) {
|
||||
return this.getItemById(alias.itemId, language, alias.name);
|
||||
}
|
||||
|
||||
const url = new URL(`${this.xivapiBaseUrl}/search`);
|
||||
url.searchParams.set('sheets', 'Item');
|
||||
url.searchParams.set('query', keyword);
|
||||
url.searchParams.set('language', params.language || 'zh');
|
||||
url.searchParams.set('fields', 'Name,Icon,LevelItem,IsUntradable');
|
||||
url.searchParams.set('query', `Name~"${this.escapeXivapiValue(keyword)}"`);
|
||||
url.searchParams.set('language', language);
|
||||
url.searchParams.set('limit', '10');
|
||||
|
||||
const data = await this.requestJson<{ results?: XivapiSearchItem[] }>(
|
||||
url,
|
||||
'GET',
|
||||
'XIVAPI 物品解析',
|
||||
);
|
||||
const item = (data.results || []).find(
|
||||
(result) =>
|
||||
@ -100,6 +119,7 @@ export class QqbotFf14ClientService {
|
||||
|
||||
return {
|
||||
icon: item.fields?.Icon,
|
||||
isUntradable: item.fields?.IsUntradable,
|
||||
itemId: Number(item.row_id || item.id),
|
||||
itemLevel: item.fields?.LevelItem,
|
||||
name: item.fields?.Name || item.name || keyword,
|
||||
@ -107,23 +127,49 @@ export class QqbotFf14ClientService {
|
||||
}
|
||||
|
||||
async getPrice(params: {
|
||||
dataCenter?: string;
|
||||
hq?: boolean;
|
||||
item?: string;
|
||||
itemId?: number | string;
|
||||
language?: string;
|
||||
region?: string;
|
||||
world?: string;
|
||||
}): Promise<QqbotFf14PriceResult> {
|
||||
const world = this.normalizeWorld(params.world);
|
||||
const marketTarget = this.resolveMarketTarget(params);
|
||||
const item = await this.resolveItem(params);
|
||||
const url = new URL(`${this.universalisBaseUrl}/${world}/${item.itemId}`);
|
||||
if (item.isUntradable) {
|
||||
return {
|
||||
hq: params.hq,
|
||||
item,
|
||||
listings: [],
|
||||
replyText: `FF14 查价:${item.name}\n该物品不可交易,暂无市场价格。`,
|
||||
world: marketTarget.label,
|
||||
};
|
||||
}
|
||||
|
||||
const url = new URL(
|
||||
`${this.universalisBaseUrl}/${encodeURIComponent(
|
||||
marketTarget.target,
|
||||
)}/${item.itemId}`,
|
||||
);
|
||||
url.searchParams.set('entries', '5');
|
||||
url.searchParams.set('listings', '5');
|
||||
if (params.hq !== undefined) url.searchParams.set('hq', `${params.hq}`);
|
||||
|
||||
const data = await this.requestJson<UniversalisMarketResponse>(url, 'GET');
|
||||
const minPrice = this.pickPrice(data, params.hq, 'min');
|
||||
const averagePrice = this.pickPrice(data, params.hq, 'average');
|
||||
const data = await this.requestJson<UniversalisMarketResponse>(
|
||||
url,
|
||||
'GET',
|
||||
'Universalis 市场查询',
|
||||
);
|
||||
const listings = (data.listings || []).slice(0, 5);
|
||||
const minPrice = this.normalizeMarketPrice(
|
||||
this.pickPrice(data, params.hq, 'min'),
|
||||
listings,
|
||||
);
|
||||
const averagePrice = this.normalizeMarketPrice(
|
||||
this.pickPrice(data, params.hq, 'average'),
|
||||
listings,
|
||||
);
|
||||
const updatedAt = data.lastUploadTime
|
||||
? new Date(data.lastUploadTime).toISOString()
|
||||
: undefined;
|
||||
@ -141,26 +187,33 @@ export class QqbotFf14ClientService {
|
||||
listings,
|
||||
minPrice,
|
||||
updatedAt,
|
||||
world,
|
||||
world: marketTarget.label,
|
||||
}),
|
||||
updatedAt,
|
||||
world,
|
||||
world: marketTarget.label,
|
||||
};
|
||||
}
|
||||
|
||||
private async getItemById(
|
||||
itemId: number,
|
||||
language = 'zh',
|
||||
displayName?: string,
|
||||
): Promise<QqbotFf14ResolvedItem> {
|
||||
const url = new URL(`${this.xivapiBaseUrl}/sheet/Item/${itemId}`);
|
||||
url.searchParams.set('language', language);
|
||||
const data = await this.requestJson<Record<string, any>>(url, 'GET');
|
||||
url.searchParams.set('fields', 'Name,Icon,LevelItem,IsUntradable');
|
||||
url.searchParams.set('language', this.normalizeXivapiLanguage(language));
|
||||
const data = await this.requestJson<Record<string, any>>(
|
||||
url,
|
||||
'GET',
|
||||
'XIVAPI 物品解析',
|
||||
);
|
||||
const fields = data.fields || data;
|
||||
return {
|
||||
icon: fields.Icon,
|
||||
isUntradable: fields.IsUntradable,
|
||||
itemId,
|
||||
itemLevel: fields.LevelItem,
|
||||
name: fields.Name || `${itemId}`,
|
||||
name: displayName || fields.Name || `${itemId}`,
|
||||
};
|
||||
}
|
||||
|
||||
@ -182,7 +235,7 @@ export class QqbotFf14ClientService {
|
||||
|
||||
return [
|
||||
`FF14 查价:${result.item.name}${quality}`,
|
||||
`服务器:${result.world}`,
|
||||
`查询范围:${result.world}`,
|
||||
`最低价:${minPrice}`,
|
||||
`均价:${averagePrice}`,
|
||||
`近期挂单:`,
|
||||
@ -209,13 +262,47 @@ export class QqbotFf14ClientService {
|
||||
);
|
||||
}
|
||||
|
||||
private normalizeMarketPrice(
|
||||
price: number | undefined,
|
||||
listings: UniversalisListing[],
|
||||
) {
|
||||
if (!listings.length && (!price || price <= 0)) return undefined;
|
||||
return price;
|
||||
}
|
||||
|
||||
private normalizeWorld(world?: string) {
|
||||
const raw =
|
||||
`${world || this.configService.get<string>('FF14_DEFAULT_WORLD') || '中国'}`.trim();
|
||||
return encodeURIComponent(raw);
|
||||
return raw;
|
||||
}
|
||||
|
||||
private requestJson<T>(url: URL, method: HttpMethod) {
|
||||
private resolveMarketTarget(params: {
|
||||
dataCenter?: string;
|
||||
region?: string;
|
||||
world?: string;
|
||||
}) {
|
||||
return resolveQqbotFf14MarketTarget({
|
||||
dataCenter: params.dataCenter,
|
||||
fallback: this.normalizeWorld(params.world),
|
||||
region: params.region,
|
||||
world: params.world,
|
||||
});
|
||||
}
|
||||
|
||||
private normalizeXivapiLanguage(language?: string) {
|
||||
const value = `${language || 'en'}`.trim().toLowerCase();
|
||||
return ['en', 'ja', 'de', 'fr'].includes(value) ? value : 'en';
|
||||
}
|
||||
|
||||
private resolveCnItemAlias(keyword: string) {
|
||||
return this.cnItemAliases.get(keyword.trim());
|
||||
}
|
||||
|
||||
private escapeXivapiValue(value: string) {
|
||||
return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
||||
}
|
||||
|
||||
private requestJson<T>(url: URL, method: HttpMethod, context: string) {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
const client = url.protocol === 'http:' ? http : https;
|
||||
const request = client.request(
|
||||
@ -236,7 +323,9 @@ export class QqbotFf14ClientService {
|
||||
});
|
||||
response.on('end', () => {
|
||||
if ((response.statusCode || 500) >= 400) {
|
||||
reject(new Error(`FF14 接口请求失败:${response.statusCode}`));
|
||||
reject(
|
||||
new Error(`${context}失败:${response.statusCode}`),
|
||||
);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
||||
@ -60,13 +60,15 @@ export class QqbotFf14MarketPluginService {
|
||||
description: '查询指定服务器的 FF14 市场最低价、均价与近期挂单。',
|
||||
inputSchema: {
|
||||
properties: {
|
||||
dataCenter: { description: '大区名,如陆行鸟', type: 'string' },
|
||||
hq: { description: '是否只查 HQ', type: 'boolean' },
|
||||
item: { description: '物品名称或 ID', type: 'string' },
|
||||
itemId: { description: '物品 ID', type: 'number' },
|
||||
language: { default: 'zh', type: 'string' },
|
||||
world: { description: '服务器名或大区名', type: 'string' },
|
||||
region: { description: '地区名,如中国', type: 'string' },
|
||||
world: { description: '小区/服务器名,如红玉海', type: 'string' },
|
||||
},
|
||||
required: ['item', 'world'],
|
||||
required: ['item'],
|
||||
type: 'object',
|
||||
},
|
||||
key: 'ff14.market.price',
|
||||
|
||||
205
src/qqbot/plugins/ff14Market/qqbot-ff14-worlds.ts
Normal file
205
src/qqbot/plugins/ff14Market/qqbot-ff14-worlds.ts
Normal file
@ -0,0 +1,205 @@
|
||||
export type QqbotFf14DataCenter = {
|
||||
name: string;
|
||||
region: string;
|
||||
worlds: string[];
|
||||
};
|
||||
|
||||
export type QqbotFf14MarketTarget = {
|
||||
dataCenter?: string;
|
||||
label: string;
|
||||
region?: string;
|
||||
target: string;
|
||||
world?: string;
|
||||
};
|
||||
|
||||
export const QQBOT_FF14_DEFAULT_REGION = '中国';
|
||||
|
||||
export const QQBOT_FF14_CHINA_DATA_CENTERS: QqbotFf14DataCenter[] = [
|
||||
{
|
||||
name: '陆行鸟',
|
||||
region: QQBOT_FF14_DEFAULT_REGION,
|
||||
worlds: [
|
||||
'红玉海',
|
||||
'神意之地',
|
||||
'拉诺西亚',
|
||||
'幻影群岛',
|
||||
'萌芽池',
|
||||
'宇宙和音',
|
||||
'沃仙曦染',
|
||||
'晨曦王座',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: '莫古力',
|
||||
region: QQBOT_FF14_DEFAULT_REGION,
|
||||
worlds: [
|
||||
'白银乡',
|
||||
'白金幻象',
|
||||
'神拳痕',
|
||||
'潮风亭',
|
||||
'旅人栈桥',
|
||||
'拂晓之间',
|
||||
'龙巢神殿',
|
||||
'梦羽宝境',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: '猫小胖',
|
||||
region: QQBOT_FF14_DEFAULT_REGION,
|
||||
worlds: [
|
||||
'紫水栈桥',
|
||||
'延夏',
|
||||
'静语庄园',
|
||||
'摩杜纳',
|
||||
'海猫茶屋',
|
||||
'柔风海湾',
|
||||
'琥珀原',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: '豆豆柴',
|
||||
region: QQBOT_FF14_DEFAULT_REGION,
|
||||
worlds: ['水晶塔', '银泪湖', '太阳海岸', '伊修加德', '红茶川'],
|
||||
},
|
||||
];
|
||||
|
||||
export function isQqbotFf14DataCenterName(value?: string) {
|
||||
const name = normalizeQqbotFf14WorldValue(value);
|
||||
return QQBOT_FF14_CHINA_DATA_CENTERS.some((item) => item.name === name);
|
||||
}
|
||||
|
||||
export function isQqbotFf14RegionName(value?: string) {
|
||||
return normalizeQqbotFf14WorldValue(value) === QQBOT_FF14_DEFAULT_REGION;
|
||||
}
|
||||
|
||||
export function isQqbotFf14WorldName(value?: string) {
|
||||
const name = normalizeQqbotFf14WorldValue(value);
|
||||
return QQBOT_FF14_CHINA_DATA_CENTERS.some((item) =>
|
||||
item.worlds.includes(name),
|
||||
);
|
||||
}
|
||||
|
||||
export function isQqbotFf14LocationName(value?: string) {
|
||||
const name = normalizeQqbotFf14WorldValue(value);
|
||||
const path = splitQqbotFf14WorldPath(name);
|
||||
return (
|
||||
isQqbotFf14RegionName(name) ||
|
||||
isQqbotFf14DataCenterName(name) ||
|
||||
isQqbotFf14WorldName(name) ||
|
||||
(!!path.dataCenter && !!path.world)
|
||||
);
|
||||
}
|
||||
|
||||
export function splitQqbotFf14WorldPath(value?: string) {
|
||||
const raw = normalizeQqbotFf14WorldValue(value);
|
||||
if (!raw) return {};
|
||||
|
||||
const parts = raw
|
||||
.split(/\s*(?:->|=>|>|\/|\\|:|:)\s*/)
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean);
|
||||
if (parts.length < 2) return {};
|
||||
|
||||
if (parts.length === 2) {
|
||||
return {
|
||||
dataCenter: parts[0],
|
||||
world: parts[1],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
dataCenter: parts[parts.length - 2],
|
||||
region: parts[0],
|
||||
world: parts[parts.length - 1],
|
||||
};
|
||||
}
|
||||
|
||||
export function findQqbotFf14DataCenterByWorld(world?: string) {
|
||||
const worldName = normalizeQqbotFf14WorldValue(world);
|
||||
return QQBOT_FF14_CHINA_DATA_CENTERS.find((item) =>
|
||||
item.worlds.includes(worldName),
|
||||
);
|
||||
}
|
||||
|
||||
export function resolveQqbotFf14MarketTarget(params: {
|
||||
dataCenter?: string;
|
||||
fallback?: string;
|
||||
region?: string;
|
||||
world?: string;
|
||||
}): QqbotFf14MarketTarget {
|
||||
const fallback = normalizeQqbotFf14WorldValue(params.fallback);
|
||||
const path = splitQqbotFf14WorldPath(params.world);
|
||||
const region = normalizeQqbotFf14WorldValue(params.region || path.region);
|
||||
const dataCenter = normalizeQqbotFf14WorldValue(
|
||||
params.dataCenter || path.dataCenter,
|
||||
);
|
||||
const rawWorld = normalizeQqbotFf14WorldValue(path.world || params.world);
|
||||
const world =
|
||||
dataCenter && rawWorld === QQBOT_FF14_DEFAULT_REGION ? '' : rawWorld;
|
||||
const raw =
|
||||
world || dataCenter || region || fallback || QQBOT_FF14_DEFAULT_REGION;
|
||||
|
||||
if (region && dataCenter && (!world || world === region)) {
|
||||
return {
|
||||
dataCenter,
|
||||
label: `${region} / ${dataCenter}`,
|
||||
region,
|
||||
target: dataCenter,
|
||||
};
|
||||
}
|
||||
|
||||
if (raw === QQBOT_FF14_DEFAULT_REGION) {
|
||||
return {
|
||||
label: QQBOT_FF14_DEFAULT_REGION,
|
||||
region: QQBOT_FF14_DEFAULT_REGION,
|
||||
target: QQBOT_FF14_DEFAULT_REGION,
|
||||
};
|
||||
}
|
||||
|
||||
if (dataCenter && world && world !== dataCenter) {
|
||||
const matchedDataCenter = QQBOT_FF14_CHINA_DATA_CENTERS.find(
|
||||
(item) => item.name === dataCenter,
|
||||
);
|
||||
if (matchedDataCenter && !matchedDataCenter.worlds.includes(world)) {
|
||||
throw new Error(`服务器 ${world} 不属于大区 ${dataCenter}`);
|
||||
}
|
||||
return {
|
||||
dataCenter,
|
||||
label: region
|
||||
? `${region} / ${dataCenter} / ${world}`
|
||||
: `${dataCenter} / ${world}`,
|
||||
region,
|
||||
target: world,
|
||||
world,
|
||||
};
|
||||
}
|
||||
|
||||
const matchedWorldDataCenter = findQqbotFf14DataCenterByWorld(raw);
|
||||
if (matchedWorldDataCenter) {
|
||||
return {
|
||||
dataCenter: matchedWorldDataCenter.name,
|
||||
label: `${matchedWorldDataCenter.region} / ${matchedWorldDataCenter.name} / ${raw}`,
|
||||
region: matchedWorldDataCenter.region,
|
||||
target: raw,
|
||||
world: raw,
|
||||
};
|
||||
}
|
||||
|
||||
if (isQqbotFf14DataCenterName(raw)) {
|
||||
return {
|
||||
dataCenter: raw,
|
||||
label: `${QQBOT_FF14_DEFAULT_REGION} / ${raw}`,
|
||||
region: QQBOT_FF14_DEFAULT_REGION,
|
||||
target: raw,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
label: raw,
|
||||
target: raw,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeQqbotFf14WorldValue(value?: string) {
|
||||
return `${value || ''}`.trim();
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user