Telegram entegrasyonu

This commit is contained in:
Sedat ÖZTÜRK 2026-08-28 15:03:36 +03:00
parent 91de623a85
commit a670d2e966
31 changed files with 403 additions and 48 deletions

View file

@ -9,5 +9,5 @@ public static class NotificationChannels
public const string UiActivity = "UiActivity"; //UI Activity (ABP UI Activity)
public const string UiToast = "UiToast"; //UI Toast (ABP UI Toast) Ayarlar/Sistem/Bildirimler/Chrome açık olması gerekiyor.
public const string WhatsApp = "WhatsApp"; //WhatsApp (HTTP API, template-based)
// public const string Telegram = "Telegram";
public const string Telegram = "Telegram"; //Telegram (Bot API, chat based)
}

View file

@ -40,7 +40,7 @@ public class NotificationIdentifierProvider : INotificationIdentifierProvider
NotificationChannels.UiActivity => user.Id.ToString(),
NotificationChannels.UiToast => user.Id.ToString(),
NotificationChannels.WhatsApp => user.PhoneNumber,
// NotificationChannels.Telegram => user.PhoneNumber,
NotificationChannels.Telegram => user.GetProperty<string?>("TelegramChatId"),
_ => null,
};

View file

@ -5,6 +5,7 @@ using Sozsoft.Sender.Mail.AmazonSes;
using Sozsoft.Sender.Rocket;
using Sozsoft.Sender.Sms;
using Sozsoft.Sender.Sms.PostaGuvercini;
using Sozsoft.Sender.Telegram;
using Sozsoft.Sender.WhatsApp;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.Emailing;
@ -36,6 +37,7 @@ public class ErpSenderModule : AbpModule
context.Services.AddScoped<ISozsoftSmsSender>(sp => sp.GetRequiredService<PostaGuverciniSmsSender>());
context.Services.AddScoped<ISozsoftRocketSender>(sp => sp.GetRequiredService<RocketSender>());
context.Services.AddScoped<ISozsoftWhatsAppSender>(sp => sp.GetRequiredService<SozsoftWhatsAppSender>());
context.Services.AddScoped<ISozsoftTelegramSender>(sp => sp.GetRequiredService<SozsoftTelegramSender>());
context.Services.AddHttpClient<PostaGuverciniHttpClient>();
context.Services.AddScoped<IPostaGuverciniHttpClient>(sp => sp.GetRequiredService<PostaGuverciniHttpClient>());
@ -46,6 +48,9 @@ public class ErpSenderModule : AbpModule
context.Services.AddHttpClient<WhatsAppHttpClient>();
context.Services.AddScoped<IWhatsAppHttpClient>(sp => sp.GetRequiredService<WhatsAppHttpClient>());
context.Services.AddHttpClient<TelegramHttpClient>();
context.Services.AddScoped<ITelegramHttpClient>(sp => sp.GetRequiredService<TelegramHttpClient>());
}
}

View file

@ -0,0 +1,44 @@
using Microsoft.Extensions.Logging;
using Volo.Abp.DependencyInjection;
namespace Sozsoft.Sender.Telegram;
public interface ISozsoftTelegramSender : ITransientDependency
{
Task<TelegramSendResult> SendAsync(string ChatId, string Message);
}
public class SozsoftTelegramSender : ISozsoftTelegramSender
{
private readonly ITelegramHttpClient httpClient;
private readonly ILogger<SozsoftTelegramSender> logger;
public SozsoftTelegramSender(
ITelegramHttpClient httpClient,
ILogger<SozsoftTelegramSender> logger)
{
this.httpClient = httpClient;
this.logger = logger;
}
public async Task<TelegramSendResult> SendAsync(string ChatId, string Message)
{
logger.LogInformation("Sending Telegram message. {ChatId}, {Message}", ChatId, Message);
var result = await httpClient.SendMessageAsync(ChatId, Message);
var success = result?.ok == true;
if (success)
{
logger.LogInformation("Telegram sent successfully. {ChatId}, {Message}", ChatId, Message);
}
else
{
logger.LogWarning("Failed to send Telegram message. {ChatId}, {Message}, {ErrorCode}, {Description}",
ChatId, Message, result?.error_code, result?.description);
}
return new TelegramSendResult
{
Success = success,
};
}
}

View file

