Public Access
- With NickServ, auto-join waits for the logged-in reply (900) or 2 s
at most, so registered-only IRC channels let us in
- A failed SASL login falls back to NickServ (its own password, or the
SASL account and password)
- IDENTIFY names the account explicitly, and once logged in on a
fallback nick, REGAIN takes ours back from a stale session
- Auto-join entries take keys ("#private key, #public"); keys from
/join are reused when rejoining; keyed channels go first in JOIN
- Serial irc dump: whole Buffers, and which login is configured
(never the secrets)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
115 lines
3.9 KiB
C++
115 lines
3.9 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <deque>
|
|
#include <map>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#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<IrcLine> 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<std::string> send;
|
|
std::vector<IrcLogEntry> logs;
|
|
std::vector<std::string> 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<int>(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<IrcBuffer> buffers_;
|
|
std::string nick_;
|
|
bool registered_ = false;
|
|
bool quit_ = false;
|
|
int viewing_ = -1;
|
|
std::set<std::string> namesRequested_;
|
|
std::vector<std::string> rejoin_; // IRC channels to join again after a reconnect
|
|
std::map<std::string, std::string> 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
|