From 0ffa9c6bc471aed323c51bb87a3ca7d8a6b5ed0b Mon Sep 17 00:00:00 2001 From: Sainan Date: Sun, 9 Mar 2025 07:47:32 -0700 Subject: [PATCH] feat: clan motd (#1134) Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1134 --- package-lock.json | 12 ++++++++ package.json | 1 + src/controllers/api/getGuildController.ts | 2 ++ src/controllers/api/setGuildMotdController.ts | 30 +++++++++++++++++++ src/models/guildModel.ts | 13 +++++++- src/routes/api.ts | 3 ++ src/services/loginService.ts | 9 ++++++ src/types/guildTypes.ts | 8 +++++ 8 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 src/controllers/api/setGuildMotdController.ts diff --git a/package-lock.json b/package-lock.json index 98c06df8d..8e58230c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "GNU", "dependencies": { "copyfiles": "^2.4.1", + "crc-32": "^1.2.2", "express": "^5", "mongoose": "^8.11.0", "warframe-public-export-plus": "^0.5.42", @@ -1249,6 +1250,17 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "license": "MIT" }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", diff --git a/package.json b/package.json index 1a931ddfc..dc38de04a 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@types/express": "^5", "@types/morgan": "^1.9.9", "copyfiles": "^2.4.1", + "crc-32": "^1.2.2", "express": "^5", "mongoose": "^8.11.0", "morgan": "^1.10.0", diff --git a/src/controllers/api/getGuildController.ts b/src/controllers/api/getGuildController.ts index 62a27501f..e96d7be79 100644 --- a/src/controllers/api/getGuildController.ts +++ b/src/controllers/api/getGuildController.ts @@ -26,6 +26,8 @@ const getGuildController: RequestHandler = async (req, res) => { res.json({ _id: toOid(guild._id), Name: guild.Name, + MOTD: guild.MOTD, + LongMOTD: guild.LongMOTD, Members: [ { _id: { $oid: req.query.accountId }, diff --git a/src/controllers/api/setGuildMotdController.ts b/src/controllers/api/setGuildMotdController.ts new file mode 100644 index 000000000..32f6f3ec5 --- /dev/null +++ b/src/controllers/api/setGuildMotdController.ts @@ -0,0 +1,30 @@ +import { Guild } from "@/src/models/guildModel"; +import { getInventory } from "@/src/services/inventoryService"; +import { getAccountForRequest, getSuffixedName } from "@/src/services/loginService"; +import { RequestHandler } from "express"; + +export const setGuildMotdController: RequestHandler = async (req, res) => { + const account = await getAccountForRequest(req); + const inventory = await getInventory(account._id.toString()); + const guild = (await Guild.findOne({ _id: inventory.GuildId! }))!; + // TODO: Check permissions + + const IsLongMOTD = "longMOTD" in req.query; + const MOTD = req.body ? String(req.body) : undefined; + + if (IsLongMOTD) { + if (MOTD) { + guild.LongMOTD = { + message: MOTD, + authorName: getSuffixedName(account) + }; + } else { + guild.LongMOTD = undefined; + } + } else { + guild.MOTD = MOTD ?? ""; + } + await guild.save(); + + res.json({ IsLongMOTD, MOTD }); +}; diff --git a/src/models/guildModel.ts b/src/models/guildModel.ts index 7afceb98e..295d52c50 100644 --- a/src/models/guildModel.ts +++ b/src/models/guildModel.ts @@ -3,7 +3,8 @@ import { IDojoComponentDatabase, ITechProjectDatabase, ITechProjectClient, - IDojoDecoDatabase + IDojoDecoDatabase, + ILongMOTD } from "@/src/types/guildTypes"; import { Document, Model, model, Schema, Types } from "mongoose"; import { fusionTreasuresSchema, typeCountSchema } from "./inventoryModels/inventoryModel"; @@ -59,9 +60,19 @@ techProjectSchema.set("toJSON", { } }); +const longMOTDSchema = new Schema( + { + message: String, + authorName: String + }, + { _id: false } +); + const guildSchema = new Schema( { Name: { type: String, required: true }, + MOTD: { type: String, default: "" }, + LongMOTD: { type: longMOTDSchema, default: undefined }, DojoComponents: { type: [dojoComponentSchema], default: [] }, DojoCapacity: { type: Number, default: 100 }, DojoEnergy: { type: Number, default: 5 }, diff --git a/src/routes/api.ts b/src/routes/api.ts index 964ebd5d9..fe2a83d7e 100644 --- a/src/routes/api.ts +++ b/src/routes/api.ts @@ -79,6 +79,7 @@ import { setActiveShipController } from "@/src/controllers/api/setActiveShipCont import { setBootLocationController } from "@/src/controllers/api/setBootLocationController"; import { setDojoComponentMessageController } from "@/src/controllers/api/setDojoComponentMessageController"; import { setEquippedInstrumentController } from "@/src/controllers/api/setEquippedInstrumentController"; +import { setGuildMotdController } from "@/src/controllers/api/setGuildMotdController"; import { setPlacedDecoInfoController } from "@/src/controllers/api/setPlacedDecoInfoController"; import { setShipCustomizationsController } from "@/src/controllers/api/setShipCustomizationsController"; import { setShipFavouriteLoadoutController } from "@/src/controllers/api/setShipFavouriteLoadoutController"; @@ -138,6 +139,7 @@ apiRouter.get("/queueDojoComponentDestruction.php", queueDojoComponentDestructio apiRouter.get("/setActiveQuest.php", setActiveQuestController); apiRouter.get("/setActiveShip.php", setActiveShipController); apiRouter.get("/setBootLocation.php", setBootLocationController); +apiRouter.get("/setGuildMotd.php", setGuildMotdController); apiRouter.get("/setSupportedSyndicate.php", setSupportedSyndicateController); apiRouter.get("/startLibraryDailyTask.php", startLibraryDailyTaskController); apiRouter.get("/startLibraryPersonalTarget.php", startLibraryPersonalTargetController); @@ -197,6 +199,7 @@ apiRouter.post("/saveSettings.php", saveSettingsController); apiRouter.post("/sell.php", sellController); apiRouter.post("/setDojoComponentMessage.php", setDojoComponentMessageController); apiRouter.post("/setEquippedInstrument.php", setEquippedInstrumentController); +apiRouter.post("/setGuildMotd.php", setGuildMotdController); apiRouter.post("/setPlacedDecoInfo.php", setPlacedDecoInfoController); apiRouter.post("/setShipCustomizations.php", setShipCustomizationsController); apiRouter.post("/setShipFavouriteLoadout.php", setShipFavouriteLoadoutController); diff --git a/src/services/loginService.ts b/src/services/loginService.ts index 71236f07a..08d12ee9f 100644 --- a/src/services/loginService.ts +++ b/src/services/loginService.ts @@ -8,6 +8,7 @@ import { PersonalRooms } from "@/src/models/personalRoomsModel"; import { Request } from "express"; import { config } from "@/src/services/configService"; import { createStats } from "@/src/services/statsService"; +import crc32 from "crc-32"; export const isCorrectPassword = (requestPassword: string, databasePassword: string): boolean => { return requestPassword === databasePassword; @@ -99,3 +100,11 @@ export const isAdministrator = (account: TAccountDocument): boolean => { } return !!config.administratorNames.find(x => x == account.DisplayName); }; + +const platform_magics = [753, 639, 247, 37, 60]; +export const getSuffixedName = (account: TAccountDocument): string => { + const name = account.DisplayName; + const platformId = 0; + const suffix = ((crc32.str(name.toLowerCase() + "595") >>> 0) + platform_magics[platformId]) % 1000; + return name + "#" + suffix.toString().padStart(3, "0"); +}; diff --git a/src/types/guildTypes.ts b/src/types/guildTypes.ts index 9effcfbc6..8b9fd97bf 100644 --- a/src/types/guildTypes.ts +++ b/src/types/guildTypes.ts @@ -5,6 +5,8 @@ import { IFusionTreasure, IMiscItem, ITypeCount } from "@/src/types/inventoryTyp export interface IGuildDatabase { _id: Types.ObjectId; Name: string; + MOTD: string; + LongMOTD?: ILongMOTD; DojoComponents: IDojoComponentDatabase[]; DojoCapacity: number; @@ -27,6 +29,12 @@ export interface IGuildDatabase { CeremonyResetDate?: Date; } +export interface ILongMOTD { + message: string; + authorName: string; + //authorGuildName: ""; +} + export interface IGuildVault { DojoRefundRegularCredits?: number; DojoRefundMiscItems?: IMiscItem[];