plugin.cpp 34 KB
Newer Older
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
* TeamSpeak 3 demo plugin
*
* Copyright (c) 2008-2016 TeamSpeak Systems GmbH
*/

#ifdef _WIN32
#pragma warning (disable : 4100)  /* Disable Unreferenced parameter warning */
#include <Windows.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "teamspeak/public_errors.h"
#include "teamspeak/public_errors_rare.h"
#include "teamspeak/public_definitions.h"
#include "teamspeak/public_rare_definitions.h"
#include "teamspeak/clientlib_publicdefinitions.h"
#include "ts3_functions.h"
#include "plugin.h"
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
23
#include <QMap>
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46

#pragma region defines, global variables

static struct TS3Functions ts3Functions;

#ifdef _WIN32
#define _strcpy(dest, destSize, src) strcpy_s(dest, destSize, src)
#define snprintf sprintf_s
#else
#define _strcpy(dest, destSize, src) { strncpy(dest, src, destSize-1); (dest)[destSize-1] = '\0'; }
#endif

#define PLUGIN_API_VERSION 21

#define PATH_BUFSIZE 512
#define COMMAND_BUFSIZE 128
#define INFODATA_BUFSIZE 128
#define SERVERINFO_BUFSIZE 256
#define CHANNELINFO_BUFSIZE 512
#define RETURNCODE_BUFSIZE 128

static char* pluginID = NULL;

Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
47
QMap<uint64, OverlayController*> g_serverList;
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

#ifdef _WIN32
/* Helper function to convert wchar_T to Utf-8 encoded strings on Windows */
static int wcharToUtf8(const wchar_t* str, char** result) {
	int outlen = WideCharToMultiByte(CP_UTF8, 0, str, -1, 0, 0, 0, 0);
	*result = (char*)malloc(outlen);
	if (WideCharToMultiByte(CP_UTF8, 0, str, -1, *result, outlen, 0, 0) == 0) {
		*result = NULL;
		return -1;
	}
	return 0;
}
#endif

#pragma endregion


65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
/****************************** Helper functions ********************************/
/*
* Following functions are helper for the callbacks.
*/

#pragma region helper functions

uint64 getCurrentChannel(uint64 serverConnectionHandlerID)
{
	uint64 curChannel;
	anyID curClient;

	ts3Functions.getClientID(serverConnectionHandlerID, &curClient);
	ts3Functions.getChannelOfClient(serverConnectionHandlerID, curClient, &curChannel);

	return curChannel;
}

QString clientID2Name(uint64 serverConnectionHandlerID, anyID clientID)
{
	char* tmp;
	if (ts3Functions.getClientVariableAsString(serverConnectionHandlerID, clientID, CLIENT_NICKNAME, &tmp) != ERROR_ok)
		return QString();

	QString name(tmp);
90
	ts3Functions.freeMemory(tmp);
91 92 93 94 95 96 97 98 99 100
	return name;
}

QString channelID2Name(uint64 serverConnectionHandlerID, uint64 channelID)
{
	char* tmp;
	if (ts3Functions.getChannelVariableAsString(serverConnectionHandlerID, channelID, CHANNEL_NAME, &tmp) != ERROR_ok)
		return QString();

	QString name(tmp);
101
	ts3Functions.freeMemory(tmp);
102 103 104
	return name;
}

105
OverlayController* getController(uint64 serverConnectionHandlerID = 0)
106
{
107 108 109
	if (serverConnectionHandlerID == 0)
	{
		OverlayController* ret = NULL;
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
110

111
		for (auto it : g_serverList.keys())
112 113 114
		{
			int tmp;

115 116
			if (ts3Functions.getClientSelfVariableAsInt(it, CLIENT_INPUT_HARDWARE, &tmp) == ERROR_ok)
				if (tmp == 1)
117 118 119 120 121
					ret = g_serverList.find(it).value();
		}
		return ret;
	}

122 123 124 125 126 127 128 129 130 131 132 133
	auto tmp = g_serverList.find(serverConnectionHandlerID);

	if (tmp == g_serverList.end())
	{
		OverlayController* ret = new OverlayController(ts3Functions, serverConnectionHandlerID);
		g_serverList.insert(serverConnectionHandlerID, ret);
		return ret;
	}

	return tmp.value();
}

134 135 136 137 138 139 140 141 142 143
void setActiveServer(uint64 serverConnectionHanderlID)
{
	// mute all server
	for (auto& it : g_serverList)
		it->mute(true);

	// unmute the current server
	getController(serverConnectionHanderlID)->mute(false);
}

144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
/* Helper function to create a hotkey */
static struct PluginHotkey* createHotkey(const char* keyword, const char* description) {
	struct PluginHotkey* hotkey = (struct PluginHotkey*)malloc(sizeof(struct PluginHotkey));
	_strcpy(hotkey->keyword, PLUGIN_HOTKEY_BUFSZ, keyword);
	_strcpy(hotkey->description, PLUGIN_HOTKEY_BUFSZ, description);
	return hotkey;
}

