Add command to update usernames and avatars

This commit is contained in:
Kalle
2022-07-15 23:54:08 +03:00
parent e406f12a31
commit 83d34353c2
7 changed files with 134 additions and 5 deletions

View File

@@ -57,6 +57,29 @@ export function updateProfile(args: Pick<User, "country" | "id" | "bio">) {
updateProfileStm.run(args);
}
const updateByDiscordIdStm = sql.prepare(`
update "User"
set "discordAvatar" = $discordAvatar,
"discordName" = $discordName,
"discordDiscriminator" = $discordDiscriminator
where "discordId" = $discordId
`);
export const updateMany = sql.transaction(
(
argsArr: Array<
Pick<
User,
"discordAvatar" | "discordName" | "discordDiscriminator" | "discordId"
>
>
) => {
for (const updateArgs of argsArr) {
updateByDiscordIdStm.run(updateArgs);
}
}
);
const deleteAllPatronDataStm = sql.prepare(`
update "User"
set "patronTier" = null,

View File

@@ -33,6 +33,7 @@ export default function FAQPage() {
a score below 50% they get demoted a tier or in the case of +3 kicked.
</p>
</details>
<details className="faq__details">
<summary className="faq__summary">
How to get a badge prize for my event?
@@ -48,6 +49,27 @@ export default function FAQPage() {
your idea.
</p>
</details>
<details className="faq__details">
<summary className="faq__summary">
How to update my avatar or username?
</summary>
<p>
Updating username or avatar on Discord doesn&apos;t right away update
them on sendou.ink. To make that happen you have two options:
</p>
<ol>
<li>
If you are a member of this website&apos;s Discord or the Plus
Server you can simply wait. There is a routine that runs once a day
that handles the updating.
</li>
<li>
Alternatively if you want to update them right away you can log out
and back in on sendou.ink.
</li>
</ol>
</details>
</Main>
);
}

View File

@@ -1,9 +1,21 @@
import type { LoaderFunction } from "@remix-run/node";
import type { ActionFunction, LoaderFunction } from "@remix-run/node";
import { json } from "@remix-run/node";
import { db } from "~/db";
import type { UserWithPlusTier } from "~/db/types";
import { canAccessLohiEndpoint } from "~/permissions";
import { discordFullName } from "~/utils/strings";
export const action: ActionFunction = async ({ request }) => {
if (!canAccessLohiEndpoint(request)) {
throw new Response(null, { status: 403 });
}
// input untyped but we trust Lohi to give us correctly shaped request here
db.users.updateMany(await request.json());
return null;
};
export interface UsersLoaderData {
users: ({
discordFullName: string;

View File

@@ -2,6 +2,7 @@ import { questionCommand } from "./q";
import { lfgRoleCommand } from "./lfg";
import { accessCommand } from "./access";
import { plusCommand } from "./plus";
import { updateAllCommand } from "./updateall";
import type { BotCommand } from "discord-bot/types";
export const commands = [
@@ -9,6 +10,7 @@ export const commands = [
lfgRoleCommand,
accessCommand,
plusCommand,
updateAllCommand,
];
export const commandsMap = Object.fromEntries(commands.map((c) => [c.name, c]));

View File

@@ -3,12 +3,11 @@ import { Client, GuildMember, Role } from "discord.js";
import invariant from "tiny-invariant";
import ids from "../ids";
import type { BotCommand } from "../types";
import { isPlusTierRoleId, plusTierToRoleId, usersWithAccess } from "../utils";
import { plusTierToRoleId, usersWithAccess } from "../utils";
const COMMAND_NAME = "plus";
const ACTION_ARG = "dry";
// doesn't seem to remove all roles...
export const plusCommand: BotCommand = {
guilds: [ids.guilds.adminServer],
name: COMMAND_NAME,

View File

@@ -0,0 +1,66 @@
import { SlashCommandBuilder } from "@discordjs/builders";
import { Client } from "discord.js";
import invariant from "tiny-invariant";
import type { User } from "../../app/db/types";
import ids from "../ids";
import type { BotCommand } from "../types";
import { sendouInkFetch } from "../utils";
const COMMAND_NAME = "updateall";
const guildsToCrawlForUpdates = [ids.guilds.plusServer, ids.guilds.sendou];
export const updateAllCommand: BotCommand = {
guilds: [ids.guilds.adminServer],
name: COMMAND_NAME,
builder: new SlashCommandBuilder()
.setName(COMMAND_NAME)
.setDescription("Update sendou.ink usernames and avatars"),
// @ts-expect-error TODO: fix. Library doesn't seem to extract API Message type so I could fix this error?
execute: async ({ interaction, client }) => {
await interaction.deferReply({ ephemeral: true });
const userUpdates = await getUsersToUpdate(client);
const response = await sendouInkFetch("/users", {
method: "post",
body: JSON.stringify(userUpdates),
});
if (!response.ok) {
return interaction.editReply(
`Update failed with status code ${response.status}`
);
}
return interaction.editReply(`Sent ${userUpdates.length} users`);
},
};
async function getUsersToUpdate(client: Client<boolean>) {
const usersSeen = new Set<string>();
const userUpdates: Array<
Pick<
User,
"discordId" | "discordName" | "discordDiscriminator" | "discordAvatar"
>
> = [];
for (const guildId of guildsToCrawlForUpdates) {
const guild = client.guilds.cache.find((g) => g.id === guildId);
invariant(guild);
for (const [, { user }] of await guild.members.fetch()) {
if (usersSeen.has(user.id)) continue;
userUpdates.push({
discordId: user.id,
discordAvatar: user.avatar,
discordDiscriminator: user.discriminator,
discordName: user.username,
});
usersSeen.add(user.id);
}
}
return userUpdates;
}

View File

@@ -3,13 +3,18 @@ import { LOHI_TOKEN_HEADER_NAME } from "~/constants";
import type { PlusListLoaderData } from "~/routes/plus/list";
import ids from "./ids";
export async function usersWithAccess(): Promise<PlusListLoaderData> {
export function sendouInkFetch(path: string, init?: RequestInit) {
invariant(process.env["SENDOU_INK_URL"], "SENDOU_INK_URL is not set");
invariant(process.env["LOHI_TOKEN"], "LOHI_TOKEN is not set");
const response = await fetch(`${process.env["SENDOU_INK_URL"]}/plus/list`, {
return fetch(`${process.env["SENDOU_INK_URL"]}${path}`, {
headers: [[LOHI_TOKEN_HEADER_NAME, process.env["LOHI_TOKEN"]]],
...init,
});
}
export async function usersWithAccess(): Promise<PlusListLoaderData> {
const response = await sendouInkFetch("/plus/list");
if (!response.ok) {
throw new Error(