forked from OpenWF/SpaceNinjaServer
		
	feat: Kinematic Instant Messaging (#801)
This commit is contained in:
		
							parent
							
								
									f1c3dcbefc
								
							
						
					
					
						commit
						a8fb9095c5
					
				
							
								
								
									
										23
									
								
								src/controllers/api/clearDialogueHistoryController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/controllers/api/clearDialogueHistoryController.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					import { getInventory } from "@/src/services/inventoryService";
 | 
				
			||||||
 | 
					import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
				
			||||||
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const clearDialogueHistoryController: RequestHandler = async (req, res) => {
 | 
				
			||||||
 | 
					    const accountId = await getAccountIdForRequest(req);
 | 
				
			||||||
 | 
					    const inventory = await getInventory(accountId);
 | 
				
			||||||
 | 
					    const request = JSON.parse(String(req.body)) as IClearDialogueRequest;
 | 
				
			||||||
 | 
					    if (inventory.DialogueHistory && inventory.DialogueHistory.Dialogues) {
 | 
				
			||||||
 | 
					        for (const dialogueName of request.Dialogues) {
 | 
				
			||||||
 | 
					            const index = inventory.DialogueHistory.Dialogues.findIndex(x => x.DialogueName == dialogueName);
 | 
				
			||||||
 | 
					            if (index != -1) {
 | 
				
			||||||
 | 
					                inventory.DialogueHistory.Dialogues.splice(index, 1);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    await inventory.save();
 | 
				
			||||||
 | 
					    res.end();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface IClearDialogueRequest {
 | 
				
			||||||
 | 
					    Dialogues: string[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										85
									
								
								src/controllers/api/saveDialogueController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/controllers/api/saveDialogueController.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,85 @@
 | 
				
			|||||||
 | 
					import { getInventory } from "@/src/services/inventoryService";
 | 
				
			||||||
 | 
					import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
				
			||||||
 | 
					import { ICompletedDialogue } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
				
			||||||
 | 
					import { logger } from "@/src/utils/logger";
 | 
				
			||||||
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const saveDialogueController: RequestHandler = async (req, res) => {
 | 
				
			||||||
 | 
					    const accountId = await getAccountIdForRequest(req);
 | 
				
			||||||
 | 
					    const request = JSON.parse(String(req.body)) as SaveDialogueRequest;
 | 
				
			||||||
 | 
					    if ("YearIteration" in request) {
 | 
				
			||||||
 | 
					        const inventory = await getInventory(accountId);
 | 
				
			||||||
 | 
					        if (inventory.DialogueHistory) {
 | 
				
			||||||
 | 
					            inventory.DialogueHistory.YearIteration = request.YearIteration;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            inventory.DialogueHistory = { YearIteration: request.YearIteration };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        await inventory.save();
 | 
				
			||||||
 | 
					        res.end();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        const inventory = await getInventory(accountId);
 | 
				
			||||||
 | 
					        if (!inventory.DialogueHistory) {
 | 
				
			||||||
 | 
					            throw new Error("bad inventory state");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (request.QueuedDialogues.length != 0 || request.OtherDialogueInfos.length != 0) {
 | 
				
			||||||
 | 
					            logger.error(`saveDialogue request not fully handled: ${String(req.body)}`);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        inventory.DialogueHistory.Dialogues ??= [];
 | 
				
			||||||
 | 
					        let dialogue = inventory.DialogueHistory.Dialogues.find(x => x.DialogueName == request.DialogueName);
 | 
				
			||||||
 | 
					        if (!dialogue) {
 | 
				
			||||||
 | 
					            dialogue =
 | 
				
			||||||
 | 
					                inventory.DialogueHistory.Dialogues[
 | 
				
			||||||
 | 
					                    inventory.DialogueHistory.Dialogues.push({
 | 
				
			||||||
 | 
					                        Rank: 0,
 | 
				
			||||||
 | 
					                        Chemistry: 0,
 | 
				
			||||||
 | 
					                        AvailableDate: new Date(0),
 | 
				
			||||||
 | 
					                        AvailableGiftDate: new Date(0),
 | 
				
			||||||
 | 
					                        RankUpExpiry: new Date(0),
 | 
				
			||||||
 | 
					                        BountyChemExpiry: new Date(0),
 | 
				
			||||||
 | 
					                        Gifts: [],
 | 
				
			||||||
 | 
					                        Booleans: [],
 | 
				
			||||||
 | 
					                        Completed: [],
 | 
				
			||||||
 | 
					                        DialogueName: request.DialogueName
 | 
				
			||||||
 | 
					                    }) - 1
 | 
				
			||||||
 | 
					                ];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        dialogue.Rank = request.Rank;
 | 
				
			||||||
 | 
					        dialogue.Chemistry = request.Chemistry;
 | 
				
			||||||
 | 
					        //dialogue.QueuedDialogues = request.QueuedDialogues;
 | 
				
			||||||
 | 
					        for (const bool of request.Booleans) {
 | 
				
			||||||
 | 
					            dialogue.Booleans.push(bool);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (const bool of request.ResetBooleans) {
 | 
				
			||||||
 | 
					            const index = dialogue.Booleans.findIndex(x => x == bool);
 | 
				
			||||||
 | 
					            if (index != -1) {
 | 
				
			||||||
 | 
					                dialogue.Booleans.splice(index, 1);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        dialogue.Completed.push(request.Data);
 | 
				
			||||||
 | 
					        const tomorrowAt0Utc = (Math.trunc(Date.now() / (86400 * 1000)) + 1) * 86400 * 1000;
 | 
				
			||||||
 | 
					        dialogue.AvailableDate = new Date(tomorrowAt0Utc);
 | 
				
			||||||
 | 
					        await inventory.save();
 | 
				
			||||||
 | 
					        res.json({
 | 
				
			||||||
 | 
					            InventoryChanges: [],
 | 
				
			||||||
 | 
					            AvailableDate: { $date: { $numberLong: tomorrowAt0Utc.toString() } }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SaveDialogueRequest = SaveYearIterationRequest | SaveCompletedDialogueRequest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface SaveYearIterationRequest {
 | 
				
			||||||
 | 
					    YearIteration: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface SaveCompletedDialogueRequest {
 | 
				
			||||||
 | 
					    DialogueName: string;
 | 
				
			||||||
 | 
					    Rank: number;
 | 
				
			||||||
 | 
					    Chemistry: number;
 | 
				
			||||||
 | 
					    CompletionType: number;
 | 
				
			||||||
 | 
					    QueuedDialogues: string[]; // unsure
 | 
				
			||||||
 | 
					    Booleans: string[];
 | 
				
			||||||
 | 
					    ResetBooleans: string[];
 | 
				
			||||||
 | 
					    Data: ICompletedDialogue;
 | 
				
			||||||
 | 
					    OtherDialogueInfos: string[]; // unsure
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -47,7 +47,12 @@ import {
 | 
				
			|||||||
    ICrewShipPilotWeapon,
 | 
					    ICrewShipPilotWeapon,
 | 
				
			||||||
    IShipExterior,
 | 
					    IShipExterior,
 | 
				
			||||||
    IHelminthFoodRecord,
 | 
					    IHelminthFoodRecord,
 | 
				
			||||||
    ICrewShipMembersDatabase
 | 
					    ICrewShipMembersDatabase,
 | 
				
			||||||
 | 
					    IDialogueHistoryDatabase,
 | 
				
			||||||
 | 
					    IDialogueDatabase,
 | 
				
			||||||
 | 
					    IDialogueGift,
 | 
				
			||||||
 | 
					    ICompletedDialogue,
 | 
				
			||||||
 | 
					    IDialogueClient
 | 
				
			||||||
} from "../../types/inventoryTypes/inventoryTypes";
 | 
					} from "../../types/inventoryTypes/inventoryTypes";
 | 
				
			||||||
import { IOid } from "../../types/commonTypes";
 | 
					import { IOid } from "../../types/commonTypes";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
@ -710,6 +715,60 @@ crewShipSchema.set("toJSON", {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const dialogueGiftSchema = new Schema<IDialogueGift>(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Item: String,
 | 
				
			||||||
 | 
					        GiftedQuantity: Number
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    { _id: false }
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const completedDialogueSchema = new Schema<ICompletedDialogue>(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Id: { type: String, required: true },
 | 
				
			||||||
 | 
					        Booleans: { type: [String], required: true },
 | 
				
			||||||
 | 
					        Choices: { type: [Number], required: true }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    { _id: false }
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const dialogueSchema = new Schema<IDialogueDatabase>(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Rank: Number,
 | 
				
			||||||
 | 
					        Chemistry: Number,
 | 
				
			||||||
 | 
					        AvailableDate: Date,
 | 
				
			||||||
 | 
					        AvailableGiftDate: Date,
 | 
				
			||||||
 | 
					        RankUpExpiry: Date,
 | 
				
			||||||
 | 
					        BountyChemExpiry: Date,
 | 
				
			||||||
 | 
					        //QueuedDialogues: ???
 | 
				
			||||||
 | 
					        Gifts: { type: [dialogueGiftSchema], default: [] },
 | 
				
			||||||
 | 
					        Booleans: { type: [String], default: [] },
 | 
				
			||||||
 | 
					        Completed: { type: [completedDialogueSchema], default: [] },
 | 
				
			||||||
 | 
					        DialogueName: String
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    { _id: false }
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					dialogueSchema.set("toJSON", {
 | 
				
			||||||
 | 
					    virtuals: true,
 | 
				
			||||||
 | 
					    transform(_doc, ret) {
 | 
				
			||||||
 | 
					        const db = ret as IDialogueDatabase;
 | 
				
			||||||
 | 
					        const client = ret as IDialogueClient;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        client.AvailableDate = toMongoDate(db.AvailableDate);
 | 
				
			||||||
 | 
					        client.AvailableGiftDate = toMongoDate(db.AvailableGiftDate);
 | 
				
			||||||
 | 
					        client.RankUpExpiry = toMongoDate(db.RankUpExpiry);
 | 
				
			||||||
 | 
					        client.BountyChemExpiry = toMongoDate(db.BountyChemExpiry);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const dialogueHistorySchema = new Schema<IDialogueHistoryDatabase>(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        YearIteration: { type: Number, required: true },
 | 
				
			||||||
 | 
					        Dialogues: { type: [dialogueSchema], required: false }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    { _id: false }
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
					const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        accountOwnerId: Schema.Types.ObjectId,
 | 
					        accountOwnerId: Schema.Types.ObjectId,
 | 
				
			||||||
@ -1069,7 +1128,9 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
				
			|||||||
        //Grustag three
 | 
					        //Grustag three
 | 
				
			||||||
        DeathSquadable: Boolean,
 | 
					        DeathSquadable: Boolean,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        EndlessXP: { type: [endlessXpProgressSchema], default: undefined }
 | 
					        EndlessXP: { type: [endlessXpProgressSchema], default: undefined },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        DialogueHistory: dialogueHistorySchema
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    { timestamps: { createdAt: "Created" } }
 | 
					    { timestamps: { createdAt: "Created" } }
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,7 @@ import { archonFusionController } from "@/src/controllers/api/archonFusionContro
 | 
				
			|||||||
import { artifactsController } from "../controllers/api/artifactsController";
 | 
					import { artifactsController } from "../controllers/api/artifactsController";
 | 
				
			||||||
import { checkDailyMissionBonusController } from "@/src/controllers/api/checkDailyMissionBonusController";
 | 
					import { checkDailyMissionBonusController } from "@/src/controllers/api/checkDailyMissionBonusController";
 | 
				
			||||||
import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompletedRecipeController";
 | 
					import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompletedRecipeController";
 | 
				
			||||||
 | 
					import { clearDialogueHistoryController } from "@/src/controllers/api/clearDialogueHistoryController";
 | 
				
			||||||
import { createGuildController } from "@/src/controllers/api/createGuildController";
 | 
					import { createGuildController } from "@/src/controllers/api/createGuildController";
 | 
				
			||||||
import { creditsController } from "@/src/controllers/api/creditsController";
 | 
					import { creditsController } from "@/src/controllers/api/creditsController";
 | 
				
			||||||
import { deleteSessionController } from "@/src/controllers/api/deleteSessionController";
 | 
					import { deleteSessionController } from "@/src/controllers/api/deleteSessionController";
 | 
				
			||||||
@ -52,6 +53,7 @@ import { projectionManagerController } from "../controllers/api/projectionManage
 | 
				
			|||||||
import { purchaseController } from "@/src/controllers/api/purchaseController";
 | 
					import { purchaseController } from "@/src/controllers/api/purchaseController";
 | 
				
			||||||
import { queueDojoComponentDestructionController } from "@/src/controllers/api/queueDojoComponentDestructionController";
 | 
					import { queueDojoComponentDestructionController } from "@/src/controllers/api/queueDojoComponentDestructionController";
 | 
				
			||||||
import { rerollRandomModController } from "@/src/controllers/api/rerollRandomModController";
 | 
					import { rerollRandomModController } from "@/src/controllers/api/rerollRandomModController";
 | 
				
			||||||
 | 
					import { saveDialogueController } from "@/src/controllers/api/saveDialogueController";
 | 
				
			||||||
import { saveLoadoutController } from "@/src/controllers/api/saveLoadout";
 | 
					import { saveLoadoutController } from "@/src/controllers/api/saveLoadout";
 | 
				
			||||||
import { sellController } from "@/src/controllers/api/sellController";
 | 
					import { sellController } from "@/src/controllers/api/sellController";
 | 
				
			||||||
import { setActiveQuestController } from "@/src/controllers/api/setActiveQuestController";
 | 
					import { setActiveQuestController } from "@/src/controllers/api/setActiveQuestController";
 | 
				
			||||||
@ -118,6 +120,7 @@ apiRouter.post("/arcaneCommon.php", arcaneCommonController);
 | 
				
			|||||||
apiRouter.post("/archonFusion.php", archonFusionController);
 | 
					apiRouter.post("/archonFusion.php", archonFusionController);
 | 
				
			||||||
apiRouter.post("/artifacts.php", artifactsController);
 | 
					apiRouter.post("/artifacts.php", artifactsController);
 | 
				
			||||||
apiRouter.post("/claimCompletedRecipe.php", claimCompletedRecipeController);
 | 
					apiRouter.post("/claimCompletedRecipe.php", claimCompletedRecipeController);
 | 
				
			||||||
 | 
					apiRouter.post("/clearDialogueHistory.php", clearDialogueHistoryController);
 | 
				
			||||||
apiRouter.post("/createGuild.php", createGuildController);
 | 
					apiRouter.post("/createGuild.php", createGuildController);
 | 
				
			||||||
apiRouter.post("/endlessXp.php", endlessXpController);
 | 
					apiRouter.post("/endlessXp.php", endlessXpController);
 | 
				
			||||||
apiRouter.post("/evolveWeapon.php", evolveWeaponController);
 | 
					apiRouter.post("/evolveWeapon.php", evolveWeaponController);
 | 
				
			||||||
@ -142,6 +145,7 @@ apiRouter.post("/playerSkills.php", playerSkillsController);
 | 
				
			|||||||
apiRouter.post("/projectionManager.php", projectionManagerController);
 | 
					apiRouter.post("/projectionManager.php", projectionManagerController);
 | 
				
			||||||
apiRouter.post("/purchase.php", purchaseController);
 | 
					apiRouter.post("/purchase.php", purchaseController);
 | 
				
			||||||
apiRouter.post("/rerollRandomMod.php", rerollRandomModController);
 | 
					apiRouter.post("/rerollRandomMod.php", rerollRandomModController);
 | 
				
			||||||
 | 
					apiRouter.post("/saveDialogue.php", saveDialogueController);
 | 
				
			||||||
apiRouter.post("/saveLoadout.php", saveLoadoutController);
 | 
					apiRouter.post("/saveLoadout.php", saveLoadoutController);
 | 
				
			||||||
apiRouter.post("/sell.php", sellController);
 | 
					apiRouter.post("/sell.php", sellController);
 | 
				
			||||||
apiRouter.post("/setEquippedInstrument.php", setEquippedInstrumentController);
 | 
					apiRouter.post("/setEquippedInstrument.php", setEquippedInstrumentController);
 | 
				
			||||||
 | 
				
			|||||||
@ -306,6 +306,7 @@ export interface IInventoryResponse extends IDailyAffiliations {
 | 
				
			|||||||
    Harvestable: boolean;
 | 
					    Harvestable: boolean;
 | 
				
			||||||
    DeathSquadable: boolean;
 | 
					    DeathSquadable: boolean;
 | 
				
			||||||
    EndlessXP?: IEndlessXpProgress[];
 | 
					    EndlessXP?: IEndlessXpProgress[];
 | 
				
			||||||
 | 
					    DialogueHistory?: IDialogueHistoryDatabase;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IAffiliation {
 | 
					export interface IAffiliation {
 | 
				
			||||||
@ -948,3 +949,46 @@ export interface IEndlessXpProgress {
 | 
				
			|||||||
    Category: TEndlessXpCategory;
 | 
					    Category: TEndlessXpCategory;
 | 
				
			||||||
    Choices: string[];
 | 
					    Choices: string[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IDialogueHistoryClient {
 | 
				
			||||||
 | 
					    YearIteration: number;
 | 
				
			||||||
 | 
					    Dialogues?: IDialogueClient[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IDialogueHistoryDatabase {
 | 
				
			||||||
 | 
					    YearIteration: number;
 | 
				
			||||||
 | 
					    Dialogues?: IDialogueDatabase[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IDialogueClient {
 | 
				
			||||||
 | 
					    Rank: number;
 | 
				
			||||||
 | 
					    Chemistry: number;
 | 
				
			||||||
 | 
					    AvailableDate: IMongoDate;
 | 
				
			||||||
 | 
					    AvailableGiftDate: IMongoDate;
 | 
				
			||||||
 | 
					    RankUpExpiry: IMongoDate;
 | 
				
			||||||
 | 
					    BountyChemExpiry: IMongoDate;
 | 
				
			||||||
 | 
					    //QueuedDialogues: any[];
 | 
				
			||||||
 | 
					    Gifts: IDialogueGift[];
 | 
				
			||||||
 | 
					    Booleans: string[];
 | 
				
			||||||
 | 
					    Completed: ICompletedDialogue[];
 | 
				
			||||||
 | 
					    DialogueName: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IDialogueDatabase
 | 
				
			||||||
 | 
					    extends Omit<IDialogueClient, "AvailableDate" | "AvailableGiftDate" | "RankUpExpiry" | "BountyChemExpiry"> {
 | 
				
			||||||
 | 
					    AvailableDate: Date;
 | 
				
			||||||
 | 
					    AvailableGiftDate: Date;
 | 
				
			||||||
 | 
					    RankUpExpiry: Date;
 | 
				
			||||||
 | 
					    BountyChemExpiry: Date;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IDialogueGift {
 | 
				
			||||||
 | 
					    Item: string;
 | 
				
			||||||
 | 
					    GiftedQuantity: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface ICompletedDialogue {
 | 
				
			||||||
 | 
					    Id: string;
 | 
				
			||||||
 | 
					    Booleans: string[];
 | 
				
			||||||
 | 
					    Choices: number[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user