C#调用钉钉API发送通知教程

在企业信息化建设中,实时通知是提升工作效率的重要手段。钉钉作为国内流行的企业级通讯工具,提供了丰富的API接口,允许开发者通过代码发送各类通知。本文将详细介绍如何使用C#语言调用钉钉API发送通知,帮助企业实现自动化消息推送。

一、钉钉开放平台配置

在开始编码前,我们需要在钉钉开放平台完成相关配置,获取调用API所需的凭证。

1.1 创建钉钉应用

  1. 登录钉钉开放平台

  2. 进入"应用开发" -> "企业内部开发" -> "应用管理"

  3. 点击"创建应用",填写应用名称、描述等信息

  4. 上传应用图标,选择应用类型为"H5微应用"

  5. 点击"确定创建"按钮完成应用创建

1.2 获取应用凭证

应用创建成功后,我们需要获取两个重要的凭证:

  1. AppKey:应用的唯一标识

  2. AppSecret:应用的密钥,用于生成访问令牌

这两个凭证可以在应用详情页的"凭证与基础信息"中找到。

1.3 添加接口权限

为了能够发送通知,我们需要为应用添加相应的接口权限:

  1. 在应用详情页中,点击"权限管理"

  2. 搜索并添加以下权限:

    • 企业消息通知

    • 获取部门基础信息(如需按部门发送通知)

    • 获取用户基本信息(如需指定用户发送通知)

  3. 点击"申请权限",等待管理员审核通过

1.4 创建自定义机器人(可选)

除了通过应用发送通知外,我们还可以通过自定义机器人发送群消息:

  1. 打开钉钉群聊,点击右上角"群设置"

  2. 选择"智能群助手" -> "添加机器人"

  3. 选择"自定义机器人"

  4. 设置机器人名称、安全设置(建议选择"加签"方式)

  5. 完成创建后,复制Webhook地址,保存备用

二、C#项目准备

现在,我们开始准备C#项目环境,为调用钉钉API做准备。

2.1 创建项目

  1. 打开Visual Studio,创建一个新的控制台应用或Web应用项目

  2. 选择.NET Framework或.NET Core版本(本文以.NET Core 3.1为例)

2.2 添加必要的NuGet包

我们需要添加以下NuGet包来简化开发:

# 使用Package Manager Console
Install-Package Newtonsoft.Json
Install-Package RestSharp

或通过NuGet包管理器界面搜索并安装这些包。

2.3 创建配置文件

在项目中添加一个配置文件appsettings.json,用于存储我们在钉钉开放平台获取的凭证信息:

{
  "DingTalk": {
    "AppKey": "your_app_key",
    "AppSecret": "your_app_secret",
    "AgentId": 123456789,
    "Webhook": "your_webhook_url",
    "Secret": "your_robot_secret"
  }
}

三、实现钉钉API调用工具类

接下来,我们将创建一个工具类,封装钉钉API的调用方法。

3.1 创建访问令牌管理类

首先,我们需要创建一个类来管理访问令牌(AccessToken):

using Newtonsoft.Json;
using RestSharp;
using System;
using System.Configuration;
using System.Threading.Tasks;
​
public class DingTalkTokenManager
{
    private static string _accessToken = string.Empty;
    private static DateTime _tokenExpireTime = DateTime.MinValue;
    private readonly string _appKey;
    private readonly string _appSecret;
​
    public DingTalkTokenManager(string appKey, string appSecret)
    {
        _appKey = appKey;
        _appSecret = appSecret;
    }
​
    public async Task<string> GetAccessTokenAsync()
    {
        // 检查令牌是否已过期,如未过期则直接返回
        if (!string.IsNullOrEmpty(_accessToken) && DateTime.Now < _tokenExpireTime)
        {
            return _accessToken;
        }
​
        // 调用钉钉API获取新的访问令牌
        var client = new RestClient("https://oapi.dingtalk.com");
        var request = new RestRequest("gettoken", Method.GET);
        request.AddParameter("appkey", _appKey);
        request.AddParameter("appsecret", _appSecret);
​
        var response = await client.ExecuteAsync(request);
        var result = JsonConvert.DeserializeObject<dynamic>(response.Content);
​
        if (result.errcode == 0)
        {
            _accessToken = result.access_token;
            // 访问令牌有效期为7200秒,我们设置提前10分钟刷新
            _tokenExpireTime = DateTime.Now.AddSeconds(result.expires_in - 600);
            return _accessToken;
        }
        else
        {
            throw new Exception($"获取访问令牌失败:{result.errmsg}");
        }
    }
}

