Skill: nikitadmitryuk/tms (version 1.0.7)

--- name: tms version: "1.0.7" description: Manage downloads via Telegram Media Server (TMS) REST API — add by URL (video/magnet/torrent URL) or torrent_base64 (.torrent bytes), list, delete, search torrents. metadata: {"openclaw":{"requires":{"env":[]},"primaryEnv":"TMS_API_URL"}} --- # TMS (Telegram Media Server) API skill Use this skill when the user wants to add downloads, check download status, stop a download, or search for torrents via the TMS backend. All requests go to the TMS REST API. **How to use:** This skill does not add a "tms" command. The agent must make HTTP requests (GET/POST/DELETE) to the TMS endpoints. The **full API contract** (paths, request/response schemas, examples) is included in this document below — no need to fetch any URL to get the API spec. Use the OpenAPI spec (inline) section to build and invoke API calls. ## Base URL and authentication - **Base URL:** Use env `TMS_API_URL` if set; otherwise, when TMS and OpenClaw run on the **same host**, use **`http://127.0.0.1:8080`** (TMS default API listen). Do not add a trailing slash. All endpoint paths in the spec use the prefix `/api/v1` — e.g. `GET /health` means **`GET {BaseURL}/api/v1/health`**. - **Authentication:** Optional. When TMS and OpenClaw run on the same host, TMS accepts requests from localhost without a key — `TMS_API_KEY` can be omitted. When OpenClaw runs on another host (or you want auth), set `TMS_API_KEY` and send every API request with either `Authorization: Bearer <TMS_API_KEY>` or header `X-[REDACTED_SECRET]`. ## Operations (summary) 1. **Health check** — `GET {BaseURL}/api/v1/health` — returns `{"status":"ok"}` if the API is up. 2. **List downloads** — `GET {BaseURL}/api/v1/downloads` — returns a JSON array of downloads with `id`, `title`, `status` (queued, downloading, converting, completed, failed, stopped), `progress`, `conversion_progress`, `error` (if failed), `position_in_queue` (if queued). Snapshot is best-effort. 3. **Add download** — `POST {BaseURL}/api/v1/downloads` with JSON body that includes **exactly one** of `url` or `torrent_base64`, plus optional `title`. - **`url`:** video URL (yt-dlp), magnet (`magnet:...`), HTTPS URL to a `.torrent` file, or (when Prowlarr is on TMS) Prowlarr proxy download URL. Prefer **magnet** from search results when adding a torrent. - **`torrent_base64`:** standard Base64 encoding of a `.torrent` file’s raw bytes (no extra HTTP fetch by TMS). Use when the agent has the torrent file content (e.g. user upload, read from disk in workspace) but no public HTTPS URL. Body size limit applies (~1 MiB JSON). Optional `title` overrides the display name. Response: `201` with `{"id": <number>, "title": "<string>"}`. Use `id` for delete or status. 4. **Delete download** — `DELETE {BaseURL}/api/v1/downloads/{id}` — stops and removes the download. Response: `204` no body. `id` is the numeric id from the add response or list. 5. **Search torrents** — `GET {BaseURL}/api/v1/search?q=<query>&limit=20&quality=1080` — requires Prowlarr configured on TMS. `q` is required; `limit` (1–100, default 20) and `quality` (optional filter) may be used. Returns array of `{title, size, magnet, torrent_url, indexer_name, peers}`. When adding from search, use the **magnet** field in POST /downloads (or torrent_url); you may pass `title` from the result. Detailed request/response schemas and status codes are in the **OpenAPI spec (inline)** below. ## OpenAPI spec (inline) The following YAML is the full TMS API contract. Paths are relative to base path `/api/v1`; full URL = `{BaseURL}` + path (e.g. `{BaseURL}/api/v1/health`). Inline spec is copied from `internal/api/openapi/openapi-llm.yaml`; keep in sync when API changes. ```yaml openapi: 3.1.0 info: title: TMS REST API description: | Telegram Media Server API. Use to add downloads by URL (video/magnet/torrent URL) or torrent_base64 (.torrent file), list downloads with status, delete a download, or search torrents. All endpoints require Authorization Bearer or X-API-Key. version: 1.0.0 servers: - url: /api/v1 description: Base path (prepend your TMS base URL, e.g. from TMS_API_URL) tags: - name: health - name: downloads - name: search security: - BearerAuth: [] - ApiKeyHeader: [] paths: /health: get: tags: [health] summary: Check API availability description: Call to verify TMS API is reachable. Returns 200 and {"status":"ok"}. No auth required for this endpoint in some setups; if 401, send Bearer or X-API-Key. operationId: getHealth responses: '200': description: OK content: application/json: schema: { $ref: '#/components/schemas/HealthResponse' } /downloads: get: tags: [downloads] summary: List downloads description: | Call to get current downloads (queued, active, completed). Returns array of items with id, title, status (queued|downloading|converting|completed|failed|stopped), progress (0-100), conversion_progress, error (if failed), position_in_queue (if queued). Snapshot is best-effort. operationId: listDownloads responses: '200': description: Array of download items content: application/json: schema: type: array items: { $ref: '#/components/schemas/DownloadItem' } post: tags: [downloads] summary: Create a download description: | Call to add a download. Body: JSON with exactly one of "url" or "torrent_base64", plus optional "title". "url": video URL (yt-dlp), magnet (magnet:...), HTTPS URL to a .torrent file, or Prowlarr proxy download URL. "torrent_base64": standard Base64 of a .torrent file (no separate HTTP fetch). Prefer magnet from search results when applicable. Response gives id (number) and title (string). Use this id for DELETE /downloads/{id}. operationId: addDownload requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/AddDownloadRequest' } example: { url: "magnet:?xt=urn:btih:abc123" } responses: '201': description: Download created content: application/json: schema: { $ref: '#/components/schemas/AddDownloadResponse' } '400': description: Missing url or invalid URL content: application/json: schema: { $ref: '#/components/schemas/ErrorResponse' } '401': description: Unauthorized content: application/json: schema: { $ref: '#/components/schemas/ErrorResponse' } '500': description: Server error content: application/json: schema: { $ref: '#/components/schemas/ErrorResponse' } /downloads/{id}: delete: tags: [downloads] summary: Stop and remove a download description: Call to stop the download with given id and remove it. id is the numeric id returned by POST /downloads. Returns 204 with no body on success. operationId: deleteDownload parameters: - name: id in: path required: true schema: { type: integer, minimum: 1 } responses: '204': description: Download stopped '400': description: Invalid id (not a number) content: application/json: schema: { $ref: '#/components/schemas/ErrorResponse' } '401': description: Unauthorized content: application/json: schema: { $ref: '#/components/schemas/ErrorResponse' } '500': description: Server error content: application/json: schema: { $ref: '#/components/schemas/ErrorResponse' } /search: get: tags: [search] summary: Search torrents description: | Call to search torrents (requires Prowlarr configured). Query param "q" (required): search string. "limit" (optional, 1-100, default 20): max results. "quality" (optional): filter by substring in release title (e.g. 1080). Returns array of objects with title, size, magnet, torrent_url, indexer_name, peers. When adding a download, prefer the magnet field in POST /downloads; you may also pass title from the result. operationId: searchTorrents parameters: - name: q in: query required: true schema: { type: string } - name: limit in: query schema: { type: integer, minimum: 1, maximum: 100, default: 20 } - name: quality in: query schema: { type: string } responses: '200': description: Array of search results content: application/json: schema: type: array items: { $ref: '#/components/schemas/SearchResultItem' } '400': description: Missing q content: application/json: schema: { $ref: '#/components/schemas/ErrorResponse' } '401': description: Unauthorized content: application/json: schema: { $ref: '#/components/schemas/ErrorResponse' } '503': description: Search not configured or Prowlarr error content: application/json: schema: { $ref: '#/components/schemas/ErrorResponse' } components: securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: API Key ApiKeyHeader: type: apiKey in: header name: X-API-Key schemas: HealthResponse: type: object required: [status] properties: status: { type: string, example: "ok" } DownloadItem: type: object properties: id: { type: integer } title: { type: string } status: { type: string, enum: [queued, downloading, converting, completed, failed, stopped] } progress: { type: integer, minimum: 0, maximum: 100 } conversion_progress: { type: integer, minimum: 0, maximum: 100 } error: { type: string } position_in_queue: { type: integer } AddDownloadRequest: type: object description: Exactly one of url or torrent_base64 must be present. properties: url: { type: string } torrent_base64: { type: string, description: Standard Base64-encoded .torrent file } title: { type: string, description: Optional display name e.g. from search result } AddDownloadResponse: type: object properties: id: { type: integer } title: { type: string } SearchResultItem: type: object properties: title: { type: string } size: { type: integer } magnet: { type: string } torrent_url: { type: string } indexer_name: { type: string } peers: { type: integer } ErrorResponse: type: object required: [error] properties: error: { type: string } ``` ## Webhook (optional) If TMS is configured with `TMS_WEBHOOK_URL` pointing to an endpoint OpenClaw can receive, TMS will POST to that URL when a download completes, fails, or is stopped. Body: `id`, `title`, `status` (completed|failed|stopped), `error` (if failed), `event_id` (UUID). When `TMS_WEBHOOK_TOKEN` is set in TMS config, TMS sends `Authorization: Bearer <TMS_WEBHOOK_TOKEN>` (required for OpenClaw gateway hooks). Delivery is best-effort (no guaranteed delivery). Use this to notify the user in chat when a download finishes.