/* Some makros to make the code to create hotkeys a bit more readable */
#define BEGIN_CREATE_HOTKEYS(x) const size_t sz = x + 1; size_t n = 0; *hotkeys = (struct PluginHotkey**)malloc(sizeof(struct PluginHotkey*) * sz);
#define CREATE_HOTKEY(a, b) (*hotkeys)[n++] = createHotkey(a, b);
#define END_CREATE_HOTKEYS (*hotkeys)[n++] = NULL; assert(n == sz);

#pragma endregion


Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
/*********************************** Required functions ************************************/
/*
* If any of these required functions is not implemented, TS3 will refuse to load the plugin
*/

#pragma region required functions

/* Unique name identifying this plugin */
const char* ts3plugin_name() {
#ifdef _WIN32
	/* TeamSpeak expects UTF-8 encoded characters. Following demonstrates a possibility how to convert UTF-16 wchar_t into UTF-8. */
	static char* result = NULL;  /* Static variable so it's allocated only once */
	if (!result) {
		const wchar_t* name = L"Cute Overlay";
		if (wcharToUtf8(name, &result) == -1) {  /* Convert name into UTF-8 encoded result */
			result = "Cute Overlay";  /* Conversion failed, fallback here */
		}
	}
	return result;
#else
	return "Cute Overlay";
#endif
}

/* Plugin version */
const char* ts3plugin_version() {
186
	return "1.0";
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
}

/* Plugin API version. Must be the same as the clients API major version, else the plugin fails to load. */
int ts3plugin_apiVersion() {
	return PLUGIN_API_VERSION;
}

/* Plugin author */
const char* ts3plugin_author() {
	/* If you want to use wchar_t, see ts3plugin_name() on how to use */
	return "GT-Anakin";
}

/* Plugin description */
const char* ts3plugin_description() {
	/* If you want to use wchar_t, see ts3plugin_name() on how to use */
	return "This plugin is a simple overlay based on Qt";
}

/* Set TeamSpeak 3 callback functions */
void ts3plugin_setFunctionPointers(const struct TS3Functions funcs) {
	ts3Functions = funcs;
}

/*
* Custom code called right after loading the plugin. Returns 0 on success, 1 on failure.
* If the function returns 1 on failure, the plugin will be unloaded again.
*/
int ts3plugin_init() {
216 217 218
	char pluginPath[PATH_BUFSIZE];

	ts3Functions.getPluginPath(pluginPath, PATH_BUFSIZE, pluginID);
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
219 220 221 222 223 224 225 226 227 228

	return 0;  /* 0 = success, 1 = failure, -2 = failure but client will not show a "failed to load" warning */
			   /* -2 is a very special case and should only be used if a plugin displays a dialog (e.g. overlay) asking the user to disable
			   * the plugin again, avoiding the show another dialog by the client telling the user the plugin failed to load.
			   * For normal case, if a plugin really failed to load because of an error, the correct return value is 1. */
}

/* Custom code called right before the plugin is unloaded */
void ts3plugin_shutdown() {

Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
229
	while (g_serverList.size() > 0)
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
230
		delete g_serverList.take(g_serverList.firstKey());
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
231

232 233 234 235
	if (pluginID) {
		free(pluginID);
		pluginID = NULL;
	}
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275

	/*
	* Note:
	* If your plugin implements a settings dialog, it must be closed and deleted here, else the
	* TeamSpeak client will most likely crash (DLL removed but dialog from DLL code still open).
	*/
}

#pragma endregion


/****************************** Optional functions ********************************/
/*
* Following functions are optional, if not needed you don't need to implement them.
*/

#pragma region optional functions

/* Tell client if plugin offers a configuration window. If this function is not implemented, it's an assumed "does not offer" (PLUGIN_OFFERS_NO_CONFIGURE). */
int ts3plugin_offersConfigure() {
	/*
	* Return values:
	* PLUGIN_OFFERS_NO_CONFIGURE         - Plugin does not implement ts3plugin_configure
	* PLUGIN_OFFERS_CONFIGURE_NEW_THREAD - Plugin does implement ts3plugin_configure and requests to run this function in an own thread
	* PLUGIN_OFFERS_CONFIGURE_QT_THREAD  - Plugin does implement ts3plugin_configure and requests to run this function in the Qt GUI thread
	*/
	return PLUGIN_OFFERS_NO_CONFIGURE;  /* In this case ts3plugin_configure does not need to be implemented */
}

/* Plugin might offer a configuration window. If ts3plugin_offersConfigure returns 0, this function does not need to be implemented. */
void ts3plugin_configure(void* handle, void* qParentWidget) {

}

/*
* If the plugin wants to use error return codes, plugin commands, hotkeys or menu items, it needs to register a command ID. This function will be
* automatically called after the plugin was initialized. This function is optional. If you don't use these features, this function can be omitted.
* Note the passed pluginID parameter is no longer valid after calling this function, so you must copy it and store it in the plugin.
*/
void ts3plugin_registerPluginID(const char* id) {
276 277 278
	const size_t sz = strlen(id) + 1;
	pluginID = (char*)malloc(sz * sizeof(char));
	_strcpy(pluginID, sz, id);
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
}

/* Plugin command keyword. Return NULL or "" if not used. */
const char* ts3plugin_commandKeyword() {
	return NULL;
}

/* Plugin processes console command. Return 0 if plugin handled the command, 1 if not handled. */
int ts3plugin_processCommand(uint64 serverConnectionHandlerID, const char* command) {
	return 1;  /* Plugin handled command */
}

/* Client changed current server connection handler */
void ts3plugin_currentServerConnectionChanged(uint64 serverConnectionHandlerID) {
}

/*
* Implement the following three functions when the plugin should display a line in the server/channel/client info.
* If any of ts3plugin_infoTitle, ts3plugin_infoData or ts3plugin_freeMemory is missing, the info text will not be displayed.
*/

/* Static title shown in the left column in the info frame */
const char* ts3plugin_infoTitle() {
	return "";
}

/*
* Dynamic content shown in the right column in the info frame. Memory for the data string needs to be allocated in this
* function. The client will call ts3plugin_freeMemory once done with the string to release the allocated memory again.
* Check the parameter "type" if you want to implement this feature only for specific item types. Set the parameter
* "data" to NULL to have the client ignore the info data.
*/
void ts3plugin_infoData(uint64 serverConnectionHandlerID, uint64 id, enum PluginItemType type, char** data) {
}

/* Required to release the memory for parameter "data" allocated in ts3plugin_infoData and ts3plugin_initMenus */
void ts3plugin_freeMemory(void* data) {
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
316
	free(data);
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
317 318 319 320 321 322 323 324 325 326 327
}

/*
* Plugin requests to be always automatically loaded by the TeamSpeak 3 client unless
* the user manually disabled it in the plugin dialog.
* This function is optional. If missing, no autoload is assumed.
*/
int ts3plugin_requestAutoload() {
	return 0;  /* 1 = request autoloaded, 0 = do not request autoload */
}

328
/*
329 330 331
* Initialize plugin hotkeys. If your plugin does not use this feature, this function can be omitted.
* Hotkeys require ts3plugin_registerPluginID and ts3plugin_freeMemory to be implemented.
* This function is automatically called by the client after ts3plugin_init.
332
*/
333 334 335 336
void ts3plugin_initHotkeys(struct PluginHotkey*** hotkeys) {
	/* Register hotkeys giving a keyword and a description.
	* The keyword will be later passed to ts3plugin_onHotkeyEvent to identify which hotkey was triggered.
	* The description is shown in the clients hotkey dialog. */
337
	BEGIN_CREATE_HOTKEYS(1);  /* Create 3 hotkeys. Size must be correct for allocating memory. */
338
	CREATE_HOTKEY("Channellist", "Displays a simple channellist of the server");
339
	END_CREATE_HOTKEYS;
340

341
	/* The client will call ts3plugin_freeMemory to release all allocated memory */
342 343
}

Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
344
#pragma endregion
345 346


Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
347 348 349 350 351 352 353 354 355 356 357 358 359 360
/************************** TeamSpeak callbacks ***************************/
/*
* Following functions are optional, feel free to remove unused callbacks.
* See the clientlib documentation for details on each function.
*/

#pragma region callbacks clientlib

/* Clientlib */

void ts3plugin_onConnectStatusChangeEvent(uint64 serverConnectionHandlerID, int newStatus, unsigned int errorNumber) {
	
	if (newStatus == STATUS_DISCONNECTED)
	{
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
361 362
		auto tmp = g_serverList.take(serverConnectionHandlerID);
		delete tmp;
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
363
	}
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
364 365 366 367 368
	else if (newStatus == STATUS_CONNECTED)
	{
		// let helper insert new controller to avoid double entries
		getController(serverConnectionHandlerID);
	}
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
369 370
	else if (newStatus == STATUS_CONNECTION_ESTABLISHED)
	{
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
371
		getController(serverConnectionHandlerID)->updateChannelList();
372
		getController(serverConnectionHandlerID)->updateClientList();
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
373
		getController(serverConnectionHandlerID)->displayChannelList();
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
374 375 376 377 378 379 380
	}
}

void ts3plugin_onNewChannelEvent(uint64 serverConnectionHandlerID, uint64 channelID, uint64 channelParentID) {
}

void ts3plugin_onNewChannelCreatedEvent(uint64 serverConnectionHandlerID, uint64 channelID, uint64 channelParentID, anyID invokerID, const char* invokerName, const char* invokerUniqueIdentifier) {
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
381
	getController(serverConnectionHandlerID)->updateChannelList();
382
	getController(serverConnectionHandlerID)->updateClientList();
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
383 384 385
}

void ts3plugin_onDelChannelEvent(uint64 serverConnectionHandlerID, uint64 channelID, anyID invokerID, const char* invokerName, const char* invokerUniqueIdentifier) {
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
386
	getController(serverConnectionHandlerID)->updateChannelList();
387
	getController(serverConnectionHandlerID)->updateClientList();
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
388 389 390
}

void ts3plugin_onChannelMoveEvent(uint64 serverConnectionHandlerID, uint64 channelID, uint64 newChannelParentID, anyID invokerID, const char* invokerName, const char* invokerUniqueIdentifier) {
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
391
	getController(serverConnectionHandlerID)->updateChannelList();
392
	getController(serverConnectionHandlerID)->updateClientList();
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
393 394 395 396 397 398
}