@ -0,0 +1,44 @@
using System.Net.Http.Json;
using Volo.Abp.Settings;
namespace Sozsoft.Sender.Telegram;
public interface ITelegramHttpClient
{
Task<TelegramSendResultDto> SendMessageAsync(string ChatId, string Message);
}
public sealed class TelegramHttpClient : ITelegramHttpClient, IDisposable
{
private readonly HttpClient httpClient;
private readonly ISettingProvider settingProvider;
public TelegramHttpClient(
HttpClient httpClient,
ISettingProvider settingProvider)
{
this.httpClient = httpClient;
this.settingProvider = settingProvider;
}
public async Task<TelegramSendResultDto> SendMessageAsync(string ChatId, string Message)
{
var url = await settingProvider.GetOrNullAsync(TelegramSettingNames.Url);
var botToken = await settingProvider.GetOrNullAsync(TelegramSettingNames.BotToken);
var parseMode = await settingProvider.GetOrNullAsync(TelegramSettingNames.ParseMode);
var response = await httpClient.PostAsJsonAsync($"{url.TrimEnd('/')}/bot{botToken}/sendMessage",
new TelegramPostMessageInput
{
chat_id = ChatId,
text = Message,
parse_mode = parseMode.IsNullOrWhiteSpace() ? null : parseMode,
disable_web_page_preview = true
});
// Bot API hatayı 4xx gövdesinde açıklar; durum kodunu fırlatmak açıklamayı kaybettirir.
return await response.Content.ReadFromJsonAsync<TelegramSendResultDto>();
}
public void Dispose() => httpClient?.Dispose();
}

View file

@ -0,0 +1,9 @@
namespace Sozsoft.Sender.Telegram;
public class TelegramPostMessageInput
{
public string chat_id { get; set; }
public string text { get; set; }
public string parse_mode { get; set; }
public bool disable_web_page_preview { get; set; }
}

View file

@ -0,0 +1,6 @@
namespace Sozsoft.Sender.Telegram;
public class TelegramSendResult
{
public bool Success { get; set; }
}

View file

@ -0,0 +1,23 @@
namespace Sozsoft.Sender.Telegram;
public class TelegramSendResultDto
{
public bool ok { get; set; }
public int? error_code { get; set; }
public string description { get; set; }
public Message result { get; set; }
public class Message
{
public long message_id { get; set; }
public int date { get; set; }
public Chat chat { get; set; }
public class Chat
{
public long id { get; set; }
public string type { get; set; }
public string username { get; set; }
}
}
}

View file

@ -0,0 +1,9 @@
namespace Sozsoft.Sender.Telegram;
public static class TelegramSettingNames
{
public const string Default = "App.Sender.Telegram.";
public const string Url = Default + "Url"; // https://api.telegram.org
public const string BotToken = Default + "BotToken"; // BotFather token: 123456:ABC-DEF...
public const string ParseMode = Default + "ParseMode"; // HTML | Markdown | MarkdownV2 | (bos)
}

View file

@ -38,6 +38,7 @@ public class UserInfoViewModel : ExtensibleObject
public bool ShouldChangePasswordOnNextLogin { get; set; }
public string Avatar { get; set; }
public string RocketUsername { get; set; }
public string TelegramChatId { get; set; }
public DateTimeOffset? CreationTime { get; set; }
public DateTimeOffset? LastModificationTime { get; set; }
public string WorkHour { get; set; }

View file

@ -260,6 +260,7 @@ public class PlatformIdentityAppService : ApplicationService
user.Surname = UserInfo.Surname;
user.SetPhoneNumber(UserInfo.PhoneNumber, user.PhoneNumberConfirmed);
user.SetRocketUsername(UserInfo.RocketUsername);
user.SetTelegramChatId(UserInfo.TelegramChatId);
user.SetWorkHour(UserInfo.WorkHour);
user.SetDepartmentId(UserInfo.DepartmentId);
user.SetJobPositionId(UserInfo.JobPositionId);

View file

@ -16,6 +16,7 @@ public static class UserInfoViewModelMappingExtensions
userInfoViewModel.LoginEndDate = user.GetLoginEndDate();
userInfoViewModel.Avatar = user.GetAvatar();
userInfoViewModel.RocketUsername = user.GetRocketUsername();
userInfoViewModel.TelegramChatId = user.GetTelegramChatId();
userInfoViewModel.WorkHour = user.GetWorkHour();
userInfoViewModel.DepartmentId = user.GetDepartmentId();
userInfoViewModel.JobPositionId = user.GetJobPositionId();

