Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

guildleave #2722

Merged
merged 18 commits into from
Jan 25, 2025
1 change: 1 addition & 0 deletions Core/src/commands/guild/GuildKickCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ async function acceptGuildKick(player: Player, kickedPlayer: Player, response: D
kickedPlayer.guildId = null;

if (guild.elderId === kickedPlayer.id) {
draftBotInstance.logsDatabase.logGuildElderRemove(guild, guild.elderId).then();
guild.elderId = null;
}
await Promise.all([
Expand Down
121 changes: 121 additions & 0 deletions Core/src/commands/guild/GuildLeaveCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import {commandRequires, CommandUtils} from "../../core/utils/CommandUtils";
import {GuildConstants} from "../../../../Lib/src/constants/GuildConstants";
import {DraftBotPacket, makePacket, PacketContext} from "../../../../Lib/src/packets/DraftBotPacket";
import Player, {Players} from "../../core/database/game/models/Player";
import {
CommandGuildLeaveAcceptPacketRes, CommandGuildLeaveNotInAGuildPacketRes,
CommandGuildLeavePacketReq, CommandGuildLeaveRefusePacketRes
} from "../../../../Lib/src/packets/commands/CommandGuildLeavePacket";
import {Guilds} from "../../core/database/game/models/Guild";
import {ReactionCollectorGuildLeave} from "../../../../Lib/src/packets/interaction/ReactionCollectorGuildLeave";
import {EndCallback, ReactionCollectorInstance} from "../../core/utils/ReactionsCollector";
import {ReactionCollectorAcceptReaction} from "../../../../Lib/src/packets/interaction/ReactionCollectorPacket";
import {BlockingUtils} from "../../core/utils/BlockingUtils";
import {BlockingConstants} from "../../../../Lib/src/constants/BlockingConstants";
import {draftBotInstance} from "../../index";
import {LogsDatabase} from "../../core/database/logs/LogsDatabase";


/**
* Allow the player to leave its guild
* @param player
* @param response
*/
async function acceptGuildLeave(player: Player, response: DraftBotPacket[]): Promise<void> {
await player.reload();
// The player is no longer in a guild since the menu
if (player.guildId === null) {
response.push(makePacket(CommandGuildLeaveNotInAGuildPacketRes, {}));
return;
}
const guild = await Guilds.getById(player.guildId);
if (player.id === guild.chiefId) {
// The guild's chief is leaving
if (guild.elderId !== null) {
draftBotInstance.logsDatabase.logGuildElderRemove(guild, guild.elderId).then();
draftBotInstance.logsDatabase.logGuildChiefChange(guild, guild.elderId).then();
// An elder can recover the guild
player.guildId = null;
const elder = await Players.getById(guild.elderId);
guild.elderId = null;
guild.chiefId = elder.id;
response.push(makePacket(CommandGuildLeaveAcceptPacketRes, {
newChiefKeycloakId: elder.keycloakId,
guildName: guild.name
}));

await Promise.all([
elder.save(),
guild.save(),
player.save()
]);
return;
}
// No elder => the guild will be destroyed
await guild.completelyDestroyAndDeleteFromTheDatabase();
response.push(makePacket(CommandGuildLeaveAcceptPacketRes, {
guildName: guild.name,
isGuildDestroyed: true
}));
return;
}
if (guild.elderId === player.id) {
// The guild's elder is leaving
draftBotInstance.logsDatabase.logGuildElderRemove(guild, guild.elderId).then();
guild.elderId = null;
}
LogsDatabase.logGuildLeave(guild, player.keycloakId).then();
player.guildId = null;
response.push(makePacket(CommandGuildLeaveAcceptPacketRes, {
guildName: guild.name
}));
await Promise.all([
player.save(),
guild.save()
]);
}

function endCallback(player: Player): EndCallback {
return async (collector, response): Promise<void> => {
const reaction = collector.getFirstReaction();
if (reaction && reaction.reaction.type === ReactionCollectorAcceptReaction.name) {
await acceptGuildLeave(player, response);
}
else {
response.push(makePacket(CommandGuildLeaveRefusePacketRes, {}));
}
BlockingUtils.unblockPlayer(player.id, BlockingConstants.REASONS.GUILD_LEAVE);
};
}
export default class GuildLeaveCommand {
@commandRequires(CommandGuildLeavePacketReq, {
notBlocked: true,
disallowedEffects: CommandUtils.DISALLOWED_EFFECTS.NOT_STARTED_OR_DEAD,
level: GuildConstants.REQUIRED_LEVEL,
guildNeeded: true
})
async execute(response: DraftBotPacket[], player: Player, packet: CommandGuildLeavePacketReq, context: PacketContext): Promise<void> {
const guild = await Guilds.getById(player.guildId);
const newChief = guild.chiefId === player.id && guild.elderId ? await Players.getById(guild.elderId) : null;

const collector = new ReactionCollectorGuildLeave(
guild.name,
guild.chiefId === player.id && guild.elderId === null,
newChief?.keycloakId
);

const collectorPacket = new ReactionCollectorInstance(
collector,
context,
{
allowedPlayerKeycloakIds: [player.keycloakId],
reactionLimit: 1
},
endCallback(player)
)
.block(player.id, BlockingConstants.REASONS.GUILD_LEAVE)
.build();

response.push(collectorPacket);
}
}
1 change: 1 addition & 0 deletions Discord/src/commands/guild/GuildElderCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export async function handleCommandGuildElderRefusePacketRes(packet: CommandGuil
});
}


/**
* Handle the response of the server after a guild elder,
* this packet is only sent if the promotion is accepted
Expand Down
109 changes: 109 additions & 0 deletions Discord/src/commands/guild/GuildLeaveCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import {makePacket, PacketContext} from "../../../../Lib/src/packets/DraftBotPacket";
import {ICommand} from "../ICommand";
import {SlashCommandBuilderGenerator} from "../SlashCommandBuilderGenerator";
import {
CommandGuildLeaveAcceptPacketRes,
CommandGuildLeavePacketReq, CommandGuildLeaveRefusePacketRes
} from "../../../../Lib/src/packets/commands/CommandGuildLeavePacket";
import {ReactionCollectorCreationPacket} from "../../../../Lib/src/packets/interaction/ReactionCollectorPacket";
import {DiscordCache} from "../../bot/DiscordCache";
import {DraftBotEmbed} from "../../messages/DraftBotEmbed";
import i18n from "../../translations/i18n";
import {DiscordCollectorUtils} from "../../utils/DiscordCollectorUtils";
import {ReactionCollectorGuildLeaveData} from "../../../../Lib/src/packets/interaction/ReactionCollectorGuildLeave";
import {KeycloakUtils} from "../../../../Lib/src/keycloak/KeycloakUtils";
import {keycloakConfig} from "../../bot/DraftBotShard";

/**
* Create a collector to accept/refuse to leave the guild
* @param packet
* @param context
*/
export async function createGuildLeaveCollector(packet: ReactionCollectorCreationPacket, context: PacketContext): Promise<void> {
const interaction = DiscordCache.getInteraction(context.discord!.interaction)!;
await interaction.deferReply();
const data = packet.data.data as ReactionCollectorGuildLeaveData;
const newChiefPseudo = data.newChiefKeycloakId ? (await KeycloakUtils.getUserByKeycloakId(keycloakConfig, data.newChiefKeycloakId))!.attributes.gameUsername : "";
const keyDesc = data.isGuildDestroyed ? "confirmChiefDesc" : data.newChiefKeycloakId ? "confirmChiefDescWithElder" : "confirmDesc";
const embed = new DraftBotEmbed().formatAuthor(i18n.t("commands:guildLeave.title", {
lng: interaction.userLanguage,
pseudo: interaction.user.displayName
}), interaction.user)
.setDescription(
i18n.t(`commands:guildLeave.${keyDesc}`, {
lng: interaction.userLanguage,
newChiefPseudo,
guildName: data.guildName
})
);

await DiscordCollectorUtils.createAcceptRefuseCollector(interaction, embed, packet, context);
}

/**
* Handle the response when the player leave its guild
* @param packet
* @param context
*/
export async function handleCommandGuildLeaveAcceptPacketRes(packet: CommandGuildLeaveAcceptPacketRes, context: PacketContext): Promise<void> {
const originalInteraction = DiscordCache.getInteraction(context.discord!.interaction!);
const buttonInteraction = DiscordCache.getButtonInteraction(context.discord!.buttonInteraction!);
const keyTitle = packet.newChiefKeycloakId ? "newChiefTitle" : "successTitle";
const keyDesc = packet.isGuildDestroyed ? "destroySuccess" : "leavingSuccess";
const newChiefPseudo = packet.newChiefKeycloakId ? (await KeycloakUtils.getUserByKeycloakId(keycloakConfig, packet.newChiefKeycloakId))!.attributes.gameUsername : "";
if (buttonInteraction && originalInteraction) {
await buttonInteraction.editReply({
embeds: [
new DraftBotEmbed().formatAuthor(i18n.t(`commands:guildLeave.${keyTitle}`, {
lng: originalInteraction.userLanguage,
pseudo: originalInteraction.user.displayName,
newChiefPseudo,
guildName: packet.guildName
}), originalInteraction.user)
.setDescription(
i18n.t(`commands:guildLeave.${keyDesc}`, {lng: originalInteraction.userLanguage, guildName: packet.guildName})
)
]
});
}
}

/**
* Handle the response when the player don't leave its guild
* @param packet
* @param context
*/
export async function handleCommandGuildLeaveRefusePacketRes(packet: CommandGuildLeaveRefusePacketRes, context: PacketContext): Promise<void> {
const originalInteraction = DiscordCache.getInteraction(context.discord!.interaction!);
if (!originalInteraction) {
return;
}
const buttonInteraction = DiscordCache.getButtonInteraction(context.discord!.buttonInteraction!);
await buttonInteraction?.editReply({
embeds: [
new DraftBotEmbed().formatAuthor(i18n.t("commands:guildLeave.canceledTitle", {
lng: originalInteraction.userLanguage,
pseudo: originalInteraction.user.displayName
}), originalInteraction.user)
.setDescription(
i18n.t("commands:guildLeave.canceledDesc", {
lng: originalInteraction.userLanguage
})
)
.setErrorColor()
]
});
}

/**
* Allow the player to leave its guild
*/
function getPacket(): CommandGuildLeavePacketReq {
return makePacket(CommandGuildLeavePacketReq, {});
}

export const commandInfo: ICommand = {
slashCommandBuilder: SlashCommandBuilderGenerator.generateBaseCommand("guildLeave"),
getPacket,
mainGuildCommand: false
};
31 changes: 27 additions & 4 deletions Discord/src/packetHandlers/handlers/CommandHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,14 @@
CommandGuildElderRefusePacketRes,
CommandGuildElderSameGuildPacketRes
} from "../../../../Lib/src/packets/commands/CommandGuildElderPacket";
import {
CommandGuildLeaveAcceptPacketRes, CommandGuildLeaveNotInAGuildPacketRes,
CommandGuildLeaveRefusePacketRes
} from "../../../../Lib/src/packets/commands/CommandGuildLeavePacket";
import {
handleCommandGuildLeaveAcceptPacketRes,
handleCommandGuildLeaveRefusePacketRes
} from "../../commands/guild/GuildLeaveCommand";
import {handleCommandGuildElderAcceptPacketRes, handleCommandGuildElderRefusePacketRes} from "../../commands/guild/GuildElderCommand";
import {CommandSwitchCancelled, CommandSwitchErrorNoItemToSwitch, CommandSwitchSuccess} from "../../../../Lib/src/packets/commands/CommandSwitchPacket";
import {handleItemSwitch} from "../../commands/player/SwitchCommand";
Expand Down Expand Up @@ -274,22 +282,22 @@
}

