Skip to content

feat(mail): support sharing emails to IM chats#637

Merged
chanthuang merged 7 commits intomainfrom
worktree-feat+mail-share-to-chat
Apr 24, 2026
Merged

feat(mail): support sharing emails to IM chats#637
chanthuang merged 7 commits intomainfrom
worktree-feat+mail-share-to-chat

Conversation

@chanthuang
Copy link
Copy Markdown
Collaborator

@chanthuang chanthuang commented Apr 23, 2026

Summary

Add +share-to-chat shortcut that shares an email or thread as a card to a Lark IM chat (group or individual).

  • Share a single email or an entire thread to any IM conversation
  • Support multiple receiver ID types: chat_id, open_id, user_id, union_id, email
  • Dry-run mode to preview API calls without executing
  • Input validation for mutually exclusive options and receiver ID types

Usage

# Share a single email to a group chat
lark-cli mail +share-to-chat --message-id <id> --receive-id oc_xxx

# Share an entire thread
lark-cli mail +share-to-chat --thread-id <id> --receive-id oc_xxx

# Share to an individual by email
lark-cli mail +share-to-chat --message-id <id> --receive-id user@example.com --receive-id-type email

Test plan

  • Unit tests for validation logic and receiver ID type coverage
  • E2E verification of email card delivery to group and individual chats

Summary by CodeRabbit

  • New Features
    • Added +share-to-chat shortcut to share an email message or thread to Lark IM as an interactive card; supports message-id or thread-id and multiple recipient ID types.
    • Dry-run mode shows the two-step share-token + send request flow without sending.
  • Documentation
    • CLI examples, required scopes, permission notes, and troubleshooting added.
  • Tests
    • New unit and end-to-end dry-run tests covering validation, success outputs (card_id, im_message_id) and error paths.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new MailShareToChat CLI shortcut (+share-to-chat) to share an email message or thread as a Lark IM card, plus unit and e2e dry-run tests and documentation; implements flag validation, mailbox resolution, share-token creation, and card send flows.

Changes

Cohort / File(s) Summary
Mail Share-to-Chat Implementation
shortcuts/mail/mail_share_to_chat.go
New MailShareToChat shortcut: mutual-exclusion --message-id/--thread-id, required --receive-id + --receive-id-type whitelist, --mailbox resolution, DryRun and Execute flows to create share-token and send card; exports MailShareToChat.
Unit Tests
shortcuts/mail/mail_share_to_chat_test.go
Comprehensive unit tests for validation, DryRun/Execute success paths, error propagation on share-token and send failures, and validReceiveIDTypes checks.
Shortcut Registration
shortcuts/mail/shortcuts.go
Registers MailShareToChat in the mail shortcuts list.
Docs: Domain & Skill
skill-template/domains/mail.md, skills/lark-mail/SKILL.md
Adds draft callback/link security rule and documents +share-to-chat behaviour, required OAuth scopes, CLI examples, and updates API/permission entries (multi_entity.search, etc.).
User Guide / Reference
skills/lark-mail/references/lark-mail-share-to-chat.md
New CLI reference documenting flags, mutual-exclusion rules, dry-run behavior, responses (card_id, im_message_id), workflows, and common errors.
E2E Dry-Run Tests
tests/cli_e2e/mail/mail_share_to_chat_dryrun_test.go
Adds CLI e2e dry-run tests verifying two POST calls (share-token create and send), request endpoints, payload shapes, and receive_id_type across scenarios.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as User/CLI
    participant Validator as Validator
    participant MailAPI as Mail Service
    participant IMAPI as IM Service

    CLI->>Validator: invoke +share-to-chat (flags)
    Validator->>Validator: validate flags (message_id xor thread_id, receive_id/type, mailbox)
    alt validation fails
        Validator-->>CLI: return validation error
    else
        Validator->>MailAPI: POST /mailboxes/{mailbox}/share_token (message_id|thread_id)
        MailAPI-->>Validator: { card_id }
        Validator->>IMAPI: POST /im/messages/send (card_id, receive_id, receive_id_type)
        IMAPI-->>Validator: { im_message_id }
        Validator-->>CLI: output { card_id, im_message_id }
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • infeng
  • zhaoleibd

Poem