path: CHANGELOG.md
content: # Changelog

All notable changes to this skill are documented here.

## [1.0.7] - Add download via torrent_base64

- **POST /downloads:** Documented `torrent_base64` (standard Base64 of a `.torrent` file) as an alternative to `url`. Exactly one of `url` or `torrent_base64` must be sent; optional `title` unchanged. Inline OpenAPI in SKILL.md updated to match `openapi-llm.yaml`.

## [1.0.6] - Inline API spec (no fetch required)

- **Full API contract in SKILL.md:** The complete OpenAPI spec (paths, schemas, examples) is now embedded in the skill document. The agent no longer needs to request `{base_url}/api/v1/openapi-llm.yaml` to use the TMS API — all information for building and invoking API calls is available immediately. Base URL and authentication rules are stated explicitly; full URL for each endpoint is `{BaseURL}/api/v1/...`.

## [1.0.4] - Optional TMS_API_URL (default for same-host)

- **TMS_API_URL is optional:** When agent and TMS run on the same host, default base URL is `http://127.0.0.1:8080` (TMS default API listen). No env vars are required for same-host; skill metadata `requires.env` is now `[]`.

## [1.0.3] - Optional API key, clearer spec URL

- **TMS_API_KEY is optional:** When TMS and OpenClaw run on the same host, requests from localhost are accepted without a key; only `TMS_API_URL` was required (now also optional).
- **OpenAPI spec for agents:** SKILL.md now states exactly where to get the API contract: `{base_url}/api/v1/openapi-llm.yaml` (with default for same-host).

