1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//! The memory port: notes the bot keeps about people, rooms and the team (design §5.4).
use serde::{Deserialize, Serialize};
use crate::knowledge::Requester;
use crate::{BoxError, async_trait};
/// Who a note is for.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum MemoryScope {
/// The requester only.
User,
/// The current room (chat) only.
Room,
/// Everyone who talks to the bot.
Global,
}
/// A note to save.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MemoryNote {
/// Who it is for.
pub scope: MemoryScope,
/// Short title; derived from the text when `None`.
pub title: Option<String>,
/// The note.
pub text: String,
}
/// A stored note.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MemoryEntry {
/// Id in the knowledge bundle (`memory/users/zalo-shop/u1/coffee`).
pub id: String,
/// Who it is for.
pub scope: MemoryScope,
/// Title.
pub title: String,
/// The note.
pub text: String,
/// `platform:instance/user` of the author.
pub author: Option<String>,
/// Last update (`YYYY-MM-DD`).
pub updated: String,
}
/// A memory operation failed.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum MemoryError {
/// No such note in the requester's scope.
#[error("no note `{0}` is visible here")]
NotFound(String),
/// The note exists but the requester may not change it.
#[error("not allowed: {0}")]
Forbidden(String),
/// The request is not acceptable (empty, too long).
#[error("{0}")]
Invalid(String),
/// Storage failed.
#[error("memory storage: {0}")]
Backend(#[source] BoxError),
}
/// Saves, lists and forgets notes, always within the requester's visibility.
#[async_trait]
pub trait MemoryStore: Send + Sync {
/// Saves a note.
async fn save(&self, who: &Requester, note: MemoryNote) -> Result<MemoryEntry, MemoryError>;
/// Notes visible to `who`: their own, their room's and the team's.
async fn list(&self, who: &Requester) -> Result<Vec<MemoryEntry>, MemoryError>;
/// Removes a note by id. Personal and room notes can be removed by anyone who sees them;
/// team notes by their author (operators use `okbot memory forget`).
async fn forget(&self, who: &Requester, id: &str) -> Result<MemoryEntry, MemoryError>;
/// Appends a quick note to today's journal of `who` (`User`) or of the room (`Room`) and
/// returns the journal's id (design §5.9). Stores without a journal refuse.
async fn note(
&self,
who: &Requester,
scope: MemoryScope,
text: &str,
) -> Result<String, MemoryError> {
let _ = (who, scope, text);
Err(MemoryError::Invalid("this bot keeps no journal".into()))
}
}