@kookapp/js-sdk
v0.1.2
Published
KOOK Bot JavaScript/TypeScript SDK
Readme
@kookapp/js-sdk
KOOK Bot JavaScript/TypeScript SDK, providing WebSocket connection management, REST API client, card message builder, directive system, and plugin system.
Installation
npm install @kookapp/js-sdk
# or
yarn add @kookapp/js-sdkQuick Start
import { KookClient } from '@kookapp/js-sdk'
const client = new KookClient({
botToken: 'your-bot-token',
})
client.on('textChannelEvent', (event) => {
console.log(`${event.extra.author.username}: ${event.content}`)
})
client.on('systemEvent', (event) => {
console.log('System event:', event.extra.type)
})
await client.connect()Obtaining a Bot Token
- Go to the KOOK Developer Platform
- Create or select an application
- Navigate to the "Bot" page and copy the Token
Core Modules
KookClient
Top-level client that combines WebSocket, REST API, directive, and plugin systems.
import { KookClient } from '@kookapp/js-sdk'
const client = new KookClient({
botToken: 'your-token',
baseUrl: 'https://www.kookapp.cn', // optional, default
compression: true, // optional, WebSocket compression
autoReconnect: true, // optional, auto reconnect on disconnect
timing: { heartbeatIntervalMs: 30000 }, // optional, WebSocket timing
})
await client.connect()
// Bot info (available after connect)
console.log(client.me?.username)
// Event listeners
client.on('textChannelEvent', (event, sn) => {
/* ... */
})
client.on('systemEvent', (event, sn) => {
/* ... */
})
client.on('event', (event, sn) => {
/* ... */
})
client.on('stateChange', (newState, oldState) => {
/* ... */
})
client.on('open', () => {
/* ... */
})
client.on('close', () => {
/* ... */
})
client.on('error', (message) => {
/* ... */
})
client.on('reset', () => {
/* ... */
})
// Disconnect
client.disconnect()RestClient
HTTP API client with automatic rate limiting.
// Access via client.api
const result = await client.api.createMessage({
type: 9, // KMarkdown
target_id: 'channel-id',
content: 'Hello!',
})
if (result.success) {
console.log('Message sent:', result.data.msg_id)
}All API methods return KResponseExt<T> with { success: boolean, code: number, message: string, data: T }. They never throw.
Available methods:
| Method | Description |
| ----------------------------- | ----------------------------- |
| createMessage(props) | Send a message to a channel |
| updateMessage(props) | Update an existing message |
| deleteMessage(props) | Delete a message |
| addReaction(props) | Add a reaction to a message |
| deleteReaction(props) | Remove a reaction |
| uploadAsset(formData) | Upload a file and get its URL |
| getSelfUser() | Get the bot's own user info |
| getUser(props) | Get a user's info |
| listGuilds() | List guilds the bot is in |
| viewGuild(props) | Get guild details |
| listGuildMembers(props) | List members of a guild |
| listChannels(props) | List channels of a guild |
| createChannel(props) | Create a channel |
| deleteChannel(props) | Delete a channel |
| listGuildRoles(props) | List guild roles |
| createGuildRole(props) | Create a guild role |
| grantRole(props) | Grant a role to a user |
| revokeRole(props) | Revoke a role from a user |
| createDirectMessage(props) | Send a direct message |
| request(url, method, data?) | Raw API request |
For a standalone RestClient (without WebSocket):
import { RestClient } from '@kookapp/js-sdk'
const api = new RestClient({ token: 'your-token' })
const result = await api.createMessage({ type: 9, target_id: '...', content: '...' })CardBuilder
Chainable builder for KOOK card messages.
import { CardBuilder } from '@kookapp/js-sdk'
const card = CardBuilder.fromTemplate({ initialCard: { theme: 'info' } })
.addHeader('Title')
.addKMarkdownText('**Bold** text')
.addDivider()
.addImage('https://example.com/image.png')
.addContext('Footer text')
.build()
await client.api.createMessage({
type: 10, // Card
target_id: 'channel-id',
content: card,
})Available methods:
| Method | Description |
| ------------------------------------------ | ------------------------------------------------------------------------------ |
| .theme(theme) | Set card theme: primary, secondary, info, warning, danger, success |
| .size(size) | Set card size: sm, lg |
| .color(hex) | Set card border color |
| .addHeader(text) | Add a header |
| .addKMarkdownText(content) | Add KMarkdown text section |
| .addPlainText(text) | Add plain text section |
| .addIconWithKMarkdownText(iconUrl, text) | Add text with icon |
| .addImage(url) | Add an image container |
| .addFile(title, url, size) | Add a file module |
| .addDivider() | Add a divider |
| .addContext(text) | Add plain text context |
| .addKMarkdownContext(content) | Add KMarkdown context |
| .addActionGroup(buttons) | Add a button group |
| .addHourCountDown(endAt) | Add hour countdown |
| .addDayCountDown(endAt) | Add day countdown |
| .addSecondCountDown(endAt) | Add second countdown |
| .undoLastAdd() | Remove the last added module |
| .createSnapshot() | Create a snapshot for rollback (returns CardSnapshot \| null) |
| .restore(snapshot) | Restore from a snapshot |
| .build() | Serialize to JSON string |
| .serializedLength | Get the serialized length |
| .lastModule | Get the last module |
Directive System
Register slash-style commands (/command parameter) with permission control.
// Register a directive
client.registerDirective({
triggerWord: 'ping', // or ['ping', 'p'] for aliases
description: 'Ping pong',
parameterDescription: '',
permissionGroups: ['everyone'],
handler: async (context) => {
await client.api.createMessage({
type: 9,
target_id: context.event.target_id,
content: 'Pong!',
quote: context.event.msg_id,
})
},
})
// Create a dispatcher
const dispatcher = client.createDispatcher({
permissionResolver: (userId, userRoles, required) => {
if (required.includes('everyone')) return true
return userRoles.some((r) => required.includes(r))
},
onPermissionDenied: async (context) => {
await client.api.createMessage({
type: 9,
target_id: context.event.target_id,
content: 'Permission denied',
})
},
})
// In your event handler
client.on('textChannelEvent', async (event) => {
const handled = await dispatcher.dispatch(event)
if (!handled) {
// Not a directive, handle as normal message
}
})The DirectiveContext passed to handlers contains:
interface DirectiveContext {
event: KEvent<KTextChannelExtra> // Original event
directive: string // Matched directive name
parameter: string | undefined // Parameter after the directive
user: KUser // Author of the message
mentionRoleIds: number[] // Mentioned role IDs
mentionUserIds: string[] // Mentioned user IDs
}Plugin System
Extend the bot with reusable plugins.
import { KookPlugin, PluginContext } from '@kookapp/js-sdk'
const myPlugin: KookPlugin = {
name: 'my-plugin',
description: 'A custom plugin',
async onLoad(context: PluginContext) {
context.logger.info('Plugin loaded!')
},
onUnload() {
// Cleanup
},
}
await client.use(myPlugin)Plugin lifecycle hooks:
| Hook | Description |
| -------------------------------- | --------------------------------------------- |
| onLoad(context) | Called when the plugin is loaded |
| onUnload() | Called when the plugin is unloaded |
| onReset() | Called when the WebSocket connection is reset |
| onEvent(event, sn?) | Called for all events |
| onTextChannelEvent(event, sn?) | Called for text channel events |
| onSystemEvent(event, sn?) | Called for system events |
Types
Common types exported from the SDK:
import type {
// WebSocket timing configuration
CreateMessageProps,
// createMessage parameters
CreateMessageResult,
// createMessage return data
// Card message structure
KCardElement,
// Role info
KCardMessage,
// Card element
KCardModule,
KEvent,
// Detailed user info
KGuild,
// Card module
KResponse,
// API response
KResponseExt,
// Guild info
KRole,
// User info
KSelfUser,
// Text channel event extra data
KSystemEventExtra,
// WebSocket event
KTextChannelExtra,
// System event extra data
KUser,
// Bot's own user info
KUserDetail,
// Extended API response with success flag
KWSState,
// WebSocket connection state
WsTimingConfig,
} from '@kookapp/js-sdk'Constant objects (use these instead of enums):
import {
// Connection states: Idle, Connected, etc.
ChannelTypes,
// Channel types
KCardSizes,
// Card sizes: sm, lg
KCardThemes,
KEventTypes,
// Event types: System, KMarkdown, Card, etc.
KMessageKinds,
// WebSocket message kinds: Event, Hello, Ping, Pong, etc.
KWSStates,
// Card themes: primary, secondary, etc.
RequestMethods, // HTTP methods: GET, POST, etc.
} from '@kookapp/js-sdk'Utilities
import {
// Min-heap priority queue
KMessageQueue,
// Message queue for event ordering
// Async task queue with concurrency control
PriorityQueue,
// Decompress WebSocket message
TaskQueue,
createLogger,
// Convert object to URL query string
decompressKMessage,
// Create a logger instance
extractContent,
// Extract plain text from KMarkdown event
isExplicitlyMentioningBot,
// Remove KMarkdown labels from text
parseDirective,
// Parse directive from event
queryFromObject,
// Check if event @mentions the bot
removingKMarkdownLabels,
} from '@kookapp/js-sdk'Error Handling
The SDK follows a no-throw design. All functions return null, undefined, or a result object with success: false on failure instead of throwing exceptions. This ensures that runtime errors in the SDK never crash your application.
RestClientmethods returnKResponseExt<T>withsuccess: falseon failuredecompressKMessage()returnsnullon failureCardBuilder.createSnapshot()returnsnullon failureCardBuilder.build()returns'[]'on failure- Event listeners and directive handlers are wrapped in try-catch internally
Requirements
- Node.js >= 18.0.0
- TypeScript >= 5.7.0 (for development)