void ts3plugin_onUpdateChannelEvent(uint64 serverConnectionHandlerID, uint64 channelID) {
}

void ts3plugin_onUpdateChannelEditedEvent(uint64 serverConnectionHandlerID, uint64 channelID, anyID invokerID, const char* invokerName, const char* invokerUniqueIdentifier) {
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
399
	getController(serverConnectionHandlerID)->updateChannelList();
400
	getController(serverConnectionHandlerID)->updateClientList();
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
401 402 403 404 405 406
}

void ts3plugin_onUpdateClientEvent(uint64 serverConnectionHandlerID, anyID clientID, anyID invokerID, const char* invokerName, const char* invokerUniqueIdentifier) {
}

void ts3plugin_onClientMoveEvent(uint64 serverConnectionHandlerID, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility, const char* moveMessage) {
407

Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
408
	getController(serverConnectionHandlerID)->updateClientList();
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
409

410
	QString ClientName = clientID2Name(serverConnectionHandlerID, clientID);
411 412 413 414 415 416
	QString oldChannel = channelID2Name(serverConnectionHandlerID, oldChannelID);
	QString newChannel = channelID2Name(serverConnectionHandlerID, newChannelID);

	anyID myID;
	ts3Functions.getClientID(serverConnectionHandlerID, &myID);

417
	if (!ClientName.isEmpty() && !oldChannel.isEmpty() && !newChannel.isEmpty())
418 419
	{
		// joined my channel
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
420
		if (newChannelID == getCurrentChannel(serverConnectionHandlerID))
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
421
			getController(serverConnectionHandlerID)->addChatLine(QString("<font color=\"#00FFFF\">%1 <font size=\"-5\">entered from</font> %2</font>").arg(ClientName.toHtmlEscaped()).arg(oldChannel));
422
		// quit my channel
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
423
		else if (oldChannelID == getCurrentChannel(serverConnectionHandlerID))
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
424
			getController(serverConnectionHandlerID)->addChatLine(QString("<font color=\"#00FFFF\">%1 <font size=\"-5\">left to</font> %2</font>").arg(ClientName.toHtmlEscaped()).arg(newChannel));
425 426
		// somewhere else
		else
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
427
			getController(serverConnectionHandlerID)->addChatLine(QString("<font size=\"-5\">%1 moved from %2 to %3</font>").arg(ClientName.toHtmlEscaped()).arg(oldChannel).arg(newChannel));
428 429 430
	}
	else if (!ClientName.isEmpty() && oldChannel.isEmpty() && !newChannel.isEmpty())
	{
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
431
		getController(serverConnectionHandlerID)->addChatLine(QString("%1 <font size=\"-5\">connected to</font> %2").arg(ClientName.toHtmlEscaped()).arg(newChannel));
432
	}
433

Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
434 435 436 437 438 439
}

void ts3plugin_onClientMoveSubscriptionEvent(uint64 serverConnectionHandlerID, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility) {
}

void ts3plugin_onClientMoveTimeoutEvent(uint64 serverConnectionHandlerID, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility, const char* timeoutMessage) {
440

Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
441
	getController(serverConnectionHandlerID)->updateClientList();
442 443 444 445 446 447 448 449 450 451 452 453

	QString ClientName = clientID2Name(serverConnectionHandlerID, clientID);
	QString oldChannel = channelID2Name(serverConnectionHandlerID, oldChannelID);
	QString newChannel = channelID2Name(serverConnectionHandlerID, newChannelID);

	anyID myID;
	ts3Functions.getClientID(serverConnectionHandlerID, &myID);

	if (!ClientName.isEmpty() && !oldChannel.isEmpty() && !newChannel.isEmpty())
	{
		// joined my channel
		if (newChannelID == getCurrentChannel(serverConnectionHandlerID))
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
454
			getController(serverConnectionHandlerID)->addChatLine(QString("<font color=\"#00FFFF\">%1 <font size=\"-5\">timeout from</font> %2</font>").arg(ClientName.toHtmlEscaped()).arg(oldChannel));
455 456
		// quit my channel
		else if (oldChannelID == getCurrentChannel(serverConnectionHandlerID))
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
457
			getController(serverConnectionHandlerID)->addChatLine(QString("<font color=\"#00FFFF\">%1 <font size=\"-5\">timeout to</font> %2</font>").arg(ClientName.toHtmlEscaped()).arg(newChannel));
458 459
		// somewhere else
		else
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
460
			getController(serverConnectionHandlerID)->addChatLine(QString("<font size=\"-5\">%1 timeout from %2 to %3</font>").arg(ClientName.toHtmlEscaped()).arg(oldChannel).arg(newChannel));
461
	}
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
462 463 464
}

