@@ -497,11 +497,13 @@ let killSpammerAutomated
497497 InlineKeyboardButton.WithCallbackData( " ✅ NOT a spam" , string callback.id)
498498 ]
499499 else
500- // Potential spam → two callbacks
500+ // Potential spam → three callbacks
501501 let! killCallback = DB.newCallbackPending ( CallbackMessage.Spam { message = message }) message.From.Id channelId
502+ let! softSpamCallback = DB.newCallbackPending ( CallbackMessage.MarkAsSpam { message = message }) message.From.Id channelId
502503 let! notSpamCallback = DB.newCallbackPending ( CallbackMessage.NotASpam { message = message }) message.From.Id channelId
503- return [ killCallback.id; notSpamCallback.id], InlineKeyboardMarkup [|
504+ return [ killCallback.id; softSpamCallback.id ; notSpamCallback.id], InlineKeyboardMarkup [|
504505 InlineKeyboardButton.WithCallbackData( " 🚫 KILL" , string killCallback.id);
506+ InlineKeyboardButton.WithCallbackData( " ⚠️ SPAM" , string softSpamCallback.id);
505507 InlineKeyboardButton.WithCallbackData( " ✅ NOT SPAM" , string notSpamCallback.id)
506508 |]
507509 }
@@ -526,31 +528,45 @@ let killSpammerAutomated
526528 logger.LogInformation logMsg
527529}
528530
529- let autoBan
531+ /// Checks user's social score and triggers auto-ban if below threshold
532+ /// Returns true if user was auto-banned, false otherwise
533+ let checkAndAutoBan
530534 ( botUser : DbUser )
531535 ( botClient : ITelegramBotClient )
532536 ( botConfig : BotConfiguration )
533537 ( message : Message )
534538 ( logger : ILogger ) = task {
535- use banOnReplyActivity = botActivity.StartActivity( " autoBan" )
536- % banOnReplyActivity
537- .SetTag( " spammerId" , message.From.Id)
538- .SetTag( " spammerUsername" , message.From.Username)
539-
540- let! userStats = DB.getUserStatsByLastNMessages botConfig.MlSpamAutobanCheckLastMsgCount message.From.Id
541- let socialScore = userStats.good - userStats.bad
542-
543- % banOnReplyActivity.SetTag( " socialScore" , socialScore)
544-
545- if double socialScore <= botConfig.MlSpamAutobanScoreThreshold then
546- // ban user in all monitored chats
547- do ! totalBan botClient botConfig message botUser logger
548- let msg = $" Auto-banned user {prependUsername message.From.Username} ({message.From.Id}) due to the low social score {socialScore}"
549- logger.LogInformation msg
550- do ! botClient.SendTextMessageAsync(
551- chatId = ChatId( botConfig.AllLogsChannelId),
552- text = msg
553- ) |> taskIgnore
539+ if not botConfig.MlSpamAutobanEnabled then
540+ return false
541+ else
542+ use banOnReplyActivity = botActivity.StartActivity( " checkAndAutoBan" )
543+ % banOnReplyActivity
544+ .SetTag( " spammerId" , message.From.Id)
545+ .SetTag( " spammerUsername" , message.From.Username)
546+
547+ let! userStats = DB.getUserStatsByLastNMessages botConfig.MlSpamAutobanCheckLastMsgCount message.From.Id
548+ let socialScore = userStats.good - userStats.bad
549+
550+ % banOnReplyActivity.SetTag( " socialScore" , socialScore)
551+
552+ if double socialScore <= botConfig.MlSpamAutobanScoreThreshold then
553+ // ban user in all monitored chats
554+ do ! totalBan botClient botConfig message botUser logger
555+ let msg = $" Auto-banned user {prependUsername message.From.Username} ({message.From.Id}) due to the low social score {socialScore}"
556+ logger.LogInformation msg
557+ do ! botClient.SendTextMessageAsync(
558+ chatId = ChatId( botConfig.AllLogsChannelId),
559+ text = msg
560+ ) |> taskIgnore
561+ return true
562+ else
563+ return false
564+ }
565+
566+ /// Wrapper for backward compatibility - calls checkAndAutoBan and ignores result
567+ let autoBan botUser botClient botConfig message logger = task {
568+ let! _ = checkAndAutoBan botUser botClient botConfig message logger
569+ return ()
554570}
555571
556572let totalBanByReaction
@@ -744,10 +760,8 @@ let justMessage
744760 if prediction.Score >= botConfig.MlSpamThreshold then
745761 // delete message
746762 do ! killSpammerAutomated botClient botConfig message logger botConfig.MlSpamDeletionEnabled prediction.Score
747-
748- if botConfig.MlSpamAutobanEnabled then
749- // trigger auto-ban check
750- do ! autoBan botUser botClient botConfig message logger
763+ // trigger auto-ban check (checkAndAutoBan handles MlSpamAutobanEnabled internally)
764+ do ! autoBan botUser botClient botConfig message logger
751765 elif prediction.Score >= botConfig.MlWarningThreshold then
752766 // just warn
753767 do ! killSpammerAutomated botClient botConfig message logger false prediction.Score
@@ -985,8 +999,50 @@ let vahterMarkedAsSpam
985999 logger
9861000}
9871001
1002+ /// Soft spam handler - deletes message and marks as spam for ML, but does NOT ban user
1003+ /// User may get auto-banned if karma threshold is reached
1004+ let vahterSoftSpam
1005+ ( botUser : DbUser )
1006+ ( botClient : ITelegramBotClient )
1007+ ( botConfig : BotConfiguration )
1008+ ( logger : ILogger )
1009+ ( vahter : DbUser )
1010+ ( message : MessageWrapper ) = task {
1011+ let msg = message.message
1012+ let msgId = msg.MessageId
1013+ let chatId = msg.Chat.Id
1014+ let chatName = msg.Chat.Username
1015+ use _ =
1016+ botActivity
1017+ .StartActivity( " vahterSoftSpam" )
1018+ .SetTag( " messageId" , msgId)
1019+ .SetTag( " chatId" , chatId)
1020+
1021+ // 1. Delete the message from original chat
1022+ recordDeletedMessage chatId chatName " softSpam"
1023+ do ! botClient.DeleteMessageAsync( ChatId( chatId), msgId)
1024+ |> safeTaskAwait ( fun e -> logger.LogWarning( $" Failed to delete message {msgId} from chat {chatId}" , e))
1025+
1026+ // 2. Mark as false negative (for ML training + karma)
1027+ do ! DB.markMessageAsFalseNegative chatId msgId
1028+
1029+ // 3. Log the action
1030+ let vahterUsername = vahter.username |> Option.defaultValue null
1031+ let logMsg = $" Vahter {prependUsername vahterUsername} ({vahter.id}) marked message {msgId} in {prependUsername chatName}({chatId}) as SPAM (soft, no ban)\n {msg.TextOrCaption}"
1032+ do ! botClient.SendTextMessageAsync(
1033+ chatId = ChatId( botConfig.AllLogsChannelId),
1034+ text = logMsg
1035+ ) |> taskIgnore
1036+ logger.LogInformation logMsg
1037+
1038+ // 4. Check auto-ban using shared logic (karma system)
1039+ let! _ = checkAndAutoBan botUser botClient botConfig msg logger
1040+ ()
1041+ }
1042+
9881043// just an aux function to reduce indentation in onCallback and prevent FS3511
9891044let onCallbackAux
1045+ ( botUser : DbUser )
9901046 ( botClient : ITelegramBotClient )
9911047 ( botConfig : BotConfiguration )
9921048 ( logger : ILogger )
@@ -995,12 +1051,13 @@ let onCallbackAux
9951051 ( dbCallback : DbCallback )
9961052 ( callbackQuery : CallbackQuery )= task {
9971053 let callback = dbCallback.data
998- let msg = match callback with NotASpam m | Spam m -> m
1054+ let msg = match callback with NotASpam m | Spam m | MarkAsSpam m -> m
9991055
10001056 // Determine action type based on callback data and channel
10011057 let actionType =
10021058 match callback with
10031059 | Spam _ -> " potential_kill"
1060+ | MarkAsSpam _ -> " potential_soft_spam"
10041061 | NotASpam _ ->
10051062 if dbCallback.action_ channel_ id = Some botConfig.DetectedSpamChannelId
10061063 then " detected_not_spam"
@@ -1021,6 +1078,9 @@ let onCallbackAux
10211078 | Spam msg ->
10221079 % onCallbackActivity.SetTag( " type" , " Spam" )
10231080 do ! vahterMarkedAsSpam botClient botConfig logger vahter msg
1081+ | MarkAsSpam msg ->
1082+ % onCallbackActivity.SetTag( " type" , " MarkAsSpam" )
1083+ do ! vahterSoftSpam botUser botClient botConfig logger vahter msg
10241084
10251085 do ! botClient.AnswerCallbackQueryAsync( callbackQuery.Id, " Done! +1 🎯" )
10261086 else
@@ -1044,6 +1104,7 @@ let onCallbackAux
10441104}
10451105
10461106let onCallback
1107+ ( botUser : DbUser )
10471108 ( botClient : ITelegramBotClient )
10481109 ( botConfig : BotConfiguration )
10491110 ( logger : ILogger )
@@ -1078,6 +1139,7 @@ let onCallback
10781139 do ! botClient.AnswerCallbackQueryAsync( callbackQuery.Id, " Not authorized" )
10791140 else
10801141 do ! onCallbackAux
1142+ botUser
10811143 botClient
10821144 botConfig
10831145 logger
@@ -1165,7 +1227,7 @@ let onUpdate
11651227 ( update : Update ) = task {
11661228 use _ = botActivity.StartActivity( " onUpdate" )
11671229 if update.CallbackQuery <> null then
1168- do ! onCallback botClient botConfig logger update.CallbackQuery
1230+ do ! onCallback botUser botClient botConfig logger update.CallbackQuery
11691231 elif update.MessageReaction <> null then
11701232 do ! onMessageReaction botClient botConfig logger update.MessageReaction
11711233 elif update.EditedOrMessage <> null then
0 commit comments