折腾笔记[52]-使用kimi发送消息到matrix房间

摘要

在macOS上配置kimi-cli通过matrix-cli技能发送消息和文件到Matrix聊天房间.

声明

本文人类为第一作者, 龙虾为通讯作者.本文有AI生成内容.

matrix-cli技能

SKILL.md

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Matrix CLI Skill</title>
<style>
  :root {
    --bg: #f6f8fa;
    --fg: #1f2328;
    --border: #d1d9e0;
    --code-bg: #f3f4f6;
    --table-border: #d1d9e0;
    --table-header-bg: #f6f8fa;
    --table-row-alt: #ffffff;
    --accent: #0969da;
    --shadow: 0 1px 3px rgba(31,35,40,0.12), 0 8px 24px rgba(66,74,83,0.12);
  }
  * { box-sizing: border-box; }
  body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif;
    line-height: 1.6;
    color: var(--fg);
    background: #ffffff;
    max-width: 900px;
    margin: 0 auto;
    padding: 40px 24px;
  }
  h1 { font-size: 2em; border-bottom: 1px solid var(--border); padding-bottom: .3em; margin-top: 0; }
  h2 { font-size: 1.5em; border-bottom: 1px solid var(--border); padding-bottom: .3em; margin-top: 1.5em; }
  h3 { font-size: 1.25em; margin-top: 1.5em; }
  h4 { font-size: 1em; margin-top: 1.2em; }
  code {
    background: var(--code-bg);
    padding: .2em .4em;
    border-radius: 6px;
    font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
    font-size: 85%;
  }
  pre {
    background: var(--code-bg);
    padding: 16px;
    border-radius: 8px;
    overflow-x: auto;
    border: 1px solid var(--border);
  }
  pre code { background: none; padding: 0; font-size: 100%; }
  table {
    width: 100%;
    border-collapse: collapse;
    margin: 1em 0;
    border: 1px solid var(--table-border);
    border-radius: 8px;
    overflow: hidden;
    box-shadow: var(--shadow);
  }
  th, td {
    padding: 10px 14px;
    text-align: left;
    border-bottom: 1px solid var(--table-border);
  }
  th { background: var(--table-header-bg); font-weight: 600; }
  tr:nth-child(even) { background: var(--table-row-alt); }
  ul, ol { padding-left: 1.5em; }
  li { margin: .3em 0; }
  strong { font-weight: 600; }
  hr { border: 0; border-top: 1px solid var(--border); margin: 1.5em 0; }
  .meta {
    background: linear-gradient(135deg, #f0f4ff 0%, #f8faff 100%);
    border: 1px solid #c8d6f5;
    border-radius: 8px;
    padding: 16px 20px;
    margin-bottom: 24px;
    box-shadow: var(--shadow);
  }
  .meta p { margin: 4px 0; color: #4a5568; font-size: .95em; }
  .meta strong { color: #2b4c8c; }
</style>
<base target="_blank">
</head>
<body>

<div class="meta">
<p><strong>name:</strong> matrix-cli</p>
<p><strong>description:</strong> Send messages and files to Matrix chat rooms via CLI. Use when the user needs to (1) send text messages to Matrix rooms, (2) send images/files to Matrix rooms, (3) interact with Matrix servers from command line, or (4) automate Matrix messaging workflows.</p>
</div>

<h1>Matrix CLI Skill</h1>

<p>Send messages and files to Matrix chat rooms using a Python-based CLI tool.</p>

<h2>Quick Start</h2>

<p>Use the bundled script to send messages:</p>

<pre><code># Send text message
python scripts/matrix-cli.py \
  -s http://xxx.qsbye.cn:xxx  \
  -u @username:xxx.qsbye.cn \
  -t "ACCESS_TOKEN" \
  -r "!ROOM_ID:xxx.qsbye.cn" \
  -m "Hello World"

# Send file/image
python scripts/matrix-cli.py \
  -s http://xxx.qsbye.cn:xxx  \
  -u @username:xxx.qsbye.cn \
  -p "PASSWORD" \
  -r "!ROOM_ID:xxx.qsbye.cn" \
  -f /path/to/file.png</code></pre>

<h2>Authentication</h2>

<p>Two authentication methods supported:</p>

<p><strong>Default Account:</strong></p>
<ul><li>访问令牌: <code>你的令牌</code></li><li>用户名: <code>@ollama:xxx.qsbye.cn</code></li><li>密码: <code>你的密码</code></li><li>房间ID: <code>!你的id:xxx.qsbye.cn</code></li></ul>

<p><strong>Alternative Account:</strong></p>
<ul><li>访问令牌: <code>你的令牌</code></li><li>用户名: <code>@zeroclaw:xxx.qsbye.cn</code></li><li>密码: <code>你的密码</code></li></ul>

<ol><li><strong>Access Token</strong> (preferred for automation):</li></ol>
<p>   ```bash</p>
<p>   -t "YOUR_ACCESS_TOKEN"</p>
<p>   ```</p>

<ol><li><strong>Password</strong> (generates new token):</li></ol>
<p>   ```bash</p>
<p>   -p "YOUR_PASSWORD"</p>
<p>   ```</p>

<h2>Parameters</h2>

<table>
<tr><th>Flag</th><th>Description</th><th>Required</th></tr>
<tr><td><code>-s, --server</code></td><td>Matrix homeserver URL</td><td>Yes</td></tr>
<tr><td><code>-u, --user</code></td><td>User ID (@user:server)</td><td>Yes</td></tr>
<tr><td><code>-r, --room</code></td><td>Room ID (!room:server)</td><td>Yes</td></tr>
<tr><td><code>-t, --token</code></td><td>Access token</td><td>Token or password</td></tr>
<tr><td><code>-p, --password</code></td><td>Password</td><td>Token or password</td></tr>
<tr><td><code>-m, --message</code></td><td>Text message to send</td><td>Message, file, or --test</td></tr>
<tr><td><code>-f, --file</code></td><td>File path to send</td><td>Message, file, or --test</td></tr>
<tr><td><code>--test</code></td><td>Send "hello" test message</td><td>Optional</td></tr>
</table>

<h2>Common Patterns</h2>

<h3>Send Test Message</h3>
<pre><code>python scripts/matrix-cli.py -s SERVER -u USER -t TOKEN -r ROOM --test</code></pre>

<h3>Send Multiple Items</h3>
<p>Combine <code>-m</code> and <code>-f</code> to send both text and file in one command.</p>

<h3>Default Server Configuration</h3>
<p>For the server at <code>xx.qsbye.cn:xxx</code>:</p>
<ul><li>Server: <code>http://xxx.qsbye.cn:xxx </code></li><li>Test room: <code>!xxx:xxx.qsbye.cn</code></li></ul>

<h2>Dependencies</h2>

<p>Requires <code>matrix-nio</code> Python package:</p>
<pre><code>pip install matrix-nio</code></pre>

<h2>Script Location</h2>

<ul><li><strong>Script</strong>: <code>scripts/matrix-cli.py</code></li><li><strong>Shebang</strong>: <code>#!/home/qsbye/.venv/bin/python3</code></li></ul>

<p>Run directly: <code>./scripts/matrix-cli.py</code> (executable)</p>
<p>Or with Python: <code>python scripts/matrix-cli.py</code></p>
</body>
</html>

scripts/matrix-cli.py

#!/Users/workspace/Desktop/projects/ByeIO/software/exp323-kimi-skills/matrix-cli/.venv/bin/python3
"""Matrix CLI - Send messages and files to Matrix chat rooms."""

import argparse
import asyncio
import mimetypes
import os
import sys
from pathlib import Path

from nio import AsyncClient, UploadResponse


DEFAULT_SERVER = "http://xxx.qsbye.cn:xxx"
DEFAULT_USER = "@ollama:xxx.qsbye.cn"
DEFAULT_TOKEN = "你的令牌"
DEFAULT_ROOM = "!你的房间:xxx.qsbye.cn"


async def send_message(client, room_id, message):
    """Send a text message to a Matrix room."""
    await client.room_send(
        room_id=room_id,
        message_type="m.room.message",
        content={
            "msgtype": "m.text",
            "body": message,
        },
    )
    print(f"Message sent to {room_id}")


async def send_file(client, room_id, file_path):
    """Send a file to a Matrix room."""
    file_path = Path(file_path)
    if not file_path.exists():
        print(f"Error: File not found: {file_path}")
        return False

    mime_type, _ = mimetypes.guess_type(str(file_path))
    if mime_type is None:
        mime_type = "application/octet-stream"

    with open(file_path, "rb") as f:
        file_data = f.read()

    upload_response, maybe_keys = await client.upload(
        data_provider=lambda _429, _timeouts: file_data,
        content_type=mime_type,
        filesize=len(file_data),
        filename=file_path.name,
    )

    if isinstance(upload_response, UploadResponse):
        content = {
            "msgtype": "m.file",
            "body": file_path.name,
            "url": upload_response.content_uri,
            "info": {
                "size": len(file_data),
                "mimetype": mime_type,
            },
        }

        # If image, use m.image type
        if mime_type.startswith("image/"):
            content["msgtype"] = "m.image"

        await client.room_send(
            room_id=room_id,
            message_type="m.room.message",
            content=content,
        )
        print(f"File sent: {file_path.name}")
        return True
    else:
        print(f"Error uploading file: {upload_response}")
        return False


async def main():
    parser = argparse.ArgumentParser(description="Send messages/files to Matrix rooms")
    parser.add_argument("-s", "--server", default=DEFAULT_SERVER, help="Matrix homeserver URL")
    parser.add_argument("-u", "--user", default=DEFAULT_USER, help="User ID (@user:server)")
    parser.add_argument("-r", "--room", default=DEFAULT_ROOM, help="Room ID (!room:server)")
    parser.add_argument("-t", "--token", default=DEFAULT_TOKEN, help="Access token")
    parser.add_argument("-p", "--password", help="Password (alternative to token)")
    parser.add_argument("-m", "--message", help="Text message to send")
    parser.add_argument("-f", "--file", help="File path to send")
    parser.add_argument("--test", action="store_true", help="Send test message")

    args = parser.parse_args()

    if not args.message and not args.file and not args.test:
        parser.print_help()
        sys.exit(1)

    client = AsyncClient(args.server, args.user)

    try:
        if args.token:
            client.access_token = args.token
        elif args.password:
            response = await client.login(args.password)
            if hasattr(response, "access_token"):
                client.access_token = response.access_token
                print(f"Logged in, token: {response.access_token}")
            else:
                print(f"Login failed: {response}")
                return
        else:
            print("Error: Provide either --token or --password")
            sys.exit(1)

        if args.test:
            await send_message(client, args.room, "Hello from matrix-cli! 👋")

        if args.message:
            await send_message(client, args.room, args.message)

        if args.file:
            await send_file(client, args.room, args.file)

    finally:
        await client.close()


if __name__ == "__main__":
    asyncio.run(main())

运行效果

  1. 运行python scripts/matrix-cli.py --test发送测试消息到默认房间
  2. 运行python scripts/matrix-cli.py -m "自定义消息"发送文本消息
  3. 运行python scripts/matrix-cli.py -f /path/to/file.png发送文件或图片
posted @ 2026-05-02 20:46  qsBye  阅读(2)  评论(0)    收藏  举报