技能备份 - 2026-04-15 (40个技能)
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
# Feishu Post (RichText) Skill
|
||||
|
||||
Send Rich Text (Post) messages to Feishu.
|
||||
This format is distinct from Cards. It supports native rich text elements but is less flexible in layout than cards. It is better for long-form text mixed with images/links.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
node skills/feishu-post/send.js --target "ou_..." --text-file "temp/msg.md" --title "Optional Title"
|
||||
```
|
||||
|
||||
## Options
|
||||
- `-t, --target <id>`: Target ID (user/chat).
|
||||
- `-f, --text-file <path>`: Markdown content file.
|
||||
- `--title <text>`: Title of the post.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Feishu Post (RichText) Skill
|
||||
|
||||
Send Rich Text (Post) messages to Feishu.
|
||||
This format is distinct from Cards. It supports native rich text elements but is less flexible in layout than cards.
|
||||
It is better for long-form text mixed with images/links.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Install `feishu-common` first.
|
||||
- This skill depends on `../feishu-common/index.js` via `utils/feishu-client.js`.
|
||||
|
||||
## Features
|
||||
- **Native Emoji Support**: Automatically converts `[微笑]`, `[得意]` etc. to Feishu native emoji tags.
|
||||
- **Markdown-like Parsing**: Supports simple newlines and paragraphs.
|
||||
- **Rich Text**: Uses Feishu's Post content structure.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
node skills/feishu-post/send.js --target "ou_..." --text-file "temp/msg.md" --title "Optional Title"
|
||||
```
|
||||
|
||||
## Options
|
||||
- `-t, --target <id>`: Target ID (user `ou_...` or chat `oc_...`).
|
||||
- `-x, --text <text>`: Text content (supports `\n` for newlines and `[emoji]` tags).
|
||||
- `-f, --text-file <path>`: Read content from file.
|
||||
- `--title <text>`: Title of the post.
|
||||
- `--reply-to <id>`: Message ID to reply to.
|
||||
|
||||
## Emoji List
|
||||
Supported emojis include: `[微笑]`, `[色]`, `[亲亲]`, `[大哭]`, `[强]`, `[加油]`, and many more.
|
||||
See `emoji-map.js` for the full mapping.
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"ownerId": "kn7apafdj4thknczrgxdzfd2v1808svf",
|
||||
"slug": "feishu-post",
|
||||
"version": "1.1.7",
|
||||
"publishedAt": 1771169184529
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
const { fetchWithAuth } = require('./utils/feishu-client.js');
|
||||
|
||||
async function run() {
|
||||
const msgId = 'om_x100b5747191850a0b2c501a826f753e';
|
||||
console.log('Fetching msg:', msgId);
|
||||
try {
|
||||
const res = await fetchWithAuth(`https://open.feishu.cn/open-apis/im/v1/messages/${msgId}`, { method: 'GET' });
|
||||
const data = await res.json();
|
||||
console.log('Current Msg:', JSON.stringify(data, null, 2));
|
||||
|
||||
if (data.data && data.data.items && data.data.items[0].parent_id) {
|
||||
const parentId = data.data.items[0].parent_id;
|
||||
console.log('Fetching Parent:', parentId);
|
||||
const res2 = await fetchWithAuth(`https://open.feishu.cn/open-apis/im/v1/messages/${parentId}`, { method: 'GET' });
|
||||
const data2 = await res2.json();
|
||||
console.log('Parent Msg:', JSON.stringify(data2, null, 2));
|
||||
}
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
run();
|
||||
@@ -0,0 +1,111 @@
|
||||
module.exports = {
|
||||
// "[色]": "DROOL", // Not in official list
|
||||
"[亲亲]": "KISS",
|
||||
// "[憨笑]": "LAUGH", // Not in official list
|
||||
"[大哭]": "CRY",
|
||||
"[鼓掌]": "CLAP",
|
||||
"[强]": "THUMBSUP",
|
||||
"[害羞]": "SHY",
|
||||
"[闭嘴]": "SHUTUP",
|
||||
// "[睡]": "SLEEP", // Not in official list
|
||||
// "[拥抱]": "HUG", // Not in official list
|
||||
"[得意]": "PROUD",
|
||||
"[发怒]": "ANGRY",
|
||||
// "[惊讶]": "SURPRISED", // Not in official list
|
||||
"[委屈]": "WRONGED",
|
||||
"[吐]": "VOMIT",
|
||||
"[偷笑]": "CHUCKLE",
|
||||
// "[可爱]": "CUTE", // Not in official list
|
||||
"[白眼]": "ROLL_EYES",
|
||||
"[嘘]": "HUSH",
|
||||
"[困]": "DROWSY",
|
||||
"[惊恐]": "FEAR",
|
||||
// "[流汗]": "SWEAT", // Not in official list
|
||||
"[大笑]": "BIG_SMILE",
|
||||
"[奋斗]": "STRIVE",
|
||||
// "[骂人]": "CURSE", // Not in official list
|
||||
// "[疑问]": "DOUBT", // Not in official list
|
||||
"[晕]": "DIZZY",
|
||||
// "[折磨]": "TORMENT", // Not in official list
|
||||
"[衰]": "DECAY",
|
||||
"[骷髅]": "SKULL",
|
||||
"[敲打]": "HAMMER",
|
||||
"[再见]": "BYE",
|
||||
// "[擦汗]": "WIPE", // Not in official list
|
||||
"[抠鼻]": "NOSE_PICK",
|
||||
// "[糗大了]": "EMBARRASSED", // Not in official list
|
||||
"[坏笑]": "SMIRK",
|
||||
// "[左哼哼]": "LEFT_HUM", // Not in official list
|
||||
// "[右哼哼]": "RIGHT_HUM", // Not in official list
|
||||
"[哈欠]": "YAWN",
|
||||
"[鄙视]": "DESPISE",
|
||||
"[快哭了]": "TEARS",
|
||||
// "[阴险]": "SLY", // Not in official list
|
||||
// "[吓]": "SCARE", // Not in official list
|
||||
"[可怜]": "PITIFUL",
|
||||
// "[菜刀]": "KNIFE", // Not in official list
|
||||
// "[西瓜]": "WATERMELON", // Not in official list
|
||||
"[啤酒]": "BEER",
|
||||
"[篮球]": "BASKETBALL",
|
||||
// "[乒乓]": "PINGPONG", // Not in official list
|
||||
"[咖啡]": "COFFEE",
|
||||
// "[饭]": "RICE", // Not in official list
|
||||
// "[猪头]": "PIG", // Not in official list
|
||||
"[玫瑰]": "ROSE",
|
||||
// "[凋谢]": "WILT", // Not in official list
|
||||
// "[示爱]": "LIPS", // Not in official list
|
||||
"[爱心]": "HEART",
|
||||
"[心碎]": "BROKEN_HEART",
|
||||
"[蛋糕]": "CAKE",
|
||||
// "[闪电]": "LIGHTNING", // Not in official list
|
||||
"[炸弹]": "BOMB",
|
||||
"[便便]": "SHIT",
|
||||
// "[月亮]": "MOON", // Not in official list
|
||||
// "[太阳]": "SUN", // Not in official list
|
||||
"[礼物]": "GIFT",
|
||||
// "[弱]": "THUMBSDOWN", // Not in official list
|
||||
"[握手]": "SHAKE",
|
||||
// "[胜利]": "VICTORY", // Not in official list
|
||||
"[抱拳]": "SALUTE",
|
||||
// "[勾引]": "BECKON", // Not in official list
|
||||
// "[拳头]": "FIST", // Not in official list
|
||||
// "[差劲]": "POOR", // Not in official list
|
||||
// "[爱你]": "LOVE_YOU", // Not in official list
|
||||
// "[NO]": "NO", // Not in official list
|
||||
"[OK]": "OK",
|
||||
// "[Love]": "LOVE", // Not in official list
|
||||
"[飞吻]": "BLOW_KISS",
|
||||
// "[跳跳]": "JUMP", // Not in official list
|
||||
// "[发抖]": "TREMBLE", // Not in official list
|
||||
// "[怄火]": "ANNOYED", // Not in official list
|
||||
// "[转圈]": "TWIRL", // Not in official list
|
||||
// "[磕头]": "KOWTOW", // Not in official list
|
||||
// "[回头]": "LOOK_BACK", // Not in official list
|
||||
// "[跳绳]": "SKIP", // Not in official list
|
||||
"[挥手]": "WAVE",
|
||||
// "[激动]": "EXCITED", // Not in official list
|
||||
// "[街舞]": "HIPHOP", // Not in official list
|
||||
// "[献吻]": "KISS_FLY", // Not in official list
|
||||
// "[左太极]": "LEFT_TAIJI", // Not in official list
|
||||
// "[右太极]": "RIGHT_TAIJI", // Not in official list
|
||||
"[加油]": "JIAYOU",
|
||||
"[机智]": "SMART",
|
||||
"[耶]": "YEAH",
|
||||
"[思考]": "THINKING",
|
||||
"[我想静静]": "SILENT",
|
||||
"[捂脸]": "FACEPALM",
|
||||
"[笑哭]": "JOY",
|
||||
"[无语]": "SPEECHLESS",
|
||||
"[我看行]": "LOOKS_GOOD",
|
||||
"[+1]": "PLUS_ONE",
|
||||
"[撒花]": "CELEBRATE",
|
||||
"[什么?]": "WHAT",
|
||||
"[黑线]": "BLACK_LINE",
|
||||
"[灵光一闪]": "IDEA",
|
||||
"[震惊]": "SHOCKED",
|
||||
"[石化]": "PETRIFIED",
|
||||
"[听歌]": "LISTEN_MUSIC",
|
||||
"[摸头]": "PAT_HEAD",
|
||||
"[皱眉]": "FROWN",
|
||||
"[送心]": "SEND_HEART",
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
const { sendPost } = require('../feishu-common/index.js');
|
||||
|
||||
async function main() {
|
||||
const args = require('minimist')(process.argv.slice(2));
|
||||
|
||||
// Usage: node skills/feishu-post/index.js --title "Title" --text "Line1\nLine2" --target "open_id"
|
||||
// Rich Text Format:
|
||||
// Supports markdown-like input in 'text' argument?
|
||||
// Or simple line breaks.
|
||||
// For now, let's keep it simple: Text -> Paragraphs
|
||||
|
||||
const title = args.title || 'Notification';
|
||||
const text = args.text || '';
|
||||
const target = args.target;
|
||||
|
||||
if (!target) {
|
||||
console.error('Error: --target is required');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Convert newlines to multiple paragraph elements
|
||||
const lines = text.split('\\n');
|
||||
const content = [
|
||||
lines.map(line => ({
|
||||
tag: 'text',
|
||||
text: line
|
||||
}))
|
||||
];
|
||||
|
||||
const postContent = {
|
||||
zh_cn: {
|
||||
title: title,
|
||||
content: content
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await sendPost(target, postContent);
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (error) {
|
||||
console.error('Failed to send post:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
Generated
+595
@@ -0,0 +1,595 @@
|
||||
{
|
||||
"name": "feishu-post",
|
||||
"version": "1.1.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "feishu-post",
|
||||
"version": "1.1.6",
|
||||
"dependencies": {
|
||||
"@larksuiteoapi/node-sdk": "^1.58.0",
|
||||
"commander": "^14.0.3",
|
||||
"dotenv": "^17.2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@larksuiteoapi/node-sdk": {
|
||||
"version": "1.58.0",
|
||||
"resolved": "https://registry.npmjs.org/@larksuiteoapi/node-sdk/-/node-sdk-1.58.0.tgz",
|
||||
"integrity": "sha512-NcQNHdGuHOxOWY3bRGS9WldwpbR6+k7Fi0H1IJXDNNmbSrEB/8rLwqHRC8tAbbj/Mp8TWH/v1O+p487m6xskxw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "~1.13.3",
|
||||
"lodash.identity": "^3.0.0",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"lodash.pickby": "^4.6.0",
|
||||
"protobufjs": "^7.2.6",
|
||||
"qs": "^6.13.0",
|
||||
"ws": "^8.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@protobufjs/aspromise": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||
"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/base64": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
|
||||
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/codegen": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
|
||||
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/eventemitter": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
|
||||
"integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/fetch": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
|
||||
"integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@protobufjs/aspromise": "^1.1.1",
|
||||
"@protobufjs/inquire": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@protobufjs/float": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
|
||||
"integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/inquire": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
|
||||
"integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/path": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
|
||||
"integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/pool": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
|
||||
"integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/utf8": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
|
||||
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.0.tgz",
|
||||
"integrity": "sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz",
|
||||
"integrity": "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.4",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bound": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
||||
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"get-intrinsic": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "14.0.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
|
||||
"integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "17.2.3",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
|
||||
"integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-set-tostringtag": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.6",
|
||||
"has-tostringtag": "^1.0.2",
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
|
||||
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.1.1",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"math-intrinsics": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-tostringtag": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash.identity": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.identity/-/lodash.identity-3.0.0.tgz",
|
||||
"integrity": "sha512-AupTIzdLQxJS5wIYUQlgGyk2XRTfGXA+MCghDHqZk0pzUNYvd3EESS6dkChNauNYVIutcb0dfHw1ri9Q1yPV8Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.pickby": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz",
|
||||
"integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/long": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
|
||||
"integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/protobufjs": {
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz",
|
||||
"integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@protobufjs/aspromise": "^1.1.2",
|
||||
"@protobufjs/base64": "^1.1.2",
|
||||
"@protobufjs/codegen": "^2.0.4",
|
||||
"@protobufjs/eventemitter": "^1.1.0",
|
||||
"@protobufjs/fetch": "^1.1.0",
|
||||
"@protobufjs/float": "^1.0.2",
|
||||
"@protobufjs/inquire": "^1.1.0",
|
||||
"@protobufjs/path": "^1.1.2",
|
||||
"@protobufjs/pool": "^1.1.0",
|
||||
"@protobufjs/utf8": "^1.1.0",
|
||||
"@types/node": ">=13.7.0",
|
||||
"long": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.14.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
|
||||
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
||||
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"object-inspect": "^1.13.3",
|
||||
"side-channel-list": "^1.0.0",
|
||||
"side-channel-map": "^1.0.1",
|
||||
"side-channel-weakmap": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-list": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
||||
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"object-inspect": "^1.13.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-map": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
||||
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.5",
|
||||
"object-inspect": "^1.13.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-weakmap": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
||||
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.5",
|
||||
"object-inspect": "^1.13.3",
|
||||
"side-channel-map": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.19.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
|
||||
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "feishu-post",
|
||||
"version": "1.1.6",
|
||||
"description": "Send rich text messages (Post/RichText) to Feishu users/groups. Supports markdown conversion.",
|
||||
"main": "send.js",
|
||||
"dependencies": {
|
||||
"@larksuiteoapi/node-sdk": "^1.58.0",
|
||||
"commander": "^14.0.3",
|
||||
"dotenv": "^17.2.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
#!/usr/bin/env node
|
||||
const { fetchWithAuth } = require('./utils/feishu-client.js');
|
||||
const { parseMarkdownToFeishu } = require('./utils/markdown-parser.js');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// --- Upstream Logic Injection (Simplified) ---
|
||||
// Ported from upstream src/send.ts (m1heng/clawdbot-feishu)
|
||||
// Adapted for our lightweight architecture
|
||||
|
||||
async function sendPost(options) {
|
||||
// Normalize common mis-usage:
|
||||
// - Feishu message_id usually starts with "om_". If user passes it as --target,
|
||||
// interpret it as --reply-to to avoid sending an invalid open_id.
|
||||
if (options && typeof options.target === 'string' && options.target.startsWith('om_') && !options.replyTo) {
|
||||
options.replyTo = options.target;
|
||||
delete options.target;
|
||||
}
|
||||
|
||||
if (options.content && !options.text) {
|
||||
options.text = options.content;
|
||||
}
|
||||
if (options.markdown && !options.text) {
|
||||
options.text = options.markdown;
|
||||
}
|
||||
|
||||
let contentText = options.text || '';
|
||||
if (options.textFile) {
|
||||
try {
|
||||
if (!fs.existsSync(options.textFile)) {
|
||||
throw new Error(`File not found: ${options.textFile}`);
|
||||
}
|
||||
contentText = fs.readFileSync(options.textFile, 'utf8');
|
||||
} catch (e) {
|
||||
throw new Error(`Failed to read message file: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!contentText && !options.content) {
|
||||
throw new Error('No content provided (use --text or --text-file)');
|
||||
}
|
||||
|
||||
// Validate target/replyTo requirements
|
||||
const hasReplyTo = !!options.replyTo;
|
||||
const hasTarget = typeof options.target === 'string' && options.target.length > 0;
|
||||
if (!hasReplyTo && !hasTarget) {
|
||||
throw new Error('Missing target (use --target "ou_..." or "oc_..." or provide --reply-to "om_...")');
|
||||
}
|
||||
|
||||
// Determine ID Type
|
||||
let receiveIdType = 'open_id';
|
||||
if (hasTarget) {
|
||||
if (options.target.startsWith('oc_')) receiveIdType = 'chat_id';
|
||||
else if (options.target.startsWith('ou_')) receiveIdType = 'open_id';
|
||||
else if (options.target.includes('@')) receiveIdType = 'email';
|
||||
else if (options.target.startsWith('om_')) {
|
||||
// Should have been normalized earlier; keep as explicit guard.
|
||||
throw new Error('Invalid target "om_...": message_id cannot be used as receive_id. Use --reply-to "om_..."');
|
||||
} else {
|
||||
throw new Error('Invalid target id. Expected "ou_..." (open_id), "oc_..." (chat_id), or an email address.');
|
||||
}
|
||||
}
|
||||
|
||||
// Build Payload (RichText)
|
||||
// Upstream Logic: Wrap markdown in Post Object
|
||||
|
||||
// Split text by newlines to create paragraphs
|
||||
// Unescape literal \n if passed from command line
|
||||
const rawLines = contentText.replace(/\\n/g, '\n').split(/\r?\n/);
|
||||
const contentBody = rawLines.map(line => parseMarkdownToFeishu(line));
|
||||
|
||||
const postContent = {
|
||||
zh_cn: {
|
||||
title: options.title || '',
|
||||
content: contentBody
|
||||
}
|
||||
};
|
||||
|
||||
const messageBody = {
|
||||
receive_id: options.target,
|
||||
msg_type: 'post',
|
||||
content: JSON.stringify(postContent)
|
||||
};
|
||||
|
||||
// Support Reply (New Feature from Upstream)
|
||||
let url = `https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=${receiveIdType}`;
|
||||
if (options.replyTo) {
|
||||
url = `https://open.feishu.cn/open-apis/im/v1/messages/${options.replyTo}/reply`;
|
||||
delete messageBody.receive_id; // Reply doesn't need receive_id
|
||||
}
|
||||
|
||||
// console.log(`Sending Post to ${options.target}...`);
|
||||
|
||||
try {
|
||||
const res = await fetchWithAuth(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(messageBody)
|
||||
});
|
||||
|
||||
const text = await res.text();
|
||||
let data;
|
||||
|
||||
try {
|
||||
data = JSON.parse(text);
|
||||
} catch (e) {
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}: ${text}`);
|
||||
throw new Error(`Invalid JSON response: ${text.slice(0, 200)}...`);
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
// Check if it's a recoverable error (like invalid emoji)
|
||||
const isEmojiError = data.code === 230001 && data.msg && data.msg.includes("emoji_type is invalid");
|
||||
if (!isEmojiError) {
|
||||
throw new Error(`HTTP ${res.status} ${res.statusText}: ${JSON.stringify(data)}`);
|
||||
}
|
||||
// If it is an emoji error, flow through to the fallback logic below
|
||||
}
|
||||
|
||||
if (data.code !== 0) {
|
||||
// Hotfix: If invalid emoji detected (230001), fallback to plain text stripping emojis
|
||||
if (data.code === 230001 && data.msg && data.msg.includes("emoji_type is invalid")) {
|
||||
console.warn("Detected invalid emoji in Feishu post. Retrying as plain text fallback...");
|
||||
// Fallback strategy: Send as plain text message, which renders emoji codes as text [Code]
|
||||
// This ensures the message gets through even if the specific emoji is not supported.
|
||||
const fallbackBody = {
|
||||
receive_id: messageBody.receive_id,
|
||||
msg_type: 'text',
|
||||
content: JSON.stringify({ text: contentText })
|
||||
};
|
||||
|
||||
// Reuse URL (it might be a reply URL)
|
||||
if (options.replyTo) {
|
||||
delete fallbackBody.receive_id;
|
||||
}
|
||||
|
||||
const res2 = await fetchWithAuth(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(fallbackBody)
|
||||
});
|
||||
|
||||
const text2 = await res2.text();
|
||||
const data2 = JSON.parse(text2);
|
||||
if (data2.code === 0) return data2.data;
|
||||
// If fallback fails, throw original error (or new one)
|
||||
throw new Error(`API Error ${data.code}: ${data.msg} (Fallback also failed: ${data2.code} ${data2.msg})`);
|
||||
}
|
||||
throw new Error(`API Error ${data.code}: ${data.msg}`);
|
||||
}
|
||||
|
||||
// ... (rest of function)
|
||||
|
||||
} catch (e) {
|
||||
console.error(`Send Failed: ${e.message}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// CLI Wrapper
|
||||
if (require.main === module) {
|
||||
const { program } = require('commander');
|
||||
program
|
||||
.option('-t, --target <id>', 'Target ID')
|
||||
.option('-x, --text <text>', 'Text content')
|
||||
.option('-c, --content <text>', 'Content (alias for --text)')
|
||||
.option('-m, --markdown <text>', 'Markdown content (alias for --text)')
|
||||
.option('-f, --text-file <path>', 'File content')
|
||||
.option('--title <text>', 'Title')
|
||||
.option('--reply-to <id>', 'Message ID to reply to')
|
||||
.parse(process.argv);
|
||||
|
||||
const opts = program.opts();
|
||||
|
||||
// Safety: if --text contains shell-mangled content (missing chars),
|
||||
// try to detect and warn. Also support stdin piping as fallback.
|
||||
if (opts.text && !opts.textFile) {
|
||||
// Write to temp file to avoid shell escaping issues with special chars
|
||||
const tmpPath = path.join('/tmp', `feishu_post_${Date.now()}_${Math.random().toString(36).slice(2)}.txt`);
|
||||
fs.writeFileSync(tmpPath, opts.text);
|
||||
opts.textFile = tmpPath;
|
||||
opts.text = undefined;
|
||||
// Clean up after send
|
||||
sendPost(opts).then(() => {
|
||||
try { fs.unlinkSync(tmpPath); } catch (_) {}
|
||||
}).catch((e) => {
|
||||
try { fs.unlinkSync(tmpPath); } catch (_) {}
|
||||
process.exit(1);
|
||||
});
|
||||
} else {
|
||||
sendPost(opts).catch(() => process.exit(1));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { sendPost };
|
||||
@@ -0,0 +1,13 @@
|
||||
function loadFeishuClient() {
|
||||
try {
|
||||
return require("../../feishu-common/index.js");
|
||||
} catch (err) {
|
||||
const depErr = new Error(
|
||||
"Missing dependency: feishu-common. Install feishu-common skill first, then retry.",
|
||||
);
|
||||
depErr.cause = err;
|
||||
throw depErr;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = loadFeishuClient();
|
||||
@@ -0,0 +1,70 @@
|
||||
const emojiMap = require('../emoji-map.js');
|
||||
|
||||
function parseMarkdownToFeishu(text) {
|
||||
const segments = [];
|
||||
|
||||
// Helper to process formatting (Bold/Italic/Code)
|
||||
const processFormatting = (str) => {
|
||||
const res = [];
|
||||
|
||||
// 1. Split by Code: `text`
|
||||
const parts = str.split(/(`[^`]+`)/g);
|
||||
for (const p of parts) {
|
||||
if (p.startsWith('`') && p.endsWith('`') && p.length > 2) {
|
||||
// Feishu Post 'text' tag does not support 'code' style officially.
|
||||
// Reverting to plain text with backticks to ensure visibility.
|
||||
res.push({ tag: 'text', text: p });
|
||||
} else {
|
||||
// 2. Split by Bold: **text**
|
||||
const subParts = p.split(/(\*\*[^*]+\*\*)/g);
|
||||
for (const sp of subParts) {
|
||||
if (sp.startsWith('**') && sp.endsWith('**') && sp.length > 4) {
|
||||
res.push({ tag: 'text', text: sp.slice(2, -2), style: ['bold'] });
|
||||
} else {
|
||||
// 3. Split by Italic: *text*
|
||||
const subSubParts = sp.split(/(\*[^*]+\*)/g);
|
||||
for (const ssp of subSubParts) {
|
||||
if (ssp.startsWith('*') && ssp.endsWith('*') && ssp.length > 2) {
|
||||
res.push({ tag: 'text', text: ssp.slice(1, -1), style: ['italic'] });
|
||||
} else if (ssp) {
|
||||
res.push({ tag: 'text', text: ssp });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
// Regex: Link ([text](url)) OR Emoji ([text])
|
||||
const regex = /(\[[^\]]+\]\([^)]+\))|(\[[^\]]+\])/g;
|
||||
const parts = text.split(regex);
|
||||
|
||||
for (const part of parts) {
|
||||
if (!part) continue;
|
||||
|
||||
// Handle Link
|
||||
if (part.startsWith('[') && part.includes('](') && part.endsWith(')')) {
|
||||
const m = part.match(/^\[(.*?)\]\((.*?)\)$/);
|
||||
if (m) {
|
||||
segments.push({ tag: 'a', text: m[1], href: m[2] });
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Emoji
|
||||
if (part.startsWith('[') && part.endsWith(']')) {
|
||||
if (emojiMap[part]) {
|
||||
segments.push({ tag: 'emotion', emoji_type: emojiMap[part] });
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Text Formatting
|
||||
segments.push(...processFormatting(part));
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
|
||||
module.exports = { parseMarkdownToFeishu };
|
||||
Reference in New Issue
Block a user