localsend
使用 LocalSend 协议向附近的设备发送和接收文件。使用 /localsend 触发以获得带有真正内联按钮的交互式 Telegram 菜单 - 设备发现、文件发送、文本发送和接收。
安装 / 下载方式
TotalClaw CLI推荐
totalclaw install totalclaw:totalclaw~chordlini-localsendcURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/totalclaw%3Atotalclaw~chordlini-localsend/file -o chordlini-localsend.md## 概述(中文)
使用 LocalSend 协议向附近的设备发送和接收文件。使用 /localsend 触发以获得带有真正内联按钮的交互式 Telegram 菜单 - 设备发现、文件发送、文本发送和接收。
## 原文
# LocalSend
Interactive file transfer between devices on the local network using **real Telegram inline keyboard buttons**. Works with any device running the LocalSend app (Android, iOS, Windows, macOS, Linux).
## Install
The `localsend-cli` is a zero-dependency Python CLI. Install from GitHub:
```bash
curl -fsSL https://raw.githubusercontent.com/Chordlini/localsend-cli/master/localsend-cli -o ~/.local/bin/localsend-cli
chmod +x ~/.local/bin/localsend-cli
```
Full docs: https://github.com/Chordlini/localsend-cli
Requires Python 3.8+ and `openssl` (for TLS).
---
## Telegram Button Format
All menus MUST use OpenClaw's inline button format. Send buttons alongside your message using this structure:
```json
buttons: [
[{ "text": "Label", "callback_data": "ls:action" }],
[{ "text": "Row 2", "callback_data": "ls:other" }]
]
```
- Outer array = rows of buttons
- Inner array = buttons per row (max 3 per row for readability)
- Prefix all callback_data with `ls:` to namespace this skill
- When user taps a button, you receive: `callback_data: ls:action`
---
## State Awareness (CRITICAL)
This skill uses conversational state. Track where you are in the flow:
| State | Meaning | Next user input should be treated as... |
|-------|---------|----------------------------------------|
| `idle` | No active flow | Normal message — respond normally |
| `awaiting_file` | Asked user to drop/specify a file to send | **The file to send** — do NOT comment on it, describe it, or react to it. Immediately use it as the send payload. |
| `awaiting_text` | Asked user to type text to send | **The text payload** — send it, don't discuss it |
| `awaiting_confirm` | Waiting for send confirmation | Expect `ls:confirm-send` or `ls:menu` |
| `receiving` | Receiver is active | Monitor for incoming files |
**RULES:**
- When in `awaiting_file` state and user sends an image/file/path → treat it as the file to send. Show confirmation buttons immediately.
- When in `awaiting_text` state and user types anything → treat it as the text to send.
- NEVER comment on, describe, or react to a file/image when you're in `awaiting_file` state.
- State resets to `idle` when user taps `ls:menu` or the flow completes.
---
## On Trigger: Main Menu
When the user types `/localsend` or mentions sending/receiving files locally, send this message **with real inline buttons**:
**Message:**
```
📡 LocalSend — File Transfer
```
**Buttons:**
```json
buttons: [
[
{ "text": "📤 Send", "callback_data": "ls:send" },
{ "text": "📥 Receive", "callback_data": "ls:receive" }
],
[
{ "text": "🔍 Scan Devices", "callback_data": "ls:devices" }
]
]
```
Do NOT run any commands yet. Wait for the button tap.
---
## Flow: Scan Devices
**Trigger:** `callback_data: ls:devices` or user says "scan", "discover", "find devices"
1. Run:
```bash
localsend-cli discover --json -t 2
```
2. **Devices found** — create one button per device, plus Refresh and Back:
**Message:**
```
📡 Found 3 devices:
```
**Buttons (one device per row):**
```json
buttons: [
[{ "text": "📱 Fast Potato — 192.168.0.148", "callback_data": "ls:dev:Fast Potato" }],
[{ "text": "💻 Rami-Desktop — 192.168.0.100", "callback_data": "ls:dev:Rami-Desktop" }],
[{ "text": "🖥️ Living Room PC — 192.168.0.105", "callback_data": "ls:dev:Living Room PC" }],
[
{ "text": "🔄 Refresh", "callback_data": "ls:devices" },
{ "text": "⬅️ Back", "callback_data": "ls:menu" }
]
]
```
3. **No devices found:**
**Message:**
```
📡 No devices found.
Make sure LocalSend is open on the other device and both are on the same WiFi.
```
**Buttons:**
```json
buttons: [
[
{ "text": "🔄 Try Again", "callback_data": "ls:devices" },
{ "text": "⬅️ Back", "callback_data": "ls:menu" }
]
]
```
4. **User taps a device** (`callback_data: ls:dev:DEVICENAME`) — store it as the selected target. Show action menu:
**Message:**
```
✅ Selected: Fast Potato (192.168.0.148)
What do you want to do?
```
**Buttons:**
```json
buttons: [
[
{ "text": "📄 Send File", "callback_data": "ls:sendfile" },
{ "text": "📝 Send Text", "callback_data": "ls:sendtext" }
],
[
{ "text": "📦 Send Multiple", "callback_data": "ls:sendmulti" },
{ "text": "⬅️ Back", "callback_data": "ls:devices" }
]
]
```
---
## Flow: Send
**Trigger:** `callback_data: ls:send`
### Step 1 — Pick target device (if not already selected)
Run discover and show device picker (see Scan Devices flow above).
### Step 2 — Choose what to send
**Message:**
```
Send to Fast Potato:
```
**Buttons:**
```json
buttons: [
[
{ "text": "📄 Send File", "callback_data": "ls:sendfile" },
{ "text": "📝 Send Text", "callback_data": "ls:sendtext" }
],
[
{ "text": "📦 Send Multiple", "callback_data": "ls:sendmulti" },
{ "text": "⬅️ Back", "callback_data": "ls:menu" }
]
]
```
### Send File (`callback_data: ls:sendfile`)
1. Ask: `"Send me the file, drop a path, or tell me which file to send"`
2. User provides file path or sends a file via chat
3. Get file size with `stat` or `ls -lh`
4. Confirm with buttons:
**Message:**
```
📤 Send to Fast Potato?
📄 project.zip — 4.2 MB
```
**Buttons:**
```json
buttons: [
[
{ "text": "✅ Send", "callback_data": "ls:confirm-send" },
{ "text": "❌ Cancel", "callback_data": "ls:menu" }
]
]
```
5. On confirm, run:
```bash
localsend-cli send --to "Fast Potato" /path/to/project.zip
```
6. Report result:
**Message:**
```
✅ Sent project.zip (4.2 MB) to Fast Potato
```
**Buttons:**
```json
buttons: [
[
{ "text": "📤 Send Another", "callback_data": "ls:send" },
{ "text": "⬅️ Menu", "callback_data": "ls:menu" }
]
]
```
### Send Text (`callback_data: ls:sendtext`)
1. Ask: `"Type the text you want to send:"`
2. User types their message
3. Write text to temp file, send:
```bash
echo "user's text" > /tmp/localsend-text.txt
localsend-cli send --to "Fast Potato" /tmp/localsend-text.txt
rm /tmp/localsend-text.txt
```
4. Confirm:
**Message:**
```
✅ Text sent to Fast Potato
```
**Buttons:**
```json
buttons: [
[
{ "text": "📝 Send More Text", "callback_data": "ls:sendtext" },
{ "text": "📤 Send File", "callback_data": "ls:sendfile" }
],
[{ "text": "⬅️ Menu", "callback_data": "ls:menu" }]
]
```
### Send Multiple (`callback_data: ls:sendmulti`)
1. Ask: `"List the files or give me a glob pattern (e.g. ~/Screenshots/*.png)"`
2. User provides paths or pattern
3. Expand glob, list files with sizes:
**Message:**
```
📦 Send 5 files to Fast Potato?
📄 photo1.jpg — 2.1 MB
📄 photo2.jpg — 1.8 MB
📄 photo3.jpg — 3.2 MB
📄 photo4.jpg — 2.5 MB
📄 photo5.jpg — 1.9 MB
📊 Total: 11.5 MB
```
**Buttons:**
```json
buttons: [
[
{ "text": "✅ Send All", "callback_data": "ls:confirm-send" },
{ "text": "❌ Cancel", "callback_data": "ls:menu" }
]
]
```
4. On confirm, run:
```bash
localsend-cli send --to "Fast Potato" photo1.jpg photo2.jpg photo3.jpg photo4.jpg photo5.jpg
```
5. Report:
**Message:**
```
✅ Sent 5 files (11.5 MB) to Fast Potato
```
**Buttons:**
```json
buttons: [
[
{ "text": "📤 Send More", "callback_data": "ls:send" },
{ "text": "⬅️ Menu", "callback_data": "ls:menu" }
]
]
```
---
## Flow: Receive
**Trigger:** `callback_data: ls:receive` or user says "receive", "start receiving", "listen"
### Step 1 — Snapshot current files
```bash
ls -1 /home/rami/.openclaw/workspace/_incoming/ > /tmp/localsend-before.txt
```
### Step 2 — Start receiver in background
```ba