-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.rs
More file actions
165 lines (153 loc) · 5.6 KB
/
main.rs
File metadata and controls
165 lines (153 loc) · 5.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
mod commands;
mod subscriptions;
use chrono::{Local, Weekday};
use indoc::formatdoc;
use poise::serenity_prelude::{self as serenity, ChannelType, CreateThread};
use std::env::var;
use std::sync::Arc;
use subscriptions::SubscriptionStore;
use tokio_schedule::Job;
// Types used by all command functions
type Error = anyhow::Error;
type Context<'a> = poise::Context<'a, Data, Error>;
// Custom user data passed to all command functions
pub struct Data {
subscriptions: Arc<SubscriptionStore>,
}
async fn on_error(error: poise::FrameworkError<'_, Data, Error>) {
// This is our custom error handler
// They are many errors that can occur, so we only handle the ones we want
// to customize and forward the rest to the default handler
match error {
poise::FrameworkError::Setup { error, .. } => {
panic!("Failed to start bot: {:?}", error)
}
poise::FrameworkError::Command { error, ctx, .. } => {
println!("Error in command `{}`: {:?}", ctx.command().name, error,);
}
error => {
if let Err(e) = poise::builtins::on_error(error).await {
println!("Error while handling error: {}", e)
}
}
}
}
#[tokio::main]
async fn main() {
//// Load config from environment ////
// WAFFLECORD_SUBSCRIBERS_DIR: Directory where sled can store the list of
// subscribed channels
// DISCORD_TOKEN: Token for the discord bot
let subscriptions = Arc::new(
SubscriptionStore::try_load(
var("WAFFLECORD_SUBSCRIBERS_DIR")
.expect("Missing `WAFFLECORD_SUBSCRIBERS_DIR` env var")
.into(),
)
.expect("Error loading subscribers database."),
);
let token = var("WAFFLECORD_DISCORD_TOKEN").expect(
"Missing `DISCORD_TOKEN` env var, see README for more information.",
);
//// Configure poise framework ////
let commands: Vec<poise::Command<Data, Error>> =
vec![commands::subscribe(), commands::unsubscribe()];
let options = poise::FrameworkOptions {
commands,
on_error: |error| {
eprintln!("An error occurred: {error}");
Box::pin(on_error(error))
},
pre_command: |ctx| {
Box::pin(async move {
println!(
"Executing command {}...",
ctx.command().qualified_name
);
})
},
post_command: |ctx| {
Box::pin(async move {
println!(
"Finished executing {}.",
ctx.command().qualified_name
);
})
},
..Default::default()
};
let subscriptions_clone = subscriptions.clone();
let framework = poise::Framework::builder()
.setup(move |ctx, _ready, framework| {
Box::pin(async move {
println!("Logged in as {}", _ready.user.name);
poise::builtins::register_globally(
ctx,
&framework.options().commands,
)
.await?;
Ok(Data {
subscriptions: subscriptions_clone,
})
})
})
.options(options)
.build();
let intents = serenity::GatewayIntents::GUILDS
| serenity::GatewayIntents::GUILD_MESSAGES;
let mut client = serenity::ClientBuilder::new(token, intents)
.framework(framework)
.await
.expect("Error creating client");
//// Schedule notifications ////
let subscriptions_clone = subscriptions.clone();
let http_clone = client.http.clone();
let notification_task = tokio_schedule::every(4)
.weeks()
.on(Weekday::Wed)
.at(4, 30, 00)
.in_timezone(&Local)
.perform(move || {
let subscriptions = subscriptions_clone.clone();
let http = http_clone.clone();
async move {
let date_str = Local::now().format("%D");
let mut successes = 0;
for sub in subscriptions.subscribers_iter() {
let role_ping = match sub.role_id {
Some(role_id) => format!("<@&{role_id}>"),
None => "Waflers".into(),
};
let content = formatdoc! {"
# It's Waffle Wednesday!
{role_ping}, leave your waffles in the thread below.
-# (week of {date_str})
"};
let message =
match sub.channel_id.say(&http, &content).await {
Ok(msg) => msg,
Err(e) => {
eprintln!("Failed to send message: {e}");
return;
}
};
let thread =
CreateThread::new(format!("{date_str} waffling"))
.kind(ChannelType::PublicThread);
if let Err(e) = sub
.channel_id
.create_thread_from_message(&http, message.id, thread)
.await
{
eprintln!("Failed to create thread: {e}");
return;
}
successes += 1;
}
println!("Sent notifications to {successes} channels.");
}
});
tokio::spawn(notification_task);
//// Start the bot ////
client.start().await.unwrap()
}