Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
UCC
Discord Bot
Commits
eb688f48
Unverified
Commit
eb688f48
authored
Mar 07, 2021
by
Tom Almeida
Browse files
First (barely) working async version
[TEC], wtf if this. It's all nigh upon unreadable and is in a terrible code style
parent
c6ab8e0f
Pipeline
#297
failed with stages
Changes
6
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
src/ldap.rs
View file @
eb688f48
...
...
@@ -11,7 +11,7 @@ pub struct LDAPUser {
pub
fn
ldap_search
(
username
:
&
str
)
->
Option
<
LDAPUser
>
{
let
settings
=
LdapConnSettings
::
new
()
.set_no_tls_verify
(
true
);
let
ldap
=
let
mut
ldap
=
LdapConn
::
with_settings
(
settings
,
&
CONFIG
.bind_address
)
.expect
(
"Unable to connect to LDAP"
);
ldap
.simple_bind
(
"cn=ucc-discord-bot,cn=Users,dc=ad,dc=ucc,dc=gu,dc=uwa,dc=edu,dc=au"
,
...
...
@@ -31,7 +31,7 @@ pub fn ldap_search(username: &str) -> Option<LDAPUser> {
.success
()
.expect
(
"LDAP search error"
);
if
rs
.is_empty
()
{
return
None
return
None
;
}
let
result
=
SearchEntry
::
construct
(
rs
[
0
]
.clone
())
.attrs
;
Some
(
LDAPUser
{
...
...
@@ -44,9 +44,9 @@ pub fn ldap_search(username: &str) -> Option<LDAPUser> {
.expect
(
"LDAP failed to get 'displayName' field"
)
.join
(
""
),
when_created
:
""
.to_string
(),
// result
// .get("whenCreated")
// .expect("LDAP failed to get 'whenCreated' field")
// .join(""),
// .get("whenCreated")
// .expect("LDAP failed to get 'whenCreated' field")
// .join(""),
login_shell
:
result
.get
(
"loginShell"
)
.expect
(
"LDAP failed to get 'loginShell' field"
)
...
...
src/reaction_roles.rs
View file @
eb688f48
use
crate
::
config
::{
ReactRoleMap
,
CONFIG
};
use
crate
::
util
::{
get_react_from_string
,
get_string_from_react
};
use
rayon
::
prelude
::
*
;
use
serenity
::{
client
::
Context
,
model
::{
channel
::
Message
,
channel
::
Reaction
,
id
::
RoleId
,
id
::
UserId
},
...
...
@@ -198,21 +197,17 @@ async fn get_all_role_reaction_message(ctx: &Context) -> Vec<(Message, &'static
let
channels
=
ctx
.http
.get_channels
(
*
guild
.id
.as_u64
())
.await
.unwrap
();
info!
(
" Find role-react message: channels determined"
);
let
http
=
ctx
.http
.clone
();
channels
.par_iter
()
.flat_map
(|
channel
|
{
// since we don't know which channels the messages are in, we check every combination
// of message and channel and ignore the bad matches using .ok() and .filter_map()
let
h
=
http
.clone
();
// thread-local copy
CONFIG
.react_role_messages
.par_iter
()
.filter_map
(
move
|
rrm
|
async
{
h
.get_message
(
*
channel
.id
.as_u64
(),
*
rrm
.message
.as_u64
())
.await
.ok
()
.map
(|
m
|
(
m
,
&
rrm
.mapping
))
})
})
.collect
()
let
mut
v
=
Vec
::
new
();
for
channel
in
channels
{
for
reaction
in
CONFIG
.react_role_messages
.iter
()
{
if
let
Some
(
m
)
=
http
.get_message
(
*
channel
.id
.as_u64
(),
*
reaction
.message
.as_u64
())
.await
.ok
()
{
v
.push
((
m
,
&
reaction
.mapping
))
}
}
}
v
}
src/serenity_handler.rs
View file @
eb688f48
...
...
@@ -6,7 +6,7 @@ use serenity::{
utils
::
MessageBuilder
,
};
use
rand
::
seq
::
SliceRandom
;
//
use rand::seq::SliceRandom;
use
crate
::
config
::
CONFIG
;
use
crate
::
ldap
;
...
...
@@ -35,9 +35,8 @@ impl EventHandler for Handler {
send_message!
(
msg
.channel_id
,
&
ctx
.http
,
MENTION_RESPONSES
.choose
(
&
mut
rand
::
thread_rng
())
.expect
(
"We couldn't get any sass"
)
MENTION_RESPONSES
[
0
]
//.choose(&mut rand::random())
//.expect("We couldn't get any sass")
);
}
return
;
...
...
@@ -136,16 +135,12 @@ impl EventHandler for Handler {
async
fn
reaction_add
(
&
self
,
ctx
:
Context
,
add_reaction
:
channel
::
Reaction
)
{
match
add_reaction
.message
(
&
ctx
.http
)
.await
{
Ok
(
message
)
=>
match
get_message_type
(
&
message
)
{
MessageType
::
RoleReactMessage
if
add_reaction
.user_id
.unwrap
()
!=
CONFIG
.bot_id
=>
{
add_role_by_reaction
(
&
ctx
,
message
,
add_reaction
);
return
;
MessageType
::
RoleReactMessage
if
add_reaction
.user_id
==
Some
(
CONFIG
.bot_id
)
=>
{
add_role_by_reaction
(
&
ctx
,
message
,
add_reaction
)
.await
}
_
if
message
.author.id
!=
CONFIG
.bot_id
||
add_reaction
.user_id
.unwrap
()
==
CONFIG
.bot_id
=>
{
return
}
MessageType
::
Motion
=>
voting
::
reaction_add
(
ctx
,
add_reaction
)
.await
,
||
add_reaction
.user_id
==
Some
(
CONFIG
.bot_id
)
=>
{}
MessageType
::
Motion
=>
voting
::
reaction_add
(
&
ctx
,
add_reaction
)
.await
,
MessageType
::
LogReact
=>
{
let
react_user
=
add_reaction
.user
(
&
ctx
)
.await
.unwrap
();
let
react_as_string
=
get_string_from_react
(
&
add_reaction
.emoji
);
...
...
@@ -186,28 +181,24 @@ impl EventHandler for Handler {
MessageType
::
RoleReactMessage
if
removed_reaction
.user_id
.unwrap
()
!=
CONFIG
.bot_id
=>
{
remove_role_by_reaction
(
&
ctx
,
message
,
removed_reaction
);
return
;
remove_role_by_reaction
(
&
ctx
,
message
,
removed_reaction
)
.await
}
_
if
message
.author.id
!=
CONFIG
.bot_id
||
removed_reaction
.user_id
.unwrap
()
==
CONFIG
.bot_id
=>
{
return
}
MessageType
::
Motion
=>
voting
::
reaction_remove
(
ctx
,
removed_reaction
)
.await
,
||
removed_reaction
.user_id
.unwrap
()
==
CONFIG
.bot_id
=>
{}
MessageType
::
Motion
=>
voting
::
reaction_remove
(
&
ctx
,
removed_reaction
)
.await
,
_
=>
{}
},
Err
(
why
)
=>
error!
(
"Failed to get react message {:?}"
,
why
),
}
}
fn
guild_member_addition
(
async
fn
guild_member_addition
(
&
self
,
ctx
:
Context
,
_guild_id
:
serenity
::
model
::
id
::
GuildId
,
the_new_member
:
Member
,
)
{
user_management
::
new_member
(
&
ctx
,
the_new_member
)
;
user_management
::
new_member
(
&
ctx
,
the_new_member
)
.await
}
// Set a handler to be called on the `ready` event. This is called when a
...
...
@@ -216,13 +207,13 @@ impl EventHandler for Handler {
// private channels, and more.
//
// In this case, just print what the current user's username is.
fn
ready
(
&
self
,
ctx
:
Context
,
ready
:
Ready
)
{
async
fn
ready
(
&
self
,
ctx
:
Context
,
ready
:
Ready
)
{
info!
(
"{} is connected!"
,
ready
.user.name
);
sync_all_role_reactions
(
&
ctx
)
;
sync_all_role_reactions
(
&
ctx
)
.await
}
fn
resume
(
&
self
,
ctx
:
Context
,
_
:
serenity
::
model
::
event
::
ResumedEvent
)
{
sync_all_role_reactions
(
&
ctx
)
;
async
fn
resume
(
&
self
,
ctx
:
Context
,
_
:
serenity
::
model
::
event
::
ResumedEvent
)
{
sync_all_role_reactions
(
&
ctx
)
.await
}
}
...
...
src/token_management.rs
View file @
eb688f48
...
...
@@ -17,7 +17,7 @@ lazy_static! {
}
fn
text_encrypt
(
plaintext
:
&
str
)
->
String
{
base64
::
encode
(
CIPHER
.encrypt_vec
(
plaintext
.as_bytes
()))
base64
::
encode
(
CIPHER
.
clone
()
.
encrypt_vec
(
plaintext
.as_bytes
()))
}
fn
text_decrypt
(
ciphertext
:
&
str
)
->
Option
<
String
>
{
...
...
@@ -25,7 +25,7 @@ fn text_decrypt(ciphertext: &str) -> Option<String> {
warn!
(
"Unable to decode base64 text"
);
return
None
});
guard!
(
let
Ok
(
decrypted_vec
)
=
CIPHER
.decrypt_vec
(
&
cipher_vec
)
else
{
guard!
(
let
Ok
(
decrypted_vec
)
=
CIPHER
.
clone
()
.
decrypt_vec
(
&
cipher_vec
)
else
{
warn!
(
"Text decryption failed"
);
return
None
});
...
...
src/user_management.rs
View file @
eb688f48
...
...
@@ -290,7 +290,7 @@ impl Commands {
let
user
=
match
ctx
.http
.get_user
(
member
.discord_id
as
_
)
.await
{
Ok
(
u
)
=>
u
,
Err
(
e
)
=>
{
error!
(
"Couldn't find matching Discord ID for username!
"
);
error!
(
"Couldn't find matching Discord ID for username!
{:?}"
,
e
);
return
;
}
};
...
...
@@ -492,8 +492,7 @@ impl Commands {
pub
async
fn
clear_info
(
ctx
:
Context
,
msg
:
Message
,
field
:
&
str
)
{
if
field
.trim
()
.is_empty
()
{
// just show the help page from set_info
Commands
::
set_info
(
ctx
,
msg
,
""
);
return
;
return
Commands
::
set_info
(
ctx
,
msg
,
""
)
.await
;
}
match
field
{
"bio"
=>
database
::
set_member_bio
(
&
msg
.author.id
.0
,
None
),
...
...
src/voting.rs
View file @
eb688f48
...
...
@@ -5,7 +5,7 @@ use serenity::{
};
use
std
::
collections
::
HashMap
;
use
std
::
str
::
FromStr
;
use
std
::
sync
::
Mutex
;
use
tokio
::
sync
::
Mutex
;
use
crate
::
config
::
CONFIG
;
use
crate
::
util
::
get_string_from_react
;
...
...
@@ -15,8 +15,7 @@ impl Commands {
pub
async
fn
move_something
(
ctx
:
Context
,
msg
:
Message
,
content
:
&
str
)
{
let
motion
=
content
;
if
!
motion
.is_empty
()
{
create_motion
(
&
ctx
,
&
msg
,
motion
);
return
;
return
create_motion
(
&
ctx
,
&
msg
,
motion
)
.await
;
}
send_message!
(
msg
.channel_id
,
...
...
@@ -34,8 +33,7 @@ impl Commands {
pub
async
fn
poll
(
ctx
:
Context
,
msg
:
Message
,
content
:
&
str
)
{
let
topic
=
content
;
if
!
topic
.is_empty
()
{
create_poll
(
&
ctx
,
&
msg
,
topic
);
return
;
return
create_poll
(
&
ctx
,
&
msg
,
topic
)
.await
;
}
send_message!
(
msg
.channel_id
,
...
...
@@ -167,45 +165,42 @@ lazy_static! {
}
async
fn
get_cached_motion
(
ctx
:
&
Context
,
msg
:
&
Message
)
->
MotionInfo
{
let
mut
cached_motions
=
MOTIONS_CACHE
.lock
()
.
unwrap
()
;
let
mut
cached_motions
=
MOTIONS_CACHE
.lock
()
.
await
;
if
!
cached_motions
.contains_key
(
&
msg
.id
)
{
info!
(
"Initialising representation of motion {:?}"
,
msg
.id
);
let
for_votes
=
msg
.reaction_users
(
ctx
,
EmojiIdentifier
::
from_str
(
&
CONFIG
.for_vote
)
.unwrap
(),
Some
(
100
),
None
,
)
.await
.unwrap
();
let
against_votes
=
msg
.reaction_users
(
ctx
,
EmojiIdentifier
::
from_str
(
&
CONFIG
.against_vote
)
.unwrap
(),
Some
(
100
),
None
,
)
.await
.unwrap
();
let
abstain_votes
=
msg
.reaction_users
(
ctx
,
EmojiIdentifier
::
from_str
(
&
CONFIG
.abstain_vote
)
.unwrap
(),
Some
(
100
),
None
,
)
.await
.unwrap
();
let
this_motion
=
MotionInfo
{
votes
:
{
let
mut
m
=
HashMap
::
new
();
m
.insert
(
CONFIG
.for_vote
.to_string
(),
msg
.reaction_users
(
ctx
,
EmojiIdentifier
::
from_str
(
&
CONFIG
.for_vote
)
.unwrap
(),
Some
(
100
),
None
,
)
.await
.unwrap
(),
);
m
.insert
(
CONFIG
.against_vote
.to_string
(),
msg
.reaction_users
(
ctx
,
EmojiIdentifier
::
from_str
(
&
CONFIG
.against_vote
)
.unwrap
(),
Some
(
100
),
None
,
)
.await
.unwrap
(),
);
m
.insert
(
CONFIG
.abstain_vote
.to_string
(),
msg
.reaction_users
(
ctx
,
EmojiIdentifier
::
from_str
(
&
CONFIG
.abstain_vote
)
.unwrap
(),
Some
(
100
),
None
,
)
.await
.unwrap
(),
);
m
.insert
(
CONFIG
.for_vote
.to_string
(),
for_votes
);
m
.insert
(
CONFIG
.against_vote
.to_string
(),
against_votes
);
m
.insert
(
CONFIG
.abstain_vote
.to_string
(),
abstain_votes
);
m
},
};
...
...
@@ -213,34 +208,15 @@ async fn get_cached_motion(ctx: &Context, msg: &Message) -> MotionInfo {
}
(
*
cached_motions
.get
(
&
msg
.id
)
.unwrap
())
.clone
()
}
fn
set_cached_motion
(
id
:
serenity
::
model
::
id
::
MessageId
,
motion_info
:
MotionInfo
)
{
if
let
Some
(
motion
)
=
MOTIONS_CACHE
.lock
()
.unwrap
()
.get_mut
(
&
id
)
{
async
fn
set_cached_motion
(
id
:
serenity
::
model
::
id
::
MessageId
,
motion_info
:
MotionInfo
)
{
if
let
Some
(
motion
)
=
MOTIONS_CACHE
.lock
()
.await
.get_mut
(
&
id
)
{
*
motion
=
motion_info
;
return
;
}
warn!
(
"{}"
,
"Couldn't find motion in cache to set"
);
}
macro_rules!
tiebreaker
{
(
$ctx
:
expr
,
$vote
:
expr
,
$motion_info
:
expr
)
=>
{
if
$motion_info
.votes
.get
(
$vote
)
.unwrap
()
.iter
()
.any
(|
u
|
async
{
u
.has_role
(
$ctx
,
CONFIG
.server_id
,
CONFIG
.tiebreaker_role
)
.await
.unwrap
()
})
{
0.25
}
else
{
0.0
}
};
}
async
fn
update_motion
(
ctx
:
&
Context
,
msg
:
&
mut
Message
,
...
...
@@ -250,15 +226,33 @@ async fn update_motion(
)
{
let
motion_info
:
MotionInfo
=
get_cached_motion
(
ctx
,
msg
)
.await
;
async
fn
tiebreaker
(
ctx
:
&
Context
,
motion
:
&
MotionInfo
,
vote_type
:
&
str
)
->
f32
{
if
let
Some
(
votes
)
=
motion
.votes
.get
(
vote_type
)
{
for
voter
in
votes
{
match
voter
.has_role
(
ctx
,
CONFIG
.server_id
,
CONFIG
.tiebreaker_role
)
.await
{
Ok
(
true
)
=>
return
0.25
,
_
=>
continue
,
}
}
0.0
}
else
{
warn!
(
"Couldn't find
\"
{}
\"
vote for {:?}"
,
vote_type
,
motion
);
0.0
}
}
let
for_votes
=
motion_info
.votes
.get
(
&
CONFIG
.for_vote
)
.unwrap
()
.len
()
as
isize
-
1
;
let
against_votes
=
motion_info
.votes
.get
(
&
CONFIG
.against_vote
)
.unwrap
()
.len
()
as
isize
-
1
;
let
abstain_votes
=
motion_info
.votes
.get
(
&
CONFIG
.abstain_vote
)
.unwrap
()
.len
()
as
isize
-
1
;
let
for_strength
=
for_votes
as
f32
+
tiebreaker
!
(
ctx
,
&
CONFIG
.for_vote
,
motion_info
)
;
let
for_strength
=
for_votes
as
f32
+
tiebreaker
(
ctx
,
&
motion_info
,
&
CONFIG
.for_vote
)
.await
;
let
against_strength
=
against_votes
as
f32
+
tiebreaker
!
(
ctx
,
&
CONFIG
.against_vote
,
motion_info
)
;
against_votes
as
f32
+
tiebreaker
(
ctx
,
&
motion_info
,
&
CONFIG
.against_vote
)
.await
;
let
abstain_strength
=
abstain_votes
as
f32
+
tiebreaker
!
(
ctx
,
&
CONFIG
.abstain_vote
,
motion_info
)
;
abstain_votes
as
f32
+
tiebreaker
(
ctx
,
&
motion_info
,
&
CONFIG
.abstain_vote
)
.await
;
let
old_embed
=
msg
.embeds
[
0
]
.clone
();
let
topic
=
old_embed
.clone
()
.title
.unwrap
();
...
...
@@ -271,10 +265,13 @@ async fn update_motion(
topic
);
let
update_status
=
|
e
:
&
mut
serenity
::
builder
::
CreateEmbed
,
status
:
&
str
,
last_status_full
:
String
,
topic
:
&
str
|
async
{
fn
update_status
(
ctx
:
&
Context
,
e
:
&
mut
serenity
::
builder
::
CreateEmbed
,
status
:
&
str
,
last_status_full
:
String
,
topic
:
&
str
,
)
{
let
last_status
=
last_status_full
.lines
()
.next
()
.expect
(
"No previous status"
);
if
last_status
==
status
{
e
.field
(
"Status"
,
last_status_full
,
true
);
...
...
@@ -291,9 +288,15 @@ async fn update_motion(
message
.push
(
" is now "
);
message
.push_bold
(
status
);
message
.push_italic
(
format!
(
" (was {})"
,
last_status
));
send_message!
(
CONFIG
.announcement_channel
,
&
ctx
.http
,
message
.build
());
let
ctx
=
ctx
.clone
();
tokio
::
spawn
(
async
move
{
CONFIG
.announcement_channel
.say
(
ctx
.http
,
message
.build
())
.await
});
}
}
;
}
if
let
Err
(
why
)
=
msg
.edit
(
ctx
,
|
m
|
{
...
...
@@ -319,14 +322,14 @@ async fn update_motion(
.value
;
if
for_strength
>
(
CONFIG
.vote_pool_size
as
f32
/
2.0
)
{
e
.colour
(
serenity
::
utils
::
Colour
::
TEAL
);
update_status
(
e
,
"Passed"
,
last_status_full
,
&
topic
);
update_status
(
ctx
,
e
,
"Passed"
,
last_status_full
,
&
topic
);
}
else
if
against_strength
+
abstain_strength
>
(
CONFIG
.vote_pool_size
as
f32
/
2.0
)
{
e
.colour
(
serenity
::
utils
::
Colour
::
RED
);
update_status
(
e
,
"Failed"
,
last_status_full
,
&
topic
);
update_status
(
ctx
,
e
,
"Failed"
,
last_status_full
,
&
topic
);
}
else
{
e
.colour
(
serenity
::
utils
::
Colour
::
GOLD
);
update_status
(
e
,
"Under Consideration"
,
last_status_full
,
&
topic
);
update_status
(
ctx
,
e
,
"Under Consideration"
,
last_status_full
,
&
topic
);
}
e
.field
(
format!
(
...
...
@@ -354,7 +357,7 @@ async fn update_motion(
}
}
pub
async
fn
reaction_add
(
ctx
:
Context
,
add_reaction
:
channel
::
Reaction
)
{
pub
async
fn
reaction_add
(
ctx
:
&
Context
,
add_reaction
:
channel
::
Reaction
)
{
let
react_as_string
=
get_string_from_react
(
&
add_reaction
.emoji
);
match
add_reaction
.message
(
&
ctx
.http
)
.await
{
Ok
(
mut
message
)
=>
{
...
...
@@ -407,8 +410,8 @@ pub async fn reaction_add(ctx: Context, add_reaction: channel::Reaction) {
vote
.retain
(|
u
|
u
.id
!=
user
.id
);
vote
.push
(
user
.clone
());
}
set_cached_motion
(
message
.id
,
motion_info
);
update_motion
(
&
ctx
,
&
mut
message
,
&
user
,
"add"
,
add_reaction
)
;
set_cached_motion
(
message
.id
,
motion_info
)
.await
;
update_motion
(
&
ctx
,
&
mut
message
,
&
user
,
"add"
,
add_reaction
)
.await
}
Ok
(
false
)
=>
{
if
!
[
...
...
@@ -434,7 +437,7 @@ pub async fn reaction_add(ctx: Context, add_reaction: channel::Reaction) {
}
}
pub
async
fn
reaction_remove
(
ctx
:
Context
,
removed_reaction
:
channel
::
Reaction
)
{
pub
async
fn
reaction_remove
(
ctx
:
&
Context
,
removed_reaction
:
channel
::
Reaction
)
{
match
removed_reaction
.message
(
&
ctx
.http
)
.await
{
Ok
(
mut
message
)
=>
{
if
let
Ok
(
user
)
=
removed_reaction
.user
(
&
ctx
)
.await
{
...
...
@@ -445,8 +448,8 @@ pub async fn reaction_remove(ctx: Context, removed_reaction: channel::Reaction)
{
vote
.retain
(|
u
|
u
.id
!=
user
.id
);
}
set_cached_motion
(
message
.id
,
motion_info
);
update_motion
(
&
ctx
,
&
mut
message
,
&
user
,
"remove"
,
removed_reaction
);
set_cached_motion
(
message
.id
,
motion_info
)
.await
;
update_motion
(
&
ctx
,
&
mut
message
,
&
user
,
"remove"
,
removed_reaction
)
.await
;
}
}
Err
(
why
)
=>
{
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment