Skip to main content

okbot_core/
memory.rs

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