Skip to content

Commit 863edcf

Browse files
committed
nevent/nprofile
1 parent 9bca396 commit 863edcf

File tree

4 files changed

+67
-10
lines changed

4 files changed

+67
-10
lines changed

src/apps/web/Bech32Utils.h

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,51 @@ inline std::string encodeBech32Simple(const std::string &hrp, std::string_view v
4141
return bech32::encode(hrp, values5, bech32::Encoding::BECH32);
4242
}
4343

44-
inline std::string decodeBech32Simple(std::string_view v) {
44+
inline std::string decodeBech32Raw(std::string_view v) {
4545
auto res = bech32::decode(std::string(v));
4646

4747
if (res.encoding == bech32::Encoding::INVALID) throw herr("invalid bech32");
4848
else if (res.encoding == bech32::Encoding::BECH32M) throw herr("got bech32m");
4949

5050
std::vector<uint8_t> out;
5151
if (!convertbits<5, 8, false>(out, res.data)) throw herr("convertbits failed");
52-
if (out.size() != 32) throw herr("unexpected size from bech32");
5352

5453
return std::string((char*)out.data(), out.size());
5554
}
55+
56+
inline std::string decodeBech32Simple(std::string_view v) {
57+
auto out = decodeBech32Raw(v);
58+
if (out.size() != 32) throw herr("unexpected size from bech32");
59+
return out;
60+
}
61+
62+
inline uint8_t getByte(std::string_view &encoded) {
63+
if (encoded.size() < 1) throw herr("parse ends prematurely");
64+
uint8_t output = encoded[0];
65+
encoded = encoded.substr(1);
66+
return output;
67+
}
68+
69+
inline std::string getBytes(std::string_view &encoded, size_t n) {
70+
if (encoded.size() < n) throw herr("parse ends prematurely");
71+
auto res = encoded.substr(0, n);
72+
encoded = encoded.substr(n);
73+
return std::string(res);
74+
};
75+
76+
inline std::string decodeBech32GetSpecial(std::string_view origStr) {
77+
auto decoded = decodeBech32Raw(origStr);
78+
std::string_view s(decoded);
79+
80+
while (s.size()) {
81+
auto tag = getByte(s);
82+
auto len = getByte(s);
83+
auto val = getBytes(s, len);
84+
if (tag == 0) {
85+
if (len != 32) throw herr("unexpected size from bech32 special");
86+
return std::string(val);
87+
}
88+
}
89+
90+
throw herr("no special tag found");
91+
}

src/apps/web/TODO

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
read
2-
* support nprofile/nevent/etc links
3-
example nevent: https://oddbean.com/e/note1qmye0at28we63aze93xjr92nzw725td0a5ncz3htwlc3wg78kp6q7802ad
4-
example nprofile: https://oddbean.com/e/note1ykjalrpaj6jvxeuc434yd7ksrj8yd2vte478700ta8np250l3clsyjvh4q
52
* non-500 error pages when bech32 fails to parse, for example
63
* search field: enter anything, pubkey (hex or npub), eventId, etc. maybe even full-text search?
74
* rss

src/apps/web/WebData.h

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ struct Event {
337337
}
338338

339339
void preprocessEventContent(lmdb::txn &txn, Decompressor &decomp, UserCache &userCache, std::string &content, bool withLinks = true) const {
340-
static RE2 matcher(R"((?is)(.*?)(https?://\S+|#\[\d+\]|nostr:(?:note|npub)1\w+))");
340+
static RE2 matcher(R"((?is)(.*?)(https?://\S+|#\[\d+\]|nostr:(?:note|npub|nevent|nprofile)1\w+))");
341341

342342
std::string output;
343343

@@ -367,16 +367,38 @@ struct Event {
367367
std::string path = "/e/";
368368
path += sv(match).substr(6);
369369
appendLink(path, sv(match));
370+
} else if (match.starts_with("nostr:nevent1")) {
371+
bool didTransform = false;
372+
373+
try {
374+
auto id = decodeBech32GetSpecial(sv(match).substr(6));
375+
std::string path = "/e/";
376+
path += encodeBech32Simple("note", id);
377+
378+
appendLink(path, sv(match));
379+
didTransform = true;
380+
} catch(...) {}
381+
382+
if (!didTransform) output += sv(match);
370383
} else if (match.starts_with("nostr:npub1")) {
371384
bool didTransform = false;
372385

373386
try {
374387
const auto *u = userCache.getUser(txn, decomp, decodeBech32Simple(sv(match).substr(6)));
375388
appendLink(std::string("/u/") + u->npubId, std::string("@") + u->username);
376389
didTransform = true;
377-
} catch(std::exception &e) {
378-
//LW << "tag parse error: " << e.what();
379-
}
390+
} catch(...) {}
391+
392+
if (!didTransform) output += sv(match);
393+
} else if (match.starts_with("nostr:nprofile")) {
394+
bool didTransform = false;
395+
396+
try {
397+
auto pubkey = decodeBech32GetSpecial(sv(match).substr(6));
398+
const auto *u = userCache.getUser(txn, decomp, pubkey);
399+
appendLink(std::string("/u/") + u->npubId, std::string("@") + u->username);
400+
didTransform = true;
401+
} catch(...) {}
380402

381403
if (!didTransform) output += sv(match);
382404
} else if (match.starts_with("#[")) {

src/apps/web/bech32.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ namespace bech32
3737
namespace
3838
{
3939

40+
const size_t BECH32_MAX_SIZE = 5000;
41+
4042
typedef std::vector<uint8_t> data;
4143

4244
/** The Bech32 character set for encoding. */
@@ -204,7 +206,7 @@ DecodeResult decode(const std::string& str) {
204206
}
205207
if (lower && upper) return {};
206208
size_t pos = str.rfind('1');
207-
if (str.size() > 90 || pos == str.npos || pos == 0 || pos + 7 > str.size()) {
209+
if (str.size() > BECH32_MAX_SIZE || pos == str.npos || pos == 0 || pos + 7 > str.size()) {
208210
return {};
209211
}
210212
data values(str.size() - 1 - pos);

0 commit comments

Comments
 (0)