微信/企业微信通知模块 (C#)

基于方糖的微信服务号通知 和 企业微信应用通知

需要安装两个库

  1. RestSharp
  2. RestSharp.Serializers.NewtonsoftJson

Program.cs 代码如下:

using Push.Core.Abstracts;
using Push.Core.Implements.FangTang;
using Push.Core.Implements.WeCom;

// 方糖官网:https://sct.ftqq.com/

{
    IPusher<FangTangInput, FangTangOutput> push = new FangTangPusher(new FangTangConfig("GCT122141TedAbdWYXmOoFCbEBjaNrJNi6"));
    var output = push.Send(new FangTangInput("测试用例", "这是一条测试内容。"));
    Console.WriteLine("response: {0}", output.Content);

    var push2 = new FangTangPusher("GCT122141TedAbdWYXmOoFCbEBjaNrJNi6");
    var desp = @"# test Title


        > test case.


        1. test one.
        2. the test is ok.
        3. check the msg.

    
        ```
        # code block
        print '3 backticks or'
        print 'indent 4 spaces'
        ```";
    var output2 = push2.Send("test case", desp);
    Console.WriteLine("response2: {0}", output2.Content);
}

/**
 * output:
 *  1. response: {"code":0,"message":"","data":{"pushid":"138222432","readkey":"SCTwh4qB55Q25EY","error":"SUCCESS","errno":0}}
 *  2. response: {"message":"[AUTH]错误的Key","code":40001,"info":"错误的Key","args":[null],"scode":461}
 */


// ==============================================================================================


// WeCom:https://developer.work.weixin.qq.com/document/path/91039

{
    var weComPush = new WeComPusher(new WeComConfig("wcf4f2154226633cd9", "JCaa7boA_K9cn1HqOPegsnpadsb4-_zdUOXhy123Rx8", "1000012"));
    var output = weComPush.Send("hello, this is a test message.");
    Console.WriteLine("response: {0}", output.Content);
}

/**
 * output:
 *  1. response: {"errcode":0,"errmsg":"ok","msgid":"fcLc6UhB2bbaSaoED21VFHkNKrbj3SiK5M65TA5qR_6t6oqvhmm11fWd4K6ws0YPYSjFI3Vfgq2ZpixLAH7Hpg"}
 *  2. response: {"errcode":40056,"errmsg":"invalid agentid, hint: [1287746691374881334034345], from ip: 121.168.13.52, more info at https://open.work.weixin.qq.com/devtool/query?e=40056"}
 */


public interface IPusher<in TInput, out TOutput>
{
    public TOutput Send(TInput input);
}

public record PusherResponse
{
    public string Content { get; set; }
}
方糖微信服务号
using System.Net;
using System.Text.RegularExpressions;
using Push.Core.Abstracts;
using RestSharp;
using RestSharp.Serializers.NewtonsoftJson;

namespace Push.Core.Implements.FangTang;

public class FangTangConfig
{
    public FangTangConfig(string key)
    {
        Key = key;
    }

    public string Key { get; set; }
}

public class FangTangInput
{
    public FangTangInput()
    {
    }

    public FangTangInput(string title, string desp)
    {
        Title = title;
        Desp = desp;
    }

    public string Title { get; set; }
    public string Desp { get; set; }
}

public record FangTangOutput : PusherResponse
{
    public string PushId { get; set; }
    public string ReadKey { get; set; }
    public string Error { get; set; }
    public int ErrNo { get; set; }
}

public class FangTangPusher : IPusher<FangTangInput, FangTangOutput>, IDisposable
{
    private const string BaseUrl = "https://sctapi.ftqq.com/";
    private readonly RestClient _client;
    private readonly FangTangConfig _config;

    public FangTangPusher(string key)
    {
        this._config = new FangTangConfig(key);
        var options = new RestClientOptions(BaseUrl);
        this._client = new RestClient(
            options
            , configureSerialization: s => s.UseNewtonsoftJson()
        );
    }

    public FangTangPusher(FangTangConfig config)
    {
        this._config = config;
        var options = new RestClientOptions(BaseUrl);
        this._client = new RestClient(
            options
            , configureSerialization: s => s.UseNewtonsoftJson()
        );
    }

    /// <inheritdoc />
    public FangTangOutput Send(FangTangInput input)
    {
        var resource = $"{this._config.Key}.send";
        RestRequest restRequest = new RestRequest(resource, Method.Post).AddJsonBody(input);
        var restResponse = this._client.ExecutePost<FangTangOutput>(restRequest);

        if (!restResponse.IsSuccessful && restResponse.StatusCode != HttpStatusCode.BadRequest)
            throw new InvalidOperationException($"Incorrect request. {restResponse.ErrorException?.Message}");

        var data = restResponse.Data;
        if (data == null)
            throw new InvalidDataException("Incorrect request to send the content, please check for the key.");
        if (restResponse.Content != null)
            data.Content = Regex.Unescape(restResponse.Content);

        return data;
    }

    /// <inheritdoc />
    public void Dispose()
    {
        _client.Dispose();
        GC.SuppressFinalize(this);
    }
}

public static class FangTangPusherExtensions
{
    public static FangTangOutput Send(this FangTangPusher pusher, string title, string description)
        => pusher.Send(new FangTangInput(title, description));
}
企业微信
using Newtonsoft.Json;
using Push.Core.Abstracts;
using RestSharp;
using RestSharp.Serializers.NewtonsoftJson;

namespace Push.Core.Implements.WeCom;

public class WeComConfig
{
    public WeComConfig(string corpId, string corpSecret, string agentId)
    {
        CorpId = corpId;
        CorpSecret = corpSecret;
        AgentId = agentId;
    }

    public string CorpId { get; set; }
    public string CorpSecret { get; set; }
    public string AgentId { get; set; }

    public bool EnableDebug { get; set; }
}

public class WeComInput
{
    public WeComInput(string content)
    {
        Content = content;
    }

    public string Content { get; set; }
}

public record WeComOutput : WeComPusher.WeComBaseModel
{
    [JsonProperty(PropertyName = "msgid")]
    public string MessageId { get; set; }
}

public class WeComPusher : IPusher<WeComInput, WeComOutput>, IDisposable
{
    private const string BaseUrl = "https://qyapi.weixin.qq.com/";
    private readonly RestClient _client;
    private readonly WeComConfig _config;
    private string? _accessToken = string.Empty;

    public WeComPusher(WeComConfig config)
    {
        this._config = config;
        var options = new RestClientOptions(BaseUrl);
        this._client = new RestClient(
            options
            , configureSerialization: s => s.UseNewtonsoftJson()
        );
    }

    private void EnsuringAccessToken()
    {
        const string resource = "cgi-bin/gettoken";
        var restRequest = new RestRequest(resource)
            .AddQueryParameter("corpid", this._config.CorpId)
            .AddQueryParameter("corpsecret", this._config.CorpSecret);

        // turn on debug mode
        if (this._config.EnableDebug)
            restRequest.AddQueryParameter("debug", "1");

        var restResponse = this._client.ExecuteGet<AccessTokenModel>(restRequest);
        if (!restResponse.IsSuccessful)
            throw new InvalidOperationException($"Incorrect request. {restResponse.ErrorException?.Message}");

        var data = restResponse.Data;
        if (data == null)
            throw new InvalidDataException("Incorrect request to obtain access token, please check for CorpId or CorpSecret.");

        if (data.ErrorCode == 0)
        {
            this._accessToken = data.AccessToken;
        }
        else
        {
            throw new InvalidDataException(data.ErrorMessage);
        }
    }

    /// <inheritdoc />
    public WeComOutput Send(WeComInput input)
    {
        EnsuringAccessToken();

        const string resource = "cgi-bin/message/send";
        var requestModel = new WeComRequestModel("@all", "text", this._config.AgentId, input.Content);
        var restRequest = new RestRequest(resource, Method.Post)
            .AddQueryParameter("access_token", this._accessToken)
            .AddJsonBody(requestModel);

        // turn on debug mode
        if (this._config.EnableDebug)
            restRequest.AddQueryParameter("debug", "1");

        var restResponse = this._client.ExecutePost<WeComOutput>(restRequest);
        if (!restResponse.IsSuccessful)
            throw new InvalidOperationException($"Incorrect request. {restResponse.ErrorException?.Message}");

        var data = restResponse.Data;
        if (data == null)
            throw new InvalidDataException("Incorrect request to send the content, please check for AccessToken.");

        data.Content = restResponse.Content;
        return data;
    }

    #region internal classes

    public record WeComBaseModel : PusherResponse
    {
        [JsonProperty(PropertyName = "errcode")]
        public int ErrorCode { get; set; }

        [JsonProperty(PropertyName = "errmsg")]
        public string ErrorMessage { get; set; }
    }

    private record AccessTokenModel : WeComBaseModel
    {
        [JsonProperty(PropertyName = "access_token")]
        public string? AccessToken { get; set; }

        [JsonProperty(PropertyName = "expires_in")]
        public int ExpiresIn { get; set; }
    }

    private record WeComRequestModel
    {
        public WeComRequestModel(string touser, string msgType, string agentId, string text)
        {
            Touser = touser;
            MsgType = msgType;
            AgentId = agentId;
            Text = new TextRequestModel(text);
        }

        [JsonProperty(PropertyName = "touser")]
        public string Touser { get; set; }

        [JsonProperty(PropertyName = "msgtype")]
        public string MsgType { get; set; }

        [JsonProperty(PropertyName = "agentid")]
        public string AgentId { get; set; }

        [JsonProperty(PropertyName = "text")]
        public TextRequestModel Text { get; set; }
    }

    private record TextRequestModel(string Content)
    {
        [JsonProperty(PropertyName = "content")]
        public string Content { get; set; } = Content;
    }

    #endregion

    /// <inheritdoc />
    public void Dispose()
    {
        _client.Dispose();
        GC.SuppressFinalize(this);
    }
}

public static class WeComPusherExtensions
{
    public static WeComOutput Send(this WeComPusher pusher, string content)
        => pusher.Send(new WeComInput(content));
}
posted @ 2023-06-25 16:23  灵火  阅读(232)  评论(1编辑  收藏  举报