Skip to content

Commit 89b3eb8

Browse files
committed
feat: verify ui
1 parent ef4f0d9 commit 89b3eb8

File tree

14 files changed

+256
-128
lines changed

14 files changed

+256
-128
lines changed

api/src/dynamo.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ pub fn _as_i32(val: Option<&AttributeValue>, default: i32) -> i32 {
3030
default
3131
}
3232

33+
pub fn as_u32(val: Option<&AttributeValue>, default: u32) -> u32 {
34+
if let Some(v) = val {
35+
if let Ok(n) = v.as_n() {
36+
if let Ok(n) = n.parse::<u32>() {
37+
return n;
38+
}
39+
}
40+
}
41+
default
42+
}
43+
3344
pub fn as_u64(val: Option<&AttributeValue>, default: u64) -> u64 {
3445
if let Some(v) = val {
3546
if let Ok(n) = v.as_n() {
@@ -50,13 +61,6 @@ pub fn as_string_vec(val: Option<&AttributeValue>) -> Vec<String> {
5061
.collect();
5162
}
5263
}
53-
// val
54-
// .map(|v| v.as_l())
55-
// .unwrap_or_else(|| Ok(&Vec::<AttributeValue>::new()))
56-
// .unwrap_or_else(|_| &Vec::<AttributeValue>::new())
57-
// .iter()
58-
// .map(|v| as_string(Some(v), &"".to_string()))
59-
// .collect();
6064
vec![]
6165
}
6266

api/src/guilds/mod.rs

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use std::collections::HashMap;
1+
use std::collections::{HashSet};
22
use crate::AppState;
3-
use crate::guilds::models::{Guild, Verify};
3+
use crate::guilds::models::{Guild, VerifyRole};
44
use axum::extract::State;
55
use axum::{Extension, Json, Router, extract::Path, routing::get};
66
use http::StatusCode;
@@ -14,6 +14,7 @@ use twilight_model::guild::Permissions;
1414
use twilight_model::id::Id;
1515
use twilight_model::id::marker::GuildMarker;
1616
use twilight_model::user::CurrentUser;
17+
use crate::guilds::tasks::{add_role_to_guild, remove_role_from_guild};
1718
use crate::utils::{admin_guilds, is_client_admin_guild, ise};
1819

1920
pub mod models;
@@ -66,14 +67,14 @@ async fn post_guilds(
6667
..Default::default()
6768
})
6869
}
69-
70-
for i in 0..guilds.len() {
71-
let guild_dsc = app_state.discord_bot.guild(guilds[i].guild_id).await.map_err(ise)?.model().await.map_err(ise)?;
72-
guilds[i].name = guild_dsc.name;
73-
guilds[i].icon = guild_dsc.icon;
74-
guilds[i].save(&app_state.dynamo).await;
70+
71+
for guild in &mut guilds {
72+
let guild_dsc = app_state.discord_bot.guild(guild.guild_id).await.map_err(ise)?.model().await.map_err(ise)?;
73+
guild.name = guild_dsc.name;
74+
guild.icon = guild_dsc.icon;
75+
guild.save(&app_state.dynamo).await;
7576
}
76-
77+
7778
Ok(Json(json!(guilds)))
7879
}
7980

