npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vesselx-bot-api

v2.0.0

Published

Unofficial Vesselx Bot API framework for Node.js

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

Requirements: 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);
});