@elizaos/plugin-roblox 2.0.0-alpha.7 → 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/dist/index.js CHANGED
@@ -51,59 +51,15 @@ class RobloxTestSuite {
51
51
  ];
52
52
  }
53
53
 
54
- // actions/executeGameAction.ts
54
+ // actions/robloxAction.ts
55
55
  import {
56
56
  logger
57
57
  } from "@elizaos/core";
58
- var actionName = "EXECUTE_ROBLOX_ACTION";
59
- var executeGameActionExamples = [
60
- [
61
- {
62
- name: actionName,
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 parseGameAction(message) {
156
- for (const action of KNOWN_ACTIONS) {
157
- for (const pattern of action.patterns) {
158
- const match = message.match(pattern);
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
- actionName: action.name,
162
- parameters: action.extractParams(match)
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 = message.match(/(?:execute|run|do)\s+(\w+)/i);
224
+ const genericMatch = text.match(/(?:execute|run|do|trigger)\s+(\w+)/i);
168
225
  if (genericMatch) {
169
226
  return {
170
- actionName: genericMatch[1].toLowerCase(),
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
- var executeGameAction = {
177
- name: actionName,
178
- similes: [],
179
- description: "Execute an action in the connected Roblox game.",
180
- examples: executeGameActionExamples,
181
- validate: async (runtime, message, state, options) => {
182
- const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
183
- const __avText = __avTextRaw.toLowerCase();
184
- const __avKeywords = ["execute", "roblox"];
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
- var executeGameAction_default = executeGameAction;
275
-
276
- // actions/getPlayerInfo.ts
277
- import {
278
- logger as logger2
279
- } from "@elizaos/core";
280
- var actionName2 = "GET_ROBLOX_PLAYER";
281
- var getPlayerInfoExamples = [
282
- [
283
- {
284
- name: actionName2,
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 = message.match(/\b(?:user(?:name)?|player)\s*[:#]?\s*([A-Za-z0-9_]{3,20})\b/i);
319
- if (usernameMatch) {
320
- const username = usernameMatch[1];
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
- var getPlayerInfo = {
328
- name: actionName2,
329
- similes: [],
330
- description: "Fetch Roblox player information by username or user id.",
331
- examples: getPlayerInfoExamples,
332
- validate: async (runtime, message, state, options) => {
333
- const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
334
- const __avText = __avTextRaw.toLowerCase();
335
- const __avKeywords = ["get", "roblox", "player"];
336
- const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
337
- const __avRegex = /\b(?:get|roblox|player)\b/i;
338
- const __avRegexOk = __avRegex.test(__avText);
339
- const __avSource = String(message?.content?.source ?? message?.source ?? "");
340
- const __avExpectedSource = "";
341
- const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
342
- const __avOptions = options && typeof options === "object" ? options : {};
343
- const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
344
- if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
345
- return false;
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
- const __avLegacyValidate = async (runtime2) => {
348
- const apiKey = runtime2.getSetting("ROBLOX_API_KEY");
349
- return Boolean(apiKey);
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
- var getPlayerInfo_default = getPlayerInfo;
462
-
463
- // actions/sendGameMessage.ts
464
- import {
465
- logger as logger3
466
- } from "@elizaos/core";
467
- var actionName3 = "SEND_ROBLOX_MESSAGE";
468
- var sendGameMessageExamples = [
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: actionName3,
472
- content: {
473
- text: "Tell everyone in the game that there's a special event happening"
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: actionName3,
478
- content: {
479
- text: "I'll announce the special event to all players in the game!",
480
- action: "SEND_ROBLOX_MESSAGE"
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: "{{user1}}",
487
- content: {
488
- text: "Send a message to player123 welcoming them to the game"
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: "{{agentName}}",
493
- content: {
494
- text: "I'll send a personalized welcome message to player123.",
495
- action: "SEND_ROBLOX_MESSAGE"
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
- var sendGameMessage = {
501
- name: actionName3,
502
- similes: [],
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 __avLegacyValidate = async (runtime2) => {
521
- const apiKey = runtime2.getSetting("ROBLOX_API_KEY");
522
- const universeId = runtime2.getSetting("ROBLOX_UNIVERSE_ID");
523
- return Boolean(apiKey && universeId);
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
- const service = runtime.getService(ROBLOX_SERVICE_NAME);
534
- if (!service) {
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
- const messageContent = state?.message || message.content.text || "";
548
- if (!messageContent) {
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
- const targetPlayerIds = extractPlayerIds(messageContent);
562
- await service.sendMessage(runtime.agentId, messageContent, targetPlayerIds);
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: true,
573
- text: `Sent message ${targetPlayerIds && targetPlayerIds.length > 0 ? `to ${targetPlayerIds.length} specific player(s)` : "to all players"} in the game`,
574
- data: {
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
- logger3.error({ error }, "Failed to send Roblox message");
451
+ logger.error({ error }, "Roblox action failed");
581
452
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
582
- if (callback) {
583
- callback({
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
- function extractPlayerIds(message) {
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 = [sendGameMessage_default, executeGameAction_default, getPlayerInfo_default];
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 { text: "", data: {}, values: {} };
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 { text: "", data: {}, values: {} };
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
- "## Roblox Game Connection",
632
- "",
633
- `- **Universe ID**: ${config.universeId}`
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(`- **Place ID**: ${config.placeId}`);
561
+ parts.push(`placeId: ${config.placeId}`);
637
562
  }
638
563
  if (experienceInfo) {
639
- parts.push(`- **Experience Name**: ${experienceInfo.name}`);
564
+ parts.push(`experienceName: ${experienceInfo.name.slice(0, EXPERIENCE_NAME_LIMIT)}`);
640
565
  if (experienceInfo.playing !== undefined) {
641
- parts.push(`- **Active Players**: ${experienceInfo.playing}`);
566
+ parts.push(`activePlayers: ${experienceInfo.playing}`);
642
567
  }
643
568
  if (experienceInfo.visits !== undefined) {
644
- parts.push(`- **Total Visits**: ${experienceInfo.visits.toLocaleString()}`);
569
+ parts.push(`totalVisits: ${experienceInfo.visits}`);
645
570
  }
646
- parts.push(`- **Creator**: ${experienceInfo.creator.name} (${experienceInfo.creator.type})`);
571
+ parts.push(`creator: ${experienceInfo.creator.name} (${experienceInfo.creator.type})`);
572
+ } else {
573
+ parts.push("experience: unavailable");
647
574
  }
648
- parts.push(`- **Messaging Topic**: ${config.messagingTopic}`);
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 { text: parts.join(`
654
- `), data: {}, values: {} };
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
- return { text: "", data: {}, values: {} };
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(actionName4, parameters, targetPlayerIds) {
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: actionName4,
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, actionName4, parameters, targetPlayerIds) {
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(actionName4, parameters, targetPlayerIds);
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 typescript_default = robloxPlugin;
1049
+ var plugin_roblox_default = robloxPlugin;
1125
1050
  export {
1126
1051
  robloxPlugin,
1127
- typescript_default as default,
1052
+ plugin_roblox_default as default,
1128
1053
  RobloxService,
1129
1054
  RobloxClient,
1130
1055
  RobloxApiError
1131
1056
  };
1132
1057
 
1133
- //# debugId=740C50B5F8DE5EDA64756E2164756E21
1058
+ //# debugId=A93F5CB897AB162864756E2164756E21