feat: friends #2004
							
								
								
									
										60
									
								
								src/controllers/api/addFriendController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/controllers/api/addFriendController.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
			
		||||
import { toOid } from "@/src/helpers/inventoryHelpers";
 | 
			
		||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
			
		||||
import { Friendship } from "@/src/models/friendModel";
 | 
			
		||||
import { addAccountDataToFriendInfo, addInventoryDataToFriendInfo } from "@/src/services/friendService";
 | 
			
		||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
			
		||||
import { IFriendInfo } from "@/src/types/friendTypes";
 | 
			
		||||
import { RequestHandler } from "express";
 | 
			
		||||
 | 
			
		||||
export const addFriendController: RequestHandler = async (req, res) => {
 | 
			
		||||
    const accountId = await getAccountIdForRequest(req);
 | 
			
		||||
    const payload = getJSONfromString<IAddFriendRequest>(String(req.body));
 | 
			
		||||
    const promises: Promise<void>[] = [];
 | 
			
		||||
    const newFriends: IFriendInfo[] = [];
 | 
			
		||||
    if (payload.friend == "all") {
 | 
			
		||||
        const [internalFriendships, externalFriendships] = await Promise.all([
 | 
			
		||||
            Friendship.find({ owner: accountId }, "friend"),
 | 
			
		||||
            Friendship.find({ friend: accountId }, "owner")
 | 
			
		||||
        ]);
 | 
			
		||||
        for (const externalFriendship of externalFriendships) {
 | 
			
		||||
            if (!internalFriendships.find(x => x.friend.equals(externalFriendship.owner))) {
 | 
			
		||||
                promises.push(
 | 
			
		||||
                    Friendship.insertOne({
 | 
			
		||||
                        owner: accountId,
 | 
			
		||||
                        friend: externalFriendship.owner,
 | 
			
		||||
                        Note: externalFriendship.Note // TOVERIFY: Should the note be copied when accepting a friend request?
 | 
			
		||||
                    }) as unknown as Promise<void>
 | 
			
		||||
                );
 | 
			
		||||
                newFriends.push({
 | 
			
		||||
                    _id: toOid(externalFriendship.owner)
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        const externalFriendship = await Friendship.findOne({ owner: payload.friend, friend: accountId }, "Note");
 | 
			
		||||
        if (externalFriendship) {
 | 
			
		||||
            promises.push(
 | 
			
		||||
                Friendship.insertOne({
 | 
			
		||||
                    owner: accountId,
 | 
			
		||||
                    friend: payload.friend,
 | 
			
		||||
                    Note: externalFriendship.Note
 | 
			
		||||
                }) as unknown as Promise<void>
 | 
			
		||||
            );
 | 
			
		||||
            newFriends.push({
 | 
			
		||||
                _id: { $oid: payload.friend }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    for (const newFriend of newFriends) {
 | 
			
		||||
        promises.push(addAccountDataToFriendInfo(newFriend));
 | 
			
		||||
        promises.push(addInventoryDataToFriendInfo(newFriend));
 | 
			
		||||
    }
 | 
			
		||||
    await Promise.all(promises);
 | 
			
		||||
    res.json({
 | 
			
		||||
        Friends: newFriends
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
interface IAddFriendRequest {
 | 
			
		||||
    friend: string; // oid or "all" in which case all=1 is also a query parameter
 | 
			
		||||
}
 | 
			
		||||
@ -2,7 +2,7 @@ import { toOid } from "@/src/helpers/inventoryHelpers";
 | 
			
		||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
			
		||||
import { Account, Ignore } from "@/src/models/loginModel";
 | 
			
		||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
			
		||||
import { IFriendInfo } from "@/src/types/guildTypes";
 | 
			
		||||
import { IFriendInfo } from "@/src/types/friendTypes";
 | 
			
		||||
import { RequestHandler } from "express";
 | 
			
		||||
 | 
			
		||||
export const addIgnoredUserController: RequestHandler = async (req, res) => {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										51
									
								
								src/controllers/api/addPendingFriendController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/controllers/api/addPendingFriendController.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
import { toOid } from "@/src/helpers/inventoryHelpers";
 | 
			
		||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
			
		||||
import { Friendship } from "@/src/models/friendModel";
 | 
			
		||||
import { Account } from "@/src/models/loginModel";
 | 
			
		||||
import { addInventoryDataToFriendInfo, areFriendsOfFriends } from "@/src/services/friendService";
 | 
			
		||||
import { getInventory } from "@/src/services/inventoryService";
 | 
			
		||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
			
		||||
import { IFriendInfo } from "@/src/types/friendTypes";
 | 
			
		||||
import { RequestHandler } from "express";
 | 
			
		||||
 | 
			
		||||
export const addPendingFriendController: RequestHandler = async (req, res) => {
 | 
			
		||||
    const payload = getJSONfromString<IAddPendingFriendRequest>(String(req.body));
 | 
			
		||||
 | 
			
		||||
    const account = await Account.findOne({ DisplayName: payload.friend });
 | 
			
		||||
    if (!account) {
 | 
			
		||||
        res.status(400).end();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const accountId = await getAccountIdForRequest(req);
 | 
			
		||||
    const inventory = await getInventory(account._id.toString(), "Settings");
 | 
			
		||||
    if (
 | 
			
		||||
        inventory.Settings?.FriendInvRestriction == "GIFT_MODE_NONE" ||
 | 
			
		||||
        (inventory.Settings?.FriendInvRestriction == "GIFT_MODE_FRIENDS" &&
 | 
			
		||||
            !(await areFriendsOfFriends(account._id, accountId)))
 | 
			
		||||
    ) {
 | 
			
		||||
        res.status(400).send("Friend Invite Restriction");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    await Friendship.insertOne({
 | 
			
		||||
        owner: accountId,
 | 
			
		||||
        friend: account._id,
 | 
			
		||||
        Note: payload.message
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const friendInfo: IFriendInfo = {
 | 
			
		||||
        _id: toOid(account._id),
 | 
			
		||||
        DisplayName: account.DisplayName,
 | 
			
		||||
        Note: payload.message
 | 
			
		||||
    };
 | 
			
		||||
    await addInventoryDataToFriendInfo(friendInfo);
 | 
			
		||||
    res.json({
 | 
			
		||||
        Friend: friendInfo
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
interface IAddPendingFriendRequest {
 | 
			
		||||
    friend: string;
 | 
			
		||||
    message: string;
 | 
			
		||||
}
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
import { Guild, GuildMember } from "@/src/models/guildModel";
 | 
			
		||||
import { Account } from "@/src/models/loginModel";
 | 
			
		||||
import { fillInInventoryDataForGuildMember, hasGuildPermission } from "@/src/services/guildService";
 | 
			
		||||
import { addInventoryDataToFriendInfo, areFriends } from "@/src/services/friendService";
 | 
			
		||||
import { hasGuildPermission } from "@/src/services/guildService";
 | 
			
		||||
import { createMessage } from "@/src/services/inboxService";
 | 
			
		||||
import { getInventory } from "@/src/services/inventoryService";
 | 
			
		||||
import { getAccountForRequest, getAccountIdForRequest, getSuffixedName } from "@/src/services/loginService";
 | 
			
		||||
@ -22,15 +23,18 @@ export const addToGuildController: RequestHandler = async (req, res) => {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const senderAccount = await getAccountForRequest(req);
 | 
			
		||||
        const inventory = await getInventory(account._id.toString(), "Settings");
 | 
			
		||||
        // TODO: Also consider GIFT_MODE_FRIENDS once friends are implemented
 | 
			
		||||
        if (inventory.Settings?.GuildInvRestriction == "GIFT_MODE_NONE") {
 | 
			
		||||
        if (
 | 
			
		||||
            inventory.Settings?.GuildInvRestriction == "GIFT_MODE_NONE" ||
 | 
			
		||||
            (inventory.Settings?.GuildInvRestriction == "GIFT_MODE_FRIENDS" &&
 | 
			
		||||
                !(await areFriends(account._id, senderAccount._id)))
 | 
			
		||||
        ) {
 | 
			
		||||
            res.status(400).json("Invite restricted");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const guild = (await Guild.findById(payload.GuildId.$oid, "Name Ranks"))!;
 | 
			
		||||
        const senderAccount = await getAccountForRequest(req);
 | 
			
		||||
        if (!(await hasGuildPermission(guild, senderAccount._id.toString(), GuildPermission.Recruiter))) {
 | 
			
		||||
            res.status(400).json("Invalid permission");
 | 
			
		||||
        }
 | 
			
		||||
@ -74,7 +78,7 @@ export const addToGuildController: RequestHandler = async (req, res) => {
 | 
			
		||||
            Rank: 7,
 | 
			
		||||
            Status: 2
 | 
			
		||||
        };
 | 
			
		||||
        await fillInInventoryDataForGuildMember(member);
 | 
			
		||||
        await addInventoryDataToFriendInfo(member);
 | 
			
		||||
        res.json({ NewMember: member });
 | 
			
		||||
    } else if ("RequestMsg" in payload) {
 | 
			
		||||
        // Player applying to join a clan
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,54 @@
 | 
			
		||||
import { Request, Response } from "express";
 | 
			
		||||
import { toOid } from "@/src/helpers/inventoryHelpers";
 | 
			
		||||
import { Friendship } from "@/src/models/friendModel";
 | 
			
		||||
import { addAccountDataToFriendInfo, addInventoryDataToFriendInfo } from "@/src/services/friendService";
 | 
			
		||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
			
		||||
import { IFriendInfo } from "@/src/types/friendTypes";
 | 
			
		||||
import { Request, RequestHandler, Response } from "express";
 | 
			
		||||
 | 
			
		||||
// POST with {} instead of GET as of 38.5.0
 | 
			
		||||
const getFriendsController = (_request: Request, response: Response): void => {
 | 
			
		||||
    response.writeHead(200, {
 | 
			
		||||
        //Connection: "keep-alive",
 | 
			
		||||
        //"Content-Encoding": "gzip",
 | 
			
		||||
        "Content-Type": "text/html",
 | 
			
		||||
        // charset: "UTF - 8",
 | 
			
		||||
        "Content-Length": "3"
 | 
			
		||||
    });
 | 
			
		||||
    response.end(Buffer.from([0x7b, 0x7d, 0x0a]));
 | 
			
		||||
export const getFriendsController: RequestHandler = async (req: Request, res: Response) => {
 | 
			
		||||
    const accountId = await getAccountIdForRequest(req);
 | 
			
		||||
    const response: IGetFriendsResponse = {
 | 
			
		||||
        Current: [],
 | 
			
		||||
        IncomingFriendRequests: [],
 | 
			
		||||
        OutgoingFriendRequests: []
 | 
			
		||||
    };
 | 
			
		||||
    const [internalFriendships, externalFriendships] = await Promise.all([
 | 
			
		||||
        Friendship.find({ owner: accountId }),
 | 
			
		||||
        Friendship.find({ friend: accountId }, "owner Note")
 | 
			
		||||
    ]);
 | 
			
		||||
    for (const externalFriendship of externalFriendships) {
 | 
			
		||||
        if (!internalFriendships.find(x => x.friend.equals(externalFriendship.owner))) {
 | 
			
		||||
            response.IncomingFriendRequests.push({
 | 
			
		||||
                _id: toOid(externalFriendship.owner),
 | 
			
		||||
                Note: externalFriendship.Note
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    for (const internalFriendship of internalFriendships) {
 | 
			
		||||
        const friendInfo: IFriendInfo = {
 | 
			
		||||
            _id: toOid(internalFriendship.friend)
 | 
			
		||||
        };
 | 
			
		||||
        if (externalFriendships.find(x => x.owner.equals(internalFriendship.friend))) {
 | 
			
		||||
            response.Current.push(friendInfo);
 | 
			
		||||
        } else {
 | 
			
		||||
            response.OutgoingFriendRequests.push(friendInfo);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    const promises: Promise<void>[] = [];
 | 
			
		||||
    for (const arr of Object.values(response)) {
 | 
			
		||||
        for (const friendInfo of arr) {
 | 
			
		||||
            promises.push(addAccountDataToFriendInfo(friendInfo));
 | 
			
		||||
            promises.push(addInventoryDataToFriendInfo(friendInfo));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    await Promise.all(promises);
 | 
			
		||||
    res.json(response);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export { getFriendsController };
 | 
			
		||||
// interface IGetFriendsResponse {
 | 
			
		||||
//     Current: IFriendInfo[];
 | 
			
		||||
//     IncomingFriendRequests: IFriendInfo[];
 | 
			
		||||
//     OutgoingFriendRequests: IFriendInfo[];
 | 
			
		||||
// }
 | 
			
		||||
type IGetFriendsResponse = Record<"Current" | "IncomingFriendRequests" | "OutgoingFriendRequests", IFriendInfo[]>;
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
import { toOid } from "@/src/helpers/inventoryHelpers";
 | 
			
		||||
import { Account, Ignore } from "@/src/models/loginModel";
 | 
			
		||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
			
		||||
import { IFriendInfo } from "@/src/types/guildTypes";
 | 
			
		||||
import { IFriendInfo } from "@/src/types/friendTypes";
 | 
			
		||||
import { parallelForeach } from "@/src/utils/async-utils";
 | 
			
		||||
import { RequestHandler } from "express";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
			
		||||
import { Account } from "@/src/models/loginModel";
 | 
			
		||||
import { areFriends } from "@/src/services/friendService";
 | 
			
		||||
import { createMessage } from "@/src/services/inboxService";
 | 
			
		||||
import { getInventory, updateCurrency } from "@/src/services/inventoryService";
 | 
			
		||||
import { getAccountForRequest, getSuffixedName } from "@/src/services/loginService";
 | 
			
		||||
@ -30,8 +31,11 @@ export const giftingController: RequestHandler = async (req, res) => {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 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") {
 | 
			
		||||
    const senderAccount = await getAccountForRequest(req);
 | 
			
		||||
    if (
 | 
			
		||||
        inventory.Settings?.GiftMode == "GIFT_MODE_NONE" ||
 | 
			
		||||
        (inventory.Settings?.GiftMode == "GIFT_MODE_FRIENDS" && !(await areFriends(account._id, senderAccount._id)))
 | 
			
		||||
    ) {
 | 
			
		||||
        res.status(400).send("17").end();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
@ -40,7 +44,6 @@ export const giftingController: RequestHandler = async (req, res) => {
 | 
			
		||||
    // 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"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										36
									
								
								src/controllers/api/removeFriendController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/controllers/api/removeFriendController.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
			
		||||
import { toOid } from "@/src/helpers/inventoryHelpers";
 | 
			
		||||
import { Friendship } from "@/src/models/friendModel";
 | 
			
		||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
			
		||||
import { IOid } from "@/src/types/commonTypes";
 | 
			
		||||
import { RequestHandler } from "express";
 | 
			
		||||
 | 
			
		||||
export const removeFriendGetController: RequestHandler = async (req, res) => {
 | 
			
		||||
    const accountId = await getAccountIdForRequest(req);
 | 
			
		||||
    if (req.query.all) {
 | 
			
		||||
        const [internalFriendships, externalFriendships] = await Promise.all([
 | 
			
		||||
            Friendship.find({ owner: accountId }, "friend"),
 | 
			
		||||
            Friendship.find({ friend: accountId }, "owner")
 | 
			
		||||
        ]);
 | 
			
		||||
        const promises: Promise<void>[] = [];
 | 
			
		||||
        const friends: IOid[] = [];
 | 
			
		||||
        for (const externalFriendship of externalFriendships) {
 | 
			
		||||
            if (!internalFriendships.find(x => x.friend.equals(externalFriendship.owner))) {
 | 
			
		||||
                promises.push(Friendship.deleteOne({ _id: externalFriendship._id }) as unknown as Promise<void>);
 | 
			
		||||
                friends.push(toOid(externalFriendship.owner));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
        res.json({
 | 
			
		||||
            Friends: friends
 | 
			
		||||
        });
 | 
			
		||||
    } else {
 | 
			
		||||
        const friendId = req.query.friendId as string;
 | 
			
		||||
        await Promise.all([
 | 
			
		||||
            Friendship.deleteOne({ owner: accountId, friend: friendId }),
 | 
			
		||||
            Friendship.deleteOne({ owner: friendId, friend: accountId })
 | 
			
		||||
        ]);
 | 
			
		||||
        res.json({
 | 
			
		||||
            Friends: [{ $oid: friendId } satisfies IOid]
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										30
									
								
								src/controllers/api/setFriendNoteController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/controllers/api/setFriendNoteController.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
			
		||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
			
		||||
import { Friendship } from "@/src/models/friendModel";
 | 
			
		||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
			
		||||
import { RequestHandler } from "express";
 | 
			
		||||
 | 
			
		||||
export const setFriendNoteController: RequestHandler = async (req, res) => {
 | 
			
		||||
    const accountId = await getAccountIdForRequest(req);
 | 
			
		||||
    const payload = getJSONfromString<ISetFriendNoteRequest>(String(req.body));
 | 
			
		||||
    const friendship = await Friendship.findOne({ owner: accountId, friend: payload.FriendId }, "Note Favorite");
 | 
			
		||||
    if (friendship) {
 | 
			
		||||
        if ("Note" in payload) {
 | 
			
		||||
            friendship.Note = payload.Note;
 | 
			
		||||
        } else {
 | 
			
		||||
            friendship.Favorite = payload.Favorite;
 | 
			
		||||
        }
 | 
			
		||||
        await friendship.save();
 | 
			
		||||
    }
 | 
			
		||||
    res.json({
 | 
			
		||||
        Id: payload.FriendId,
 | 
			
		||||
        SetNote: "Note" in payload,
 | 
			
		||||
        Note: friendship?.Note,
 | 
			
		||||
        Favorite: friendship?.Favorite
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
interface ISetFriendNoteRequest {
 | 
			
		||||
    FriendId: string;
 | 
			
		||||
    Note?: string;
 | 
			
		||||
    Favorite?: boolean;
 | 
			
		||||
}
 | 
			
		||||
@ -10,6 +10,7 @@ import { Stats } from "@/src/models/statsModel";
 | 
			
		||||
import { GuildMember } from "@/src/models/guildModel";
 | 
			
		||||
import { Leaderboard } from "@/src/models/leaderboardModel";
 | 
			
		||||
import { deleteGuild } from "@/src/services/guildService";
 | 
			
		||||
import { Friendship } from "@/src/models/friendModel";
 | 
			
		||||
 | 
			
		||||
export const deleteAccountController: RequestHandler = async (req, res) => {
 | 
			
		||||
    const accountId = await getAccountIdForRequest(req);
 | 
			
		||||
@ -22,6 +23,8 @@ export const deleteAccountController: RequestHandler = async (req, res) => {
 | 
			
		||||
 | 
			
		||||
    await Promise.all([
 | 
			
		||||
        Account.deleteOne({ _id: accountId }),
 | 
			
		||||
        Friendship.deleteMany({ owner: accountId }),
 | 
			
		||||
        Friendship.deleteMany({ friend: accountId }),
 | 
			
		||||
        GuildMember.deleteMany({ accountId: accountId }),
 | 
			
		||||
        Ignore.deleteMany({ ignorer: accountId }),
 | 
			
		||||
        Ignore.deleteMany({ ignoree: accountId }),
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										15
									
								
								src/models/friendModel.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/models/friendModel.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
import { IFriendship } from "@/src/types/friendTypes";
 | 
			
		||||
import { model, Schema } from "mongoose";
 | 
			
		||||
 | 
			
		||||
const friendshipSchema = new Schema<IFriendship>({
 | 
			
		||||
    owner: { type: Schema.Types.ObjectId, required: true },
 | 
			
		||||
    friend: { type: Schema.Types.ObjectId, required: true },
 | 
			
		||||
    Note: String,
 | 
			
		||||
    Favorite: Boolean
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
friendshipSchema.index({ owner: 1 });
 | 
			
		||||
friendshipSchema.index({ friend: 1 });
 | 
			
		||||
friendshipSchema.index({ owner: 1, friend: 1 }, { unique: true });
 | 
			
		||||
 | 
			
		||||
export const Friendship = model<IFriendship>("Friendship", friendshipSchema);
 | 
			
		||||
@ -3,8 +3,10 @@ import { abandonLibraryDailyTaskController } from "@/src/controllers/api/abandon
 | 
			
		||||
import { abortDojoComponentController } from "@/src/controllers/api/abortDojoComponentController";
 | 
			
		||||
import { abortDojoComponentDestructionController } from "@/src/controllers/api/abortDojoComponentDestructionController";
 | 
			
		||||
import { activateRandomModController } from "@/src/controllers/api/activateRandomModController";
 | 
			
		||||
import { addFriendController } from "@/src/controllers/api/addFriendController";
 | 
			
		||||
import { addFriendImageController } from "@/src/controllers/api/addFriendImageController";
 | 
			
		||||
import { addIgnoredUserController } from "@/src/controllers/api/addIgnoredUserController";
 | 
			
		||||
import { addPendingFriendController } from "@/src/controllers/api/addPendingFriendController";
 | 
			
		||||
import { addToAllianceController } from "@/src/controllers/api/addToAllianceController";
 | 
			
		||||
import { addToGuildController } from "@/src/controllers/api/addToGuildController";
 | 
			
		||||
import { arcaneCommonController } from "@/src/controllers/api/arcaneCommonController";
 | 
			
		||||
@ -101,6 +103,7 @@ import { questControlController } from "@/src/controllers/api/questControlContro
 | 
			
		||||
import { queueDojoComponentDestructionController } from "@/src/controllers/api/queueDojoComponentDestructionController";
 | 
			
		||||
import { redeemPromoCodeController } from "@/src/controllers/api/redeemPromoCodeController";
 | 
			
		||||
import { releasePetController } from "@/src/controllers/api/releasePetController";
 | 
			
		||||
import { removeFriendGetController } from "@/src/controllers/api/removeFriendController";
 | 
			
		||||
import { removeFromAllianceController } from "@/src/controllers/api/removeFromAllianceController";
 | 
			
		||||
import { removeFromGuildController } from "@/src/controllers/api/removeFromGuildController";
 | 
			
		||||
import { removeIgnoredUserController } from "@/src/controllers/api/removeIgnoredUserController";
 | 
			
		||||
@ -120,6 +123,7 @@ import { setDojoComponentColorsController } from "@/src/controllers/api/setDojoC
 | 
			
		||||
import { setDojoComponentMessageController } from "@/src/controllers/api/setDojoComponentMessageController";
 | 
			
		||||
import { setDojoComponentSettingsController } from "@/src/controllers/api/setDojoComponentSettingsController";
 | 
			
		||||
import { setEquippedInstrumentController } from "@/src/controllers/api/setEquippedInstrumentController";
 | 
			
		||||
import { setFriendNoteController } from "@/src/controllers/api/setFriendNoteController";
 | 
			
		||||
import { setGuildMotdController } from "@/src/controllers/api/setGuildMotdController";
 | 
			
		||||
import { setHubNpcCustomizationsController } from "@/src/controllers/api/setHubNpcCustomizationsController";
 | 
			
		||||
import { setPlacedDecoInfoController } from "@/src/controllers/api/setPlacedDecoInfoController";
 | 
			
		||||
@ -180,13 +184,13 @@ apiRouter.get("/getGuildContributions.php", getGuildContributionsController);
 | 
			
		||||
apiRouter.get("/getGuildDojo.php", getGuildDojoController);
 | 
			
		||||
apiRouter.get("/getGuildLog.php", getGuildLogController);
 | 
			
		||||
apiRouter.get("/getIgnoredUsers.php", getIgnoredUsersController);
 | 
			
		||||
apiRouter.get("/getMessages.php", inboxController); // unsure if this is correct, but needed for U17
 | 
			
		||||
apiRouter.get("/getNewRewardSeed.php", getNewRewardSeedController);
 | 
			
		||||
apiRouter.get("/getShip.php", getShipController);
 | 
			
		||||
apiRouter.get("/getVendorInfo.php", getVendorInfoController);
 | 
			
		||||
apiRouter.get("/hub", hubController);
 | 
			
		||||
apiRouter.get("/hubInstances", hubInstancesController);
 | 
			
		||||
apiRouter.get("/inbox.php", inboxController);
 | 
			
		||||
apiRouter.get("/getMessages.php", inboxController); // unsure if this is correct, but needed for U17
 | 
			
		||||
apiRouter.get("/inventory.php", inventoryController);
 | 
			
		||||
apiRouter.get("/loginRewards.php", loginRewardsController);
 | 
			
		||||
apiRouter.get("/logout.php", logoutController);
 | 
			
		||||
@ -196,6 +200,7 @@ apiRouter.get("/modularWeaponSale.php", modularWeaponSaleController);
 | 
			
		||||
apiRouter.get("/playedParkourTutorial.php", playedParkourTutorialController);
 | 
			
		||||
apiRouter.get("/questControl.php", questControlController);
 | 
			
		||||
apiRouter.get("/queueDojoComponentDestruction.php", queueDojoComponentDestructionController);
 | 
			
		||||
apiRouter.get("/removeFriend.php", removeFriendGetController);
 | 
			
		||||
apiRouter.get("/removeFromAlliance.php", removeFromAllianceController);
 | 
			
		||||
apiRouter.get("/setActiveQuest.php", setActiveQuestController);
 | 
			
		||||
apiRouter.get("/setActiveShip.php", setActiveShipController);
 | 
			
		||||
@ -213,8 +218,10 @@ apiRouter.get("/updateSession.php", updateSessionGetController);
 | 
			
		||||
// post
 | 
			
		||||
apiRouter.post("/abortDojoComponent.php", abortDojoComponentController);
 | 
			
		||||
apiRouter.post("/activateRandomMod.php", activateRandomModController);
 | 
			
		||||
apiRouter.post("/addFriend.php", addFriendController);
 | 
			
		||||
apiRouter.post("/addFriendImage.php", addFriendImageController);
 | 
			
		||||
apiRouter.post("/addIgnoredUser.php", addIgnoredUserController);
 | 
			
		||||
apiRouter.post("/addPendingFriend.php", addPendingFriendController);
 | 
			
		||||
apiRouter.post("/addToAlliance.php", addToAllianceController);
 | 
			
		||||
apiRouter.post("/addToGuild.php", addToGuildController);
 | 
			
		||||
apiRouter.post("/arcaneCommon.php", arcaneCommonController);
 | 
			
		||||
@ -297,6 +304,7 @@ apiRouter.post("/setDojoComponentColors.php", setDojoComponentColorsController);
 | 
			
		||||
apiRouter.post("/setDojoComponentMessage.php", setDojoComponentMessageController);
 | 
			
		||||
apiRouter.post("/setDojoComponentSettings.php", setDojoComponentSettingsController);
 | 
			
		||||
apiRouter.post("/setEquippedInstrument.php", setEquippedInstrumentController);
 | 
			
		||||
apiRouter.post("/setFriendNote.php", setFriendNoteController);
 | 
			
		||||
apiRouter.post("/setGuildMotd.php", setGuildMotdController);
 | 
			
		||||
apiRouter.post("/setHubNpcCustomizations.php", setHubNpcCustomizationsController);
 | 
			
		||||
apiRouter.post("/setPlacedDecoInfo.php", setPlacedDecoInfoController);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										44
									
								
								src/services/friendService.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/services/friendService.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
			
		||||
import { IFriendInfo } from "../types/friendTypes";
 | 
			
		||||
import { getInventory } from "./inventoryService";
 | 
			
		||||
import { config } from "./configService";
 | 
			
		||||
import { Account } from "../models/loginModel";
 | 
			
		||||
import { Types } from "mongoose";
 | 
			
		||||
import { Friendship } from "../models/friendModel";
 | 
			
		||||
 | 
			
		||||
export const addAccountDataToFriendInfo = async (info: IFriendInfo): Promise<void> => {
 | 
			
		||||
    info.DisplayName = (await Account.findById(info._id.$oid, "DisplayName"))!.DisplayName;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const addInventoryDataToFriendInfo = async (info: IFriendInfo): Promise<void> => {
 | 
			
		||||
    const inventory = await getInventory(info._id.$oid, "PlayerLevel ActiveAvatarImageType");
 | 
			
		||||
    info.PlayerLevel = config.spoofMasteryRank == -1 ? inventory.PlayerLevel : config.spoofMasteryRank;
 | 
			
		||||
    info.ActiveAvatarImageType = inventory.ActiveAvatarImageType;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const areFriends = async (a: Types.ObjectId | string, b: Types.ObjectId | string): Promise<boolean> => {
 | 
			
		||||
    const [aAddedB, bAddedA] = await Promise.all([
 | 
			
		||||
        Friendship.exists({ owner: a, friend: b }),
 | 
			
		||||
        Friendship.exists({ owner: b, friend: a })
 | 
			
		||||
    ]);
 | 
			
		||||
    return Boolean(aAddedB && bAddedA);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const areFriendsOfFriends = async (a: Types.ObjectId | string, b: Types.ObjectId | string): Promise<boolean> => {
 | 
			
		||||
    const [aInternalFriends, bInternalFriends] = await Promise.all([
 | 
			
		||||
        Friendship.find({ owner: a }),
 | 
			
		||||
        Friendship.find({ owner: b })
 | 
			
		||||
    ]);
 | 
			
		||||
    for (const aInternalFriend of aInternalFriends) {
 | 
			
		||||
        if (bInternalFriends.find(x => x.friend.equals(aInternalFriend.friend))) {
 | 
			
		||||
            const c = aInternalFriend.friend;
 | 
			
		||||
            const [cAcceptedA, cAcceptedB] = await Promise.all([
 | 
			
		||||
                Friendship.exists({ owner: c, friend: a }),
 | 
			
		||||
                Friendship.exists({ owner: c, friend: b })
 | 
			
		||||
            ]);
 | 
			
		||||
            if (cAcceptedA && cAcceptedB) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
};
 | 
			
		||||
@ -24,7 +24,6 @@ import { Types } from "mongoose";
 | 
			
		||||
import { ExportDojoRecipes, ExportResources, IDojoBuild, IDojoResearch } from "warframe-public-export-plus";
 | 
			
		||||
import { logger } from "../utils/logger";
 | 
			
		||||
import { config } from "./configService";
 | 
			
		||||
import { Account } from "../models/loginModel";
 | 
			
		||||
import { getRandomInt } from "./rngService";
 | 
			
		||||
import { Inbox } from "../models/inboxModel";
 | 
			
		||||
import { IFusionTreasure, ITypeCount } from "../types/inventoryTypes/inventoryTypes";
 | 
			
		||||
@ -32,6 +31,7 @@ import { IInventoryChanges } from "../types/purchaseTypes";
 | 
			
		||||
import { parallelForeach } from "../utils/async-utils";
 | 
			
		||||
import allDecoRecipes from "@/static/fixed_responses/allDecoRecipes.json";
 | 
			
		||||
import { createMessage } from "./inboxService";
 | 
			
		||||
import { addAccountDataToFriendInfo, addInventoryDataToFriendInfo } from "./friendService";
 | 
			
		||||
 | 
			
		||||
export const getGuildForRequest = async (req: Request): Promise<TGuildDatabaseDocument> => {
 | 
			
		||||
    const accountId = await getAccountIdForRequest(req);
 | 
			
		||||
@ -71,12 +71,8 @@ export const getGuildClient = async (guild: TGuildDatabaseDocument, accountId: s
 | 
			
		||||
        if (guildMember.accountId.equals(accountId)) {
 | 
			
		||||
            missingEntry = false;
 | 
			
		||||
        } else {
 | 
			
		||||
            dataFillInPromises.push(
 | 
			
		||||
                (async (): Promise<void> => {
 | 
			
		||||
                    member.DisplayName = (await Account.findById(guildMember.accountId, "DisplayName"))!.DisplayName;
 | 
			
		||||
                })()
 | 
			
		||||
            );
 | 
			
		||||
            dataFillInPromises.push(fillInInventoryDataForGuildMember(member));
 | 
			
		||||
            dataFillInPromises.push(addAccountDataToFriendInfo(member));
 | 
			
		||||
            dataFillInPromises.push(addInventoryDataToFriendInfo(member));
 | 
			
		||||
        }
 | 
			
		||||
        members.push(member);
 | 
			
		||||
    }
 | 
			
		||||
@ -466,12 +462,6 @@ export const setDojoRoomLogFunded = (guild: TGuildDatabaseDocument, component: I
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const fillInInventoryDataForGuildMember = async (member: IGuildMemberClient): Promise<void> => {
 | 
			
		||||
    const inventory = await getInventory(member._id.$oid, "PlayerLevel ActiveAvatarImageType");
 | 
			
		||||
    member.PlayerLevel = config.spoofMasteryRank == -1 ? inventory.PlayerLevel : config.spoofMasteryRank;
 | 
			
		||||
    member.ActiveAvatarImageType = inventory.ActiveAvatarImageType;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const createUniqueClanName = async (name: string): Promise<string> => {
 | 
			
		||||
    const initialDiscriminator = getRandomInt(0, 999);
 | 
			
		||||
    let discriminator = initialDiscriminator;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								src/types/friendTypes.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/types/friendTypes.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
import { Types } from "mongoose";
 | 
			
		||||
import { IMongoDate, IOid } from "./commonTypes";
 | 
			
		||||
 | 
			
		||||
export interface IFriendInfo {
 | 
			
		||||
    _id: IOid;
 | 
			
		||||
    DisplayName?: string;
 | 
			
		||||
    PlatformNames?: string[];
 | 
			
		||||
    PlatformAccountId?: string;
 | 
			
		||||
    Status?: number;
 | 
			
		||||
    ActiveAvatarImageType?: string;
 | 
			
		||||
    LastLogin?: IMongoDate;
 | 
			
		||||
    PlayerLevel?: number;
 | 
			
		||||
    Suffix?: number;
 | 
			
		||||
    Note?: string;
 | 
			
		||||
    Favorite?: boolean;
 | 
			
		||||
    NewRequest?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IFriendship {
 | 
			
		||||
    owner: Types.ObjectId;
 | 
			
		||||
    friend: Types.ObjectId;
 | 
			
		||||
    Note?: string;
 | 
			
		||||
    Favorite?: boolean;
 | 
			
		||||
}
 | 
			
		||||
@ -2,6 +2,7 @@ import { Types } from "mongoose";
 | 
			
		||||
import { IOid, IMongoDate } from "@/src/types/commonTypes";
 | 
			
		||||
import { IFusionTreasure, IMiscItem, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
			
		||||
import { IPictureFrameInfo } from "./shipTypes";
 | 
			
		||||
import { IFriendInfo } from "./friendTypes";
 | 
			
		||||
 | 
			
		||||
export interface IGuildClient {
 | 
			
		||||
    _id: IOid;
 | 
			
		||||
@ -104,21 +105,6 @@ export interface IGuildMemberDatabase {
 | 
			
		||||
    ShipDecorationsContributed?: ITypeCount[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IFriendInfo {
 | 
			
		||||
    _id: IOid;
 | 
			
		||||
    DisplayName?: string;
 | 
			
		||||
    PlatformNames?: string[];
 | 
			
		||||
    PlatformAccountId?: string;
 | 
			
		||||
    Status?: number;
 | 
			
		||||
    ActiveAvatarImageType?: string;
 | 
			
		||||
    LastLogin?: IMongoDate;
 | 
			
		||||
    PlayerLevel?: number;
 | 
			
		||||
    Suffix?: number;
 | 
			
		||||
    Note?: string;
 | 
			
		||||
    Favorite?: boolean;
 | 
			
		||||
    NewRequest?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GuildMemberInfo
 | 
			
		||||
export interface IGuildMemberClient extends IFriendInfo {
 | 
			
		||||
    Rank: number;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user