View file

@ -414,6 +414,58 @@
"selectOptions": {},
"order": 16
},
{
"code": "App.Sender.Telegram.Url",
"nameKey": "App.Sender.Telegram.Url",
"descriptionKey": "App.SenderUrl.UrlDescription",
"defaultValue": "https://api.telegram.org",
"isVisibleToClients": false,
"providers": "T|G|D",
"isInherited": false,
"isEncrypted": false,
"mainGroupKey": "App.Sender",
"subGroupKey": "App.Sender.Telegram",
"requiredPermissionName": "App.Sender.Telegram",
"dataType": "Text",
"selectOptions": {},
"order": 17
},
{
"code": "App.Sender.Telegram.BotToken",
"nameKey": "App.Sender.Telegram.BotToken",
"descriptionKey": "App.TelegramBotToken.BotTokenDescription",
"defaultValue": "8783091157:AAHlTHofhUmQtAd3JwPShkGBbL_JwPJjI7c",
"isVisibleToClients": false,
"providers": "T|G|D",
"isInherited": false,
"isEncrypted": true,
"mainGroupKey": "App.Sender",
"subGroupKey": "App.Sender.Telegram",
"requiredPermissionName": "App.Sender.Telegram",
"dataType": "Text",
"selectOptions": {},
"order": 18
},
{
"code": "App.Sender.Telegram.ParseMode",
"nameKey": "App.Sender.Telegram.ParseMode",
"descriptionKey": "App.TelegramParseMode.ParseModeDescription",
"defaultValue": "HTML",
"isVisibleToClients": false,
"providers": "T|G|D",
"isInherited": false,
"isEncrypted": false,
"mainGroupKey": "App.Sender",
"subGroupKey": "App.Sender.Telegram",
"requiredPermissionName": "App.Sender.Telegram",
"dataType": "List",
"selectOptions": {
"HTML": "HTML",
"Markdown": "Markdown",
"MarkdownV2": "MarkdownV2"
},
"order": 19
},
{
"code": "App.Sender.Rocket.Url",
"nameKey": "App.Sender.Rocket.Url",
@ -428,7 +480,7 @@
"requiredPermissionName": "App.Sender.Rocket",
"dataType": "Text",
"selectOptions": {},
"order": 17
"order": 20
},
{
"code": "App.Sender.Rocket.UserId",
@ -444,7 +496,7 @@
"requiredPermissionName": "App.Sender.Rocket",
"dataType": "Text",
"selectOptions": {},
"order": 18
"order": 21
},
{
"code": "App.Sender.Rocket.Token",
@ -460,7 +512,7 @@
"requiredPermissionName": "App.Sender.Rocket",
"dataType": "Text",
"selectOptions": {},
"order": 19
"order": 22
},
{
"code": "Abp.Mailing.DefaultFromDisplayName",
@ -476,7 +528,7 @@
"requiredPermissionName": "Abp.Mailing",
"dataType": "Text",
"selectOptions": {},
"order": 20
"order": 23
},
{
"code": "Abp.Mailing.DefaultFromAddress",
@ -492,7 +544,7 @@
"requiredPermissionName": "Abp.Mailing",
"dataType": "Text",
"selectOptions": {},
"order": 21
"order": 24
},
{
"code": "Abp.Mailing.Smtp.UserName",
@ -508,7 +560,7 @@
"requiredPermissionName": "Abp.Mailing.Smtp",
"dataType": "Text",
"selectOptions": {},
"order": 22
"order": 25
},
{
"code": "Abp.Mailing.Smtp.Password",
@ -524,7 +576,7 @@
"requiredPermissionName": "Abp.Mailing.Smtp",
"dataType": "Text",
"selectOptions": {},
"order": 23
"order": 26
},
{
"code": "Abp.Mailing.Smtp.Host",
@ -540,7 +592,7 @@
"requiredPermissionName": "Abp.Mailing.Smtp",
"dataType": "Text",
"selectOptions": {},
"order": 24
"order": 27
},
{
"code": "Abp.Mailing.Smtp.Port",
@ -556,7 +608,7 @@
"requiredPermissionName": "Abp.Mailing.Smtp",
"dataType": "Number",
"selectOptions": {},
"order": 25
"order": 28
},
{
"code": "Abp.Mailing.Smtp.Domain",
@ -572,7 +624,7 @@
"requiredPermissionName": "Abp.Mailing.Smtp",
"dataType": "Text",
"selectOptions": {},
"order": 26
"order": 29
},
{
"code": "Abp.Mailing.Smtp.EnableSsl",
@ -588,7 +640,7 @@
"requiredPermissionName": "Abp.Mailing.Smtp",
"dataType": "Bool",
"selectOptions": {},
"order": 27
"order": 30
},
{
"code": "Abp.Mailing.Smtp.UseDefaultCredentials",
@ -604,7 +656,7 @@
"requiredPermissionName": "Abp.Mailing.Smtp",
"dataType": "Bool",
"selectOptions": {},
"order": 28
"order": 31
},
{
"code": "Abp.Mailing.AWS.Profile",
@ -620,7 +672,7 @@
"requiredPermissionName": "Abp.Mailing.AWS",
"dataType": "Text",
"selectOptions": {},
"order": 29
"order": 32
},
{
"code": "Abp.Mailing.AWS.Region",
@ -636,7 +688,7 @@
"requiredPermissionName": "Abp.Mailing.AWS",
"dataType": "Text",
"selectOptions": {},
"order": 30
"order": 33
},
{
"code": "Abp.Mailing.AWS.AccessKey",
@ -652,7 +704,7 @@
"requiredPermissionName": "Abp.Mailing.AWS",
"dataType": "Text",
"selectOptions": {},
"order": 31
"order": 34
},
{
"code": "Abp.Mailing.AWS.AccessKeyId",
@ -668,7 +720,7 @@
"requiredPermissionName": "Abp.Mailing.AWS",
"dataType": "Text",
"selectOptions": {},
"order": 32
"order": 35
},
{
"code": "Abp.Account.IsSelfRegistrationEnabled",
@ -684,7 +736,7 @@
"requiredPermissionName": "Abp.Account.General",
"dataType": "Bool",
"selectOptions": {},
"order": 40
"order": 43
},
{
"code": "Abp.Account.EnableLocalLogin",
@ -700,7 +752,7 @@
"requiredPermissionName": "Abp.Account.General",
"dataType": "Bool",
"selectOptions": {},
"order": 41
"order": 44
},
{
"code": "Abp.Account.TwoFactor.Enabled",
@ -716,7 +768,7 @@
"requiredPermissionName": "Abp.Account.TwoFactor",
"dataType": "Bool",
"selectOptions": {},
"order": 42
"order": 45
},
{
"code": "Abp.Account.Captcha.MaxFailedAccessAttempts",
@ -732,7 +784,7 @@
"requiredPermissionName": "Abp.Account.Captcha",
"dataType": "Number",
"selectOptions": {},
"order": 43
"order": 46
},
{
"code": "Abp.Account.Captcha.EndPoint",
@ -748,7 +800,7 @@
"requiredPermissionName": "Abp.Account.Captcha",
"dataType": "Text",
"selectOptions": {},
"order": 44
"order": 47
},
{
"code": "Abp.Account.Captcha.SiteKey",
@ -764,7 +816,7 @@
"requiredPermissionName": "Abp.Account.Captcha",
"dataType": "Text",
"selectOptions": {},
"order": 45
"order": 48
},
{
"code": "Abp.Account.Captcha.SecretKey",
@ -780,7 +832,7 @@
"requiredPermissionName": "Abp.Account.Captcha",
"dataType": "Text",
"selectOptions": {},
"order": 46
"order": 49
},
{
"code": "Abp.Identity.Profile.General.RequireVerifiedAccount",
@ -796,7 +848,7 @@
"requiredPermissionName": "Abp.Identity.Profile",
"dataType": "Bool",
"selectOptions": {},
"order": 50
"order": 53
},
{
"code": "Abp.Identity.Profile.General.BlacklistedEmailProviders",
@ -812,7 +864,7 @@
"requiredPermissionName": "Abp.Identity.Profile",
"dataType": "Memo",
"selectOptions": {},
"order": 51
"order": 54
},
{
"code": "Abp.Identity.SignIn.RequireConfirmedEmail",
@ -828,7 +880,7 @@
"requiredPermissionName": "Abp.Identity.SignIn",
"dataType": "Bool",
"selectOptions": {},
"order": 60
"order": 63
},
{
"code": "Abp.Identity.SignIn.RequireEmailVerificationToRegister",
@ -844,7 +896,7 @@
"requiredPermissionName": "Abp.Identity.SignIn",
"dataType": "Bool",
"selectOptions": {},
"order": 61
"order": 64
},
{
"code": "Abp.Identity.SignIn.RequireConfirmedPhoneNumber",
@ -860,7 +912,7 @@
"requiredPermissionName": "Abp.Identity.SignIn",
"dataType": "Bool",
"selectOptions": {},
"order": 62
"order": 65
},
{
"code": "Abp.Identity.SignIn.EnablePhoneNumberConfirmation",
@ -876,7 +928,7 @@
"requiredPermissionName": "Abp.Identity.SignIn",
"dataType": "Bool",
"selectOptions": {},
"order": 63
"order": 66
},
{
"code": "Abp.Identity.User.IsUserNameUpdateEnabled",
@ -892,7 +944,7 @@
"requiredPermissionName": "Abp.Identity.User",
"dataType": "Bool",
"selectOptions": {},
"order": 64
"order": 67
},
{
"code": "Abp.Identity.User.IsEmailUpdateEnabled",
@ -908,7 +960,7 @@
"requiredPermissionName": "Abp.Identity.User",
"dataType": "Bool",
"selectOptions": {},
"order": 65
"order": 68
},
@ -926,7 +978,7 @@
"requiredPermissionName": "Abp.Identity.Password",
"dataType": "Bool",
"selectOptions": {},
"order": 70
"order": 73
},
{
"code": "Abp.Identity.Password.PasswordChangePeriodDays",
@ -942,7 +994,7 @@
"requiredPermissionName": "Abp.Identity.Password",
"dataType": "Number",
"selectOptions": {},
"order": 71
"order": 74
},
{
"code": "Abp.Identity.Password.RequiredLength",
@ -958,7 +1010,7 @@
"requiredPermissionName": "Abp.Identity.Password",
"dataType": "Number",
"selectOptions": {},
"order": 72
"order": 75
},
{
"code": "Abp.Identity.Password.RequiredUniqueChars",
@ -974,7 +1026,7 @@
"requiredPermissionName": "Abp.Identity.Password",
"dataType": "Number",
"selectOptions": {},
"order": 73
"order": 76
},
{
"code": "Abp.Identity.Password.RequireNonAlphanumeric",
@ -990,7 +1042,7 @@
"requiredPermissionName": "Abp.Identity.Password",
"dataType": "Bool",
"selectOptions": {},
"order": 74
"order": 77
},
{
"code": "Abp.Identity.Password.RequireLowercase",
@ -1006,7 +1058,7 @@
"requiredPermissionName": "Abp.Identity.Password",
"dataType": "Bool",
"selectOptions": {},
"order": 75
"order": 78
},
{
"code": "Abp.Identity.Password.RequireUppercase",
@ -1022,7 +1074,7 @@
"requiredPermissionName": "Abp.Identity.Password",
"dataType": "Bool",
"selectOptions": {},
"order": 76
"order": 79
},
{
"code": "Abp.Identity.Password.RequireDigit",
@ -1038,7 +1090,7 @@
"requiredPermissionName": "Abp.Identity.Password",
"dataType": "Bool",
"selectOptions": {},
"order": 77
"order": 80
},
{
"code": "Abp.Identity.Lockout.AllowedForNewUsers",
@ -1054,7 +1106,7 @@
"requiredPermissionName": "Abp.Identity.Lockout",
"dataType": "Bool",
"selectOptions": {},
"order": 78
"order": 81
},
{
"code": "Abp.Identity.Lockout.LockoutDuration",
@ -1070,7 +1122,7 @@
"requiredPermissionName": "Abp.Identity.Lockout",
"dataType": "Number",
"selectOptions": {},
"order": 79
"order": 82
},
{
"code": "Abp.Identity.Lockout.MaxFailedAccessAttempts",
@ -1086,7 +1138,7 @@
"requiredPermissionName": "Abp.Identity.Lockout",
"dataType": "Number",
"selectOptions": {},
"order": 80
"order": 83
},
{
"code": "Abp.Identity.OrganizationUnit.MaxUserMembershipCount",
@ -1102,7 +1154,7 @@
"requiredPermissionName": "Abp.Identity.OrganizationUnits",
"dataType": "Number",
"selectOptions": {},
"order": 90
"order": 93
}
],
"NotificationTypes": [],

