Third-party request proxy for the Vomit Matrix client: Tenor/Klipy GIF search, Signal and Telegram sticker packs (animated stickers converted to WebP). AGPL-3.0, derived from commetchat/proxy.
  • JavaScript 89.9%
  • Python 8.1%
  • Dockerfile 2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Robocub 4e1e12007c docs: Vommet naming (repo renamed to robocub/vommet-proxy)
The service, its systemd unit and paths on the deployed container keep the
vomit-proxy name; only the repo and docs change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 14:24:58 +00:00
convert feat: convert animated Telegram stickers to animated WebP 2026-10-04 21:21:28 +00:00
deploy feat: convert animated Telegram stickers to animated WebP 2026-10-04 21:21:28 +00:00
fixtures feat: convert animated Telegram stickers to animated WebP 2026-10-04 21:21:28 +00:00
test feat: cap the converted-sticker cache (LRU), harden serving and tests 2026-10-04 21:31:35 +00:00
.env.example feat: cap the converted-sticker cache (LRU), harden serving and tests 2026-10-04 21:31:35 +00:00
.gitignore vomit-proxy: Node port of commetchat/proxy with Telegram sticker routes 2026-10-04 19:25:04 +00:00
docker-compose.yml vomit-proxy: Node port of commetchat/proxy with Telegram sticker routes 2026-10-04 19:25:04 +00:00
Dockerfile feat: convert animated Telegram stickers to animated WebP 2026-10-04 21:21:28 +00:00
LICENSE vomit-proxy: Node port of commetchat/proxy with Telegram sticker routes 2026-10-04 19:25:04 +00:00
package.json docs: Vommet naming (repo renamed to robocub/vommet-proxy) 2026-10-05 14:24:58 +00:00
README.md docs: Vommet naming (repo renamed to robocub/vommet-proxy) 2026-10-05 14:24:58 +00:00
server.js feat: cap the converted-sticker cache (LRU), harden serving and tests 2026-10-04 21:31:35 +00:00

vommet-proxy

Third-party request proxy for Vommet, the LLM-assisted fork of the Commet Matrix client. The client never talks to Tenor, Klipy, Signal or Telegram directly; it talks to this, so API keys and the Telegram bot token stay server-side and the user's IP never reaches those services.

Derived from commetchat/proxy (AGPL-3.0, a Cloudflare Worker), ported to a plain Node 20+ service with no dependencies so it runs in a container. Route shapes are identical to upstream's, so the client's GIF picker and Signal importer work unchanged. The Telegram routes are new. Do not point a client at proxy.commet.chat; that is Commet's infrastructure, not ours.

Routes

Route Upstream Notes
GET /healthz – {"ok":true}
GET /proxy/tenor/api/v2/search?q=… tenor.googleapis.com only q forwarded, key appended
GET /proxy/tenor/media/<path> media.tenor.com passthrough
GET /proxy/klipy/api/v2/search?q=… api.klipy.com only q forwarded, key appended
GET /proxy/klipy/media/<path> static.klipy.com passthrough
GET /proxy/signal/stickers/<pack>/… cdn-ca.signal.org passthrough of encrypted pack data; the client holds the key
GET /proxy/telegram/stickers/<set_name> Bot API getStickerSet normalised JSON, see below
GET /proxy/telegram/media/<file_id> Bot API getFile + download streamed with a real Content-Type
GET /proxy/telegram/media/<file_id>?as=webp same animated stickers converted to animated WebP (cached)

A route whose key or token is not configured answers 503.

Telegram sticker set shape

{
  "name": "FoxPack",
  "title": "Fox Pack",
  "sticker_type": "regular",
  "cover": "/proxy/telegram/media/<file_id>",
  "stickers": [
    { "id": "<file_unique_id>", "file_id": "…", "emoji": "🦊", "width": 512, "height": 512,
      "animated": false, "format": "webp", "source_format": "webp",
      "url": "/proxy/telegram/media/<file_id>" },
    { "id": "…", "file_id": "…", "emoji": "😺", "width": 256, "height": 256,
      "animated": true, "format": "webp", "source_format": "tgs",
      "url": "/proxy/telegram/media/<file_id>?as=webp" }
  ]
}

source_format is Telegram's own format: webp (static), tgs (gzipped Lottie animation) or webm (video sticker); animated is true for the latter two.

When STICKER_CONVERTER is set (the Docker image and the systemd unit both set it), animated stickers are listed as "format": "webp" with a ?as=webp URL, and the proxy converts them to animated WebP on first request: 256 px, at most 20 fps, quality 60 by default (STICKER_SIZE, STICKER_FPS, STICKER_QUALITY). A typical Lottie sticker converts in about a second to roughly 400 KB; video stickers are usually far smaller. Results are cached on disk by Telegram's stable file id, at most CONVERT_CONCURRENCY conversions run at once, concurrent requests for the same sticker share one job, and the cache is capped at CACHE_MAX_MB (default 2048) by evicting the least recently served stickers. Transparency is preserved. Without a converter, animated stickers are listed in their original format and ?as=webp answers 501.

The converter (convert/convert_sticker.py) uses rlottie-python + Pillow for tgs and ffmpeg (libvpx + libwebp) for webm. This is why the proxy is a container rather than a Cloudflare Worker like upstream's.

Run

Without Docker (systemd): see deploy/vomit-proxy.service; the header lists the four commands. The service reads /etc/vomit-proxy.env (mode 600).

With Docker:

cp .env.example .env   # fill in keys
docker compose up -d --build
curl -s localhost:8788/healthz

Publish 127.0.0.1:8788 as https://proxy.nether.im via cloudflared or any reverse proxy. The client's proxy_url preference (default proxy.nether.im in Vommet) must point at that hostname; the client always uses HTTPS.

Develop

node --test           # offline; every upstream is a local stub
PORT=8788 TELEGRAM_BOT_TOKEN=… node server.js

License

AGPL-3.0-only, same as the proxy it is derived from.