@@ -134,10 +135,7 @@ async fn get_guilds_id(
134135
let guild = app_state.discord_bot.guild(guild_id).await.unwrap().model().await.unwrap();
135136
let new_guild = Guild {
136137
guild_id,
137-
verify: Verify { roles: vec![], user_links: vec![] },
138-
name: guild.name,
139-
icon: guild.icon,
140-
user_links: HashMap::new()
138+
..Default::default()
141139
};
142140
new_guild.save(&app_state.dynamo).await;
143141
new_guild
@@ -147,15 +145,33 @@ async fn get_guilds_id(
147145

148146
async fn put_guilds_id(
149147
Path(guild_id): Path<Id<GuildMarker>>,
150-
Extension(discord_user): Extension<Arc<twilight_http::Client>>,
148+
Extension(current_user): Extension<CurrentUser>,
151149
State(app_state): State<AppState>,
152-
Json(guild_req): Json<models::Guild>,
150+
Json(guild_req): Json<Guild>,
153151
) -> Result<Json<Value>, StatusCode> {
154-
if !utils::is_intersect_admin_guild(guild_id, &discord_user, &app_state.discord_bot).await.map_err(crate::utils::ise)? {
155-
warn!("User is not an admin in guild {}", guild_id);
156-
return Err(StatusCode::NOT_FOUND);
152+
if !is_client_admin_guild(guild_id, &current_user, &app_state.discord_bot).await? {
153+
return Err(StatusCode::UNAUTHORIZED);
154+
}
155+
156+
let mut old_guild = match Guild::from_db(guild_id, &app_state.dynamo).await {
157+
Some(g) => g,
158+
None => return Err(StatusCode::NOT_FOUND),
159+
};
160+
161+
let old_set: HashSet<VerifyRole> = HashSet::from_iter(old_guild.verify.roles.clone());
162+
let new_set: HashSet<VerifyRole> = HashSet::from_iter(guild_req.verify.roles);
163+
164+
let add_roles: HashSet<VerifyRole> = new_set.difference(&old_set).cloned().collect();
165+
let remove_roles: HashSet<VerifyRole> = old_set.difference(&new_set).cloned().collect();
166+
167+
for role in add_roles {
168+
add_role_to_guild(&mut old_guild, role, &app_state.discord_bot).await;
169+
}
170+
171+
for role in remove_roles {
172+
remove_role_from_guild(&mut old_guild, role.role_id, &app_state.discord_bot).await;
157173
}
158174

159-
guild_req.save(&app_state.dynamo).await;
160-
Ok(Json(json!(guild_req)))
175+
old_guild.save(&app_state.dynamo).await;
176+
Ok(Json(json!(old_guild)))
161177
}

api/src/guilds/models.rs

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,30 @@
1-
use crate::dynamo::{as_map, as_map_vec, as_string, as_string_opt};
1+
use crate::dynamo::{as_u32, as_map, as_map_vec, as_string, as_string_opt};
22
use aws_sdk_dynamodb::types::{AttributeValue, KeysAndAttributes};
33
use http::StatusCode;
44
use lambda_http::tracing::error;
55
use serde::{Deserialize, Serialize};
66
use std::collections::HashMap;
7+
use std::hash::Hash;
78
use twilight_model::id::Id;
89
use twilight_model::id::marker::{GuildMarker, RoleMarker, UserMarker};
910
use twilight_model::util::ImageHash;
1011
use crate::users::models::Link;
1112

12-
#[derive(Clone, Serialize, Deserialize, PartialEq)]
13+
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
1314
pub struct VerifyRole {
1415
pub role_id: Id<RoleMarker>,
1516
pub role_name: Option<String>,
1617
pub pattern: String,
1718
pub members: u32,
1819
}
1920

21+
impl Hash for VerifyRole {
22+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
23+
self.role_id.hash(state);
24+
self.pattern.hash(state);
25+
}
26+
}
27+
2028
impl From<&HashMap<String, AttributeValue>> for VerifyRole {
2129
fn from(item: &HashMap<String, AttributeValue>) -> Self {
2230
VerifyRole {
@@ -27,9 +35,7 @@ impl From<&HashMap<String, AttributeValue>> for VerifyRole {
2735
),
2836
role_name: as_string_opt(item.get("role_name")),
2937
pattern: as_string(item.get("pattern"), &"".to_string()),
30-
members: as_string(item.get("members"), &"0".to_string())
31-
.parse::<u32>()
32-
.unwrap_or(0),
38+
members: as_u32(item.get("members"), 0),
3339
}
3440
}
3541
}
@@ -78,20 +84,20 @@ impl From<UserLink> for HashMap<String, AttributeValue> {
7884
#[derive(Clone, Serialize, Deserialize, PartialEq)]
7985
pub struct Verify {
8086
pub roles: Vec<VerifyRole>,
81-
pub user_links: Vec<UserLink>
87+
pub user_links: HashMap<Id<UserMarker>, Vec<Link>>,
8288
}
8389

8490
impl From<&HashMap<String, AttributeValue>> for Verify {
8591
fn from(item: &HashMap<String, AttributeValue>) -> Self {
86-
let roles = as_map_vec(item.get("roles"))
92+
Verify {
93+
roles: as_map_vec(item.get("roles"))
8794
.into_iter()
8895
.map(|m| m.into())
89-
.collect();
90-
let user_links = as_map_vec(item.get("user_links"))
91-
.into_iter()
92-
.map(|m| m.into())
93-
.collect();
94-
Verify { roles, user_links }
96+
.collect(),
97+
user_links: as_map(item.get("user_links")).unwrap_or(&HashMap::new()).iter().map(
98+
|(k,v)| (Id::new(k.parse::<u64>().unwrap_or(0)), as_map_vec(Some(v)).iter().map(|&l| l.into()).collect()))
99+
.collect()
100+
}
95101
}
96102
}
97103

@@ -100,8 +106,11 @@ impl From<Verify> for HashMap<String, AttributeValue> {
100106
let mut verify_map = HashMap::new();
101107
let roles: Vec<AttributeValue> = verify.roles.into_iter().map(|r| AttributeValue::M(r.into())).collect();
102108
verify_map.insert("roles".to_string(), AttributeValue::L(roles));
103-
let user_links: Vec<AttributeValue> = verify.user_links.into_iter().map(|ul| AttributeValue::M(ul.into())).collect();
104-
verify_map.insert("user_links".to_string(), AttributeValue::L(user_links));
109+
110+
verify_map.insert("user_links".to_string(), AttributeValue::M(verify.user_links.iter().map(
111+
|(k,v)| (k.to_string(), AttributeValue::L(v.iter().map(|l| AttributeValue::M(l.clone().into())).collect()))
112+
).collect()));
113+
105114
verify_map
106115
}
107116
}
@@ -112,17 +121,15 @@ pub struct Guild {
112121
pub verify: Verify,
113122
pub name: String,
114123
pub icon: Option<ImageHash>,
115-
pub user_links: HashMap<Id<UserMarker>, Vec<Link>>,
116124
}
117125

118126
impl Default for Guild {
119127
fn default() -> Self {
120128
Guild {
121129
guild_id: Id::new(1),
122-
verify: Verify { roles: vec![], user_links: vec![] },
130+
verify: Verify { roles: vec![], user_links: HashMap::new() },
123131
name: "".to_string(),
124132
icon: None,
125-
user_links: HashMap::new(),
126133
}
127134
}
128135
}
@@ -137,9 +144,6 @@ impl From<&HashMap<String, AttributeValue>> for Guild {
137144
verify: as_map(item.get("verify")).unwrap().into(),
138145
name: as_string(item.get("name"), &"".to_string()),
139146
icon: as_string_opt(item.get("icon")).and_then(|s| ImageHash::parse(s.as_bytes()).ok()),
140-
user_links: as_map(item.get("user_links")).unwrap_or(&HashMap::new()).iter().map(
141-
|(k,v)| (Id::new(k.parse::<u64>().unwrap_or(0)), as_map_vec(Some(v)).iter().map(|&l| l.into()).collect()))
142-
.collect(),
143147
}
144148
}
145149
}

