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

@gridlock/adapter-telegram

v0.2.2

Published

Reference Telegram bot adapter for the @gridlock/channel-contract surface (#1609). Implements ChannelAdapter end-to-end against the Telegram Bot API — zero external runtime dependencies (uses fetch). Ships a record/replay stub for hermetic CI use.

Readme

@gridlock/adapter-telegram

Reference Telegram bot adapter for the @gridlock/channel-contract surface (#1609, MS-33). Implements ChannelAdapter end-to-end against the Telegram Bot HTTP API with zero runtime deps beyond @gridlock/channel-contract — uses the platform fetch.

What's in here

| Symbol | Role | |---|---| | TelegramAdapter | Production adapter. Talks to api.telegram.org. | | TelegramAdapterStub | Record/replay double for hermetic CI. | | parseTelegramUpdate | Inbound parser: Telegram Update → PlayerAction. | | TELEGRAM_TRANSPORT | The transport literal 'telegram'. |

Production wiring

import { TelegramAdapter, parseTelegramUpdate } from '@gridlock/adapter-telegram';
import type { ChannelDispatcher } from '@gridlock/channel-contract';

const adapter = new TelegramAdapter({
  botToken: process.env.TELEGRAM_BOT_TOKEN!,
  // optional log hook
  log: (entry) => console.log('telegram:', entry),
});

// Outbound: register the adapter on the dispatcher.
dispatcher.registerAdapter(adapter);

// Inbound: parse webhook bodies into PlayerAction and feed to your handler.
app.post('/webhook/telegram', async (req, res) => {
  const update = req.body as TelegramUpdate;
  const result = parseTelegramUpdate(update, {
    resolvePlayerId: (telegramUserId) => playerStore.byTelegramId(telegramUserId),
    resolveTargetNpcId: (chatId) => npcStore.byTelegramChatId(chatId),
  });
  if (result.kind === 'ok') {
    await dialogueLayer.handle(result.action);
  }
  res.status(200).send('ok');
});

Player-profile shape

TelegramAdapter reads recipient.providerIdentity.telegramChatId (string or number). Hosts populate this when the player links their Telegram account. Missing chat IDs surface as a DeliveryResult with error.code: 'INVALID_RECIPIENT' — the adapter never throws on a missing identity.

Error mapping

| Telegram response | DeliveryResult.error.code | |---|---| | HTTP 429 (rate-limit) | RATE_LIMITED | | Forbidden: bot was blocked by the user | PLAYER_BLOCKED | | chat not found (400/403) | UNKNOWN_RECIPIENT | | message is too long (400) | CONTENT_TOO_LONG | | HTTP 5xx | PROVIDER_INTERNAL | | Fetch failure (network) | NETWORK_ERROR | | Other | TELEGRAM_ERROR (description preserved) | | kind: 'template' (WhatsApp-only) | UNSUPPORTED_CONTENT | | Missing telegramChatId | INVALID_RECIPIENT |

The adapter never throws on transport failures — exceptions are reserved for programmer errors (missing botToken, no fetch available).

Testing

The package ships with the full @gridlock/channel-contract/acceptance suite wired to both the production adapter (against an injected fake fetch) and the stub:

pnpm --filter @gridlock/adapter-telegram test       # 27 / 27 pass
pnpm --filter @gridlock/adapter-telegram typecheck  # tsc --noEmit

Live-bot test recipe (manual)

CI cannot test against a real Telegram bot without a token. To validate locally:

  1. Talk to @BotFather and create a test bot. Save the token.
  2. Send /start to your test bot from your personal Telegram account so the bot has a chat to send to. Note your Telegram user ID (visible via @userinfobot).
  3. Run:
    import { TelegramAdapter } from '@gridlock/adapter-telegram';
    const adapter = new TelegramAdapter({ botToken: process.env.TELEGRAM_BOT_TOKEN! });
    await adapter.send(
      { type: 'dialogue.response', recipientPlayerId: 'p1', sourceNpcId: 'alice',
        content: { kind: 'text', body: 'Hello from gridlock' }, timestamp: new Date() },
      { playerId: 'p1', transport: 'telegram', verified: true, verifiedAt: new Date(),
        optIn: { narrativeMessages: true, systemNotifications: true },
        providerIdentity: { telegramChatId: YOUR_CHAT_ID } },
      { npcId: 'alice', worldId: 'w', transport: 'telegram', channelTier: 'contact',
        persona: { displayName: 'Alice' }, status: 'active', provisionedAt: new Date(),
        providerIdentity: {} },
    );
  4. Confirm the message arrives in your Telegram.

Independence from @tic/*

The package has zero @tic/* imports — verified by inspection. Only dependencies is @gridlock/channel-contract; only devDependencies are @types/node, tsx, typescript.