Wip commit
This commit is contained in:
@ -7,10 +7,10 @@ edition = "2018"
|
||||
[dependencies]
|
||||
rocket = { git = "https://github.com/SergioBenitez/Rocket", branch = "master", features = ["tls", "secrets", "json"] }
|
||||
rocket_dyn_templates = { git = "https://github.com/SergioBenitez/Rocket", branch = "master", features = ["tera"] }
|
||||
serenity = { version = "0.11", default-features = false, features = ["builder", "cache", "client", "gateway", "http", "model", "utils", "rustls_backend"] }
|
||||
serenity = { version = "0.12", default-features = false, features = ["builder", "cache", "client", "gateway", "http", "model", "utils", "rustls_backend"] }
|
||||
oauth2 = "4"
|
||||
log = "0.4"
|
||||
reqwest = "0.11"
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "macros", "mysql", "chrono", "json"] }
|
||||
chrono = "0.4"
|
||||
@ -20,3 +20,4 @@ rand = "0.8"
|
||||
base64 = "0.13"
|
||||
csv = "1.2"
|
||||
prometheus = "0.13.3"
|
||||
secrecy = "0.8.0"
|
||||
|
@ -23,14 +23,13 @@ pub const CHARACTERS: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX
|
||||
use std::{collections::HashSet, env, iter::FromIterator};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use serenity::model::prelude::AttachmentType;
|
||||
use serenity::builder::CreateAttachment;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref DEFAULT_AVATAR: AttachmentType<'static> = (
|
||||
pub static ref DEFAULT_AVATAR: CreateAttachment = CreateAttachment::bytes(
|
||||
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/webhook.jpg")) as &[u8],
|
||||
"webhook.jpg",
|
||||
)
|
||||
.into();
|
||||
);
|
||||
pub static ref SUBSCRIPTION_ROLES: HashSet<u64> = HashSet::from_iter(
|
||||
env::var("PATREON_ROLE_ID")
|
||||
.map(|var| var
|
||||
|
@ -35,8 +35,8 @@ type Database = MySql;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Error {
|
||||
SQLx(sqlx::Error),
|
||||
Serenity(serenity::Error),
|
||||
SQLx,
|
||||
Serenity,
|
||||
}
|
||||
|
||||
pub async fn initialize(
|
||||
@ -169,11 +169,11 @@ pub async fn check_subscription(cache_http: impl CacheHttp, user_id: impl Into<U
|
||||
offline!(true);
|
||||
|
||||
if let Some(subscription_guild) = *CNC_GUILD {
|
||||
let guild_member = GuildId(subscription_guild).member(cache_http, user_id).await;
|
||||
let guild_member = GuildId::new(subscription_guild).member(cache_http, user_id).await;
|
||||
|
||||
if let Ok(member) = guild_member {
|
||||
for role in member.roles {
|
||||
if SUBSCRIPTION_ROLES.contains(role.as_u64()) {
|
||||
if SUBSCRIPTION_ROLES.contains(&role.get()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -191,9 +191,7 @@ pub async fn check_guild_subscription(
|
||||
) -> bool {
|
||||
offline!(true);
|
||||
|
||||
if let Some(guild) = cache_http.cache().unwrap().guild(guild_id) {
|
||||
let owner = guild.owner_id;
|
||||
|
||||
if let Some(owner) = cache_http.cache().unwrap().guild(guild_id).map(|guild| guild.owner_id) {
|
||||
check_subscription(&cache_http, owner).await
|
||||
} else {
|
||||
false
|
||||
@ -203,7 +201,7 @@ pub async fn check_guild_subscription(
|
||||
pub async fn check_authorization(
|
||||
cookies: &CookieJar<'_>,
|
||||
ctx: &Context,
|
||||
guild: u64,
|
||||
guild_id: u64,
|
||||
) -> Result<(), JsonValue> {
|
||||
let user_id = cookies.get_private("userid").map(|c| c.value().parse::<u64>().ok()).flatten();
|
||||
|
||||
@ -217,38 +215,30 @@ pub async fn check_authorization(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match GuildId(guild).to_guild_cached(ctx) {
|
||||
Some(guild) => {
|
||||
let member_res = guild.member(ctx, UserId(user_id)).await;
|
||||
let guild_id = GuildId::new(guild_id);
|
||||
|
||||
match member_res {
|
||||
match guild_id.member(ctx, UserId::new(user_id)).await {
|
||||
Ok(member) => {
|
||||
let permissions_res = member.permissions(ctx);
|
||||
|
||||
match permissions_res {
|
||||
Err(_) => {
|
||||
return Err(json!({"error": "User not in guild"}));
|
||||
return Err(json!({"error": "Couldn't fetch permissions"}));
|
||||
}
|
||||
|
||||
Ok(member) => {
|
||||
let permissions_res = member.permissions(ctx);
|
||||
|
||||
match permissions_res {
|
||||
Err(_) => {
|
||||
return Err(json!({"error": "Couldn't fetch permissions"}));
|
||||
}
|
||||
|
||||
Ok(permissions) => {
|
||||
if !(permissions.manage_messages()
|
||||
|| permissions.manage_guild()
|
||||
|| permissions.administrator())
|
||||
{
|
||||
return Err(json!({"error": "Incorrect permissions"}));
|
||||
}
|
||||
}
|
||||
Ok(permissions) => {
|
||||
if !(permissions.manage_messages()
|
||||
|| permissions.manage_guild()
|
||||
|| permissions.administrator())
|
||||
{
|
||||
return Err(json!({"error": "Incorrect permissions"}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None => {
|
||||
return Err(json!({"error": "Bot not in guild"}));
|
||||
Err(_) => {
|
||||
return Err(json!({"error": "User not in guild"}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,13 +32,13 @@ pub async fn get_guild_channels(
|
||||
}])));
|
||||
check_authorization(cookies, ctx.inner(), id).await?;
|
||||
|
||||
match GuildId(id).to_guild_cached(ctx.inner()) {
|
||||
match GuildId::new(id).to_guild_cached(ctx.inner()) {
|
||||
Some(guild) => {
|
||||
let mut channels = guild
|
||||
.channels
|
||||
.iter()
|
||||
.filter_map(|(id, channel)| channel.to_owned().guild().map(|c| (id.to_owned(), c)))
|
||||
.filter(|(_, channel)| channel.is_text_based())
|
||||
.map(|(id, channel)| (id.to_owned(), channel.to_owned()))
|
||||
.collect::<Vec<(ChannelId, GuildChannel)>>();
|
||||
|
||||
channels.sort_by(|(_, c1), (_, c2)| c1.position.cmp(&c2.position));
|
||||
|
@ -22,19 +22,22 @@ pub async fn get_guild_info(id: u64, cookies: &CookieJar<'_>, ctx: &State<Contex
|
||||
offline!(Ok(json!({ "patreon": true, "name": "Guild" })));
|
||||
check_authorization(cookies, ctx.inner(), id).await?;
|
||||
|
||||
match GuildId(id).to_guild_cached(ctx.inner()) {
|
||||
Some(guild) => {
|
||||
let member_res = GuildId(env::var("PATREON_GUILD_ID").unwrap().parse().unwrap())
|
||||
.member(&ctx.inner(), guild.owner_id)
|
||||
match GuildId::new(id)
|
||||
.to_guild_cached(ctx.inner())
|
||||
.map(|guild| (guild.owner_id, guild.name.clone()))
|
||||
{
|
||||
Some((owner_id, name)) => {
|
||||
let member_res = GuildId::new(env::var("PATREON_GUILD_ID").unwrap().parse().unwrap())
|
||||
.member(&ctx.inner(), owner_id)
|
||||
.await;
|
||||
|
||||
let patreon = member_res.map_or(false, |member| {
|
||||
member
|
||||
.roles
|
||||
.contains(&RoleId(env::var("PATREON_ROLE_ID").unwrap().parse().unwrap()))
|
||||
.contains(&RoleId::new(env::var("PATREON_ROLE_ID").unwrap().parse().unwrap()))
|
||||
});
|
||||
|
||||
Ok(json!({ "patreon": patreon, "name": guild.name }))
|
||||
Ok(json!({ "patreon": patreon, "name": name }))
|
||||
}
|
||||
|
||||
None => json_err!("Bot not in guild"),
|
||||
|
@ -38,8 +38,8 @@ pub async fn create_guild_reminder(
|
||||
match create_reminder(
|
||||
ctx.inner(),
|
||||
&mut transaction,
|
||||
GuildId(id),
|
||||
UserId(user_id),
|
||||
GuildId::new(id),
|
||||
UserId::new(user_id),
|
||||
reminder.into_inner(),
|
||||
)
|
||||
.await
|
||||
@ -65,14 +65,14 @@ pub async fn get_reminders(
|
||||
) -> JsonResult {
|
||||
check_authorization(cookies, ctx.inner(), id).await?;
|
||||
|
||||
let channels_res = GuildId(id).channels(&ctx.inner()).await;
|
||||
let channels_res = GuildId::new(id).channels(&ctx.inner()).await;
|
||||
|
||||
match channels_res {
|
||||
Ok(channels) => {
|
||||
let channels = channels
|
||||
.keys()
|
||||
.into_iter()
|
||||
.map(|k| k.as_u64().to_string())
|
||||
.map(|k| k.get().to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(",");
|
||||
|
||||
@ -253,10 +253,12 @@ pub async fn edit_reminder(
|
||||
}
|
||||
|
||||
if reminder.channel > 0 {
|
||||
let channel = ChannelId(reminder.channel).to_channel_cached(&ctx.inner());
|
||||
match channel {
|
||||
Some(channel) => {
|
||||
let channel_matches_guild = channel.guild().map_or(false, |c| c.guild_id.0 == id);
|
||||
let channel_guild = ChannelId::new(reminder.channel)
|
||||
.to_channel_cached(&ctx.cache)
|
||||
.map(|channel| channel.guild_id);
|
||||
match channel_guild {
|
||||
Some(channel_guild) => {
|
||||
let channel_matches_guild = channel_guild.get() == id;
|
||||
|
||||
if !channel_matches_guild {
|
||||
warn!(
|
||||
@ -269,7 +271,7 @@ pub async fn edit_reminder(
|
||||
|
||||
let channel = create_database_channel(
|
||||
ctx.inner(),
|
||||
ChannelId(reminder.channel),
|
||||
ChannelId::new(reminder.channel),
|
||||
&mut transaction,
|
||||
)
|
||||
.await;
|
||||
|
@ -39,7 +39,7 @@ pub async fn get_user_info(
|
||||
if let Some(user_id) =
|
||||
cookies.get_private("userid").map(|u| u.value().parse::<u64>().ok()).flatten()
|
||||
{
|
||||
let member_res = GuildId(env::var("PATREON_GUILD_ID").unwrap().parse().unwrap())
|
||||
let member_res = GuildId::new(env::var("PATREON_GUILD_ID").unwrap().parse().unwrap())
|
||||
.member(&ctx.inner(), user_id)
|
||||
.await;
|
||||
|
||||
@ -58,7 +58,7 @@ pub async fn get_user_info(
|
||||
patreon: member_res.map_or(false, |member| {
|
||||
member
|
||||
.roles
|
||||
.contains(&RoleId(env::var("PATREON_ROLE_ID").unwrap().parse().unwrap()))
|
||||
.contains(&RoleId::new(env::var("PATREON_ROLE_ID").unwrap().parse().unwrap()))
|
||||
}),
|
||||
timezone,
|
||||
};
|
||||
|
@ -33,14 +33,14 @@ pub async fn export_reminders(
|
||||
|
||||
let mut csv_writer = WriterBuilder::new().quote_style(QuoteStyle::Always).from_writer(vec![]);
|
||||
|
||||
let channels_res = GuildId(id).channels(&ctx.inner()).await;
|
||||
let channels_res = GuildId::new(id).channels(&ctx.inner()).await;
|
||||
|
||||
match channels_res {
|
||||
Ok(channels) => {
|
||||
let channels = channels
|
||||
.keys()
|
||||
.into_iter()
|
||||
.map(|k| k.as_u64().to_string())
|
||||
.map(|k| k.get().to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(",");
|
||||
|
||||
@ -181,8 +181,8 @@ pub(crate) async fn import_reminders(
|
||||
create_reminder(
|
||||
ctx.inner(),
|
||||
&mut transaction,
|
||||
GuildId(id),
|
||||
UserId(user_id),
|
||||
GuildId::new(id),
|
||||
UserId::new(user_id),
|
||||
reminder,
|
||||
)
|
||||
.await?;
|
||||
@ -289,7 +289,7 @@ pub async fn import_todos(
|
||||
) -> JsonResult {
|
||||
check_authorization(cookies, ctx.inner(), id).await?;
|
||||
|
||||
let channels_res = GuildId(id).channels(&ctx.inner()).await;
|
||||
let channels_res = GuildId::new(id).channels(&ctx.inner()).await;
|
||||
|
||||
match channels_res {
|
||||
Ok(channels) => match base64::decode(&body.body) {
|
||||
@ -307,7 +307,7 @@ pub async fn import_todos(
|
||||
|
||||
match channel_id.parse::<u64>() {
|
||||
Ok(channel_id) => {
|
||||
if channels.contains_key(&ChannelId(channel_id)) {
|
||||
if channels.contains_key(&ChannelId::new(channel_id)) {
|
||||
query_params.push((record.value, Some(channel_id), id));
|
||||
} else {
|
||||
return json_err!(format!(
|
||||
|
@ -8,10 +8,12 @@ use rocket::{
|
||||
response::Redirect,
|
||||
serde::json::json,
|
||||
};
|
||||
use secrecy::ExposeSecret;
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serenity::{
|
||||
all::CacheHttp,
|
||||
builder::CreateWebhook,
|
||||
client::Context,
|
||||
http::Http,
|
||||
model::id::{ChannelId, GuildId, UserId},
|
||||
};
|
||||
use sqlx::types::Json;
|
||||
@ -363,12 +365,12 @@ pub(crate) async fn create_reminder(
|
||||
reminder: Reminder,
|
||||
) -> JsonResult {
|
||||
// check guild in db
|
||||
match sqlx::query!("SELECT 1 as A FROM guilds WHERE guild = ?", guild_id.0)
|
||||
match sqlx::query!("SELECT 1 as A FROM guilds WHERE guild = ?", guild_id.get())
|
||||
.fetch_one(transaction.executor())
|
||||
.await
|
||||
{
|
||||
Err(sqlx::Error::RowNotFound) => {
|
||||
if sqlx::query!("INSERT INTO guilds (guild) VALUES (?)", guild_id.0)
|
||||
if sqlx::query!("INSERT INTO guilds (guild) VALUES (?)", guild_id.get())
|
||||
.execute(transaction.executor())
|
||||
.await
|
||||
.is_err()
|
||||
@ -379,23 +381,26 @@ pub(crate) async fn create_reminder(
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// validate channel
|
||||
let channel = ChannelId(reminder.channel).to_channel_cached(&ctx);
|
||||
let channel_exists = channel.is_some();
|
||||
{
|
||||
// validate channel
|
||||
let channel = ChannelId::new(reminder.channel).to_channel_cached(&ctx.cache);
|
||||
let channel_exists = channel.is_some();
|
||||
|
||||
let channel_matches_guild =
|
||||
channel.map_or(false, |c| c.guild().map_or(false, |c| c.guild_id == guild_id));
|
||||
let channel_matches_guild =
|
||||
channel.map_or(false, |c| c.guild(&ctx.cache).map_or(false, |c| c.id == guild_id));
|
||||
|
||||
if !channel_matches_guild || !channel_exists {
|
||||
warn!(
|
||||
"Error in `create_reminder`: channel {} not found for guild {} (channel exists: {})",
|
||||
reminder.channel, guild_id, channel_exists
|
||||
);
|
||||
if !channel_matches_guild || !channel_exists {
|
||||
warn!(
|
||||
"Error in `create_reminder`: channel {} not found for guild {} (channel exists: {})",
|
||||
reminder.channel, guild_id, channel_exists
|
||||
);
|
||||
|
||||
return Err(json!({"error": "Channel not found"}));
|
||||
return Err(json!({"error": "Channel not found"}));
|
||||
}
|
||||
}
|
||||
|
||||
let channel = create_database_channel(&ctx, ChannelId(reminder.channel), transaction).await;
|
||||
let channel =
|
||||
create_database_channel(&ctx, ChannelId::new(reminder.channel), transaction).await;
|
||||
|
||||
if let Err(e) = channel {
|
||||
warn!("`create_database_channel` returned an error code: {:?}", e);
|
||||
@ -590,32 +595,38 @@ pub(crate) async fn create_reminder(
|
||||
}
|
||||
|
||||
async fn create_database_channel(
|
||||
ctx: impl AsRef<Http>,
|
||||
ctx: impl CacheHttp,
|
||||
channel: ChannelId,
|
||||
transaction: &mut Transaction<'_>,
|
||||
) -> Result<u32, crate::Error> {
|
||||
let row =
|
||||
sqlx::query!("SELECT webhook_token, webhook_id FROM channels WHERE channel = ?", channel.0)
|
||||
.fetch_one(transaction.executor())
|
||||
.await;
|
||||
let row = sqlx::query!(
|
||||
"SELECT webhook_token, webhook_id FROM channels WHERE channel = ?",
|
||||
channel.get()
|
||||
)
|
||||
.fetch_one(transaction.executor())
|
||||
.await;
|
||||
|
||||
match row {
|
||||
Ok(row) => {
|
||||
if row.webhook_token.is_none() || row.webhook_id.is_none() {
|
||||
let webhook = channel
|
||||
.create_webhook_with_avatar(&ctx, "Reminder", DEFAULT_AVATAR.clone())
|
||||
.create_webhook(&ctx, CreateWebhook::new("Reminder").avatar(&*DEFAULT_AVATAR))
|
||||
.await
|
||||
.map_err(|e| Error::Serenity(e))?;
|
||||
.map_err(|_| Error::Serenity)?;
|
||||
|
||||
let token = webhook.token.unwrap();
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE channels SET webhook_id = ?, webhook_token = ? WHERE channel = ?",
|
||||
webhook.id.0,
|
||||
webhook.token,
|
||||
channel.0
|
||||
"
|
||||
UPDATE channels SET webhook_id = ?, webhook_token = ? WHERE channel = ?
|
||||
",
|
||||
webhook.id.get(),
|
||||
token.expose_secret(),
|
||||
channel.get()
|
||||
)
|
||||
.execute(transaction.executor())
|
||||
.await
|
||||
.map_err(|e| Error::SQLx(e))?;
|
||||
.map_err(|_| Error::SQLx)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -624,35 +635,39 @@ async fn create_database_channel(
|
||||
Err(sqlx::Error::RowNotFound) => {
|
||||
// create webhook
|
||||
let webhook = channel
|
||||
.create_webhook_with_avatar(&ctx, "Reminder", DEFAULT_AVATAR.clone())
|
||||
.create_webhook(&ctx, CreateWebhook::new("Reminder").avatar(&*DEFAULT_AVATAR))
|
||||
.await
|
||||
.map_err(|e| Error::Serenity(e))?;
|
||||
.map_err(|_| Error::Serenity)?;
|
||||
|
||||
let token = webhook.token.unwrap();
|
||||
|
||||
// create database entry
|
||||
sqlx::query!(
|
||||
"INSERT INTO channels (
|
||||
"
|
||||
INSERT INTO channels (
|
||||
webhook_id,
|
||||
webhook_token,
|
||||
channel
|
||||
) VALUES (?, ?, ?)",
|
||||
webhook.id.0,
|
||||
webhook.token,
|
||||
channel.0
|
||||
) VALUES (?, ?, ?)
|
||||
",
|
||||
webhook.id.get(),
|
||||
token.expose_secret(),
|
||||
channel.get()
|
||||
)
|
||||
.execute(transaction.executor())
|
||||
.await
|
||||
.map_err(|e| Error::SQLx(e))?;
|
||||
.map_err(|_| Error::SQLx)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Err(e) => Err(Error::SQLx(e)),
|
||||
Err(_) => Err(Error::SQLx),
|
||||
}?;
|
||||
|
||||
let row = sqlx::query!("SELECT id FROM channels WHERE channel = ?", channel.0)
|
||||
let row = sqlx::query!("SELECT id FROM channels WHERE channel = ?", channel.get())
|
||||
.fetch_one(transaction.executor())
|
||||
.await
|
||||
.map_err(|e| Error::SQLx(e))?;
|
||||
.map_err(|_| Error::SQLx)?;
|
||||
|
||||
Ok(row.id)
|
||||
}
|
||||
|
@ -102,10 +102,9 @@ pub async fn discord_callback(
|
||||
|
||||
match user_res {
|
||||
Ok(user) => {
|
||||
let user_name = format!("{}#{}", user.name, user.discriminator);
|
||||
let user_id = user.id.as_u64().to_string();
|
||||
let user_id = user.id.get().to_string();
|
||||
|
||||
cookies.add_private(Cookie::new("username", user_name));
|
||||
cookies.add_private(Cookie::new("username", user.name));
|
||||
cookies.add_private(Cookie::new("userid", user_id));
|
||||
|
||||
Ok(Redirect::to(uri!(super::return_to_same_site("dashboard"))))
|
||||
|
Reference in New Issue
Block a user