vesselx-bot-api
v2.0.0
Published
Unofficial Vesselx Bot API framework for Node.js
Maintainers
Readme
# Vesselx Bot API
Node.js framework for building Vesselx bots. A Telegraf-like, lightweight wrapper around the Vesselx Bot API with zero extra dependencies (just axios).
---
## Table of Contents
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Configuration](#configuration)
- [Polling Mode](#polling-mode)
- [Webhook Mode](#webhook-mode)
- [Handling Updates](#handling-updates)
- [Commands](#commands)
- [Pattern Matching (Hears)](#pattern-matching-hears)
- [Event Listeners](#event-listeners)
- [Sending Messages](#sending-messages)
- [Text Messages](#text-messages)
- [Photos](#photos)
- [Videos](#videos)
- [Audio](#audio)
- [Documents](#documents)
- [Typing Indicator](#typing-indicator)
- [Group Administration](#group-administration)
- [Admin Management](#admin-management)
- [Moderation (Kick, Ban, Unban)](#moderation-kick-ban-unban)
- [Message Management (Pin, Delete)](#message-management-pin-delete)
- [Group Info (Title, Description, Photo)](#group-info-title-description-photo)
- [Context Object Reference](#context-object-reference)
- [Group Events](#group-events)
- [Client API Reference](#client-api-reference)
- [Error Handling](#error-handling)
- [License](#license)
---
## Installation
```bash
npm install vesselx-bot-apiRequirements: Node.js >= 14.0.0
Quick Start
const { VesselxBot } = require('vesselx-bot-api');
// Create a bot instance with your API key
const bot = new VesselxBot('vxbot_YOUR_API_KEY', {
polling: true,
updateInterval: 3000 // check for new messages every 3 seconds (default)
});
// Handle /start command
bot.command('start', async (ctx) => {
await ctx.reply('👋 Hello! I am a Vesselx bot. Type /help to see what I can do.');
});
// Echo any text message
bot.on('message', async (ctx) => {
if (ctx.text) {
await ctx.reply(`You said: ${ctx.text}`);
}
});
// Start the bot
bot.start();Configuration
Polling Mode
The bot regularly checks for new updates. Best for development and small bots.
const bot = new VesselxBot('vxbot_YOUR_KEY', {
polling: true,
updateInterval: 2000, // optional, default: 3000ms
apiHost: 'https://api.vesselx.qzz.io' // optional, default
});Webhook Mode
The API pushes updates to your server. Best for production.
const bot = new VesselxBot('vxbot_YOUR_KEY', {
webhook: {
port: 3000,
path: '/webhook',
secret: 'your_webhook_secret' // optional HMAC verification
}
});Note: When using webhook mode, make sure your server is publicly accessible (use ngrok for local development).
Handling Updates
Commands
// Simple command
bot.command('help', async (ctx) => {
await ctx.reply('Available commands:\n/start - Start the bot\n/help - Show this help\n/echo <text> - Echo text');
});
// Command with arguments
bot.command('echo', async (ctx) => {
const text = ctx.args.join(' ');
if (!text) {
return ctx.reply('Usage: /echo <your message>');
}
await ctx.reply(`🔊 ${text}`);
});Pattern Matching (Hears)
// Match exact phrase
bot.hears('hello', async (ctx) => {
await ctx.reply('Hi there! 👋');
});
// Match with regex
bot.hears(/how are you/i, async (ctx) => {
await ctx.reply("I'm doing great, thanks for asking! 😊");
});
// Match with capture groups
bot.hears(/my name is (\w+)/i, async (ctx, match) => {
const name = match[1];
await ctx.reply(`Nice to meet you, ${name}!`);
});
// Match URLs
bot.hears(/(https?:\/\/[^\s]+)/, async (ctx, match) => {
await ctx.reply(`I see you shared a link: ${match[1]}`);
});Event Listeners
// All updates
bot.on('update', (ctx) => {
console.log('New update:', ctx.type);
});
// Text messages
bot.on('message', async (ctx) => {
await ctx.reply(`Received: ${ctx.text}`);
});
// Media messages
bot.on('image', async (ctx) => {
await ctx.reply('Nice picture! 📸');
});
bot.on('video', async (ctx) => {
await ctx.reply('Great video! 🎬');
});
bot.on('audio', async (ctx) => {
await ctx.reply('Nice track! 🎵');
});
bot.on('file', async (ctx) => {
await ctx.reply('Got your file! 📁');
});
// Inline queries
bot.on('inline_query', async (ctx) => {
await ctx.answerInline([
{ type: 'article', id: '1', title: 'Result 1', description: 'Description', text: 'Hello!' }
]);
});
// Group admin events (see Group Events section for more)
bot.on('group:adminChanged', (ctx) => {
console.log('An admin was promoted or demoted');
});Sending Messages
Text Messages
// Simple text reply
await ctx.reply('Hello there!');
// Reply with typing indicator first
await ctx.sendTyping();
await new Promise(r => setTimeout(r, 2000)); // simulate thinking
await ctx.reply('Here is your response!');Photos
Send images from public HTTPS URLs with optional captions.
bot.command('cat', async (ctx) => {
await ctx.replyWithPhoto(
'https://cataas.com/cat',
'Here is a random cat for you! 🐱'
);
});
// Using the client directly (if you don't have a context)
await bot.client.sendPhoto({
username: 'john_doe',
url: 'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=800',
caption: 'Beautiful beach sunset 🌅'
});Videos
Send videos from URLs with captions and duration.
bot.command('trailer', async (ctx) => {
await ctx.replyWithVideo(
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
'🎥 Big Buck Bunny - Enjoy!',
596 // duration in seconds
);
});Audio
Send audio files with captions and duration.
bot.command('music', async (ctx) => {
await ctx.replyWithAudio(
'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3',
'🎵 SoundHelix - Song 1',
367 // duration in seconds
);
});Documents
Send documents/files with filename and size info.
bot.command('report', async (ctx) => {
await ctx.replyWithDocument(
'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf',
'📄 Monthly Report',
'report.pdf',
13264 // file size in bytes
);
});Typing Indicator
bot.command('think', async (ctx) => {
await ctx.sendTyping();
// Simulate processing...
await new Promise(resolve => setTimeout(resolve, 3000));
await ctx.reply('Done thinking! 🤔');
});Group Administration
Your bot must be added to the group and given admin permissions to use these features.
Admin Management
// List all admins
bot.command('admins', async (ctx) => {
if (!ctx.groupId) return ctx.reply('⚠️ This command only works in groups.');
const { result } = await ctx.getChatAdmins();
const list = result.map(a => {
const crown = a.role === 'owner' ? ' 👑' : '';
const title = a.title ? ` — ${a.title}` : '';
return `• @${a.user.username}${crown}${title}`;
}).join('\n');
await ctx.reply(`👥 **Group Admins:**\n${list}`);
});
// Check a member's status
bot.command('whois', async (ctx) => {
if (!ctx.groupId) return ctx.reply('⚠️ This command only works in groups.');
const username = ctx.args[0];
if (!username) return ctx.reply('Usage: /whois @username');
const { result } = await ctx.getChatMember(username);
await ctx.reply(`@${result.user.username}\nStatus: ${result.status}\nRole: ${result.role || 'member'}`);
});
// Promote a member to admin
bot.command('promote', async (ctx) => {
if (!ctx.groupId) return ctx.reply('⚠️ This command only works in groups.');
const username = ctx.args[0];
if (!username) return ctx.reply('Usage: /promote @username [title]');
const title = ctx.args.slice(1).join(' ') || 'Moderator';
await ctx.promoteChatMember(username, {
title,
permissions: {
canDeleteMessages: true,
canPinMessages: true,
canBanUsers: false,
canInviteUsers: true,
canChangeInfo: false,
canManageChat: false,
canPromoteMembers: false
}
});
await ctx.reply(`✅ @${username} promoted to admin as "${title}"`);
});
// Demote an admin
bot.command('demote', async (ctx) => {
if (!ctx.groupId) return ctx.reply('⚠️ This command only works in groups.');
const username = ctx.args[0];
if (!username) return ctx.reply('Usage: /demote @username');
await ctx.demoteChatMember(username);
await ctx.reply(`⬇️ @${username} demoted to regular member.`);
});Moderation (Kick, Ban, Unban)
// Kick a member (can rejoin)
bot.command('kick', async (ctx) => {
if (!ctx.groupId) return ctx.reply('⚠️ This command only works in groups.');
const username = ctx.args[0];
if (!username) return ctx.reply('Usage: /kick @username');
await ctx.kickChatMember(username);
await ctx.reply(`👢 @${username} has been kicked from the group.`);
});
// Ban a member (cannot rejoin until unbanned)
bot.command('ban', async (ctx) => {
if (!ctx.groupId) return ctx.reply('⚠️ This command only works in groups.');
const username = ctx.args[0];
if (!username) return ctx.reply('Usage: /ban @username [reason]');
const reason = ctx.args.slice(1).join(' ') || 'Violating group rules';
await ctx.banChatMember(username, reason);
await ctx.reply(`🚫 @${username} has been banned.\nReason: ${reason}`);
});
// Unban a member
bot.command('unban', async (ctx) => {
if (!ctx.groupId) return ctx.reply('⚠️ This command only works in groups.');
const username = ctx.args[0];
if (!username) return ctx.reply('Usage: /unban @username');
await ctx.unbanChatMember(username);
await ctx.reply(`✅ @${username} has been unbanned. They can now rejoin.`);
});Message Management (Pin, Delete)
// Delete a message (reply to the message with /delete)
bot.command('delete', async (ctx) => {
if (!ctx.groupId) return ctx.reply('⚠️ This command only works in groups.');
if (!ctx.message.replyTo?.messageId) {
return ctx.reply('⚠️ Reply to a message with /delete to remove it.');
}
await ctx.deleteMessage(ctx.message.replyTo.messageId);
await ctx.reply('🗑️ Message deleted.');
});
// Pin a message (reply to the message with /pin)
bot.command('pin', async (ctx) => {
if (!ctx.groupId) return ctx.reply('⚠️ This command only works in groups.');
if (!ctx.message.replyTo?.messageId) {
return ctx.reply('⚠️ Reply to a message with /pin to pin it.');
}
await ctx.pinMessage(ctx.message.replyTo.messageId);
await ctx.reply('📌 Message pinned!');
});
// Unpin a specific message (or all messages if no reply)
bot.command('unpin', async (ctx) => {
if (!ctx.groupId) return ctx.reply('⚠️ This command only works in groups.');
const messageId = ctx.message.replyTo?.messageId || null;
await ctx.unpinMessage(messageId); // null = unpin all
const msg = messageId ? 'Message unpinned.' : 'All messages unpinned.';
await ctx.reply(`📌 ${msg}`);
});
// Get all pinned messages
bot.command('pinned', async (ctx) => {
if (!ctx.groupId) return ctx.reply('⚠️ This command only works in groups.');
// This uses a user endpoint via the client
const { data } = await require('axios').get(
`${bot.client.apiHost}/api/groups/${ctx.groupId}/pinned`,
{ headers: { 'Authorization': `Bot ${bot.token}` } }
);
const list = data.pinnedMessages.map(m => `• ${m.text?.substring(0, 100)}`).join('\n');
await ctx.reply(`📌 Pinned Messages:\n${list || 'No pinned messages.'}`);
});Group Info (Title, Description, Photo)
// Change group title
bot.command('settitle', async (ctx) => {
if (!ctx.groupId) return ctx.reply('⚠️ This command only works in groups.');
const title = ctx.args.join(' ');
if (!title) return ctx.reply('Usage: /settitle <new group name>');
await ctx.setChatTitle(title);
await ctx.reply(`✅ Group name changed to: ${title}`);
});
// Change group description
bot.command('setdesc', async (ctx) => {
if (!ctx.groupId) return ctx.reply('⚠️ This command only works in groups.');
const desc = ctx.args.join(' ');
if (!desc) return ctx.reply('Usage: /setdesc <new description>');
await ctx.setChatDescription(desc);
await ctx.reply('✅ Group description updated.');
});
// Change group photo
bot.command('setphoto', async (ctx) => {
if (!ctx.groupId) return ctx.reply('⚠️ This command only works in groups.');
const url = ctx.args[0];
if (!url) return ctx.reply('Usage: /setphoto <image URL>');
await ctx.setChatPhoto(url);
await ctx.reply('✅ Group photo updated!');
});Context Object Reference
The ctx object passed to handlers contains:
Property Type Description ctx.update object Raw update from the API ctx.type string Update type: 'message', 'command', 'inline_query', etc. ctx.from object User who sent the update (username, name, profilePicture, etc.) ctx.message object The message object (text, type, mediaUrl, groupId, etc.) ctx.text string Message text (or null) ctx.command string Command name without / (or null) ctx.args string[] Command arguments as an array ctx.chat object Chat/group info if in a group context ctx.groupId string Group ID if the message is from a group (or null) ctx.inlineQuery object Inline query data (if applicable) ctx.callbackQuery object Callback query data (if applicable)
Context Methods:
Method Description ctx.reply(text, extra) Send a text reply ctx.replyWithPhoto(url, caption) Send a photo with caption ctx.replyWithVideo(url, caption, duration) Send a video with caption ctx.replyWithAudio(url, caption, duration) Send audio with caption ctx.replyWithDocument(url, caption, fileName, fileSize) Send a document ctx.sendTyping() Send typing indicator ctx.answerInline(results) Answer an inline query ctx.getChatAdmins() Get group admin list ctx.getChatMember(username) Get member info ctx.kickChatMember(username) Kick a member ctx.banChatMember(username, reason) Ban a member ctx.unbanChatMember(username) Unban a member ctx.deleteMessage(messageId) Delete a message ctx.pinMessage(messageId) Pin a message ctx.unpinMessage(messageId?) Unpin message(s) ctx.setChatTitle(title) Change group title ctx.setChatDescription(desc) Change group description ctx.setChatPhoto(url) Change group photo ctx.promoteChatMember(username, opts) Promote to admin ctx.demoteChatMember(username) Demote from admin
Group Events
Listen for real-time group administration events:
// Admin changed (promoted or demoted)
bot.on('group:adminChanged', (ctx) => {
const { memberId, action } = ctx.update;
console.log(`User ${memberId} was ${action}`);
});
// Message pinned
bot.on('group:messagePinned', (ctx) => {
console.log('Message pinned in group:', ctx.update.messageId);
});
// Message unpinned
bot.on('group:messageUnpinned', (ctx) => {
console.log('Message unpinned in group:', ctx.update.messageId);
});
// Message deleted by admin or bot
bot.on('group:messageUnsent', (ctx) => {
console.log('Message deleted:', ctx.update.messageId);
});
// Group info updated (name, photo, etc.)
bot.on('group:groupUpdated', (ctx) => {
console.log('Group info updated:', ctx.update);
});
// Generic group event catch-all
bot.on('group_event', (ctx) => {
console.log('Group event:', ctx.type);
});Client API Reference
The bot.client object provides direct access to all API methods:
// Bot info
await bot.client.getMe();
// Messaging
await bot.client.sendMessage({ username: 'john', text: 'Hello!' });
await bot.client.sendMessage({ groupId: '...', text: 'Hello group!' });
await bot.client.sendPhoto({ username: 'john', url: '...', caption: 'Look!' });
await bot.client.sendVideo({ username: 'john', url: '...', caption: 'Watch!', duration: 120 });
await bot.client.sendAudio({ username: 'john', url: '...', caption: 'Listen!', duration: 240 });
await bot.client.sendDocument({ username: 'john', url: '...', caption: 'File!', fileName: 'doc.pdf', fileSize: 1024 });
await bot.client.sendTyping('john');
// Updates
await bot.client.getUpdates(offset, limit);
await bot.client.markUpdateProcessed(updateId);
// Commands
await bot.client.setCommands([{ command: 'start', description: 'Start the bot' }]);
// Webhook
await bot.client.setWebhook({ url: '...', secret: '...', events: ['message'] });
await bot.client.deleteWebhook();
// Group Admin
await bot.client.getChatAdmins(groupId);
await bot.client.getChatMember(groupId, username);
await bot.client.kickChatMember(groupId, username);
await bot.client.banChatMember(groupId, username, 'Spam');
await bot.client.unbanChatMember(groupId, username);
await bot.client.deleteMessage(groupId, messageId);
await bot.client.pinMessage(groupId, messageId);
await bot.client.unpinMessage(groupId, messageId); // omit messageId to unpin all
await bot.client.setChatTitle(groupId, 'New Name');
await bot.client.setChatDescription(groupId, 'New description');
await bot.client.setChatPhoto(groupId, 'https://...jpg');
await bot.client.promoteChatMember(groupId, username, { title: 'Mod', permissions: {...} });
await bot.client.demoteChatMember(groupId, username);
// Inline
await bot.client.answerInline(queryId, results);
// User
await bot.client.getUser('username');
// Stats
await bot.client.getStats();Error Handling
// Wrap handlers in try-catch
bot.command('risky', async (ctx) => {
try {
await ctx.banChatMember('some_user', 'testing');
} catch (err) {
console.error('Ban failed:', err.message);
await ctx.reply('❌ Failed to ban user. Make sure I have the right permissions.');
}
});
// Global error handling on the bot instance
bot.on('error', (err) => {
console.error('Bot error:', err);
});
// Unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});