Changeset

3682:47e1c94fb6fb

mod_muc_media_metadata: Module to automatically fetch metadata for posted media
author Matthew Wild <mwild1@gmail.com>
date Sun, 29 Sep 2019 16:47:57 +0100 (2019-09-29)
parents 3681:d267e381255f
children 3683:1432020c33c7
files mod_muc_media_metadata/README.md mod_muc_media_metadata/mod_muc_media_metadata.lua
diffstat 2 files changed, 99 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_muc_media_metadata/README.md	Sun Sep 29 16:47:57 2019 +0100
@@ -0,0 +1,38 @@
+# Introduction
+
+This module adds additional metadata to media shared in a MUC. This can help clients
+make decisions and provide better UI and enhanced privacy, by knowing things like file
+size without needing to make external network requests.
+
+# Configuring
+
+## Enabling
+
+``` {.lua}
+Component "rooms.example.net" "muc"
+modules_enabled = {
+    "muc_hide_media";
+}
+```
+
+## Settings
+
+There are no configuration options for this module.
+
+# Developers
+
+Example stanza:
+
+```
+<message from="test@rooms.example.com/matthew" id="9f45a784-5e5b-4db5-a9b3-8ea1d7c1162d" type="groupchat">
+  <body>https://matthewwild.co.uk/share.php/70334772-ff74-439b-8173-a71e40ca28db/mam-flow.png</body>
+  <x xmlns="jabber:x:oob">
+    <url>https://matthewwild.co.uk/share.php/70334772-ff74-439b-8173-a71e40ca28db/mam-flow.png</url>
+    <metadata xmlns="https://prosody.im/protocol/media-metadata#0">
+      <bytes>15690</bytes>
+      <type>image/png</type>
+      <blurhash>LEHV6nWB2yk8pyo0adR*.7kCMdnj</blurhash>
+    </metadata>
+  </x>
+</message>
+```
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_muc_media_metadata/mod_muc_media_metadata.lua	Sun Sep 29 16:47:57 2019 +0100
@@ -0,0 +1,61 @@
+local async = require "util.async";
+local promise = require "util.promise";
+local http = require "net.http";
+local st = require "util.stanza";
+
+local xmlns_metadata = "https://prosody.im/protocol/media-metadata#0"
+
+local fetch_headers = {
+	bytes = "content-length";
+	type = "content-type";
+	etag = "etag";
+	blurhash = "blurhash";
+};
+
+local function fetch_media_metadata(url)
+	return promise.new(function (resolve)
+		http.request(url, { method = "HEAD" }, function (body, code, response) --luacheck: ignore 212/body
+			if code == 200 then
+				local metadata = {};
+				for metadata_name, header_name in pairs(fetch_headers) do
+					metadata[metadata_name] = response.headers[header_name];
+				end
+				resolve(metadata);
+			else
+				resolve(nil);
+			end
+		end);
+	end);
+end
+
+local function metadata_to_tag(metadata)
+	if not metadata then return; end
+
+	local metadata_tag = st.stanza("metadata", { xmlns = xmlns_metadata });
+	for k, v in pairs(metadata) do
+		metadata_tag:text_tag(k, v)
+	end
+
+	return metadata_tag;
+end
+
+module:hook("muc-occupant-groupchat", function (event)
+	local stanza = event.stanza;
+
+	local promises;
+
+	for oob in stanza:childtags("x", "jabber:x:oob") do
+		if not promises then promises = {}; end
+		local url = oob:get_child_text("url");
+		local p = fetch_media_metadata(url)
+			:next(metadata_to_tag)
+			:next(function (metadata_tag)
+				oob:add_child(metadata_tag);
+			end);
+		table.insert(promises, p);
+	end
+
+	if not promises then return; end
+
+	async.wait(promise.all(promises));
+end);