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 🙏

© 2024 – Pkg Stats / Ryan Hefner

vercel-grammy

v1.0.0

Published

Utilities for grammY on Vercel

Downloads

258

Readme

grammY helpers for Vercel

Collection of useful methods to run your bot on Vercel

How to ...

Install

npm i vercel-grammy

Import

import {/* methods */} from "vercel-grammy"

Use

import {Bot} from "grammy"
import {getURL} from "vercel-grammy"

const url = getURL({path: "api/index"})

const bot = new Bot(/* token */)

await bot.api.setWebhook(url)

Examples

Get current hostname

// Anywhere in your code
getHost() // *.vercel.app (from `process.env.VERCEL_URL`)

// At your function handler 
export default ({headers}) => {
    getHost({headers}) // domain.com (from `x-forwarded-host` header)
}

Get URL for current hostname

// Anywhere in your code
getURL({path: "api/index"}) // https://*.vercel.app/api/index

// At your function handler 
export default ({headers}) => {
    getURL({headers, path: "api/index"}) // https://domain.com/api/index
}

Set webhook for current hostname

// Anywhere in your code
bot.api.setWebhook(getURL({path: "api/index"}))

// As function handler
export default setWebhookCallback(bot, {path: "api/index"})

Use streaming response in webhook handler

Note that this will work only at Vercel Edge Functions

// As function handler
export default webhookStream(bot) // Instead of webhookCallback(bot)

export const config = {
    runtime: "edge"
}

Guides

Sets webhook URL automatically

When you deploy a project to Vercel, one of these environments is installed for it:

  • production — default for main or master branches
  • preview — for all other branches in your repository
  • development — when using the vercel dev command

In the early stages of bot development, it is enough to install a webhook on the main (production) domain, such as project.vercel.app

However, if you want to test new changes without stopping the bot, then you can simply use a separate (test) bot (for example @awesome_beta_bot) and set the webhook to the URL of the branch — project-git-branch-username.vercel.app

But what if you have several separate branches with different changes and want to test them without creating a separate bot for each or manually managing webhooks ?

Q: You didn't make a separate plugin for this, right ?
A: 😏
Q: Didn't do it, right ?

Thanks to the Vercel build step, we can run some code before a new version of the bot is published and no one will stop us from using it

Just add this code to a new JavaScript file:

const {
    VERCEL_ENV,
} = process.env

// List of allowed environments
const allowedEnvs = [
    "production",
    "preview"
]

// Exit in case of unsuitable environments
if (!allowedEnvs.includes(VERCEL_ENV)) process.exit()

// Webhook URL generation
const url = getURL({path: "api/index"})

// Installing a webhook
await bot.api.setWebhook(url)

And specify the path to it in the vercel.json file:

{
  "buildCommand": "node path/to/new/file.js"
}

By the way, you can manage tokens for each environment (or even branch) in the project settings

Avoiding invocation timeouts

By default, Vercel limits the invocation time for your code:

So, without streaming (and paying) you can get up to 25 seconds with default grammY webhookCallback adapter at Edge Functions

On the other hand, we also have a time limit for responding to incoming requests from Telegram — 60 seconds, after which, the request will be considered unsuccessful and will be retried, which you probably don't want

To get around these limitations you can proxy the request before calling the function by following scheme:

  1. Telegram sends an update request
  2. Proxy service passes the original request to your function
  3. Answer within 60 seconds will be returned to Telegram
  4. Otherwise, proxy responds with a 200 status to prevent a recurrence
  5. Your function may continue to work for the next 940 seconds

Q: What proxy server is suitable for this ?
A: I don't know, but I made it 🙂

Proxy

Source: ProlongRequest

Endpoint: https://prolong-request.fly.dev

Reference:

  • /domain.com
  • /http://domain.com
  • /https://domain.com
  • /https://domain.com/path/to/file.txt
  • /https://domain.com/route?with=parameters

Also supports any HTTP methods and transmits raw headers and body

How to use this for bot

Just prepend proxy endpoint to webhook URL:

https://prolong-request.fly.dev/https://*.vercel.app/api/index

Or do it automatically:

const proxy = "https://prolong-request.fly.dev"

const url = getURL({path: "api/index"})

bot.api.setWebhook(`${proxy}/${url}`)

And use streaming response in webhook handler:

export default webhookStream(bot, {
    timeoutMilliseconds: 999 // where you can also control timeout
})

export const config = {
    runtime: "edge"
}

Limitations

  • Processing updates will overlap
  • States and sessions will be inconsistent
  • Request may break and will not be retried

Benefits

  • You can do anything during this time
  • You can wait anything within this time
  • You can solve anything using this time

API

getHost([options])

  • options (object, optional) — Options for hostname
    • headers (Headers, optional) — Headers from incoming request
    • header (string, optional) — Header name which contains the hostname
    • fallback (string, optional) — Fallback hostname (process.env.VERCEL_URL by default)
  • returns string — Target hostname

This method generates a hostname from the options passed to it

getURL([options])

  • options (object, optional) — Options for URL
    • path (string, optional) — Path to a function that receives updates
    • host (string, optional) — Hostname without protocol (replaces getHost options)
    • ...options (object, optional) — Options for getHost
  • returns string — Target URL

This method generates a URL from the options passed to it

setWebhookCallback(bot[, options])

  • bot (Bot, required) — grammY bot instance
  • options (object, optional) — Options for webhooks
    • url (string, optional) — URL for webhooks (replaces getURL options)
    • onError ("throw" | "return", optional) — Strategy for handling errors
    • allowedEnvs (array, optional) — List of environments where this method allowed
    • ...options (object, optional) — Options for bot.api.setWebhook
    • ...options (object, optional) — Options for getURL
  • returns () => Promise<Response> — Target callback method

Callback factory for grammY bot.api.setWebhook method

webhookStream(bot[, options])

  • bot (Bot, required) — grammY bot instance
  • options (object, optional) — Options for stream
    • chunk (string, optional) — Content for chunks
    • intervalMilliseconds (number, optional) — Interval for writing chunks to stream
    • ...options (object, optional) — Options for webhookCallback
  • returns () => Response — Target callback method

Callback factory for streaming webhook response

jsonResponse(value[, options])

  • value (any, required) — Serializable value
  • options (object, optional) — Options for JSON response
    • replacer ((string | number)[] | null | undefined, optional)
    • space (string | number | undefined, optional)
    • ...options (ResponseInit, optional)
  • returns Response — Target JSON Response

This method generates Response objects for JSON

Templates using this package

Made with 💜 by Vladislav Ponomarev