feat: gifting
All checks were successful
Build / build (18) (push) Successful in 48s
Build / build (20) (push) Successful in 1m11s
Build / build (18) (pull_request) Successful in 48s
Build / build (20) (pull_request) Successful in 1m12s
Build / build (22) (pull_request) Successful in 1m7s
Build / build (22) (push) Successful in 40s

This commit is contained in:
Sainan 2025-03-27 12:47:42 +01:00
parent 2516af9acc
commit 213ff8ed83
5 changed files with 128 additions and 6 deletions

View 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");
// 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;
}

View File

@ -8,9 +8,10 @@ import {
getMessage
} from "@/src/services/inboxService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { addItems, getInventory } from "@/src/services/inventoryService";
import { addItems, combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
import { logger } from "@/src/utils/logger";
import { 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;
@ -29,12 +30,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 +55,21 @@ export const inboxController: RequestHandler = async (req, res) => {
if (attachmentCountedItems) {
await addItems(inventory, attachmentCountedItems, inventoryChanges);
}
if (message.gifts) {
for (const gift of message.gifts) {
combineInventoryChanges(
inventoryChanges,
(
await handleStoreItemAcquisition(
gift.GiftType,
inventory,
message.arg!.find(x => x.Key == "GIFT_QUANTITY")!.Tag as number
)
).InventoryChanges
);
}
}
await inventory.save();
await message.save();
res.json({ InventoryChanges: inventoryChanges });
} else if (latestClientMessageId) {
await createNewEventMessages(req);

View File

@ -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();

View File

@ -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: {

View File

@ -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);