Compare commits
No commits in common. "15a2ffd7e6e1269f7c830b2a9df2824dd317a578" and "ed72bcebcfe7c46f9517d674d9e99613d98b4ed2" have entirely different histories.
15a2ffd7e6
...
ed72bcebcf
4 changed files with 93 additions and 126 deletions
|
@ -1,107 +0,0 @@
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::ops::Sub;
|
|
||||||
|
|
||||||
use nostr_sdk::prelude::*;
|
|
||||||
use tokio::sync::mpsc::Sender;
|
|
||||||
|
|
||||||
use crate::kinds::TRACKING_KIND;
|
|
||||||
use crate::tasks;
|
|
||||||
use log::{debug, error, info, trace, warn};
|
|
||||||
use nostr_sdk::Event;
|
|
||||||
|
|
||||||
const UNDO_DELAY: u64 = 60;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
||||||
pub(crate) enum MostrMessage {
|
|
||||||
Flush,
|
|
||||||
NewRelay(Url),
|
|
||||||
AddTasks(Url, Vec<Event>),
|
|
||||||
}
|
|
||||||
|
|
||||||
type Events = Vec<Event>;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) struct EventSender {
|
|
||||||
pub(crate) url: Option<Url>,
|
|
||||||
pub(crate) tx: Sender<MostrMessage>,
|
|
||||||
pub(crate) keys: Keys,
|
|
||||||
pub(crate) queue: RefCell<Events>,
|
|
||||||
}
|
|
||||||
impl EventSender {
|
|
||||||
pub(crate) fn from(url: Option<Url>, tx: &Sender<MostrMessage>, keys: &Keys) -> Self {
|
|
||||||
EventSender {
|
|
||||||
url,
|
|
||||||
tx: tx.clone(),
|
|
||||||
keys: keys.clone(),
|
|
||||||
queue: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO this direly needs testing
|
|
||||||
pub(crate) fn submit(&self, event_builder: EventBuilder) -> Result<Event> {
|
|
||||||
let min = Timestamp::now().sub(UNDO_DELAY);
|
|
||||||
{
|
|
||||||
// Always flush if oldest event older than a minute or newer than now
|
|
||||||
let borrow = self.queue.borrow();
|
|
||||||
if borrow
|
|
||||||
.iter()
|
|
||||||
.any(|e| e.created_at < min || e.created_at > Timestamp::now())
|
|
||||||
{
|
|
||||||
drop(borrow);
|
|
||||||
debug!("Flushing event queue because it is older than a minute");
|
|
||||||
self.force_flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut queue = self.queue.borrow_mut();
|
|
||||||
Ok(event_builder.to_event(&self.keys).inspect(|event| {
|
|
||||||
if event.kind == TRACKING_KIND
|
|
||||||
&& event.created_at > min
|
|
||||||
&& event.created_at < tasks::now()
|
|
||||||
{
|
|
||||||
// Do not send redundant movements
|
|
||||||
queue.retain(|e| e.kind != TRACKING_KIND);
|
|
||||||
}
|
|
||||||
queue.push(event.clone());
|
|
||||||
})?)
|
|
||||||
}
|
|
||||||
/// Sends all pending events
|
|
||||||
fn force_flush(&self) {
|
|
||||||
debug!("Flushing {} events from queue", self.queue.borrow().len());
|
|
||||||
let values = self.clear();
|
|
||||||
self.url.as_ref().map(|url| {
|
|
||||||
self.tx
|
|
||||||
.try_send(MostrMessage::AddTasks(url.clone(), values))
|
|
||||||
.err()
|
|
||||||
.map(|e| {
|
|
||||||
error!(
|
|
||||||
"Nostr communication thread failure, changes will not be persisted: {}",
|
|
||||||
e
|
|
||||||
)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/// Sends all pending events if there is a non-tracking event
|
|
||||||
pub(crate) fn flush(&self) {
|
|
||||||
if self
|
|
||||||
.queue
|
|
||||||
.borrow()
|
|
||||||
.iter()
|
|
||||||
.any(|event| event.kind != TRACKING_KIND)
|
|
||||||
{
|
|
||||||
self.force_flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub(crate) fn clear(&self) -> Events {
|
|
||||||
trace!("Cleared queue: {:?}", self.queue.borrow());
|
|
||||||
self.queue.replace(Vec::with_capacity(3))
|
|
||||||
}
|
|
||||||
pub(crate) fn pubkey(&self) -> PublicKey {
|
|
||||||
self.keys.public_key()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Drop for EventSender {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.force_flush();
|
|
||||||
debug!("Dropped {:?}", self);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::task::MARKER_PARENT;
|
use crate::task::{State, MARKER_PARENT};
|
||||||
use crate::tasks::HIGH_PRIO;
|
use crate::tasks::HIGH_PRIO;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
104
src/main.rs
104
src/main.rs
|
@ -1,17 +1,14 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
use std::env::{args, var};
|
use std::env::{args, var};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufRead, BufReader, Write};
|
use std::io::{BufRead, BufReader, Write};
|
||||||
|
use std::ops::Sub;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::event_sender::MostrMessage;
|
|
||||||
use crate::helpers::*;
|
|
||||||
use crate::kinds::{Prio, BASIC_KINDS, PROPERTY_COLUMNS, PROP_KINDS};
|
|
||||||
use crate::task::{State, Task, TaskState};
|
|
||||||
use crate::tasks::{PropertyCollection, StateFilter, TasksRelay};
|
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use directories::ProjectDirs;
|
use directories::ProjectDirs;
|
||||||
|
@ -25,15 +22,21 @@ use rustyline::config::Configurer;
|
||||||
use rustyline::error::ReadlineError;
|
use rustyline::error::ReadlineError;
|
||||||
use rustyline::DefaultEditor;
|
use rustyline::DefaultEditor;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
use tokio::sync::mpsc::Sender;
|
||||||
use tokio::time::error::Elapsed;
|
use tokio::time::error::Elapsed;
|
||||||
use tokio::time::timeout;
|
use tokio::time::timeout;
|
||||||
|
|
||||||
|
use crate::helpers::*;
|
||||||
|
use crate::kinds::{Prio, BASIC_KINDS, PROPERTY_COLUMNS, PROP_KINDS, TRACKING_KIND};
|
||||||
|
use crate::task::{State, Task, TaskState};
|
||||||
|
use crate::tasks::{PropertyCollection, StateFilter, TasksRelay};
|
||||||
|
|
||||||
mod helpers;
|
mod helpers;
|
||||||
mod task;
|
mod task;
|
||||||
mod tasks;
|
mod tasks;
|
||||||
mod kinds;
|
mod kinds;
|
||||||
mod event_sender;
|
|
||||||
|
|
||||||
|
const UNDO_DELAY: u64 = 60;
|
||||||
const INACTVITY_DELAY: u64 = 200;
|
const INACTVITY_DELAY: u64 = 200;
|
||||||
const LOCAL_RELAY_NAME: &str = "TEMP";
|
const LOCAL_RELAY_NAME: &str = "TEMP";
|
||||||
|
|
||||||
|
@ -59,6 +62,84 @@ macro_rules! or_warn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Events = Vec<Event>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct EventSender {
|
||||||
|
url: Option<Url>,
|
||||||
|
tx: Sender<MostrMessage>,
|
||||||
|
keys: Keys,
|
||||||
|
queue: RefCell<Events>,
|
||||||
|
}
|
||||||
|
impl EventSender {
|
||||||
|
fn from(url: Option<Url>, tx: &Sender<MostrMessage>, keys: &Keys) -> Self {
|
||||||
|
EventSender {
|
||||||
|
url,
|
||||||
|
tx: tx.clone(),
|
||||||
|
keys: keys.clone(),
|
||||||
|
queue: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO this direly needs testing
|
||||||
|
fn submit(&self, event_builder: EventBuilder) -> Result<Event> {
|
||||||
|
let min = Timestamp::now().sub(UNDO_DELAY);
|
||||||
|
{
|
||||||
|
// Always flush if oldest event older than a minute or newer than now
|
||||||
|
let borrow = self.queue.borrow();
|
||||||
|
if borrow.iter().any(|e| e.created_at < min || e.created_at > Timestamp::now()) {
|
||||||
|
drop(borrow);
|
||||||
|
debug!("Flushing event queue because it is older than a minute");
|
||||||
|
self.force_flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut queue = self.queue.borrow_mut();
|
||||||
|
Ok(event_builder.to_event(&self.keys).inspect(|event| {
|
||||||
|
if event.kind == TRACKING_KIND && event.created_at > min && event.created_at < tasks::now() {
|
||||||
|
// Do not send redundant movements
|
||||||
|
queue.retain(|e| e.kind != TRACKING_KIND);
|
||||||
|
}
|
||||||
|
queue.push(event.clone());
|
||||||
|
})?)
|
||||||
|
}
|
||||||
|
/// Sends all pending events
|
||||||
|
fn force_flush(&self) {
|
||||||
|
debug!("Flushing {} events from queue", self.queue.borrow().len());
|
||||||
|
let values = self.clear();
|
||||||
|
self.url.as_ref().map(|url| {
|
||||||
|
self.tx.try_send(MostrMessage::AddTasks(url.clone(), values)).err().map(|e| {
|
||||||
|
error!("Nostr communication thread failure, changes will not be persisted: {}", e)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/// Sends all pending events if there is a non-tracking event
|
||||||
|
fn flush(&self) {
|
||||||
|
if self.queue.borrow().iter().any(|event| event.kind != TRACKING_KIND) {
|
||||||
|
self.force_flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn clear(&self) -> Events {
|
||||||
|
trace!("Cleared queue: {:?}", self.queue.borrow());
|
||||||
|
self.queue.replace(Vec::with_capacity(3))
|
||||||
|
}
|
||||||
|
pub(crate) fn pubkey(&self) -> PublicKey {
|
||||||
|
self.keys.public_key()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Drop for EventSender {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.force_flush();
|
||||||
|
debug!("Dropped {:?}", self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
pub(crate) enum MostrMessage {
|
||||||
|
Flush,
|
||||||
|
NewRelay(Url),
|
||||||
|
AddTasks(Url, Vec<Event>),
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
let mut rl = DefaultEditor::new()?;
|
let mut rl = DefaultEditor::new()?;
|
||||||
|
@ -89,15 +170,8 @@ async fn main() -> Result<()> {
|
||||||
|
|
||||||
let config_dir =
|
let config_dir =
|
||||||
ProjectDirs::from("", "", "mostr")
|
ProjectDirs::from("", "", "mostr")
|
||||||
.map(|p| {
|
.map(|p| p.config_dir().to_path_buf())
|
||||||
let config = p.config_dir();
|
.unwrap_or(PathBuf::new());
|
||||||
or_warn!(fs::create_dir_all(config), "Could not create config directory");
|
|
||||||
config.to_path_buf()
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
warn!("Could not determine config directory, using current directory");
|
|
||||||
PathBuf::new()
|
|
||||||
});
|
|
||||||
let keysfile = config_dir.join("key");
|
let keysfile = config_dir.join("key");
|
||||||
let relayfile = config_dir.join("relays");
|
let relayfile = config_dir.join("relays");
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque};
|
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque};
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::io::Write;
|
use std::io::{stdout, Error, Write};
|
||||||
use std::iter::{empty, once, FusedIterator};
|
use std::iter::{empty, once, FusedIterator};
|
||||||
use std::ops::{Div, Rem};
|
use std::ops::{Div, Rem};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::event_sender::{EventSender, MostrMessage};
|
|
||||||
use crate::helpers::{format_timestamp_local, format_timestamp_relative, format_timestamp_relative_to, parse_tracking_stamp, some_non_empty, CHARACTER_THRESHOLD};
|
use crate::helpers::{format_timestamp_local, format_timestamp_relative, format_timestamp_relative_to, parse_tracking_stamp, some_non_empty, CHARACTER_THRESHOLD};
|
||||||
use crate::kinds::*;
|
use crate::kinds::*;
|
||||||
use crate::task::{State, Task, TaskState, MARKER_DEPENDS, MARKER_PARENT, MARKER_PROPERTY};
|
use crate::task::{State, Task, TaskState, MARKER_DEPENDS, MARKER_PARENT, MARKER_PROPERTY};
|
||||||
|
use crate::{EventSender, MostrMessage};
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use itertools::Itertools;
|
use itertools::{Either, Itertools};
|
||||||
use log::{debug, error, info, trace, warn};
|
use log::{debug, error, info, trace, warn};
|
||||||
use nostr_sdk::prelude::Marker;
|
use nostr_sdk::prelude::Marker;
|
||||||
use nostr_sdk::{Event, EventBuilder, EventId, JsonUtil, Keys, Kind, Metadata, PublicKey, Tag, TagStandard, Timestamp, UncheckedUrl, Url};
|
use nostr_sdk::{Event, EventBuilder, EventId, JsonUtil, Keys, Kind, Metadata, PublicKey, Tag, TagStandard, Timestamp, UncheckedUrl, Url};
|
||||||
|
|
Loading…
Add table
Reference in a new issue