View file

@ -1986,6 +1986,42 @@
"en": "The token for the WhatsApp service.",
"tr": "WhatsApp hizmeti için token."
},
{
"resourceName": "Platform",
"key": "App.Sender.Telegram",
"en": "Telegram",
"tr": "Telegram"
},
{
"resourceName": "Platform",
"key": "App.Sender.Telegram.Url",
"en": "Url",
"tr": "Url"
},
{
"resourceName": "Platform",
"key": "App.Sender.Telegram.BotToken",
"en": "Bot Token",
"tr": "Bot Token"
},
{
"resourceName": "Platform",
"key": "App.TelegramBotToken.BotTokenDescription",
"en": "The BotFather token of the Telegram bot.",
"tr": "Telegram botunun BotFather token bilgisi."
},
{
"resourceName": "Platform",
"key": "App.Sender.Telegram.ParseMode",
"en": "Parse Mode",
"tr": "Biçimlendirme Modu"
},
{
"resourceName": "Platform",
"key": "App.TelegramParseMode.ParseModeDescription",
"en": "The formatting mode used for Telegram messages.",
"tr": "Telegram mesajlarında kullanılan biçimlendirme modu."
},
{
"resourceName": "Platform",
"key": "App.Sender.Rocket",
@ -3468,6 +3504,12 @@
"en": "Rocket User Name",
"tr": "Rocket Kullanıcı Adı"
},
{
"resourceName": "Platform",
"key": "App.Platform.TelegramChatId",
"en": "Telegram Chat Id",
"tr": "Telegram Sohbet Kimliği"
},
{
"resourceName": "Platform",
"key": "Abp.Identity.UserLockedOutMessage",

View file

@ -5387,6 +5387,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
new () { Key="Sms", Name="App.Sender.Sms" },
new () { Key="Mail", Name="App.StaticLookup.Mail" },
new () { Key="WhatsApp", Name="App.Sender.WhatsApp" },
new () { Key="Telegram", Name="App.Sender.Telegram" },
new () { Key="Rocket", Name="App.StaticLookup.Rocket" },
new () { Key="UiActivity", Name="App.StaticLookup.UiActivity" },
new () { Key="UiToast", Name="App.StaticLookup.UiToast" },
@ -5608,6 +5609,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
new () { Key="UiActivity", Name="App.StaticLookup.UiActivity" },
new () { Key="UiToast", Name="App.StaticLookup.UiToast" },
new () { Key="WhatsApp", Name="App.Sender.WhatsApp" },
new () { Key="Telegram", Name="App.Sender.Telegram" },
}),
}),
ColumnCustomizationJson = DefaultColumnCustomizationJson,

