开发文档:
钉钉 自定义机器人
一、创建、设置钉钉机器人
- 创建机器人:钉钉群 ->群设置 -> 群机器人 如下图所示
- 设置钉钉机器人信息
二、发送钉钉消息
根据钉钉开发文档,当前钉钉机器人支持的消息类型主要有三种:文本类型、link类型、markdown类型。本文主要介绍发送“markdown类型”消息。
事实上,钉钉机器人发送消息,就是发送一次HTTP Post请求给webhook,数据最终都是json格式的,所以在通过机器人发送消息未成功时,可以检查自己的webhook是否正确,json数据是否符合开发文档要求。
废话不多说,直接开始。
- 编写消息类型类Model
MarkDown:MarkDown类型
At:At
DingDingMessage:钉钉消息
DingDingResponse:消息响应
/// <summary>
/// 消息类型
/// </summary>
public class MarkDown
{
public MarkDown() { }
public string title { set; get; }//标题
public string text { set; get; }//文本消息
}
/// <summary>
/// At
/// </summary>
public class At
{
public At()
{
atMobiles = new List<string>();
}
private List<string> _atMobiles;//@的手机号
private bool _isAtAll;//是否@所有人
/// <summary>
/// @的联系人
/// </summary>
public List<string> atMobiles
{
set { _atMobiles = value; }
get { return _atMobiles; }
}
/// <summary>
/// 是否@所有人
/// </summary>
public bool isAtAll
{
set { _isAtAll = value; }
get { return _isAtAll; }
}
}
/// <summary>
/// 钉钉消息
/// </summary>
public class DingDingMessage
{
public DingDingMessage()
{
this.at = new At();
this.text = new Text();
this.markdown = new MarkDown();
}
public string msgtype { set; get; }//消息类型
public Model.Text text { set; get; }//text类型
public Model.MarkDown markdown { set; get; }//markdown类型
public Model.At at { set; get; }//@
}
/// <summary>
/// 消息响应
/// </summary>
public class DingDingResponse
{
private string _msg;
private string _code;
/// <summary>
/// 错误信息
/// </summary>
public string ErrMsg
{
set { _msg = value; }
get { return _msg; }
}
/// <summary>
/// 错误码
/// </summary>
public string ErrCode
{
set { _code = value; }
get { return _code; }
}
}
- 编写发送钉钉消息的方法
/// <summary>
/// 创建测试任务、修改测试任务
/// 向钉钉群发送消息
/// </summary>
/// <param name="task"></param>
/// <param name="type"></param>
public static Model.DingDingResponse Send_TestTaskMessageToDingDing(Model.Task task, string type)
{
StringBuilder msg = new StringBuilder();
Model.DingDingResponse response = new Model.DingDingResponse();
List<string> phoneList = new List<string>();
Model.DingDingMessage message = new Model.DingDingMessage();
message.msgtype = "markdown";
if (type.Equals("新建"))
{
message.markdown.title = "【测试任务分配】创建人:" + Utility.UserInfo.UserName;
msg.Append("### 【测试任务分配】项目负责人:" + task.Project_Leader_Name + "\n");
}
else if (type.Equals("修改"))
{
message.markdown.title = "【测试任务分配】修改人:" + Utility.UserInfo.UserName;
msg.Append("### 【测试任务分配】项目负责人:" + task.Project_Leader_Name + "\n");
}
else if (type.Equals("删除"))
{
message.markdown.title = "【测试任务删除】删除人:" + Utility.UserInfo.UserName;
msg.Append("### 【测试任务删除】项目负责人:" + task.Project_Leader_Name + "\n");
msg.Append("* 任务名:" + task.TaskName + "\n");
msg.Append("* 删除人:" + Utility.UserInfo.UserName + "\n");
msg.Append("* 创建时间:" + task.PublishTime.ToString() + "\n");
msg.Append("* 删除时间:" + Utility.Common.GetSysTime().ToString() + "\n");
msg.Append("* 所属机型项目:" + task.Project_Id.ToUpper() + "\n");
msg.Append("\n");
msg.Append("@所有人");
}
if (type.Equals("新建") || type.Equals("修改"))
{
//@所有人与@某些人不能一起使用
message.at.isAtAll = false;
}
else if(type.Equals("删除"))
{
message.at.isAtAll = true;
}
if (type.Equals("新建") || type.Equals("修改"))
{
msg.Append("* 任务名:" + task.TaskName + "\n");
msg.Append("* 测试机型:" + task.Project_Id.ToUpper() + "(" + task.PhoneType + ")\n");
msg.Append("* 测试周期:" + task.TestCycle + "天\n");
msg.Append("* 测试时间:" + task.StartTime.ToString("yyyy/MM/dd HH:mm") + " 至 " + task.EndTime.ToString("yyyy/MM/dd HH:mm") + "\n");
msg.Append("* 其他:请查看OverseaPMS系统->个人任务\n");
//msg.Append("* 备注:" + task.BZ + "\n");
msg.Append("------\n");
msg.Append("------\n");
DataTable dt_allChildTasks = BLL.TestTask.ChildTask.Que_AllChildTasksByTaskId(task.TaskId).Tables[0];
if (dt_allChildTasks != null && dt_allChildTasks.Rows.Count > 0)
{
msg.Append("#### 测试模块安排如下:\n");
string phone = "";
for (int i = 0; i < dt_allChildTasks.Rows.Count; i++)
{
phone = dt_allChildTasks.Rows[i]["phone"].ToString();
//去除重复手机号
if (!phoneList.Contains("@" + phone))
{
phoneList.Add("@" + phone);
}
msg.Append(" \n > (" + (i + 1) + ") " + dt_allChildTasks.Rows[i]["module_name"] + "," + dt_allChildTasks.Rows[i]["tester_name"] +
",测试要求:" + dt_allChildTasks.Rows[i]["requirement"]);
//msg.Append("\n @" + phone);
msg.Append("\n");
}
msg.Append("\n");
if (phoneList.Count > 20)
{
for (int i = 0; i < 20; i++)
{
msg.Append(phoneList[i] + " ");
phone = phoneList[i].Trim().Substring(1);
message.at.atMobiles.Add(phone);
}
//删除
phoneList.RemoveRange(0, 20);
}
else
{
for (int i = 0; i < phoneList.Count; i++)
{
msg.Append(phoneList[i] + " ");
phone = phoneList[i].Trim().Substring(1);
message.at.atMobiles.Add(phone);
}
//删除
phoneList.Clear();
}
msg.Append("\n");
}
}
//message.text.content = msg.ToString();
message.markdown.text = msg.ToString();
String textMsg = Utility.JsonHelper.SerializeObject(message);//Json将对象序列化
string webhook = Get_TestTaskWebhook();
//返回响应
string result = Utility.Common.PostDingDing(textMsg, webhook);
response = Utility.JsonHelper.DeserializeJsonToObject<Model.DingDingResponse>(result);
if (response.ErrCode.Equals("0"))
{
if (phoneList.Count > 0)
{
//将剩余手机号分组,并发送消息
response = Send_SplitOtherPhoneToDingDing(phoneList, task);
}
}
//if (!response.ErrCode.Equals("0"))
//{
// MessageBox.Show("钉钉请求异常!\n错误码:" + response.ErrCode + ",错误信息:" + response.ErrMsg, "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
//}
return response;
}
/// <summary>
/// 手机号列表分组,并发送到钉钉群
/// </summary>
/// <param name="phoneList"></param>
/// <returns></returns>
public static Model.DingDingResponse Send_SplitOtherPhoneToDingDing(List<string> phoneList, Model.Task task=null)
{
Model.DingDingResponse response = new Model.DingDingResponse();
List<string> temp = new List<string>();
if (phoneList.Count > 20)
{
int count = Convert.ToInt32(Math.Ceiling(phoneList.Count / 20.0));
for (int i = 0; i < count; i++)
{
temp = phoneList.GetRange(1+(20*i), 20);
response = Send_OtherPhoneToDingDing(temp,task);
if (!response.ErrCode.Equals("0"))
{
return response;
}
}
}
else
{
response = Send_OtherPhoneToDingDing(phoneList,task);
}
return response;
}
/// <summary>
/// 发送手机号到钉钉群
/// </summary>
/// <param name="phoneList"></param>
/// <returns></returns>
public static Model.DingDingResponse Send_OtherPhoneToDingDing(List<string> phoneList, Model.Task task=null)
{
StringBuilder msg = new StringBuilder();
Model.DingDingResponse response = new Model.DingDingResponse();
Model.DingDingMessage message = new Model.DingDingMessage();
message.msgtype = "markdown";
message.markdown.title = "【测试任务分配】任务名:" + task.TaskName;
string phone = "";
for (int i = 0; i < phoneList.Count; i++)
{
msg.Append(phoneList[i] + " ");
phone = phoneList[i].Trim().Substring(1);
message.at.atMobiles.Add(phone);
}
message.markdown.text = msg.ToString();
String textMsg = Utility.JsonHelper.SerializeObject(message);//Json将对象序列化
string webhook = Get_TestTaskWebhook();
//返回响应
string result = Utility.Common.PostDingDing(textMsg, webhook);
response = Utility.JsonHelper.DeserializeJsonToObject<Model.DingDingResponse>(result);
return response;
}
/// <summary>
/// 以Post方式发送请求
/// </summary>
/// <param name="apiurl">请求的URL</param>
/// <param name="jsonString">请求的json参数</param>
/// <param name="headers">请求头的key-value字典</param>
public static String PostDingDing(string jsonString, string apiurl = null, Dictionary<String, String> headers = null)
{
if (apiurl == null)
{
apiurl = WEB_HOOK;//机器人的webhook
}
WebRequest request = WebRequest.Create(@apiurl);
request.Method = "POST";
request.ContentType = "application/json";
if (headers != null)
{
foreach (var keyValue in headers)
{
if (keyValue.Key == "Content-Type")
{
request.ContentType = keyValue.Value;
continue;
}
request.Headers.Add(keyValue.Key, keyValue.Value);
}
}
if (String.IsNullOrEmpty(jsonString))
{
request.ContentLength = 0;
}
else
{
byte[] bs = Encoding.UTF8.GetBytes(jsonString);
request.ContentLength = bs.Length;
Stream newStream = request.GetRequestStream();
newStream.Write(bs, 0, bs.Length);
newStream.Close();
}
WebResponse response = request.GetResponse();
Stream stream = response.GetResponseStream();
Encoding encode = Encoding.UTF8;
StreamReader reader = new StreamReader(stream, encode);
string resultJson = reader.ReadToEnd();
return resultJson;
}
效果图: