feat: implement user tracking
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
WOV_CLAN_ID=
|
WOV_CLAN_ID=
|
||||||
WOV_API_KEY=
|
WOV_API_KEY=
|
||||||
WOV_FETCH_INTERVAL="14400000" # 4 hours
|
WOV_FETCH_INTERVAL="14400000" # 4 hours
|
||||||
|
WOV_TRACKING_INTERVAL="432000000" # 12 hours
|
||||||
|
|
||||||
QUEST_REWARDS=
|
QUEST_REWARDS=
|
||||||
QUEST_EXCLUDE=
|
QUEST_EXCLUDE=
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ const schema = z.object({
|
|||||||
DISCORD_REWARDS_CHANNEL: z.string(),
|
DISCORD_REWARDS_CHANNEL: z.string(),
|
||||||
DISCORD_ADMIN_MENTION: z.string(),
|
DISCORD_ADMIN_MENTION: z.string(),
|
||||||
DISCORD_ADMIN_CHANNEL: z.string(),
|
DISCORD_ADMIN_CHANNEL: z.string(),
|
||||||
|
DISCORD_TRACKING_CHANNEL: z.string(),
|
||||||
WOV_API_KEY: z.string(),
|
WOV_API_KEY: z.string(),
|
||||||
WOV_CLAN_ID: z.string(),
|
WOV_CLAN_ID: z.string(),
|
||||||
WOV_FETCH_INTERVAL: z.coerce.number(),
|
WOV_FETCH_INTERVAL: z.coerce.number(),
|
||||||
|
WOV_TRACKING_INTERVAL: z.coerce.number(),
|
||||||
QUEST_REWARDS: z
|
QUEST_REWARDS: z
|
||||||
.string()
|
.string()
|
||||||
.transform((x) => x.split(",").map((x) => x.trim()))
|
.transform((x) => x.split(",").map((x) => x.trim()))
|
||||||
|
|||||||
117
src/index.ts
117
src/index.ts
@@ -1,6 +1,7 @@
|
|||||||
import { getAccountBalance, initAccounts, setAccountBalance } from "./account";
|
import { getAccountBalance, initAccounts, setAccountBalance } from "./account";
|
||||||
import { makeResultEmbed } from "./discord";
|
import { makeResultEmbed } from "./discord";
|
||||||
import { env } from "./env";
|
import { env } from "./env";
|
||||||
|
import { initTracking, listTrackedPlayers, trackWovPlayer } from "./tracking";
|
||||||
import {
|
import {
|
||||||
checkForNewQuest,
|
checkForNewQuest,
|
||||||
getClanInfos,
|
getClanInfos,
|
||||||
@@ -13,6 +14,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
ChannelType,
|
ChannelType,
|
||||||
Client,
|
Client,
|
||||||
|
EmbedBuilder,
|
||||||
GatewayIntentBits,
|
GatewayIntentBits,
|
||||||
Message,
|
Message,
|
||||||
Partials,
|
Partials,
|
||||||
@@ -163,6 +165,35 @@ const fn = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const trackingCron = async () => {
|
||||||
|
const trackedPlayers = await listTrackedPlayers();
|
||||||
|
for (const playerId of trackedPlayers) {
|
||||||
|
const res = await trackWovPlayer(playerId);
|
||||||
|
if (res.event !== "changed") return;
|
||||||
|
|
||||||
|
const chan = client.channels.cache.get(env.DISCORD_TRACKING_CHANNEL);
|
||||||
|
if (!chan?.isSendable()) throw "Invalid tracking channel";
|
||||||
|
|
||||||
|
const lastUsername = res.oldUsernames[res.oldUsernames.length - 1];
|
||||||
|
|
||||||
|
await chan.send({
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
description: `### [UPDATE] \`${lastUsername}\` -> \`${res.newUsername}\` [\`${playerId}\`]`,
|
||||||
|
fields: [
|
||||||
|
{ name: "Nouveau pseudo", value: `\`${res.newUsername}\`` },
|
||||||
|
{
|
||||||
|
name: "Anciens pseudos",
|
||||||
|
value: res.oldUsernames.map((x) => `- \`${x}\``).join("\n"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
color: 0x89cff0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
client.on("ready", async (client) => {
|
client.on("ready", async (client) => {
|
||||||
console.log(`Logged in as ${client.user.username}`);
|
console.log(`Logged in as ${client.user.username}`);
|
||||||
|
|
||||||
@@ -179,9 +210,13 @@ client.on("ready", async (client) => {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await initAccounts();
|
await initAccounts();
|
||||||
|
await initTracking();
|
||||||
|
|
||||||
await fn();
|
await fn();
|
||||||
setInterval(fn, env.WOV_FETCH_INTERVAL);
|
setInterval(fn, env.WOV_FETCH_INTERVAL);
|
||||||
|
|
||||||
|
await trackingCron();
|
||||||
|
setInterval(trackingCron, env.WOV_TRACKING_INTERVAL);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -197,6 +232,88 @@ client.on("messageCreate", async (message) => {
|
|||||||
.split(" ");
|
.split(" ");
|
||||||
if (command === "ping") {
|
if (command === "ping") {
|
||||||
await message.reply("pong");
|
await message.reply("pong");
|
||||||
|
} else if (command === "track") {
|
||||||
|
let playerName = args[0];
|
||||||
|
if (!playerName) {
|
||||||
|
await message.reply({
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
description: `### ❌ Erreur\n\n\n\nUsage:\`@LBF track NOM_JOUEUR\`, exemple: \`@LBF track Yuno\`.\n**Attention les majuscules sont importantes**`,
|
||||||
|
color: 15335424,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const player = await searchPlayer(playerName);
|
||||||
|
if (!player) {
|
||||||
|
await message.reply({
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
description: `### ❌ Erreur\n\n\n\nCette personne n'existe pas.\n**Attention les majuscules sont importantes**`,
|
||||||
|
color: 15335424,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0x89cff0
|
||||||
|
|
||||||
|
const res = await trackWovPlayer(player.id);
|
||||||
|
switch (res.event) {
|
||||||
|
case "notFound": {
|
||||||
|
await message.reply({
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
description: `### ❌ Erreur\n\n\n\nCette personne n'existe pas.\n**Attention les majuscules sont importantes**`,
|
||||||
|
color: 15335424,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "registered": {
|
||||||
|
await message.reply({
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
description: `Tracker enregistré pour \`${playerName}\` [\`${player.id}\`]`,
|
||||||
|
color: 0x89cff0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const chan = client.channels.cache.get(env.DISCORD_TRACKING_CHANNEL);
|
||||||
|
if (!chan?.isSendable()) throw "Invalid tracking channel";
|
||||||
|
|
||||||
|
await chan.send({
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
description: `### [NEW] \`${playerName}\` [\`${player.id}\`]`,
|
||||||
|
color: 0x89cff0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case "none": {
|
||||||
|
await message.reply({
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
description: `Tracker déjà enregistré pour \`${playerName}\` [\`${player.id}\`]`,
|
||||||
|
color: 0x89cff0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case "changed": {
|
||||||
|
// ignored
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (command === "icone") {
|
} else if (command === "icone") {
|
||||||
let playerName = args[0];
|
let playerName = args[0];
|
||||||
if (!playerName) {
|
if (!playerName) {
|
||||||
|
|||||||
57
src/tracking.ts
Normal file
57
src/tracking.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { getPlayer } from "./wov";
|
||||||
|
|
||||||
|
const TRACKED_PLAYER_FILE = "./.cache/tracked.json";
|
||||||
|
|
||||||
|
type TrackedPlayers = Record<string, string[]>;
|
||||||
|
|
||||||
|
export async function initTracking(): Promise<void> {
|
||||||
|
if (!(await Bun.file(TRACKED_PLAYER_FILE).exists())) {
|
||||||
|
Bun.file(TRACKED_PLAYER_FILE).write("{}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listTrackedPlayers(): Promise<string[]> {
|
||||||
|
const trackedPlayers: TrackedPlayers =
|
||||||
|
await Bun.file(TRACKED_PLAYER_FILE).json();
|
||||||
|
|
||||||
|
return Object.keys(trackedPlayers);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function trackWovPlayer(playerId: string): Promise<
|
||||||
|
| { event: "notFound" }
|
||||||
|
| {
|
||||||
|
event: "registered";
|
||||||
|
}
|
||||||
|
| { event: "changed"; oldUsernames: string[]; newUsername: string }
|
||||||
|
| { event: "none" }
|
||||||
|
> {
|
||||||
|
const trackedPlayers: TrackedPlayers =
|
||||||
|
await Bun.file(TRACKED_PLAYER_FILE).json();
|
||||||
|
|
||||||
|
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 Bun.file(TRACKED_PLAYER_FILE).write(JSON.stringify(trackedPlayers));
|
||||||
|
|
||||||
|
return {
|
||||||
|
event: "changed",
|
||||||
|
oldUsernames,
|
||||||
|
newUsername: player.username,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
event: "none",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
trackedPlayers[playerId] = [player.username];
|
||||||
|
await Bun.file(TRACKED_PLAYER_FILE).write(JSON.stringify(trackedPlayers));
|
||||||
|
return { event: "registered" };
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/wov.ts
25
src/wov.ts
@@ -91,6 +91,7 @@ export const searchPlayer = async (username: string) => {
|
|||||||
if (response.status === 404) return null;
|
if (response.status === 404) return null;
|
||||||
|
|
||||||
const data = (await response.json()) as {
|
const data = (await response.json()) as {
|
||||||
|
id: string;
|
||||||
clanId: string | null;
|
clanId: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -115,3 +116,27 @@ export const getClanInfos = async (clanId: string) => {
|
|||||||
|
|
||||||
return data;
|
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;
|
||||||
|
|
||||||
|
return { username: "test" };
|
||||||
|
|
||||||
|
const data = (await response.json()) as {
|
||||||
|
username: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user