forked from nolan/titlebot-ng-ng
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e49f27c0df
|
||
|
|
da84226a45
|
||
|
|
a9b1115269
|
||
|
|
d3fac99ecf
|
||
|
|
bade2a1284
|
||
|
|
a8674ef0ad
|
||
|
|
a18eb2a41f
|
||
|
|
1b999c6f87
|
||
|
|
de23493572
|
||
|
|
3b5a4a8cb2
|
@@ -0,0 +1,22 @@
|
||||
// Project-local debug tasks
|
||||
//
|
||||
// For more documentation on how to configure debug tasks,
|
||||
// see: https://zed.dev/docs/debugger
|
||||
|
||||
[
|
||||
{
|
||||
"label": "Build & Debug magbot-rs",
|
||||
"build": {
|
||||
"command": "cargo",
|
||||
"args": ["build"],
|
||||
},
|
||||
"program": "$ZED_WORKTREE_ROOT/target/debug/magbot-rs",
|
||||
// sourceLanguages is required for CodeLLDB (not GDB) when using Rust
|
||||
"sourceLanguages": ["rust"],
|
||||
"request": "launch",
|
||||
"adapter": "CodeLLDB",
|
||||
"env": {
|
||||
"HOMESERVER_URL": "https://matrix.org",
|
||||
},
|
||||
},
|
||||
]
|
||||
Generated
+886
-856
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -1,11 +1,11 @@
|
||||
username: "magbot"
|
||||
password: "zC1b9qCKT8Cv0D"
|
||||
room_id: "!ajyOBbqoCIbBhEqNPU:matrix.org" # Magbot Testing Space
|
||||
room_id: "!ajyOBbqoCIbBhEqNPU:matrix.org" # Magbot Testing Space
|
||||
# room_id: "!HXiyYVEZOMRtkyvarN:jupiterbroadcasting.com" # Da Lunch
|
||||
# room_id: "!gJYEKNllaubNlNkFIj:jupiterbroadcasting.com" # The Main Chat
|
||||
lookback_seconds: 86400 # 24 hours
|
||||
lookback_seconds: 86400 # 24 hours
|
||||
lookback_limit: 400
|
||||
dump_mode: True
|
||||
dbName: ""
|
||||
vote_host: "localhost" # Hostname where voting interface is accessible (e.g., "localhost", "example.com", "vote.mysite.xyz")
|
||||
vote_activity_hours: 6672 # Hours a user must have been active within to be eligible to vote
|
||||
vote_host: "localhost" # Hostname where voting interface is accessible (e.g., "localhost", "example.com", "vote.mysite.xyz")
|
||||
vote_activity_hours: 6672 # Hours a user must have been active within to be eligible to vote
|
||||
|
||||
@@ -15,10 +15,11 @@ path = "src/bin/main.rs"
|
||||
[dependencies]
|
||||
anyhow = "1.0.100"
|
||||
libsqlite3-sys = { version = "0.35.0", features = ["bundled"] }
|
||||
matrix-sdk = "0.16.0"
|
||||
matrix-sdk = "0.17.0"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_yml = "0.0.12"
|
||||
tokio = { version = "1.48.0", features = ["rt-multi-thread", "macros"] }
|
||||
tracing = "0.1.41"
|
||||
tracing-subscriber = "0.3.22"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -6,7 +6,8 @@ use libmagbot::config::MagbotConfig;
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let config = MagbotConfig::from_env()?;
|
||||
let file_str = libmagbot::config::read_file()?;
|
||||
let config = MagbotConfig::from_str(&file_str)?;
|
||||
let Ok(homeserver_fqdn) = env::var("HOMESERVER_URL") else {
|
||||
eprintln!("Set the homeserver fqdn as the `HOMESERVER_URL` environment variable");
|
||||
std::process::exit(1)
|
||||
|
||||
+56
-12
@@ -1,10 +1,14 @@
|
||||
use matrix_sdk::{
|
||||
Room,
|
||||
event_handler::Ctx,
|
||||
ruma::events::{
|
||||
reaction::ReactionEventContent,
|
||||
relation::Annotation,
|
||||
room::message::{OriginalSyncRoomMessageEvent, RoomMessageEventContent},
|
||||
ruma::{
|
||||
UserId,
|
||||
events::{
|
||||
Mentions,
|
||||
reaction::ReactionEventContent,
|
||||
relation::Annotation,
|
||||
room::message::{OriginalSyncRoomMessageEvent, RoomMessageEventContent},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -24,16 +28,23 @@ pub async fn start(
|
||||
room: &Room,
|
||||
mut ctx: Ctx<MagbotContext>,
|
||||
) -> anyhow::Result<()> {
|
||||
tracing::debug!("Executing !start command");
|
||||
let user = event.sender.to_string();
|
||||
let ids = vec![UserId::parse(user.clone()).unwrap()];
|
||||
tracing::debug!("Command invoked by: {}", user);
|
||||
// set to listening
|
||||
match ctx.0.status {
|
||||
MagbotStatus::Running => {
|
||||
tracing::info!("Bot status already Running, not changing state");
|
||||
let mentions = Mentions::with_user_ids(ids);
|
||||
let msg = RoomMessageEventContent::text_plain(format!(
|
||||
"{user} Magbot is already listening for titles!"
|
||||
));
|
||||
))
|
||||
.add_mentions(mentions);
|
||||
room.send(msg).await?;
|
||||
}
|
||||
MagbotStatus::Sleeping => {
|
||||
tracing::info!("Changing bot status from Sleeping to Running");
|
||||
ctx.0.status = MagbotStatus::Running;
|
||||
let msg = RoomMessageEventContent::text_plain(format!(
|
||||
"{user} Magbot is now listening for titles!"
|
||||
@@ -50,8 +61,12 @@ pub async fn stop(
|
||||
room: &Room,
|
||||
mut ctx: Ctx<MagbotContext>,
|
||||
) -> anyhow::Result<()> {
|
||||
tracing::debug!("Executing !stop command");
|
||||
let user = event.sender.to_string();
|
||||
let ids = vec![UserId::parse(user.clone()).unwrap()];
|
||||
tracing::debug!("Command invoked by: {}", user);
|
||||
|
||||
tracing::debug!("Clearing {} saved titles", ctx.0.titles.len());
|
||||
// clear out saved titles
|
||||
ctx.0.titles.clear();
|
||||
|
||||
@@ -59,9 +74,11 @@ pub async fn stop(
|
||||
ctx.0.status = MagbotStatus::Sleeping;
|
||||
|
||||
// send message that magbot is no longer listening
|
||||
let mentions = Mentions::with_user_ids(ids);
|
||||
let msg = RoomMessageEventContent::text_plain(format!(
|
||||
"{user} Magbot is no longer listening for titles!"
|
||||
));
|
||||
))
|
||||
.add_mentions(mentions);
|
||||
room.send(msg).await?;
|
||||
|
||||
Ok(())
|
||||
@@ -73,12 +90,18 @@ pub async fn help(
|
||||
room: &Room,
|
||||
ctx: Ctx<MagbotContext>,
|
||||
) -> anyhow::Result<()> {
|
||||
tracing::debug!("Executing !help command");
|
||||
let user = event.sender.to_string();
|
||||
let ids = vec![UserId::parse(user.clone()).unwrap()];
|
||||
tracing::debug!("Command invoked by: {}", user);
|
||||
tracing::debug!("Sending help message with current status: {}", ctx.0.status);
|
||||
// send message with available commands
|
||||
let mentions = Mentions::with_user_ids(ids);
|
||||
let msg = RoomMessageEventContent::text_plain(format!(
|
||||
"{user} available commands are:\n{HELP_MSG}\nBot current status: {}",
|
||||
ctx.0.status
|
||||
));
|
||||
))
|
||||
.add_mentions(mentions);
|
||||
room.send(msg).await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -89,9 +112,12 @@ pub async fn suggest(
|
||||
room: &Room,
|
||||
mut ctx: Ctx<MagbotContext>,
|
||||
) -> anyhow::Result<()> {
|
||||
// save the title into memory with author
|
||||
tracing::debug!("Executing !suggest command");
|
||||
let user = event.sender.to_string();
|
||||
tracing::debug!("Command invoked by: {}", user);
|
||||
match ctx.0.status {
|
||||
MagbotStatus::Running => {
|
||||
tracing::info!("Processing title suggestion");
|
||||
// grab the index of the first <space> character
|
||||
let start_index = event
|
||||
.content
|
||||
@@ -100,8 +126,13 @@ pub async fn suggest(
|
||||
.position(|c| c == ' ')
|
||||
.unwrap_or(8); // start at 8 if no space since '!suggest' is 8 chars
|
||||
let suggestion_str = &event.content.body()[start_index + 1..];
|
||||
tracing::debug!("Extracted suggestion: {}", suggestion_str);
|
||||
let suggestion = TitleSuggestion::new(event.sender.as_ref(), suggestion_str);
|
||||
ctx.0.titles.push(suggestion);
|
||||
tracing::info!(
|
||||
"Title suggestion saved, total titles: {}",
|
||||
ctx.0.titles.len()
|
||||
);
|
||||
let reaction = ReactionEventContent::new(Annotation::new(
|
||||
event.event_id.clone(),
|
||||
"👍".to_string(),
|
||||
@@ -109,7 +140,7 @@ pub async fn suggest(
|
||||
room.send(reaction).await?;
|
||||
}
|
||||
MagbotStatus::Sleeping => {
|
||||
let msg = RoomMessageEventContent::text_plain("{user} Magbot is not running");
|
||||
let msg = RoomMessageEventContent::text_plain(format!("{user} Magbot is not running"));
|
||||
room.send(msg).await?;
|
||||
}
|
||||
}
|
||||
@@ -123,22 +154,29 @@ pub async fn get_titles(
|
||||
room: &Room,
|
||||
mut ctx: Ctx<MagbotContext>,
|
||||
) -> anyhow::Result<()> {
|
||||
tracing::debug!("Executing !gettitles command");
|
||||
let user = event.sender.to_string();
|
||||
tracing::debug!("Command invoked by: {}", user);
|
||||
tracing::debug!("Retrieving {} titles", ctx.0.titles.len());
|
||||
// sends all the titles with an initial reaction to be used for voting
|
||||
let user = event.sender.to_string();
|
||||
match ctx.0.status {
|
||||
MagbotStatus::Running => {
|
||||
for title in ctx.0.titles {
|
||||
let id = vec![UserId::parse(&title.user).unwrap()];
|
||||
let mentions = Mentions::with_user_ids(id);
|
||||
let msg = RoomMessageEventContent::text_plain(format!(
|
||||
"{} : {}",
|
||||
title.user, title.title
|
||||
));
|
||||
))
|
||||
.add_mentions(mentions);
|
||||
let response = room.send(msg).await?;
|
||||
let reaction = ReactionEventContent::new(Annotation::new(
|
||||
response.event_id.clone(),
|
||||
response.response.event_id.clone(),
|
||||
"👍".to_string(),
|
||||
));
|
||||
room.send(reaction).await?;
|
||||
ctx.0.message_ids.push(response.event_id);
|
||||
ctx.0.message_ids.push(response.response.event_id);
|
||||
}
|
||||
}
|
||||
MagbotStatus::Sleeping => {
|
||||
@@ -155,6 +193,9 @@ pub async fn count_votes(
|
||||
room: &Room,
|
||||
ctx: Ctx<MagbotContext>,
|
||||
) -> anyhow::Result<()> {
|
||||
tracing::debug!("Executing !countvotes command");
|
||||
tracing::debug!("Command invoked by: {}", event.sender);
|
||||
tracing::debug!("Processing {} title messages", ctx.0.message_ids.len());
|
||||
// count the reactions of the title messages
|
||||
// send message with winning title
|
||||
todo!()
|
||||
@@ -165,6 +206,8 @@ pub async fn not_implemented(
|
||||
event: &OriginalSyncRoomMessageEvent,
|
||||
room: &Room,
|
||||
) -> anyhow::Result<()> {
|
||||
tracing::debug!("Executing !not_implemented command");
|
||||
tracing::debug!("Command invoked by: {}", event.sender);
|
||||
// get '!' to the next '<space>' char and output as a command
|
||||
let end_index = event
|
||||
.content
|
||||
@@ -173,6 +216,7 @@ pub async fn not_implemented(
|
||||
.position(|c| c == ' ')
|
||||
.unwrap_or(event.content.body().chars().count());
|
||||
let att_cmd = &event.content.body()[..end_index];
|
||||
tracing::debug!("Not implemented command: {}", att_cmd);
|
||||
let content =
|
||||
RoomMessageEventContent::text_plain(format!("The command `{att_cmd}` is not implemented"));
|
||||
room.send(content).await?;
|
||||
|
||||
+51
-7
@@ -26,15 +26,59 @@ pub struct MagbotConfig {
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
pub room_id: String,
|
||||
pub status: MagbotStatus,
|
||||
|
||||
// has to be option since getting from configuration file will fail without the field
|
||||
pub status: Option<MagbotStatus>,
|
||||
}
|
||||
impl MagbotConfig {
|
||||
pub fn from_env() -> anyhow::Result<Self> {
|
||||
let mut file = File::open("config.yaml").unwrap_or(File::open("config.yaml")?);
|
||||
let mut file_str = String::new();
|
||||
file.read_to_string(&mut file_str)?;
|
||||
|
||||
let config: MagbotConfig = serde_yml::from_str(&file_str)?;
|
||||
pub fn from_str(input: &str) -> anyhow::Result<Self> {
|
||||
let mut config: MagbotConfig = serde_yml::from_str(input)?;
|
||||
config.status = Some(MagbotStatus::default());
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
impl Default for MagbotConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
room_id: String::new(),
|
||||
status: Some(MagbotStatus::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_file() -> anyhow::Result<String> {
|
||||
let mut file = File::open("config.yml").unwrap_or(File::open("config.yaml")?);
|
||||
let mut file_str = String::new();
|
||||
file.read_to_string(&mut file_str)?;
|
||||
Ok(file_str)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_config() {
|
||||
let username = "magbot";
|
||||
let password = "password";
|
||||
let room_id = "!ajyOBbqoCIbBhEqNPU:matrix.org";
|
||||
let _lookpack_sec = 86400;
|
||||
let _lookpack_limit = 400;
|
||||
let _dump_mode = true;
|
||||
let _db_name = "";
|
||||
let _vote_activity_hrs = 6672;
|
||||
|
||||
let str = r##"username: "magbot"
|
||||
password: "password"
|
||||
room_id: "!asdf:asdf.org"
|
||||
"##;
|
||||
|
||||
let parsed = MagbotConfig::from_str(str).unwrap();
|
||||
|
||||
assert_eq!(username, parsed.username);
|
||||
assert_eq!(password, parsed.password);
|
||||
assert_eq!(room_id, parsed.room_id);
|
||||
}
|
||||
}
|
||||
|
||||
+16
-2
@@ -57,49 +57,63 @@ async fn on_room_message(
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
match &text_content.body {
|
||||
let body = &text_content.body;
|
||||
tracing::debug!("Received message: {}", body);
|
||||
tracing::debug!("Message sender: {}", event.sender);
|
||||
|
||||
match body {
|
||||
// start listening for titles
|
||||
s if s.to_lowercase().starts_with("!showstart") => {
|
||||
tracing::info!("Processing command: !showstart");
|
||||
commands::start(&event, &room, ctx).await?;
|
||||
}
|
||||
|
||||
// stop listening for titles
|
||||
s if s.to_lowercase().starts_with("!showstop") => {
|
||||
tracing::info!("Processing command: !showstop");
|
||||
commands::stop(&event, &room, ctx).await?;
|
||||
}
|
||||
|
||||
// shows available commands
|
||||
s if s.to_lowercase().starts_with("!help") => {
|
||||
tracing::info!("Processing command: !help");
|
||||
commands::help(&event, &room, ctx).await?;
|
||||
}
|
||||
|
||||
// submit a title suggestion
|
||||
s if s.to_lowercase().starts_with("!suggest") => {
|
||||
tracing::info!("Processing command: !suggest");
|
||||
tracing::debug!("Suggestion text: {}", s);
|
||||
commands::suggest(&event, &room, ctx).await?;
|
||||
}
|
||||
|
||||
// retrieve list of titles
|
||||
s if s.to_lowercase().starts_with("!gettitles") => {
|
||||
tracing::info!("Processing command: !gettitles");
|
||||
commands::get_titles(&event, &room, ctx).await?;
|
||||
}
|
||||
|
||||
// count upvotes on posted suggestions
|
||||
s if s.to_lowercase().starts_with("!countvotes") => {
|
||||
tracing::info!("Processing command: !countvotes");
|
||||
commands::count_votes(&event, &room, ctx).await?;
|
||||
}
|
||||
|
||||
// generate chaotic text using Ollama
|
||||
s if s.to_lowercase().starts_with("!mayhem") => {
|
||||
tracing::info!("Processing command: !mayhem");
|
||||
commands::not_implemented(&event, &room).await?;
|
||||
}
|
||||
|
||||
// output the voting URL where users can cast votes
|
||||
s if s.to_lowercase().starts_with("!voteurl") => {
|
||||
tracing::info!("Processing command: !voteurl");
|
||||
commands::not_implemented(&event, &room).await?;
|
||||
}
|
||||
|
||||
// message is not a command; ie just a regular message
|
||||
_ => {
|
||||
tracing::debug!("Message not a recognized command, ignoring");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
@@ -111,7 +125,7 @@ pub async fn login_and_sync(
|
||||
username: String,
|
||||
password: String,
|
||||
) -> anyhow::Result<()> {
|
||||
let context = MagbotContext::default();
|
||||
let mut 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.
|
||||
|
||||
Reference in New Issue
Block a user