View file

@ -2714,6 +2714,15 @@
"MultiTenancySide": 3,
"MenuGroup": "Erp|Kurs"
},
{
"GroupName": "App.Administration",
"Name": "App.Sender.Telegram",
"ParentName": "App.Sender",
"DisplayName": "App.Sender.Telegram",
"IsEnabled": true,
"MultiTenancySide": 3,
"MenuGroup": "Erp|Kurs"
},
{
"GroupName": "App.Administration",
"Name": "App.Sender.Rocket",

View file

@ -105,6 +105,7 @@ public static class PlatformConsts
public const string IsVerified = "IsVerified";
public const string Avatar = "Avatar";
public const string RocketUsername = "RocketUsername";
public const string TelegramChatId = "TelegramChatId";
public const string HomeAddress = "HomeAddress";
public const string EducationLevel = "EducationLevel";
public const string GraduationSchool = "GraduationSchool";
@ -342,6 +343,15 @@ public static class PlatformConsts
public const string TemplateName = Default + ".TemplateName";
public const string Token = Default + ".Token";
}
public static class Telegram
{
public const string Default = GroupName + ".Telegram";
public const string Url = Default + ".Url";
public const string BotToken = Default + ".BotToken";
public const string ParseMode = Default + ".ParseMode";
}
}
}

