···39394040- `/markov`: For a reply from Bingus in the current channel
4141- `/weights`: View the weights for a specific token
4242+- *`/forget`: Clear a word from memory, this gets rid of any instance of the token
4243- *`/dump_chain`: Dump Bingus' "brain", his entire database of known words and relations
4344- *`/load_chain`: Additively load a brain file into Bingus
4445
+43-1
src/brain.rs
···6565 None
6666 }
67676868+ pub fn forget(&mut self, token: &Token) {
6969+ if let Some(w) = self.0.remove(token) {
7070+ self.1 -= w as u64;
7171+ }
7272+ }
7373+6874 pub fn iter_weights(&self) -> impl Iterator<Item = (&Token, Weight, f64)> {
6975 self.0
7076 .iter()
···137143 .unwrap_or_default()
138144 }
139145146146+ pub fn forget(&mut self, word: &str) {
147147+ let tok = Self::normalize_token(word);
148148+149149+ self.0.remove(&tok);
150150+151151+ for edge in self.0.values_mut() {
152152+ edge.forget(&tok);
153153+ }
154154+ }
155155+140156 pub fn merge_from(&mut self, other: Self) {
141157 for (k, v) in other.0.into_iter() {
142158 if let Some(edges) = self.0.get_mut(&k) {
···226242 }
227243228244 pub fn get_weights(&self, tok: &str) -> Option<&Edges> {
229229- self.0.get(&Self::normalize_token(tok))
245245+ self.0
246246+ .get(&Self::normalize_token(tok))
247247+ .filter(|e| !e.0.is_empty())
230248 }
231249232250 fn legacy_token_format(tok: &Token) -> String {
···336354 let reply = brain.respond("hello", false, false, None);
337355 assert_eq!(reply, Some("world".to_string()));
338356 }
357357+ }
358358+359359+ #[test]
360360+ fn forget_word() {
361361+ let mut brain = Brain::default();
362362+363363+ brain.ingest("hello world");
364364+ brain.ingest("hello evil world");
365365+366366+ brain.forget("evil");
367367+368368+ assert!(
369369+ !brain.0.contains_key(&Some(String::from("evil"))),
370370+ "Edges still exist for evil"
371371+ );
372372+ let edges = brain
373373+ .0
374374+ .get(&Some(String::from("hello")))
375375+ .expect("No weights for hello");
376376+ assert!(
377377+ !edges.0.contains_key(&Some(String::from("evil"))),
378378+ "Edges for hello still has evil"
379379+ );
380380+ assert_eq!(edges.1, 1);
339381 }
340382341383 #[test]
+45
src/cmd/forget.rs
···11+use std::sync::Arc;
22+33+use twilight_interactions::command::{CommandModel, CreateCommand};
44+use twilight_model::application::interaction::{Interaction, application_command::CommandData};
55+66+use crate::{BotContext, cmd::DEFER_INTER_RESP_EPHEMERAL, prelude::*, require_owner};
77+88+#[derive(CommandModel, CreateCommand)]
99+#[command(
1010+ name = "forget",
1111+ desc = "Erase a word from all edges. THIS ACTION IS IRREVERSIBLE!"
1212+)]
1313+pub struct ForgetCommand {
1414+ /// The token to forget
1515+ token: String,
1616+}
1717+1818+impl ForgetCommand {
1919+ pub async fn handle(inter: Interaction, data: CommandData, ctx: Arc<BotContext>) -> Result {
2020+ let client = ctx.http.interaction(ctx.app_id);
2121+2222+ require_owner!(inter, ctx, client);
2323+2424+ let Self { token } =
2525+ Self::from_interaction(data.into()).context("Failed to parse command data")?;
2626+2727+ client
2828+ .create_response(inter.id, &inter.token, &DEFER_INTER_RESP_EPHEMERAL)
2929+ .await
3030+ .context("Failed to defer")?;
3131+3232+ {
3333+ let mut brain = ctx.brain_handle.write().await;
3434+ brain.forget(token.as_str());
3535+ }
3636+3737+ client
3838+ .update_response(&inter.token)
3939+ .content(Some("Token forgotten"))
4040+ .await
4141+ .context("Failed to send brain")?;
4242+4343+ Ok(())
4444+ }
4545+}