Skip to content

Commit

Permalink
Workers requested features (Soft Ban) (#1)
Browse files Browse the repository at this point in the history
* USE_POLLING for better development experience

* Soft ban feature
  • Loading branch information
Keroosha authored Jan 10, 2024
1 parent 17c2060 commit c66fa83
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 4 deletions.
97 changes: 95 additions & 2 deletions src/VahterBanBot/Bot.fs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ let isBanCommand (message: Message) =
let isUnbanCommand (message: Message) =
message.Text.StartsWith "/unban "

let isSoftBanCommand (message: Message) =
message.Text.StartsWith "/sban"

let isSoftBanOnReplyCommand (message: Message) =
isSoftBanCommand message &&
message.ReplyToMessage <> null

let isBanOnReplyCommand (message: Message) =
isBanCommand message &&
message.ReplyToMessage <> null
Expand All @@ -42,7 +49,8 @@ let isBannedPersonAdmin (botConfig: BotConfiguration) (message: Message) =
let isKnownCommand (message: Message) =
isPingCommand message ||
isBanCommand message ||
isUnbanCommand message
isUnbanCommand message ||
isSoftBanCommand message

let isBanAuthorized
(botConfig: BotConfiguration)
Expand Down Expand Up @@ -87,6 +95,31 @@ let banInAllChats (botConfig: BotConfiguration) (botClient: ITelegramBotClient)
return! Task.WhenAll banTasks
}

let softBanInChat (botClient: ITelegramBotClient) (chatId: ChatId) targetUserId (duration: int) = task {
let permissions = ChatPermissions(
CanSendMessages = false,
CanSendAudios = false,
CanSendDocuments = false,
CanSendPhotos = false,
CanSendVideos = false,
CanSendVideoNotes = false,
CanSendVoiceNotes = false,
CanSendPolls = false,
CanSendOtherMessages = false,
CanAddWebPagePreviews = false,
CanChangeInfo = false,
CanInviteUsers = false,
CanPinMessages = false,
CanManageTopics = false
)
let untilDate = DateTime.Now.AddHours duration
try
do! botClient.RestrictChatMemberAsync(chatId, targetUserId, permissions, Nullable(), untilDate)
return Ok(chatId, targetUserId)
with e ->
return Error(chatId, targetUserId, e)
}

