mirror of
https://github.com/revanced/revanced-discord-bot.git
synced 2025-05-17 05:17:06 +02:00
feat(utils): poll
command (#40)
Co-authored-by: Ax333l <main@axelen.xyz>
This commit is contained in:
parent
90643206b1
commit
3be6b4693c
@ -1,4 +1,11 @@
|
|||||||
# The Discord authorization token for the bot, requires the MESSAGE_CONTENT intent
|
# The Discord authorization token for the bot, requires the MESSAGE_CONTENT intent
|
||||||
DISCORD_AUTHORIZATION_TOKEN=
|
DISCORD_AUTHORIZATION_TOKEN=
|
||||||
# The connection string to the MongoDB database
|
# The connection string to the MongoDB database
|
||||||
MONGODB_URI=''
|
MONGODB_URI=''
|
||||||
|
|
||||||
|
# The api server for the poll command
|
||||||
|
API_SERVER=''
|
||||||
|
# The client id for the api
|
||||||
|
API_CLIENT_ID=''
|
||||||
|
# The client secret for the api
|
||||||
|
API_CLIENT_SECRET=''
|
||||||
|
@ -63,7 +63,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"description": "Introduce new threads with a message.",
|
"description": "Introduce new threads with a message.",
|
||||||
"minItems": 1,
|
|
||||||
"uniqueItems": true
|
"uniqueItems": true
|
||||||
},
|
},
|
||||||
"message_responses": {
|
"message_responses": {
|
||||||
@ -138,8 +137,7 @@
|
|||||||
"items": {
|
"items": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"uniqueItems": true,
|
"uniqueItems": true
|
||||||
"minItems": 1
|
|
||||||
},
|
},
|
||||||
"match": {
|
"match": {
|
||||||
"$ref": "#/$defs/regex",
|
"$ref": "#/$defs/regex",
|
||||||
|
62
src/api/client.rs
Normal file
62
src/api/client.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
use reqwest::header::HeaderMap;
|
||||||
|
use reqwest::Client;
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
|
||||||
|
use super::model::auth::Authentication;
|
||||||
|
|
||||||
|
use super::routing::Endpoint;
|
||||||
|
|
||||||
|
pub struct Api {
|
||||||
|
pub client: Client,
|
||||||
|
pub server: reqwest::Url,
|
||||||
|
pub client_id: String,
|
||||||
|
pub client_secret: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RequestInfo<'a> {
|
||||||
|
headers: Option<HeaderMap>,
|
||||||
|
route: Endpoint<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Api {
|
||||||
|
pub fn new(server: reqwest::Url, client_id: String, client_secret: String) -> Self {
|
||||||
|
let client = Client::builder()
|
||||||
|
.build()
|
||||||
|
.expect("Cannot build reqwest::Client");
|
||||||
|
|
||||||
|
Api {
|
||||||
|
client,
|
||||||
|
server,
|
||||||
|
client_id,
|
||||||
|
client_secret,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fire<T: DeserializeOwned>(&self, request_info: &RequestInfo<'_>) -> Result<T, reqwest::Error> {
|
||||||
|
let client = &self.client;
|
||||||
|
let mut req = request_info.route.to_request(&self.server);
|
||||||
|
|
||||||
|
if let Some(headers) = &request_info.headers {
|
||||||
|
*req.headers_mut() = headers.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
client.execute(req).await?.json::<T>().await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn authenticate(
|
||||||
|
&self,
|
||||||
|
discord_id_hash: &str,
|
||||||
|
) -> Result<Authentication, reqwest::Error> {
|
||||||
|
let route = Endpoint::Authenticate {
|
||||||
|
id: &self.client_id,
|
||||||
|
secret: &self.client_secret,
|
||||||
|
discord_id_hash,
|
||||||
|
};
|
||||||
|
self
|
||||||
|
.fire(&RequestInfo {
|
||||||
|
headers: None,
|
||||||
|
route,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
3
src/api/mod.rs
Normal file
3
src/api/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub mod client;
|
||||||
|
pub mod model;
|
||||||
|
mod routing;
|
6
src/api/model/auth.rs
Normal file
6
src/api/model/auth.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Authentication {
|
||||||
|
pub access_token: String,
|
||||||
|
}
|
1
src/api/model/mod.rs
Normal file
1
src/api/model/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod auth;
|
28
src/api/routing.rs
Normal file
28
src/api/routing.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
use reqwest::{Body, Method, Request};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum Endpoint<'a> {
|
||||||
|
Authenticate {
|
||||||
|
id: &'a str,
|
||||||
|
secret: &'a str,
|
||||||
|
discord_id_hash: &'a str,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! route {
|
||||||
|
($self:ident, $server:ident, $endpoint:literal, $method:ident) => {{
|
||||||
|
let mut req = Request::new(Method::$method, $server.join($endpoint).unwrap());
|
||||||
|
*req.body_mut() = Some(Body::from(serde_json::to_vec($self).unwrap()));
|
||||||
|
req
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Endpoint<'_> {
|
||||||
|
pub fn to_request(&self, server: &reqwest::Url) -> Request {
|
||||||
|
match self {
|
||||||
|
Self::Authenticate { .. } => route!(self, server, "/auth/", POST),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
use poise::serenity_prelude::{self as serenity, MessageId};
|
use chrono::Utc;
|
||||||
|
use poise::serenity_prelude::{self as serenity, MessageId, ReactionType};
|
||||||
use poise::ReplyHandle;
|
use poise::ReplyHandle;
|
||||||
|
|
||||||
use crate::{Context, Error};
|
use crate::{Context, Error};
|
||||||
@ -10,6 +11,13 @@ pub async fn reply(
|
|||||||
#[description = "The message id to reply to"] reply_message: Option<String>,
|
#[description = "The message id to reply to"] reply_message: Option<String>,
|
||||||
#[description = "The message to send"] message: String,
|
#[description = "The message to send"] message: String,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
async fn send_ephermal<'a>(
|
||||||
|
ctx: &Context<'a>,
|
||||||
|
content: &str,
|
||||||
|
) -> Result<ReplyHandle<'a>, serenity::Error> {
|
||||||
|
ctx.send(|f| f.ephemeral(true).content(content)).await
|
||||||
|
}
|
||||||
|
|
||||||
let http = &ctx.discord().http;
|
let http = &ctx.discord().http;
|
||||||
let channel = &ctx.channel_id();
|
let channel = &ctx.channel_id();
|
||||||
|
|
||||||
@ -38,9 +46,47 @@ pub async fn reply(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_ephermal<'a>(
|
/// Start a poll.
|
||||||
ctx: &Context<'a>,
|
#[poise::command(slash_command)]
|
||||||
content: &str,
|
pub async fn poll(
|
||||||
) -> Result<ReplyHandle<'a>, serenity::Error> {
|
ctx: Context<'_>,
|
||||||
ctx.send(|f| f.ephemeral(true).content(content)).await
|
#[description = "The id of the poll"] id: u64,
|
||||||
|
#[description = "The poll message"] message: String,
|
||||||
|
#[description = "The poll title"] title: String,
|
||||||
|
#[description = "The minumum server age in days to allow members to poll"] age: u16,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let data = ctx.data().read().await;
|
||||||
|
let configuration = &data.configuration;
|
||||||
|
let embed_color = configuration.general.embed_color;
|
||||||
|
|
||||||
|
ctx.send(|m| {
|
||||||
|
m.embed(|e| {
|
||||||
|
let guild = &ctx.guild().unwrap();
|
||||||
|
if let Some(url) = guild.icon_url() {
|
||||||
|
e.thumbnail(url.clone()).footer(|f| {
|
||||||
|
f.icon_url(url).text(format!(
|
||||||
|
"{} • {}",
|
||||||
|
guild.name,
|
||||||
|
Utc::today().format("%Y/%m/%d")
|
||||||
|
))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
e
|
||||||
|
}
|
||||||
|
.title(title)
|
||||||
|
.description(message)
|
||||||
|
.color(embed_color)
|
||||||
|
})
|
||||||
|
.components(|c| {
|
||||||
|
c.create_action_row(|r| {
|
||||||
|
r.create_button(|b| {
|
||||||
|
b.label("Vote")
|
||||||
|
.emoji(ReactionType::Unicode("🗳️".to_string()))
|
||||||
|
.custom_id(format!("poll:{}:{}", id, age))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use bson::Document;
|
use bson::Document;
|
||||||
use poise::serenity_prelude::{PermissionOverwrite};
|
use poise::serenity_prelude::PermissionOverwrite;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with_macros::skip_serializing_none;
|
use serde_with_macros::skip_serializing_none;
|
||||||
|
|
||||||
@ -23,6 +23,21 @@ pub struct LockedChannel {
|
|||||||
pub overwrites: Option<Vec<PermissionOverwrite>>,
|
pub overwrites: Option<Vec<PermissionOverwrite>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[skip_serializing_none]
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
|
pub struct Poll {
|
||||||
|
pub author: Option<PollAuthor>,
|
||||||
|
pub image_url: Option<String>,
|
||||||
|
pub votes: Option<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[skip_serializing_none]
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
|
pub struct PollAuthor {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub id: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Muted> for Document {
|
impl From<Muted> for Document {
|
||||||
fn from(muted: Muted) -> Self {
|
fn from(muted: Muted) -> Self {
|
||||||
to_document(&muted)
|
to_document(&muted)
|
||||||
|
53
src/events/interaction.rs
Normal file
53
src/events/interaction.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
use chrono::{Duration, Utc};
|
||||||
|
use poise::serenity_prelude::{
|
||||||
|
ComponentType,
|
||||||
|
MessageComponentInteraction,
|
||||||
|
MessageComponentInteractionData,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::utils;
|
||||||
|
pub async fn interaction_create(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
interaction: &serenity::Interaction,
|
||||||
|
) -> Result<(), crate::serenity::SerenityError> {
|
||||||
|
if let serenity::Interaction::MessageComponent(MessageComponentInteraction {
|
||||||
|
data:
|
||||||
|
MessageComponentInteractionData {
|
||||||
|
component_type: ComponentType::Button,
|
||||||
|
custom_id,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
}) = interaction
|
||||||
|
{
|
||||||
|
if custom_id.starts_with("poll") {
|
||||||
|
handle_poll(ctx, interaction, custom_id).await?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn handle_poll(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
interaction: &serenity::Interaction,
|
||||||
|
custom_id: &str,
|
||||||
|
) -> Result<(), crate::serenity::SerenityError> {
|
||||||
|
fn parse<T>(str: &str) -> T
|
||||||
|
where
|
||||||
|
<T as std::str::FromStr>::Err: std::fmt::Debug,
|
||||||
|
T: std::str::FromStr,
|
||||||
|
{
|
||||||
|
str.parse::<T>().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
let poll: Vec<_> = custom_id.split(':').collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let poll_id = parse::<u64>(poll[1]);
|
||||||
|
let min_age = parse::<i64>(poll[2]);
|
||||||
|
|
||||||
|
let min_join_date = serenity::Timestamp::from(Utc::now() - Duration::days(min_age));
|
||||||
|
|
||||||
|
utils::poll::handle_poll(ctx, interaction, poll_id, min_join_date).await
|
||||||
|
}
|
@ -1,11 +1,13 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use poise::serenity_prelude::{self as serenity, Mutex, RwLock, ShardManager, UserId};
|
use poise::serenity_prelude::{self as serenity, Mutex, RwLock, ShardManager, UserId};
|
||||||
|
use tracing::log::error;
|
||||||
|
|
||||||
use crate::{Data, Error};
|
use crate::{Data, Error};
|
||||||
|
|
||||||
mod guild_member_addition;
|
mod guild_member_addition;
|
||||||
mod guild_member_update;
|
mod guild_member_update;
|
||||||
|
mod interaction;
|
||||||
mod message_create;
|
mod message_create;
|
||||||
mod ready;
|
mod ready;
|
||||||
mod thread_create;
|
mod thread_create;
|
||||||
@ -46,10 +48,21 @@ impl<T: Send + Sync> Handler<T> {
|
|||||||
// Manually dispatch events from serenity to poise
|
// Manually dispatch events from serenity to poise
|
||||||
#[serenity::async_trait]
|
#[serenity::async_trait]
|
||||||
impl serenity::EventHandler for Handler<Arc<RwLock<Data>>> {
|
impl serenity::EventHandler for Handler<Arc<RwLock<Data>>> {
|
||||||
async fn ready(&self, ctx: serenity::Context, ready: serenity::Ready) {
|
async fn guild_member_addition(
|
||||||
*self.bot_id.write().await = Some(ready.user.id);
|
&self,
|
||||||
|
ctx: serenity::Context,
|
||||||
|
mut new_member: serenity::Member,
|
||||||
|
) {
|
||||||
|
guild_member_addition::guild_member_addition(&ctx, &mut new_member).await;
|
||||||
|
}
|
||||||
|
|
||||||
ready::load_muted_members(&ctx, &ready).await;
|
async fn guild_member_update(
|
||||||
|
&self,
|
||||||
|
ctx: serenity::Context,
|
||||||
|
old_if_available: Option<serenity::Member>,
|
||||||
|
new: serenity::Member,
|
||||||
|
) {
|
||||||
|
guild_member_update::guild_member_update(&ctx, &old_if_available, &new).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn message(&self, ctx: serenity::Context, new_message: serenity::Message) {
|
async fn message(&self, ctx: serenity::Context, new_message: serenity::Message) {
|
||||||
@ -61,13 +74,6 @@ impl serenity::EventHandler for Handler<Arc<RwLock<Data>>> {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn interaction_create(&self, ctx: serenity::Context, interaction: serenity::Interaction) {
|
|
||||||
self.dispatch_poise_event(&ctx, &poise::Event::InteractionCreate {
|
|
||||||
interaction,
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn message_update(
|
async fn message_update(
|
||||||
&self,
|
&self,
|
||||||
ctx: serenity::Context,
|
ctx: serenity::Context,
|
||||||
@ -83,20 +89,24 @@ impl serenity::EventHandler for Handler<Arc<RwLock<Data>>> {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn ready(&self, ctx: serenity::Context, ready: serenity::Ready) {
|
||||||
|
*self.bot_id.write().await = Some(ready.user.id);
|
||||||
|
|
||||||
|
ready::load_muted_members(&ctx, &ready).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn interaction_create(&self, ctx: serenity::Context, interaction: serenity::Interaction) {
|
||||||
|
if let Err(e) = interaction::interaction_create(&ctx, &interaction).await {
|
||||||
|
error!("Failed to handle interaction: {:?}.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dispatch_poise_event(&ctx, &poise::Event::InteractionCreate {
|
||||||
|
interaction,
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
async fn thread_create(&self, ctx: serenity::Context, thread: serenity::GuildChannel) {
|
async fn thread_create(&self, ctx: serenity::Context, thread: serenity::GuildChannel) {
|
||||||
thread_create::thread_create(&ctx, &thread).await;
|
thread_create::thread_create(&ctx, &thread).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn guild_member_addition(&self, ctx: serenity::Context, mut new_member: serenity::Member) {
|
|
||||||
guild_member_addition::guild_member_addition(&ctx, &mut new_member).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn guild_member_update(
|
|
||||||
&self,
|
|
||||||
ctx: serenity::Context,
|
|
||||||
old_if_available: Option<serenity::Member>,
|
|
||||||
new: serenity::Member,
|
|
||||||
) {
|
|
||||||
guild_member_update::guild_member_update(&ctx, &old_if_available, &new).await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
12
src/main.rs
12
src/main.rs
@ -2,6 +2,7 @@ use std::collections::HashMap;
|
|||||||
use std::env;
|
use std::env;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use api::client::Api;
|
||||||
use commands::{configuration, misc, moderation};
|
use commands::{configuration, misc, moderation};
|
||||||
use db::database::Database;
|
use db::database::Database;
|
||||||
use events::Handler;
|
use events::Handler;
|
||||||
@ -12,6 +13,7 @@ use utils::bot::load_configuration;
|
|||||||
|
|
||||||
use crate::model::application::Configuration;
|
use crate::model::application::Configuration;
|
||||||
|
|
||||||
|
mod api;
|
||||||
mod commands;
|
mod commands;
|
||||||
mod db;
|
mod db;
|
||||||
mod events;
|
mod events;
|
||||||
@ -30,6 +32,7 @@ pub struct Data {
|
|||||||
configuration: Configuration,
|
configuration: Configuration,
|
||||||
database: Arc<Database>,
|
database: Arc<Database>,
|
||||||
pending_unmutes: HashMap<u64, JoinHandle<Option<Error>>>,
|
pending_unmutes: HashMap<u64, JoinHandle<Option<Error>>>,
|
||||||
|
api: Api,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@ -53,6 +56,7 @@ async fn main() {
|
|||||||
moderation::lock(),
|
moderation::lock(),
|
||||||
moderation::unlock(),
|
moderation::unlock(),
|
||||||
misc::reply(),
|
misc::reply(),
|
||||||
|
misc::poll(),
|
||||||
];
|
];
|
||||||
poise::set_qualified_names(&mut commands);
|
poise::set_qualified_names(&mut commands);
|
||||||
|
|
||||||
@ -79,6 +83,14 @@ async fn main() {
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
),
|
),
|
||||||
pending_unmutes: HashMap::new(),
|
pending_unmutes: HashMap::new(),
|
||||||
|
api: Api::new(
|
||||||
|
reqwest::Url::parse(
|
||||||
|
&env::var("API_SERVER").expect("API_SERVER environment variable not set"),
|
||||||
|
)
|
||||||
|
.expect("Invalid API_SERVER"),
|
||||||
|
env::var("API_CLIENT_ID").expect("API_CLIENT_ID environment variable not set"),
|
||||||
|
env::var("API_CLIENT_SECRET").expect("API_CLIENT_SECRET environment variable not set"),
|
||||||
|
),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let handler = Arc::new(Handler::new(
|
let handler = Arc::new(Handler::new(
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use poise::serenity_prelude::{self as serenity, Member, RoleId};
|
use poise::serenity_prelude::{self as serenity, Member, RoleId};
|
||||||
|
|
||||||
|
pub mod autorespond;
|
||||||
pub mod bot;
|
pub mod bot;
|
||||||
pub mod decancer;
|
pub mod decancer;
|
||||||
pub mod embed;
|
pub mod embed;
|
||||||
|
pub mod media_channel;
|
||||||
pub mod moderation;
|
pub mod moderation;
|
||||||
pub mod autorespond;
|
pub mod poll;
|
||||||
pub mod media_channel;
|
|
||||||
|
78
src/utils/poll.rs
Normal file
78
src/utils/poll.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use poise::serenity_prelude::{ButtonStyle, ReactionType, Timestamp};
|
||||||
|
|
||||||
|
use tracing::log::{error, info, trace};
|
||||||
|
|
||||||
|
use super::bot::get_data_lock;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub async fn handle_poll(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
interaction: &serenity::Interaction,
|
||||||
|
poll_id: u64,
|
||||||
|
min_join_date: Timestamp,
|
||||||
|
) -> Result<(), crate::serenity::SerenityError> {
|
||||||
|
trace!("Handling poll: {}.", poll_id);
|
||||||
|
|
||||||
|
let data = get_data_lock(ctx).await;
|
||||||
|
let data = data.read().await;
|
||||||
|
|
||||||
|
let component = &interaction.clone().message_component().unwrap();
|
||||||
|
|
||||||
|
let member = component.member.as_ref().unwrap();
|
||||||
|
|
||||||
|
let eligible = member.joined_at.unwrap() <= min_join_date;
|
||||||
|
let auth_token = if eligible {
|
||||||
|
let result = data
|
||||||
|
.api
|
||||||
|
.authenticate(&member.user.id.to_string())
|
||||||
|
.await
|
||||||
|
.map(|auth| auth.access_token);
|
||||||
|
|
||||||
|
if let Err(ref e) = result {
|
||||||
|
error!("API Request error: {}", e)
|
||||||
|
}
|
||||||
|
result.ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
component
|
||||||
|
.create_interaction_response(&ctx.http, |r| {
|
||||||
|
r.interaction_response_data(|m| {
|
||||||
|
if let Some(token) = auth_token.as_deref() {
|
||||||
|
let url = format!("https://revanced.app/polling#{}", token);
|
||||||
|
m.components(|c| {
|
||||||
|
c.create_action_row(|r| {
|
||||||
|
r.create_button(|b| {
|
||||||
|
b.label("Vote")
|
||||||
|
.emoji(ReactionType::Unicode("🗳️".to_string()))
|
||||||
|
.style(ButtonStyle::Link)
|
||||||
|
.url(&url)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
m
|
||||||
|
}
|
||||||
|
.ephemeral(true)
|
||||||
|
.embed(|e| {
|
||||||
|
if auth_token.is_some() {
|
||||||
|
e.title("Cast your vote")
|
||||||
|
.description("You can now vote on the poll.")
|
||||||
|
} else if !eligible {
|
||||||
|
info!("Member {} failed to vote.", member.display_name());
|
||||||
|
e.title("You can not vote")
|
||||||
|
.description("You are not eligible to vote on this poll.")
|
||||||
|
} else {
|
||||||
|
e.title("Error")
|
||||||
|
.description("An error has occured. Please try again later.")
|
||||||
|
}
|
||||||
|
.color(data.configuration.general.embed_color)
|
||||||
|
.thumbnail(member.user.face())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user