#pragma once #include #include #include #include #include #include #include "irc_config.h" #include "irc_message.h" namespace roro { struct IrcLine { enum class Kind { Message, Action, Notice, Info, Own, OwnAction }; int64_t utc; // -1 if the clock wasn't set Kind kind; std::string nick; std::string text; }; // One IRC conversation (see Buffer in CONTEXT.md). struct IrcBuffer { enum class Type { Server, Channel, Query }; std::string name; Type type; std::deque lines; // the most recent kLinesPerBuffer; full history goes to Logs int unread = 0; bool mentioned = false; bool joined = false; std::string topic; }; struct IrcLogEntry { std::string buffer; IrcLine line; }; // What the session wants done: lines to send, Log entries, Notification texts. struct IrcEffects { std::vector send; std::vector logs; std::vector notifications; }; // The IRC protocol, without any networking: registration (SASL PLAIN or NickServ), Buffers, // unread counts, Mentions, the user's commands, and rejoining after a pause. The IRC Service // feeds it received lines and carries out the effects. class IrcSession { public: static constexpr size_t kLinesPerBuffer = 50; static constexpr int kMaxBuffers = 12; explicit IrcSession(const IrcConfig& config); // Joins wait this long for NickServ to confirm a login before going ahead anyway. static constexpr uint32_t kNickservWaitMs = 2000; // Uptime in ms, called regularly: releases joins held back for NickServ. void tick(uint32_t nowMs); void connected(int64_t utc); void disconnected(int64_t utc, const std::string& reason); void receive(const std::string& raw, int64_t utc); void input(int buffer, const std::string& text, int64_t utc); // The Buffer the user is looking at (-1: none); it neither counts unread nor notifies. void setViewing(int buffer); bool registered() const { return registered_; } bool quitRequested() const { return quit_; } const std::string& nick() const { return nick_; } int bufferCount() const { return static_cast(buffers_.size()); } const IrcBuffer& buffer(int i) const { return buffers_[i]; } int totalUnread() const; // Changes whenever a Buffer gains a line or changes, so a view knows when to redraw. uint32_t revision() const { return revision_; } IrcEffects takeEffects(); private: void send(const std::string& line) { effects_.send.push_back(line); } int findBuffer(const std::string& name) const; int bufferFor(const std::string& name, IrcBuffer::Type type); // creates if needed and possible void add(int buffer, IrcLine::Kind kind, const std::string& nick, const std::string& text, int64_t utc, bool countsAsMention = false); void info(int buffer, const std::string& text, int64_t utc) { add(buffer, IrcLine::Kind::Info, "", text, utc); } bool mentionsMe(const std::string& text) const; void onPrivmsg(const IrcMessage& m, int64_t utc, bool notice); void onWelcome(const IrcMessage& m, int64_t utc); void joinChannels(); void command(int buffer, const std::string& text, int64_t utc); void say(int buffer, const std::string& text, int64_t utc, bool action); const IrcConfig& config_; std::vector buffers_; std::string nick_; bool registered_ = false; bool quit_ = false; int viewing_ = -1; std::set namesRequested_; std::vector rejoin_; // IRC channels to join again after a reconnect std::map keys_; // lower-case IRC channel -> key, from config and /join bool joinsHeld_ = false; // waiting for NickServ before joining bool saslFailed_ = false; uint32_t heldSinceMs_ = 0; uint32_t nowMs_ = 0; IrcEffects effects_; uint32_t revision_ = 0; }; } // namespace roro