返回提交历史
Added
src/controllers/api/giftingController.ts
+92
-0
Modified
src/controllers/api/inboxController.ts
+46
-9
Modified
src/controllers/api/inventoryController.ts
+1
-0
Modified
src/models/inboxModel.ts
+14
-0
Modified
src/routes/api.ts
+2
-0
Modified
src/services/loginService.ts
+4
-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
代码差异
6 个文件
+159
-9
@@ -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
}
@@ -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);
@@ -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
@@ -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: {
@@ -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);
@@ -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
};