[Closes #10] Implement group ban/unban. Also fix cap system to accept certain HTTP responses as valid states even with no valid LLSD body.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import * as LLSD from '@caspertech/llsd';
|
||||
import * as request from 'request';
|
||||
import * as url from 'url';
|
||||
import {Region} from './Region';
|
||||
import {Subscription} from 'rxjs/internal/Subscription';
|
||||
import {EventQueueClient} from './EventQueueClient';
|
||||
@@ -8,6 +9,8 @@ import {ClientEvents} from './ClientEvents';
|
||||
import {Agent} from './Agent';
|
||||
import {Subject} from 'rxjs/internal/Subject';
|
||||
import {HTTPAssets} from '..';
|
||||
import { ParsedUrlQuery } from 'querystring';
|
||||
import { ICapResponse } from './interfaces/ICapResponse';
|
||||
|
||||
export class Caps
|
||||
{
|
||||
@@ -127,9 +130,9 @@ export class Caps
|
||||
req.push('ViewerStartAuction');
|
||||
req.push('ViewerStats');
|
||||
this.active = true;
|
||||
this.request(seedURL, LLSD.LLSD.formatXML(req), 'application/llsd+xml').then((body: string) =>
|
||||
this.request(seedURL, LLSD.LLSD.formatXML(req), 'application/llsd+xml').then((resp: ICapResponse) =>
|
||||
{
|
||||
this.capabilities = LLSD.LLSD.parseXML(body);
|
||||
this.capabilities = LLSD.LLSD.parseXML(resp.body);
|
||||
this.gotSeedCap = true;
|
||||
this.onGotSeedCap.next();
|
||||
if (this.capabilities['EventQueueGet'])
|
||||
@@ -151,9 +154,9 @@ export class Caps
|
||||
{
|
||||
return new Promise<Buffer>((resolve, reject) =>
|
||||
{
|
||||
this.getCapability('ViewerAsset').then((url) =>
|
||||
this.getCapability('ViewerAsset').then((capURL) =>
|
||||
{
|
||||
const assetURL = url + '/?' + type + '_id=' + uuid.toString();
|
||||
const assetURL = capURL + '/?' + type + '_id=' + uuid.toString();
|
||||
request({
|
||||
'uri': assetURL,
|
||||
'rejectUnauthorized': false,
|
||||
@@ -174,16 +177,16 @@ export class Caps
|
||||
});
|
||||
}
|
||||
|
||||
request(url: string, data: string | Buffer, contentType: string): Promise<string>
|
||||
request(capURL: string, data: string | Buffer, contentType: string): Promise<ICapResponse>
|
||||
{
|
||||
return new Promise<string>((resolve, reject) =>
|
||||
return new Promise<ICapResponse>((resolve, reject) =>
|
||||
{
|
||||
request({
|
||||
'headers': {
|
||||
'Content-Length': data.length,
|
||||
'Content-Type': contentType
|
||||
},
|
||||
'uri': url,
|
||||
'uri': capURL,
|
||||
'body': data,
|
||||
'rejectUnauthorized': false,
|
||||
'method': 'POST'
|
||||
@@ -195,18 +198,18 @@ export class Caps
|
||||
}
|
||||
else
|
||||
{
|
||||
resolve(body);
|
||||
resolve({status: res.statusCode, body: body});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
requestGet(url: string): Promise<string>
|
||||
requestGet(requestURL: string): Promise<ICapResponse>
|
||||
{
|
||||
return new Promise<string>((resolve, reject) =>
|
||||
return new Promise<ICapResponse>((resolve, reject) =>
|
||||
{
|
||||
request({
|
||||
'uri': url,
|
||||
'uri': requestURL,
|
||||
'rejectUnauthorized': false,
|
||||
'method': 'GET'
|
||||
}, (err, res, body) =>
|
||||
@@ -217,7 +220,7 @@ export class Caps
|
||||
}
|
||||
else
|
||||
{
|
||||
resolve(body);
|
||||
resolve({status: res.statusCode, body: body});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -265,13 +268,31 @@ export class Caps
|
||||
});
|
||||
}
|
||||
|
||||
capsRequestUpload(url: string, data: Buffer): Promise<any>
|
||||
capsRequestUpload(capURL: string, data: Buffer): Promise<any>
|
||||
{
|
||||
return new Promise<any>((resolve, reject) =>
|
||||
{
|
||||
this.request(url, data, 'application/octet-stream').then((body: string) =>
|
||||
this.request(capURL, data, 'application/octet-stream').then((resp: ICapResponse) =>
|
||||
{
|
||||
resolve(LLSD.LLSD.parseXML(body));
|
||||
try
|
||||
{
|
||||
resolve(LLSD.LLSD.parseXML(resp.body));
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
if (resp.status === 201)
|
||||
{
|
||||
resolve({});
|
||||
}
|
||||
else if (resp.status === 403)
|
||||
{
|
||||
reject(new Error('Access Denied'));
|
||||
}
|
||||
else
|
||||
{
|
||||
reject(err);
|
||||
}
|
||||
}
|
||||
}).catch((err) =>
|
||||
{
|
||||
console.error(err);
|
||||
@@ -284,20 +305,29 @@ export class Caps
|
||||
{
|
||||
return new Promise<any>((resolve, reject) =>
|
||||
{
|
||||
this.getCapability(capability).then((url) =>
|
||||
this.getCapability(capability).then((capURL) =>
|
||||
{
|
||||
this.requestGet(url).then((body: string) =>
|
||||
this.requestGet(capURL).then((resp: ICapResponse) =>
|
||||
{
|
||||
let result: any = null;
|
||||
try
|
||||
{
|
||||
result = LLSD.LLSD.parseXML(body);
|
||||
result = LLSD.LLSD.parseXML(resp.body);
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
console.error('Error parsing LLSD');
|
||||
console.error(body);
|
||||
reject(err);
|
||||
if (resp.status === 201)
|
||||
{
|
||||
resolve({});
|
||||
}
|
||||
else if (resp.status === 403)
|
||||
{
|
||||
reject(new Error('Access Denied'));
|
||||
}
|
||||
else
|
||||
{
|
||||
reject(err);
|
||||
}
|
||||
}
|
||||
resolve(result);
|
||||
}).catch((err) =>
|
||||
@@ -346,22 +376,33 @@ export class Caps
|
||||
});
|
||||
}
|
||||
|
||||
capsPerformXMLRequest(url: string, data: any): Promise<any>
|
||||
capsPerformXMLRequest(capURL: string, data: any): Promise<any>
|
||||
{
|
||||
return new Promise<any>(async (resolve, reject) =>
|
||||
{
|
||||
const xml = LLSD.LLSD.formatXML(data);
|
||||
this.request(url, xml, 'application/llsd+xml').then((body: string) =>
|
||||
this.request(capURL, xml, 'application/llsd+xml').then((resp: ICapResponse) =>
|
||||
{
|
||||
let result: any = null;
|
||||
try
|
||||
{
|
||||
result = LLSD.LLSD.parseXML(body);
|
||||
result = LLSD.LLSD.parseXML(resp.body);
|
||||
resolve(result);
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
reject(err);
|
||||
if (resp.status === 201)
|
||||
{
|
||||
resolve({});
|
||||
}
|
||||
else if (resp.status === 403)
|
||||
{
|
||||
reject(new Error('Access Denied'));
|
||||
}
|
||||
else
|
||||
{
|
||||
reject(err);
|
||||
}
|
||||
}
|
||||
}).catch((err) =>
|
||||
{
|
||||
@@ -371,23 +412,44 @@ export class Caps
|
||||
});
|
||||
}
|
||||
|
||||
async capsRequestXML(capability: string, data: any, debug = false): Promise<any>
|
||||
async capsRequestXML(capability: string | [string, {[key: string]: string}], data: any, debug = false): Promise<any>
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
console.log(data);
|
||||
}
|
||||
|
||||
await this.waitForCapTimeout(capability);
|
||||
let capName = '';
|
||||
let queryParams: {[key: string]: string} = {};
|
||||
if (typeof capability === 'string')
|
||||
{
|
||||
capName = capability;
|
||||
}
|
||||
else
|
||||
{
|
||||
capName = capability[0];
|
||||
queryParams = capability[1];
|
||||
}
|
||||
|
||||
const url = await this.getCapability(capability);
|
||||
await this.waitForCapTimeout(capName);
|
||||
|
||||
let capURL = await this.getCapability(capName);
|
||||
if (Object.keys(queryParams).length > 0)
|
||||
{
|
||||
const parsedURL = url.parse(capURL, true);
|
||||
for (const key of Object.keys(queryParams))
|
||||
{
|
||||
parsedURL.query[key] = queryParams[key];
|
||||
}
|
||||
capURL = url.format(parsedURL);
|
||||
}
|
||||
try
|
||||
{
|
||||
return await this.capsPerformXMLRequest(url, data);
|
||||
return await this.capsPerformXMLRequest(capURL, data);
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
console.log('Error with cap ' + capability);
|
||||
console.log('Error with cap ' + capName);
|
||||
console.log(error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
import {CommandsBase} from './CommandsBase';
|
||||
import {UUID} from '../UUID';
|
||||
import {InstantMessageDialog} from '../../enums/InstantMessageDialog';
|
||||
import {Utils} from '../Utils';
|
||||
import {PacketFlags} from '../../enums/PacketFlags';
|
||||
import {ImprovedInstantMessageMessage} from '../messages/ImprovedInstantMessage';
|
||||
import {Vector3} from '../Vector3';
|
||||
import {InviteGroupRequestMessage} from '../messages/InviteGroupRequest';
|
||||
import {GroupRole} from '../GroupRole';
|
||||
import {GroupRoleDataRequestMessage} from '../messages/GroupRoleDataRequest';
|
||||
import {Message} from '../../enums/Message';
|
||||
import {Packet} from '../Packet';
|
||||
import {GroupRoleDataReplyMessage} from '../messages/GroupRoleDataReply';
|
||||
import {GroupMember} from '../GroupMember';
|
||||
import {FilterResponse} from '../../enums/FilterResponse';
|
||||
import { CommandsBase } from './CommandsBase';
|
||||
import { UUID } from '../UUID';
|
||||
import { InstantMessageDialog } from '../../enums/InstantMessageDialog';
|
||||
import { Utils } from '../Utils';
|
||||
import { PacketFlags } from '../../enums/PacketFlags';
|
||||
import { ImprovedInstantMessageMessage } from '../messages/ImprovedInstantMessage';
|
||||
import { Vector3 } from '../Vector3';
|
||||
import { InviteGroupRequestMessage } from '../messages/InviteGroupRequest';
|
||||
import { GroupRole } from '../GroupRole';
|
||||
import { GroupRoleDataRequestMessage } from '../messages/GroupRoleDataRequest';
|
||||
import { Message } from '../../enums/Message';
|
||||
import { GroupRoleDataReplyMessage } from '../messages/GroupRoleDataReply';
|
||||
import { GroupMember } from '../GroupMember';
|
||||
import { FilterResponse } from '../../enums/FilterResponse';
|
||||
import * as LLSD from '@caspertech/llsd';
|
||||
import {GroupInviteEvent} from '../..';
|
||||
import {EjectGroupMemberRequestMessage} from '../messages/EjectGroupMemberRequest';
|
||||
import {GroupProfileRequestMessage} from '../messages/GroupProfileRequest';
|
||||
import {GroupProfileReplyMessage} from '../messages/GroupProfileReply';
|
||||
import {GroupProfileReplyEvent} from '../..';
|
||||
import { GroupInviteEvent, GroupProfileReplyEvent } from '../..';
|
||||
import { EjectGroupMemberRequestMessage } from '../messages/EjectGroupMemberRequest';
|
||||
import { GroupProfileRequestMessage } from '../messages/GroupProfileRequest';
|
||||
import { GroupProfileReplyMessage } from '../messages/GroupProfileReply';
|
||||
import { GroupBanAction } from '../../enums/GroupBanAction';
|
||||
|
||||
export class GroupCommands extends CommandsBase
|
||||
{
|
||||
@@ -176,6 +175,50 @@ export class GroupCommands extends CommandsBase
|
||||
return await circuit.waitForAck(sequenceNo, 10000);
|
||||
}
|
||||
|
||||
async unbanMembers(groupID: UUID | string, avatars: UUID | string | string[] | UUID[])
|
||||
{
|
||||
this.banMembers(groupID, avatars, GroupBanAction.Unban);
|
||||
}
|
||||
|
||||
async banMembers(groupID: UUID | string, avatars: UUID | string | string[] | UUID[], groupAction: GroupBanAction = GroupBanAction.Ban)
|
||||
{
|
||||
const listOfIDs: string[] = [];
|
||||
|
||||
if (Array.isArray(avatars))
|
||||
{
|
||||
for (const av of avatars)
|
||||
{
|
||||
if (typeof av === 'string')
|
||||
{
|
||||
listOfIDs.push(av);
|
||||
}
|
||||
else
|
||||
{
|
||||
listOfIDs.push(av.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (typeof avatars === 'string')
|
||||
{
|
||||
listOfIDs.push(avatars);
|
||||
}
|
||||
else
|
||||
{
|
||||
listOfIDs.push(avatars.toString());
|
||||
}
|
||||
|
||||
const requestData: any = {
|
||||
'ban_action': groupAction,
|
||||
'ban_ids': []
|
||||
};
|
||||
for (const id of listOfIDs)
|
||||
{
|
||||
requestData.ban_ids.push(new LLSD.UUID(id));
|
||||
}
|
||||
|
||||
await this.currentRegion.caps.capsRequestXML(['GroupAPIv1', {'group_id': groupID.toString()}], requestData);
|
||||
}
|
||||
|
||||
getMemberList(groupID: UUID | string): Promise<GroupMember[]>
|
||||
{
|
||||
return new Promise<GroupMember[]>((resolve, reject) =>
|
||||
|
||||
5
lib/classes/interfaces/ICapResponse.ts
Normal file
5
lib/classes/interfaces/ICapResponse.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export interface ICapResponse
|
||||
{
|
||||
status: number;
|
||||
body: string;
|
||||
}
|
||||
5
lib/enums/GroupBanAction.ts
Normal file
5
lib/enums/GroupBanAction.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export enum GroupBanAction
|
||||
{
|
||||
Ban = 1,
|
||||
Unban = 2
|
||||
}
|
||||
Reference in New Issue
Block a user