Merge branch 'generate-device-name' into staging
Some checks failed
ci/woodpecker/push/build-and-publish Pipeline failed

This commit is contained in:
Radical 2025-07-20 16:28:02 +02:00
commit 969b517e18
11 changed files with 1034 additions and 18 deletions

View file

@ -62,4 +62,6 @@ random-string = "1.1"
lettre = { version = "0.11", features = ["tokio1", "tokio1-native-tls"] } lettre = { version = "0.11", features = ["tokio1", "tokio1-native-tls"] }
chrono = { version = "0.4.41", features = ["serde"] } chrono = { version = "0.4.41", features = ["serde"] }
tracing-subscriber = "0.3.19" tracing-subscriber = "0.3.19"
rand = "0.9.1"

View file

@ -0,0 +1,2 @@
-- This file should undo anything in `up.sql`
ALTER TABLE refresh_tokens ALTER COLUMN device_name TYPE varchar(16);

View file

@ -0,0 +1,2 @@
-- Your SQL goes here
ALTER TABLE refresh_tokens ALTER COLUMN device_name TYPE varchar(64);

View file

@ -21,7 +21,7 @@ use crate::{
schema::*, schema::*,
utils::{ utils::{
PASSWORD_REGEX, generate_token, new_refresh_token_cookie, PASSWORD_REGEX, generate_token, new_refresh_token_cookie,
user_uuid_from_identifier, user_uuid_from_identifier, generate_device_name
}, },
}; };
@ -29,7 +29,6 @@ use crate::{
pub struct LoginInformation { pub struct LoginInformation {
username: String, username: String,
password: String, password: String,
device_name: String,
} }
pub async fn response( pub async fn response(
@ -72,12 +71,14 @@ pub async fn response(
use refresh_tokens::dsl as rdsl; use refresh_tokens::dsl as rdsl;
let device_name = generate_device_name();
insert_into(refresh_tokens::table) insert_into(refresh_tokens::table)
.values(( .values((
rdsl::token.eq(&refresh_token), rdsl::token.eq(&refresh_token),
rdsl::uuid.eq(uuid), rdsl::uuid.eq(uuid),
rdsl::created_at.eq(current_time), rdsl::created_at.eq(current_time),
rdsl::device_name.eq(&login_information.device_name), rdsl::device_name.eq(&device_name),
)) ))
.execute(&mut conn) .execute(&mut conn)
.await?; .await?;
@ -94,7 +95,7 @@ pub async fn response(
.execute(&mut conn) .execute(&mut conn)
.await?; .await?;
let mut response = (StatusCode::OK, Json(Response { access_token })).into_response(); let mut response = (StatusCode::OK, Json(Response { access_token, device_name })).into_response();
response.headers_mut().append( response.headers_mut().append(
"Set-Cookie", "Set-Cookie",

View file

@ -27,6 +27,7 @@ mod verify_email;
#[derive(Serialize)] #[derive(Serialize)]
pub struct Response { pub struct Response {
access_token: String, access_token: String,
device_name: String,
} }

View file

@ -19,8 +19,7 @@ use crate::{
schema::{ schema::{
access_tokens::{self, dsl}, access_tokens::{self, dsl},
refresh_tokens::{self, dsl as rdsl}, refresh_tokens::{self, dsl as rdsl},
}, }, utils::{generate_token, new_refresh_token_cookie}
utils::{generate_token, new_refresh_token_cookie},
}; };
pub async fn post( pub async fn post(
@ -69,6 +68,7 @@ pub async fn post(
} }
let current_time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() as i64; let current_time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() as i64;
let mut device_name: String = String::new();
if lifetime > 1987200 { if lifetime > 1987200 {
let new_refresh_token = generate_token::<32>()?; let new_refresh_token = generate_token::<32>()?;
@ -79,11 +79,13 @@ pub async fn post(
rdsl::token.eq(&new_refresh_token), rdsl::token.eq(&new_refresh_token),
rdsl::created_at.eq(current_time), rdsl::created_at.eq(current_time),
)) ))
.execute(&mut conn) .returning(rdsl::device_name)
.get_result::<String>(&mut conn)
.await .await
{ {
Ok(_) => { Ok(existing_device_name) => {
refresh_token = new_refresh_token; refresh_token = new_refresh_token;
device_name = existing_device_name;
} }
Err(error) => { Err(error) => {
error!("{error}"); error!("{error}");
@ -102,7 +104,7 @@ pub async fn post(
.execute(&mut conn) .execute(&mut conn)
.await?; .await?;
let mut response = (StatusCode::OK, Json(Response { access_token })).into_response(); let mut response = (StatusCode::OK, Json(Response { access_token, device_name })).into_response();
// TODO: Dont set this when refresh token is unchanged // TODO: Dont set this when refresh token is unchanged
response.headers_mut().append( response.headers_mut().append(

View file

@ -30,7 +30,7 @@ use crate::{
}, },
utils::{ utils::{
EMAIL_REGEX, PASSWORD_REGEX, USERNAME_REGEX, generate_token, EMAIL_REGEX, PASSWORD_REGEX, USERNAME_REGEX, generate_token,
new_refresh_token_cookie, new_refresh_token_cookie, generate_device_name
}, },
}; };
@ -39,7 +39,6 @@ pub struct AccountInformation {
identifier: String, identifier: String,
email: String, email: String,
password: String, password: String,
device_name: String,
} }
#[derive(Serialize)] #[derive(Serialize)]
@ -137,12 +136,14 @@ pub async fn post(
let current_time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() as i64; let current_time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() as i64;
let device_name = generate_device_name();
insert_into(refresh_tokens::table) insert_into(refresh_tokens::table)
.values(( .values((
rdsl::token.eq(&refresh_token), rdsl::token.eq(&refresh_token),
rdsl::uuid.eq(uuid), rdsl::uuid.eq(uuid),
rdsl::created_at.eq(current_time), rdsl::created_at.eq(current_time),
rdsl::device_name.eq(&account_information.device_name), rdsl::device_name.eq(&device_name),
)) ))
.execute(&mut conn) .execute(&mut conn)
.await?; .await?;
@ -161,7 +162,7 @@ pub async fn post(
Member::new(&app_state, uuid, initial_guild).await?; Member::new(&app_state, uuid, initial_guild).await?;
} }
let mut response = (StatusCode::OK, Json(Response {access_token})).into_response(); let mut response = (StatusCode::OK, Json(Response {access_token, device_name})).into_response();
response.headers_mut().append( response.headers_mut().append(
"Set-Cookie", "Set-Cookie",

View file

@ -8,7 +8,6 @@ use objects::MailClient;
use socketioxide::SocketIo; use socketioxide::SocketIo;
use std::{sync::Arc, time::SystemTime}; use std::{sync::Arc, time::SystemTime};
use tower_http::cors::{AllowOrigin, CorsLayer}; use tower_http::cors::{AllowOrigin, CorsLayer};
mod config;
use config::{Config, ConfigBuilder}; use config::{Config, ConfigBuilder};
use diesel_migrations::{EmbeddedMigrations, MigrationHarness, embed_migrations}; use diesel_migrations::{EmbeddedMigrations, MigrationHarness, embed_migrations};
@ -17,6 +16,8 @@ pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
type Conn = type Conn =
deadpool::managed::Object<AsyncDieselConnectionManager<diesel_async::AsyncPgConnection>>; deadpool::managed::Object<AsyncDieselConnectionManager<diesel_async::AsyncPgConnection>>;
mod config;
mod wordlist;
mod api; mod api;
pub mod error; pub mod error;
pub mod objects; pub mod objects;

View file

@ -103,7 +103,7 @@ diesel::table! {
token -> Varchar, token -> Varchar,
uuid -> Uuid, uuid -> Uuid,
created_at -> Int8, created_at -> Int8,
#[max_length = 16] #[max_length = 64]
device_name -> Varchar, device_name -> Varchar,
} }
} }

View file

@ -1,4 +1,5 @@
use std::sync::LazyLock; use std::sync::LazyLock;
use rand::{seq::IndexedRandom};
use axum::body::Bytes; use axum::body::Bytes;
use axum_extra::extract::cookie::{Cookie, SameSite}; use axum_extra::extract::cookie::{Cookie, SameSite};
@ -19,6 +20,7 @@ use crate::{
error::Error, error::Error,
objects::{HasIsAbove, HasUuid}, objects::{HasIsAbove, HasUuid},
schema::users, schema::users,
wordlist::{ADJECTIVES, ANIMALS}
}; };
pub static EMAIL_REGEX: LazyLock<Regex> = LazyLock::new(|| { pub static EMAIL_REGEX: LazyLock<Regex> = LazyLock::new(|| {
@ -207,3 +209,12 @@ impl AppState {
.await .await
} }
} }
pub fn generate_device_name() -> String {
let mut rng = rand::rng();
let adjective = ADJECTIVES.choose(&mut rng).unwrap();
let animal = ANIMALS.choose(&mut rng).unwrap();
return [*adjective, *animal].join(" ")
}

993
src/wordlist.rs Normal file
View file

@ -0,0 +1,993 @@
pub const ANIMALS: [&'static str; 223] = [
"Aardvark",
"Albatross",
"Alligator",
"Alpaca",
"Ant",
"Anteater",
"Antelope",
"Ape",
"Armadillo",
"Donkey",
"Baboon",
"Badger",
"Barracuda",
"Bat",
"Bear",
"Beaver",
"Bee",
"Bison",
"Boar",
"Buffalo",
"Butterfly",
"Camel",
"Capybara",
"Caribou",
"Cassowary",
"Cat",
"Caterpillar",
"Cattle",
"Chamois",
"Cheetah",
"Chicken",
"Chimpanzee",
"Chinchilla",
"Chough",
"Clam",
"Cobra",
"Cockroach",
"Cod",
"Cormorant",
"Coyote",
"Crab",
"Crane",
"Crocodile",
"Crow",
"Curlew",
"Deer",
"Dinosaur",
"Dog",
"Dogfish",
"Dolphin",
"Dotterel",
"Dove",
"Dragonfly",
"Duck",
"Dugong",
"Dunlin",
"Eagle",
"Echidna",
"Eel",
"Eland",
"Elephant",
"Elk",
"Emu",
"Falcon",
"Ferret",
"Finch",
"Fish",
"Flamingo",
"Fly",
"Fox",
"Frog",
"Gaur",
"Gazelle",
"Gerbil",
"Giraffe",
"Gnat",
"Gnu",
"Goat",
"Goldfinch",
"Goldfish",
"Goose",
"Gorilla",
"Goshawk",
"Grasshopper",
"Grouse",
"Guanaco",
"Gull",
"Hamster",
"Hare",
"Hawk",
"Hedgehog",
"Heron",
"Herring",
"Hippopotamus",
"Hornet",
"Horse",
"Hummingbird",
"Hyena",
"Ibex",
"Ibis",
"Jackal",
"Jaguar",
"Jay",
"Jellyfish",
"Kangaroo",
"Kingfisher",
"Koala",
"Kookabura",
"Kouprey",
"Kudu",
"Lapwing",
"Lark",
"Lemur",
"Leopard",
"Lion",
"Llama",
"Lobster",
"Locust",
"Loris",
"Louse",
"Lyrebird",
"Magpie",
"Mallard",
"Manatee",
"Mandrill",
"Mantis",
"Marten",
"Meerkat",
"Mink",
"Mole",
"Mongoose",
"Monkey",
"Moose",
"Mosquito",
"Mouse",
"Mule",
"Narwhal",
"Newt",
"Nightingale",
"Octopus",
"Okapi",
"Opossum",
"Oryx",
"Ostrich",
"Otter",
"Owl",
"Oyster",
"Panther",
"Parrot",
"Partridge",
"Peafowl",
"Pelican",
"Penguin",
"Pheasant",
"Pig",
"Pigeon",
"Pony",
"Porcupine",
"Porpoise",
"Quail",
"Quelea",
"Quetzal",
"Rabbit",
"Raccoon",
"Rail",
"Ram",
"Rat",
"Raven",
"Red Deer",
"Red Panda",
"Reindeer",
"Rhinoceros",
"Rook",
"Salamander",
"Salmon",
"Sand Dollar",
"Sandpiper",
"Sardine",
"Scorpion",
"Seahorse",
"Seal",
"Shark",
"Sheep",
"Shrew",
"Skunk",
"Snail",
"Snake",
"Sparrow",
"Spider",
"Spoonbill",
"Squid",
"Squirrel",
"Starling",
"Stingray",
"Stinkbug",
"Stork",
"Swallow",
"Swan",
"Tapir",
"Tarsier",
"Termite",
"Tiger",
"Toad",
"Trout",
"Turkey",
"Turtle",
"Viper",
"Vulture",
"Wallaby",
"Walrus",
"Wasp",
"Weasel",
"Whale",
"Wildcat",
"Wolf",
"Wolverine",
"Wombat",
"Woodcock",
"Woodpecker",
"Worm",
"Wren",
"Yak",
"Zebra",
];
pub const ADJECTIVES: [&'static str; 765] = [
"Other",
"Such",
"First",
"Many",
"New",
"More",
"Same",
"Own",
"Good",
"Different",
"Great",
"Long",
"High",
"Social",
"Little",
"Much",
"Important",
"Small",
"Most",
"Large",
"Old",
"Few",
"General",
"Second",
"Public",
"Last",
"Several",
"Early",
"Certain",
"Economic",
"Least",
"Common",
"Present",
"Next",
"Local",
"Best",
"Particular",
"Young",
"Various",
"Necessary",
"Whole",
"Only",
"True",
"Able",
"Major",
"Full",
"Low",
"Available",
"Real",
"Similar",
"Total",
"Special",
"Less",
"Short",
"Specific",
"Single",
"Self",
"National",
"Individual",
"Clear",
"Personal",
"Higher",
"Better",
"Third",
"Natural",
"Greater",
"Open",
"Difficult",
"Current",
"Further",
"Main",
"Physical",
"Foreign",
"Lower",
"Strong",
"Private",
"Likely",
"International",
"Significant",
"Late",
"Basic",
"Hard",
"Modern",
"Simple",
"Normal",
"Sure",
"Central",
"Original",
"Effective",
"Following",
"Direct",
"Final",
"Cultural",
"Big",
"Recent",
"Complete",
"Financial",
"Positive",
"Primary",
"Appropriate",
"Legal",
"European",
"Equal",
"Larger",
"Average",
"Historical",
"Critical",
"Wide",
"Traditional",
"Additional",
"Active",
"Complex",
"Former",
"Independent",
"Entire",
"Actual",
"Close",
"Constant",
"Previous",
"Easy",
"Serious",
"Potential",
"Fine",
"Industrial",
"Subject",
"Future",
"Internal",
"Initial",
"Well",
"Essential",
"Dark",
"Popular",
"Successful",
"Standard",
"Year",
"Past",
"Ready",
"Professional",
"Wrong",
"Very",
"Proper",
"Separate",
"Heavy",
"Civil",
"Responsible",
"Considerable",
"Light",
"Cold",
"Above",
"Older",
"Practical",
"External",
"Sufficient",
"Interesting",
"Upper",
"Scientific",
"Key",
"Annual",
"Limited",
"Smaller",
"Southern",
"Earlier",
"Commercial",
"Powerful",
"Later",
"Like",
"Clinical",
"Ancient",
"Educational",
"Typical",
"Technical",
"Environmental",
"Formal",
"Aware",
"Beautiful",
"Variable",
"Obvious",
"Secondary",
"Enough",
"Urban",
"Regular",
"Relevant",
"Greatest",
"Spiritual",
"Time",
"Double",
"Happy",
"Term",
"Multiple",
"Dependent",
"Correct",
"Northern",
"Middle",
"Rural",
"Official",
"Fundamental",
"Numerous",
"Overall",
"Usual",
"Native",
"Regional",
"Highest",
"North",
"Agricultural",
"Literary",
"Broad",
"Perfect",
"Experimental",
"Fourth",
"Global",
"Ordinary",
"Related",
"Apparent",
"Daily",
"Principal",
"Contemporary",
"Severe",
"Reasonable",
"Subsequent",
"Worth",
"Longer",
"Emotional",
"Intellectual",
"Unique",
"Pure",
"Familiar",
"American",
"Solid",
"Brief",
"Famous",
"Fresh",
"Day",
"Corresponding",
"Characteristic",
"Maximum",
"Detailed",
"Outside",
"Theoretical",
"Fair",
"Opposite",
"Capable",
"Visual",
"Interested",
"Joint",
"Adequate",
"Based",
"Substantial",
"Unable",
"Structural",
"Soft",
"False",
"Largest",
"Inner",
"Mean",
"Extensive",
"Excellent",
"Rapid",
"Absolute",
"Consistent",
"Continuous",
"Administrative",
"Strange",
"Willing",
"Alternative",
"Slow",
"Distinct",
"Safe",
"Permanent",
"Front",
"Corporate",
"Academic",
"Thin",
"Nineteenth",
"Universal",
"Functional",
"Unknown",
"Careful",
"Narrow",
"Evident",
"Sound",
"Classical",
"Minor",
"Weak",
"Suitable",
"Chief",
"Extreme",
"Yellow",
"Warm",
"Mixed",
"Flat",
"Huge",
"Vast",
"Stable",
"Valuable",
"Rare",
"Visible",
"Sensitive",
"Mechanical",
"State",
"Radical",
"Extra",
"Superior",
"Conventional",
"Thick",
"Dominant",
"Post",
"Collective",
"Younger",
"Efficient",
"Linear",
"Organic",
"Oral",
"Century",
"Creative",
"Vertical",
"Dynamic",
"Empty",
"Minimum",
"Cognitive",
"Logical",
"Afraid",
"Equivalent",
"Quick",
"Near",
"Concrete",
"Mass",
"Acute",
"Sharp",
"Easier",
"Quiet",
"Adult",
"Accurate",
"Ideal",
"Partial",
"Bright",
"Identical",
"Conservative",
"Magnetic",
"Frequent",
"Electronic",
"Fixed",
"Square",
"Cross",
"Clean",
"Back",
"Organizational",
"Constitutional",
"Genetic",
"Ultimate",
"Secret",
"Vital",
"Dramatic",
"Objective",
"Round",
"Alive",
"Straight",
"Unusual",
"Rational",
"Electric",
"Mutual",
"Class",
"Competitive",
"Revolutionary",
"Statistical",
"Random",
"Musical",
"Crucial",
"Racial",
"Sudden",
"Acid",
"Content",
"Temporary",
"Line",
"Remarkable",
"Exact",
"Valid",
"Helpful",
"Nice",
"Comprehensive",
"United",
"Level",
"Fifth",
"Nervous",
"Expensive",
"Prominent",
"Healthy",
"Liquid",
"Institutional",
"Silent",
"Sweet",
"Strategic",
"Molecular",
"Comparative",
"Called",
"Electrical",
"Raw",
"Acceptable",
"Scale",
"Violent",
"All",
"Desirable",
"Tall",
"Steady",
"Wonderful",
"Sub",
"Distant",
"Progressive",
"Enormous",
"Horizontal",
"And",
"Intense",
"Smooth",
"Applicable",
"Over",
"Animal",
"Abstract",
"Wise",
"Worst",
"Gold",
"Precise",
"Legislative",
"Remote",
"Technological",
"Outer",
"Uniform",
"Slight",
"Attractive",
"Evil",
"Tiny",
"Royal",
"Angry",
"Advanced",
"Friendly",
"Dear",
"Busy",
"Spatial",
"Rough",
"Primitive",
"Judicial",
"Systematic",
"Lateral",
"Sorry",
"Plain",
"Off",
"Comfortable",
"Definite",
"Massive",
"Firm",
"Widespread",
"Prior",
"Twentieth",
"Mathematical",
"Verbal",
"Marginal",
"Excessive",
"Stronger",
"Gross",
"World",
"Productive",
"Wider",
"Glad",
"Linguistic",
"Patient",
"Symbolic",
"Earliest",
"Plastic",
"Type",
"Prime",
"Eighteenth",
"Blind",
"Neutral",
"Guilty",
"Hand",
"Extraordinary",
"Metal",
"Surprising",
"Fellow",
"York",
"Grand",
"Thermal",
"Artificial",
"Five",
"Lowest",
"Genuine",
"Dimensional",
"Optical",
"Unlikely",
"Developmental",
"Reliable",
"Executive",
"Comparable",
"Satisfactory",
"Golden",
"Diverse",
"Preliminary",
"Wooden",
"Noble",
"Part",
"Striking",
"Cool",
"Classic",
"Elderly",
"Four",
"Temporal",
"Indirect",
"Romantic",
"Intermediate",
"Differential",
"Passive",
"Life",
"Voluntary",
"Out",
"Adjacent",
"Behavioral",
"Exclusive",
"Closed",
"Inherent",
"Inevitable",
"Complicated",
"Quantitative",
"Respective",
"Artistic",
"Probable",
"Anxious",
"Informal",
"Strict",
"Fiscal",
"Ideological",
"Profound",
"Extended",
"Eternal",
"Known",
"Infinite",
"Proud",
"Honest",
"Peculiar",
"Absent",
"Pleasant",
"Optimal",
"Renal",
"Static",
"Outstanding",
"Presidential",
"Digital",
"Integrated",
"Legitimate",
"Curious",
"Aggressive",
"Deeper",
"Elementary",
"History",
"Surgical",
"Occasional",
"Flexible",
"Convenient",
"Solar",
"Atomic",
"Isolated",
"Latest",
"Sad",
"Conceptual",
"Underlying",
"Everyday",
"Cost",
"Intensive",
"Odd",
"Subjective",
"Mid",
"Worthy",
"Pale",
"Meaningful",
"Therapeutic",
"Making",
"Circular",
"Realistic",
"Multi",
"Child",
"Sophisticated",
"Down",
"Leading",
"Intelligent",
"Governmental",
"Numerical",
"Minimal",
"Diagnostic",
"Indigenous",
"Aesthetic",
"Distinctive",
"Operational",
"Sole",
"Material",
"Fast",
"Bitter",
"Broader",
"Brilliant",
"Peripheral",
"Rigid",
"Automatic",
"Lesser",
"Routine",
"Favorable",
"Cooperative",
"Cardiac",
"Arbitrary",
"Loose",
"Favorite",
"Subtle",
"Uncertain",
"Hostile",
"Monthly",
"Naval",
"Physiological",
"Historic",
"Developed",
"Skilled",
"Anterior",
"Pro",
"Gentle",
"Loud",
"Pulmonary",
"Innocent",
"Provincial",
"Mild",
"Page",
"Specialized",
"Bare",
"Excess",
"Inter",
"Shaped",
"Theological",
"Sensory",
"The",
"Stress",
"Novel",
"Working",
"Shorter",
"Secular",
"Geographical",
"Intimate",
"Liable",
"Selective",
"Influential",
"Modest",
"Successive",
"Continued",
"Water",
"Expert",
"Municipal",
"Marine",
"Thirty",
"Adverse",
"Wacky",
"Closer",
"Virtual",
"Peaceful",
"Mobile",
"Sixth",
"Immune",
"Coastal",
"Representative",
"Lead",
"Forward",
"Faithful",
"Crystal",
"Protective",
"Elaborate",
"Tremendous",
"Welcoming",
"Abnormal",
"Grateful",
"Proportional",
"Dual",
"Operative",
"Precious",
"Sympathetic",
"Accessible",
"Lovely",
"Spinal",
"Even",
"Marked",
"Observed",
"Point",
"Mature",
"Competent",
"Residential",
"Impressive",
"Unexpected",
"Nearby",
"Unnecessary",
"Generous",
"Cerebral",
"Unpublished",
"Delicate",
"Analytical",
"Tropical",
"Statutory",
"Cell",
"Weekly",
"End",
"Online",
"Beneficial",
"Aged",
"Tough",
"Eager",
"Ongoing",
"Silver",
"Persistent",
"Calm",
"Nearest",
"Hidden",
"Magic",
"Pretty",
"Wealthy",
"Exciting",
"Decisive",
"Confident",
"Invisible",
"Notable",
"Medium",
"Manual",
"Select",
"Thorough",
"Causal",
"Giant",
"Bigger",
"Pink",
"Improved",
"Immense",
"Hour",
"Intact",
"Grade",
"Dense",
"Hungry",
"Biggest",
"Abundant",
"Handsome",
"Retail",
"Insufficient",
"Irregular",
"Intrinsic",
"Residual",
"Follow",
"Fluid",
"Mysterious",
"Descriptive",
"Elastic",
"Destructive",
"Architectural",
"Synthetic",
"Continental",
"Evolutionary",
"Lucky",
"Bold",
"Funny",
"Peak",
"Smallest",
"Reluctant",
"Suspicious",
"Smart",
"Mighty",
"Brave",
"Humble",
"Vocal",
"Obscure",
"Innovative",
];