add voice service call service and more
This commit is contained in:
16
src/GreenHome.Sms.Ippanel/GreenHome.Sms.Ippanel.csproj
Normal file
16
src/GreenHome.Sms.Ippanel/GreenHome.Sms.Ippanel.csproj
Normal file
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
26
src/GreenHome.Sms.Ippanel/ISmsService.cs
Normal file
26
src/GreenHome.Sms.Ippanel/ISmsService.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using static GreenHome.Sms.Ippanel.IppanelSmsService;
|
||||
|
||||
namespace GreenHome.Sms.Ippanel;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for SMS sending service
|
||||
/// </summary>
|
||||
public interface ISmsService
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends a webservice SMS message
|
||||
/// </summary>
|
||||
/// <param name="request">SMS request details</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>SMS response with bulk ID</returns>
|
||||
Task<IppanelApiResponse<IppanelData>?> SendWebserviceSmsAsync(WebserviceSmsRequest request, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Sends a pattern SMS message
|
||||
/// </summary>
|
||||
/// <param name="request">Pattern SMS request details</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>SMS response with bulk ID</returns>
|
||||
Task<IppanelApiResponse<IppanelData>?> SendPatternSmsAsync(PatternSmsRequest request, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
23
src/GreenHome.Sms.Ippanel/IppanelSmsOptions.cs
Normal file
23
src/GreenHome.Sms.Ippanel/IppanelSmsOptions.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace GreenHome.Sms.Ippanel;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration options for IPPanel SMS service
|
||||
/// </summary>
|
||||
public sealed class IppanelSmsOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// IPPanel API base URL
|
||||
/// </summary>
|
||||
public string BaseUrl { get; set; } = "https://edge.ippanel.com/v1";
|
||||
|
||||
/// <summary>
|
||||
/// IPPanel authorization token
|
||||
/// </summary>
|
||||
public required string AuthorizationToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Default sender number for webservice SMS
|
||||
/// </summary>
|
||||
public string? DefaultSender { get; set; }
|
||||
}
|
||||
|
||||
160
src/GreenHome.Sms.Ippanel/IppanelSmsService.cs
Normal file
160
src/GreenHome.Sms.Ippanel/IppanelSmsService.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace GreenHome.Sms.Ippanel;
|
||||
|
||||
/// <summary>
|
||||
/// IPPanel SMS service implementation
|
||||
/// </summary>
|
||||
public sealed class IppanelSmsService : ISmsService
|
||||
{
|
||||
private readonly HttpClient httpClient;
|
||||
private readonly IppanelSmsOptions options;
|
||||
private readonly ILogger<IppanelSmsService> logger;
|
||||
|
||||
public IppanelSmsService(
|
||||
HttpClient httpClient,
|
||||
IppanelSmsOptions options,
|
||||
ILogger<IppanelSmsService> logger)
|
||||
{
|
||||
this.httpClient = httpClient;
|
||||
this.options = options;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public async Task<IppanelApiResponse<IppanelData>?> SendWebserviceSmsAsync(
|
||||
WebserviceSmsRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Split recipients by comma and create array
|
||||
var recipients = request.Recipient
|
||||
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||||
.ToList();
|
||||
|
||||
var payload = new
|
||||
{
|
||||
sending_type = "webservice",
|
||||
from_number = request.Sender ?? options.DefaultSender,
|
||||
@params = new
|
||||
{
|
||||
recipients
|
||||
},
|
||||
message = request.Message
|
||||
};
|
||||
|
||||
var response = await httpClient.PostAsJsonAsync(
|
||||
"api/send",
|
||||
payload,
|
||||
cancellationToken);
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<IppanelApiResponse<IppanelData>>(
|
||||
cancellationToken: cancellationToken);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
logger.LogError(ex, "Error sending webservice SMS to {Recipient}", request.Recipient);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Unexpected error sending webservice SMS");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IppanelApiResponse<IppanelData>?> SendPatternSmsAsync(
|
||||
PatternSmsRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
var payload = new
|
||||
{
|
||||
sending_type = "pattern",
|
||||
code = request.PatternCode,
|
||||
from_number = request.Originator ?? options.DefaultSender ?? string.Empty,
|
||||
recipients = request.Recipients,
|
||||
@params = ToAnonymousObject(request.Variables)
|
||||
};
|
||||
|
||||
var response = await httpClient.PostAsJsonAsync(
|
||||
"api/send",
|
||||
payload,
|
||||
cancellationToken);
|
||||
|
||||
//response.EnsureSuccessStatusCode();
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<IppanelApiResponse<IppanelData>>(
|
||||
cancellationToken: cancellationToken);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
logger.LogError(ex, "Error sending pattern SMS to {Recipient}", request.Recipients);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Unexpected error sending pattern SMS");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public object ToAnonymousObject(Dictionary<string, string> dictionary)
|
||||
{
|
||||
var type = dictionary.GetType();
|
||||
var obj = new System.Dynamic.ExpandoObject();
|
||||
var dict = obj as IDictionary<string, object>;
|
||||
|
||||
foreach (var item in dictionary)
|
||||
{
|
||||
dict.Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
public class IppanelApiResponse<T>
|
||||
{
|
||||
[JsonPropertyName("data")]
|
||||
public T? Data { get; set; }
|
||||
|
||||
[JsonPropertyName("meta")]
|
||||
public IppanelMeta Meta { get; set; }
|
||||
}
|
||||
|
||||
public class IppanelMeta
|
||||
{
|
||||
[JsonPropertyName("status")]
|
||||
public bool Status { get; set; }
|
||||
|
||||
[JsonPropertyName("message")]
|
||||
public string? Message { get; set; }
|
||||
|
||||
[JsonPropertyName("message_parameters")]
|
||||
public List<object> MessageParameters { get; set; } = new List<object>();
|
||||
|
||||
[JsonPropertyName("message_code")]
|
||||
public string? MessageCode { get; set; }
|
||||
|
||||
[JsonPropertyName("errors")]
|
||||
public Dictionary<string, List<string>> Errors { get; set; } = new Dictionary<string, List<string>>();
|
||||
}
|
||||
|
||||
// Specific data models for different response types
|
||||
public class IppanelData
|
||||
{
|
||||
[JsonPropertyName("message_outbox_ids")]
|
||||
public List<long> MessageOutboxIds { get; set; } = new List<long>();
|
||||
}
|
||||
}
|
||||
|
||||
63
src/GreenHome.Sms.Ippanel/Models.cs
Normal file
63
src/GreenHome.Sms.Ippanel/Models.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
namespace GreenHome.Sms.Ippanel;
|
||||
|
||||
// SMS DTOs
|
||||
public sealed class WebserviceSmsRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Sender number (must be registered in IPPanel)
|
||||
/// </summary>
|
||||
public string? Sender { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Recipient phone number(s) - comma separated for multiple recipients
|
||||
/// </summary>
|
||||
public required string Recipient { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SMS message content
|
||||
/// </summary>
|
||||
public required string Message { get; set; }
|
||||
}
|
||||
|
||||
public sealed class PatternSmsRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Pattern code (must be registered in IPPanel)
|
||||
/// </summary>
|
||||
public required string PatternCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Recipient phone number(s) - comma separated for multiple recipients
|
||||
/// Note: Pattern SMS typically supports single recipient per request
|
||||
/// </summary>
|
||||
public required List<string> Recipients { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Pattern variables as key-value pairs
|
||||
/// </summary>
|
||||
public required Dictionary<string, string> Variables { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional sender/originator number (if not provided, uses default from configuration)
|
||||
/// </summary>
|
||||
public string? Originator { get; set; }
|
||||
}
|
||||
|
||||
public sealed class SmsResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Bulk ID for tracking the SMS
|
||||
/// </summary>
|
||||
public required List<long> BulkId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Response status
|
||||
/// </summary>
|
||||
public required string Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Response message
|
||||
/// </summary>
|
||||
public string? Message { get; set; }
|
||||
}
|
||||
|
||||
130
src/GreenHome.Sms.Ippanel/README.md
Normal file
130
src/GreenHome.Sms.Ippanel/README.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# GreenHome.Sms.Ippanel
|
||||
|
||||
سرویس ارسال پیامک IPPanel برای پروژه GreenHome
|
||||
|
||||
## نصب و راهاندازی
|
||||
|
||||
### 1. اضافه کردن Reference به پروژه
|
||||
|
||||
در فایل `.csproj` پروژه خود:
|
||||
|
||||
```xml
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GreenHome.Sms.Ippanel\GreenHome.Sms.Ippanel.csproj" />
|
||||
</ItemGroup>
|
||||
```
|
||||
|
||||
### 2. ثبت سرویس در Program.cs
|
||||
|
||||
```csharp
|
||||
using GreenHome.Sms.Ippanel;
|
||||
|
||||
// روش 1: استفاده از Configuration (پیشنهادی)
|
||||
builder.Services.AddIppanelSms(builder.Configuration);
|
||||
|
||||
// روش 2: استفاده از Configuration Section
|
||||
builder.Services.AddIppanelSms(builder.Configuration.GetSection("IppanelSms"));
|
||||
|
||||
// روش 3: تنظیم دستی
|
||||
builder.Services.AddIppanelSms(options =>
|
||||
{
|
||||
options.BaseUrl = "https://edge.ippanel.com/v1";
|
||||
options.AuthorizationToken = "YOUR_TOKEN_HERE";
|
||||
options.DefaultSender = "10001001"; // اختیاری
|
||||
});
|
||||
```
|
||||
|
||||
### 3. تنظیمات در appsettings.json
|
||||
|
||||
```json
|
||||
{
|
||||
"IppanelSms": {
|
||||
"BaseUrl": "https://edge.ippanel.com/v1",
|
||||
"AuthorizationToken": "YOUR_IPPANEL_TOKEN_HERE",
|
||||
"DefaultSender": "10001001"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## استفاده
|
||||
|
||||
### تزریق سرویس
|
||||
|
||||
```csharp
|
||||
public class MyController : ControllerBase
|
||||
{
|
||||
private readonly ISmsService smsService;
|
||||
|
||||
public MyController(ISmsService smsService)
|
||||
{
|
||||
this.smsService = smsService;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ارسال Webservice SMS
|
||||
|
||||
```csharp
|
||||
var request = new WebserviceSmsRequest
|
||||
{
|
||||
Sender = "10001001",
|
||||
Recipient = "09123456789", // یا چند شماره با کاما: "09123456789,09187654321"
|
||||
Message = "سلام، این یک پیامک تست است"
|
||||
};
|
||||
|
||||
var result = await smsService.SendWebserviceSmsAsync(request);
|
||||
// result.BulkId برای ردیابی پیامک استفاده میشود
|
||||
```
|
||||
|
||||
### ارسال Pattern SMS
|
||||
|
||||
```csharp
|
||||
var request = new PatternSmsRequest
|
||||
{
|
||||
PatternCode = "your-pattern-code",
|
||||
Recipient = "09123456789",
|
||||
Variables = new Dictionary<string, string>
|
||||
{
|
||||
{ "code", "12345" },
|
||||
{ "name", "کاربر" }
|
||||
},
|
||||
Originator = "10001001" // اختیاری، اگر نباشد از DefaultSender استفاده میشود
|
||||
};
|
||||
|
||||
var result = await smsService.SendPatternSmsAsync(request);
|
||||
```
|
||||
|
||||
## ساختار پروژه
|
||||
|
||||
```
|
||||
GreenHome.Sms.Ippanel/
|
||||
├── IppanelSmsService.cs # پیادهسازی سرویس
|
||||
├── IppanelSmsOptions.cs # تنظیمات
|
||||
└── ServiceCollectionExtensions.cs # Extension Methods برای ثبت سرویس
|
||||
```
|
||||
|
||||
## نکات مهم
|
||||
|
||||
1. **توکن IPPanel**: حتماً توکن معتبر خود را از پنل IPPanel دریافت و در تنظیمات قرار دهید
|
||||
2. **Webservice SMS**: میتوانید چند گیرنده را با کاما جدا کنید
|
||||
3. **Pattern SMS**: معمولاً یک گیرنده در هر درخواست پشتیبانی میشود
|
||||
4. **خطاها**: تمام خطاها لاگ میشوند و به صورت Exception پرتاب میشوند
|
||||
|
||||
## استفاده در پروژههای دیگر
|
||||
|
||||
این پروژه مستقل است و میتوانید آن را در هر پروژه .NET دیگری استفاده کنید:
|
||||
|
||||
1. پروژه را به Solution خود اضافه کنید
|
||||
2. Reference را اضافه کنید
|
||||
3. `GreenHome.Application` را نیز Reference کنید (برای Interface و DTOs)
|
||||
4. سرویس را ثبت کنید
|
||||
|
||||
## تغییر سرویس SMS
|
||||
|
||||
اگر بخواهید از سرویس SMS دیگری استفاده کنید:
|
||||
|
||||
1. یک پیادهسازی جدید از `ISmsService` ایجاد کنید
|
||||
2. در `Program.cs` به جای `AddIppanelSms` از متد ثبت سرویس جدید استفاده کنید
|
||||
|
||||
Interface و DTOs در `GreenHome.Application` باقی میمانند و نیازی به تغییر کدهای استفادهکننده نیست.
|
||||
|
||||
101
src/GreenHome.Sms.Ippanel/ServiceCollectionExtensions.cs
Normal file
101
src/GreenHome.Sms.Ippanel/ServiceCollectionExtensions.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace GreenHome.Sms.Ippanel;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for registering IPPanel SMS service
|
||||
/// </summary>
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds IPPanel SMS service to the service collection
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection</param>
|
||||
/// <param name="configuration">Configuration section for IppanelSms</param>
|
||||
/// <returns>The service collection for chaining</returns>
|
||||
public static IServiceCollection AddIppanelSms(
|
||||
this IServiceCollection services,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
return services.AddIppanelSms(configuration.GetSection("IppanelSms"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds IPPanel SMS service to the service collection
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection</param>
|
||||
/// <param name="configurationSection">Configuration section for IppanelSms</param>
|
||||
/// <returns>The service collection for chaining</returns>
|
||||
public static IServiceCollection AddIppanelSms(
|
||||
this IServiceCollection services,
|
||||
IConfigurationSection? configurationSection = null)
|
||||
{
|
||||
// Configure options
|
||||
IppanelSmsOptions? options = null;
|
||||
if (configurationSection != null)
|
||||
{
|
||||
options = configurationSection.Get<IppanelSmsOptions>();
|
||||
}
|
||||
|
||||
if (options == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"IppanelSms configuration section is missing. " +
|
||||
"Please add 'IppanelSms' section to your appsettings.json with 'AuthorizationToken' property.");
|
||||
}
|
||||
|
||||
// Register options as singleton
|
||||
services.AddSingleton(options);
|
||||
|
||||
// Register HttpClient and service
|
||||
services.AddHttpClient<ISmsService, IppanelSmsService>(client =>
|
||||
{
|
||||
// Ensure BaseUrl ends with / for proper relative path handling
|
||||
var baseUrl = options.BaseUrl.TrimEnd('/') + "/";
|
||||
client.BaseAddress = new Uri(baseUrl);
|
||||
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
|
||||
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", options.AuthorizationToken);
|
||||
client.Timeout = TimeSpan.FromSeconds(30);
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds IPPanel SMS service to the service collection with explicit options
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection</param>
|
||||
/// <param name="configureOptions">Action to configure options</param>
|
||||
/// <returns>The service collection for chaining</returns>
|
||||
public static IServiceCollection AddIppanelSms(
|
||||
this IServiceCollection services,
|
||||
Action<IppanelSmsOptions> configureOptions)
|
||||
{
|
||||
var options = new IppanelSmsOptions
|
||||
{
|
||||
AuthorizationToken = string.Empty // Will be set by configureOptions
|
||||
};
|
||||
configureOptions(options);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(options.AuthorizationToken))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"IppanelSms AuthorizationToken is required.");
|
||||
}
|
||||
|
||||
// Register options as singleton
|
||||
services.AddSingleton(options);
|
||||
|
||||
// Register HttpClient and service
|
||||
services.AddHttpClient<ISmsService, IppanelSmsService>(client =>
|
||||
{
|
||||
client.BaseAddress = new Uri(options.BaseUrl);
|
||||
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", options.AuthorizationToken);
|
||||
client.Timeout = TimeSpan.FromSeconds(30);
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user