novaapp-sdk 1.0.0
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/README.md +329 -0
- package/dist/index.d.mts +535 -0
- package/dist/index.d.ts +535 -0
- package/dist/index.js +463 -0
- package/dist/index.mjs +430 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
# nova-bot-sdk
|
|
2
|
+
|
|
3
|
+
Official SDK for building bots on the [Nova](https://nova.chat) platform.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install nova-bot-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { NovaClient } from 'nova-bot-sdk'
|
|
15
|
+
|
|
16
|
+
const client = new NovaClient({ token: 'nova_bot_your_token_here' })
|
|
17
|
+
|
|
18
|
+
client.on('ready', (bot) => {
|
|
19
|
+
console.log(`Logged in as ${bot.botUser.username}`)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
client.on('interactionCreate', async (interaction) => {
|
|
23
|
+
if (interaction.commandName === 'ping') {
|
|
24
|
+
await client.interactions.respond(interaction.id, { content: 'Pong! 🏓' })
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
// Register commands once at startup
|
|
29
|
+
await client.connect()
|
|
30
|
+
|
|
31
|
+
await client.commands.setSlash([
|
|
32
|
+
{ name: 'ping', description: 'Check if the bot is alive' },
|
|
33
|
+
])
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Configuration
|
|
37
|
+
|
|
38
|
+
| Option | Type | Default | Description |
|
|
39
|
+
|---|---|---|---|
|
|
40
|
+
| `token` | `string` | **required** | Your bot token (`nova_bot_...`) |
|
|
41
|
+
| `baseUrl` | `string` | `https://api.nova.chat` | Override for self-hosted deployments |
|
|
42
|
+
|
|
43
|
+
## API Reference
|
|
44
|
+
|
|
45
|
+
### `client.messages`
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
// Send a message
|
|
49
|
+
await client.messages.send(channelId, { content: 'Hello!' })
|
|
50
|
+
|
|
51
|
+
// Send with embed
|
|
52
|
+
await client.messages.send(channelId, {
|
|
53
|
+
embed: {
|
|
54
|
+
title: 'Report',
|
|
55
|
+
description: 'All systems operational.',
|
|
56
|
+
color: 0x5865f2,
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// Send with action buttons
|
|
61
|
+
await client.messages.send(channelId, {
|
|
62
|
+
content: 'Confirm action?',
|
|
63
|
+
components: [
|
|
64
|
+
{ type: 'button', customId: 'confirm', label: 'Confirm', style: 'success' },
|
|
65
|
+
{ type: 'button', customId: 'cancel', label: 'Cancel', style: 'danger' },
|
|
66
|
+
],
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
// Edit a message
|
|
70
|
+
await client.messages.edit(messageId, { content: 'Updated!' })
|
|
71
|
+
|
|
72
|
+
// Delete a message
|
|
73
|
+
await client.messages.delete(messageId)
|
|
74
|
+
|
|
75
|
+
// Fetch recent messages
|
|
76
|
+
const messages = await client.messages.fetch(channelId, { limit: 50 })
|
|
77
|
+
|
|
78
|
+
// Show typing indicator
|
|
79
|
+
await client.messages.typing(channelId)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### `client.commands`
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
// Slash commands
|
|
86
|
+
await client.commands.setSlash([
|
|
87
|
+
{ name: 'ban', description: 'Ban a user', options: [
|
|
88
|
+
{ name: 'user', description: 'User to ban', type: 'USER', required: true },
|
|
89
|
+
{ name: 'reason', description: 'Reason', type: 'STRING' },
|
|
90
|
+
]},
|
|
91
|
+
])
|
|
92
|
+
const slash = await client.commands.getSlash()
|
|
93
|
+
await client.commands.deleteSlash('ban')
|
|
94
|
+
|
|
95
|
+
// Prefix commands
|
|
96
|
+
await client.commands.setPrefix([
|
|
97
|
+
{ prefix: '!', name: 'help', description: 'Show help' },
|
|
98
|
+
{ prefix: '!', name: 'stats', description: 'Show bot stats' },
|
|
99
|
+
])
|
|
100
|
+
const prefix = await client.commands.getPrefix()
|
|
101
|
+
await client.commands.deletePrefix('!', 'help')
|
|
102
|
+
|
|
103
|
+
// Context menu commands (right-click)
|
|
104
|
+
await client.commands.setContext([
|
|
105
|
+
{ name: 'Report message', target: 'MESSAGE' },
|
|
106
|
+
{ name: 'View profile', target: 'USER' },
|
|
107
|
+
])
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### `client.interactions`
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
client.on('interactionCreate', async (interaction) => {
|
|
114
|
+
// Acknowledge immediately (shows loading state)
|
|
115
|
+
await client.interactions.ack(interaction.id)
|
|
116
|
+
|
|
117
|
+
// Do your work...
|
|
118
|
+
const result = await doExpensiveWork()
|
|
119
|
+
|
|
120
|
+
// Respond with result
|
|
121
|
+
await client.interactions.respond(interaction.id, {
|
|
122
|
+
content: result,
|
|
123
|
+
ephemeral: true, // only visible to the user who triggered it
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
// Poll for pending interactions (HTTP polling mode, no WebSocket)
|
|
128
|
+
const pending = await client.interactions.poll({ limit: 20 })
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### `client.members`
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
// List server members
|
|
135
|
+
const members = await client.members.list(serverId, { limit: 100 })
|
|
136
|
+
|
|
137
|
+
// Kick a member (cannot kick OWNER or ADMIN)
|
|
138
|
+
await client.members.kick(serverId, userId)
|
|
139
|
+
|
|
140
|
+
// Ban a member
|
|
141
|
+
await client.members.ban(serverId, userId, 'Spamming')
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### `client.servers`
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
// List all servers the bot is in
|
|
148
|
+
const servers = await client.servers.list()
|
|
149
|
+
console.log(`Active in ${servers.length} servers`)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Events
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
// Connection
|
|
156
|
+
client.on('ready', (bot) => console.log('Ready!', bot.botUser.username))
|
|
157
|
+
client.on('disconnect', (reason) => console.log('Disconnected:', reason))
|
|
158
|
+
client.on('error', (err) => console.error('Error:', err.message))
|
|
159
|
+
|
|
160
|
+
// Interactions
|
|
161
|
+
client.on('interactionCreate', (interaction) => { /* ... */ })
|
|
162
|
+
|
|
163
|
+
// Messages (convenience events)
|
|
164
|
+
client.on('messageCreate', (data) => { /* ... */ })
|
|
165
|
+
client.on('messageUpdate', (data) => { /* ... */ })
|
|
166
|
+
client.on('messageDelete', (data) => { /* ... */ })
|
|
167
|
+
client.on('reactionAdd', (data) => { /* ... */ })
|
|
168
|
+
client.on('reactionRemove',(data) => { /* ... */ })
|
|
169
|
+
|
|
170
|
+
// Members
|
|
171
|
+
client.on('memberAdd', (data) => { /* ... */ })
|
|
172
|
+
client.on('memberRemove', (data) => { /* ... */ })
|
|
173
|
+
client.on('typingStart', (data) => { /* ... */ })
|
|
174
|
+
|
|
175
|
+
// All raw events
|
|
176
|
+
client.on('event', (event) => {
|
|
177
|
+
console.log(event.type, event.data, event.timestamp)
|
|
178
|
+
})
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### All event types
|
|
182
|
+
|
|
183
|
+
| Event type | Fired when |
|
|
184
|
+
|---|---|
|
|
185
|
+
| `message.created` | A message is sent in a server the bot is in |
|
|
186
|
+
| `message.edited` | A message is edited |
|
|
187
|
+
| `message.deleted` | A message is deleted |
|
|
188
|
+
| `message.reaction_added` | A reaction is added |
|
|
189
|
+
| `message.reaction_removed` | A reaction is removed |
|
|
190
|
+
| `message.pinned` | A message is pinned |
|
|
191
|
+
| `user.joined_server` | A user joins a server |
|
|
192
|
+
| `user.left_server` | A user leaves a server |
|
|
193
|
+
| `user.updated_profile` | A user updates their profile |
|
|
194
|
+
| `user.banned` | A user is banned |
|
|
195
|
+
| `user.unbanned` | A user is unbanned |
|
|
196
|
+
| `user.role_added` | A user receives a role |
|
|
197
|
+
| `user.role_removed` | A role is removed from a user |
|
|
198
|
+
| `user.started_typing` | A user starts typing |
|
|
199
|
+
| `user.voice_joined` | A user joins a voice channel |
|
|
200
|
+
| `user.voice_left` | A user leaves a voice channel |
|
|
201
|
+
| `interaction.slash_command` | A slash command is used |
|
|
202
|
+
| `interaction.button_click` | A button is clicked |
|
|
203
|
+
| `interaction.select_menu` | A select menu item is chosen |
|
|
204
|
+
| `interaction.modal_submit` | A modal is submitted |
|
|
205
|
+
| `interaction.autocomplete` | An autocomplete request fires |
|
|
206
|
+
| `server.updated` | Server settings change |
|
|
207
|
+
| `channel.created` | A channel is created |
|
|
208
|
+
| `channel.deleted` | A channel is deleted |
|
|
209
|
+
| `channel.updated` | A channel is updated |
|
|
210
|
+
| `role.created` | A role is created |
|
|
211
|
+
| `role.deleted` | A role is deleted |
|
|
212
|
+
|
|
213
|
+
## WebSocket helpers
|
|
214
|
+
|
|
215
|
+
For ultra-low-latency message sending without an HTTP round-trip:
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
// Send via WebSocket (fire and forget)
|
|
219
|
+
client.wsSend(channelId, 'Hello!')
|
|
220
|
+
|
|
221
|
+
// Typing indicators via WebSocket
|
|
222
|
+
client.wsTypingStart(channelId)
|
|
223
|
+
client.wsTypingStop(channelId)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Full example — Moderation bot
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
import { NovaClient } from 'nova-bot-sdk'
|
|
230
|
+
|
|
231
|
+
const client = new NovaClient({ token: process.env.NOVA_BOT_TOKEN! })
|
|
232
|
+
|
|
233
|
+
client.on('ready', async (bot) => {
|
|
234
|
+
console.log(`[${bot.botUser.username}] Ready!`)
|
|
235
|
+
|
|
236
|
+
await client.commands.setSlash([
|
|
237
|
+
{
|
|
238
|
+
name: 'ban',
|
|
239
|
+
description: 'Ban a user from the server',
|
|
240
|
+
options: [
|
|
241
|
+
{ name: 'user', description: 'User to ban', type: 'USER', required: true },
|
|
242
|
+
{ name: 'reason', description: 'Reason for ban', type: 'STRING', required: false },
|
|
243
|
+
],
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
name: 'warn',
|
|
247
|
+
description: 'Warn a user',
|
|
248
|
+
options: [
|
|
249
|
+
{ name: 'user', description: 'User to warn', type: 'USER', required: true },
|
|
250
|
+
{ name: 'message', description: 'Warning message', type: 'STRING', required: true },
|
|
251
|
+
],
|
|
252
|
+
},
|
|
253
|
+
])
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
client.on('interactionCreate', async (interaction) => {
|
|
257
|
+
if (interaction.commandName === 'ban') {
|
|
258
|
+
await client.interactions.ack(interaction.id)
|
|
259
|
+
|
|
260
|
+
const data = interaction.data as { options?: Array<{ name: string; value: string }> }
|
|
261
|
+
const userId = data.options?.find((o) => o.name === 'user')?.value
|
|
262
|
+
const reason = data.options?.find((o) => o.name === 'reason')?.value ?? 'No reason provided'
|
|
263
|
+
|
|
264
|
+
if (!userId || !interaction.serverId) {
|
|
265
|
+
await client.interactions.respond(interaction.id, {
|
|
266
|
+
content: '❌ Missing user or server context.',
|
|
267
|
+
ephemeral: true,
|
|
268
|
+
})
|
|
269
|
+
return
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
await client.members.ban(interaction.serverId, userId, reason)
|
|
274
|
+
await client.interactions.respond(interaction.id, {
|
|
275
|
+
embed: {
|
|
276
|
+
title: '🔨 User banned',
|
|
277
|
+
description: `<@${userId}> was banned. Reason: ${reason}`,
|
|
278
|
+
color: 0xff4444,
|
|
279
|
+
},
|
|
280
|
+
})
|
|
281
|
+
} catch (err: unknown) {
|
|
282
|
+
const message = err instanceof Error ? err.message : 'Unknown error'
|
|
283
|
+
await client.interactions.respond(interaction.id, {
|
|
284
|
+
content: `❌ Failed to ban: ${message}`,
|
|
285
|
+
ephemeral: true,
|
|
286
|
+
})
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (interaction.commandName === 'warn') {
|
|
291
|
+
const data = interaction.data as { options?: Array<{ name: string; value: string }> }
|
|
292
|
+
const userId = data.options?.find((o) => o.name === 'user')?.value
|
|
293
|
+
const message = data.options?.find((o) => o.name === 'message')?.value
|
|
294
|
+
|
|
295
|
+
await client.interactions.respond(interaction.id, {
|
|
296
|
+
embed: {
|
|
297
|
+
title: '⚠️ Warning issued',
|
|
298
|
+
description: `<@${userId}> — ${message}`,
|
|
299
|
+
color: 0xffa500,
|
|
300
|
+
},
|
|
301
|
+
})
|
|
302
|
+
}
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
client.on('error', (err) => {
|
|
306
|
+
console.error('[Bot]', err.message)
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
client.connect().then(() => console.log('Connected to Nova gateway'))
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Self-hosted deployments
|
|
313
|
+
|
|
314
|
+
If you're running your own Nova server, pass the base URL:
|
|
315
|
+
|
|
316
|
+
```ts
|
|
317
|
+
const client = new NovaClient({
|
|
318
|
+
token: 'nova_bot_...',
|
|
319
|
+
baseUrl: 'https://my-nova-server.example.com',
|
|
320
|
+
})
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## Rate limits
|
|
324
|
+
|
|
325
|
+
The Nova API enforces a limit of **50 requests/second** per bot. The SDK will surface a `429` error if you exceed this. Build in exponential back-off for batch operations.
|
|
326
|
+
|
|
327
|
+
## License
|
|
328
|
+
|
|
329
|
+
MIT
|