Skip to content
9 changes: 9 additions & 0 deletions DscEvents/clientReady.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ module.exports = async client => {
// Log that the bot is online.
logger.log(`${client.user.tag}, ready to serve ${client.guilds.cache.map(g => g.memberCount).reduce((a, b) => a + b)} users in ${client.guilds.cache.size} servers.`, "ready");

// Register slash commands globally so they work in DMs
try {
const globalCommands = client.container.slashcmds.map(cmd => cmd.data);
await client.application?.commands.set(globalCommands);
logger.log(`Registered ${globalCommands.length} global slash commands`, "ready");
} catch (error) {
logger.log(`Failed to register global commands: ${error.message}`, "error");
}

/// this sets the activity, so the bot is being silly.
client.user.setActivity(`with the Metaverse`, { type: "PLAYING" });
};
14 changes: 4 additions & 10 deletions DscEvents/guildCreate.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,8 @@ const logger = require("../modules/Logger.js");

module.exports = (client, guild) => {
logger.log(`[GUILD JOIN] ${guild.id} added the bot. Owner: ${guild.ownerId}`);
// We'll partition the slash commands based on the guildOnly boolean.
// Separating them into the correct objects defined in the array below.
const [globalCmds, guildCmds] = client.container.slashcmds.partition(c => !c.conf.guildOnly);

// We'll use set but please keep in mind that `set` is overkill for a singular command.
// Set the guild commands like
client.guilds.cache.get(guild.id)?.commands.set(guildCmds.map(c => c.commandData));

// Then set the global commands like
client.application?.commands.set(globalCmds.map(c => c.commandData)).catch(e => console.log(e));

// Global commands are already registered in clientReady event
// No need to register them again here since they work in DMs and all guilds now
logger.log(`Global slash commands are available in guild ${guild.id}`, "log");
};
28 changes: 19 additions & 9 deletions DscEvents/messageCreate.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,25 @@ module.exports = async (client, message) => {
// Oh boi it is.
if (value === message.channel.id) {

// console.debug("Discord Channel ID: " + message.channel.id + " maps to SL Group UUID: " + key + " " + typeof key + typeof message.channel.id);
const groupID = new nmv.UUID(key);
// Start a group chat session - equivalent to opening a group chat but not sending a message
await container.SLbot.clientCommands.comms.startGroupChatSession(groupID, '');
let guild = client.guilds.fetch(message.guild.id);
const member = await message.guild.members.fetch(message.author);
let nickname = member ? member.displayName : null;
// Send a group message
await container.SLbot.clientCommands.comms.sendGroupMessage(groupID, 'From Discord: ' + nickname + ': ' + message.content);
// Check if SL bot is connected and ready before trying to use it
if (!container.SLbot || !container.SLbot.clientCommands) {
logger.log('SL bot not ready yet, skipping message relay', 'warn');
return;
}

try {
// console.debug("Discord Channel ID: " + message.channel.id + " maps to SL Group UUID: " + key + " " + typeof key + typeof message.channel.id);
const groupID = new nmv.UUID(key);
// Start a group chat session - equivalent to opening a group chat but not sending a message
await container.SLbot.clientCommands.comms.startGroupChatSession(groupID, '');
let guild = client.guilds.fetch(message.guild.id);
const member = await message.guild.members.fetch(message.author);
let nickname = member ? member.displayName : null;
// Send a group message
await container.SLbot.clientCommands.comms.sendGroupMessage(groupID, 'From Discord: ' + nickname + ': ' + message.content);
} catch (error) {
logger.log(`Failed to relay message to SL: ${error.message}`, 'error');
}
}

})
Expand Down
14 changes: 14 additions & 0 deletions DscSlash/stats.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ module.exports = {

async execute(interaction) {
const duration = durationFormatter.format(interaction.client.uptime);

// Get region restart handler status
const regionRestartHandler = interaction.client.container.regionRestartHandler;
let currentLocation = 'Unknown';
let evacuationStatus = 'Unknown';

if (regionRestartHandler) {
const status = regionRestartHandler.getStatus();
currentLocation = status.currentRegion || 'Unknown';
evacuationStatus = status.isEvacuating ? '🚨 Evacuating Region Restarts' : '✅ Normal';
}

const statsEmbed = new EmbedBuilder()
.setColor('#0099ff')
.setTitle('STATISTICS')
Expand All @@ -25,6 +37,8 @@ module.exports = {
{ name: 'Discord.js', value: `v${version}`, inline: true },
{ name: 'Node.js', value: process.version, inline: true },
{ name: 'Bot Version', value: `v${dversion}`, inline: true },
{ name: 'Current Region', value: currentLocation, inline: true },
{ name: 'Restarts Evacuation Status', value: evacuationStatus, inline: true },
)
.setTimestamp();

Expand Down
6 changes: 6 additions & 0 deletions DscSlash/teleport.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ module.exports = {
const position = new Vector3(loc.x, loc.y, loc.z);
const lookAt = position;
await interaction.client.container.SLbot.clientCommands.teleport.teleportTo(loc.region, position, lookAt);

const regionRestartHandler = interaction.client.container.regionRestartHandler;
if (regionRestartHandler) {
regionRestartHandler.currentRegion = loc.region;
}

await interaction.followUp(`Teleport successful to ${loc.region} (${loc.x}, ${loc.y}, ${loc.z})`);
} catch (err) {
await interaction.followUp(`Teleport failed: ${err.message}`);
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ A Discord and Second Life bot capable of relaying messages between inworld Secon

## Roadmap

- [ ] Add Region Restart Evasion with Fallback Regions in the Config File
- [x] Add Region Restart Evasion with Fallback Regions in the Config File
* On Region Restart notice, teleport to a fallback region, as well as on login, if can't login to the main region, teleport to a fallback region, don't want your bots sticking around somewhere you don't want it
- [ ] Add Reload Capability of SL and Discord Commands/Event files.

Expand Down
19 changes: 17 additions & 2 deletions config.js.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,26 @@ const config = {

// Bot Support, level 8 by default. Array of user ID strings
'support': [],
// To be implemented, fallback regions if the .env main region can't be reached.
// Fallback regions configuration for region restart scenarios
// Format: ['region-name', 'uri:RegionName&x&y&z']
fallbackRegions: new Map([
['regioname', 'uri'],
['sandbox', 'uri:Sandbox%20Island&128&128&25'],
['help', 'uri:Help%20Island%20Public&128&128&25'],
['welcome', 'uri:Welcome%20Center&128&128&25']
]),

// Region restart evasion settings
regionRestartSettings: {
// Enable automatic teleportation on region restart alerts
enabled: true,
// Time to wait between teleport attempts (seconds or minutes)
teleportRetryDelay: "30s",
// Time to wait before trying to return to startup location (seconds or minutes)
returnToStartupDelay: "5m",
// Maximum number of teleport attempts per location
maxTeleportAttempts: 3
},

// //// This maps inworld group UUIDs to Discord Channels.
relays: new Map([
["e38d0caa-2b71-031a-3e1b-4abe1a22e8f3", "1411469523856396359"] // Red Robot Main Group (Client: dark.nebula)
Expand Down
24 changes: 22 additions & 2 deletions index.js
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made some changes and sent a pr to your fork. This will update the index.js file to changes we pushed the other day on the main repo.

Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ const { readdirSync } = require('fs');
const { intents, partials, permLevels } = require('./config.js');
const config = require('./config.js');
const logger = require('./modules/Logger.js');
const RegionRestartHandler = require('./modules/RegionRestartHandler.js');
// This is your client. Some people call it `bot`, some people call it `self`,
// some might call it `cootchie`. Either way, when you see `client.something`,
// or `bot.something`, this is what we're referring to. Your client.
const packageinfo = require("../package.json");
const packageinfo = require("./package.json");
const client = new Client({ intents, partials });
const slashcmds = new Collection();

Expand Down Expand Up @@ -45,6 +46,10 @@ client.container = {
nmv,
};

// Initialize Region Restart Handler
const regionRestartHandler = new RegionRestartHandler(client);
client.container.regionRestartHandler = regionRestartHandler;

const GroupChatEventHandler = require('./SLevents/GroupChat.js');
const ChatEventHandler = require('./SLevents/ChatEvent.js');

Expand All @@ -61,6 +66,22 @@ const init = async () => {
return client.container.SLbot.connectToSim();
}).then(() => {
logger.log('SL: Connected to Region', 'log');

// Set startup location for region restart handler
const startLocation = process.env.SL_START;
if (startLocation && startLocation !== 'last') {
// Parse URI format: uri:RegionName&x&y&z
const uriMatch = startLocation.match(/uri:([^&]+)&(\d+)&(\d+)&(\d+)/);
if (uriMatch) {
const region = decodeURIComponent(uriMatch[1]);
const x = parseInt(uriMatch[2], 10);
const y = parseInt(uriMatch[3], 10);
const z = parseInt(uriMatch[4], 10);
regionRestartHandler.setStartupLocation(region, x, y, z);
}
} else {
logger.log('SL: Startup location set to default, will update region restart handler when region info is available', 'log');
}


}).then(async () => {
Expand Down Expand Up @@ -154,7 +175,6 @@ const init = async () => {
console.error('Discord.js Error:', err);
});


// Here we login the client.
client.login();

Expand Down
Loading