feat: update MCP config and TOOLS.md with calendar/contacts note
This commit is contained in:
181
.agents/skills/dingtalk-api/scripts/add-approval-comment.ts
Normal file
181
.agents/skills/dingtalk-api/scripts/add-approval-comment.ts
Normal file
@@ -0,0 +1,181 @@
|
||||
// 添加审批评论
|
||||
// 用法: ts-node scripts/add-approval-comment.ts <instanceId> <userId> <comment> [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const { default: dingtalkWorkflow, AddProcessInstanceCommentHeaders, AddProcessInstanceCommentRequest } = require('@alicloud/dingtalk/workflow_1_0');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
const { RuntimeOptions } = require('@alicloud/tea-util');
|
||||
|
||||
interface Result {
|
||||
success: boolean;
|
||||
instanceId: string;
|
||||
userId: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function addApprovalComment(
|
||||
accessToken: string,
|
||||
instanceId: string,
|
||||
userId: string,
|
||||
comment: string,
|
||||
debug: boolean
|
||||
): Promise<void> {
|
||||
const client = new dingtalkWorkflow(createConfig());
|
||||
|
||||
const headers = new AddProcessInstanceCommentHeaders({});
|
||||
headers.xAcsDingtalkAccessToken = accessToken;
|
||||
|
||||
const request = new AddProcessInstanceCommentRequest({
|
||||
processInstanceId: instanceId,
|
||||
commentUserId: userId,
|
||||
text: comment,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.addProcessInstanceCommentWithOptions(
|
||||
request,
|
||||
headers,
|
||||
new RuntimeOptions({})
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const result: Result = {
|
||||
success: response.body?.result?.success || false,
|
||||
instanceId: instanceId,
|
||||
userId: userId,
|
||||
message: '评论已添加',
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || '未知错误',
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function parseArgs(args: string[]): { instanceId: string; userId: string; comment: string; debug: boolean } {
|
||||
let instanceId = '';
|
||||
let userId = '';
|
||||
let comment = '';
|
||||
let debug = false;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
if (arg === '--debug') {
|
||||
debug = true;
|
||||
} else if (!arg.startsWith('--')) {
|
||||
if (!instanceId) {
|
||||
instanceId = arg;
|
||||
} else if (!userId) {
|
||||
userId = arg;
|
||||
} else if (!comment) {
|
||||
comment = arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { instanceId, userId, comment, debug };
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
const { instanceId, userId, comment, debug } = parseArgs(args);
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/add-approval-comment.ts <instanceId> <userId> "<comment>" [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!instanceId || !userId || !comment) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供审批实例 ID(instanceId)、用户 ID(userId)和评论内容(comment)',
|
||||
usage: 'ts-node scripts/add-approval-comment.ts <instanceId> <userId> "<comment>" [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在添加审批评论...');
|
||||
|
||||
await addApprovalComment(accessToken, instanceId, userId, comment, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
205
.agents/skills/dingtalk-api/scripts/create-approval-instance.ts
Normal file
205
.agents/skills/dingtalk-api/scripts/create-approval-instance.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
// 发起审批实例
|
||||
// 用法: ts-node scripts/create-approval-instance.ts <processCode> <originatorUserId> <deptId> <formValuesJson> [--ccList "user1,user2"] [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const { default: dingtalkWorkflow, CreateProcessInstanceHeaders, CreateProcessInstanceRequest } = require('@alicloud/dingtalk/workflow_1_0');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
const { RuntimeOptions } = require('@alicloud/tea-util');
|
||||
|
||||
interface Result {
|
||||
success: boolean;
|
||||
processCode: string;
|
||||
originatorUserId: string;
|
||||
instanceId: string;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function createApprovalInstance(
|
||||
accessToken: string,
|
||||
processCode: string,
|
||||
originatorUserId: string,
|
||||
deptId: string,
|
||||
formComponentValues: any[],
|
||||
ccList: string | undefined,
|
||||
debug: boolean
|
||||
): Promise<void> {
|
||||
const client = new dingtalkWorkflow(createConfig());
|
||||
|
||||
const headers = new CreateProcessInstanceHeaders({});
|
||||
headers.xAcsDingtalkAccessToken = accessToken;
|
||||
|
||||
const request = new CreateProcessInstanceRequest({
|
||||
processCode: processCode,
|
||||
originatorUserId: originatorUserId,
|
||||
deptId: deptId,
|
||||
formComponentValues: formComponentValues,
|
||||
ccList: ccList,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.createProcessInstanceWithOptions(
|
||||
request,
|
||||
headers,
|
||||
new RuntimeOptions({})
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const result: Result = {
|
||||
success: true,
|
||||
processCode: processCode,
|
||||
originatorUserId: originatorUserId,
|
||||
instanceId: response.body?.result?.processInstanceId || '',
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || '未知错误',
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function parseArgs(args: string[]): { processCode: string; originatorUserId: string; deptId: string; formValuesJson: string; ccList?: string; debug: boolean } {
|
||||
let processCode = '';
|
||||
let originatorUserId = '';
|
||||
let deptId = '';
|
||||
let formValuesJson = '';
|
||||
let ccList: string | undefined;
|
||||
let debug = false;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
if (arg === '--debug') {
|
||||
debug = true;
|
||||
} else if (arg === '--ccList' && i + 1 < args.length) {
|
||||
ccList = args[++i];
|
||||
} else if (!arg.startsWith('--')) {
|
||||
if (!processCode) {
|
||||
processCode = arg;
|
||||
} else if (!originatorUserId) {
|
||||
originatorUserId = arg;
|
||||
} else if (!deptId) {
|
||||
deptId = arg;
|
||||
} else if (!formValuesJson) {
|
||||
formValuesJson = arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { processCode, originatorUserId, deptId, formValuesJson, ccList, debug };
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
const { processCode, originatorUserId, deptId, formValuesJson, ccList, debug } = parseArgs(args);
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/create-approval-instance.ts <processCode> <originatorUserId> <deptId> \'[{"name":"标题","value":"请假申请"}]\' [--ccList "user1,user2"] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!processCode || !originatorUserId || !deptId || !formValuesJson) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供 processCode, originatorUserId, deptId 和 formValuesJson',
|
||||
usage: 'ts-node scripts/create-approval-instance.ts <processCode> <originatorUserId> <deptId> \'[{"name":"标题","value":"请假申请"}]\' [--ccList "user1,user2"] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let formComponentValues: any[];
|
||||
try {
|
||||
formComponentValues = JSON.parse(formValuesJson);
|
||||
} catch (e) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_JSON',
|
||||
message: 'formValuesJson 参数不是有效的 JSON 字符串',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在发起审批实例...');
|
||||
|
||||
await createApprovalInstance(accessToken, processCode, originatorUserId, deptId, formComponentValues, ccList, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
205
.agents/skills/dingtalk-api/scripts/execute-approval-task.ts
Normal file
205
.agents/skills/dingtalk-api/scripts/execute-approval-task.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
// 执行审批任务(同意/拒绝)
|
||||
// 用法: ts-node scripts/execute-approval-task.ts <instanceId> <userId> <result> [--taskId <taskId>] [--remark "审批意见"] [--debug]
|
||||
// result: agree 或 refuse
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const { default: dingtalkWorkflow, ExecuteTaskHeaders, ExecuteTaskRequest } = require('@alicloud/dingtalk/workflow_1_0');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
const { RuntimeOptions } = require('@alicloud/tea-util');
|
||||
|
||||
interface Result {
|
||||
success: boolean;
|
||||
instanceId: string;
|
||||
userId: string;
|
||||
action: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function executeApprovalTask(
|
||||
accessToken: string,
|
||||
instanceId: string,
|
||||
taskId: string | undefined,
|
||||
userId: string,
|
||||
result: string,
|
||||
remark: string | undefined,
|
||||
debug: boolean
|
||||
): Promise<void> {
|
||||
const client = new dingtalkWorkflow(createConfig());
|
||||
|
||||
const headers = new ExecuteTaskHeaders({});
|
||||
headers.xAcsDingtalkAccessToken = accessToken;
|
||||
|
||||
const request = new ExecuteTaskRequest({
|
||||
processInstanceId: instanceId,
|
||||
taskId: taskId,
|
||||
actionerUserId: userId,
|
||||
result: result,
|
||||
remark: remark || '',
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.executeTaskWithOptions(
|
||||
request,
|
||||
headers,
|
||||
new RuntimeOptions({})
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const res: Result = {
|
||||
success: response.body?.result?.success || false,
|
||||
instanceId: instanceId,
|
||||
userId: userId,
|
||||
action: result,
|
||||
message: result === 'agree' ? '已同意审批' : '已拒绝审批',
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(res, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || '未知错误',
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function parseArgs(args: string[]): { instanceId: string; taskId?: string; userId: string; result: string; remark?: string; debug: boolean } {
|
||||
let instanceId = '';
|
||||
let taskId: string | undefined;
|
||||
let userId = '';
|
||||
let result = '';
|
||||
let remark: string | undefined;
|
||||
let debug = false;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
if (arg === '--debug') {
|
||||
debug = true;
|
||||
} else if (arg === '--taskId' && i + 1 < args.length) {
|
||||
taskId = args[++i];
|
||||
} else if (arg === '--remark' && i + 1 < args.length) {
|
||||
remark = args[++i];
|
||||
} else if (!arg.startsWith('--')) {
|
||||
if (!instanceId) {
|
||||
instanceId = arg;
|
||||
} else if (!userId) {
|
||||
userId = arg;
|
||||
} else if (!result) {
|
||||
result = arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { instanceId, taskId, userId, result, remark, debug };
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
const { instanceId, taskId, userId, result, remark, debug } = parseArgs(args);
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/execute-approval-task.ts <instanceId> <userId> <agree|refuse> [--taskId <taskId>] [--remark "审批意见"] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!instanceId || !userId || !result) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供审批实例 ID(instanceId)、用户 ID(userId)和审批结果(agree/refuse)',
|
||||
usage: 'ts-node scripts/execute-approval-task.ts <instanceId> <userId> <agree|refuse> [--taskId <taskId>] [--remark "审批意见"] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (result !== 'agree' && result !== 'refuse') {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_RESULT',
|
||||
message: '审批结果必须是 agree(同意)或 refuse(拒绝)',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在执行审批任务...');
|
||||
|
||||
await executeApprovalTask(accessToken, instanceId, taskId, userId, result, remark, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
169
.agents/skills/dingtalk-api/scripts/get-approval-instance.ts
Normal file
169
.agents/skills/dingtalk-api/scripts/get-approval-instance.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
// 获取审批实例详情
|
||||
// 用法: ts-node scripts/get-approval-instance.ts <instanceId> [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const { default: dingtalkWorkflow, GetProcessInstanceHeaders, GetProcessInstanceRequest } = require('@alicloud/dingtalk/workflow_1_0');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
const { RuntimeOptions } = require('@alicloud/tea-util');
|
||||
|
||||
interface Result {
|
||||
success: boolean;
|
||||
instanceId: string;
|
||||
instance: {
|
||||
processInstanceId: string;
|
||||
title?: string;
|
||||
createTimeGMT?: string;
|
||||
finishTimeGMT?: string;
|
||||
originatorUserId?: string;
|
||||
originatorDeptId?: string;
|
||||
status?: string;
|
||||
processCode?: string;
|
||||
formComponentValues?: any[];
|
||||
operationRecords?: any[];
|
||||
tasks?: any[];
|
||||
};
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
instanceId: string;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function getApprovalInstance(
|
||||
accessToken: string,
|
||||
instanceId: string,
|
||||
debug: boolean
|
||||
): Promise<void> {
|
||||
const client = new dingtalkWorkflow(createConfig());
|
||||
|
||||
const headers = new GetProcessInstanceHeaders({});
|
||||
headers.xAcsDingtalkAccessToken = accessToken;
|
||||
|
||||
const request = new GetProcessInstanceRequest({
|
||||
processInstanceId: instanceId,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getProcessInstanceWithOptions(
|
||||
request,
|
||||
headers,
|
||||
new RuntimeOptions({})
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const result: Result = {
|
||||
success: true,
|
||||
instanceId: instanceId,
|
||||
instance: response.body?.result || {},
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
instanceId: instanceId,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || '未知错误',
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const debug = args.includes('--debug');
|
||||
const filteredArgs = args.filter(arg => arg !== '--debug');
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/get-approval-instance.ts <instanceId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (filteredArgs.length < 1) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供审批实例 ID(instanceId)',
|
||||
usage: 'ts-node scripts/get-approval-instance.ts <instanceId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const instanceId = filteredArgs[0];
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在查询审批实例详情...');
|
||||
|
||||
await getApprovalInstance(accessToken, instanceId, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
208
.agents/skills/dingtalk-api/scripts/get-bot-list.ts
Normal file
208
.agents/skills/dingtalk-api/scripts/get-bot-list.ts
Normal file
@@ -0,0 +1,208 @@
|
||||
// 获取群内机器人列表脚本
|
||||
// 用法: ts-node scripts/get-bot-list.ts <openConversationId>
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const { default: dingtalkRobot, GetBotListInGroupHeaders, GetBotListInGroupRequest } = require('@alicloud/dingtalk/robot_1_0');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
const { RuntimeOptions } = require('@alicloud/tea-util');
|
||||
|
||||
interface BotInfo {
|
||||
robotCode: string;
|
||||
robotName: string;
|
||||
robotAvatar: string;
|
||||
openRobotType?: number;
|
||||
}
|
||||
|
||||
interface GetBotListResult {
|
||||
success: boolean;
|
||||
openConversationId: string;
|
||||
botList: BotInfo[];
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
openConversationId: string;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
description: string | null;
|
||||
requestId: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建钉钉客户端配置
|
||||
* @returns Config 实例
|
||||
*/
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Access Token
|
||||
* @param appKey 应用 Key
|
||||
* @param appSecret 应用 Secret
|
||||
* @returns Access Token
|
||||
*/
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群内机器人列表
|
||||
* @param accessToken Access Token
|
||||
* @param openConversationId 开放会话 ID
|
||||
* @param debug 是否开启调试模式
|
||||
*/
|
||||
async function getBotList(accessToken: string, openConversationId: string, debug: boolean = false): Promise<void> {
|
||||
const client = new dingtalkRobot(createConfig());
|
||||
|
||||
const headers = new GetBotListInGroupHeaders({});
|
||||
headers.xAcsDingtalkAccessToken = accessToken;
|
||||
|
||||
const request = new GetBotListInGroupRequest({
|
||||
openConversationId: openConversationId,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getBotListInGroupWithOptions(
|
||||
request,
|
||||
headers,
|
||||
new RuntimeOptions({})
|
||||
);
|
||||
|
||||
// 调试模式:输出完整响应
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('响应 body:', JSON.stringify(response.body, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
// 格式化输出结果
|
||||
// API 返回的是 chatbotInstanceVOList,不是 botList
|
||||
const botList = response.body?.chatbotInstanceVOList || [];
|
||||
const result: GetBotListResult = {
|
||||
success: true,
|
||||
openConversationId: openConversationId,
|
||||
botList: botList.map((bot: any) => ({
|
||||
robotCode: bot.robotCode,
|
||||
robotName: bot.name,
|
||||
robotAvatar: bot.downloadIconURL,
|
||||
openRobotType: bot.openRobotType,
|
||||
})),
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
|
||||
// 如果列表为空,给出提示
|
||||
if (botList.length === 0) {
|
||||
console.error('\n⚠️ 返回的机器人列表为空,可能的原因:');
|
||||
console.error(' 1. 应用没有 "Robot.Read" 或 "机器人信息读取" 权限');
|
||||
console.error(' 2. openConversationId 不正确(请确认是当前应用可访问的群)');
|
||||
console.error(' 3. 群内机器人不是通过当前企业内部应用创建的');
|
||||
console.error(' 4. 群内机器人是 webhook 类型的自定义机器人(此类机器人不通过此 API 返回)');
|
||||
console.error('\n 排查建议:');
|
||||
console.error(' • 在钉钉开放平台检查应用权限(权限管理 -> 机器人相关权限)');
|
||||
console.error(' • 使用 --debug 参数查看完整响应');
|
||||
console.error(' • 确认 openConversationId 是通过当前应用的接口获取的');
|
||||
}
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
openConversationId: openConversationId,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || '未知错误',
|
||||
description: err.description || null,
|
||||
requestId: err.requestId || null,
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 主函数
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
// 检查是否有调试参数
|
||||
const debug = args.includes('--debug');
|
||||
const filteredArgs = args.filter(arg => arg !== '--debug');
|
||||
|
||||
// 从环境变量读取配置
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/get-bot-list.ts "<openConversationId>" [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (filteredArgs.length < 1) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供 openConversationId(开放会话 ID)',
|
||||
usage: 'ts-node scripts/get-bot-list.ts "<openConversationId>" [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const openConversationId = filteredArgs[0];
|
||||
|
||||
try {
|
||||
// 自动获取 access_token
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在获取群内机器人列表...');
|
||||
|
||||
// 使用获取到的 token 获取机器人列表
|
||||
await getBotList(accessToken, openConversationId, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
227
.agents/skills/dingtalk-api/scripts/get-department.ts
Normal file
227
.agents/skills/dingtalk-api/scripts/get-department.ts
Normal file
@@ -0,0 +1,227 @@
|
||||
// 获取部门详情脚本
|
||||
// 用法: ts-node scripts/get-department.ts <deptId> [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const https = require('https');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
|
||||
interface DepartmentInfo {
|
||||
deptId: number;
|
||||
name: string;
|
||||
parentId: number;
|
||||
order?: number;
|
||||
createDeptGroup?: boolean;
|
||||
autoAddUser?: boolean;
|
||||
deptGroupChatId?: string;
|
||||
brief?: string;
|
||||
deptManagerUseridList?: string[];
|
||||
outerDept?: boolean;
|
||||
}
|
||||
|
||||
interface SuccessResult {
|
||||
success: boolean;
|
||||
department: DepartmentInfo;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
deptId: number;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建钉钉客户端配置
|
||||
* @returns Config 实例
|
||||
*/
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Access Token
|
||||
* @param appKey 应用 Key
|
||||
* @param appSecret 应用 Secret
|
||||
* @returns Access Token
|
||||
*/
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用钉钉新版 API 调用函数
|
||||
*/
|
||||
async function dingtalkRequest(accessToken: string, method: string, path: string, body?: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: 'oapi.dingtalk.com',
|
||||
path: `${path}?access_token=${accessToken}`,
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
} as Record<string, string>,
|
||||
};
|
||||
const req = https.request(options, (res: any) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk: string) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (parsed.errcode !== undefined && parsed.errcode !== 0) {
|
||||
reject({ code: parsed.errcode, message: parsed.errmsg });
|
||||
} else if (res.statusCode && res.statusCode >= 400) {
|
||||
reject(parsed);
|
||||
} else {
|
||||
resolve(parsed);
|
||||
}
|
||||
} catch {
|
||||
reject(new Error(`Invalid JSON response: ${data}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
if (body) req.write(JSON.stringify(body));
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门详情
|
||||
* @param accessToken Access Token
|
||||
* @param deptId 部门 ID
|
||||
* @param debug 是否开启调试模式
|
||||
*/
|
||||
async function getDepartment(accessToken: string, deptId: number, debug: boolean = false): Promise<void> {
|
||||
try {
|
||||
// 钉钉 TOP API: POST /topapi/v2/department/get
|
||||
// 请求体: { "dept_id": number }
|
||||
const response = await dingtalkRequest(
|
||||
accessToken,
|
||||
'POST',
|
||||
'/topapi/v2/department/get',
|
||||
{ dept_id: deptId }
|
||||
);
|
||||
|
||||
// 调试模式:输出完整响应
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const result: SuccessResult = {
|
||||
success: true,
|
||||
department: response.result,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
deptId: deptId,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || err.msg || JSON.stringify(err),
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 主函数
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
// 检查是否有调试参数
|
||||
const debug = args.includes('--debug');
|
||||
const filteredArgs = args.filter(arg => arg !== '--debug');
|
||||
|
||||
// 从环境变量读取配置
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/get-department.ts <deptId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (filteredArgs.length < 1) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供部门 ID(deptId)',
|
||||
usage: 'ts-node scripts/get-department.ts <deptId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const deptId = parseInt(filteredArgs[0], 10);
|
||||
if (isNaN(deptId)) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:deptId 必须是数字',
|
||||
usage: 'ts-node scripts/get-department.ts <deptId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
// 自动获取 access_token
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在获取部门详情...');
|
||||
|
||||
// 使用获取到的 token 获取部门详情
|
||||
await getDepartment(accessToken, deptId, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
178
.agents/skills/dingtalk-api/scripts/get-user-by-mobile.ts
Normal file
178
.agents/skills/dingtalk-api/scripts/get-user-by-mobile.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
// 根据手机号查询用户
|
||||
// 用法: ts-node scripts/get-user-by-mobile.ts <mobile> [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const https = require('https');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
|
||||
interface SuccessResult {
|
||||
success: boolean;
|
||||
mobile: string;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
mobile: string;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function dingtalkRequest(accessToken: string, method: string, path: string, body?: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: 'oapi.dingtalk.com',
|
||||
path: `${path}?access_token=${accessToken}`,
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
} as Record<string, string>,
|
||||
};
|
||||
const req = https.request(options, (res: any) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk: string) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (parsed.errcode !== undefined && parsed.errcode !== 0) {
|
||||
reject({ code: parsed.errcode, message: parsed.errmsg });
|
||||
} else if (res.statusCode && res.statusCode >= 400) {
|
||||
reject(parsed);
|
||||
} else {
|
||||
resolve(parsed);
|
||||
}
|
||||
} catch {
|
||||
reject(new Error(`Invalid JSON response: ${data}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
if (body) req.write(JSON.stringify(body));
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function getUserByMobile(accessToken: string, mobile: string, debug: boolean = false): Promise<void> {
|
||||
try {
|
||||
const response = await dingtalkRequest(
|
||||
accessToken,
|
||||
'POST',
|
||||
'/topapi/v2/user/getbymobile',
|
||||
{ mobile: mobile }
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const result: SuccessResult = {
|
||||
success: true,
|
||||
mobile: mobile,
|
||||
userId: response.result?.userid,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
mobile: mobile,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || err.msg || JSON.stringify(err),
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const debug = args.includes('--debug');
|
||||
const filteredArgs = args.filter(arg => arg !== '--debug');
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/get-user-by-mobile.ts <mobile> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (filteredArgs.length < 1) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供手机号(mobile)',
|
||||
usage: 'ts-node scripts/get-user-by-mobile.ts <mobile> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const mobile = filteredArgs[0];
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在根据手机号查询用户...');
|
||||
|
||||
await getUserByMobile(accessToken, mobile, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
178
.agents/skills/dingtalk-api/scripts/get-user-by-unionid.ts
Normal file
178
.agents/skills/dingtalk-api/scripts/get-user-by-unionid.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
// 根据unionid获取用户userid
|
||||
// 用法: ts-node scripts/get-user-by-unionid.ts <unionid> [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const https = require('https');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
|
||||
interface SuccessResult {
|
||||
success: boolean;
|
||||
unionid: string;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
unionid: string;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function dingtalkRequest(accessToken: string, method: string, path: string, body?: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: 'oapi.dingtalk.com',
|
||||
path: `${path}?access_token=${accessToken}`,
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
} as Record<string, string>,
|
||||
};
|
||||
const req = https.request(options, (res: any) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk: string) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (parsed.errcode !== undefined && parsed.errcode !== 0) {
|
||||
reject({ code: parsed.errcode, message: parsed.errmsg });
|
||||
} else if (res.statusCode && res.statusCode >= 400) {
|
||||
reject(parsed);
|
||||
} else {
|
||||
resolve(parsed);
|
||||
}
|
||||
} catch {
|
||||
reject(new Error(`Invalid JSON response: ${data}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
if (body) req.write(JSON.stringify(body));
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function getUserByUnionid(accessToken: string, unionid: string, debug: boolean = false): Promise<void> {
|
||||
try {
|
||||
const response = await dingtalkRequest(
|
||||
accessToken,
|
||||
'POST',
|
||||
'/topapi/user/getbyunionid',
|
||||
{ unionid: unionid }
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const result: SuccessResult = {
|
||||
success: true,
|
||||
unionid: unionid,
|
||||
userId: response.result?.userid,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
unionid: unionid,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || err.msg || JSON.stringify(err),
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const debug = args.includes('--debug');
|
||||
const filteredArgs = args.filter(arg => arg !== '--debug');
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/get-user-by-unionid.ts <unionid> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (filteredArgs.length < 1) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供 unionid',
|
||||
usage: 'ts-node scripts/get-user-by-unionid.ts <unionid> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const unionid = filteredArgs[0];
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在根据 unionid 查询用户...');
|
||||
|
||||
await getUserByUnionid(accessToken, unionid, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
162
.agents/skills/dingtalk-api/scripts/get-user-count.ts
Normal file
162
.agents/skills/dingtalk-api/scripts/get-user-count.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
// 获取员工人数
|
||||
// 用法: ts-node scripts/get-user-count.ts [--onlyActive] [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const https = require('https');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
|
||||
interface SuccessResult {
|
||||
success: boolean;
|
||||
onlyActive: boolean;
|
||||
count: number;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function dingtalkRequest(accessToken: string, method: string, path: string, body?: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: 'oapi.dingtalk.com',
|
||||
path: `${path}?access_token=${accessToken}`,
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
} as Record<string, string>,
|
||||
};
|
||||
const req = https.request(options, (res: any) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk: string) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (parsed.errcode !== undefined && parsed.errcode !== 0) {
|
||||
reject({ code: parsed.errcode, message: parsed.errmsg });
|
||||
} else if (res.statusCode && res.statusCode >= 400) {
|
||||
reject(parsed);
|
||||
} else {
|
||||
resolve(parsed);
|
||||
}
|
||||
} catch {
|
||||
reject(new Error(`Invalid JSON response: ${data}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
if (body) req.write(JSON.stringify(body));
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function getUserCount(accessToken: string, onlyActive: boolean, debug: boolean = false): Promise<void> {
|
||||
try {
|
||||
const response = await dingtalkRequest(
|
||||
accessToken,
|
||||
'POST',
|
||||
'/topapi/user/count',
|
||||
{ only_active: onlyActive }
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const result: SuccessResult = {
|
||||
success: true,
|
||||
onlyActive: onlyActive,
|
||||
count: response.result?.count || 0,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || err.msg || JSON.stringify(err),
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const debug = args.includes('--debug');
|
||||
const onlyActive = args.includes('--onlyActive');
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/get-user-count.ts [--onlyActive] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在获取员工人数...');
|
||||
|
||||
await getUserCount(accessToken, onlyActive, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
155
.agents/skills/dingtalk-api/scripts/get-user-todo-count.ts
Normal file
155
.agents/skills/dingtalk-api/scripts/get-user-todo-count.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
// 获取用户待审批数量
|
||||
// 用法: ts-node scripts/get-user-todo-count.ts <userId> [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const { default: dingtalkWorkflow, GetTodoNumHeaders, GetTodoNumRequest } = require('@alicloud/dingtalk/workflow_1_0');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
const { RuntimeOptions } = require('@alicloud/tea-util');
|
||||
|
||||
interface Result {
|
||||
success: boolean;
|
||||
userId: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function getUserTodoCount(
|
||||
accessToken: string,
|
||||
userId: string,
|
||||
debug: boolean
|
||||
): Promise<void> {
|
||||
const client = new dingtalkWorkflow(createConfig());
|
||||
|
||||
const headers = new GetTodoNumHeaders({});
|
||||
headers.xAcsDingtalkAccessToken = accessToken;
|
||||
|
||||
const request = new GetTodoNumRequest({
|
||||
userId: userId,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getTodoNumWithOptions(
|
||||
request,
|
||||
headers,
|
||||
new RuntimeOptions({})
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const result: Result = {
|
||||
success: true,
|
||||
userId: userId,
|
||||
count: response.body?.result?.count || 0,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || '未知错误',
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const debug = args.includes('--debug');
|
||||
const filteredArgs = args.filter(arg => arg !== '--debug');
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/get-user-todo-count.ts <userId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (filteredArgs.length < 1) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供用户 ID(userId)',
|
||||
usage: 'ts-node scripts/get-user-todo-count.ts <userId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const userId = filteredArgs[0];
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在查询用户待审批数量...');
|
||||
|
||||
await getUserTodoCount(accessToken, userId, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
189
.agents/skills/dingtalk-api/scripts/get-user.ts
Normal file
189
.agents/skills/dingtalk-api/scripts/get-user.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
// 查询用户详情
|
||||
// 用法: ts-node scripts/get-user.ts <userId> [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const https = require('https');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
|
||||
interface UserInfo {
|
||||
userid: string;
|
||||
name: string;
|
||||
mobile?: string;
|
||||
email?: string;
|
||||
dept_id_list?: number[];
|
||||
avatar?: string;
|
||||
job_number?: string;
|
||||
title?: string;
|
||||
hired_date?: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface SuccessResult {
|
||||
success: boolean;
|
||||
user: UserInfo;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
userId: string;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function dingtalkRequest(accessToken: string, method: string, path: string, body?: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: 'oapi.dingtalk.com',
|
||||
path: `${path}?access_token=${accessToken}`,
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
} as Record<string, string>,
|
||||
};
|
||||
const req = https.request(options, (res: any) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk: string) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (parsed.errcode !== undefined && parsed.errcode !== 0) {
|
||||
reject({ code: parsed.errcode, message: parsed.errmsg });
|
||||
} else if (res.statusCode && res.statusCode >= 400) {
|
||||
reject(parsed);
|
||||
} else {
|
||||
resolve(parsed);
|
||||
}
|
||||
} catch {
|
||||
reject(new Error(`Invalid JSON response: ${data}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
if (body) req.write(JSON.stringify(body));
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function getUser(accessToken: string, userId: string, debug: boolean = false): Promise<void> {
|
||||
try {
|
||||
const response = await dingtalkRequest(
|
||||
accessToken,
|
||||
'POST',
|
||||
'/topapi/v2/user/get',
|
||||
{ userid: userId, language: 'zh_CN' }
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const result: SuccessResult = {
|
||||
success: true,
|
||||
user: response.result,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
userId: userId,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || err.msg || JSON.stringify(err),
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const debug = args.includes('--debug');
|
||||
const filteredArgs = args.filter(arg => arg !== '--debug');
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/get-user.ts <userId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (filteredArgs.length < 1) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供用户 ID(userId)',
|
||||
usage: 'ts-node scripts/get-user.ts <userId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const userId = filteredArgs[0];
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在查询用户详情...');
|
||||
|
||||
await getUser(accessToken, userId, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,206 @@
|
||||
// 获取审批实例 ID 列表
|
||||
// 用法: ts-node scripts/list-approval-instance-ids.ts <processCode> --startTime <timestamp> --endTime <timestamp> [--size <size>] [--nextToken <token>] [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const { default: dingtalkWorkflow, ListProcessInstanceIdsHeaders, ListProcessInstanceIdsRequest } = require('@alicloud/dingtalk/workflow_1_0');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
const { RuntimeOptions } = require('@alicloud/tea-util');
|
||||
|
||||
interface Result {
|
||||
success: boolean;
|
||||
processCode: string;
|
||||
instanceIds: string[];
|
||||
totalCount: number;
|
||||
hasMore: boolean;
|
||||
nextToken?: string;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function listApprovalInstanceIds(
|
||||
accessToken: string,
|
||||
processCode: string,
|
||||
startTime: number,
|
||||
endTime: number,
|
||||
size: number,
|
||||
nextToken: string | undefined,
|
||||
debug: boolean
|
||||
): Promise<void> {
|
||||
const client = new dingtalkWorkflow(createConfig());
|
||||
|
||||
const headers = new ListProcessInstanceIdsHeaders({});
|
||||
headers.xAcsDingtalkAccessToken = accessToken;
|
||||
|
||||
const request = new ListProcessInstanceIdsRequest({
|
||||
processCode: processCode,
|
||||
startTime: startTime,
|
||||
endTime: endTime,
|
||||
size: size,
|
||||
nextToken: nextToken,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.listProcessInstanceIdsWithOptions(
|
||||
request,
|
||||
headers,
|
||||
new RuntimeOptions({})
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const instanceIds = response.body?.result?.list || [];
|
||||
const result: Result = {
|
||||
success: true,
|
||||
processCode: processCode,
|
||||
instanceIds: instanceIds,
|
||||
totalCount: instanceIds.length,
|
||||
hasMore: !!response.body?.result?.nextToken,
|
||||
nextToken: response.body?.result?.nextToken,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || '未知错误',
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function parseArgs(args: string[]): { processCode: string; startTime: number; endTime: number; size: number; nextToken?: string; debug: boolean } {
|
||||
let processCode = '';
|
||||
let startTime = 0;
|
||||
let endTime = 0;
|
||||
let size = 20;
|
||||
let nextToken: string | undefined;
|
||||
let debug = false;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
if (arg === '--debug') {
|
||||
debug = true;
|
||||
} else if (arg === '--startTime' && i + 1 < args.length) {
|
||||
startTime = parseInt(args[++i], 10);
|
||||
} else if (arg === '--endTime' && i + 1 < args.length) {
|
||||
endTime = parseInt(args[++i], 10);
|
||||
} else if (arg === '--size' && i + 1 < args.length) {
|
||||
size = parseInt(args[++i], 10);
|
||||
} else if (arg === '--nextToken' && i + 1 < args.length) {
|
||||
nextToken = args[++i];
|
||||
} else if (!arg.startsWith('--') && !processCode) {
|
||||
processCode = arg;
|
||||
}
|
||||
}
|
||||
|
||||
return { processCode, startTime, endTime, size, nextToken, debug };
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
const { processCode, startTime, endTime, size, nextToken, debug } = parseArgs(args);
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/list-approval-instance-ids.ts <processCode> --startTime <timestamp> --endTime <timestamp> [--size <size>] [--nextToken <token>] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!processCode) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供审批模板 processCode',
|
||||
usage: 'ts-node scripts/list-approval-instance-ids.ts <processCode> --startTime <timestamp> --endTime <timestamp> [--size <size>] [--nextToken <token>] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!startTime || !endTime) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供 startTime 和 endTime(Unix 时间戳,毫秒)',
|
||||
usage: 'ts-node scripts/list-approval-instance-ids.ts <processCode> --startTime <timestamp> --endTime <timestamp> [--size <size>] [--nextToken <token>] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在查询审批实例 ID 列表...');
|
||||
|
||||
await listApprovalInstanceIds(accessToken, processCode, startTime, endTime, size, nextToken, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
189
.agents/skills/dingtalk-api/scripts/list-department-parents.ts
Normal file
189
.agents/skills/dingtalk-api/scripts/list-department-parents.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
// 获取指定部门的所有父部门列表
|
||||
// 用法: ts-node scripts/list-department-parents.ts <deptId> [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const https = require('https');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
|
||||
interface SuccessResult {
|
||||
success: boolean;
|
||||
deptId: number;
|
||||
parentIdList: number[];
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
deptId: number;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function dingtalkRequest(accessToken: string, method: string, path: string, body?: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: 'oapi.dingtalk.com',
|
||||
path: `${path}?access_token=${accessToken}`,
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
} as Record<string, string>,
|
||||
};
|
||||
const req = https.request(options, (res: any) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk: string) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (parsed.errcode !== undefined && parsed.errcode !== 0) {
|
||||
reject({ code: parsed.errcode, message: parsed.errmsg });
|
||||
} else if (res.statusCode && res.statusCode >= 400) {
|
||||
reject(parsed);
|
||||
} else {
|
||||
resolve(parsed);
|
||||
}
|
||||
} catch {
|
||||
reject(new Error(`Invalid JSON response: ${data}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
if (body) req.write(JSON.stringify(body));
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function listDepartmentParents(accessToken: string, deptId: number, debug: boolean = false): Promise<void> {
|
||||
try {
|
||||
const response = await dingtalkRequest(
|
||||
accessToken,
|
||||
'POST',
|
||||
'/topapi/v2/department/listparentbydept',
|
||||
{ dept_id: deptId }
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const result: SuccessResult = {
|
||||
success: true,
|
||||
deptId: deptId,
|
||||
parentIdList: response.result || [],
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
deptId: deptId,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || err.msg || JSON.stringify(err),
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const debug = args.includes('--debug');
|
||||
const filteredArgs = args.filter(arg => arg !== '--debug');
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/list-department-parents.ts <deptId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (filteredArgs.length < 1) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供部门 ID(deptId)',
|
||||
usage: 'ts-node scripts/list-department-parents.ts <deptId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const deptId = parseInt(filteredArgs[0], 10);
|
||||
if (isNaN(deptId)) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:deptId 必须是数字',
|
||||
usage: 'ts-node scripts/list-department-parents.ts <deptId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在获取父部门列表...');
|
||||
|
||||
await listDepartmentParents(accessToken, deptId, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,227 @@
|
||||
// 获取部门用户基础信息(分页)
|
||||
// 用法: ts-node scripts/list-department-user-details.ts <deptId> [--cursor <cursor>] [--size <size>] [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const https = require('https');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
|
||||
interface UserDetail {
|
||||
userid: string;
|
||||
name: string;
|
||||
mobile?: string;
|
||||
email?: string;
|
||||
avatar?: string;
|
||||
job_number?: string;
|
||||
title?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface SuccessResult {
|
||||
success: boolean;
|
||||
deptId: number;
|
||||
users: UserDetail[];
|
||||
hasMore: boolean;
|
||||
nextCursor?: number;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
deptId: number;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function dingtalkRequest(accessToken: string, method: string, path: string, body?: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: 'oapi.dingtalk.com',
|
||||
path: `${path}?access_token=${accessToken}`,
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
} as Record<string, string>,
|
||||
};
|
||||
const req = https.request(options, (res: any) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk: string) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (parsed.errcode !== undefined && parsed.errcode !== 0) {
|
||||
reject({ code: parsed.errcode, message: parsed.errmsg });
|
||||
} else if (res.statusCode && res.statusCode >= 400) {
|
||||
reject(parsed);
|
||||
} else {
|
||||
resolve(parsed);
|
||||
}
|
||||
} catch {
|
||||
reject(new Error(`Invalid JSON response: ${data}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
if (body) req.write(JSON.stringify(body));
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function listDepartmentUserDetails(accessToken: string, deptId: number, cursor: number, size: number, debug: boolean = false): Promise<void> {
|
||||
try {
|
||||
const response = await dingtalkRequest(
|
||||
accessToken,
|
||||
'POST',
|
||||
'/topapi/v2/user/list',
|
||||
{ dept_id: deptId, cursor: cursor, size: size }
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const result: SuccessResult = {
|
||||
success: true,
|
||||
deptId: deptId,
|
||||
users: response.result?.list || [],
|
||||
hasMore: response.result?.has_more || false,
|
||||
nextCursor: response.result?.next_cursor,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
deptId: deptId,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || err.msg || JSON.stringify(err),
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function parseArgs(args: string[]): { deptId: number; cursor: number; size: number; debug: boolean } {
|
||||
let deptId: number | null = null;
|
||||
let cursor = 0;
|
||||
let size = 100;
|
||||
let debug = false;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--debug') {
|
||||
debug = true;
|
||||
} else if (args[i] === '--cursor' && i + 1 < args.length) {
|
||||
cursor = parseInt(args[i + 1], 10);
|
||||
if (isNaN(cursor)) {
|
||||
throw new Error('--cursor 必须是数字');
|
||||
}
|
||||
i++;
|
||||
} else if (args[i] === '--size' && i + 1 < args.length) {
|
||||
size = parseInt(args[i + 1], 10);
|
||||
if (isNaN(size) || size < 1 || size > 100) {
|
||||
throw new Error('--size 必须是 1-100 之间的数字');
|
||||
}
|
||||
i++;
|
||||
} else if (!deptId && !args[i].startsWith('--')) {
|
||||
deptId = parseInt(args[i], 10);
|
||||
if (isNaN(deptId)) {
|
||||
throw new Error('deptId 必须是数字');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deptId === null) {
|
||||
throw new Error('缺少 deptId 参数');
|
||||
}
|
||||
|
||||
return { deptId, cursor, size, debug };
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/list-department-user-details.ts <deptId> [--cursor <cursor>] [--size <size>] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let parsedArgs;
|
||||
try {
|
||||
parsedArgs = parseArgs(args);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: err.message,
|
||||
usage: 'ts-node scripts/list-department-user-details.ts <deptId> [--cursor <cursor>] [--size <size>] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在获取部门用户详情...');
|
||||
|
||||
await listDepartmentUserDetails(accessToken, parsedArgs.deptId, parsedArgs.cursor, parsedArgs.size, parsedArgs.debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
191
.agents/skills/dingtalk-api/scripts/list-department-user-ids.ts
Normal file
191
.agents/skills/dingtalk-api/scripts/list-department-user-ids.ts
Normal file
@@ -0,0 +1,191 @@
|
||||
// 获取部门用户userid列表
|
||||
// 用法: ts-node scripts/list-department-user-ids.ts <deptId> [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const https = require('https');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
|
||||
interface SuccessResult {
|
||||
success: boolean;
|
||||
deptId: number;
|
||||
userIds: string[];
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
deptId: number;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function dingtalkRequest(accessToken: string, method: string, path: string, body?: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: 'oapi.dingtalk.com',
|
||||
path: `${path}?access_token=${accessToken}`,
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
} as Record<string, string>,
|
||||
};
|
||||
const req = https.request(options, (res: any) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk: string) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (parsed.errcode !== undefined && parsed.errcode !== 0) {
|
||||
reject({ code: parsed.errcode, message: parsed.errmsg });
|
||||
} else if (res.statusCode && res.statusCode >= 400) {
|
||||
reject(parsed);
|
||||
} else {
|
||||
resolve(parsed);
|
||||
}
|
||||
} catch {
|
||||
reject(new Error(`Invalid JSON response: ${data}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
if (body) req.write(JSON.stringify(body));
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function listDepartmentUserIds(accessToken: string, deptId: number, debug: boolean = false): Promise<void> {
|
||||
try {
|
||||
const response = await dingtalkRequest(
|
||||
accessToken,
|
||||
'POST',
|
||||
'/topapi/user/listid',
|
||||
{ dept_id: deptId }
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const userIds = (response.result || []).map((item: any) => item.userid);
|
||||
|
||||
const result: SuccessResult = {
|
||||
success: true,
|
||||
deptId: deptId,
|
||||
userIds: userIds,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
deptId: deptId,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || err.msg || JSON.stringify(err),
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const debug = args.includes('--debug');
|
||||
const filteredArgs = args.filter(arg => arg !== '--debug');
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/list-department-user-ids.ts <deptId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (filteredArgs.length < 1) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供部门 ID(deptId)',
|
||||
usage: 'ts-node scripts/list-department-user-ids.ts <deptId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const deptId = parseInt(filteredArgs[0], 10);
|
||||
if (isNaN(deptId)) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:deptId 必须是数字',
|
||||
usage: 'ts-node scripts/list-department-user-ids.ts <deptId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在获取部门用户ID列表...');
|
||||
|
||||
await listDepartmentUserIds(accessToken, deptId, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
238
.agents/skills/dingtalk-api/scripts/list-department-users.ts
Normal file
238
.agents/skills/dingtalk-api/scripts/list-department-users.ts
Normal file
@@ -0,0 +1,238 @@
|
||||
// 获取部门用户列表脚本
|
||||
// 用法: ts-node scripts/list-department-users.ts <deptId> [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const https = require('https');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
|
||||
interface SimpleUser {
|
||||
userId: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface SuccessResult {
|
||||
success: boolean;
|
||||
deptId: number;
|
||||
users: SimpleUser[];
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
deptId: number;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建钉钉客户端配置
|
||||
* @returns Config 实例
|
||||
*/
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Access Token
|
||||
* @param appKey 应用 Key
|
||||
* @param appSecret 应用 Secret
|
||||
* @returns Access Token
|
||||
*/
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用钉钉新版 API 调用函数
|
||||
*/
|
||||
async function dingtalkRequest(accessToken: string, method: string, path: string, body?: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: 'oapi.dingtalk.com',
|
||||
path: `${path}?access_token=${accessToken}`,
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
} as Record<string, string>,
|
||||
};
|
||||
const req = https.request(options, (res: any) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk: string) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (parsed.errcode !== undefined && parsed.errcode !== 0) {
|
||||
reject({ code: parsed.errcode, message: parsed.errmsg });
|
||||
} else if (res.statusCode && res.statusCode >= 400) {
|
||||
reject(parsed);
|
||||
} else {
|
||||
resolve(parsed);
|
||||
}
|
||||
} catch {
|
||||
reject(new Error(`Invalid JSON response: ${data}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
if (body) req.write(JSON.stringify(body));
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门用户列表(简略信息)
|
||||
* @param accessToken Access Token
|
||||
* @param deptId 部门 ID
|
||||
* @param debug 是否开启调试模式
|
||||
*/
|
||||
async function listDepartmentUsers(accessToken: string, deptId: number, debug: boolean = false): Promise<void> {
|
||||
try {
|
||||
// 钉钉 TOP API: POST /topapi/v2/user/list
|
||||
// 请求体: { "dept_id": number, "cursor": number, "size": number }
|
||||
let allUsers: SimpleUser[] = [];
|
||||
let cursor = 0;
|
||||
let hasMore = true;
|
||||
|
||||
while (hasMore) {
|
||||
const response = await dingtalkRequest(
|
||||
accessToken,
|
||||
'POST',
|
||||
'/topapi/v2/user/list',
|
||||
{ dept_id: deptId, cursor: cursor, size: 100 }
|
||||
);
|
||||
|
||||
// 调试模式:输出完整响应
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const users = response.result?.list || [];
|
||||
allUsers = allUsers.concat(users.map((u: any) => ({
|
||||
userId: u.userid,
|
||||
name: u.name,
|
||||
})));
|
||||
|
||||
hasMore = response.result?.has_more || false;
|
||||
if (hasMore) {
|
||||
cursor = response.result?.next_cursor || 0;
|
||||
}
|
||||
}
|
||||
|
||||
const result: SuccessResult = {
|
||||
success: true,
|
||||
deptId: deptId,
|
||||
users: allUsers,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
deptId: deptId,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || err.msg || JSON.stringify(err),
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 主函数
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
// 检查是否有调试参数
|
||||
const debug = args.includes('--debug');
|
||||
const filteredArgs = args.filter(arg => arg !== '--debug');
|
||||
|
||||
// 从环境变量读取配置
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/list-department-users.ts <deptId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (filteredArgs.length < 1) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供部门 ID(deptId)',
|
||||
usage: 'ts-node scripts/list-department-users.ts <deptId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const deptId = parseInt(filteredArgs[0], 10);
|
||||
if (isNaN(deptId)) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:deptId 必须是数字',
|
||||
usage: 'ts-node scripts/list-department-users.ts <deptId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
// 自动获取 access_token
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在获取部门用户列表...');
|
||||
|
||||
// 使用获取到的 token 获取部门用户列表
|
||||
await listDepartmentUsers(accessToken, deptId, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
244
.agents/skills/dingtalk-api/scripts/list-inactive-users.ts
Normal file
244
.agents/skills/dingtalk-api/scripts/list-inactive-users.ts
Normal file
@@ -0,0 +1,244 @@
|
||||
// 获取未登录钉钉的员工列表
|
||||
// 用法: ts-node scripts/list-inactive-users.ts <queryDate> [--deptIds <id1,id2,...>] [--offset <offset>] [--size <size>] [--debug]
|
||||
// queryDate 格式: yyyyMMdd
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const https = require('https');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
|
||||
interface SuccessResult {
|
||||
success: boolean;
|
||||
queryDate: string;
|
||||
userIds: string[];
|
||||
hasMore: boolean;
|
||||
nextOffset?: number;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function dingtalkRequest(accessToken: string, method: string, path: string, body?: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: 'oapi.dingtalk.com',
|
||||
path: `${path}?access_token=${accessToken}`,
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
} as Record<string, string>,
|
||||
};
|
||||
const req = https.request(options, (res: any) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk: string) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (parsed.errcode !== undefined && parsed.errcode !== 0) {
|
||||
reject({ code: parsed.errcode, message: parsed.errmsg });
|
||||
} else if (res.statusCode && res.statusCode >= 400) {
|
||||
reject(parsed);
|
||||
} else {
|
||||
resolve(parsed);
|
||||
}
|
||||
} catch {
|
||||
reject(new Error(`Invalid JSON response: ${data}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
if (body) req.write(JSON.stringify(body));
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function listInactiveUsers(
|
||||
accessToken: string,
|
||||
queryDate: string,
|
||||
deptIds: number[],
|
||||
offset: number,
|
||||
size: number,
|
||||
debug: boolean = false
|
||||
): Promise<void> {
|
||||
try {
|
||||
const body: any = {
|
||||
is_active: false,
|
||||
query_date: queryDate,
|
||||
offset: offset,
|
||||
size: size,
|
||||
};
|
||||
|
||||
if (deptIds.length > 0) {
|
||||
body.dept_ids = deptIds;
|
||||
}
|
||||
|
||||
const response = await dingtalkRequest(
|
||||
accessToken,
|
||||
'POST',
|
||||
'/topapi/inactive/user/v2/get',
|
||||
body
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const result: SuccessResult = {
|
||||
success: true,
|
||||
queryDate: queryDate,
|
||||
userIds: response.result?.list || [],
|
||||
hasMore: response.result?.has_more || false,
|
||||
nextOffset: response.result?.has_more ? (offset + size) : undefined,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || err.msg || JSON.stringify(err),
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function parseArgs(args: string[]): { queryDate: string; deptIds: number[]; offset: number; size: number; debug: boolean } {
|
||||
let queryDate: string | null = null;
|
||||
let deptIds: number[] = [];
|
||||
let offset = 0;
|
||||
let size = 100;
|
||||
let debug = false;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--debug') {
|
||||
debug = true;
|
||||
} else if (args[i] === '--deptIds' && i + 1 < args.length) {
|
||||
deptIds = args[i + 1].split(',').map(id => parseInt(id.trim(), 10)).filter(id => !isNaN(id));
|
||||
i++;
|
||||
} else if (args[i] === '--offset' && i + 1 < args.length) {
|
||||
offset = parseInt(args[i + 1], 10);
|
||||
if (isNaN(offset)) {
|
||||
throw new Error('--offset 必须是数字');
|
||||
}
|
||||
i++;
|
||||
} else if (args[i] === '--size' && i + 1 < args.length) {
|
||||
size = parseInt(args[i + 1], 10);
|
||||
if (isNaN(size) || size < 1 || size > 100) {
|
||||
throw new Error('--size 必须是 1-100 之间的数字');
|
||||
}
|
||||
i++;
|
||||
} else if (!queryDate && !args[i].startsWith('--')) {
|
||||
queryDate = args[i];
|
||||
if (!/^\d{8}$/.test(queryDate)) {
|
||||
throw new Error('queryDate 必须是 yyyyMMdd 格式(如 20240115)');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!queryDate) {
|
||||
throw new Error('缺少 queryDate 参数');
|
||||
}
|
||||
|
||||
return { queryDate, deptIds, offset, size, debug };
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/list-inactive-users.ts <queryDate> [--deptIds <id1,id2,...>] [--offset <offset>] [--size <size>] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let parsedArgs;
|
||||
try {
|
||||
parsedArgs = parseArgs(args);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: err.message,
|
||||
usage: 'ts-node scripts/list-inactive-users.ts <queryDate> [--deptIds <id1,id2,...>] [--offset <offset>] [--size <size>] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在获取未登录用户列表...');
|
||||
|
||||
await listInactiveUsers(
|
||||
accessToken,
|
||||
parsedArgs.queryDate,
|
||||
parsedArgs.deptIds,
|
||||
parsedArgs.offset,
|
||||
parsedArgs.size,
|
||||
parsedArgs.debug
|
||||
);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
249
.agents/skills/dingtalk-api/scripts/list-resigned-users.ts
Normal file
249
.agents/skills/dingtalk-api/scripts/list-resigned-users.ts
Normal file
@@ -0,0 +1,249 @@
|
||||
// 查询离职记录列表
|
||||
// 用法: ts-node scripts/list-resigned-users.ts <startTime> [<endTime>] [--nextToken <token>] [--maxResults <max>] [--debug]
|
||||
// startTime/endTime 格式: ISO8601 (如 2024-01-15T00:00:00+08:00)
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const https = require('https');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
|
||||
interface ResignedRecord {
|
||||
userId: string;
|
||||
name?: string;
|
||||
stateCode?: string;
|
||||
mobile?: string;
|
||||
leaveTime?: string;
|
||||
leaveReason?: string;
|
||||
}
|
||||
|
||||
interface SuccessResult {
|
||||
success: boolean;
|
||||
startTime: string;
|
||||
endTime?: string;
|
||||
records: ResignedRecord[];
|
||||
nextToken?: string;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function newApiRequest(accessToken: string, path: string, queryParams?: Record<string, string>): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const queryString = queryParams ? '?' + new URLSearchParams(queryParams).toString() : '';
|
||||
const options = {
|
||||
hostname: 'api.dingtalk.com',
|
||||
path: `/v1.0${path}${queryString}`,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'x-acs-dingtalk-access-token': accessToken,
|
||||
'Content-Type': 'application/json',
|
||||
} as Record<string, string>,
|
||||
};
|
||||
const req = https.request(options, (res: any) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk: string) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (res.statusCode && res.statusCode >= 400) {
|
||||
reject(parsed);
|
||||
} else {
|
||||
resolve(parsed);
|
||||
}
|
||||
} catch {
|
||||
reject(new Error(`Invalid JSON response: ${data}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function listResignedUsers(
|
||||
accessToken: string,
|
||||
startTime: string,
|
||||
endTime: string | null,
|
||||
nextToken: string | null,
|
||||
maxResults: number,
|
||||
debug: boolean = false
|
||||
): Promise<void> {
|
||||
try {
|
||||
const queryParams: Record<string, string> = {
|
||||
startTime: startTime,
|
||||
};
|
||||
|
||||
if (endTime) {
|
||||
queryParams.endTime = endTime;
|
||||
}
|
||||
if (nextToken) {
|
||||
queryParams.nextToken = nextToken;
|
||||
}
|
||||
queryParams.maxResults = maxResults.toString();
|
||||
|
||||
const response = await newApiRequest(
|
||||
accessToken,
|
||||
'/contact/empLeaveRecords',
|
||||
queryParams
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const result: SuccessResult = {
|
||||
success: true,
|
||||
startTime: startTime,
|
||||
endTime: endTime || undefined,
|
||||
records: response.records || [],
|
||||
nextToken: response.nextToken,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
error: {
|
||||
code: err.code || err.Code || 'UNKNOWN_ERROR',
|
||||
message: err.message || err.Message || JSON.stringify(err),
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function parseArgs(args: string[]): { startTime: string; endTime: string | null; nextToken: string | null; maxResults: number; debug: boolean } {
|
||||
let startTime: string | null = null;
|
||||
let endTime: string | null = null;
|
||||
let nextToken: string | null = null;
|
||||
let maxResults = 100;
|
||||
let debug = false;
|
||||
let positionalCount = 0;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--debug') {
|
||||
debug = true;
|
||||
} else if (args[i] === '--nextToken' && i + 1 < args.length) {
|
||||
nextToken = args[i + 1];
|
||||
i++;
|
||||
} else if (args[i] === '--maxResults' && i + 1 < args.length) {
|
||||
maxResults = parseInt(args[i + 1], 10);
|
||||
if (isNaN(maxResults) || maxResults < 1 || maxResults > 100) {
|
||||
throw new Error('--maxResults 必须是 1-100 之间的数字');
|
||||
}
|
||||
i++;
|
||||
} else if (!args[i].startsWith('--')) {
|
||||
positionalCount++;
|
||||
if (positionalCount === 1) {
|
||||
startTime = args[i];
|
||||
} else if (positionalCount === 2) {
|
||||
endTime = args[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!startTime) {
|
||||
throw new Error('缺少 startTime 参数');
|
||||
}
|
||||
|
||||
return { startTime, endTime, nextToken, maxResults, debug };
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/list-resigned-users.ts <startTime> [<endTime>] [--nextToken <token>] [--maxResults <max>] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let parsedArgs;
|
||||
try {
|
||||
parsedArgs = parseArgs(args);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: err.message,
|
||||
usage: 'ts-node scripts/list-resigned-users.ts <startTime> [<endTime>] [--nextToken <token>] [--maxResults <max>] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在查询离职记录...');
|
||||
|
||||
await listResignedUsers(
|
||||
accessToken,
|
||||
parsedArgs.startTime,
|
||||
parsedArgs.endTime,
|
||||
parsedArgs.nextToken,
|
||||
parsedArgs.maxResults,
|
||||
parsedArgs.debug
|
||||
);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
217
.agents/skills/dingtalk-api/scripts/list-sub-departments.ts
Normal file
217
.agents/skills/dingtalk-api/scripts/list-sub-departments.ts
Normal file
@@ -0,0 +1,217 @@
|
||||
// 获取子部门列表脚本
|
||||
// 用法: ts-node scripts/list-sub-departments.ts <deptId> [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
// 根部门 deptId = 1
|
||||
export {};
|
||||
|
||||
const https = require('https');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
|
||||
interface SuccessResult {
|
||||
success: boolean;
|
||||
deptId: number;
|
||||
subDepartmentIds: number[];
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
deptId: number;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建钉钉客户端配置
|
||||
* @returns Config 实例
|
||||
*/
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Access Token
|
||||
* @param appKey 应用 Key
|
||||
* @param appSecret 应用 Secret
|
||||
* @returns Access Token
|
||||
*/
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用钉钉新版 API 调用函数
|
||||
*/
|
||||
async function dingtalkRequest(accessToken: string, method: string, path: string, body?: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: 'oapi.dingtalk.com',
|
||||
path: `${path}?access_token=${accessToken}`,
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
} as Record<string, string>,
|
||||
};
|
||||
const req = https.request(options, (res: any) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk: string) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (parsed.errcode !== undefined && parsed.errcode !== 0) {
|
||||
reject({ code: parsed.errcode, message: parsed.errmsg });
|
||||
} else if (res.statusCode && res.statusCode >= 400) {
|
||||
reject(parsed);
|
||||
} else {
|
||||
resolve(parsed);
|
||||
}
|
||||
} catch {
|
||||
reject(new Error(`Invalid JSON response: ${data}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
if (body) req.write(JSON.stringify(body));
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取子部门 ID 列表
|
||||
* @param accessToken Access Token
|
||||
* @param deptId 部门 ID
|
||||
* @param debug 是否开启调试模式
|
||||
*/
|
||||
async function listSubDepartments(accessToken: string, deptId: number, debug: boolean = false): Promise<void> {
|
||||
try {
|
||||
// 钉钉 TOP API: POST /topapi/v2/department/listsub
|
||||
// 请求体: { "dept_id": number }
|
||||
const response = await dingtalkRequest(
|
||||
accessToken,
|
||||
'POST',
|
||||
'/topapi/v2/department/listsub',
|
||||
{ dept_id: deptId }
|
||||
);
|
||||
|
||||
// 调试模式:输出完整响应
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const result: SuccessResult = {
|
||||
success: true,
|
||||
deptId: deptId,
|
||||
subDepartmentIds: (response.result || []).map((d: any) => d.dept_id),
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
deptId: deptId,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || err.msg || JSON.stringify(err),
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 主函数
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
// 检查是否有调试参数
|
||||
const debug = args.includes('--debug');
|
||||
const filteredArgs = args.filter(arg => arg !== '--debug');
|
||||
|
||||
// 从环境变量读取配置
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/list-sub-departments.ts <deptId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (filteredArgs.length < 1) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供部门 ID(deptId),根部门为 1',
|
||||
usage: 'ts-node scripts/list-sub-departments.ts <deptId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const deptId = parseInt(filteredArgs[0], 10);
|
||||
if (isNaN(deptId)) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:deptId 必须是数字',
|
||||
usage: 'ts-node scripts/list-sub-departments.ts <deptId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
// 自动获取 access_token
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在获取子部门列表...');
|
||||
|
||||
// 使用获取到的 token 获取子部门列表
|
||||
await listSubDepartments(accessToken, deptId, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
194
.agents/skills/dingtalk-api/scripts/list-user-cc-approvals.ts
Normal file
194
.agents/skills/dingtalk-api/scripts/list-user-cc-approvals.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
// 获取抄送用户的审批列表
|
||||
// 用法: ts-node scripts/list-user-cc-approvals.ts <userId> [--startTime <timestamp>] [--endTime <timestamp>] [--maxResults <max>] [--nextToken <token>] [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const { default: dingtalkWorkflow, ListUserCcProcessInstancesHeaders, ListUserCcProcessInstancesRequest } = require('@alicloud/dingtalk/workflow_1_0');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
const { RuntimeOptions } = require('@alicloud/tea-util');
|
||||
|
||||
interface Result {
|
||||
success: boolean;
|
||||
userId: string;
|
||||
instances: any[];
|
||||
totalCount: number;
|
||||
hasMore: boolean;
|
||||
nextToken?: string;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function listUserCcApprovals(
|
||||
accessToken: string,
|
||||
userId: string,
|
||||
startTime: number | undefined,
|
||||
endTime: number | undefined,
|
||||
maxResults: number,
|
||||
nextToken: string | undefined,
|
||||
debug: boolean
|
||||
): Promise<void> {
|
||||
const client = new dingtalkWorkflow(createConfig());
|
||||
|
||||
const headers = new ListUserCcProcessInstancesHeaders({});
|
||||
headers.xAcsDingtalkAccessToken = accessToken;
|
||||
|
||||
const request = new ListUserCcProcessInstancesRequest({
|
||||
userId: userId,
|
||||
startTime: startTime,
|
||||
endTime: endTime,
|
||||
maxResults: maxResults,
|
||||
nextToken: nextToken,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.listUserCcProcessInstancesWithOptions(
|
||||
request,
|
||||
headers,
|
||||
new RuntimeOptions({})
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const instances = response.body?.result?.data || [];
|
||||
const result: Result = {
|
||||
success: true,
|
||||
userId: userId,
|
||||
instances: instances,
|
||||
totalCount: instances.length,
|
||||
hasMore: !!response.body?.result?.nextToken,
|
||||
nextToken: response.body?.result?.nextToken,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || '未知错误',
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function parseArgs(args: string[]): { userId: string; startTime?: number; endTime?: number; maxResults: number; nextToken?: string; debug: boolean } {
|
||||
let userId = '';
|
||||
let startTime: number | undefined;
|
||||
let endTime: number | undefined;
|
||||
let maxResults = 20;
|
||||
let nextToken: string | undefined;
|
||||
let debug = false;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
if (arg === '--debug') {
|
||||
debug = true;
|
||||
} else if (arg === '--startTime' && i + 1 < args.length) {
|
||||
startTime = parseInt(args[++i], 10);
|
||||
} else if (arg === '--endTime' && i + 1 < args.length) {
|
||||
endTime = parseInt(args[++i], 10);
|
||||
} else if (arg === '--maxResults' && i + 1 < args.length) {
|
||||
maxResults = parseInt(args[++i], 10);
|
||||
} else if (arg === '--nextToken' && i + 1 < args.length) {
|
||||
nextToken = args[++i];
|
||||
} else if (!arg.startsWith('--') && !userId) {
|
||||
userId = arg;
|
||||
}
|
||||
}
|
||||
|
||||
return { userId, startTime, endTime, maxResults, nextToken, debug };
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
const { userId, startTime, endTime, maxResults, nextToken, debug } = parseArgs(args);
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/list-user-cc-approvals.ts <userId> [--startTime <timestamp>] [--endTime <timestamp>] [--maxResults <max>] [--nextToken <token>] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供用户 ID(userId)',
|
||||
usage: 'ts-node scripts/list-user-cc-approvals.ts <userId> [--startTime <timestamp>] [--endTime <timestamp>] [--maxResults <max>] [--nextToken <token>] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在查询抄送用户的审批列表...');
|
||||
|
||||
await listUserCcApprovals(accessToken, userId, startTime, endTime, maxResults, nextToken, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
194
.agents/skills/dingtalk-api/scripts/list-user-done-approvals.ts
Normal file
194
.agents/skills/dingtalk-api/scripts/list-user-done-approvals.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
// 获取用户已处理的审批列表
|
||||
// 用法: ts-node scripts/list-user-done-approvals.ts <userId> [--startTime <timestamp>] [--endTime <timestamp>] [--maxResults <max>] [--nextToken <token>] [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const { default: dingtalkWorkflow, ListUserDoneProcessInstancesHeaders, ListUserDoneProcessInstancesRequest } = require('@alicloud/dingtalk/workflow_1_0');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
const { RuntimeOptions } = require('@alicloud/tea-util');
|
||||
|
||||
interface Result {
|
||||
success: boolean;
|
||||
userId: string;
|
||||
instances: any[];
|
||||
totalCount: number;
|
||||
hasMore: boolean;
|
||||
nextToken?: string;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function listUserDoneApprovals(
|
||||
accessToken: string,
|
||||
userId: string,
|
||||
startTime: number | undefined,
|
||||
endTime: number | undefined,
|
||||
maxResults: number,
|
||||
nextToken: string | undefined,
|
||||
debug: boolean
|
||||
): Promise<void> {
|
||||
const client = new dingtalkWorkflow(createConfig());
|
||||
|
||||
const headers = new ListUserDoneProcessInstancesHeaders({});
|
||||
headers.xAcsDingtalkAccessToken = accessToken;
|
||||
|
||||
const request = new ListUserDoneProcessInstancesRequest({
|
||||
userId: userId,
|
||||
startTime: startTime,
|
||||
endTime: endTime,
|
||||
maxResults: maxResults,
|
||||
nextToken: nextToken,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.listUserDoneProcessInstancesWithOptions(
|
||||
request,
|
||||
headers,
|
||||
new RuntimeOptions({})
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const instances = response.body?.result?.data || [];
|
||||
const result: Result = {
|
||||
success: true,
|
||||
userId: userId,
|
||||
instances: instances,
|
||||
totalCount: instances.length,
|
||||
hasMore: !!response.body?.result?.nextToken,
|
||||
nextToken: response.body?.result?.nextToken,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || '未知错误',
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function parseArgs(args: string[]): { userId: string; startTime?: number; endTime?: number; maxResults: number; nextToken?: string; debug: boolean } {
|
||||
let userId = '';
|
||||
let startTime: number | undefined;
|
||||
let endTime: number | undefined;
|
||||
let maxResults = 20;
|
||||
let nextToken: string | undefined;
|
||||
let debug = false;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
if (arg === '--debug') {
|
||||
debug = true;
|
||||
} else if (arg === '--startTime' && i + 1 < args.length) {
|
||||
startTime = parseInt(args[++i], 10);
|
||||
} else if (arg === '--endTime' && i + 1 < args.length) {
|
||||
endTime = parseInt(args[++i], 10);
|
||||
} else if (arg === '--maxResults' && i + 1 < args.length) {
|
||||
maxResults = parseInt(args[++i], 10);
|
||||
} else if (arg === '--nextToken' && i + 1 < args.length) {
|
||||
nextToken = args[++i];
|
||||
} else if (!arg.startsWith('--') && !userId) {
|
||||
userId = arg;
|
||||
}
|
||||
}
|
||||
|
||||
return { userId, startTime, endTime, maxResults, nextToken, debug };
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
const { userId, startTime, endTime, maxResults, nextToken, debug } = parseArgs(args);
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/list-user-done-approvals.ts <userId> [--startTime <timestamp>] [--endTime <timestamp>] [--maxResults <max>] [--nextToken <token>] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供用户 ID(userId)',
|
||||
usage: 'ts-node scripts/list-user-done-approvals.ts <userId> [--startTime <timestamp>] [--endTime <timestamp>] [--maxResults <max>] [--nextToken <token>] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在查询用户已处理的审批列表...');
|
||||
|
||||
await listUserDoneApprovals(accessToken, userId, startTime, endTime, maxResults, nextToken, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,194 @@
|
||||
// 获取用户发起的审批列表
|
||||
// 用法: ts-node scripts/list-user-initiated-approvals.ts <userId> [--startTime <timestamp>] [--endTime <timestamp>] [--maxResults <max>] [--nextToken <token>] [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const { default: dingtalkWorkflow, ListUserStartedProcessInstancesHeaders, ListUserStartedProcessInstancesRequest } = require('@alicloud/dingtalk/workflow_1_0');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
const { RuntimeOptions } = require('@alicloud/tea-util');
|
||||
|
||||
interface Result {
|
||||
success: boolean;
|
||||
userId: string;
|
||||
instances: any[];
|
||||
totalCount: number;
|
||||
hasMore: boolean;
|
||||
nextToken?: string;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function listUserInitiatedApprovals(
|
||||
accessToken: string,
|
||||
userId: string,
|
||||
startTime: number | undefined,
|
||||
endTime: number | undefined,
|
||||
maxResults: number,
|
||||
nextToken: string | undefined,
|
||||
debug: boolean
|
||||
): Promise<void> {
|
||||
const client = new dingtalkWorkflow(createConfig());
|
||||
|
||||
const headers = new ListUserStartedProcessInstancesHeaders({});
|
||||
headers.xAcsDingtalkAccessToken = accessToken;
|
||||
|
||||
const request = new ListUserStartedProcessInstancesRequest({
|
||||
userId: userId,
|
||||
startTime: startTime,
|
||||
endTime: endTime,
|
||||
maxResults: maxResults,
|
||||
nextToken: nextToken,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.listUserStartedProcessInstancesWithOptions(
|
||||
request,
|
||||
headers,
|
||||
new RuntimeOptions({})
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const instances = response.body?.result?.data || [];
|
||||
const result: Result = {
|
||||
success: true,
|
||||
userId: userId,
|
||||
instances: instances,
|
||||
totalCount: instances.length,
|
||||
hasMore: !!response.body?.result?.nextToken,
|
||||
nextToken: response.body?.result?.nextToken,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || '未知错误',
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function parseArgs(args: string[]): { userId: string; startTime?: number; endTime?: number; maxResults: number; nextToken?: string; debug: boolean } {
|
||||
let userId = '';
|
||||
let startTime: number | undefined;
|
||||
let endTime: number | undefined;
|
||||
let maxResults = 20;
|
||||
let nextToken: string | undefined;
|
||||
let debug = false;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
if (arg === '--debug') {
|
||||
debug = true;
|
||||
} else if (arg === '--startTime' && i + 1 < args.length) {
|
||||
startTime = parseInt(args[++i], 10);
|
||||
} else if (arg === '--endTime' && i + 1 < args.length) {
|
||||
endTime = parseInt(args[++i], 10);
|
||||
} else if (arg === '--maxResults' && i + 1 < args.length) {
|
||||
maxResults = parseInt(args[++i], 10);
|
||||
} else if (arg === '--nextToken' && i + 1 < args.length) {
|
||||
nextToken = args[++i];
|
||||
} else if (!arg.startsWith('--') && !userId) {
|
||||
userId = arg;
|
||||
}
|
||||
}
|
||||
|
||||
return { userId, startTime, endTime, maxResults, nextToken, debug };
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
const { userId, startTime, endTime, maxResults, nextToken, debug } = parseArgs(args);
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/list-user-initiated-approvals.ts <userId> [--startTime <timestamp>] [--endTime <timestamp>] [--maxResults <max>] [--nextToken <token>] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供用户 ID(userId)',
|
||||
usage: 'ts-node scripts/list-user-initiated-approvals.ts <userId> [--startTime <timestamp>] [--endTime <timestamp>] [--maxResults <max>] [--nextToken <token>] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在查询用户发起的审批列表...');
|
||||
|
||||
await listUserInitiatedApprovals(accessToken, userId, startTime, endTime, maxResults, nextToken, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,178 @@
|
||||
// 获取指定用户的所有父部门列表
|
||||
// 用法: ts-node scripts/list-user-parent-departments.ts <userId> [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const https = require('https');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
|
||||
interface SuccessResult {
|
||||
success: boolean;
|
||||
userId: string;
|
||||
parentIdList: number[];
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
userId: string;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function dingtalkRequest(accessToken: string, method: string, path: string, body?: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: 'oapi.dingtalk.com',
|
||||
path: `${path}?access_token=${accessToken}`,
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
} as Record<string, string>,
|
||||
};
|
||||
const req = https.request(options, (res: any) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk: string) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (parsed.errcode !== undefined && parsed.errcode !== 0) {
|
||||
reject({ code: parsed.errcode, message: parsed.errmsg });
|
||||
} else if (res.statusCode && res.statusCode >= 400) {
|
||||
reject(parsed);
|
||||
} else {
|
||||
resolve(parsed);
|
||||
}
|
||||
} catch {
|
||||
reject(new Error(`Invalid JSON response: ${data}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
if (body) req.write(JSON.stringify(body));
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function listUserParentDepartments(accessToken: string, userId: string, debug: boolean = false): Promise<void> {
|
||||
try {
|
||||
const response = await dingtalkRequest(
|
||||
accessToken,
|
||||
'POST',
|
||||
'/topapi/v2/department/listparentbyuser',
|
||||
{ userid: userId }
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const result: SuccessResult = {
|
||||
success: true,
|
||||
userId: userId,
|
||||
parentIdList: response.result || [],
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
userId: userId,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || err.msg || JSON.stringify(err),
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const debug = args.includes('--debug');
|
||||
const filteredArgs = args.filter(arg => arg !== '--debug');
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/list-user-parent-departments.ts <userId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (filteredArgs.length < 1) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供用户 ID(userId)',
|
||||
usage: 'ts-node scripts/list-user-parent-departments.ts <userId> [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const userId = filteredArgs[0];
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在获取用户的父部门列表...');
|
||||
|
||||
await listUserParentDepartments(accessToken, userId, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
184
.agents/skills/dingtalk-api/scripts/list-user-todo-approvals.ts
Normal file
184
.agents/skills/dingtalk-api/scripts/list-user-todo-approvals.ts
Normal file
@@ -0,0 +1,184 @@
|
||||
// 获取用户待处理的审批列表
|
||||
// 用法: ts-node scripts/list-user-todo-approvals.ts <userId> [--maxResults <max>] [--nextToken <token>] [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const { default: dingtalkWorkflow, ListUserTodoProcessInstancesHeaders, ListUserTodoProcessInstancesRequest } = require('@alicloud/dingtalk/workflow_1_0');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
const { RuntimeOptions } = require('@alicloud/tea-util');
|
||||
|
||||
interface Result {
|
||||
success: boolean;
|
||||
userId: string;
|
||||
instances: any[];
|
||||
totalCount: number;
|
||||
hasMore: boolean;
|
||||
nextToken?: string;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function listUserTodoApprovals(
|
||||
accessToken: string,
|
||||
userId: string,
|
||||
maxResults: number,
|
||||
nextToken: string | undefined,
|
||||
debug: boolean
|
||||
): Promise<void> {
|
||||
const client = new dingtalkWorkflow(createConfig());
|
||||
|
||||
const headers = new ListUserTodoProcessInstancesHeaders({});
|
||||
headers.xAcsDingtalkAccessToken = accessToken;
|
||||
|
||||
const request = new ListUserTodoProcessInstancesRequest({
|
||||
userId: userId,
|
||||
maxResults: maxResults,
|
||||
nextToken: nextToken,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.listUserTodoProcessInstancesWithOptions(
|
||||
request,
|
||||
headers,
|
||||
new RuntimeOptions({})
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const instances = response.body?.result?.data || [];
|
||||
const result: Result = {
|
||||
success: true,
|
||||
userId: userId,
|
||||
instances: instances,
|
||||
totalCount: instances.length,
|
||||
hasMore: !!response.body?.result?.nextToken,
|
||||
nextToken: response.body?.result?.nextToken,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || '未知错误',
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function parseArgs(args: string[]): { userId: string; maxResults: number; nextToken?: string; debug: boolean } {
|
||||
let userId = '';
|
||||
let maxResults = 20;
|
||||
let nextToken: string | undefined;
|
||||
let debug = false;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
if (arg === '--debug') {
|
||||
debug = true;
|
||||
} else if (arg === '--maxResults' && i + 1 < args.length) {
|
||||
maxResults = parseInt(args[++i], 10);
|
||||
} else if (arg === '--nextToken' && i + 1 < args.length) {
|
||||
nextToken = args[++i];
|
||||
} else if (!arg.startsWith('--') && !userId) {
|
||||
userId = arg;
|
||||
}
|
||||
}
|
||||
|
||||
return { userId, maxResults, nextToken, debug };
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
const { userId, maxResults, nextToken, debug } = parseArgs(args);
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/list-user-todo-approvals.ts <userId> [--maxResults <max>] [--nextToken <token>] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供用户 ID(userId)',
|
||||
usage: 'ts-node scripts/list-user-todo-approvals.ts <userId> [--maxResults <max>] [--nextToken <token>] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在查询用户待处理的审批列表...');
|
||||
|
||||
await listUserTodoApprovals(accessToken, userId, maxResults, nextToken, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
188
.agents/skills/dingtalk-api/scripts/search-department.ts
Normal file
188
.agents/skills/dingtalk-api/scripts/search-department.ts
Normal file
@@ -0,0 +1,188 @@
|
||||
// 钉钉部门搜索脚本
|
||||
// 用法: ts-node scripts/search-department.ts <搜索关键词> [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const { default: dingtalkContact, SearchDepartmentHeaders, SearchDepartmentRequest } = require('@alicloud/dingtalk/contact_1_0');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
const { RuntimeOptions } = require('@alicloud/tea-util');
|
||||
|
||||
interface SearchResult {
|
||||
success: boolean;
|
||||
keyword: string;
|
||||
totalCount: number;
|
||||
hasMore: boolean;
|
||||
departmentIds: number[];
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
keyword: string;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
description: string | null;
|
||||
requestId: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建钉钉客户端配置
|
||||
* @returns Config 实例
|
||||
*/
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Access Token
|
||||
* @param appKey 应用 Key
|
||||
* @param appSecret 应用 Secret
|
||||
* @returns Access Token
|
||||
*/
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索部门
|
||||
* @param accessToken Access Token
|
||||
* @param keyword 搜索关键词(部门名称)
|
||||
* @param debug 是否开启调试模式
|
||||
*/
|
||||
async function searchDepartment(accessToken: string, keyword: string, debug: boolean = false): Promise<void> {
|
||||
const client = new dingtalkContact(createConfig());
|
||||
|
||||
const headers = new SearchDepartmentHeaders({});
|
||||
headers.xAcsDingtalkAccessToken = accessToken;
|
||||
|
||||
const request = new SearchDepartmentRequest({
|
||||
queryWord: keyword,
|
||||
offset: 0,
|
||||
size: 20,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.searchDepartmentWithOptions(
|
||||
request,
|
||||
headers,
|
||||
new RuntimeOptions({})
|
||||
);
|
||||
|
||||
// 调试模式:输出完整响应
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('响应 body:', JSON.stringify(response.body, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
// 格式化输出结果
|
||||
const departmentIds = response.body?.list || [];
|
||||
const result: SearchResult = {
|
||||
success: true,
|
||||
keyword: keyword,
|
||||
totalCount: response.body?.totalCount || 0,
|
||||
hasMore: response.body?.hasMore || false,
|
||||
departmentIds: departmentIds,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
keyword: keyword,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || '未知错误',
|
||||
description: err.description || null,
|
||||
requestId: err.requestId || null,
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 主函数
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
// 检查是否有调试参数
|
||||
const debug = args.includes('--debug');
|
||||
const filteredArgs = args.filter(arg => arg !== '--debug');
|
||||
|
||||
// 从环境变量读取配置
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/search-department.ts "<搜索关键词>" [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (filteredArgs.length < 1) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供搜索关键词(部门名称)',
|
||||
usage: 'ts-node scripts/search-department.ts "<搜索关键词>" [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const keyword = filteredArgs[0];
|
||||
|
||||
try {
|
||||
// 自动获取 access_token
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在搜索部门...');
|
||||
|
||||
// 使用获取到的 token 搜索部门
|
||||
await searchDepartment(accessToken, keyword, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
175
.agents/skills/dingtalk-api/scripts/search-user.ts
Normal file
175
.agents/skills/dingtalk-api/scripts/search-user.ts
Normal file
@@ -0,0 +1,175 @@
|
||||
// 钉钉用户搜索脚本
|
||||
// 用法: ts-node scripts/search-user.ts <搜索关键词>
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const { default: dingtalkContact, SearchUserHeaders, SearchUserRequest } = require('@alicloud/dingtalk/contact_1_0');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
const { RuntimeOptions } = require('@alicloud/tea-util');
|
||||
|
||||
interface SearchResult {
|
||||
success: boolean;
|
||||
keyword: string;
|
||||
totalCount: number;
|
||||
hasMore: boolean;
|
||||
userIds: string[];
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
keyword: string;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
description: string | null;
|
||||
requestId: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建钉钉客户端配置
|
||||
* @returns Config 实例
|
||||
*/
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Access Token
|
||||
* @param appKey 应用 Key
|
||||
* @param appSecret 应用 Secret
|
||||
* @returns Access Token
|
||||
*/
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索用户
|
||||
* @param accessToken Access Token
|
||||
* @param keyword 搜索关键词(姓名)
|
||||
*/
|
||||
async function searchUser(accessToken: string, keyword: string): Promise<void> {
|
||||
const client = new dingtalkContact(createConfig());
|
||||
|
||||
const headers = new SearchUserHeaders({});
|
||||
headers.xAcsDingtalkAccessToken = accessToken;
|
||||
|
||||
const request = new SearchUserRequest({
|
||||
queryWord: keyword,
|
||||
offset: 0,
|
||||
size: 20,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.searchUserWithOptions(
|
||||
request,
|
||||
headers,
|
||||
new RuntimeOptions({})
|
||||
);
|
||||
|
||||
// 格式化输出结果
|
||||
const userIds = response.body?.list || [];
|
||||
const result: SearchResult = {
|
||||
success: true,
|
||||
keyword: keyword,
|
||||
totalCount: response.body?.totalCount || 0,
|
||||
hasMore: response.body?.hasMore || false,
|
||||
userIds: userIds,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
keyword: keyword,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || '未知错误',
|
||||
description: err.description || null,
|
||||
requestId: err.requestId || null,
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 主函数
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
// 从环境变量读取配置
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/search-user.ts "<搜索关键词>"'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (args.length < 1) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供搜索关键词',
|
||||
usage: 'ts-node scripts/search-user.ts "<搜索关键词>"'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const keyword = args[0];
|
||||
|
||||
try {
|
||||
// 自动获取 access_token
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在搜索用户...');
|
||||
|
||||
// 使用获取到的 token 搜索用户
|
||||
await searchUser(accessToken, keyword);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
206
.agents/skills/dingtalk-api/scripts/send-group-message.ts
Normal file
206
.agents/skills/dingtalk-api/scripts/send-group-message.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
// 机器人发送群消息脚本
|
||||
// 用法: ts-node scripts/send-group-message.ts <openConversationId> <robotCode> <消息内容>
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const { default: dingtalkRobot, OrgGroupSendHeaders, OrgGroupSendRequest } = require('@alicloud/dingtalk/robot_1_0');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
const { RuntimeOptions } = require('@alicloud/tea-util');
|
||||
|
||||
interface SendMessageResult {
|
||||
success: boolean;
|
||||
openConversationId: string;
|
||||
robotCode: string;
|
||||
processQueryKey: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
description: string | null;
|
||||
requestId: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建钉钉客户端配置
|
||||
* @returns Config 实例
|
||||
*/
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Access Token
|
||||
* @param appKey 应用 Key
|
||||
* @param appSecret 应用 Secret
|
||||
* @returns Access Token
|
||||
*/
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送群消息
|
||||
* @param accessToken Access Token
|
||||
* @param openConversationId 开放会话 ID
|
||||
* @param robotCode 机器人 Code
|
||||
* @param message 消息内容
|
||||
* @param debug 是否开启调试模式
|
||||
*/
|
||||
async function sendGroupMessage(
|
||||
accessToken: string,
|
||||
openConversationId: string,
|
||||
robotCode: string,
|
||||
message: string,
|
||||
debug: boolean = false
|
||||
): Promise<void> {
|
||||
const client = new dingtalkRobot(createConfig());
|
||||
|
||||
const headers = new OrgGroupSendHeaders({});
|
||||
headers.xAcsDingtalkAccessToken = accessToken;
|
||||
|
||||
// 构建消息参数
|
||||
const msgParam = JSON.stringify({ content: message });
|
||||
|
||||
const request = new OrgGroupSendRequest({
|
||||
openConversationId: openConversationId,
|
||||
robotCode: robotCode,
|
||||
msgKey: 'sampleText', // 文本消息类型
|
||||
msgParam: msgParam,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.orgGroupSendWithOptions(
|
||||
request,
|
||||
headers,
|
||||
new RuntimeOptions({})
|
||||
);
|
||||
|
||||
// 调试模式:输出完整响应
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('响应 body:', JSON.stringify(response.body, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
// 格式化输出结果
|
||||
const processQueryKey = response.body?.processQueryKey;
|
||||
|
||||
if (!processQueryKey) {
|
||||
throw new Error('发送消息失败:响应中未包含 processQueryKey');
|
||||
}
|
||||
|
||||
const result: SendMessageResult = {
|
||||
success: true,
|
||||
openConversationId: openConversationId,
|
||||
robotCode: robotCode,
|
||||
processQueryKey: processQueryKey,
|
||||
message: message,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
console.error(`✅ 消息发送成功!processQueryKey: ${processQueryKey}`);
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || '未知错误',
|
||||
description: err.description || null,
|
||||
requestId: err.requestId || null,
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 主函数
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
// 检查是否有调试参数
|
||||
const debug = args.includes('--debug');
|
||||
const filteredArgs = args.filter(arg => arg !== '--debug');
|
||||
|
||||
// 从环境变量读取配置
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/send-group-message.ts "<openConversationId>" "<robotCode>" "<消息内容>" [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (filteredArgs.length < 3) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供 openConversationId、robotCode 和消息内容',
|
||||
usage: 'ts-node scripts/send-group-message.ts "<openConversationId>" "<robotCode>" "<消息内容>" [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const openConversationId = filteredArgs[0];
|
||||
const robotCode = filteredArgs[1];
|
||||
const message = filteredArgs[2];
|
||||
|
||||
try {
|
||||
// 自动获取 access_token
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在发送群消息...');
|
||||
|
||||
// 使用获取到的 token 发送消息
|
||||
await sendGroupMessage(accessToken, openConversationId, robotCode, message, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'SEND_FAILED',
|
||||
message: err.message || '发送失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
210
.agents/skills/dingtalk-api/scripts/send-user-message.ts
Normal file
210
.agents/skills/dingtalk-api/scripts/send-user-message.ts
Normal file
@@ -0,0 +1,210 @@
|
||||
// 机器人发送单聊消息脚本
|
||||
// 用法: ts-node scripts/send-user-message.ts <userId> <robotCode> <消息内容> [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const { default: dingtalkRobot, BatchSendOTOHeaders, BatchSendOTORequest } = require('@alicloud/dingtalk/robot_1_0');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
const { RuntimeOptions } = require('@alicloud/tea-util');
|
||||
|
||||
interface SendMessageResult {
|
||||
success: boolean;
|
||||
userId: string;
|
||||
robotCode: string;
|
||||
processQueryKey: string;
|
||||
flowControlledStaffIdList: string[];
|
||||
invalidStaffIdList: string[];
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
description: string | null;
|
||||
requestId: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建钉钉客户端配置
|
||||
* @returns Config 实例
|
||||
*/
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Access Token
|
||||
* @param appKey 应用 Key
|
||||
* @param appSecret 应用 Secret
|
||||
* @returns Access Token
|
||||
*/
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送单聊消息
|
||||
* @param accessToken Access Token
|
||||
* @param userId 用户 ID
|
||||
* @param robotCode 机器人 Code
|
||||
* @param message 消息内容
|
||||
* @param debug 是否开启调试模式
|
||||
*/
|
||||
async function sendUserMessage(
|
||||
accessToken: string,
|
||||
userId: string,
|
||||
robotCode: string,
|
||||
message: string,
|
||||
debug: boolean = false
|
||||
): Promise<void> {
|
||||
const client = new dingtalkRobot(createConfig());
|
||||
|
||||
const headers = new BatchSendOTOHeaders({});
|
||||
headers.xAcsDingtalkAccessToken = accessToken;
|
||||
|
||||
// 构建消息参数
|
||||
const msgParam = JSON.stringify({ content: message });
|
||||
|
||||
const request = new BatchSendOTORequest({
|
||||
robotCode: robotCode,
|
||||
userIds: [userId],
|
||||
msgKey: 'sampleText', // 文本消息类型
|
||||
msgParam: msgParam,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.batchSendOTOWithOptions(
|
||||
request,
|
||||
headers,
|
||||
new RuntimeOptions({})
|
||||
);
|
||||
|
||||
// 调试模式:输出完整响应
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('响应 body:', JSON.stringify(response.body, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
// 格式化输出结果
|
||||
const processQueryKey = response.body?.processQueryKey;
|
||||
|
||||
if (!processQueryKey) {
|
||||
throw new Error('发送消息失败:响应中未包含 processQueryKey');
|
||||
}
|
||||
|
||||
const result: SendMessageResult = {
|
||||
success: true,
|
||||
userId: userId,
|
||||
robotCode: robotCode,
|
||||
processQueryKey: processQueryKey,
|
||||
flowControlledStaffIdList: response.body?.flowControlledStaffIdList || [],
|
||||
invalidStaffIdList: response.body?.invalidStaffIdList || [],
|
||||
message: message,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
console.error(`✅ 消息发送成功!processQueryKey: ${processQueryKey}`);
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || '未知错误',
|
||||
description: err.description || null,
|
||||
requestId: err.requestId || null,
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 主函数
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
// 检查是否有调试参数
|
||||
const debug = args.includes('--debug');
|
||||
const filteredArgs = args.filter(arg => arg !== '--debug');
|
||||
|
||||
// 从环境变量读取配置
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/send-user-message.ts "<userId>" "<robotCode>" "<消息内容>" [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (filteredArgs.length < 3) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供 userId、robotCode 和消息内容',
|
||||
usage: 'ts-node scripts/send-user-message.ts "<userId>" "<robotCode>" "<消息内容>" [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const userId = filteredArgs[0];
|
||||
const robotCode = filteredArgs[1];
|
||||
const message = filteredArgs[2];
|
||||
|
||||
try {
|
||||
// 自动获取 access_token
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在发送单聊消息...');
|
||||
|
||||
// 使用获取到的 token 发送消息
|
||||
await sendUserMessage(accessToken, userId, robotCode, message, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'SEND_FAILED',
|
||||
message: err.message || '发送失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,179 @@
|
||||
// 撤销/终止审批实例
|
||||
// 用法: ts-node scripts/terminate-approval-instance.ts <instanceId> <operatingUserId> [--remark "撤销原因"] [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const { default: dingtalkWorkflow, TerminateProcessInstanceHeaders, TerminateProcessInstanceRequest } = require('@alicloud/dingtalk/workflow_1_0');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
const { RuntimeOptions } = require('@alicloud/tea-util');
|
||||
|
||||
interface Result {
|
||||
success: boolean;
|
||||
instanceId: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function terminateApprovalInstance(
|
||||
accessToken: string,
|
||||
instanceId: string,
|
||||
operatingUserId: string,
|
||||
remark: string | undefined,
|
||||
debug: boolean
|
||||
): Promise<void> {
|
||||
const client = new dingtalkWorkflow(createConfig());
|
||||
|
||||
const headers = new TerminateProcessInstanceHeaders({});
|
||||
headers.xAcsDingtalkAccessToken = accessToken;
|
||||
|
||||
const request = new TerminateProcessInstanceRequest({
|
||||
processInstanceId: instanceId,
|
||||
operatingUserId: operatingUserId,
|
||||
remark: remark || '撤销审批',
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.terminateProcessInstanceWithOptions(
|
||||
request,
|
||||
headers,
|
||||
new RuntimeOptions({})
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const result: Result = {
|
||||
success: response.body?.result?.success || false,
|
||||
instanceId: instanceId,
|
||||
message: '审批实例已终止',
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || '未知错误',
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function parseArgs(args: string[]): { instanceId: string; operatingUserId: string; remark?: string; debug: boolean } {
|
||||
let instanceId = '';
|
||||
let operatingUserId = '';
|
||||
let remark: string | undefined;
|
||||
let debug = false;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
if (arg === '--debug') {
|
||||
debug = true;
|
||||
} else if (arg === '--remark' && i + 1 < args.length) {
|
||||
remark = args[++i];
|
||||
} else if (!arg.startsWith('--')) {
|
||||
if (!instanceId) {
|
||||
instanceId = arg;
|
||||
} else if (!operatingUserId) {
|
||||
operatingUserId = arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { instanceId, operatingUserId, remark, debug };
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
const { instanceId, operatingUserId, remark, debug } = parseArgs(args);
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/terminate-approval-instance.ts <instanceId> <operatingUserId> [--remark "撤销原因"] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!instanceId || !operatingUserId) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供审批实例 ID(instanceId)和操作人用户 ID(operatingUserId)',
|
||||
usage: 'ts-node scripts/terminate-approval-instance.ts <instanceId> <operatingUserId> [--remark "撤销原因"] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在终止审批实例...');
|
||||
|
||||
await terminateApprovalInstance(accessToken, instanceId, operatingUserId, remark, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
220
.agents/skills/dingtalk-api/scripts/transfer-approval-task.ts
Normal file
220
.agents/skills/dingtalk-api/scripts/transfer-approval-task.ts
Normal file
@@ -0,0 +1,220 @@
|
||||
// 转交审批任务
|
||||
// 用法: ts-node scripts/transfer-approval-task.ts <instanceId> <userId> <transferToUserId> [--taskId <taskId>] [--remark "转交原因"] [--debug]
|
||||
// 需要设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET
|
||||
export {};
|
||||
|
||||
const https = require('https');
|
||||
const { default: dingtalkOauth2_1_0, GetAccessTokenRequest } = require('@alicloud/dingtalk/oauth2_1_0');
|
||||
const { Config } = require('@alicloud/openapi-client');
|
||||
|
||||
interface Result {
|
||||
success: boolean;
|
||||
instanceId: string;
|
||||
userId: string;
|
||||
transferToUserId: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
success: false;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createConfig(): any {
|
||||
const config = new Config({});
|
||||
config.protocol = "https";
|
||||
config.regionId = "central";
|
||||
return config;
|
||||
}
|
||||
|
||||
async function getAccessToken(appKey: string, appSecret: string): Promise<string> {
|
||||
const config = createConfig();
|
||||
const client = new dingtalkOauth2_1_0(config);
|
||||
|
||||
const request = new GetAccessTokenRequest({
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.getAccessToken(request);
|
||||
const accessToken = response.body?.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('获取 access_token 失败:响应中未包含 token');
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
} catch (err: any) {
|
||||
throw new Error(`获取 access_token 失败: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function dingtalkRequest(accessToken: string, path: string, body?: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: 'oapi.dingtalk.com',
|
||||
path: `${path}?access_token=${accessToken}`,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
} as Record<string, string>,
|
||||
};
|
||||
const req = https.request(options, (res: any) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk: string) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (parsed.errcode !== undefined && parsed.errcode !== 0) {
|
||||
reject({ code: parsed.errcode, message: parsed.errmsg });
|
||||
} else if (res.statusCode && res.statusCode >= 400) {
|
||||
reject(parsed);
|
||||
} else {
|
||||
resolve(parsed);
|
||||
}
|
||||
} catch {
|
||||
reject(new Error(`Invalid JSON response: ${data}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
if (body) req.write(JSON.stringify(body));
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function transferApprovalTask(
|
||||
accessToken: string,
|
||||
instanceId: string,
|
||||
taskId: string | undefined,
|
||||
userId: string,
|
||||
transferToUserId: string,
|
||||
remark: string | undefined,
|
||||
debug: boolean
|
||||
): Promise<void> {
|
||||
try {
|
||||
// 转交审批使用 TOP API
|
||||
const response = await dingtalkRequest(
|
||||
accessToken,
|
||||
'/topapi/process/workrecord/task/transfer',
|
||||
{
|
||||
process_instance_id: instanceId,
|
||||
task_id: taskId,
|
||||
userid: userId,
|
||||
transfer_to_userid: transferToUserId,
|
||||
remark: remark || '转交审批任务',
|
||||
}
|
||||
);
|
||||
|
||||
if (debug) {
|
||||
console.error('\n=== 调试信息 ===');
|
||||
console.error('完整响应:', JSON.stringify(response, null, 2));
|
||||
console.error('==============\n');
|
||||
}
|
||||
|
||||
const result: Result = {
|
||||
success: response.errcode === 0,
|
||||
instanceId: instanceId,
|
||||
userId: userId,
|
||||
transferToUserId: transferToUserId,
|
||||
message: '审批任务已转交',
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (err: any) {
|
||||
const errorResult: ErrorResult = {
|
||||
success: false,
|
||||
error: {
|
||||
code: err.code || 'UNKNOWN_ERROR',
|
||||
message: err.message || '未知错误',
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function parseArgs(args: string[]): { instanceId: string; taskId?: string; userId: string; transferToUserId: string; remark?: string; debug: boolean } {
|
||||
let instanceId = '';
|
||||
let taskId: string | undefined;
|
||||
let userId = '';
|
||||
let transferToUserId = '';
|
||||
let remark: string | undefined;
|
||||
let debug = false;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
if (arg === '--debug') {
|
||||
debug = true;
|
||||
} else if (arg === '--taskId' && i + 1 < args.length) {
|
||||
taskId = args[++i];
|
||||
} else if (arg === '--remark' && i + 1 < args.length) {
|
||||
remark = args[++i];
|
||||
} else if (!arg.startsWith('--')) {
|
||||
if (!instanceId) {
|
||||
instanceId = arg;
|
||||
} else if (!userId) {
|
||||
userId = arg;
|
||||
} else if (!transferToUserId) {
|
||||
transferToUserId = arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { instanceId, taskId, userId, transferToUserId, remark, debug };
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
const { instanceId, taskId, userId, transferToUserId, remark, debug } = parseArgs(args);
|
||||
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
const appSecret = process.env.DINGTALK_APP_SECRET;
|
||||
|
||||
if (!appKey || !appSecret) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_CREDENTIALS',
|
||||
message: '缺少钉钉应用凭证,请设置环境变量 DINGTALK_APP_KEY 和 DINGTALK_APP_SECRET',
|
||||
usage: 'export DINGTALK_APP_KEY=your-app-key && export DINGTALK_APP_SECRET=your-app-secret && ts-node scripts/transfer-approval-task.ts <instanceId> <userId> <transferToUserId> [--taskId <taskId>] [--remark "转交原因"] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!instanceId || !userId || !transferToUserId) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ARGUMENTS',
|
||||
message: '参数错误:需要提供审批实例 ID(instanceId)、当前处理人用户 ID(userId)和转交目标用户 ID(transferToUserId)',
|
||||
usage: 'ts-node scripts/transfer-approval-task.ts <instanceId> <userId> <transferToUserId> [--taskId <taskId>] [--remark "转交原因"] [--debug]'
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
console.error('正在获取 access_token...');
|
||||
const accessToken = await getAccessToken(appKey, appSecret);
|
||||
console.error('access_token 获取成功,正在转交审批任务...');
|
||||
|
||||
await transferApprovalTask(accessToken, instanceId, taskId, userId, transferToUserId, remark, debug);
|
||||
} catch (err: any) {
|
||||
console.error(JSON.stringify({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'AUTH_FAILED',
|
||||
message: err.message || '认证失败',
|
||||
}
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user