M1 step 4a: IRC protocol core (host-tested)

lib/irc: IrcMessage parse/serialize, base64, ReconnectPolicy
(5 s .. 5 min), IrcConfig (validated, persisted) and IrcSession:
registration with SASL PLAIN or NickServ, nick fallback, PING, Buffers
capped at 50 lines with unread counts, Mentions, CTCP ACTION/VERSION,
topics, /names on request only, others' join/part/quit hidden, user
commands, and rejoining after a pause. No networking: the session
returns lines to send, Log entries and Notifications.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
2026-10-02 15:03:21 +02:00
co-authored by Claude Opus 5.5
parent d3948ccc3a
commit 311eb38723
11 changed files with 1142 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
#pragma once
#include <string>
#include <vector>
#include "key_value_store.h"
namespace roro {
// The one IRC server the IRC Service connects to, persisted in internal flash.
class IrcConfig {
public:
explicit IrcConfig(KeyValueStore& store) : store_(store) {}
std::string host = "irc.libera.chat";
int port = 6697;
bool tls = true;
bool allowSelfSigned = false; // pin the certificate on first use instead of checking a CA
std::string pinnedSha256; // hex fingerprint once pinned
std::string nick;
std::string saslUser, saslPassword; // SASL PLAIN when both are set
std::string nickservPassword; // otherwise IDENTIFY with NickServ, if set
std::vector<std::string> autojoin;
void load(const std::string& defaultNick);
std::string save(); // empty on success, otherwise why it was refused
std::string validate() const;
static std::vector<std::string> parseChannels(const std::string& text);
static std::string formatChannels(const std::vector<std::string>& channels);
private:
KeyValueStore& store_;
};
} // namespace roro