refactor(discord-bot): improve project structure
This commit is contained in:
34
apps/discord-bot/src/services/account.ts
Normal file
34
apps/discord-bot/src/services/account.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { readFile, writeFile, access } from "node:fs/promises";
|
||||
import { constants } from "node:fs";
|
||||
|
||||
const ACCOUNTS_FILE = "./.cache/accounts.json";
|
||||
|
||||
export const initAccounts = async (): Promise<void> => {
|
||||
try {
|
||||
await access(ACCOUNTS_FILE, constants.F_OK);
|
||||
} catch {
|
||||
await writeFile(ACCOUNTS_FILE, "{}");
|
||||
}
|
||||
};
|
||||
|
||||
export const getAccountBalance = async (playerId: string): Promise<number> => {
|
||||
const content = await readFile(ACCOUNTS_FILE, "utf-8");
|
||||
const accounts: Record<string, number> = JSON.parse(content);
|
||||
if (accounts[playerId]) return accounts[playerId];
|
||||
|
||||
accounts[playerId] = 0;
|
||||
await writeFile(ACCOUNTS_FILE, JSON.stringify(accounts));
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
export const setAccountBalance = async (
|
||||
playerId: string,
|
||||
balance: number,
|
||||
): Promise<void> => {
|
||||
const content = await readFile(ACCOUNTS_FILE, "utf-8");
|
||||
const accounts: Record<string, number> = JSON.parse(content);
|
||||
accounts[playerId] = balance;
|
||||
|
||||
await writeFile(ACCOUNTS_FILE, JSON.stringify(accounts));
|
||||
};
|
||||
74
apps/discord-bot/src/services/tracking.ts
Normal file
74
apps/discord-bot/src/services/tracking.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { getPlayer } from "~/services/wov";
|
||||
import { readFile, writeFile, access } from "node:fs/promises";
|
||||
import { constants } from "node:fs";
|
||||
import type { TrackedPlayers } from "~/types";
|
||||
|
||||
const TRACKED_PLAYER_FILE = "./.cache/tracked.json";
|
||||
|
||||
export async function initTracking(): Promise<void> {
|
||||
try {
|
||||
await access(TRACKED_PLAYER_FILE, constants.F_OK);
|
||||
} catch {
|
||||
await writeFile(TRACKED_PLAYER_FILE, "{}");
|
||||
}
|
||||
}
|
||||
|
||||
export async function listTrackedPlayers(): Promise<string[]> {
|
||||
const content = await readFile(TRACKED_PLAYER_FILE, "utf-8");
|
||||
const trackedPlayers: TrackedPlayers = JSON.parse(content);
|
||||
|
||||
return Object.keys(trackedPlayers);
|
||||
}
|
||||
|
||||
export async function untrackWovPlayer(
|
||||
playerId: string,
|
||||
): Promise<{ event: "notTracked" } | { event: "trackerRemoved" }> {
|
||||
const content = await readFile(TRACKED_PLAYER_FILE, "utf-8");
|
||||
const trackedPlayers: TrackedPlayers = JSON.parse(content);
|
||||
|
||||
if (!trackedPlayers[playerId]) return { event: "notTracked" };
|
||||
|
||||
delete trackedPlayers[playerId];
|
||||
await writeFile(TRACKED_PLAYER_FILE, JSON.stringify(trackedPlayers));
|
||||
|
||||
return { event: "trackerRemoved" };
|
||||
}
|
||||
|
||||
export async function trackWovPlayer(playerId: string): Promise<
|
||||
| { event: "notFound" }
|
||||
| {
|
||||
event: "registered";
|
||||
}
|
||||
| { event: "changed"; oldUsernames: string[]; newUsername: string }
|
||||
| { event: "none" }
|
||||
> {
|
||||
const content = await readFile(TRACKED_PLAYER_FILE, "utf-8");
|
||||
const trackedPlayers: TrackedPlayers = JSON.parse(content);
|
||||
|
||||
const player = await getPlayer(playerId);
|
||||
if (!player) return { event: "notFound" };
|
||||
|
||||
const currentUsernames = trackedPlayers[playerId];
|
||||
if (currentUsernames) {
|
||||
const oldUsernames = [...currentUsernames];
|
||||
if (!currentUsernames.includes(player.username)) {
|
||||
currentUsernames.push(player.username);
|
||||
|
||||
await writeFile(TRACKED_PLAYER_FILE, JSON.stringify(trackedPlayers));
|
||||
|
||||
return {
|
||||
event: "changed",
|
||||
oldUsernames,
|
||||
newUsername: player.username,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
event: "none",
|
||||
};
|
||||
}
|
||||
} else {
|
||||
trackedPlayers[playerId] = [player.username];
|
||||
await writeFile(TRACKED_PLAYER_FILE, JSON.stringify(trackedPlayers));
|
||||
return { event: "registered" };
|
||||
}
|
||||
}
|
||||
138
apps/discord-bot/src/services/wov.ts
Normal file
138
apps/discord-bot/src/services/wov.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import { env } from "~/env";
|
||||
import { mkdir, readFile, writeFile, access } from "node:fs/promises";
|
||||
import { constants } from "node:fs";
|
||||
import type { QuestResult } from "~/types";
|
||||
|
||||
export const getLatestQuest = async (): Promise<QuestResult> => {
|
||||
const response = await fetch(
|
||||
`https://api.wolvesville.com/clans/${env.WOV_CLAN_ID}/quests/history`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: { Authorization: `Bot ${env.WOV_API_KEY}` },
|
||||
},
|
||||
);
|
||||
const history = (await response.json()) as Array<QuestResult>;
|
||||
return history[0];
|
||||
};
|
||||
|
||||
export const checkForNewQuest = async (): Promise<QuestResult | null> => {
|
||||
const lastQuest = await getLatestQuest();
|
||||
|
||||
const lastId = lastQuest.quest.id;
|
||||
const cacheFilePath = ".cache/.quest_cache";
|
||||
await mkdir(".cache", { recursive: true });
|
||||
|
||||
try {
|
||||
await access(cacheFilePath, constants.F_OK);
|
||||
const cachedQuestId = await readFile(cacheFilePath, "utf-8");
|
||||
if (cachedQuestId === lastId || cachedQuestId === "IGNORE") {
|
||||
return null;
|
||||
}
|
||||
} catch {
|
||||
// File doesn't exist, continue
|
||||
}
|
||||
|
||||
await writeFile(cacheFilePath, lastId);
|
||||
return lastQuest;
|
||||
};
|
||||
|
||||
export const getClanMembers = async (): Promise<
|
||||
Array<{ playerId: string; username: string }>
|
||||
> => {
|
||||
const cacheFilePath = ".clan_members_cache";
|
||||
await mkdir(".cache", { recursive: true });
|
||||
|
||||
let cached: {
|
||||
timestamp: number;
|
||||
data: Array<{ playerId: string; username: string }>;
|
||||
} | null = null;
|
||||
|
||||
try {
|
||||
await access(cacheFilePath, constants.F_OK);
|
||||
const content = await readFile(cacheFilePath, "utf-8");
|
||||
cached = JSON.parse(content);
|
||||
if (cached && Date.now() - cached.timestamp < 60 * 60 * 1000) {
|
||||
return cached.data;
|
||||
}
|
||||
} catch {
|
||||
// File doesn't exist or is invalid, continue
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
`https://api.wolvesville.com/clans/${env.WOV_CLAN_ID}/members`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: { Authorization: `Bot ${env.WOV_API_KEY}` },
|
||||
},
|
||||
);
|
||||
const data = (await response.json()) as Array<{
|
||||
playerId: string;
|
||||
username: string;
|
||||
}>;
|
||||
await writeFile(
|
||||
cacheFilePath,
|
||||
JSON.stringify({ timestamp: Date.now(), data }),
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const searchPlayer = async (username: string) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://api.wolvesville.com//players/search?username=${username}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: { Authorization: `Bot ${env.WOV_API_KEY}` },
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status === 404) return null;
|
||||
|
||||
const data = (await response.json()) as {
|
||||
id: string;
|
||||
clanId: string | null;
|
||||
};
|
||||
|
||||
return data;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const getClanInfos = async (clanId: string) => {
|
||||
const response = await fetch(
|
||||
`https://api.wolvesville.com/clans/${clanId}/info`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: { Authorization: `Bot ${env.WOV_API_KEY}` },
|
||||
},
|
||||
);
|
||||
const data = (await response.json()) as {
|
||||
name: string;
|
||||
tag: string;
|
||||
};
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
export async function getPlayer(playerId: string) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://api.wolvesville.com/players/${playerId}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: { Authorization: `Bot ${env.WOV_API_KEY}` },
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status === 404) return null;
|
||||
|
||||
const data = (await response.json()) as {
|
||||
username: string;
|
||||
};
|
||||
|
||||
return data;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user