Unable to Bulk Suspend/Deactivate Users in Rails Console on Discourse 3.5.0.beta5-dev

Hi everyone,

I’m facing a persistent issue trying to bulk suspend/deactivate users on my Discourse instance (version 3.5.0.beta5-dev) via the Rails console. I’m trying to exclude my admin account (hoangthai, user ID 100) and permanently suspend all other active non-admin/non-moderator users with a custom reason “Chưa gia hạn tài khoản” (Account renewal pending).

I’ve tried several approaches based on common Discourse console commands and debugging suggestions, but the accounts remain active: true even though suspended_at and suspended_till might get set.

Here’s a breakdown of what I’ve attempted and the errors/results:

My Goal: Suspend all users permanently (except hoangthai, ID 100) with the reason “Chưa gia hạn tài khoản” (Account renewal pending).

Attempts & Issues:

  1. Initial Attempt (Setting suspend_reason, suspended_at, suspended_till directly):

    admin_id_to_exclude = 100
    User.where.not(id: admin_id_to_exclude).each do |user|
      if user.active? && !user.admin? && !user.moderator?
        user.suspend_reason = "Tài khoản tạm khóa do nghỉ phép dài hạn." # First reason attempt
        user.suspended_at = Time.current
        user.suspended_till = Time.current + 100.years
        user.save!
        puts "Khóa tài khoản: #{user.username}"
      end
    end
    
    • Result: NoMethodError: undefined method 'suspend_reason=' for an instance of User (suggested suspended_at=). This indicated suspend_reason was not the correct attribute.
  2. Second Attempt (Using UserSuspension.suspend_user as suggested for newer versions):

    admin_id_to_exclude = 100
    User.where.not(id: admin_id_to_exclude).each do |user|
      if user.active? && !user.admin? && !user.moderator?
        UserSuspension.suspend_user(
            user,
            Time.current + 100.years,
            nil, # suspend_reason_id
            "Tài khoản tạm khóa: Nghỉ phép dài hạn.", # custom_suspend_reason
            Discourse.system_user
        )
        puts "Đã khóa tài khoản: #{user.username}"
      end
    end
    
    • Result: NameError: uninitialized constant UserSuspension (suggested UserSuspender). This indicated the class name was incorrect.
  3. Third Attempt (Corrected class name to UserSuspender):

    admin_id_to_exclude = 100
    User.where.not(id: admin_id_to_exclude).each do |user|
      if user.active? && !user.admin? && !user.moderator?
        UserSuspender.suspend_user( # Still trying suspend_user
            user,
            Time.current + 100.years,
            nil,
            "Tài khoản tạm khóa: Nghỉ phép dài hạn.",
            Discourse.system_user
        )
        puts "Đã khóa tài khoản: #{user.username}"
      end
    end
    
    • Result: NoMethodError: undefined method 'suspend_user' for class UserSuspender (suggested UserSuspended?). This showed the method name was also incorrect.
  4. Fourth Attempt (Using UserSuspender.new(...).suspend as the service object pattern):

    admin_id_to_exclude = 100
    User.where.not(id: admin_id_to_exclude).each do |user|
      if user.active? && !user.admin? && !user.moderator?
        begin
            UserSuspender.new(
                user,
                Discourse.system_user,
                reason: "Tài khoản tạm khóa: Nghỉ phép dài hạn.",
                suspended_till: nil # For permanent suspension
            ).suspend
            puts "Đã khóa tài khoản: #{user.username}"
        rescue => e
            puts "Lỗi khi khóa tài khoản #{user.username}: #{e.message}"
        end
      end
    end
    
    • Result: This command ran without errors in the console. It printed “Đã khóa tài khoản: [username]”.
    • However, when checking user status in the admin UI or directly querying user.active in console, users still showed active: true. Only suspended_at and suspended_till were updated (e.g., suspended_at: "2025-05-26 00:37:01.406064419 +0000", suspended_till: nil). The accounts were still able to log in.
  5. Fifth Attempt (Directly setting active = false along with suspension fields):

    admin_id_to_exclude = 100
    User.where.not(id: admin_id_to_exclude).each do |user|
      if user.active? && !user.admin? && !user.moderator?
        begin
            user.suspended_at = Time.current
            user.suspended_till = nil # Permanent
            user.custom_suspension_reason = "Chưa gia hạn tài khoản" # Custom reason
            user.active = false # Crucial part
            user.save!
            puts "Đã **khóa VĨNH VIỄN** và vô hiệu hóa tài khoản: #{user.username}"
        rescue => e
            puts "LỖI khi xử lý tài khoản #{user.username}: #{e.message}"
        end
      end
    end
    
    • Result: This command also ran without errors and printed success messages.
    • However, the outcome was the same: active: true remained, and suspended_at/suspended_till were updated, but accounts were not actually deactivated or prevented from logging in.

My Current Situation:

I can manually suspend users via the admin UI (Admin → Users → Suspend) and they get correctly suspended (e.g., as shown in your previous screenshot with “Tạm khóa bởi: hoangthai, Lý do: Chưa gia hạn tài khoản”, and they cannot log in). This confirms the core functionality works.

I suspect there’s a specific API nuance in Discourse 3.5.0.beta5-dev for programmatically suspending/deactivating users that I’m missing, or perhaps a caching issue that’s not being cleared, or a background job that needs to run.

Could anyone with experience in Discourse 3.5.x beta versions or deep Rails console knowledge offer some guidance on how to correctly achieve a bulk, permanent suspension that actually prevents user logins and sets the active status to false?

Thank you for your time and help!

1 Like

I don’t believe deactivation is part of the user suspension process. In the UI, you would need to press the ‘deactivate’ button separately if you wanted a returning suspended user to also reverify their email.

Deciding if that’s what you want and tackling it as a separate operation may get better results. :crossed_fingers:

There’s some guidance for bulk suspensions in this topic if that helps Administrative Bulk Operations

Though if you try that and find it’s outdated let them know and they should be able to update the guide accordingly.

1 Like