Profile: 614787c4...

note.cpp

updates:

- removed markdown

```cpp

#ifndef QNOSTRRELAY_H

#define QNOSTRRELAY_H

#include

#include

#include

#include

#include

#include

#include

#include "qtnostr_global.h"

QT_BEGIN_NAMESPACE

class LIBQTNOSTR_CORE_EXPORT QNostrRelay : public QObject

{

Q_OBJECT

class Private;

friend class QNostr;

public:

struct LIBQTNOSTR_CORE_EXPORT Event {

std::optional id;

std::optional pubkey;

std::optional created_at;

int kind;

QList tags;

QString content;

std::optional sig;

QJsonArray tagsArray() const;

QString serialize() const;

static Event deserialize(const QJsonObject &obj);

};

struct LIBQTNOSTR_CORE_EXPORT Request {

std::optional subscriptionId;

QStringList ids;

QStringList authors;

QList kinds;

QStringList e;

QStringList p;

std::optional since;

std::optional until;

int limit = 1;

QString serialize() const;

};

struct LIBQTNOSTR_CORE_EXPORT Close {

QString subscriptionId;

QString serialize() const;

};

QNostrRelay(const QUrl &relay, const QString &secretKey, QObject *parent = nullptr);

QNostrRelay(const QUrl &relay, const QString &publicKey, const QString &privateKey, QObject *parent = nullptr);

virtual ~QNostrRelay();

public Q_SLOTS:

void start();

void stop();

QString sendEvent(const QString &content);

QString sendEvent(Event event, bool prepared = false);

QString sendRequest(Request request);

void sendClose(const Close &request);

void sendClose(const QString &subscriptionId);

static void prepareEvent(Event &event, const QByteArray &publicKey, const QByteArray &privateKey);

static QByteArray compressedPublicKey(const QString &secretKey);

static QByteArray extractPrivateKey(const QByteArray& base64SecretKey);

Q_SIGNALS:

void failed(const QString &id, const QString &reason);

void successfully(const QString &id);

void error(QAbstractSocket::SocketError error);

void sslErrors(const QList &errors);

void newEvent(const QString &subscribeId, const Event &event, bool storedEvent);

void notice(const QString &msg);

void syncEventsFinished(const QString &subscribeId);

void disconnected();

void connected();

protected:

static QString calculateId(const Event &event);

static QByteArray sign(const QByteArray &data, const QByteArray &privateKey);

private:

void serverConnected();

void serverDisonnected();

void analizeData(const QString &data);

void init();

private:

Private *p;

};

QT_END_NAMESPACE

#endif // QNOSTRRELAY_H

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

const int CHECKSUM_LENGTH = 6;

const std::string CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";

QWidget* window;

QBoxLayout* events_container;

QLabel* pk_label;

QLineEdit * filename_input;

QTextEdit* textEdit;

QString secret;

QString pk;

QString pk_hex;

QNostr* nostr;

QWebSocket* socket;

std::unordered_map CHAR_LOOKUP;

std::vector expandPrefix(const std::string& prefix) {

std::vector outBuffer(2 * prefix.length() + 1);

for (size_t i = 0; i < prefix.length(); i++) {

char code = prefix[i];

outBuffer[i] = code >> 5;

outBuffer[i + prefix.length() + 1] = code & 31;

}

outBuffer[prefix.length()] = 0;

return outBuffer;

}

std::vector decodeWithPrefix(const std::string& prefix, const std::string& message) {

std::vector dst(message.length() + 2 * prefix.length() + 1);

std::vector prefixBuffer = expandPrefix(prefix);

for (size_t i = 0; i < prefixBuffer.size(); i++) {

dst[i] = prefixBuffer[i];

}

for (size_t i = 0; i < message.length(); i++) {

dst[2 * prefix.length() + 1 + i] = CHAR_LOOKUP[message[i]];

}

return dst;

}

std::vector decodeTo5BitArray(const std::string& message) {

std::string lowerCaseMsg = message;

std::transform(lowerCaseMsg.begin(), lowerCaseMsg.end(), lowerCaseMsg.begin(), ::tolower);

size_t sepIdx = lowerCaseMsg.find_last_of('1');

std::string prefix = lowerCaseMsg.substr(0, sepIdx);

std::string suffix = lowerCaseMsg.substr(sepIdx + 1);

std::vector bitArray = decodeWithPrefix(prefix, suffix);

return std::vector(bitArray.begin() + 2 * prefix.length() + 1, bitArray.end() - CHECKSUM_LENGTH);

}

std::vector from5BitArray(const std::vector& src) {

size_t len = (src.size() * 5) / 8;

std::vector dst(len);

int acc = 0;

int bits = 0;

int dstIndex = 0;

for (size_t i = 0; i < src.size(); i++) {

acc = (acc << 5) | src[i];

bits += 5;

while (bits >= 8) {

bits -= 8;

dst[dstIndex++] = (acc >> bits) & 0xFF;

}

}

return dst;

}

std::string toHex(const std::vector& data) {

std::string hex;

for (uint8_t b : data) {

std::bitset<8> bits(b);

std::stringstream ss;

ss << std::hex << bits.to_ulong();

std::string temp = ss.str();

if (temp.length() == 1) {

temp = "0" + temp;

}

hex += temp;

}

return hex;

}

QString bech32tohex(QString id){

if(id.length() != 63){

return id;

}

std::vector data = decodeTo5BitArray(id.toStdString());

std::vector result = from5BitArray(data);

return QString::fromStdString(toHex(result));

}

void add_note_button(QString eid){

QPushButton* event_button = new QPushButton(eid.left(15), window);

events_container->addWidget(event_button);

QObject::connect(event_button, &QPushButton::clicked, [eid](){

textEdit->setText(eid);

});

}

void save_privkey(QString key){

QFile file("note_privkey.txt");

if (!file.open(QIODevice::WriteOnly | QIODevice::Text))

return;

QTextStream out(&file);

out << key;

}

QString load_privkey(){

QFile file("note_privkey.txt");

if (!file.open(QIODevice::ReadOnly | QIODevice::Text)){

QString sk = QNostr::generateNewSecret();

save_privkey(sk);

return sk;

}

return QString(file.readLine());

}

void load_notes(){

QFile file("mynotes.txt");

if (!file.open(QIODevice::ReadOnly | QIODevice::Text))

return;

while (!file.atEnd()) {

QString eid = QString(file.readLine()).trimmed();

add_note_button(eid);

}

}

void append_note(QString eid){

QFile file("mynotes.txt");

if (!file.open(QIODevice::Append | QIODevice::Text))

return;

QTextStream out(&file);

out << eid << "\n";

}

/*QString request_named_note(QString name){

const QString eid = bech32tohex(textEdit->toPlainText());

QNostrRelay::Request req;

req.authors << pk;

req["#d"] = name;

relay.sendRequest(req);

}*/

int main(int argc, char **argv) {

QApplication app(argc, argv);

window = new QWidget;

events_container = new QBoxLayout(QBoxLayout::TopToBottom);

for (size_t i = 0; i < CHARSET.length(); i++) {

CHAR_LOOKUP[CHARSET[i]] = i;

}

secret = load_privkey();

nostr = new QNostr(secret);

pk_hex = QByteArray::fromBase64(nostr->publicKey().toLocal8Bit(), QByteArray::Base64Encoding)

.mid(1).toHex().toLower();

pk = QNostrRelay::compressedPublicKey(secret);

QFont font;

font.setPointSize(18);

window->setWindowTitle("note.cpp | nostr notepad");

window->setFont(font);

window->resize(1280, 800);

textEdit = new QTextEdit(window);

textEdit->setAcceptRichText(false);

textEdit->setPlaceholderText("write a message or paste eventid");

filename_input = new QLineEdit(window);

filename_input->setPlaceholderText("filename");

QPushButton* eid_button = new QPushButton("", window);

eid_button->setFlat(true);

eid_button->setEnabled(false);

QPushButton* send_button = new QPushButton("Send", window);

QPushButton* query_button = new QPushButton("Query", window);

QGridLayout layout = QGridLayout(window);

window->setLayout(&layout);

layout.addWidget(filename_input, 0, 0, 1, 2);

layout.addWidget(textEdit, 1, 0, 1, 2);

layout.addWidget(send_button, 2, 0);

layout.addWidget(query_button, 2, 1);

layout.addLayout(events_container, 0, 2, 3, 1, Qt::AlignTop);

pk_label = new QLabel(pk_hex.left(25), window);

events_container->addWidget(pk_label);

load_notes();

window->show();

socket = new QWebSocket();

socket->open(QUrl("wss://nos.lol"));

QObject::connect(send_button, &QPushButton::clicked, [](){

QString content = textEdit->toPlainText();

QNostrRelay::Event event;

event.kind = 1;

QStringList dtag;

dtag << "d" << filename_input->text();

event.tags << dtag;

event.content = content;

QNostrRelay::prepareEvent(event, pk.toLocal8Bit(), QNostrRelay::extractPrivateKey(secret.toLatin1()));

QString event_str = event.serialize();

socket->sendTextMessage(event.serialize());

add_note_button(event.id.value());

append_note(event.id.value());

textEdit->setText("");

filename_input->setText("");

});

QObject::connect(query_button, &QPushButton::clicked, [](){

const QString eid = bech32tohex(textEdit->toPlainText());

QNostrRelay::Request req;

req.subscriptionId = "q";

req.ids << eid;

socket->sendTextMessage(req.serialize());

});

QNostr::connect(socket, &QWebSocket::textMessageReceived, [](const QString &message) {

QJsonDocument jsonDoc = QJsonDocument::fromJson(message.toUtf8());

QJsonArray jsonArray = jsonDoc.array();

QString dvalue = QString("");

if(jsonArray.at(0).toString() == "EVENT"){

QString content = jsonArray.at(2).toObject().value("content").toString();

QJsonArray tags = jsonArray.at(2).toObject().value("tags").toArray();

for (int i = 0; i < tags.size(); i++) {

QJsonArray tag = tags.at(i).toArray();

if(tag.at(0).toString() == "d"){

dvalue = tag.at(1).toString();

break;

}

}

filename_input->setText(dvalue);

textEdit->setText(content);

}

});

app.exec();

}

```

