Name2key, Group chat, Group invite / group invite accept / group invite reject

This commit is contained in:
Casper Warden
2017-12-14 18:22:41 +00:00
parent 65526c6d21
commit eab57f8ced
33 changed files with 526 additions and 21 deletions

View File

@@ -245,7 +245,18 @@ export class Caps
{
this.request(url, LLSD.LLSD.formatXML(data), 'application/llsd+xml').then((body: string) =>
{
resolve(LLSD.LLSD.parseXML(body));
let result: any = null;
try
{
result = LLSD.LLSD.parseXML(body);
}
catch (err)
{
console.error('Error parsing LLSD');
console.error(body);
reject(err);
}
resolve(result);
}).catch((err) =>
{
console.error(err);

View File

@@ -6,6 +6,7 @@ import {InstantMessageEvent} from '../events/InstantMessageEvent';
import {GroupInviteEvent} from '../events/GroupInviteEvent';
import {FriendRequestEvent} from '../events/FriendRequestEvent';
import {DisconnectEvent} from '../events/DisconnectEvent';
import {GroupChatEvent} from '../events/GroupChatEvent';
export class ClientEvents
{
@@ -17,4 +18,5 @@ export class ClientEvents
onTeleportEvent: Subject<TeleportEvent> = new Subject<TeleportEvent>();
onDisconnected: Subject<DisconnectEvent> = new Subject<DisconnectEvent>();
onCircuitLatency: Subject<number> = new Subject<number>();
onGroupChat: Subject<GroupChatEvent> = new Subject<GroupChatEvent>();
}

View File

@@ -13,6 +13,8 @@ import {ClientEvents} from './ClientEvents';
import {InstantMessageEvent} from '../events/InstantMessageEvent';
import {ChatSourceType} from '../enums/ChatSourceType';
import {InstantMessageEventFlags} from '../enums/InstantMessageEventFlags';
import {GroupInviteEvent} from '../events/GroupInviteEvent';
import {GroupChatEvent} from '../events/GroupChatEvent';
export class Comms
{
@@ -53,6 +55,12 @@ export class Comms
case InstantMessageDialog.MessageBox:
break;
case InstantMessageDialog.GroupInvitation:
const giEvent = new GroupInviteEvent();
giEvent.from = im.AgentData.AgentID;
giEvent.fromName = Utils.BufferToStringSimple(im.MessageBlock.FromAgentName);
giEvent.message = Utils.BufferToStringSimple(im.MessageBlock.Message);
giEvent.inviteID = im.MessageBlock.ID;
this.clientEvents.onGroupInvite.next(giEvent);
break;
case InstantMessageDialog.InventoryOffered:
break;
@@ -157,6 +165,16 @@ export class Comms
this.clientEvents.onInstantMessage.next(imEvent);
break;
}
case InstantMessageDialog.SessionSend:
{
const groupChatEvent = new GroupChatEvent();
groupChatEvent.from = im.AgentData.AgentID;
groupChatEvent.fromName = Utils.BufferToStringSimple(im.MessageBlock.FromAgentName);
groupChatEvent.groupID = im.MessageBlock.ID;
groupChatEvent.message = Utils.BufferToStringSimple(im.MessageBlock.Message);
this.clientEvents.onGroupChat.next(groupChatEvent);
break;
}
}
break;

View File

@@ -6,6 +6,9 @@ import {IPAddress} from './IPAddress';
import {TeleportEvent} from '../events/TeleportEvent';
import {ClientEvents} from './ClientEvents';
import {TeleportEventType} from '../enums/TeleportEventType';
import {GroupChatEvent} from '../events/GroupChatEvent';
import {Utils} from './Utils';
import {UUID} from './UUID';
export class EventQueueClient
{
@@ -252,6 +255,39 @@ export class EventQueueClient
this.clientEvents.onTeleportEvent.next(tpEvent);
break;
}
case 'ChatterBoxInvitation':
{
if (event['body'] && event['body']['instantmessage'] && event['body']['instantmessage']['message_params'] && event['body']['instantmessage']['message_params']['id'])
{
const messageParams = event['body']['instantmessage']['message_params'];
const imSessionID = messageParams['id'];
const requestedFolders = {
'method': 'accept invitation',
'session-id': imSessionID
};
const groupChatEvent = new GroupChatEvent();
groupChatEvent.from = new UUID(messageParams['from_id'].toString());
groupChatEvent.fromName = messageParams['from_name'];
groupChatEvent.groupID = new UUID(messageParams['id'].toString());
groupChatEvent.message = messageParams['message'];
this.caps.capsRequestXML('ChatSessionRequest', requestedFolders).then((result: any) =>
{
this.clientEvents.onGroupChat.next(groupChatEvent);
}).catch((err) =>
{
console.error(err);
});
}
break;
}
case 'ChatterBoxSessionAgentListUpdates':
{
// TODO
break;
}
case 'TeleportFinish':
{
const info = event['body']['Info'][0];

View File

@@ -9,6 +9,11 @@ export class UUID
{
return new UUID();
}
static random(): UUID
{
const newUUID = uuid.v4();
return new UUID(newUUID);
}
constructor(buf?: Buffer | string, pos?: number)
{
@@ -31,6 +36,7 @@ export class UUID
else
{
console.error('Can\'t accept UUIDs of type ' + typeof buf);
console.trace();
}
}
}

View File

@@ -8,6 +8,7 @@ import {ChatFromViewerMessage} from '../messages/ChatFromViewer';
import {ChatType} from '../../enums/ChatType';
import {InstantMessageDialog} from '../../enums/InstantMessageDialog';
import Timer = NodeJS.Timer;
import {GroupInviteEvent} from '../../events/GroupInviteEvent';
export class CommunicationsCommands extends CommandsBase
{
@@ -33,7 +34,7 @@ export class CommunicationsCommands extends CommandsBase
Offline: 1,
Dialog: 0,
ID: UUID.zero(),
Timestamp: 0,
Timestamp: Math.floor(new Date().getTime() / 1000),
FromAgentName: Utils.StringToBuffer(agentName),
Message: Utils.StringToBuffer(message),
BinaryBucket: Buffer.allocUnsafe(0)
@@ -134,7 +135,7 @@ export class CommunicationsCommands extends CommandsBase
Offline: 0,
Dialog: InstantMessageDialog.StartTyping,
ID: UUID.zero(),
Timestamp: 0,
Timestamp: Math.floor(new Date().getTime() / 1000),
FromAgentName: Utils.StringToBuffer(agentName),
Message: Utils.StringToBuffer(''),
BinaryBucket: Buffer.allocUnsafe(0)
@@ -168,7 +169,67 @@ export class CommunicationsCommands extends CommandsBase
Offline: 0,
Dialog: InstantMessageDialog.StopTyping,
ID: UUID.zero(),
Timestamp: 0,
Timestamp: Math.floor(new Date().getTime() / 1000),
FromAgentName: Utils.StringToBuffer(agentName),
Message: Utils.StringToBuffer(''),
BinaryBucket: Buffer.allocUnsafe(0)
};
im.EstateBlock = {
EstateID: 0
};
const sequenceNo = circuit.sendMessage(im, PacketFlags.Reliable);
return circuit.waitForAck(sequenceNo, 10000);
}
acceptGroupInvite(event: GroupInviteEvent): Promise<void>
{
const circuit = this.circuit;
const agentName = this.agent.firstName + ' ' + this.agent.lastName;
const im: ImprovedInstantMessageMessage = new ImprovedInstantMessageMessage();
im.AgentData = {
AgentID: this.agent.agentID,
SessionID: circuit.sessionID
};
im.MessageBlock = {
FromGroup: false,
ToAgentID: event.from,
ParentEstateID: 0,
RegionID: UUID.zero(),
Position: Vector3.getZero(),
Offline: 0,
Dialog: InstantMessageDialog.GroupInvitationAccept,
ID: event.inviteID,
Timestamp: Math.floor(new Date().getTime() / 1000),
FromAgentName: Utils.StringToBuffer(agentName),
Message: Utils.StringToBuffer(''),
BinaryBucket: Buffer.allocUnsafe(0)
};
im.EstateBlock = {
EstateID: 0
};
const sequenceNo = circuit.sendMessage(im, PacketFlags.Reliable);
return circuit.waitForAck(sequenceNo, 10000);
}
rejectGroupInvite(event: GroupInviteEvent): Promise<void>
{
const circuit = this.circuit;
const agentName = this.agent.firstName + ' ' + this.agent.lastName;
const im: ImprovedInstantMessageMessage = new ImprovedInstantMessageMessage();
im.AgentData = {
AgentID: this.agent.agentID,
SessionID: circuit.sessionID
};
im.MessageBlock = {
FromGroup: false,
ToAgentID: event.from,
ParentEstateID: 0,
RegionID: UUID.zero(),
Position: Vector3.getZero(),
Offline: 0,
Dialog: InstantMessageDialog.GroupInvitationDecline,
ID: event.inviteID,
Timestamp: Math.floor(new Date().getTime() / 1000),
FromAgentName: Utils.StringToBuffer(agentName),
Message: Utils.StringToBuffer(''),
BinaryBucket: Buffer.allocUnsafe(0)
@@ -244,6 +305,36 @@ export class CommunicationsCommands extends CommandsBase
});
}
sendGroupMessage(groupID: UUID, message: string): Promise<void>
{
const circuit = this.circuit;
const agentName = this.agent.firstName + ' ' + this.agent.lastName;
const im: ImprovedInstantMessageMessage = new ImprovedInstantMessageMessage();
im.AgentData = {
AgentID: this.agent.agentID,
SessionID: circuit.sessionID
};
im.MessageBlock = {
FromGroup: false,
ToAgentID: groupID,
ParentEstateID: 0,
RegionID: UUID.zero(),
Position: Vector3.getZero(),
Offline: 0,
Dialog: InstantMessageDialog.SessionSend,
ID: groupID,
Timestamp: Math.floor(new Date().getTime() / 1000),
FromAgentName: Utils.StringToBuffer(agentName),
Message: Utils.StringToBuffer(message),
BinaryBucket: Utils.StringToBuffer('')
};
im.EstateBlock = {
EstateID: 0
};
const sequenceNo = circuit.sendMessage(im, PacketFlags.Reliable);
return circuit.waitForAck(sequenceNo, 10000);
}
typeLocalMessage(message: string, thinkingTime?: number, charactersPerSecond?: number): Promise<void>
{
return new Promise<void>((resolve, reject) =>

View File

@@ -13,6 +13,8 @@ import {PacketFlags} from '../../enums/PacketFlags';
import {GridItemType} from '../../enums/GridItemType';
import {RegionIDAndHandleReplyMessage} from '../messages/RegionIDAndHandleReply';
import {CommandsBase} from './CommandsBase';
import {AvatarPickerRequestMessage} from '../messages/AvatarPickerRequest';
import {AvatarPickerReplyMessage} from '../messages/AvatarPickerReply';
export class GridCommands extends CommandsBase
{
getRegionHandle(regionID: UUID): Promise<Long>
@@ -138,4 +140,70 @@ export class GridCommands extends CommandsBase
});
});
}
name2Key(name: string): Promise<UUID>
{
const check = name.split('.');
if (check.length > 1)
{
name = check.join(' ');
}
else
{
name += ' resident';
}
name = name.toLowerCase();
const queryID = UUID.random();
return new Promise<UUID>((resolve, reject) =>
{
const aprm = new AvatarPickerRequestMessage();
aprm.AgentData = {
AgentID: this.agent.agentID,
SessionID: this.circuit.sessionID,
QueryID: queryID
};
aprm.Data = {
Name: Utils.StringToBuffer(name)
};
this.circuit.sendMessage(aprm, PacketFlags.Reliable);
this.circuit.waitForMessage(Message.AvatarPickerReply, 10000, (packet: Packet): boolean =>
{
const apr = packet.message as AvatarPickerReplyMessage;
if (apr.AgentData.QueryID.toString() === queryID.toString())
{
return true;
}
else
{
return false;
}
}).then((packet: Packet) =>
{
let found: UUID | null = null;
const apr = packet.message as AvatarPickerReplyMessage;
apr.Data.forEach((dataBlock) =>
{
const resultName = (Utils.BufferToStringSimple(dataBlock.FirstName) + ' ' + Utils.BufferToStringSimple(dataBlock.LastName)).toLowerCase();
if (resultName === name)
{
found = dataBlock.AvatarID;
}
});
if (found !== null)
{
resolve(found);
}
else
{
reject('Name not found')
}
}).catch((err) =>
{
reject(err);
});
});
}
}