# HG changeset patch # User Trần H. Trung # Date 1684860300 -25200 # Node ID f16720087ef2b2ca52c9d3a0ed1f4132bf760cc8 # Parent bb083e9f78dd4cad7ebcdd9c5ff749808c1a5413 mod_webpresence: Add opt-in mechanism. diff -r bb083e9f78dd -r f16720087ef2 mod_webpresence/README.markdown --- a/mod_webpresence/README.markdown Thu May 18 21:11:13 2023 +0200 +++ b/mod_webpresence/README.markdown Tue May 23 23:45:00 2023 +0700 @@ -2,65 +2,80 @@ labels: - 'Stage-Stable' summary: Display your online status in web pages +rockspec: + build: + copy_directories: + - icons ... Introduction ============ Quite often you may want to publish your Jabber status to your blog or -website. mod\_webpresence allows you to do exactly this. - -Details -======= - -This module uses Prosody's built-in HTTP server (it does not depend on -mod\_httpserver). It supplies a status icon representative of a user's -online state. +website. mod\_webpresence allows you to do exactly this via adhoc control. Installation ============ -Simply copy mod\_webpresence.lua to your modules directory, the image -files are embedded within it. Then add "webpresence" to your -modules\_enabled list. +Copy mod\_webpresence.lua to your modules directory then add it to your +modules\_enabled list: + +``` -Usage -===== + modules_enabled = { + "webpresence"; + }; + +``` + +Configuration & Usage +===================== + +There is a set of icons supplied with the module. But you can configure it to +load your own in the config file: -Once loaded you can embed the icon into a page using a simple `` -tag, as follows: +``` + + webpresence_icons = "/path/to/your/icons"; - +``` + +Beware that the icon files must have the same names as the default files. + +This module will always returns offline until you enable it via adhoc. -Alternatively, it can be used to get status name as plaint text, status -message as plain text or html-code for embedding on web-pages. +You can embed the icon into a page using a simple `` tag, as follows: + + -To get status name in plain text you can use something like that link: -`http://prosody.example.com:5280/status/john.smith/text` +Alternatively, it can be used to get status name as plain text, status message +as plain text or html-code for embedding on web-pages. + +To get status name in plain text you can use something like this link: +`http://prosody.example.com:5280/status/john.smith@domain.net/text` To get status message as plain text you can use something like following -link: `http://prosody.example.com:5280/status/john.smith/message` +link: `http://prosody.example.com:5280/status/john.smith@domain.net/message` -To get html code, containig status name, status image and status message -(if set): `http://prosody.example.com:5280/status/john.smith/html` - -All other +To get html code, containing status name, status image and status message +(if set): `http://prosody.example.com:5280/status/john.smith@domain.net/html` Compatibility ============= - ----- ------- - trunk Works - 0.10 Works - 0.9 Works - 0.8 Works - 0.7 Works - 0.6 Works - ----- ------- + ----- ------- + trunk Works + 0.12.3 Works + 0.10 Works + 0.9 Works + 0.8 Works + 0.7 Works + 0.6 Works + ----- ------- Todo ==== - Display PEP information (maybe a new plugin?) -- More (free) iconsets - Internal/external image generator (GD, ImageMagick) +- Display the correct boolean in the first form. diff -r bb083e9f78dd -r f16720087ef2 mod_webpresence/mod_webpresence.lua --- a/mod_webpresence/mod_webpresence.lua Thu May 18 21:11:13 2023 +0200 +++ b/mod_webpresence/mod_webpresence.lua Tue May 23 23:45:00 2023 +0700 @@ -1,13 +1,156 @@ +module:depends("adhoc"); module:depends("http"); +local moduleHost = module.host; +local moduleName = module:get_name(); +local jid = require "util.jid"; local jid_split = require "util.jid".prepped_split; +local serialization = require "util.serialization"; + +-- ADHOC +local storage = module:open_store(moduleName, "keyval"); +local utilDataforms = require "util.dataforms"; +local utilAdhoc = require "util.adhoc"; +local adhoc_new = module:require("adhoc").new; + +local function webpresence_set(user, value) + local ok, err = storage:set(user, value); -- value is table. + + if not ok or err then + module:log(error, "Could not write data %s", tostring(user)); + return ok, err; + else + return ok; + end +end +local function webpresence_get(user) + local result = storage:get(user); + if not result then + result = { ["webpresence"] = false }; + webpresence_set(user, result); + end + return result[moduleName]; -- bool +end + +local form = utilDataforms.new { + title = "Web Presence Policy"; + instructions = "Your webpresence shows offline by default"; + { + type = "boolean"; + name = moduleName; + label = "Show"; + value = webpresence_get(); + }; +}; +local formResult = utilDataforms.new { + title = "Web Presence Policy"; + { + type = "boolean"; + name = moduleName; + label = "Show"; + value; + }; + { + type = "text-multi"; + name = "url"; + label = "Check your presence at"; + value = "text-multi\n"; + }; +}; + +local function webpresence_url(jid_bare) + local config, path = module:get_option("http_paths"); + if config then + for k, v in pairs(config) do + if k == moduleName then path = v; + else path = "/status" .. "/"; end + end + else path = "/status" .. "/"; end + + local urlConfig = module:get_option_string("http_external_link"); + local urlBase = urlConfig..path..jid_bare; + local style = { "/text", "/message", "/json", "/html" }; + local urlResult = urlBase.."\n"; + for _, v in ipairs(style) do + urlResult = urlResult..urlBase..v.."\n"; + end + return urlResult; +end + +local adhoc_handler = utilAdhoc.new_simple_form(form, function(fields, state, data) + local jid_bare = jid.bare(data.from); + local user, host = jid_split(jid_bare); + + local oldData, _ = storage:get(user); + oldValue = webpresence_get(user); + form.webpresence = oldValue; + + local urlResult = webpresence_url(jid_bare); + local newValue = { + [moduleName] = fields.webpresence; + }; + + if state then + return { + status = "completed"; + info = "No change for: "..tostring(data.from).." …\n" + .."Old data: "..serialization.serialize(oldData).."\n" + .."New data: "..serialization.serialize(newValue).."\n"; + result = { + layout = formResult; + values = { + webpresence = oldValue; + url = urlResult; + }; + }; + }; + else + local resultSet, resultErr = webpresence_set(user, newValue) + if not resultSet or resultErr then + module:log(error, "Could not set value: %s", errOut); + return { + status = "completed"; + info = "Could not set value: "..tostring(data.from).." …\n" + .."Old data: "..serialization.serialize(oldData).."\n" + .."New data: "..serialization.serialize(newValue).."\n" + .."Error: "..errOut.."\n"; + result = { + layout = formResult; + values = { + webpresence = newValue[moduleName]; + url = urlResult; + }; + }; + }; + else + return { + status = "completed"; + info = "Changing value for: "..tostring(data.from).." …\n" + .."Old data: "..serialization.serialize(oldData).."\n" + .."New data: "..serialization.serialize(newValue).."\n"; + result = { + layout = formResult; + values = { + webpresence = newValue[moduleName]; + url = urlResult; + }; + }; + }; + end + end +end); + +module:provides("adhoc", adhoc_new("Web Presence Policy", moduleName, adhoc_handler, "any")); + +-- HTTP local b64 = require "util.encodings".base64.encode; local sha1 = require "util.hashes".sha1; local stanza = require "util.stanza".stanza; local json = require "util.json".encode_ordered; +local usermanager = require "core.usermanager"; local function require_resource(name) - local icon_path = module:get_option_string("presence_icons", "icons"); + local icon_path = module:get_option_string("webpresence_icons", "icons"); local f, err = module:load_resource(icon_path.."/"..name); if f then return f:read("*a"); @@ -18,101 +161,112 @@ local statuses = { online = {}, away = {}, xa = {}, dnd = {}, chat = {}, offline = {} }; +local function user_list(host) return user:list(host); end + local function handle_request(event, path) - local status, message; - local jid, type = path:match("([^/]+)/?(.*)$"); - if jid then - local user, host = jid_split(jid); - if host and not user then - user, host = host, event.request.headers.host; - if host then host = host:gsub(":%d+$", ""); end - end - if user and host then - local user_sessions = hosts[host] and hosts[host].sessions[user]; - if user_sessions and user_sessions.top_resources then - status = user_sessions.top_resources[1]; - if status and status.presence then - message = status.presence:child_with_name("status"); - status = status.presence:child_with_name("show"); - if not status then - status = "online"; - else - status = status:get_text(); - end - if message then - message = message:get_text(); - end - end - end - end - end - status = status or "offline"; + local status, message; + local jid, type = path:match("([^/]+)/?(.*)$"); + if jid then + local user, host = jid_split(jid); + if host and not user then + user, host = host, event.request.headers.host; + if host then host = host:gsub(":%d+$", ""); end + end + if host ~= moduleHost then + status = "offline"; + else + if user and host and usermanager.user_exists(user, host) then + local user_sessions = hosts[host] and hosts[host].sessions[user]; + local show = webpresence_get(user) + if show == false then + status = "offline"; + else + if user_sessions and user_sessions.top_resources then + status = user_sessions.top_resources[1]; + if status and status.presence then + message = status.presence:child_with_name("status"); + status = status.presence:child_with_name("show"); + if not status then + status = "online"; + else + status = status:get_text(); + end + if message then + message = message:get_text(); + end + end + end + end + end + end + status = status or "offline"; + end - statuses[status].image = function() - return { status_code = 200, headers = { content_type = "image/png" }, - body = require_resource("status_"..status..".png") - }; +statuses[status].image = function() + return { status_code = 200, headers = { content_type = "image/png" }, + body = require_resource("status_"..status..".png") +}; end; statuses[status].html = function() - local jid_hash = sha1(jid, true); - return { status_code = 200, headers = { content_type = "text/html" }, - body = [[]].. - tostring( - stanza("html") - :tag("head") - :tag("title"):text("XMPP Status Page for "..jid):up():up() - :tag("body") - :tag("div", { id = jid_hash.."_status", class = "xmpp_status" }) - :tag("img", { id = jid_hash.."_img", class = "xmpp_status_image xmpp_status_"..status, - src = "data:image/png;base64,"..b64(require_resource("status_"..status..".png")) }):up() - :tag("span", { id = jid_hash.."_status_name", class = "xmpp_status_name" }) - :text("\194\160"..status):up() - :tag("span", { id = jid_hash.."_status_message", class = "xmpp_status_message" }) - :text(message and "\194\160"..message.."" or "") - ) - }; + local jid_hash = sha1(jid, true); + return { status_code = 200, headers = { content_type = "text/html" }, + body = [[]].. + tostring( + stanza("html") + :tag("head") + :tag("title"):text("XMPP Status Page for "..jid):up():up() + :tag("body") + :tag("div", { id = jid_hash.."_status", class = "xmpp_status" }) + :tag("img", { id = jid_hash.."_img", class = "xmpp_status_image xmpp_status_"..status, + src = "data:image/png;base64,"..b64(require_resource("status_"..status..".png")) }):up() + :tag("span", { id = jid_hash.."_status_name", class = "xmpp_status_name" }) + :text("\194\160"..status):up() + :tag("span", { id = jid_hash.."_status_message", class = "xmpp_status_message" }) + :text(message and "\194\160"..message.."" or "") + ) + }; end; statuses[status].text = function() - return { status_code = 200, headers = { content_type = "text/plain" }, - body = status - }; + return { status_code = 200, headers = { content_type = "text/plain" }, + body = status + }; end; statuses[status].message = function() - return { status_code = 200, headers = { content_type = "text/plain" }, - body = (message and message or "") - }; + return { status_code = 200, headers = { content_type = "text/plain" }, + body = (message and message or "") + }; end; statuses[status].json = function() - return { status_code = 200, headers = { content_type = "application/json" }, - body = json({ - jid = jid, - show = status, - status = (message and message or "null") - }) - }; + return { status_code = 200, headers = { content_type = "application/json" }, + body = json({ + jid = jid, + show = status, + status = (message and message or "null") + }) + }; end; statuses[status].xml = function() - return { status_code = 200, headers = { content_type = "application/xml" }, - body = [[]].. - tostring( - stanza("result") - :tag("jid"):text(jid):up() - :tag("show"):text(status):up() - :tag("status"):text(message) - ) - }; + return { status_code = 200, headers = { content_type = "application/xml" }, + body = [[]].. + tostring( + stanza("result") + :tag("jid"):text(jid):up() + :tag("show"):text(status):up() + :tag("status"):text(message) + ) + }; end if ((type == "") or (not statuses[status][type])) then - type = "image" + type = "image" end; return statuses[status][type](); end module:provides("http", { - default_path = "/status"; - route = { - ["GET /*"] = handle_request; - }; + default_path = "/status"; + route = { + ["GET /*"] = handle_request; + }; });