mirror of
https://github.com/MetaFam/TheGame.git
synced 2026-04-24 03:00:09 -04:00
Added cron trigger to sync guild memberships from Discord
This commit is contained in:
committed by
Alec LaLonde
parent
2db3d575a3
commit
1fcf78d9d1
@@ -21,3 +21,8 @@
|
||||
retry_interval_seconds: 10
|
||||
comment: Checks for cache entries more than four days old and queues them to be
|
||||
recached.
|
||||
- name: syncAllGuildDiscordMembers
|
||||
webhook: '{{ACTION_BASE_ENDPOINT}}/syncAllGuildDiscordMembers'
|
||||
schedule: 31 5 * * *
|
||||
include_in_metadata: true
|
||||
payload: {}
|
||||
|
||||
@@ -128,14 +128,6 @@
|
||||
- name: GuildType
|
||||
using:
|
||||
foreign_key_constraint_on: type
|
||||
- name: metadata
|
||||
using:
|
||||
manual_configuration:
|
||||
remote_table:
|
||||
schema: public
|
||||
name: guild_metadata
|
||||
column_mapping:
|
||||
id: guild_id
|
||||
array_relationships:
|
||||
- name: guild_players
|
||||
using:
|
||||
@@ -401,7 +393,6 @@
|
||||
- ethereum_address
|
||||
- id
|
||||
- player_type_id
|
||||
- profile_layout
|
||||
- pronouns
|
||||
- rank
|
||||
- role
|
||||
@@ -418,7 +409,6 @@
|
||||
- color_mask
|
||||
- ethereum_address
|
||||
- id
|
||||
- profile_layout
|
||||
- pronouns
|
||||
- rank
|
||||
- role
|
||||
@@ -435,7 +425,6 @@
|
||||
- availability_hours
|
||||
- color_mask
|
||||
- player_type_id
|
||||
- profile_layout
|
||||
- pronouns
|
||||
- role
|
||||
- timezone
|
||||
|
||||
@@ -2,6 +2,7 @@ import express from 'express';
|
||||
import multer from 'multer';
|
||||
|
||||
import { asyncHandlerWrapper } from '../../lib/apiHelpers';
|
||||
import { syncAllGuildDiscordMembers } from '../triggers/syncDiscordGuildMembers';
|
||||
import { guildRoutes } from './guild/routes';
|
||||
import { cacheRoutes } from './idxCache/routes';
|
||||
import { migrateSourceCredAccounts } from './migrateSourceCredAccounts/handler';
|
||||
@@ -18,6 +19,10 @@ actionRoutes.post(
|
||||
'/migrateSourceCredAccounts',
|
||||
asyncHandlerWrapper(migrateSourceCredAccounts),
|
||||
);
|
||||
actionRoutes.post(
|
||||
'/syncAllGuildDiscordMembers',
|
||||
asyncHandlerWrapper(syncAllGuildDiscordMembers),
|
||||
);
|
||||
|
||||
actionRoutes.use('/quests', questsRoutes);
|
||||
|
||||
|
||||
@@ -88,6 +88,8 @@ export const GuildFragment = gql`
|
||||
type
|
||||
website_url
|
||||
discord_id
|
||||
status
|
||||
membership_through_discord
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -113,6 +115,12 @@ gql`
|
||||
}
|
||||
}
|
||||
|
||||
query GetGuilds($status: GuildStatus_enum) {
|
||||
guild(where: { status: { _eq: $status } }) {
|
||||
...GuildFragment
|
||||
}
|
||||
}
|
||||
|
||||
query GetGuildMetadataById($id: uuid!) {
|
||||
guild_metadata(where: { guild_id: { _eq: $id } }) {
|
||||
guild_id
|
||||
|
||||
@@ -3,10 +3,12 @@ import {
|
||||
createDiscordClient,
|
||||
GuildDiscordMetadata,
|
||||
} from '@metafam/discord-bot';
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
import {
|
||||
Guild,
|
||||
Guild_Player_Insert_Input,
|
||||
GuildFragmentFragment,
|
||||
GuildStatus_Enum,
|
||||
SyncGuildMembersMutation,
|
||||
} from '../../lib/autogen/hasura-sdk';
|
||||
@@ -18,111 +20,148 @@ export const syncDiscordGuildMembers = async (
|
||||
) => {
|
||||
const { new: guild } = payload.event.data;
|
||||
|
||||
if (guild?.discord_id == null) return;
|
||||
|
||||
console.log(`Updating guild members for ${guild?.name} from Discord...`);
|
||||
|
||||
try {
|
||||
const getMetadataResponse = await client.GetGuildMetadataById({
|
||||
id: guild.id,
|
||||
});
|
||||
const guildMetadata = getMetadataResponse.guild_metadata[0];
|
||||
if (
|
||||
guildMetadata == null ||
|
||||
guildMetadata.discord_metadata == null ||
|
||||
guild.membership_through_discord === false
|
||||
)
|
||||
return;
|
||||
|
||||
// at least one membership role must be defined
|
||||
const discordServerMembershipRoles = (guildMetadata.discord_metadata as GuildDiscordMetadata)
|
||||
.membershipRoleIds;
|
||||
if (
|
||||
discordServerMembershipRoles == null ||
|
||||
discordServerMembershipRoles?.length === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// only sync on ACTIVE guilds. For all others, remove all guild_players
|
||||
if (guild.status !== GuildStatus_Enum.Active) {
|
||||
const removeResponse = await client.RemoveAllGuildMembers({
|
||||
guildId: guild.id,
|
||||
});
|
||||
const numDeleted = removeResponse.delete_guild_player?.affected_rows;
|
||||
if (numDeleted != null && numDeleted > 0) {
|
||||
console.log(`Removed ${numDeleted} players from ${guild.status} guild`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const discordClient = await createDiscordClient();
|
||||
const discordGuild = await discordClient.guilds.fetch(guild.discord_id);
|
||||
|
||||
if (discordGuild == null)
|
||||
throw new Error(`Discord server ${guild.discord_id} does not exist!`);
|
||||
|
||||
const getGuildMembersResponse = await client.GetGuildMembers({
|
||||
id: guild.id,
|
||||
});
|
||||
const guildMemberDiscordIds = getGuildMembersResponse.guild[0].guild_players
|
||||
.filter((p) => p.Player.discord_id != null)
|
||||
.map((p) => p.Player.discord_id) as string[];
|
||||
|
||||
await discordGuild.members.fetch();
|
||||
|
||||
// gather all discord server members who have at least one of the "membership" roles
|
||||
// as defined by this guild
|
||||
const discordGuildMembers = discordGuild.members.cache.filter(
|
||||
(discordMember) =>
|
||||
discordMember.roles.cache.some((role) =>
|
||||
discordServerMembershipRoles.includes(role.id),
|
||||
),
|
||||
);
|
||||
|
||||
// gather discord server members who are not already members of this guild
|
||||
const discordServerMemberIds: string[] = [];
|
||||
const playerDiscordIdsToAdd: string[] = [];
|
||||
discordGuildMembers.forEach((discordMember) => {
|
||||
discordServerMemberIds.push(discordMember.user.id);
|
||||
if (!guildMemberDiscordIds.includes(discordMember.user.id)) {
|
||||
playerDiscordIdsToAdd.push(discordMember.user.id);
|
||||
}
|
||||
});
|
||||
|
||||
// gather current members of this guild who are not in the list of discord server members
|
||||
const playersToRemove = guildMemberDiscordIds.filter(
|
||||
(discordId) => !discordServerMemberIds.includes(discordId),
|
||||
);
|
||||
|
||||
const getPlayerIdsResponse = await client.GetPlayersByDiscordId({
|
||||
discordIds: playerDiscordIdsToAdd,
|
||||
});
|
||||
|
||||
const playersToAdd: Guild_Player_Insert_Input[] = getPlayerIdsResponse.player.map(
|
||||
(player) => ({
|
||||
guild_id: guild.id,
|
||||
player_id: player.id,
|
||||
}),
|
||||
);
|
||||
|
||||
const syncResponse: SyncGuildMembersMutation = await client.SyncGuildMembers(
|
||||
{
|
||||
memberDiscordIdsToRemove: playersToRemove,
|
||||
membersToAdd: playersToAdd,
|
||||
},
|
||||
);
|
||||
|
||||
const numDeleted = syncResponse.delete_guild_player?.affected_rows;
|
||||
const numInserted = syncResponse.insert_guild_player?.affected_rows;
|
||||
|
||||
if (numDeleted != null && numDeleted > 0) {
|
||||
console.log(`Removed ${numDeleted} players`);
|
||||
}
|
||||
if (numInserted != null && numInserted > 0) {
|
||||
console.log(`Added ${numInserted} players`);
|
||||
if (guild != null) {
|
||||
await syncGuildMembers(guild);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
export const syncAllGuildDiscordMembers = async (
|
||||
_req: Request,
|
||||
res: Response,
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const { guild: guilds } = await client.GetGuilds({
|
||||
status: GuildStatus_Enum.Active,
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
guilds
|
||||
.filter((guild) => guild.membership_through_discord === true)
|
||||
.map((guild) => syncGuildMembers(guild)),
|
||||
);
|
||||
|
||||
res.sendStatus(200);
|
||||
} catch (e) {
|
||||
const msg = (e as Error).message;
|
||||
console.warn('Error syncing guild memberships from discord', msg);
|
||||
console.error(e);
|
||||
|
||||
res.sendStatus(500);
|
||||
}
|
||||
};
|
||||
|
||||
const syncGuildMembers = async (guild: GuildFragmentFragment) => {
|
||||
if (guild?.discord_id == null) return;
|
||||
|
||||
const getMetadataResponse = await client.GetGuildMetadataById({
|
||||
id: guild.id,
|
||||
});
|
||||
const guildMetadata = getMetadataResponse.guild_metadata[0];
|
||||
if (
|
||||
guildMetadata == null ||
|
||||
guildMetadata.discord_metadata == null ||
|
||||
guild.membership_through_discord === false
|
||||
)
|
||||
return;
|
||||
|
||||
// at least one membership role must be defined
|
||||
const discordServerMembershipRoles = (guildMetadata.discord_metadata as GuildDiscordMetadata)
|
||||
.membershipRoleIds;
|
||||
if (
|
||||
discordServerMembershipRoles == null ||
|
||||
discordServerMembershipRoles?.length === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// only sync on ACTIVE guilds. For all others, remove all guild_players
|
||||
if (guild.status !== GuildStatus_Enum.Active) {
|
||||
const removeResponse = await client.RemoveAllGuildMembers({
|
||||
guildId: guild.id,
|
||||
});
|
||||
const numDeleted = removeResponse.delete_guild_player?.affected_rows;
|
||||
if (numDeleted != null && numDeleted > 0) {
|
||||
console.log(`Removed ${numDeleted} players from ${guild.status} guild`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const discordClient = await createDiscordClient();
|
||||
const discordGuild = await discordClient.guilds.fetch(guild.discord_id);
|
||||
|
||||
if (discordGuild == null)
|
||||
throw new Error(`Discord server ${guild.discord_id} does not exist!`);
|
||||
|
||||
const getGuildMembersResponse = await client.GetGuildMembers({
|
||||
id: guild.id,
|
||||
});
|
||||
const guildMemberDiscordIds = getGuildMembersResponse.guild[0].guild_players
|
||||
.filter((p) => p.Player.discord_id != null)
|
||||
.map((p) => p.Player.discord_id) as string[];
|
||||
|
||||
await discordGuild.members.fetch();
|
||||
|
||||
// gather all discord server members who have at least one of the "membership" roles
|
||||
// as defined by this guild
|
||||
const discordGuildMembers = discordGuild.members.cache.filter(
|
||||
(discordMember) =>
|
||||
discordMember.roles.cache.some((role) =>
|
||||
discordServerMembershipRoles.includes(role.id),
|
||||
),
|
||||
);
|
||||
|
||||
// gather discord server members who are not already members of this guild
|
||||
const discordServerMemberIds: string[] = [];
|
||||
const playerDiscordIdsToAdd: string[] = [];
|
||||
discordGuildMembers.forEach((discordMember) => {
|
||||
discordServerMemberIds.push(discordMember.user.id);
|
||||
if (!guildMemberDiscordIds.includes(discordMember.user.id)) {
|
||||
playerDiscordIdsToAdd.push(discordMember.user.id);
|
||||
}
|
||||
});
|
||||
|
||||
// gather current members of this guild who are not in the list of discord server members
|
||||
const playersToRemove = guildMemberDiscordIds.filter(
|
||||
(discordId) => !discordServerMemberIds.includes(discordId),
|
||||
);
|
||||
|
||||
const getPlayerIdsResponse = await client.GetPlayersByDiscordId({
|
||||
discordIds: playerDiscordIdsToAdd,
|
||||
});
|
||||
|
||||
const playersToAdd: Guild_Player_Insert_Input[] = getPlayerIdsResponse.player.map(
|
||||
(player) => ({
|
||||
guild_id: guild.id,
|
||||
player_id: player.id,
|
||||
}),
|
||||
);
|
||||
|
||||
const syncResponse: SyncGuildMembersMutation = await client.SyncGuildMembers({
|
||||
memberDiscordIdsToRemove: playersToRemove,
|
||||
membersToAdd: playersToAdd,
|
||||
});
|
||||
|
||||
const numDeleted = syncResponse.delete_guild_player?.affected_rows;
|
||||
const numInserted = syncResponse.insert_guild_player?.affected_rows;
|
||||
|
||||
let logStr = '';
|
||||
|
||||
if (numInserted != null && numInserted > 0) {
|
||||
logStr = `Added ${numInserted} players`;
|
||||
}
|
||||
if (numDeleted != null && numDeleted > 0) {
|
||||
logStr += `${
|
||||
logStr.length > 0 ? ' and removed' : 'Removed'
|
||||
} ${numDeleted} players`;
|
||||
}
|
||||
|
||||
if (logStr.length > 0) {
|
||||
console.log(
|
||||
`Updated guild members for ${guild?.name} from Discord. ${logStr}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19156,6 +19156,15 @@ loader-utils@^1.1.0, loader-utils@^1.4.0:
|
||||
emojis-list "^3.0.0"
|
||||
json5 "^1.0.1"
|
||||
|
||||
loader-utils@^2.0.0:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129"
|
||||
integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==
|
||||
dependencies:
|
||||
big.js "^5.2.2"
|
||||
emojis-list "^3.0.0"
|
||||
json5 "^2.1.2"
|
||||
|
||||
loady@~0.0.1:
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/loady/-/loady-0.0.5.tgz#b17adb52d2fb7e743f107b0928ba0b591da5d881"
|
||||
|
||||
Reference in New Issue
Block a user