{"skill":{"slug":"zulip","displayName":"Zulip","summary":"Interact with Zulip chat platform via REST API and Python client. Use when you need to read messages from streams/topics, send messages to channels or users,...","description":"---\nname: zulip\ndescription: Interact with Zulip chat platform via REST API and Python client. Use when you need to read messages from streams/topics, send messages to channels or users, manage DM conversations, list users, or integrate with Zulip organizations for team communication workflows.\n---\n\n# Zulip Integration\n\nInteract with Zulip chat platform for team communication.\n\n## Setup\n\n### 1. Install Python Client\n\n```bash\npip install zulip\n```\n\n### 2. Create Configuration File\n\nCreate `~/.config/zulip/zuliprc`:\n\n```ini\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\n### 3. Verify Connection\n\n```bash\npython scripts/zulip_client.py streams\n```\n\n## Quick Start\n\n### Using the Helper Script\n\nThe `scripts/zulip_client.py` provides common operations:\n\n**List streams:**\n```bash\npython scripts/zulip_client.py streams\npython scripts/zulip_client.py streams --json\n```\n\n**Read messages:**\n```bash\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\n**Note:** Stream names may have descriptions that look like part of the name. Use `--stream-id` for unambiguous identification.\n\n**Send messages:**\n```bash\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\n**List users:**\n```bash\npython scripts/zulip_client.py users\npython scripts/zulip_client.py users --json\n```\n\n### Using Python Client Directly\n\n```python\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```\n\n### Using curl\n\n```bash\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```\n\n## Common Workflows\n\n### Monitor Stream for New Messages\n\n```python\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```\n\n### Reply to Topic\n\n```python\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```\n\n### Search Messages\n\n```python\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```\n\n### Get User ID by Email\n\n```python\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```\n\n## Message Formatting\n\nZulip uses Markdown:\n\n- **Bold:** `**text**`\n- **Italic:** `*text*`\n- **Code:** `` `code` ``\n- **Code block:** ` ```language\\ncode\\n``` `\n- **Quote:** `> quoted text`\n- **Mention user:** `@**Full Name**`\n- **Link stream:** `#**stream-name**`\n- **Link:** `[text](url)`\n\n## Advanced Features\n\n### Upload and Share Files\n\n```python\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```\n\n### React to Messages\n\n```python\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```\n\n## Reference\n\nSee `references/api-quick-reference.md` for complete API documentation, endpoints, and examples.\n\n## Troubleshooting\n\n**Config file not found:**\n- Ensure `~/.config/zulip/zuliprc` exists with correct format\n- Check file permissions (should be readable)\n\n**Authentication failed:**\n- Verify API key is correct\n- Check bot is active in Zulip admin panel\n- Ensure site URL matches organization URL\n\n**Empty messages array:**\n- Bot might not be subscribed to the stream\n- Use `client.get_subscriptions()` to check subscriptions\n- Admin may need to add bot to private streams\n\n**Rate limit errors:**\n- Standard limit: 200 requests/minute\n- Message limit: ~20-30/minute\n- Add delays between bulk operations\n- Check `Retry-After` header on 429 responses\n","tags":{"latest":"1.0.2"},"stats":{"comments":0,"downloads":1328,"installsAllTime":50,"installsCurrent":5,"stars":0,"versions":3},"createdAt":1771231811059,"updatedAt":1778990927720},"latestVersion":{"version":"1.0.2","createdAt":1771232728314,"changelog":"Re-publish with metadata fix","license":null},"metadata":null,"owner":{"handle":"suky57","userId":"s178y9w8rrjyekadr70m74fqmx8851qy","displayName":"suky57","image":"https://avatars.githubusercontent.com/u/8288385?v=4"},"moderation":null}