message_event.rb

  1# frozen_string_literal: true
  2
  3require "json"
  4
  5module MessageEvent
  6	module Emittable
  7		def emit(redis, stream: "messages")
  8			args = to_redis_fields.flat_map { |k, v| [k, v] }
  9			redis.xadd(stream, "*", *args).catch do |e|
 10				puts "WARN Failed to emit message event: #{e.message}"
 11			end
 12		end
 13	end
 14
 15	module ValidTel
 16		def self.===(value)
 17			value.to_s.match?(/\A\+1\d{10}\z/)
 18		end
 19	end
 20
 21	class Base
 22		include Emittable
 23
 24		def initialize(event:, timestamp: nil)
 25			raise ArgumentError, "event must be a String" unless event.is_a?(String)
 26
 27			timestamp = Time.iso8601(timestamp) if timestamp.is_a?(String)
 28			@event = event
 29			@timestamp = timestamp
 30		end
 31
 32		attr_reader :event, :timestamp
 33
 34		# We use to_redis_fields instead of to_h because we want to serialize values
 35		# (e.g., Time -> ISO8601 string, arrays -> JSON). A plain to_h would return
 36		# raw Ruby objects, which is less useful for parsing from other
 37		# projects/languages.
 38		def to_redis_fields
 39			fields = {
 40				"event" => @event,
 41				"source" => "bwmsgsv2"
 42			}
 43			if @timestamp
 44				fields["timestamp"] = @timestamp.is_a?(Time) ? @timestamp.iso8601 : @timestamp
 45			end
 46			fields
 47		end
 48	end
 49
 50	class Message < Base
 51		def initialize(to:, from:, body:, owner:, bandwidth_id: nil, media_urls: [], **kwargs)
 52			raise ArgumentError, "owner must be a valid US telephone number" unless ValidTel === owner
 53			raise ArgumentError, "to must be an array" unless to.is_a?(Array)
 54			raise ArgumentError, "body must be a String" unless body.is_a?(String)
 55			raise ArgumentError, "media_urls must be an Array" unless media_urls.is_a?(Array)
 56			if bandwidth_id && !bandwidth_id.is_a?(String)
 57				raise ArgumentError, "bandwidth_id must be a String"
 58			end
 59
 60			@from = from
 61			@to = to
 62			@body = body
 63			@media_urls = media_urls
 64			@owner = owner
 65			@bandwidth_id = bandwidth_id if bandwidth_id
 66			super(**kwargs)
 67		end
 68
 69		attr_reader :from, :to, :body, :media_urls
 70
 71		def to_redis_fields
 72			fields = super.merge(
 73				"owner" => @owner,
 74				"from" => @from,
 75				"to" => JSON.dump(@to)
 76			)
 77			fields["body"] = @body unless @body.nil?
 78			fields["bandwidth_id"] = @bandwidth_id
 79			fields["media_urls"] = JSON.dump(@media_urls) unless @media_urls.empty?
 80			fields
 81		end
 82	end
 83
 84	class In < Message
 85		def initialize(**kwargs)
 86			super(event: "in", **kwargs)
 87		end
 88
 89		attr_reader :owner, :bandwidth_id
 90	end
 91
 92	class Out < Message
 93		def initialize(stanza_id:, **kwargs)
 94			raise ArgumentError, "stanza_id must be a String" unless stanza_id.is_a?(String)
 95
 96			@stanza_id = stanza_id
 97			super(event: "out", **kwargs)
 98		end
 99
100		attr_reader :stanza_id, :bandwidth_id
101
102		def to_redis_fields
103			super.merge("stanza_id" => @stanza_id)
104		end
105	end
106
107	class Thru < Message
108		def initialize(stanza_id:, **kwargs)
109			raise ArgumentError, "stanza_id must be a String" unless stanza_id.is_a?(String)
110
111			@stanza_id = stanza_id
112			super(event: "thru", **kwargs)
113		end
114
115		attr_reader :stanza_id
116
117		def to_redis_fields
118			super.merge("stanza_id" => @stanza_id)
119		end
120	end
121
122	class Delivered < Base
123		def initialize(stanza_id:, bandwidth_id:, **kwargs)
124			raise ArgumentError, "stanza_id must be a String" unless stanza_id.is_a?(String)
125			raise ArgumentError, "bandwidth_id must be a String" unless bandwidth_id.is_a?(String)
126
127			@stanza_id = stanza_id
128			@bandwidth_id = bandwidth_id
129			super(event: "delivered", **kwargs)
130		end
131
132		attr_reader :stanza_id, :bandwidth_id
133
134		def to_redis_fields
135			super.merge(
136				"stanza_id" => @stanza_id,
137				"bandwidth_id" => @bandwidth_id
138			)
139		end
140	end
141
142	class Failed < Base
143		def initialize(stanza_id:, bandwidth_id:, error_code:, error_description:, **kwargs)
144			raise ArgumentError, "stanza_id must be a String" unless stanza_id.is_a?(String)
145			raise ArgumentError, "bandwidth_id must be a String" unless bandwidth_id.is_a?(String)
146			raise ArgumentError, "error_code must be a String" unless error_code.is_a?(String)
147			raise ArgumentError, "error_description must be a String" unless error_description.is_a?(String)
148
149			@stanza_id = stanza_id
150			@bandwidth_id = bandwidth_id
151			@error_code = error_code
152			@error_description = error_description
153			super(event: "failed", **kwargs)
154		end
155
156		attr_reader :stanza_id, :bandwidth_id, :error_code, :error_description
157
158		def to_redis_fields
159			super.merge(
160				"stanza_id" => @stanza_id,
161				"bandwidth_id" => @bandwidth_id,
162				"error_code" => @error_code,
163				"error_description" => @error_description
164			)
165		end
166	end
167end