#![allow(zero_ptr)]
pub mod bridge;
mod context;
mod dispatch;
mod error;
mod event_handler;
pub use self::{
context::Context,
error::Error as ClientError,
event_handler::EventHandler
};
pub use gateway;
pub use http as rest;
#[cfg(feature = "cache")]
pub use CACHE;
use http;
use internal::prelude::*;
use parking_lot::Mutex;
use self::bridge::gateway::{ShardManager, ShardManagerMonitor, ShardManagerOptions};
use std::sync::Arc;
use threadpool::ThreadPool;
use typemap::ShareMap;
#[cfg(feature = "framework")]
use framework::Framework;
#[cfg(feature = "voice")]
use model::id::UserId;
#[cfg(feature = "voice")]
use self::bridge::voice::ClientVoiceManager;
pub struct Client {
pub data: Arc<Mutex<ShareMap>>,
#[cfg(feature = "framework")] framework: Arc<Mutex<Option<Box<Framework + Send>>>>,
pub shard_manager: Arc<Mutex<ShardManager>>,
shard_manager_worker: ShardManagerMonitor,
pub threadpool: ThreadPool,
pub token: Arc<Mutex<String>>,
#[cfg(feature = "voice")]
pub voice_manager: Arc<Mutex<ClientVoiceManager>>,
pub ws_uri: Arc<Mutex<String>>,
}
impl Client {
pub fn new<H>(token: &str, handler: H) -> Result<Self>
where H: EventHandler + Send + Sync + 'static {
let token = token.trim();
let token = if token.starts_with("Bot ") {
token.to_string()
} else {
format!("Bot {}", token)
};
http::set_token(&token);
let locked = Arc::new(Mutex::new(token));
let name = "serenity client".to_owned();
let threadpool = ThreadPool::with_name(name, 5);
let url = Arc::new(Mutex::new(http::get_gateway()?.url));
let data = Arc::new(Mutex::new(ShareMap::custom()));
let event_handler = Arc::new(handler);
#[cfg(feature = "framework")]
let framework = Arc::new(Mutex::new(None));
#[cfg(feature = "voice")]
let voice_manager = Arc::new(Mutex::new(ClientVoiceManager::new(
0,
UserId(0),
)));
let (shard_manager, shard_manager_worker) = {
ShardManager::new(ShardManagerOptions {
data: &data,
event_handler: &event_handler,
#[cfg(feature = "framework")]
framework: &framework,
shard_index: 0,
shard_init: 0,
shard_total: 0,
threadpool: threadpool.clone(),
token: &locked,
#[cfg(feature = "voice")]
voice_manager: &voice_manager,
ws_url: &url,
})
};
Ok(Client {
token: locked,
ws_uri: url,
#[cfg(feature = "framework")]
framework,
data,
shard_manager,
shard_manager_worker,
threadpool,
#[cfg(feature = "voice")]
voice_manager,
})
}
#[cfg(feature = "framework")]
pub fn with_framework<F: Framework + Send + 'static>(&mut self, f: F) {
*self.framework.lock() = Some(Box::new(f));
}
pub fn start(&mut self) -> Result<()> {
self.start_connection([0, 0, 1])
}
pub fn start_autosharded(&mut self) -> Result<()> {
let (x, y) = {
let res = http::get_bot_gateway()?;
(res.shards as u64 - 1, res.shards as u64)
};
self.start_connection([0, x, y])
}
pub fn start_shard(&mut self, shard: u64, shards: u64) -> Result<()> {
self.start_connection([shard, shard, shards])
}
pub fn start_shards(&mut self, total_shards: u64) -> Result<()> {
self.start_connection([0, total_shards - 1, total_shards])
}
pub fn start_shard_range(&mut self, range: [u64; 2], total_shards: u64) -> Result<()> {
self.start_connection([range[0], range[1], total_shards])
}
fn start_connection(&mut self, shard_data: [u64; 3]) -> Result<()> {
#[cfg(feature = "voice")]
self.voice_manager.lock().set_shard_count(shard_data[2]);
#[cfg(any(all(feature = "standard_framework", feature = "framework"),
feature = "voice"))]
{
let user = http::get_current_user()?;
#[cfg(all(feature = "standard_framework", feature = "framework"))]
{
if let Some(ref mut framework) = *self.framework.lock() {
framework.update_current_user(user.id);
}
}
#[cfg(feature = "voice")]
{
self.voice_manager.lock().set_user_id(user.id);
}
}
{
let mut manager = self.shard_manager.lock();
let init = shard_data[1] - shard_data[0] + 1;
manager.set_shards(shard_data[0], init, shard_data[2]);
debug!(
"Initializing shard info: {} - {}/{}",
shard_data[0],
init,
shard_data[2],
);
if let Err(why) = manager.initialize() {
error!("Failed to boot a shard: {:?}", why);
info!("Shutting down all shards");
manager.shutdown_all();
return Err(Error::Client(ClientError::ShardBootFailure));
}
}
self.shard_manager_worker.run();
Ok(())
}
}
pub fn validate_token(token: &str) -> Result<()> {
if token.is_empty() {
return Err(Error::Client(ClientError::InvalidToken));
}
let parts: Vec<&str> = token.split('.').collect();
if parts.len() != 3 {
return Err(Error::Client(ClientError::InvalidToken));
}
if parts[1].len() < 6 {
return Err(Error::Client(ClientError::InvalidToken));
}
if token.trim() != token {
return Err(Error::Client(ClientError::InvalidToken));
}
Ok(())
}