incoming_mms.rb

 1# frozen_string_literal: true
 2
 3class IncomingMMS
 4	def self.for(to, json)
 5		raise "Unknown encoding: #{json.inspect}" if json["encoding"] != "native"
 6
 7		txt, media = extract_parts(json)
 8
 9		EMPromise.all(
10			txt.map { |u|
11				EM::HttpRequest.new(u, tls: { verify_peer: true }).get.then(&:response)
12			}
13		).then do |fetched_txt|
14			new(to, fetched_txt, media, clean_fake_array(json["recip"]))
15		end
16	end
17
18	def self.extract_parts(json)
19		clean_fake_array(json["files"])
20			.grep_v(/\.smil\Z/i)
21			.map { |part| "#{json['url']}&authkey=#{json['authkey']}&file=#{part}" }
22			.partition { |url| url.end_with?(".txt") }
23	end
24
25	def self.clean_fake_array(str)
26		# Instead of arrays we get sent comma-seperated strings
27		# which sometimes contain blank elements or random whitespace
28		str.to_s.split(",").map(&:strip).reject(&:empty?)
29	end
30
31	def initialize(to, txt, media, recip)
32		@to = to
33		@txt = txt
34		@media = media
35		@recip = recip
36	end
37
38	# MMS text parts are separate fetched .txt files; join with paragraph breaks
39	def body_text
40		@txt.empty? ? "" : @txt.join("\n\n")
41	end
42
43	def media_urls
44		@media
45	end
46
47	def unproxied_to
48		ProxiedJID.new(@to).unproxied
49	end
50
51	def oobs
52		@media.map(&OOB.method(:new))
53	end
54
55	def addresses
56		return nil if @recip.empty?
57
58		@addresses ||= Addresses.new(
59			[{ type: "to", jid: unproxied_to }] +
60			@recip.map { |r| { type: "to", uri: "sms:+#{r}", delivered: true } }
61		)
62	end
63
64	def to_stanza
65		Blather::Stanza::Message.new(
66			addresses ? unproxied_to.domain : unproxied_to,
67			@txt.empty? ? nil : @txt.join("\n\n")
68		).tap do |m|
69			oobs.each { |oob| m << oob }
70			m << addresses if addresses
71		end
72	end
73end