#![allow(dead_code)]
#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
extern crate chrono;
#[macro_use]
extern crate lazy_static;
extern crate ts3plugin_sys;
pub use ts3plugin_sys::clientlib_publicdefinitions::*;
pub use ts3plugin_sys::plugin_definitions::*;
pub use ts3plugin_sys::public_definitions::*;
pub use ts3plugin_sys::public_errors::Error;
pub use ts3plugin_sys::ts3functions::Ts3Functions;
pub use plugin::*;
use std::collections::HashMap as Map;
use std::ffi::{CStr, CString};
use std::mem::transmute;
use std::os::raw::{c_char, c_int};
use chrono::*;
macro_rules! to_cstring {
($string: expr) => {
CString::new($string).unwrap_or(
CString::new("String contains null character").unwrap())
};
}
macro_rules! to_string {
($string: expr) => {
String::from_utf8_lossy(CStr::from_ptr($string).to_bytes()).into_owned()
};
}
pub mod ts3interface;
pub mod plugin;
include!(concat!(env!("OUT_DIR"), "/structs.rs"));
pub struct TsApi {
servers: Map<ServerId, Server>,
plugin_id: String,
}
#[derive(Eq, Clone)]
pub struct Invoker {
id: ConnectionId,
uid: String,
name: String,
}
#[derive(Clone)]
pub enum MessageReceiver {
Connection(ConnectionId),
Channel,
Server,
}
#[derive(Clone)]
pub struct Permissions;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub struct ServerId(u64);
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub struct ChannelId(u64);
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub struct ConnectionId(u16);
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub struct PermissionId(u32);
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub struct ServerGroupId(u64);
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub struct ChannelGroupId(u64);
impl PartialEq<Invoker> for Invoker {
fn eq(&self, other: &Invoker) -> bool {
self.id == other.id
}
}
impl Invoker {
fn new(id: ConnectionId, uid: String, name: String) -> Invoker {
Invoker {
id: id,
uid: uid,
name: name,
}
}
pub fn get_id(&self) -> ConnectionId {
self.id
}
pub fn get_uid(&self) -> &String {
&self.uid
}
pub fn get_name(&self) -> &String {
&self.name
}
}
impl PartialEq<Server> for Server {
fn eq(&self, other: &Server) -> bool {
self.id == other.id
}
}
impl Eq for Server {}
impl Server {
fn get_property_as_string(id: ServerId, property: VirtualServerProperties) -> Result<String, Error> {
unsafe {
let mut name: *mut c_char = std::ptr::null_mut();
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_server_variable_as_string)
(id.0, property as usize, &mut name));
match res {
Error::Ok => Ok(to_string!(name)),
_ => Err(res)
}
}
}
fn get_property_as_int(id: ServerId, property: VirtualServerProperties) -> Result<i32, Error> {
unsafe {
let mut number: c_int = 0;
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_server_variable_as_int)
(id.0, property as usize, &mut number));
match res {
Error::Ok => Ok(number as i32),
_ => Err(res)
}
}
}
fn get_property_as_uint64(id: ServerId, property: VirtualServerProperties) -> Result<u64, Error> {
unsafe {
let mut number: u64 = 0;
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_server_variable_as_uint64)
(id.0, property as usize, &mut number));
match res {
Error::Ok => Ok(number),
_ => Err(res)
}
}
}
fn query_own_connection_id(id: ServerId) -> Result<ConnectionId, Error> {
unsafe {
let mut number: u16 = 0;
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_client_id)
(id.0, &mut number));
match res {
Error::Ok => Ok(ConnectionId(number)),
_ => Err(res)
}
}
}
fn query_connections(id: ServerId) -> Map<ConnectionId, Connection> {
let mut map = Map::new();
let mut result: *mut u16 = std::ptr::null_mut();
let res: Error = unsafe { transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_client_list)
(id.0, &mut result)) };
if res == Error::Ok {
unsafe {
let mut counter = 0;
while *result.offset(counter) != 0 {
let connection_id = ConnectionId(*result.offset(counter));
let mut connection = Connection::new(id, connection_id);
connection.update();
map.insert(connection_id, connection);
counter += 1;
}
}
}
map
}
fn query_channels(id: ServerId) -> Result<Map<ChannelId, Channel>, Error> {
let mut map = Map::new();
let mut result: *mut u64 = std::ptr::null_mut();
let res: Error = unsafe { transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_channel_list)
(id.0, &mut result)) };
if res == Error::Ok {
unsafe {
let mut counter = 0;
while *result.offset(counter) != 0 {
let channel_id = ChannelId(*result.offset(counter));
let mut channel = Channel::new(id, channel_id);
channel.update();
map.insert(channel_id, channel);
counter += 1;
}
}
Ok(map)
} else {
Err(res)
}
}
fn add_connection(&mut self, connection_id: ConnectionId) -> &mut Connection {
let mut connection = Connection::new(self.id, connection_id);
connection.update();
self.visible_connections.insert(connection_id, connection);
self.visible_connections.get_mut(&connection_id).unwrap()
}
fn remove_connection(&mut self, connection_id: ConnectionId) -> Option<Connection> {
self.visible_connections.remove(&connection_id)
}
fn add_channel(&mut self, channel_id: ChannelId) -> Result<&mut Channel, Error> {
match self.channels {
Ok(ref mut cs) => {
let mut channel = Channel::new(self.id, channel_id);
channel.update();
cs.insert(channel_id, channel);
Ok(cs.get_mut(&channel_id).unwrap())
}
Err(error) => Err(error),
}
}
fn remove_channel(&mut self, channel_id: ChannelId) -> Option<Channel> {
self.channels.as_mut().ok().and_then(|mut cs| cs.remove(&channel_id))
}
pub fn get_connection_ids(&self) -> Vec<ConnectionId> {
self.visible_connections.keys().cloned().collect()
}
pub fn get_channel_ids(&self) -> Vec<ChannelId> {
match self.channels {
Ok(ref cs) => cs.keys().cloned().collect(),
Err(_) => Vec::new(),
}
}
pub fn get_connection(&self, connection_id: ConnectionId) -> Option<&Connection> {
self.visible_connections.get(&connection_id)
}
pub fn get_mut_connection(&mut self, connection_id: ConnectionId) -> Option<&mut Connection> {
self.visible_connections.get_mut(&connection_id)
}
pub fn get_channel(&self, channel_id: ChannelId) -> Option<&Channel> {
self.channels.as_ref().ok().and_then(|cs| cs.get(&channel_id))
}
pub fn get_mut_channel(&mut self, channel_id: ChannelId) -> Option<&mut Channel> {
self.channels.as_mut().ok().and_then(|cs| cs.get_mut(&channel_id))
}
pub fn send_message<S: AsRef<str>>(&self, message: S) -> Result<(), Error> {
unsafe {
let text = to_cstring!(message.as_ref());
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").request_send_server_text_msg)
(self.id.0, text.as_ptr(), std::ptr::null()));
match res {
Error::Ok => Ok(()),
_ => Err(res)
}
}
}
pub fn print_message<S: AsRef<str>>(&self, message: S, target: MessageTarget) {
unsafe {
let text = to_cstring!(message.as_ref());
(TS3_FUNCTIONS.as_ref().expect("Functions should be loaded").print_message)
(self.id.0, text.as_ptr(), target);
}
}
}
impl PartialEq<Channel> for Channel {
fn eq(&self, other: &Channel) -> bool {
self.server_id == other.server_id && self.id == other.id
}
}
impl Eq for Channel {}
impl Channel {
fn get_property_as_string(server_id: ServerId, id: ChannelId, property: ChannelProperties) -> Result<String, Error> {
unsafe {
let mut name: *mut c_char = std::ptr::null_mut();
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_channel_variable_as_string)
(server_id.0, id.0, property as usize, &mut name));
match res {
Error::Ok => Ok(to_string!(name)),
_ => Err(res)
}
}
}
fn get_property_as_int(server_id: ServerId, id: ChannelId, property: ChannelProperties) -> Result<i32, Error> {
unsafe {
let mut number: c_int = 0;
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_channel_variable_as_int)
(server_id.0, id.0, property as usize, &mut number));
match res {
Error::Ok => Ok(number as i32),
_ => Err(res)
}
}
}
fn get_property_as_uint64(server_id: ServerId, id: ChannelId, property: ChannelProperties) -> Result<i32, Error> {
unsafe {
let mut number: u64 = 0;
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_channel_variable_as_uint64)
(server_id.0, id.0, property as usize, &mut number));
match res {
Error::Ok => Ok(number as i32),
_ => Err(res)
}
}
}
fn query_parent_channel_id(server_id: ServerId, id: ChannelId) -> Result<ChannelId, Error> {
unsafe {
let mut number: u64 = 0;
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_parent_channel_of_channel)
(server_id.0, id.0, &mut number));
match res {
Error::Ok => Ok(ChannelId(number)),
_ => Err(res)
}
}
}
pub fn send_message<S: AsRef<str>>(&self, message: S) -> Result<(), Error> {
unsafe {
let text = to_cstring!(message.as_ref());
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").request_send_channel_text_msg)
(self.server_id.0, text.as_ptr(), self.id.0, std::ptr::null()));
match res {
Error::Ok => Ok(()),
_ => Err(res)
}
}
}
}
impl PartialEq<Connection> for Connection {
fn eq(&self, other: &Connection) -> bool {
self.server_id == other.server_id && self.id == other.id
}
}
impl Eq for Connection {}
impl Connection {
fn get_connection_property_as_string(server_id: ServerId, id: ConnectionId, property: ConnectionProperties) -> Result<String, Error> {
unsafe {
let mut name: *mut c_char = std::ptr::null_mut();
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_connection_variable_as_string)
(server_id.0, id.0, property as usize, &mut name));
match res {
Error::Ok => Ok(to_string!(name)),
_ => Err(res)
}
}
}
fn get_connection_property_as_uint64(server_id: ServerId, id: ConnectionId, property: ConnectionProperties) -> Result<u64, Error> {
unsafe {
let mut number: u64 = 0;
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_connection_variable_as_uint64)
(server_id.0, id.0, property as usize, &mut number));
match res {
Error::Ok => Ok(number),
_ => Err(res)
}
}
}
fn get_connection_property_as_double(server_id: ServerId, id: ConnectionId, property: ConnectionProperties) -> Result<f64, Error> {
unsafe {
let mut number: f64 = 0.0;
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_connection_variable_as_double)
(server_id.0, id.0, property as usize, &mut number));
match res {
Error::Ok => Ok(number),
_ => Err(res)
}
}
}
fn get_client_property_as_string(server_id: ServerId, id: ConnectionId, property: ClientProperties) -> Result<String, Error> {
unsafe {
let mut name: *mut c_char = std::ptr::null_mut();
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_client_variable_as_string)
(server_id.0, id.0, property as usize, &mut name));
match res {
Error::Ok => Ok(to_string!(name)),
_ => Err(res)
}
}
}
fn get_client_property_as_int(server_id: ServerId, id: ConnectionId, property: ClientProperties) -> Result<c_int, Error> {
unsafe {
let mut number: c_int = 0;
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_client_variable_as_int)
(server_id.0, id.0, property as usize, &mut number));
match res {
Error::Ok => Ok(number),
_ => Err(res)
}
}
}
fn query_channel_id(server_id: ServerId, id: ConnectionId) -> Result<ChannelId, Error> {
unsafe {
let mut number: u64 = 0;
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_channel_of_client)
(server_id.0, id.0, &mut number));
match res {
Error::Ok => Ok(ChannelId(number)),
_ => Err(res)
}
}
}
fn query_whispering(server_id: ServerId, id: ConnectionId) -> Result<bool, Error> {
unsafe {
let mut number: c_int = 0;
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").is_whispering)
(server_id.0, id.0, &mut number));
match res {
Error::Ok => Ok(number != 0),
_ => Err(res)
}
}
}
pub fn send_message<S: AsRef<str>>(&self, message: S) -> Result<(), Error> {
unsafe {
let text = to_cstring!(message.as_ref());
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").request_send_private_text_msg)
(self.server_id.0, text.as_ptr(), self.id.0, std::ptr::null()));
match res {
Error::Ok => Ok(()),
_ => Err(res)
}
}
}
}
static mut TS3_FUNCTIONS: Option<Ts3Functions> = None;
impl TsApi {
fn new(plugin_id: String) -> TsApi {
TsApi {
servers: Map::new(),
plugin_id: plugin_id,
}
}
fn load(&mut self) -> Result<(), Error> {
let mut result: *mut u64 = std::ptr::null_mut();
let res: Error = unsafe { transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_server_connection_handler_list)
(&mut result)) };
match res {
Error::Ok => unsafe {
let mut counter = 0;
while *result.offset(counter) != 0 {
let mut status: c_int = 0;
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_connection_status)
(*result.offset(counter), &mut status));
if res == Error::Ok && transmute::<c_int, ConnectStatus>(status) != ConnectStatus::Disconnected {
self.add_server(ServerId(*result.offset(counter)));
}
counter += 1;
}
},
_ => return Err(res)
}
Ok(())
}
pub fn static_log_message<S1: AsRef<str>, S2: AsRef<str>>(message: S1, channel: S2, severity: LogLevel) -> Result<(), Error> {
unsafe {
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").log_message)
(to_cstring!(message.as_ref()).as_ptr(),
severity, to_cstring!(channel.as_ref()).as_ptr(), 0));
match res {
Error::Ok => Ok(()),
_ => Err(res)
}
}
}
pub fn static_log_or_print<S1: AsRef<str>, S2: AsRef<str>>(message: S1, channel: S2, severity: LogLevel) {
if let Err(error) = TsApi::static_log_message(message.as_ref(), channel.as_ref(), severity) {
println!("Error {:?} while printing '{}' to '{}' ({:?})", error,
message.as_ref(), channel.as_ref(), severity);
}
}
pub fn static_get_error_message(error: Error) -> Result<String, Error> {
unsafe {
let mut message: *mut c_char = std::ptr::null_mut();
let res: Error = transmute((TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_error_message)
(error as u32, &mut message));
match res {
Error::Ok => Ok(to_string!(message)),
_ => Err(res)
}
}
}
fn add_server(&mut self, server_id: ServerId) -> &mut Server {
self.servers.insert(server_id, Server::new(server_id));
let mut server = self.servers.get_mut(&server_id).unwrap();
server.update();
server
}
fn remove_server(&mut self, server_id: ServerId) -> bool {
self.servers.remove(&server_id).is_some()
}
fn try_update_invoker(&mut self, server_id: ServerId, invoker: &Invoker) {
if let Some(server) = self.get_mut_server(server_id) {
if let Some(mut connection) = server.get_mut_connection(invoker.get_id()) {
if connection.get_uid() != Ok(invoker.get_uid()) {
connection.uid = Ok(invoker.get_uid().clone());
}
if connection.get_name() != Ok(invoker.get_name()) {
connection.name = Ok(invoker.get_name().clone())
}
}
}
}
fn get_path<F: Fn(*mut c_char, usize)>(fun: F) -> String {
const START_SIZE: usize = 512;
const MAX_SIZE: usize = 1000000;
let mut size = START_SIZE;
loop {
let mut buf = vec![0 as u8; size];
fun(buf.as_mut_ptr() as *mut c_char, size - 1);
if buf[size - 3] != 0 {
size *= 2;
} else {
buf[size - 1] = 0;
let s = unsafe { CStr::from_ptr(buf.as_ptr() as *const c_char) };
let result = s.to_string_lossy();
return result.into_owned();
}
}
}
pub unsafe fn get_raw_api(&self) -> &Ts3Functions {
TS3_FUNCTIONS.as_ref().unwrap()
}
pub fn get_plugin_id(&self) -> &str {
&self.plugin_id
}
pub fn get_server_ids(&self) -> Vec<ServerId> {
self.servers.keys().cloned().collect()
}
pub fn log_message<S1: AsRef<str>, S2: AsRef<str>>(&self, message: S1, channel: S2, severity: LogLevel) -> Result<(), Error> {
TsApi::static_log_message(message, channel, severity)
}
pub fn log_or_print<S1: AsRef<str>, S2: AsRef<str>>(&self, message: S1, channel: S2, severity: LogLevel) {
TsApi::static_log_or_print(message, channel, severity)
}
pub fn get_server(&self, server_id: ServerId) -> Option<&Server> {
self.servers.get(&server_id)
}
pub fn get_mut_server(&mut self, server_id: ServerId) -> Option<&mut Server> {
self.servers.get_mut(&server_id)
}
pub fn print_message<S: AsRef<str>>(&self, message: S) {
unsafe {
let text = to_cstring!(message.as_ref());
(TS3_FUNCTIONS.as_ref().expect("Functions should be loaded").print_message_to_current_tab)
(text.as_ptr());
}
}
pub fn get_app_path(&self) -> String {
unsafe {
TsApi::get_path(|p, l| (TS3_FUNCTIONS.as_ref().expect("Functions should be loaded").get_app_path)(p, l))
}
}
pub fn get_resources_path(&self) -> String {
unsafe {
TsApi::get_path(|p, l| (TS3_FUNCTIONS.as_ref().expect("Functions should be loaded").get_resources_path)(p, l))
}
}
pub fn get_config_path(&self) -> String {
unsafe {
TsApi::get_path(|p, l| (TS3_FUNCTIONS.as_ref().expect("Functions should be loaded").get_config_path)(p, l))
}
}
pub fn get_plugin_path(&self) -> String {
unsafe {
TsApi::get_path(|p, l| (TS3_FUNCTIONS.as_ref()
.expect("Functions should be loaded").get_plugin_path)(p, l,
to_cstring!(self.plugin_id.as_str()).as_ptr()))
}
}
}