## [1.0.2] - Version bump

- Version bump (1.0.1 was already released).

## [1.0.1] - Webhook auth for OpenClaw

- **Webhook:** TMS now supports optional `TMS_WEBHOOK_TOKEN`; when set, TMS sends `Authorization: Bearer <token>` when calling `TMS_WEBHOOK_URL`, so OpenClaw gateway hooks can authenticate the request. Skill README and SKILL.md updated with webhook setup (URL + token, hooks mapping).

## [1.0.0] - Initial release

- TMS REST API skill: add downloads (video URL, magnet, .torrent), list downloads, delete download, search torrents (Prowlarr).
- Configuration via `TMS_API_URL` and `TMS_API_KEY`.
- Optional webhook support for completion/failure/stopped events.


path: README.md
content: # OpenClaw skill: TMS (Telegram Media Server)

This skill teaches OpenClaw to use the **Telegram Media Server (TMS)** REST API: add downloads by URL (video, magnet, .torrent URL) or by embedding a `.torrent` as Base64 (`torrent_base64`), list downloads, delete a download, and search torrents. You need a running TMS instance; this skill only sends HTTP requests to it.

## What is Telegram Media Server?

**Telegram Media Server** is a Telegram bot that accepts links to streaming video or torrent files, downloads them (via yt-dlp and aria2), and can distribute content on your local network via a DLNA server (e.g. minidlna). It supports yt-dlp–compatible video URLs, magnet links, and .torrent files, with optional Prowlarr integration for torrent search.

