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

@injitools/bot

v0.4.0

Published

Messenger-agnostic chat-bot core for Inji: commands, dialog input, message templates and keyboards over pluggable social providers (Telegram, VK) and a pluggable storage (TypeORM included).

Readme

@injitools/bot

Messenger-agnostic chat-bot core for the Inji framework. Commands, dialog input, message templates and keyboards over pluggable social providers (one per messenger) and a pluggable storage. The main entry depends on nothing; @injitools/bot/typeorm adds the TypeORM storage.

Part of the Inji monorepo.

Install

npm install @injitools/bot @injitools/bot-telegram typeorm

typeorm is an optional peer dependency — only @injitools/bot/typeorm needs it.

Usage

import {MultiBot} from "@injitools/bot";
import {MultiBotTypeOrmProvider, multiBotEntities} from "@injitools/bot/typeorm";
import {MultiBotTelegramProvider, TelegramApi} from "@injitools/bot-telegram";

// entities: [...multiBotEntities, ...yourEntities] in your DataSource
const bot = new MultiBot(new MultiBotTypeOrmProvider(dataSource.manager));
bot.addProvider(await MultiBotTelegramProvider.fromApi(new TelegramApi(process.env.TELEGRAM_BOT_TOKEN)));

bot.command('start', async (_cmd, msg) => {
    await msg.answer(t => t.text('Hello 👋').br().text('Pick one:').keyboard(k => {
        k.inline();
        k.line().button('Status').command('status');
        k.line().button('Docs').link('https://inji.ru');
    }));
});
bot.command('phone', async (_cmd, msg) => {
    await msg.requestInput('set_phone', 'Your number?');      // the NEXT message goes to the input handler
});
bot.input('set_phone', async (_input, msg) => {
    await msg.chat.setOption('phone', msg.text);
    await msg.answer('Saved.');
});
bot.message('status', async (msg) => msg.answer('All good.'));   // exact text (trimmed, lower-cased)
bot.message('*', async (msg) => msg.answer('Say /start'));     // everything else

// Telegram only reveals a block by refusing a send; VK sends message_allow/deny. Both land here.
bot.onBlocked(async (chat, blocked) => profiles.setBlocked(chat.id, blocked));

// poll every provider (see @injitools/core/lifecycle makeProcess for a supervised loop)
for (const provider of Object.values(bot.getProviders())) {
    setInterval(() => provider.pullUpdates().catch(console.error), 1000);
}

How an update flows

Every provider feeds each raw update into bot.ingest(provider, event, parse):

  1. dedup by (provider, eventId) — an update already executed is skipped;
  2. the raw update is stored verbatim (MultiBotProviderEvent);
  3. the provider's parse maps it to ParsedIncoming (from, providerId, text, an explicit payload for callback buttons…) or returns null for "nothing to dispatch";
  4. the chat is found or created — by peer, or by chatProviderId where a chat is not a peer (groups);
  5. /command args text becomes a command; the message is stored; onBlocked(chat, false) fires;
  6. the message is dispatched; the event is marked executed.

A throwing handler is logged and recorded on the stored event (error), which stays executed: false — the poll loop continues, the failure is visible in the table.

Peers and providers

Peers are strings <provider>:<kind>:<id> (telegram:user:123, vk:user:456); a provider's name is <provider>:<bot|user>:<uid>. Register as many providers as you like — a chat knows its provider, so chat.sendMessage(...) always goes to the right messenger.

Writing your own provider means implementing IMultiBotSocialProvider (pullUpdates, sendMessage, editMessage, setTypingStatus) and calling bot.ingest — see @injitools/bot-telegram for a complete one. A storage is IMultiBotStorageProvider; MultiBotMemoryStorage is the in-memory reference implementation (tests, prototypes).

License

MIT