import { ServerContentBase } from "../ContentBase.ts"; import NameDictionary from "./dict.ts"; import Profile from "./profile.ts"; import { SelfAccount, type RecNetAccount } from "./types/profile.ts"; import Command from "./../commands/command.ts"; import z from "zod"; import { PlatformMask, PlatformType, ProfileRole } from "../platforms/types.ts"; const profiles: Map = new Map(); class ProfileManagerBase extends ServerContentBase { static profilesKey = "profiles"; static profileByNameKey = "profileName"; async #getUnusedId() { let id = Math.round(Math.random() * 2_147_483_647); if (await this.get(id)) id = await this.#getUnusedId(); return id; } getActiveProfileReferences() { return profiles.values().toArray(); } async #getUnusedUsername() { const adjective = NameDictionary.Adjectives[Math.floor(Math.random() * NameDictionary.Adjectives.length)]; const noun = NameDictionary.Nouns[Math.floor(Math.random() * NameDictionary.Nouns.length)]; const discriminator = Math.round(Math.random() * 1000); let username = `${adjective}${noun}#${discriminator}`; if (await this.getByUsername(username)) username = await this.#getUnusedUsername(); return username; } async #getUsernameDefault(username: string) { const prof = await this.getByUsername(username); if (!prof) return username; else return await this.#getUnusedUsername(); } async create(platform: PlatformType, platformId: string, username?: string) { const id = await this.#getUnusedId(); const newUsername = username ? await this.#getUsernameDefault(username) : await this.#getUnusedUsername(); const newProfile: RecNetAccount = { accountId: id, username: newUsername, displayName: newUsername, platforms: PlatformMask.None, profileImage: "DefaultProfileImage.png", createdAt: new Date() } await this.kv.getKv().set([ ProfileManagerBase.profilesKey, id ], newProfile); await this.kv.getKv().set([ ProfileManagerBase.profilesKey, newUsername ], id); await this.server.Platforms.addCachedLogin(platform, platformId, id); return this.get(id); } async get(id: number) { const profile = profiles.get(id); if (!profile) { const info = await this.kv.getKv().get([ ProfileManagerBase.profilesKey, id ]); if (!info.value) return null; const prof = new Profile(info.value, this.kv, this.server); profiles.set(id, prof); return prof; } return profile; } async getMany(...ids: number[]) { return (await Promise.all(ids.map(id => this.get(id)))).filter(prof => prof !== null); } async getAll() { const keys = this.kv.getKv().list({ prefix: [ ProfileManagerBase.profilesKey ] }); const awaitedKeys = await Array.fromAsync(keys); return awaitedKeys.map(entry => entry.key).map(val => val[1]).filter(val => typeof val == 'number'); } async getByUsername(username: string) { const id = (await this.kv.getKv().get([ ProfileManagerBase.profilesKey, username ])).value; if (typeof id == 'number') return this.get(id); else return id; } override start() { this.server.Commands.addRootCommand(new Command({ key: ['account', 'profile', 'acc', 'prof'], subcommands: [ new Command({ key: ['get', 'g', 'fetch', 'f'], exec: async (id: number) => { const prof = await this.get(id); if (!prof) return prof; else return await prof.export(); }, zod: z.tuple([ z.string().transform(Number) ]), help: 'Fetch a profile: ' }), new Command({ key: ['getall', 'listall', 'fetchall', 'all', 'a'], exec: async () => { const ids = await this.getAll(); return ids; }, zod: z.tuple([]), help: 'Fetch all profile IDs' }), new Command({ key: ['getrole', 'gr'], exec: async (id: number) => { const profile = await this.get(id); if (!profile) return new Error("No such profile"); else return await profile.getRole(); }, zod: z.tuple([ z.string().transform(Number) ]), help: 'Set the profile role: ' }), new Command({ key: ['setrole', 'sr'], exec: async (id: number, role: ProfileRole) => { const profile = await this.get(id); if (!profile) return new Error("No such profile"); else return await profile.setRole(role); }, zod: z.tuple([ z.string().transform(Number), z.string() ]), help: 'Set the profile role: ' }) ] })); } } export default ProfileManagerBase;