void ts3plugin_onClientMoveMovedEvent(uint64 serverConnectionHandlerID, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility, anyID moverID, const char* moverName, const char* moverUniqueIdentifier, const char* moveMessage) {
465

Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
466
	getController(serverConnectionHandlerID)->updateClientList();
467 468 469 470 471 472 473 474 475 476 477 478

	QString ClientName = clientID2Name(serverConnectionHandlerID, clientID);
	QString oldChannel = channelID2Name(serverConnectionHandlerID, oldChannelID);
	QString newChannel = channelID2Name(serverConnectionHandlerID, newChannelID);

	anyID myID;
	ts3Functions.getClientID(serverConnectionHandlerID, &myID);

	if (!ClientName.isEmpty() && !oldChannel.isEmpty() && !newChannel.isEmpty())
	{
		// joined my channel
		if (newChannelID == getCurrentChannel(serverConnectionHandlerID))
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
479
			getController(serverConnectionHandlerID)->addChatLine(QString("<font color=\"#00FFFF\">%1 <font size=\"-5\">entered from</font> %2 <font size=\"-5\">by</font> %3</font>").arg(ClientName.toHtmlEscaped()).arg(oldChannel).arg(QString(moverName).toHtmlEscaped()));
480 481
		// quit my channel
		else if (oldChannelID == getCurrentChannel(serverConnectionHandlerID))
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
482
			getController(serverConnectionHandlerID)->addChatLine(QString("<font color=\"#00FFFF\">%1 <font size=\"-5\">left to</font> %2 <font size=\"-5\">left by</font> %3</font>").arg(ClientName.toHtmlEscaped()).arg(newChannel).arg(QString(moverName).toHtmlEscaped()));
483 484
		// somewhere else
		else
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
485
			getController(serverConnectionHandlerID)->addChatLine(QString("<font size=\"-5\">%1 moved from %2 to %3 by %4</font>").arg(ClientName.toHtmlEscaped()).arg(oldChannel).arg(newChannel).arg(QString(moverName).toHtmlEscaped()));
486
	}
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
487 488 489
}

void ts3plugin_onClientKickFromChannelEvent(uint64 serverConnectionHandlerID, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility, anyID kickerID, const char* kickerName, const char* kickerUniqueIdentifier, const char* kickMessage) {
490

Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
491
	getController(serverConnectionHandlerID)->updateClientList();
492 493 494 495 496 497 498

	QString ClientName = clientID2Name(serverConnectionHandlerID, clientID);
	QString oldChannel = channelID2Name(serverConnectionHandlerID, oldChannelID);
	QString newChannel = channelID2Name(serverConnectionHandlerID, newChannelID);

	if (!ClientName.isEmpty() && !oldChannel.isEmpty() && !newChannel.isEmpty())
	{
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
499
		getController(serverConnectionHandlerID)->addChatLine(QString("<font color=\"#FF0000\">%1 <font size=\"-5\">was from</font> %2 <font size=\"-5\">to</font> %3 <font size=\"-5\">by</font> %4 <font size=\"-5\">because</font> %5</font>").arg(ClientName.toHtmlEscaped()).arg(oldChannel).arg(newChannel).arg(QString(kickerName).toHtmlEscaped()).arg(kickMessage));
500
	}
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
501 502 503
}

void ts3plugin_onClientKickFromServerEvent(uint64 serverConnectionHandlerID, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility, anyID kickerID, const char* kickerName, const char* kickerUniqueIdentifier, const char* kickMessage) {
504

Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
505
	getController(serverConnectionHandlerID)->updateClientList();
506 507 508 509 510

	QString ClientName = clientID2Name(serverConnectionHandlerID, clientID);

	if (!ClientName.isEmpty())
	{
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
511
		getController(serverConnectionHandlerID)->addChatLine(QString("<font color=\"#FF0000\">%1 <font size=\"-5\">was from Server by</font> %2 <font size=\"-5\">because</font> %3</font>").arg(ClientName.toHtmlEscaped()).arg(QString(kickerName).toHtmlEscaped()).arg(kickMessage));
512
	}
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
}

void ts3plugin_onClientIDsEvent(uint64 serverConnectionHandlerID, const char* uniqueClientIdentifier, anyID clientID, const char* clientName) {
}

void ts3plugin_onClientIDsFinishedEvent(uint64 serverConnectionHandlerID) {
}

void ts3plugin_onServerEditedEvent(uint64 serverConnectionHandlerID, anyID editerID, const char* editerName, const char* editerUniqueIdentifier) {
}

void ts3plugin_onServerUpdatedEvent(uint64 serverConnectionHandlerID) {
}

int ts3plugin_onServerErrorEvent(uint64 serverConnectionHandlerID, const char* errorMessage, unsigned int error, const char* returnCode, const char* extraMessage) {
	return 0;  /* If no plugin return code was used, the return value of this function is ignored */
}

void ts3plugin_onServerStopEvent(uint64 serverConnectionHandlerID, const char* shutdownMessage) {
}

int ts3plugin_onTextMessageEvent(uint64 serverConnectionHandlerID, anyID targetMode, anyID toID, anyID fromID, const char* fromName, const char* fromUniqueIdentifier, const char* message, int ffIgnored) {
	
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
536
	getController(serverConnectionHandlerID)->addChatLine(QString("<font size=\"-5\">&lt;%1&gt;</font> %2").arg(QString(fromName).toHtmlEscaped()).arg(message));
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
537 538 539 540 541 542 543 544 545 546

	return 0;  /* 0 = handle normally, 1 = client will ignore the text message */
}

