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

@ajac-zero/chat-adapter-zulip

v0.1.0

Published

Community Zulip adapter for the Chat SDK (https://chat-sdk.dev)

Downloads

42

Readme

@ajac-zero/chat-adapter-zulip

npm version npm downloads

Community Zulip adapter for the Chat SDK. Connects a Chat SDK bot to a Zulip organization via the outgoing-webhook integration for inbound traffic and the Zulip REST API for outbound traffic.

Status: community-maintained. A merge of this implementation into the official @chat-adapter/* scope is pending — track the upstream proposal at the Chat SDK repository. Once merged officially, this package will be deprecated in favour of @chat-adapter/zulip.

Installation

pnpm add @ajac-zero/chat-adapter-zulip chat

chat is a peer dependency so the bot uses the same Chat instance as every other adapter.

Usage

The adapter auto-detects ZULIP_SITE, ZULIP_BOT_EMAIL, ZULIP_API_KEY, ZULIP_BOT_USERNAME, and ZULIP_WEBHOOK_TOKEN from the environment.

import { Chat } from "chat";
import { createZulipAdapter } from "@ajac-zero/chat-adapter-zulip";

const bot = new Chat({
  userName: "mybot",
  adapters: {
    zulip: createZulipAdapter(),
  },
});

bot.onNewMention(async (thread, message) => {
  await thread.post(`You said: ${message.text}`);
});

Webhook route

import { bot } from "@/lib/bot";

export async function POST(request: Request): Promise<Response> {
  return bot.webhooks.zulip(request);
}

Configure the URL as a Zulip outgoing-webhook bot under Personal settings → Bots → Add a new bot → Outgoing webhook, then point it at https://your-domain.com/api/webhooks/zulip. Copy the bot's webhook token (shown on the bot's settings page — distinct from the bot's API key, even though both look like 32-character tokens) into ZULIP_WEBHOOK_TOKEN.

Configuration

| Option | Required | Description | |--------|----------|-------------| | site | Yes | Zulip organization base URL, e.g. https://example.zulipchat.com. Auto-detected from ZULIP_SITE | | apiKey | Yes | Bot API key. Auto-detected from ZULIP_API_KEY | | botEmail | No | Bot user email, used as the REST API Basic Auth username. Auto-detected from ZULIP_BOT_EMAIL. Falls back to the value reported in the first webhook payload | | webhookToken | No | Shared token embedded in outgoing-webhook payloads. Auto-detected from ZULIP_WEBHOOK_TOKEN. Distinct from apiKey — Zulip generates a separate value when you create an outgoing-webhook bot. When unset, incoming requests are accepted without verification (a warning is logged once) | | userName | No | Bot display name for mention detection. Auto-detected from ZULIP_BOT_USERNAME or /api/v1/users/me | | logger | No | Logger instance (defaults to ConsoleLogger("info")) |

Environment variables

ZULIP_SITE=https://example.zulipchat.com
[email protected]
ZULIP_API_KEY=...
ZULIP_BOT_USERNAME="My Bot"
ZULIP_WEBHOOK_TOKEN=...

Thread IDs

  • Stream topics: zulip:s:{streamId}:{base64UrlTopic}
  • Direct messages: zulip:p:{sortedUserIds}

Topics are base64url-encoded because they may contain colons and other delimiter characters.

Features

Messaging

| Feature | Supported | |---------|-----------| | Post message | Yes | | Edit message | Yes | | Delete message | Yes | | File uploads | No | | Streaming | Post+Edit fallback |

Rich content

| Feature | Supported | |---------|-----------| | Card format | Markdown fallback | | Buttons | No | | Link buttons | No | | Select menus | No | | Tables | GFM | | Fields | Yes | | Images in cards | No | | Modals | No |

Conversations

| Feature | Supported | |---------|-----------| | Slash commands | No | | Mentions | Yes (outgoing-webhook trigger) | | Add reactions | Yes | | Remove reactions | Yes | | Reaction events | No (outgoing webhooks only fire on new messages) | | Typing indicator | Yes | | DMs | Yes | | Ephemeral messages | No |

Message history

| Feature | Supported | |---------|-----------| | Fetch messages | Yes (REST /api/v1/messages) | | Fetch single message | No (planned) | | Fetch thread info | Yes | | Fetch channel info | Yes (streams only) | | List threads | No | | Fetch channel messages | No (planned) | | Post channel message | Use Thread.post |

Notes

  • This adapter intentionally limits its surface to what Zulip's outgoing-webhook integration delivers: new stream messages where the bot is @-mentioned, and DMs addressed to the bot. Reaction events, message edits, and presence updates are not received via the webhook channel. Layer a Zulip event-queue poller on top if you need them.
  • Cards render as Zulip markdown using the SDK's standard cardToFallbackText helper. Buttons and select menus have no Zulip equivalent and are dropped from the rendered output.
  • Bodies are truncated to fit Zulip's 10 000-character per-message limit, with an ellipsis appended.
  • Stream sending uses type: "stream" with the numeric stream id; DMs use type: "direct" with a JSON-encoded list of recipient ids.
  • SDK emoji placeholders ({{emoji:wave}}) are substituted with Unicode emoji before sending so the literal text never leaks into your Zulip channel.

Building from source

pnpm install
pnpm test
pnpm typecheck
pnpm build

License

MIT