note.cpp

updates:

- added bold CTRL+B, italic CTRL+I, underline CTRL+U

```cpp

#ifndef QNOSTRRELAY_H

#define QNOSTRRELAY_H

#include

#include

#include

#include

#include

#include

#include

#include "qtnostr_global.h"

QT_BEGIN_NAMESPACE

class LIBQTNOSTR_CORE_EXPORT QNostrRelay : public QObject

{

Q_OBJECT

class Private;

friend class QNostr;

public:

struct LIBQTNOSTR_CORE_EXPORT Event {

std::optional id;

std::optional pubkey;

std::optional created_at;

int kind;

QList tags;

QString content;

std::optional sig;

QJsonArray tagsArray() const;

QString serialize() const;

static Event deserialize(const QJsonObject &obj);

};

struct LIBQTNOSTR_CORE_EXPORT Request {

std::optional subscriptionId;

QStringList ids;

QStringList authors;

QList kinds;

QStringList e;

QStringList p;

std::optional since;

std::optional until;

int limit = 1;

QString serialize() const;

};

struct LIBQTNOSTR_CORE_EXPORT Close {

QString subscriptionId;

QString serialize() const;

};

QNostrRelay(const QUrl &relay, const QString &secretKey, QObject *parent =

nullptr);

QNostrRelay(const QUrl &relay, const QString &publicKey, const QString

&privateKey, QObject *parent = nullptr);

virtual ~QNostrRelay();

public Q_SLOTS:

void start();

void stop();

QString sendEvent(const QString &content);

QString sendEvent(Event event, bool prepared = false);

QString sendRequest(Request request);

void sendClose(const Close &request);

void sendClose(const QString &subscriptionId);

static void prepareEvent(Event &event, const QByteArray &publicKey, const

QByteArray &privateKey);

static QByteArray compressedPublicKey(const QString &secretKey);

static QByteArray extractPrivateKey(const QByteArray& base64SecretKey);

Q_SIGNALS:

void failed(const QString &id, const QString &reason);

void successfully(const QString &id);

void error(QAbstractSocket::SocketError error);

void sslErrors(const QList &errors);

void newEvent(const QString &subscribeId, const Event &event, bool

storedEvent);

void notice(const QString &msg);

void syncEventsFinished(const QString &subscribeId);

void disconnected();

void connected();

protected:

static QString calculateId(const Event &event);

static QByteArray sign(const QByteArray &data, const QByteArray

&privateKey);

private:

void serverConnected();

void serverDisonnected();

void analizeData(const QString &data);

void init();

private:

Private *p;

};

QT_END_NAMESPACE

#endif // QNOSTRRELAY_H

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

const int CHECKSUM_LENGTH = 6;

const std::string CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";

QWidget* window;

QBoxLayout* events_container;

QLabel* pk_label;

QLineEdit * filename_input;

QTextEdit* textEdit;

QString secret;

QString pk;

QString pk_hex;

QNostr* nostr;

QWebSocket* socket;

QMap>

format_functions = {

{'B', [](QTextCursor &cursor, QTextCharFormat &format) {

format.setFontWeight(cursor.charFormat().font().bold() ? QFont::Normal :

QFont::Bold);

}},

{'I', [](QTextCursor &cursor, QTextCharFormat &format) {

format.setFontItalic(cursor.charFormat().font().italic() ?

QFont::StyleNormal : QFont::StyleItalic);

}},

{'U', [](QTextCursor &cursor, QTextCharFormat &format) {

format.setFontUnderline(!cursor.charFormat().font().underline());

}}

};

std::unordered_map CHAR_LOOKUP;

std::vector expandPrefix(const std::string& prefix) {

std::vector outBuffer(2 * prefix.length() + 1);

for (size_t i = 0; i < prefix.length(); i++) {

char code = prefix[i];

outBuffer[i] = code >> 5;

outBuffer[i + prefix.length() + 1] = code & 31;

}

outBuffer[prefix.length()] = 0;

return outBuffer;

}

std::vector decodeWithPrefix(const std::string& prefix, const

std::string& message) {

std::vector dst(message.length() + 2 * prefix.length() + 1);

std::vector prefixBuffer = expandPrefix(prefix);

for (size_t i = 0; i < prefixBuffer.size(); i++) {

dst[i] = prefixBuffer[i];

}

for (size_t i = 0; i < message.length(); i++) {

dst[2 * prefix.length() + 1 + i] = CHAR_LOOKUP[message[i]];

}

return dst;

}

std::vector decodeTo5BitArray(const std::string& message) {

std::string lowerCaseMsg = message;

std::transform(lowerCaseMsg.begin(), lowerCaseMsg.end(),

lowerCaseMsg.begin(), ::tolower);

size_t sepIdx = lowerCaseMsg.find_last_of('1');

std::string prefix = lowerCaseMsg.substr(0, sepIdx);

std::string suffix = lowerCaseMsg.substr(sepIdx + 1);

std::vector bitArray = decodeWithPrefix(prefix, suffix);

return std::vector(bitArray.begin() + 2 * prefix.length() + 1,

bitArray.end() - CHECKSUM_LENGTH);

}

std::vector from5BitArray(const std::vector& src) {

size_t len = (src.size() * 5) / 8;

std::vector dst(len);

int acc = 0;

int bits = 0;

int dstIndex = 0;

for (size_t i = 0; i < src.size(); i++) {

acc = (acc << 5) | src[i];

bits += 5;

while (bits >= 8) {

bits -= 8;

dst[dstIndex++] = (acc >> bits) & 0xFF;

}

}

return dst;

}

std::string toHex(const std::vector& data) {

std::string hex;

for (uint8_t b : data) {

std::bitset<8> bits(b);

std::stringstream ss;

ss << std::hex << bits.to_ulong();

std::string temp = ss.str();

if (temp.length() == 1) {

temp = "0" + temp;

}

hex += temp;

}

return hex;

}

QString bech32tohex(QString id){

if(id.length() != 63){

return id;

}

std::vector data = decodeTo5BitArray(id.toStdString());

std::vector result = from5BitArray(data);

return QString::fromStdString(toHex(result));

}

void add_note_button(QString eid){

QPushButton* event_button = new QPushButton(eid.left(15), window);

events_container->addWidget(event_button);

QObject::connect(event_button, &QPushButton::clicked, [eid](){

textEdit->setText(eid);

});

}

void save_privkey(QString key){

QFile file("note_privkey.txt");

if (!file.open(QIODevice::WriteOnly | QIODevice::Text))

return;

QTextStream out(&file);

out << key;

}

QString load_privkey(){

QFile file("note_privkey.txt");

if (!file.open(QIODevice::ReadOnly | QIODevice::Text)){

QString sk = QNostr::generateNewSecret();

save_privkey(sk);

return sk;

}

return QString(file.readLine());

}

void load_notes(){

QFile file("mynotes.txt");

if (!file.open(QIODevice::ReadOnly | QIODevice::Text))

return;

while (!file.atEnd()) {

QString eid = QString(file.readLine()).trimmed();

add_note_button(eid);

}

}

void append_note(QString eid){

QFile file("mynotes.txt");

if (!file.open(QIODevice::Append | QIODevice::Text))

return;

QTextStream out(&file);

out << eid << "\n";

}

/*QString request_named_note(QString name){

const QString eid = bech32tohex(textEdit->toPlainText());

QNostrRelay::Request req;

req.authors << pk;

req["#d"] = name;

relay.sendRequest(req);

}*/

int main(int argc, char **argv) {

QApplication app(argc, argv);

window = new QWidget;

events_container = new QBoxLayout(QBoxLayout::TopToBottom);

for (size_t i = 0; i < CHARSET.length(); i++) {

CHAR_LOOKUP[CHARSET[i]] = i;

}

secret = load_privkey();

nostr = new QNostr(secret);

pk_hex = QByteArray::fromBase64(nostr->publicKey().toLocal8Bit(),

QByteArray::Base64Encoding)

.mid(1).toHex().toLower();

pk = QNostrRelay::compressedPublicKey(secret);

QFont font;

font.setPointSize(18);

window->setWindowTitle("note.cpp | nostr notepad");

window->setFont(font);

window->resize(1280, 800);

textEdit = new QTextEdit(window);

textEdit->setAcceptRichText(true);

textEdit->setPlaceholderText("write a message or paste eventid");

filename_input = new QLineEdit(window);

filename_input->setPlaceholderText("filename");

QPushButton* eid_button = new QPushButton("", window);

eid_button->setFlat(true);

eid_button->setEnabled(false);

QPushButton* send_button = new QPushButton("Send", window);

QPushButton* query_button = new QPushButton("Query", window);

QGridLayout layout = QGridLayout(window);

window->setLayout(&layout);

layout.addWidget(filename_input, 0, 0, 1, 2);

layout.addWidget(textEdit, 1, 0, 1, 2);

layout.addWidget(send_button, 2, 0);

layout.addWidget(query_button, 2, 1);

layout.addLayout(events_container, 0, 2, 3, 1, Qt::AlignTop);

pk_label = new QLabel(pk_hex.left(25), window);

events_container->addWidget(pk_label);

load_notes();

window->show();

socket = new QWebSocket();

socket->open(QUrl("wss://nos.lol"));

for(char key : {'B', 'I', 'U'}) {

QAction *action = new QAction("ctrl_" + QString(key), window);

action->setShortcut(QKeySequence(Qt::CTRL + static_cast(key)));

textEdit->addAction(action);

QObject::connect(action, &QAction::triggered, [key](){

QTextCursor cursor = textEdit->textCursor();

cursor.select(QTextCursor::WordUnderCursor);

QTextCharFormat format;

format_functions[QChar(key)](cursor, format);

cursor.mergeCharFormat(format);

});

}

QObject::connect(send_button, &QPushButton::clicked, [](){

QString content = textEdit->toMarkdown();

QNostrRelay::Event event;

event.kind = 1;

QStringList dtag;

dtag << "d" << filename_input->text();

event.tags << dtag;

event.content = content;

QNostrRelay::prepareEvent(event, pk.toLocal8Bit(),

QNostrRelay::extractPrivateKey(secret.toLatin1()));

QString event_str = event.serialize();

socket->sendTextMessage(event.serialize());

add_note_button(event.id.value());

append_note(event.id.value());

textEdit->setText("");

filename_input->setText("");

});

QObject::connect(query_button, &QPushButton::clicked, [](){

const QString eid = bech32tohex(textEdit->toPlainText());

QNostrRelay::Request req;

req.subscriptionId = "q";

req.ids << eid;

socket->sendTextMessage(req.serialize());

});

QNostr::connect(socket, &QWebSocket::textMessageReceived, [](const QString

&message) {

QJsonDocument jsonDoc = QJsonDocument::fromJson(message.toUtf8());

QJsonArray jsonArray = jsonDoc.array();

QString dvalue = QString("");

if(jsonArray.at(0).toString() == "EVENT"){

QString content = jsonArray.at(2).toObject().value("content").toString();

QJsonArray tags = jsonArray.at(2).toObject().value("tags").toArray();

for (int i = 0; i < tags.size(); i++) {

QJsonArray tag = tags.at(i).toArray();

if(tag.at(0).toString() == "d"){

dvalue = tag.at(1).toString();

break;

}

}

filename_input->setText(dvalue);

textEdit->setMarkdown(content);

}

});

app.exec();

}

```