let unbanInAllChats (botConfig: BotConfiguration) (botClient: ITelegramBotClient) targetUserId = task {
let banTasks =
botConfig.ChatsToMonitor
Expand Down Expand Up @@ -166,6 +199,15 @@ let aggregateUnbanResultInLogMsg message targetUserId targetUsername =
targetUserId
targetUsername

let softBanResultInLogMsg (message: Message) (duration: int) =
let logMsgBuilder = StringBuilder()
%logMsgBuilder.Append $"Vahter @{message.From.Username}({message.From.Id}) "
%logMsgBuilder.Append $"softbanned @{message.ReplyToMessage.From.Username}({message.ReplyToMessage.From.Id}) "
%logMsgBuilder.Append $"in @{message.Chat.Username}({message.Chat.Id}) "
%logMsgBuilder.Append $"until {DateTime.Now.AddHours duration}"
string logMsgBuilder


let ping
(botClient: ITelegramBotClient)
(message: Message) = task {
Expand Down Expand Up @@ -261,6 +303,45 @@ let banOnReply
do! deleteReplyTask
}

let softBanOnReply
(botClient: ITelegramBotClient)
(botConfig: BotConfiguration)
(message: Message)
(logger: ILogger) = task {
use banOnReplyActivity = botActivity.StartActivity("softBanOnReply")
%banOnReplyActivity
.SetTag("vahterId", message.From.Id)
.SetTag("vahterUsername", message.From.Username)
.SetTag("targetId", message.ReplyToMessage.From.Id)
.SetTag("targetUsername", message.ReplyToMessage.From.Username)

let deleteReplyTask = task {
use _ =
botActivity
.StartActivity("deleteReplyMsg")
.SetTag("msgId", message.ReplyToMessage.MessageId)
.SetTag("chatId", message.Chat.Id)
.SetTag("chatUsername", message.Chat.Username)
do! botClient.DeleteMessageAsync(ChatId(message.Chat.Id), message.ReplyToMessage.MessageId)
|> safeTaskAwait (fun e -> logger.LogError ($"Failed to delete reply message {message.ReplyToMessage.MessageId} from chat {message.Chat.Id}", e))
}

let maybeDurationString = message.Text.Split " " |> Seq.last
// use last value as soft ban duration
let duration =
match Int32.TryParse maybeDurationString with
| true, x -> x
| _ -> 24 // 1 day should be enough

let logText = softBanResultInLogMsg message duration

do! softBanInChat botClient (ChatId message.Chat.Id) message.ReplyToMessage.From.Id duration |> taskIgnore
do! deleteReplyTask

do! botClient.SendTextMessageAsync(ChatId(botConfig.LogsChannelId), logText) |> taskIgnore
logger.LogInformation logText
}

let unban
(botClient: ITelegramBotClient)
(botConfig: BotConfiguration)
Expand Down Expand Up @@ -366,7 +447,19 @@ let onUpdate
false
if authed then
do! unban botClient botConfig message logger targetUserId

elif isSoftBanOnReplyCommand message then
let targetUserId = message.ReplyToMessage.From.Id
let targetUsername = Option.ofObj message.ReplyToMessage.From.Username
let authed =
isBanAuthorized
botConfig
message
logger
targetUserId
targetUsername
true
if authed then
do! softBanOnReply botClient botConfig message logger
// ping command for testing that bot works and you can talk to it
elif isPingCommand message then
do! ping botClient message
Expand Down
24 changes: 23 additions & 1 deletion src/VahterBanBot/Program.fs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
open System
open System.Collections.Generic
open System.Threading
open System.Threading.Tasks
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Http
open Microsoft.Extensions.Logging
open Microsoft.FSharp.Core
open Newtonsoft.Json
open Telegram.Bot
open Telegram.Bot.Polling
open Telegram.Bot.Types
open Giraffe
open Microsoft.Extensions.DependencyInjection
open Telegram.Bot.Types.Enums
open VahterBanBot
open VahterBanBot.Cleanup
open VahterBanBot.Utils
Expand All @@ -32,7 +36,8 @@ let botConf =
ChatsToMonitor = getEnv "CHATS_TO_MONITOR" |> JsonConvert.DeserializeObject<_>
AllowedUsers = getEnv "ALLOWED_USERS" |> JsonConvert.DeserializeObject<_>
ShouldDeleteChannelMessages = getEnvOr "SHOULD_DELETE_CHANNEL_MESSAGES" "true" |> bool.Parse
IgnoreSideEffects = getEnvOr "IGNORE_SIDE_EFFECTS" "false" |> bool.Parse }
IgnoreSideEffects = getEnvOr "IGNORE_SIDE_EFFECTS" "false" |> bool.Parse
UsePolling = getEnvOr "USE_POLLING" "false" |> bool.Parse }

let validateApiKey (ctx : HttpContext) =
match ctx.TryGetRequestHeader "X-Telegram-Bot-Api-Secret-Token" with
Expand Down Expand Up @@ -140,4 +145,21 @@ app.Logger.LogInformation startLogMsg
if not botConf.IgnoreSideEffects then
telegramClient.SendTextMessageAsync(ChatId(botConf.LogsChannelId), startLogMsg).Wait()

// Dev mode only
if botConf.UsePolling then
let pollingHandler = {
new IUpdateHandler with
member x.HandleUpdateAsync (botClient: ITelegramBotClient, update: Update, cancellationToken: CancellationToken) =
task {
if update.Message <> null && update.Message.Type = MessageType.Text then
let ctx = app.Services.CreateScope()
let logger = ctx.ServiceProvider.GetRequiredService<ILogger<IUpdateHandler>>()
let client = ctx.ServiceProvider.GetRequiredService<ITelegramBotClient>()
do! onUpdate client botConf logger update.Message
}
member x.HandlePollingErrorAsync (botClient: ITelegramBotClient, ex: Exception, cancellationToken: CancellationToken) =
Task.CompletedTask
}
telegramClient.StartReceiving(pollingHandler, null, CancellationToken.None)

server.Wait()
3 changes: 2 additions & 1 deletion src/VahterBanBot/Types.fs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ type BotConfiguration =
ChatsToMonitor: Dictionary<string, int64>
AllowedUsers: Dictionary<string, int64>
ShouldDeleteChannelMessages: bool
IgnoreSideEffects: bool }
IgnoreSideEffects: bool
UsePolling: bool }

[<CLIMutable>]
type DbUser =
Expand Down

0 comments on commit c66fa83

Please sign in to comment.