{"id":50291467,"url":"https://github.com/nightrunner91/pipebot","last_synced_at":"2026-05-28T06:10:40.174Z","repository":{"id":359411619,"uuid":"1244833582","full_name":"nightrunner91/pipebot","owner":"nightrunner91","description":"Production-ready webhook relay that bridges GitLab CI/CD pipeline events to Telegram group chats.","archived":false,"fork":false,"pushed_at":"2026-05-26T19:10:29.000Z","size":195,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-26T20:22:25.425Z","etag":null,"topics":["expressjs","gitlab","nodejs","telegraf","telegram","telegrambot","telegrambotapi"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nightrunner91.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-20T16:36:10.000Z","updated_at":"2026-05-26T19:10:43.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/nightrunner91/pipebot","commit_stats":null,"previous_names":["nightrunner91/pipeline-alertbot","nightrunner91/pipebot"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/nightrunner91/pipebot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nightrunner91%2Fpipebot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nightrunner91%2Fpipebot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nightrunner91%2Fpipebot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nightrunner91%2Fpipebot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nightrunner91","download_url":"https://codeload.github.com/nightrunner91/pipebot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nightrunner91%2Fpipebot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33596375,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["expressjs","gitlab","nodejs","telegraf","telegram","telegrambot","telegrambotapi"],"created_at":"2026-05-28T06:10:39.597Z","updated_at":"2026-05-28T06:10:40.166Z","avatar_url":"https://github.com/nightrunner91.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# \u003cp align=\"center\"\u003ePipebot\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eReal-time GitLab CI/CD pipeline notifications delivered to Telegram.\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003e\u003cimg src=\"https://flagcdn.com/w20/us.png\" width=\"20\"\u003e English\u003c/strong\u003e | \u003ca href=\"README.ru.md\"\u003e\u003cimg src=\"https://flagcdn.com/w20/ru.png\" width=\"20\"\u003e Russian\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Version-1.2.0-blue?style=for-the-badge\" alt=\"Version\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/License-ISC-green?style=for-the-badge\" alt=\"License\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Node.js-%3E%3D18-6da55f?style=for-the-badge\u0026logo=node.js\" alt=\"Node Version\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Express-5.2.1-000000?style=for-the-badge\u0026logo=express\" alt=\"Express\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Telegraf-4.16.3-2CA5E0?style=for-the-badge\u0026logo=telegram\" alt=\"Telegraf\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/GitLab-FC6D26?style=for-the-badge\u0026logo=gitlab\" alt=\"GitLab\"\u003e\n\u003c/p\u003e\n\n## Table of Contents\n\n- [Project Overview](#project-overview)\n- [Features](#features)\n- [Tech Stack](#tech-stack)\n- [Project Structure](#project-structure)\n- [Getting Started](#getting-started)\n  - [Prerequisites](#prerequisites)\n  - [Installation](#installation)\n  - [Quick Start](#quick-start)\n- [Configuration](#configuration)\n  - [Environment Variables](#environment-variables)\n  - [Multi-Repository Mode](#multi-repository-mode)\n  - [Notification Rules](#notification-rules)\n  - [Deploy Links](#deploy-links)\n  - [Alert Styles](#alert-styles)\n  - [Config File Tool](#config-file-tool)\n- [GitLab Webhook Setup](#gitlab-webhook-setup)\n- [Deployment](#deployment)\n- [Usage](#usage)\n  - [Running Locally](#running-locally)\n  - [Webhook Endpoint](#webhook-endpoint)\n  - [Diagnostics Endpoint](#diagnostics-endpoint)\n- [Testing](#testing)\n- [Development](#development)\n- [License](#license)\n\n## Project Overview\n\n**pipebot** is a lightweight, production-ready webhook relay that bridges GitLab CI/CD pipeline events to Telegram group chats. It receives pipeline webhooks from GitLab, formats them into readable notifications, and sends them to designated Telegram chats in real time. The bot supports **multi-repository mode**, allowing you to monitor multiple GitLab projects and route alerts to different Telegram groups. \n\n## Features\n\n- **Multi-repository support** - Monitor many GitLab projects and route alerts to different Telegram chats\n- **Stage-aware notifications** - Per-job alerts with stage name headers and configurable filtering via `notifyRules`\n- **Job-level webhooks** - Receives individual job events from GitLab for real-time per-stage notifications without state tracking\n- **Customizable alerts** - Per-repo display names, three message styles (card, tree, minimal), and custom deploy link buttons\n- **Inline keyboard** - Quick-access buttons for pipeline, commit, and repository links in every notification\n- **Secure \u0026 production-ready** - Timing-safe webhook validation, rate limiting, security headers, payload size limits, structured logging with sensitive data sanitization\n- **Flexible deployment** - Configurable port with automatic fallback, bot runs via long polling alongside the Express webhook server\n- **Diagnostics endpoint** - Health check endpoint that reports configuration status without exposing secrets\n- **Zero-downtime startup** - Bot initializes independently from the webhook server; missing credentials do not block server startup\n\n## Tech Stack\n\n| Technology | Purpose |\n|---|---|\n| [Node.js](https://nodejs.org/) \u003e= 18 | JavaScript runtime |\n| [Express 5](https://expressjs.com/) | Webhook HTTP server |\n| [Telegraf](https://telegraf.js.org/) | Telegram Bot API client |\n| [dotenv](https://github.com/motdotla/dotenv) | Environment variable loading |\n\n## Project Structure\n\n```\npipebot/\n├── src/\n│   ├── index.js              # Entry point: config, initialization, multi-port startup\n│   ├── bot/\n│   │   └── index.js          # Telegraf bot creation and message dispatch\n│   ├── server/\n│   │   └── index.js          # Express server: routes, webhook validation, payload routing, rate limiting\n│   ├── services/\n│   │   ├── gitlab.js         # GitLab payload formatting entry point\n│   │   └── message-builder.js # Message templates (card, tree, minimal) and HTML escaping\n│   └── utils/\n│       ├── logger.js         # Structured JSON logging (INFO/ERROR) with sensitive data sanitization\n│       ├── repo-config.js    # Multi-repository config parser, routing, and notification filtering\n│       └── pipeline-state.js # Stage transition detection (legacy pipeline webhook support)\n├── config/\n│   └── repos.config.js       # Readable repository configuration (not committed)\n├── scripts/\n│   └── build-config.js       # CLI tool to generate env-ready REPOSITORY_CONFIG\n├── test/\n│   ├── fixtures/             # Sample GitLab webhook payloads\n│   │   ├── pipeline-running.json\n│   │   ├── pipeline-success.json\n│   │   ├── pipeline-failed.json\n│   │   ├── pipeline-canceled.json\n│   │   ├── job-running.json\n│   │   ├── job-success.json\n│   │   ├── job-failed.json\n│   │   └── job-canceled.json\n│   ├── test-unit.js          # Unit tests for message formatting\n│   ├── test-repo-config.js   # Multi-repository config tests\n│   ├── test-pipeline-state.js # Pipeline state tracking tests\n│   ├── test-webhooks.js      # Webhook integration tests\n│   ├── test-security.js      # Security validation tests\n│   ├── test-telegram.js      # Telegram bot tests\n│   └── test-visual.js        # Visual output tests\n├── index.js                  # Root entry point (re-exports src/index.js)\n├── package.json              # Dependencies and npm scripts\n└── .env                      # Environment variables (not committed)\n```\n\n## Getting Started\n\n### Prerequisites\n\n- Node.js \u003e= 18\n- npm (bundled with Node.js)\n- A Telegram bot token from [@BotFather](https://t.me/botfather)\n- One or more Telegram group/channel IDs for notifications\n- Access to GitLab project webhook settings\n\n### Installation\n\n1. Clone the repository:\n\n   ```bash\n   git clone https://github.com/your-org/pipebot.git\n   cd pipebot\n   ```\n\n2. Install dependencies:\n\n   ```bash\n   npm install\n   ```\n\n3. Create a `.env` file:\n\n   ```bash\n   cp .env.example .env   # or create .env manually\n   ```\n\n### Quick Start\n\n1. Fill in your `.env` file with the required credentials (see [Configuration](#configuration)).\n2. Start the application:\n\n   ```bash\n   npm start\n   ```\n\n3. Configure GitLab webhooks pointing to your server URL (see [GitLab Webhook Setup](#gitlab-webhook-setup)).\n4. Trigger a pipeline in GitLab and watch the notification appear in Telegram.\n\n## Configuration\n\n### Environment Variables\n\nCreate a `.env` file in the project root with the following variables:\n\n| Variable | Required | Default | Description |\n|---|---|---|---|\n| `TELEGRAM_BOT_TOKEN` | Yes | -- | Bot token obtained from [@BotFather](https://t.me/botfather) |\n| `REPOSITORY_CONFIG` | Yes | -- | JSON array mapping GitLab projects to Telegram chats (see below) |\n| `ALERT_STYLE` | No | `card` | Global fallback message format: `card`, `tree`, or `minimal` |\n| `PORT` | No | `3000` | Primary port for the webhook server |\n| `WEBHOOK_MODE` | No | `both` | Event type to process: `both`, `jobs` (job events only), or `pipeline` (pipeline events only) |\n\n\u003e [!IMPORTANT]\n\u003e Never commit your `.env` file. It is listed in `.gitignore` and should only exist locally or in your deployment platform's secret store.\n\n### Multi-Repository Mode\n\nThe `REPOSITORY_CONFIG` variable defines which GitLab projects to monitor and where to send their alerts. It is a JSON array of repository entries:\n\n```json\n[\n  {\n    \"projectId\": 123,\n    \"projectName\": \"My Awesome API\",\n    \"chatId\": \"-1001234567890\",\n    \"secret\": \"webhook-secret-for-repo-1\",\n    \"style\": \"card\"\n  },\n  {\n    \"projectId\": 456,\n    \"projectName\": \"Frontend App\",\n    \"chatId\": \"-1009876543210\",\n    \"secret\": \"webhook-secret-for-repo-2\",\n    \"style\": \"tree\"\n  }\n]\n```\n\n**Fields per repository entry:**\n\n| Field | Required | Description |\n|---|---|---|\n| `projectId` | Yes | GitLab project ID (numeric, found in Settings \u003e General) |\n| `projectName` | No | Custom display name for alerts (overrides GitLab project name) |\n| `chatId` | Yes | Telegram chat/group ID to send alerts to |\n| `secret` | No | Webhook secret token (must match GitLab webhook settings) |\n| `style` | No | Alert style: `card`, `tree`, or `minimal` (falls back to `ALERT_STYLE`) |\n| `notifyRules` | No | Per-stage notification filtering rules (see [Notification Rules](#notification-rules)) |\n| `deployLinks` | No | Per-stage custom action buttons for notifications (see [Deploy Links](#deploy-links)) |\n\nIn your `.env` file, the JSON must be on a **single line**:\n\n```env\nREPOSITORY_CONFIG=[{\"projectId\":123,\"projectName\":\"My API\",\"chatId\":\"-1001234567890\",\"secret\":\"secret1\",\"style\":\"card\"},{\"projectId\":456,\"projectName\":\"Frontend\",\"chatId\":\"-1009876543210\",\"secret\":\"secret2\",\"style\":\"tree\"}]\n```\n\n### Notification Rules\n\nThe `notifyRules` field lets you control which pipeline statuses trigger notifications for each stage. This is useful for reducing noise -- for example, only getting alerts for failed tests or successful deployments.\n\n```json\n{\n  \"projectId\": 123,\n  \"projectName\": \"My API\",\n  \"chatId\": \"-1001234567890\",\n  \"notifyRules\": {\n    \"build\":  { \"send\": [\"success\", \"failed\", \"running\"], \"ignore\": [\"canceled\", \"pending\", \"manual\"] },\n    \"deploy\": { \"send\": [\"success\", \"failed\"], \"ignore\": [] },\n    \"test\":   { \"send\": [\"failed\"], \"ignore\": [] }\n  }\n}\n```\n\n**How it works:**\n\n- `send` is a **whitelist** -- only these statuses trigger a notification for that stage.\n- `ignore` is a **blacklist** -- these statuses are skipped even if they would otherwise be sent.\n- If a stage is **not listed** in `notifyRules`, all its events are sent (backward compatible).\n- If `notifyRules` is **not present** at all, all events are sent (default behavior).\n- `send` **takes priority**: if a status is in both `send` and `ignore`, it is sent.\n- Valid statuses: `success`, `failed`, `running`, `canceled`, `pending`, `manual`.\n- The stage name is extracted from the GitLab payload (`builds[].stage`, `detailed_status.context`, or `stages`). If the stage cannot be determined, it is treated as `\"unknown\"` in `repo-config.js` or `\"pipeline\"` in `message-builder.js` -- if `\"unknown\"` is not in `notifyRules`, the event is sent.\n\n### Deploy Links\n\nThe `deployLinks` field adds a custom full-width button at the bottom of the notification keyboard. Each stage can have its own link with a custom name:\n\n```json\n{\n  \"projectId\": 123,\n  \"projectName\": \"My API\",\n  \"chatId\": \"-1001234567890\",\n  \"deployLinks\": {\n    \"build\":  { \"url\": \"\", \"name\": \"\" },\n    \"deploy\": { \"url\": \"https://my-api.example.com\", \"name\": \"Open Site\" },\n    \"test\":   { \"url\": \"https://my-api.example.com/swagger\", \"name\": \"View Swagger\" }\n  }\n}\n```\n\n- Each stage can have its own link with `url` and `name`.\n- Links are optional -- if a stage has no `url`, no button is shown for it.\n- The button appears on its own row at the very bottom, after the Pipeline/Commit/View repository buttons.\n- If `deployLinks` is not configured for the repo or the current stage, behavior is unchanged.\n\n### Alert Styles\n\nThe `style` field (per repo) or `ALERT_STYLE` (global fallback) controls how pipeline notifications appear in Telegram:\n\n| Style | Description |\n|---|---|\n| `card` | Clean card layout with bold labels and monospaced values.|\n| `tree` | Structured list with tree-style connectors (`├─` / `└─`).|\n| `minimal` | Compact format with inline badges separated by label.|\n\nAll styles include an inline keyboard with buttons linking to the pipeline, commit, and repository. When `deployLinks` is configured, an additional custom button appears at the bottom.\n\n**Stage-aware headers:** All notification styles include the pipeline stage name in the header line:\n\n```\n🔄 Running [build]\n❌ Failed [deploy]\n✅ Passed [test]\n```\n\nThe stage name is extracted from the GitLab payload (`builds[].stage`, `detailed_status.context`, or `stages`). If none are available, it falls back to `[pipeline]`.\n\n### Config File Tool\n\nInstead of writing inline JSON in `.env`, you can use the config file tool for a cleaner workflow:\n\n1. Edit `config/repos.config.js` -- a readable JS object format for all your repositories.\n2. Generate the env-ready JSON:\n\n   ```bash\n   # Print JSON to stdout\n   npm run config:build\n\n   # Print in REPOSITORY_CONFIG= format\n   npm run config:build -- --env\n\n   # Write directly to .env\n   npm run config:write\n   ```\n\n### GitLab Webhook Setup\n\nConfigure a webhook **for each repository** you want to monitor:\n\n1. Open your GitLab project and navigate to **Settings \u003e Webhooks**.\n2. Set the **URL** to your deployed endpoint:\n\n   ```\n   https://your-domain.com/api/webhook/gitlab\n   ```\n\n3. Set the **Secret token** to the `secret` value from the corresponding entry in `REPOSITORY_CONFIG`.\n4. Under **Trigger**, check **Job events** (recommended) or **Pipeline events** (legacy support).\n5. Keep **Enable SSL verification** enabled for production deployments.\n6. Click **Add webhook** and test using **Test \u003e Job events** (or **Pipeline events** if using legacy mode).\n\n\u003e [!NOTE]\n\u003e All repositories use the same webhook endpoint. The bot routes alerts to the correct Telegram chat based on the `projectId` in the incoming payload.\n\u003e\n\u003e **Job events** (recommended): Each job status change triggers a separate webhook. Notifications are sent per-job with the stage name in the header. No state tracking needed.\n\u003e\n\u003e **Pipeline events** (legacy): The bot still supports pipeline webhooks for backward compatibility. Stage transitions are detected from the `builds[]` array.\n\u003e\n\u003e The bot processes events with statuses: `running`, `success`, `failed`, and `canceled`. Other events are acknowledged but ignored.\n\n### Deployment\n\nThe application can be deployed to any platform that supports Node.js (Railway, Render, Fly.io, Docker, VPS, etc.):\n\n1. Set the required environment variables in your platform's configuration:\n   - `TELEGRAM_BOT_TOKEN` -- your bot token\n   - `REPOSITORY_CONFIG` -- single-line JSON array of repository mappings\n   - `ALERT_STYLE` -- optional global fallback style (default: `card`)\n   - `PORT` -- the port your platform assigns (default: `3000`)\n2. Set the start command to:\n\n   ```bash\n   node src/index.js\n   ```\n\n3. Deploy. The server will bind to `0.0.0.0` on the configured port.\n\n\u003e [!NOTE]\n\u003e Make sure your deployment platform exposes the webhook endpoint publicly over HTTPS. GitLab requires SSL verification for webhooks in production.\n\n## Usage\n\n### Running Locally\n\n```bash\nnpm start\n```\n\nExpected output:\n\n```json\n{\"level\":\"INFO\",\"timestamp\":\"2026-05-22T12:00:00.000Z\",\"message\":\"Telegram bot initialized successfully\",\"context\":{}}\n{\"level\":\"INFO\",\"timestamp\":\"2026-05-22T12:00:00.000Z\",\"message\":\"Loaded 2 repository configuration(s)\",\"context\":{}}\n{\"level\":\"INFO\",\"timestamp\":\"2026-05-22T12:00:00.000Z\",\"message\":\"Starting webhook server...\",\"context\":{}}\n{\"level\":\"INFO\",\"timestamp\":\"2026-05-22T12:00:00.000Z\",\"message\":\"Webhook server listening on 0.0.0.0:3000\",\"context\":{}}\n```\n\n### Webhook Endpoint\n\n- **URL**: `POST /api/webhook/gitlab`\n- **Headers**: `X-Gitlab-Token: \u003crepo-secret\u003e`\n- **Body**: GitLab job event or pipeline event payload (JSON, max 50kb)\n- **Response**: `200 OK` on success, `400 Bad Request` if payload is invalid JSON, `401 Unauthorized` if the secret does not match, `404` if no repository config matches the project, `413 Payload Too Large` if payload exceeds 50kb, `429 Too Many Requests` if rate limit exceeded, `500 Internal Server Error` on processing error, `503 Service Unavailable` if bot is not initialized\n\n### Diagnostics Endpoint\n\n- **URL**: `GET /`\n- **Response**: JSON object with server status and timestamp\n\n```json\n{\n  \"status\": \"running\",\n  \"timestamp\": \"2026-05-26T20:53:57.063Z\"\n}\n```\n\n## Testing\n\nRun the full test suite:\n\n```bash\nnpm test\n```\n\nThis executes five test suites in sequence:\n\n| Script | Description |\n|---|---|\n| `npm run test:unit` | Unit tests for message formatting, duration parsing, and stage formatting |\n| `npm run test:repo-config` | Multi-repository config parsing and routing tests |\n| `npm run test:pipeline-state` | Pipeline state tracking and stage transition detection tests |\n| `npm run test:webhooks` | Webhook integration tests with sample payloads |\n| `npm run test:security` | Security validation tests (token verification, input sanitization) |\n\nTests use fixture files in `test/fixtures/` that simulate real GitLab job and pipeline event payloads.\n\n## Development\n\n### Available Scripts\n\n| Command | Description |\n|---|---|\n| `npm start` | Start the application |\n| `npm test` | Run all tests |\n| `npm run test:unit` | Run unit tests only |\n| `npm run test:repo-config` | Run repository config tests only |\n| `npm run test:pipeline-state` | Run pipeline state tests only |\n| `npm run test:webhooks` | Run webhook tests only |\n| `npm run test:security` | Run security tests only |\n| `npm run test:telegram` | Run Telegram bot tests only |\n| `npm run config:build` | Print env-ready REPOSITORY_CONFIG JSON to stdout |\n| `npm run config:write` | Write REPOSITORY_CONFIG to .env |\n\n### Adding a New Alert Style\n\n1. Open `src/services/message-builder.js`.\n2. Create a new formatter function (e.g., `formatCompactStyle(data)`).\n3. Add a case in the `buildMessage()` switch statement.\n4. Add corresponding tests in `test/test-unit.js` using an existing or new fixture.\n5. Run `npm test` to verify.\n\n### Logging\n\nThe application uses structured JSON logging via `src/utils/logger.js`. All log entries include a `level`, `timestamp`, `message`, and optional `context`. Errors additionally include `error` and `stack` fields. Sensitive data (tokens, secrets, passwords) is automatically sanitized in logs. Logs are written to `stdout` (INFO) and `stderr` (ERROR) for easy integration with log aggregators.\n\n## License\n\nThis project is licensed under the [ISC License](LICENSE).\n\n---\n\n\u003cp align=\"center\"\u003e\n  Built with ❤ by \u003ca href=\"https://t.me/nightrunner91\"\u003enightrunner91\u003c/a\u003e\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnightrunner91%2Fpipebot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnightrunner91%2Fpipebot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnightrunner91%2Fpipebot/lists"}