void ts3plugin_onTalkStatusChangeEvent(uint64 serverConnectionHandlerID, int status, int isReceivedWhisper, anyID clientID) {

	char name[512];
	if (ts3Functions.getClientDisplayName(serverConnectionHandlerID, clientID, name, 512) == ERROR_ok)
	{
		if (status == STATUS_TALKING)
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
547
			getController(serverConnectionHandlerID)->addSpeaker(name);
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
548
		else
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
549
			getController(serverConnectionHandlerID)->removeSpeaker(name);
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
550 551 552 553 554 555 556 557 558 559 560 561 562
	}
}

void ts3plugin_onConnectionInfoEvent(uint64 serverConnectionHandlerID, anyID clientID) {
}

void ts3plugin_onServerConnectionInfoEvent(uint64 serverConnectionHandlerID) {
}

void ts3plugin_onChannelSubscribeEvent(uint64 serverConnectionHandlerID, uint64 channelID) {
}

void ts3plugin_onChannelSubscribeFinishedEvent(uint64 serverConnectionHandlerID) {
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
563
	getController(serverConnectionHandlerID)->updateClientList();
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
}

void ts3plugin_onChannelUnsubscribeEvent(uint64 serverConnectionHandlerID, uint64 channelID) {
}

void ts3plugin_onChannelUnsubscribeFinishedEvent(uint64 serverConnectionHandlerID) {
}

void ts3plugin_onChannelDescriptionUpdateEvent(uint64 serverConnectionHandlerID, uint64 channelID) {
}

void ts3plugin_onChannelPasswordChangedEvent(uint64 serverConnectionHandlerID, uint64 channelID) {
}

void ts3plugin_onPlaybackShutdownCompleteEvent(uint64 serverConnectionHandlerID) {
}

void ts3plugin_onSoundDeviceListChangedEvent(const char* modeID, int playOrCap) {
}

void ts3plugin_onEditPlaybackVoiceDataEvent(uint64 serverConnectionHandlerID, anyID clientID, short* samples, int sampleCount, int channels) {
}

void ts3plugin_onEditPostProcessVoiceDataEvent(uint64 serverConnectionHandlerID, anyID clientID, short* samples, int sampleCount, int channels, const unsigned int* channelSpeakerArray, unsigned int* channelFillMask) {
}

void ts3plugin_onEditMixedPlaybackVoiceDataEvent(uint64 serverConnectionHandlerID, short* samples, int sampleCount, int channels, const unsigned int* channelSpeakerArray, unsigned int* channelFillMask) {
}

void ts3plugin_onEditCapturedVoiceDataEvent(uint64 serverConnectionHandlerID, short* samples, int sampleCount, int channels, int* edited) {
}

void ts3plugin_onCustom3dRolloffCalculationClientEvent(uint64 serverConnectionHandlerID, anyID clientID, float distance, float* volume) {
}

void ts3plugin_onCustom3dRolloffCalculationWaveEvent(uint64 serverConnectionHandlerID, uint64 waveHandle, float distance, float* volume) {
}

void ts3plugin_onUserLoggingMessageEvent(const char* logMessage, int logLevel, const char* logChannel, uint64 logID, const char* logTime, const char* completeLogString) {
}

#pragma endregion


/* Clientlib rare */

#pragma region callbacks clientlib rare

void ts3plugin_onClientBanFromServerEvent(uint64 serverConnectionHandlerID, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility, anyID kickerID, const char* kickerName, const char* kickerUniqueIdentifier, uint64 time, const char* kickMessage) {
}

int ts3plugin_onClientPokeEvent(uint64 serverConnectionHandlerID, anyID fromClientID, const char* pokerName, const char* pokerUniqueIdentity, const char* message, int ffIgnored) {
	
617
	getController(serverConnectionHandlerID)->addChatLine(QString("<font color=\"#00FF00\"><font size=\"-5\">%1</font> Wake up! %2</font>").arg(pokerName).arg(message));
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
618 619 620 621 622

	return 0;  /* 0 = handle normally, 1 = client will ignore the poke */
}

void ts3plugin_onClientSelfVariableUpdateEvent(uint64 serverConnectionHandlerID, int flag, const char* oldValue, const char* newValue) {
623 624 625 626 627 628 629 630 631 632

	if (flag == CLIENT_INPUT_HARDWARE)
	{
		// from mute to speak
		if (atoi(oldValue) == 0 && atoi(newValue) == 1)
			setActiveServer(serverConnectionHandlerID);
		// this should not happen
		else
			getController(serverConnectionHandlerID)->debugPrint("This should not happen");
	}
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802
}

void ts3plugin_onFileListEvent(uint64 serverConnectionHandlerID, uint64 channelID, const char* path, const char* name, uint64 size, uint64 datetime, int type, uint64 incompletesize, const char* returnCode) {
}

void ts3plugin_onFileListFinishedEvent(uint64 serverConnectionHandlerID, uint64 channelID, const char* path) {
}

void ts3plugin_onFileInfoEvent(uint64 serverConnectionHandlerID, uint64 channelID, const char* name, uint64 size, uint64 datetime) {
}