🐰 I nibbled a mail and shaped it neat,
A tiny card to send down the street,
Hop, token, bounce — into chat it flew,
card_id glows, im_message_id too! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary feature being added: support for sharing emails to IM chats via the new +share-to-chat shortcut.
Description check ✅ Passed The description is well-structured and covers all required template sections with clear details about functionality, usage examples, and test verification methods.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-feat+mail-share-to-chat

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added domain/mail PR touches the mail domain size/M Single-domain feat or fix with limited business impact labels Apr 23, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
shortcuts/mail/mail_share_to_chat_test.go (2)

103-114: Replace hand-rolled containsStr/searchStr with strings.Contains.

Standard library already provides this; the custom implementation adds noise and an unnecessary len(s) >= len(substr) guard (strings.Contains already handles all cases).

♻️ Proposed diff
 import (
 	"fmt"
+	"strings"
 	"testing"
 )
-				if !containsStr(err.Error(), tt.wantErr) {
+				if !strings.Contains(err.Error(), tt.wantErr) {
 					t.Fatalf("expected error containing %q, got %q", tt.wantErr, err.Error())
 				}
-func containsStr(s, substr string) bool {
-	return len(s) >= len(substr) && searchStr(s, substr)
-}
-
-func searchStr(s, substr string) bool {
-	for i := 0; i <= len(s)-len(substr); i++ {
-		if s[i:i+len(substr)] == substr {
-			return true
-		}
-	}
-	return false
-}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shortcuts/mail/mail_share_to_chat_test.go` around lines 103 - 114, The file
defines custom containsStr and searchStr functions; replace their usage with the
stdlib strings.Contains and delete these two functions: change any call sites
using containsStr(...) to strings.Contains(...), remove searchStr entirely, and
add "strings" to the imports if not already present so the tests compile.

54-101: Tests duplicate validation logic instead of exercising MailShareToChat.Validate.

validateShareToChat is a parallel reimplementation of the real validation in mail_share_to_chat.go. If someone later changes the production Validate (e.g., adds a receive-id-type, tightens a rule, or modifies the error wrapping with output.ErrValidation), these tests will still pass while the shipped behavior silently diverges. The only thing currently covered end-to-end is validReceiveIDTypes (in TestValidReceiveIDTypes).

Additionally, containsStr() and searchStr() reinvent strings.Contains().

Consider rewriting to drive MailShareToChat.Validate through a real *common.RuntimeContext (as other shortcut tests in this package do via cmdutil.TestFactory and runMountedMailShortcut) so assertions exercise the production code path, the output.ErrValidation wrapping, and avoid duplication.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shortcuts/mail/mail_share_to_chat_test.go` around lines 54 - 101, The test
duplicates validation logic via validateShareToChat; replace those table-driven
calls with exercising the real MailShareToChat.Validate path using a runtime
context (e.g., create a MailShareToChat instance and call its Validate with a
*common.RuntimeContext built via cmdutil.TestFactory and runMountedMailShortcut
like other shortcuts in this package), remove the validateShareToChat helper,
and assert errors using strings.Contains on the returned error (verifying
output.ErrValidation wrapping where appropriate) instead of the custom
containsStr/searchStr helpers so the unit test follows production validation
logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@shortcuts/mail/mail_share_to_chat_test.go`:
- Around line 103-114: The file defines custom containsStr and searchStr
functions; replace their usage with the stdlib strings.Contains and delete these
two functions: change any call sites using containsStr(...) to
strings.Contains(...), remove searchStr entirely, and add "strings" to the
imports if not already present so the tests compile.
- Around line 54-101: The test duplicates validation logic via
validateShareToChat; replace those table-driven calls with exercising the real
MailShareToChat.Validate path using a runtime context (e.g., create a
MailShareToChat instance and call its Validate with a *common.RuntimeContext
built via cmdutil.TestFactory and runMountedMailShortcut like other shortcuts in
this package), remove the validateShareToChat helper, and assert errors using
strings.Contains on the returned error (verifying output.ErrValidation wrapping
where appropriate) instead of the custom containsStr/searchStr helpers so the
unit test follows production validation logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 08b5827d-e48d-41ab-8d96-7c9ea20fb938

📥 Commits

Reviewing files that changed from the base of the PR and between 600fa50 and 9a3fae8.

📒 Files selected for processing (6)
  • shortcuts/mail/mail_share_to_chat.go
  • shortcuts/mail/mail_share_to_chat_test.go
  • shortcuts/mail/shortcuts.go
  • skill-template/domains/mail.md
  • skills/lark-mail/SKILL.md
  • skills/lark-mail/references/lark-mail-share-to-chat.md

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 23, 2026

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@b8697bcbfc2c6549b4f12e82645f1c483154727f

🧩 Skill update

Unavailable for this PR because source repo/branch metadata is missing.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 23, 2026

Codecov Report

❌ Patch coverage is 65.00000% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 62.99%. Comparing base (24afe39) to head (b8697bc).
⚠️ Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
shortcuts/mail/mail_share_to_chat.go 66.10% 19 Missing and 1 partial ⚠️
shortcuts/mail/shortcuts.go 0.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #637   +/-   ##
=======================================
  Coverage   62.98%   62.99%           
=======================================
  Files         428      429    +1     
  Lines       37327    37387   +60     
=======================================
+ Hits        23512    23551   +39     
- Misses      11703    11723   +20     
- Partials     2112     2113    +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@shortcuts/mail/mail_share_to_chat_test.go`:
- Around line 88-121: The test TestShareToChatExecuteWithThreadID only checks
for card_002 and doesn't verify om_002 or that the outgoing request used
thread_id, so update the test to assert the response contains "om_002" (same as
the message-id test) and enhance the second httpmock.Stub (the POST to
"/user_mailboxes/me/share_tokens/card_002/send") to capture and assert the
request body includes "thread_id":"t1" (use the httpmock request-capture
facility) to detect regressions where Execute might send message_id instead of
thread_id; keep references to TestShareToChatExecuteWithThreadID,
runMountedMailShortcut, and MailShareToChat when making these changes.
- Around line 1-190: The PR is missing the required dry-run E2E test for the new
MailShareToChat shortcut; add a dry-run E2E test under tests/cli_e2e/dryrun/
that runs the CLI with MailShareToChat and the --dry-run flag while setting
LARKSUITE_CLI_APP_ID, APP_SECRET and BRAND env vars, and assert the generated
request payloads for the create-share-token (messages/share_token) and send-card
(share_tokens/{card_id}/send) calls match the expected structure; implement the
test so it invokes the same command-line invocation used in unit tests (e.g.,
"+share-to-chat" with --message-id/--thread-id and
--receive-id/--receive-id-type) and validates both requests are produced without
actually calling the network.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ec6a2c18-7850-4a20-b46b-bf917087a193

📥 Commits

Reviewing files that changed from the base of the PR and between 9a3fae8 and 06820f1.

📒 Files selected for processing (2)
  • shortcuts/mail/mail_share_to_chat.go
  • shortcuts/mail/mail_share_to_chat_test.go

Comment thread shortcuts/mail/mail_share_to_chat_test.go
Comment thread shortcuts/mail/mail_share_to_chat_test.go
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/cli_e2e/mail/mail_share_to_chat_dryrun_test.go (1)

89-91: Prefer strconv.Itoa(i) over string(rune('0'+i)) for the gjson index.

The string(rune('0'+i)) trick silently produces non-digit runes once i >= 10, which would cause confusing gjson lookups returning empty strings rather than a clear test failure. Since the current test only iterates 2 URLs it works, but it's a footgun for anyone extending wantURLs. Using strconv.Itoa (or fmt.Sprintf) is clearer and future-proof.

♻️ Proposed refactor
 		out := result.Stdout
 		for i, wantURL := range tt.wantURLs {
-			gotMethod := gjson.Get(out, "api."+string(rune('0'+i))+".method").String()
-			gotURL := gjson.Get(out, "api."+string(rune('0'+i))+".url").String()
+			idx := strconv.Itoa(i)
+			gotMethod := gjson.Get(out, "api."+idx+".method").String()
+			gotURL := gjson.Get(out, "api."+idx+".url").String()

And add "strconv" to the imports.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/mail/mail_share_to_chat_dryrun_test.go` around lines 89 - 91,
The test uses string(rune('0'+i)) to build gjson paths which breaks for i>=10;
replace string(rune('0'+i)) with strconv.Itoa(i) when constructing the gjson key
in the loop that reads wantURLs and sets gotMethod/gotURL (the gjson.Get calls),
and add "strconv" to the imports so multi-digit indices produce correct JSON
lookups and clear failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/cli_e2e/mail/mail_share_to_chat_dryrun_test.go`:
- Around line 89-91: The test uses string(rune('0'+i)) to build gjson paths
which breaks for i>=10; replace string(rune('0'+i)) with strconv.Itoa(i) when
constructing the gjson key in the loop that reads wantURLs and sets
gotMethod/gotURL (the gjson.Get calls), and add "strconv" to the imports so
multi-digit indices produce correct JSON lookups and clear failures.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e190ed06-955b-4222-b24e-7053a3fffc03

📥 Commits

Reviewing files that changed from the base of the PR and between 06820f1 and 182f993.

📒 Files selected for processing (1)
  • tests/cli_e2e/mail/mail_share_to_chat_dryrun_test.go

Comment thread shortcuts/mail/mail_share_to_chat.go Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/cli_e2e/mail/mail_share_to_chat_dryrun_test.go`:
- Around line 101-112: Add an explicit assertion that the dry-run emitted
exactly len(tt.wantURLs) API calls before iterating: compute the number of
entries in the JSON API array from result.Stdout (e.g. use gjson.Get(out,
"api").Array() or gjson.Get(out, "api.#") to get the count), compare it to
len(tt.wantURLs), and fail the test if they differ; place this check just before
the existing for-loop that uses out, tt.wantURLs, and gjson.Get so any
unexpected extra/missing calls are caught.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: eae887e6-ccef-4ce5-9f48-d481f5bd7e08

📥 Commits

Reviewing files that changed from the base of the PR and between 182f993 and 931b5c3.

📒 Files selected for processing (2)
  • shortcuts/mail/mail_share_to_chat.go
  • tests/cli_e2e/mail/mail_share_to_chat_dryrun_test.go

Comment thread tests/cli_e2e/mail/mail_share_to_chat_dryrun_test.go
infeng
infeng previously approved these changes Apr 23, 2026
Copy link
Copy Markdown
Collaborator

@infeng infeng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Prior AI review thread resolved after re-checking the current head 097b716288bd3548e7a4682dfd98a3a6aaac5a6b.

Review baseline: base main, head worktree-feat+mail-share-to-chat.

Two-step API (create share token → send card) wrapped in a single
shortcut. Supports message-id/thread-id, five receive-id-type variants
(chat_id, open_id, user_id, union_id, email), and dry-run mode.

Change-Id: Ic7b8c01c0d25fef262f35be92555f1fd019bd679
Co-Authored-By: AI
…edit

Add missing safety rule 8 (draft link rule) to skill-template/domains/mail.md
so it survives regeneration. SKILL.md is now produced by `make gen-skills`
in the registry repo rather than hand-edited.

Change-Id: I9cf3605deae8b6de2042e40819fedc304967e78e
Co-Authored-By: AI
- Add Go doc comments to exported symbols for docstring coverage
- Rewrite tests to exercise MailShareToChat.Validate via RuntimeContext
  instead of duplicating validation logic
- Replace hand-rolled containsStr with strings.Contains
- Add httpmock stubs for execute and error path tests

Change-Id: Ic781494f61e9e844224185844bce7b0c48e8e200
Co-Authored-By: AI
Validate request shape (method, URL, mailbox path) under --dry-run
with fake credentials. Covers message-id, thread-id, and custom
mailbox variants.

Change-Id: Iae87bf141cbe4f312d3e9b1fca4ba175052c5c35
Co-Authored-By: AI
DryRun now mirrors Execute: the share-token POST shows message_id or
thread_id, and the send POST shows receive_id_type and receive_id.
E2E test updated to assert these fields. Also fix strconv.Itoa usage.

Change-Id: I00f8770fd5a12b7354986c5e5077f97cfe5d6653
Change-Id: I47dc6a9a47252dcfb7853737f88dfdaef65a0ae7
Change-Id: I9f4a1a183b55d03f5248eb4adddfddb08037ca95
@chanthuang chanthuang force-pushed the worktree-feat+mail-share-to-chat branch from cd7b849 to b8697bc Compare April 23, 2026 14:44
Copy link
Copy Markdown
Collaborator

@infeng infeng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Re-checked the current head b8697bcbfc2c6549b4f12e82645f1c483154727f; the only follow-up change after the previous review is the dry-run API-count assertion, and the share-to-chat test suite still passes on the current tree.

Review baseline: base main, head worktree-feat+mail-share-to-chat.

@chanthuang chanthuang merged commit 2e7a11a into main Apr 24, 2026
21 checks passed
@chanthuang chanthuang deleted the worktree-feat+mail-share-to-chat branch April 24, 2026 13:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain/mail PR touches the mail domain size/M Single-domain feat or fix with limited business impact

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants