@elizaos/plugin-roblox 2.0.0-alpha.6 → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +28 -0
- package/README.md +186 -0
- package/dist/actions/executeGameAction.d.ts.map +1 -1
- package/dist/actions/executeRobloxAction.d.ts +4 -0
- package/dist/actions/executeRobloxAction.d.ts.map +1 -0
- package/dist/actions/getPlayerInfo.d.ts.map +1 -1
- package/dist/actions/index.d.ts +2 -4
- package/dist/actions/index.d.ts.map +1 -1
- package/dist/actions/robloxAction.d.ts +4 -0
- package/dist/actions/robloxAction.d.ts.map +1 -0
- package/dist/actions/sendGameMessage.d.ts.map +1 -1
- package/dist/actions/sendRobloxMessage.d.ts +4 -0
- package/dist/actions/sendRobloxMessage.d.ts.map +1 -0
- package/dist/generated/specs/specs.d.ts +1 -18
- package/dist/generated/specs/specs.d.ts.map +1 -1
- package/dist/index.js +443 -518
- package/dist/index.js.map +9 -11
- package/dist/providers/gameStateProvider.d.ts.map +1 -1
- package/dist/providers/robloxPlayerProvider.d.ts +3 -0
- package/dist/providers/robloxPlayerProvider.d.ts.map +1 -0
- package/dist/services/RobloxService.d.ts +0 -4
- package/dist/services/RobloxService.d.ts.map +1 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/config.d.ts +0 -1
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/vitest.config.d.ts +3 -0
- package/dist/vitest.config.d.ts.map +1 -0
- package/package.json +10 -17
package/dist/index.js
CHANGED
|
@@ -51,59 +51,15 @@ class RobloxTestSuite {
|
|
|
51
51
|
];
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
// actions/
|
|
54
|
+
// actions/robloxAction.ts
|
|
55
55
|
import {
|
|
56
56
|
logger
|
|
57
57
|
} from "@elizaos/core";
|
|
58
|
-
var actionName = "
|
|
59
|
-
var
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
content: {
|
|
64
|
-
text: "Start a fireworks show in the game"
|
|
65
|
-
}
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
name: actionName,
|
|
69
|
-
content: {
|
|
70
|
-
text: "I'll trigger the fireworks show for everyone in the game!",
|
|
71
|
-
action: "EXECUTE_ROBLOX_ACTION"
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
],
|
|
75
|
-
[
|
|
76
|
-
{
|
|
77
|
-
name: "{{user1}}",
|
|
78
|
-
content: {
|
|
79
|
-
text: "Give player456 100 coins as a reward"
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
name: "{{agentName}}",
|
|
84
|
-
content: {
|
|
85
|
-
text: "I'll give player456 100 coins right away!",
|
|
86
|
-
action: "EXECUTE_ROBLOX_ACTION"
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
],
|
|
90
|
-
[
|
|
91
|
-
{
|
|
92
|
-
name: "{{user1}}",
|
|
93
|
-
content: {
|
|
94
|
-
text: "Teleport everyone to the lobby"
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
name: "{{agentName}}",
|
|
99
|
-
content: {
|
|
100
|
-
text: "Teleporting all players to the lobby now!",
|
|
101
|
-
action: "EXECUTE_ROBLOX_ACTION"
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
]
|
|
105
|
-
];
|
|
106
|
-
var KNOWN_ACTIONS = [
|
|
58
|
+
var actionName = "ROBLOX";
|
|
59
|
+
var ROBLOX_ACTION_TIMEOUT_MS = 15000;
|
|
60
|
+
var MAX_ROBLOX_TARGET_IDS = 25;
|
|
61
|
+
var MAX_ROBLOX_MESSAGE_LENGTH = 1000;
|
|
62
|
+
var KNOWN_GAME_ACTIONS = [
|
|
107
63
|
{
|
|
108
64
|
name: "move_npc",
|
|
109
65
|
patterns: [
|
|
@@ -125,16 +81,14 @@ var KNOWN_ACTIONS = [
|
|
|
125
81
|
name: "give_coins",
|
|
126
82
|
patterns: [/give\s+(?:player\s*)?(\d+)\s+(\d+)\s+coins?/i],
|
|
127
83
|
extractParams: (match) => ({
|
|
128
|
-
playerId: parseInt(match[1], 10),
|
|
129
|
-
amount: parseInt(match[2], 10)
|
|
84
|
+
playerId: Number.parseInt(match[1], 10),
|
|
85
|
+
amount: Number.parseInt(match[2], 10)
|
|
130
86
|
})
|
|
131
87
|
},
|
|
132
88
|
{
|
|
133
89
|
name: "teleport",
|
|
134
90
|
patterns: [/teleport\s+(?:everyone|all)\s+to\s+(?:the\s+)?(\w+)/i],
|
|
135
|
-
extractParams: (match) => ({
|
|
136
|
-
destination: match[1]
|
|
137
|
-
})
|
|
91
|
+
extractParams: (match) => ({ destination: match[1] })
|
|
138
92
|
},
|
|
139
93
|
{
|
|
140
94
|
name: "spawn_entity",
|
|
@@ -147,478 +101,447 @@ var KNOWN_ACTIONS = [
|
|
|
147
101
|
{
|
|
148
102
|
name: "start_event",
|
|
149
103
|
patterns: [/start\s+(?:a\s+)?(\w+)\s+(?:show|event|celebration)/i],
|
|
150
|
-
extractParams: (match) => ({
|
|
151
|
-
eventType: match[1]
|
|
152
|
-
})
|
|
104
|
+
extractParams: (match) => ({ eventType: match[1] })
|
|
153
105
|
}
|
|
154
106
|
];
|
|
155
|
-
function
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
107
|
+
function isRecord(value) {
|
|
108
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
109
|
+
}
|
|
110
|
+
function parseJsonObject(text) {
|
|
111
|
+
const trimmed = text.trim();
|
|
112
|
+
if (!trimmed.startsWith("{") || !trimmed.endsWith("}"))
|
|
113
|
+
return {};
|
|
114
|
+
try {
|
|
115
|
+
const parsed = JSON.parse(trimmed);
|
|
116
|
+
return isRecord(parsed) ? parsed : {};
|
|
117
|
+
} catch {
|
|
118
|
+
return {};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function readParams(options) {
|
|
122
|
+
const maybeParams = isRecord(options) && isRecord(options.parameters) ? options.parameters : {};
|
|
123
|
+
return maybeParams;
|
|
124
|
+
}
|
|
125
|
+
function mergedInput(message, options) {
|
|
126
|
+
return {
|
|
127
|
+
...parseJsonObject(message.content.text ?? ""),
|
|
128
|
+
...readParams(options)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function readString(params, ...keys) {
|
|
132
|
+
for (const key of keys) {
|
|
133
|
+
const value = params[key];
|
|
134
|
+
if (typeof value === "string" && value.trim())
|
|
135
|
+
return value.trim();
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
function readNumber(params, ...keys) {
|
|
140
|
+
for (const key of keys) {
|
|
141
|
+
const value = params[key];
|
|
142
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
143
|
+
return value;
|
|
144
|
+
if (typeof value === "string" && value.trim()) {
|
|
145
|
+
const parsed = Number(value);
|
|
146
|
+
if (Number.isFinite(parsed))
|
|
147
|
+
return parsed;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
function normalizeSubaction(value) {
|
|
153
|
+
const normalized = value?.trim().toLowerCase().replace(/[_\s-]+/g, "_");
|
|
154
|
+
if (!normalized)
|
|
155
|
+
return null;
|
|
156
|
+
if (["message", "send_message", "send", "chat", "announce"].includes(normalized)) {
|
|
157
|
+
return "message";
|
|
158
|
+
}
|
|
159
|
+
if (["execute", "action", "game_action", "run", "trigger"].includes(normalized)) {
|
|
160
|
+
return "execute";
|
|
161
|
+
}
|
|
162
|
+
if (["get_player", "player", "player_info", "user", "lookup"].includes(normalized)) {
|
|
163
|
+
return "get_player";
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
function inferSubaction(text, params) {
|
|
168
|
+
const explicit = normalizeSubaction(readString(params, "subaction", "action", "type"));
|
|
169
|
+
if (explicit)
|
|
170
|
+
return explicit;
|
|
171
|
+
const lower = text.toLowerCase();
|
|
172
|
+
if (/\b(who is|look up|lookup|find|get).*\b(player|user|roblox)\b/.test(lower)) {
|
|
173
|
+
return "get_player";
|
|
174
|
+
}
|
|
175
|
+
if (/\b(execute|run|trigger|start|give|teleport|spawn|move|walk)\b/.test(lower)) {
|
|
176
|
+
return "execute";
|
|
177
|
+
}
|
|
178
|
+
if (/\b(send|message|tell|announce|chat|say)\b/.test(lower)) {
|
|
179
|
+
return "message";
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
function readTargetPlayerIds(params, text) {
|
|
184
|
+
const explicit = params.targetPlayerIds;
|
|
185
|
+
if (Array.isArray(explicit)) {
|
|
186
|
+
const ids = explicit.map((value) => typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN).filter((value) => Number.isInteger(value) && value > 0);
|
|
187
|
+
if (ids.length)
|
|
188
|
+
return ids.slice(0, MAX_ROBLOX_TARGET_IDS);
|
|
189
|
+
}
|
|
190
|
+
const single = readNumber(params, "targetPlayerId", "playerId", "userId");
|
|
191
|
+
if (single !== null && Number.isInteger(single) && single > 0)
|
|
192
|
+
return [single];
|
|
193
|
+
const matches = [...text.matchAll(/\bplayer\s*(\d+)\b/gi)];
|
|
194
|
+
return matches.length ? matches.map((match) => Number.parseInt(match[1], 10)).slice(0, MAX_ROBLOX_TARGET_IDS) : undefined;
|
|
195
|
+
}
|
|
196
|
+
function withRobloxTimeout(promise, label) {
|
|
197
|
+
return Promise.race([
|
|
198
|
+
promise,
|
|
199
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(`${label} timed out`)), ROBLOX_ACTION_TIMEOUT_MS))
|
|
200
|
+
]);
|
|
201
|
+
}
|
|
202
|
+
function parseGameAction(text, params) {
|
|
203
|
+
const explicitActionName = readString(params, "actionName", "gameAction", "command");
|
|
204
|
+
const explicitParameters = params.parameters;
|
|
205
|
+
if (explicitActionName) {
|
|
206
|
+
return {
|
|
207
|
+
name: explicitActionName,
|
|
208
|
+
parameters: sanitizeParameters(explicitParameters),
|
|
209
|
+
targetPlayerIds: readTargetPlayerIds(params, text)
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
for (const gameAction of KNOWN_GAME_ACTIONS) {
|
|
213
|
+
for (const pattern of gameAction.patterns) {
|
|
214
|
+
const match = text.match(pattern);
|
|
159
215
|
if (match) {
|
|
160
216
|
return {
|
|
161
|
-
|
|
162
|
-
parameters:
|
|
217
|
+
name: gameAction.name,
|
|
218
|
+
parameters: gameAction.extractParams(match),
|
|
219
|
+
targetPlayerIds: readTargetPlayerIds(params, text)
|
|
163
220
|
};
|
|
164
221
|
}
|
|
165
222
|
}
|
|
166
223
|
}
|
|
167
|
-
const genericMatch =
|
|
224
|
+
const genericMatch = text.match(/(?:execute|run|do|trigger)\s+(\w+)/i);
|
|
168
225
|
if (genericMatch) {
|
|
169
226
|
return {
|
|
170
|
-
|
|
171
|
-
parameters: {}
|
|
227
|
+
name: genericMatch[1].toLowerCase(),
|
|
228
|
+
parameters: {},
|
|
229
|
+
targetPlayerIds: readTargetPlayerIds(params, text)
|
|
172
230
|
};
|
|
173
231
|
}
|
|
174
232
|
return null;
|
|
175
233
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
|
|
186
|
-
const __avRegex = /\b(?:execute|roblox)\b/i;
|
|
187
|
-
const __avRegexOk = __avRegex.test(__avText);
|
|
188
|
-
const __avSource = String(message?.content?.source ?? message?.source ?? "");
|
|
189
|
-
const __avExpectedSource = "";
|
|
190
|
-
const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
|
|
191
|
-
const __avOptions = options && typeof options === "object" ? options : {};
|
|
192
|
-
const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
|
|
193
|
-
if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
|
|
194
|
-
return false;
|
|
195
|
-
}
|
|
196
|
-
const __avLegacyValidate = async (runtime2) => {
|
|
197
|
-
const apiKey = runtime2.getSetting("ROBLOX_API_KEY");
|
|
198
|
-
const universeId = runtime2.getSetting("ROBLOX_UNIVERSE_ID");
|
|
199
|
-
return Boolean(apiKey && universeId);
|
|
200
|
-
};
|
|
201
|
-
try {
|
|
202
|
-
return Boolean(await __avLegacyValidate(runtime, message, state, options));
|
|
203
|
-
} catch {
|
|
204
|
-
return false;
|
|
205
|
-
}
|
|
206
|
-
},
|
|
207
|
-
handler: async (runtime, message, state, _options, callback) => {
|
|
208
|
-
try {
|
|
209
|
-
const service = runtime.getService(ROBLOX_SERVICE_NAME);
|
|
210
|
-
if (!service) {
|
|
211
|
-
logger.error("Roblox service not found");
|
|
212
|
-
if (callback) {
|
|
213
|
-
callback({
|
|
214
|
-
text: "Roblox service not available.",
|
|
215
|
-
action: "EXECUTE_ROBLOX_ACTION"
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
return {
|
|
219
|
-
success: false,
|
|
220
|
-
error: "Roblox service not found"
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
const messageContent = state?.message || message.content.text || "";
|
|
224
|
-
const parsedAction = parseGameAction(messageContent);
|
|
225
|
-
if (!parsedAction) {
|
|
226
|
-
logger.warn("Could not parse game action from message");
|
|
227
|
-
if (callback) {
|
|
228
|
-
callback({
|
|
229
|
-
text: "Could not parse action from message.",
|
|
230
|
-
action: "EXECUTE_ROBLOX_ACTION"
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
return {
|
|
234
|
-
success: false,
|
|
235
|
-
error: "Could not parse game action from message"
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
const { actionName: actionName2, parameters } = parsedAction;
|
|
239
|
-
const playerIdMatch = messageContent.match(/player\s*(\d+)/i);
|
|
240
|
-
const targetPlayerIds = playerIdMatch ? [parseInt(playerIdMatch[1], 10)] : undefined;
|
|
241
|
-
await service.executeAction(runtime.agentId, actionName2, parameters, targetPlayerIds);
|
|
242
|
-
logger.info({ actionName: actionName2, parameters, targetPlayerIds }, "Executed Roblox game action");
|
|
243
|
-
if (callback) {
|
|
244
|
-
callback({
|
|
245
|
-
text: `I've triggered the "${actionName2}" action in the game.`,
|
|
246
|
-
action: "EXECUTE_ROBLOX_ACTION"
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
return {
|
|
250
|
-
success: true,
|
|
251
|
-
text: `Executed "${actionName2}" action in the game`,
|
|
252
|
-
data: {
|
|
253
|
-
actionName: actionName2,
|
|
254
|
-
parameters,
|
|
255
|
-
targetPlayerIds: targetPlayerIds || undefined
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
} catch (error) {
|
|
259
|
-
logger.error({ error }, "Failed to execute Roblox action");
|
|
260
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
261
|
-
if (callback) {
|
|
262
|
-
callback({
|
|
263
|
-
text: "Error executing action.",
|
|
264
|
-
action: "EXECUTE_ROBLOX_ACTION"
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
return {
|
|
268
|
-
success: false,
|
|
269
|
-
error: errorMessage
|
|
270
|
-
};
|
|
234
|
+
function sanitizeParameters(value) {
|
|
235
|
+
if (!isRecord(value))
|
|
236
|
+
return {};
|
|
237
|
+
const out = {};
|
|
238
|
+
for (const [key, item] of Object.entries(value)) {
|
|
239
|
+
if (item === null) {
|
|
240
|
+
out[key] = null;
|
|
241
|
+
} else if (typeof item === "string" || typeof item === "number" || typeof item === "boolean") {
|
|
242
|
+
out[key] = item;
|
|
271
243
|
}
|
|
272
244
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
content: {
|
|
286
|
-
text: "Who is player 12345678?"
|
|
287
|
-
}
|
|
288
|
-
},
|
|
289
|
-
{
|
|
290
|
-
name: actionName2,
|
|
291
|
-
content: {
|
|
292
|
-
text: "Let me look up that player's information for you.",
|
|
293
|
-
action: "GET_ROBLOX_PLAYER"
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
],
|
|
297
|
-
[
|
|
298
|
-
{
|
|
299
|
-
name: "{{user1}}",
|
|
300
|
-
content: {
|
|
301
|
-
text: "Look up the Roblox user JohnDoe123"
|
|
302
|
-
}
|
|
303
|
-
},
|
|
304
|
-
{
|
|
305
|
-
name: "{{agentName}}",
|
|
306
|
-
content: {
|
|
307
|
-
text: "I'll find the information for JohnDoe123.",
|
|
308
|
-
action: "GET_ROBLOX_PLAYER"
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
]
|
|
312
|
-
];
|
|
313
|
-
function extractUserIdentifier(message) {
|
|
314
|
-
const idMatch = message.match(/\b(?:player|user|id)\s*[:#]?\s*(\d{5,})\b/i);
|
|
245
|
+
return out;
|
|
246
|
+
}
|
|
247
|
+
function extractUserIdentifier(text, params) {
|
|
248
|
+
const userId = readNumber(params, "playerId", "userId", "id");
|
|
249
|
+
if (userId !== null && Number.isInteger(userId) && userId > 0) {
|
|
250
|
+
return { type: "id", value: userId };
|
|
251
|
+
}
|
|
252
|
+
const username = readString(params, "username", "playerName", "user");
|
|
253
|
+
if (username && !/^\d+$/.test(username)) {
|
|
254
|
+
return { type: "username", value: username };
|
|
255
|
+
}
|
|
256
|
+
const idMatch = text.match(/\b(?:player|user|id)\s*[:#]?\s*(\d{5,})\b/i);
|
|
315
257
|
if (idMatch) {
|
|
316
|
-
return { type: "id", value: parseInt(idMatch[1], 10) };
|
|
258
|
+
return { type: "id", value: Number.parseInt(idMatch[1], 10) };
|
|
317
259
|
}
|
|
318
|
-
const usernameMatch =
|
|
319
|
-
if (usernameMatch) {
|
|
320
|
-
|
|
321
|
-
if (!/^\d+$/.test(username)) {
|
|
322
|
-
return { type: "username", value: username };
|
|
323
|
-
}
|
|
260
|
+
const usernameMatch = text.match(/\b(?:user(?:name)?|player)\s*[:#]?\s*([A-Za-z0-9_]{3,20})\b/i);
|
|
261
|
+
if (usernameMatch && !/^\d+$/.test(usernameMatch[1])) {
|
|
262
|
+
return { type: "username", value: usernameMatch[1] };
|
|
324
263
|
}
|
|
325
264
|
return null;
|
|
326
265
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
266
|
+
async function handleMessage(runtime, service, message, state, params, callback) {
|
|
267
|
+
const content = readString(params, "message", "text", "content") ?? (typeof state?.message === "string" ? state.message : undefined) ?? (message.content.text ?? "").trim();
|
|
268
|
+
if (!content) {
|
|
269
|
+
await callback?.({ text: "I need a message to send to the Roblox game.", action: actionName });
|
|
270
|
+
return { success: false, error: "No message content to send" };
|
|
271
|
+
}
|
|
272
|
+
const targetPlayerIds = readTargetPlayerIds(params, content);
|
|
273
|
+
const cappedContent = content.slice(0, MAX_ROBLOX_MESSAGE_LENGTH);
|
|
274
|
+
await withRobloxTimeout(service.sendMessage(runtime.agentId, cappedContent, targetPlayerIds), "roblox message");
|
|
275
|
+
const targetText = targetPlayerIds && targetPlayerIds.length > 0 ? `to ${targetPlayerIds.length} player(s)` : "to all players";
|
|
276
|
+
await callback?.({ text: `Sent Roblox message ${targetText}.`, action: actionName });
|
|
277
|
+
return {
|
|
278
|
+
success: true,
|
|
279
|
+
text: `Sent Roblox message ${targetText}`,
|
|
280
|
+
data: { subaction: "message", targetPlayerIds, messageLength: cappedContent.length }
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
async function handleExecute(runtime, service, message, params, callback) {
|
|
284
|
+
const parsedAction = parseGameAction(message.content.text ?? "", params);
|
|
285
|
+
if (!parsedAction) {
|
|
286
|
+
await callback?.({ text: "Could not parse Roblox game action.", action: actionName });
|
|
287
|
+
return { success: false, error: "Could not parse Roblox game action" };
|
|
288
|
+
}
|
|
289
|
+
await withRobloxTimeout(service.executeAction(runtime.agentId, parsedAction.name, parsedAction.parameters, parsedAction.targetPlayerIds), "roblox execute");
|
|
290
|
+
await callback?.({ text: `Triggered Roblox action "${parsedAction.name}".`, action: actionName });
|
|
291
|
+
return {
|
|
292
|
+
success: true,
|
|
293
|
+
text: `Executed Roblox action "${parsedAction.name}"`,
|
|
294
|
+
data: {
|
|
295
|
+
subaction: "execute",
|
|
296
|
+
actionName: parsedAction.name,
|
|
297
|
+
parameters: parsedAction.parameters,
|
|
298
|
+
targetPlayerIds: parsedAction.targetPlayerIds
|
|
346
299
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
async function handleGetPlayer(runtime, service, message, params, callback) {
|
|
303
|
+
const client = service.getClient(runtime.agentId);
|
|
304
|
+
if (!client) {
|
|
305
|
+
await callback?.({ text: "The Roblox client is not available.", action: actionName });
|
|
306
|
+
return { success: false, error: "Roblox client not found for agent" };
|
|
307
|
+
}
|
|
308
|
+
const identifier = extractUserIdentifier(message.content.text ?? "", params);
|
|
309
|
+
if (!identifier) {
|
|
310
|
+
await callback?.({
|
|
311
|
+
text: "I need a Roblox player ID or username to look up.",
|
|
312
|
+
action: actionName
|
|
313
|
+
});
|
|
314
|
+
return { success: false, error: "Could not extract Roblox player identifier" };
|
|
315
|
+
}
|
|
316
|
+
let user;
|
|
317
|
+
if (identifier.type === "id") {
|
|
318
|
+
user = await withRobloxTimeout(client.getUserById(identifier.value), "roblox get user");
|
|
319
|
+
} else {
|
|
320
|
+
user = await withRobloxTimeout(client.getUserByUsername(identifier.value), "roblox get user");
|
|
321
|
+
}
|
|
322
|
+
if (!user) {
|
|
323
|
+
await callback?.({
|
|
324
|
+
text: `No Roblox user found for ${identifier.type}: ${identifier.value}.`,
|
|
325
|
+
action: actionName
|
|
326
|
+
});
|
|
327
|
+
return {
|
|
328
|
+
success: true,
|
|
329
|
+
text: `Roblox user not found for ${identifier.type}: ${identifier.value}`,
|
|
330
|
+
data: { subaction: "get_player", found: false, identifier }
|
|
350
331
|
};
|
|
351
|
-
try {
|
|
352
|
-
return Boolean(await __avLegacyValidate(runtime, message, state, options));
|
|
353
|
-
} catch {
|
|
354
|
-
return false;
|
|
355
|
-
}
|
|
356
|
-
},
|
|
357
|
-
handler: async (runtime, message, state, _options, callback) => {
|
|
358
|
-
try {
|
|
359
|
-
const service = runtime.getService(ROBLOX_SERVICE_NAME);
|
|
360
|
-
if (!service) {
|
|
361
|
-
logger2.error("Roblox service not found");
|
|
362
|
-
if (callback) {
|
|
363
|
-
callback({
|
|
364
|
-
text: "I couldn't connect to the Roblox service.",
|
|
365
|
-
action: "GET_ROBLOX_PLAYER"
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
return {
|
|
369
|
-
success: false,
|
|
370
|
-
error: "Roblox service not found"
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
const client = service.getClient(runtime.agentId);
|
|
374
|
-
if (!client) {
|
|
375
|
-
logger2.error("Roblox client not found for agent");
|
|
376
|
-
if (callback) {
|
|
377
|
-
callback({
|
|
378
|
-
text: "The Roblox client isn't available right now.",
|
|
379
|
-
action: "GET_ROBLOX_PLAYER"
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
return {
|
|
383
|
-
success: false,
|
|
384
|
-
error: "Roblox client not found for agent"
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
const messageContent = state?.message || message.content.text || "";
|
|
388
|
-
const identifier = extractUserIdentifier(messageContent);
|
|
389
|
-
if (!identifier) {
|
|
390
|
-
logger2.warn("Could not extract user identifier from message");
|
|
391
|
-
if (callback) {
|
|
392
|
-
callback({
|
|
393
|
-
text: "I need a player ID or username to look up. Please provide one.",
|
|
394
|
-
action: "GET_ROBLOX_PLAYER"
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
|
-
return {
|
|
398
|
-
success: false,
|
|
399
|
-
error: "Could not extract user identifier from message"
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
let user;
|
|
403
|
-
if (identifier.type === "id") {
|
|
404
|
-
user = await client.getUserById(identifier.value);
|
|
405
|
-
} else {
|
|
406
|
-
user = await client.getUserByUsername(identifier.value);
|
|
407
|
-
}
|
|
408
|
-
if (!user) {
|
|
409
|
-
if (callback) {
|
|
410
|
-
callback({
|
|
411
|
-
text: `I couldn't find a Roblox user with that ${identifier.type === "id" ? "ID" : "username"}.`,
|
|
412
|
-
action: "GET_ROBLOX_PLAYER"
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
return {
|
|
416
|
-
success: true,
|
|
417
|
-
text: `User not found with ${identifier.type === "id" ? "ID" : "username"}: ${identifier.value}`
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
const avatarUrl = await client.getAvatarUrl(user.id);
|
|
421
|
-
user.avatarUrl = avatarUrl;
|
|
422
|
-
logger2.info({ userId: user.id, username: user.username }, "Found Roblox user");
|
|
423
|
-
if (callback) {
|
|
424
|
-
const createdDate = user.createdAt ? user.createdAt.toLocaleDateString() : "Unknown";
|
|
425
|
-
const bannedStatus = user.isBanned ? " (Banned)" : "";
|
|
426
|
-
callback({
|
|
427
|
-
text: `**${user.displayName}** (@${user.username})${bannedStatus}
|
|
428
|
-
- User ID: ${user.id}
|
|
429
|
-
- Account created: ${createdDate}`,
|
|
430
|
-
action: "GET_ROBLOX_PLAYER"
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
return {
|
|
434
|
-
success: true,
|
|
435
|
-
text: `Found Roblox user: ${user.displayName} (@${user.username})`,
|
|
436
|
-
data: {
|
|
437
|
-
userId: user.id,
|
|
438
|
-
username: user.username,
|
|
439
|
-
displayName: user.displayName,
|
|
440
|
-
avatarUrl: avatarUrl || undefined,
|
|
441
|
-
isBanned: user.isBanned,
|
|
442
|
-
createdAt: user.createdAt ? user.createdAt.toISOString() : undefined
|
|
443
|
-
}
|
|
444
|
-
};
|
|
445
|
-
} catch (error) {
|
|
446
|
-
logger2.error({ error }, "Failed to get Roblox player info");
|
|
447
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
448
|
-
if (callback) {
|
|
449
|
-
callback({
|
|
450
|
-
text: "I encountered an error looking up that player. Please try again.",
|
|
451
|
-
action: "GET_ROBLOX_PLAYER"
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
return {
|
|
455
|
-
success: false,
|
|
456
|
-
error: errorMessage
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
332
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
333
|
+
const avatarUrl = await withRobloxTimeout(client.getAvatarUrl(user.id), "roblox avatar");
|
|
334
|
+
user.avatarUrl = avatarUrl;
|
|
335
|
+
await callback?.({
|
|
336
|
+
text: `${user.displayName} (@${user.username}) - Roblox user ID ${user.id}`,
|
|
337
|
+
action: actionName
|
|
338
|
+
});
|
|
339
|
+
return {
|
|
340
|
+
success: true,
|
|
341
|
+
text: `Found Roblox user: ${user.displayName} (@${user.username})`,
|
|
342
|
+
data: {
|
|
343
|
+
subaction: "get_player",
|
|
344
|
+
found: true,
|
|
345
|
+
userId: user.id,
|
|
346
|
+
username: user.username,
|
|
347
|
+
displayName: user.displayName,
|
|
348
|
+
avatarUrl: avatarUrl || undefined,
|
|
349
|
+
isBanned: user.isBanned,
|
|
350
|
+
createdAt: user.createdAt ? user.createdAt.toISOString() : undefined
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
var robloxAction = {
|
|
355
|
+
name: actionName,
|
|
356
|
+
contexts: ["media", "automation"],
|
|
357
|
+
contextGate: { anyOf: ["media", "automation"] },
|
|
358
|
+
roleGate: { minRole: "USER" },
|
|
359
|
+
similes: ["ROBLOX", "ROBLOX_ROUTER", "ROBLOX_GAME_ACTION"],
|
|
360
|
+
description: "Route Roblox game integration with action message, execute, or get_player.",
|
|
361
|
+
descriptionCompressed: "Route Roblox action: message, execute, or get_player.",
|
|
362
|
+
parameters: [
|
|
470
363
|
{
|
|
471
|
-
name:
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
364
|
+
name: "action",
|
|
365
|
+
description: "Roblox operation: message, execute, or get_player.",
|
|
366
|
+
descriptionCompressed: "Roblox action.",
|
|
367
|
+
required: true,
|
|
368
|
+
schema: { type: "string", enum: ["message", "execute", "get_player"] }
|
|
475
369
|
},
|
|
476
370
|
{
|
|
477
|
-
name:
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
],
|
|
484
|
-
[
|
|
371
|
+
name: "message",
|
|
372
|
+
description: "Message content for the message subaction.",
|
|
373
|
+
descriptionCompressed: "message text",
|
|
374
|
+
required: false,
|
|
375
|
+
schema: { type: "string" }
|
|
376
|
+
},
|
|
485
377
|
{
|
|
486
|
-
name: "
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
378
|
+
name: "actionName",
|
|
379
|
+
description: "Game-side action name for execute.",
|
|
380
|
+
descriptionCompressed: "game action name",
|
|
381
|
+
required: false,
|
|
382
|
+
schema: { type: "string" }
|
|
490
383
|
},
|
|
491
384
|
{
|
|
492
|
-
name: "
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
}
|
|
385
|
+
name: "parameters",
|
|
386
|
+
description: "Game-side action parameters for execute.",
|
|
387
|
+
descriptionCompressed: "game action params",
|
|
388
|
+
required: false,
|
|
389
|
+
schema: { type: "object" }
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
name: "targetPlayerIds",
|
|
393
|
+
description: "Roblox player IDs to target for message or execute.",
|
|
394
|
+
descriptionCompressed: "target player ids",
|
|
395
|
+
required: false,
|
|
396
|
+
schema: { type: "array", items: { type: "number" } }
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
name: "playerId",
|
|
400
|
+
description: "Roblox player/user ID for lookup or targeting.",
|
|
401
|
+
descriptionCompressed: "Roblox user id",
|
|
402
|
+
required: false,
|
|
403
|
+
schema: { type: "number" }
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
name: "username",
|
|
407
|
+
description: "Roblox username for lookup.",
|
|
408
|
+
descriptionCompressed: "Roblox username",
|
|
409
|
+
required: false,
|
|
410
|
+
schema: { type: "string" }
|
|
497
411
|
}
|
|
498
|
-
]
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
description: "Send a message to players in the Roblox game.",
|
|
504
|
-
examples: sendGameMessageExamples,
|
|
505
|
-
validate: async (runtime, message, state, options) => {
|
|
506
|
-
const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
|
|
507
|
-
const __avText = __avTextRaw.toLowerCase();
|
|
508
|
-
const __avKeywords = ["send", "roblox", "message"];
|
|
509
|
-
const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
|
|
510
|
-
const __avRegex = /\b(?:send|roblox|message)\b/i;
|
|
511
|
-
const __avRegexOk = __avRegex.test(__avText);
|
|
512
|
-
const __avSource = String(message?.content?.source ?? message?.source ?? "");
|
|
513
|
-
const __avExpectedSource = "";
|
|
514
|
-
const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
|
|
515
|
-
const __avOptions = options && typeof options === "object" ? options : {};
|
|
516
|
-
const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
|
|
517
|
-
if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
|
|
412
|
+
],
|
|
413
|
+
validate: async (runtime, message, _state) => {
|
|
414
|
+
const params = parseJsonObject(message.content.text ?? "");
|
|
415
|
+
const hasIntent = Boolean(normalizeSubaction(readString(params, "subaction", "action", "type"))) || /\b(roblox|player|user|send|message|announce|execute|trigger|teleport|spawn|coins|lookup|look up)\b/i.test(message.content.text ?? "");
|
|
416
|
+
if (!hasIntent)
|
|
518
417
|
return false;
|
|
418
|
+
const apiKey = runtime.getSetting("ROBLOX_API_KEY");
|
|
419
|
+
const universeId = runtime.getSetting("ROBLOX_UNIVERSE_ID");
|
|
420
|
+
return Boolean(apiKey && universeId);
|
|
421
|
+
},
|
|
422
|
+
handler: async (runtime, message, state, options, callback) => {
|
|
423
|
+
const service = runtime.getService(ROBLOX_SERVICE_NAME);
|
|
424
|
+
if (!service) {
|
|
425
|
+
logger.error("Roblox service not found");
|
|
426
|
+
await callback?.({ text: "Roblox service not available.", action: actionName });
|
|
427
|
+
return { success: false, error: "Roblox service not found" };
|
|
519
428
|
}
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
};
|
|
525
|
-
try {
|
|
526
|
-
return Boolean(await __avLegacyValidate(runtime, message, state, options));
|
|
527
|
-
} catch {
|
|
528
|
-
return false;
|
|
429
|
+
const params = mergedInput(message, options);
|
|
430
|
+
const maxRobloxTargetIds = MAX_ROBLOX_TARGET_IDS;
|
|
431
|
+
if (Array.isArray(params.targetPlayerIds)) {
|
|
432
|
+
params.targetPlayerIds = params.targetPlayerIds.slice(0, maxRobloxTargetIds);
|
|
529
433
|
}
|
|
530
|
-
|
|
531
|
-
handler: async (runtime, message, state, _options, callback) => {
|
|
434
|
+
const subaction = inferSubaction(message.content.text ?? "", params);
|
|
532
435
|
try {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
logger3.error("Roblox service not found");
|
|
536
|
-
if (callback) {
|
|
537
|
-
callback({
|
|
538
|
-
text: "Roblox service not available.",
|
|
539
|
-
action: "SEND_ROBLOX_MESSAGE"
|
|
540
|
-
});
|
|
541
|
-
}
|
|
542
|
-
return {
|
|
543
|
-
success: false,
|
|
544
|
-
error: "Roblox service not found"
|
|
545
|
-
};
|
|
436
|
+
if (subaction === "message") {
|
|
437
|
+
return await handleMessage(runtime, service, message, state, params, callback);
|
|
546
438
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
logger3.warn("No message content to send");
|
|
550
|
-
if (callback) {
|
|
551
|
-
callback({
|
|
552
|
-
text: "I need a message to send to the game.",
|
|
553
|
-
action: "SEND_ROBLOX_MESSAGE"
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
return {
|
|
557
|
-
success: false,
|
|
558
|
-
error: "No message content to send"
|
|
559
|
-
};
|
|
439
|
+
if (subaction === "execute") {
|
|
440
|
+
return await handleExecute(runtime, service, message, params, callback);
|
|
560
441
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
logger3.info({ targetPlayerIds, messageLength: messageContent.length }, "Sent message to Roblox game");
|
|
564
|
-
if (callback) {
|
|
565
|
-
const targetInfo = targetPlayerIds && targetPlayerIds.length > 0 ? `to ${targetPlayerIds.length} specific player(s)` : "to all players";
|
|
566
|
-
callback({
|
|
567
|
-
text: `I've sent the message ${targetInfo} in the game.`,
|
|
568
|
-
action: "SEND_ROBLOX_MESSAGE"
|
|
569
|
-
});
|
|
442
|
+
if (subaction === "get_player") {
|
|
443
|
+
return await handleGetPlayer(runtime, service, message, params, callback);
|
|
570
444
|
}
|
|
571
445
|
return {
|
|
572
|
-
success:
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
targetPlayerIds,
|
|
576
|
-
messageLength: messageContent.length
|
|
577
|
-
}
|
|
446
|
+
success: false,
|
|
447
|
+
error: "Missing Roblox subaction",
|
|
448
|
+
text: "Missing Roblox subaction"
|
|
578
449
|
};
|
|
579
450
|
} catch (error) {
|
|
580
|
-
|
|
451
|
+
logger.error({ error }, "Roblox action failed");
|
|
581
452
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
text: "Error sending message.",
|
|
585
|
-
action: "SEND_ROBLOX_MESSAGE"
|
|
586
|
-
});
|
|
587
|
-
}
|
|
588
|
-
return {
|
|
589
|
-
success: false,
|
|
590
|
-
error: errorMessage
|
|
591
|
-
};
|
|
453
|
+
await callback?.({ text: `Roblox action failed: ${errorMessage}`, action: actionName });
|
|
454
|
+
return { success: false, error: errorMessage };
|
|
592
455
|
}
|
|
593
|
-
}
|
|
456
|
+
},
|
|
457
|
+
examples: [
|
|
458
|
+
[
|
|
459
|
+
{
|
|
460
|
+
name: "{{user1}}",
|
|
461
|
+
content: { text: "Tell everyone in Roblox that the event starts now" }
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
name: "{{agentName}}",
|
|
465
|
+
content: {
|
|
466
|
+
text: "I'll send that message to the Roblox game.",
|
|
467
|
+
action: actionName
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
]
|
|
471
|
+
]
|
|
594
472
|
};
|
|
595
|
-
|
|
596
|
-
const playerIdPattern = /\bplayer\s*(\d+)\b/gi;
|
|
597
|
-
const matches = [...message.matchAll(playerIdPattern)];
|
|
598
|
-
if (matches.length > 0) {
|
|
599
|
-
return matches.map((m) => parseInt(m[1], 10));
|
|
600
|
-
}
|
|
601
|
-
return;
|
|
602
|
-
}
|
|
603
|
-
var sendGameMessage_default = sendGameMessage;
|
|
473
|
+
var robloxAction_default = robloxAction;
|
|
604
474
|
|
|
605
475
|
// actions/index.ts
|
|
606
|
-
var robloxActions = [
|
|
476
|
+
var robloxActions = [robloxAction_default];
|
|
607
477
|
|
|
608
478
|
// providers/gameStateProvider.ts
|
|
609
479
|
var providerName = "roblox-game-state";
|
|
480
|
+
var EXPERIENCE_NAME_LIMIT = 160;
|
|
610
481
|
var gameStateProvider = {
|
|
611
482
|
name: providerName,
|
|
612
483
|
description: "Provides information about the connected Roblox game/experience",
|
|
484
|
+
descriptionCompressed: "Read Roblox connection state, experience metadata, and messaging topic.",
|
|
485
|
+
contexts: ["automation", "agent_internal"],
|
|
486
|
+
contextGate: { anyOf: ["automation", "agent_internal"] },
|
|
487
|
+
cacheStable: false,
|
|
488
|
+
cacheScope: "turn",
|
|
613
489
|
get: async (runtime, _message, _state) => {
|
|
490
|
+
const apiKeyConfigured = Boolean(runtime.getSetting("ROBLOX_API_KEY"));
|
|
491
|
+
const universeIdSetting = runtime.getSetting("ROBLOX_UNIVERSE_ID");
|
|
492
|
+
const universeId = typeof universeIdSetting === "string" ? universeIdSetting : undefined;
|
|
493
|
+
const configured = apiKeyConfigured && Boolean(universeId);
|
|
614
494
|
try {
|
|
615
495
|
const service = runtime.getService(ROBLOX_SERVICE_NAME);
|
|
616
496
|
if (!service) {
|
|
617
|
-
return {
|
|
497
|
+
return {
|
|
498
|
+
text: [
|
|
499
|
+
"Roblox:",
|
|
500
|
+
`configured: ${configured}`,
|
|
501
|
+
"service: unavailable",
|
|
502
|
+
`apiKey: ${apiKeyConfigured ? "configured" : "missing"}`,
|
|
503
|
+
`universeId: ${universeId ?? "missing"}`
|
|
504
|
+
].join(`
|
|
505
|
+
`),
|
|
506
|
+
data: {
|
|
507
|
+
configured,
|
|
508
|
+
apiKeyConfigured,
|
|
509
|
+
universeId: universeId ?? null,
|
|
510
|
+
serviceAvailable: false,
|
|
511
|
+
clientAvailable: false
|
|
512
|
+
},
|
|
513
|
+
values: {
|
|
514
|
+
configured,
|
|
515
|
+
serviceAvailable: false,
|
|
516
|
+
clientAvailable: false
|
|
517
|
+
}
|
|
518
|
+
};
|
|
618
519
|
}
|
|
619
520
|
const client = service.getClient(runtime.agentId);
|
|
620
521
|
if (!client) {
|
|
621
|
-
return {
|
|
522
|
+
return {
|
|
523
|
+
text: [
|
|
524
|
+
"Roblox:",
|
|
525
|
+
`configured: ${configured}`,
|
|
526
|
+
"service: available",
|
|
527
|
+
"client: unavailable",
|
|
528
|
+
`apiKey: ${apiKeyConfigured ? "configured" : "missing"}`,
|
|
529
|
+
`universeId: ${universeId ?? "missing"}`
|
|
530
|
+
].join(`
|
|
531
|
+
`),
|
|
532
|
+
data: {
|
|
533
|
+
configured,
|
|
534
|
+
apiKeyConfigured,
|
|
535
|
+
universeId: universeId ?? null,
|
|
536
|
+
serviceAvailable: true,
|
|
537
|
+
clientAvailable: false
|
|
538
|
+
},
|
|
539
|
+
values: {
|
|
540
|
+
configured,
|
|
541
|
+
serviceAvailable: true,
|
|
542
|
+
clientAvailable: false
|
|
543
|
+
}
|
|
544
|
+
};
|
|
622
545
|
}
|
|
623
546
|
const config = client.getConfig();
|
|
624
547
|
let experienceInfo = null;
|
|
@@ -628,33 +551,64 @@ var gameStateProvider = {
|
|
|
628
551
|
experienceInfo = null;
|
|
629
552
|
}
|
|
630
553
|
const parts = [
|
|
631
|
-
"
|
|
632
|
-
"",
|
|
633
|
-
|
|
554
|
+
"Roblox:",
|
|
555
|
+
"configured: true",
|
|
556
|
+
"service: available",
|
|
557
|
+
"client: available",
|
|
558
|
+
`universeId: ${config.universeId}`
|
|
634
559
|
];
|
|
635
560
|
if (config.placeId) {
|
|
636
|
-
parts.push(
|
|
561
|
+
parts.push(`placeId: ${config.placeId}`);
|
|
637
562
|
}
|
|
638
563
|
if (experienceInfo) {
|
|
639
|
-
parts.push(
|
|
564
|
+
parts.push(`experienceName: ${experienceInfo.name.slice(0, EXPERIENCE_NAME_LIMIT)}`);
|
|
640
565
|
if (experienceInfo.playing !== undefined) {
|
|
641
|
-
parts.push(
|
|
566
|
+
parts.push(`activePlayers: ${experienceInfo.playing}`);
|
|
642
567
|
}
|
|
643
568
|
if (experienceInfo.visits !== undefined) {
|
|
644
|
-
parts.push(
|
|
569
|
+
parts.push(`totalVisits: ${experienceInfo.visits}`);
|
|
645
570
|
}
|
|
646
|
-
parts.push(
|
|
571
|
+
parts.push(`creator: ${experienceInfo.creator.name} (${experienceInfo.creator.type})`);
|
|
572
|
+
} else {
|
|
573
|
+
parts.push("experience: unavailable");
|
|
647
574
|
}
|
|
648
|
-
parts.push(
|
|
575
|
+
parts.push(`messagingTopic: ${config.messagingTopic}`);
|
|
649
576
|
if (config.dryRun) {
|
|
650
|
-
parts.push("");
|
|
651
|
-
parts.push("*Dry run mode is enabled - actions are simulated*");
|
|
577
|
+
parts.push("dryRun: true");
|
|
652
578
|
}
|
|
653
|
-
return {
|
|
654
|
-
|
|
579
|
+
return {
|
|
580
|
+
text: parts.join(`
|
|
581
|
+
`),
|
|
582
|
+
data: {
|
|
583
|
+
configured: true,
|
|
584
|
+
serviceAvailable: true,
|
|
585
|
+
clientAvailable: true,
|
|
586
|
+
universeId: config.universeId,
|
|
587
|
+
placeId: config.placeId ?? null,
|
|
588
|
+
messagingTopic: config.messagingTopic,
|
|
589
|
+
dryRun: config.dryRun,
|
|
590
|
+
experienceInfo
|
|
591
|
+
},
|
|
592
|
+
values: {
|
|
593
|
+
configured: true,
|
|
594
|
+
serviceAvailable: true,
|
|
595
|
+
clientAvailable: true,
|
|
596
|
+
universeId: config.universeId,
|
|
597
|
+
placeId: config.placeId ?? null,
|
|
598
|
+
messagingTopic: config.messagingTopic,
|
|
599
|
+
dryRun: config.dryRun,
|
|
600
|
+
experienceName: experienceInfo?.name ?? null,
|
|
601
|
+
activePlayers: experienceInfo?.playing ?? null
|
|
602
|
+
}
|
|
603
|
+
};
|
|
655
604
|
} catch (error) {
|
|
656
605
|
runtime.logger.error({ error }, "Error in gameStateProvider");
|
|
657
|
-
|
|
606
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
607
|
+
return {
|
|
608
|
+
text: `Roblox provider error: ${message}`,
|
|
609
|
+
data: { configured, error: message },
|
|
610
|
+
values: { configured, error: true }
|
|
611
|
+
};
|
|
658
612
|
}
|
|
659
613
|
}
|
|
660
614
|
};
|
|
@@ -876,7 +830,6 @@ class RobloxClient {
|
|
|
876
830
|
// utils/config.ts
|
|
877
831
|
var ROBLOX_DEFAULTS = {
|
|
878
832
|
MESSAGING_TOPIC: "eliza-agent",
|
|
879
|
-
POLL_INTERVAL: 30,
|
|
880
833
|
DRY_RUN: false
|
|
881
834
|
};
|
|
882
835
|
function hasRobloxEnabled(runtime) {
|
|
@@ -896,8 +849,6 @@ function validateRobloxConfig(runtime) {
|
|
|
896
849
|
const placeId = runtime.getSetting("ROBLOX_PLACE_ID");
|
|
897
850
|
const webhookSecret = runtime.getSetting("ROBLOX_WEBHOOK_SECRET");
|
|
898
851
|
const messagingTopic = runtime.getSetting("ROBLOX_MESSAGING_TOPIC") || ROBLOX_DEFAULTS.MESSAGING_TOPIC;
|
|
899
|
-
const pollIntervalStr = runtime.getSetting("ROBLOX_POLL_INTERVAL");
|
|
900
|
-
const pollInterval = pollIntervalStr ? parseInt(pollIntervalStr, 10) : ROBLOX_DEFAULTS.POLL_INTERVAL;
|
|
901
852
|
const dryRunStr = runtime.getSetting("ROBLOX_DRY_RUN");
|
|
902
853
|
const dryRun = dryRunStr === "true";
|
|
903
854
|
return {
|
|
@@ -906,7 +857,6 @@ function validateRobloxConfig(runtime) {
|
|
|
906
857
|
placeId,
|
|
907
858
|
webhookSecret,
|
|
908
859
|
messagingTopic,
|
|
909
|
-
pollInterval,
|
|
910
860
|
dryRun
|
|
911
861
|
};
|
|
912
862
|
}
|
|
@@ -916,7 +866,6 @@ class RobloxAgentManager {
|
|
|
916
866
|
runtime;
|
|
917
867
|
client;
|
|
918
868
|
config;
|
|
919
|
-
pollTimer = null;
|
|
920
869
|
isRunning = false;
|
|
921
870
|
constructor(runtime, config) {
|
|
922
871
|
this.runtime = runtime;
|
|
@@ -929,35 +878,11 @@ class RobloxAgentManager {
|
|
|
929
878
|
}
|
|
930
879
|
this.isRunning = true;
|
|
931
880
|
this.runtime.logger.info({ universeId: this.config.universeId }, "Roblox agent manager started");
|
|
932
|
-
if (this.config.pollInterval > 0) {
|
|
933
|
-
this.startPolling();
|
|
934
|
-
}
|
|
935
881
|
}
|
|
936
882
|
async stop() {
|
|
937
883
|
this.isRunning = false;
|
|
938
|
-
this.stopPolling();
|
|
939
884
|
this.runtime.logger.info("Roblox agent manager stopped");
|
|
940
885
|
}
|
|
941
|
-
startPolling() {
|
|
942
|
-
if (this.pollTimer) {
|
|
943
|
-
return;
|
|
944
|
-
}
|
|
945
|
-
const pollIntervalMs = this.config.pollInterval * 1000;
|
|
946
|
-
this.pollTimer = setInterval(() => {
|
|
947
|
-
this.poll().catch((error) => {
|
|
948
|
-
this.runtime.logger.error({ error }, "Error during Roblox polling");
|
|
949
|
-
});
|
|
950
|
-
}, pollIntervalMs);
|
|
951
|
-
this.runtime.logger.debug({ intervalSeconds: this.config.pollInterval }, "Roblox polling started");
|
|
952
|
-
}
|
|
953
|
-
stopPolling() {
|
|
954
|
-
if (this.pollTimer) {
|
|
955
|
-
clearInterval(this.pollTimer);
|
|
956
|
-
this.pollTimer = null;
|
|
957
|
-
this.runtime.logger.debug("Roblox polling stopped");
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
async poll() {}
|
|
961
886
|
async sendMessage(content, targetPlayerIds) {
|
|
962
887
|
await this.client.sendAgentMessage({
|
|
963
888
|
topic: this.config.messagingTopic,
|
|
@@ -973,12 +898,12 @@ class RobloxAgentManager {
|
|
|
973
898
|
}
|
|
974
899
|
});
|
|
975
900
|
}
|
|
976
|
-
async executeAction(
|
|
901
|
+
async executeAction(actionName2, parameters, targetPlayerIds) {
|
|
977
902
|
await this.client.sendAgentMessage({
|
|
978
903
|
topic: this.config.messagingTopic,
|
|
979
904
|
data: {
|
|
980
905
|
type: "agent_action",
|
|
981
|
-
action:
|
|
906
|
+
action: actionName2,
|
|
982
907
|
parameters,
|
|
983
908
|
targetPlayerIds,
|
|
984
909
|
timestamp: Date.now()
|
|
@@ -1059,12 +984,12 @@ class RobloxService extends Service {
|
|
|
1059
984
|
}
|
|
1060
985
|
await manager.sendMessage(content, targetPlayerIds);
|
|
1061
986
|
}
|
|
1062
|
-
async executeAction(agentId,
|
|
987
|
+
async executeAction(agentId, actionName2, parameters, targetPlayerIds) {
|
|
1063
988
|
const manager = this.managers.get(agentId);
|
|
1064
989
|
if (!manager) {
|
|
1065
990
|
throw new Error(`No Roblox manager found for agent ${agentId}`);
|
|
1066
991
|
}
|
|
1067
|
-
await manager.executeAction(
|
|
992
|
+
await manager.executeAction(actionName2, parameters, targetPlayerIds);
|
|
1068
993
|
}
|
|
1069
994
|
async healthCheck() {
|
|
1070
995
|
const managerStatuses = {};
|
|
@@ -1121,13 +1046,13 @@ var robloxPlugin = {
|
|
|
1121
1046
|
runtime.logger.info({ universeId }, "Roblox plugin initialized");
|
|
1122
1047
|
}
|
|
1123
1048
|
};
|
|
1124
|
-
var
|
|
1049
|
+
var plugin_roblox_default = robloxPlugin;
|
|
1125
1050
|
export {
|
|
1126
1051
|
robloxPlugin,
|
|
1127
|
-
|
|
1052
|
+
plugin_roblox_default as default,
|
|
1128
1053
|
RobloxService,
|
|
1129
1054
|
RobloxClient,
|
|
1130
1055
|
RobloxApiError
|
|
1131
1056
|
};
|
|
1132
1057
|
|
|
1133
|
-
//# debugId=
|
|
1058
|
+
//# debugId=A93F5CB897AB162864756E2164756E21
|