feat: update MCP config and TOOLS.md with calendar/contacts note

This commit is contained in:
root
2026-03-29 18:45:26 +08:00
parent 1a9fdc7274
commit c3e845f89c
3427 changed files with 2447281 additions and 1 deletions

View 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: '参数错误:需要提供审批实例 IDinstanceId、用户 IDuserId和评论内容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();

View 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();

View 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: '参数错误:需要提供审批实例 IDinstanceId、用户 IDuserId和审批结果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();

View 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: '参数错误:需要提供审批实例 IDinstanceId',
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();

View 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();

View 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: '参数错误:需要提供部门 IDdeptId',
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();

View 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();

View 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();

View 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();

View 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: '参数错误:需要提供用户 IDuserId',
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();

View 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: '参数错误:需要提供用户 IDuserId',
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();

View File

@@ -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 和 endTimeUnix 时间戳,毫秒)',
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();

View 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: '参数错误:需要提供部门 IDdeptId',
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();

View File

@@ -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();

View 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: '参数错误:需要提供部门 IDdeptId',
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();

View 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: '参数错误:需要提供部门 IDdeptId',
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();

View 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();

View 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();

View 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: '参数错误:需要提供部门 IDdeptId根部门为 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();

View 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: '参数错误:需要提供用户 IDuserId',
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();

View 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: '参数错误:需要提供用户 IDuserId',
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();

View File

@@ -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: '参数错误:需要提供用户 IDuserId',
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();

View File

@@ -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: '参数错误:需要提供用户 IDuserId',
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();

View 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: '参数错误:需要提供用户 IDuserId',
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();

View 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();

View 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();

View 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();

View 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();

View File

@@ -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: '参数错误:需要提供审批实例 IDinstanceId和操作人用户 IDoperatingUserId',
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();

View 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: '参数错误:需要提供审批实例 IDinstanceId、当前处理人用户 IDuserId和转交目标用户 IDtransferToUserId',
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();