mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-25 20:50:14 -05:00
Add command to update usernames and avatars
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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'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'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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]));
|
||||
|
||||
@@ -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,
|
||||
|
||||
66
discord-bot/commands/updateall.ts
Normal file
66
discord-bot/commands/updateall.ts
Normal 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;
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user