{
  "schemaVersion": "1.0",
  "item": {
    "slug": "zulip",
    "name": "Zulip",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/suky57/zulip",
    "canonicalUrl": "https://clawhub.ai/suky57/zulip",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/zulip",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=zulip",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "SKILL.md",
      "references/api-quick-reference.md",
      "scripts/zulip_client.py"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-05-07T17:22:31.273Z",
      "expiresAt": "2026-05-14T17:22:31.273Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=afrexai-annual-report",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=afrexai-annual-report",
        "contentDisposition": "attachment; filename=\"afrexai-annual-report-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/zulip"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/zulip",
    "agentPageUrl": "https://openagent3.xyz/skills/zulip/agent",
    "manifestUrl": "https://openagent3.xyz/skills/zulip/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/zulip/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "Zulip Integration",
        "body": "Interact with Zulip chat platform for team communication."
      },
      {
        "title": "1. Install Python Client",
        "body": "pip install zulip"
      },
      {
        "title": "2. Create Configuration File",
        "body": "Create ~/.config/zulip/zuliprc:\n\n[api]\nemail=bot@example.zulipchat.com\nkey=YOUR_API_KEY_HERE\nsite=https://example.zulipchat.com\n\nGet credentials from Zulip admin panel (Settings → Bots)."
      },
      {
        "title": "3. Verify Connection",
        "body": "python scripts/zulip_client.py streams"
      },
      {
        "title": "Using the Helper Script",
        "body": "The scripts/zulip_client.py provides common operations:\n\nList streams:\n\npython scripts/zulip_client.py streams\npython scripts/zulip_client.py streams --json\n\nRead messages:\n\n# Recent stream messages (by name)\npython scripts/zulip_client.py messages --stream \"General\" --num 10\n\n# By stream ID (more reliable, use 'streams' to find IDs)\npython scripts/zulip_client.py messages --stream-id 42 --num 10\n\n# Specific topic\npython scripts/zulip_client.py messages --stream \"General\" --topic \"Updates\"\n\n# Private messages\npython scripts/zulip_client.py messages --type private --num 5\n\n# Mentions\npython scripts/zulip_client.py messages --type mentioned\n\nNote: Stream names may have descriptions that look like part of the name. Use --stream-id for unambiguous identification.\n\nSend messages:\n\n# To stream\npython scripts/zulip_client.py send --type stream --to \"General\" --topic \"Updates\" --content \"Hello!\"\n\n# Private message (user_id)\npython scripts/zulip_client.py send --type private --to 123 --content \"Hi there\"\n\nList users:\n\npython scripts/zulip_client.py users\npython scripts/zulip_client.py users --json"
      },
      {
        "title": "Using Python Client Directly",
        "body": "import zulip\n\nclient = zulip.Client(config_file=\"~/.config/zulip/zuliprc\")\n\n# Read messages\nresult = client.get_messages({\n    \"anchor\": \"newest\",\n    \"num_before\": 10,\n    \"num_after\": 0,\n    \"narrow\": [{\"operator\": \"stream\", \"operand\": \"General\"}]\n})\n\n# Send to stream\nclient.send_message({\n    \"type\": \"stream\",\n    \"to\": \"General\",\n    \"topic\": \"Updates\",\n    \"content\": \"Message text\"\n})\n\n# Send DM\nclient.send_message({\n    \"type\": \"private\",\n    \"to\": [user_id],\n    \"content\": \"Private message\"\n})"
      },
      {
        "title": "Using curl",
        "body": "# List streams\ncurl -u \"bot@example.com:KEY\" https://example.zulipchat.com/api/v1/streams\n\n# Get messages\ncurl -u \"bot@example.com:KEY\" -G \\\n  \"https://example.zulipchat.com/api/v1/messages\" \\\n  --data-urlencode 'anchor=newest' \\\n  --data-urlencode 'num_before=20' \\\n  --data-urlencode 'num_after=0' \\\n  --data-urlencode 'narrow=[{\"operator\":\"stream\",\"operand\":\"General\"}]'\n\n# Send message\ncurl -X POST \"https://example.zulipchat.com/api/v1/messages\" \\\n  -u \"bot@example.com:KEY\" \\\n  --data-urlencode 'type=stream' \\\n  --data-urlencode 'to=General' \\\n  --data-urlencode 'topic=Updates' \\\n  --data-urlencode 'content=Hello!'"
      },
      {
        "title": "Monitor Stream for New Messages",
        "body": "def get_latest_messages(client, stream_name, last_seen_id=None):\n    narrow = [{\"operator\": \"stream\", \"operand\": stream_name}]\n    \n    if last_seen_id:\n        # Get only messages after last seen\n        request = {\n            \"anchor\": last_seen_id,\n            \"num_before\": 0,\n            \"num_after\": 100,\n            \"narrow\": narrow\n        }\n    else:\n        # Get recent messages\n        request = {\n            \"anchor\": \"newest\",\n            \"num_before\": 20,\n            \"num_after\": 0,\n            \"narrow\": narrow\n        }\n    \n    result = client.get_messages(request)\n    return result[\"messages\"]"
      },
      {
        "title": "Reply to Topic",
        "body": "def reply_to_message(client, original_message, reply_text):\n    \"\"\"Reply in the same stream/topic as original message.\"\"\"\n    client.send_message({\n        \"type\": \"stream\",\n        \"to\": original_message[\"display_recipient\"],\n        \"topic\": original_message[\"subject\"],\n        \"content\": reply_text\n    })"
      },
      {
        "title": "Search Messages",
        "body": "def search_messages(client, keyword, stream=None):\n    narrow = [{\"operator\": \"search\", \"operand\": keyword}]\n    \n    if stream:\n        narrow.append({\"operator\": \"stream\", \"operand\": stream})\n    \n    result = client.get_messages({\n        \"anchor\": \"newest\",\n        \"num_before\": 50,\n        \"num_after\": 0,\n        \"narrow\": narrow\n    })\n    \n    return result[\"messages\"]"
      },
      {
        "title": "Get User ID by Email",
        "body": "def get_user_id(client, email):\n    \"\"\"Find user_id by email address.\"\"\"\n    result = client.get_members()\n    \n    for user in result[\"members\"]:\n        if user[\"email\"] == email:\n            return user[\"user_id\"]\n    \n    return None"
      },
      {
        "title": "Message Formatting",
        "body": "Zulip uses Markdown:\n\nBold: **text**\nItalic: *text*\nCode: `code`\nCode block: ```language\\ncode\\n```\nQuote: > quoted text\nMention user: @**Full Name**\nLink stream: #**stream-name**\nLink: [text](url)"
      },
      {
        "title": "Upload and Share Files",
        "body": "with open(\"file.pdf\", \"rb\") as f:\n    result = client.upload_file(f)\n    file_url = result[\"uri\"]\n\n# Share in message\nclient.send_message({\n    \"type\": \"stream\",\n    \"to\": \"General\",\n    \"topic\": \"Files\",\n    \"content\": f\"Check out [this file]({file_url})\"\n})"
      },
      {
        "title": "React to Messages",
        "body": "# Add reaction\nclient.add_reaction({\n    \"message_id\": 123,\n    \"emoji_name\": \"thumbs_up\"\n})\n\n# Remove reaction\nclient.remove_reaction({\n    \"message_id\": 123,\n    \"emoji_name\": \"thumbs_up\"\n})"
      },
      {
        "title": "Reference",
        "body": "See references/api-quick-reference.md for complete API documentation, endpoints, and examples."
      },
      {
        "title": "Troubleshooting",
        "body": "Config file not found:\n\nEnsure ~/.config/zulip/zuliprc exists with correct format\nCheck file permissions (should be readable)\n\nAuthentication failed:\n\nVerify API key is correct\nCheck bot is active in Zulip admin panel\nEnsure site URL matches organization URL\n\nEmpty messages array:\n\nBot might not be subscribed to the stream\nUse client.get_subscriptions() to check subscriptions\nAdmin may need to add bot to private streams\n\nRate limit errors:\n\nStandard limit: 200 requests/minute\nMessage limit: ~20-30/minute\nAdd delays between bulk operations\nCheck Retry-After header on 429 responses"
      }
    ],
    "body": "Zulip Integration\n\nInteract with Zulip chat platform for team communication.\n\nSetup\n1. Install Python Client\npip install zulip\n\n2. Create Configuration File\n\nCreate ~/.config/zulip/zuliprc:\n\n[api]\nemail=bot@example.zulipchat.com\nkey=YOUR_API_KEY_HERE\nsite=https://example.zulipchat.com\n\n\nGet credentials from Zulip admin panel (Settings → Bots).\n\n3. Verify Connection\npython scripts/zulip_client.py streams\n\nQuick Start\nUsing the Helper Script\n\nThe scripts/zulip_client.py provides common operations:\n\nList streams:\n\npython scripts/zulip_client.py streams\npython scripts/zulip_client.py streams --json\n\n\nRead messages:\n\n# Recent stream messages (by name)\npython scripts/zulip_client.py messages --stream \"General\" --num 10\n\n# By stream ID (more reliable, use 'streams' to find IDs)\npython scripts/zulip_client.py messages --stream-id 42 --num 10\n\n# Specific topic\npython scripts/zulip_client.py messages --stream \"General\" --topic \"Updates\"\n\n# Private messages\npython scripts/zulip_client.py messages --type private --num 5\n\n# Mentions\npython scripts/zulip_client.py messages --type mentioned\n\n\nNote: Stream names may have descriptions that look like part of the name. Use --stream-id for unambiguous identification.\n\nSend messages:\n\n# To stream\npython scripts/zulip_client.py send --type stream --to \"General\" --topic \"Updates\" --content \"Hello!\"\n\n# Private message (user_id)\npython scripts/zulip_client.py send --type private --to 123 --content \"Hi there\"\n\n\nList users:\n\npython scripts/zulip_client.py users\npython scripts/zulip_client.py users --json\n\nUsing Python Client Directly\nimport zulip\n\nclient = zulip.Client(config_file=\"~/.config/zulip/zuliprc\")\n\n# Read messages\nresult = client.get_messages({\n    \"anchor\": \"newest\",\n    \"num_before\": 10,\n    \"num_after\": 0,\n    \"narrow\": [{\"operator\": \"stream\", \"operand\": \"General\"}]\n})\n\n# Send to stream\nclient.send_message({\n    \"type\": \"stream\",\n    \"to\": \"General\",\n    \"topic\": \"Updates\",\n    \"content\": \"Message text\"\n})\n\n# Send DM\nclient.send_message({\n    \"type\": \"private\",\n    \"to\": [user_id],\n    \"content\": \"Private message\"\n})\n\nUsing curl\n# List streams\ncurl -u \"bot@example.com:KEY\" https://example.zulipchat.com/api/v1/streams\n\n# Get messages\ncurl -u \"bot@example.com:KEY\" -G \\\n  \"https://example.zulipchat.com/api/v1/messages\" \\\n  --data-urlencode 'anchor=newest' \\\n  --data-urlencode 'num_before=20' \\\n  --data-urlencode 'num_after=0' \\\n  --data-urlencode 'narrow=[{\"operator\":\"stream\",\"operand\":\"General\"}]'\n\n# Send message\ncurl -X POST \"https://example.zulipchat.com/api/v1/messages\" \\\n  -u \"bot@example.com:KEY\" \\\n  --data-urlencode 'type=stream' \\\n  --data-urlencode 'to=General' \\\n  --data-urlencode 'topic=Updates' \\\n  --data-urlencode 'content=Hello!'\n\nCommon Workflows\nMonitor Stream for New Messages\ndef get_latest_messages(client, stream_name, last_seen_id=None):\n    narrow = [{\"operator\": \"stream\", \"operand\": stream_name}]\n    \n    if last_seen_id:\n        # Get only messages after last seen\n        request = {\n            \"anchor\": last_seen_id,\n            \"num_before\": 0,\n            \"num_after\": 100,\n            \"narrow\": narrow\n        }\n    else:\n        # Get recent messages\n        request = {\n            \"anchor\": \"newest\",\n            \"num_before\": 20,\n            \"num_after\": 0,\n            \"narrow\": narrow\n        }\n    \n    result = client.get_messages(request)\n    return result[\"messages\"]\n\nReply to Topic\ndef reply_to_message(client, original_message, reply_text):\n    \"\"\"Reply in the same stream/topic as original message.\"\"\"\n    client.send_message({\n        \"type\": \"stream\",\n        \"to\": original_message[\"display_recipient\"],\n        \"topic\": original_message[\"subject\"],\n        \"content\": reply_text\n    })\n\nSearch Messages\ndef search_messages(client, keyword, stream=None):\n    narrow = [{\"operator\": \"search\", \"operand\": keyword}]\n    \n    if stream:\n        narrow.append({\"operator\": \"stream\", \"operand\": stream})\n    \n    result = client.get_messages({\n        \"anchor\": \"newest\",\n        \"num_before\": 50,\n        \"num_after\": 0,\n        \"narrow\": narrow\n    })\n    \n    return result[\"messages\"]\n\nGet User ID by Email\ndef get_user_id(client, email):\n    \"\"\"Find user_id by email address.\"\"\"\n    result = client.get_members()\n    \n    for user in result[\"members\"]:\n        if user[\"email\"] == email:\n            return user[\"user_id\"]\n    \n    return None\n\nMessage Formatting\n\nZulip uses Markdown:\n\nBold: **text**\nItalic: *text*\nCode: `code`\nCode block: ```language\\ncode\\n```\nQuote: > quoted text\nMention user: @**Full Name**\nLink stream: #**stream-name**\nLink: [text](url)\nAdvanced Features\nUpload and Share Files\nwith open(\"file.pdf\", \"rb\") as f:\n    result = client.upload_file(f)\n    file_url = result[\"uri\"]\n\n# Share in message\nclient.send_message({\n    \"type\": \"stream\",\n    \"to\": \"General\",\n    \"topic\": \"Files\",\n    \"content\": f\"Check out [this file]({file_url})\"\n})\n\nReact to Messages\n# Add reaction\nclient.add_reaction({\n    \"message_id\": 123,\n    \"emoji_name\": \"thumbs_up\"\n})\n\n# Remove reaction\nclient.remove_reaction({\n    \"message_id\": 123,\n    \"emoji_name\": \"thumbs_up\"\n})\n\nReference\n\nSee references/api-quick-reference.md for complete API documentation, endpoints, and examples.\n\nTroubleshooting\n\nConfig file not found:\n\nEnsure ~/.config/zulip/zuliprc exists with correct format\nCheck file permissions (should be readable)\n\nAuthentication failed:\n\nVerify API key is correct\nCheck bot is active in Zulip admin panel\nEnsure site URL matches organization URL\n\nEmpty messages array:\n\nBot might not be subscribed to the stream\nUse client.get_subscriptions() to check subscriptions\nAdmin may need to add bot to private streams\n\nRate limit errors:\n\nStandard limit: 200 requests/minute\nMessage limit: ~20-30/minute\nAdd delays between bulk operations\nCheck Retry-After header on 429 responses"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/suky57/zulip",
    "publisherUrl": "https://clawhub.ai/suky57/zulip",
    "owner": "suky57",
    "version": "1.0.2",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/zulip",
    "downloadUrl": "https://openagent3.xyz/downloads/zulip",
    "agentUrl": "https://openagent3.xyz/skills/zulip/agent",
    "manifestUrl": "https://openagent3.xyz/skills/zulip/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/zulip/agent.md"
  }
}