--- id: wangyendt/wayne-skills/lark-custom-bot version: "e3c7b7ad" license: MIT install: manual updated: 2026-07-21 --- # pywayne-lark-custom-bot — Pywayne Lark Custom Bot is a lightweight webhook wrapper for pushing messages to Feishu/Lark channels without requiring event listening or two-way interaction. It handles text messages, image uploads, rich text posts, interactive cards, and chat sharing, with optional signature verification for security and @mention support. Ideal for alerts, notifications, scheduled tasks, and one-way notification scenarios. Publisher: wangyendt · Stars: 8 · Updated: 2026-07-21 Install (manual): `git clone https://github.com/wangyendt/wayne-skills` ## SKILL.md # Pywayne Lark Custom Bot - Webhook Message Sender ## Overview `LarkCustomBot` is a webhook-based Feishu (Lark) bot wrapper designed for **one-way message pushing**. It's ideal for scenarios where you only need to send messages to Feishu groups without listening for incoming messages or managing complex interactions. **Key Characteristics**: - Lightweight, simple webhook-based architecture - No event subscription or listening capabilities - Perfect for alerts, notifications, scheduled tasks - Supports signature verification for security **When to Use LarkCustomBot**: - Push-only scenarios (notifications, alerts, reports) - Simple scheduled tasks sending updates - Quick setup without event subscription configuration - Don't need message replies, reactions, or chat management **When to Use LarkBot Instead**: - Need to listen and reply to messages - Require message lifecycle management (recall, edit, reactions) - Need chat management (members, admins, announcements) - Interactive features like button callbacks ## Installation ```bash pip install pywayne ``` ## Quick Start ```python from pywayne.lark_custom_bot import LarkCustomBot # Initialize bot with webhook bot = LarkCustomBot( webhook="https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxxx" ) # Send simple text message bot.send_text("Hello, Feishu!") # Send text with @all mention bot.send_text("Important announcement!", mention_all=True) ``` ## LarkCustomBot Class ### Constructor ```python bot = LarkCustomBot( webhook: str, # Required: Webhook URL from Feishu group bot settings secret: str = '', # Optional: Signing secret for request verification bot_app_id: str = '', # Optional: App ID for image upload authentication bot_secret: str = '' # Optional: App secret for image upload authentication ) ``` **Parameters**: - `webhook`: Webhook URL obtained from Feishu group custom bot settings - `secret`: Signing secret for signature verification (enhances security) - `bot_app_id`: Required for `upload_image()` and `upload_image_from_cv2()` - `bot_secret`: Required for `upload_image()` and `upload_image_from_cv2()` **Note**: Image upload requires app credentials (`bot_app_id` and `bot_secret`) because it uses Feishu's OpenAPI authentication, not webhook. ## Core Methods ### send_text - Send Text Message Send plain text message with optional @all mention. ```python bot.send_text(text: str, mention_all: bool = False) -> None ``` **Parameters**: - `text`: Message text content - `mention_all`: Whether to @all users in the group (default `False`) **Examples**: ```python # Simple text bot.send_text("Daily backup completed successfully") # With @all mention bot.send_text("System maintenance at 23:00 tonight", mention_all=True) # Multi-line text bot.send_text(""" Deployment completed: - API: v1.2.3 - Frontend: v2.4.5 - Database migration: done """) # With HTML-like formatting (supported in text messages) bot.send_text("Bold text and italic text") ``` ### send_post - Send Rich Text Post Send rich text message with structured content including text, links, @mentions, and images. ```python bot.send_post( content: List[List[Dict]], # 2D list of content elements title: Optional[str] = None # Optional post title ) -> None ``` **Parameters**: - `content`: 2D list structure where: - Outer list = multiple lines - Inner list = multiple elements in the same line - `title`: Post title displayed at the top **Content Structure**: ```python content = [ [element1, element2], # Line 1 with 2 elements [element3], # Line 2 with 1 element [element4, element5], # Line 3 with 2 elements ] ``` **Basic Example**: ```python from pywayne.lark_custom_bot import ( LarkCustomBot, create_text_content, create_link_content, create_at_content, create_image_content ) bot = LarkCustomBot( webhook="https://open.feishu.cn/open-apis/bot/v2/hook/xxx", bot_app_id="cli_xxx", bot_secret="sec_xxx" ) # Upload image first image_key = bot.upload_image("/tmp/report.png") # Construct post content content = [ # Line 1: Title text [create_text_content("Daily Report", unescape=False)], # Line 2: Link [create_link_content(href="https://dashboard.example.com", text="View Dashboard")], # Line 3: @mention [create_at_content(user_id="all", user_name="Everyone")], # Line 4: Image [create_image_content(image_key=image_key, width=400, height=300)], # Line 5: Multiple elements in one line [ create_text_content("Status: "), create_text_content("✅ Completed", unescape=True) ] ] bot.send_post(content, title="Daily Operations Report") ``` ### send_image - Send Image Message Send image message using image key. ```python bot.send_image(image_key: str) -> None ``` **Note**: Must upload image first using `upload_image()` or `upload_image_from_cv2()`. **Example**: ```python # Upload and send local image image_key = bot.upload_image("/path/to/chart.png") bot.send_image(image_key) # Upload from OpenCV image import cv2 import numpy as np img = cv2.imread("/path/to/image.jpg") # Process image... image_key = bot.upload_image_from_cv2(img) bot.send_image(image_key) ``` ### send_interactive - Send Interactive Card Send interactive card message with buttons, forms, or other interactive elements. ```python bot.send_interactive(card: Dict) -> None ``` **Parameters**: - `card`: Interactive card JSON structure following Feishu card schema **Example: Simple Notification Card**: ```python card = { "config": {"wide_screen_mode": True}, "header": { "title": {"tag": "plain_text", "content": "Approval Required"}, "template": "red" }, "elements": [ { "tag": "markdown", "content": "**Ticket #1234** is waiting for approval" }, { "tag": "action", "actions": [ { "tag": "button", "text": {"tag": "plain_text", "content": "View Details"}, "type": "primary", "url": "https://example.com/ticket/1234" } ] } ] } bot.send_interactive(card) ``` **Example: Status Card with Multiple Elements**: ```python card = { "header": { "title": {"tag": "plain_text", "content": "Build Status"}, "template": "blue" }, "elements": [ { "tag": "div", "text": {"tag": "lark_md", "content": "**Build #456** completed"} }, { "tag": "hr" }, { "tag": "div", "fields": [ {"is_short": True, "text": {"tag": "lark_md", "content": "**Duration**\n3m 42s"}}, {"is_short": True, "text": {"tag": "lark_md", "content": "**Status**\n✅ Success"}} ] } ] } bot.send_interactive(card) ``` ### send_share_chat - Share Chat Share a chat group as a card. ```python bot.send_share_chat(share_chat_id: str) -> None ``` **Example**: ```python # Share a group chat bot.send_share_chat("oc_a1b2c3d4e5f6g7h8") ``` ## Image Upload Methods ### upload_image - Upload from File Path Upload local image file to Feishu and get image key. ```python image_key = bot.upload_image(file_path: str) -> str ``` **Parameters**: - `file_path`: Local path to image file **Returns**: - `str`: Image key if successful, empty string if failed **Example**: ```python # Upload and send image_key = bot.upload_image("/tmp/screenshot.png") if image_key: bot.send_image(image_key) else: print("Image upload failed") ``` **Requirements**: - Must set `bot_app_id` and `bot_secret` in constructor - File must exist and not be empty - Supported formats: JPEG, PNG, GIF, etc. ### upload_image_from_cv2 - Upload from OpenCV Image Upload image directly from OpenCV/numpy array. ```python image_key = bot.upload_image_from_cv2(cv2_image: np.ndarray) -> str ``` **Parameters**: - `cv2_image`: OpenCV image array (np.ndarray) **Returns**: - `str`: Image key if successful, empty string if failed **Example: Generate and Send Visualization**: ```python import cv2 import numpy as np # Create visualization img = np.zeros((400, 600, 3), dtype=np.uint8) cv2.putText(img, "TEST PASSED", (80, 220), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 0), 5) cv2.rectangle(img, (50, 50), (550, 350), (0, 255, 0), 3) # Upload and send directly image_key = bot.upload_image_from_cv2(img) bot.send_image(image_key) ``` **Example: Process and Send**: ```python import cv2 # Read and process image original = cv2.imread("/input/image.jpg") gray = cv2.cvtColor(original, cv2.COLOR_BGR2GRAY) edges = cv2.Canny(gray, 50, 150) edges_colored = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR) # Upload processed result image_key = bot.upload_image_from_cv2(edges_colored) bot.send_image(image_key) ``` **Use Cases**: - Algorithm result visualization - Real-time monitoring screenshots - Computer vision processing results - Generated charts and plots ## Content Builder Functions These module-level functions help construct `send_post()` content elements. ### create_text_content Create text content element. ```python create_text_content(text: str, unescape: bool = False) -> Dict ``` **Parameters**: - `text`: Text content - `unescape`: Whether to unescape HTML entities (default `False`) **Example**: ```python text_elem = create_text_content("Normal text") unescaped_elem = create_text_content("Bold", unescape=True) ``` ### create_link_content Create hyperlink content element. ```python create_link_content(href: str, text: str) -> Dict ``` **Parameters**: - `href`: URL link - `text`: Display text for the link **Example**: ```python link_elem = create_link_content("https://www.feishu.cn", "Visit Feishu") ``` ### create_at_content Create @mention content element. ```python create_at_content(user_id: str, user_name: str) -> Dict ``` **Parameters**: - `user_id`: User ID or "all" for @everyone - `user_name`: Display name for the mention **Examples**: ```python # @specific user at_user = create_at_content("ou_xxxxxxxxxxxx", "John Doe") # @everyone at_all = create_at_content("all", "Everyone") ``` ### create_image_content Create image content element. ```python create_image_content( image_key: str, width: Optional[int] = None, height: Optional[int] = None ) -> Dict ``` **Parameters**: - `image_key`: Image key obtained from upload - `width`: Optional image display width in pixels - `height`: Optional image display height in pixels **Example**: ```python img_elem = create_image_content( image_key="img_v3_xxxxxxxxxxxx", width=500, height=300 ) ``` ## Common Usage Scenarios ### Scenario 1: Simple Scheduled Notification ```python from pywayne.lark_custom_bot import LarkCustomBot bot = LarkCustomBot(webhook="https://open.feishu.cn/open-apis/bot/v2/hook/xxx") # Daily morning notification bot.send_text("Good morning! Daily inspection started at 09:00") ``` ### Scenario 2: Rich Announcement with Multiple Elements ```python from pywayne.lark_custom_bot import ( LarkCustomBot, create_text_content, create_link_content, create_at_content ) bot = LarkCustomBot(webhook="https://open.feishu.cn/open-apis/bot/v2/hook/xxx") content = [ [create_text_content("Release v1.2.0 Completed", unescape=False)], [create_text_content("New Features:", unescape=False)], [create_text_content(" • API optimization")], [create_text_content(" • Bug fixes")], [create_link_content("https://example.com/release-notes", "View Release Notes")], [ create_at_content("all", "Everyone"), create_text_content(" please review and confirm.") ] ] bot.send_post(content, title="Release Announcement") ``` ### Scenario 3: Upload and Send Local Image ```python bot = LarkCustomBot( webhook="https://open.feishu.cn/open-apis/bot/v2/hook/xxx", bot_app_id="cli_xxx", bot_secret="sec_xxx" ) # Upload image file image_key = bot.upload_image("/tmp/performance_chart.png") if image_key: bot.send_image(image_key) else: bot.send_text("Failed to upload image") ``` ### Scenario 4: OpenCV Processing Pipeline ```python import cv2 import numpy as np bot = LarkCustomBot( webhook="https://open.feishu.cn/open-apis/bot/v2/hook/xxx", bot_app_id="cli_xxx", bot_secret="sec_xxx" ) # Generate test result visualization img = np.zeros((400, 600, 3), dtype=np.uint8) # Add test status status = "PASS" color = (0, 255, 0) # Green cv2.putText(img, status, (120, 220), cv2.FONT_HERSHEY_SIMPLEX, 3, color, 6) cv2.rectangle(img, (30, 30), (570, 370), color, 3) # Upload OpenCV image directly image_key = bot.upload_image_from_cv2(img) bot.send_image(image_key) ``` ### Scenario 5: Combined Post with Text, Image, and Links ```python from pywayne.lark_custom_bot import ( LarkCustomBot, create_text_content, create_link_content, create_image_content ) bot = LarkCustomBot( webhook="https://open.feishu.cn/open-apis/bot/v2/hook/xxx", bot_app_id="cli_xxx", bot_secret="sec_xxx" ) # Upload dashboard screenshot image_key = bot.upload_image("/tmp/dashboard.png") # Construct rich post content = [ [create_text_content("Monitoring Snapshot:")], [create_image_content(image_key, width=600, height=400)], [create_link_content("https://grafana.example.com", "Open Full Dashboard")], [create_text_content("Generated at: 2026-03-12 14:30:00")] ] bot.send_post(content, title="Daily Monitoring Report") ``` ### Scenario 6: Interactive Approval Card ```python card = { "config": {"wide_screen_mode": True}, "header": { "title": {"tag": "plain_text", "content": "Approval Request"}, "template": "orange" }, "elements": [ { "tag": "markdown", "content": "**Deployment Request #5678**\n\nEnvironment: Production\nRequested by: John Doe" }, { "tag": "hr" }, { "tag": "action", "actions": [ { "tag": "button", "text": {"tag": "plain_text", "content": "Approve"}, "type": "primary", "url": "https://example.com/approve/5678" }, { "tag": "button", "text": {"tag": "plain_text", "content": "Reject"}, "type": "danger", "url": "https://example.com/reject/5678" } ] } ] } bot.send_interactive(card) ``` ### Scenario 7: Secure Sending with Signature Verification ```python # Initialize with signature secret bot = LarkCustomBot( webhook="https://open.feishu.cn/open-apis/bot/v2/hook/xxx", secret="your_signing_secret" ) # All messages will automatically include timestamp and signature bot.send_text("This message is signed for security") ``` ### Scenario 8: Multi-Step Operations Report ```python from pywayne.lark_custom_bot import LarkCustomBot, create_text_content, create_link_content bot = LarkCustomBot( webhook="https://open.feishu.cn/open-apis/bot/v2/hook/xxx" ) # Step 1: Start notification bot.send_text("🔄 Nightly inspection started...") # Perform tasks... # ... inspection logic ... # Step 2: Send detailed report content = [ [create_text_content("✅ Nightly Inspection Completed", unescape=True)], [create_text_content("")], [create_text_content("Results:")], [create_text_content(" • Database backup: OK")], [create_text_content(" • Log cleanup: OK")], [create_text_content(" • Health check: OK")], [create_text_content(" • Disk usage: 42%")], [create_text_content("")], [create_link_content("https://monitoring.example.com", "View Detailed Report")] ] bot.send_post(content, title="Inspection Report - 2026-03-12") ``` ### Scenario 9: Share Group Chat Card ```python # Share current group to another channel bot.send_share_chat("oc_a1b2c3d4e5f6g7h8") ``` ### Scenario 10: Monitoring Alert Pipeline ```python import cv2 from pywayne.lark_custom_bot import LarkCustomBot, create_text_content, create_image_content bot = LarkCustomBot( webhook="https://open.feishu.cn/open-apis/bot/v2/hook/xxx", bot_app_id="cli_xxx", bot_secret="sec_xxx" ) def send_alert(alert_type: str, message: str, screenshot_path: str): """Send alert with screenshot to Feishu group""" # Upload screenshot image_key = bot.upload_image(screenshot_path) # Choose emoji based on alert type emoji_map = { "critical": "🔴", "warning": "⚠️", "info": "ℹ️" } emoji = emoji_map.get(alert_type, "📢") # Construct alert message content = [ [create_text_content(f"{emoji} Alert: {alert_type.upper()}", unescape=True)], [create_text_content("")], [create_text_content(message)], [create_image_content(image_key, width=500)] ] bot.send_post(content, title=f"{alert_type.title()} Alert") # Usage send_alert("warning", "CPU usage exceeded 80%", "/tmp/cpu_chart.png") ``` ## Content Builder Functions Reference All helper functions return `Dict` that can be used in `send_post()` content. | Function | Purpose | Key Parameters | |----------|---------|----------------| | `create_text_content` | Plain or formatted text | `text`, `unescape` | | `create_link_content` | Hyperlink | `href`, `text` | | `create_at_content` | @mention user | `user_id`, `user_name` | | `create_image_content` | Embed image | `image_key`, `width`, `height` | ## Error Handling All methods include built-in logging and error handling: ```python import logging # The bot logs all operations # Success: DEBUG level # Errors: ERROR level with details # Enable logging to see detailed output logging.basicConfig(level=logging.DEBUG) bot = LarkCustomBot(webhook="...") bot.send_text("Test message") # Logs: "Message sent successfully" or error details ``` **Common Errors**: 1. **Image upload fails**: - Missing `bot_app_id` or `bot_secret` - Empty image file - Invalid file format - Network issues 2. **Webhook request fails**: - Invalid webhook URL - Signature verification failed (check `secret`) - Rate limiting - Network timeout ## Security Features ### Signature Verification When enabled, bot automatically signs all outgoing requests: ```python bot = LarkCustomBot( webhook="https://open.feishu.cn/open-apis/bot/v2/hook/xxx", secret="your_signing_secret" ) # Automatic signature added to all requests bot.send_text("Signed message") ``` **How it works**: 1. Generates HMAC-SHA256 signature using `timestamp + secret` 2. Includes `timestamp` and `sign` in request body 3. Feishu server verifies signature before processing ## Complete Example: Comprehensive Report ```python from pywayne.lark_custom_bot import ( LarkCustomBot, create_text_content, create_link_content, create_at_content, create_image_content ) import cv2 import numpy as np # Initialize bot with full credentials bot = LarkCustomBot( webhook="https://open.feishu.cn/open-apis/bot/v2/hook/xxx", secret="signing_secret", bot_app_id="cli_xxx", bot_secret="sec_xxx" ) # Generate performance chart chart = np.zeros((300, 500, 3), dtype=np.uint8) cv2.putText(chart, "Performance: 95%", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 255, 0), 2) # Upload chart chart_key = bot.upload_image_from_cv2(chart) # Upload additional screenshot screenshot_key = bot.upload_image("/tmp/dashboard.png") # Construct comprehensive report content = [ [create_text_content("System Health Report", unescape=False)], [create_text_content("="*40)], [create_text_content("")], [create_text_content("📊 Performance Metrics:", unescape=True)], [create_image_content(chart_key, width=500, height=300)], [create_text_content("")], [create_text_content("🖥️ Dashboard Snapshot:", unescape=True)], [create_image_content(screenshot_key, width=600, height=400)], [create_text_content("")], [create_link_content("https://monitoring.example.com", "View Live Monitoring")], [create_text_content("")], [ create_at_content("all", "All Members"), create_text_content(" - Please review the report.") ] ] bot.send_post(content, title="Daily Health Report - 2026-03-12") # Send completion notification bot.send_text("✅ Daily report has been sent", mention_all=False) ``` ## Comparison: LarkCustomBot vs LarkBot | Feature | LarkCustomBot | LarkBot | |---------|---------------|---------| | **Setup** | Simple (webhook only) | Requires app configuration + event subscription | | **Direction** | One-way (send only) | Two-way (send + receive) | | **Use Case** | Notifications, alerts, reports | Interactive bots, auto-reply, chat management | | **Message Types** | Text, post, image, card, share | All types + reply, forward, update, recall | | **Image Upload** | ✅ Yes | ✅ Yes | | **Listen Messages** | ❌ No | ✅ Yes (via LarkBotListener) | | **Reply to Message** | ❌ No | ✅ Yes | | **Reactions** | ❌ No | ✅ Yes | | **Message Recall** | ❌ No | ✅ Yes | | **Chat Management** | ❌ No | ✅ Yes | | **Button Callbacks** | ❌ No | ✅ Yes | **Decision Guide**: - **Use LarkCustomBot** if: You only need to push messages, don't need responses - **Use LarkBot** if: You need any form of two-way interaction or chat management ## Important Notes 1. **Image Upload Requirements**: - Must provide `bot_app_id` and `bot_secret` for image upload - Image upload uses Feishu OpenAPI authentication, not webhook - Empty files will be rejected with error log 2. **Content Structure**: - `send_post()` requires strict 2D list structure - Each inner list represents one line - Mix different content types in the same line 3. **Signature Security**: - Always use `secret` parameter in production for security - Signature is automatically generated and included in requests - Prevents unauthorized webhook access 4. **Interactive Cards**: - `send_interactive()` only sends cards, doesn't handle button clicks - Button callbacks require full app bot with event subscription - For interactive scenarios, migrate to `LarkBot` + `LarkBotListener` 5. **Rate Limiting**: - Feishu may rate-limit webhook requests - Consider adding retry logic for production use - Use `@retry` decorator from `pywayne.tools` for reliability ## Error Handling Best Practices ```python from pywayne.tools import retry from pywayne.lark_custom_bot import LarkCustomBot bot = LarkCustomBot(webhook="https://open.feishu.cn/open-apis/bot/v2/hook/xxx") # Add retry for reliability @retry(max_tries=3, delay=1.0, backoff=2.0) def send_with_retry(message: str): bot.send_text(message) try: send_with_retry("Important notification") except Exception as e: # Fallback notification method print(f"Failed to send after retries: {e}") ``` ## Dependencies ``` requests>=2.25.0 requests-toolbelt>=0.9.0 numpy>=1.19.0 (optional, for upload_image_from_cv2) opencv-python (optional, for upload_image_from_cv2) ``` [View on SkillFed](https://skillfed.io/wangyendt/wayne-skills/lark-custom-bot) · [View on GitHub](https://github.com/wangyendt/wayne-skills)