feat: assign users to task

This commit is contained in:
xeruf 2024-11-25 02:15:18 +01:00
parent 87392fccb6
commit 6ef5c47e98
3 changed files with 48 additions and 27 deletions

View File

@ -1,7 +1,8 @@
use crate::task::MARKER_PARENT;
use crate::tasks::nostr_users::NostrUsers;
use crate::tasks::HIGH_PRIO;
use itertools::Itertools;
use nostr_sdk::{EventBuilder, EventId, Kind, Tag, TagKind, TagStandard};
use nostr_sdk::{EventBuilder, EventId, Kind, PublicKey, Tag, TagKind, TagStandard};
use std::borrow::Cow;
pub const TASK_KIND: Kind = Kind::GitIssue;
@ -99,22 +100,27 @@ pub(crate) fn extract_hashtags(input: &str) -> impl Iterator<Item=Tag> + '_ {
/// as well as various embedded tags.
///
/// Expects sanitized input.
pub(crate) fn extract_tags(input: &str) -> (String, Vec<Tag>) {
pub(crate) fn extract_tags(input: &str, users: &NostrUsers) -> (String, Vec<Tag>) {
let words = input.split_ascii_whitespace();
let mut prio = None;
let mut tags = Vec::with_capacity(4);
let result = words.filter(|s| {
if s.starts_with('*') {
if s.len() == 1 {
prio = Some(HIGH_PRIO);
if s.starts_with('@') {
if let Ok(key) = PublicKey::parse(&s[1..]) {
tags.push(Tag::public_key(key));
return false;
} else if let Some((key, _)) = users.find_user(&s[1..]) {
tags.push(Tag::public_key(*key));
return false;
}
return match s[1..].parse::<Prio>() {
Ok(num) => {
prio = Some(num * (if s.len() > 2 { 1 } else { 10 }));
false
}
_ => true,
};
} else if s.starts_with('*') {
if s.len() == 1 {
tags.push(to_prio_tag(HIGH_PRIO));
return false;
}
if let Ok(num) = s[1..].parse::<Prio>() {
tags.push(to_prio_tag(num * (if s.len() > 2 { 1 } else { 10 })));
return false
}
}
true
}).collect_vec();
@ -122,7 +128,7 @@ pub(crate) fn extract_tags(input: &str) -> (String, Vec<Tag>) {
let main = split.next().unwrap().join(" ");
let mut tags = extract_hashtags(&main)
.chain(split.flatten().map(|s| to_hashtag_tag(&s)))
.chain(prio.map(|p| to_prio_tag(p)))
.chain(tags)
.collect_vec();
tags.sort();
tags.dedup();
@ -161,10 +167,11 @@ pub fn to_prio_tag(value: Prio) -> Tag {
#[test]
fn test_extract_tags() {
assert_eq!(extract_tags("Hello from #mars with #greetings #yeah *4 # # yeah done-it"),
assert_eq!(extract_tags("Hello from #mars with #greetings #yeah *4 # # yeah done-it", &Default::default()),
("Hello from #mars with #greetings #yeah".to_string(),
std::iter::once(Tag::custom(TagKind::Custom(Cow::from(PRIO)), [40.to_string()]))
.chain(["done-it", "greetings", "mars", "yeah"].into_iter().map(to_hashtag_tag)).collect()));
assert_eq!(extract_tags("So tagless #"),
("So tagless".to_string(), vec![]));
.chain(["done-it", "greetings", "mars", "yeah"].into_iter().map(to_hashtag_tag))
.collect()));
assert_eq!(extract_tags("So tagless @hewo #", &Default::default()),
("So tagless @hewo".to_string(), vec![]));
}

View File

@ -1,4 +1,4 @@
mod nostr_users;
pub(crate) mod nostr_users;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque};
use std::fmt::{Display, Formatter};
@ -7,18 +7,18 @@ use std::ops::{Div, Rem};
use std::str::FromStr;
use std::time::Duration;
use crate::hashtag::Hashtag;
use crate::event_sender::{EventSender, MostrMessage};
use crate::hashtag::Hashtag;
use crate::helpers::{format_timestamp_local, format_timestamp_relative, format_timestamp_relative_to, parse_tracking_stamp, some_non_empty, to_string_or_default, CHARACTER_THRESHOLD};
use crate::kinds::*;
use crate::task::{State, Task, TaskState, MARKER_DEPENDS, MARKER_PARENT, MARKER_PROPERTY};
use crate::tasks::nostr_users::NostrUsers;
use colored::Colorize;
use itertools::Itertools;
use log::{debug, error, info, trace, warn};
use nostr_sdk::{Alphabet, Event, EventBuilder, EventId, JsonUtil, Keys, Kind, Metadata, PublicKey, SingleLetterTag, Tag, TagKind, Timestamp, Url};
use regex::bytes::Regex;
use tokio::sync::mpsc::Sender;
use crate::tasks::nostr_users::NostrUsers;
const DEFAULT_PRIO: Prio = 25;
const QUICK_PRIO: Prio = 35;
@ -1058,7 +1058,7 @@ impl TasksRelay {
///
/// Sanitizes input
pub(crate) fn make_task_with(&mut self, input: &str, tags: impl IntoIterator<Item=Tag>, set_state: bool) -> EventId {
let (input, input_tags) = extract_tags(input.trim());
let (input, input_tags) = extract_tags(input.trim(), &self.users);
let prio =
if input_tags.iter().any(|t| t.kind().to_string() == PRIO) { None } else { self.priority.map(|p| to_prio_tag(p)) };
info!("Created task \"{input}\" with tags [{}]", join_tags(&input_tags));
@ -1282,7 +1282,7 @@ impl TasksRelay {
} else {
vec![id]
};
let (desc, tags) = extract_tags(comment);
let (desc, tags) = extract_tags(comment, &self.users);
let prop =
EventBuilder::new(state.into(), desc)
.tags(ids.into_iter()
@ -1310,7 +1310,7 @@ impl TasksRelay {
/// Creates a note or activity, depending on whether the parent is a task.
/// Sanitizes Input.
pub(crate) fn make_note(&mut self, note: &str) -> EventId {
let (name, tags) = extract_tags(note.trim());
let (name, tags) = extract_tags(note.trim(), &self.users);
let format = format!("\"{name}\" with tags [{}]", join_tags(&tags));
let mut prop =
EventBuilder::new(Kind::TextNote, name)

View File

@ -1,6 +1,6 @@
use std::collections::HashMap;
use std::str::FromStr;
use nostr_sdk::{Metadata, PublicKey};
use nostr_sdk::{Keys, Metadata, PublicKey, Tag};
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct NostrUsers {
@ -15,13 +15,18 @@ impl NostrUsers {
// Find username or key starting with the given term.
pub(crate) fn find_user(&self, term: &str) -> Option<(&PublicKey, &Metadata)> {
let lowered = term.trim().to_ascii_lowercase();
let term = lowered.as_str();
if term.is_empty() {
return None
}
if let Ok(key) = PublicKey::from_str(term) {
return self.users.get_key_value(&key);
}
self.users.iter().find(|(k, v)|
// TODO regex word boundary
v.name.as_ref().is_some_and(|n| n.starts_with(term)) ||
v.display_name.as_ref().is_some_and(|n| n.starts_with(term)) ||
v.name.as_ref().is_some_and(|n| n.to_ascii_lowercase().starts_with(term)) ||
v.display_name.as_ref().is_some_and(|n| n.to_ascii_lowercase().starts_with(term)) ||
(term.len() > 4 && k.to_string().starts_with(term)))
}
@ -46,4 +51,13 @@ impl NostrUsers {
self.users.insert(pubkey, Default::default());
}
}
}
#[test]
fn test_user_extract() {
let keys = Keys::generate();
let mut users = NostrUsers::default();
users.insert(keys.public_key, Metadata::new().display_name("Tester Jo"));
assert_eq!(crate::kinds::extract_tags("Hello @test", &users),
("Hello".to_string(), vec![Tag::public_key(keys.public_key)]));
}