feat: clan members #1143

Merged
OrdisPrime merged 12 commits from clan-members into main 2025-03-10 16:40:41 -07:00
13 changed files with 375 additions and 87 deletions

View File

@ -0,0 +1,75 @@
import { Guild, GuildMember } from "@/src/models/guildModel";
import { Account } from "@/src/models/loginModel";
import { fillInInventoryDataForGuildMember } from "@/src/services/guildService";
import { createMessage } from "@/src/services/inboxService";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountForRequest, getSuffixedName } from "@/src/services/loginService";
import { IOid } from "@/src/types/commonTypes";
import { IGuildMemberClient } from "@/src/types/guildTypes";
import { RequestHandler } from "express";
import { ExportFlavour } from "warframe-public-export-plus";
export const addToGuildController: RequestHandler = async (req, res) => {
const payload = JSON.parse(String(req.body)) as IAddToGuildRequest;
const account = await Account.findOne({ DisplayName: payload.UserName });
if (!account) {
res.status(400).json("Username does not exist");
return;
}
const guild = (await Guild.findOne({ _id: payload.GuildId.$oid }, "Name"))!;
// TODO: Check sender is allowed to send invites for this guild.
if (
await GuildMember.exists({
accountId: account._id,
guildId: payload.GuildId.$oid
})
) {
res.status(400).json("User already invited to clan");
return;
}
await GuildMember.insertOne({
accountId: account._id,
guildId: payload.GuildId.$oid,
status: 2 // outgoing invite
});
const senderAccount = await getAccountForRequest(req);
const senderInventory = await getInventory(senderAccount._id.toString(), "ActiveAvatarImageType");
await createMessage(account._id.toString(), [
{
sndr: getSuffixedName(senderAccount),
msg: "/Lotus/Language/Menu/Mailbox_ClanInvite_Body",
arg: [
{
Key: "clan",
Tag: guild.Name + "#000"
}
],
sub: "/Lotus/Language/Menu/Mailbox_ClanInvite_Title",
icon: ExportFlavour[senderInventory.ActiveAvatarImageType].icon,
contextInfo: payload.GuildId.$oid,
highPriority: true,
acceptAction: "GUILD_INVITE",
declineAction: "GUILD_INVITE",
hasAccountAction: true
}
]);
const member: IGuildMemberClient = {
_id: { $oid: account._id.toString() },
DisplayName: account.DisplayName,
Rank: 7,
Status: 2
};
await fillInInventoryDataForGuildMember(member);
res.json({ NewMember: member });
};
interface IAddToGuildRequest {
UserName: string;
GuildId: IOid;
}

View File

@ -0,0 +1,32 @@
import { Guild, GuildMember } from "@/src/models/guildModel";
import { getGuildClient, updateInventoryForConfirmedGuildJoin } from "@/src/services/guildService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express";
import { Types } from "mongoose";
export const confirmGuildInvitationController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const guildMember = await GuildMember.findOne({
accountId: accountId,
guildId: req.query.clanId as string
});
if (guildMember) {
guildMember.status = 0;
await guildMember.save();
await updateInventoryForConfirmedGuildJoin(accountId, new Types.ObjectId(req.query.clanId as string));
const guild = (await Guild.findOne({ _id: req.query.clanId as string }))!;
res.json({
...(await getGuildClient(guild, accountId)),
InventoryChanges: {
Recipes: [
{
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
ItemCount: 1
}
]
}
});
} else {
res.end();
}
};

View File

@ -1,8 +1,8 @@
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { Inventory } from "@/src/models/inventoryModels/inventoryModel"; import { Guild, GuildMember } from "@/src/models/guildModel";
import { Guild } from "@/src/models/guildModel"; import { updateInventoryForConfirmedGuildJoin } from "@/src/services/guildService";
export const createGuildController: RequestHandler = async (req, res) => { export const createGuildController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
@ -14,20 +14,15 @@ export const createGuildController: RequestHandler = async (req, res) => {
}); });
await guild.save(); await guild.save();
// Update inventory // Create guild member on database
const inventory = await Inventory.findOne({ accountOwnerId: accountId }); await GuildMember.insertOne({
if (inventory) { accountId: accountId,
// Set GuildId guildId: guild._id,
inventory.GuildId = guild._id; status: 0,
rank: 0
});
// Give clan key (TODO: This should only be a blueprint) await updateInventoryForConfirmedGuildJoin(accountId, guild._id);
inventory.LevelKeys.push({
ItemType: "/Lotus/Types/Keys/DojoKey",
ItemCount: 1
});
await inventory.save();
}
res.json(guild); res.json(guild);
}; };

View File

@ -0,0 +1,14 @@
import { GuildMember } from "@/src/models/guildModel";
import { getAccountForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express";
export const declineGuildInviteController: RequestHandler = async (req, res) => {
const accountId = await getAccountForRequest(req);
await GuildMember.deleteOne({
accountId: accountId,
guildId: req.query.clanId as string
});
res.end();
};

View File

@ -1,18 +1,13 @@
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
import { Guild } from "@/src/models/guildModel"; import { Guild } from "@/src/models/guildModel";
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
import { getGuildVault } from "@/src/services/guildService";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { getInventory } from "@/src/services/inventoryService";
import { getGuildClient } from "@/src/services/guildService";
const getGuildController: RequestHandler = async (req, res) => { const getGuildController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
const inventory = await Inventory.findOne({ accountOwnerId: accountId }); const inventory = await getInventory(accountId);
if (!inventory) {
res.status(400).json({ error: "inventory was undefined" });
return;
}
if (inventory.GuildId) { if (inventory.GuildId) {
const guild = await Guild.findOne({ _id: inventory.GuildId }); const guild = await Guild.findOne({ _id: inventory.GuildId });
if (guild) { if (guild) {
@ -23,64 +18,7 @@ const getGuildController: RequestHandler = async (req, res) => {
guild.CeremonyResetDate = undefined; guild.CeremonyResetDate = undefined;
await guild.save(); await guild.save();
} }
res.json({ res.json(await getGuildClient(guild, accountId));
_id: toOid(guild._id),
Name: guild.Name,
MOTD: guild.MOTD,
LongMOTD: guild.LongMOTD,
Members: [
{
_id: { $oid: req.query.accountId },
Rank: 0,
Status: 0
}
],
Ranks: [
{
Name: "/Lotus/Language/Game/Rank_Creator",
Permissions: 16351
},
{
Name: "/Lotus/Language/Game/Rank_Warlord",
Permissions: 14303
},
{
Name: "/Lotus/Language/Game/Rank_General",
Permissions: 4318
},
{
Name: "/Lotus/Language/Game/Rank_Officer",
Permissions: 4314
},
{
Name: "/Lotus/Language/Game/Rank_Leader",
Permissions: 4106
},
{
Name: "/Lotus/Language/Game/Rank_Sage",
Permissions: 4304
},
{
Name: "/Lotus/Language/Game/Rank_Soldier",
Permissions: 4098
},
{
Name: "/Lotus/Language/Game/Rank_Initiate",
Permissions: 4096
},
{
Name: "/Lotus/Language/Game/Rank_Utility",
Permissions: 4096
}
],
Tier: 1,
Vault: getGuildVault(guild),
Class: guild.Class,
XP: guild.XP,
IsContributor: !!guild.CeremonyContributors?.find(x => x.equals(accountId)),
NumContributors: guild.CeremonyContributors?.length ?? 0,
CeremonyResetDate: guild.CeremonyResetDate ? toMongoDate(guild.CeremonyResetDate) : undefined
});
return; return;
} }
} }

View File

@ -0,0 +1,45 @@
import { GuildMember } from "@/src/models/guildModel";
import { getGuildForRequest } from "@/src/services/guildService";
import { getInventory } from "@/src/services/inventoryService";
import { RequestHandler } from "express";
export const removeFromGuildController: RequestHandler = async (req, res) => {
const guild = await getGuildForRequest(req);
// TODO: Check permissions
const payload = JSON.parse(String(req.body)) as IRemoveFromGuildRequest;
const guildMember = (await GuildMember.findOne({ accountId: payload.userId, guildId: guild._id }))!;
if (guildMember.status == 0) {
const inventory = await getInventory(payload.userId);
inventory.GuildId = undefined;
// Remove clan key or blueprint from kicked member
const itemIndex = inventory.MiscItems.findIndex(x => x.ItemType == "/Lotus/Types/Keys/DojoKey");
if (itemIndex != -1) {
inventory.MiscItems.splice(itemIndex, 1);
} else {
const recipeIndex = inventory.Recipes.findIndex(x => x.ItemType == "/Lotus/Types/Keys/DojoKeyBlueprint");
if (recipeIndex != -1) {
inventory.Recipes.splice(itemIndex, 1);
}
}
await inventory.save();
// TODO: Handle clan leader kicking themselves (guild should be deleted in this case, I think)
} else if (guildMember.status == 2) {
// TODO: Maybe the inbox message for the sent invite should be deleted?
}
await GuildMember.deleteOne({ _id: guildMember._id });
res.json({
_id: payload.userId,
ItemToRemove: "/Lotus/Types/Keys/DojoKey",
RecipeToRemove: "/Lotus/Types/Keys/DojoKeyBlueprint"
});
};
interface IRemoveFromGuildRequest {
userId: string;
kicker?: string;
}

View File

@ -7,11 +7,14 @@ import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
import { PersonalRooms } from "@/src/models/personalRoomsModel"; import { PersonalRooms } from "@/src/models/personalRoomsModel";
import { Ship } from "@/src/models/shipModel"; import { Ship } from "@/src/models/shipModel";
import { Stats } from "@/src/models/statsModel"; import { Stats } from "@/src/models/statsModel";
import { GuildMember } from "@/src/models/guildModel";
export const deleteAccountController: RequestHandler = async (req, res) => { export const deleteAccountController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
// TODO: Handle the account being the creator of a guild
await Promise.all([ await Promise.all([
Account.deleteOne({ _id: accountId }), Account.deleteOne({ _id: accountId }),
GuildMember.deleteOne({ accountId: accountId }),
Inbox.deleteMany({ ownerId: accountId }), Inbox.deleteMany({ ownerId: accountId }),
Inventory.deleteOne({ accountOwnerId: accountId }), Inventory.deleteOne({ accountOwnerId: accountId }),
Loadout.deleteOne({ loadoutOwnerId: accountId }), Loadout.deleteOne({ loadoutOwnerId: accountId }),

View File

@ -4,7 +4,8 @@ import {
ITechProjectDatabase, ITechProjectDatabase,
ITechProjectClient, ITechProjectClient,
IDojoDecoDatabase, IDojoDecoDatabase,
ILongMOTD ILongMOTD,
IGuildMemberDatabase
} from "@/src/types/guildTypes"; } from "@/src/types/guildTypes";
import { Document, Model, model, Schema, Types } from "mongoose"; import { Document, Model, model, Schema, Types } from "mongoose";
import { fusionTreasuresSchema, typeCountSchema } from "./inventoryModels/inventoryModel"; import { fusionTreasuresSchema, typeCountSchema } from "./inventoryModels/inventoryModel";
@ -70,7 +71,7 @@ const longMOTDSchema = new Schema<ILongMOTD>(
const guildSchema = new Schema<IGuildDatabase>( const guildSchema = new Schema<IGuildDatabase>(
{ {
Name: { type: String, required: true }, Name: { type: String, required: true, unique: true },
MOTD: { type: String, default: "" }, MOTD: { type: String, default: "" },
LongMOTD: { type: longMOTDSchema, default: undefined }, LongMOTD: { type: longMOTDSchema, default: undefined },
DojoComponents: { type: [dojoComponentSchema], default: [] }, DojoComponents: { type: [dojoComponentSchema], default: [] },
@ -113,3 +114,14 @@ export type TGuildDatabaseDocument = Document<unknown, {}, IGuildDatabase> &
keyof GuildDocumentProps keyof GuildDocumentProps
> & > &
GuildDocumentProps; GuildDocumentProps;
const guildMemberSchema = new Schema<IGuildMemberDatabase>({
accountId: Types.ObjectId,
guildId: Types.ObjectId,
status: { type: Number, required: true },
rank: { type: Number, default: 7 }
});
guildMemberSchema.index({ accountId: 1, guildId: 1 }, { unique: true });
export const GuildMember = model<IGuildMemberDatabase>("GuildMember", guildMemberSchema);

View File

@ -32,6 +32,10 @@ export interface IMessage {
transmission?: string; transmission?: string;
arg?: Arg[]; arg?: Arg[];
r?: boolean; r?: boolean;
contextInfo?: string;
acceptAction?: string;
declineAction?: string;
hasAccountAction?: boolean;
} }
export interface Arg { export interface Arg {
@ -100,7 +104,11 @@ const messageSchema = new Schema<IMessageDatabase>(
} }
], ],
default: undefined default: undefined
} },
contextInfo: String,
acceptAction: String,
declineAction: String,
hasAccountAction: Boolean
}, },
{ timestamps: { createdAt: "date", updatedAt: false }, id: false } { timestamps: { createdAt: "date", updatedAt: false }, id: false }
); );

View File

@ -1249,7 +1249,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
Drones: [droneSchema], Drones: [droneSchema],
//Active profile ico //Active profile ico
ActiveAvatarImageType: String, ActiveAvatarImageType: { type: String, default: "/Lotus/Types/StoreItems/AvatarImages/AvatarImageDefault" },
// open location store like EidolonPlainsDiscoverable or OrbVallisCaveDiscoverable // open location store like EidolonPlainsDiscoverable or OrbVallisCaveDiscoverable
DiscoveredMarkers: [Schema.Types.Mixed], DiscoveredMarkers: [Schema.Types.Mixed],

View File

@ -4,6 +4,7 @@ import { abortDojoComponentController } from "@/src/controllers/api/abortDojoCom
import { abortDojoComponentDestructionController } from "@/src/controllers/api/abortDojoComponentDestructionController"; import { abortDojoComponentDestructionController } from "@/src/controllers/api/abortDojoComponentDestructionController";
import { activateRandomModController } from "@/src/controllers/api/activateRandomModController"; import { activateRandomModController } from "@/src/controllers/api/activateRandomModController";
import { addFriendImageController } from "@/src/controllers/api/addFriendImageController"; import { addFriendImageController } from "@/src/controllers/api/addFriendImageController";
import { addToGuildController } from "@/src/controllers/api/addToGuildController";
import { arcaneCommonController } from "@/src/controllers/api/arcaneCommonController"; import { arcaneCommonController } from "@/src/controllers/api/arcaneCommonController";
import { archonFusionController } from "@/src/controllers/api/archonFusionController"; import { archonFusionController } from "@/src/controllers/api/archonFusionController";
import { artifactsController } from "@/src/controllers/api/artifactsController"; import { artifactsController } from "@/src/controllers/api/artifactsController";
@ -14,11 +15,13 @@ import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompl
import { claimLibraryDailyTaskRewardController } from "@/src/controllers/api/claimLibraryDailyTaskRewardController"; import { claimLibraryDailyTaskRewardController } from "@/src/controllers/api/claimLibraryDailyTaskRewardController";
import { clearDialogueHistoryController } from "@/src/controllers/api/clearDialogueHistoryController"; import { clearDialogueHistoryController } from "@/src/controllers/api/clearDialogueHistoryController";
import { completeRandomModChallengeController } from "@/src/controllers/api/completeRandomModChallengeController"; import { completeRandomModChallengeController } from "@/src/controllers/api/completeRandomModChallengeController";
import { confirmGuildInvitationController } from "@/src/controllers/api/confirmGuildInvitationController";
import { contributeGuildClassController } from "@/src/controllers/api/contributeGuildClassController"; import { contributeGuildClassController } from "@/src/controllers/api/contributeGuildClassController";
import { contributeToDojoComponentController } from "@/src/controllers/api/contributeToDojoComponentController"; import { contributeToDojoComponentController } from "@/src/controllers/api/contributeToDojoComponentController";
import { contributeToVaultController } from "@/src/controllers/api/contributeToVaultController"; import { contributeToVaultController } from "@/src/controllers/api/contributeToVaultController";
import { createGuildController } from "@/src/controllers/api/createGuildController"; import { createGuildController } from "@/src/controllers/api/createGuildController";
import { creditsController } from "@/src/controllers/api/creditsController"; import { creditsController } from "@/src/controllers/api/creditsController";
import { declineGuildInviteController } from "@/src/controllers/api/declineGuildInviteController";
import { deleteSessionController } from "@/src/controllers/api/deleteSessionController"; import { deleteSessionController } from "@/src/controllers/api/deleteSessionController";
import { destroyDojoDecoController } from "@/src/controllers/api/destroyDojoDecoController"; import { destroyDojoDecoController } from "@/src/controllers/api/destroyDojoDecoController";
import { dojoComponentRushController } from "@/src/controllers/api/dojoComponentRushController"; import { dojoComponentRushController } from "@/src/controllers/api/dojoComponentRushController";
@ -69,6 +72,7 @@ import { playerSkillsController } from "@/src/controllers/api/playerSkillsContro
import { projectionManagerController } from "@/src/controllers/api/projectionManagerController"; import { projectionManagerController } from "@/src/controllers/api/projectionManagerController";
import { purchaseController } from "@/src/controllers/api/purchaseController"; import { purchaseController } from "@/src/controllers/api/purchaseController";
import { queueDojoComponentDestructionController } from "@/src/controllers/api/queueDojoComponentDestructionController"; import { queueDojoComponentDestructionController } from "@/src/controllers/api/queueDojoComponentDestructionController";
import { removeFromGuildController } from "@/src/controllers/api/removeFromGuildController";
import { rerollRandomModController } from "@/src/controllers/api/rerollRandomModController"; import { rerollRandomModController } from "@/src/controllers/api/rerollRandomModController";
import { saveDialogueController } from "@/src/controllers/api/saveDialogueController"; import { saveDialogueController } from "@/src/controllers/api/saveDialogueController";
import { saveLoadoutController } from "@/src/controllers/api/saveLoadout"; import { saveLoadoutController } from "@/src/controllers/api/saveLoadout";
@ -113,7 +117,9 @@ apiRouter.get("/abandonLibraryDailyTask.php", abandonLibraryDailyTaskController)
apiRouter.get("/abortDojoComponentDestruction.php", abortDojoComponentDestructionController); apiRouter.get("/abortDojoComponentDestruction.php", abortDojoComponentDestructionController);
apiRouter.get("/checkDailyMissionBonus.php", checkDailyMissionBonusController); apiRouter.get("/checkDailyMissionBonus.php", checkDailyMissionBonusController);
apiRouter.get("/claimLibraryDailyTaskReward.php", claimLibraryDailyTaskRewardController); apiRouter.get("/claimLibraryDailyTaskReward.php", claimLibraryDailyTaskRewardController);
apiRouter.get("/confirmGuildInvitation.php", confirmGuildInvitationController);
apiRouter.get("/credits.php", creditsController); apiRouter.get("/credits.php", creditsController);
apiRouter.get("/declineGuildInvite.php", declineGuildInviteController);
apiRouter.get("/deleteSession.php", deleteSessionController); apiRouter.get("/deleteSession.php", deleteSessionController);
apiRouter.get("/dojo", dojoController); apiRouter.get("/dojo", dojoController);
apiRouter.get("/drones.php", dronesController); apiRouter.get("/drones.php", dronesController);
@ -150,6 +156,7 @@ apiRouter.get("/updateSession.php", updateSessionGetController);
apiRouter.post("/abortDojoComponent.php", abortDojoComponentController); apiRouter.post("/abortDojoComponent.php", abortDojoComponentController);
apiRouter.post("/activateRandomMod.php", activateRandomModController); apiRouter.post("/activateRandomMod.php", activateRandomModController);
apiRouter.post("/addFriendImage.php", addFriendImageController); apiRouter.post("/addFriendImage.php", addFriendImageController);
apiRouter.post("/addToGuild.php", addToGuildController);
apiRouter.post("/arcaneCommon.php", arcaneCommonController); apiRouter.post("/arcaneCommon.php", arcaneCommonController);
apiRouter.post("/archonFusion.php", archonFusionController); apiRouter.post("/archonFusion.php", archonFusionController);
apiRouter.post("/artifacts.php", artifactsController); apiRouter.post("/artifacts.php", artifactsController);
@ -193,6 +200,7 @@ apiRouter.post("/placeDecoInComponent.php", placeDecoInComponentController);
apiRouter.post("/playerSkills.php", playerSkillsController); apiRouter.post("/playerSkills.php", playerSkillsController);
apiRouter.post("/projectionManager.php", projectionManagerController); apiRouter.post("/projectionManager.php", projectionManagerController);
apiRouter.post("/purchase.php", purchaseController); apiRouter.post("/purchase.php", purchaseController);
apiRouter.post("/removeFromGuild.php", removeFromGuildController);
apiRouter.post("/rerollRandomMod.php", rerollRandomModController); apiRouter.post("/rerollRandomMod.php", rerollRandomModController);
apiRouter.post("/saveDialogue.php", saveDialogueController); apiRouter.post("/saveDialogue.php", saveDialogueController);
apiRouter.post("/saveLoadout.php", saveLoadoutController); apiRouter.post("/saveLoadout.php", saveLoadoutController);

View File

@ -1,19 +1,23 @@
import { Request } from "express"; import { Request } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { getInventory } from "@/src/services/inventoryService"; import { addRecipes, getInventory } from "@/src/services/inventoryService";
import { Guild, TGuildDatabaseDocument } from "@/src/models/guildModel"; import { Guild, GuildMember, TGuildDatabaseDocument } from "@/src/models/guildModel";
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel"; import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import { import {
IDojoClient, IDojoClient,
IDojoComponentClient, IDojoComponentClient,
IDojoContributable, IDojoContributable,
IDojoDecoClient, IDojoDecoClient,
IGuildClient,
IGuildMemberClient,
IGuildVault IGuildVault
} from "@/src/types/guildTypes"; } from "@/src/types/guildTypes";
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers"; import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
import { Types } from "mongoose"; import { Types } from "mongoose";
import { ExportDojoRecipes, IDojoBuild } from "warframe-public-export-plus"; import { ExportDojoRecipes, IDojoBuild } from "warframe-public-export-plus";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { config } from "./configService";
import { Account } from "../models/loginModel";
export const getGuildForRequest = async (req: Request): Promise<TGuildDatabaseDocument> => { export const getGuildForRequest = async (req: Request): Promise<TGuildDatabaseDocument> => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
@ -36,6 +40,99 @@ export const getGuildForRequestEx = async (
return guild; return guild;
}; };
export const getGuildClient = async (guild: TGuildDatabaseDocument, accountId: string): Promise<IGuildClient> => {
const guildMembers = await GuildMember.find({ guildId: guild._id });
const members: IGuildMemberClient[] = [];
let missingEntry = true;
for (const guildMember of guildMembers) {
const member: IGuildMemberClient = {
_id: toOid(guildMember.accountId),
Rank: guildMember.rank,
Status: guildMember.status
};
if (guildMember.accountId.equals(accountId)) {
missingEntry = false;
} else {
member.DisplayName = (await Account.findOne(
{
_id: guildMember.accountId
},
"DisplayName"
))!.DisplayName;
await fillInInventoryDataForGuildMember(member);
}
members.push(member);
}
if (missingEntry) {
// Handle clans created prior to creation of the GuildMember model.
await GuildMember.insertOne({
accountId: accountId,
guildId: guild._id,
status: 0,
rank: 0
});
members.push({
_id: { $oid: accountId },
Status: 0,
Rank: 0
});
}
return {
_id: toOid(guild._id),
Name: guild.Name,
MOTD: guild.MOTD,
LongMOTD: guild.LongMOTD,
Members: members,
Ranks: [
{
Name: "/Lotus/Language/Game/Rank_Creator",
Permissions: 16351
},
{
Name: "/Lotus/Language/Game/Rank_Warlord",
Permissions: 14303
},
{
Name: "/Lotus/Language/Game/Rank_General",
Permissions: 4318
},
{
Name: "/Lotus/Language/Game/Rank_Officer",
Permissions: 4314
},
{
Name: "/Lotus/Language/Game/Rank_Leader",
Permissions: 4106
},
{
Name: "/Lotus/Language/Game/Rank_Sage",
Permissions: 4304
},
{
Name: "/Lotus/Language/Game/Rank_Soldier",
Permissions: 4098
},
{
Name: "/Lotus/Language/Game/Rank_Initiate",
Permissions: 4096
},
{
Name: "/Lotus/Language/Game/Rank_Utility",
Permissions: 4096
}
],
Tier: 1,
Vault: getGuildVault(guild),
Class: guild.Class,
XP: guild.XP,
IsContributor: !!guild.CeremonyContributors?.find(x => x.equals(accountId)),
NumContributors: guild.CeremonyContributors?.length ?? 0,
CeremonyResetDate: guild.CeremonyResetDate ? toMongoDate(guild.CeremonyResetDate) : undefined
};
};
export const getGuildVault = (guild: TGuildDatabaseDocument): IGuildVault => { export const getGuildVault = (guild: TGuildDatabaseDocument): IGuildVault => {
return { return {
DojoRefundRegularCredits: guild.VaultRegularCredits, DojoRefundRegularCredits: guild.VaultRegularCredits,
@ -192,3 +289,29 @@ export const processDojoBuildMaterialsGathered = (guild: TGuildDatabaseDocument,
} }
} }
}; };
export const fillInInventoryDataForGuildMember = async (member: IGuildMemberClient): Promise<void> => {
const inventory = await getInventory(member._id.$oid, "PlayerLevel ActiveAvatarImageType");
member.PlayerLevel = config.spoofMasteryRank == -1 ? inventory.PlayerLevel : config.spoofMasteryRank;
member.ActiveAvatarImageType = inventory.ActiveAvatarImageType;
};
export const updateInventoryForConfirmedGuildJoin = async (
accountId: string,
guildId: Types.ObjectId
): Promise<void> => {
const inventory = await getInventory(accountId);
// Set GuildId
inventory.GuildId = guildId;
// Give clan key blueprint
addRecipes(inventory, [
{
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
ItemCount: 1
}
]);
await inventory.save();
};

