111 lines
2.6 KiB
Ruby
111 lines
2.6 KiB
Ruby
class SmsMessage < ApplicationRecord
|
|
# Normalize metadata to always be a Hash
|
|
attribute :metadata, :jsonb, default: {}
|
|
|
|
belongs_to :gateway, optional: true
|
|
|
|
validates :phone_number, presence: true
|
|
validates :message_body, presence: true, length: { maximum: 1600 }
|
|
validates :direction, presence: true, inclusion: { in: %w[inbound outbound] }
|
|
validates :message_id, presence: true, uniqueness: true
|
|
validates :status, inclusion: { in: %w[pending queued sent delivered failed] }
|
|
|
|
validate :phone_number_format
|
|
|
|
before_validation :ensure_metadata_is_hash
|
|
before_validation :generate_message_id, on: :create
|
|
before_validation :normalize_phone_number
|
|
after_create_commit :enqueue_sending, if: :outbound?
|
|
|
|
scope :pending, -> { where(status: "pending") }
|
|
scope :queued, -> { where(status: "queued") }
|
|
scope :sent, -> { where(status: "sent") }
|
|
scope :delivered, -> { where(status: "delivered") }
|
|
scope :failed, -> { where(status: "failed") }
|
|
scope :inbound, -> { where(direction: "inbound") }
|
|
scope :outbound, -> { where(direction: "outbound") }
|
|
scope :recent, -> { order(created_at: :desc) }
|
|
|
|
# Check message direction
|
|
def outbound?
|
|
direction == "outbound"
|
|
end
|
|
|
|
def inbound?
|
|
direction == "inbound"
|
|
end
|
|
|
|
# Check if message can be retried
|
|
def can_retry?
|
|
failed? && retry_count < 3
|
|
end
|
|
|
|
# Mark message as sent
|
|
def mark_sent!(gateway)
|
|
update!(
|
|
status: "sent",
|
|
gateway: gateway,
|
|
sent_at: Time.current
|
|
)
|
|
gateway.increment_sent_count!
|
|
end
|
|
|
|
# Mark message as delivered
|
|
def mark_delivered!
|
|
update!(
|
|
status: "delivered",
|
|
delivered_at: Time.current
|
|
)
|
|
end
|
|
|
|
# Mark message as failed
|
|
def mark_failed!(error_msg = nil)
|
|
update!(
|
|
status: "failed",
|
|
failed_at: Time.current,
|
|
error_message: error_msg
|
|
)
|
|
end
|
|
|
|
# Increment retry counter
|
|
def increment_retry!
|
|
increment!(:retry_count)
|
|
end
|
|
|
|
# Check if message status is failed
|
|
def failed?
|
|
status == "failed"
|
|
end
|
|
|
|
private
|
|
|
|
def generate_message_id
|
|
self.message_id ||= "msg_#{SecureRandom.hex(16)}"
|
|
end
|
|
|
|
def normalize_phone_number
|
|
return unless phone_number.present?
|
|
|
|
# Remove spaces and special characters
|
|
self.phone_number = phone_number.gsub(/[^\d+]/, "")
|
|
end
|
|
|
|
def phone_number_format
|
|
return unless phone_number.present?
|
|
|
|
phone = Phonelib.parse(phone_number)
|
|
unless phone.valid?
|
|
errors.add(:phone_number, "is not a valid phone number")
|
|
end
|
|
end
|
|
|
|
def enqueue_sending
|
|
SendSmsJob.perform_later(id)
|
|
end
|
|
|
|
def ensure_metadata_is_hash
|
|
self.metadata = {} if metadata.nil?
|
|
self.metadata = {} unless metadata.is_a?(Hash)
|
|
end
|
|
end
|