229 lines
7.0 KiB
Rust
229 lines
7.0 KiB
Rust
use async_trait::async_trait;
|
|
use inbox::*;
|
|
use lazy_static::lazy_static;
|
|
use serenity::client::Client;
|
|
use serenity::framework::standard::{
|
|
macros::{command, group},
|
|
CommandResult, StandardFramework,
|
|
};
|
|
use serenity::model::channel::{Reaction, ReactionType};
|
|
use serenity::model::{
|
|
channel::Message,
|
|
id::{ChannelId, GuildId, UserId},
|
|
prelude::User,
|
|
};
|
|
use serenity::prelude::*;
|
|
use std::fs::File;
|
|
use std::io::{self, BufRead, BufReader};
|
|
use time::Duration;
|
|
mod inbox;
|
|
|
|
const APPLICATION_NAME: &str = "didgeridoo";
|
|
const INBOX_OWNER: UserId = UserId(354694403798990848);
|
|
|
|
lazy_static! {
|
|
static ref INBOX: Inbox = Inbox(redis::Client::open("redis://127.0.0.1/").unwrap());
|
|
}
|
|
|
|
macro_rules! send_or_log {
|
|
($e: expr) => {
|
|
if let Err(e) = $e {
|
|
eprintln!("Couldn’t send message because {:?}", e);
|
|
}
|
|
};
|
|
}
|
|
|
|
macro_rules! role_action {
|
|
($reaction: expr, $ctx: expr, $action: ident) => {
|
|
$reaction
|
|
.guild_id
|
|
.unwrap()
|
|
.member(&$ctx, $reaction.user_id.unwrap())
|
|
.await
|
|
.unwrap()
|
|
.$action(&$ctx, 356421827708321795)
|
|
.await
|
|
.unwrap()
|
|
};
|
|
}
|
|
|
|
#[group]
|
|
#[commands(name, message, question, inbox)]
|
|
struct Fluff;
|
|
struct Handler;
|
|
#[async_trait]
|
|
impl EventHandler for Handler {
|
|
async fn guild_ban_addition(&self, ctx: Context, guild_id: GuildId, _: User) {
|
|
if guild_id == 427456811973738498 {
|
|
send_or_log!({
|
|
ChannelId(562731470423064587)
|
|
.say(&ctx, "Dies ist eine flauschige Diktatur!")
|
|
.await
|
|
});
|
|
}
|
|
}
|
|
|
|
async fn reaction_add(&self, ctx: Context, reaction: Reaction) {
|
|
if reaction.message_id == 911630315376738384
|
|
&& reaction.emoji == ReactionType::Unicode(String::from("🧪"))
|
|
{
|
|
role_action!(reaction, ctx, add_role)
|
|
}
|
|
}
|
|
|
|
async fn reaction_remove(&self, ctx: Context, reaction: Reaction) {
|
|
if reaction.message_id == 911630315376738384
|
|
&& reaction.emoji == ReactionType::Unicode(String::from("🧪"))
|
|
{
|
|
role_action!(reaction, ctx, remove_role)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn read_token() -> io::Result<String> {
|
|
let reader = BufReader::new(File::open("secret")?);
|
|
reader.lines().next().unwrap()
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let mut client = Client::builder(&read_token().expect("no secret file"))
|
|
.event_handler(Handler)
|
|
.framework(
|
|
StandardFramework::new()
|
|
.configure(|c| c.prefix("!"))
|
|
.group(&FLUFF_GROUP),
|
|
)
|
|
.await
|
|
.expect("Error creating client");
|
|
|
|
INBOX.count_messages("test"); // initialize the lazy static so we know if redis is unavailable
|
|
if let Err(why) = client.start().await {
|
|
println!("An error occurred while running the client: {:?}", why);
|
|
}
|
|
}
|
|
|
|
#[command]
|
|
async fn name(ctx: &Context, msg: &Message) -> CommandResult {
|
|
if let Some((_, name)) = msg.content.split_once(' ') {
|
|
let (name, adjustment) = name.split_once(" -").unwrap_or((name, "0"));
|
|
let adjustment = adjustment.strip_suffix("h").unwrap_or(adjustment);
|
|
msg.guild(&ctx)
|
|
.await
|
|
.unwrap()
|
|
.edit_member(&ctx, msg.author.id, |m| m.nickname(name))
|
|
.await?;
|
|
let now = time::OffsetDateTime::now_utc()
|
|
- adjustment
|
|
.parse::<f32>()
|
|
.map(|a| (a * 3600.0) as i64)
|
|
.map(Duration::seconds)
|
|
.ok()
|
|
.unwrap_or_else(Duration::zero);
|
|
println!(
|
|
"{},{},{},{},{}",
|
|
now.format("%d.%m.%Y %H:%M"),
|
|
name,
|
|
now.format("%y%m"),
|
|
msg.author.id,
|
|
now.unix_timestamp()
|
|
);
|
|
if msg.author.id == INBOX_OWNER {
|
|
let msg_count = INBOX.count_messages(name);
|
|
if msg_count > 0 {
|
|
send_or_log!(
|
|
msg.reply(&ctx, format!("{} neue Nachrichten für {}", msg_count, name))
|
|
.await
|
|
);
|
|
} else {
|
|
send_or_log!(
|
|
msg.reply(&ctx, format!("Keine neuen Nachrichten für {}", name))
|
|
.await
|
|
);
|
|
}
|
|
}
|
|
} else {
|
|
send_or_log!(msg.reply(&ctx, "Please specify a new name.").await);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
async fn inbox(ctx: &Context, msg: &Message) -> CommandResult {
|
|
if msg.author.id != INBOX_OWNER || !msg.is_private() {
|
|
send_or_log!(
|
|
msg.reply(&ctx, "You don’t have the permission to do that")
|
|
.await
|
|
);
|
|
return Ok(());
|
|
}
|
|
if let Some((_, name)) = msg.content.split_once(' ') {
|
|
let messages = INBOX.fetch_messages(name);
|
|
if messages.is_empty() {
|
|
send_or_log!(
|
|
msg.reply(&ctx, format!("Keine neuen Nachrichten für {}.", name))
|
|
.await
|
|
);
|
|
return Ok(());
|
|
}
|
|
for message in &messages {
|
|
let content = message.to_string();
|
|
// Since we’re prepending a name (and later a time) to the message,
|
|
// it could go over the limit of 2000 characters.
|
|
// There is some leniency in the numbers here because unicode codepoints are meh.
|
|
if content.len() > 1900 {
|
|
let (first, second) = content.split_at(1500);
|
|
send_or_log!(msg.channel_id.say(&ctx, &first).await);
|
|
send_or_log!(msg.channel_id.say(&ctx, &second).await);
|
|
} else {
|
|
send_or_log!(msg.channel_id.say(&ctx, &content).await);
|
|
}
|
|
}
|
|
println!("{:?}", messages); // paranoia for now so the messages aren’t lost
|
|
if let Err(e) = INBOX.clear_messages(name) {
|
|
eprintln!("Could not clear inbox of user {} because {}", name, e);
|
|
}
|
|
} else {
|
|
send_or_log!(msg.reply(&ctx, "Kein Name für die Abfrage.").await);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
async fn message(ctx: &Context, msg: &Message) -> CommandResult {
|
|
message_internal(ctx, msg, &msg.content).await
|
|
}
|
|
|
|
#[command]
|
|
async fn question(ctx: &Context, msg: &Message) -> CommandResult {
|
|
message_internal(
|
|
ctx,
|
|
msg,
|
|
&msg.content.replacen("!question", "!message Stream:", 1),
|
|
)
|
|
.await
|
|
}
|
|
|
|
async fn message_internal(ctx: &Context, msg: &Message, content: &str) -> CommandResult {
|
|
let result = InboxMessage::parse(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));
|
|
match result {
|
|
Ok(name) => send_or_log!(
|
|
msg.reply(
|
|
&ctx,
|
|
format!("Deine Nachricht wurde erfolgreich an {} gesendet", name),
|
|
)
|
|
.await
|
|
),
|
|
Err(e) => send_or_log!(msg.reply(&ctx, e).await),
|
|
};
|
|
Ok(())
|
|
}
|