某个项目,测试环境下,通知开发组人员各种情况,比如给测试额外信息支持之类,就想到了用钉钉机器人通知。
不过官方只有py和java,php的,没有c#。这里写一套仅供参考。
吐槽一下,文中有java 和 php就被自动定义关键字“java”和“php”了!c#却没有被带入关键字!果然是四等人!
    /// <summary>
    /// 返回结果 不是重点,仅仅是完整性
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Result<T>
    {
        /// <summary>
        /// 状态
        /// </summary>
        public bool Status;
        /// <summary>
        /// 状态代码
        /// </summary>
        public long StatusCode;
        /// <summary>
        /// 消息
        /// </summary>
        public String Message = string.Empty;
        /// <summary>
        /// 数据
        /// </summary>
        public T Data;
        /// <summary>
        /// 错误详情 用于调试获取错误详细信息, 可不写
        /// </summary>
        public string ErrorDetail = "";
    }
    /// <summary>
    /// 运营通知接口,这个不是核心可以不看
    /// </summary>
    public interface IOperationNotification
    {
        /// <summary>
        /// 发送通知
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        Result<string> Send(string msg);
    }
    /// <summary>
    /// 钉钉运营通知 重点在内
    /// </summary>
    public class DingDingOperationNotification : IOperationNotification
    {
        /// <summary>
        /// 钉钉结果,这是钉钉的官方返回结果结构,没错errcode就是0
        /// </summary>
        public class dingdingResult
        {
            public int errcode { get; set; }
            public string errmsg { get; set; }
        }
        /// <summary>
        /// 钉钉发送通知,调用入口
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        public Result<string> Send(string msg)
        {
            if (msg.NullEmpty()) {
                return "没有发送内容".FailRes<string>();
            }
            var send_res = this.SendToDingDing(msg);
            var res_x = send_res.ToObject<dingdingResult>();
            if (res_x.errcode == 0)
            {
                return "ok".SuccRes();
            }
            else
            {
                return res_x.errmsg.FailRes<string>();
            }
        }
        /// <summary>
        /// 发送到钉钉 机器人
        /// TOKEN 要更换成相应的
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        public string SendToDingDing(string msg)
        {
            string main_url = "https://oapi.dingtalk.com/robot/send?access_token=TOKEN";
            // 这里要参考官方文档制定发送内容的允许关键字
            string data = $"{{\"msgtype\": \"text\",\"text\": {{\"content\": \"验证信息:{msg}\"}}}}";
            var sign = TransEx.sign();// 签名是重点
            // 必须urlcode
            var urlcode_sign = HttpUtility.UrlEncode(sign.Item2);
            // 组成格式
            string fin_url = $"{main_url}×tamp={sign.Item1}&sign={urlcode_sign}";
            // 这里用了 扩展方式的post,可以自己定义
            var res = fin_url.HttpPost(data: data);
            return res;
        }
        /// <summary>
        /// 时间戳1970年
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public TimeSpan GetTimeSpan1970(DateTime time)
        {
            return time - new DateTime(1970, 01, 01, 0, 0, 0, 0);
        }
        /// <summary>
        /// 元年时间戳
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public TimeSpan GetTimeSpan(DateTime time)
        {
            return time - new DateTime();
        }
        /// <summary>
        /// 时间戳转长整型
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public long GetLongTimeSpan(DateTime time)
        {
            var timestamp = time.GetTimeSpan1970();
            long long_timestamp = Convert.ToInt64(Math.Round(timestamp.TotalMilliseconds));
            return long_timestamp;
        }
        /// <summary>
        /// 钉钉签名-- 这里超级重要 --
        /// </summary>
        /// <returns></returns>
        public Tuple<long, string> sign()
        {
            // 钉钉用的是utc时间做时间戳,不是本地时间
            long timestamp = DateTime.UtcNow.GetLongTimeSpan();
            // 官方给你的secret
            string secret = "SECRET";
            Encoding code = UTF8Encoding.UTF8;//utf8
            var secret_byte = code.GetBytes(secret);
            string string_to_sign = $"{timestamp}\n{secret}";//看官方,签名组成法
            // 签名转字节
            var string_to_sign_enc = code.GetBytes(string_to_sign);
            // 散列加密用HMAC SHA256,用的key就是secret字节组
            byte[] hash = new HMACSHA256(secret_byte).ComputeHash(string_to_sign_enc);
            // 要base64
            var base64Code = Convert.ToBase64String(hash);
            return new Tuple<long, string>(timestamp, base64Code);
        }
        /// <summary>
        /// 钉钉post
        /// </summary>
        /// <param name="url"></param>
        /// <param name="data"></param>
        /// <param name="contentType"></param>
        /// <returns></returns>
        public string HttpPost(string url, string data = "", string contentType = "application/json")
        {
            string res = "";
            var t = Task.Run(async () =>
            {
                using (HttpClient client = new HttpClient())
                {
                    try
                    {
                        var uri = url;
                        HttpRequestMessage req = new HttpRequestMessage(new HttpMethod("POST"), uri);
                        req.Content = new StringContent(data);
                        req.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType);
                        return await client.SendAsync(req).Result.Content.ReadAsStringAsync();
                        //return client.GetAsync(uri).Result.Content.ReadAsStringAsync();
                    }
                    catch (Exception ex)
                    {
                        return await Task.Run<string>(() => { return ex.Message; });
                    }
                }
            });
            Task.WaitAll(t);
            res = t.Result;
            return res;
        }
    }
    /// <summary>
    /// 运营管理
    /// </summary>
    public class OperationManager
    {
        /// <summary>
        /// 创建通知器
        /// </summary>
        /// <returns></returns>
        public static IOperationNotification CreateNotification()
        {
            return new DingDingOperationNotification();
        }
    }