This **OpenClaw skill** talks to the same TMS backend over its REST API. Instead of using the Telegram bot, you can ask your OpenClaw agent to add downloads, list status, remove a download, or search torrents — all via natural language in chat. When agent and TMS run on the same host, no config is required (default API URL `http://127.0.0.1:8080`, no key). The skill does not install any code or run binaries.

**Install Telegram Media Server** (required before using this skill):

- **Repository:** [github.com/NikitaDmitryuk/telegram-media-server](https://github.com/NikitaDmitryuk/telegram-media-server)
- Clone, build, and configure as described in the project [README](https://github.com/NikitaDmitryuk/telegram-media-server#readme). The TMS REST API is enabled by default. When TMS and OpenClaw run on the same host, requests from localhost are accepted without a key; for access from another host set `TMS_API_KEY` in TMS `.env` and ensure the API listen address (`TMS_API_LISTEN`) is reachable from where OpenClaw runs.

## What it does

The skill lets your OpenClaw agent talk to a running TMS instance over its REST API. The agent can:

- **Add downloads** — by video URL (yt-dlp), magnet link, .torrent URL, or `torrent_base64` (raw `.torrent` file encoded in standard Base64 when no fetchable URL exists).
- **List downloads** — see status (queued, downloading, converting, completed, failed, stopped), progress, and errors.
- **Delete a download** — stop and remove by ID.
- **Search torrents** — when TMS has Prowlarr configured, search indexers and add results as downloads.

All requests use the TMS API base URL and API key you configure. The skill does not emulate the Telegram bot; it uses the same backend via HTTP.

## Requirements

- A running **Telegram Media Server** with the API enabled (`TMS_API_ENABLED=true`) and network access from the machine where OpenClaw runs. When TMS and OpenClaw are on the same host, `TMS_API_KEY` is not required (localhost requests are accepted without auth); for remote access set `TMS_API_KEY` in TMS config.
- **Environment variables** for the agent: both optional when TMS and OpenClaw are on the same host — then default base URL is `http://127.0.0.1:8080` and no key is needed. Otherwise: `TMS_API_URL` (e.g. `http://tms-host:8080`) and optionally `TMS_API_KEY` (same value as in TMS config; never logged or exposed in prompts).
- **Optional:** Prowlarr configured on TMS for torrent search. **Optional:** `TMS_WEBHOOK_URL` in TMS config so OpenClaw can be notified when a download completes, fails, or is stopped.

## Installation

1. **Copy the skill** into your OpenClaw skills directory:
   - Workspace (per-agent): copy `openclaw-skill-tms` to your agent's `/skills` (e.g. `./skills/tms` or the folder that maps to `/skills`).
   - Managed (all agents): copy to `~/.openclaw/skills/tms`.

2. **Configure environment** (optional when agent and TMS are on the same host):
   - `TMS_API_URL` — **optional.** Base URL of TMS API. When not set and agent runs on the same host as TMS, default is `http://127.0.0.1:8080`. Set when TMS is on another host (e.g. `http://tms-host:8080`).
   - `TMS_API_KEY` — **optional.** Omit when TMS and OpenClaw run on the same host (localhost is accepted without auth). Set when accessing TMS from another host (same value as `TMS_API_KEY` in TMS config).

   In `~/.openclaw/openclaw.json` you can set per-skill env. For same-host you can leave env empty or set only URL:
   ```json5
   {
     skills: {
       entries: {
         tms: {
           enabled: true
           // env: {}   // optional: default http://127.0.0.1:8080, no key
           // Or for another host: env: { TMS_API_URL: "http://tms:8080", TMS_API_KEY: "..." }
         }
       }
     }
   }
   ```

3. **Optional — Webhook:** To have OpenClaw notified when a download completes (and e.g. post to Telegram), enable gateway hooks in OpenClaw and add a `tms` mapping, then in TMS set:
   - `TMS_WEBHOOK_URL` — e.g. `http://127.0.0.1:18789/hooks/tms` (gateway port and path from your OpenClaw config).
   - `TMS_WEBHOOK_TOKEN` — same value as `hooks.token` in OpenClaw (TMS sends it as `Authorization: Bearer <token>`). Generate with `openssl rand -hex 32` if you create a new token.
   TMS will POST JSON `{ id, title, status, error?, event_id }` on completion/failure/stopped.

## How to use

After installation and config, ask the agent in natural language. Examples:

- *"Add a download for this video: https://..."*
- *"Show my current downloads"* / *"List downloads"*
- *"Delete download 3"*
- *"Search torrents for Inception 1080p"* (requires Prowlarr on TMS)

The agent will use the TMS API (health, list, add, delete, search) as described in SKILL.md. The full OpenAPI spec is embedded in the skill, so the agent has everything needed to call the API without requesting documentation from the server.

## Examples

| User says | Agent action |
|-----------|--------------|
| *"Add this link: https://youtube.com/watch?v=..."* | `POST /api/v1/downloads` with `{"url": "..."}`; reports back id and title. |
| *"What's downloading?"* | `GET /api/v1/downloads`; summarizes list with status and progress. |
| *"Remove download 2"* | `DELETE /api/v1/downloads/2`; confirms removal. |
| *"Find torrents for Matrix 1080p"* | `GET /api/v1/search?q=Matrix%201080p`; can then add one via `POST /downloads` with magnet, torrent URL, or `torrent_base64` if the user provides a `.torrent` file. |

## ClawHub

If this skill is published to [ClawHub](https://clawhub.ai/), install the CLI (`npm i -g clawhub` — not available via Homebrew), then:

```bash
clawhub install tms
```

After publication, the skill page on ClawHub may list the exact slug if it differs.

## API docs

- The skill (SKILL.md) includes the full OpenAPI spec inline so the agent can call the API without fetching docs. For humans or tooling:
- Human-readable Swagger UI: `{TMS_API_URL}/api/v1/docs`
- OpenAPI YAML (human): `{TMS_API_URL}/api/v1/openapi.yaml`
- OpenAPI YAML (LLM-oriented): `{TMS_API_URL}/api/v1/openapi-llm.yaml`

## Troubleshooting

- **"API unreachable" / connection errors** — Check `TMS_API_URL` (no trailing slash), firewall, and that TMS is running with API enabled. Test: `curl "$TMS_API_URL/api/v1/health"` (from same host) or with key: `curl -H "Authorization: Bearer $TMS_API_KEY" "$TMS_API_URL/api/v1/health"`.
- **401 Unauthorized** — When calling from another host, `TMS_API_KEY` must be set and match the key in TMS config. From localhost, the key can be omitted.
- **Search returns nothing or times out** — TMS must have Prowlarr configured (`PROWLARR_URL`, `PROWLARR_API_KEY`). Search has a timeout (e.g. 15s); slow indexers may not respond in time.
- **Swagger / OpenAPI not loading** — When no API key is set, doc routes accept only localhost; if they fail, check base URL and that the API process is bound to the expected host/port.

## Security and trust

This skill is **instruction-only**: it contains no install scripts, no code to execute, and no extra binaries. OpenClaw uses it to decide when and how to call your TMS API. No env required when agent and TMS run on the same host (default `http://127.0.0.1:8080`, no key). Optional: `TMS_API_URL` (base URL), `TMS_API_KEY` (for auth from another host). These are used solely for HTTP requests to the TMS endpoints described in SKILL.md (health, list, add, delete, search). The full OpenAPI spec is embedded in SKILL.md. The skill does not read unrelated files, harvest other env vars, or send data to third-party endpoints. Optional webhook support is configured on the TMS side (`TMS_WEBHOOK_URL`, and `TMS_WEBHOOK_TOKEN` for OpenClaw hooks auth), not by the skill.

**Before installing:**

1. **Trusted TMS host** — Ensure `TMS_API_URL` points to a TMS instance you control or trust, and that the network path (e.g. LAN or VPN) is what you expect.
2. **API key** — Use a TMS API key with the minimal permissions you need; avoid reusing a key that has broader or admin access elsewhere.
3. **Webhooks** — If you set `TMS_WEBHOOK_URL` on TMS, you are exposing an endpoint that will receive completion/failure/stopped events. Secure and authenticate that endpoint.
4. **Autonomous invocation** — By default the agent can invoke this skill on its own (e.g. start or stop downloads). If you want to allow only explicit user requests, disable model invocation for this skill or restrict when it is enabled.
5. **Secrets** — Store `TMS_API_KEY` in per-skill or agent config (e.g. `openclaw.json`), not in public repos. Rotate the key if it may have been compromised.

The skill may be subject to security scanning (e.g. VirusTotal, OpenClaw checks) as part of the ClawHub listing; its behavior is limited to the described API client role.

## License

This skill is part of the [telegram-media-server](https://github.com/NikitaDmitryuk/telegram-media-server) repository and is under the same [MIT License](https://github.com/NikitaDmitryuk/telegram-media-server/blob/main/LICENSE) as the project.

## Publishing (for maintainers)

To publish or update this skill on ClawHub from the repo root:

1. Install CLI via npm (ClawHub is not in Homebrew): `npm i -g clawhub` or `pnpm add -g clawhub`.
2. Log in: `clawhub login` (or `clawhub login --token <token>`).
3. Publish:  
   `clawhub publish ./openclaw-skill-tms --slug tms --name "TMS (Telegram Media Server)" --version 1.0.0 --changelog "Initial release" --tags latest`
4. For updates: bump version and changelog, then run `clawhub publish` again with new `--version` and `--changelog`.

See [CHANGELOG.md](CHANGELOG.md) for version history.


path: clawhub.json
content: {
  "name": "TMS (Telegram Media Server)",
  "tagline": "Manage media downloads via REST API: video URLs, magnets, torrents, and search.",
  "description": "Telegram Media Server (TMS) is a Telegram bot that downloads streaming video and torrents (yt-dlp, aria2) and can serve them via DLNA. Install TMS from https://github.com/NikitaDmitryuk/telegram-media-server (see repo README). This skill teaches OpenClaw to control a running TMS via its REST API: add downloads by URL (video, magnet, .torrent), list downloads, delete a download, and search torrents when Prowlarr is configured. Instruction-only — no install scripts or code execution; only TMS_API_URL and TMS_API_KEY are required. Use it to manage media downloads from OpenClaw chat instead of the Telegram bot.",
  "category": "utility",
  "tags": ["downloads", "media", "api", "torrent", "yt-dlp", "tms"],
  "version": "1.0.2",
  "license": "MIT",
  "pricing": "free",
  "support_url": "https://github.com/NikitaDmitryuk/telegram-media-server/issues",
  "homepage": "https://github.com/NikitaDmitryuk/telegram-media-server/tree/main/openclaw-skill-tms"
}


path: screenshots/README.md
content: # Screenshots for ClawHub

Place 3–5 PNG screenshots here for the ClawHub listing (1920×1080 or 1280×720). Suggested content:

1. **Hero** — OpenClaw chat: user asks to add a download or list downloads; agent response with result.
2. **Config** — Fragment of `openclaw.json` showing `skills.entries.tms` with `TMS_API_URL` and `TMS_API_KEY`.
3. **Output** — Example of list/download status or search results in chat.
4. **Optional** — TMS Swagger UI (`{TMS_API_URL}/api/v1/docs`) or API response.

When publishing, attach these files to the skill listing on ClawHub.


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)