diff --git a/TypeScript/10ScopesAndTypes/types/callbacks/SaveCallbacks.d.ts b/TypeScript/10ScopesAndTypes/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/10ScopesAndTypes/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/10ScopesAndTypes/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/10ScopesAndTypes/types/controllers/DialogueController.d.ts b/TypeScript/10ScopesAndTypes/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/10ScopesAndTypes/types/controllers/DialogueController.d.ts +++ b/TypeScript/10ScopesAndTypes/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/10ScopesAndTypes/types/helpers/ProfileHelper.d.ts b/TypeScript/10ScopesAndTypes/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/10ScopesAndTypes/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/10ScopesAndTypes/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/10ScopesAndTypes/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/10ScopesAndTypes/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/10ScopesAndTypes/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/10ScopesAndTypes/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/10ScopesAndTypes/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/10ScopesAndTypes/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/10ScopesAndTypes/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/10ScopesAndTypes/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/10ScopesAndTypes/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/10ScopesAndTypes/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/10ScopesAndTypes/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/10ScopesAndTypes/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/10ScopesAndTypes/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/10ScopesAndTypes/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/10ScopesAndTypes/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/10ScopesAndTypes/types/models/enums/ConfigTypes.d.ts b/TypeScript/10ScopesAndTypes/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/10ScopesAndTypes/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/10ScopesAndTypes/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/10ScopesAndTypes/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/10ScopesAndTypes/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/10ScopesAndTypes/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/10ScopesAndTypes/types/services/BackupService.d.ts b/TypeScript/10ScopesAndTypes/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/10ScopesAndTypes/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/11BundleLoadingSample/types/callbacks/SaveCallbacks.d.ts b/TypeScript/11BundleLoadingSample/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/11BundleLoadingSample/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/11BundleLoadingSample/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/11BundleLoadingSample/types/controllers/DialogueController.d.ts b/TypeScript/11BundleLoadingSample/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/11BundleLoadingSample/types/controllers/DialogueController.d.ts +++ b/TypeScript/11BundleLoadingSample/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/11BundleLoadingSample/types/helpers/ProfileHelper.d.ts b/TypeScript/11BundleLoadingSample/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/11BundleLoadingSample/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/11BundleLoadingSample/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/11BundleLoadingSample/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/11BundleLoadingSample/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/11BundleLoadingSample/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/11BundleLoadingSample/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/11BundleLoadingSample/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/11BundleLoadingSample/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/11BundleLoadingSample/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/11BundleLoadingSample/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/11BundleLoadingSample/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/11BundleLoadingSample/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/11BundleLoadingSample/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/11BundleLoadingSample/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/11BundleLoadingSample/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/11BundleLoadingSample/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/11BundleLoadingSample/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/11BundleLoadingSample/types/models/enums/ConfigTypes.d.ts b/TypeScript/11BundleLoadingSample/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/11BundleLoadingSample/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/11BundleLoadingSample/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/11BundleLoadingSample/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/11BundleLoadingSample/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/11BundleLoadingSample/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/11BundleLoadingSample/types/services/BackupService.d.ts b/TypeScript/11BundleLoadingSample/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/11BundleLoadingSample/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/12ClassExtensionOverride/types/callbacks/SaveCallbacks.d.ts b/TypeScript/12ClassExtensionOverride/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/12ClassExtensionOverride/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/12ClassExtensionOverride/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/12ClassExtensionOverride/types/controllers/DialogueController.d.ts b/TypeScript/12ClassExtensionOverride/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/12ClassExtensionOverride/types/controllers/DialogueController.d.ts +++ b/TypeScript/12ClassExtensionOverride/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/12ClassExtensionOverride/types/helpers/ProfileHelper.d.ts b/TypeScript/12ClassExtensionOverride/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/12ClassExtensionOverride/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/12ClassExtensionOverride/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/12ClassExtensionOverride/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/12ClassExtensionOverride/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/12ClassExtensionOverride/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/12ClassExtensionOverride/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/12ClassExtensionOverride/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/12ClassExtensionOverride/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/12ClassExtensionOverride/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/12ClassExtensionOverride/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/12ClassExtensionOverride/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/12ClassExtensionOverride/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/12ClassExtensionOverride/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/12ClassExtensionOverride/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/12ClassExtensionOverride/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/12ClassExtensionOverride/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/12ClassExtensionOverride/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/12ClassExtensionOverride/types/models/enums/ConfigTypes.d.ts b/TypeScript/12ClassExtensionOverride/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/12ClassExtensionOverride/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/12ClassExtensionOverride/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/12ClassExtensionOverride/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/12ClassExtensionOverride/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/12ClassExtensionOverride/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/12ClassExtensionOverride/types/services/BackupService.d.ts b/TypeScript/12ClassExtensionOverride/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/12ClassExtensionOverride/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/13.1AddTraderWithAssortJSON/types/callbacks/SaveCallbacks.d.ts b/TypeScript/13.1AddTraderWithAssortJSON/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/13.1AddTraderWithAssortJSON/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/13.1AddTraderWithAssortJSON/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/13.1AddTraderWithAssortJSON/types/controllers/DialogueController.d.ts b/TypeScript/13.1AddTraderWithAssortJSON/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/13.1AddTraderWithAssortJSON/types/controllers/DialogueController.d.ts +++ b/TypeScript/13.1AddTraderWithAssortJSON/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/13.1AddTraderWithAssortJSON/types/helpers/ProfileHelper.d.ts b/TypeScript/13.1AddTraderWithAssortJSON/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/13.1AddTraderWithAssortJSON/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/13.1AddTraderWithAssortJSON/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/13.1AddTraderWithAssortJSON/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/13.1AddTraderWithAssortJSON/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/13.1AddTraderWithAssortJSON/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/13.1AddTraderWithAssortJSON/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/13.1AddTraderWithAssortJSON/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/13.1AddTraderWithAssortJSON/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/13.1AddTraderWithAssortJSON/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/13.1AddTraderWithAssortJSON/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/13.1AddTraderWithAssortJSON/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/13.1AddTraderWithAssortJSON/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/13.1AddTraderWithAssortJSON/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/13.1AddTraderWithAssortJSON/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/13.1AddTraderWithAssortJSON/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/13.1AddTraderWithAssortJSON/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/13.1AddTraderWithAssortJSON/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/13.1AddTraderWithAssortJSON/types/models/enums/ConfigTypes.d.ts b/TypeScript/13.1AddTraderWithAssortJSON/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/13.1AddTraderWithAssortJSON/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/13.1AddTraderWithAssortJSON/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/13.1AddTraderWithAssortJSON/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/13.1AddTraderWithAssortJSON/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/13.1AddTraderWithAssortJSON/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/13.1AddTraderWithAssortJSON/types/services/BackupService.d.ts b/TypeScript/13.1AddTraderWithAssortJSON/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/13.1AddTraderWithAssortJSON/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/13AddTrader/types/callbacks/SaveCallbacks.d.ts b/TypeScript/13AddTrader/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/13AddTrader/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/13AddTrader/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/13AddTrader/types/controllers/DialogueController.d.ts b/TypeScript/13AddTrader/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/13AddTrader/types/controllers/DialogueController.d.ts +++ b/TypeScript/13AddTrader/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/13AddTrader/types/helpers/ProfileHelper.d.ts b/TypeScript/13AddTrader/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/13AddTrader/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/13AddTrader/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/13AddTrader/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/13AddTrader/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/13AddTrader/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/13AddTrader/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/13AddTrader/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/13AddTrader/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/13AddTrader/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/13AddTrader/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/13AddTrader/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/13AddTrader/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/13AddTrader/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/13AddTrader/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/13AddTrader/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/13AddTrader/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/13AddTrader/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/13AddTrader/types/models/enums/ConfigTypes.d.ts b/TypeScript/13AddTrader/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/13AddTrader/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/13AddTrader/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/13AddTrader/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/13AddTrader/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/13AddTrader/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/13AddTrader/types/services/BackupService.d.ts b/TypeScript/13AddTrader/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/13AddTrader/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/14AfterDBLoadHook/types/callbacks/SaveCallbacks.d.ts b/TypeScript/14AfterDBLoadHook/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/14AfterDBLoadHook/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/14AfterDBLoadHook/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/14AfterDBLoadHook/types/controllers/DialogueController.d.ts b/TypeScript/14AfterDBLoadHook/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/14AfterDBLoadHook/types/controllers/DialogueController.d.ts +++ b/TypeScript/14AfterDBLoadHook/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/14AfterDBLoadHook/types/helpers/ProfileHelper.d.ts b/TypeScript/14AfterDBLoadHook/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/14AfterDBLoadHook/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/14AfterDBLoadHook/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/14AfterDBLoadHook/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/14AfterDBLoadHook/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/14AfterDBLoadHook/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/14AfterDBLoadHook/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/14AfterDBLoadHook/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/14AfterDBLoadHook/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/14AfterDBLoadHook/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/14AfterDBLoadHook/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/14AfterDBLoadHook/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/14AfterDBLoadHook/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/14AfterDBLoadHook/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/14AfterDBLoadHook/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/14AfterDBLoadHook/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/14AfterDBLoadHook/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/14AfterDBLoadHook/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/14AfterDBLoadHook/types/models/enums/ConfigTypes.d.ts b/TypeScript/14AfterDBLoadHook/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/14AfterDBLoadHook/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/14AfterDBLoadHook/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/14AfterDBLoadHook/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/14AfterDBLoadHook/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/14AfterDBLoadHook/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/14AfterDBLoadHook/types/services/BackupService.d.ts b/TypeScript/14AfterDBLoadHook/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/14AfterDBLoadHook/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/15HttpListenerExample/types/callbacks/SaveCallbacks.d.ts b/TypeScript/15HttpListenerExample/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/15HttpListenerExample/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/15HttpListenerExample/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/15HttpListenerExample/types/controllers/DialogueController.d.ts b/TypeScript/15HttpListenerExample/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/15HttpListenerExample/types/controllers/DialogueController.d.ts +++ b/TypeScript/15HttpListenerExample/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/15HttpListenerExample/types/helpers/ProfileHelper.d.ts b/TypeScript/15HttpListenerExample/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/15HttpListenerExample/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/15HttpListenerExample/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/15HttpListenerExample/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/15HttpListenerExample/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/15HttpListenerExample/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/15HttpListenerExample/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/15HttpListenerExample/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/15HttpListenerExample/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/15HttpListenerExample/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/15HttpListenerExample/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/15HttpListenerExample/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/15HttpListenerExample/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/15HttpListenerExample/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/15HttpListenerExample/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/15HttpListenerExample/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/15HttpListenerExample/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/15HttpListenerExample/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/15HttpListenerExample/types/models/enums/ConfigTypes.d.ts b/TypeScript/15HttpListenerExample/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/15HttpListenerExample/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/15HttpListenerExample/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/15HttpListenerExample/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/15HttpListenerExample/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/15HttpListenerExample/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/15HttpListenerExample/types/services/BackupService.d.ts b/TypeScript/15HttpListenerExample/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/15HttpListenerExample/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/16ImporterUtil/types/callbacks/SaveCallbacks.d.ts b/TypeScript/16ImporterUtil/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/16ImporterUtil/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/16ImporterUtil/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/16ImporterUtil/types/controllers/DialogueController.d.ts b/TypeScript/16ImporterUtil/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/16ImporterUtil/types/controllers/DialogueController.d.ts +++ b/TypeScript/16ImporterUtil/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/16ImporterUtil/types/helpers/ProfileHelper.d.ts b/TypeScript/16ImporterUtil/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/16ImporterUtil/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/16ImporterUtil/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/16ImporterUtil/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/16ImporterUtil/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/16ImporterUtil/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/16ImporterUtil/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/16ImporterUtil/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/16ImporterUtil/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/16ImporterUtil/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/16ImporterUtil/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/16ImporterUtil/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/16ImporterUtil/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/16ImporterUtil/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/16ImporterUtil/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/16ImporterUtil/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/16ImporterUtil/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/16ImporterUtil/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/16ImporterUtil/types/models/enums/ConfigTypes.d.ts b/TypeScript/16ImporterUtil/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/16ImporterUtil/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/16ImporterUtil/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/16ImporterUtil/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/16ImporterUtil/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/16ImporterUtil/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/16ImporterUtil/types/services/BackupService.d.ts b/TypeScript/16ImporterUtil/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/16ImporterUtil/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/17AsyncImporterWithDependency1/types/callbacks/SaveCallbacks.d.ts b/TypeScript/17AsyncImporterWithDependency1/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/17AsyncImporterWithDependency1/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/17AsyncImporterWithDependency1/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/17AsyncImporterWithDependency1/types/controllers/DialogueController.d.ts b/TypeScript/17AsyncImporterWithDependency1/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/17AsyncImporterWithDependency1/types/controllers/DialogueController.d.ts +++ b/TypeScript/17AsyncImporterWithDependency1/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/17AsyncImporterWithDependency1/types/helpers/ProfileHelper.d.ts b/TypeScript/17AsyncImporterWithDependency1/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/17AsyncImporterWithDependency1/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/17AsyncImporterWithDependency1/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/17AsyncImporterWithDependency1/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/17AsyncImporterWithDependency1/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/17AsyncImporterWithDependency1/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/17AsyncImporterWithDependency1/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/17AsyncImporterWithDependency1/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/17AsyncImporterWithDependency1/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/17AsyncImporterWithDependency1/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/17AsyncImporterWithDependency1/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/17AsyncImporterWithDependency1/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/17AsyncImporterWithDependency1/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/17AsyncImporterWithDependency1/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/17AsyncImporterWithDependency1/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/17AsyncImporterWithDependency1/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/17AsyncImporterWithDependency1/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/17AsyncImporterWithDependency1/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/17AsyncImporterWithDependency1/types/models/enums/ConfigTypes.d.ts b/TypeScript/17AsyncImporterWithDependency1/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/17AsyncImporterWithDependency1/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/17AsyncImporterWithDependency1/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/17AsyncImporterWithDependency1/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/17AsyncImporterWithDependency1/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/17AsyncImporterWithDependency1/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/17AsyncImporterWithDependency1/types/services/BackupService.d.ts b/TypeScript/17AsyncImporterWithDependency1/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/17AsyncImporterWithDependency1/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/17AsyncImporterWithDependency2/types/callbacks/SaveCallbacks.d.ts b/TypeScript/17AsyncImporterWithDependency2/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/17AsyncImporterWithDependency2/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/17AsyncImporterWithDependency2/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/17AsyncImporterWithDependency2/types/controllers/DialogueController.d.ts b/TypeScript/17AsyncImporterWithDependency2/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/17AsyncImporterWithDependency2/types/controllers/DialogueController.d.ts +++ b/TypeScript/17AsyncImporterWithDependency2/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/17AsyncImporterWithDependency2/types/helpers/ProfileHelper.d.ts b/TypeScript/17AsyncImporterWithDependency2/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/17AsyncImporterWithDependency2/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/17AsyncImporterWithDependency2/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/17AsyncImporterWithDependency2/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/17AsyncImporterWithDependency2/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/17AsyncImporterWithDependency2/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/17AsyncImporterWithDependency2/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/17AsyncImporterWithDependency2/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/17AsyncImporterWithDependency2/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/17AsyncImporterWithDependency2/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/17AsyncImporterWithDependency2/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/17AsyncImporterWithDependency2/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/17AsyncImporterWithDependency2/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/17AsyncImporterWithDependency2/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/17AsyncImporterWithDependency2/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/17AsyncImporterWithDependency2/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/17AsyncImporterWithDependency2/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/17AsyncImporterWithDependency2/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/17AsyncImporterWithDependency2/types/models/enums/ConfigTypes.d.ts b/TypeScript/17AsyncImporterWithDependency2/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/17AsyncImporterWithDependency2/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/17AsyncImporterWithDependency2/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/17AsyncImporterWithDependency2/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/17AsyncImporterWithDependency2/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/17AsyncImporterWithDependency2/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/17AsyncImporterWithDependency2/types/services/BackupService.d.ts b/TypeScript/17AsyncImporterWithDependency2/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/17AsyncImporterWithDependency2/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/18.1CustomItemServiceLootBox/types/callbacks/SaveCallbacks.d.ts b/TypeScript/18.1CustomItemServiceLootBox/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/18.1CustomItemServiceLootBox/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/18.1CustomItemServiceLootBox/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/18.1CustomItemServiceLootBox/types/controllers/DialogueController.d.ts b/TypeScript/18.1CustomItemServiceLootBox/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/18.1CustomItemServiceLootBox/types/controllers/DialogueController.d.ts +++ b/TypeScript/18.1CustomItemServiceLootBox/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/18.1CustomItemServiceLootBox/types/helpers/ProfileHelper.d.ts b/TypeScript/18.1CustomItemServiceLootBox/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/18.1CustomItemServiceLootBox/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/18.1CustomItemServiceLootBox/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/18.1CustomItemServiceLootBox/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/18.1CustomItemServiceLootBox/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/18.1CustomItemServiceLootBox/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/18.1CustomItemServiceLootBox/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/18.1CustomItemServiceLootBox/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/18.1CustomItemServiceLootBox/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/18.1CustomItemServiceLootBox/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/18.1CustomItemServiceLootBox/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/18.1CustomItemServiceLootBox/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/18.1CustomItemServiceLootBox/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/18.1CustomItemServiceLootBox/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/18.1CustomItemServiceLootBox/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/18.1CustomItemServiceLootBox/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/18.1CustomItemServiceLootBox/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/18.1CustomItemServiceLootBox/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/18.1CustomItemServiceLootBox/types/models/enums/ConfigTypes.d.ts b/TypeScript/18.1CustomItemServiceLootBox/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/18.1CustomItemServiceLootBox/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/18.1CustomItemServiceLootBox/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/18.1CustomItemServiceLootBox/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/18.1CustomItemServiceLootBox/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/18.1CustomItemServiceLootBox/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/18.1CustomItemServiceLootBox/types/services/BackupService.d.ts b/TypeScript/18.1CustomItemServiceLootBox/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/18.1CustomItemServiceLootBox/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/18CustomItemService/types/callbacks/SaveCallbacks.d.ts b/TypeScript/18CustomItemService/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/18CustomItemService/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/18CustomItemService/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/18CustomItemService/types/controllers/DialogueController.d.ts b/TypeScript/18CustomItemService/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/18CustomItemService/types/controllers/DialogueController.d.ts +++ b/TypeScript/18CustomItemService/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/18CustomItemService/types/helpers/ProfileHelper.d.ts b/TypeScript/18CustomItemService/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/18CustomItemService/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/18CustomItemService/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/18CustomItemService/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/18CustomItemService/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/18CustomItemService/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/18CustomItemService/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/18CustomItemService/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/18CustomItemService/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/18CustomItemService/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/18CustomItemService/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/18CustomItemService/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/18CustomItemService/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/18CustomItemService/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/18CustomItemService/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/18CustomItemService/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/18CustomItemService/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/18CustomItemService/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/18CustomItemService/types/models/enums/ConfigTypes.d.ts b/TypeScript/18CustomItemService/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/18CustomItemService/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/18CustomItemService/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/18CustomItemService/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/18CustomItemService/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/18CustomItemService/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/18CustomItemService/types/services/BackupService.d.ts b/TypeScript/18CustomItemService/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/18CustomItemService/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/19UseExternalLibraries/types/callbacks/SaveCallbacks.d.ts b/TypeScript/19UseExternalLibraries/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/19UseExternalLibraries/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/19UseExternalLibraries/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/19UseExternalLibraries/types/controllers/DialogueController.d.ts b/TypeScript/19UseExternalLibraries/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/19UseExternalLibraries/types/controllers/DialogueController.d.ts +++ b/TypeScript/19UseExternalLibraries/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/19UseExternalLibraries/types/helpers/ProfileHelper.d.ts b/TypeScript/19UseExternalLibraries/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/19UseExternalLibraries/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/19UseExternalLibraries/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/19UseExternalLibraries/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/19UseExternalLibraries/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/19UseExternalLibraries/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/19UseExternalLibraries/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/19UseExternalLibraries/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/19UseExternalLibraries/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/19UseExternalLibraries/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/19UseExternalLibraries/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/19UseExternalLibraries/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/19UseExternalLibraries/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/19UseExternalLibraries/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/19UseExternalLibraries/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/19UseExternalLibraries/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/19UseExternalLibraries/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/19UseExternalLibraries/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/19UseExternalLibraries/types/models/enums/ConfigTypes.d.ts b/TypeScript/19UseExternalLibraries/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/19UseExternalLibraries/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/19UseExternalLibraries/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/19UseExternalLibraries/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/19UseExternalLibraries/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/19UseExternalLibraries/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/19UseExternalLibraries/types/services/BackupService.d.ts b/TypeScript/19UseExternalLibraries/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/19UseExternalLibraries/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/1LogToConsole/types/callbacks/SaveCallbacks.d.ts b/TypeScript/1LogToConsole/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/1LogToConsole/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/1LogToConsole/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/1LogToConsole/types/controllers/DialogueController.d.ts b/TypeScript/1LogToConsole/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/1LogToConsole/types/controllers/DialogueController.d.ts +++ b/TypeScript/1LogToConsole/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/1LogToConsole/types/helpers/ProfileHelper.d.ts b/TypeScript/1LogToConsole/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/1LogToConsole/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/1LogToConsole/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/1LogToConsole/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/1LogToConsole/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/1LogToConsole/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/1LogToConsole/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/1LogToConsole/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/1LogToConsole/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/1LogToConsole/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/1LogToConsole/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/1LogToConsole/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/1LogToConsole/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/1LogToConsole/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/1LogToConsole/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/1LogToConsole/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/1LogToConsole/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/1LogToConsole/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/1LogToConsole/types/models/enums/ConfigTypes.d.ts b/TypeScript/1LogToConsole/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/1LogToConsole/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/1LogToConsole/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/1LogToConsole/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/1LogToConsole/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/1LogToConsole/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/1LogToConsole/types/services/BackupService.d.ts b/TypeScript/1LogToConsole/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/1LogToConsole/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/20CustomChatBot/types/callbacks/SaveCallbacks.d.ts b/TypeScript/20CustomChatBot/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/20CustomChatBot/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/20CustomChatBot/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/20CustomChatBot/types/controllers/DialogueController.d.ts b/TypeScript/20CustomChatBot/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/20CustomChatBot/types/controllers/DialogueController.d.ts +++ b/TypeScript/20CustomChatBot/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/20CustomChatBot/types/helpers/ProfileHelper.d.ts b/TypeScript/20CustomChatBot/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/20CustomChatBot/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/20CustomChatBot/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/20CustomChatBot/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/20CustomChatBot/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/20CustomChatBot/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/20CustomChatBot/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/20CustomChatBot/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/20CustomChatBot/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/20CustomChatBot/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/20CustomChatBot/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/20CustomChatBot/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/20CustomChatBot/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/20CustomChatBot/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/20CustomChatBot/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/20CustomChatBot/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/20CustomChatBot/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/20CustomChatBot/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/20CustomChatBot/types/models/enums/ConfigTypes.d.ts b/TypeScript/20CustomChatBot/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/20CustomChatBot/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/20CustomChatBot/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/20CustomChatBot/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/20CustomChatBot/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/20CustomChatBot/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/20CustomChatBot/types/services/BackupService.d.ts b/TypeScript/20CustomChatBot/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/20CustomChatBot/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/21CustomCommandoCommand/types/callbacks/SaveCallbacks.d.ts b/TypeScript/21CustomCommandoCommand/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/21CustomCommandoCommand/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/21CustomCommandoCommand/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/21CustomCommandoCommand/types/controllers/DialogueController.d.ts b/TypeScript/21CustomCommandoCommand/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/21CustomCommandoCommand/types/controllers/DialogueController.d.ts +++ b/TypeScript/21CustomCommandoCommand/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/21CustomCommandoCommand/types/helpers/ProfileHelper.d.ts b/TypeScript/21CustomCommandoCommand/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/21CustomCommandoCommand/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/21CustomCommandoCommand/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/21CustomCommandoCommand/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/21CustomCommandoCommand/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/21CustomCommandoCommand/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/21CustomCommandoCommand/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/21CustomCommandoCommand/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/21CustomCommandoCommand/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/21CustomCommandoCommand/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/21CustomCommandoCommand/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/21CustomCommandoCommand/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/21CustomCommandoCommand/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/21CustomCommandoCommand/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/21CustomCommandoCommand/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/21CustomCommandoCommand/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/21CustomCommandoCommand/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/21CustomCommandoCommand/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/21CustomCommandoCommand/types/models/enums/ConfigTypes.d.ts b/TypeScript/21CustomCommandoCommand/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/21CustomCommandoCommand/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/21CustomCommandoCommand/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/21CustomCommandoCommand/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/21CustomCommandoCommand/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/21CustomCommandoCommand/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/21CustomCommandoCommand/types/services/BackupService.d.ts b/TypeScript/21CustomCommandoCommand/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/21CustomCommandoCommand/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/22CustomSptCommand/types/callbacks/SaveCallbacks.d.ts b/TypeScript/22CustomSptCommand/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/22CustomSptCommand/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/22CustomSptCommand/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/22CustomSptCommand/types/controllers/DialogueController.d.ts b/TypeScript/22CustomSptCommand/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/22CustomSptCommand/types/controllers/DialogueController.d.ts +++ b/TypeScript/22CustomSptCommand/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/22CustomSptCommand/types/helpers/ProfileHelper.d.ts b/TypeScript/22CustomSptCommand/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/22CustomSptCommand/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/22CustomSptCommand/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/22CustomSptCommand/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/22CustomSptCommand/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/22CustomSptCommand/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/22CustomSptCommand/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/22CustomSptCommand/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/22CustomSptCommand/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/22CustomSptCommand/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/22CustomSptCommand/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/22CustomSptCommand/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/22CustomSptCommand/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/22CustomSptCommand/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/22CustomSptCommand/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/22CustomSptCommand/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/22CustomSptCommand/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/22CustomSptCommand/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/22CustomSptCommand/types/models/enums/ConfigTypes.d.ts b/TypeScript/22CustomSptCommand/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/22CustomSptCommand/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/22CustomSptCommand/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/22CustomSptCommand/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/22CustomSptCommand/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/22CustomSptCommand/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/22CustomSptCommand/types/services/BackupService.d.ts b/TypeScript/22CustomSptCommand/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/22CustomSptCommand/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/23CustomAbstractChatBot/types/callbacks/SaveCallbacks.d.ts b/TypeScript/23CustomAbstractChatBot/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/23CustomAbstractChatBot/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/23CustomAbstractChatBot/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/23CustomAbstractChatBot/types/controllers/DialogueController.d.ts b/TypeScript/23CustomAbstractChatBot/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/23CustomAbstractChatBot/types/controllers/DialogueController.d.ts +++ b/TypeScript/23CustomAbstractChatBot/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/23CustomAbstractChatBot/types/helpers/ProfileHelper.d.ts b/TypeScript/23CustomAbstractChatBot/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/23CustomAbstractChatBot/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/23CustomAbstractChatBot/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/23CustomAbstractChatBot/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/23CustomAbstractChatBot/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/23CustomAbstractChatBot/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/23CustomAbstractChatBot/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/23CustomAbstractChatBot/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/23CustomAbstractChatBot/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/23CustomAbstractChatBot/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/23CustomAbstractChatBot/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/23CustomAbstractChatBot/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/23CustomAbstractChatBot/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/23CustomAbstractChatBot/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/23CustomAbstractChatBot/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/23CustomAbstractChatBot/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/23CustomAbstractChatBot/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/23CustomAbstractChatBot/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/23CustomAbstractChatBot/types/models/enums/ConfigTypes.d.ts b/TypeScript/23CustomAbstractChatBot/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/23CustomAbstractChatBot/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/23CustomAbstractChatBot/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/23CustomAbstractChatBot/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/23CustomAbstractChatBot/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/23CustomAbstractChatBot/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/23CustomAbstractChatBot/types/services/BackupService.d.ts b/TypeScript/23CustomAbstractChatBot/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/23CustomAbstractChatBot/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/24WebSocket/types/callbacks/SaveCallbacks.d.ts b/TypeScript/24WebSocket/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/24WebSocket/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/24WebSocket/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/24WebSocket/types/controllers/DialogueController.d.ts b/TypeScript/24WebSocket/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/24WebSocket/types/controllers/DialogueController.d.ts +++ b/TypeScript/24WebSocket/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/24WebSocket/types/helpers/ProfileHelper.d.ts b/TypeScript/24WebSocket/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/24WebSocket/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/24WebSocket/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/24WebSocket/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/24WebSocket/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/24WebSocket/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/24WebSocket/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/24WebSocket/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/24WebSocket/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/24WebSocket/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/24WebSocket/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/24WebSocket/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/24WebSocket/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/24WebSocket/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/24WebSocket/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/24WebSocket/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/24WebSocket/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/24WebSocket/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/24WebSocket/types/models/enums/ConfigTypes.d.ts b/TypeScript/24WebSocket/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/24WebSocket/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/24WebSocket/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/24WebSocket/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/24WebSocket/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/24WebSocket/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/24WebSocket/types/services/BackupService.d.ts b/TypeScript/24WebSocket/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/24WebSocket/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/2EditDatabase/types/callbacks/SaveCallbacks.d.ts b/TypeScript/2EditDatabase/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/2EditDatabase/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/2EditDatabase/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/2EditDatabase/types/controllers/DialogueController.d.ts b/TypeScript/2EditDatabase/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/2EditDatabase/types/controllers/DialogueController.d.ts +++ b/TypeScript/2EditDatabase/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/2EditDatabase/types/helpers/ProfileHelper.d.ts b/TypeScript/2EditDatabase/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/2EditDatabase/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/2EditDatabase/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/2EditDatabase/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/2EditDatabase/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/2EditDatabase/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/2EditDatabase/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/2EditDatabase/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/2EditDatabase/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/2EditDatabase/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/2EditDatabase/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/2EditDatabase/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/2EditDatabase/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/2EditDatabase/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/2EditDatabase/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/2EditDatabase/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/2EditDatabase/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/2EditDatabase/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/2EditDatabase/types/models/enums/ConfigTypes.d.ts b/TypeScript/2EditDatabase/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/2EditDatabase/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/2EditDatabase/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/2EditDatabase/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/2EditDatabase/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/2EditDatabase/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/2EditDatabase/types/services/BackupService.d.ts b/TypeScript/2EditDatabase/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/2EditDatabase/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/3GetSptConfigFile/types/callbacks/SaveCallbacks.d.ts b/TypeScript/3GetSptConfigFile/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/3GetSptConfigFile/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/3GetSptConfigFile/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/3GetSptConfigFile/types/controllers/DialogueController.d.ts b/TypeScript/3GetSptConfigFile/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/3GetSptConfigFile/types/controllers/DialogueController.d.ts +++ b/TypeScript/3GetSptConfigFile/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/3GetSptConfigFile/types/helpers/ProfileHelper.d.ts b/TypeScript/3GetSptConfigFile/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/3GetSptConfigFile/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/3GetSptConfigFile/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/3GetSptConfigFile/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/3GetSptConfigFile/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/3GetSptConfigFile/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/3GetSptConfigFile/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/3GetSptConfigFile/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/3GetSptConfigFile/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/3GetSptConfigFile/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/3GetSptConfigFile/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/3GetSptConfigFile/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/3GetSptConfigFile/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/3GetSptConfigFile/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/3GetSptConfigFile/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/3GetSptConfigFile/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/3GetSptConfigFile/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/3GetSptConfigFile/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/3GetSptConfigFile/types/models/enums/ConfigTypes.d.ts b/TypeScript/3GetSptConfigFile/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/3GetSptConfigFile/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/3GetSptConfigFile/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/3GetSptConfigFile/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/3GetSptConfigFile/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/3GetSptConfigFile/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/3GetSptConfigFile/types/services/BackupService.d.ts b/TypeScript/3GetSptConfigFile/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/3GetSptConfigFile/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/callbacks/SaveCallbacks.d.ts b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/controllers/DialogueController.d.ts b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/controllers/DialogueController.d.ts +++ b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/helpers/ProfileHelper.d.ts b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/enums/ConfigTypes.d.ts b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/services/BackupService.d.ts b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/4.1UseACustomJson5OrJsonCConfigFile/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/4UseACustomConfigFile/types/callbacks/SaveCallbacks.d.ts b/TypeScript/4UseACustomConfigFile/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/4UseACustomConfigFile/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/4UseACustomConfigFile/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/4UseACustomConfigFile/types/controllers/DialogueController.d.ts b/TypeScript/4UseACustomConfigFile/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/4UseACustomConfigFile/types/controllers/DialogueController.d.ts +++ b/TypeScript/4UseACustomConfigFile/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/4UseACustomConfigFile/types/helpers/ProfileHelper.d.ts b/TypeScript/4UseACustomConfigFile/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/4UseACustomConfigFile/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/4UseACustomConfigFile/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/4UseACustomConfigFile/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/4UseACustomConfigFile/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/4UseACustomConfigFile/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/4UseACustomConfigFile/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/4UseACustomConfigFile/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/4UseACustomConfigFile/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/4UseACustomConfigFile/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/4UseACustomConfigFile/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/4UseACustomConfigFile/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/4UseACustomConfigFile/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/4UseACustomConfigFile/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/4UseACustomConfigFile/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/4UseACustomConfigFile/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/4UseACustomConfigFile/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/4UseACustomConfigFile/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/4UseACustomConfigFile/types/models/enums/ConfigTypes.d.ts b/TypeScript/4UseACustomConfigFile/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/4UseACustomConfigFile/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/4UseACustomConfigFile/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/4UseACustomConfigFile/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/4UseACustomConfigFile/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/4UseACustomConfigFile/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/4UseACustomConfigFile/types/services/BackupService.d.ts b/TypeScript/4UseACustomConfigFile/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/4UseACustomConfigFile/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/5ReplaceMethod/types/callbacks/SaveCallbacks.d.ts b/TypeScript/5ReplaceMethod/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/5ReplaceMethod/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/5ReplaceMethod/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/5ReplaceMethod/types/controllers/DialogueController.d.ts b/TypeScript/5ReplaceMethod/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/5ReplaceMethod/types/controllers/DialogueController.d.ts +++ b/TypeScript/5ReplaceMethod/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/5ReplaceMethod/types/helpers/ProfileHelper.d.ts b/TypeScript/5ReplaceMethod/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/5ReplaceMethod/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/5ReplaceMethod/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/5ReplaceMethod/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/5ReplaceMethod/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/5ReplaceMethod/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/5ReplaceMethod/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/5ReplaceMethod/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/5ReplaceMethod/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/5ReplaceMethod/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/5ReplaceMethod/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/5ReplaceMethod/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/5ReplaceMethod/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/5ReplaceMethod/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/5ReplaceMethod/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/5ReplaceMethod/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/5ReplaceMethod/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/5ReplaceMethod/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/5ReplaceMethod/types/models/enums/ConfigTypes.d.ts b/TypeScript/5ReplaceMethod/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/5ReplaceMethod/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/5ReplaceMethod/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/5ReplaceMethod/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/5ReplaceMethod/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/5ReplaceMethod/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/5ReplaceMethod/types/services/BackupService.d.ts b/TypeScript/5ReplaceMethod/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/5ReplaceMethod/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/6ReferenceAnotherClass/types/callbacks/SaveCallbacks.d.ts b/TypeScript/6ReferenceAnotherClass/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/6ReferenceAnotherClass/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/6ReferenceAnotherClass/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/6ReferenceAnotherClass/types/controllers/DialogueController.d.ts b/TypeScript/6ReferenceAnotherClass/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/6ReferenceAnotherClass/types/controllers/DialogueController.d.ts +++ b/TypeScript/6ReferenceAnotherClass/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/6ReferenceAnotherClass/types/helpers/ProfileHelper.d.ts b/TypeScript/6ReferenceAnotherClass/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/6ReferenceAnotherClass/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/6ReferenceAnotherClass/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/6ReferenceAnotherClass/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/6ReferenceAnotherClass/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/6ReferenceAnotherClass/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/6ReferenceAnotherClass/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/6ReferenceAnotherClass/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/6ReferenceAnotherClass/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/6ReferenceAnotherClass/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/6ReferenceAnotherClass/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/6ReferenceAnotherClass/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/6ReferenceAnotherClass/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/6ReferenceAnotherClass/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/6ReferenceAnotherClass/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/6ReferenceAnotherClass/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/6ReferenceAnotherClass/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/6ReferenceAnotherClass/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/6ReferenceAnotherClass/types/models/enums/ConfigTypes.d.ts b/TypeScript/6ReferenceAnotherClass/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/6ReferenceAnotherClass/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/6ReferenceAnotherClass/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/6ReferenceAnotherClass/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/6ReferenceAnotherClass/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/6ReferenceAnotherClass/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/6ReferenceAnotherClass/types/services/BackupService.d.ts b/TypeScript/6ReferenceAnotherClass/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/6ReferenceAnotherClass/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/7OnLoadHook/types/callbacks/SaveCallbacks.d.ts b/TypeScript/7OnLoadHook/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/7OnLoadHook/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/7OnLoadHook/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/7OnLoadHook/types/controllers/DialogueController.d.ts b/TypeScript/7OnLoadHook/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/7OnLoadHook/types/controllers/DialogueController.d.ts +++ b/TypeScript/7OnLoadHook/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/7OnLoadHook/types/helpers/ProfileHelper.d.ts b/TypeScript/7OnLoadHook/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/7OnLoadHook/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/7OnLoadHook/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/7OnLoadHook/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/7OnLoadHook/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/7OnLoadHook/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/7OnLoadHook/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/7OnLoadHook/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/7OnLoadHook/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/7OnLoadHook/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/7OnLoadHook/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/7OnLoadHook/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/7OnLoadHook/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/7OnLoadHook/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/7OnLoadHook/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/7OnLoadHook/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/7OnLoadHook/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/7OnLoadHook/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/7OnLoadHook/types/models/enums/ConfigTypes.d.ts b/TypeScript/7OnLoadHook/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/7OnLoadHook/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/7OnLoadHook/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/7OnLoadHook/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/7OnLoadHook/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/7OnLoadHook/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/7OnLoadHook/types/services/BackupService.d.ts b/TypeScript/7OnLoadHook/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/7OnLoadHook/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/8OnUpdateHook/types/callbacks/SaveCallbacks.d.ts b/TypeScript/8OnUpdateHook/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/8OnUpdateHook/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/8OnUpdateHook/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/8OnUpdateHook/types/controllers/DialogueController.d.ts b/TypeScript/8OnUpdateHook/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/8OnUpdateHook/types/controllers/DialogueController.d.ts +++ b/TypeScript/8OnUpdateHook/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/8OnUpdateHook/types/helpers/ProfileHelper.d.ts b/TypeScript/8OnUpdateHook/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/8OnUpdateHook/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/8OnUpdateHook/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/8OnUpdateHook/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/8OnUpdateHook/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/8OnUpdateHook/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/8OnUpdateHook/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/8OnUpdateHook/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/8OnUpdateHook/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/8OnUpdateHook/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/8OnUpdateHook/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/8OnUpdateHook/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/8OnUpdateHook/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/8OnUpdateHook/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/8OnUpdateHook/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/8OnUpdateHook/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/8OnUpdateHook/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/8OnUpdateHook/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/8OnUpdateHook/types/models/enums/ConfigTypes.d.ts b/TypeScript/8OnUpdateHook/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/8OnUpdateHook/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/8OnUpdateHook/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/8OnUpdateHook/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/8OnUpdateHook/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/8OnUpdateHook/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/8OnUpdateHook/types/services/BackupService.d.ts b/TypeScript/8OnUpdateHook/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/8OnUpdateHook/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +} diff --git a/TypeScript/9RouterHooks/types/callbacks/SaveCallbacks.d.ts b/TypeScript/9RouterHooks/types/callbacks/SaveCallbacks.d.ts index 8f836cb..3d5f212 100644 --- a/TypeScript/9RouterHooks/types/callbacks/SaveCallbacks.d.ts +++ b/TypeScript/9RouterHooks/types/callbacks/SaveCallbacks.d.ts @@ -3,11 +3,13 @@ import { OnUpdate } from "@spt/di/OnUpdate"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { SaveServer } from "@spt/servers/SaveServer"; +import { BackupService } from "@spt/services/BackupService"; export declare class SaveCallbacks implements OnLoad, OnUpdate { protected saveServer: SaveServer; protected configServer: ConfigServer; + protected backupService: BackupService; protected coreConfig: ICoreConfig; - constructor(saveServer: SaveServer, configServer: ConfigServer); + constructor(saveServer: SaveServer, configServer: ConfigServer, backupService: BackupService); onLoad(): Promise; getRoute(): string; onUpdate(secondsSinceLastRun: number): Promise; diff --git a/TypeScript/9RouterHooks/types/controllers/DialogueController.d.ts b/TypeScript/9RouterHooks/types/controllers/DialogueController.d.ts index 2f00b13..ded87cf 100644 --- a/TypeScript/9RouterHooks/types/controllers/DialogueController.d.ts +++ b/TypeScript/9RouterHooks/types/controllers/DialogueController.d.ts @@ -1,5 +1,8 @@ import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { DialogueHelper } from "@spt/helpers/DialogueHelper"; +import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IDeleteFriendRequest } from "@spt/models/eft/dialog/IDeleteFriendRequest"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetAllAttachmentsResponse } from "@spt/models/eft/dialog/IGetAllAttachmentsResponse"; @@ -20,11 +23,13 @@ export declare class DialogueController { protected saveServer: SaveServer; protected timeUtil: TimeUtil; protected dialogueHelper: DialogueHelper; + protected notificationSendHelper: NotificationSendHelper; + protected profileHelper: ProfileHelper; protected mailSendService: MailSendService; protected localisationService: LocalisationService; protected configServer: ConfigServer; protected dialogueChatBots: IDialogueChatBot[]; - constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); + constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, dialogueHelper: DialogueHelper, notificationSendHelper: NotificationSendHelper, profileHelper: ProfileHelper, mailSendService: MailSendService, localisationService: LocalisationService, configServer: ConfigServer, dialogueChatBots: IDialogueChatBot[]); registerChatBot(chatBot: IDialogueChatBot): void; /** Handle onUpdate spt event */ update(): void; @@ -151,4 +156,6 @@ export declare class DialogueController { protected messageHasExpired(message: IMessage): boolean; /** Handle client/friend/request/send */ sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse; + /** Handle client/friend/delete */ + deleteFriend(sessionID: string, request: IDeleteFriendRequest): void; } diff --git a/TypeScript/9RouterHooks/types/helpers/ProfileHelper.d.ts b/TypeScript/9RouterHooks/types/helpers/ProfileHelper.d.ts index c2871b4..f68c580 100644 --- a/TypeScript/9RouterHooks/types/helpers/ProfileHelper.d.ts +++ b/TypeScript/9RouterHooks/types/helpers/ProfileHelper.d.ts @@ -2,6 +2,7 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Common, ICounterKeyValue, IStats } from "@spt/models/eft/common/tables/IBotBase"; import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { BonusType } from "@spt/models/enums/BonusType"; @@ -90,6 +91,24 @@ export declare class ProfileHelper { * @returns ISptProfile object */ getFullProfile(sessionID: string): ISptProfile | undefined; + /** + * Get full representation of a players profile JSON by the account ID, or undefined if not found + * @param accountId Account ID to find + * @returns + */ + getFullProfileByAccountId(accountID: string): ISptProfile | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given session ID + * @param sessionID The session ID to return the profile for + * @returns + */ + getChatRoomMemberFromSessionId(sessionID: string): ISearchFriendResponse | undefined; + /** + * Retrieve a ChatRoomMember formatted profile for the given PMC profile data + * @param pmcProfile The PMC profile data to format into a ChatRoomMember structure + * @returns + */ + getChatRoomMemberFromPmcProfile(pmcProfile: IPmcData): ISearchFriendResponse; /** * Get a PMC profile by its session id * @param sessionID Profile id to return diff --git a/TypeScript/9RouterHooks/types/models/eft/profile/ISearchFriendResponse.d.ts b/TypeScript/9RouterHooks/types/models/eft/profile/ISearchFriendResponse.d.ts index d3cc7df..628b82c 100644 --- a/TypeScript/9RouterHooks/types/models/eft/profile/ISearchFriendResponse.d.ts +++ b/TypeScript/9RouterHooks/types/models/eft/profile/ISearchFriendResponse.d.ts @@ -8,4 +8,5 @@ export interface Info { Side: string; Level: number; MemberCategory: number; + SelectedMemberCategory: number; } diff --git a/TypeScript/9RouterHooks/types/models/eft/profile/ISptProfile.d.ts b/TypeScript/9RouterHooks/types/models/eft/profile/ISptProfile.d.ts index aba539b..af8c92f 100644 --- a/TypeScript/9RouterHooks/types/models/eft/profile/ISptProfile.d.ts +++ b/TypeScript/9RouterHooks/types/models/eft/profile/ISptProfile.d.ts @@ -19,6 +19,8 @@ export interface ISptProfile { traderPurchases?: Record>; /** Achievements earned by player */ achievements: Record; + /** List of friend profile IDs */ + friends: string[]; } export declare class ITraderPurchaseData { count: number; diff --git a/TypeScript/9RouterHooks/types/models/eft/ws/IWsFriendsListAccept.d.ts b/TypeScript/9RouterHooks/types/models/eft/ws/IWsFriendsListAccept.d.ts new file mode 100644 index 0000000..4a96db3 --- /dev/null +++ b/TypeScript/9RouterHooks/types/models/eft/ws/IWsFriendsListAccept.d.ts @@ -0,0 +1,5 @@ +import { IWsNotificationEvent } from "@spt/models/eft/ws/IWsNotificationEvent"; +import { ISearchFriendResponse } from "../profile/ISearchFriendResponse"; +export interface IWsFriendsListAccept extends IWsNotificationEvent { + profile: ISearchFriendResponse; +} diff --git a/TypeScript/9RouterHooks/types/models/eft/ws/IWsNotificationEvent.d.ts b/TypeScript/9RouterHooks/types/models/eft/ws/IWsNotificationEvent.d.ts index 5fc72f3..de119c8 100644 --- a/TypeScript/9RouterHooks/types/models/eft/ws/IWsNotificationEvent.d.ts +++ b/TypeScript/9RouterHooks/types/models/eft/ws/IWsNotificationEvent.d.ts @@ -1,4 +1,4 @@ export interface IWsNotificationEvent { type: string; - eventId: string; + eventId?: string; } diff --git a/TypeScript/9RouterHooks/types/models/enums/ConfigTypes.d.ts b/TypeScript/9RouterHooks/types/models/enums/ConfigTypes.d.ts index 2c4483a..5bed6f1 100644 --- a/TypeScript/9RouterHooks/types/models/enums/ConfigTypes.d.ts +++ b/TypeScript/9RouterHooks/types/models/enums/ConfigTypes.d.ts @@ -1,5 +1,6 @@ export declare enum ConfigTypes { AIRDROP = "spt-airdrop", + BACKUP = "spt-backup", BOT = "spt-bot", PMC = "spt-pmc", CORE = "spt-core", diff --git a/TypeScript/9RouterHooks/types/models/spt/config/IBackupConfig.d.ts b/TypeScript/9RouterHooks/types/models/spt/config/IBackupConfig.d.ts new file mode 100644 index 0000000..06bb747 --- /dev/null +++ b/TypeScript/9RouterHooks/types/models/spt/config/IBackupConfig.d.ts @@ -0,0 +1,12 @@ +import { IBaseConfig } from "@spt/models/spt/config/IBaseConfig"; +export interface IBackupConfig extends IBaseConfig { + kind: "spt-backup"; + enabled: boolean; + maxBackups: number; + directory: string; + backupInterval: IBackupConfigInterval; +} +export interface IBackupConfigInterval { + enabled: boolean; + intervalMinutes: number; +} diff --git a/TypeScript/9RouterHooks/types/services/BackupService.d.ts b/TypeScript/9RouterHooks/types/services/BackupService.d.ts new file mode 100644 index 0000000..aeab6c1 --- /dev/null +++ b/TypeScript/9RouterHooks/types/services/BackupService.d.ts @@ -0,0 +1,98 @@ +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { IBackupConfig } from "@spt/models/spt/config/IBackupConfig"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +export declare class BackupService { + protected logger: ILogger; + protected preSptModLoader: PreSptModLoader; + protected configServer: ConfigServer; + protected backupConfig: IBackupConfig; + protected readonly activeServerMods: string[]; + protected readonly profileDir = "./user/profiles"; + constructor(logger: ILogger, preSptModLoader: PreSptModLoader, configServer: ConfigServer); + /** + * Initializes the backup process. + * + * This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning + * up old backups if the number exceeds the configured maximum. + * + * @returns A promise that resolves when the backup process is complete. + */ + init(): Promise; + /** + * Fetches the names of all JSON files in the profile directory. + * + * This method normalizes the profile directory path and reads all files within it. It then filters the files to + * include only those with a `.json` extension and returns their names. + * + * @returns A promise that resolves to an array of JSON file names. + */ + protected fetchProfileFiles(): Promise; + /** + * Check to see if the backup service is enabled via the config. + * + * @returns True if enabled, false otherwise. + */ + protected isEnabled(): boolean; + /** + * Generates the target directory path for the backup. The directory path is constructed using the `directory` from + * the configuration and the current backup date. + * + * @returns The target directory path for the backup. + */ + protected generateBackupTargetDir(): string; + /** + * Generates a formatted backup date string in the format `YYYY-MM-DD_hh-mm-ss`. + * + * @returns The formatted backup date string. + */ + protected generateBackupDate(): string; + /** + * Cleans up old backups in the backup directory. + * + * This method reads the backup directory, and sorts backups by modification time. If the number of backups exceeds + * the configured maximum, it deletes the oldest backups. + * + * @returns A promise that resolves when the cleanup is complete. + */ + protected cleanBackups(): Promise; + /** + * Retrieves and sorts the backup file paths from the specified directory. + * + * @param dir - The directory to search for backup files. + * @returns A promise that resolves to an array of sorted backup file paths. + */ + private getBackupPaths; + /** + * Compares two backup folder names based on their extracted dates. + * + * @param a - The name of the first backup folder. + * @param b - The name of the second backup folder. + * @returns The difference in time between the two dates in milliseconds, or `null` if either date is invalid. + */ + private compareBackupDates; + /** + * Extracts a date from a folder name string formatted as `YYYY-MM-DD_hh-mm-ss`. + * + * @param folderName - The name of the folder from which to extract the date. + * @returns A Date object if the folder name is in the correct format, otherwise null. + */ + private extractDateFromFolderName; + /** + * Removes excess backups from the backup directory. + * + * @param backups - An array of backup file names to be removed. + * @returns A promise that resolves when all specified backups have been removed. + */ + private removeExcessBackups; + /** + * Start the backup interval if enabled in the configuration. + */ + protected startBackupInterval(): void; + /** + * Get an array of active server mod details. + * + * @returns An array of mod names. + */ + protected getActiveServerMods(): string[]; +}