#include "./bridge.hpp" #include #include #include #include #include #include Bridge::Bridge( Contact3Registry& cr, RegistryMessageModelI& rmm, ConfigModelI& conf, MessageCommandDispatcher* mcd ) : _cr(cr), _rmm(rmm), _rmm_sr(_rmm.newSubRef(this)), _conf(conf), _mcd(mcd) { _rmm_sr.subscribe(RegistryMessageModel_Event::message_construct); if (!_conf.has_bool("Bridge", "username_angle_brackets")) { _conf.set("Bridge", "username_angle_brackets", true); } if (!_conf.has_bool("Bridge", "username_id")) { _conf.set("Bridge", "username_id", true); } if (!_conf.has_bool("Bridge", "username_colon")) { _conf.set("Bridge", "username_colon", true); } // load synced contacts (bridged groups) std::map tmp_name_to_id; for (const auto [contact_id, vgroup_str] : _conf.entries_string("Bridge", "contact_to_vgroup")) { const auto tmp_vgroup_str = std::string{vgroup_str.start, vgroup_str.start+vgroup_str.extend}; if (!tmp_name_to_id.count(tmp_vgroup_str)) { tmp_name_to_id[tmp_vgroup_str] = _vgroups.size(); _vgroups.emplace_back().vg_name = tmp_vgroup_str; } auto& v_group = _vgroups.at(tmp_name_to_id.at(tmp_vgroup_str)); auto& new_vgc = v_group.contacts.emplace_back(); new_vgc.c = {_cr, entt::null}; // this is annoying af new_vgc.id = hex2bin(contact_id); } updateVGroups(); registerCommands(); } Bridge::~Bridge(void) { } void Bridge::iterate(float time_delta) { _iterate_timer += time_delta; if (_iterate_timer >= 10.f) { _iterate_timer = 0.f; updateVGroups(); } } void Bridge::updateVGroups(void) { // fill in contacts, some contacts might be created late for (size_t i_vg = 0; i_vg < _vgroups.size(); i_vg++) { for (auto& vgc : _vgroups[i_vg].contacts) { assert(!vgc.id.empty()); if (!vgc.c.valid()) { // search auto view = _cr.view(); for (const auto c : view) { if (view.get(c).data == vgc.id) { vgc.c = {_cr, c}; std::cout << "Bridge: found contact for vgroup\n"; break; } } } if (vgc.c.valid()) { _c_to_vg[vgc.c] = i_vg; } } } } void Bridge::registerCommands(void) { if (_mcd == nullptr) { return; } _mcd->registerCommand( "Bridge", "bridge", "users", [this](std::string_view params, Message3Handle m) -> bool { const auto contact_from = m.get().c; const auto contact_to = m.get().c; Contact3Handle group_contact; const VirtualGroups* vg = nullptr; if (!params.empty()) { // vgroup_name supplied for (const auto& vg_it : _vgroups) { if (vg_it.vg_name == params) { vg = &vg_it; break; } } if (vg == nullptr) { _rmm.sendText( contact_from, "The supplied vgroup name does not exist!" ); return true; } } else { if (/*is public ?*/ _c_to_vg.count({_cr, contact_to})) { // message was sent public in group group_contact = {_cr, contact_to}; } else if (_cr.all_of(contact_from)) { // use parent of sender group_contact = {_cr, _cr.get(contact_from).parent}; } else if (false /* parent of contact_to ? */) { } if (!_c_to_vg.count(group_contact)) { // nope _rmm.sendText( contact_from, "It appears you are not bridged or forgot to supply the vgroup name!" ); return true; } assert(static_cast(group_contact)); vg = &_vgroups.at(_c_to_vg.at(group_contact)); } assert(vg != nullptr); _rmm.sendText( contact_from, "Contacts online in other bridged group(s) in vgroup '" + vg->vg_name + "'" ); for (const auto& vgc : vg->contacts) { if (vgc.c == group_contact) { // skip self continue; } if (vgc.c.all_of()) { _rmm.sendText( contact_from, " online in '" + (vgc.c.all_of() ? vgc.c.get().name : "") + "' id:" + bin2hex(vgc.id) ); // for each sub contact for (const auto& sub_c : vgc.c.get().subs) { if ( const auto* sub_cs = _cr.try_get(sub_c); sub_cs != nullptr && sub_cs->state == Contact::Components::ConnectionState::disconnected ) { // skip offline continue; } _rmm.sendText( contact_from, " '" + (_cr.all_of(sub_c) ? _cr.get(sub_c).name : "") + "'" + (_cr.all_of(sub_c) ? " id:" + bin2hex(_cr.get(sub_c).data) : "") ); } } else { // contact without parent _rmm.sendText( contact_from, " online contact '" + (vgc.c.all_of() ? vgc.c.get().name : "") + "' id:" + bin2hex(vgc.id) ); } } return true; }, "List users connected in the other bridged group(s).", MessageCommandDispatcher::Perms::EVERYONE // TODO: should proably be MODERATOR ); std::cout << "Bridge: registered commands\n"; } const Bridge::VirtualGroups* Bridge::findVGforContact(const Contact3Handle& c) { return nullptr; } bool Bridge::onEvent(const Message::Events::MessageConstruct& e) { if (!e.e.valid()) { return false; // how } if (!e.e.all_of()) { return false; // non text message, skip } if (!e.e.all_of()) { return false; // how } if (e.e.all_of()) { int64_t time_diff = int64_t(Message::getTimeMS()) - int64_t(e.e.get().ts); if (time_diff > 5*1000*60) { return false; // message too old } } const auto contact_from = e.e.get().c; if (_cr.any_of(contact_from)) { return false; // skip own messages } const auto contact_to = e.e.get().c; // if e.e is in c to vg const auto it = _c_to_vg.find(Contact3Handle{_cr, contact_to}); if (it == _c_to_vg.cend()) { return false; // contact is not bridged } const auto& vgroup = _vgroups.at(it->second); const auto& message_text = e.e.get().text; const bool is_action = e.e.all_of(); const bool use_angle_brackets = _conf.get_bool("Bridge", "username_angle_brackets", vgroup.vg_name).value(); std::string from_str; if (use_angle_brackets) { from_str += "<"; } if (_cr.all_of(contact_from)) { const auto& name = _cr.get(contact_from).name; if (name.empty()) { from_str += "UNK"; } else { from_str += name.substr(0, 16); } } if (_conf.get_bool("Bridge", "username_id", vgroup.vg_name).value()) { if (_cr.all_of(contact_from)) { // copy auto id = _cr.get(contact_from).data; id.resize(3); // TODO:make size configurable // TODO: make seperator configurable from_str += "#" + bin2hex(id); } } if (use_angle_brackets) { from_str += ">"; } if (_conf.get_bool("Bridge", "username_colon", vgroup.vg_name).value()) { from_str += ":"; } from_str += " "; // for each c in vg not c... for (const auto& other_vc : vgroup.contacts) { if (other_vc.c == contact_to) { continue; // skip self } // TODO: support fake/virtual contacts. true bridging std::string relayed_message {from_str}; relayed_message += message_text; _rmm.sendText( other_vc.c, relayed_message, is_action ); } return false; }