2023-03-03 15:23:46 +00:00
import { inject , injectable } from "tsyringe" ;
import { DialogueHelper } from "../helpers/DialogueHelper" ;
2023-07-26 09:38:33 +01:00
import { ProfileHelper } from "../helpers/ProfileHelper" ;
2023-03-03 15:23:46 +00:00
import { IGetAllAttachmentsResponse } from "../models/eft/dialog/IGetAllAttachmentsResponse" ;
import { IGetFriendListDataResponse } from "../models/eft/dialog/IGetFriendListDataResponse" ;
2023-07-15 15:01:23 +01:00
import { IGetMailDialogViewRequestData } from "../models/eft/dialog/IGetMailDialogViewRequestData" ;
2023-03-03 15:23:46 +00:00
import {
IGetMailDialogViewResponseData
} from "../models/eft/dialog/IGetMailDialogViewResponseData" ;
2023-07-15 15:01:23 +01:00
import { ISendMessageRequest } from "../models/eft/dialog/ISendMessageRequest" ;
import { Dialogue , DialogueInfo , IAkiProfile , IUserDialogInfo , Message } from "../models/eft/profile/IAkiProfile" ;
2023-07-21 19:37:51 +01:00
import { GiftSentResult } from "../models/enums/GiftSentResult" ;
2023-07-15 11:02:48 +01:00
import { MemberCategory } from "../models/enums/MemberCategory" ;
2023-03-03 15:23:46 +00:00
import { MessageType } from "../models/enums/MessageType" ;
2023-07-21 17:08:32 +00:00
import { ILogger } from "../models/spt/utils/ILogger" ;
2023-03-03 15:23:46 +00:00
import { SaveServer } from "../servers/SaveServer" ;
2023-07-21 17:08:32 +00:00
import { GiftService } from "../services/GiftService" ;
import { MailSendService } from "../services/MailSendService" ;
2023-07-20 20:51:49 +01:00
import { HashUtil } from "../utils/HashUtil" ;
2023-07-26 09:38:33 +01:00
import { RandomUtil } from "../utils/RandomUtil" ;
2023-03-03 15:23:46 +00:00
import { TimeUtil } from "../utils/TimeUtil" ;
@injectable ( )
export class DialogueController
{
constructor (
2023-07-21 17:08:32 +00:00
@inject ( "WinstonLogger" ) protected logger : ILogger ,
2023-03-03 15:23:46 +00:00
@inject ( "SaveServer" ) protected saveServer : SaveServer ,
@inject ( "TimeUtil" ) protected timeUtil : TimeUtil ,
2023-07-20 19:27:28 +00:00
@inject ( "DialogueHelper" ) protected dialogueHelper : DialogueHelper ,
2023-07-26 09:38:33 +01:00
@inject ( "ProfileHelper" ) protected profileHelper : ProfileHelper ,
@inject ( "RandomUtil" ) protected randomUtil : RandomUtil ,
2023-07-21 17:08:32 +00:00
@inject ( "MailSendService" ) protected mailSendService : MailSendService ,
@inject ( "GiftService" ) protected giftService : GiftService ,
2023-07-20 19:27:28 +00:00
@inject ( "HashUtil" ) protected hashUtil : HashUtil
2023-03-03 15:23:46 +00:00
)
2023-07-21 17:08:32 +00:00
{ }
2023-03-03 15:23:46 +00:00
2023-07-15 10:56:00 +01:00
/** Handle onUpdate spt event */
2023-03-03 15:23:46 +00:00
public update ( ) : void
{
const profiles = this . saveServer . getProfiles ( ) ;
for ( const sessionID in profiles )
{
2023-04-22 22:41:04 +00:00
this . removeExpiredItemsFromMessages ( sessionID ) ;
2023-03-03 15:23:46 +00:00
}
}
2023-07-15 10:56:00 +01:00
/ * *
* Handle client / friend / list
* @returns IGetFriendListDataResponse
* /
2023-03-03 15:23:46 +00:00
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getFriendList ( sessionID : string ) : IGetFriendListDataResponse
{
2023-07-23 12:30:15 +01:00
// Force a fake friend called SPT into friend list
2023-03-03 15:23:46 +00:00
return {
2023-07-23 12:30:15 +01:00
"Friends" : [ this . getSptFriendData ( ) ] ,
2023-03-03 15:23:46 +00:00
"Ignore" : [ ] ,
"InIgnoreList" : [ ]
} ;
}
/ * *
2023-07-15 10:56:00 +01:00
* Handle client / mail / dialog / list
2023-03-03 15:23:46 +00:00
* Create array holding trader dialogs and mail interactions with player
* Set the content of the dialogue on the list tab .
* @param sessionID Session Id
* @returns array of dialogs
* /
public generateDialogueList ( sessionID : string ) : DialogueInfo [ ]
{
const data : DialogueInfo [ ] = [ ] ;
for ( const dialogueId in this . saveServer . getProfile ( sessionID ) . dialogues )
{
data . push ( this . getDialogueInfo ( dialogueId , sessionID ) ) ;
}
return data ;
}
/ * *
* Get the content of a dialogue
* @param dialogueID Dialog id
* @param sessionID Session Id
* @returns DialogueInfo
* /
public getDialogueInfo ( dialogueID : string , sessionID : string ) : DialogueInfo
{
const dialogue = this . saveServer . getProfile ( sessionID ) . dialogues [ dialogueID ] ;
2023-03-07 22:25:23 +00:00
const result : DialogueInfo = {
2023-07-21 17:08:32 +00:00
_id : dialogueID ,
type : dialogue . type ? dialogue.type : MessageType.NPC_TRADER ,
message : this.dialogueHelper.getMessagePreview ( dialogue ) ,
new : dialogue . new ,
attachmentsNew : dialogue.attachmentsNew ,
pinned : dialogue.pinned ,
Users : this.getDialogueUsers ( dialogue , dialogue . type , sessionID )
2023-03-03 15:23:46 +00:00
} ;
2023-03-07 22:25:23 +00:00
return result ;
2023-03-03 15:23:46 +00:00
}
2023-07-21 17:08:32 +00:00
/ * *
2023-07-25 15:59:39 +01:00
* Get the users involved in a dialog ( player + other party )
* @param dialog The dialog to check for users
* @param messageType What type of message is being sent
* @param sessionID Player id
* @returns IUserDialogInfo array
2023-07-21 17:08:32 +00:00
* /
public getDialogueUsers ( dialog : Dialogue , messageType : MessageType , sessionID : string ) : IUserDialogInfo [ ]
2023-07-20 19:27:28 +00:00
{
const profile = this . saveServer . getProfile ( sessionID ) ;
2023-07-25 15:59:39 +01:00
// User to user messages are special in that they need the player to exist in them, add if they don't
2023-07-21 17:08:32 +00:00
if ( messageType === MessageType . USER_MESSAGE && ! dialog . Users ? . find ( x = > x . _id === profile . characters . pmc . _id ) )
2023-07-20 19:27:28 +00:00
{
2023-07-21 17:08:32 +00:00
if ( ! dialog . Users )
{
dialog . Users = [ ] ;
}
dialog . Users . push ( {
2023-07-20 19:27:28 +00:00
_id : profile.characters.pmc._id ,
info : {
Level : profile.characters.pmc.Info.Level ,
Nickname : profile.characters.pmc.Info.Nickname ,
Side : profile.characters.pmc.Info.Side ,
MemberCategory : profile.characters.pmc.Info.MemberCategory
}
} ) ;
}
2023-07-21 17:08:32 +00:00
return dialog . Users ? dialog.Users : undefined ;
2023-07-20 19:27:28 +00:00
}
2023-03-03 15:23:46 +00:00
/ * *
2023-07-15 10:56:00 +01:00
* Handle client / mail / dialog / view
2023-03-03 15:23:46 +00:00
* Handle player clicking 'messenger' and seeing all the messages they ' ve recieved
* Set the content of the dialogue on the details panel , showing all the messages
* for the specified dialogue .
2023-07-15 15:01:23 +01:00
* @param request Get dialog request
* @param sessionId Session id
2023-03-03 15:23:46 +00:00
* @returns IGetMailDialogViewResponseData object
* /
2023-07-15 15:01:23 +01:00
public generateDialogueView ( request : IGetMailDialogViewRequestData , sessionId : string ) : IGetMailDialogViewResponseData
2023-03-03 15:23:46 +00:00
{
2023-07-15 15:01:23 +01:00
const dialogueId = request . dialogId ;
2023-07-25 15:59:39 +01:00
const fullProfile = this . saveServer . getProfile ( sessionId ) ;
const dialogue = this . getDialogByIdFromProfile ( fullProfile , request ) ;
2023-07-15 15:01:23 +01:00
2023-07-25 15:59:39 +01:00
// Dialog was opened, remove the little [1] on screen
2023-03-03 15:23:46 +00:00
dialogue . new = 0 ;
// Set number of new attachments, but ignore those that have expired.
2023-07-15 15:01:23 +01:00
dialogue . attachmentsNew = this . getUnreadMessagesWithAttachmentsCount ( sessionId , dialogueId ) ;
2023-03-03 15:23:46 +00:00
return {
messages : dialogue.messages ,
2023-07-25 15:59:39 +01:00
profiles : this.getProfilesForMail ( fullProfile , dialogue . Users ) ,
2023-03-03 15:23:46 +00:00
hasMessagesWithRewards : this.messagesHaveUncollectedRewards ( dialogue . messages )
} ;
}
2023-07-15 15:01:23 +01:00
/ * *
* Get dialog from player profile , create if doesn ' t exist
* @param profile Player profile
* @param request get dialog request ( params used when dialog doesnt exist in profile )
* @returns Dialogue
* /
protected getDialogByIdFromProfile ( profile : IAkiProfile , request : IGetMailDialogViewRequestData ) : Dialogue
{
if ( ! profile . dialogues [ request . dialogId ] )
{
profile . dialogues [ request . dialogId ] = {
_id : request.dialogId ,
attachmentsNew : 0 ,
pinned : false ,
messages : [ ] ,
new : 0 ,
type : request . type
} ;
if ( request . type === MessageType . USER_MESSAGE )
{
profile . dialogues [ request . dialogId ] . Users = [ ] ;
2023-07-23 12:30:15 +01:00
profile . dialogues [ request . dialogId ] . Users . push ( this . getSptFriendData ( request . dialogId ) ) ;
2023-07-15 15:01:23 +01:00
}
}
return profile . dialogues [ request . dialogId ] ;
}
2023-07-21 17:08:32 +00:00
/ * *
2023-07-25 15:59:39 +01:00
* Get the users involved in a mail between two entities
* @param fullProfile Player profile
* @param dialogUsers The participants of the mail
* @returns IUserDialogInfo array
2023-07-21 17:08:32 +00:00
* /
2023-07-25 15:59:39 +01:00
protected getProfilesForMail ( fullProfile : IAkiProfile , dialogUsers : IUserDialogInfo [ ] ) : IUserDialogInfo [ ]
2023-03-07 22:25:23 +00:00
{
const result : IUserDialogInfo [ ] = [ ] ;
if ( dialogUsers )
{
result . push ( . . . dialogUsers ) ;
2023-07-25 15:59:39 +01:00
// Plyer doesnt exist, add them in before returning
if ( ! result . find ( x = > x . _id === fullProfile . info . id ) )
{
const pmcProfile = fullProfile . characters . pmc ;
result . push ( {
_id : fullProfile.info.id ,
info : {
Nickname : pmcProfile.Info.Nickname ,
Side : pmcProfile.Info.Side ,
Level : pmcProfile.Info.Level ,
MemberCategory : pmcProfile.Info.MemberCategory
}
} ) ;
}
2023-03-07 22:25:23 +00:00
}
return result ;
}
2023-03-03 15:23:46 +00:00
/ * *
* Get a count of messages with attachments from a particular dialog
* @param sessionID Session id
* @param dialogueID Dialog id
* @returns Count of messages with attachments
* /
protected getUnreadMessagesWithAttachmentsCount ( sessionID : string , dialogueID : string ) : number
{
let newAttachmentCount = 0 ;
const activeMessages = this . getActiveMessagesFromDialog ( sessionID , dialogueID ) ;
for ( const message of activeMessages )
{
if ( message . hasRewards && ! message . rewardCollected )
{
newAttachmentCount ++ ;
}
}
return newAttachmentCount ;
}
/ * *
* Does array have messages with uncollected rewards ( includes expired rewards )
* @param messages Messages to check
* @returns true if uncollected rewards found
* /
protected messagesHaveUncollectedRewards ( messages : Message [ ] ) : boolean
{
return messages . some ( x = > x . items ? . data ? . length > 0 ) ;
}
2023-07-15 10:56:00 +01:00
/** Handle client/mail/dialog/remove */
2023-03-03 15:23:46 +00:00
public removeDialogue ( dialogueID : string , sessionID : string ) : void
{
delete this . saveServer . getProfile ( sessionID ) . dialogues [ dialogueID ] ;
}
public setDialoguePin ( dialogueID : string , shouldPin : boolean , sessionID : string ) : void
{
this . saveServer . getProfile ( sessionID ) . dialogues [ dialogueID ] . pinned = shouldPin ;
}
2023-07-15 10:56:00 +01:00
/** Handle client/mail/dialog/read */
2023-03-03 15:23:46 +00:00
public setRead ( dialogueIDs : string [ ] , sessionID : string ) : void
{
const dialogueData = this . saveServer . getProfile ( sessionID ) . dialogues ;
for ( const dialogID of dialogueIDs )
{
dialogueData [ dialogID ] . new = 0 ;
dialogueData [ dialogID ] . attachmentsNew = 0 ;
}
}
/ * *
2023-07-15 10:56:00 +01:00
* Handle client / mail / dialog / getAllAttachments
2023-03-03 15:23:46 +00:00
* Get all uncollected items attached to mail in a particular dialog
* @param dialogueID Dialog to get mail attachments from
* @param sessionID Session id
* @returns
* /
public getAllAttachments ( dialogueID : string , sessionID : string ) : IGetAllAttachmentsResponse
{
// Removes corner 'new messages' tag
this . saveServer . getProfile ( sessionID ) . dialogues [ dialogueID ] . attachmentsNew = 0 ;
const activeMessages = this . getActiveMessagesFromDialog ( sessionID , dialogueID ) ;
const messagesWithAttachments = this . getMessagesWithAttachments ( activeMessages ) ;
return {
messages : messagesWithAttachments ,
profiles : [ ] ,
hasMessagesWithRewards : this.messagesHaveUncollectedRewards ( messagesWithAttachments )
} ;
}
2023-07-15 15:01:23 +01:00
/** client/mail/msg/send */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public sendMessage ( sessionId : string , request : ISendMessageRequest ) : string
{
2023-07-21 17:08:32 +00:00
this . mailSendService . sendPlayerMessageToNpc ( sessionId , request . dialogId , request . text ) ;
2023-07-26 09:38:33 +01:00
// Handle when player types a keyword to sptFriend user
2023-07-21 17:08:32 +00:00
if ( request . dialogId . includes ( "sptFriend" ) )
2023-07-15 15:01:23 +01:00
{
2023-07-21 17:08:32 +00:00
this . handleChatWithSPTFriend ( sessionId , request ) ;
2023-07-15 15:01:23 +01:00
}
return request . dialogId ;
}
2023-07-26 09:38:33 +01:00
/ * *
* Send responses back to player when they communicate with SPT friend on friends list
* @param sessionId Session Id
* @param request send message request
* /
2023-07-21 17:08:32 +00:00
protected handleChatWithSPTFriend ( sessionId : string , request : ISendMessageRequest ) : void
{
2023-07-26 09:38:33 +01:00
const sender = this . profileHelper . getPmcProfile ( sessionId ) ;
2023-07-23 12:30:15 +01:00
const sptFriendUser = this . getSptFriendData ( ) ;
2023-07-21 17:08:32 +00:00
const giftSent = this . giftService . sendGiftToPlayer ( sessionId , request . text ) ;
if ( giftSent === GiftSentResult . SUCCESS )
{
2023-07-26 09:38:33 +01:00
this . mailSendService . sendUserMessageToPlayer ( sessionId , sptFriendUser , this . randomUtil . getArrayValue ( [ "Hey! you got the right code!" , "A secret code, how exciting!" , "You found a gift code!" ] ) ) ;
return ;
2023-07-21 17:08:32 +00:00
}
if ( giftSent === GiftSentResult . FAILED_GIFT_ALREADY_RECEIVED )
{
2023-07-26 09:38:33 +01:00
this . mailSendService . sendUserMessageToPlayer ( sessionId , sptFriendUser , this . randomUtil . getArrayValue ( [ "Looks like you already used that code" , "You already have that!!" ] ) ) ;
return ;
2023-07-21 17:08:32 +00:00
}
if ( request . text . toLowerCase ( ) . includes ( "love you" ) )
{
2023-07-26 09:38:33 +01:00
this . mailSendService . sendUserMessageToPlayer ( sessionId , sptFriendUser , this . randomUtil . getArrayValue ( [ "That's quite forward but i love you too in a purely chatbot-human way" , "I love you too buddy :3!" , "uwu" , ` love you too ${ sender ? . Info ? . Nickname } ` ] ) ) ;
2023-07-21 17:08:32 +00:00
}
if ( request . text . toLowerCase ( ) === "spt" )
{
2023-07-26 09:38:33 +01:00
this . mailSendService . sendUserMessageToPlayer ( sessionId , sptFriendUser , this . randomUtil . getArrayValue ( [ "Its me!!" , "spt? i've heard of that project" ] ) ) ;
}
if ( request . text . toLowerCase ( ) === "hello" )
{
this . mailSendService . sendUserMessageToPlayer ( sessionId , sptFriendUser , this . randomUtil . getArrayValue ( [ "Howdy" , "Hi" , "Greetings" , "Hello" , "bonjoy" , "Yo" , "Sup" , "Heyyyyy" , "Hey there" , ` Hello ${ sender ? . Info ? . Nickname } ` ] ) ) ;
}
if ( request . text . toLowerCase ( ) === "nikita" )
{
this . mailSendService . sendUserMessageToPlayer ( sessionId , sptFriendUser , this . randomUtil . getArrayValue ( [ "I know that guy!" , "Cool guy, he made EFT!" , "Kegend" , "Remember when he said webel-webel-webel-webel, classic nikita moment" ] ) ) ;
}
if ( request . text . toLowerCase ( ) === "are you a bot" )
{
this . mailSendService . sendUserMessageToPlayer ( sessionId , sptFriendUser , this . randomUtil . getArrayValue ( [ "beep boop" , "**sad boop**" , "probably" , "sometimes" , "yeah lol" ] ) ) ;
2023-07-21 17:08:32 +00:00
}
}
2023-07-23 12:30:15 +01:00
protected getSptFriendData ( friendId = "sptFriend" ) : IUserDialogInfo
{
return {
_id : friendId ,
info : {
Level : 1 ,
MemberCategory : MemberCategory.DEVELOPER ,
Nickname : "SPT" ,
Side : "Usec"
}
} ;
}
2023-03-03 15:23:46 +00:00
/ * *
* Get messages from a specific dialog that have items not expired
* @param sessionId Session id
* @param dialogueId Dialog to get mail attachments from
* @returns Message array
* /
protected getActiveMessagesFromDialog ( sessionId : string , dialogueId : string ) : Message [ ]
{
const timeNow = this . timeUtil . getTimestamp ( ) ;
return this . saveServer . getProfile ( sessionId ) . dialogues [ dialogueId ] . messages . filter ( x = > timeNow < ( x . dt + x . maxStorageTime ) ) ;
}
/ * *
* Return array of messages with uncollected items ( includes expired )
* @param messages Messages to parse
* @returns messages with items to collect
* /
protected getMessagesWithAttachments ( messages : Message [ ] ) : Message [ ]
{
return messages . filter ( x = > x . items ? . data ? . length > 0 ) ;
}
/ * *
2023-04-22 22:41:04 +00:00
* Delete expired items from all messages in player profile . triggers when updating traders .
* @param sessionId Session id
2023-03-03 15:23:46 +00:00
* /
2023-04-22 22:41:04 +00:00
protected removeExpiredItemsFromMessages ( sessionId : string ) : void
2023-03-03 15:23:46 +00:00
{
2023-04-22 22:41:04 +00:00
for ( const dialogueId in this . saveServer . getProfile ( sessionId ) . dialogues )
2023-03-03 15:23:46 +00:00
{
2023-04-22 22:41:04 +00:00
this . removeExpiredItemsFromMessage ( sessionId , dialogueId ) ;
}
}
/ * *
* Removes expired items from a message in player profile
* @param sessionId Session id
* @param dialogueId Dialog id
* /
protected removeExpiredItemsFromMessage ( sessionId : string , dialogueId : string ) : void
{
for ( const message of this . saveServer . getProfile ( sessionId ) . dialogues [ dialogueId ] . messages )
{
if ( this . messageHasExpired ( message ) )
2023-03-03 15:23:46 +00:00
{
2023-04-22 22:41:04 +00:00
message . items = { } ;
2023-03-03 15:23:46 +00:00
}
}
}
2023-04-22 22:41:04 +00:00
/ * *
* Has a dialog message expired
* @param message Message to check expiry of
* @returns true or false
* /
protected messageHasExpired ( message : Message ) : boolean
{
return ( this . timeUtil . getTimestamp ( ) ) > ( message . dt + message . maxStorageTime ) ;
}
2023-03-03 15:23:46 +00:00
}