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 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