C#調(diào)用釘釘API發(fā)送通知的實(shí)現(xiàn)示例
在企業(yè)信息化建設(shè)中,實(shí)時(shí)通知是提升工作效率的重要手段。釘釘作為國(guó)內(nèi)流行的企業(yè)級(jí)通訊工具,提供了豐富的API接口,允許開發(fā)者通過(guò)代碼發(fā)送各類通知。本文將詳細(xì)介紹如何使用C#語(yǔ)言調(diào)用釘釘API發(fā)送通知,幫助企業(yè)實(shí)現(xiàn)自動(dòng)化消息推送。
一、釘釘開放平臺(tái)配置
在開始編碼前,我們需要在釘釘開放平臺(tái) 完成相關(guān)配置,獲取調(diào)用API所需的憑證。
1.1 創(chuàng)建釘釘應(yīng)用
- 登錄釘釘開放平臺(tái)
- 進(jìn)入"應(yīng)用開發(fā)" -> "企業(yè)內(nèi)部開發(fā)" -> "應(yīng)用管理"
- 點(diǎn)擊"創(chuàng)建應(yīng)用",填寫應(yīng)用名稱、描述等信息
- 上傳應(yīng)用圖標(biāo),選擇應(yīng)用類型為"H5微應(yīng)用"
- 點(diǎn)擊"確定創(chuàng)建"按鈕完成應(yīng)用創(chuàng)建
1.2 獲取應(yīng)用憑證
應(yīng)用創(chuàng)建成功后,我們需要獲取兩個(gè)重要的憑證:
- AppKey:應(yīng)用的唯一標(biāo)識(shí)
- AppSecret:應(yīng)用的密鑰,用于生成訪問(wèn)令牌
這兩個(gè)憑證可以在應(yīng)用詳情頁(yè)的"憑證與基礎(chǔ)信息"中找到。
1.3 添加接口權(quán)限
為了能夠發(fā)送通知,我們需要為應(yīng)用添加相應(yīng)的接口權(quán)限:
在應(yīng)用詳情頁(yè)中,點(diǎn)擊"權(quán)限管理"
搜索并添加以下權(quán)限:
企業(yè)消息通知獲取部門基礎(chǔ)信息(如需按部門發(fā)送通知)獲取用戶基本信息(如需指定用戶發(fā)送通知)
點(diǎn)擊"申請(qǐng)權(quán)限",等待管理員審核通過(guò)
1.4 創(chuàng)建自定義機(jī)器人(可選)
除了通過(guò)應(yīng)用發(fā)送通知外,我們還可以通過(guò)自定義機(jī)器人發(fā)送群消息:
- 打開釘釘群聊,點(diǎn)擊右上角"群設(shè)置"
- 選擇"智能群助手" -> "添加機(jī)器人"
- 選擇"自定義機(jī)器人"
- 設(shè)置機(jī)器人名稱、安全設(shè)置(建議選擇"加簽"方式)
- 完成創(chuàng)建后,復(fù)制Webhook地址,保存?zhèn)溆?/li>
二、C#項(xiàng)目準(zhǔn)備
現(xiàn)在,我們開始準(zhǔn)備C#項(xiàng)目環(huán)境,為調(diào)用釘釘API做準(zhǔn)備。
2.1 創(chuàng)建項(xiàng)目
- 打開Visual Studio,創(chuàng)建一個(gè)新的控制臺(tái)應(yīng)用或Web應(yīng)用項(xiàng)目
- 選擇.NET Framework或.NET Core版本(本文以.NET Core 3.1為例)
2.2 添加必要的NuGet包
我們需要添加以下NuGet包來(lái)簡(jiǎn)化開發(fā):
# 使用Package Manager Console Install-Package Newtonsoft.Json Install-Package RestSharp
或通過(guò)NuGet包管理器界面搜索并安裝這些包。
2.3 創(chuàng)建配置文件
在項(xiàng)目中添加一個(gè)配置文件appsettings.json,用于存儲(chǔ)我們?cè)卺斸旈_放平臺(tái)獲取的憑證信息:
{
"DingTalk": {
"AppKey": "your_app_key",
"AppSecret": "your_app_secret",
"AgentId": 123456789,
"Webhook": "your_webhook_url",
"Secret": "your_robot_secret"
}
}三、實(shí)現(xiàn)釘釘API調(diào)用工具類
接下來(lái),我們將創(chuàng)建一個(gè)工具類,封裝釘釘API的調(diào)用方法。
3.1 創(chuàng)建訪問(wèn)令牌管理類
首先,我們需要?jiǎng)?chuàng)建一個(gè)類來(lái)管理訪問(wèn)令牌(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()
{
// 檢查令牌是否已過(guò)期,如未過(guò)期則直接返回
if (!string.IsNullOrEmpty(_accessToken) && DateTime.Now < _tokenExpireTime)
{
return _accessToken;
}
?
// 調(diào)用釘釘API獲取新的訪問(wèn)令牌
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;
// 訪問(wèn)令牌有效期為7200秒,我們?cè)O(shè)置提前10分鐘刷新
_tokenExpireTime = DateTime.Now.AddSeconds(result.expires_in - 600);
return _accessToken;
}
else
{
throw new Exception($"獲取訪問(wèn)令牌失敗:{result.errmsg}");
}
}
}3.2 創(chuàng)建釘釘消息發(fā)送類
接下來(lái),我們創(chuàng)建一個(gè)類來(lái)處理各種類型的消息發(fā)送:
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>
/// 發(fā)送工作通知消息
/// </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列表,多個(gè)用戶用逗號(hào)分隔
dept_id_list = deptIdList, // 部門ID列表,多個(gè)部門用逗號(hào)分隔
to_all_user = toAllUser, // 是否發(fā)送給全員
msg = new
{
msgtype = "text",
text = new { content = message }
}
};
?
request.AddJsonBody(requestBody);
var response = await client.ExecuteAsync(request);
return JsonConvert.DeserializeObject<dynamic>(response.Content);
}
?
/// <summary>
/// 通過(guò)機(jī)器人發(fā)送群消息
/// </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>
/// 計(jì)算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);
}
}
}四、實(shí)現(xiàn)發(fā)送不同類型的通知
釘釘支持多種消息類型,除了基本的文本消息外,還支持鏈接、Markdown、卡片等豐富的消息類型。下面我們來(lái)實(shí)現(xiàn)幾種常見(jiàn)的消息發(fā)送方式。
4.1 發(fā)送文本消息
/// <summary>
/// 發(fā)送文本消息(擴(kuò)展方法)
/// </summary>
public async Task<dynamic> SendTextMessageAsync(string userIdList, string message)
{
return await SendWorkNoticeAsync(userIdList, "", "false", message);
}4.2 發(fā)送Markdown消息
/// <summary>
/// 發(fā)送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 發(fā)送卡片消息
/// <summary>
/// 發(fā)送卡片消息
/// </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);
}五、完整使用示例
下面是一個(gè)完整的使用示例,展示如何在實(shí)際項(xiàng)目中使用我們創(chuàng)建的工具類發(fā)送釘釘通知:
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
{
// 初始化消息發(fā)送器
var messageSender = new DingTalkMessageSender(appKey, appSecret, agentId, webhook, secret);
?
// 示例1:發(fā)送工作通知文本消息
Console.WriteLine("發(fā)送工作通知文本消息...");
var userIdList = "user1,user2";
var textResponse = await messageSender.SendTextMessageAsync(userIdList, "這是一條測(cè)試消息,由C#程序發(fā)送。");
Console.WriteLine($"發(fā)送結(jié)果:{JsonConvert.SerializeObject(textResponse)}");
?
// 示例2:發(fā)送Markdown消息
Console.WriteLine("\n發(fā)送Markdown消息...");
var markdownTitle = "項(xiàng)目進(jìn)度通知";
var markdownContent = @"# 項(xiàng)目進(jìn)度通報(bào)\n\n> 項(xiàng)目A已完成70%\n> 項(xiàng)目B已完成45%\n\n## 本周重點(diǎn)任務(wù)\n- 完成項(xiàng)目A的模塊三開發(fā)\n- 開始項(xiàng)目B的系統(tǒng)測(cè)試\n\n**請(qǐng)相關(guān)負(fù)責(zé)人注意時(shí)間節(jié)點(diǎn)!**";
var markdownResponse = await messageSender.SendMarkdownMessageAsync(userIdList, markdownTitle, markdownContent);
Console.WriteLine($"發(fā)送結(jié)果:{JsonConvert.SerializeObject(markdownResponse)}");
?
// 示例3:發(fā)送卡片消息
Console.WriteLine("\n發(fā)送卡片消息...");
var cardTitle = "重要會(huì)議通知";
var cardContent = @"# 產(chǎn)品評(píng)審會(huì)議\n\n**時(shí)間:** 2023年10月15日 14:00-16:00\n**地點(diǎn):** 三樓會(huì)議室A\n**參會(huì)人員:** 產(chǎn)品部全體成員、開發(fā)團(tuán)隊(duì)負(fù)責(zé)人\n\n## 會(huì)議議程\n1. 產(chǎn)品需求回顧\n2. 功能設(shè)計(jì)評(píng)審\n3. 開發(fā)進(jìn)度同步\n4. 問(wèn)題討論與解決";
var cardResponse = await messageSender.SendActionCardMessageAsync(userIdList, cardTitle, cardContent, "查看詳情", "https://example.com/meeting-details");
Console.WriteLine($"發(fā)送結(jié)果:{JsonConvert.SerializeObject(cardResponse)}");
?
// 示例4:通過(guò)機(jī)器人發(fā)送群消息
Console.WriteLine("\n通過(guò)機(jī)器人發(fā)送群消息...");
var robotResponse = await messageSender.SendRobotMessageAsync("【系統(tǒng)通知】服務(wù)器備份任務(wù)已完成,備份文件大?。?.5GB");
Console.WriteLine($"發(fā)送結(jié)果:{JsonConvert.SerializeObject(robotResponse)}");
?
Console.WriteLine("\n所有消息發(fā)送完成!");
}
catch (Exception ex)
{
Console.WriteLine($"發(fā)送消息時(shí)發(fā)生錯(cuò)誤:{ex.Message}");
}
?
Console.ReadLine();
}
}到此這篇關(guān)于C#調(diào)用釘釘API發(fā)送通知的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)C#調(diào)用釘釘API內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SQLite之C#版 System.Data.SQLite使用方法
這篇文章主要介紹了SQLite之C#版 System.Data.SQLite使用方法,需要的朋友可以參考下2020-10-10
基于C#的socket編程的TCP異步的實(shí)現(xiàn)代碼
本篇文章主要介紹了基于C#的socket編程的TCP異步的實(shí)現(xiàn)代碼,詳解的講訴了TCP通信異步的實(shí)現(xiàn),有興趣的可以了解一下。2016-11-11
基于C#編寫一個(gè)接受圖片流的OCR識(shí)別接口
這篇文章主要為大家詳細(xì)介紹了如何使用C#寫一個(gè)接受圖片流的OCR識(shí)別接口,以及測(cè)試用例調(diào)用接口,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-03-03
在C#中根據(jù)HardwareID獲取驅(qū)動(dòng)程序信息的實(shí)現(xiàn)代碼
這篇文章主要介紹了C#中根據(jù)HardwareID獲取驅(qū)動(dòng)程序信息的實(shí)現(xiàn)代碼,需要的朋友可以參考下2016-12-12
C#開發(fā)微信門戶及應(yīng)用(3) 文本消息和圖文消息應(yīng)答
這篇文章主要為大家詳細(xì)介紹了C#開發(fā)微信門戶及應(yīng)用第二篇,微信文本消息和圖文消息的應(yīng)答,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06