3.2 创建钉钉消息发送类

接下来,我们创建一个类来处理各种类型的消息发送:

using Newtonsoft.Json;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
​
public class DingTalkMessageSender
{
    private readonly DingTalkTokenManager _tokenManager;
    private readonly string _agentId;
    private readonly string _webhook;
    private readonly string _secret;
​
    public DingTalkMessageSender(string appKey, string appSecret, string agentId, string webhook = "", string secret = "")
    {
        _tokenManager = new DingTalkTokenManager(appKey, appSecret);
        _agentId = agentId;
        _webhook = webhook;
        _secret = secret;
    }
​
    /// <summary>
    /// 发送工作通知消息
    /// </summary>
    public async Task<dynamic> SendWorkNoticeAsync(string userIdList, string deptIdList, string toAllUser, string message)
    {
        var accessToken = await _tokenManager.GetAccessTokenAsync();
        var client = new RestClient("https://oapi.dingtalk.com");
        var request = new RestRequest("topapi/message/corpconversation/asyncsend_v2", Method.POST);
        request.AddParameter("access_token", accessToken);
​
        var requestBody = new
        {
            agent_id = _agentId,
            userid_list = userIdList, // 用户ID列表,多个用户用逗号分隔
            dept_id_list = deptIdList, // 部门ID列表,多个部门用逗号分隔
            to_all_user = toAllUser, // 是否发送给全员
            msg = new
            {
                msgtype = "text",
                text = new { content = message }
            }
        };
​
        request.AddJsonBody(requestBody);
        var response = await client.ExecuteAsync(request);
        return JsonConvert.DeserializeObject<dynamic>(response.Content);
    }
​
    /// <summary>
    /// 通过机器人发送群消息
    /// </summary>
    public async Task<dynamic> SendRobotMessageAsync(string message)
    {
        if (string.IsNullOrEmpty(_webhook))
        {
            throw new Exception("Webhook地址未配置");
        }
​
        var client = new RestClient(_webhook);
        var request = new RestRequest(Method.POST);
        
        // 如果配置了加签,需要生成签名
        if (!string.IsNullOrEmpty(_secret))
        {
            var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString();
            var stringToSign = $"{timestamp}\n{_secret}";
            var signature = ComputeHmacSha256(stringToSign);
            request.AddQueryParameter("timestamp", timestamp);
            request.AddQueryParameter("sign", signature);
        }
​
        var requestBody = new
        {
            msgtype = "text",
            text = new { content = message }
        };
​
        request.AddJsonBody(requestBody);
        var response = await client.ExecuteAsync(request);
        return JsonConvert.DeserializeObject<dynamic>(response.Content);
    }
​
    /// <summary>
    /// 计算HMAC-SHA256签名
    /// </summary>
    private string ComputeHmacSha256(string data)
    {
        var encoding = new UTF8Encoding();
        byte[] keyBytes = encoding.GetBytes(_secret);
        byte[] messageBytes = encoding.GetBytes(data);
​
        using (var hmacsha256 = new HMACSHA256(keyBytes))
        {
            byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
            return Convert.ToBase64String(hashmessage);
        }
    }
}

四、实现发送不同类型的通知

钉钉支持多种消息类型,除了基本的文本消息外,还支持链接、Markdown、卡片等丰富的消息类型。下面我们来实现几种常见的消息发送方式。

4.1 发送文本消息

/// <summary>
/// 发送文本消息(扩展方法)
/// </summary>
public async Task<dynamic> SendTextMessageAsync(string userIdList, string message)
{
    return await SendWorkNoticeAsync(userIdList, "", "false", message);
}

4.2 发送Markdown消息

/// <summary>
/// 发送Markdown消息
/// </summary>
public async Task<dynamic> SendMarkdownMessageAsync(string userIdList, string title, string markdownText)
{
    var accessToken = await _tokenManager.GetAccessTokenAsync();
    var client = new RestClient("https://oapi.dingtalk.com");
    var request = new RestRequest("topapi/message/corpconversation/asyncsend_v2", Method.POST);
    request.AddParameter("access_token", accessToken);
​
    var requestBody = new
    {
        agent_id = _agentId,
        userid_list = userIdList,
        to_all_user = "false",
        msg = new
        {
            msgtype = "markdown",
            markdown = new
            {
                title = title,
                text = markdownText
            }
        }
    };
​
    request.AddJsonBody(requestBody);
    var response = await client.ExecuteAsync(request);
    return JsonConvert.DeserializeObject<dynamic>(response.Content);
}

