17 changed files with 355 additions and 39 deletions
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
||||
|
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
||||
|
<xs:element name="Weavers"> |
||||
|
<xs:complexType> |
||||
|
<xs:all> |
||||
|
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
||||
|
<xs:complexType> |
||||
|
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:all> |
||||
|
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:schema> |
||||
@ -0,0 +1,25 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0;net9.0</TargetFrameworks> |
||||
|
<AssemblyName>LINGYUN.Abp.Notifications.Webhook.WeChat.Work</AssemblyName> |
||||
|
<PackageId>LINGYUN.Abp.Notifications.Webhook.WeChat.Work</PackageId> |
||||
|
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
||||
|
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
||||
|
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="CommonMark.NET" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\..\framework\wechat\LINGYUN.Abp.WeChat.Work\LINGYUN.Abp.WeChat.Work.csproj" /> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.Notifications.Webhook\LINGYUN.Abp.Notifications.Webhook.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,18 @@ |
|||||
|
using LINGYUN.Abp.WeChat.Work; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.Webhook.WeChat.Work; |
||||
|
|
||||
|
[DependsOn( |
||||
|
typeof(AbpNotificationsWebhookModule), |
||||
|
typeof(AbpWeChatWorkModule))] |
||||
|
public class AbpNotificationsWebhookWeChatWorkModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpNotificationsWebhookOptions>(options => |
||||
|
{ |
||||
|
options.Contributors.Add(new WeChatWorkWebhookNotificationContributor()); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
namespace LINGYUN.Abp.Notifications.Webhook.WeChat.Work; |
||||
|
public class AbpNotificationsWebhookWeChatWorkOptions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 发送Markdown类型通知时是否使用MarkdownV2格式通知, 默认: true
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// 详见: https://developer.work.weixin.qq.com/document/path/99110#markdown-v2%E7%B1%BB%E5%9E%8B
|
||||
|
/// </remarks>
|
||||
|
public bool UseMarkdownV2 { get; set; } |
||||
|
public AbpNotificationsWebhookWeChatWorkOptions() |
||||
|
{ |
||||
|
UseMarkdownV2 = true; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,74 @@ |
|||||
|
using LINGYUN.Abp.WeChat.Work.Messages; |
||||
|
using LINGYUN.Abp.WeChat.Work.Messages.Models; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.Webhook.WeChat.Work; |
||||
|
public class WeChatWorkWebhookNotificationContributor : IWebhookNotificationContributor |
||||
|
{ |
||||
|
public string Name => "WeChat.Work"; |
||||
|
|
||||
|
public async virtual Task ContributeAsync(IWebhookNotificationContext context) |
||||
|
{ |
||||
|
var options = context.ServiceProvider.GetRequiredService<IOptions<AbpNotificationsWebhookWeChatWorkOptions>>().Value; |
||||
|
var notificationDataSerializer = context.ServiceProvider.GetRequiredService<INotificationDataSerializer>(); |
||||
|
|
||||
|
var data = await notificationDataSerializer.ToStandard(context.Notification.Data); |
||||
|
var notificationContent = data.Message; |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
if (context.Notification.ContentType == NotificationContentType.Html) |
||||
|
{ |
||||
|
notificationContent = CommonMark.CommonMarkConverter.Convert(notificationContent); |
||||
|
} |
||||
|
|
||||
|
WeChatWorkWebhookMessage message; |
||||
|
|
||||
|
switch (context.Notification.ContentType) |
||||
|
{ |
||||
|
case NotificationContentType.Text: |
||||
|
message = new WeChatWorkWebhookTextMessage( |
||||
|
new WebhookTextMessage(notificationContent)); |
||||
|
break; |
||||
|
case NotificationContentType.Html: |
||||
|
case NotificationContentType.Markdown: |
||||
|
if (options.UseMarkdownV2) |
||||
|
{ |
||||
|
message = new WeChatWorkWebhookMarkdownV2Message( |
||||
|
new WebhookMarkdownV2Message(notificationContent)); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
message = new WeChatWorkWebhookMarkdownMessage( |
||||
|
new WebhookMarkdownMessage(notificationContent)); |
||||
|
} |
||||
|
break; |
||||
|
default: |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (message == null) |
||||
|
{ |
||||
|
context.ServiceProvider |
||||
|
.GetService<ILogger<WeChatWorkWebhookNotificationContributor>>() |
||||
|
?.LogWarning("Unable to send work weixin messages because WeChatWorkMessage is null."); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
context.Webhook = new WebhookNotificationData( |
||||
|
context.Notification.Name, |
||||
|
message); |
||||
|
context.Handled = true; |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
context.ServiceProvider |
||||
|
.GetService<ILogger<WeChatWorkWebhookNotificationContributor>>() |
||||
|
?.LogWarning("Failed to parse the content of the Webhook message: {message}", ex.Message); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
||||
|
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
||||
|
<xs:element name="Weavers"> |
||||
|
<xs:complexType> |
||||
|
<xs:all> |
||||
|
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
||||
|
<xs:complexType> |
||||
|
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:all> |
||||
|
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:schema> |
||||
@ -0,0 +1,21 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0;net9.0</TargetFrameworks> |
||||
|
<AssemblyName>LINGYUN.Abp.Notifications.Webhook</AssemblyName> |
||||
|
<PackageId>LINGYUN.Abp.Notifications.Webhook</PackageId> |
||||
|
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
||||
|
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
||||
|
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\webhooks\LINGYUN.Abp.Webhooks\LINGYUN.Abp.Webhooks.csproj" /> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.Notifications\LINGYUN.Abp.Notifications.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,18 @@ |
|||||
|
using LINGYUN.Abp.Webhooks; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.Webhook; |
||||
|
|
||||
|
[DependsOn( |
||||
|
typeof(AbpNotificationsCoreModule), |
||||
|
typeof(AbpWebhooksModule))] |
||||
|
public class AbpNotificationsWebhookModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpNotificationsPublishOptions>(options => |
||||
|
{ |
||||
|
options.PublishProviders.Add<WebhookNotificationPublishProvider>(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.Webhook; |
||||
|
public class AbpNotificationsWebhookOptions |
||||
|
{ |
||||
|
public IList<IWebhookNotificationContributor> Contributors { get; } |
||||
|
public AbpNotificationsWebhookOptions() |
||||
|
{ |
||||
|
Contributors = new List<IWebhookNotificationContributor>(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.Webhook; |
||||
|
public interface IWebhookNotificationContext : IServiceProviderAccessor |
||||
|
{ |
||||
|
WebhookNotificationData Webhook { get; set; } |
||||
|
NotificationInfo Notification { get; } |
||||
|
bool Handled { get; set; } |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.Webhook; |
||||
|
public interface IWebhookNotificationContributor |
||||
|
{ |
||||
|
string Name { get; } |
||||
|
Task ContributeAsync(IWebhookNotificationContext context); |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.Webhook; |
||||
|
public class WebhookNotificationContext : IWebhookNotificationContext |
||||
|
{ |
||||
|
public IServiceProvider ServiceProvider { get; } |
||||
|
public NotificationInfo Notification { get; } |
||||
|
public WebhookNotificationData Webhook { get; set; } |
||||
|
public bool Handled { get; set; } |
||||
|
public WebhookNotificationContext(IServiceProvider ServiceProvider, NotificationInfo notification) |
||||
|
{ |
||||
|
Notification = notification; |
||||
|
} |
||||
|
public bool HasResolved() |
||||
|
{ |
||||
|
return Handled || Webhook != null; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
using LINGYUN.Abp.Webhooks; |
||||
|
using Volo.Abp; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.Webhook; |
||||
|
public class WebhookNotificationData |
||||
|
{ |
||||
|
public string WebhookName { get; } |
||||
|
public object Data { get; } |
||||
|
public bool SendExactSameData { get; set; } |
||||
|
public WebhookHeader Headers { get; set; } |
||||
|
public WebhookNotificationData(string webhookName, object data) |
||||
|
{ |
||||
|
WebhookName = Check.NotNullOrWhiteSpace(webhookName, nameof(webhookName)); |
||||
|
Data = Check.NotNull(data, nameof(data)); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,49 @@ |
|||||
|
using LINGYUN.Abp.Webhooks; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.Webhook; |
||||
|
public class WebhookNotificationPublishProvider : NotificationPublishProvider |
||||
|
{ |
||||
|
public const string ProviderName = "Webhook"; |
||||
|
public override string Name => ProviderName; |
||||
|
|
||||
|
private readonly IServiceScopeFactory _serviceScopeFactory; |
||||
|
private readonly AbpNotificationsWebhookOptions _options; |
||||
|
private readonly IWebhookPublisher _webhookPublisher; |
||||
|
|
||||
|
public WebhookNotificationPublishProvider( |
||||
|
IServiceScopeFactory serviceScopeFactory, |
||||
|
IWebhookPublisher webhookPublisher, |
||||
|
IOptions<AbpNotificationsWebhookOptions> options) |
||||
|
{ |
||||
|
_serviceScopeFactory = serviceScopeFactory; |
||||
|
_webhookPublisher = webhookPublisher; |
||||
|
_options = options.Value; |
||||
|
} |
||||
|
|
||||
|
protected override async Task PublishAsync(NotificationInfo notification, IEnumerable<UserIdentifier> identifiers, CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
using var scope = _serviceScopeFactory.CreateScope(); |
||||
|
|
||||
|
foreach (var contributor in _options.Contributors) |
||||
|
{ |
||||
|
var context = new WebhookNotificationContext(scope.ServiceProvider, notification); |
||||
|
|
||||
|
await contributor.ContributeAsync(context); |
||||
|
|
||||
|
if (context.HasResolved()) |
||||
|
{ |
||||
|
await _webhookPublisher.PublishAsync( |
||||
|
context.Webhook.WebhookName, |
||||
|
context.Webhook.Data, |
||||
|
notification.TenantId, |
||||
|
context.Webhook.SendExactSameData, |
||||
|
context.Webhook.Headers); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue