diff options
Diffstat (limited to 'teal-src/prosody/plugins')
-rw-r--r-- | teal-src/prosody/plugins/mod_cron.tl | 141 | ||||
-rw-r--r-- | teal-src/prosody/plugins/muc/muc.lib.d.tl | 178 |
2 files changed, 319 insertions, 0 deletions
diff --git a/teal-src/prosody/plugins/mod_cron.tl b/teal-src/prosody/plugins/mod_cron.tl new file mode 100644 index 00000000..4defc808 --- /dev/null +++ b/teal-src/prosody/plugins/mod_cron.tl @@ -0,0 +1,141 @@ +module:set_global(); + +local async = require "prosody.util.async"; + +local cron_initial_delay = module:get_option_number("cron_initial_delay", 1); +local cron_check_delay = module:get_option_number("cron_check_delay", 3600); +local cron_spread_factor = module:get_option_number("cron_spread_factor", 0); + +local record map_store<K,V> + -- TODO move to somewhere sensible + get : function (map_store<K,V>, string, K) : V + set : function (map_store<K,V>, string, K, V) +end + +local enum frequency + "hourly" + "daily" + "weekly" +end + +local record task_spec + id : string -- unique id + name : string -- name or short description + when : frequency + period : number + last : integer + run : function (task_spec, integer) + save : function (task_spec, integer) + restore : function (task_spec, integer) +end + +local record task_event + source : module + item : task_spec +end + +local active_hosts : { string : boolean } = { } + +function module.add_host(host_module : moduleapi) + + local last_run_times = host_module:open_store("cron", "map") as map_store<string,integer>; + active_hosts[host_module.host] = true; + + local function save_task(task : task_spec, started_at : integer) + last_run_times:set(nil, task.id, started_at); + end + + local function restore_task(task : task_spec) + if task.last == nil then + task.last = last_run_times:get(nil, task.id); + end + end + + local function task_added(event : task_event) : boolean + local task = event.item; + if task.name == nil then + task.name = task.when; + end + if task.id == nil then + task.id = event.source.name .. "/" .. task.name:gsub("%W", "_"):lower(); + end + task.period = host_module:get_option_period(task.id:gsub("/", "_") .. "_period", "1" .. task.when, 60, 86400*7*53); + task.restore = restore_task; + task.save = save_task; + module:log("debug", "%s task %s added", task.when, task.id); + return true; + end + + local function task_removed(event : task_event) : boolean + local task = event.item; + host_module:log("debug", "Task %s removed", task.id); + return true; + end + + host_module:handle_items("task", task_added, task_removed, true); + + function host_module.unload() + active_hosts[host_module.host]=nil; + end +end + +local function should_run(task : task_spec, last : integer) : boolean + return not last or last + task.period * 0.995 <= os.time(); +end + +local function run_task(task : task_spec) + task:restore(); + if not should_run(task, task.last) then + return; + end + local started_at = os.time(); + task:run(started_at); + task.last = started_at; + task:save(started_at); +end + +local function spread(t : number, factor : number) : number + return t * (1 - factor + 2*factor*math.random()); +end + +local task_runner : async.runner_t<task_spec> = async.runner(run_task); +scheduled = module:add_timer(cron_initial_delay, function() : number + module:log("info", "Running periodic tasks"); + local delay = spread(cron_check_delay, cron_spread_factor); + for host in pairs(active_hosts) do + module:log("debug", "Running periodic tasks for host %s", host); + for _, task in ipairs(module:context(host):get_host_items("task") as { task_spec } ) do + task_runner:run(task); + end + end + module:log("debug", "Wait %gs", delay); + return delay; +end); + +-- TODO measure load, pick a good time to do stuff + +module:add_item("shell-command", { + section = "cron"; + section_desc = "View and manage recurring tasks"; + name = "tasks"; + desc = "View registered tasks"; + args = {}; + handler = function (self, filter_host : string) + local format_table = require "prosody.util.human.io".table; + local it = require "util.iterators"; + local row = format_table({ + { title = "Host", width = "2p" }; + { title = "Task", width = "3p" }; + { title = "Desc", width = "3p" }; + { title = "When", width = "1p" }; + { title = "Last run", width = "20" }; + }, self.session.width); + local print = self.session.print; + print(row()); + for host in it.sorted_pairs(filter_host and { [filter_host]=true } or active_hosts) do + for _, task in ipairs(module:context(host):get_host_items("task")) do + print(row { host, task.id, task.name, task.when, task.last and os.date("%Y-%m-%d %R:%S", task.last) or "never" }); + end + end + end; +}); diff --git a/teal-src/prosody/plugins/muc/muc.lib.d.tl b/teal-src/prosody/plugins/muc/muc.lib.d.tl new file mode 100644 index 00000000..6e9663a1 --- /dev/null +++ b/teal-src/prosody/plugins/muc/muc.lib.d.tl @@ -0,0 +1,178 @@ +local Stanza = require "prosody.util.stanza".stanza_t + +local record Room + jid : string + + enum Affiliation + "outcast" + "none" + "member" + "admin" + "owner" + end + + enum Role + "none" + "visitor" + "participant" + "moderator" + end + + record Occupant + bare_jid : string + nick : string + sessions : { string : Stanza } + role : Role + jid : string + + choose_new_primary : function (Occupant) : string + set_session : function (Occupant, string, Stanza, boolean) + remove_session : function (Occupant, string) + each_session : function (Occupant) -- TODO Iterator + + end + + -- Private properties + _jid_nick : { string : string } + _occupants : { string : Occupant } + _data : { string : any } + _affiliations : { string : Affiliation } + _affiliation_data : { string : { string : any } } + + -- Occupant methods + get_occupant_jid : function (Room, real_jid : string) : string + new_occupant : function (Room, bare_real_jid : string, nick : string) : Occupant + get_occupant_by_nick : function (Room, nick : string) : Occupant + type OccupantIterator = function ({string:Occupant}, occupant_jid : string) : string, Occupant + each_occupant : function (Room, read_only : boolean) : OccupantIterator, {string:Occupant}, nil + has_occupant : function (Room) : boolean + get_occupant_by_real_jid : function (Room, real_jid : string) : Occupant + save_occupant :function (Room, Occupant) : Occupant + + -- Affiliation methods + type AffiliationIterator = function (any, jid : string) : string, Affiliation + get_affiliation : function (Room, jid : string) : Affiliation + each_affiliation : function (Room, Affiliation) : AffiliationIterator, nil, nil + set_affiliation : function (Room, jid : string, Affiliation, reason : string, data : { string : any }) : boolean, string, string, string -- ok + error tripplet + get_affiliation_data : function (Room, jid : string, key : string) : any + set_affiliation_data : function (Room, jid : string, key : string, value : any) : boolean + get_registered_nick : function (Room, jid : string) : string + get_registered_jid : function (Room, nick : string) : string + + -- Role methods + get_default_role : function (Room, Affiliation) : Role, integer + get_role : function (Room, nick : string) : Role + may_set_role : function (Room, actor : string, Occupant, Role) : boolean + set_role : function (Room, actor : string, occupant_jid : string, Role, reason : string) : boolean, string, string, string + + -- Routing input, generally handled by mod_muc and hooked up to Prosody routing events + handle_first_presence : function (Room, table, Stanza) : boolean + handle_normal_presence : function (Room, table, Stanza) : boolean + handle_presence_to_room : function (Room, table, Stanza) : boolean + handle_presence_to_occupant : function (Room, table, Stanza) : boolean + handle_message_to_room : function (Room, table, Stanza) : boolean + handle_message_to_occupant : function (Room, table, Stanza) : boolean + handle_groupchat_to_room : function (Room, table, Stanza) : boolean + handle_iq_to_occupant : function (Room, table, Stanza) : boolean + handle_disco_info_get_query : function (Room, table, Stanza) : boolean + handle_disco_items_get_query : function (Room, table, Stanza) : boolean + handle_admin_query_set_command : function (Room, table, Stanza) : boolean + handle_admin_query_get_command : function (Room, table, Stanza) : boolean + handle_owner_query_get_to_room : function (Room, table, Stanza) : boolean + handle_owner_query_set_to_room : function (Room, table, Stanza) : boolean + handle_mediated_invite : function (Room, table, Stanza) : boolean + handle_mediated_decline : function (Room, table, Stanza) : boolean + handle_role_request : function (Room, table, Stanza) : boolean + handle_register_iq : function (Room, table, Stanza) : boolean + handle_kickable : function (Room, table, Stanza) : boolean + + -- Routing output + broadcast : function (Room, Stanza, function (nick : string, Occupant) : boolean) + broadcast_message : function (Room, Stanza) : boolean + route_stanza : function (Room, Stanza) + route_to_occupant : function (Room, Occupant, Stanza) + + -- Sending things to someone joining + publicise_occupant_status : function (Room, Occupant, x : Stanza, nick : string, actor : string, reason : string, prev_role : Role, force_unavailable : boolean, recipient : Occupant) + send_occupant_list : function (Room, to : string, filter : function (occupant_jid : string, Occupant) : boolean) + send_history : function (Room, Stanza) + send_subject : function (Room, to : string, time : number) + + respond_to_probe : function (Room, table, Stanza, Occupant) + + -- Constructors for various answer stanzas + get_disco_info : function (Room, Stanza) : Stanza + get_disco_items : function (Room, Stanza) : Stanza + + build_item_list : function (Room, Occupant, Stanza, is_anonymous : boolean, nick : string, actor_nick : string, actor_jid : string, reason : string) : Stanza + build_unavailable_presence : function (Room, from_muc_jid : string, to_jid : string) : Stanza + + -- Form handling + send_form : function (Room, table, Stanza) + get_form_layout : function (Room, actor : string) : table + process_form : function (Room, table, Stanza) : boolean + + -- Properties and configuration + get_name : function (Room) : string + set_name : function (Room, string) : boolean + get_description : function (Room) : string + set_description : function (Room, string) : boolean + get_language : function (Room) : string + set_language : function (Room, string) : boolean + get_hidden : function (Room) : boolean + set_hidden : function (Room, boolean) + get_public : function (Room) : boolean + set_public : function (Room, boolean) + get_password : function (Room) : string + set_password : function (Room, string) : boolean + get_members_only : function (Room) : boolean + set_members_only : function (Room, boolean) : boolean + get_allow_member_invites : function (Room) : boolean + set_allow_member_invites : function (Room, boolean) : boolean + get_moderated : function (Room) : boolean + set_moderated : function (Room, boolean) : boolean + get_persistent : function (Room) : boolean + set_persistent : function (Room, boolean) : boolean + get_changesubject : function (Room) : boolean + set_changesubject : function (Room, boolean) : boolean + get_subject : function (Room) : string + set_subject : function (Room, string) : boolean + get_historylength : function (Room) : integer + set_historylength : function (Room, integer) : boolean + get_presence_broadcast : function (Room) : { Role : boolean } + set_presence_broadcast : function (Room, { Role : boolean }) : boolean + + is_anonymous_for : function (Room, jid : string) : boolean + get_salt : function (Room) : string + get_occupant_id : function (Room, Occupant) + + -- Room teardown + clear : function (Room, x : Stanza) + destroy : function (Room, newjid : string, reason : string, password : string) : boolean + + -- Room state persistence + record FrozenRoom + _jid : string + _data : { string : any } + _affiliation_data : { string : { string : any } } + -- { string : Affiliation } + end + + record StateEntry + bare_jid : string + role : Role + jid : string + end + + save : function (Room, forced : boolean, savestate : boolean) : boolean + freeze : function (Room, live : boolean) : FrozenRoom, { string : StateEntry } +end + +local record lib + new_room : function (jid : string, config : { string : any }) : Room + restore_room : function (Room.FrozenRoom, { string : Room.StateEntry }) : Room + + room_mt : metatable +end + +return lib |