dsfs f **sfsdf** **_ds**_

this text is _underlined_

this _text_ _is_ _underlined_, **this is bold**

font **weight** test

note.cpp | nostr notepad

updates:

- single file release

install:

1) copy paste *source code* below

2)

```bash

git clone https://github.com/Aseman-Land/QNostr.git

qmake && make

```

run:

```bash

./note

```

source code:

```cpp

#ifndef QNOSTRRELAY_H

#define QNOSTRRELAY_H

#include

#include

#include

#include

#include

#include

#include

#include "qtnostr_global.h"

QT_BEGIN_NAMESPACE

class LIBQTNOSTR_CORE_EXPORT QNostrRelay : public QObject

{

Q_OBJECT

class Private;

friend class QNostr;

public:

struct LIBQTNOSTR_CORE_EXPORT Event {

std::optional id;

std::optional pubkey;

std::optional created_at;

int kind;

QList tags;

QString content;

std::optional sig;

QJsonArray tagsArray() const;

QString serialize() const;

static Event deserialize(const QJsonObject &obj);

};

struct LIBQTNOSTR_CORE_EXPORT Request {

std::optional subscriptionId;

QStringList ids;

QStringList authors;

QList kinds;

QStringList e;

QStringList p;

std::optional since;

std::optional until;

int limit = 1;

QString serialize() const;

};

struct LIBQTNOSTR_CORE_EXPORT Close {

QString subscriptionId;

QString serialize() const;

};

QNostrRelay(const QUrl &relay, const QString &secretKey, QObject *parent = nullptr);

QNostrRelay(const QUrl &relay, const QString &publicKey, const QString &privateKey, QObject *parent = nullptr);

virtual ~QNostrRelay();

public Q_SLOTS:

void start();

void stop();

QString sendEvent(const QString &content);

QString sendEvent(Event event, bool prepared = false);

QString sendRequest(Request request);

void sendClose(const Close &request);

void sendClose(const QString &subscriptionId);

static void prepareEvent(Event &event, const QByteArray &publicKey, const QByteArray &privateKey);

static QByteArray compressedPublicKey(const QString &secretKey);

static QByteArray extractPrivateKey(const QByteArray& base64SecretKey);

Q_SIGNALS:

void failed(const QString &id, const QString &reason);

void successfully(const QString &id);

void error(QAbstractSocket::SocketError error);

void sslErrors(const QList &errors);

void newEvent(const QString &subscribeId, const Event &event, bool storedEvent);

void notice(const QString &msg);

void syncEventsFinished(const QString &subscribeId);

void disconnected();

void connected();

protected:

static QString calculateId(const Event &event);

static QByteArray sign(const QByteArray &data, const QByteArray &privateKey);

private:

void serverConnected();

void serverDisonnected();

void analizeData(const QString &data);

void init();

private:

Private *p;

};

QT_END_NAMESPACE

#endif // QNOSTRRELAY_H

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

const int CHECKSUM_LENGTH = 6;

const std::string CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";

QWidget* window;

QBoxLayout* events_container;

QLabel* pk_label;

QLineEdit * filename_input;

QTextEdit* textEdit;

QString secret;

QString pk;

QString pk_hex;

QNostr* nostr;

QWebSocket* socket;

std::unordered_map CHAR_LOOKUP;

std::vector expandPrefix(const std::string& prefix) {

std::vector outBuffer(2 * prefix.length() + 1);

for (size_t i = 0; i < prefix.length(); i++) {

char code = prefix[i];

outBuffer[i] = code >> 5;

outBuffer[i + prefix.length() + 1] = code & 31;

}

outBuffer[prefix.length()] = 0;

return outBuffer;

}

std::vector decodeWithPrefix(const std::string& prefix, const std::string& message) {

std::vector dst(message.length() + 2 * prefix.length() + 1);

std::vector prefixBuffer = expandPrefix(prefix);

for (size_t i = 0; i < prefixBuffer.size(); i++) {

dst[i] = prefixBuffer[i];

}

for (size_t i = 0; i < message.length(); i++) {

dst[2 * prefix.length() + 1 + i] = CHAR_LOOKUP[message[i]];

}

return dst;

}

std::vector decodeTo5BitArray(const std::string& message) {

std::string lowerCaseMsg = message;

std::transform(lowerCaseMsg.begin(), lowerCaseMsg.end(), lowerCaseMsg.begin(), ::tolower);

size_t sepIdx = lowerCaseMsg.find_last_of('1');

std::string prefix = lowerCaseMsg.substr(0, sepIdx);

std::string suffix = lowerCaseMsg.substr(sepIdx + 1);

std::vector bitArray = decodeWithPrefix(prefix, suffix);

return std::vector(bitArray.begin() + 2 * prefix.length() + 1, bitArray.end() - CHECKSUM_LENGTH);

}

std::vector from5BitArray(const std::vector& src) {

size_t len = (src.size() * 5) / 8;

std::vector dst(len);

int acc = 0;

int bits = 0;

int dstIndex = 0;

for (size_t i = 0; i < src.size(); i++) {

acc = (acc << 5) | src[i];

bits += 5;

while (bits >= 8) {

bits -= 8;

dst[dstIndex++] = (acc >> bits) & 0xFF;

}

}

return dst;

}

std::string toHex(const std::vector& data) {

std::string hex;

for (uint8_t b : data) {

std::bitset<8> bits(b);

std::stringstream ss;

ss << std::hex << bits.to_ulong();

std::string temp = ss.str();

if (temp.length() == 1) {

temp = "0" + temp;

}

hex += temp;

}

return hex;

}

QString bech32tohex(QString id){

if(id.length() != 63){

return id;

}

std::vector data = decodeTo5BitArray(id.toStdString());

std::vector result = from5BitArray(data);

return QString::fromStdString(toHex(result));

}

void add_note_button(QString eid){

QPushButton* event_button = new QPushButton(eid.left(15), window);

events_container->addWidget(event_button);

QObject::connect(event_button, &QPushButton::clicked, [eid](){

textEdit->setText(eid);

});

}

void save_privkey(QString key){

QFile file("note_privkey.txt");

if (!file.open(QIODevice::WriteOnly | QIODevice::Text))

return;

QTextStream out(&file);

out << key;

}

QString load_privkey(){

QFile file("note_privkey.txt");

if (!file.open(QIODevice::ReadOnly | QIODevice::Text)){

QString sk = QNostr::generateNewSecret();

save_privkey(sk);

return sk;

}

return QString(file.readLine());

}

void load_notes(){

QFile file("mynotes.txt");

if (!file.open(QIODevice::ReadOnly | QIODevice::Text))

return;

while (!file.atEnd()) {

QString eid = QString(file.readLine()).trimmed();

add_note_button(eid);

}

}

void append_note(QString eid){

QFile file("mynotes.txt");

if (!file.open(QIODevice::Append | QIODevice::Text))

return;

QTextStream out(&file);

out << eid << "\n";

}

/*QString request_named_note(QString name){

const QString eid = bech32tohex(textEdit->toPlainText());

QNostrRelay::Request req;

req.authors << pk;

req["#d"] = name;

relay.sendRequest(req);

}*/

int main(int argc, char **argv) {

QApplication app(argc, argv);

window = new QWidget;

events_container = new QBoxLayout(QBoxLayout::TopToBottom);

for (size_t i = 0; i < CHARSET.length(); i++) {

CHAR_LOOKUP[CHARSET[i]] = i;

}

secret = load_privkey();

nostr = new QNostr(secret);

pk_hex = QByteArray::fromBase64(nostr->publicKey().toLocal8Bit(), QByteArray::Base64Encoding)

.mid(1).toHex().toLower();

pk = QNostrRelay::compressedPublicKey(secret);

QFont font;

font.setPointSize(18);

window->setWindowTitle("note.cpp | nostr notepad");

window->setFont(font);

window->resize(1280, 800);

textEdit = new QTextEdit(window);

textEdit->setAcceptRichText(false);

textEdit->setPlaceholderText("write a message or paste eventid");

filename_input = new QLineEdit(window);

filename_input->setPlaceholderText("filename");

QPushButton* eid_button = new QPushButton("", window);

eid_button->setFlat(true);

eid_button->setEnabled(false);

QPushButton* send_button = new QPushButton("Send", window);

QPushButton* query_button = new QPushButton("Query", window);

QGridLayout layout = QGridLayout(window);

window->setLayout(&layout);

layout.addWidget(filename_input, 0, 0, 1, 2);

layout.addWidget(textEdit, 1, 0, 1, 2);

layout.addWidget(send_button, 2, 0);

layout.addWidget(query_button, 2, 1);

layout.addLayout(events_container, 0, 2, 3, 1, Qt::AlignTop);

pk_label = new QLabel(pk_hex.left(25), window);

events_container->addWidget(pk_label);

load_notes();

window->show();

socket = new QWebSocket();

socket->open(QUrl("wss://nos.lol"));

QObject::connect(send_button, &QPushButton::clicked, [](){

QString content = textEdit->toPlainText();

QNostrRelay::Event event;

event.kind = 1;

QStringList dtag;

dtag << "d" << filename_input->text();

event.tags << dtag;

event.content = content;

QNostrRelay::prepareEvent(event, pk.toLocal8Bit(), QNostrRelay::extractPrivateKey(secret.toLatin1()));

QString event_str = event.serialize();

socket->sendTextMessage(event.serialize());

add_note_button(event.id.value());

append_note(event.id.value());

textEdit->setText("");

filename_input->setText("");

});

QObject::connect(query_button, &QPushButton::clicked, [](){

const QString eid = bech32tohex(textEdit->toPlainText());

QNostrRelay::Request req;

req.subscriptionId = "q";

req.ids << eid;

socket->sendTextMessage(req.serialize());

});

QNostr::connect(socket, &QWebSocket::textMessageReceived, [](const QString &message) {

QJsonDocument jsonDoc = QJsonDocument::fromJson(message.toUtf8());

QJsonArray jsonArray = jsonDoc.array();

QString dvalue = QString("");

if(jsonArray.at(0).toString() == "EVENT"){

QString content = jsonArray.at(2).toObject().value("content").toString();

QJsonArray tags = jsonArray.at(2).toObject().value("tags").toArray();

for (int i = 0; i < tags.size(); i++) {

QJsonArray tag = tags.at(i).toArray();

if(tag.at(0).toString() == "d"){

dvalue = tag.at(1).toString();

break;

}

}

filename_input->setText(dvalue);

textEdit->setText(content);

}

});

app.exec();

}

```