View file

@ -84,6 +84,13 @@ public static class PlatformModuleExtensionConfigurator
property.DisplayName = new LocalizableString(typeof(PlatformResource), PlatformConsts.AbpIdentity.User.RocketUsername);
});
user.AddOrUpdateProperty<string>(
PlatformConsts.AbpIdentity.User.TelegramChatId,
property =>
{
property.DisplayName = new LocalizableString(typeof(PlatformResource), PlatformConsts.AbpIdentity.User.TelegramChatId);
});
user.AddOrUpdateProperty<Guid>(
PlatformConsts.AbpIdentity.User.DepartmentId,
property =>

View file

@ -10,6 +10,7 @@ using Sozsoft.Platform.Data.Seeds;
using Sozsoft.Sender.Mail;
using Sozsoft.Sender.Rocket;
using Sozsoft.Sender.Sms;
using Sozsoft.Sender.Telegram;
using Sozsoft.Sender.WhatsApp;
using Volo.Abp.BackgroundWorkers;
using Volo.Abp.Domain.Repositories;
@ -28,6 +29,7 @@ public class NotificationWorker : BackgroundWorkerBase
protected ISozsoftEmailSender ErpEmailSender { get; }
protected ISozsoftSmsSender ErpSmsSender { get; }
protected ISozsoftWhatsAppSender ErpWhatsAppSender { get; }
protected ISozsoftTelegramSender ErpTelegramSender { get; }
protected ISettingProvider SettingProvider { get; }
public NotificationWorker(
@ -38,6 +40,7 @@ public class NotificationWorker : BackgroundWorkerBase
ISozsoftRocketSender erpRocketSender,
ISozsoftEmailSender erpEmailSender,
ISozsoftWhatsAppSender erpWhatsAppSender,
ISozsoftTelegramSender erpTelegramSender,
ISettingProvider settingProvider)
{
Repository = repository;
@ -47,6 +50,7 @@ public class NotificationWorker : BackgroundWorkerBase
ErpRocketSender = erpRocketSender;
ErpEmailSender = erpEmailSender;
ErpWhatsAppSender = erpWhatsAppSender;
ErpTelegramSender = erpTelegramSender;
SettingProvider = settingProvider;
}
@ -140,6 +144,25 @@ public class NotificationWorker : BackgroundWorkerBase
}
}
var telegram = await Repository.GetListAsync(a => a.NotificationChannel == NotificationChannels.Telegram && !a.IsSent, cancellationToken: cancellationToken);
foreach (var notification in telegram)
{
try
{
var result = await ErpTelegramSender.SendAsync(notification.Identifier, notification.Message);
if (result.Success)
{
notification.IsSent = true;
notification.IsRead = true;
await Repository.UpdateAsync(notification, autoSave: true, cancellationToken: cancellationToken);
}
}
catch (Exception ex)
{
Logger.LogException(ex);
}
}
await uow.SaveChangesAsync(cancellationToken);
}
}