void ts3plugin_onServerGroupListEvent(uint64 serverConnectionHandlerID, uint64 serverGroupID, const char* name, int type, int iconID, int saveDB) {
}

void ts3plugin_onServerGroupListFinishedEvent(uint64 serverConnectionHandlerID) {
}

void ts3plugin_onServerGroupByClientIDEvent(uint64 serverConnectionHandlerID, const char* name, uint64 serverGroupList, uint64 clientDatabaseID) {
}

void ts3plugin_onServerGroupPermListEvent(uint64 serverConnectionHandlerID, uint64 serverGroupID, unsigned int permissionID, int permissionValue, int permissionNegated, int permissionSkip) {
}

void ts3plugin_onServerGroupPermListFinishedEvent(uint64 serverConnectionHandlerID, uint64 serverGroupID) {
}

void ts3plugin_onServerGroupClientListEvent(uint64 serverConnectionHandlerID, uint64 serverGroupID, uint64 clientDatabaseID, const char* clientNameIdentifier, const char* clientUniqueID) {
}

void ts3plugin_onChannelGroupListEvent(uint64 serverConnectionHandlerID, uint64 channelGroupID, const char* name, int type, int iconID, int saveDB) {
}

void ts3plugin_onChannelGroupListFinishedEvent(uint64 serverConnectionHandlerID) {
}

void ts3plugin_onChannelGroupPermListEvent(uint64 serverConnectionHandlerID, uint64 channelGroupID, unsigned int permissionID, int permissionValue, int permissionNegated, int permissionSkip) {
}

void ts3plugin_onChannelGroupPermListFinishedEvent(uint64 serverConnectionHandlerID, uint64 channelGroupID) {
}

void ts3plugin_onChannelPermListEvent(uint64 serverConnectionHandlerID, uint64 channelID, unsigned int permissionID, int permissionValue, int permissionNegated, int permissionSkip) {
}

void ts3plugin_onChannelPermListFinishedEvent(uint64 serverConnectionHandlerID, uint64 channelID) {
}

void ts3plugin_onClientPermListEvent(uint64 serverConnectionHandlerID, uint64 clientDatabaseID, unsigned int permissionID, int permissionValue, int permissionNegated, int permissionSkip) {
}

void ts3plugin_onClientPermListFinishedEvent(uint64 serverConnectionHandlerID, uint64 clientDatabaseID) {
}

void ts3plugin_onChannelClientPermListEvent(uint64 serverConnectionHandlerID, uint64 channelID, uint64 clientDatabaseID, unsigned int permissionID, int permissionValue, int permissionNegated, int permissionSkip) {
}

void ts3plugin_onChannelClientPermListFinishedEvent(uint64 serverConnectionHandlerID, uint64 channelID, uint64 clientDatabaseID) {
}

void ts3plugin_onClientChannelGroupChangedEvent(uint64 serverConnectionHandlerID, uint64 channelGroupID, uint64 channelID, anyID clientID, anyID invokerClientID, const char* invokerName, const char* invokerUniqueIdentity) {
}

int ts3plugin_onServerPermissionErrorEvent(uint64 serverConnectionHandlerID, const char* errorMessage, unsigned int error, const char* returnCode, unsigned int failedPermissionID) {
	return 0;  /* See onServerErrorEvent for return code description */
}

void ts3plugin_onPermissionListGroupEndIDEvent(uint64 serverConnectionHandlerID, unsigned int groupEndID) {
}

void ts3plugin_onPermissionListEvent(uint64 serverConnectionHandlerID, unsigned int permissionID, const char* permissionName, const char* permissionDescription) {
}

void ts3plugin_onPermissionListFinishedEvent(uint64 serverConnectionHandlerID) {
}

void ts3plugin_onPermissionOverviewEvent(uint64 serverConnectionHandlerID, uint64 clientDatabaseID, uint64 channelID, int overviewType, uint64 overviewID1, uint64 overviewID2, unsigned int permissionID, int permissionValue, int permissionNegated, int permissionSkip) {
}

void ts3plugin_onPermissionOverviewFinishedEvent(uint64 serverConnectionHandlerID) {
}

void ts3plugin_onServerGroupClientAddedEvent(uint64 serverConnectionHandlerID, anyID clientID, const char* clientName, const char* clientUniqueIdentity, uint64 serverGroupID, anyID invokerClientID, const char* invokerName, const char* invokerUniqueIdentity) {
}

void ts3plugin_onServerGroupClientDeletedEvent(uint64 serverConnectionHandlerID, anyID clientID, const char* clientName, const char* clientUniqueIdentity, uint64 serverGroupID, anyID invokerClientID, const char* invokerName, const char* invokerUniqueIdentity) {
}

void ts3plugin_onClientNeededPermissionsEvent(uint64 serverConnectionHandlerID, unsigned int permissionID, int permissionValue) {
}

void ts3plugin_onClientNeededPermissionsFinishedEvent(uint64 serverConnectionHandlerID) {
}

void ts3plugin_onFileTransferStatusEvent(anyID transferID, unsigned int status, const char* statusMessage, uint64 remotefileSize, uint64 serverConnectionHandlerID) {
}

