From: Attila Molnar Date: Sat, 24 Jan 2015 13:49:10 +0000 (+0100) Subject: Add User::ForEachNeighbor() X-Git-Url: https://git.netwichtig.de/gitweb/?a=commitdiff_plain;h=662dfa6c181a8c1d97a0c65499679e0eb1b399e2;p=user%2Fhenk%2Fcode%2Finspircd.git Add User::ForEachNeighbor() --- diff --git a/include/users.h b/include/users.h index ceee4396b..6f319018f 100644 --- a/include/users.h +++ b/include/users.h @@ -248,6 +248,19 @@ class CoreExport User : public Extensible std::bitset modes; public: + /** To execute a function for each local neighbor of a user, inherit from this class and + * pass an instance of it to User::ForEachNeighbor(). + */ + class ForEachNeighborHandler + { + public: + /** Method to execute for each local neighbor of a user. + * Derived classes must implement this. + * @param user Current neighbor + */ + virtual void Execute(LocalUser* user) = 0; + }; + /** List of Memberships for this user */ typedef insp::intrusive_list ChanList; @@ -542,6 +555,16 @@ class CoreExport User : public Extensible */ void WriteCommonQuit(const std::string &normal_text, const std::string &oper_text); + /** Execute a function once for each local neighbor of this user. By default, the neighbors of a user are the users + * who have at least one common channel with the user. Modules are allowed to alter the set of neighbors freely. + * This function is used for example to send something conditionally to neighbors, or to send different messages + * to different users depending on their oper status. + * @param handler Function object to call, inherited from ForEachNeighborHandler. + * @param include_self True to include this user in the set of neighbors, false otherwise. + * Modules may override this. Has no effect if this user is not local. + */ + void ForEachNeighbor(ForEachNeighborHandler& handler, bool include_self = true); + /** Dump text to a user target, splitting it appropriately to fit * @param linePrefix text to prefix each complete line with * @param textStream the text to send to the user diff --git a/src/users.cpp b/src/users.cpp index 34986a183..cb1bc901e 100644 --- a/src/users.cpp +++ b/src/users.cpp @@ -936,6 +936,59 @@ void User::WriteCommonQuit(const std::string &normal_text, const std::string &op } } +void User::ForEachNeighbor(ForEachNeighborHandler& handler, bool include_self) +{ + // The basic logic for visiting the neighbors of a user is to iterate the channel list of the user + // and visit all users on those channels. Because two users may share more than one common channel, + // we must skip users that we have already visited. + // To do this, we make use of a global counter and an integral 'already_sent' field in LocalUser. + // The global counter is incremented every time we do something for each neighbor of a user. Then, + // before visiting a member we examine user->already_sent. If it's equal to the current counter, we + // skip the member. Otherwise, we set it to the current counter and visit the member. + + // Ask modules to build a list of exceptions. + // Mods may also exclude entire channels by erasing them from include_chans. + IncludeChanList include_chans(chans.begin(), chans.end()); + std::map exceptions; + exceptions[this] = include_self; + FOREACH_MOD(OnBuildNeighborList, (this, include_chans, exceptions)); + + // Get next id, guaranteed to differ from the already_sent field of all users + const already_sent_t newid = ++LocalUser::already_sent_id; + + // Handle exceptions first + for (std::map::const_iterator i = exceptions.begin(); i != exceptions.end(); ++i) + { + LocalUser* curr = IS_LOCAL(i->first); + if (curr) + { + // Mark as visited to ensure we won't visit again if there is a common channel + curr->already_sent = newid; + // Always treat quitting users as excluded + if ((i->second) && (!curr->quitting)) + handler.Execute(curr); + } + } + + // Now consider the real neighbors + for (IncludeChanList::const_iterator i = include_chans.begin(); i != include_chans.end(); ++i) + { + Channel* chan = (*i)->chan; + const Channel::MemberMap& userlist = chan->GetUsers(); + for (Channel::MemberMap::const_iterator j = userlist.begin(); j != userlist.end(); ++j) + { + LocalUser* curr = IS_LOCAL(j->first); + // User not yet visited? + if ((curr) && (curr->already_sent != newid)) + { + // Mark as visited and execute function + curr->already_sent = newid; + handler.Execute(curr); + } + } + } +} + void LocalUser::SendText(const std::string& line) { Write(line);