msgcli
v0.1.10
Published
Multi-platform messaging CLI - send messages to individuals and groups via DingTalk, with extensible platform support
Maintainers
Readme
msgcli
Multi-platform messaging CLI — send notifications to individuals and groups from your terminal.
Supports DingTalk out of the box. Designed with a clean platform interface so you can add WeChat Work, Lark, Slack, Telegram, or any other messenger with minimal code.
Quick Start
npm install -g msgcli
# Initialize config
msgcli config init
# Set your DingTalk credentials
msgcli config set dingtalk.default.client_id "your-client-id"
msgcli config set dingtalk.default.client_secret "your-client-secret"
msgcli config set dingtalk.default.robot_code "your-robot-code"
msgcli config set dingtalk.default.open_conversation_id "your-group-conversation-id"
# Send a message
msgcli send "Hello from CLI!"Installation
npm (recommended)
# Global install — use `msgcli` anywhere
npm install -g msgcli
# Or run on the fly without installing
npx msgcli send "Hello"Bun (for contributors / local development)
git clone <repo-url> && cd msgcli
bun install
bun link # makes `msgcli` globally available
# Or run directly
bun run src/index.ts send "Hello"Usage
Send a message
msgcli send "Your message here"msgcli send -p dingtalk -t user123 --to-type user "Hi there"msgcli send -p dingtalk -t conv123456 "Group announcement"Send a file
msgcli send -f report.pdf "Check this file"File is uploaded to DingTalk storage and sent as a file message. Works with groups and individual users.
msgcli send -f photo.jpg -t user123 --to-type user
msgcli send -f data.csv -t conv123456Options
| Flag | Short | Description |
|------|-------|-------------|
| --platform | -p | Target platform (default: dingtalk) |
| --to | -t | Recipient — user ID or conversation ID |
| --to-type | | Recipient type: user or group (default: group) |
| --profile | | Config profile name (default: default) |
| --file | -f | Attach a file (uploads to DingTalk and sends as file message) |
| --help | -h | Show help |
Config management
# Initialize empty config
msgcli config init
# Set a value (supports nested keys with dot notation)
msgcli config set dingtalk.default.client_id "your-client-id"
msgcli config set dingtalk.default.client_secret "your-client-secret"
# Get a value
msgcli config get dingtalk.default.client_id
# List all config
msgcli config listConfiguration
Config file location: ~/.config/msgcli/auth.json
Format
{
// Platform name → profile name → key-value pairs
"dingtalk": {
"default": {
"client_id": "your-app-key",
"client_secret": "your-app-secret",
"robot_code": "your-robot-code",
"open_conversation_id": "your-group-conversation-id"
},
"work": {
"client_id": "...",
"client_secret": "...",
"robot_code": "...",
"open_conversation_id": "..."
}
},
"feishu": {
"default": {
"app_id": "your-app-id",
"app_secret": "your-app-secret",
"chat_id": "your-group-chat-id"
}
},
"wechat_work": {
"default": {
"webhook_url": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your-key"
}
},
"slack": {
"default": {
"token": "...",
"channel": "..."
}
}
}Each platform can have multiple named profiles (e.g. default, work, personal). Select one with --profile.
Manual editing
You can edit ~/.config/msgcli/auth.json directly with any text editor:
vim ~/.config/msgcli/auth.jsonPlatform: DingTalk
Prerequisites
- Go to DingTalk Open Platform and create an application.
- Obtain Client ID (AppKey) and Client Secret (AppSecret).
- Create a robot under the application and get the Robot Code.
- For group messages: get the
openConversationIdfrom a group the robot has joined. - For single user messages: know the user's DingTalk user ID.
API Reference
| Action | Endpoint |
|--------|----------|
| Get access token | POST https://api.dingtalk.com/v1.0/oauth2/accessToken |
| Send group message | POST https://api.dingtalk.com/v1.0/robot/groupMessages/send |
| Send single message | POST https://api.dingtalk.com/v1.0/robot/oToMessages/send |
| Upload media | POST https://oapi.dingtalk.com/media/upload |
Examples
# Send to default group (uses open_conversation_id from config)
msgcli send "Daily standup reminder"
# Send to a specific user
msgcli send -t "manager123" --to-type user "Please review the PR"
# Send to a specific group conversation
msgcli send -t "cidXxxXxxxXxxxXxxx==" "Hello group"
# Use a different profile
msgcli send --profile work "Production deploy starting"
# Send a file to the default group
msgcli send -f report.pdf "Monthly report attached"
# Send a file to an individual user
msgcli send -t user123 --to-type user -f photo.jpgPlatform: Feishu (飞书)
Prerequisites
- Go to Feishu Open Platform and create an application.
- Obtain App ID and App Secret from the "Credentials" page.
- Add the Robot capability and grant required permissions:
im:messageim:resource
- For group messages: get the
chat_idfrom a group the bot is a member of.- You can get it via the List Chats API or from the group URL.
- For single user messages: know the user's
open_id(Feishu's user identifier).- Grant
contact:user.base:readonlypermission and use the Get User API.
- Grant
Configuration
msgcli config set feishu.default.app_id "cli_xxxxx"
msgcli config set feishu.default.app_secret "xxxxxxxx"
msgcli config set feishu.default.chat_id "oc_xxxxxxxx"API Reference
| Action | Endpoint |
|--------|----------|
| Get tenant access token | POST https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal |
| Send message | POST https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type={type} |
| Upload file | POST https://open.feishu.cn/open-apis/im/v1/files |
Examples
# Send to default group (uses chat_id from config)
msgcli send -p feishu "Daily standup reminder"
# Send to a specific group chat
msgcli send -p feishu -t "oc_xxx" "Hello group"
# Send to a specific user (by open_id)
msgcli send -p feishu -t "ou_xxx" --to-type user "Direct message"
# Send a file
msgcli send -p feishu -f report.pdf "Monthly report"Platform: WeChat Work (Webhook)
Prerequisites
- Open a group chat in WeChat Work, click Group Settings > Group Robot > Add Robot.
- Choose Custom Robot, give it a name, and save.
- Copy the Webhook URL (format:
https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx).
Configuration
msgcli config set wechat_work.default.webhook_url "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your-key"API Reference
| Action | Endpoint |
|--------|----------|
| Send message | POST {webhook_url} |
| Upload media | POST https://qyapi.weixin.qq.com/cgi-bin/webhook/upload_media?key={key}&type=file |
Limitations
- Webhook robots can only send to the group they are added to (no
--to/--to-typesupport). - Rate limit: 3 messages per second.
- File upload max: 10 MB.
Examples
# Send text message to the group
msgcli send -p wechat_work "Deploy completed"
# Send a file
msgcli send -p wechat_work -f report.pdfExtending: Adding a new platform
Create a new file in src/platforms/ that implements the Platform interface:
// src/platforms/wechat_work.ts
import type { Platform, SendOptions, MessageResult } from "./index.ts"
export const wechat_work: Platform = {
name: "wechat_work",
async sendMessage(
config: Record<string, unknown>,
options: SendOptions
): Promise<MessageResult> {
// Your implementation here
return { success: true, data: {}, platform: "wechat_work" }
},
}Then register it in src/index.ts:
import { wechat_work } from "./platforms/wechat_work.ts"
const PLATFORMS: Record<string, Platform> = {
dingtalk,
wechat_work, // <-- add here
}Users can then configure it under the same profile system:
msgcli config set wechat_work.default.webhook_url "https://qyapi.weixin.qq.com/..."
msgcli send -p wechat_work "Message via WeChat Work"All platforms can be listed with msgcli send --help.
Supported Platforms
| Platform | Identifier | Text | File |
|----------|-----------|------|------|
| DingTalk | dingtalk | ✓ | ✓ |
| Feishu (Lark) | feishu | ✓ | ✓ |
| WeChat Work (Webhook) | wechat_work | ✓ | ✓ |
Architecture
msgcli/
├── package.json # NPM package config, bin, scripts
├── tsconfig.json
├── LICENSE
├── README.md
├── .gitignore
├── scripts/
│ └── build.ts # Build script (bun build -> dist/)
├── src/
│ ├── index.ts # CLI entry point, arg parsing, dispatch
│ ├── config.ts # ~/.config/msgcli/auth.json management
│ └── platforms/
│ ├── index.ts # Platform interface definitions
│ ├── dingtalk.ts # DingTalk implementation
│ └── feishu.ts # Feishu (Lark) implementation
└── dist/ # Build output (published to npm)
└── index.jsHow it works
config.tsloads/stores profiles from~/.config/msgcli/auth.jsonusing dot-notation keys.index.tsparses CLI args, loads the requested platform's profile, and callsplatform.sendMessage().- Each platform module implements the
Platforminterface — it receives a flat config object and send options, performs the API call, and returns aMessageResult.
Development
# Run locally (source)
bun run src/index.ts send "test"
# Build for npm distribution
bun run build
# Test the built bundle
node dist/index.js send "test"
# Type checking
bun run tsc --noEmitPublishing to npm
Prerequisites
- A npm account:
npm adduserornpm login - Node.js >= 18 installed
- Bun installed (for building)
Steps
# 1. Bump version
npm version patch # or minor / major
# 2. Build
bun run build
# 3. Dry run to verify package contents
npm pack --dry-run
# 4. Publish
npm publishThe prepublishOnly script automatically runs bun run build before publish, so you can also just run npm publish directly.
Package contents
The published package includes:
dist/— compiled JS bundle (platform-agnostic, works with Node >= 18)README.md,LICENSE
No npm runtime dependencies — fetch is built into Node 18+.
No bun required at runtime — the published bundle runs on plain Node.js.
CI publish (GitHub Actions)
# .github/workflows/publish.yml
name: Publish to npm
on:
push:
tags: "v*"
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- uses: actions/setup-node@v4
with:
node-version: 22
registry-url: "https://registry.npmjs.org"
- run: bun install
- run: bun run build
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}License
MIT