View File

@ -2,6 +2,25 @@ import { Types } from "mongoose";
import { IOid, IMongoDate } from "@/src/types/commonTypes"; import { IOid, IMongoDate } from "@/src/types/commonTypes";
import { IFusionTreasure, IMiscItem, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes"; import { IFusionTreasure, IMiscItem, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
export interface IGuildClient {
_id: IOid;
Name: string;
MOTD: string;
LongMOTD?: ILongMOTD;
Members: IGuildMemberClient[];
Ranks: {
Name: string;
Permissions: number;
}[];
Tier: number;
Vault: IGuildVault;
Class: number;
XP: number;
IsContributor: boolean;
NumContributors: number;
CeremonyResetDate?: IMongoDate;
}
export interface IGuildDatabase { export interface IGuildDatabase {
_id: Types.ObjectId; _id: Types.ObjectId;
Name: string; Name: string;
@ -35,6 +54,22 @@ export interface ILongMOTD {
//authorGuildName: ""; //authorGuildName: "";
} }
export interface IGuildMemberDatabase {
accountId: Types.ObjectId;
guildId: Types.ObjectId;
status: number;
rank: number;
}
export interface IGuildMemberClient {
_id: IOid;
Status: number;
Rank: number;
DisplayName?: string;
ActiveAvatarImageType?: string;
PlayerLevel?: number;
}
export interface IGuildVault { export interface IGuildVault {
DojoRefundRegularCredits?: number; DojoRefundRegularCredits?: number;
DojoRefundMiscItems?: IMiscItem[]; DojoRefundMiscItems?: IMiscItem[];