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

chat-toolkit

v0.2.12

Published

![GitHub Tag](https://img.shields.io/github/v/tag/fulcanelly/chat-toolkit) <a href="https://www.npmjs.com/package/chat-toolkit"><img alt="NPM Downloads" src="https://img.shields.io/npm/dy/chat-toolkit?link=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Fchat-tool

Readme

Chat toolkit

GitHub Tag GitHub Actions Workflow Status

What is it ?

it's library/toolkit for easier chat-bot state managment/persistion

currently only telegram API supported, further extension for discrod API possible

Content tree

Adding new state

Describe state using actions like say, expect, switchState and others


export async function mainState() {

  await say('Enter a')

  const a = Number(await expect_())

  await say('Enter b')

  const b = Number(await expect_())

  await say("Reuslt is " + (a + b))
  
  await switchState('mainState')
  // or 
  await stateSwitcher.mainState()

}

And then add that state to AllStates to safe typing

declare module 'chat-toolkit' {
  interface AllStates {
    mainState: typeof mainState
  }
}

Default / Main state

You need to specify what is the entry point state and what should run first

that's done at setup, see this for details

const handler
  = createTelegramHandler({
    bot,
    allStates,
    defaultState: 'mainState'
  }, dbParams)

Setup

Ensure prisma config and file structure is right

Currently this npm relies on prisma (in future planned ability to chose over other orms) and it assumes you have enabled your prismaSchemaFolder in schema.prisma and it lies in prisma/schema/ folder

generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["typedSql", "prismaSchemaFolder"]
}

Generate models

Then you need to run

npx chat-toolkit setup 

which would create needed models in your project folder

Apply prisma migrations

After that you'll have to apply migration migrate

yarn prisma migrate dev --name add_chat_toolkit_models

Adjust code to supply events to handler

Example with Telegraf:


const bot = new Telegraf(process.env.TG_TOKEN)
const prisma = new PrismaClient()

const dbParams = {
  findOrCreateUser: findOrCreateUserPrisma(prisma as any), 
  stateManager: defaultPrismaStateManagerImplementation(prisma as any),
  // unfrotunatelly cast to any needed, since local prisma is not the same as chat-toolkits 
  // right now idk how to avoid this but it's works just fine
}

const allStates = {
  mainState
}

const handler
  = createTelegramHandler({
    bot,
    allStates,
    defaultState: 'mainState'
  }, dbParams)


bot.start(async ctx => {
  await handler.handlePrivateMessage(ctx, true)
})


bot.on('message', async ctx => {
  await handler.handlePrivateMessage(ctx as any, false)
}) 

Actions

Avaliable actions

say :: String -> Effect ()

^ Just sends text message to user

expect_ :: () -> Effect (String)

^ Expects text message from user

expectAny :: (Message -> a) -> Effect a 

^ In case if if need to wait for specific user message or combined

for example

await expectAny(msg => {
  if ('photo' in msg) {
    return msg.photo[0]
  }
  if ('text' in msg) {
    return msg.text
  }
})
random :: () -> Effect (Number)

^ since states can do anything it's better not to use Math.random directly so it's just wrapper over it (needed for restoring state)

TODO:

suggest

suggestIt

_disableRecording _onRestoreDoRun

switchState escape_

Modifing actions

TODO

Notification / Interrupt state

TODO

Inline keyboard handler

Suppose case when you need inline keyboard for example [like, dislike]

then each button should execute some kind of logic

in this case you can get use of createCallbackHandle:

await bot.sendMessage('you liked that post?', {
  reply_markup: { 
    inline_keyboard: [[
      {
        text: "Like",
        callback_data: await recordingObject.like({post_id: post.id}),
      },
      {
        text: "Dislike",
        callback_data: await recordingObject.dislike({post_id: post.id}),
      }
    ]]
  }
}) 

where recordingObject would be defined like this:

const recordingObject = createCallbackHandle({
  namespace: 'post-likes',
  handler: ctx => ({
    async like({post_id}: { post_id: number }) {
      const user = await User.find(ctx.from.id)
      await Post.find(post_id).likeBy(user)
      await ctx.editMessageText //...
    },

    dislike({post_id}: { post_id: number }) {
      const user = await User.find(ctx.from.id)
      await Post.find(post_id).likeBy(user)
      await ctx.editMessageText //...
    }
  })
}) 

To use this you need to enable handler

const redis = new Redis() // from ioredis npm
const bot = new Telegraf(process.env.TG_TOKEN)

/// ...

export const { 
  setupCallbackHandler, 
  createCallbackHandle,
 } = createRedisInlineKeyboardHandler({
  redis,
  projectName: 'mybot', 
  ivalidateIn: duration(1, 'day'),
 })

// ...

setupCallbackHandler(bot)

Each call to recordingObject generates a UUID that is stored in callback_data on the Telegram side. When a button is pressed, the corresponding function is invoked.

On our end, the arguments for the handler function are stored in Redis under the key ${projectName}:callbackquery:${uuid} for 24 hours (this is the default duration and covers 99% of use cases).

Adjusting global state

TODO

declare module 'chat-toolkit' {
  interface GlobalSharedAppContext {
   
  }
}

Adjusting escape ctx

declare module 'chat-toolkit' {
  interface EscapeData {
    user: User
  }
}

TODO

  • [ ] Notifications
  • [ ] Custom expects
  • [ ] GC
  • [ ] Detect long states
  • [ ] Error handling strategy