View file

@ -305,6 +305,15 @@ public static class SeedConsts
public const string TemplateName = Default + ".TemplateName";
public const string Token = Default + ".Token";
}
public static class Telegram
{
public const string Default = GroupName + ".Telegram";
public const string Url = Default + ".Url";
public const string BotToken = Default + ".BotToken";
public const string ParseMode = Default + ".ParseMode";
}
}
}

View file

@ -61,6 +61,16 @@ public static class AbpIdentityUserExtensions
return user.GetProperty<string>(PlatformConsts.AbpIdentity.User.RocketUsername);
}
//Telegram Chat Id
public static void SetTelegramChatId(this IdentityUser user, string telegramChatId)
{
user.SetProperty(PlatformConsts.AbpIdentity.User.TelegramChatId, telegramChatId);
}
public static string GetTelegramChatId(this IdentityUser user)
{
return user.GetProperty<string>(PlatformConsts.AbpIdentity.User.TelegramChatId);
}
//Work Hour
public static void SetWorkHour(this IdentityUser user, string workHour)
{

View file

@ -65,6 +65,15 @@ public static class PlatformEfCoreEntityExtensionMappings
}
);
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityUser, string>(
PlatformConsts.AbpIdentity.User.TelegramChatId,
(entityBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(64).HasDefaultValue(null);
}
);
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityUser, string>(
PlatformConsts.AbpIdentity.User.WorkHour,

View file

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace Sozsoft.Platform.Migrations
{
[DbContext(typeof(PlatformDbContext))]
[Migration("20260826070015_Initial")]
[Migration("20260828094327_Initial")]
partial class Initial
{
/// <inheritdoc />
@ -7854,6 +7854,10 @@ namespace Sozsoft.Platform.Migrations
.HasColumnType("nvarchar(64)")
.HasColumnName("Surname");
b.Property<string>("TelegramChatId")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");

View file

@ -461,6 +461,7 @@ namespace Sozsoft.Platform.Migrations
SequenceNo = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: true),
SerialNo = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: true),
SskNo = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: true),
TelegramChatId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
TerminationDate = table.Column<DateTime>(type: "datetime2", nullable: true),
UserType = table.Column<int>(type: "int", nullable: false, defaultValue: 0),
Village = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),

