2021-11-12 12:05:26 +00:00
#[ macro_use ] extern crate log ;
2021-11-12 13:56:05 +00:00
#[ macro_use ] extern crate serde ;
2021-11-12 12:05:26 +00:00
use std ::collections ::HashSet ;
use std ::env ;
use std ::fs ::File ;
use std ::ops ::Deref ;
use std ::path ::PathBuf ;
use std ::sync ::Arc ;
use clap ::ArgMatches ;
use diesel ::r2d2 ::{ ConnectionManager } ;
use diesel ::SqliteConnection ;
use crate ::config ::{ Config , ManifoldConfig } ;
use serenity ::prelude ::* ;
use serenity ::framework ::standard ::{ * , macros ::* } ;
use serenity ::http ::Http ;
2021-11-12 12:22:16 +00:00
use serenity ::model ::prelude ::{ GuildId , Message , UserId } ;
2021-11-12 12:05:26 +00:00
use crate ::error ::ManifoldResult ;
use crate ::events ::Handler ;
use crate ::responses ::Responses ;
pub mod config ;
pub mod error ;
pub mod responses ;
pub mod events ;
2021-11-12 13:23:14 +00:00
pub mod core_commands ;
// Retrieve build info from output file
pub mod built_info {
include! ( concat! ( env! ( " OUT_DIR " ) , " /built.rs " ) ) ;
}
use crate ::core_commands ::* ;
2021-11-12 12:05:26 +00:00
2021-11-12 22:22:20 +00:00
pub type ManifoldDatabasePool = diesel ::r2d2 ::Pool < ConnectionManager < SqliteConnection > > ;
2021-11-12 12:05:26 +00:00
pub struct Db {
2021-11-12 22:28:51 +00:00
pub pool : ManifoldDatabasePool ,
2021-11-12 12:05:26 +00:00
}
impl Deref for Db {
2021-11-12 22:22:20 +00:00
type Target = ManifoldDatabasePool ;
2021-11-12 12:05:26 +00:00
fn deref ( & self ) -> & Self ::Target { & self . pool }
}
2021-11-12 22:22:20 +00:00
pub struct ManifoldDatabase ;
2021-11-12 12:05:26 +00:00
impl TypeMapKey for ManifoldDatabase {
type Value = Arc < Mutex < Db > > ;
}
impl TypeMapKey for Responses {
type Value = Arc < Mutex < Responses > > ;
}
impl TypeMapKey for ManifoldConfig {
type Value = Arc < Mutex < ManifoldConfig > > ;
}
2021-11-12 12:15:59 +00:00
#[ group ]
2021-11-12 13:23:14 +00:00
#[ commands(ping, set_config, get_config, version, set_activity, get_environment) ]
2021-11-12 12:15:59 +00:00
struct Core ;
2021-11-12 12:43:20 +00:00
pub async fn prepare_client ( arguments : ArgMatches < '_ > , mut framework : StandardFramework ) -> ManifoldResult < Client > {
2021-11-12 12:05:26 +00:00
let bot_environment = arguments . value_of ( " environment " ) . unwrap_or ( " Production " ) . to_string ( ) ;
let config_file = format! ( " config/ {} " , arguments . value_of ( " config-file " ) . unwrap_or ( " manifold.json " ) ) ;
if arguments . occurrences_of ( " make-config " ) > 0 {
if File ::open ( & config_file ) . is_ok ( ) {
warn! ( " Not making a new configuration file - configuration exists at {}. Please remove this if you want to generate a fresh config, or provide an alternative path. " , & config_file ) ;
Err ( " ConfigExists " ) ?
} else {
let config = ManifoldConfig ::new ( ) ;
match config . create_default_config ( & config_file , & bot_environment ) {
Ok ( _ ) = > warn! ( " Writing new configuration to {} " , & config_file ) ,
Err ( e ) = > { error! ( " Could not write new config! {} " , e . details ) ; Err ( e ) ? }
}
}
}
info! ( " Reading configuration... " ) ;
debug! ( " Configuration file path: {} " , & config_file ) ;
let config = load_config ( & config_file , & bot_environment ) ;
debug! ( " Current configuration: {:?} " , & config ) ;
let prefix = config . get_value ( & " BotPrefix " . to_string ( ) ) . unwrap ( ) ;
let manager = ConnectionManager ::< SqliteConnection > ::new ( " manifold.db " ) ;
let pool = r2d2 ::Pool ::builder ( )
. max_size ( 1 )
. build ( manager )
. expect ( " Database setup error! " ) ;
let mut responses = Responses ::new ( ) ;
responses . reload ( & PathBuf ::from ( config . get_value ( & " ResponsesFilePath " . to_string ( ) ) . unwrap_or ( & " txt/responses.txt " . to_string ( ) ) ) ) . expect ( " Could not load responses file! " ) ;
let token = env ::var ( " DISCORD_TOKEN " ) . expect (
" Could not find an environment variable called DISCORD_TOKEN " ,
) ;
let http = Http ::new_with_token ( & token ) ;
let ( owners , bot_id ) = match http . get_current_application_info ( ) . await {
Ok ( info ) = > {
let mut owners = HashSet ::new ( ) ;
owners . insert ( info . owner . id ) ;
( owners , info . id )
}
Err ( why ) = > panic! ( " Could not get HTTP application information - exiting: {:?} " , why ) ,
} ;
2021-11-12 12:43:20 +00:00
framework = framework . configure ( | c | c
2021-11-12 12:05:26 +00:00
. with_whitespace ( true )
. on_mention ( Some ( bot_id ) )
. prefix ( & prefix )
. owners ( owners )
. case_insensitivity ( true )
2021-11-12 12:43:20 +00:00
) . before ( before )
. help ( & MANIFOLD_HELP ) ;
framework . group_add ( & CORE_GROUP ) ;
2021-11-12 12:05:26 +00:00
2021-11-12 12:15:59 +00:00
let client = Client ::builder ( & token )
2021-11-12 12:05:26 +00:00
. event_handler ( Handler ::new ( ) )
. framework ( framework )
. await
. expect ( " Error creating client! " ) ;
{
let mut data = client . data . write ( ) . await ;
let db = Db { pool } ;
data . insert ::< ManifoldConfig > ( Arc ::new ( Mutex ::new ( config ) ) ) ;
data . insert ::< ManifoldDatabase > ( Arc ::new ( Mutex ::new ( db ) ) ) ;
data . insert ::< Responses > ( Arc ::new ( Mutex ::new ( responses ) ) ) ;
}
Ok ( client )
}
fn load_config ( config_file : & String , bot_environment : & String ) -> ManifoldConfig {
let mut config = ManifoldConfig ::new ( ) ;
* config . get_path_mut ( ) = PathBuf ::from ( config_file ) ;
* config . get_environment_mut ( ) = bot_environment . to_owned ( ) ;
config . load_config ( ) . expect ( " Could not load config - halting " ) ;
config
}
2021-11-12 12:22:16 +00:00
#[ help ]
async fn manifold_help (
ctx : & Context ,
msg : & Message ,
args : Args ,
help_options : & 'static HelpOptions ,
groups : & [ & 'static CommandGroup ] ,
owners : HashSet < UserId >
) -> CommandResult {
let _ = help_commands ::with_embeds ( ctx , msg , args , help_options , groups , owners ) . await ;
Ok ( ( ) )
}
2021-11-12 12:05:26 +00:00
#[ check ]
#[ name = " ModOrHigher " ]
async fn mod_or_higher_check ( ctx : & Context , msg : & Message , _ : & mut Args , _ : & CommandOptions ) -> Result < ( ) , Reason > {
let data = ctx . data . read ( ) . await ;
let config = match data . get ::< ManifoldConfig > ( ) {
Some ( c ) = > c . lock ( ) . await ,
None = > return Err ( Reason ::Log ( " Couldn't lock config " . to_string ( ) ) )
} ;
let role_guild_config = match config . get_value ( & " RoleGuild " . to_string ( ) ) {
Some ( rgc ) = > rgc ,
None = > {
error! ( " Guild not configured " ) ;
return Err ( Reason ::Unknown ) ;
}
} ;
let role_guild_number = match role_guild_config . parse ::< u64 > ( ) {
Ok ( rgn ) = > rgn ,
Err ( e ) = > {
error! ( " Parse GuildId fail " ) ;
return Err ( Reason ::Log ( e . to_string ( ) ) ) ;
}
} ;
let guild_id = GuildId ( role_guild_number ) ;
let administrator_role = config . get_role ( & " Admin " . to_string ( ) ) ;
if ! msg . author . has_role ( & ctx , guild_id , administrator_role ) . await . unwrap_or ( false ) {
return Err ( Reason ::User ( " User not administrative " . to_string ( ) ) ) ;
}
Ok ( ( ) )
}
#[ hook ]
async fn before ( _ : & Context , msg : & Message , command_name : & str ) -> bool {
info! ( " Received command '{}' from user '{}' " , command_name , msg . author . name ) ;
true
}