Fresh start - excluded large ROM JSON files
This commit is contained in:
7
skills/qbittorrent/.clawdhub/origin.json
Normal file
7
skills/qbittorrent/.clawdhub/origin.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 1,
|
||||
"registry": "https://clawdhub.com",
|
||||
"slug": "qbittorrent",
|
||||
"installedVersion": "1.0.0",
|
||||
"installedAt": 1769737771792
|
||||
}
|
||||
122
skills/qbittorrent/README.md
Normal file
122
skills/qbittorrent/README.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# qBittorrent Skill
|
||||
|
||||
Manage torrents via qBittorrent WebUI from Clawdbot.
|
||||
|
||||
## What It Does
|
||||
|
||||
- **List torrents** — filter by status, category, or tags
|
||||
- **Add torrents** — by magnet link, URL, or local file
|
||||
- **Control downloads** — pause, resume, delete, recheck
|
||||
- **Speed limits** — set upload/download limits
|
||||
- **Categories & tags** — organize your torrents
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Enable WebUI
|
||||
|
||||
1. Open qBittorrent
|
||||
2. Go to **Tools → Options → Web UI**
|
||||
3. Enable **Web User Interface (Remote control)**
|
||||
4. Set a username and password
|
||||
5. Note the port (default: 8080)
|
||||
|
||||
### 2. Create Credentials File
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.clawdbot/credentials/qbittorrent
|
||||
cat > ~/.clawdbot/credentials/qbittorrent/config.json << 'EOF'
|
||||
{
|
||||
"url": "http://localhost:8080",
|
||||
"username": "admin",
|
||||
"password": "your-password-here"
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
Replace with your actual WebUI credentials.
|
||||
|
||||
### 3. Test It
|
||||
|
||||
```bash
|
||||
./skills/qbittorrent/scripts/qbit-api.sh version
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### List torrents
|
||||
|
||||
```bash
|
||||
# All torrents
|
||||
qbit-api.sh list
|
||||
|
||||
# Filter by status
|
||||
qbit-api.sh list --filter downloading
|
||||
qbit-api.sh list --filter seeding
|
||||
qbit-api.sh list --filter paused
|
||||
|
||||
# Filter by category
|
||||
qbit-api.sh list --category movies
|
||||
```
|
||||
|
||||
### Add torrents
|
||||
|
||||
```bash
|
||||
# By magnet link
|
||||
qbit-api.sh add "magnet:?xt=..." --category movies
|
||||
|
||||
# By .torrent file
|
||||
qbit-api.sh add-file /path/to/file.torrent --paused
|
||||
```
|
||||
|
||||
### Control torrents
|
||||
|
||||
```bash
|
||||
qbit-api.sh pause <hash> # or "all"
|
||||
qbit-api.sh resume <hash> # or "all"
|
||||
qbit-api.sh delete <hash> # keep files
|
||||
qbit-api.sh delete <hash> --files # delete files too
|
||||
```
|
||||
|
||||
### Speed limits
|
||||
|
||||
```bash
|
||||
qbit-api.sh transfer # view current speeds
|
||||
qbit-api.sh set-speedlimit --down 5M --up 1M
|
||||
```
|
||||
|
||||
### Categories & tags
|
||||
|
||||
```bash
|
||||
qbit-api.sh categories
|
||||
qbit-api.sh tags
|
||||
qbit-api.sh set-category <hash> movies
|
||||
qbit-api.sh add-tags <hash> "important,archive"
|
||||
```
|
||||
|
||||
## Environment Variables (Alternative)
|
||||
|
||||
Instead of a config file, you can set:
|
||||
|
||||
```bash
|
||||
export QBIT_URL="http://localhost:8080"
|
||||
export QBIT_USER="admin"
|
||||
export QBIT_PASS="your-password"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**"QBIT_URL must be set"**
|
||||
→ Check your config file exists at `~/.clawdbot/credentials/qbittorrent/config.json`
|
||||
|
||||
**Connection refused**
|
||||
→ Make sure WebUI is enabled in qBittorrent settings
|
||||
|
||||
**403 Forbidden**
|
||||
→ Check username/password, or whitelist your IP in qBittorrent WebUI settings
|
||||
|
||||
**"Banned" after too many attempts**
|
||||
→ qBittorrent bans IPs after failed logins — wait or restart qBittorrent
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
102
skills/qbittorrent/SKILL.md
Normal file
102
skills/qbittorrent/SKILL.md
Normal file
@@ -0,0 +1,102 @@
|
||||
---
|
||||
name: qbittorrent
|
||||
version: 1.0.0
|
||||
description: Manage torrents with qBittorrent. Use when the user asks to "list torrents", "add torrent", "pause torrent", "resume torrent", "delete torrent", "check download status", "torrent speed", "qBittorrent stats", or mentions qBittorrent/qbit torrent management.
|
||||
---
|
||||
|
||||
# qBittorrent WebUI API
|
||||
|
||||
Manage torrents via qBittorrent's WebUI API (v4.1+).
|
||||
|
||||
## Setup
|
||||
|
||||
Config: `~/.clawdbot/credentials/qbittorrent/config.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"url": "http://localhost:8080",
|
||||
"username": "admin",
|
||||
"password": "adminadmin"
|
||||
}
|
||||
```
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### List Torrents
|
||||
|
||||
```bash
|
||||
# All torrents
|
||||
./scripts/qbit-api.sh list
|
||||
|
||||
# Filter by status
|
||||
./scripts/qbit-api.sh list --filter downloading
|
||||
./scripts/qbit-api.sh list --filter seeding
|
||||
./scripts/qbit-api.sh list --filter paused
|
||||
|
||||
# Filter by category
|
||||
./scripts/qbit-api.sh list --category movies
|
||||
```
|
||||
|
||||
Filters: `all`, `downloading`, `seeding`, `completed`, `paused`, `active`, `inactive`, `stalled`, `errored`
|
||||
|
||||
### Get Torrent Info
|
||||
|
||||
```bash
|
||||
./scripts/qbit-api.sh info <hash>
|
||||
./scripts/qbit-api.sh files <hash>
|
||||
./scripts/qbit-api.sh trackers <hash>
|
||||
```
|
||||
|
||||
### Add Torrent
|
||||
|
||||
```bash
|
||||
# By magnet or URL
|
||||
./scripts/qbit-api.sh add "magnet:?xt=..." --category movies
|
||||
|
||||
# By file
|
||||
./scripts/qbit-api.sh add-file /path/to/file.torrent --paused
|
||||
```
|
||||
|
||||
### Control Torrents
|
||||
|
||||
```bash
|
||||
./scripts/qbit-api.sh pause <hash> # or "all"
|
||||
./scripts/qbit-api.sh resume <hash> # or "all"
|
||||
./scripts/qbit-api.sh delete <hash> # keep files
|
||||
./scripts/qbit-api.sh delete <hash> --files # delete files too
|
||||
./scripts/qbit-api.sh recheck <hash>
|
||||
```
|
||||
|
||||
### Categories & Tags
|
||||
|
||||
```bash
|
||||
./scripts/qbit-api.sh categories
|
||||
./scripts/qbit-api.sh tags
|
||||
./scripts/qbit-api.sh set-category <hash> movies
|
||||
./scripts/qbit-api.sh add-tags <hash> "important,archive"
|
||||
```
|
||||
|
||||
### Transfer Info
|
||||
|
||||
```bash
|
||||
./scripts/qbit-api.sh transfer # global speed/stats
|
||||
./scripts/qbit-api.sh speedlimit # current limits
|
||||
./scripts/qbit-api.sh set-speedlimit --down 5M --up 1M
|
||||
```
|
||||
|
||||
### App Info
|
||||
|
||||
```bash
|
||||
./scripts/qbit-api.sh version
|
||||
./scripts/qbit-api.sh preferences
|
||||
```
|
||||
|
||||
## Response Format
|
||||
|
||||
Torrent object includes:
|
||||
- `hash`, `name`, `state`, `progress`
|
||||
- `dlspeed`, `upspeed`, `eta`
|
||||
- `size`, `downloaded`, `uploaded`
|
||||
- `category`, `tags`, `save_path`
|
||||
|
||||
States: `downloading`, `stalledDL`, `uploading`, `stalledUP`, `pausedDL`, `pausedUP`, `queuedDL`, `queuedUP`, `checkingDL`, `checkingUP`, `error`, `missingFiles`
|
||||
370
skills/qbittorrent/scripts/qbit-api.sh
Normal file
370
skills/qbittorrent/scripts/qbit-api.sh
Normal file
@@ -0,0 +1,370 @@
|
||||
#!/bin/bash
|
||||
# qBittorrent WebUI API helper script
|
||||
# Usage: qbit-api.sh <command> [args...]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CONFIG_FILE="${QBIT_CONFIG:-$HOME/.clawdbot/credentials/qbittorrent/config.json}"
|
||||
COOKIE_FILE="${QBIT_COOKIE:-/tmp/qbit_cookie_$(id -u).txt}"
|
||||
|
||||
# Load config
|
||||
if [[ -f "$CONFIG_FILE" ]]; then
|
||||
QBIT_URL=$(jq -r '.url // empty' "$CONFIG_FILE")
|
||||
QBIT_USER=$(jq -r '.username // empty' "$CONFIG_FILE")
|
||||
QBIT_PASS=$(jq -r '.password // empty' "$CONFIG_FILE")
|
||||
fi
|
||||
|
||||
QBIT_URL="${QBIT_URL:-}"
|
||||
QBIT_USER="${QBIT_USER:-}"
|
||||
QBIT_PASS="${QBIT_PASS:-}"
|
||||
|
||||
if [[ -z "$QBIT_URL" ]]; then
|
||||
echo "Error: QBIT_URL must be set (via env or $CONFIG_FILE)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remove trailing slash
|
||||
QBIT_URL="${QBIT_URL%/}"
|
||||
|
||||
# Login and get session cookie
|
||||
do_login() {
|
||||
local resp
|
||||
resp=$(curl -sS -i -X POST \
|
||||
-H "Referer: $QBIT_URL" \
|
||||
-d "username=$QBIT_USER&password=$QBIT_PASS" \
|
||||
"$QBIT_URL/api/v2/auth/login" 2>&1)
|
||||
|
||||
if echo "$resp" | grep -iq "set-cookie: SID="; then
|
||||
local sid
|
||||
sid=$(echo "$resp" | grep -ioP 'SID=\K[^;]+')
|
||||
echo "$sid" > "$COOKIE_FILE"
|
||||
return 0
|
||||
else
|
||||
echo "Login failed" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Ensure we have a valid session
|
||||
ensure_session() {
|
||||
if [[ ! -f "$COOKIE_FILE" ]]; then
|
||||
do_login
|
||||
return
|
||||
fi
|
||||
|
||||
# Test session validity
|
||||
local sid
|
||||
sid=$(cat "$COOKIE_FILE")
|
||||
local resp
|
||||
resp=$(curl -sS -o /dev/null -w "%{http_code}" \
|
||||
--cookie "SID=$sid" \
|
||||
"$QBIT_URL/api/v2/app/version")
|
||||
|
||||
if [[ "$resp" != "200" ]]; then
|
||||
do_login
|
||||
fi
|
||||
}
|
||||
|
||||
api_call() {
|
||||
local method="$1"
|
||||
local endpoint="$2"
|
||||
shift 2
|
||||
|
||||
ensure_session
|
||||
local sid
|
||||
sid=$(cat "$COOKIE_FILE")
|
||||
|
||||
curl -sS -X "$method" \
|
||||
--cookie "SID=$sid" \
|
||||
-H "Referer: $QBIT_URL" \
|
||||
"$@" \
|
||||
"${QBIT_URL}${endpoint}"
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
qBittorrent WebUI API CLI
|
||||
|
||||
Usage: $(basename "$0") <command> [options]
|
||||
|
||||
Commands:
|
||||
list [--filter F] [--category C] [--tag T] [--sort S] [--limit N]
|
||||
info <hash> Get torrent properties
|
||||
files <hash> Get torrent files
|
||||
trackers <hash> Get torrent trackers
|
||||
|
||||
add <url|magnet> [--category C] [--tags T] [--paused] [--skip-check]
|
||||
add-file <path> [--category C] [--tags T] [--paused]
|
||||
|
||||
pause <hash|all> Pause torrent(s)
|
||||
resume <hash|all> Resume torrent(s)
|
||||
delete <hash> [--files] Delete torrent (optionally with files)
|
||||
recheck <hash> Recheck torrent
|
||||
reannounce <hash> Reannounce to trackers
|
||||
|
||||
set-category <hash> <category>
|
||||
add-tags <hash> <tags>
|
||||
remove-tags <hash> <tags>
|
||||
|
||||
categories List categories
|
||||
tags List tags
|
||||
|
||||
transfer Global transfer info
|
||||
speedlimit Get speed limits
|
||||
set-speedlimit [--down D] [--up U] Set limits (e.g., 5M, 1024K)
|
||||
toggle-alt-speed Toggle alternative speed limits
|
||||
|
||||
version App version
|
||||
preferences App preferences
|
||||
|
||||
Filters: all, downloading, seeding, completed, paused, active, inactive, stalled, stalled_uploading, stalled_downloading, errored
|
||||
|
||||
Examples:
|
||||
$(basename "$0") list --filter downloading
|
||||
$(basename "$0") add "magnet:?xt=..." --category movies
|
||||
$(basename "$0") pause all
|
||||
$(basename "$0") set-speedlimit --down 10M
|
||||
EOF
|
||||
}
|
||||
|
||||
cmd_list() {
|
||||
local filter="" category="" tag="" sort="" limit="" offset=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--filter|-f) filter="$2"; shift 2 ;;
|
||||
--category|-c) category="$2"; shift 2 ;;
|
||||
--tag|-t) tag="$2"; shift 2 ;;
|
||||
--sort|-s) sort="$2"; shift 2 ;;
|
||||
--limit|-l) limit="$2"; shift 2 ;;
|
||||
--offset|-o) offset="$2"; shift 2 ;;
|
||||
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
local params=()
|
||||
[[ -n "$filter" ]] && params+=("filter=$filter")
|
||||
[[ -n "$category" ]] && params+=("category=$category")
|
||||
[[ -n "$tag" ]] && params+=("tag=$tag")
|
||||
[[ -n "$sort" ]] && params+=("sort=$sort")
|
||||
[[ -n "$limit" ]] && params+=("limit=$limit")
|
||||
[[ -n "$offset" ]] && params+=("offset=$offset")
|
||||
|
||||
local query=""
|
||||
if [[ ${#params[@]} -gt 0 ]]; then
|
||||
query="?$(IFS='&'; echo "${params[*]}")"
|
||||
fi
|
||||
|
||||
api_call GET "/api/v2/torrents/info${query}"
|
||||
}
|
||||
|
||||
cmd_info() {
|
||||
local hash="$1"
|
||||
api_call GET "/api/v2/torrents/properties?hash=$hash"
|
||||
}
|
||||
|
||||
cmd_files() {
|
||||
local hash="$1"
|
||||
api_call GET "/api/v2/torrents/files?hash=$hash"
|
||||
}
|
||||
|
||||
cmd_trackers() {
|
||||
local hash="$1"
|
||||
api_call GET "/api/v2/torrents/trackers?hash=$hash"
|
||||
}
|
||||
|
||||
cmd_add() {
|
||||
local url="$1"; shift
|
||||
local category="" tags="" paused="false" skip_check="false"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--category|-c) category="$2"; shift 2 ;;
|
||||
--tags|-t) tags="$2"; shift 2 ;;
|
||||
--paused|-p) paused="true"; shift ;;
|
||||
--skip-check) skip_check="true"; shift ;;
|
||||
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
local data="urls=$url&paused=$paused&skip_checking=$skip_check"
|
||||
[[ -n "$category" ]] && data+="&category=$category"
|
||||
[[ -n "$tags" ]] && data+="&tags=$tags"
|
||||
|
||||
api_call POST "/api/v2/torrents/add" -d "$data"
|
||||
echo '{"status": "ok"}'
|
||||
}
|
||||
|
||||
cmd_add_file() {
|
||||
local filepath="$1"; shift
|
||||
local category="" tags="" paused="false"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--category|-c) category="$2"; shift 2 ;;
|
||||
--tags|-t) tags="$2"; shift 2 ;;
|
||||
--paused|-p) paused="true"; shift ;;
|
||||
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
ensure_session
|
||||
local sid
|
||||
sid=$(cat "$COOKIE_FILE")
|
||||
|
||||
local args=(-F "torrents=@$filepath" -F "paused=$paused")
|
||||
[[ -n "$category" ]] && args+=(-F "category=$category")
|
||||
[[ -n "$tags" ]] && args+=(-F "tags=$tags")
|
||||
|
||||
curl -sS --cookie "SID=$sid" -H "Referer: $QBIT_URL" "${args[@]}" "$QBIT_URL/api/v2/torrents/add"
|
||||
echo '{"status": "ok"}'
|
||||
}
|
||||
|
||||
cmd_pause() {
|
||||
local hashes="$1"
|
||||
api_call POST "/api/v2/torrents/pause" -d "hashes=$hashes"
|
||||
echo '{"status": "ok"}'
|
||||
}
|
||||
|
||||
cmd_resume() {
|
||||
local hashes="$1"
|
||||
api_call POST "/api/v2/torrents/resume" -d "hashes=$hashes"
|
||||
echo '{"status": "ok"}'
|
||||
}
|
||||
|
||||
cmd_delete() {
|
||||
local hash="$1"; shift
|
||||
local delete_files="false"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--files|-f) delete_files="true"; shift ;;
|
||||
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
api_call POST "/api/v2/torrents/delete" -d "hashes=$hash&deleteFiles=$delete_files"
|
||||
echo '{"status": "ok"}'
|
||||
}
|
||||
|
||||
cmd_recheck() {
|
||||
local hash="$1"
|
||||
api_call POST "/api/v2/torrents/recheck" -d "hashes=$hash"
|
||||
echo '{"status": "ok"}'
|
||||
}
|
||||
|
||||
cmd_reannounce() {
|
||||
local hash="$1"
|
||||
api_call POST "/api/v2/torrents/reannounce" -d "hashes=$hash"
|
||||
echo '{"status": "ok"}'
|
||||
}
|
||||
|
||||
cmd_set_category() {
|
||||
local hash="$1"
|
||||
local category="$2"
|
||||
api_call POST "/api/v2/torrents/setCategory" -d "hashes=$hash&category=$category"
|
||||
echo '{"status": "ok"}'
|
||||
}
|
||||
|
||||
cmd_add_tags() {
|
||||
local hash="$1"
|
||||
local tags="$2"
|
||||
api_call POST "/api/v2/torrents/addTags" -d "hashes=$hash&tags=$tags"
|
||||
echo '{"status": "ok"}'
|
||||
}
|
||||
|
||||
cmd_remove_tags() {
|
||||
local hash="$1"
|
||||
local tags="$2"
|
||||
api_call POST "/api/v2/torrents/removeTags" -d "hashes=$hash&tags=$tags"
|
||||
echo '{"status": "ok"}'
|
||||
}
|
||||
|
||||
cmd_categories() {
|
||||
api_call GET "/api/v2/torrents/categories"
|
||||
}
|
||||
|
||||
cmd_tags() {
|
||||
api_call GET "/api/v2/torrents/tags"
|
||||
}
|
||||
|
||||
cmd_transfer() {
|
||||
api_call GET "/api/v2/transfer/info"
|
||||
}
|
||||
|
||||
cmd_speedlimit() {
|
||||
echo "{"
|
||||
echo " \"download\": $(api_call GET '/api/v2/transfer/downloadLimit'),"
|
||||
echo " \"upload\": $(api_call GET '/api/v2/transfer/uploadLimit')"
|
||||
echo "}"
|
||||
}
|
||||
|
||||
parse_speed() {
|
||||
local val="$1"
|
||||
local num="${val%[KMG]}"
|
||||
local suffix="${val: -1}"
|
||||
|
||||
case "$suffix" in
|
||||
K|k) echo $((num * 1024)) ;;
|
||||
M|m) echo $((num * 1024 * 1024)) ;;
|
||||
G|g) echo $((num * 1024 * 1024 * 1024)) ;;
|
||||
*) echo "$val" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
cmd_set_speedlimit() {
|
||||
local down="" up=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--down|-d) down=$(parse_speed "$2"); shift 2 ;;
|
||||
--up|-u) up=$(parse_speed "$2"); shift 2 ;;
|
||||
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -n "$down" ]] && api_call POST "/api/v2/transfer/setDownloadLimit" -d "limit=$down"
|
||||
[[ -n "$up" ]] && api_call POST "/api/v2/transfer/setUploadLimit" -d "limit=$up"
|
||||
echo '{"status": "ok"}'
|
||||
}
|
||||
|
||||
cmd_toggle_alt_speed() {
|
||||
api_call POST "/api/v2/transfer/toggleSpeedLimitsMode"
|
||||
echo '{"status": "ok"}'
|
||||
}
|
||||
|
||||
cmd_version() {
|
||||
api_call GET "/api/v2/app/version"
|
||||
}
|
||||
|
||||
cmd_preferences() {
|
||||
api_call GET "/api/v2/app/preferences"
|
||||
}
|
||||
|
||||
# Main dispatch
|
||||
case "${1:-}" in
|
||||
list) shift; cmd_list "$@" ;;
|
||||
info) shift; cmd_info "$@" ;;
|
||||
files) shift; cmd_files "$@" ;;
|
||||
trackers) shift; cmd_trackers "$@" ;;
|
||||
add) shift; cmd_add "$@" ;;
|
||||
add-file) shift; cmd_add_file "$@" ;;
|
||||
pause) shift; cmd_pause "$@" ;;
|
||||
resume) shift; cmd_resume "$@" ;;
|
||||
delete) shift; cmd_delete "$@" ;;
|
||||
recheck) shift; cmd_recheck "$@" ;;
|
||||
reannounce) shift; cmd_reannounce "$@" ;;
|
||||
set-category) shift; cmd_set_category "$@" ;;
|
||||
add-tags) shift; cmd_add_tags "$@" ;;
|
||||
remove-tags) shift; cmd_remove_tags "$@" ;;
|
||||
categories) shift; cmd_categories "$@" ;;
|
||||
tags) shift; cmd_tags "$@" ;;
|
||||
transfer) shift; cmd_transfer "$@" ;;
|
||||
speedlimit) shift; cmd_speedlimit "$@" ;;
|
||||
set-speedlimit) shift; cmd_set_speedlimit "$@" ;;
|
||||
toggle-alt-speed) shift; cmd_toggle_alt_speed "$@" ;;
|
||||
version) shift; cmd_version "$@" ;;
|
||||
preferences) shift; cmd_preferences "$@" ;;
|
||||
-h|--help|help|"") usage ;;
|
||||
*) echo "Unknown command: $1" >&2; usage; exit 1 ;;
|
||||
esac
|
||||
Reference in New Issue
Block a user