@@ -26,7 +26,10 @@ use lnurl::lnurl::LnUrl;
26
26
use nostr:: key:: SecretKey ;
27
27
use nostr:: nips:: nip04:: { decrypt, encrypt} ;
28
28
use nostr:: nips:: nip47:: * ;
29
- use nostr:: { Event , EventBuilder , EventId , Filter , JsonUtil , Keys , Kind , Metadata , Tag , Timestamp } ;
29
+ use nostr:: {
30
+ Alphabet , Event , EventBuilder , EventId , Filter , JsonUtil , Keys , Kind , Metadata ,
31
+ SingleLetterTag , Tag , TagKind , Timestamp ,
32
+ } ;
30
33
use nostr_sdk:: { Client , NostrSigner , RelayPoolNotification } ;
31
34
use serde:: { Deserialize , Serialize } ;
32
35
use std:: collections:: { HashMap , HashSet } ;
@@ -1239,6 +1242,39 @@ impl<S: MutinyStorage> NostrManager<S> {
1239
1242
Ok ( event_id)
1240
1243
}
1241
1244
1245
+ /// Creates a recommendation event for a federation
1246
+ pub async fn recommend_federation (
1247
+ & self ,
1248
+ invite_code : & InviteCode ,
1249
+ review : Option < & str > ,
1250
+ ) -> Result < EventId , MutinyError > {
1251
+ let kind = Kind :: from ( 38000 ) ;
1252
+
1253
+ // properly tag the event as a federation with the federation id
1254
+ let d_tag = Tag :: Identifier ( invite_code. federation_id ( ) . to_string ( ) ) ;
1255
+ let k_tag = Tag :: Generic (
1256
+ TagKind :: SingleLetter ( SingleLetterTag :: lowercase ( Alphabet :: K ) ) ,
1257
+ vec ! [ "38173" . to_string( ) ] ,
1258
+ ) ;
1259
+
1260
+ // tag the federation invite code
1261
+ let invite_code_tag = Tag :: Generic (
1262
+ TagKind :: SingleLetter ( SingleLetterTag :: lowercase ( Alphabet :: U ) ) ,
1263
+ vec ! [ invite_code. to_string( ) ] ,
1264
+ ) ;
1265
+
1266
+ // todo tag the federation announcement event, to do so we need to have the pubkey of the federation
1267
+
1268
+ let builder = EventBuilder :: new (
1269
+ kind,
1270
+ review. unwrap_or_default ( ) ,
1271
+ [ d_tag, k_tag, invite_code_tag] ,
1272
+ ) ;
1273
+
1274
+ // send the event
1275
+ Ok ( self . client . send_event_builder ( builder) . await ?)
1276
+ }
1277
+
1242
1278
/// Queries our relays for federation announcements
1243
1279
pub async fn discover_federations ( & self ) -> Result < Vec < NostrDiscoveredFedimint > , MutinyError > {
1244
1280
// get contacts by npub
@@ -1284,11 +1320,13 @@ impl<S: MutinyStorage> NostrManager<S> {
1284
1320
let mints = Filter :: new ( ) . kind ( Kind :: from ( 38173 ) ) ;
1285
1321
// filter for finding federation recommendations from trusted people
1286
1322
let trusted_recommendations = Filter :: new ( )
1287
- . kind ( Kind :: from ( 18173 ) )
1323
+ . kind ( Kind :: from ( 38000 ) )
1324
+ . custom_tag ( SingleLetterTag :: lowercase ( Alphabet :: K ) , [ "38173" ] )
1288
1325
. authors ( npubs. keys ( ) . copied ( ) ) ;
1289
1326
// filter for finding federation recommendations from random people
1290
1327
let recommendations = Filter :: new ( )
1291
- . kind ( Kind :: from ( 18173 ) )
1328
+ . kind ( Kind :: from ( 38000 ) )
1329
+ . custom_tag ( SingleLetterTag :: lowercase ( Alphabet :: K ) , [ "38173" ] )
1292
1330
. limit ( NUM_TRUSTED_USERS as usize ) ;
1293
1331
// fetch events
1294
1332
let events = self
@@ -1353,10 +1391,16 @@ impl<S: MutinyStorage> NostrManager<S> {
1353
1391
// add on contact recommendations to mints
1354
1392
for event in events {
1355
1393
// only process federation recommendations
1356
- if event. kind != Kind :: from ( 18173 ) {
1394
+ if event. kind != Kind :: from ( 38000 )
1395
+ && event. tags . iter ( ) . any ( |tag| {
1396
+ tag. kind ( ) == TagKind :: Custom ( "k" . to_string ( ) )
1397
+ && tag. as_vec ( ) . get ( 1 ) . is_some_and ( |x| x == "38173" )
1398
+ } )
1399
+ {
1357
1400
continue ;
1358
1401
}
1359
1402
1403
+ // if we don't have the contact, skip
1360
1404
let contact = match npubs. get ( & event. pubkey ) {
1361
1405
Some ( contact) => contact. clone ( ) ,
1362
1406
None => continue ,
@@ -1366,12 +1410,9 @@ impl<S: MutinyStorage> NostrManager<S> {
1366
1410
. tags
1367
1411
. iter ( )
1368
1412
. filter_map ( |tag| {
1413
+ // try to parse the invite code
1369
1414
let vec = tag. as_vec ( ) ;
1370
- // if there's 3 elements, make sure the identifier is for a fedimint
1371
- // if there's 2 elements, just try to parse the invite code
1372
- if ( vec. len ( ) == 3 && vec[ 0 ] == "u" && vec[ 2 ] == "fedimint" )
1373
- || ( vec. len ( ) == 2 && vec[ 0 ] == "u" )
1374
- {
1415
+ if vec. len ( ) == 2 && vec[ 0 ] == "u" {
1375
1416
InviteCode :: from_str ( & vec[ 1 ] ) . ok ( )
1376
1417
} else {
1377
1418
None
0 commit comments