2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 Waqas Hussain
4 -- Copyright (C) 2014 Daurnimator
6 -- This project is MIT/X11 licensed. Please see the
7 -- COPYING file in the source package for more information.
10 local gettime
= os
.time
;
11 local datetime
= require
"util.datetime";
12 local st
= require
"util.stanza";
14 local default_history_length
= 20;
15 local max_history_length
= module
:get_option_number("max_history_messages", math
.huge
);
17 local function set_max_history_length(_max_history_length
)
18 max_history_length
= _max_history_length
or math
.huge
;
21 local function get_historylength(room
)
22 return math
.min(room
._data
.history_length
or default_history_length
, max_history_length
);
25 local function set_historylength(room
, length
)
27 length
= assert(tonumber(length
), "Length not a valid number");
29 if length
== default_history_length
then length
= nil; end
30 room
._data
.history_length
= length
;
34 -- Fix for clients who don't support XEP-0045 correctly
35 -- Default number of history messages the room returns
36 local function get_defaulthistorymessages(room
)
37 return room
._data
.default_history_messages
or default_history_length
;
39 local function set_defaulthistorymessages(room
, number)
40 number = math
.min(tonumber(number) or default_history_length
, room
._data
.history_length
or default_history_length
);
41 if number == default_history_length
then
44 room
._data
.default_history_messages
= number;
47 module
:hook("muc-config-form", function(event
)
48 table.insert(event
.form
, {
49 name
= "muc#roomconfig_historylength";
51 label
= "Maximum number of history messages returned by room";
52 desc
= "Specify the maximum number of previous messages that should be sent to users when they join the room";
53 value
= tostring(get_historylength(event
.room
));
55 table.insert(event
.form
, {
56 name
= 'muc#roomconfig_defaulthistorymessages',
58 label
= 'Default number of history messages returned by room',
59 desc
= "Specify the number of previous messages sent to new users when they join the room";
60 value
= tostring(get_defaulthistorymessages(event
.room
))
64 module
:hook("muc-config-submitted/muc#roomconfig_historylength", function(event
)
65 if set_historylength(event
.room
, event
.value
) then
66 event
.status_codes
["104"] = true;
70 module
:hook("muc-config-submitted/muc#roomconfig_defaulthistorymessages", function(event
)
71 if set_defaulthistorymessages(event
.room
, event
.value
) then
72 event
.status_codes
["104"] = true;
76 local function parse_history(stanza
)
77 local x_tag
= stanza
:get_child("x", "http://jabber.org/protocol/muc");
78 local history_tag
= x_tag
and x_tag
:get_child("history", "http://jabber.org/protocol/muc");
79 if not history_tag
then
83 local maxchars
= tonumber(history_tag
.attr
.maxchars
);
85 local maxstanzas
= tonumber(history_tag
.attr
.maxstanzas
);
87 -- messages received since the UTC datetime specified
88 local since
= history_tag
.attr
.since
;
90 since
= datetime
.parse(since
);
93 -- messages received in the last "X" seconds.
94 local seconds
= tonumber(history_tag
.attr
.seconds
);
96 seconds
= gettime() - seconds
;
98 since
= math
.max(since
, seconds
);
104 return maxchars
, maxstanzas
, since
;
107 module
:hook("muc-get-history", function(event
)
108 local room
= event
.room
;
109 local history
= room
._history
; -- send discussion history
110 if not history
then return nil end
111 local history_len
= #history
;
114 local maxchars
= event
.maxchars
;
115 local maxstanzas
= event
.maxstanzas
or history_len
;
116 local since
= event
.since
;
119 for i
=history_len
,1,-1 do
120 local entry
= history
[i
];
122 if not entry
.chars
then
123 entry
.stanza
.attr
.to
= "";
124 entry
.chars
= #tostring(entry
.stanza
);
126 charcount
= charcount
+ entry
.chars
+ #to
;
127 if charcount
> maxchars
then break; end
129 if since
and since
> entry
.timestamp
then break; end
130 if n
+ 1 > maxstanzas
then break; end
134 local i
= history_len
-n
+1
135 function event
.next_stanza()
136 if i
> history_len
then return nil end
137 local entry
= history
[i
];
138 local msg
= entry
.stanza
;
146 local function send_history(room
, stanza
)
147 local maxchars
, maxstanzas
, since
= parse_history(stanza
);
148 if not(maxchars
or maxstanzas
or since
) then
149 maxstanzas
= get_defaulthistorymessages(room
);
154 to
= stanza
.attr
.from
; -- `to` is required to calculate the character count for `maxchars`
156 maxstanzas
= maxstanzas
,
158 next_stanza
= function() end; -- events should define this iterator
160 module
:fire_event("muc-get-history", event
);
161 for msg
in event
.next_stanza
, event
do
162 room
:route_stanza(msg
);
166 -- Send history on join
167 module
:hook("muc-occupant-session-new", function(event
)
168 send_history(event
.room
, event
.stanza
);
169 end, 50); -- Before subject(20)
172 module
:hook("muc-add-history", function(event
)
173 local room
= event
.room
174 local history
= room
._history
;
175 if not history
then history
= {}; room
._history
= history
; end
176 local stanza
= st
.clone(event
.stanza
);
178 local ts
= gettime();
179 local stamp
= datetime
.datetime(ts
);
180 stanza
:tag("delay", { -- XEP-0203
181 xmlns
= "urn:xmpp:delay", from
= room
.jid
, stamp
= stamp
183 stanza
:tag("x", { -- XEP-0091 (deprecated)
184 xmlns
= "jabber:x:delay", from
= room
.jid
, stamp
= datetime
.legacy()
186 local entry
= { stanza
= stanza
, timestamp
= ts
};
187 table.insert(history
, entry
);
188 while #history
> get_historylength(room
) do table.remove(history
, 1) end
192 -- Have a single muc-add-history event, so that plugins can mark it
193 -- as handled without stopping other muc-broadcast-message handlers
194 module
:hook("muc-broadcast-message", function(event
)
195 if module
:fire_event("muc-message-is-historic", event
) then
196 module
:fire_event("muc-add-history", event
);
200 module
:hook("muc-message-is-historic", function (event
)
201 return event
.stanza
:get_child("body");
205 set_max_length
= set_max_history_length
;
206 parse_history
= parse_history
;
208 get_length
= get_historylength
;
209 set_length
= set_historylength
;