Skill: zykkk-power/qqbot-send (version 1.0.0)

--- name: qqbot-send description: Send local files, images, audio, video, and other media through QQBot end to end. Use when the user asks to send a file to QQ, send a desktop/downloads/local absolute-path file, resend a local attachment, or when QQBot file sending should automatically stage the file into the QQ media relay directory and then deliver it with qqmedia tagging. --- # QQBot Send Use this skill for QQ file sending end to end. ## Core rule When the user wants a file sent through QQ, complete the full send flow instead of only returning a path. ## Built-in staging flow This skill includes the old `qqbot-local-media` behavior. For local files that are not already in the QQ media relay directory: 1. Run: - `python scripts/stage_media.py <source_path>` 2. The script copies the file into: - `~/.openclaw/media/qqbot/` 3. Use the returned absolute path in: - `<qqmedia>absolute-path</qqmedia>` ## Decision flow 1. Identify the source. - If the source is a local absolute path outside the QQ media relay area, stage it first. - If the source is already inside the OpenClaw QQ media relay directory, use it directly. - If the source is an HTTP(S) URL, send it directly with `<qqmedia>...</qqmedia>`. - If the source is a local attachment path already provided in context and QQBot can send it directly, send it directly. 2. For local files that need staging, use the bundled script in this skill. 3. Then send with QQ rich media. - Use `<qqmedia>absolute-path-or-url</qqmedia>` - One file per tag. ## Default practical flow For most local desktop/downloads/workspace-external files: 1. Stage into `~/.openclaw/media/qqbot/` 2. Send with `<qqmedia>...</qqmedia>` ## Size and path rules - Absolute local paths only - File size limit is 10 MB - Preserve the original file extension - Copy the file, do not modify the original - Do not claim you cannot send local files when the path is readable and within the size limit ## Response behavior - If the user asked to send the file, actually send it. - Do not only explain the flow unless the user asked a conceptual question. - If staging fails because the file does not exist, say that directly. - If staging fails because the file is too large, say that directly. ## Examples ### Example 1: Desktop PDF - User asks: send `C:\Users\name\Desktop\resume.pdf` to QQ - Action: 1. `python scripts/stage_media.py "C:\Users\name\Desktop\resume.pdf"` 2. send `<qqmedia>staged-absolute-path</qqmedia>` ### Example 2: Already staged file - User asks to send `C:\Users\name\.openclaw\media\qqbot\abc.pdf` - Action: 1. send `<qqmedia>C:\Users\name\.openclaw\media\qqbot\abc.pdf</qqmedia>` directly ### Example 3: URL image - User asks to send an image URL to QQ - Action: 1. send `<qqmedia>https://example.com/image.png</qqmedia>` directly ## Notes - Prefer completing the send in one turn. - Use the bundled staging script whenever the file is local and not already staged. - Use `<qqmedia>...</qqmedia>` for final delivery.

path: README.md
content: # qqbot-send

An OpenClaw skill for sending files through QQBot end to end.

It combines two responsibilities into one skill:

1. stage local files into the QQBot media relay directory
2. send the staged path with QQ rich-media tagging

## What this skill does

When a user asks to send a local file to QQ, this skill decides the safest path automatically.

### Local absolute-path file
For files like desktop files, downloads, and workspace-external files, the skill:

1. runs `python scripts/stage_media.py <source_path>`
2. copies the file into:
   - `~/.openclaw/media/qqbot/`
3. uses the returned path with:
   - `<qqmedia>...</qqmedia>`

### Already staged file
If the file is already inside the QQBot relay directory, it sends it directly.

### URL media
If the source is an HTTP(S) URL, it can be sent directly with `<qqmedia>...</qqmedia>`.

## Repository layout

```text
qqbot-send/
├─ SKILL.md
├─ README.md
├─ LICENSE
├─ .gitignore
└─ scripts/
   └─ stage_media.py
```

## Script behavior

The bundled script:

- accepts a single file path argument
- checks that the file exists
- rejects files larger than 10 MB
- preserves the file extension
- copies the file instead of changing the original
- writes to `~/.openclaw/media/qqbot/`
- prints the staged absolute path

## Example

```bash
python scripts/stage_media.py "C:\Users\zyk\Desktop\resume.pdf"
```

Example output:

```text
C:\Users\zyk\.openclaw\media\qqbot\9b0f3b80-19b2-410b-adfc-438d6028b93a.pdf
```

Then send:

```xml
<qqmedia>C:\Users\zyk\.openclaw\media\qqbot\9b0f3b80-19b2-410b-adfc-438d6028b93a.pdf</qqmedia>
```

## Notes

- This skill replaces the old split flow where staging and sending were handled by separate skills
- The final delivery format still uses QQBot rich-media tagging
- Staged media output is not part of this repository


path: scripts/stage_media.py
content: import shutil
import sys
import uuid
from pathlib import Path

MAX_SIZE = 10 * 1024 * 1024
DEST_DIR = Path.home() / '.openclaw' / 'media' / 'qqbot'


def main() -> int:
    if len(sys.argv) != 2:
        print('usage: python stage_media.py <source_path>', file=sys.stderr)
        return 2

    src = Path(sys.argv[1]).expanduser()
    if not src.is_absolute():
        src = src.resolve()

    if not src.exists() or not src.is_file():
        print(f'ERROR: file not found: {src}', file=sys.stderr)
        return 1

    size = src.stat().st_size
    if size > MAX_SIZE:
        print(f'ERROR: file too large: {size} bytes > {MAX_SIZE} bytes', file=sys.stderr)
        return 1

    DEST_DIR.mkdir(parents=True, exist_ok=True)
    ext = src.suffix
    dest = DEST_DIR / f'{uuid.uuid4()}{ext}'
    shutil.copy2(src, dest)
    print(dest)
    return 0


if __name__ == '__main__':
    raise SystemExit(main())


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