XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

SpaceNinjaServer

A simple server for a small space ninja game

公开
关注 0 Fork 1 Star 0
返回提交历史

XFEstudio/SpaceNinjaServer

feat: gifting (#1344)

Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1344 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>

36d2b2dd
Sainan <63328889+Sainan@users.noreply.github.com>
提交于

代码差异

6 个文件 +159 -9
Added src/controllers/api/giftingController.ts +92 -0
@@ -0,0 +1,92 @@
1 import { getJSONfromString } from "@/src/helpers/stringHelpers";
2 import { Account } from "@/src/models/loginModel";
3 import { createMessage } from "@/src/services/inboxService";
4 import { getInventory, updateCurrency } from "@/src/services/inventoryService";
5 import { getAccountForRequest, getSuffixedName } from "@/src/services/loginService";
6 import { IOid } from "@/src/types/commonTypes";
7 import { IPurchaseParams } from "@/src/types/purchaseTypes";
8 import { RequestHandler } from "express";
9 import { ExportFlavour } from "warframe-public-export-plus";
10
11 export const giftingController: RequestHandler = async (req, res) => {
12 const data = getJSONfromString<IGiftingRequest>(String(req.body));
13 if (data.PurchaseParams.Source != 0 || !data.PurchaseParams.UsePremium) {
14 throw new Error(`unexpected purchase params in gifting request: ${String(req.body)}`);
15 }
16
17 const account = await Account.findOne(
18 data.RecipientId ? { _id: data.RecipientId.$oid } : { DisplayName: data.Recipient }
19 );
20 if (!account) {
21 res.status(400).send("9").end();
22 return;
23 }
24 const inventory = await getInventory(account._id.toString(), "Suits Settings");
25
26 // Cannot gift items to players that have not completed the tutorial.
27 if (inventory.Suits.length == 0) {
28 res.status(400).send("14").end();
29 return;
30 }
31
32 // Cannot gift to players who have gifting disabled.
33 // TODO: Also consider GIFT_MODE_FRIENDS once friends are implemented
34 if (inventory.Settings?.GiftMode == "GIFT_MODE_NONE") {
35 res.status(400).send("17").end();
36 return;
37 }
38
39 // TODO: Cannot gift items with mastery requirement to players who are too low level. (Code 2)
40 // TODO: Cannot gift archwing items to players that have not completed the archwing quest. (Code 7)
41 // TODO: Cannot gift necramechs to players that have not completed heart of deimos. (Code 20)
42
43 const senderAccount = await getAccountForRequest(req);
44 const senderInventory = await getInventory(
45 senderAccount._id.toString(),
46 "PremiumCredits PremiumCreditsFree ActiveAvatarImageType GiftsRemaining"
47 );
48
49 if (senderInventory.GiftsRemaining == 0) {
50 res.status(400).send("10").end();
51 return;
52 }
53 senderInventory.GiftsRemaining -= 1;
54
55 updateCurrency(senderInventory, data.PurchaseParams.ExpectedPrice, true);
56 await senderInventory.save();
57
58 const senderName = getSuffixedName(senderAccount);
59 await createMessage(account._id.toString(), [
60 {
61 sndr: senderName,
62 msg: data.Message || "/Lotus/Language/Menu/GiftReceivedBody_NoCustomMessage",
63 arg: [
64 {
65 Key: "GIFTER_NAME",
66 Tag: senderName
67 },
68 {
69 Key: "GIFT_QUANTITY",
70 Tag: data.PurchaseParams.Quantity
71 }
72 ],
73 sub: "/Lotus/Language/Menu/GiftReceivedSubject",
74 icon: ExportFlavour[senderInventory.ActiveAvatarImageType].icon,
75 gifts: [
76 {
77 GiftType: data.PurchaseParams.StoreItem
78 }
79 ]
80 }
81 ]);
82
83 res.end();
84 };
85
86 interface IGiftingRequest {
87 PurchaseParams: IPurchaseParams;
88 Message?: string;
89 Recipient?: string;
90 RecipientId?: IOid;
91 buildLabel: string;
92 }
Modified src/controllers/api/inboxController.ts +46 -9
@@ -1,21 +1,24 @@
1 1 import { RequestHandler } from "express";
2 2 import { Inbox } from "@/src/models/inboxModel";
3 3 import {
4 createMessage,
4 5 createNewEventMessages,
5 6 deleteAllMessagesRead,
6 7 deleteMessageRead,
7 8 getAllMessagesSorted,
8 9 getMessage
9 10 } from "@/src/services/inboxService";
10 import { getAccountIdForRequest } from "@/src/services/loginService";
11 import { addItems, getInventory } from "@/src/services/inventoryService";
11 import { getAccountForRequest, getAccountFromSuffixedName, getSuffixedName } from "@/src/services/loginService";
12 import { addItems, combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
12 13 import { logger } from "@/src/utils/logger";
13 import { ExportGear } from "warframe-public-export-plus";
14 import { ExportFlavour, ExportGear } from "warframe-public-export-plus";
15 import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
14 16
15 17 export const inboxController: RequestHandler = async (req, res) => {
16 18 const { deleteId, lastMessage: latestClientMessageId, messageId } = req.query;
17 19
18 const accountId = await getAccountIdForRequest(req);
20 const account = await getAccountForRequest(req);
21 const accountId = account._id.toString();
19 22
20 23 if (deleteId) {
21 24 if (deleteId === "DeleteAllRead") {
@@ -29,12 +32,12 @@ export const inboxController: RequestHandler = async (req, res) => {
29 32 } else if (messageId) {
30 33 const message = await getMessage(messageId as string);
31 34 message.r = true;
35 await message.save();
36
32 37 const attachmentItems = message.att;
33 38 const attachmentCountedItems = message.countedAtt;
34 39
35 if (!attachmentItems && !attachmentCountedItems) {
36 await message.save();
37
40 if (!attachmentItems && !attachmentCountedItems && !message.gifts) {
38 41 res.status(200).end();
39 42 return;
40 43 }
@@ -54,9 +57,43 @@ export const inboxController: RequestHandler = async (req, res) => {
54 57 if (attachmentCountedItems) {
55 58 await addItems(inventory, attachmentCountedItems, inventoryChanges);
56 59 }
60 if (message.gifts) {
61 const sender = await getAccountFromSuffixedName(message.sndr);
62 const recipientName = getSuffixedName(account);
63 const giftQuantity = message.arg!.find(x => x.Key == "GIFT_QUANTITY")!.Tag as number;
64 for (const gift of message.gifts) {
65 combineInventoryChanges(
66 inventoryChanges,
67 (await handleStoreItemAcquisition(gift.GiftType, inventory, giftQuantity)).InventoryChanges
68 );
69 if (sender) {
70 await createMessage(sender._id.toString(), [
71 {
72 sndr: recipientName,
73 msg: "/Lotus/Language/Menu/GiftReceivedConfirmationBody",
74 arg: [
75 {
76 Key: "RECIPIENT_NAME",
77 Tag: recipientName
78 },
79 {
80 Key: "GIFT_TYPE",
81 Tag: gift.GiftType
82 },
83 {
84 Key: "GIFT_QUANTITY",
85 Tag: giftQuantity
86 }
87 ],
88 sub: "/Lotus/Language/Menu/GiftReceivedConfirmationSubject",
89 icon: ExportFlavour[inventory.ActiveAvatarImageType].icon,
90 highPriority: true
91 }
92 ]);
93 }
94 }
95 }
57 96 await inventory.save();
58 await message.save();
59
60 97 res.json({ InventoryChanges: inventoryChanges });
61 98 } else if (latestClientMessageId) {
62 99 await createNewEventMessages(req);
Modified src/controllers/api/inventoryController.ts +1 -0
@@ -33,6 +33,7 @@ export const inventoryController: RequestHandler = async (request, response) =>
33 33 inventory[key] = 16000 + inventory.PlayerLevel * 500;
34 34 }
35 35 inventory.DailyFocus = 250000 + inventory.PlayerLevel * 5000;
36 inventory.GiftsRemaining = Math.max(8, inventory.PlayerLevel);
36 37
37 38 inventory.LibraryAvailableDailyTaskInfo = createLibraryDailyTask();
38 39
Modified src/models/inboxModel.ts +14 -0
@@ -31,6 +31,7 @@ export interface IMessage {
31 31 countedAtt?: ITypeCount[];
32 32 transmission?: string;
33 33 arg?: Arg[];
34 gifts?: IGift[];
34 35 r?: boolean;
35 36 contextInfo?: string;
36 37 acceptAction?: string;
@@ -43,6 +44,10 @@ export interface Arg {
43 44 Tag: string | number;
44 45 }
45 46
47 export interface IGift {
48 GiftType: string;
49 }
50
46 51 //types are wrong
47 52 // export interface IMessageDatabase {
48 53 // _id: Types.ObjectId;
@@ -80,6 +85,14 @@ export interface Arg {
80 85 // cinematic: string;
81 86 // requiredLevel: string;
82 87 // }
88
89 const giftSchema = new Schema<IGift>(
90 {
91 GiftType: String
92 },
93 { _id: false }
94 );
95
83 96 const messageSchema = new Schema<IMessageDatabase>(
84 97 {
85 98 ownerId: Schema.Types.ObjectId,
@@ -93,6 +106,7 @@ const messageSchema = new Schema<IMessageDatabase>(
93 106 endDate: Date,
94 107 r: Boolean,
95 108 att: { type: [String], default: undefined },
109 gifts: { type: [giftSchema], default: undefined },
96 110 countedAtt: { type: [typeCountSchema], default: undefined },
97 111 transmission: String,
98 112 arg: {
Modified src/routes/api.ts +2 -0
@@ -51,6 +51,7 @@ import { getNewRewardSeedController } from "@/src/controllers/api/getNewRewardSe
51 51 import { getShipController } from "@/src/controllers/api/getShipController";
52 52 import { getVendorInfoController } from "@/src/controllers/api/getVendorInfoController";
53 53 import { getVoidProjectionRewardsController } from "@/src/controllers/api/getVoidProjectionRewardsController";
54 import { giftingController } from "@/src/controllers/api/giftingController";
54 55 import { gildWeaponController } from "@/src/controllers/api/gildWeaponController";
55 56 import { giveKeyChainTriggeredItemsController } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
56 57 import { giveKeyChainTriggeredMessageController } from "@/src/controllers/api/giveKeyChainTriggeredMessageController";
@@ -203,6 +204,7 @@ apiRouter.post("/getAlliance.php", getAllianceController);
203 204 apiRouter.post("/getFriends.php", getFriendsController);
204 205 apiRouter.post("/getGuildDojo.php", getGuildDojoController);
205 206 apiRouter.post("/getVoidProjectionRewards.php", getVoidProjectionRewardsController);
207 apiRouter.post("/gifting.php", giftingController);
206 208 apiRouter.post("/gildWeapon.php", gildWeaponController);
207 209 apiRouter.post("/giveKeyChainTriggeredItems.php", giveKeyChainTriggeredItemsController);
208 210 apiRouter.post("/giveKeyChainTriggeredMessage.php", giveKeyChainTriggeredMessageController);
Modified src/services/loginService.ts +4 -0
@@ -100,3 +100,7 @@ export const getSuffixedName = (account: TAccountDocument): string => {
100 100 const suffix = ((crc32.str(name.toLowerCase() + "595") >>> 0) + platform_magics[platformId]) % 1000;
101 101 return name + "#" + suffix.toString().padStart(3, "0");
102 102 };
103
104 export const getAccountFromSuffixedName = (name: string): Promise<TAccountDocument | null> => {
105 return Account.findOne({ DisplayName: name.split("#")[0] });
106 };