mirror of
https://github.com/matrix-construct/construct
synced 2024-11-15 14:31:11 +01:00
ircd:Ⓜ️:typing: Move remaining assets into namespace; minor reorg.
This commit is contained in:
parent
ea97994fe3
commit
f9df9bfbda
4 changed files with 353 additions and 373 deletions
|
@ -11,11 +11,30 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#define HAVE_IRCD_M_TYPING_H
|
#define HAVE_IRCD_M_TYPING_H
|
||||||
|
|
||||||
namespace ircd::m
|
namespace ircd::m::typing
|
||||||
{
|
{
|
||||||
struct typing;
|
struct typist;
|
||||||
|
struct commit;
|
||||||
|
|
||||||
|
using edu = m::edu::m_typing;
|
||||||
|
using closure = std::function<bool (const edu &)>;
|
||||||
|
|
||||||
|
// Iterate all of the active typists held in RA<
|
||||||
|
//NOTE: no yielding in this iteration.
|
||||||
|
bool for_each(const closure &);
|
||||||
|
|
||||||
|
// Get whether a user enabled typing events for a room. The type string
|
||||||
|
// can be "send" or "sync" prevent typing one's events from being sent or
|
||||||
|
// others' from being sync'ed, respectively
|
||||||
|
bool allow(const id::user &, const id::room &, const string_view &type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Interface to update the typing state, generate all events, send etc.
|
||||||
|
struct ircd::m::typing::commit
|
||||||
|
{
|
||||||
|
commit(const edu &);
|
||||||
|
};
|
||||||
|
|
||||||
struct ircd::m::edu::m_typing
|
struct ircd::m::edu::m_typing
|
||||||
:json::tuple
|
:json::tuple
|
||||||
<
|
<
|
||||||
|
@ -29,27 +48,15 @@ struct ircd::m::edu::m_typing
|
||||||
using super_type::operator=;
|
using super_type::operator=;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ircd::m::typing
|
struct ircd::m::typing::typist
|
||||||
:m::edu::m_typing
|
|
||||||
{
|
{
|
||||||
struct commit;
|
using is_transparent = void;
|
||||||
|
|
||||||
using closure = std::function<bool (const typing &)>;
|
system_point timesout;
|
||||||
|
m::user::id::buf user_id;
|
||||||
|
m::room::id::buf room_id;
|
||||||
|
|
||||||
// Iterate all of the active typists held in RA<
|
bool operator()(const typist &a, const string_view &b) const noexcept;
|
||||||
//NOTE: no yielding in this iteration.
|
bool operator()(const string_view &a, const typist &b) const noexcept;
|
||||||
static bool for_each(const closure &);
|
bool operator()(const typist &a, const typist &b) const noexcept;
|
||||||
|
|
||||||
// Get whether a user enabled typing events for a room. The type string
|
|
||||||
// can be "send" or "sync" prevent typing one's events from being sent or
|
|
||||||
// others' from being sync'ed, respectively
|
|
||||||
static bool allow(const id::user &, const id::room &, const string_view &type);
|
|
||||||
|
|
||||||
using edu::m_typing::m_typing;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Interface to update the typing state, generate all events, send etc.
|
|
||||||
struct ircd::m::typing::commit
|
|
||||||
{
|
|
||||||
commit(const typing &);
|
|
||||||
};
|
};
|
||||||
|
|
653
matrix/typing.cc
653
matrix/typing.cc
|
@ -8,77 +8,82 @@
|
||||||
// copyright notice and this permission notice is present in all copies. The
|
// copyright notice and this permission notice is present in all copies. The
|
||||||
// full license for this software is available in the LICENSE file.
|
// full license for this software is available in the LICENSE file.
|
||||||
|
|
||||||
using namespace ircd;
|
namespace ircd::m::typing
|
||||||
|
{
|
||||||
|
static system_point calc_timesout(milliseconds relative);
|
||||||
|
static bool update_state(const edu &);
|
||||||
|
static m::event::id::buf set_typing(const edu &);
|
||||||
|
static void _handle_edu(const m::event &, const edu &);
|
||||||
|
static void handle_edu(const m::event &, m::vm::eval &);
|
||||||
|
static void timeout_timeout(const typist &);
|
||||||
|
static void timeout_check();
|
||||||
|
static void timeout_worker();
|
||||||
|
|
||||||
log::log
|
extern log::log log;
|
||||||
typing_log
|
extern ctx::dock dock;
|
||||||
|
extern ctx::mutex mutex;
|
||||||
|
extern std::set<typist, typist> typists;
|
||||||
|
extern conf::item<milliseconds> timeout_max;
|
||||||
|
extern conf::item<milliseconds> timeout_min;
|
||||||
|
extern conf::item<milliseconds> timeout_int;
|
||||||
|
extern hookfn<vm::eval &> on_eval;
|
||||||
|
extern context timeout_context;
|
||||||
|
}
|
||||||
|
|
||||||
|
decltype(ircd::m::typing::log)
|
||||||
|
ircd::m::typing::log
|
||||||
{
|
{
|
||||||
"m.typing"
|
"m.typing"
|
||||||
};
|
};
|
||||||
|
|
||||||
struct typist
|
decltype(ircd::m::typing::dock)
|
||||||
{
|
ircd::m::typing::dock;
|
||||||
using is_transparent = void;
|
|
||||||
|
|
||||||
system_point timesout;
|
decltype(ircd::m::typing::mutex)
|
||||||
m::user::id::buf user_id;
|
ircd::m::typing::mutex;
|
||||||
m::room::id::buf room_id;
|
|
||||||
|
|
||||||
bool operator()(const typist &a, const string_view &b) const;
|
decltype(ircd::m::typing::typists)
|
||||||
bool operator()(const string_view &a, const typist &b) const;
|
ircd::m::typing::typists;
|
||||||
bool operator()(const typist &a, const typist &b) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
static ctx::dock
|
decltype(ircd::m::typing::timeout_max)
|
||||||
timeout_dock;
|
ircd::m::typing::timeout_max
|
||||||
|
|
||||||
static ctx::mutex
|
|
||||||
typists_mutex;
|
|
||||||
|
|
||||||
static std::set<typist, typist>
|
|
||||||
typists;
|
|
||||||
|
|
||||||
conf::item<milliseconds>
|
|
||||||
timeout_max
|
|
||||||
{
|
{
|
||||||
{ "name", "ircd.typing.timeout.max" },
|
{ "name", "ircd.typing.timeout.max" },
|
||||||
{ "default", 90 * 1000L },
|
{ "default", 90 * 1000L },
|
||||||
};
|
};
|
||||||
|
|
||||||
conf::item<milliseconds>
|
decltype(ircd::m::typing::timeout_min)
|
||||||
timeout_min
|
ircd::m::typing::timeout_min
|
||||||
{
|
{
|
||||||
{ "name", "ircd.typing.timeout.min" },
|
{ "name", "ircd.typing.timeout.min" },
|
||||||
{ "default", 15 * 1000L },
|
{ "default", 15 * 1000L },
|
||||||
};
|
};
|
||||||
|
|
||||||
conf::item<milliseconds>
|
decltype(ircd::m::typing::timeout_int)
|
||||||
timeout_int
|
ircd::m::typing::timeout_int
|
||||||
{
|
{
|
||||||
{ "name", "ircd.typing.timeout.int" },
|
{ "name", "ircd.typing.timeout.int" },
|
||||||
{ "default", 5 * 1000L },
|
{ "default", 5 * 1000L },
|
||||||
};
|
};
|
||||||
|
|
||||||
static system_point calc_timesout(milliseconds relative);
|
decltype(ircd::m::typing::timeout_context)
|
||||||
static bool update_state(const m::edu::m_typing &);
|
ircd::m::typing::timeout_context
|
||||||
|
{
|
||||||
//
|
"typing",
|
||||||
// typing edu handler stack (local and remote)
|
768_KiB,
|
||||||
//
|
context::POST,
|
||||||
|
timeout_worker,
|
||||||
static m::event::id::buf set_typing(const m::edu::m_typing &edu);
|
};
|
||||||
static void _handle_edu_m_typing(const m::event &, const m::typing &edu);
|
|
||||||
static void handle_edu_m_typing(const m::event &, m::vm::eval &);
|
|
||||||
|
|
||||||
/// Hooks all federation typing edus from remote servers as well as
|
/// Hooks all federation typing edus from remote servers as well as
|
||||||
/// the above commit from local clients. This hook rewrites the edu into
|
/// the above commit from local clients. This hook rewrites the edu into
|
||||||
/// a new event formatted for client /sync and then runs that through eval
|
/// a new event formatted for client /sync and then runs that through eval
|
||||||
/// so our clients can receive the typing events.
|
/// so our clients can receive the typing events.
|
||||||
///
|
///
|
||||||
m::hookfn<m::vm::eval &>
|
decltype(ircd::m::typing::on_eval)
|
||||||
_m_typing_eval
|
ircd::m::typing::on_eval
|
||||||
{
|
{
|
||||||
handle_edu_m_typing,
|
handle_edu,
|
||||||
{
|
{
|
||||||
{ "_site", "vm.eval" },
|
{ "_site", "vm.eval" },
|
||||||
{ "type", "m.typing" },
|
{ "type", "m.typing" },
|
||||||
|
@ -86,7 +91,104 @@ _m_typing_eval
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
handle_edu_m_typing(const m::event &event,
|
ircd::m::typing::timeout_worker()
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for(;; ctx::sleep(milliseconds(timeout_int)))
|
||||||
|
{
|
||||||
|
dock.wait([]
|
||||||
|
{
|
||||||
|
return !typists.empty();
|
||||||
|
});
|
||||||
|
|
||||||
|
const std::lock_guard lock
|
||||||
|
{
|
||||||
|
mutex
|
||||||
|
};
|
||||||
|
|
||||||
|
timeout_check();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(const std::exception &e)
|
||||||
|
{
|
||||||
|
log::critical
|
||||||
|
{
|
||||||
|
log, "Typing timeout worker fatal :%s",
|
||||||
|
e.what()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::m::typing::timeout_check()
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto now
|
||||||
|
{
|
||||||
|
ircd::now<system_point>()
|
||||||
|
};
|
||||||
|
|
||||||
|
for(auto it(begin(typists)); it != end(typists); )
|
||||||
|
{
|
||||||
|
if(it->timesout < now)
|
||||||
|
{
|
||||||
|
// have to restart the loop if there's a timeout because
|
||||||
|
// the call will have yields and invalidate iterators etc.
|
||||||
|
timeout_timeout(*it);
|
||||||
|
it = typists.erase(it);
|
||||||
|
}
|
||||||
|
else ++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(const std::exception &e)
|
||||||
|
{
|
||||||
|
log::critical
|
||||||
|
{
|
||||||
|
log, "Typing timeout check :%s",
|
||||||
|
e.what(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::m::typing::timeout_timeout(const typist &t)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
assert(run::level == run::level::RUN);
|
||||||
|
|
||||||
|
const edu edu
|
||||||
|
{
|
||||||
|
{ "user_id", t.user_id },
|
||||||
|
{ "room_id", t.room_id },
|
||||||
|
{ "typing", false },
|
||||||
|
};
|
||||||
|
|
||||||
|
log::debug
|
||||||
|
{
|
||||||
|
log, "Typing timeout for %s in %s",
|
||||||
|
string_view{t.user_id},
|
||||||
|
string_view{t.room_id}
|
||||||
|
};
|
||||||
|
|
||||||
|
m::event event;
|
||||||
|
json::get<"origin"_>(event) = my_host();
|
||||||
|
json::get<"type"_>(event) = "m.typing"_sv;
|
||||||
|
|
||||||
|
// Call this manually because it currently composes the event
|
||||||
|
// sent to clients to stop the typing for this timed out user.
|
||||||
|
_handle_edu(event, edu);
|
||||||
|
}
|
||||||
|
catch(const std::exception &e)
|
||||||
|
{
|
||||||
|
log::error
|
||||||
|
{
|
||||||
|
log, "Typing timeout for %s in %s :%s",
|
||||||
|
string_view{t.user_id},
|
||||||
|
string_view{t.room_id},
|
||||||
|
e.what(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::m::typing::handle_edu(const m::event &event,
|
||||||
m::vm::eval &eval)
|
m::vm::eval &eval)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -95,7 +197,7 @@ try
|
||||||
at<"content"_>(event)
|
at<"content"_>(event)
|
||||||
};
|
};
|
||||||
|
|
||||||
_handle_edu_m_typing(event, content);
|
_handle_edu(event, content);
|
||||||
}
|
}
|
||||||
catch(const ctx::interrupted &)
|
catch(const ctx::interrupted &)
|
||||||
{
|
{
|
||||||
|
@ -105,7 +207,7 @@ catch(const std::exception &e)
|
||||||
{
|
{
|
||||||
log::derror
|
log::derror
|
||||||
{
|
{
|
||||||
typing_log, "m.typing from %s :%s",
|
log, "m.typing from %s :%s",
|
||||||
json::get<"origin"_>(event),
|
json::get<"origin"_>(event),
|
||||||
e.what(),
|
e.what(),
|
||||||
};
|
};
|
||||||
|
@ -114,8 +216,8 @@ catch(const std::exception &e)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
_handle_edu_m_typing(const m::event &event,
|
ircd::m::typing::_handle_edu(const m::event &event,
|
||||||
const m::typing &edu)
|
const edu &edu)
|
||||||
{
|
{
|
||||||
// This check prevents interference between the two competing edu formats;
|
// This check prevents interference between the two competing edu formats;
|
||||||
// The federation edu has a room_id field while the client edu only has a
|
// The federation edu has a room_id field while the client edu only has a
|
||||||
|
@ -140,12 +242,11 @@ _handle_edu_m_typing(const m::event &event,
|
||||||
|
|
||||||
// Check if this server can send an edu for this user. We make an exception
|
// Check if this server can send an edu for this user. We make an exception
|
||||||
// for our server to allow the timeout worker to use this codepath.
|
// for our server to allow the timeout worker to use this codepath.
|
||||||
if(!my_host(origin))
|
if(!my_host(origin) && user_id.host() != origin)
|
||||||
if(user_id.host() != origin)
|
|
||||||
{
|
{
|
||||||
log::dwarning
|
log::dwarning
|
||||||
{
|
{
|
||||||
typing_log, "Ignoring %s from %s for alien %s",
|
log, "Ignoring %s from %s for alien %s",
|
||||||
at<"type"_>(event),
|
at<"type"_>(event),
|
||||||
origin,
|
origin,
|
||||||
string_view{user_id}
|
string_view{user_id}
|
||||||
|
@ -160,7 +261,7 @@ _handle_edu_m_typing(const m::event &event,
|
||||||
{
|
{
|
||||||
log::dwarning
|
log::dwarning
|
||||||
{
|
{
|
||||||
typing_log, "Ignoring %s from '%s' in %s :denied by m.room.server_acl.",
|
log, "Ignoring %s from '%s' in %s :denied by m.room.server_acl.",
|
||||||
at<"type"_>(event),
|
at<"type"_>(event),
|
||||||
origin,
|
origin,
|
||||||
string_view{room_id},
|
string_view{room_id},
|
||||||
|
@ -182,7 +283,7 @@ _handle_edu_m_typing(const m::event &event,
|
||||||
{
|
{
|
||||||
log::dwarning
|
log::dwarning
|
||||||
{
|
{
|
||||||
typing_log, "Ignoring %s from %s for user %s because not in room '%s'",
|
log, "Ignoring %s from %s for user %s because not in room '%s'",
|
||||||
at<"type"_>(event),
|
at<"type"_>(event),
|
||||||
origin,
|
origin,
|
||||||
string_view{user_id},
|
string_view{user_id},
|
||||||
|
@ -205,9 +306,155 @@ _handle_edu_m_typing(const m::event &event,
|
||||||
set_typing(edu);
|
set_typing(edu);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
ircd::m::event::id::buf
|
||||||
// interface
|
ircd::m::typing::set_typing(const edu &edu)
|
||||||
//
|
{
|
||||||
|
assert(json::get<"room_id"_>(edu));
|
||||||
|
const m::user::id &user_id
|
||||||
|
{
|
||||||
|
at<"user_id"_>(edu)
|
||||||
|
};
|
||||||
|
|
||||||
|
const m::user user
|
||||||
|
{
|
||||||
|
user_id
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!exists(user))
|
||||||
|
create(user);
|
||||||
|
|
||||||
|
const m::user::room user_room
|
||||||
|
{
|
||||||
|
user
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto &timeout
|
||||||
|
{
|
||||||
|
json::get<"timeout"_>(edu)?
|
||||||
|
json::get<"timeout"_>(edu):
|
||||||
|
json::get<"typing"_>(edu)?
|
||||||
|
milliseconds(timeout_max).count():
|
||||||
|
0L
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto evid
|
||||||
|
{
|
||||||
|
send(user_room, user_id, "ircd.typing",
|
||||||
|
{
|
||||||
|
{ "room_id", at<"room_id"_>(edu) },
|
||||||
|
{ "typing", json::get<"typing"_>(edu) },
|
||||||
|
{ "timeout", timeout },
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
char pbuf[32];
|
||||||
|
log::info
|
||||||
|
{
|
||||||
|
log, "%s %s typing in %s timeout:%s",
|
||||||
|
string_view{user_id},
|
||||||
|
json::get<"typing"_>(edu)?
|
||||||
|
"started"_sv:
|
||||||
|
"stopped"_sv,
|
||||||
|
at<"room_id"_>(edu),
|
||||||
|
util::pretty(pbuf, milliseconds(timeout), 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
return evid;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ircd::m::typing::update_state(const edu &object)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto &user_id
|
||||||
|
{
|
||||||
|
at<"user_id"_>(object)
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto &room_id
|
||||||
|
{
|
||||||
|
at<"room_id"_>(object)
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto &typing
|
||||||
|
{
|
||||||
|
at<"typing"_>(object)
|
||||||
|
};
|
||||||
|
|
||||||
|
const milliseconds timeout
|
||||||
|
{
|
||||||
|
at<"timeout"_>(object)
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::lock_guard lock
|
||||||
|
{
|
||||||
|
mutex
|
||||||
|
};
|
||||||
|
|
||||||
|
auto it
|
||||||
|
{
|
||||||
|
typists.lower_bound(user_id)
|
||||||
|
};
|
||||||
|
|
||||||
|
const bool was_typing
|
||||||
|
{
|
||||||
|
it != end(typists) && it->user_id == user_id
|
||||||
|
};
|
||||||
|
|
||||||
|
if(typing && !was_typing)
|
||||||
|
{
|
||||||
|
typists.emplace_hint(it, typist
|
||||||
|
{
|
||||||
|
calc_timesout(timeout), user_id, room_id
|
||||||
|
});
|
||||||
|
|
||||||
|
dock.notify_one();
|
||||||
|
}
|
||||||
|
else if(typing && was_typing)
|
||||||
|
{
|
||||||
|
auto &t(const_cast<typist &>(*it));
|
||||||
|
t.timesout = calc_timesout(timeout);
|
||||||
|
}
|
||||||
|
else if(!typing && was_typing)
|
||||||
|
{
|
||||||
|
typists.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool transmit
|
||||||
|
{
|
||||||
|
(typing && !was_typing) || (!typing && was_typing)
|
||||||
|
};
|
||||||
|
|
||||||
|
log::debug
|
||||||
|
{
|
||||||
|
log, "Typing %s in %s now[%b] was[%b] xmit[%b]",
|
||||||
|
string_view{at<"user_id"_>(object)},
|
||||||
|
string_view{at<"room_id"_>(object)},
|
||||||
|
json::get<"typing"_>(object),
|
||||||
|
was_typing,
|
||||||
|
transmit
|
||||||
|
};
|
||||||
|
|
||||||
|
return transmit;
|
||||||
|
}
|
||||||
|
catch(const std::exception &e)
|
||||||
|
{
|
||||||
|
log::error
|
||||||
|
{
|
||||||
|
log, "Failed to update state :%s",
|
||||||
|
e.what(),
|
||||||
|
};
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
ircd::system_point
|
||||||
|
ircd::m::typing::calc_timesout(milliseconds timeout)
|
||||||
|
{
|
||||||
|
timeout = std::max(timeout, milliseconds(timeout_min));
|
||||||
|
timeout = std::min(timeout, milliseconds(timeout_max));
|
||||||
|
return now<system_point>() + timeout;
|
||||||
|
}
|
||||||
|
|
||||||
/// typing commit handler stack (local user)
|
/// typing commit handler stack (local user)
|
||||||
///
|
///
|
||||||
|
@ -218,7 +465,7 @@ _handle_edu_m_typing(const m::event &event,
|
||||||
/// event to clients we hook it during eval and create a new event formatted
|
/// event to clients we hook it during eval and create a new event formatted
|
||||||
/// for clients and then run that through eval too. (see below).
|
/// for clients and then run that through eval too. (see below).
|
||||||
///
|
///
|
||||||
ircd::m::typing::commit::commit(const m::typing &edu)
|
ircd::m::typing::commit::commit(const edu &edu)
|
||||||
{
|
{
|
||||||
using json::at;
|
using json::at;
|
||||||
|
|
||||||
|
@ -306,7 +553,7 @@ ircd::m::typing::for_each(const closure &closure)
|
||||||
{
|
{
|
||||||
const std::lock_guard lock
|
const std::lock_guard lock
|
||||||
{
|
{
|
||||||
typists_mutex
|
mutex
|
||||||
};
|
};
|
||||||
|
|
||||||
for(const auto &t : typists)
|
for(const auto &t : typists)
|
||||||
|
@ -316,7 +563,7 @@ ircd::m::typing::for_each(const closure &closure)
|
||||||
system_clock::to_time_t(t.timesout)
|
system_clock::to_time_t(t.timesout)
|
||||||
};
|
};
|
||||||
|
|
||||||
const m::typing event
|
const edu event
|
||||||
{
|
{
|
||||||
{ "user_id", t.user_id },
|
{ "user_id", t.user_id },
|
||||||
{ "room_id", t.room_id },
|
{ "room_id", t.room_id },
|
||||||
|
@ -331,304 +578,30 @@ ircd::m::typing::for_each(const closure &closure)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// timeout worker stack
|
|
||||||
//
|
|
||||||
|
|
||||||
static void timeout_timeout(const typist &);
|
|
||||||
static void timeout_check();
|
|
||||||
static void timeout_worker();
|
|
||||||
|
|
||||||
static context
|
|
||||||
timeout_context
|
|
||||||
{
|
|
||||||
"typing",
|
|
||||||
256_KiB,
|
|
||||||
context::POST,
|
|
||||||
timeout_worker
|
|
||||||
};
|
|
||||||
|
|
||||||
static const ircd::run::changed
|
|
||||||
timeout_context_terminate
|
|
||||||
{
|
|
||||||
run::level::QUIT, []
|
|
||||||
{
|
|
||||||
timeout_context.terminate();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
timeout_worker()
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for(;; ctx::sleep(milliseconds(timeout_int)))
|
|
||||||
{
|
|
||||||
timeout_dock.wait([]
|
|
||||||
{
|
|
||||||
return !typists.empty();
|
|
||||||
});
|
|
||||||
|
|
||||||
timeout_check();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(const std::exception &e)
|
|
||||||
{
|
|
||||||
log::critical
|
|
||||||
{
|
|
||||||
typing_log, "Typing timeout worker fatal :%s",
|
|
||||||
e.what()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
timeout_check()
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const std::lock_guard lock
|
|
||||||
{
|
|
||||||
typists_mutex
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto now
|
|
||||||
{
|
|
||||||
ircd::now<system_point>()
|
|
||||||
};
|
|
||||||
|
|
||||||
for(auto it(begin(typists)); it != end(typists); )
|
|
||||||
{
|
|
||||||
if(it->timesout < now)
|
|
||||||
{
|
|
||||||
// have to restart the loop if there's a timeout because
|
|
||||||
// the call will have yields and invalidate iterators etc.
|
|
||||||
timeout_timeout(*it);
|
|
||||||
it = typists.erase(it);
|
|
||||||
}
|
|
||||||
else ++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(const std::exception &e)
|
|
||||||
{
|
|
||||||
log::critical
|
|
||||||
{
|
|
||||||
typing_log, "Typing timeout check :%s",
|
|
||||||
e.what(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
timeout_timeout(const typist &t)
|
|
||||||
try
|
|
||||||
{
|
|
||||||
assert(run::level == run::level::RUN);
|
|
||||||
|
|
||||||
const m::typing edu
|
|
||||||
{
|
|
||||||
{ "user_id", t.user_id },
|
|
||||||
{ "room_id", t.room_id },
|
|
||||||
{ "typing", false },
|
|
||||||
};
|
|
||||||
|
|
||||||
log::debug
|
|
||||||
{
|
|
||||||
typing_log, "Typing timeout for %s in %s",
|
|
||||||
string_view{t.user_id},
|
|
||||||
string_view{t.room_id}
|
|
||||||
};
|
|
||||||
|
|
||||||
m::event event;
|
|
||||||
json::get<"origin"_>(event) = my_host();
|
|
||||||
json::get<"type"_>(event) = "m.typing"_sv;
|
|
||||||
|
|
||||||
// Call this manually because it currently composes the event
|
|
||||||
// sent to clients to stop the typing for this timed out user.
|
|
||||||
_handle_edu_m_typing(event, edu);
|
|
||||||
}
|
|
||||||
catch(const std::exception &e)
|
|
||||||
{
|
|
||||||
log::error
|
|
||||||
{
|
|
||||||
typing_log, "Typing timeout for %s in %s :%s",
|
|
||||||
string_view{t.user_id},
|
|
||||||
string_view{t.room_id},
|
|
||||||
e.what(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// internal
|
|
||||||
//
|
|
||||||
|
|
||||||
ircd::m::event::id::buf
|
|
||||||
set_typing(const m::edu::m_typing &edu)
|
|
||||||
{
|
|
||||||
assert(json::get<"room_id"_>(edu));
|
|
||||||
const m::user::id &user_id
|
|
||||||
{
|
|
||||||
at<"user_id"_>(edu)
|
|
||||||
};
|
|
||||||
|
|
||||||
const m::user user
|
|
||||||
{
|
|
||||||
user_id
|
|
||||||
};
|
|
||||||
|
|
||||||
if(!exists(user))
|
|
||||||
create(user);
|
|
||||||
|
|
||||||
const m::user::room user_room
|
|
||||||
{
|
|
||||||
user
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto &timeout
|
|
||||||
{
|
|
||||||
json::get<"timeout"_>(edu)?
|
|
||||||
json::get<"timeout"_>(edu):
|
|
||||||
json::get<"typing"_>(edu)?
|
|
||||||
milliseconds(timeout_max).count():
|
|
||||||
0L
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto evid
|
|
||||||
{
|
|
||||||
send(user_room, user_id, "ircd.typing",
|
|
||||||
{
|
|
||||||
{ "room_id", at<"room_id"_>(edu) },
|
|
||||||
{ "typing", json::get<"typing"_>(edu) },
|
|
||||||
{ "timeout", timeout },
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
char pbuf[32];
|
|
||||||
log::info
|
|
||||||
{
|
|
||||||
typing_log, "%s %s typing in %s timeout:%s",
|
|
||||||
string_view{user_id},
|
|
||||||
json::get<"typing"_>(edu)?
|
|
||||||
"started"_sv:
|
|
||||||
"stopped"_sv,
|
|
||||||
at<"room_id"_>(edu),
|
|
||||||
pretty(pbuf, milliseconds(long(timeout)), 1),
|
|
||||||
};
|
|
||||||
|
|
||||||
return evid;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
update_state(const m::edu::m_typing &object)
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const auto &user_id
|
|
||||||
{
|
|
||||||
at<"user_id"_>(object)
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto &room_id
|
|
||||||
{
|
|
||||||
at<"room_id"_>(object)
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto &typing
|
|
||||||
{
|
|
||||||
at<"typing"_>(object)
|
|
||||||
};
|
|
||||||
|
|
||||||
const milliseconds timeout
|
|
||||||
{
|
|
||||||
at<"timeout"_>(object)
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::lock_guard lock
|
|
||||||
{
|
|
||||||
typists_mutex
|
|
||||||
};
|
|
||||||
|
|
||||||
auto it
|
|
||||||
{
|
|
||||||
typists.lower_bound(user_id)
|
|
||||||
};
|
|
||||||
|
|
||||||
const bool was_typing
|
|
||||||
{
|
|
||||||
it != end(typists) && it->user_id == user_id
|
|
||||||
};
|
|
||||||
|
|
||||||
if(typing && !was_typing)
|
|
||||||
{
|
|
||||||
typists.emplace_hint(it, typist
|
|
||||||
{
|
|
||||||
calc_timesout(timeout), user_id, room_id
|
|
||||||
});
|
|
||||||
|
|
||||||
timeout_dock.notify_one();
|
|
||||||
}
|
|
||||||
else if(typing && was_typing)
|
|
||||||
{
|
|
||||||
auto &t(const_cast<typist &>(*it));
|
|
||||||
t.timesout = calc_timesout(timeout);
|
|
||||||
}
|
|
||||||
else if(!typing && was_typing)
|
|
||||||
{
|
|
||||||
typists.erase(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool transmit
|
|
||||||
{
|
|
||||||
(typing && !was_typing) || (!typing && was_typing)
|
|
||||||
};
|
|
||||||
|
|
||||||
log::debug
|
|
||||||
{
|
|
||||||
typing_log, "Typing %s in %s now[%b] was[%b] xmit[%b]",
|
|
||||||
string_view{at<"user_id"_>(object)},
|
|
||||||
string_view{at<"room_id"_>(object)},
|
|
||||||
json::get<"typing"_>(object),
|
|
||||||
was_typing,
|
|
||||||
transmit
|
|
||||||
};
|
|
||||||
|
|
||||||
return transmit;
|
|
||||||
}
|
|
||||||
catch(const std::exception &e)
|
|
||||||
{
|
|
||||||
log::error
|
|
||||||
{
|
|
||||||
typing_log, "Failed to update state :%s",
|
|
||||||
e.what(),
|
|
||||||
};
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
system_point
|
|
||||||
calc_timesout(milliseconds timeout)
|
|
||||||
{
|
|
||||||
timeout = std::max(timeout, milliseconds(timeout_min));
|
|
||||||
timeout = std::min(timeout, milliseconds(timeout_max));
|
|
||||||
return now<system_point>() + timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// typist struct
|
// typist struct
|
||||||
//
|
//
|
||||||
|
|
||||||
bool
|
bool
|
||||||
typist::operator()(const typist &a, const string_view &b)
|
ircd::m::typing::typist::operator()(const typist &a,
|
||||||
const
|
const string_view &b)
|
||||||
|
const noexcept
|
||||||
{
|
{
|
||||||
return a.user_id < b;
|
return a.user_id < b;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
typist::operator()(const string_view &a, const typist &b)
|
ircd::m::typing::typist::operator()(const string_view &a,
|
||||||
const
|
const typist &b)
|
||||||
|
const noexcept
|
||||||
{
|
{
|
||||||
return a < b.user_id;
|
return a < b.user_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
typist::operator()(const typist &a, const typist &b)
|
ircd::m::typing::typist::operator()(const typist &a,
|
||||||
const
|
const typist &b)
|
||||||
|
const noexcept
|
||||||
{
|
{
|
||||||
return a.user_id < b.user_id;
|
return a.user_id < b.user_id;
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ put__typing(client &client,
|
||||||
request.get("timeout", milliseconds(timeout_default).count())
|
request.get("timeout", milliseconds(timeout_default).count())
|
||||||
};
|
};
|
||||||
|
|
||||||
const m::typing event
|
const m::typing::edu event
|
||||||
{
|
{
|
||||||
{ "room_id", room_id },
|
{ "room_id", room_id },
|
||||||
{ "typing", typing },
|
{ "typing", typing },
|
||||||
|
|
|
@ -12547,7 +12547,7 @@ bool
|
||||||
console_cmd__user__typing(opt &out, const string_view &line)
|
console_cmd__user__typing(opt &out, const string_view &line)
|
||||||
{
|
{
|
||||||
m::typing::for_each([&out]
|
m::typing::for_each([&out]
|
||||||
(const m::typing &event)
|
(const m::typing::edu &event)
|
||||||
{
|
{
|
||||||
out << event << std::endl;
|
out << event << std::endl;
|
||||||
return true;
|
return true;
|
||||||
|
|
Loading…
Reference in a new issue