feat: assign users to task
This commit is contained in:
parent
87392fccb6
commit
6ef5c47e98
43
src/kinds.rs
43
src/kinds.rs
|
@ -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![]));
|
||||
}
|
12
src/tasks.rs
12
src/tasks.rs
|
@ -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)
|
||||
|
|
|
@ -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)]));
|
||||
}
|
Loading…
Reference in New Issue