snikket.rb

  1# frozen_string_literal: true
  2
  3require "blather"
  4require "em-http"
  5require "em_promise"
  6require "em-synchrony/em-http" # For apost vs post
  7require "link-header-parser"
  8
  9module Snikket
 10	class Launch < Blather::Stanza::Iq
 11		register nil, "launch", "xmpp:snikket.org/hosting/v1"
 12
 13		def self.new(type=nil, to=nil, id=nil, domain: nil)
 14			stanza = super(type || :set, to, id)
 15			node = Nokogiri::XML::Node.new("launch", stanza.document)
 16			node.default_namespace = registered_ns
 17			stanza << node
 18			stanza.domain = domain if domain
 19			stanza
 20		end
 21
 22		def domain=(domain)
 23			query.at_xpath("./ns:domain", ns: self.class.registered_ns)&.remove
 24			node = Nokogiri::XML::Node.new("domain", document)
 25			node.default_namespace = self.class.registered_ns
 26			node.content = domain
 27			query << node
 28		end
 29
 30		def query
 31			at_xpath("./ns:launch", ns: self.class.registered_ns)
 32		end
 33	end
 34
 35	class Launched < Blather::Stanza::Iq
 36		register :snikket_launched, "launched", "xmpp:snikket.org/hosting/v1"
 37
 38		def instance_id
 39			query
 40				.at_xpath("./ns:instance-id", ns: self.class.registered_ns)
 41				&.content
 42		end
 43
 44		def bootstrap_token
 45			query
 46				.at_xpath("./ns:bootstrap/ns:token", ns: self.class.registered_ns)
 47				&.content
 48		end
 49
 50		def bootstrap_uri(instance_domain)
 51			"https://#{instance_domain}/invites_bootstrap?token=#{bootstrap_token}"
 52		end
 53
 54		def fetch_invite(instance_domain)
 55			url = bootstrap_uri(instance_domain)
 56			EM::HttpRequest.new(
 57				url, tls: { verify_peer: true }
 58			).ahead(redirects: 5).then { |res|
 59				LinkHeaderParser.parse(
 60					Array(res.response_header["LINK"]), base: url
 61				).group_by_relation_type[:alternate]&.find do |header|
 62					URI.parse(header.target_uri).scheme == "xmpp"
 63				end&.target_uri
 64			}.catch { nil }
 65		end
 66
 67		def query
 68			at_xpath("./ns:launched", ns: self.class.registered_ns)
 69		end
 70	end
 71
 72	class Instance < Blather::Stanza::Iq
 73		register :snikket_instance, "instance", "xmpp:snikket.org/hosting/v1"
 74
 75		def self.new(type=nil, to=nil, id=nil, instance_id: nil)
 76			stanza = super(type || :get, to, id)
 77			node = Nokogiri::XML::Node.new("instance", stanza.document)
 78			node.default_namespace = registered_ns
 79			stanza << node
 80			stanza.instance_id = instance_id if instance_id
 81			stanza
 82		end
 83
 84		def inherit(*)
 85			query.remove
 86			super
 87		end
 88
 89		def instance_id=(instance_id)
 90			query.at_xpath("./ns:instance-id", ns: self.class.registered_ns)&.remove
 91			node = Nokogiri::XML::Node.new("instance-id", document)
 92			node.default_namespace = self.class.registered_ns
 93			node.content = instance_id
 94			query << node
 95		end
 96
 97		def instance_id
 98			query
 99				.at_xpath("./ns:instance-id", ns: self.class.registered_ns)
100				&.content
101		end
102
103		def update_needed?
104			!!query.at_xpath("./ns:update-needed", ns: self.class.registered_ns)
105		end
106
107		def operation
108			query.at_xpath("./ns:operation", ns: self.class.registered_ns)
109		end
110
111		def status
112			query
113				.at_xpath("./ns:status", ns: self.class.registered_ns)
114				&.content&.to_sym
115		end
116
117		def query
118			at_xpath("./ns:instance", ns: self.class.registered_ns)
119		end
120	end
121end