api/src/guilds/tasks.rs

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use std::collections::HashMap;
2-
use crate::guilds::models::Guild;
3-
use crate::utils::retry_on_rl;
1+
use http::StatusCode;
2+
use crate::guilds::models::{Guild, VerifyRole};
3+
use crate::utils::{ise, retry_on_rl};
44
use lambda_http::tracing::info;
55
use regex::Regex;
66
use twilight_model::id::Id;
@@ -37,10 +37,9 @@ pub async fn update_guilds(bot: &twilight_http::Client, dynamo: &aws_sdk_dynamod
3737
if !found {
3838
let new_guild = Guild {
3939
guild_id: d_guild.id,
40-
verify: crate::guilds::models::Verify { roles: vec![], user_links: vec![] },
4140
name: d_guild.name.clone(),
4241
icon: d_guild.icon,
43-
user_links: HashMap::new(),
42+
..Default::default()
4443
};
4544
new_guild.save(dynamo).await;
4645
}
@@ -68,57 +67,69 @@ async fn remove_role_user(
6867
.await;
6968
}
7069

71-
pub async fn assign_roles_guild_role(
72-
guild_id: Id<GuildMarker>,
70+
pub async fn remove_role_from_guild(
71+
guild: &mut Guild,
7372
role_id: Id<RoleMarker>,
7473
bot: &twilight_http::Client,
75-
dynamo: &aws_sdk_dynamodb::Client,
7674
) {
77-
let guild = Guild::from_db(guild_id, dynamo).await.unwrap();
78-
let user_links = &guild.verify.user_links;
79-
let roles = &guild.verify.roles;
80-
let role = roles.iter().find(|r| r.role_id == role_id);
81-
if role.is_none() {
75+
if !guild.verify.roles.iter().any(|r| r.role_id == role_id) {
8276
return;
8377
}
84-
for user_link in user_links {
85-
if Regex::new(&role.unwrap().pattern)
86-
.unwrap()
87-
.is_match(&user_link.link_address)
88-
{
89-
assign_role_user(guild_id, user_link.user_id, role.unwrap().role_id, bot).await;
78+
for (&user_id, user_links) in &mut guild.verify.user_links {
79+
remove_role_user(guild.guild_id, user_id, role_id, bot).await;
80+
}
81+
guild.verify.roles.retain(|r| r.role_id != role_id);
82+
}
83+
84+
pub async fn add_role_to_guild(
85+
guild: &mut Guild,
86+
mut role: VerifyRole,
87+
bot: &twilight_http::Client
88+
){
89+
remove_role_from_guild(guild, role.role_id, bot).await;
90+
for (&user_id, user_links) in &mut guild.verify.user_links {
91+
for user_link in user_links {
92+
if Regex::new(&role.pattern)
93+
.unwrap()
94+
.is_match(&*user_link.link_address)
95+
{
96+
assign_role_user(guild.guild_id, user_id, role.role_id, bot).await;
97+
role.members += 1;
98+
break;
99+
}
90100
}
91101
}
102+
guild.verify.roles.push(role);
92103
}
93104

94105
pub async fn assign_roles_guild_user_link(
106+
enabled: bool,
95107
link_address: &str,
96108
user_id: Id<UserMarker>,
97-
guild: &Guild,
109+
guild: &mut Guild,
98110
bot: &twilight_http::Client,
99-
) {
100-
let roles = &guild.verify.roles;
101-
for role in roles {
102-
if Regex::new(&role.pattern)
111+
) -> Result<(), StatusCode> {
112+
let roles = &mut guild.verify.roles;
113+
for i in 0..roles.len() {
114+
let role = roles.get_mut(i).unwrap();
115+
let has_dsc_role = retry_on_rl(|| async { bot.guild_member(guild.guild_id, user_id).await}).await.map_err(ise)?.model().await.map_err(ise)?.roles.contains(&role.role_id);
116+
if enabled && Regex::new(&role.pattern)
103117
.unwrap()
104118
.is_match(link_address)
105119
{
106-
assign_role_user(guild.guild_id, user_id, role.role_id, bot).await;
107-
} else {
108-
remove_role_user(guild.guild_id, user_id, role.role_id, bot).await;
120+
info!("Assigning role {} to user {}", role.role_id, user_id);
121+
if !has_dsc_role {
122+
assign_role_user(guild.guild_id, user_id, role.role_id, bot).await;
123+
role.members += 1;
124+
}
125+
} else {
126+
info!("Removing role {} from user {}", role.role_id, user_id);
127+
if has_dsc_role {
128+
remove_role_user(guild.guild_id, user_id, role.role_id, bot).await;
129+
role.members -= 1;
130+
}
109131
}
110132
}
133+
Ok(())
111134
}
112135

113-
pub async fn assign_all_roles_user_guild(
114-
user_id: Id<UserMarker>,
115-
guild: &Guild,
116-
bot: &twilight_http::Client,
117-
) {
118-
let user_links = &guild.verify.user_links;
119-
for user_link in user_links {
120-
if user_link.user_id == user_id {
121-
assign_roles_guild_user_link(&user_link.link_address, user_id, guild, bot).await;
122-
}
123-
}
124-
}

api/src/users/links.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,17 +86,15 @@ async fn post_link(
8686
.links
8787
.retain(|l| l.link_address != new_link.link_address);
8888
for link_guild in &user_model.link_guilds {
89-
if !link_guild.enabled {
90-
continue;
91-
}
92-
let guild = Guild::from_db(link_guild.guild_id, &app_state.dynamo).await.unwrap();
89+
let mut guild = Guild::from_db(link_guild.guild_id, &app_state.dynamo).await.unwrap();
9390
crate::guilds::tasks::assign_roles_guild_user_link(
91+
link_guild.enabled,
9492
&new_link.link_address,
9593
user_id,
96-
&guild,
94+
&mut guild,
9795
&app_state.discord_bot,
9896
)
99-
.await;
97+
.await?;
10098
}
10199
user_model.links.push(new_link);
102100
user_model.save(&app_state.dynamo).await;

0 commit comments

Comments
 (0)