Add context to commands

Added some additional basic commands
This commit is contained in:
2025-12-17 11:08:03 -06:00
parent 34684e49f6
commit b36c7d32a2
4 changed files with 120 additions and 23 deletions
+6 -8
View File
@@ -1,19 +1,17 @@
use std::env;
use libmagbot::config::MagbotConfig;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::init();
let (Some(homeserver_url), Some(username), Some(password)) =
(env::args().nth(1), env::args().nth(2), env::args().nth(3))
else {
eprintln!(
"Usage: {} <homeserver_url> <username> <password>",
env::args().next().unwrap()
);
let config = MagbotConfig::from_env()?;
let Ok(homeserver_fqdn) = env::var("HOMESERVER_URL") else {
eprintln!("Set the homeserver fqdn as the `HOMESERVER_URL` environment variable");
std::process::exit(1)
};
libmagbot::login_and_sync(homeserver_url, username, password).await?;
libmagbot::login_and_sync(homeserver_fqdn, config.username, config.password).await?;
Ok(())
}
+50 -1
View File
@@ -1,14 +1,53 @@
use matrix_sdk::{
Room,
event_handler::Ctx,
ruma::events::room::message::{OriginalSyncRoomMessageEvent, RoomMessageEventContent},
};
use crate::MagbotContext;
/// Sets up the bot to listen for title suggestions
pub async fn start(
event: &OriginalSyncRoomMessageEvent,
room: &Room,
ctx: Ctx<MagbotContext>,
) -> anyhow::Result<()> {
// set to listening
// send message that magbot is listening
todo!()
}
/// Stops the bot from listening for title suggestions
pub async fn stop(
event: &OriginalSyncRoomMessageEvent,
room: &Room,
ctx: Ctx<MagbotContext>,
) -> anyhow::Result<()> {
// clear out saved titles
// set state to sleeping
// send message that magbot is no longer listening
todo!()
}
/// Shows available commands
pub async fn help(
event: &OriginalSyncRoomMessageEvent,
room: &Room,
ctx: Ctx<MagbotContext>,
) -> anyhow::Result<()> {
// send message with available commands
todo!()
}
/// Records title suggestions
pub async fn suggest(
event: &OriginalSyncRoomMessageEvent,
room: &Room,
text: &str,
ctx: Ctx<MagbotContext>,
) -> anyhow::Result<()> {
// save the title into memory with author
// react to the original message to show it was saved
todo!()
}
@@ -17,12 +56,21 @@ pub async fn get_titles(
event: &OriginalSyncRoomMessageEvent,
room: &Room,
text: &str,
ctx: Ctx<MagbotContext>,
) -> anyhow::Result<()> {
// sends all the titles with an initial reaction to be used for voting
// save the message ids into context for count_votes()?
todo!()
}
/// Counts the votes of title suggestions
pub async fn count_votes(event: &OriginalSyncRoomMessageEvent, room: &Room) -> anyhow::Result<()> {
pub async fn count_votes(
event: &OriginalSyncRoomMessageEvent,
room: &Room,
ctx: Ctx<MagbotContext>,
) -> anyhow::Result<()> {
// count the reactions of the title messages
// send message with winning title
todo!()
}
@@ -31,6 +79,7 @@ pub async fn not_implemented(
event: &OriginalSyncRoomMessageEvent,
room: &Room,
text: &str,
ctx: Ctx<MagbotContext>,
) -> anyhow::Result<()> {
// get '!' to the next '<space>' char and output as a command
let content = RoomMessageEventContent::text_plain("The command `{cmd}` is not implemented");
+5 -5
View File
@@ -4,7 +4,7 @@ use serde::Deserialize;
/// The status of the bot
#[derive(Clone, Debug, Deserialize)]
enum MagbotStatus {
pub enum MagbotStatus {
/// Bot is waiting for title suggestions
Running,
@@ -19,10 +19,10 @@ impl Default for MagbotStatus {
#[derive(Clone, Debug, Deserialize)]
pub struct MagbotConfig {
username: String,
password: String,
room_id: String,
status: MagbotStatus,
pub username: String,
pub password: String,
pub room_id: String,
pub status: MagbotStatus,
}
impl MagbotConfig {
pub fn from_env() -> anyhow::Result<Self> {
+59 -9
View File
@@ -1,13 +1,41 @@
use matrix_sdk::{
Client, Room, RoomState,
config::SyncSettings,
event_handler::Ctx,
ruma::events::room::message::{MessageType, OriginalSyncRoomMessageEvent},
};
mod commands;
mod config;
use crate::config::MagbotStatus;
async fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) -> anyhow::Result<()> {
mod commands;
pub mod config;
#[derive(Clone, Debug)]
struct MagbotContext {
pub(crate) status: MagbotStatus,
pub(crate) titles: Vec<TitleSuggestion>,
}
impl Default for MagbotContext {
fn default() -> Self {
Self {
status: MagbotStatus::Sleeping,
titles: vec![],
}
}
}
/// A title suggestion
#[derive(Clone, Debug)]
struct TitleSuggestion {
pub(crate) user: String,
pub(crate) title: String,
}
async fn on_room_message(
event: OriginalSyncRoomMessageEvent,
room: Room,
ctx: Ctx<MagbotContext>,
) -> anyhow::Result<()> {
if room.state() != RoomState::Joined {
return Ok(());
}
@@ -17,28 +45,44 @@ async fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) -> any
};
match &text_content.body {
// start listening for titles
s if s.to_lowercase().starts_with("!showstart") => {
commands::start(&event, &room, ctx).await?;
}
// stop listening for titles
s if s.to_lowercase().starts_with("!showstop") => {
commands::stop(&event, &room, ctx).await?;
}
// shows available commands
s if s.to_lowercase().starts_with("!help") => {
commands::help(&event, &room, ctx).await?;
}
// submit a title suggestion
s if s.to_lowercase().starts_with("!suggest") => {
commands::suggest(&event, &room, s).await?;
commands::suggest(&event, &room, s, ctx).await?;
}
// retrieve list of titles
s if s.to_lowercase().starts_with("!gettitles") => {
commands::get_titles(&event, &room, s).await?;
commands::get_titles(&event, &room, s, ctx).await?;
}
// count upvotes on posted suggestions
s if s.to_lowercase().starts_with("!countvotes") => {
commands::count_votes(&event, &room).await?;
commands::count_votes(&event, &room, ctx).await?;
}
// generate chaotic text using Ollama
s if s.to_lowercase().starts_with("!mayhem") => {
commands::not_implemented(&event, &room, s).await?;
commands::not_implemented(&event, &room, s, ctx).await?;
}
// output the voting URL where users can cast votes
s if s.to_lowercase().starts_with("!voteurl") => {
commands::not_implemented(&event, &room, s).await?;
commands::not_implemented(&event, &room, s, ctx).await?;
}
// message is not a command; ie just a regular message
@@ -54,6 +98,8 @@ pub async fn login_and_sync(
username: String,
password: String,
) -> anyhow::Result<()> {
let context = MagbotContext::default();
// Note that when encryption is enabled, you should use a persistent store to be
// able to restore the session with a working encryption setup.
// See the `persist_session` example.
@@ -65,7 +111,7 @@ pub async fn login_and_sync(
client
.matrix_auth()
.login_username(&username, &password)
.initial_device_display_name("command bot")
.initial_device_display_name("magbot-rs")
.await?;
println!("logged in as {username}");
@@ -73,6 +119,10 @@ pub async fn login_and_sync(
// An initial sync to set up state and so our bot doesn't respond to old
// messages.
let response = client.sync_once(SyncSettings::default()).await.unwrap();
// adding context for state and title suggestions
client.add_event_handler_context(context);
// add our CommandBot to be notified of incoming messages, we do this after the
// initial sync to avoid responding to messages before the bot was running.
client.add_event_handler(on_room_message);