Skip to content

Commit

Permalink
bot add random map selector
Browse files Browse the repository at this point in the history
  • Loading branch information
TsFreddie committed Jan 18, 2025
1 parent 9550f02 commit 733246c
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 8 deletions.
170 changes: 162 additions & 8 deletions src/lib/server/bots/handlers/maps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,50 @@ import { encodeAsciiURIComponent } from '$lib/link';
import { maps, type MapList } from '$lib/server/fetches/maps';
import type { Handler } from '../protocol/types';

export const handleMaps: Handler = async ({ reply, fetch, args }) => {
const mapName = args.trim();
const MAPTYPE_KEYWORDS: Record<string, string> = {
rnd: 'random',
: 'random',
ran: 'random',
roll: 'random',

nov: 'novice',
mod: 'moderate',
bru: 'brutal',
ins: 'insane',
ddm: 'ddmax',
ddx: 'ddmax',
old: 'oldschool',
dum: 'dummy',
solo: 'solo',
race: 'race',
fun: 'fun',
: 'novice',
: 'moderate',
: 'brutal',
: 'insane',
: 'ddmax',
: 'oldschool',
: 'dummy',
: 'solo',
: 'race',
: 'fun'
};

const MAPDIFF_KEYWORDS: Record<string, number> = {
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
: 1,
: 2,
: 3,
: 4,
: 5
};

export const handleMaps: Handler = async ({ reply, user, args }) => {
let mapName = args.trim();
if (!mapName) {
return await reply.textLink('查图请提供 <地图名>。或者使用 DDNet 工具箱', {
label: '🔗 排名查询工具',
Expand All @@ -14,10 +56,108 @@ export const handleMaps: Handler = async ({ reply, fetch, args }) => {
});
}

let type = '';
for (const keyword of Object.keys(MAPTYPE_KEYWORDS)) {
if (mapName.toLowerCase().startsWith(keyword)) {
type = MAPTYPE_KEYWORDS[keyword];
break;
}
}

if (type) {
mapName = args.split(' ').slice(1).join(' ');
}

// attempt to make filters for map searches
let diff = 0;

for (const keyword of Object.keys(MAPDIFF_KEYWORDS)) {
if (args.toLowerCase().includes(keyword)) {
diff = MAPDIFF_KEYWORDS[keyword];
break;
}
}

const mapData = await maps.fetch();

if (!mapName) {
const finishedList = new Set<string>();
let usingName = false;

if (user && user.data?.name) {
const playerName = user.data.name;
const response = await fetch(
`https://ddnet.org/players/?json=${encodeURIComponent(playerName)}`
);
if (response.ok) {
try {
const maps = (await response.json()) as string[];
for (const map of maps) {
finishedList.add(map);
}
usingName = true;
} catch {}
}
}

const filteredMaps = mapData.filter((map: any) => {
return (
(type == 'random' || map.type.toLowerCase().startsWith(type)) &&
(!diff || map.difficulty == diff)
);
});

const unfinishedMaps = filteredMaps.filter((map: any) => !finishedList.has(map.name));
const targetGroup = unfinishedMaps.length > 0 ? unfinishedMaps : filteredMaps;

const randomMap = targetGroup[Math.floor(Math.random() * targetGroup.length)];
const descriptor = `${type != 'random' ? mapType(type) : ''}${diff ? ` ${numberToStars(diff)}` : ''}`;
const finishingDesc = usingName
? unfinishedMaps.length > 0
? descriptor
? `随机找了一张 ${user?.data.name} 未完成的 ${descriptor} 图`
: `随机找了一张 ${user?.data.name} 未完成的图`
: descriptor
? `没有找到 ${user?.data.name} 未完成的 ${descriptor} 图,随机从所有图中选了一张`
: `没有找到 ${user?.data.name} 未完成的图,随机从所有图中选了一张`
: descriptor
? `随机找了一张 ${descriptor} 图`
: `随机找了一张图`;

if (randomMap) {
const lines = [
finishingDesc,
`${randomMap.name} (by ${randomMap.mapper})`,
`[${mapType(randomMap.type)} ${numberToStars(randomMap.difficulty)}] ${randomMap.points}pts`
];

if (descriptor) {
return await reply.imageTextLink(lines.join('\n'), randomMap.thumbnail, {
label: `🔗 查看所有 ${descriptor} 图`,
prefix: `${descriptor} 图列表: `,
url:
type == 'random'
? `https://teeworlds.cn/goto#ms${diff ? `diff=${diff}` : ''}`
: `https://teeworlds.cn/goto#mstype=${type}${diff ? `&diff=${diff}` : ''}`
});
} else {
return await reply.imageTextLink(lines.join('\n'), randomMap.thumbnail, {
label: `🔗 查看所有图`,
prefix: `地图列表: `,
url: `https://teeworlds.cn/goto#m`
});
}
}

return await reply.text(`不存在 ${descriptor} 图`);
}

const filteredMaps = mapData.filter((map: (typeof mapData)[0]) => {
return checkMapName(map.name, mapName);
return (
(!type || map.type.toLowerCase().startsWith(type)) &&
(!diff || map.difficulty == diff) &&
checkMapName(map.name, mapName)
);
});

if (filteredMaps.length == 0) {
Expand All @@ -37,13 +177,27 @@ export const handleMaps: Handler = async ({ reply, fetch, args }) => {
// sort by lowest point (easiest, probably finished by most players)
// then oldest
targetMap = filteredMaps.sort((a, b) => {
if (a.points == b.points) {
return new Date(a.release).getTime() - new Date(b.release).getTime();
const aStartsWith = a.name.toLowerCase().startsWith(mapName);
const bStartsWith = b.name.toLowerCase().startsWith(mapName);
const aCaseStartsWith = a.name.startsWith(mapName);
const bCaseStartsWith = b.name.startsWith(mapName);

if (aStartsWith == bStartsWith) {
if (aCaseStartsWith != bCaseStartsWith) {
return aCaseStartsWith ? -1 : 1;
}
if (a.points == b.points) {
return new Date(a.release).getTime() - new Date(b.release).getTime();
}
return a.points - b.points;
}

if (aStartsWith) {
return -1;
}
return a.points - b.points;
})[0];

if (!targetMap) return await reply.text(`未找到名为 ${mapName} 的地图`);
return 1;
})[0];
}

const lines = [
Expand Down
1 change: 1 addition & 0 deletions src/routes/goto/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
if (hash == 'm') target = `/ddnet/maps`;
else if (hash == 'p') target = `/ddnet/players`;
else if (hash == 's') target = `/ddnet/servers`;
else if (hash.startsWith('ms')) target = `/ddnet/maps#${hash.slice(2)}`;
else if (hash.startsWith('m')) target = `/ddnet/maps/${hash.slice(1)}`;
else if (hash.startsWith('p')) target = `/ddnet/players/${hash.slice(1)}`;
else if (hash.startsWith('s')) target = `/ddnet/servers#${hash.slice(1)}`;
Expand Down

0 comments on commit 733246c

Please sign in to comment.