@packetHandler(CommandGuildElderSameGuildPacketRes)
async guildElderSameGuildRes(packet: CommandGuildElderSameGuildPacketRes, context: PacketContext): Promise<void> {
async guildElderSameGuildRes(_packet: CommandGuildElderSameGuildPacketRes, context: PacketContext): Promise<void> {
await handleClassicError(context, "commands:guildElder.notSameGuild");
}

@packetHandler(CommandGuildElderHimselfPacketRes)
async guildElderHimselfRes(packet: CommandGuildElderHimselfPacketRes, context: PacketContext): Promise<void> {
async guildElderHimselfRes(_packet: CommandGuildElderHimselfPacketRes, context: PacketContext): Promise<void> {
await handleClassicError(context, "commands:guildElder.chiefError");
}

@packetHandler(CommandGuildElderAlreadyElderPacketRes)
async guildElderAlreadyElderRes(packet: CommandGuildElderAlreadyElderPacketRes, context: PacketContext): Promise<void> {
async guildElderAlreadyElderRes(_packet: CommandGuildElderAlreadyElderPacketRes, context: PacketContext): Promise<void> {
await handleClassicError(context, "commands:guildElder.alreadyElder");
}

@packetHandler(CommandGuildElderFoundPlayerPacketRes)
async guildElderFoundPlayerRes(packet: CommandGuildElderFoundPlayerPacketRes, context: PacketContext): Promise<void> {
async guildElderFoundPlayerRes(_packet: CommandGuildElderFoundPlayerPacketRes, context: PacketContext): Promise<void> {
await handleClassicError(context, "commands:guildElder.playerNotFound");
}

Expand All @@ -303,6 +311,21 @@
await handleCommandGuildElderAcceptPacketRes(packet, context);
}

@packetHandler(CommandGuildLeaveNotInAGuildPacketRes)
async guildLeaveNotInAGuildRes(_packet: CommandGuildLeaveNotInAGuildPacketRes, context: PacketContext): Promise<void> {
await handleClassicError(context, "commands:guildLeave.notInAGuild");
}

@packetHandler(CommandGuildLeaveRefusePacketRes)
async guildLeaveRefuseRes(packet: CommandGuildLeaveRefusePacketRes, context: PacketContext): Promise<void> {
await handleCommandGuildLeaveRefusePacketRes(packet, context);
Feiryn marked this conversation as resolved.
Show resolved Hide resolved
}

@packetHandler(CommandGuildLeaveAcceptPacketRes)
async guildLeaveAcceptRes(packet: CommandGuildLeaveAcceptPacketRes, context: PacketContext): Promise<void> {
await handleCommandGuildLeaveAcceptPacketRes(packet,context);
}


@packetHandler(CommandInventoryPacketRes)
async inventoryRes(packet: CommandInventoryPacketRes, context: PacketContext): Promise<void> {
Expand Down Expand Up @@ -340,17 +363,17 @@
}

@packetHandler(CommandReportMonsterRewardRes)
async reportMonsterRewardRes(packet: CommandReportMonsterRewardRes, context: PacketContext): Promise<void> {

Check failure on line 366 in Discord/src/packetHandlers/handlers/CommandHandlers.ts

View workflow job for this annotation

GitHub Actions / eslint-discord-module

'packet' is defined but never used

Check failure on line 366 in Discord/src/packetHandlers/handlers/CommandHandlers.ts

View workflow job for this annotation

GitHub Actions / eslint-discord-module

'context' is defined but never used
// TODO
}

@packetHandler(CommandReportErrorNoMonsterRes)
async reportErrorNoMonsterRes(packet: CommandReportErrorNoMonsterRes, context: PacketContext): Promise<void> {

Check failure on line 371 in Discord/src/packetHandlers/handlers/CommandHandlers.ts

View workflow job for this annotation

GitHub Actions / eslint-discord-module

'packet' is defined but never used

Check failure on line 371 in Discord/src/packetHandlers/handlers/CommandHandlers.ts

View workflow job for this annotation

GitHub Actions / eslint-discord-module

'context' is defined but never used
// TODO
}

@packetHandler(CommandReportRefusePveFightRes)
async reportRefusePveFightRes(packet: CommandReportRefusePveFightRes, context: PacketContext): Promise<void> {

Check failure on line 376 in Discord/src/packetHandlers/handlers/CommandHandlers.ts

View workflow job for this annotation

GitHub Actions / eslint-discord-module

'packet' is defined but never used

Check failure on line 376 in Discord/src/packetHandlers/handlers/CommandHandlers.ts

View workflow job for this annotation

GitHub Actions / eslint-discord-module

'context' is defined but never used
// TODO
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import {ReactionCollectorSkipMissionShopItemData} from "../../../../Lib/src/pack
import {skipMissionShopItemCollector} from "../../commands/mission/MissionShop";
import {createGuildElderCollector} from "../../commands/guild/GuildElderCommand";
import {ReactionCollectorGuildElderData} from "../../../../Lib/src/packets/interaction/ReactionCollectorGuildElder";
import {ReactionCollectorGuildLeaveData} from "../../../../Lib/src/packets/interaction/ReactionCollectorGuildLeave";
import {createGuildLeaveCollector} from "../../commands/guild/GuildLeaveCommand";
import {ReactionCollectorSwitchItemData} from "../../../../Lib/src/packets/interaction/ReactionCollectorSwitchItem";
import {switchItemCollector} from "../../commands/player/SwitchCommand";

Expand All @@ -59,6 +61,7 @@ export default class ReactionCollectorHandler {
ReactionCollectorHandler.collectorMap.set(ReactionCollectorGuildCreateData.name, createGuildCreateCollector);
ReactionCollectorHandler.collectorMap.set(ReactionCollectorGuildKickData.name, createGuildKickCollector);
ReactionCollectorHandler.collectorMap.set(ReactionCollectorGuildElderData.name, createGuildElderCollector);
ReactionCollectorHandler.collectorMap.set(ReactionCollectorGuildLeaveData.name, createGuildLeaveCollector);
ReactionCollectorHandler.collectorMap.set(ReactionCollectorLotteryData.name, lotteryCollector);
ReactionCollectorHandler.collectorMap.set(ReactionCollectorInteractOtherPlayersPoorData.name, interactOtherPlayersCollector);
ReactionCollectorHandler.collectorMap.set(ReactionCollectorWitchData.name, witchCollector);
Expand Down
14 changes: 14 additions & 0 deletions Lang/en/discordBuilder.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,20 @@
}
}
},
"guildElder": {
"description": "Promote a guild member to elder.",
"name": "guildelder",
"options": {
"user": {
"description": "The user who will be promoted as an elder.",
"name": "user"
}
}
},
"guildLeave": {
"description": "Leave your guild",
"name": "guildleave"
},
"profile": {
"description": "Displays the profile of a player.",
"name": "profile",
Expand Down
18 changes: 15 additions & 3 deletions Lang/fr/commands.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,24 @@
"chiefError": "En tant que chef de la guilde, vous ne pouvez pas être l'aîné.",
"alreadyElder": "Le joueur est déjà aîné de votre guilde.",
"title": "{{pseudo}}, confirmez-vous votre choix ?",
"confirmDesc": ":question: {{elderPseudo}} deviendra l'aîné de la guilde `{{guildName}}`.",
"confirmDesc": "{emote:collectors.question} {{elderPseudo}} deviendra l'aîné de la guilde `{{guildName}}`.",
"successElderAddTitle": "{{elderPseudo}} est le nouvel aîné de la guilde {{guildName}} !",
"acceptedDesc": "L'aîné sert à aider le chef dans la gestion de la guilde. Il faut donc le choisir avec prudence ! Mais rassurez-vous, si vous n'êtes pas satisfait de votre aîné, vous pouvez le remplacer avec la commande {command:guildelder}, ou si vous avez une âme de dictateur, vous pouvez définitivement le supprimer avec {command:guildelderremove}.",
"canceledDesc": "L'aîné n'a pas été promu.",
"canceledTitle": "Annulation prise en compte.",
"problemWhilePromoting": "Une erreur est survenue lors de la promotion du joueur."
"canceledTitle": "Annulation prise en compte."
},
"guildLeave": {
"title": "{{pseudo}}, confirmation :",
"confirmDesc": "{emote:collectors.question} Souhaitez vous vraiment quitter la guilde `{{guildName}}` ?",
"confirmChiefDesc": "{emote:collectors.question} Souhaitez vous vraiment **détruire définitivement** la guilde `{{guildName}}` ?",
"confirmChiefDescWithElder": "{emote:collectors.question} Souhaitez vous vraiment quitter la guilde `{{guildName}}` ? {{newChiefPseudo}} en deviendra le chef.",
"canceledDesc": "Votre départ a été annulé.",
"successTitle": "{{pseudo}} a quitté la guilde {{guildName}} !",
"leavingSuccess": "Vous êtes un pauvre cow-boy solitaire et vous êtes très loin de chez vous...",
"destroySuccess": "La guilde {{guildName}} a correctement été dissoute.",
"newChiefTitle": "{{newChiefPseudo}} est le nouveau chef de la guilde {{guildName}} !",
"notInAGuild": "Il semblerait que vous ne fassiez déjà plus parti d'une guilde.",
"canceledTitle": "Annulation prise en compte"
},
"help": {
"aliasFieldTitle": "Alias",
Expand Down
Loading
Loading