diff --git a/app/services/edit_status_service.rb b/app/services/edit_status_service.rb index 0003eaec..6984ba2b 100644 --- a/app/services/edit_status_service.rb +++ b/app/services/edit_status_service.rb @@ -25,6 +25,7 @@ class EditStatusService < BaseService return idempotency_duplicate if idempotency_given? && idempotency_duplicate? + validate_similarity! validate_links! validate_media! preprocess_attributes! @@ -92,6 +93,10 @@ class EditStatusService < BaseService raise GabSocial::ValidationError, I18n.t('media_attachments.validations.images_and_video') if @media.size > 1 && hasVideoOrGif end + def validate_similarity! + raise GabSocial::NotPermittedError if StatusSimilarityService.new.call?(@text, @account.id) + end + def validate_links! raise GabSocial::LinkBlockedError if LinkBlock.block?(@text) end diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index c93bad58..e74fe4c8 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -34,6 +34,7 @@ class PostStatusService < BaseService return idempotency_duplicate if idempotency_given? && idempotency_duplicate? + validate_similarity! validate_links! validate_media! validate_group! @@ -159,6 +160,10 @@ class PostStatusService < BaseService raise GabSocial::ValidationError, I18n.t('media_attachments.validations.images_and_video') if @media.size > 1 && hasVideoOrGif end + def validate_similarity! + raise GabSocial::NotPermittedError if StatusSimilarityService.new.call?(@text, @account.id) + end + def validate_links! raise GabSocial::NotPermittedError if LinkBlock.block?(@text) end diff --git a/app/services/status_similarity_service.rb b/app/services/status_similarity_service.rb new file mode 100644 index 00000000..285ed0ad --- /dev/null +++ b/app/services/status_similarity_service.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'similar_text' + +class StatusSimilarityService < BaseService + def call?(status_text = "", account_id = nil) + @status_text = status_text + @account_id = account_id + + # Not alike if no status_text or no account + # : todo : come up with solution for same image spamming + return false if @status_text.length == 0 || @account_id.nil? + + alike? + end + + private + + def alike? + last_status_text = nil + key = "last_status_from_account:#{@account_id}" + + Redis.current.with do |conn| + last_status_text = conn.get(key) || "" + conn.setex(key, 300, @status_text) + end + + if last_status_text.nil? || last_status_text.empty? || last_status_text.length == 0 + return false + end + + likeness = last_status_text.similar(@status_text) + + likeness > 85 + end + +end