void ts3plugin_onClientChatClosedEvent(uint64 serverConnectionHandlerID, anyID clientID, const char* clientUniqueIdentity) {
}

void ts3plugin_onClientChatComposingEvent(uint64 serverConnectionHandlerID, anyID clientID, const char* clientUniqueIdentity) {
}

void ts3plugin_onServerLogEvent(uint64 serverConnectionHandlerID, const char* logMsg) {
}

void ts3plugin_onServerLogFinishedEvent(uint64 serverConnectionHandlerID, uint64 lastPos, uint64 fileSize) {
}

void ts3plugin_onMessageListEvent(uint64 serverConnectionHandlerID, uint64 messageID, const char* fromClientUniqueIdentity, const char* subject, uint64 timestamp, int flagRead) {
}

void ts3plugin_onMessageGetEvent(uint64 serverConnectionHandlerID, uint64 messageID, const char* fromClientUniqueIdentity, const char* subject, const char* message, uint64 timestamp) {
}

void ts3plugin_onClientDBIDfromUIDEvent(uint64 serverConnectionHandlerID, const char* uniqueClientIdentifier, uint64 clientDatabaseID) {
}

void ts3plugin_onClientNamefromUIDEvent(uint64 serverConnectionHandlerID, const char* uniqueClientIdentifier, uint64 clientDatabaseID, const char* clientNickName) {
}

void ts3plugin_onClientNamefromDBIDEvent(uint64 serverConnectionHandlerID, const char* uniqueClientIdentifier, uint64 clientDatabaseID, const char* clientNickName) {
}

void ts3plugin_onComplainListEvent(uint64 serverConnectionHandlerID, uint64 targetClientDatabaseID, const char* targetClientNickName, uint64 fromClientDatabaseID, const char* fromClientNickName, const char* complainReason, uint64 timestamp) {
}

void ts3plugin_onBanListEvent(uint64 serverConnectionHandlerID, uint64 banid, const char* ip, const char* name, const char* uid, uint64 creationTime, uint64 durationTime, const char* invokerName,
	uint64 invokercldbid, const char* invokeruid, const char* reason, int numberOfEnforcements, const char* lastNickName) {
}

void ts3plugin_onClientServerQueryLoginPasswordEvent(uint64 serverConnectionHandlerID, const char* loginPassword) {
}

void ts3plugin_onPluginCommandEvent(uint64 serverConnectionHandlerID, const char* pluginName, const char* pluginCommand) {
}

void ts3plugin_onIncomingClientQueryEvent(uint64 serverConnectionHandlerID, const char* commandText) {
}

void ts3plugin_onServerTemporaryPasswordListEvent(uint64 serverConnectionHandlerID, const char* clientNickname, const char* uniqueClientIdentifier, const char* description, const char* password, uint64 timestampStart, uint64 timestampEnd, uint64 targetChannelID, const char* targetChannelPW) {
}

#pragma endregion


/* Client UI callbacks */

#pragma region callbacks client UI

/*
* Called from client when an avatar image has been downloaded to or deleted from cache.
* This callback can be called spontaneously or in response to ts3Functions.getAvatar()
*/
void ts3plugin_onAvatarUpdated(uint64 serverConnectionHandlerID, anyID clientID, const char* avatarPath) {
}

/*
* Called when a plugin menu item (see ts3plugin_initMenus) is triggered. Optional function, when not using plugin menus, do not implement this.
*
* Parameters:
* - serverConnectionHandlerID: ID of the current server tab
* - type: Type of the menu (PLUGIN_MENU_TYPE_CHANNEL, PLUGIN_MENU_TYPE_CLIENT or PLUGIN_MENU_TYPE_GLOBAL)
* - menuItemID: Id used when creating the menu item
* - selectedItemID: Channel or Client ID in the case of PLUGIN_MENU_TYPE_CHANNEL and PLUGIN_MENU_TYPE_CLIENT. 0 for PLUGIN_MENU_TYPE_GLOBAL.
*/
void ts3plugin_onMenuItemEvent(uint64 serverConnectionHandlerID, enum PluginMenuType type, int menuItemID, uint64 selectedItemID) {
}

/* This function is called if a plugin hotkey was pressed. Omit if hotkeys are unused. */
void ts3plugin_onHotkeyEvent(const char* keyword) {
803
	printf("PLUGIN: Hotkey event: %s\n", keyword);
804 805

	if (QString(keyword) == "Channellist")
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
806
	{
807 808 809 810 811
		auto controller = getController();
		if (controller != NULL)
		{
			controller->displayChannelList();
		}
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
812
	}
Carsten Fuhrmann's avatar
Carsten Fuhrmann committed
813 814 815 816 817 818 819 820 821 822 823
}

/* Called when recording a hotkey has finished after calling ts3Functions.requestHotkeyInputDialog */
void ts3plugin_onHotkeyRecordedEvent(const char* keyword, const char* key) {
}

/* Called when client custom nickname changed */
void ts3plugin_onClientDisplayNameChanged(uint64 serverConnectionHandlerID, anyID clientID, const char* displayName, const char* uniqueClientIdentifier) {
}

#pragma endregion