novaapp-sdk 1.0.6 → 1.0.9

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.d.mts CHANGED
@@ -128,12 +128,53 @@ interface Interaction {
128
128
  commandName?: string | null;
129
129
  customId?: string | null;
130
130
  data?: unknown;
131
+ /** For SELECT_MENU interactions — the chosen option values. */
132
+ values?: string[];
133
+ /** For MODAL_SUBMIT interactions — field customId → submitted value. */
134
+ modalData?: Record<string, string>;
131
135
  userId: string;
132
136
  channelId: string;
133
137
  serverId?: string | null;
134
138
  triggerMsgId?: string | null;
135
139
  createdAt: string;
136
140
  }
141
+ /** A single text input field inside a bot modal. */
142
+ interface BotModalField {
143
+ /** Unique field ID — returned as key in `modalData` on MODAL_SUBMIT. */
144
+ customId: string;
145
+ /** Label displayed above the input. */
146
+ label: string;
147
+ /** `'short'` = single-line input, `'paragraph'` = multi-line textarea. */
148
+ type: 'short' | 'paragraph';
149
+ placeholder?: string;
150
+ required?: boolean;
151
+ minLength?: number;
152
+ maxLength?: number;
153
+ /** Optional pre-filled default value. */
154
+ value?: string;
155
+ }
156
+ /**
157
+ * Pass this as `modal` inside `client.interactions.respond()` to open a
158
+ * modal dialog on the user's client instead of sending a message.
159
+ *
160
+ * @example
161
+ * await client.interactions.respond(interaction.id, {
162
+ * modal: {
163
+ * title: 'Submit a report',
164
+ * customId: 'report_modal',
165
+ * fields: [
166
+ * { customId: 'reason', label: 'Reason', type: 'paragraph', required: true },
167
+ * ],
168
+ * },
169
+ * })
170
+ */
171
+ interface BotModalDefinition {
172
+ /** Title shown at the top of the modal dialog. */
173
+ title: string;
174
+ /** Custom ID passed back with the MODAL_SUBMIT interaction. */
175
+ customId: string;
176
+ fields: BotModalField[];
177
+ }
137
178
  interface SlashCommandOption {
138
179
  name: string;
139
180
  description: string;
@@ -210,6 +251,11 @@ interface RespondInteractionOptions {
210
251
  embed?: Embed;
211
252
  components?: MessageComponent[];
212
253
  ephemeral?: boolean;
254
+ /**
255
+ * Open a modal dialog on the user's client instead of sending a message.
256
+ * When set, all other fields are ignored.
257
+ */
258
+ modal?: BotModalDefinition;
213
259
  }
214
260
  interface FetchMessagesOptions {
215
261
  limit?: number;
@@ -222,6 +268,39 @@ interface PollInteractionsOptions {
222
268
  limit?: number;
223
269
  since?: string;
224
270
  }
271
+ /** A raw permission record stored for a specific scope (server, role, or channel). */
272
+ interface BotPermissionRecord {
273
+ id: string;
274
+ botId: string;
275
+ serverId: string;
276
+ /** `null` means this record is server-wide (not channel-scoped). */
277
+ channelId: string | null;
278
+ /** `null` means this record applies to all roles. */
279
+ roleId: string | null;
280
+ /** Key-value map of permission name → granted flag. */
281
+ permissions: Record<string, boolean>;
282
+ createdAt: string;
283
+ updatedAt: string;
284
+ }
285
+ /** Response from `client.permissions.get()`. */
286
+ interface PermissionsResult {
287
+ /**
288
+ * Merged, effective permissions for the requested scope.
289
+ * Merging order (highest wins): channel > role > server-wide.
290
+ */
291
+ permissions: Record<string, boolean>;
292
+ /** All raw permission records matching the query scope. */
293
+ records: BotPermissionRecord[];
294
+ }
295
+ /** Options accepted by `client.permissions.get()`. */
296
+ interface PermissionsQueryOptions {
297
+ /** The server to query permissions for (required). */
298
+ serverId: string;
299
+ /** Narrow the scope to a specific channel. */
300
+ channelId?: string;
301
+ /** Narrow the scope to a specific role. */
302
+ roleId?: string;
303
+ }
225
304
  interface NovaClientOptions {
226
305
  /** Your bot token — starts with "nova_bot_" */
227
306
  token: string;
@@ -461,6 +540,43 @@ declare class InteractionsAPI {
461
540
  poll(options?: PollInteractionsOptions): Promise<Interaction[]>;
462
541
  }
463
542
 
543
+ declare class PermissionsAPI {
544
+ private readonly http;
545
+ constructor(http: HttpClient);
546
+ /**
547
+ * Fetch the bot's effective permissions for a given scope.
548
+ *
549
+ * Permissions are merged in priority order:
550
+ * 1. Server-wide base permissions
551
+ * 2. Role override (if `roleId` provided)
552
+ * 3. Channel override (if `channelId` provided, highest priority)
553
+ *
554
+ * `serverId` is required. `channelId` and `roleId` are optional filters.
555
+ *
556
+ * @example
557
+ * // Effective server-wide permissions
558
+ * const { permissions } = await client.permissions.get({
559
+ * serverId: 'server-id',
560
+ * })
561
+ * console.log(permissions) // { kick_members: true, ban_members: false, ... }
562
+ *
563
+ * @example
564
+ * // Scoped to a specific channel
565
+ * const { permissions } = await client.permissions.get({
566
+ * serverId: 'server-id',
567
+ * channelId: 'channel-id',
568
+ * })
569
+ *
570
+ * @example
571
+ * // Scoped to a specific role
572
+ * const { permissions } = await client.permissions.get({
573
+ * serverId: 'server-id',
574
+ * roleId: 'role-id',
575
+ * })
576
+ */
577
+ get(options: PermissionsQueryOptions): Promise<PermissionsResult>;
578
+ }
579
+
464
580
  /**
465
581
  * The main Nova bot client.
466
582
  *
@@ -494,6 +610,8 @@ declare class NovaClient extends EventEmitter {
494
610
  readonly servers: ServersAPI;
495
611
  /** Acknowledge and respond to interactions. */
496
612
  readonly interactions: InteractionsAPI;
613
+ /** Query the bot's effective permissions within a server, channel, or role scope. */
614
+ readonly permissions: PermissionsAPI;
497
615
  private socket;
498
616
  private readonly http;
499
617
  private readonly options;
@@ -536,4 +654,4 @@ declare class NovaClient extends EventEmitter {
536
654
  emit(event: string | symbol, ...args: unknown[]): boolean;
537
655
  }
538
656
 
539
- export { type Attachment, type BotApplication, type BotEvent, type BotEventType, type BotUser, CommandsAPI, type ContextCommandDefinition, type EditMessageOptions, type Embed, type EmbedField, type FetchMembersOptions, type FetchMessagesOptions, HttpClient, type Interaction, type InteractionType, InteractionsAPI, type Member, MembersAPI, type Message, type MessageComponent, MessagesAPI, NovaClient, type NovaClientEvents, type NovaClientOptions, type NovaServer, type PollInteractionsOptions, type PrefixCommandDefinition, type Reaction, type RegisteredContextCommand, type RegisteredPrefixCommand, type RegisteredSlashCommand, type RespondInteractionOptions, type SendMessageOptions, ServersAPI, type SlashCommandDefinition, type SlashCommandOption };
657
+ export { type Attachment, type BotApplication, type BotEvent, type BotEventType, type BotModalDefinition, type BotModalField, type BotPermissionRecord, type BotUser, CommandsAPI, type ContextCommandDefinition, type EditMessageOptions, type Embed, type EmbedField, type FetchMembersOptions, type FetchMessagesOptions, HttpClient, type Interaction, type InteractionType, InteractionsAPI, type Member, MembersAPI, type Message, type MessageComponent, MessagesAPI, NovaClient, type NovaClientEvents, type NovaClientOptions, type NovaServer, PermissionsAPI, type PermissionsQueryOptions, type PermissionsResult, type PollInteractionsOptions, type PrefixCommandDefinition, type Reaction, type RegisteredContextCommand, type RegisteredPrefixCommand, type RegisteredSlashCommand, type RespondInteractionOptions, type SendMessageOptions, ServersAPI, type SlashCommandDefinition, type SlashCommandOption };
package/dist/index.d.ts CHANGED
@@ -128,12 +128,53 @@ interface Interaction {
128
128
  commandName?: string | null;
129
129
  customId?: string | null;
130
130
  data?: unknown;
131
+ /** For SELECT_MENU interactions — the chosen option values. */
132
+ values?: string[];
133
+ /** For MODAL_SUBMIT interactions — field customId → submitted value. */
134
+ modalData?: Record<string, string>;
131
135
  userId: string;
132
136
  channelId: string;
133
137
  serverId?: string | null;
134
138
  triggerMsgId?: string | null;
135
139
  createdAt: string;
136
140
  }
141
+ /** A single text input field inside a bot modal. */
142
+ interface BotModalField {
143
+ /** Unique field ID — returned as key in `modalData` on MODAL_SUBMIT. */
144
+ customId: string;
145
+ /** Label displayed above the input. */
146
+ label: string;
147
+ /** `'short'` = single-line input, `'paragraph'` = multi-line textarea. */
148
+ type: 'short' | 'paragraph';
149
+ placeholder?: string;
150
+ required?: boolean;
151
+ minLength?: number;
152
+ maxLength?: number;
153
+ /** Optional pre-filled default value. */
154
+ value?: string;
155
+ }
156
+ /**
157
+ * Pass this as `modal` inside `client.interactions.respond()` to open a
158
+ * modal dialog on the user's client instead of sending a message.
159
+ *
160
+ * @example
161
+ * await client.interactions.respond(interaction.id, {
162
+ * modal: {
163
+ * title: 'Submit a report',
164
+ * customId: 'report_modal',
165
+ * fields: [
166
+ * { customId: 'reason', label: 'Reason', type: 'paragraph', required: true },
167
+ * ],
168
+ * },
169
+ * })
170
+ */
171
+ interface BotModalDefinition {
172
+ /** Title shown at the top of the modal dialog. */
173
+ title: string;
174
+ /** Custom ID passed back with the MODAL_SUBMIT interaction. */
175
+ customId: string;
176
+ fields: BotModalField[];
177
+ }
137
178
  interface SlashCommandOption {
138
179
  name: string;
139
180
  description: string;
@@ -210,6 +251,11 @@ interface RespondInteractionOptions {
210
251
  embed?: Embed;
211
252
  components?: MessageComponent[];
212
253
  ephemeral?: boolean;
254
+ /**
255
+ * Open a modal dialog on the user's client instead of sending a message.
256
+ * When set, all other fields are ignored.
257
+ */
258
+ modal?: BotModalDefinition;
213
259
  }
214
260
  interface FetchMessagesOptions {
215
261
  limit?: number;
@@ -222,6 +268,39 @@ interface PollInteractionsOptions {
222
268
  limit?: number;
223
269
  since?: string;
224
270
  }
271
+ /** A raw permission record stored for a specific scope (server, role, or channel). */
272
+ interface BotPermissionRecord {
273
+ id: string;
274
+ botId: string;
275
+ serverId: string;
276
+ /** `null` means this record is server-wide (not channel-scoped). */
277
+ channelId: string | null;
278
+ /** `null` means this record applies to all roles. */
279
+ roleId: string | null;
280
+ /** Key-value map of permission name → granted flag. */
281
+ permissions: Record<string, boolean>;
282
+ createdAt: string;
283
+ updatedAt: string;
284
+ }
285
+ /** Response from `client.permissions.get()`. */
286
+ interface PermissionsResult {
287
+ /**
288
+ * Merged, effective permissions for the requested scope.
289
+ * Merging order (highest wins): channel > role > server-wide.
290
+ */
291
+ permissions: Record<string, boolean>;
292
+ /** All raw permission records matching the query scope. */
293
+ records: BotPermissionRecord[];
294
+ }
295
+ /** Options accepted by `client.permissions.get()`. */
296
+ interface PermissionsQueryOptions {
297
+ /** The server to query permissions for (required). */
298
+ serverId: string;
299
+ /** Narrow the scope to a specific channel. */
300
+ channelId?: string;
301
+ /** Narrow the scope to a specific role. */
302
+ roleId?: string;
303
+ }
225
304
  interface NovaClientOptions {
226
305
  /** Your bot token — starts with "nova_bot_" */
227
306
  token: string;
@@ -461,6 +540,43 @@ declare class InteractionsAPI {
461
540
  poll(options?: PollInteractionsOptions): Promise<Interaction[]>;
462
541
  }
463
542
 
543
+ declare class PermissionsAPI {
544
+ private readonly http;
545
+ constructor(http: HttpClient);
546
+ /**
547
+ * Fetch the bot's effective permissions for a given scope.
548
+ *
549
+ * Permissions are merged in priority order:
550
+ * 1. Server-wide base permissions
551
+ * 2. Role override (if `roleId` provided)
552
+ * 3. Channel override (if `channelId` provided, highest priority)
553
+ *
554
+ * `serverId` is required. `channelId` and `roleId` are optional filters.
555
+ *
556
+ * @example
557
+ * // Effective server-wide permissions
558
+ * const { permissions } = await client.permissions.get({
559
+ * serverId: 'server-id',
560
+ * })
561
+ * console.log(permissions) // { kick_members: true, ban_members: false, ... }
562
+ *
563
+ * @example
564
+ * // Scoped to a specific channel
565
+ * const { permissions } = await client.permissions.get({
566
+ * serverId: 'server-id',
567
+ * channelId: 'channel-id',
568
+ * })
569
+ *
570
+ * @example
571
+ * // Scoped to a specific role
572
+ * const { permissions } = await client.permissions.get({
573
+ * serverId: 'server-id',
574
+ * roleId: 'role-id',
575
+ * })
576
+ */
577
+ get(options: PermissionsQueryOptions): Promise<PermissionsResult>;
578
+ }
579
+
464
580
  /**
465
581
  * The main Nova bot client.
466
582
  *
@@ -494,6 +610,8 @@ declare class NovaClient extends EventEmitter {
494
610
  readonly servers: ServersAPI;
495
611
  /** Acknowledge and respond to interactions. */
496
612
  readonly interactions: InteractionsAPI;
613
+ /** Query the bot's effective permissions within a server, channel, or role scope. */
614
+ readonly permissions: PermissionsAPI;
497
615
  private socket;
498
616
  private readonly http;
499
617
  private readonly options;
@@ -536,4 +654,4 @@ declare class NovaClient extends EventEmitter {
536
654
  emit(event: string | symbol, ...args: unknown[]): boolean;
537
655
  }
538
656
 
539
- export { type Attachment, type BotApplication, type BotEvent, type BotEventType, type BotUser, CommandsAPI, type ContextCommandDefinition, type EditMessageOptions, type Embed, type EmbedField, type FetchMembersOptions, type FetchMessagesOptions, HttpClient, type Interaction, type InteractionType, InteractionsAPI, type Member, MembersAPI, type Message, type MessageComponent, MessagesAPI, NovaClient, type NovaClientEvents, type NovaClientOptions, type NovaServer, type PollInteractionsOptions, type PrefixCommandDefinition, type Reaction, type RegisteredContextCommand, type RegisteredPrefixCommand, type RegisteredSlashCommand, type RespondInteractionOptions, type SendMessageOptions, ServersAPI, type SlashCommandDefinition, type SlashCommandOption };
657
+ export { type Attachment, type BotApplication, type BotEvent, type BotEventType, type BotModalDefinition, type BotModalField, type BotPermissionRecord, type BotUser, CommandsAPI, type ContextCommandDefinition, type EditMessageOptions, type Embed, type EmbedField, type FetchMembersOptions, type FetchMessagesOptions, HttpClient, type Interaction, type InteractionType, InteractionsAPI, type Member, MembersAPI, type Message, type MessageComponent, MessagesAPI, NovaClient, type NovaClientEvents, type NovaClientOptions, type NovaServer, PermissionsAPI, type PermissionsQueryOptions, type PermissionsResult, type PollInteractionsOptions, type PrefixCommandDefinition, type Reaction, type RegisteredContextCommand, type RegisteredPrefixCommand, type RegisteredSlashCommand, type RespondInteractionOptions, type SendMessageOptions, ServersAPI, type SlashCommandDefinition, type SlashCommandOption };
package/dist/index.js CHANGED
@@ -26,6 +26,7 @@ __export(index_exports, {
26
26
  MembersAPI: () => MembersAPI,
27
27
  MessagesAPI: () => MessagesAPI,
28
28
  NovaClient: () => NovaClient,
29
+ PermissionsAPI: () => PermissionsAPI,
29
30
  ServersAPI: () => ServersAPI
30
31
  });
31
32
  module.exports = __toCommonJS(index_exports);
@@ -324,6 +325,50 @@ var InteractionsAPI = class {
324
325
  }
325
326
  };
326
327
 
328
+ // src/api/permissions.ts
329
+ var PermissionsAPI = class {
330
+ constructor(http) {
331
+ this.http = http;
332
+ }
333
+ /**
334
+ * Fetch the bot's effective permissions for a given scope.
335
+ *
336
+ * Permissions are merged in priority order:
337
+ * 1. Server-wide base permissions
338
+ * 2. Role override (if `roleId` provided)
339
+ * 3. Channel override (if `channelId` provided, highest priority)
340
+ *
341
+ * `serverId` is required. `channelId` and `roleId` are optional filters.
342
+ *
343
+ * @example
344
+ * // Effective server-wide permissions
345
+ * const { permissions } = await client.permissions.get({
346
+ * serverId: 'server-id',
347
+ * })
348
+ * console.log(permissions) // { kick_members: true, ban_members: false, ... }
349
+ *
350
+ * @example
351
+ * // Scoped to a specific channel
352
+ * const { permissions } = await client.permissions.get({
353
+ * serverId: 'server-id',
354
+ * channelId: 'channel-id',
355
+ * })
356
+ *
357
+ * @example
358
+ * // Scoped to a specific role
359
+ * const { permissions } = await client.permissions.get({
360
+ * serverId: 'server-id',
361
+ * roleId: 'role-id',
362
+ * })
363
+ */
364
+ get(options) {
365
+ const params = new URLSearchParams({ serverId: options.serverId });
366
+ if (options.channelId) params.set("channelId", options.channelId);
367
+ if (options.roleId) params.set("roleId", options.roleId);
368
+ return this.http.get(`/permissions?${params.toString()}`);
369
+ }
370
+ };
371
+
327
372
  // src/client.ts
328
373
  var EVENT_MAP = {
329
374
  "message.created": "messageCreate",
@@ -357,10 +402,11 @@ var NovaClient = class extends import_node_events.EventEmitter {
357
402
  this.members = new MembersAPI(this.http);
358
403
  this.servers = new ServersAPI(this.http);
359
404
  this.interactions = new InteractionsAPI(this.http);
405
+ this.permissions = new PermissionsAPI(this.http);
360
406
  this.on("error", () => {
361
407
  });
362
408
  const cleanup = () => this.disconnect();
363
- process.once("exit", cleanup);
409
+ process.once("beforeExit", cleanup);
364
410
  process.once("SIGINT", () => {
365
411
  cleanup();
366
412
  process.exit(0);
@@ -430,12 +476,28 @@ var NovaClient = class extends import_node_events.EventEmitter {
430
476
  */
431
477
  disconnect() {
432
478
  if (this.socket) {
479
+ const sock = this.socket;
480
+ this.socket = null;
433
481
  try {
434
- this.socket.io.reconnection(false);
482
+ sock.io.reconnection(false);
483
+ } catch {
484
+ }
485
+ try {
486
+ sock.io?.engine?.socket?.terminate?.();
487
+ } catch {
488
+ }
489
+ try {
490
+ sock.io?.engine?.socket?.destroy?.();
491
+ } catch {
492
+ }
493
+ try {
494
+ sock.io?.engine?.close?.();
495
+ } catch {
496
+ }
497
+ try {
498
+ sock.disconnect();
435
499
  } catch {
436
500
  }
437
- this.socket.disconnect();
438
- this.socket = null;
439
501
  }
440
502
  }
441
503
  /**
@@ -484,5 +546,6 @@ var NovaClient = class extends import_node_events.EventEmitter {
484
546
  MembersAPI,
485
547
  MessagesAPI,
486
548
  NovaClient,
549
+ PermissionsAPI,
487
550
  ServersAPI
488
551
  });
package/dist/index.mjs CHANGED
@@ -292,6 +292,50 @@ var InteractionsAPI = class {
292
292
  }
293
293
  };
294
294
 
295
+ // src/api/permissions.ts
296
+ var PermissionsAPI = class {
297
+ constructor(http) {
298
+ this.http = http;
299
+ }
300
+ /**
301
+ * Fetch the bot's effective permissions for a given scope.
302
+ *
303
+ * Permissions are merged in priority order:
304
+ * 1. Server-wide base permissions
305
+ * 2. Role override (if `roleId` provided)
306
+ * 3. Channel override (if `channelId` provided, highest priority)
307
+ *
308
+ * `serverId` is required. `channelId` and `roleId` are optional filters.
309
+ *
310
+ * @example
311
+ * // Effective server-wide permissions
312
+ * const { permissions } = await client.permissions.get({
313
+ * serverId: 'server-id',
314
+ * })
315
+ * console.log(permissions) // { kick_members: true, ban_members: false, ... }
316
+ *
317
+ * @example
318
+ * // Scoped to a specific channel
319
+ * const { permissions } = await client.permissions.get({
320
+ * serverId: 'server-id',
321
+ * channelId: 'channel-id',
322
+ * })
323
+ *
324
+ * @example
325
+ * // Scoped to a specific role
326
+ * const { permissions } = await client.permissions.get({
327
+ * serverId: 'server-id',
328
+ * roleId: 'role-id',
329
+ * })
330
+ */
331
+ get(options) {
332
+ const params = new URLSearchParams({ serverId: options.serverId });
333
+ if (options.channelId) params.set("channelId", options.channelId);
334
+ if (options.roleId) params.set("roleId", options.roleId);
335
+ return this.http.get(`/permissions?${params.toString()}`);
336
+ }
337
+ };
338
+
295
339
  // src/client.ts
296
340
  var EVENT_MAP = {
297
341
  "message.created": "messageCreate",
@@ -325,10 +369,11 @@ var NovaClient = class extends EventEmitter {
325
369
  this.members = new MembersAPI(this.http);
326
370
  this.servers = new ServersAPI(this.http);
327
371
  this.interactions = new InteractionsAPI(this.http);
372
+ this.permissions = new PermissionsAPI(this.http);
328
373
  this.on("error", () => {
329
374
  });
330
375
  const cleanup = () => this.disconnect();
331
- process.once("exit", cleanup);
376
+ process.once("beforeExit", cleanup);
332
377
  process.once("SIGINT", () => {
333
378
  cleanup();
334
379
  process.exit(0);
@@ -398,12 +443,28 @@ var NovaClient = class extends EventEmitter {
398
443
  */
399
444
  disconnect() {
400
445
  if (this.socket) {
446
+ const sock = this.socket;
447
+ this.socket = null;
401
448
  try {
402
- this.socket.io.reconnection(false);
449
+ sock.io.reconnection(false);
450
+ } catch {
451
+ }
452
+ try {
453
+ sock.io?.engine?.socket?.terminate?.();
454
+ } catch {
455
+ }
456
+ try {
457
+ sock.io?.engine?.socket?.destroy?.();
458
+ } catch {
459
+ }
460
+ try {
461
+ sock.io?.engine?.close?.();
462
+ } catch {
463
+ }
464
+ try {
465
+ sock.disconnect();
403
466
  } catch {
404
467
  }
405
- this.socket.disconnect();
406
- this.socket = null;
407
468
  }
408
469
  }
409
470
  /**
@@ -451,5 +512,6 @@ export {
451
512
  MembersAPI,
452
513
  MessagesAPI,
453
514
  NovaClient,
515
+ PermissionsAPI,
454
516
  ServersAPI
455
517
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "novaapp-sdk",
3
- "version": "1.0.6",
3
+ "version": "1.0.9",
4
4
  "description": "Official SDK for building bots on the Nova platform",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",