Add basic inbox functionality
This commit is contained in:
parent
918f370562
commit
cdd370bd0d
|
@ -7,5 +7,10 @@ edition = "2018"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
itertools = "0.10.0"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
redis = "0.20.0"
|
||||||
|
serde = { version = "1.0.123", features = ["derive"] }
|
||||||
|
serde_json = "1.0.62"
|
||||||
serenity = "0.8.6"
|
serenity = "0.8.6"
|
||||||
time = "0.2.10"
|
time = "0.2.10"
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
use redis::{Client, Commands, RedisError};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serenity::model::prelude::*;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
// TODO: time
|
||||||
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct InboxMessage {
|
||||||
|
sender: UserId,
|
||||||
|
content: String,
|
||||||
|
recipient: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InboxMessage {
|
||||||
|
pub fn parse(raw: &str, sender: UserId) -> Option<Self> {
|
||||||
|
raw.strip_prefix("!message ")
|
||||||
|
.and_then(|m| m.split_once(":"))
|
||||||
|
.map(|(recipient, content)| InboxMessage {
|
||||||
|
sender,
|
||||||
|
content: content.trim().to_string(),
|
||||||
|
recipient: recipient.to_string(),
|
||||||
|
})
|
||||||
|
.filter(|m| m.recipient.len() < 20)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for InboxMessage {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_fmt(format_args!("<@{}>: {}", self.sender.0, self.content))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Inbox(pub Client);
|
||||||
|
|
||||||
|
impl Inbox {
|
||||||
|
pub fn fetch_messages(&self, recipient: &str) -> Vec<InboxMessage> {
|
||||||
|
self.0
|
||||||
|
.get_connection()
|
||||||
|
.and_then(|mut conn| conn.lrange(&inbox_name(recipient), 0, -1))
|
||||||
|
.map(|ss: Vec<String>| {
|
||||||
|
ss.iter()
|
||||||
|
.map(|s| serde_json::from_str(s).unwrap())
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|_| Vec::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count_messages(&self, recipient: &str) -> usize {
|
||||||
|
self.0
|
||||||
|
.get_connection()
|
||||||
|
.and_then(|mut conn| conn.llen(&inbox_name(recipient)))
|
||||||
|
.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_messages(&self, recipient: &str) -> Result<(), RedisError> {
|
||||||
|
self.0
|
||||||
|
.get_connection()
|
||||||
|
.and_then(|mut conn| conn.del(&inbox_name(recipient)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn queue_message(&self, msg: &InboxMessage) -> Result<String, String> {
|
||||||
|
self.0
|
||||||
|
.get_connection()
|
||||||
|
.and_then(|mut conn| {
|
||||||
|
conn.rpush(
|
||||||
|
&inbox_name(&msg.recipient),
|
||||||
|
serde_json::to_string(&msg).unwrap(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map(|_: usize| msg.recipient.clone())
|
||||||
|
.map_err(|e| {
|
||||||
|
eprintln!("{:?}", e);
|
||||||
|
String::from("Beim Speichern deiner Nachricht ist ein Fehler aufgetreten. Bitte sag kageru Bescheid.")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inbox_name(name: &str) -> String {
|
||||||
|
format!("{}-{}", super::APPLICATION_NAME, name.to_lowercase())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inbox_message_conversion_test() {
|
||||||
|
assert_eq!(
|
||||||
|
InboxMessage::parse("!message Lana: hawwu, ich mag dich", UserId(123)),
|
||||||
|
Some(InboxMessage {
|
||||||
|
sender: UserId(123),
|
||||||
|
content: String::from("hawwu, ich mag dich"),
|
||||||
|
recipient: String::from("Lana"),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
InboxMessage::parse("!message Ivy: hallu :3", UserId(123)),
|
||||||
|
Some(InboxMessage {
|
||||||
|
sender: UserId(123),
|
||||||
|
content: String::from("hallu :3"),
|
||||||
|
recipient: String::from("Ivy"),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
InboxMessage::parse(
|
||||||
|
"!message Flayn Gelobt seist du, oh königliche Hoheit!",
|
||||||
|
UserId(123)
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
InboxMessage::parse(
|
||||||
|
"!message Name Das hier ist ein Text, aber der Doppelpunkt ist hier -> :D",
|
||||||
|
UserId(123)
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
97
src/main.rs
97
src/main.rs
|
@ -1,7 +1,10 @@
|
||||||
|
use inbox::*;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
use serenity::client::Client;
|
use serenity::client::Client;
|
||||||
use serenity::framework::standard::{
|
use serenity::framework::standard::{
|
||||||
macros::{command, group},
|
macros::{command, group},
|
||||||
CommandResult, StandardFramework,
|
CommandError, CommandResult, StandardFramework,
|
||||||
};
|
};
|
||||||
use serenity::model::{
|
use serenity::model::{
|
||||||
channel::Message,
|
channel::Message,
|
||||||
|
@ -12,9 +15,16 @@ use serenity::prelude::*;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, BufRead, BufReader};
|
use std::io::{self, BufRead, BufReader};
|
||||||
use time::Duration;
|
use time::Duration;
|
||||||
|
mod inbox;
|
||||||
|
|
||||||
|
const APPLICATION_NAME: &str = "didgeridoo";
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref INBOX: Inbox = Inbox(redis::Client::open("redis://127.0.0.1/").unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
#[group]
|
#[group]
|
||||||
#[commands(name)]
|
#[commands(name, message, question, inbox)]
|
||||||
struct Fluff;
|
struct Fluff;
|
||||||
struct Handler;
|
struct Handler;
|
||||||
impl EventHandler for Handler {
|
impl EventHandler for Handler {
|
||||||
|
@ -43,6 +53,7 @@ fn main() {
|
||||||
.group(&FLUFF_GROUP),
|
.group(&FLUFF_GROUP),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
INBOX.count_messages("test"); // initialize the lazy static so we know if redis is unavailable
|
||||||
if let Err(why) = client.start() {
|
if let Err(why) = client.start() {
|
||||||
println!("An error occurred while running the client: {:?}", why);
|
println!("An error occurred while running the client: {:?}", why);
|
||||||
}
|
}
|
||||||
|
@ -73,7 +84,87 @@ fn name(ctx: &mut Context, msg: &Message) -> CommandResult {
|
||||||
now.unix_timestamp()
|
now.unix_timestamp()
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
msg.reply(&ctx, "Please specify a new name.")?;
|
return Err(CommandError(String::from("Please specify a new name.")));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
fn message(ctx: &mut Context, msg: &Message) -> CommandResult {
|
||||||
|
InboxMessage::parse(&msg.content, msg.author.id)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
String::from(
|
||||||
|
"Nachricht konnte nicht gesendet werden.
|
||||||
|
Bitte achte auf die richtige Formulierung: “!message <name>: <text>”,
|
||||||
|
z.B. “!message Lana: Hawwu!”",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.and_then(|m| INBOX.queue_message(&m))
|
||||||
|
.map(|n| {
|
||||||
|
if let Err(e) = msg.reply(
|
||||||
|
&ctx,
|
||||||
|
&format!("Deine Nachricht wurde erfolgreich an {} gesendet", n),
|
||||||
|
) {
|
||||||
|
eprintln!("Could not reply because of {:?}", e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map_err(|e| {
|
||||||
|
if let Err(e) = msg.reply(ctx, &e) {
|
||||||
|
eprintln!("Could not reply because of {:?}", e);
|
||||||
|
}
|
||||||
|
CommandError(e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
fn inbox(ctx: &mut Context, msg: &Message) -> CommandResult {
|
||||||
|
if let Some((_, name)) = msg.content.split_once(' ') {
|
||||||
|
let messages = INBOX.fetch_messages(name);
|
||||||
|
if messages.is_empty() {
|
||||||
|
if let Err(e) = msg.reply(ctx, "Keine neuen Nachrichten für .") {
|
||||||
|
eprintln!("Could not reply because of {:?}", e);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let output = messages.into_iter().join("\n");
|
||||||
|
if let Err(e) = msg.reply(ctx, output) {
|
||||||
|
eprintln!("Could not reply because of {:?}", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Err(e) = msg.reply(ctx, "Kein Name für die Abfrage.") {
|
||||||
|
eprintln!("Could not reply because of {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove copy paste
|
||||||
|
#[command]
|
||||||
|
fn question(ctx: &mut Context, msg: &Message) -> CommandResult {
|
||||||
|
InboxMessage::parse(
|
||||||
|
&msg.content.replacen("!question", "!message Stream:", 1),
|
||||||
|
msg.author.id,
|
||||||
|
)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
String::from(
|
||||||
|
"Nachricht konnte nicht gesendet werden.
|
||||||
|
Bitte achte auf die richtige Formulierung: “!message <name>: <text>”,
|
||||||
|
z.B. “!message Lana: Hawwu!”",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.and_then(|m| INBOX.queue_message(&m))
|
||||||
|
.map(|n| {
|
||||||
|
if let Err(e) = msg.reply(
|
||||||
|
&ctx,
|
||||||
|
&format!("Deine Nachricht wurde erfolgreich an {} gesendet", n),
|
||||||
|
) {
|
||||||
|
eprintln!("Could not reply because of {:?}", e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map_err(|e| {
|
||||||
|
if let Err(e) = msg.reply(ctx, &e) {
|
||||||
|
eprintln!("Could not reply because of {:?}", e);
|
||||||
|
}
|
||||||
|
CommandError(e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user