diff --git a/Core/Extensions/BotExtension.cs b/Core/Extensions/BotExtension.cs index 1fcf00d..0ca594a 100644 --- a/Core/Extensions/BotExtension.cs +++ b/Core/Extensions/BotExtension.cs @@ -31,5 +31,16 @@ namespace Kruzya.TelegramBot.Core.Extensions return canUse; } + + public static async Task CanDeleteMessagesAsync(this ITelegramBotClient bot, Chat chat) + { + return await CanDeleteMessagesAsync(bot, chat, await bot.GetMeAsync()); + } + + public static async Task CanDeleteMessagesAsync(this ITelegramBotClient bot, Chat chat, User user) + { + return await bot.GetChatMemberAsync(chat.Id, user.Id) is ChatMemberOwner + or ChatMemberAdministrator {CanRestrictMembers: true}; + } } } \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index a66e58a..1491513 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,6 +17,7 @@ COPY ./modules/ChatQuotes/*.csproj ./modules/ChatQuotes/ COPY ./modules/Reputation/*.csproj ./modules/Reputation/ COPY ./modules/ContentStore/*.csproj ./modules/ContentStore/ COPY ./modules/DebugTools/*.csproj ./modules/DebugTools/ +COPY ./modules/CodeWatcher/*.csproj ./modules/CodeWatcher/ RUN dotnet restore Kruzya.TelegramBot.sln # Copy all project files diff --git a/Kruzya.TelegramBot.sln b/Kruzya.TelegramBot.sln index c13f95f..3a2ccca 100644 --- a/Kruzya.TelegramBot.sln +++ b/Kruzya.TelegramBot.sln @@ -29,7 +29,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reputation", "modules\Reput EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ContentStore", "modules\ContentStore\ContentStore.csproj", "{4F1E8FE8-640F-4671-87FC-0FFCCB6EDF5E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DebugTools", "modules\DebugTools\DebugTools.csproj", "{B95C9180-4893-4A33-BF43-AB2EE998650C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DebugTools", "modules\DebugTools\DebugTools.csproj", "{B95C9180-4893-4A33-BF43-AB2EE998650C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeWatcher", "modules\CodeWatcher\CodeWatcher.csproj", "{DEF70068-93E5-4198-8CED-4B3D8AE65162}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -89,6 +91,10 @@ Global {B95C9180-4893-4A33-BF43-AB2EE998650C}.Debug|Any CPU.Build.0 = Debug|Any CPU {B95C9180-4893-4A33-BF43-AB2EE998650C}.Release|Any CPU.ActiveCfg = Release|Any CPU {B95C9180-4893-4A33-BF43-AB2EE998650C}.Release|Any CPU.Build.0 = Release|Any CPU + {DEF70068-93E5-4198-8CED-4B3D8AE65162}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DEF70068-93E5-4198-8CED-4B3D8AE65162}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DEF70068-93E5-4198-8CED-4B3D8AE65162}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DEF70068-93E5-4198-8CED-4B3D8AE65162}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -105,6 +111,7 @@ Global {03F3C7D5-6FEF-4A83-9191-501091BFC1AC} = {C7821F15-DEDD-474F-A575-A296D4B58F10} {4F1E8FE8-640F-4671-87FC-0FFCCB6EDF5E} = {C7821F15-DEDD-474F-A575-A296D4B58F10} {B95C9180-4893-4A33-BF43-AB2EE998650C} = {C7821F15-DEDD-474F-A575-A296D4B58F10} + {DEF70068-93E5-4198-8CED-4B3D8AE65162} = {C7821F15-DEDD-474F-A575-A296D4B58F10} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5BA73C3B-D5FC-4942-9091-504325CDC308} diff --git a/modules/CodeWatcher/CodeWatcher.cs b/modules/CodeWatcher/CodeWatcher.cs new file mode 100644 index 0000000..0dfe046 --- /dev/null +++ b/modules/CodeWatcher/CodeWatcher.cs @@ -0,0 +1,9 @@ +using Kruzya.TelegramBot.Core; + +namespace West.TelegramBot.CodeWatcher +{ + public class CodeWatcher : Module + { + public CodeWatcher(Core core) : base(core) {} + } +} \ No newline at end of file diff --git a/modules/CodeWatcher/CodeWatcher.csproj b/modules/CodeWatcher/CodeWatcher.csproj new file mode 100644 index 0000000..62dbc28 --- /dev/null +++ b/modules/CodeWatcher/CodeWatcher.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + enable + enable + TelegramBot.CodeWatcher + West.TelegramBot.CodeWatcher + + + + + + + diff --git a/modules/CodeWatcher/Handler.cs b/modules/CodeWatcher/Handler.cs new file mode 100644 index 0000000..e213e01 --- /dev/null +++ b/modules/CodeWatcher/Handler.cs @@ -0,0 +1,67 @@ +using BotFramework; +using BotFramework.Attributes; +using BotFramework.Enums; +using BotFramework.Utils; +using Kruzya.TelegramBot.Core.Extensions; +using Telegram.Bot; +using Telegram.Bot.Types; +using Telegram.Bot.Types.Enums; + +namespace West.TelegramBot.CodeWatcher; + +public class Handler : BotEventHandler +{ + private static readonly string ResponseMessage = new HtmlString() + .Text(", не стоит публиковать столь большой код прямо в чат. Вместо этого загрузите его на ") + .Url("https://pastebin.com", "https://pastebin.com") + .Text(" и отправьте в виде ссылки.") + .ToString(); + + + [Message(InChat.Public, MessageFlag.HasText)] + public async Task HandleMessage() + { + var message = RawUpdate.Message!; + + if (message.Entities == null) + { + return; + } + + if (!await Bot.CanDeleteMessagesAsync(Chat)) + { + return; + } + + foreach (var entity in message.Entities) + { + if (entity.Type is MessageEntityType.Code or MessageEntityType.Pre) + { + var entityLines = message.Text! + .Substring(entity.Offset, entity.Length) + .SplitToLines() + .ToList(); + + if (entityLines.Count >= 10) + { + await DeleteAndNotifyAsync(message); + return; + } + + if (entityLines.Any(line => line.Length > 80)) + { + await DeleteAndNotifyAsync(message); + return; + } + } + } + } + + public async Task DeleteAndNotifyAsync(Message message) + { + await Bot.DeleteMessageAsync(Chat.Id, message.MessageId); + await Bot.SendTextMessageAsync(Chat.Id, + (message.From?.ToHtml() ?? "") + ResponseMessage, + ParseMode.Html); + } +} \ No newline at end of file diff --git a/modules/CodeWatcher/StringExtension.cs b/modules/CodeWatcher/StringExtension.cs new file mode 100644 index 0000000..b3e98a8 --- /dev/null +++ b/modules/CodeWatcher/StringExtension.cs @@ -0,0 +1,17 @@ +namespace West.TelegramBot.CodeWatcher; + +public static class StringExtension +{ + // https://stackoverflow.com/a/23408020 + public static IEnumerable SplitToLines(this string input) + { + using (var reader = new StringReader(input)) + { + string line; + while ((line = reader.ReadLine()) != null) + { + yield return line; + } + } + } +} \ No newline at end of file