CodeyaSMS is a provider-neutral, asynchronous .NET library for Iranian SMS gateways. The gateway behavior and legacy pattern formats are ported from the GPLv3 Persian WooCommerce SMS plugin. WordPress, WooCommerce, admin UI, database, cron, order hooks, and subscription logic are intentionally excluded.
dotnet add package CodeyaSMS --version 0.1.0-preview.1
dotnet add package CodeyaSMS.Extensions.DependencyInjection --version 0.1.0-preview.1CodeyaSMS contains the core contracts and providers. The optional DI package adds named provider registration backed by IHttpClientFactory for .NET 8 and .NET 10 applications.
Core package:
net462netstandard2.0net8.0net10.0
DI package:
net8.0net10.0
The netstandard2.0 asset is the compatibility path for many .NET Framework, .NET Core, Mono, Xamarin, and modern .NET applications. The explicit targets provide framework-specific networking behavior where it materially improves reliability.
- Kavenegar: normal SMS and lookup/pattern
- SMS.ir: bulk and verify
- IPPanel: normal SMS and pattern
- MeliPayamak: normal SMS and BaseServiceNumber pattern
- Ghasedak: bulk and NewOTP
- Bale: phone-based messenger notification; not carrier SMS
Legacy SOAP gateways from the source plugin are not represented as supported until they are independently verified against current endpoints and TLS requirements.
using CodeyaSMS;
using CodeyaSMS.Models;
using CodeyaSMS.Providers.SmsIr;
using var provider = new SmsIrSmsProvider(new SmsIrOptions
{
ApiKey = configuration["Sms:ApiKey"]
?? throw new InvalidOperationException("Sms:ApiKey is required."),
LineNumber = configuration["Sms:LineNumber"]
?? throw new InvalidOperationException("Sms:LineNumber is required.")
});
var client = new SmsClient(provider);
SmsSendResult result = await client.SendPatternAsync(
new[] { "09120000000" },
"123456",
new[] { new SmsPatternParameter("Code", "481927") },
cancellationToken);
if (result.SubmissionStatus == SmsSubmissionStatus.Rejected)
{
SmsGatewayError? error = result.Error;
logger.LogWarning(
"SMS submission rejected. Provider={Provider}, Status={Status}, Stage={Stage}, MayBeAccepted={MayBeAccepted}, RetrySafety={RetrySafety}, RetryAfter={RetryAfter}",
result.ProviderId,
error?.HttpStatusCode,
error?.FailureStage,
error?.RequestMayHaveBeenAccepted,
error?.RetrySafety,
error?.RetryAfter);
}IsSuccess means every recipient was accepted by the gateway. It does not prove handset delivery. Delivery receipts require a provider-specific status query or webhook integration.
using Microsoft.Extensions.DependencyInjection;
using CodeyaSMS.Extensions.DependencyInjection;
services.AddCodeyaSms(builder => builder
.AddSmsIr("primary", options =>
{
options.ApiKey = configuration["Sms:SmsIr:ApiKey"]!;
options.LineNumber = configuration["Sms:SmsIr:LineNumber"]!;
})
.AddKavenegar("fallback", options =>
{
options.ApiKey = configuration["Sms:Kavenegar:ApiKey"]!;
options.SenderNumber = configuration["Sms:Kavenegar:Sender"]!;
}));
ISmsProviderResolver resolver = serviceProvider.GetRequiredService<ISmsProviderResolver>();
SmsClient client = resolver.CreateClient("primary");The DI package uses a bounded SocketsHttpHandler, disables redirects and cookies, rotates pooled connections, and removes the default IHttpClientFactory request loggers. Kavenegar places its API key in the URL path. This removal does not disable runtime System.Net.Http activities or custom HTTP instrumentation; hosts must filter or redact full outbound URLs before export.
- Fully asynchronous provider APIs with caller
CancellationTokensupport. - No
.Result,.Wait(),async void, or per-requestHttpClientconstruction. - Separate
SmsRequestTimeoutExceptionfor internal timeout; caller cancellation remainsOperationCanceledException. ResponseHeadersReadplus a configurable hard response limit.- Bounded JSON and URL-encoded request serialization based on final byte count.
- Per-provider concurrency gate.
- HTTPS required by default; plain HTTP must be enabled explicitly for a controlled legacy endpoint.
- Injected
HttpClientownership remains with the caller. - Provider options are snapshotted when the provider is constructed.
- Bounded diagnostic messages, references, codes, recipients, and provider request IDs.
- No automatic send retry. Failures expose
FailureStage,RequestMayHaveBeenAccepted, andRetrySafety; HTTP 5xx, transport loss, timeout, and unreadable responses are treated conservatively because blind retry can duplicate messages.
See docs/NETWORKING.md, docs/OBSERVABILITY.md, docs/PRODUCTION-READINESS.md, and SECURITY.md.
The client recognizes the pattern formats used by the source WordPress plugin, including:
pattern:123456
Code:481927
Name:Pedram
SmsSendResult result = await client.SendLegacyMessageAsync(
new[] { "09120000000" },
legacyMessage,
cancellationToken);./build.shor on Windows:
./build.ps1The build scripts use the SDK version pinned in global.json. When that SDK is not installed, the bootstrap scripts install it into the repository-local .dotnet/ directory through the official dotnet-install script.
The pipeline performs:
- recommended .NET analyzers with warnings as errors
- source API contract verification and compiled package validation
- full-SHA GitHub Action pin verification
- preview-aware package-lock validation; lock files are mandatory for stable releases
- static secret and async-pattern checks
- builds on Windows and Linux
- tests on .NET 8 and .NET 10
- an executable .NET Framework 4.6.2 smoke test on Windows
- provider response fixture tests
- package validation and NuGet audit
- package-content validation
- installation and execution from a local NuGet feed
- separate manual live-provider contract tests
- scheduled load and managed-memory smoke tests
Publishing uses NuGet Trusted Publishing through GitHub OIDC. A permanent NuGet API key is not stored in the repository. Third-party workflow actions are pinned to reviewed full commit SHAs. Official 1.x packages remain unsigned to preserve a stable assembly identity.
This project is distributed under GPL-3.0-only because it is derived from GPLv3 source. See NOTICE.md, LICENSE, and docs/PORTING-SCOPE.md.
- Repository administration:
docs/REPOSITORY-SETUP.md - First public release:
docs/FIRST-RELEASE.md - Contributions:
CONTRIBUTING.md - Support:
SUPPORT.md - Security reports:
SECURITY.md
For NETSDK1064, missing framework types, incomplete NuGet restores, or stale Visual Studio design-time errors, close Visual Studio and run repair-local-build.cmd. Detailed diagnostics are available in docs/LOCAL-BUILD-TROUBLESHOOTING.md.