feat: clan applications (#1410)
Reviewed-on: #1410 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
This commit is contained in:
parent
4a3a3de300
commit
1a4ad8b7a5
@ -3,15 +3,19 @@ import { Account } from "@/src/models/loginModel";
|
||||
import { fillInInventoryDataForGuildMember, hasGuildPermission } from "@/src/services/guildService";
|
||||
import { createMessage } from "@/src/services/inboxService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountForRequest, getSuffixedName } from "@/src/services/loginService";
|
||||
import { getAccountForRequest, getAccountIdForRequest, getSuffixedName } from "@/src/services/loginService";
|
||||
import { IOid } from "@/src/types/commonTypes";
|
||||
import { GuildPermission, IGuildMemberClient } from "@/src/types/guildTypes";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
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;
|
||||
|
||||
if ("UserName" in payload) {
|
||||
// Clan recruiter sending an invite
|
||||
|
||||
const account = await Account.findOne({ DisplayName: payload.UserName });
|
||||
if (!account) {
|
||||
res.status(400).json("Username does not exist");
|
||||
@ -48,7 +52,7 @@ export const addToGuildController: RequestHandler = async (req, res) => {
|
||||
});
|
||||
|
||||
const senderInventory = await getInventory(senderAccount._id.toString(), "ActiveAvatarImageType");
|
||||
await createMessage(account._id.toString(), [
|
||||
await createMessage(account._id, [
|
||||
{
|
||||
sndr: getSuffixedName(senderAccount),
|
||||
msg: "/Lotus/Language/Menu/Mailbox_ClanInvite_Body",
|
||||
@ -76,9 +80,30 @@ export const addToGuildController: RequestHandler = async (req, res) => {
|
||||
};
|
||||
await fillInInventoryDataForGuildMember(member);
|
||||
res.json({ NewMember: member });
|
||||
} else if ("RequestMsg" in payload) {
|
||||
// Player applying to join a clan
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
try {
|
||||
await GuildMember.insertOne({
|
||||
accountId,
|
||||
guildId: payload.GuildId.$oid,
|
||||
status: 1, // incoming invite
|
||||
RequestMsg: payload.RequestMsg,
|
||||
RequestExpiry: new Date(Date.now() + 14 * 86400 * 1000) // TOVERIFY: I can't find any good information about this with regards to live, but 2 weeks seem reasonable.
|
||||
});
|
||||
} catch (e) {
|
||||
// Assuming this is "E11000 duplicate key error" due to the guildId-accountId unique index.
|
||||
res.status(400).send("Already requested");
|
||||
}
|
||||
res.end();
|
||||
} else {
|
||||
logger.error(`data provided to ${req.path}: ${String(req.body)}`);
|
||||
res.status(400).end();
|
||||
}
|
||||
};
|
||||
|
||||
interface IAddToGuildRequest {
|
||||
UserName: string;
|
||||
UserName?: string;
|
||||
GuildId: IOid;
|
||||
RequestMsg?: string;
|
||||
}
|
||||
|
@ -1,18 +1,76 @@
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { Guild, GuildMember } from "@/src/models/guildModel";
|
||||
import { deleteGuild, getGuildClient, removeDojoKeyItems } from "@/src/services/guildService";
|
||||
import { Account } from "@/src/models/loginModel";
|
||||
import { deleteGuild, getGuildClient, hasGuildPermission, removeDojoKeyItems } from "@/src/services/guildService";
|
||||
import { addRecipes, combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountForRequest, getSuffixedName } from "@/src/services/loginService";
|
||||
import { getAccountForRequest, getAccountIdForRequest, getSuffixedName } from "@/src/services/loginService";
|
||||
import { GuildPermission } from "@/src/types/guildTypes";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { RequestHandler } from "express";
|
||||
import { Types } from "mongoose";
|
||||
|
||||
export const confirmGuildInvitationController: RequestHandler = async (req, res) => {
|
||||
if (req.body) {
|
||||
// POST request: Clan representative accepting invite(s).
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const guild = (await Guild.findById(req.query.clanId as string, "Ranks RosterActivity"))!;
|
||||
if (!(await hasGuildPermission(guild, accountId, GuildPermission.Recruiter))) {
|
||||
res.status(400).json("Invalid permission");
|
||||
return;
|
||||
}
|
||||
const payload = getJSONfromString<{ userId: string }>(String(req.body));
|
||||
const filter: { accountId?: string; status: number } = { status: 1 };
|
||||
if (payload.userId != "all") {
|
||||
filter.accountId = payload.userId;
|
||||
}
|
||||
const guildMembers = await GuildMember.find(filter);
|
||||
const newMembers: string[] = [];
|
||||
for (const guildMember of guildMembers) {
|
||||
guildMember.status = 0;
|
||||
guildMember.RequestMsg = undefined;
|
||||
guildMember.RequestExpiry = undefined;
|
||||
await guildMember.save();
|
||||
|
||||
// Remove other pending applications for this account
|
||||
await GuildMember.deleteMany({ accountId: guildMember.accountId, status: 1 });
|
||||
|
||||
// Update inventory of new member
|
||||
const inventory = await getInventory(guildMember.accountId.toString(), "GuildId Recipes");
|
||||
inventory.GuildId = new Types.ObjectId(req.query.clanId as string);
|
||||
addRecipes(inventory, [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
|
||||
ItemCount: 1
|
||||
}
|
||||
]);
|
||||
await inventory.save();
|
||||
|
||||
// Add join to clan log
|
||||
const account = (await Account.findOne({ _id: guildMember.accountId }))!;
|
||||
guild.RosterActivity ??= [];
|
||||
guild.RosterActivity.push({
|
||||
dateTime: new Date(),
|
||||
entryType: 6,
|
||||
details: getSuffixedName(account)
|
||||
});
|
||||
|
||||
newMembers.push(account._id.toString());
|
||||
}
|
||||
await guild.save();
|
||||
res.json({
|
||||
NewMembers: newMembers
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// GET request: A player accepting an invite they got in their inbox.
|
||||
|
||||
const account = await getAccountForRequest(req);
|
||||
const invitedGuildMember = await GuildMember.findOne({
|
||||
accountId: account._id,
|
||||
guildId: req.query.clanId as string
|
||||
});
|
||||
if (invitedGuildMember) {
|
||||
if (invitedGuildMember && invitedGuildMember.status == 2) {
|
||||
let inventoryChanges: IInventoryChanges = {};
|
||||
|
||||
// If this account is already in a guild, we need to do cleanup first.
|
||||
@ -31,6 +89,10 @@ export const confirmGuildInvitationController: RequestHandler = async (req, res)
|
||||
invitedGuildMember.status = 0;
|
||||
await invitedGuildMember.save();
|
||||
|
||||
// Remove pending applications for this account
|
||||
await GuildMember.deleteMany({ accountId: account._id, status: 1 });
|
||||
|
||||
// Update inventory of new member
|
||||
const inventory = await getInventory(account._id.toString(), "GuildId LevelKeys Recipes");
|
||||
inventory.GuildId = new Types.ObjectId(req.query.clanId as string);
|
||||
const recipeChanges = [
|
||||
@ -45,6 +107,7 @@ export const confirmGuildInvitationController: RequestHandler = async (req, res)
|
||||
|
||||
const guild = (await Guild.findById(req.query.clanId as string))!;
|
||||
|
||||
// Add join to clan log
|
||||
guild.RosterActivity ??= [];
|
||||
guild.RosterActivity.push({
|
||||
dateTime: new Date(),
|
||||
|
@ -9,6 +9,9 @@ export const createGuildController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const payload = getJSONfromString<ICreateGuildRequest>(String(req.body));
|
||||
|
||||
// Remove pending applications for this account
|
||||
await GuildMember.deleteMany({ accountId, status: 1 });
|
||||
|
||||
// Create guild on database
|
||||
const guild = new Guild({
|
||||
Name: await createUniqueClanName(payload.guildName)
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { GuildMember } from "@/src/models/guildModel";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IGuildMemberClient } from "@/src/types/guildTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const getGuildContributionsController: RequestHandler = async (req, res) => {
|
||||
@ -8,11 +9,11 @@ export const getGuildContributionsController: RequestHandler = async (req, res)
|
||||
const guildId = (await getInventory(accountId, "GuildId")).GuildId;
|
||||
const guildMember = (await GuildMember.findOne({ guildId, accountId: req.query.buddyId }))!;
|
||||
res.json({
|
||||
_id: { $oid: req.query.buddyId },
|
||||
_id: { $oid: req.query.buddyId as string },
|
||||
RegularCreditsContributed: guildMember.RegularCreditsContributed,
|
||||
PremiumCreditsContributed: guildMember.PremiumCreditsContributed,
|
||||
MiscItemsContributed: guildMember.MiscItemsContributed,
|
||||
ConsumablesContributed: [], // ???
|
||||
ShipDecorationsContributed: guildMember.ShipDecorationsContributed
|
||||
});
|
||||
} satisfies Partial<IGuildMemberClient>);
|
||||
};
|
||||
|
@ -2,6 +2,7 @@ import { GuildMember } from "@/src/models/guildModel";
|
||||
import { Inbox } from "@/src/models/inboxModel";
|
||||
import { Account } from "@/src/models/loginModel";
|
||||
import { deleteGuild, getGuildForRequest, hasGuildPermission, removeDojoKeyItems } from "@/src/services/guildService";
|
||||
import { createMessage } from "@/src/services/inboxService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountForRequest, getSuffixedName } from "@/src/services/loginService";
|
||||
import { GuildPermission } from "@/src/types/guildTypes";
|
||||
@ -26,6 +27,26 @@ export const removeFromGuildController: RequestHandler = async (req, res) => {
|
||||
inventory.GuildId = undefined;
|
||||
removeDojoKeyItems(inventory);
|
||||
await inventory.save();
|
||||
} else if (guildMember.status == 1) {
|
||||
// TOVERIFY: Is this inbox message actually sent on live?
|
||||
await createMessage(guildMember.accountId, [
|
||||
{
|
||||
sndr: "/Lotus/Language/Bosses/Ordis",
|
||||
msg: "/Lotus/Language/Clan/RejectedFromClan",
|
||||
sub: "/Lotus/Language/Clan/RejectedFromClanHeader",
|
||||
arg: [
|
||||
{
|
||||
Key: "PLAYER_NAME",
|
||||
Tag: (await Account.findOne({ _id: guildMember.accountId }, "DisplayName"))!.DisplayName
|
||||
},
|
||||
{
|
||||
Key: "CLAN_NAME",
|
||||
Tag: guild.Name
|
||||
}
|
||||
]
|
||||
// TOVERIFY: If this message is sent on live, is it highPriority?
|
||||
}
|
||||
]);
|
||||
} else if (guildMember.status == 2) {
|
||||
// Delete the inbox message for the invite
|
||||
await Inbox.deleteOne({
|
||||
|
@ -218,6 +218,8 @@ const guildMemberSchema = new Schema<IGuildMemberDatabase>({
|
||||
guildId: Types.ObjectId,
|
||||
status: { type: Number, required: true },
|
||||
rank: { type: Number, default: 7 },
|
||||
RequestMsg: String,
|
||||
RequestExpiry: Date,
|
||||
RegularCreditsContributed: Number,
|
||||
PremiumCreditsContributed: Number,
|
||||
MiscItemsContributed: { type: [typeCountSchema], default: undefined },
|
||||
@ -225,6 +227,7 @@ const guildMemberSchema = new Schema<IGuildMemberDatabase>({
|
||||
});
|
||||
|
||||
guildMemberSchema.index({ accountId: 1, guildId: 1 }, { unique: true });
|
||||
guildMemberSchema.index({ RequestExpiry: 1 }, { expireAfterSeconds: 0 });
|
||||
|
||||
export const GuildMember = model<IGuildMemberDatabase>("GuildMember", guildMemberSchema);
|
||||
|
||||
|
@ -188,6 +188,7 @@ apiRouter.post("/claimCompletedRecipe.php", claimCompletedRecipeController);
|
||||
apiRouter.post("/clearDialogueHistory.php", clearDialogueHistoryController);
|
||||
apiRouter.post("/clearNewEpisodeReward.php", clearNewEpisodeRewardController);
|
||||
apiRouter.post("/completeRandomModChallenge.php", completeRandomModChallengeController);
|
||||
apiRouter.post("/confirmGuildInvitation.php", confirmGuildInvitationController);
|
||||
apiRouter.post("/contributeGuildClass.php", contributeGuildClassController);
|
||||
apiRouter.post("/contributeToDojoComponent.php", contributeToDojoComponentController);
|
||||
apiRouter.post("/contributeToVault.php", contributeToVaultController);
|
||||
|
@ -57,7 +57,9 @@ export const getGuildClient = async (guild: TGuildDatabaseDocument, accountId: s
|
||||
const member: IGuildMemberClient = {
|
||||
_id: toOid(guildMember.accountId),
|
||||
Rank: guildMember.rank,
|
||||
Status: guildMember.status
|
||||
Status: guildMember.status,
|
||||
Note: guildMember.RequestMsg,
|
||||
RequestExpiry: guildMember.RequestExpiry ? toMongoDate(guildMember.RequestExpiry) : undefined
|
||||
};
|
||||
if (guildMember.accountId.equals(accountId)) {
|
||||
missingEntry = false;
|
||||
|
@ -89,19 +89,39 @@ export interface IGuildMemberDatabase {
|
||||
guildId: Types.ObjectId;
|
||||
status: number;
|
||||
rank: number;
|
||||
RequestMsg?: string;
|
||||
RequestExpiry?: Date;
|
||||
RegularCreditsContributed?: number;
|
||||
PremiumCreditsContributed?: number;
|
||||
MiscItemsContributed?: IMiscItem[];
|
||||
ShipDecorationsContributed?: ITypeCount[];
|
||||
}
|
||||
|
||||
export interface IGuildMemberClient {
|
||||
interface IFriendInfo {
|
||||
_id: IOid;
|
||||
Status: number;
|
||||
Rank: number;
|
||||
DisplayName?: string;
|
||||
PlatformNames?: string[];
|
||||
PlatformAccountId?: string;
|
||||
Status: number;
|
||||
ActiveAvatarImageType?: string;
|
||||
LastLogin?: IMongoDate;
|
||||
PlayerLevel?: number;
|
||||
Suffix?: number;
|
||||
Note?: string;
|
||||
Favorite?: boolean;
|
||||
NewRequest?: boolean;
|
||||
}
|
||||
|
||||
// GuildMemberInfo
|
||||
export interface IGuildMemberClient extends IFriendInfo {
|
||||
Rank: number;
|
||||
Joined?: IMongoDate;
|
||||
RequestExpiry?: IMongoDate;
|
||||
RegularCreditsContributed?: number;
|
||||
PremiumCreditsContributed?: number;
|
||||
MiscItemsContributed?: IMiscItem[];
|
||||
ConsumablesContributed?: ITypeCount[];
|
||||
ShipDecorationsContributed?: ITypeCount[];
|
||||
}
|
||||
|
||||
export interface IGuildVault {
|
||||
|
Loading…
x
Reference in New Issue
Block a user