Refactor examples into a better form factor
This commit is contained in:
18
examples/Camera/Camera.ts
Normal file
18
examples/Camera/Camera.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { ExampleBot } from '../ExampleBot';
|
||||
import { Vector3 } from '../../lib/classes/Vector3';
|
||||
|
||||
class Camera extends ExampleBot
|
||||
{
|
||||
async onConnected()
|
||||
{
|
||||
const height = 64;
|
||||
this.bot.clientCommands.agent.setCamera(
|
||||
new Vector3([128, 128, height]),
|
||||
new Vector3([128, 128, 0]),
|
||||
256,
|
||||
new Vector3([-1.0, 0, 0]),
|
||||
new Vector3([0.0, 1.0, 0]));
|
||||
}
|
||||
}
|
||||
|
||||
new Camera().run().then(() => {}).catch((err) => { console.error(err) });
|
||||
171
examples/ExampleBot.ts
Normal file
171
examples/ExampleBot.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
import Signals = NodeJS.Signals;
|
||||
import Timeout = NodeJS.Timeout;
|
||||
|
||||
import * as path from 'path';
|
||||
import { LoginResponse } from '../lib/classes/LoginResponse';
|
||||
import { Bot } from '../lib/Bot';
|
||||
import { LoginParameters } from '../lib/classes/LoginParameters';
|
||||
import { BotOptionFlags } from '../lib/enums/BotOptionFlags';
|
||||
|
||||
export class ExampleBot
|
||||
{
|
||||
protected masterAvatar = 'd1cd5b71-6209-4595-9bf0-771bf689ce00';
|
||||
protected isConnected = false;
|
||||
protected isConnecting = false;
|
||||
protected loginResponse?: LoginResponse;
|
||||
|
||||
protected bot: Bot;
|
||||
private reconnectTimer?: Timeout;
|
||||
|
||||
constructor()
|
||||
{
|
||||
const loginParameters = new LoginParameters();
|
||||
const parameters = require(path.join(__dirname, '..', '..', 'examples', 'loginParameters.json'));
|
||||
loginParameters.firstName = parameters.firstName;
|
||||
loginParameters.lastName = parameters.lastName;
|
||||
loginParameters.password = parameters.password;
|
||||
loginParameters.start = parameters.start;
|
||||
|
||||
// If you don't intend to use the object store (i.e you have no interest in inworld objects, textures, etc,
|
||||
// using nmv.BotOptionFlags.LiteObjectStore will drastically reduce the footprint and CPU usage.
|
||||
//
|
||||
// The full object store has a full searchable rtree index, the lite does not.
|
||||
//
|
||||
// For the minimum footprint, use :
|
||||
//
|
||||
// const options = nmv.BotOptionFlags.LiteObjectStore | nmv.BotOptionFlags.StoreMyAttachmentsOnly;
|
||||
|
||||
const options = BotOptionFlags.None;
|
||||
|
||||
this.bot = new Bot(loginParameters, options);
|
||||
|
||||
// This will tell the bot to keep trying to teleport back to the 'stay' location.
|
||||
// You can specify a region and position, such as:
|
||||
// bot.stayPut(true, 'Izanagi', new nmv.Vector3([128, 128, 21]));
|
||||
// Note that the 'stay' location will be updated if you request or accept a lure (a teleport).
|
||||
// If no region is specified, it will be set to the region you log in to.
|
||||
this.bot.stayPut(true);
|
||||
}
|
||||
|
||||
public async run()
|
||||
{
|
||||
const exitHandler = async(options: { exit?: boolean }, err: Error | number | Signals) =>
|
||||
{
|
||||
if (err && err instanceof Error)
|
||||
{
|
||||
console.log(err.stack);
|
||||
}
|
||||
if (this.isConnected)
|
||||
{
|
||||
console.log('Disconnecting');
|
||||
try
|
||||
{
|
||||
await this.bot.close();
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
console.error('Error when closing client:');
|
||||
console.error(error);
|
||||
}
|
||||
process.exit();
|
||||
return;
|
||||
}
|
||||
if (options.exit)
|
||||
{
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Do something when app is closing
|
||||
process.on('exit', exitHandler.bind(this, {}));
|
||||
|
||||
// Catches ctrl+c event
|
||||
process.on('SIGINT', exitHandler.bind(this, { exit: true }));
|
||||
|
||||
// Catches "kill pid"
|
||||
process.on('SIGUSR1', exitHandler.bind(this, { exit: true }));
|
||||
process.on('SIGUSR2', exitHandler.bind(this, { exit: true }));
|
||||
|
||||
// Catches uncaught exceptions
|
||||
process.on('uncaughtException', exitHandler.bind(this, { exit: true }));
|
||||
|
||||
await this.login();
|
||||
}
|
||||
|
||||
protected async onConnected()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private async login()
|
||||
{
|
||||
if (this.isConnecting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.isConnecting = true;
|
||||
try
|
||||
{
|
||||
if (this.reconnectTimer !== undefined)
|
||||
{
|
||||
clearInterval(this.reconnectTimer);
|
||||
}
|
||||
this.reconnectTimer = setInterval(this.reconnectCheck.bind(this), 60000);
|
||||
|
||||
console.log('Logging in..');
|
||||
this.loginResponse = await this.bot.login();
|
||||
|
||||
console.log('Login complete');
|
||||
|
||||
// Establish circuit with region
|
||||
await this.bot.connectToSim();
|
||||
|
||||
console.log('Waiting for event queue');
|
||||
await this.bot.waitForEventQueue();
|
||||
|
||||
this.isConnected = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.isConnecting = false;
|
||||
}
|
||||
return this.connected();
|
||||
}
|
||||
|
||||
private async reconnectCheck()
|
||||
{
|
||||
if (!this.isConnected)
|
||||
{
|
||||
await this.login();
|
||||
}
|
||||
}
|
||||
|
||||
private async connected()
|
||||
{
|
||||
this.bot.clientEvents.onDisconnected.subscribe((event) =>
|
||||
{
|
||||
if (event.requested)
|
||||
{
|
||||
if (this.reconnectTimer !== undefined)
|
||||
{
|
||||
clearInterval(this.reconnectTimer);
|
||||
}
|
||||
}
|
||||
this.isConnected = false;
|
||||
console.log('Disconnected from simulator: ' + event.message);
|
||||
});
|
||||
await this.onConnected();
|
||||
}
|
||||
|
||||
private async close()
|
||||
{
|
||||
if (this.reconnectTimer !== undefined)
|
||||
{
|
||||
clearInterval(this.reconnectTimer);
|
||||
this.reconnectTimer = undefined;
|
||||
}
|
||||
return this.bot.close();
|
||||
}
|
||||
}
|
||||
58
examples/Friends/Friends.ts
Normal file
58
examples/Friends/Friends.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { ExampleBot } from '../ExampleBot';
|
||||
import { FriendRequestEvent } from '../../lib/events/FriendRequestEvent';
|
||||
import { FriendResponseEvent } from '../../lib/events/FriendResponseEvent';
|
||||
|
||||
class Friends extends ExampleBot
|
||||
{
|
||||
async onConnected()
|
||||
{
|
||||
this.bot.clientEvents.onFriendRequest.subscribe(this.onFriendRequest.bind(this));
|
||||
this.bot.clientEvents.onFriendResponse.subscribe(this.onFriendResponse.bind(this));
|
||||
|
||||
this.bot.clientCommands.friends.sendFriendRequest(this.masterAvatar, 'Be friends with me?').then(() => {});
|
||||
|
||||
try
|
||||
{
|
||||
// Get map location of the master avatar. Will fail if you don't have map rights
|
||||
const regionLocation = await this.bot.clientCommands.friends.getFriendMapLocation(this.masterAvatar);
|
||||
console.log('Master is in ' + regionLocation.regionName + ' at <' + regionLocation.localX + ', ' + regionLocation.localY + '> and there are ' + regionLocation.avatars.length + ' other avatars there too! You stalker!');
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
console.log('Map location request failed. The bot probably does not have map rights on the master avatar, or they are offline.');
|
||||
}
|
||||
}
|
||||
|
||||
async onFriendRequest(event: FriendRequestEvent)
|
||||
{
|
||||
if (event.from.toString() === this.masterAvatar)
|
||||
{
|
||||
console.log('Accepting friend request from ' + event.fromName);
|
||||
this.bot.clientCommands.friends.acceptFriendRequest(event).then(() =>
|
||||
{
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log('Rejecting friend request from ' + event.fromName);
|
||||
this.bot.clientCommands.friends.rejectFriendRequest(event).then(() =>
|
||||
{
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async onFriendResponse(response: FriendResponseEvent)
|
||||
{
|
||||
if (response.accepted)
|
||||
{
|
||||
console.log(response.fromName + ' accepted your friend request');
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log(response.fromName + ' declined your friend request');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
new Friends().run().then(() => {}).catch((err) => { console.error(err) });
|
||||
96
examples/Groups/Group.ts
Normal file
96
examples/Groups/Group.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import { ExampleBot } from '../ExampleBot';
|
||||
import { UUID } from '../../lib/classes/UUID';
|
||||
import { GroupNoticeEvent } from '../../lib/events/GroupNoticeEvent';
|
||||
|
||||
class Group extends ExampleBot
|
||||
{
|
||||
async onConnected()
|
||||
{
|
||||
this.bot.clientEvents.onGroupNotice.subscribe(this.onGroupNotice.bind(this));
|
||||
|
||||
// Group invite example
|
||||
// Just omit the role parameter for "everyone" role
|
||||
//
|
||||
// bot.clientCommands.group.sendGroupInvite("c6424e05-6e2c-fb03-220b-ca7904d11e04", "d1cd5b71-6209-4595-9bf0-771bf689ce00");
|
||||
|
||||
// Advanced group invite example
|
||||
//
|
||||
|
||||
const userToInvite = new UUID('d1cd5b71-6209-4595-9bf0-771bf689ce00');
|
||||
const groupID = new UUID('4b35083d-b51a-a148-c400-6f1038a5589e');
|
||||
|
||||
// Retrieve group roles
|
||||
const roles = await this.bot.clientCommands.group.getGroupRoles(groupID);
|
||||
|
||||
for (const role of roles)
|
||||
{
|
||||
if (role.Name === 'Officers')
|
||||
{
|
||||
// IMPORTANT: IN PRODUCTION, IT IS HIGHLY RECOMMENDED TO CACHE THIS LIST.
|
||||
//
|
||||
try
|
||||
{
|
||||
const members = await this.bot.clientCommands.group.getMemberList(groupID);
|
||||
let found = true;
|
||||
for (const member of members)
|
||||
{
|
||||
if (member.AgentID.toString() === userToInvite.toString())
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
{
|
||||
console.log('User already in group, skipping invite');
|
||||
}
|
||||
else
|
||||
{
|
||||
this.bot.clientCommands.group.sendGroupInvite(groupID, userToInvite, role.RoleID).then(() =>
|
||||
{
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
console.error('Error retrieving member list for group invite');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get group member list
|
||||
try
|
||||
{
|
||||
const memberList = await this.bot.clientCommands.group.getMemberList(groupID);
|
||||
console.log(memberList.length + ' members in member list');
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
// Probably access denied
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
// Get group ban list
|
||||
try
|
||||
{
|
||||
const banList = await this.bot.clientCommands.group.getBanList(groupID);
|
||||
console.log(banList.length + ' members in ban list');
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
// Probably access denied
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async onGroupNotice(event: GroupNoticeEvent)
|
||||
{
|
||||
// Get group name
|
||||
const groupProfile = await this.bot.clientCommands.group.getGroupProfile(event.groupID);
|
||||
|
||||
console.log('Group notice from ' + event.fromName + ' (' + event.from + '), from group ' + groupProfile.Name + ' (' + event.groupID + ')');
|
||||
console.log('Subject: ' + event.subject);
|
||||
console.log('Message: ' + event.message);
|
||||
}
|
||||
}
|
||||
|
||||
new Group().run().then(() => {}).catch((err: Error) => { console.error(err) });
|
||||
80
examples/Groups/GroupChat.ts
Normal file
80
examples/Groups/GroupChat.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { ExampleBot } from '../ExampleBot';
|
||||
import { UUID } from '../../lib/classes/UUID';
|
||||
import { GroupChatEvent } from '../../lib/events/GroupChatEvent';
|
||||
|
||||
class GroupChat extends ExampleBot
|
||||
{
|
||||
private pings: {[key: string]: number} = {};
|
||||
|
||||
async onConnected()
|
||||
{
|
||||
const groupID = new UUID('4b35083d-b51a-a148-c400-6f1038a5589e');
|
||||
|
||||
this.bot.clientEvents.onGroupChat.subscribe(this.onGroupChat.bind(this));
|
||||
|
||||
// Start a group chat session - equivalent to opening a group chat but not sending a message
|
||||
await this.bot.clientCommands.comms.startGroupChatSession(groupID, '');
|
||||
|
||||
// Send a group message
|
||||
await this.bot.clientCommands.comms.sendGroupMessage(groupID, 'Test');
|
||||
|
||||
const badGuyID = new UUID('1481561a-9113-46f8-9c02-9ac1bf005de7');
|
||||
await this.bot.clientCommands.comms.moderateGroupChat(groupID, badGuyID, true, true);
|
||||
|
||||
// Now, the group mute stuff is often pretty useless because an avatar can just leave the session and re-join.
|
||||
// Let's enforce it a little better.
|
||||
const groupChatSubscriber = this.bot.clientEvents.onGroupChatAgentListUpdate.subscribe((event) =>
|
||||
{
|
||||
if (event.groupID.equals(groupID) && event.agentID.equals(badGuyID) && event.entered)
|
||||
{
|
||||
this.bot.clientCommands.comms.moderateGroupChat(groupID, badGuyID, true, true).then(() =>
|
||||
{
|
||||
console.log('Re-enforced mute on ' + badGuyID.toString());
|
||||
}).catch((err) =>
|
||||
{
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Actually, maybe we want to ban the chump.
|
||||
await this.bot.clientCommands.group.banMembers(groupID, [badGuyID]);
|
||||
await this.bot.clientCommands.group.ejectFromGroup(groupID, badGuyID);
|
||||
}
|
||||
|
||||
async onGroupChat(event: GroupChatEvent)
|
||||
{
|
||||
console.log('Group chat: ' + event.fromName + ': ' + event.message);
|
||||
if (event.message === '!ping')
|
||||
{
|
||||
const ping = UUID.random().toString();
|
||||
this.pings[ping] = Math.floor(new Date().getTime());
|
||||
try
|
||||
{
|
||||
const memberCount = await this.bot.clientCommands.comms.sendGroupMessage(event.groupID, 'ping ' + ping);
|
||||
console.log('Group message sent to ' + memberCount + ' members');
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
console.error('Failed to send group message:');
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
else if (event.from.toString() === this.bot.agentID().toString())
|
||||
{
|
||||
if (event.message.substr(0, 5) === 'ping ')
|
||||
{
|
||||
const pingID = event.message.substr(5);
|
||||
if (this.pings[pingID])
|
||||
{
|
||||
const time = (new Date().getTime()) - this.pings[pingID];
|
||||
delete this.pings[pingID];
|
||||
await this.bot.clientCommands.comms.sendGroupMessage(event.groupID, 'Chat lag: ' + time + 'ms');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new GroupChat().run().then(() => {}).catch((err: Error) => { console.error(err) });
|
||||
35
examples/InstantMessages/InstantMessages.ts
Normal file
35
examples/InstantMessages/InstantMessages.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
|
||||
import { ExampleBot } from '../ExampleBot';
|
||||
import { InstantMessageEvent } from '../../lib/events/InstantMessageEvent';
|
||||
import { ChatSourceType } from '../../lib/enums/ChatSourceType';
|
||||
import { InstantMessageEventFlags } from '../../lib/enums/InstantMessageEventFlags';
|
||||
|
||||
class InstantMessages extends ExampleBot
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
async onConnected()
|
||||
{
|
||||
this.bot.clientEvents.onInstantMessage.subscribe(this.onInstantMessage.bind(this));
|
||||
}
|
||||
|
||||
async onInstantMessage(event: InstantMessageEvent)
|
||||
{
|
||||
if (event.source === ChatSourceType.Agent)
|
||||
{
|
||||
if (!(event.flags & InstantMessageEventFlags.startTyping || event.flags & InstantMessageEventFlags.finishTyping))
|
||||
{
|
||||
// typeInstantMessage will emulate a human-ish typing speed
|
||||
await this.bot.clientCommands.comms.typeInstantMessage(event.from, 'Thanks for the message! This account is a scripted agent (bot), so cannot reply to your query. Sorry!');
|
||||
|
||||
// sendInstantMessage will send it instantly
|
||||
await this.bot.clientCommands.comms.sendInstantMessage(event.from, 'Of course I still love you!');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new InstantMessages().run().then(() => {}).catch((err: Error) => { console.error(err) });
|
||||
134
examples/Inventory/Inventory.ts
Normal file
134
examples/Inventory/Inventory.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import { ExampleBot } from '../ExampleBot';
|
||||
import { InventoryFolder } from '../../lib/classes/InventoryFolder';
|
||||
import { FolderType } from '../../lib/enums/FolderType';
|
||||
import { InventoryItem } from '../../lib/classes/InventoryItem';
|
||||
import { LLLindenText } from '../../lib/classes/LLLindenText';
|
||||
import { AssetType } from '../../lib/enums/AssetType';
|
||||
import { InventoryType } from '../../lib/enums/InventoryType';
|
||||
import { PermissionMask } from '../../lib/enums/PermissionMask';
|
||||
import { InventoryResponseEvent } from '../../lib/events/InventoryResponseEvent';
|
||||
import { InventoryOfferedEvent } from '../../lib/events/InventoryOfferedEvent';
|
||||
|
||||
class Inventory extends ExampleBot
|
||||
{
|
||||
async onConnected()
|
||||
{
|
||||
this.bot.clientEvents.onInventoryOffered.subscribe(this.onInventoryOffered.bind(this));
|
||||
this.bot.clientEvents.onInventoryResponse.subscribe(this.onInventoryResponse.bind(this));
|
||||
|
||||
// Get the root inventory folder
|
||||
const rootFolder = this.bot.clientCommands.inventory.getInventoryRoot();
|
||||
|
||||
// Populate the root folder
|
||||
await rootFolder.populate(false);
|
||||
|
||||
const exampleFolderName = 'node-metaverse example';
|
||||
const exampleNotecardName = 'Example Notecard';
|
||||
|
||||
let exampleFolder: InventoryFolder | undefined = undefined;
|
||||
for (const childFolder of rootFolder.folders)
|
||||
{
|
||||
if (childFolder.name === exampleFolderName)
|
||||
{
|
||||
exampleFolder = childFolder;
|
||||
await exampleFolder.populate(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Our folder doesnt' seem to exist, so create it
|
||||
if (exampleFolder === undefined)
|
||||
{
|
||||
exampleFolder = await rootFolder.createFolder(exampleFolderName, FolderType.None);
|
||||
}
|
||||
|
||||
// See if we've already made our test notecard to avoid clutter..
|
||||
let exampleNotecard: InventoryItem | undefined = undefined;
|
||||
for (const childItem of exampleFolder.items)
|
||||
{
|
||||
if (childItem.name === exampleNotecardName)
|
||||
{
|
||||
exampleNotecard = childItem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the notecard
|
||||
if (exampleNotecard === undefined)
|
||||
{
|
||||
const notecard = new LLLindenText();
|
||||
notecard.body = 'This is a notecard I made all by myself at ' + new Date().toString();
|
||||
exampleNotecard = await exampleFolder.uploadAsset(AssetType.Notecard, InventoryType.Notecard, notecard.toAsset(), exampleNotecardName, 'This is an example notecard');
|
||||
}
|
||||
|
||||
// Set notecard to transfer only
|
||||
|
||||
exampleNotecard.permissions.nextOwnerMask = PermissionMask.Transfer | PermissionMask.Modify;
|
||||
await exampleNotecard.update();
|
||||
|
||||
// Give the notecard to our owner
|
||||
await this.bot.clientCommands.comms.giveInventory(this.masterAvatar, exampleNotecard);
|
||||
|
||||
// Enumerate library
|
||||
const folders = this.bot.clientCommands.inventory.getLibraryRoot().getChildFolders();
|
||||
for (const folder of folders)
|
||||
{
|
||||
await this.iterateFolder(folder, '[ROOT]');
|
||||
}
|
||||
console.log('Done iterating through library');
|
||||
}
|
||||
|
||||
async onInventoryResponse(response: InventoryResponseEvent)
|
||||
{
|
||||
if (response.accepted)
|
||||
{
|
||||
console.log(response.fromName + ' accepted your inventory offer');
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log(response.fromName + ' declined your inventory offer');
|
||||
}
|
||||
}
|
||||
|
||||
async iterateFolder(folder: InventoryFolder, prefix: string)
|
||||
{
|
||||
console.log(prefix + ' [' + folder.name + ']');
|
||||
await folder.populate(false);
|
||||
|
||||
for (const subFolder of folder.folders)
|
||||
{
|
||||
await this.iterateFolder(subFolder, prefix + ' [' + folder.name + ']');
|
||||
}
|
||||
|
||||
for (const item of folder.items)
|
||||
{
|
||||
console.log(prefix + ' [' + folder.name + ']' + ': ' + item.name);
|
||||
|
||||
if (item.name === 'anim SMOOTH')
|
||||
{
|
||||
// Send this to our master av
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async onInventoryOffered(event: InventoryOfferedEvent)
|
||||
{
|
||||
if (event.from.toString() === this.masterAvatar)
|
||||
{
|
||||
console.log('Accepting inventory offer from ' + event.fromName);
|
||||
this.bot.clientCommands.inventory.acceptInventoryOffer(event).then(() =>
|
||||
{
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log('Rejecting inventory offer from ' + event.fromName);
|
||||
this.bot.clientCommands.inventory.rejectInventoryOffer(event).then(() =>
|
||||
{
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new Inventory().run().then(() => {}).catch((err) => { console.error(err) });
|
||||
21
examples/Region/Parcels.ts
Normal file
21
examples/Region/Parcels.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { ExampleBot } from '../ExampleBot';
|
||||
|
||||
class Parcels extends ExampleBot
|
||||
{
|
||||
async onConnected()
|
||||
{
|
||||
const parcelInMiddle = await this.bot.clientCommands.region.getParcelAt(128, 128);
|
||||
console.log('Parcel at 128x128 is ' + parcelInMiddle.Name);
|
||||
|
||||
const parcels = await this.bot.clientCommands.region.getParcels();
|
||||
console.log('Parcels on region:');
|
||||
console.log('========================');
|
||||
for (const p of parcels)
|
||||
{
|
||||
console.log(p.Name);
|
||||
}
|
||||
console.log('========================');
|
||||
}
|
||||
}
|
||||
|
||||
new Parcels().run().then(() => {}).catch((err) => { console.error(err) });
|
||||
48
examples/Teleports/Teleports.ts
Normal file
48
examples/Teleports/Teleports.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { ExampleBot } from '../ExampleBot';
|
||||
import { LureEvent } from '../../lib/events/LureEvent';
|
||||
|
||||
class Teleports extends ExampleBot
|
||||
{
|
||||
async onConnected()
|
||||
{
|
||||
// "OnLure" event fires when someone tries to teleport us
|
||||
this.bot.clientEvents.onLure.subscribe(this.onLure.bind(this));
|
||||
|
||||
// Alternatively we can TP someone else to us
|
||||
await this.bot.clientCommands.comms.sendTeleport(this.masterAvatar);
|
||||
}
|
||||
|
||||
async onLure(lureEvent: LureEvent)
|
||||
{
|
||||
try
|
||||
{
|
||||
const regionInfo = await this.bot.clientCommands.grid.getRegionMapInfo(lureEvent.gridX / 256, lureEvent.gridY / 256);
|
||||
if (lureEvent.from.toString() === this.masterAvatar)
|
||||
{
|
||||
console.log('Accepting teleport lure to ' + regionInfo.block.name + ' (' + regionInfo.avatars.length + ' avatar' + ((regionInfo.avatars.length === 1) ? '' : 's') + '' +
|
||||
' present) from ' + lureEvent.fromName + ' with message: ' + lureEvent.lureMessage);
|
||||
try
|
||||
{
|
||||
await this.bot.clientCommands.teleport.acceptTeleport(lureEvent);
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
console.error('Teleport error:');
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log('Ignoring teleport lure to ' + regionInfo.block.name + ' (' + regionInfo.avatars.length + ' avatar' + ((regionInfo.avatars.length === 1) ? '' : 's') + ' ' +
|
||||
'present) from ' + lureEvent.fromName + ' with message: ' + lureEvent.lureMessage);
|
||||
}
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
console.error('Failed to get region map info:');
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new Teleports().run().then(() => {}).catch((err) => { console.error(err) });
|
||||
7
examples/loginParameters.example.json
Normal file
7
examples/loginParameters.example.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"firstName": "Username",
|
||||
"lastName": "Resident",
|
||||
"password": "YourPassword",
|
||||
"start": "last", //first, last, or login uri like uri:<existing region name>&<x>&<y>&<z>
|
||||
"url": "https://login.agni.lindenlab.com/cgi-bin/login.cgi"
|
||||
}
|
||||
Reference in New Issue
Block a user