feat: gifting (#1344)
Reviewed-on: OpenWF/SpaceNinjaServer#1344 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
							
								
									a56ff89bb9
								
							
						
					
					
						commit
						36d2b2dda5
					
				
							
								
								
									
										92
									
								
								src/controllers/api/giftingController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/controllers/api/giftingController.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,92 @@
 | 
			
		||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
			
		||||
import { Account } from "@/src/models/loginModel";
 | 
			
		||||
import { createMessage } from "@/src/services/inboxService";
 | 
			
		||||
import { getInventory, updateCurrency } from "@/src/services/inventoryService";
 | 
			
		||||
import { getAccountForRequest, getSuffixedName } from "@/src/services/loginService";
 | 
			
		||||
import { IOid } from "@/src/types/commonTypes";
 | 
			
		||||
import { IPurchaseParams } from "@/src/types/purchaseTypes";
 | 
			
		||||
import { RequestHandler } from "express";
 | 
			
		||||
import { ExportFlavour } from "warframe-public-export-plus";
 | 
			
		||||
 | 
			
		||||
export const giftingController: RequestHandler = async (req, res) => {
 | 
			
		||||
    const data = getJSONfromString<IGiftingRequest>(String(req.body));
 | 
			
		||||
    if (data.PurchaseParams.Source != 0 || !data.PurchaseParams.UsePremium) {
 | 
			
		||||
        throw new Error(`unexpected purchase params in gifting request: ${String(req.body)}`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const account = await Account.findOne(
 | 
			
		||||
        data.RecipientId ? { _id: data.RecipientId.$oid } : { DisplayName: data.Recipient }
 | 
			
		||||
    );
 | 
			
		||||
    if (!account) {
 | 
			
		||||
        res.status(400).send("9").end();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    const inventory = await getInventory(account._id.toString(), "Suits Settings");
 | 
			
		||||
 | 
			
		||||
    // Cannot gift items to players that have not completed the tutorial.
 | 
			
		||||
    if (inventory.Suits.length == 0) {
 | 
			
		||||
        res.status(400).send("14").end();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Cannot gift to players who have gifting disabled.
 | 
			
		||||
    // TODO: Also consider GIFT_MODE_FRIENDS once friends are implemented
 | 
			
		||||
    if (inventory.Settings?.GiftMode == "GIFT_MODE_NONE") {
 | 
			
		||||
        res.status(400).send("17").end();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO: Cannot gift items with mastery requirement to players who are too low level. (Code 2)
 | 
			
		||||
    // TODO: Cannot gift archwing items to players that have not completed the archwing quest. (Code 7)
 | 
			
		||||
    // TODO: Cannot gift necramechs to players that have not completed heart of deimos. (Code 20)
 | 
			
		||||
 | 
			
		||||
    const senderAccount = await getAccountForRequest(req);
 | 
			
		||||
    const senderInventory = await getInventory(
 | 
			
		||||
        senderAccount._id.toString(),
 | 
			
		||||
        "PremiumCredits PremiumCreditsFree ActiveAvatarImageType GiftsRemaining"
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (senderInventory.GiftsRemaining == 0) {
 | 
			
		||||
        res.status(400).send("10").end();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    senderInventory.GiftsRemaining -= 1;
 | 
			
		||||
 | 
			
		||||
    updateCurrency(senderInventory, data.PurchaseParams.ExpectedPrice, true);
 | 
			
		||||
    await senderInventory.save();
 | 
			
		||||
 | 
			
		||||
    const senderName = getSuffixedName(senderAccount);
 | 
			
		||||
    await createMessage(account._id.toString(), [
 | 
			
		||||
        {
 | 
			
		||||
            sndr: senderName,
 | 
			
		||||
            msg: data.Message || "/Lotus/Language/Menu/GiftReceivedBody_NoCustomMessage",
 | 
			
		||||
            arg: [
 | 
			
		||||
                {
 | 
			
		||||
                    Key: "GIFTER_NAME",
 | 
			
		||||
                    Tag: senderName
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    Key: "GIFT_QUANTITY",
 | 
			
		||||
                    Tag: data.PurchaseParams.Quantity
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            sub: "/Lotus/Language/Menu/GiftReceivedSubject",
 | 
			
		||||
            icon: ExportFlavour[senderInventory.ActiveAvatarImageType].icon,
 | 
			
		||||
            gifts: [
 | 
			
		||||
                {
 | 
			
		||||
                    GiftType: data.PurchaseParams.StoreItem
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    res.end();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
interface IGiftingRequest {
 | 
			
		||||
    PurchaseParams: IPurchaseParams;
 | 
			
		||||
    Message?: string;
 | 
			
		||||
    Recipient?: string;
 | 
			
		||||
    RecipientId?: IOid;
 | 
			
		||||
    buildLabel: string;
 | 
			
		||||
}
 | 
			
		||||
@ -1,21 +1,24 @@
 | 
			
		||||
import { RequestHandler } from "express";
 | 
			
		||||
import { Inbox } from "@/src/models/inboxModel";
 | 
			
		||||
import {
 | 
			
		||||
    createMessage,
 | 
			
		||||
    createNewEventMessages,
 | 
			
		||||
    deleteAllMessagesRead,
 | 
			
		||||
    deleteMessageRead,
 | 
			
		||||
    getAllMessagesSorted,
 | 
			
		||||
    getMessage
 | 
			
		||||
} from "@/src/services/inboxService";
 | 
			
		||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
			
		||||
import { addItems, getInventory } from "@/src/services/inventoryService";
 | 
			
		||||
import { getAccountForRequest, getAccountFromSuffixedName, getSuffixedName } from "@/src/services/loginService";
 | 
			
		||||
import { addItems, combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
 | 
			
		||||
import { logger } from "@/src/utils/logger";
 | 
			
		||||
import { ExportGear } from "warframe-public-export-plus";
 | 
			
		||||
import { ExportFlavour, ExportGear } from "warframe-public-export-plus";
 | 
			
		||||
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
 | 
			
		||||
 | 
			
		||||
export const inboxController: RequestHandler = async (req, res) => {
 | 
			
		||||
    const { deleteId, lastMessage: latestClientMessageId, messageId } = req.query;
 | 
			
		||||
 | 
			
		||||
    const accountId = await getAccountIdForRequest(req);
 | 
			
		||||
    const account = await getAccountForRequest(req);
 | 
			
		||||
    const accountId = account._id.toString();
 | 
			
		||||
 | 
			
		||||
    if (deleteId) {
 | 
			
		||||
        if (deleteId === "DeleteAllRead") {
 | 
			
		||||
@ -29,12 +32,12 @@ export const inboxController: RequestHandler = async (req, res) => {
 | 
			
		||||
    } else if (messageId) {
 | 
			
		||||
        const message = await getMessage(messageId as string);
 | 
			
		||||
        message.r = true;
 | 
			
		||||
        await message.save();
 | 
			
		||||
 | 
			
		||||
        const attachmentItems = message.att;
 | 
			
		||||
        const attachmentCountedItems = message.countedAtt;
 | 
			
		||||
 | 
			
		||||
        if (!attachmentItems && !attachmentCountedItems) {
 | 
			
		||||
            await message.save();
 | 
			
		||||
 | 
			
		||||
        if (!attachmentItems && !attachmentCountedItems && !message.gifts) {
 | 
			
		||||
            res.status(200).end();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@ -54,9 +57,43 @@ export const inboxController: RequestHandler = async (req, res) => {
 | 
			
		||||
        if (attachmentCountedItems) {
 | 
			
		||||
            await addItems(inventory, attachmentCountedItems, inventoryChanges);
 | 
			
		||||
        }
 | 
			
		||||
        if (message.gifts) {
 | 
			
		||||
            const sender = await getAccountFromSuffixedName(message.sndr);
 | 
			
		||||
            const recipientName = getSuffixedName(account);
 | 
			
		||||
            const giftQuantity = message.arg!.find(x => x.Key == "GIFT_QUANTITY")!.Tag as number;
 | 
			
		||||
            for (const gift of message.gifts) {
 | 
			
		||||
                combineInventoryChanges(
 | 
			
		||||
                    inventoryChanges,
 | 
			
		||||
                    (await handleStoreItemAcquisition(gift.GiftType, inventory, giftQuantity)).InventoryChanges
 | 
			
		||||
                );
 | 
			
		||||
                if (sender) {
 | 
			
		||||
                    await createMessage(sender._id.toString(), [
 | 
			
		||||
                        {
 | 
			
		||||
                            sndr: recipientName,
 | 
			
		||||
                            msg: "/Lotus/Language/Menu/GiftReceivedConfirmationBody",
 | 
			
		||||
                            arg: [
 | 
			
		||||
                                {
 | 
			
		||||
                                    Key: "RECIPIENT_NAME",
 | 
			
		||||
                                    Tag: recipientName
 | 
			
		||||
                                },
 | 
			
		||||
                                {
 | 
			
		||||
                                    Key: "GIFT_TYPE",
 | 
			
		||||
                                    Tag: gift.GiftType
 | 
			
		||||
                                },
 | 
			
		||||
                                {
 | 
			
		||||
                                    Key: "GIFT_QUANTITY",
 | 
			
		||||
                                    Tag: giftQuantity
 | 
			
		||||
                                }
 | 
			
		||||
                            ],
 | 
			
		||||
                            sub: "/Lotus/Language/Menu/GiftReceivedConfirmationSubject",
 | 
			
		||||
                            icon: ExportFlavour[inventory.ActiveAvatarImageType].icon,
 | 
			
		||||
                            highPriority: true
 | 
			
		||||
                        }
 | 
			
		||||
                    ]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        await inventory.save();
 | 
			
		||||
        await message.save();
 | 
			
		||||
 | 
			
		||||
        res.json({ InventoryChanges: inventoryChanges });
 | 
			
		||||
    } else if (latestClientMessageId) {
 | 
			
		||||
        await createNewEventMessages(req);
 | 
			
		||||
 | 
			
		||||
@ -33,6 +33,7 @@ export const inventoryController: RequestHandler = async (request, response) =>
 | 
			
		||||
            inventory[key] = 16000 + inventory.PlayerLevel * 500;
 | 
			
		||||
        }
 | 
			
		||||
        inventory.DailyFocus = 250000 + inventory.PlayerLevel * 5000;
 | 
			
		||||
        inventory.GiftsRemaining = Math.max(8, inventory.PlayerLevel);
 | 
			
		||||
 | 
			
		||||
        inventory.LibraryAvailableDailyTaskInfo = createLibraryDailyTask();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -31,6 +31,7 @@ export interface IMessage {
 | 
			
		||||
    countedAtt?: ITypeCount[];
 | 
			
		||||
    transmission?: string;
 | 
			
		||||
    arg?: Arg[];
 | 
			
		||||
    gifts?: IGift[];
 | 
			
		||||
    r?: boolean;
 | 
			
		||||
    contextInfo?: string;
 | 
			
		||||
    acceptAction?: string;
 | 
			
		||||
@ -43,6 +44,10 @@ export interface Arg {
 | 
			
		||||
    Tag: string | number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IGift {
 | 
			
		||||
    GiftType: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//types are wrong
 | 
			
		||||
// export interface IMessageDatabase {
 | 
			
		||||
//     _id: Types.ObjectId;
 | 
			
		||||
@ -80,6 +85,14 @@ export interface Arg {
 | 
			
		||||
//     cinematic: string;
 | 
			
		||||
//     requiredLevel: string;
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
const giftSchema = new Schema<IGift>(
 | 
			
		||||
    {
 | 
			
		||||
        GiftType: String
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const messageSchema = new Schema<IMessageDatabase>(
 | 
			
		||||
    {
 | 
			
		||||
        ownerId: Schema.Types.ObjectId,
 | 
			
		||||
@ -93,6 +106,7 @@ const messageSchema = new Schema<IMessageDatabase>(
 | 
			
		||||
        endDate: Date,
 | 
			
		||||
        r: Boolean,
 | 
			
		||||
        att: { type: [String], default: undefined },
 | 
			
		||||
        gifts: { type: [giftSchema], default: undefined },
 | 
			
		||||
        countedAtt: { type: [typeCountSchema], default: undefined },
 | 
			
		||||
        transmission: String,
 | 
			
		||||
        arg: {
 | 
			
		||||
 | 
			
		||||
@ -51,6 +51,7 @@ import { getNewRewardSeedController } from "@/src/controllers/api/getNewRewardSe
 | 
			
		||||
import { getShipController } from "@/src/controllers/api/getShipController";
 | 
			
		||||
import { getVendorInfoController } from "@/src/controllers/api/getVendorInfoController";
 | 
			
		||||
import { getVoidProjectionRewardsController } from "@/src/controllers/api/getVoidProjectionRewardsController";
 | 
			
		||||
import { giftingController } from "@/src/controllers/api/giftingController";
 | 
			
		||||
import { gildWeaponController } from "@/src/controllers/api/gildWeaponController";
 | 
			
		||||
import { giveKeyChainTriggeredItemsController } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
 | 
			
		||||
import { giveKeyChainTriggeredMessageController } from "@/src/controllers/api/giveKeyChainTriggeredMessageController";
 | 
			
		||||
@ -203,6 +204,7 @@ apiRouter.post("/getAlliance.php", getAllianceController);
 | 
			
		||||
apiRouter.post("/getFriends.php", getFriendsController);
 | 
			
		||||
apiRouter.post("/getGuildDojo.php", getGuildDojoController);
 | 
			
		||||
apiRouter.post("/getVoidProjectionRewards.php", getVoidProjectionRewardsController);
 | 
			
		||||
apiRouter.post("/gifting.php", giftingController);
 | 
			
		||||
apiRouter.post("/gildWeapon.php", gildWeaponController);
 | 
			
		||||
apiRouter.post("/giveKeyChainTriggeredItems.php", giveKeyChainTriggeredItemsController);
 | 
			
		||||
apiRouter.post("/giveKeyChainTriggeredMessage.php", giveKeyChainTriggeredMessageController);
 | 
			
		||||
 | 
			
		||||
@ -100,3 +100,7 @@ export const getSuffixedName = (account: TAccountDocument): string => {
 | 
			
		||||
    const suffix = ((crc32.str(name.toLowerCase() + "595") >>> 0) + platform_magics[platformId]) % 1000;
 | 
			
		||||
    return name + "#" + suffix.toString().padStart(3, "0");
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getAccountFromSuffixedName = (name: string): Promise<TAccountDocument | null> => {
 | 
			
		||||
    return Account.findOne({ DisplayName: name.split("#")[0] });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user