4.3 发送卡片消息

/// <summary>
/// 发送卡片消息
/// </summary>
public async Task<dynamic> SendActionCardMessageAsync(string userIdList, string title, string markdownText, string singleTitle, string singleURL)
{
    var accessToken = await _tokenManager.GetAccessTokenAsync();
    var client = new RestClient("https://oapi.dingtalk.com");
    var request = new RestRequest("topapi/message/corpconversation/asyncsend_v2", Method.POST);
    request.AddParameter("access_token", accessToken);
​
    var requestBody = new
    {
        agent_id = _agentId,
        userid_list = userIdList,
        to_all_user = "false",
        msg = new
        {
            msgtype = "action_card",
            action_card = new
            {
                title = title,
                text = markdownText,
                single_title = singleTitle,
                single_url = singleURL,
                btn_orientation = "0"
            }
        }
    };
​
    request.AddJsonBody(requestBody);
    var response = await client.ExecuteAsync(request);
    return JsonConvert.DeserializeObject<dynamic>(response.Content);
}

五、完整使用示例

下面是一个完整的使用示例,展示如何在实际项目中使用我们创建的工具类发送钉钉通知:

using Microsoft.Extensions.Configuration;
using System;
using System.IO;
using System.Threading.Tasks;
​
class Program
{
    static async Task Main(string[] args)
    {
        // 读取配置文件
        var configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json")
            .Build();
​
        // 获取配置信息
        var appKey = configuration["DingTalk:AppKey"];
        var appSecret = configuration["DingTalk:AppSecret"];
        var agentId = configuration["DingTalk:AgentId"];
        var webhook = configuration["DingTalk:Webhook"];
        var secret = configuration["DingTalk:Secret"];
​
        try
        {
            // 初始化消息发送器
            var messageSender = new DingTalkMessageSender(appKey, appSecret, agentId, webhook, secret);
​
            // 示例1:发送工作通知文本消息
            Console.WriteLine("发送工作通知文本消息...");
            var userIdList = "user1,user2";
            var textResponse = await messageSender.SendTextMessageAsync(userIdList, "这是一条测试消息,由C#程序发送。");
            Console.WriteLine($"发送结果:{JsonConvert.SerializeObject(textResponse)}");
​
            // 示例2:发送Markdown消息
            Console.WriteLine("\n发送Markdown消息...");
            var markdownTitle = "项目进度通知";
            var markdownContent = @"# 项目进度通报\n\n> 项目A已完成70%\n> 项目B已完成45%\n\n## 本周重点任务\n- 完成项目A的模块三开发\n- 开始项目B的系统测试\n\n**请相关负责人注意时间节点!**";
            var markdownResponse = await messageSender.SendMarkdownMessageAsync(userIdList, markdownTitle, markdownContent);
            Console.WriteLine($"发送结果:{JsonConvert.SerializeObject(markdownResponse)}");
​
            // 示例3:发送卡片消息
            Console.WriteLine("\n发送卡片消息...");
            var cardTitle = "重要会议通知";
            var cardContent = @"# 产品评审会议\n\n**时间:** 2023年10月15日 14:00-16:00\n**地点:** 三楼会议室A\n**参会人员:** 产品部全体成员、开发团队负责人\n\n## 会议议程\n1. 产品需求回顾\n2. 功能设计评审\n3. 开发进度同步\n4. 问题讨论与解决";
            var cardResponse = await messageSender.SendActionCardMessageAsync(userIdList, cardTitle, cardContent, "查看详情", "https://example.com/meeting-details");
            Console.WriteLine($"发送结果:{JsonConvert.SerializeObject(cardResponse)}");
​
            // 示例4:通过机器人发送群消息
            Console.WriteLine("\n通过机器人发送群消息...");
            var robotResponse = await messageSender.SendRobotMessageAsync("【系统通知】服务器备份任务已完成,备份文件大小:2.5GB");
            Console.WriteLine($"发送结果:{JsonConvert.SerializeObject(robotResponse)}");
​
            Console.WriteLine("\n所有消息发送完成!");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"发送消息时发生错误:{ex.Message}");
        }
​
        Console.ReadLine();
    }
}
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿登林

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值