add invite
This commit is contained in:
Generated
+1
@@ -1291,6 +1291,7 @@ dependencies = [
|
||||
"log",
|
||||
"nostr-memory",
|
||||
"nostr-sdk",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
"smol",
|
||||
|
||||
@@ -10,6 +10,7 @@ state = { path = "../state" }
|
||||
|
||||
gpui.workspace = true
|
||||
nostr-sdk.workspace = true
|
||||
regex = "1"
|
||||
|
||||
anyhow.workspace = true
|
||||
data-encoding.workspace = true
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use concord::cord05;
|
||||
use nostr_sdk::prelude::*;
|
||||
use regex::Regex;
|
||||
|
||||
pub const KIND_COMMUNITY_ANNOUNCEMENT: u16 = 3314;
|
||||
|
||||
static INVITE_URL: LazyLock<Regex> = LazyLock::new(|| {
|
||||
Regex::new(
|
||||
r"(?i)(?:https?://\S+?/invite/naddr1[0-9a-z]+#[A-Za-z0-9_-]+)|(?:naddr1[0-9a-z]+#[A-Za-z0-9_-]+)",
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Invite {
|
||||
pub invite_url: String,
|
||||
pub link_signer: PublicKey,
|
||||
pub source: Event,
|
||||
}
|
||||
|
||||
impl Invite {
|
||||
pub fn from_event(event: &Event) -> Option<Invite> {
|
||||
let invite_url = extract_invite_urls(&event.content).into_iter().next()?;
|
||||
let link_signer = cord05::parse_link(&invite_url).ok()?.link_signer;
|
||||
|
||||
Some(Invite {
|
||||
invite_url,
|
||||
link_signer,
|
||||
source: event.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_invite_urls(text: &str) -> Vec<String> {
|
||||
let mut urls = Vec::new();
|
||||
let mut seen = BTreeSet::new();
|
||||
|
||||
for found in INVITE_URL.find_iter(text) {
|
||||
let candidate = found.as_str();
|
||||
|
||||
let Ok(link) = cord05::parse_link(candidate) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if seen.insert(link.link_signer) {
|
||||
urls.push(candidate.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
urls
|
||||
}
|
||||
|
||||
pub fn build_community_announcement(invite_url: &str) -> Option<EventBuilder> {
|
||||
if cord05::parse_link(invite_url).is_err() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(EventBuilder::new(
|
||||
Kind::Custom(KIND_COMMUNITY_ANNOUNCEMENT),
|
||||
invite_url.trim(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn build_announcement_deletion(ids: &[EventId]) -> EventBuilder {
|
||||
let mut tags: Vec<Tag> = ids.iter().map(|id| Tag::event(*id)).collect();
|
||||
tags.push(Tag::custom("k", [KIND_COMMUNITY_ANNOUNCEMENT.to_string()]));
|
||||
|
||||
EventBuilder::new(Kind::EventDeletion, "").tags(tags)
|
||||
}
|
||||
|
||||
pub fn fold_announcements(announcements: &[Event], deletes: &[Event]) -> Vec<Invite> {
|
||||
let authors: BTreeMap<EventId, PublicKey> = announcements
|
||||
.iter()
|
||||
.filter(|event| event.kind == Kind::Custom(KIND_COMMUNITY_ANNOUNCEMENT))
|
||||
.map(|event| (event.id, event.pubkey))
|
||||
.collect();
|
||||
|
||||
let deleted = deleted_ids(&authors, deletes);
|
||||
|
||||
let mut ordered: Vec<&Event> = announcements.iter().collect();
|
||||
ordered.sort_by_key(|event| std::cmp::Reverse(event.created_at));
|
||||
|
||||
let mut seen = BTreeSet::new();
|
||||
let mut folded = Vec::new();
|
||||
|
||||
for event in ordered {
|
||||
if deleted.contains(&event.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(invite) = Invite::from_event(event) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if seen.insert(invite.link_signer) {
|
||||
folded.push(invite);
|
||||
}
|
||||
}
|
||||
|
||||
folded
|
||||
}
|
||||
|
||||
fn deleted_ids(authors: &BTreeMap<EventId, PublicKey>, deletes: &[Event]) -> BTreeSet<EventId> {
|
||||
let mut deleted = BTreeSet::new();
|
||||
|
||||
for event in deletes
|
||||
.iter()
|
||||
.filter(|event| event.kind == Kind::EventDeletion)
|
||||
{
|
||||
for tag in event.tags.iter() {
|
||||
let fields = tag.as_slice();
|
||||
|
||||
if fields.len() < 2 || fields[0] != "e" {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Ok(id) = EventId::from_hex(&fields[1]) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if authors.get(&id) == Some(&event.pubkey) {
|
||||
deleted.insert(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deleted
|
||||
}
|
||||
@@ -17,6 +17,7 @@ use crate::rekey::WatchRegistry;
|
||||
|
||||
pub mod cache;
|
||||
mod community;
|
||||
pub mod discover;
|
||||
pub mod history;
|
||||
mod rekey;
|
||||
mod sync;
|
||||
|
||||
Reference in New Issue
Block a user