feat: publish dbussmsforward refactor

This commit is contained in:
2026-05-08 23:54:03 +08:00
commit 9f1ee9ffe6
77 changed files with 15585 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<InvariantGlobalization>true</InvariantGlobalization>
<RootNamespace>DbusSmsForward</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MailKit" Version="4.14.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="8.0.0" />
<PackageReference Include="Tmds.DBus.Protocol" Version="0.22.0" />
</ItemGroup>
<ItemGroup>
<Compile Remove="ProcessUserChoise/**/*.cs" />
<Compile Remove="SendMethod/**/*.cs" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="appsettings.example.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
+208
View File
@@ -0,0 +1,208 @@
using DbusSmsForward.SettingModel;
using Microsoft.Extensions.Configuration;
using System.Net;
using System.Text.Json;
namespace DbusSmsForward.Helper;
public static class ConfigHelper
{
private static readonly JsonSerializerOptions JsonOptions = new(SourceGenerationContext.Default.Options)
{
PropertyNameCaseInsensitive = true,
WriteIndented = true
};
public static string DefaultSettingsFileName => Path.Combine(AppContext.BaseDirectory, "appsettings.json");
public static string ResolveConfigPath(IConfiguration? configuration = null)
{
string[] candidates =
[
configuration?["DBUSSMSFORWARD_CONFIG_PATH"] ?? string.Empty,
Environment.GetEnvironmentVariable("DBUSSMSFORWARD_CONFIG_PATH") ?? string.Empty,
"/etc/dbussmsforward/appsettings.json",
DefaultSettingsFileName
];
foreach (string candidate in candidates)
{
if (!string.IsNullOrWhiteSpace(candidate) && File.Exists(candidate))
{
return candidate;
}
}
return candidates.Last(static c => !string.IsNullOrWhiteSpace(c));
}
public static DbusSmsForwardOptions LoadOptions(IConfiguration? configuration = null)
{
DbusSmsForwardOptions options = new();
string configPath = ResolveConfigPath(configuration);
if (File.Exists(configPath))
{
string json = File.ReadAllText(configPath);
options = JsonSerializer.Deserialize(json, SourceGenerationContext.Default.DbusSmsForwardOptions) ?? new DbusSmsForwardOptions();
}
if (configuration is not null)
{
configuration.GetSection("DbusSmsForward").Bind(options);
configuration.GetSection("Daemon").Bind(options.Daemon);
configuration.GetSection("Modem").Bind(options.Modem);
configuration.GetSection("Forwarding").Bind(options.Forwarding);
configuration.GetSection("Notifications").Bind(options.Notifications);
configuration.GetSection("LegacyCompatibility").Bind(options.LegacyCompatibility);
}
return options;
}
public static void SaveOptions(DbusSmsForwardOptions options, string? path = null)
{
string target = path ?? ResolveConfigPath();
Directory.CreateDirectory(Path.GetDirectoryName(target) ?? AppContext.BaseDirectory);
string updatedJson = JsonSerializer.Serialize(options, SourceGenerationContext.Default.DbusSmsForwardOptions);
File.WriteAllText(target, updatedJson);
}
public static void SaveLegacySettings(appsettingsModel result, string? path = null)
{
string target = path ?? DefaultSettingsFileName;
Directory.CreateDirectory(Path.GetDirectoryName(target) ?? AppContext.BaseDirectory);
string updatedJson = JsonSerializer.Serialize(result, SourceGenerationContext.Default.appsettingsModel);
File.WriteAllText(target, updatedJson);
}
public static void GetSettings(ref appsettingsModel result)
{
string settingsFileName = ResolveConfigPath();
if (!File.Exists(settingsFileName))
{
result ??= new appsettingsModel();
return;
}
string config = File.ReadAllText(settingsFileName);
try
{
if (LooksLikeNewConfig(config))
{
DbusSmsForwardOptions options = JsonSerializer.Deserialize(config, SourceGenerationContext.Default.DbusSmsForwardOptions) ?? new DbusSmsForwardOptions();
result = ConvertToLegacy(options);
return;
}
result = JsonSerializer.Deserialize(config, SourceGenerationContext.Default.appsettingsModel) ?? new appsettingsModel();
}
catch
{
result ??= new appsettingsModel();
}
}
public static void UpdateSettings(ref appsettingsModel result)
{
SaveLegacySettings(result);
}
public static bool JudgeIsForwardIgnore(uint storage)
{
appsettingsModel result = new();
GetSettings(ref result);
string forwardStorageType = result.appSettings.ForwardIgnoreStorageType?.Trim().ToLowerInvariant() ?? string.Empty;
return (forwardStorageType, storage) switch
{
("unknown", 0) => true,
("sm", 1) => true,
("me", 2) => true,
("mt", 3) => true,
("sr", 4) => true,
("bm", 5) => true,
("ta", 6) => true,
_ => false
};
}
public static string GetDeviceHostName()
{
try
{
return Dns.GetHostName();
}
catch
{
try
{
return Environment.MachineName;
}
catch
{
return string.Empty;
}
}
}
public static string ResolveDeviceName(DbusSmsForwardOptions options)
{
if (!string.IsNullOrWhiteSpace(options.Forwarding.DeviceName))
{
return options.Forwarding.DeviceName;
}
return GetDeviceHostName();
}
public static appsettingsModel ConvertToLegacy(DbusSmsForwardOptions options)
{
return new appsettingsModel
{
appSettings = new LegacyAppSettings
{
EmailConfig = options.Notifications.Email,
PushPlusConfig = options.Notifications.PushPlus,
DingTalkConfig = options.Notifications.DingTalk,
BarkConfig = options.Notifications.Bark,
WeComApplicationConfig = options.Notifications.WeComApplication,
TGBotConfig = options.Notifications.TelegramBot,
ShellConfig = options.Notifications.Shell,
SmsCodeKey = options.Forwarding.SmsCodeKey,
ForwardIgnoreStorageType = options.Forwarding.IgnoreStorageType,
DeviceName = options.Forwarding.DeviceName
}
};
}
public static DbusSmsForwardOptions ConvertFromLegacy(appsettingsModel legacy)
{
return new DbusSmsForwardOptions
{
Forwarding = new ForwardingConfig
{
DeviceName = legacy.appSettings.DeviceName,
SmsCodeKey = legacy.appSettings.SmsCodeKey,
IgnoreStorageType = legacy.appSettings.ForwardIgnoreStorageType
},
Notifications = new NotificationChannels
{
Email = legacy.appSettings.EmailConfig,
PushPlus = legacy.appSettings.PushPlusConfig,
DingTalk = legacy.appSettings.DingTalkConfig,
Bark = legacy.appSettings.BarkConfig,
WeComApplication = legacy.appSettings.WeComApplicationConfig,
TelegramBot = legacy.appSettings.TGBotConfig,
Shell = legacy.appSettings.ShellConfig
}
};
}
private static bool LooksLikeNewConfig(string json)
{
using JsonDocument document = JsonDocument.Parse(json);
JsonElement root = document.RootElement;
return root.TryGetProperty("Daemon", out _) || root.TryGetProperty("Notifications", out _) || root.TryGetProperty("Forwarding", out _);
}
}
+45
View File
@@ -0,0 +1,45 @@
using System.Net.Http.Json;
using System.Text;
using System.Text.Json.Nodes;
namespace DbusSmsForward.Helper;
public static class HttpHelper
{
public static string HttpGet(string url)
=> HttpGetAsync(url).GetAwaiter().GetResult();
public static async Task<string> HttpGetAsync(string url, bool skipTlsValidation = false, CancellationToken cancellationToken = default)
{
using HttpClient client = CreateClient(skipTlsValidation);
using HttpResponseMessage response = await client.GetAsync(url, cancellationToken);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync(cancellationToken);
}
public static string Post(string url, JsonObject obj)
=> PostAsync(url, obj).GetAwaiter().GetResult();
public static async Task<string> PostAsync(string url, JsonObject obj, bool skipTlsValidation = false, CancellationToken cancellationToken = default)
{
using HttpClient client = CreateClient(skipTlsValidation);
using StringContent content = new(obj.ToJsonString(), Encoding.UTF8, "application/json");
using HttpResponseMessage response = await client.PostAsync(url, content, cancellationToken);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync(cancellationToken);
}
private static HttpClient CreateClient(bool skipTlsValidation)
{
HttpClientHandler handler = new();
if (skipTlsValidation)
{
handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
}
return new HttpClient(handler)
{
Timeout = TimeSpan.FromSeconds(30)
};
}
}
+11
View File
@@ -0,0 +1,11 @@
namespace DbusSmsForward.Models;
public sealed class AppCommand
{
public string Name { get; init; } = "run";
public bool PrintSampleConfig { get; init; }
public bool ValidateConfig { get; init; }
public string PhoneNumber { get; init; } = string.Empty;
public string Message { get; init; } = string.Empty;
public string[] EnabledChannels { get; init; } = [];
}
File diff suppressed because it is too large Load Diff
+836
View File
@@ -0,0 +1,836 @@
using DbusSmsForward.Helper;
using DbusSmsForward.SettingModel;
using DbusSmsForward.SMSModel;
using ModemManager1.DBus;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text.Json;
using Tmds.DBus.Protocol;
namespace DbusSmsForward.ModemHelper
{
public class ModemManagerHelper
{
private readonly Lock _modemObjectPathListLockObject = new();
public Dictionary<ObjectPath, Dictionary<string, Dictionary<string, VariantValue>>> _modemObjectPathList = new Dictionary<ObjectPath, Dictionary<string, Dictionary<string, VariantValue>>>();
public string _modemObjectPathNowUse = "";
public string qmiPathNowUse = "";
public string _modemManagerObjectPath = "/org/freedesktop/ModemManager1";
public string? systemBusAddress = Address.System;
public string baseService = "org.freedesktop.ModemManager1";
public List<Action<SmsContentModel, string, string>> smsSendMethodList = new List<Action<SmsContentModel, string, string>>();
public string deviceUidNowUse = "";
private readonly Lock _subscribeSmsPathListLockObject = new();
public Dictionary<string,bool> SubscribeSmsPathList=new Dictionary<string, bool>();
public ModemManagerHelper()
{
if (!string.IsNullOrEmpty(systemBusAddress))
{
InitLoadModemObjectPathList();
WatchModems();
}
}
public void InitLoadModemObjectPathList()
{
lock (_modemObjectPathListLockObject)
{
_modemObjectPathList = GetModemsPathList().Result;
}
if (_modemObjectPathList.Count == 1)
{
SetNowUsedModemPath(_modemObjectPathList.First().Key);
}
if (_modemObjectPathList.Count == 0)
{
SetNowUsedModemPath(null);
}
foreach (var item in _modemObjectPathList)
{
SubscribeSms(item.Key);
}
}
public void SetSendMethodList(List<Action<SmsContentModel, string, string>> SendMethodList)
{
smsSendMethodList = SendMethodList;
}
public void SetNowUsedModemPath(string modemObjectPathNowUse)
{
_modemObjectPathNowUse = modemObjectPathNowUse;
if (string.IsNullOrEmpty(_modemObjectPathNowUse))
{
qmiPathNowUse = "";
deviceUidNowUse = "";
}
else
{
qmiPathNowUse = GetQMIDevice().Result;
deviceUidNowUse = GetDevice().Result;
}
}
public void WatchModems()
{
Task.Run(async () =>
{
try
{
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var objectManager = service.CreateObjectManager(_modemManagerObjectPath);
//Console.WriteLine($"Subscribing for modem RemovedChanges");
await objectManager.WatchInterfacesRemovedAsync(
async (Exception? ex, (ObjectPath ObjectPath, string[] Interfaces) change) =>
{
if (ex is null)
{
//Console.WriteLine("modem has been removed,modem path:" + change.ObjectPath.ToString());
InitLoadModemObjectPathList();
if (!string.IsNullOrEmpty(_modemObjectPathNowUse))
{
DisableAndEnableModem().Wait();
}
}
});
var tcs = new TaskCompletionSource<bool>();
var task = tcs.Task;
await task;
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
});
Task.Run(async () =>
{
try
{
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var objectManager = service.CreateObjectManager(_modemManagerObjectPath);
//Console.WriteLine($"Subscribing for modem AddedChanges");
await objectManager.WatchInterfacesAddedAsync(
async (Exception? ex, (ObjectPath ObjectPath, Dictionary<string, Dictionary<string, VariantValue>> InterfacesAndProperties) change) =>
{
if (ex is null)
{
//Console.WriteLine("new modem added,modem path:" + change.ObjectPath.ToString());
InitLoadModemObjectPathList();
if (!string.IsNullOrEmpty(_modemObjectPathNowUse))
{
DisableAndEnableModem().Wait();
}
}
});
var tcs = new TaskCompletionSource<bool>();
var task = tcs.Task;
await task;
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
});
}
public void SubscribeSms(string path)
{
Task.Run(async () =>
{
try
{
if ((!SubscribeSmsPathList.ContainsKey(path) || (SubscribeSmsPathList.ContainsKey(path) && !SubscribeSmsPathList[path]))&& _modemObjectPathList.ContainsKey(path))
{
if (SubscribeSmsPathList.ContainsKey(path))
{
lock (_subscribeSmsPathListLockObject)
{
SubscribeSmsPathList[path] = true;
}
}
else
{
lock (_subscribeSmsPathListLockObject)
{
SubscribeSmsPathList.Add(path, true);
}
}
//SetMsgDefaultStorageToME(path);
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var messaging = service.CreateMessaging(path);
//Console.WriteLine($"add subscribe sms for modem path:" + path);
await messaging.WatchAddedAsync(
async (Exception? ex, (ObjectPath Path, bool Received) change) =>
{
if (ex is null)
{
if (change.Received)
{
try
{
var smsPath = change.Path;
var service = new ModemManager1Service(connection, baseService);
var sms = service.CreateSms(smsPath);
var smsState = await sms.GetStateAsync();
if (smsState == 2 || smsState == 3)
{
uint Storage = await sms.GetStorageAsync();
if (!ConfigHelper.JudgeIsForwardIgnore(Storage))
{
string telNum = await sms.GetNumberAsync();
//string smsDate = (await sms.GetTimestampAsync()).Replace("T", " ").Replace("+08:00", " ");
string smsdateOrg = await sms.GetTimestampAsync();
string smsDate = string.Empty;
try
{
DateTimeOffset dto = DateTimeOffset.Parse(smsdateOrg, null, DateTimeStyles.RoundtripKind);
// 转换为本地时间
DateTime localTime = dto.LocalDateTime;
smsDate = localTime.ToString("yyyy-MM-dd HH:mm:ss");
}
catch (Exception ex1)
{
Console.WriteLine("smsdate转换失败:\n" + ex1.Message);
smsDate = smsdateOrg;
}
int tryCount = 0;
do
{
smsState = await sms.GetStateAsync();
Thread.Sleep(100);
} while (smsState == 2 && tryCount < 300);
if (smsState == 3)
{
appsettingsModel result = new appsettingsModel();
ConfigHelper.GetSettings(ref result);
string DeviceName = result.appSettings.DeviceName;
result = null;
if (string.IsNullOrEmpty(DeviceName) || DeviceName== "*Host*Name*")
{
DeviceName= ConfigHelper.GetDeviceHostName();
}
string smsContent = await sms.GetTextAsync();
SmsContentModel smsmodel = new SmsContentModel();
smsmodel.TelNumber = telNum;
smsmodel.SmsDate = smsDate;
smsmodel.SmsContent = smsContent;
string body = "发信电话:" + telNum + "\n" + "时间:" + smsDate + "\n" + "转发设备:" + DeviceName + "\n" + "短信内容:" + smsContent;
Console.WriteLine("smspath " + smsPath);
if (smsSendMethodList.Count() > 0)
{
//Console.WriteLine("转发方法数量"+smsSendMethodList.Count());
foreach (var action in smsSendMethodList)
{
action.Invoke(smsmodel, body, DeviceName);
}
}
}
else
{
Console.WriteLine("smspath:" + change.Path + " 接收超时,转发取消");
}
}
}
}
catch (Exception ex2)
{
Console.WriteLine("GetSmsError" + ex2.Message);
}
}
}
else
{
Console.WriteLine(ex);
}
});
Task task = Task.Run(() =>
{
var modem = service.CreateModem(path);
do
{
try
{
var Manufacturer = modem.GetManufacturerAsync().Result;
Manufacturer = null;
Thread.Sleep(1000);
}
catch (Exception ex)
{
break;
}
} while (SubscribeSmsPathList.ContainsKey(path) && SubscribeSmsPathList[path] && _modemObjectPathList.ContainsKey(path));
if (SubscribeSmsPathList.ContainsKey(path))
{
lock (_subscribeSmsPathListLockObject)
{
SubscribeSmsPathList[path] = false;
}
}
});
await task;
//Console.WriteLine($"remove subscribe sms for modem path:" + path);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
Console.WriteLine($"remove subscribe sms for modem path:" + path);
}
});
}
public async Task<bool> SendSms(string telNum, string smsContent)
{
try
{
using (var connection = new Connection(Address.System))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var messaging = service.CreateMessaging(_modemObjectPathNowUse);
var sendsmsPath = await messaging.CreateAsync(new Dictionary<string, VariantValue> { { "text", smsContent }, { "number", telNum } });
var sms = service.CreateSms(sendsmsPath);
await sms.SendAsync();
return true;
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
return false;
}
}
public async Task<Dictionary<ObjectPath, Dictionary<string, Dictionary<string, VariantValue>>>> GetModemsPathList()
{
try
{
await ScanDevices();
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var objectManager = service.CreateObjectManager(_modemManagerObjectPath);
return await objectManager.GetManagedObjectsAsync();
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return new Dictionary<ObjectPath, Dictionary<string, Dictionary<string, VariantValue>>>();
}
public async Task ScanDevices()
{
try
{
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var modemManager = service.CreateModemManager1(_modemManagerObjectPath);
await modemManager.ScanDevicesAsync();
modemManager = null;
service = null;
}
}
catch (Exception ex)
{
if (!(ex.Message.IndexOf("unsupported") >-1))
{
Console.WriteLine("ScanDevices失败" + ex.Message);
}
}
}
public async Task<string> GetManufacturer(string? path = null)
{
try
{
string actUsePath = path == null ? _modemObjectPathNowUse : path;
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var modem = service.CreateModem(actUsePath);
var Manufacturer = await modem.GetManufacturerAsync();
modem = null;
service = null;
return Manufacturer;
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
return string.Empty;
}
}
public async Task<bool> JudgeCanGetStatus(string? path = null)
{
try
{
string actUsePath = path == null ? _modemObjectPathNowUse : path;
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var modem = service.CreateSimple(actUsePath);
var status = await modem.GetStatusAsync();
modem = null;
service = null;
return true;
}
}
catch (Exception ex)
{
//Console.WriteLine(ex);
return false;
}
}
public async Task DisableAndEnableModem(string? path = null)
{
try
{
string actUsePath = path == null ? _modemObjectPathNowUse : path;
if (!string.IsNullOrEmpty(actUsePath))
{
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var modem = service.CreateModem(actUsePath);
try
{
await modem.EnableAsync(false);
Thread.Sleep(200);
await modem.EnableAsync(true);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
else
{
Console.WriteLine("DisableAndEnableModem:actUsePath为空");
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
public async Task InhibitModemDevice(string uid, bool inhibit)
{
try
{
Console.WriteLine("device:" + uid);
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var modemManager = service.CreateModemManager1(_modemManagerObjectPath);
await modemManager.InhibitDeviceAsync(uid, inhibit);
modemManager = null;
service = null;
}
}
catch (Exception ex)
{
Console.WriteLine("InhibitModemDevice失败" + ex);
}
}
public async Task<string> GetDevice(string? path = null)
{
try
{
string actUsePath = path == null ? _modemObjectPathNowUse : path;
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var modem = service.CreateModem(actUsePath);
var device = await modem.GetDeviceAsync();
modem = null;
service = null;
return device;
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
return string.Empty;
}
}
public async Task<string> GetQMIDevice(string? path = null)
{
try
{
string actUsePath = path == null ? _modemObjectPathNowUse : path;
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var modem = service.CreateModem(actUsePath);
var Ports = await modem.GetPortsAsync();
foreach (var port in Ports)
{
if (port.Item2 == 6)
{
return "/dev/" + port.Item1;
}
}
return "";
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
return string.Empty;
}
}
public async Task<string> GetModel(string? path = null)
{
try
{
string actUsePath = path == null ? _modemObjectPathNowUse : path;
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var modem = service.CreateModem(actUsePath);
var Model = await modem.GetModelAsync();
modem = null;
service = null;
return Model;
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
return string.Empty;
}
}
public async Task<string> GetRevision(string? path = null)
{
try
{
string actUsePath = path == null ? _modemObjectPathNowUse : path;
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var modem = service.CreateModem(actUsePath);
var Revision = await modem.GetRevisionAsync();
modem = null;
service = null;
return Revision;
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
return string.Empty;
}
}
public async Task<string> GetSignalQuality(string? path = null)
{
try
{
string actUsePath = path == null ? _modemObjectPathNowUse : path;
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var modem = service.CreateModem(actUsePath);
var SignalQuality = await modem.GetSignalQualityAsync();
modem = null;
service = null;
return SignalQuality.Item1.ToString();
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
return string.Empty;
}
}
public async Task<string[]> GetOwnNumbers(string? path = null)
{
try
{
string actUsePath = path == null ? _modemObjectPathNowUse : path;
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var modem = service.CreateModem(actUsePath);
var OwnNumbers = await modem.GetOwnNumbersAsync();
modem = null;
service = null;
return OwnNumbers;
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
return [];
}
}
public async Task<uint?> GetPrimarySimSlot(string? path = null)
{
try
{
string actUsePath = path == null ? _modemObjectPathNowUse : path;
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var modem = service.CreateModem(actUsePath);
var PrimarySimSlot = await modem.GetPrimarySimSlotAsync();
return PrimarySimSlot;
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
return null;
}
}
public async Task<string> GetICCID(string? path = null)
{
try
{
string actUsePath = path == null ? _modemObjectPathNowUse : path;
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var modem = service.CreateModem(actUsePath);
var simpath = await modem.GetSimAsync();
var sim = service.CreateSim(simpath);
var iccid = await sim.GetSimIdentifierAsync();
modem = null;
service = null;
return iccid;
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
return string.Empty;
}
}
public async Task<string> GetImei(string? path = null)
{
try
{
string actUsePath = path == null ? _modemObjectPathNowUse : path;
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var modem3gpp = service.CreateModem3gpp(actUsePath);
var Imei = await modem3gpp.GetImeiAsync();
modem3gpp = null;
service = null;
return Imei;
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
return string.Empty;
}
}
public async Task<string> GetOperatorCode(string? path = null)
{
try
{
string actUsePath = path == null ? _modemObjectPathNowUse : path;
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var modem3gpp = service.CreateModem3gpp(actUsePath);
var OperatorCode = await modem3gpp.GetOperatorCodeAsync();
modem3gpp = null;
service = null;
return OperatorCode;
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
return string.Empty;
}
}
public async Task<string> GetOperatorName(string? path = null)
{
try
{
string actUsePath = path == null ? _modemObjectPathNowUse : path;
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var modem3gpp = service.CreateModem3gpp(actUsePath);
var OperatorName = await modem3gpp.GetOperatorNameAsync();
modem3gpp = null;
service = null;
return OperatorName;
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
return string.Empty;
}
}
public async Task SetMsgDefaultStorageToME(string path)
{
try
{
using (var connection = new Connection(systemBusAddress))
{
await connection.ConnectAsync();
var service = new ModemManager1Service(connection, baseService);
var messaging = service.CreateMessaging(path);
await messaging.SetDefaultStorageAsync(2);
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
public bool RestartModem()
{
string qmiPath = GetQMIDevice().Result;
uint? simslot = GetPrimarySimSlot().Result;
string usedDevice = deviceUidNowUse;
if (simslot.HasValue)
{
if (simslot.Value == 0)
{
simslot += 1;
}
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = "qmicli",
Arguments = @$"-d {qmiPath} -p --uim-sim-power-off={simslot}",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
};
using (Process process = Process.Start(startInfo))
{
if (process != null)
{
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
process.WaitForExit();
if (process.ExitCode != 0)
{
Console.WriteLine(error);
return false;
}
}
else
{
Console.WriteLine("Failed to exec qmicli");
return false;
}
}
ProcessStartInfo startInfo2 = new ProcessStartInfo
{
FileName = "qmicli",
Arguments = @$"-d {qmiPath} -p --uim-sim-power-on={simslot}",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
};
using (Process process = Process.Start(startInfo2))
{
if (process != null)
{
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
process.WaitForExit();
if (process.ExitCode != 0)
{
Console.WriteLine(error);
return false;
}
}
else
{
Console.WriteLine("Failed to exec qmicli");
return false;
}
}
if (JudgeCanGetStatus().Result)
{
DisableAndEnableModem().Wait();
}
Thread.Sleep(200);
if (JudgeCanGetStatus().Result)
{
InhibitModemDevice(usedDevice, true).Wait();
Thread.Sleep(200);
InhibitModemDevice(usedDevice, false).Wait();
}
return true;
}
else
{
return false;
}
}
}
}
@@ -0,0 +1,32 @@
using DbusSmsForward.Helper;
using DbusSmsForward.Services;
using DbusSmsForward.SettingModel;
using DbusSmsForward.SMSModel;
using System.Text.Json.Nodes;
namespace DbusSmsForward.Notifications;
public sealed class AutmanPushNotificationSender(DbusSmsForwardOptions options) : NotificationSenderBase("autmanpush")
{
private readonly AutmanPushConfig _config = options.Notifications.AutmanPush;
public override bool IsEnabled => _config.Enabled && !string.IsNullOrWhiteSpace(_config.ApiUrl);
public override async Task SendAsync(SmsContentModel sms, string body, string deviceName, CancellationToken cancellationToken)
{
JsonObject payload = new()
{
["title"] = BuildTitle(sms.TelNumber, string.Empty),
["content"] = body,
["deviceName"] = deviceName,
["phoneNumber"] = sms.TelNumber
};
if (!string.IsNullOrWhiteSpace(_config.AccessToken))
{
payload["accessToken"] = _config.AccessToken;
}
await HttpHelper.PostAsync(_config.ApiUrl, payload, _config.SkipTlsValidation, cancellationToken);
}
}
@@ -0,0 +1,27 @@
using DbusSmsForward.Helper;
using DbusSmsForward.ProcessSmsContent;
using DbusSmsForward.Services;
using DbusSmsForward.SettingModel;
using DbusSmsForward.SMSModel;
using System.Web;
namespace DbusSmsForward.Notifications;
public sealed class BarkNotificationSender(DbusSmsForwardOptions options) : NotificationSenderBase("bark")
{
private readonly BarkConfig _config = options.Notifications.Bark;
public override bool IsEnabled => _config.Enabled && !string.IsNullOrWhiteSpace(_config.ServerUrl) && !string.IsNullOrWhiteSpace(_config.BarkKey);
public override async Task SendAsync(SmsContentModel sms, string body, string deviceName, CancellationToken cancellationToken)
{
SmsCodeModel code = NotificationContentBuilder.ExtractCode(options, sms);
string url = $"{_config.ServerUrl.TrimEnd('/')}/{_config.BarkKey}/{HttpUtility.UrlEncode(body)}?group={HttpUtility.UrlEncode(sms.TelNumber)}&title={HttpUtility.UrlEncode(BuildTitle(sms.TelNumber, code.CodeValue))}";
if (!string.IsNullOrWhiteSpace(code.CodeValue))
{
url += $"&autoCopy=1&copy={HttpUtility.UrlEncode(code.CodeValue)}";
}
await HttpHelper.HttpGetAsync(url, _config.SkipTlsValidation, cancellationToken);
}
}
@@ -0,0 +1,47 @@
using DbusSmsForward.Helper;
using DbusSmsForward.Services;
using DbusSmsForward.SettingModel;
using DbusSmsForward.SMSModel;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json.Nodes;
using System.Web;
namespace DbusSmsForward.Notifications;
public sealed class DingTalkNotificationSender(DbusSmsForwardOptions options) : NotificationSenderBase("dingtalk")
{
private readonly DingTalkConfig _config = options.Notifications.DingTalk;
public override bool IsEnabled => _config.Enabled && !string.IsNullOrWhiteSpace(_config.AccessToken);
public override async Task SendAsync(SmsContentModel sms, string body, string deviceName, CancellationToken cancellationToken)
{
string url = $"{_config.Endpoint}?access_token={HttpUtility.UrlEncode(_config.AccessToken)}";
if (!string.IsNullOrWhiteSpace(_config.Secret))
{
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
string sign = AddSign(timestamp, _config.Secret);
url += $"&timestamp={timestamp}&sign={sign}";
}
JsonObject payload = new()
{
["msgtype"] = "text",
["text"] = new JsonObject
{
["content"] = body
}
};
await HttpHelper.PostAsync(url, payload, _config.SkipTlsValidation, cancellationToken);
}
private static string AddSign(long timestamp, string secret)
{
string stringToSign = $"{timestamp}\n{secret}";
using HMACSHA256 hmacsha256 = new(Encoding.UTF8.GetBytes(secret));
byte[] hashmessage = hmacsha256.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
return HttpUtility.UrlEncode(Convert.ToBase64String(hashmessage), Encoding.UTF8);
}
}
@@ -0,0 +1,31 @@
using DbusSmsForward.ProcessSmsContent;
using DbusSmsForward.Services;
using DbusSmsForward.SettingModel;
using DbusSmsForward.SMSModel;
using MailKit.Net.Smtp;
using MimeKit;
namespace DbusSmsForward.Notifications;
public sealed class EmailNotificationSender(DbusSmsForwardOptions options) : NotificationSenderBase("email")
{
private readonly EmailConfig _config = options.Notifications.Email;
public override bool IsEnabled => _config.Enabled && !string.IsNullOrWhiteSpace(_config.SmtpHost) && !string.IsNullOrWhiteSpace(_config.SendEmail) && !string.IsNullOrWhiteSpace(_config.ReceiveEmail);
public override async Task SendAsync(SmsContentModel sms, string body, string deviceName, CancellationToken cancellationToken)
{
SmsCodeModel code = NotificationContentBuilder.ExtractCode(options, sms);
MimeMessage message = new();
message.From.Add(new MailboxAddress("DbusSmsForward", _config.SendEmail));
message.To.Add(new MailboxAddress(string.Empty, _config.ReceiveEmail));
message.Subject = BuildTitle(sms.TelNumber, code.CodeValue);
message.Body = new TextPart("plain") { Text = body };
using SmtpClient client = new();
await client.ConnectAsync(_config.SmtpHost, _config.SmtpPort, _config.EnableSsl, cancellationToken);
await client.AuthenticateAsync(_config.SendEmail, _config.EmailKey, cancellationToken);
await client.SendAsync(message, cancellationToken);
await client.DisconnectAsync(true, cancellationToken);
}
}
@@ -0,0 +1,10 @@
using DbusSmsForward.SMSModel;
namespace DbusSmsForward.Notifications;
public interface INotificationSender
{
string Name { get; }
bool IsEnabled { get; }
Task SendAsync(SmsContentModel sms, string body, string deviceName, CancellationToken cancellationToken);
}
@@ -0,0 +1,13 @@
using DbusSmsForward.SMSModel;
namespace DbusSmsForward.Notifications;
public abstract class NotificationSenderBase(string name) : INotificationSender
{
public string Name { get; } = name;
public abstract bool IsEnabled { get; }
public abstract Task SendAsync(SmsContentModel sms, string body, string deviceName, CancellationToken cancellationToken);
protected static string BuildTitle(string telNumber, string codeValue)
=> string.IsNullOrWhiteSpace(codeValue) ? $"短信转发 {telNumber}" : $"{codeValue} 短信转发 {telNumber}";
}
@@ -0,0 +1,31 @@
using DbusSmsForward.Helper;
using DbusSmsForward.Notifications;
using DbusSmsForward.ProcessSmsContent;
using DbusSmsForward.Services;
using DbusSmsForward.SettingModel;
using DbusSmsForward.SMSModel;
using System.Net.Http.Json;
using System.Text.Json.Nodes;
namespace DbusSmsForward.Notifications;
public sealed class PushPlusNotificationSender(DbusSmsForwardOptions options) : NotificationSenderBase("pushplus")
{
private readonly PushPlusConfig _config = options.Notifications.PushPlus;
public override bool IsEnabled => _config.Enabled && !string.IsNullOrWhiteSpace(_config.Token);
public override async Task SendAsync(SmsContentModel sms, string body, string deviceName, CancellationToken cancellationToken)
{
SmsCodeModel code = NotificationContentBuilder.ExtractCode(options, sms);
JsonObject payload = new()
{
["token"] = _config.Token,
["title"] = BuildTitle(sms.TelNumber, code.CodeValue),
["content"] = body,
["topic"] = string.Empty
};
await HttpHelper.PostAsync(_config.Endpoint, payload, _config.SkipTlsValidation, cancellationToken);
}
}
@@ -0,0 +1,51 @@
using DbusSmsForward.Services;
using DbusSmsForward.SettingModel;
using DbusSmsForward.SMSModel;
using System.Diagnostics;
namespace DbusSmsForward.Notifications;
public sealed class ShellNotificationSender(DbusSmsForwardOptions options) : NotificationSenderBase("shell")
{
private readonly ShellConfig _config = options.Notifications.Shell;
public override bool IsEnabled => _config.Enabled && !string.IsNullOrWhiteSpace(_config.ShellPath);
public override async Task SendAsync(SmsContentModel sms, string body, string deviceName, CancellationToken cancellationToken)
{
ProcessStartInfo startInfo = new()
{
FileName = _config.ShellPath,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true
};
foreach (string arg in new[]
{
sms.TelNumber,
sms.SmsDate,
sms.SmsContent,
string.Empty,
string.Empty,
deviceName
})
{
startInfo.ArgumentList.Add(arg ?? string.Empty);
}
using Process process = Process.Start(startInfo) ?? throw new InvalidOperationException("Unable to start shell process.");
using CancellationTokenRegistration registration = cancellationToken.Register(() =>
{
try { if (!process.HasExited) process.Kill(true); } catch { }
});
await process.WaitForExitAsync(cancellationToken);
if (process.ExitCode != 0)
{
string error = await process.StandardError.ReadToEndAsync(cancellationToken);
throw new InvalidOperationException($"Shell sender exited with code {process.ExitCode}: {error}");
}
}
}
@@ -0,0 +1,25 @@
using DbusSmsForward.Helper;
using DbusSmsForward.Services;
using DbusSmsForward.SettingModel;
using DbusSmsForward.SMSModel;
using System.Web;
namespace DbusSmsForward.Notifications;
public sealed class TelegramNotificationSender(DbusSmsForwardOptions options) : NotificationSenderBase("telegram")
{
private readonly TelegramBotConfig _config = options.Notifications.TelegramBot;
public override bool IsEnabled => _config.Enabled && !string.IsNullOrWhiteSpace(_config.BotToken) && !string.IsNullOrWhiteSpace(_config.ChatId);
public override async Task SendAsync(SmsContentModel sms, string body, string deviceName, CancellationToken cancellationToken)
{
string apiBase = _config.EnableCustomApi && !string.IsNullOrWhiteSpace(_config.CustomApi)
? _config.CustomApi.TrimEnd('/')
: "https://api.telegram.org";
string text = body;
string url = $"{apiBase}/bot{_config.BotToken}/sendMessage?chat_id={HttpUtility.UrlEncode(_config.ChatId)}&text={HttpUtility.UrlEncode(text)}";
await HttpHelper.HttpGetAsync(url, _config.SkipTlsValidation, cancellationToken);
}
}
@@ -0,0 +1,37 @@
using DbusSmsForward.Helper;
using DbusSmsForward.Services;
using DbusSmsForward.SettingModel;
using DbusSmsForward.SMSModel;
using System.Text.Json.Nodes;
namespace DbusSmsForward.Notifications;
public sealed class WeComApplicationNotificationSender(DbusSmsForwardOptions options) : NotificationSenderBase("wecom")
{
private readonly WeComApplicationConfig _config = options.Notifications.WeComApplication;
public override bool IsEnabled => _config.Enabled && !string.IsNullOrWhiteSpace(_config.CorpId) && !string.IsNullOrWhiteSpace(_config.AgentId) && !string.IsNullOrWhiteSpace(_config.ApplicationSecret);
public override async Task SendAsync(SmsContentModel sms, string body, string deviceName, CancellationToken cancellationToken)
{
string tokenUrl = $"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={Uri.EscapeDataString(_config.CorpId)}&corpsecret={Uri.EscapeDataString(_config.ApplicationSecret)}";
string tokenResponse = await HttpHelper.HttpGetAsync(tokenUrl, _config.SkipTlsValidation, cancellationToken);
JsonObject tokenJson = JsonNode.Parse(tokenResponse)?.AsObject() ?? throw new InvalidOperationException("Invalid WeCom token response.");
string accessToken = tokenJson["access_token"]?.ToString() ?? throw new InvalidOperationException("WeCom access_token missing.");
JsonObject payload = new()
{
["touser"] = "@all",
["msgtype"] = "text",
["agentid"] = _config.AgentId,
["text"] = new JsonObject
{
["content"] = body
},
["safe"] = 0
};
string sendUrl = $"https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={Uri.EscapeDataString(accessToken)}";
await HttpHelper.PostAsync(sendUrl, payload, _config.SkipTlsValidation, cancellationToken);
}
}
@@ -0,0 +1,137 @@
using DbusSmsForward.Helper;
using DbusSmsForward.SettingModel;
using DbusSmsForward.SMSModel;
using System.Text.RegularExpressions;
namespace DbusSmsForward.ProcessSmsContent
{
public static class GetSmsContentCode
{
public static string GetSmsCodeStr(string smscontent)
{
SmsCodeModel smsCodeModel = new SmsCodeModel();
smsCodeModel = GetSmsCodeModel(smscontent);
if (smsCodeModel.HasCode)
{
return smsCodeModel.CodeFrom + smsCodeModel.CodeValue;
}
else
{
return "";
}
}
public static SmsCodeModel GetSmsCodeModel(string smscontent)
{
SmsCodeModel smsCodeModel = new SmsCodeModel();
string newsmscontent=string.Empty;
if (JudgeSmsContentHasCode(smscontent,out newsmscontent))
{
string smscode= GetCode(newsmscontent).Trim();
if (string.IsNullOrEmpty(smscode))
{
smsCodeModel.HasCode = false;
}
else
{
smsCodeModel.HasCode = true;
smsCodeModel.CodeValue = smscode;
smsCodeModel.CodeFrom = GetCodeSmsFrom(smscontent);
}
}
else
{
smsCodeModel.HasCode = false;
}
return smsCodeModel;
}
public static bool JudgeSmsContentHasCode(string smscontent,out string newsmscontent)
{
appsettingsModel result = new appsettingsModel();
ConfigHelper.GetSettings(ref result);
string codeKeyStr = result.appSettings.SmsCodeKey;
result = null;
string[] flagStrList = {};
try
{
flagStrList = codeKeyStr.Split("±");
}
catch (Exception ex)
{
Console.WriteLine("配置文件中自定义验证码关键词格式有误,已使用默认关键词“验证码”");
flagStrList.Append("验证码");
}
foreach (string flag in flagStrList)
{
if (smscontent.IndexOf(flag) > -1)
{
newsmscontent = smscontent.Replace(flag, " "+flag+" ");
return true;
}
}
newsmscontent=string.Empty;
return false;
}
public static string GetCode(string smscontent)
{
string pattern = @"\b[A-Za-z0-9]{4,7}\b";
MatchCollection SmsCodeMatches = Regex.Matches(smscontent, pattern);
if (SmsCodeMatches.Count()> 1)
{
int maxDigits = 0;
string maxDigitsString = "";
foreach (Match match in SmsCodeMatches)
{
string currentString = match.Value;
int digitCount = CountDigits(currentString);
if (digitCount > maxDigits)
{
maxDigits = digitCount;
maxDigitsString = currentString;
}
}
return maxDigitsString;
}
else if (SmsCodeMatches.Count()==1)
{
return SmsCodeMatches[0].Value;
}
else
{
return "";
}
}
public static string GetCodeSmsFrom(string smscontent)
{
string pattern = @"^\【(.*?)\】";
Match match = Regex.Match(smscontent, pattern);
if (match.Success)
{
return match.Value;
}
else
{
string pattern1 = @"【([^【】]+)】$";
Match match1 = Regex.Match(smscontent, pattern1);
if (match1.Success)
{
return match1.Value;
}
}
return "";
}
public static int CountDigits(string input)
{
int count = 0;
foreach (char c in input)
{
if (char.IsDigit(c))
{
count++;
}
}
return count;
}
}
}
+112
View File
@@ -0,0 +1,112 @@
using DbusSmsForward.SendMethod;
using DbusSmsForward.SMSModel;
using System.Collections.Generic;
namespace DbusSmsForward.ProcessUserChoise
{
public static class ProcessChoise
{
public static string onStartGuide(string chooseOption)
{
if (string.IsNullOrEmpty(chooseOption))
{
Console.WriteLine("请选择运行模式:1为短信转发模式,2为发短信模式");
chooseOption = Console.ReadLine();
}
if (chooseOption == "1" || chooseOption == "2")
{
return chooseOption;
}
else
{
Console.WriteLine("请输入1或2");
return onStartGuide("");
}
}
public static List<Action<SmsContentModel, string, string>> sendMethodGuide(List<string> chooseOptions)
{
if(chooseOptions.Count() == 0)
{
Console.WriteLine("请选择转发渠道:1.邮箱转发,2.pushplus转发,3.企业微信转发,4.TG机器人转发,5.钉钉转发,6.Bark转发,7.自定义shell转发,同时转发多渠道请以空格分割编号(举例:1 2 3 5)");
chooseOptions.Add(Console.ReadLine());
return sendMethodGuide(chooseOptions);
}
else if (chooseOptions.Count() >= 1)
{
if (chooseOptions.Count() == 1 && chooseOptions[0].IndexOf(" ") > -1)
{
string chooseOption = chooseOptions[0];
List<string> newChooseOptions = chooseOption.Split(" ").ToList();
return sendMethodGuide(newChooseOptions);
}
else
{
if (JudgeChooseIsValid(chooseOptions,true))
{
List<Action<SmsContentModel, string, string>> actions = new List<Action<SmsContentModel, string, string>>();
foreach (var chooseOption in chooseOptions)
{
if (chooseOption == "1")
{
SendByEmail.SetupEmailInfo();
actions.Add(SendByEmail.SendSms);
}
if (chooseOption == "2")
{
SendByPushPlus.SetupPushPlusInfo();
actions.Add(SendByPushPlus.SendSms);
}
if (chooseOption == "3")
{
SendByWeComApplication.SetupWeComInfo();
actions.Add(SendByWeComApplication.SendSms);
}
if (chooseOption == "4")
{
SendByTelegramBot.SetupTGBotInfo();
actions.Add(SendByTelegramBot.SendSms);
}
if (chooseOption == "5")
{
SendByDingTalkBot.SetupDingtalkBotMsg();
actions.Add(SendByDingTalkBot.SendSms);
}
if (chooseOption == "6")
{
SendByBark.SetupBarkInfo();
actions.Add(SendByBark.SendSms);
}
if (chooseOption == "7")
{
SendByShell.SetupCustomShellInfo();
actions.Add(SendByShell.SendSms);
}
}
return actions;
}
else
{
Console.WriteLine("请输入1或2或3或4或5或6或7");
return sendMethodGuide(new List<string>());
}
}
}
return new List<Action<SmsContentModel, string, string>>();
}
public static bool JudgeChooseIsValid(List<string> chooseOptions,bool isCheckAll=false)
{
if (isCheckAll)
{
return chooseOptions.Where(a => a == "1" || a == "2" || a == "3" || a == "4" || a == "5" || a == "6" || a == "7").Count() == chooseOptions.Count();
}
else
{
return chooseOptions.Where(a => a == "1" || a == "2" || a == "3" || a == "4" || a == "5" || a == "6" || a == "7").Count() > 0;
}
}
}
}
+73
View File
@@ -0,0 +1,73 @@
using DbusSmsForward.Helper;
using DbusSmsForward.Models;
using DbusSmsForward.ModemHelper;
using DbusSmsForward.Notifications;
using DbusSmsForward.Services;
using DbusSmsForward.SettingModel;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Text.Json;
AppCommand command = CommandLineParser.Parse(args);
if (command.PrintSampleConfig)
{
DbusSmsForwardOptions sample = SampleConfigFactory.Create();
Console.WriteLine(JsonSerializer.Serialize(sample, SourceGenerationContext.Default.DbusSmsForwardOptions));
return;
}
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Configuration.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false);
builder.Services.Configure<DbusSmsForwardOptions>(builder.Configuration);
builder.Services.AddSingleton(sp =>
{
DbusSmsForwardOptions options = new();
builder.Configuration.Bind(options);
LegacyConfigurationBridge.Apply(builder.Configuration, options);
CommandLineConfigurationApplier.Apply(command, options);
return options;
});
builder.Services.AddSingleton<ModemManagerHelper>();
builder.Services.AddSingleton<NotificationDispatcher>();
builder.Services.AddSingleton<INotificationSender, EmailNotificationSender>();
builder.Services.AddSingleton<INotificationSender, PushPlusNotificationSender>();
builder.Services.AddSingleton<INotificationSender, BarkNotificationSender>();
builder.Services.AddSingleton<INotificationSender, TelegramNotificationSender>();
builder.Services.AddSingleton<INotificationSender, DingTalkNotificationSender>();
builder.Services.AddSingleton<INotificationSender, WeComApplicationNotificationSender>();
builder.Services.AddSingleton<INotificationSender, ShellNotificationSender>();
builder.Services.AddSingleton<INotificationSender, AutmanPushNotificationSender>();
builder.Services.AddHostedService<SmsForwardWorker>();
builder.Logging.ClearProviders();
builder.Logging.AddSimpleConsole();
using IHost host = builder.Build();
DbusSmsForwardOptions resolvedOptions = host.Services.GetRequiredService<DbusSmsForwardOptions>();
if (command.ValidateConfig || resolvedOptions.Daemon.ValidateOnStartup)
{
ConfigurationValidator.Validate(resolvedOptions, command);
}
if (string.Equals(command.Name, "send", StringComparison.OrdinalIgnoreCase))
{
await SendSmsAsync(host.Services, command);
return;
}
await host.RunAsync();
static async Task SendSmsAsync(IServiceProvider services, AppCommand command)
{
if (string.IsNullOrWhiteSpace(command.PhoneNumber) || string.IsNullOrWhiteSpace(command.Message))
{
throw new InvalidOperationException("send 模式需要 --to 和 --text 参数。 ");
}
ModemManagerHelper modemManagerHelper = services.GetRequiredService<ModemManagerHelper>();
bool success = await modemManagerHelper.SendSms(command.PhoneNumber, command.Message);
Console.WriteLine(success ? "短信已发送" : "短信发送失败");
}
+15
View File
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DbusSmsForward.SMSModel
{
public class SmsCodeModel
{
public bool HasCode { get; set; }=false;
public string CodeFrom { get; set; }=string.Empty;
public string CodeValue { get; set; } = string.Empty;
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace DbusSmsForward.SMSModel;
public sealed class SmsContentModel
{
public string TelNumber { get; set; } = string.Empty;
public string SmsContent { get; set; } = string.Empty;
public string SmsDate { get; set; } = string.Empty;
}
+68
View File
@@ -0,0 +1,68 @@
using DbusSmsForward.Helper;
using DbusSmsForward.ProcessSmsContent;
using DbusSmsForward.SettingModel;
using DbusSmsForward.SMSModel;
using System.Text.Json;
namespace DbusSmsForward.SendMethod
{
public static class SendByBark
{
public static void SetupBarkInfo()
{
appsettingsModel result = new appsettingsModel();
ConfigHelper.GetSettings(ref result);
string BarkUrl = result.appSettings.BarkConfig.BarkUrl;
string BrakKey = result.appSettings.BarkConfig.BarkKey;
if (string.IsNullOrEmpty(BarkUrl) && string.IsNullOrEmpty(BrakKey))
{
Console.WriteLine("首次运行请输入Bark服务器地址:");
BarkUrl = Console.ReadLine().Trim();
result.appSettings.BarkConfig.BarkUrl = BarkUrl;
Console.WriteLine("首次运行请输入Bark推送key");
BrakKey = Console.ReadLine().Trim();
result.appSettings.BarkConfig.BarkKey = BrakKey;
ConfigHelper.UpdateSettings(ref result);
}
result = null;
}
public static void SendSms(SmsContentModel smsmodel, string body, string devicename)
{
try
{
appsettingsModel result = new appsettingsModel();
ConfigHelper.GetSettings(ref result);
string BarkUrl = result.appSettings.BarkConfig.BarkUrl;
string BrakKey = result.appSettings.BarkConfig.BarkKey;
result = null;
//string SmsCodeStr = GetSmsContentCode.GetSmsCodeStr(smsmodel.SmsContent);
SmsCodeModel codeResult = GetSmsContentCode.GetSmsCodeModel(smsmodel.SmsContent);
string url = BarkUrl + "/" + BrakKey + "/";
url += System.Web.HttpUtility.UrlEncode(body);
url += "?group=" + smsmodel.TelNumber + "&title="+ (string.IsNullOrEmpty(codeResult.CodeValue) ? "" : codeResult.CodeValue + " ") + "短信转发" + smsmodel.TelNumber+(string.IsNullOrEmpty(codeResult.CodeValue) ?"":"&autoCopy=1&copy="+ codeResult.CodeValue);
string msgresult = HttpHelper.HttpGet(url);
//JsonObject jsonObjresult = JObject.Parse(msgresult);
var jsonObjresult = JsonSerializer.Deserialize(msgresult, SourceGenerationContext.Default.JsonObject);
string status = jsonObjresult["code"].ToString();
if (status == "200")
{
Console.WriteLine("Bark转发成功");
}
else
{
Console.WriteLine(jsonObjresult["code"].ToString());
Console.WriteLine(jsonObjresult["message"].ToString());
}
}
catch(Exception ex)
{
Console.WriteLine("SendByBarkError:\n"+ex);
}
}
}
}
+105
View File
@@ -0,0 +1,105 @@
using DbusSmsForward.Helper;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using DbusSmsForward.SMSModel;
using DbusSmsForward.ProcessSmsContent;
using System.Text.Json;
using System.Text.Json.Nodes;
using DbusSmsForward.SettingModel;
namespace DbusSmsForward.SendMethod
{
public class SendByDingTalkBot
{
private const string DING_TALK_BOT_URL = "https://oapi.dingtalk.com/robot/send?access_token=";
public static void SetupDingtalkBotMsg()
{
appsettingsModel result = new appsettingsModel();
ConfigHelper.GetSettings(ref result);
string DingTalkAccessToken = result.appSettings.DingTalkConfig.DingTalkAccessToken;
string DingTalkSecret = result.appSettings.DingTalkConfig.DingTalkSecret;
if (string.IsNullOrEmpty(DingTalkAccessToken) && string.IsNullOrEmpty(DingTalkSecret))
{
Console.WriteLine("首次运行请输入钉钉机器人AccessToken");
DingTalkAccessToken = Console.ReadLine().Trim();
result.appSettings.DingTalkConfig.DingTalkAccessToken = DingTalkAccessToken;
Console.WriteLine("请输入钉钉机器人加签secret");
DingTalkSecret = Console.ReadLine().Trim();
result.appSettings.DingTalkConfig.DingTalkSecret = DingTalkSecret;
ConfigHelper.UpdateSettings(ref result);
}
result = null;
}
public static void SendSms(SmsContentModel smsmodel, string body, string devicename)
{
try
{
appsettingsModel result = new appsettingsModel();
ConfigHelper.GetSettings(ref result);
string dingTalkAccessToken = result.appSettings.DingTalkConfig.DingTalkAccessToken;
string dingTalkSecret = result.appSettings.DingTalkConfig.DingTalkSecret;
result = null;
string url = DING_TALK_BOT_URL + dingTalkAccessToken;
//string SmsCodeStr = GetSmsContentCode.GetSmsCodeStr(smsmodel.SmsContent);
SmsCodeModel codeResult = GetSmsContentCode.GetSmsCodeModel(smsmodel.SmsContent);
long timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds();
string sign = addSign(timestamp, dingTalkSecret);
url += $"&timestamp={timestamp}&sign={sign}";
JsonObject msgContent = new()
{
{ "content", (string.IsNullOrEmpty(codeResult.CodeValue) ? "" : codeResult.CodeValue + "\n") + "短信转发\n" + body }
};
JsonObject msgObj = new()
{
{ "msgtype", "text" },
{ "text", msgContent }
};
string resultResp = HttpHelper.Post(url, msgObj);
//JObject jsonObjresult = JObject.Parse(resultResp);
JsonObject jsonObjresult = JsonSerializer.Deserialize(resultResp, SourceGenerationContext.Default.JsonObject);
string errcode1 = jsonObjresult["errcode"].ToString();
string errmsg1 = jsonObjresult["errmsg"].ToString();
if (errcode1 == "0" && errmsg1 == "ok")
{
Console.WriteLine("钉钉转发成功");
}
else
{
Console.WriteLine(errmsg1);
}
}
catch (Exception ex)
{
Console.WriteLine("SendByDingTalkBotError:\n" + ex);
}
}
public static string addSign(long timestamp ,string secret)
{
string secret1 = secret;
string stringToSign = timestamp + "\n" + secret1;
var encoding = new ASCIIEncoding();
byte[] keyByte = encoding.GetBytes(secret1);
byte[] messageBytes = encoding.GetBytes(stringToSign);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
return HttpUtility.UrlEncode(Convert.ToBase64String(hashmessage), Encoding.UTF8);
}
}
public static string Base64Encrypt(string input, Encoding encode)
{
return Convert.ToBase64String(encode.GetBytes(input));
}
}
}
+99
View File
@@ -0,0 +1,99 @@
using DbusSmsForward.SMSModel;
using DbusSmsForward.ProcessSmsContent;
using DbusSmsForward.Helper;
using DbusSmsForward.SettingModel;
using MimeKit;
using MailKit.Net.Smtp;
namespace DbusSmsForward.SendMethod
{
public static class SendByEmail
{
public static void SetupEmailInfo()
{
appsettingsModel result = new appsettingsModel();
ConfigHelper.GetSettings(ref result);
string smtpHost = result.appSettings.EmailConfig.smtpHost;
string smtpPort = result.appSettings.EmailConfig.smtpPort;
string emailKey = result.appSettings.EmailConfig.emailKey;
string sendEmail = result.appSettings.EmailConfig.sendEmail;
string reciveEmail = result.appSettings.EmailConfig.reciveEmail;
if (string.IsNullOrEmpty(smtpHost) && string.IsNullOrEmpty(smtpPort) && string.IsNullOrEmpty(emailKey) && string.IsNullOrEmpty(sendEmail) && string.IsNullOrEmpty(reciveEmail))
{
Console.WriteLine("首次运行请输入邮箱转发相关配置信息\n请输入smtp地址:");
smtpHost = Console.ReadLine().Trim();
result.appSettings.EmailConfig.smtpHost = smtpHost;
Console.WriteLine("请输入smtp端口:");
smtpPort = Console.ReadLine().Trim();
result.appSettings.EmailConfig.smtpPort = smtpPort;
string sslEnableInput = string.Empty;
do
{
Console.WriteLine("是否需要启用ssl(1.启用 2.不启用)");
sslEnableInput = Console.ReadLine().Trim();
} while (!(sslEnableInput == "1" || sslEnableInput == "2"));
if (sslEnableInput == "1")
{
result.appSettings.EmailConfig.enableSSL = "true";
}
else
{
result.appSettings.EmailConfig.enableSSL = "false";
}
Console.WriteLine("请输入邮箱密钥:");
emailKey = Console.ReadLine().Trim();
result.appSettings.EmailConfig.emailKey = emailKey;
Console.WriteLine("请输入发件邮箱:");
sendEmail = Console.ReadLine().Trim();
result.appSettings.EmailConfig.sendEmail = sendEmail;
Console.WriteLine("请输入收件邮箱:");
reciveEmail = Console.ReadLine().Trim();
result.appSettings.EmailConfig.reciveEmail = reciveEmail;
ConfigHelper.UpdateSettings(ref result);
}
result = null;
}
public static void SendSms(SmsContentModel smsmodel, string body, string devicename)
{
try
{
appsettingsModel result = new appsettingsModel();
ConfigHelper.GetSettings(ref result);
string smtpHost = result.appSettings.EmailConfig.smtpHost;
string smtpPort = result.appSettings.EmailConfig.smtpPort;
bool enableSSL = Convert.ToBoolean(result.appSettings.EmailConfig.enableSSL);
string emailKey = result.appSettings.EmailConfig.emailKey;
string sendEmail = result.appSettings.EmailConfig.sendEmail;
string reciveEmail = result.appSettings.EmailConfig.reciveEmail;
result = null;
var message = new MimeMessage();
message.From.Add(new MailboxAddress("SMSForwad", sendEmail));
message.To.Add(new MailboxAddress("", reciveEmail));
//string SmsCodeStr = GetSmsContentCode.GetSmsCodeStr(smsmodel.SmsContent);
SmsCodeModel codeResult = GetSmsContentCode.GetSmsCodeModel(smsmodel.SmsContent);
message.Subject = (string.IsNullOrEmpty(codeResult.CodeValue) ? "" : codeResult.CodeValue + " ") + "短信转发" + smsmodel.TelNumber;
message.Body = new TextPart("plain")
{
Text = body
};
using (var client = new SmtpClient())
{
client.Connect(smtpHost, Convert.ToInt32(smtpPort), enableSSL);
client.Authenticate(sendEmail, emailKey);
client.Send(message);
client.Disconnect(true);
}
}
catch (Exception ex)
{
Console.WriteLine("SendByEmailError:\n" + ex);
}
}
}
}
+66
View File
@@ -0,0 +1,66 @@
using DbusSmsForward.Helper;
using DbusSmsForward.ProcessSmsContent;
using DbusSmsForward.SettingModel;
using DbusSmsForward.SMSModel;
using System.Text.Json;
using System.Text.Json.Nodes;
namespace DbusSmsForward.SendMethod
{
public static class SendByPushPlus
{
public static void SetupPushPlusInfo()
{
appsettingsModel result = new appsettingsModel();
ConfigHelper.GetSettings(ref result);
string pushPlusToken = result.appSettings.PushPlusConfig.pushPlusToken;
if (string.IsNullOrEmpty(pushPlusToken))
{
Console.WriteLine("首次运行请输入PushPlusToken");
pushPlusToken = Console.ReadLine().Trim();
result.appSettings.PushPlusConfig.pushPlusToken = pushPlusToken;
ConfigHelper.UpdateSettings(ref result);
}
result = null;
}
public static void SendSms(SmsContentModel smsmodel, string body, string devicename)
{
try
{
appsettingsModel result = new appsettingsModel();
ConfigHelper.GetSettings(ref result);
string pushPlusToken = result.appSettings.PushPlusConfig.pushPlusToken;
result = null;
string pushPlusUrl = "http://www.pushplus.plus/send/";
//string SmsCodeStr = GetSmsContentCode.GetSmsCodeStr(smsmodel.SmsContent);
SmsCodeModel codeResult = GetSmsContentCode.GetSmsCodeModel(smsmodel.SmsContent);
JsonObject obj = new JsonObject();
obj.Add("token", pushPlusToken);
obj.Add("title", (string.IsNullOrEmpty(codeResult.CodeValue) ? "" : codeResult.CodeValue + " ") + "短信转发" + smsmodel.TelNumber);
obj.Add("content", body);
obj.Add("topic", "");
string msgresult = HttpHelper.Post(pushPlusUrl, obj);
//JObject jsonObjresult = JObject.Parse(msgresult);
JsonObject jsonObjresult = JsonSerializer.Deserialize(msgresult, SourceGenerationContext.Default.JsonObject);
string code = jsonObjresult["code"].ToString();
string errmsg = jsonObjresult["msg"].ToString();
if (code == "200")
{
Console.WriteLine("pushplus转发成功");
}
else
{
Console.WriteLine(errmsg);
}
}
catch (Exception ex)
{
Console.WriteLine("SetupPushPlusInfoError:\n" + ex);
}
}
}
}
+125
View File
@@ -0,0 +1,125 @@
using DbusSmsForward.Helper;
using DbusSmsForward.ProcessSmsContent;
using DbusSmsForward.SettingModel;
using DbusSmsForward.SMSModel;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Xml;
namespace DbusSmsForward.SendMethod
{
public static class SendByShell
{
public static void SetupCustomShellInfo()
{
appsettingsModel result = new appsettingsModel();
ConfigHelper.GetSettings(ref result);
string shellPath = result.appSettings.ShellConfig.ShellPath;
if (string.IsNullOrEmpty(shellPath))
{
Console.WriteLine("首次运行请输入自定义shell脚本路径:");
shellPath = Console.ReadLine().Trim();
result.appSettings.ShellConfig.ShellPath = shellPath;
ConfigHelper.UpdateSettings(ref result);
}
result = null;
}
public static void SendSms(SmsContentModel smsmodel, string body, string devicename)
{
try
{
appsettingsModel result = new appsettingsModel();
ConfigHelper.GetSettings(ref result);
string shellPath = result.appSettings.ShellConfig.ShellPath;
result = null;
SmsCodeModel codeResult = GetSmsContentCode.GetSmsCodeModel(smsmodel.SmsContent);
string[] arguments =
{
string.IsNullOrEmpty(smsmodel.TelNumber)?"":smsmodel.TelNumber,
string.IsNullOrEmpty(smsmodel.SmsDate)?"":smsmodel.SmsDate,
string.IsNullOrEmpty(smsmodel.SmsContent)?"":smsmodel.SmsContent,
string.IsNullOrEmpty(codeResult.CodeValue)?"":codeResult.CodeValue,
string.IsNullOrEmpty(codeResult.CodeFrom)?"":codeResult.CodeFrom,
string.IsNullOrEmpty(devicename)?"":devicename,
};
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = shellPath,
UseShellExecute = false, // 不直接使用操作系统shell启动
CreateNoWindow = true, // 不创建新窗口
RedirectStandardOutput = true, // 可以重定向输出
RedirectStandardError = true // 可以重定向错误
};
foreach (var arg in arguments)
{
startInfo.ArgumentList.Add(arg);
}
// 启动进程
using (Process process = new Process())
{
process.StartInfo = startInfo;
// 可以捕获输出(如果需要)
process.OutputDataReceived += (sender, e) =>
{
if (!string.IsNullOrEmpty(e.Data))
Console.WriteLine($"输出: {e.Data}");
};
process.ErrorDataReceived += (sender, e) =>
{
if (!string.IsNullOrEmpty(e.Data))
Console.WriteLine($"错误: {e.Data}");
};
bool started = process.Start();
if (started)
{
// 开始异步读取输出
process.BeginOutputReadLine();
process.BeginErrorReadLine();
// 等待进程完成(可以设置超时时间)
bool exited = process.WaitForExit(30000); // 30秒超时
if (exited)
{
int exitCode = process.ExitCode;
if (exitCode == 0)
{
Console.WriteLine("\nShell调用成功");
}
else
{
Console.WriteLine($"\nShell调用失败,退出代码: {exitCode}");
}
}
else
{
Console.WriteLine("\nShell调用超时");
process.Kill(); // 强制终止进程
}
}
else
{
Console.WriteLine("\n无法启动Shell进程");
}
}
}
catch (Exception ex)
{
Console.WriteLine("SendByShellError:\n" + ex);
}
}
}
}
+100
View File
@@ -0,0 +1,100 @@
using DbusSmsForward.Helper;
using DbusSmsForward.ProcessSmsContent;
using DbusSmsForward.SettingModel;
using DbusSmsForward.SMSModel;
using System.Text.Json;
using System.Text.Json.Nodes;
namespace DbusSmsForward.SendMethod
{
public static class SendByTelegramBot
{
public static void SetupTGBotInfo()
{
appsettingsModel result = new appsettingsModel();
ConfigHelper.GetSettings(ref result);
string TGBotToken = result.appSettings.TGBotConfig.TGBotToken;
string TGBotChatID = result.appSettings.TGBotConfig.TGBotChatID;
string IsEnableCustomTGBotApi = result.appSettings.TGBotConfig.IsEnableCustomTGBotApi;
string CustomTGBotApi = result.appSettings.TGBotConfig.CustomTGBotApi;
if (string.IsNullOrEmpty(TGBotToken) || string.IsNullOrEmpty(TGBotChatID) || string.IsNullOrEmpty(IsEnableCustomTGBotApi))
{
Console.WriteLine("首次运行请输入TG机器人Token");
TGBotToken = Console.ReadLine().Trim();
result.appSettings.TGBotConfig.TGBotToken = TGBotToken;
Console.WriteLine("请输入机器人要转发到的ChatId");
TGBotChatID = Console.ReadLine().Trim();
result.appSettings.TGBotConfig.TGBotChatID = TGBotChatID;
string customApiEnableInput=string.Empty;
do
{
Console.WriteLine("是否需要使用自定义api(1.使用 2.不使用)");
customApiEnableInput= Console.ReadLine().Trim();
} while (!(customApiEnableInput=="1"|| customApiEnableInput == "2"));
if(customApiEnableInput=="1")
{
IsEnableCustomTGBotApi = "true";
result.appSettings.TGBotConfig.IsEnableCustomTGBotApi = IsEnableCustomTGBotApi;
Console.WriteLine("请输入机器人自定义api(格式https://xxx.abc.com)");
CustomTGBotApi= Console.ReadLine().Trim();
result.appSettings.TGBotConfig.CustomTGBotApi = CustomTGBotApi;
}
else
{
IsEnableCustomTGBotApi = "false";
result.appSettings.TGBotConfig.IsEnableCustomTGBotApi = IsEnableCustomTGBotApi;
}
ConfigHelper.UpdateSettings(ref result);
}
result = null;
}
public static void SendSms(SmsContentModel smsmodel, string body, string devicename)
{
try
{
appsettingsModel result = new appsettingsModel();
ConfigHelper.GetSettings(ref result);
string TGBotToken = result.appSettings.TGBotConfig.TGBotToken;
string TGBotChatID = result.appSettings.TGBotConfig.TGBotChatID;
string IsEnableCustomTGBotApi = result.appSettings.TGBotConfig.IsEnableCustomTGBotApi;
string CustomTGBotApi = result.appSettings.TGBotConfig.CustomTGBotApi;
result = null;
//string SmsCodeStr = GetSmsContentCode.GetSmsCodeStr(smsmodel.SmsContent);
SmsCodeModel codeResult = GetSmsContentCode.GetSmsCodeModel(smsmodel.SmsContent);
string url = "";
if (IsEnableCustomTGBotApi == "true")
{
url = CustomTGBotApi;
}
else
{
url = "https://api.telegram.org";
}
url += "/bot" + TGBotToken + "/sendMessage?chat_id=" + TGBotChatID + "&text=";
url += System.Web.HttpUtility.UrlEncode((string.IsNullOrEmpty(codeResult.CodeValue) ? "" : codeResult.CodeValue + "\n") + "短信转发\n" + body);
string msgresult = HttpHelper.HttpGet(url);
JsonObject jsonObjresult = JsonSerializer.Deserialize(msgresult, SourceGenerationContext.Default.JsonObject);
string status = jsonObjresult["ok"].ToString();
if (status.ToLower() == "true")
{
Console.WriteLine("TGBot转发成功");
}
else
{
Console.WriteLine(jsonObjresult["error_code"].ToString());
Console.WriteLine(jsonObjresult["description"].ToString());
}
}
catch (Exception ex)
{
Console.WriteLine("SendByTelegramBotError:\n" + ex);
}
}
}
}
@@ -0,0 +1,103 @@
using DbusSmsForward.Helper;
using DbusSmsForward.ProcessSmsContent;
using DbusSmsForward.SettingModel;
using DbusSmsForward.SMSModel;
using System.Text.Json;
using System.Text.Json.Nodes;
namespace DbusSmsForward.SendMethod
{
public class SendByWeComApplication
{
public static void SetupWeComInfo()
{
appsettingsModel result = new appsettingsModel();
ConfigHelper.GetSettings(ref result);
string corpid = result.appSettings.WeComApplicationConfig.WeChatQYID;
string appsecret = result.appSettings.WeComApplicationConfig.WeChatQYApplicationSecret;
string appid = result.appSettings.WeComApplicationConfig.WeChatQYApplicationID;
if (string.IsNullOrEmpty(corpid) && string.IsNullOrEmpty(appsecret) && string.IsNullOrEmpty(appid))
{
Console.WriteLine("首次运行请输入企业ID");
corpid = Console.ReadLine().Trim();
result.appSettings.WeComApplicationConfig.WeChatQYID = corpid;
Console.WriteLine("请输入自建应用ID");
appid = Console.ReadLine().Trim();
result.appSettings.WeComApplicationConfig.WeChatQYApplicationID = appid;
Console.WriteLine("请输入自建应用密钥:");
appsecret = Console.ReadLine().Trim();
result.appSettings.WeComApplicationConfig.WeChatQYApplicationSecret = appsecret;
ConfigHelper.UpdateSettings(ref result);
}
result = null;
}
public static void SendSms(SmsContentModel smsmodel, string body, string devicename)
{
try
{
appsettingsModel configResult = new appsettingsModel();
ConfigHelper.GetSettings(ref configResult);
string corpid = configResult.appSettings.WeComApplicationConfig.WeChatQYID;
string corpsecret = configResult.appSettings.WeComApplicationConfig.WeChatQYApplicationSecret;
string agentid = configResult.appSettings.WeComApplicationConfig.WeChatQYApplicationID;
configResult = null;
string url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=" + corpid + "&corpsecret=" + corpsecret;
string result = HttpHelper.HttpGet(url);
//JsonObject jsonObj = JsonObject.Parse(result);
JsonObject jsonObj = JsonSerializer.Deserialize(result, SourceGenerationContext.Default.JsonObject);
string errcode = jsonObj["errcode"].ToString();
string errmsg = jsonObj["errmsg"].ToString();
//string SmsCodeStr = GetSmsContentCode.GetSmsCodeStr(smsmodel.SmsContent);
SmsCodeModel codeResult = GetSmsContentCode.GetSmsCodeModel(smsmodel.SmsContent);
if (errcode == "0" && errmsg == "ok")
{
string access_token = jsonObj["access_token"].ToString();
JsonObject obj = new JsonObject();
JsonObject obj1 = new JsonObject();
obj.Add("touser", "@all");
obj.Add("toparty", "");
obj.Add("totag", "");
obj.Add("msgtype", "text");
obj.Add("agentid", agentid);
obj1.Add("content", (string.IsNullOrEmpty(codeResult.CodeValue) ? "" : codeResult.CodeValue + "\n") + "短信转发\n" + body);
obj.Add("text", obj1);
obj.Add("safe", 0);
obj.Add("enable_id_trans", 0);
obj.Add("enable_duplicate_check", 0);
obj.Add("duplicate_check_interval", 1800);
string msgurl = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + access_token;
string msgresult = HttpHelper.Post(msgurl, obj);
//JsonObject jsonObjresult = JsonObject.Parse(msgresult);
//JsonObject jsonObjresult = JsonSerializer.Deserialize<JsonObject>(msgresult);
JsonObject jsonObjresult = JsonSerializer.Deserialize(msgresult, SourceGenerationContext.Default.JsonObject);
string errcode1 = jsonObjresult["errcode"].ToString();
string errmsg1 = jsonObjresult["errmsg"].ToString();
if (errcode1 == "0" && errmsg1 == "ok")
{
Console.WriteLine("企业微信转发成功");
}
else
{
Console.WriteLine(errmsg1);
}
}
else
{
Console.WriteLine(errmsg);
}
}
catch (Exception ex)
{
Console.WriteLine("SendByWeComApplicationError:\n" + ex);
}
}
}
}
+93
View File
@@ -0,0 +1,93 @@
using DbusSmsForward.Helper;
using DbusSmsForward.Models;
namespace DbusSmsForward.Services;
public static class CommandLineParser
{
public static AppCommand Parse(string[] args)
{
if (args.Length == 0)
{
return new AppCommand { Name = "run" };
}
List<string> enabledChannels = [];
string commandName = "run";
string phoneNumber = string.Empty;
string message = string.Empty;
bool validateConfig = false;
bool printSampleConfig = false;
for (int i = 0; i < args.Length; i++)
{
string arg = args[i];
switch (arg)
{
case "run":
case "send":
case "validate-config":
case "print-sample-config":
commandName = arg;
break;
case "--to":
if (i + 1 < args.Length) phoneNumber = args[++i];
break;
case "--text":
if (i + 1 < args.Length) message = args[++i];
break;
case "-fE":
commandName = "run";
enabledChannels.Add("email");
break;
case "-fP":
commandName = "run";
enabledChannels.Add("pushplus");
break;
case "-fW":
commandName = "run";
enabledChannels.Add("wecom");
break;
case "-fT":
commandName = "run";
enabledChannels.Add("telegram");
break;
case "-fD":
commandName = "run";
enabledChannels.Add("dingtalk");
break;
case "-fB":
commandName = "run";
enabledChannels.Add("bark");
break;
case "-fS":
commandName = "run";
enabledChannels.Add("shell");
break;
case "-fA":
commandName = "run";
enabledChannels.Add("autmanpush");
break;
case "-sS":
commandName = "send";
break;
case "--validate-config":
validateConfig = true;
break;
case "--print-sample-config":
printSampleConfig = true;
break;
}
}
return new AppCommand
{
Name = commandName,
PhoneNumber = phoneNumber,
Message = message,
EnabledChannels = enabledChannels.Distinct(StringComparer.OrdinalIgnoreCase).ToArray(),
ValidateConfig = validateConfig || commandName == "validate-config",
PrintSampleConfig = printSampleConfig || commandName == "print-sample-config"
};
}
}
@@ -0,0 +1,21 @@
using DbusSmsForward.ProcessSmsContent;
using DbusSmsForward.SMSModel;
using DbusSmsForward.SettingModel;
namespace DbusSmsForward.Services;
public static class NotificationContentBuilder
{
public static string BuildBody(SmsContentModel sms, string deviceName)
=> $"发信电话:{sms.TelNumber}\n时间:{sms.SmsDate}\n转发设备:{deviceName}\n短信内容:{sms.SmsContent}";
public static SmsCodeModel ExtractCode(DbusSmsForwardOptions options, SmsContentModel sms)
{
if (!options.Forwarding.EnableSmsCodeExtraction)
{
return new SmsCodeModel();
}
return GetSmsContentCode.GetSmsCodeModel(sms.SmsContent);
}
}
@@ -0,0 +1,26 @@
using DbusSmsForward.Notifications;
using DbusSmsForward.SMSModel;
using Microsoft.Extensions.Logging;
namespace DbusSmsForward.Services;
public sealed class NotificationDispatcher(IEnumerable<INotificationSender> senders, ILogger<NotificationDispatcher> logger)
{
private readonly IReadOnlyList<INotificationSender> _senders = senders.Where(static s => s.IsEnabled).ToList();
public async Task DispatchAsync(SmsContentModel sms, string body, string deviceName, CancellationToken cancellationToken)
{
foreach (INotificationSender sender in _senders)
{
try
{
await sender.SendAsync(sms, body, deviceName, cancellationToken);
logger.LogInformation("Notification sender {Sender} completed for {PhoneNumber}", sender.Name, sms.TelNumber);
}
catch (Exception ex)
{
logger.LogError(ex, "Notification sender {Sender} failed for {PhoneNumber}", sender.Name, sms.TelNumber);
}
}
}
}
+23
View File
@@ -0,0 +1,23 @@
using DbusSmsForward.ModemHelper;
using DbusSmsForward.SMSModel;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace DbusSmsForward.Services;
public sealed class SmsForwardWorker(ModemManagerHelper modemManagerHelper, NotificationDispatcher dispatcher, ILogger<SmsForwardWorker> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
logger.LogInformation("SmsForwardWorker started.");
modemManagerHelper.SetSendMethodList(
[
(SmsContentModel sms, string body, string deviceName) =>
{
dispatcher.DispatchAsync(sms, body, deviceName, stoppingToken).GetAwaiter().GetResult();
}
]);
await Task.Delay(Timeout.Infinite, stoppingToken);
}
}
+14
View File
@@ -0,0 +1,14 @@
using DbusSmsForward.SettingModel;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace DbusSmsForward;
[JsonSourceGenerationOptions(WriteIndented = true, PropertyNameCaseInsensitive = true)]
[JsonSerializable(typeof(JsonObject))]
[JsonSerializable(typeof(DbusSmsForwardOptions))]
[JsonSerializable(typeof(LegacyAppsettingsModel))]
[JsonSerializable(typeof(appsettingsModel))]
internal partial class SourceGenerationContext : JsonSerializerContext
{
}
+40
View File
@@ -0,0 +1,40 @@
{
"appSettings": {
"EmailConfig": {
"smtpHost": "",
"smtpPort": "",
"enableSSL": "",
"emailKey": "",
"sendEmail": "",
"reciveEmail": ""
},
"PushPlusConfig": {
"pushPlusToken": ""
},
"DingTalkConfig": {
"DingTalkAccessToken": "",
"DingTalkSecret": ""
},
"BarkConfig": {
"BarkUrl": "",
"BarkKey": ""
},
"WeComApplicationConfig": {
"WeChatQYID": "",
"WeChatQYApplicationID": "",
"WeChatQYApplicationSecret": ""
},
"TGBotConfig": {
"IsEnableCustomTGBotApi": "",
"CustomTGBotApi": "",
"TGBotToken": "",
"TGBotChatID": ""
},
"ShellConfig": {
"ShellPath": ""
},
"SmsCodeKey": "验证码±verification±code±인증±代码±随机码",
"ForwardIgnoreStorageType": "sm",
"DeviceName": ""
}
}
+165
View File
@@ -0,0 +1,165 @@
using Tmds.DBus.Protocol;
namespace DbusSmsForward.SettingModel;
public sealed class appsettingsModel
{
public LegacyAppSettings appSettings { get; set; } = new();
}
public sealed class LegacyAppsettingsModel
{
public LegacyAppSettings appSettings { get; set; } = new();
}
public sealed class LegacyAppSettings
{
public EmailConfig EmailConfig { get; set; } = new();
public PushPlusConfig PushPlusConfig { get; set; } = new();
public DingTalkConfig DingTalkConfig { get; set; } = new();
public BarkConfig BarkConfig { get; set; } = new();
public WeComApplicationConfig WeComApplicationConfig { get; set; } = new();
public TelegramBotConfig TGBotConfig { get; set; } = new();
public ShellConfig ShellConfig { get; set; } = new();
public string SmsCodeKey { get; set; } = "验证码±verification±code±认证±代码±随机码";
public string ForwardIgnoreStorageType { get; set; } = "sm";
public string DeviceName { get; set; } = string.Empty;
}
public sealed class DbusSmsForwardOptions
{
public DaemonConfig Daemon { get; set; } = new();
public ModemConfig Modem { get; set; } = new();
public ForwardingConfig Forwarding { get; set; } = new();
public NotificationChannels Notifications { get; set; } = new();
public LegacyCompatibilityConfig LegacyCompatibility { get; set; } = new();
}
public sealed class DaemonConfig
{
public string Mode { get; set; } = "forward";
public bool ValidateOnStartup { get; set; } = true;
public bool UseSystemd { get; set; } = true;
public string LogLevel { get; set; } = "Information";
}
public sealed class ModemConfig
{
public string SystemBusAddress { get; set; } = Address.System ?? string.Empty;
public string ServiceName { get; set; } = "org.freedesktop.ModemManager1";
public string ManagerObjectPath { get; set; } = "/org/freedesktop/ModemManager1";
public bool ScanDevicesOnStartup { get; set; } = true;
}
public sealed class ForwardingConfig
{
public string DeviceName { get; set; } = string.Empty;
public string SmsCodeKey { get; set; } = "验证码±verification±code±认证±代码±随机码";
public string IgnoreStorageType { get; set; } = "sm";
public bool EnableSmsCodeExtraction { get; set; } = true;
public string[] EnabledChannels { get; set; } = [];
}
public sealed class NotificationChannels
{
public EmailConfig Email { get; set; } = new();
public PushPlusConfig PushPlus { get; set; } = new();
public DingTalkConfig DingTalk { get; set; } = new();
public BarkConfig Bark { get; set; } = new();
public WeComApplicationConfig WeComApplication { get; set; } = new();
public TelegramBotConfig TelegramBot { get; set; } = new();
public ShellConfig Shell { get; set; } = new();
public AutmanPushConfig AutmanPush { get; set; } = new();
}
public sealed class LegacyCompatibilityConfig
{
public bool EnableLegacyCliFlags { get; set; } = true;
public bool WriteLegacyAppSettings { get; set; } = false;
}
public class ChannelConfigBase
{
public bool Enabled { get; set; }
public int TimeoutSeconds { get; set; } = 15;
public bool SkipTlsValidation { get; set; }
}
public sealed class EmailConfig : ChannelConfigBase
{
public string SmtpHost { get; set; } = string.Empty;
public int SmtpPort { get; set; } = 465;
public bool EnableSsl { get; set; } = true;
public string EmailKey { get; set; } = string.Empty;
public string SendEmail { get; set; } = string.Empty;
public string ReceiveEmail { get; set; } = string.Empty;
public string smtpHost { get => SmtpHost; set => SmtpHost = value; }
public string smtpPort { get => SmtpPort.ToString(); set => SmtpPort = int.TryParse(value, out int parsed) ? parsed : 465; }
public string enableSSL { get => EnableSsl.ToString().ToLowerInvariant(); set => EnableSsl = bool.TryParse(value, out bool parsed) && parsed; }
public string emailKey { get => EmailKey; set => EmailKey = value; }
public string sendEmail { get => SendEmail; set => SendEmail = value; }
public string reciveEmail { get => ReceiveEmail; set => ReceiveEmail = value; }
}
public sealed class PushPlusConfig : ChannelConfigBase
{
public string Token { get; set; } = string.Empty;
public string Endpoint { get; set; } = "https://www.pushplus.plus/send";
public string pushPlusToken { get => Token; set => Token = value; }
}
public sealed class DingTalkConfig : ChannelConfigBase
{
public string AccessToken { get; set; } = string.Empty;
public string Secret { get; set; } = string.Empty;
public string Endpoint { get; set; } = "https://oapi.dingtalk.com/robot/send";
public string DingTalkAccessToken { get => AccessToken; set => AccessToken = value; }
public string DingTalkSecret { get => Secret; set => Secret = value; }
}
public sealed class BarkConfig : ChannelConfigBase
{
public string ServerUrl { get; set; } = string.Empty;
public string BarkKey { get; set; } = string.Empty;
public string BarkUrl { get => ServerUrl; set => ServerUrl = value; }
}
public sealed class WeComApplicationConfig : ChannelConfigBase
{
public string CorpId { get; set; } = string.Empty;
public string AgentId { get; set; } = string.Empty;
public string ApplicationSecret { get; set; } = string.Empty;
public string WeChatQYID { get => CorpId; set => CorpId = value; }
public string WeChatQYApplicationID { get => AgentId; set => AgentId = value; }
public string WeChatQYApplicationSecret { get => ApplicationSecret; set => ApplicationSecret = value; }
}
public sealed class TelegramBotConfig : ChannelConfigBase
{
public bool EnableCustomApi { get; set; }
public string CustomApi { get; set; } = string.Empty;
public string BotToken { get; set; } = string.Empty;
public string ChatId { get; set; } = string.Empty;
public string IsEnableCustomTGBotApi { get => EnableCustomApi.ToString().ToLowerInvariant(); set => EnableCustomApi = bool.TryParse(value, out bool parsed) && parsed; }
public string CustomTGBotApi { get => CustomApi; set => CustomApi = value; }
public string TGBotToken { get => BotToken; set => BotToken = value; }
public string TGBotChatID { get => ChatId; set => ChatId = value; }
}
public sealed class ShellConfig : ChannelConfigBase
{
public string ShellPath { get; set; } = string.Empty;
}
public sealed class AutmanPushConfig : ChannelConfigBase
{
public string ApiUrl { get; set; } = string.Empty;
public string AccessToken { get; set; } = string.Empty;
public string Method { get; set; } = "POST";
}