View file

@ -7851,6 +7851,10 @@ namespace Sozsoft.Platform.Migrations
.HasColumnType("nvarchar(64)")
.HasColumnName("Surname");
b.Property<string>("TelegramChatId")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");

View file

@ -6,7 +6,7 @@ const NotificationChannels = {
UiActivity: 'UiActivity',
UiToast: 'UiToast',
WhatsApp: 'WhatsApp',
// Telegram: 'Telegram',
Telegram: 'Telegram',
}
export const NotificationChannelColors: Record<string, string> = {
@ -17,7 +17,7 @@ export const NotificationChannelColors: Record<string, string> = {
UiActivity: 'bg-lime-600',
UiToast: 'bg-emerald-800',
WhatsApp: 'bg-cyan-600',
// Telegram: 'bg-purple-900',
Telegram: 'bg-purple-900',
}
export default NotificationChannels

View file

@ -140,6 +140,7 @@ export interface UserInfoViewModel extends ExtensibleObject {
shouldChangePasswordOnNextLogin: boolean
avatar: string
rocketUsername?: string
telegramChatId?: string
creationTime: Date | string
lastModificationTime: Date | string
workHour?: string

View file

@ -35,6 +35,7 @@ const ActivityLog = () => {
NotificationChannels.UiActivity,
NotificationChannels.UiToast,
NotificationChannels.WhatsApp,
NotificationChannels.Telegram,
])
const fetchData = useCallback(async (isLoadmore: boolean = false) => {

View file

@ -15,6 +15,7 @@ const ticketCheckboxes = [
{ label: NotificationChannels.UiActivity, value: NotificationChannels.UiActivity },
{ label: NotificationChannels.UiToast, value: NotificationChannels.UiToast },
{ label: NotificationChannels.WhatsApp, value: NotificationChannels.WhatsApp },
{ label: NotificationChannels.Telegram, value: NotificationChannels.Telegram },
]
const CategoryTitle = ({ children, className }: CategoryTitleProps) => {

View file

@ -20,6 +20,7 @@ import {
import 'react-advanced-cropper/dist/style.css'
import {
FaFacebookMessenger,
FaTelegramPlane,
FaEnvelope,
FaTrashAlt,
FaUserCircle,
@ -293,6 +294,16 @@ const General = () => {
value={profileData?.extraProperties?.['RocketUsername'] as string | undefined}
></Input>
</FormItem>
<FormItem label={translate('::App.Platform.TelegramChatId')}>
<Input
disabled
type="text"
prefix={<FaTelegramPlane className="text-xl" />}
value={
profileData?.extraProperties?.['TelegramChatId'] as string | undefined
}
></Input>
</FormItem>
</div>
<div>

View file

@ -49,6 +49,7 @@ import {
FaPhone,
FaUserCircle,
FaFacebookMessenger,
FaTelegramPlane,
FaHome,
FaUniversity,
FaCalendarAlt,
@ -828,6 +829,21 @@ function UserDetails() {
</FormItem>
</div>
<div>
<FormItem
size="sm"
label={translate('::App.Platform.TelegramChatId')}
>
<Field
type="text"
name="telegramChatId"
placeholder={translate('::App.Platform.TelegramChatId')}
component={Input}
prefix={<FaTelegramPlane className="text-xl" />}
/>
</FormItem>
</div>
<div>
<FormItem
label={translate('::Abp.Identity.User.UserInformation.SskNo')}