diff --git a/src/com/github/theholywaffle/teamspeak3/EventManager.java b/src/com/github/theholywaffle/teamspeak3/EventManager.java new file mode 100644 index 0000000..b405a5c --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/EventManager.java @@ -0,0 +1,178 @@ +package com.github.theholywaffle.teamspeak3; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.event.*; +import com.github.theholywaffle.teamspeak3.api.exception.TS3UnknownEventException; +import com.github.theholywaffle.teamspeak3.api.wrapper.Wrapper; +import com.github.theholywaffle.teamspeak3.commands.response.DefaultArrayResponse; +import net.md_5.bungee.api.ProxyServer; + +import java.util.*; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class EventManager { + + private static final Logger log = ProxyServer.getInstance().getLogger(); + + // CopyOnWriteArrayList for thread safety + private final Collection tasks = new CopyOnWriteArrayList<>(); + private final TS3Query ts3; + + EventManager(TS3Query query) { + ts3 = query; + } + + public void addListeners(TS3Listener... listeners) { + for (TS3Listener listener : listeners) { + if (listener == null) throw new IllegalArgumentException("A listener was null"); + ListenerTask task = new ListenerTask(listener); + tasks.add(task); + } + } + + public void removeListeners(TS3Listener... listeners) { + // Bad performance (O(n*m)), but this method is rarely if ever used + Iterator taskIterator = tasks.iterator(); + while (taskIterator.hasNext()) { + ListenerTask task = taskIterator.next(); + TS3Listener taskListener = task.getListener(); + + for (TS3Listener listener : listeners) { + if (taskListener.equals(listener)) { + taskIterator.remove(); + break; + } + } + } + } + + public void fireEvent(String notifyName, String notifyBody) { + final DefaultArrayResponse response = DefaultArrayResponse.parse(notifyBody); + + for (Wrapper dataWrapper : response.getResponses()) { + Map eventData = dataWrapper.getMap(); + TS3Event event = createEvent(notifyName, eventData); + + fireEvent(event); + } + } + + public void fireEvent(TS3Event event) { + if (event == null) throw new IllegalArgumentException("TS3Event was null"); + for (ListenerTask task : tasks) { + task.enqueueEvent(event); + } + } + + private static TS3Event createEvent(String notifyName, Map eventData) { + switch (notifyName) { + case "notifytextmessage": + return new TextMessageEvent(eventData); + case "notifycliententerview": + return new ClientJoinEvent(eventData); + case "notifyclientleftview": + return new ClientLeaveEvent(eventData); + case "notifyserveredited": + return new ServerEditedEvent(eventData); + case "notifychanneledited": + return new ChannelEditedEvent(eventData); + case "notifychanneldescriptionchanged": + return new ChannelDescriptionEditedEvent(eventData); + case "notifyclientmoved": + return new ClientMovedEvent(eventData); + case "notifychannelcreated": + return new ChannelCreateEvent(eventData); + case "notifychanneldeleted": + return new ChannelDeletedEvent(eventData); + case "notifychannelmoved": + return new ChannelMovedEvent(eventData); + case "notifychannelpasswordchanged": + return new ChannelPasswordChangedEvent(eventData); + case "notifytokenused": + return new PrivilegeKeyUsedEvent(eventData); + default: + throw new TS3UnknownEventException(notifyName + " " + eventData); + } + } + + /* + * Do not synchronize on instances of this class from outside the class itself! + */ + private class ListenerTask implements Runnable { + + private static final int START_QUEUE_SIZE = 16; + + private final TS3Listener listener; + private final Queue eventQueue; + + ListenerTask(TS3Listener ts3Listener) { + listener = ts3Listener; + eventQueue = new ArrayDeque<>(START_QUEUE_SIZE); + } + + TS3Listener getListener() { + return listener; + } + + synchronized void enqueueEvent(TS3Event event) { + if (eventQueue.isEmpty()) { + // Add the event to the queue and start a task to process this event and any events + // that might be enqueued before the last event is removed from the queue + eventQueue.add(event); + ts3.submitUserTask("Event listener task", this); + } else { + // Just add the event to the queue, the running task will pick it up + eventQueue.add(event); + } + } + + @Override + public void run() { + TS3Event currentEvent; + synchronized (this) { + currentEvent = eventQueue.peek(); + if (currentEvent == null) throw new IllegalStateException("Task started without events"); + } + + do { + try { + currentEvent.fire(listener); + } catch (Throwable throwable) { + log.log(Level.SEVERE, "Event listener threw an exception", throwable); + } + + synchronized (this) { + eventQueue.remove(); + currentEvent = eventQueue.peek(); + } + } while (currentEvent != null); + } + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/FileTransferHelper.java b/src/com/github/theholywaffle/teamspeak3/FileTransferHelper.java new file mode 100644 index 0000000..436e13e --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/FileTransferHelper.java @@ -0,0 +1,161 @@ +package com.github.theholywaffle.teamspeak3; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2016 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.wrapper.FileTransferParameters; +import net.md_5.bungee.api.ProxyServer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.CRC32; + +public class FileTransferHelper { + + private static final Logger log = ProxyServer.getInstance().getLogger(); + private static final int BUFFER_SIZE = 16_384; // 16 kB + + // Can only be in the range 0 - 65535 + private final AtomicInteger clientTransferId = new AtomicInteger(0); + private final String defaultHost; + + FileTransferHelper(String host) { + defaultHost = host; + } + + // FILES + + public void downloadFile(OutputStream dataOut, FileTransferParameters params) throws IOException { + final String host = getHostFromResponse(params.getFileServerHost()); + final int port = params.getFileServerPort(); + final long dataLength = params.getFileSize(); + final int downloadId = params.getClientTransferId() + 1; + + log.log(Level.INFO, "[Download {}] Download started", downloadId); + try (Socket socket = new Socket(host, port)) { + socket.setReceiveBufferSize(BUFFER_SIZE); + int actualSize = socket.getReceiveBufferSize(); + + OutputStream out = socket.getOutputStream(); + out.write(params.getFileTransferKey().getBytes("UTF-8")); + out.flush(); + + InputStream in = socket.getInputStream(); + byte[] buffer = new byte[actualSize]; + long total = 0; + while (total < dataLength) { + int read = in.read(buffer); + if (read < 0) throw new IOException("Server response contained less data than specified"); + total += read; + if (total > dataLength) throw new IOException("Server response contained more data than specified"); + dataOut.write(buffer, 0, read); + } + log.log(Level.INFO, "[Download {}] Download finished", downloadId); + } catch (IOException e) { + // Log and re-throw + log.log(Level.INFO, String.format("[Download %s] Download failed: %s", downloadId, e.getMessage())); + throw e; + } + } + + public void uploadFile(InputStream dataIn, long dataLength, FileTransferParameters params) throws IOException { + final String host = getHostFromResponse(params.getFileServerHost()); + final int port = params.getFileServerPort(); + final int uploadId = params.getClientTransferId() + 1; + + log.log(Level.INFO, "[Upload {}] Upload started", uploadId); + try (Socket socket = new Socket(host, port)) { + socket.setSendBufferSize(BUFFER_SIZE); + int actualSize = socket.getSendBufferSize(); + + OutputStream out = socket.getOutputStream(); + out.write(params.getFileTransferKey().getBytes("UTF-8")); + out.flush(); + + byte[] buffer = new byte[actualSize]; + long total = 0; + while (total < dataLength) { + int toRead = (int) Math.min(actualSize, dataLength - total); + int read = dataIn.read(buffer, 0, toRead); + if (read < 0) throw new IOException("User stream did not contain enough data"); + total += read; + out.write(buffer, 0, read); + } + log.log(Level.INFO, "[Upload {}] Upload finished", uploadId); + } catch (IOException e) { + // Log and re-throw + log.log(Level.WARNING, String.format("[Upload %s] Upload failed: %s", uploadId, e.getMessage())); + throw e; + } + } + + // ICONS + + public long getIconId(byte[] data) { + final CRC32 crc32 = new CRC32(); + crc32.update(data); + return crc32.getValue(); + } + + // UTIL + + public byte[] readFully(InputStream dataIn, long dataLength) throws IOException { + if (dataLength > Integer.MAX_VALUE - 64) throw new IOException("File too large"); + + final int len = (int) dataLength; + byte[] data = new byte[len]; + int total = 0; + while (total < len) { + int read = dataIn.read(data, total, len - total); + if (read < 0) throw new IOException("User stream did not contain enough data"); + total += read; + } + return data; + } + + public int getClientTransferId() { + // AtomicInteger#getAndUpdate without Java 8 + int prev, next; + do { + prev = clientTransferId.get(); + next = (prev + 1) & 0xFFFF; + } while (!clientTransferId.compareAndSet(prev, next)); + return prev; + } + + private String getHostFromResponse(String raw) { + if (raw == null || raw.isEmpty()) return defaultHost; + if (raw.startsWith("0.0.0.0")) return defaultHost; + int firstComma = raw.indexOf(','); + if (firstComma <= 0) return defaultHost; + return raw.substring(0, firstComma); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/KeepAliveThread.java b/src/com/github/theholywaffle/teamspeak3/KeepAliveThread.java new file mode 100644 index 0000000..3e924b1 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/KeepAliveThread.java @@ -0,0 +1,67 @@ +package com.github.theholywaffle.teamspeak3; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + + +import net.md_5.bungee.api.ProxyServer; + +import java.util.logging.Level; +import java.util.logging.Logger; + +public class KeepAliveThread extends Thread { + + private static final Logger log = ProxyServer.getInstance().getLogger(); + private static final int SLEEP = 60_000; + + private final SocketWriter writer; + private final TS3ApiAsync asyncApi; + + public KeepAliveThread(SocketWriter writer, TS3ApiAsync asyncApi) { + super("[TeamSpeak-3-Java-API] Keep alive"); + this.writer = writer; + this.asyncApi = asyncApi; + } + + @Override + public void run() { + try { + while (!isInterrupted()) { + final long idleTime = writer.getIdleTime(); + if (idleTime >= SLEEP) { + // Using the asynchronous API so we get InterruptedExceptions + asyncApi.whoAmI().await(); + } else { + Thread.sleep(SLEEP - idleTime); + } + } + } catch (final InterruptedException e) { + // Thread stopped properly, ignore + } catch (final Exception e) { + log.log(Level.WARNING, "KeepAlive thread has stopped!", e); + } + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/QueryIO.java b/src/com/github/theholywaffle/teamspeak3/QueryIO.java new file mode 100644 index 0000000..9a5c931 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/QueryIO.java @@ -0,0 +1,152 @@ +package com.github.theholywaffle.teamspeak3; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2015 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.exception.TS3ConnectionFailedException; +import com.github.theholywaffle.teamspeak3.api.exception.TS3QueryShutDownException; +import com.github.theholywaffle.teamspeak3.api.reconnect.ConnectionHandler; +import com.github.theholywaffle.teamspeak3.api.reconnect.DisconnectingConnectionHandler; +import com.github.theholywaffle.teamspeak3.commands.Command; +import com.github.theholywaffle.teamspeak3.commands.response.ResponseBuilder; + +import java.io.IOException; +import java.net.Socket; +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +public class QueryIO { + + private final Socket socket; + private final SocketReader socketReader; + private final SocketWriter socketWriter; + private final KeepAliveThread keepAlive; + + private final BlockingQueue sendQueue; + private final BlockingQueue receiveQueue; + + QueryIO(TS3Query query, TS3Config config) { + sendQueue = new LinkedBlockingQueue<>(); + ConnectionHandler handler = config.getReconnectStrategy().create(null); + if (config.getFloodRate() == TS3Query.FloodRate.UNLIMITED && handler instanceof DisconnectingConnectionHandler) { + // Don't wait for the last response before sending more commands + receiveQueue = new LinkedBlockingQueue<>(); + } else { + // Wait for the response to the last command to arrive before sending the next one + receiveQueue = new ArrayBlockingQueue<>(1); + } + + Socket tmpSocket = null; + try { + tmpSocket = new Socket(config.getHost(), config.getQueryPort()); + socket = tmpSocket; + socket.setTcpNoDelay(true); + socket.setSoTimeout(config.getCommandTimeout()); + + socketWriter = new SocketWriter(this, config); + socketReader = new SocketReader(this, socketWriter, query, config); + keepAlive = new KeepAliveThread(socketWriter, query.getAsyncApi()); + } catch (IOException e) { + // Clean up resources and fail + if (tmpSocket != null) { + try { + tmpSocket.close(); + } catch (IOException ignored) { + } + } + + throw new TS3ConnectionFailedException(e); + } + + // From here on: all resources have been initialized and are non-null + socketReader.start(); + socketWriter.start(); + keepAlive.start(); + } + + public void continueFrom(QueryIO io) { + if (io == null) return; + + // Resend commands which remained unanswered first + io.socketReader.drainCommandsTo(sendQueue); + io.socketWriter.drainCommandsTo(sendQueue); + + io.receiveQueue.clear(); + io.sendQueue.clear(); + } + + public void disconnect() { + keepAlive.interrupt(); + socketWriter.interrupt(); + socketReader.interrupt(); + + try { + keepAlive.join(); + socketWriter.join(); + socketReader.join(); + } catch (final InterruptedException e) { + // Restore the interrupt for the caller + Thread.currentThread().interrupt(); + } + + try { + socket.close(); + } catch (IOException ignored) { + } + } + + void failRemainingCommands() { + Collection commands = new ArrayList<>(receiveQueue.size() + sendQueue.size() + 1); + socketReader.drainCommandsTo(commands); + socketWriter.drainCommandsTo(commands); + + for (Command command : commands) { + command.getFuture().fail(new TS3QueryShutDownException()); + } + } + + public void enqueueCommand(Command command) { + if (command == null) throw new IllegalArgumentException("Command cannot be null!"); + sendQueue.add(command); + } + + // Internals for communication with other IO classes + + Socket getSocket() { + return socket; + } + + BlockingQueue getSendQueue() { + return sendQueue; + } + + BlockingQueue getReceiveQueue() { + return receiveQueue; + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/SocketReader.java b/src/com/github/theholywaffle/teamspeak3/SocketReader.java new file mode 100644 index 0000000..a7a2045 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/SocketReader.java @@ -0,0 +1,200 @@ +package com.github.theholywaffle.teamspeak3; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.CommandFuture; +import com.github.theholywaffle.teamspeak3.api.exception.TS3CommandFailedException; +import com.github.theholywaffle.teamspeak3.api.wrapper.QueryError; +import com.github.theholywaffle.teamspeak3.commands.Command; +import com.github.theholywaffle.teamspeak3.commands.response.DefaultArrayResponse; +import com.github.theholywaffle.teamspeak3.commands.response.ResponseBuilder; +import net.md_5.bungee.api.ProxyServer; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.SocketTimeoutException; +import java.util.Collection; +import java.util.Queue; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class SocketReader extends Thread { + + private static final Logger log = ProxyServer.getInstance().getLogger(); + + private final TS3Query ts3; + private final Queue receiveQueue; + private final BufferedReader in; + private final SocketWriter writer; + private final long commandTimeout; + private final boolean logComms; + + private String lastEvent = ""; + + public SocketReader(QueryIO io, SocketWriter writer, TS3Query ts3Query, TS3Config config) throws IOException { + super("[TeamSpeak-3-Java-API] SocketReader"); + this.receiveQueue = io.getReceiveQueue(); + this.writer = writer; + this.commandTimeout = config.getCommandTimeout(); + this.ts3 = ts3Query; + this.logComms = config.getEnableCommunicationsLogging(); + + // Connect + this.in = new BufferedReader(new InputStreamReader(io.getSocket().getInputStream(), "UTF-8")); + int i = 0; + while (i < 4 || in.ready()) { + String welcomeMessage = in.readLine(); + if (logComms) log.log(Level.FINE, "< {}", welcomeMessage); + i++; + } + } + + @Override + public void run() { + while (!isInterrupted()) { + final String line; + + try { + // Will block until a full line of text could be read. + line = in.readLine(); + } catch (SocketTimeoutException socketTimeout) { + // Really disconnected or just no data transferred for milliseconds? + if (receiveQueue.isEmpty() || writer.getIdleTime() < commandTimeout) { + continue; + } else { + log.log(Level.SEVERE, "Connection timed out.", socketTimeout); + break; + } + } catch (IOException io) { + if (!isInterrupted()) { + log.log(Level.SEVERE, "Connection error occurred.", io); + } + break; + } + + if (line == null) { + // End of stream: connection terminated by server + log.log(Level.SEVERE, "Connection closed by the server."); + break; + } else if (line.isEmpty()) { + continue; // The server is sending garbage + } + + if (line.startsWith("notify")) { + handleEvent(line); + } else { + handleCommandResponse(line); + } + } + + try { + in.close(); + } catch (IOException ignored) { + // Ignore + } + + if (!isInterrupted()) { + ts3.fireDisconnect(); + } + } + + private void handleEvent(final String event) { + if (logComms) log.log(Level.FINE, "[event] < {}", event); + + // Filter out duplicate events for join, quit and channel move events + if (!isDuplicate(event)) { + final String arr[] = event.split(" ", 2); + ts3.getEventManager().fireEvent(arr[0], arr[1]); + } + } + + private void handleCommandResponse(final String response) { + final ResponseBuilder responseBuilder = receiveQueue.peek(); + if (responseBuilder == null) { + log.log(Level.WARNING, "[UNHANDLED] < {}", response); + return; + } + + if (logComms) log.log(Level.FINE, responseBuilder.getCommand().getName() + " < " + response); + + if (response.startsWith("error ")) { + handleCommandError(responseBuilder, response); + } else { + responseBuilder.appendResponse(response); + } + } + + private void handleCommandError(ResponseBuilder responseBuilder, final String error) { + final Command command = responseBuilder.getCommand(); + if (command.getName().equals("quit")) { + // Response to a quit command received, we're done + interrupt(); + } + + receiveQueue.remove(); + + final QueryError queryError = DefaultArrayResponse.parseError(error); + final CommandFuture future = command.getFuture(); + + if (queryError.isSuccessful()) { + final DefaultArrayResponse response = responseBuilder.buildResponse(); + + ts3.submitUserTask("Future SuccessListener (" + command.getName() + ")", () -> future.set(response)); + } else { + log.log(Level.FINE, "TS3 command error: {}", queryError); + + ts3.submitUserTask("Future FailureListener (" + command.getName() + ")", + () -> future.fail(new TS3CommandFailedException(queryError, command.getName()))); + } + } + + private boolean isDuplicate(String eventMessage) { + if (!(eventMessage.startsWith("notifyclientmoved") + || eventMessage.startsWith("notifycliententerview") + || eventMessage.startsWith("notifyclientleftview"))) { + + // Event that will never cause duplicates + return false; + } + + if (eventMessage.equals(lastEvent)) { + // Duplicate event! + lastEvent = ""; // Let's only ever filter one duplicate + return true; + } + + lastEvent = eventMessage; + return false; + } + + void drainCommandsTo(Collection commands) { + for (ResponseBuilder responseBuilder : receiveQueue) { + commands.add(responseBuilder.getCommand()); + } + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/SocketWriter.java b/src/com/github/theholywaffle/teamspeak3/SocketWriter.java new file mode 100644 index 0000000..91e58b8 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/SocketWriter.java @@ -0,0 +1,112 @@ +package com.github.theholywaffle.teamspeak3; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.commands.Command; +import com.github.theholywaffle.teamspeak3.commands.response.ResponseBuilder; +import net.md_5.bungee.api.ProxyServer; + +import java.io.IOException; +import java.io.PrintStream; +import java.util.Collection; +import java.util.concurrent.BlockingQueue; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class SocketWriter extends Thread { + + private static final Logger log = ProxyServer.getInstance().getLogger(); + + private final BlockingQueue sendQueue; + private final BlockingQueue receiveQueue; + private final int floodRate; + private final boolean logComms; + private final PrintStream out; + + private volatile long lastCommandTime = System.currentTimeMillis(); + + private Command interruptedCommand = null; + + public SocketWriter(QueryIO io, TS3Config config) throws IOException { + super("[TeamSpeak-3-Java-API] SocketWriter"); + this.sendQueue = io.getSendQueue(); + this.receiveQueue = io.getReceiveQueue(); + this.floodRate = config.getFloodRate().getMs(); + this.logComms = config.getEnableCommunicationsLogging(); + this.out = new PrintStream(io.getSocket().getOutputStream(), true, "UTF-8"); + } + + @Override + public void run() { + try { + // Initial sleep to prevent flood ban shortly after connecting + if (floodRate > 0) Thread.sleep(floodRate); + + while (!isInterrupted()) { + final Command c = sendQueue.take(); + final String msg = c.toString(); + + lastCommandTime = System.currentTimeMillis(); + + try { + receiveQueue.put(new ResponseBuilder(c)); + } catch (InterruptedException e) { + // Properly handle commands removed from the sendQueue + // but not inserted into the receiveQueue + interruptedCommand = c; + interrupt(); + break; + } + + if (logComms) log.log(Level.FINE, String.format("%s > %s", c.getName(), msg)); + out.println(msg); + + if (floodRate > 0) Thread.sleep(floodRate); + } + } catch (InterruptedException e) { + // Regular shutdown + interrupt(); + } + + out.close(); + + if (!isInterrupted()) { + log.log(Level.WARNING, "SocketWriter has stopped!"); + } + } + + long getIdleTime() { + return System.currentTimeMillis() - lastCommandTime; + } + + void drainCommandsTo(Collection commands) { + if (interruptedCommand != null) { + commands.add(interruptedCommand); + } + sendQueue.drainTo(commands); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/TS3Api.java b/src/com/github/theholywaffle/teamspeak3/TS3Api.java new file mode 100644 index 0000000..0b7387d --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/TS3Api.java @@ -0,0 +1,4578 @@ +package com.github.theholywaffle.teamspeak3; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.*; +import com.github.theholywaffle.teamspeak3.api.event.TS3EventType; +import com.github.theholywaffle.teamspeak3.api.event.TS3Listener; +import com.github.theholywaffle.teamspeak3.api.exception.TS3CommandFailedException; +import com.github.theholywaffle.teamspeak3.api.exception.TS3ConnectionFailedException; +import com.github.theholywaffle.teamspeak3.api.exception.TS3FileTransferFailedException; +import com.github.theholywaffle.teamspeak3.api.wrapper.*; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * API to interact with the {@link TS3Query} synchronously. + *

+ * This class is used to easily interact with a {@link TS3Query}. It constructs commands, + * sends them to the TeamSpeak3 server, processes the response and returns the result. + *

+ * All methods in this class are synchronous, so they will block until the response arrives. + * Calls to this API will usually take about 50 milliseconds to complete (plus ping), + * but delays can range up to 4 seconds. + * If a command takes longer than 4 seconds to complete, a {@link TS3ConnectionFailedException} + * will be thrown. + *

+ * You won't be able to execute most commands while you're not logged in due to missing permissions. + * Make sure to either pass your login credentials to the {@link TS3Config} object when + * creating the {@code TS3Query} or to call {@link #login(String, String)} to log in. + *

+ * After that, most commands also require you to select a {@linkplain VirtualServer virtual server}. + * To do so, call either {@link #selectVirtualServerByPort(int)} or {@link #selectVirtualServerById(int)}. + *

+ * Be aware that many methods in this class will return {@code null} or {@code -1} if a command fails. + *

+ * + * @see TS3ApiAsync The asynchronous version of the API + */ +public class TS3Api { + + private final TS3ApiAsync asyncApi; + + /** + * Creates a new synchronous API object for the given {@code TS3Query}. + *

+ * Usually, this constructor should not be called. Use {@link TS3Query#getApi()} instead. + *

+ * + * @param asyncApi + * the asynchronous version of the API this class routes its method calls through + */ + public TS3Api(TS3ApiAsync asyncApi) { + this.asyncApi = asyncApi; + } + + /** + * Adds a new ban entry. At least one of the parameters {@code ip}, {@code name} or {@code uid} needs to be non-null. + * Returns the ID of the newly created ban entry. + * + * @param ip + * a RegEx pattern to match a client's IP against, can be {@code null} + * @param name + * a RegEx pattern to match a client's name against, can be {@code null} + * @param uid + * the unique identifier of a client, can be {@code null} + * @param timeInSeconds + * the duration of the ban in seconds. 0 equals a permanent ban + * @param reason + * the reason for the ban, can be {@code null} + * + * @return the ID of the newly created ban entry + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Pattern RegEx Pattern + * @see #addBan(String, String, String, String, long, String) + * @see Client#getId() + * @see Client#getUniqueIdentifier() + * @see ClientInfo#getIp() + */ + public int addBan(String ip, String name, String uid, long timeInSeconds, String reason) { + return asyncApi.addBan(ip, name, uid, timeInSeconds, reason).getUninterruptibly(); + } + + /** + * Adds a new ban entry. At least one of the parameters {@code ip}, {@code name}, {@code uid}, or + * {@code myTSId} needs to be non-null. Returns the ID of the newly created ban entry. + *

+ * Note that creating a ban entry for the {@code "empty"} "myTeamSpeak" ID will ban all clients who + * don't have a linked "myTeamSpeak" account. + *

+ * + * @param ip + * a RegEx pattern to match a client's IP against, can be {@code null} + * @param name + * a RegEx pattern to match a client's name against, can be {@code null} + * @param uid + * the unique identifier of a client, can be {@code null} + * @param myTSId + * the "myTeamSpeak" ID of a client, the string {@code "empty"}, or {@code null} + * @param timeInSeconds + * the duration of the ban in seconds. 0 equals a permanent ban + * @param reason + * the reason for the ban, can be {@code null} + * + * @return the ID of the newly created ban entry + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Pattern RegEx Pattern + * @see Client#getId() + * @see Client#getUniqueIdentifier() + * @see ClientInfo#getIp() + */ + public int addBan(String ip, String name, String uid, String myTSId, long timeInSeconds, String reason) { + return asyncApi.addBan(ip, name, uid, myTSId, timeInSeconds, reason).getUninterruptibly(); + } + + /** + * Adds a specified permission to a client in a specific channel. + * + * @param channelId + * the ID of the channel wherein the permission should be granted + * @param clientDBId + * the database ID of the client to add a permission to + * @param permName + * the name of the permission to grant + * @param permValue + * the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false) + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see Client#getDatabaseId() + * @see Permission + */ + public void addChannelClientPermission(int channelId, int clientDBId, String permName, int permValue) { + asyncApi.addChannelClientPermission(channelId, clientDBId, permName, permValue).getUninterruptibly(); + } + + /** + * Creates a new channel group for clients using a given name and returns its ID. + *

+ * To create channel group templates or ones for server queries, + * use {@link #addChannelGroup(String, PermissionGroupDatabaseType)}. + *

+ * + * @param name + * the name of the new channel group + * + * @return the ID of the newly created channel group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup + */ + public int addChannelGroup(String name) { + return asyncApi.addChannelGroup(name).getUninterruptibly(); + } + + /** + * Creates a new channel group using a given name and returns its ID. + * + * @param name + * the name of the new channel group + * @param type + * the desired type of channel group + * + * @return the ID of the newly created channel group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup + */ + public int addChannelGroup(String name, PermissionGroupDatabaseType type) { + return asyncApi.addChannelGroup(name, type).getUninterruptibly(); + } + + /** + * Adds a specified permission to a channel group. + * + * @param groupId + * the ID of the channel group to grant the permission + * @param permName + * the name of the permission to be granted + * @param permValue + * the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false) + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + * @see Permission + */ + public void addChannelGroupPermission(int groupId, String permName, int permValue) { + asyncApi.addChannelGroupPermission(groupId, permName, permValue).getUninterruptibly(); + } + + /** + * Adds a specified permission to a channel. + * + * @param channelId + * the ID of the channel wherein the permission should be granted + * @param permName + * the name of the permission to grant + * @param permValue + * the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false) + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see Permission + */ + public void addChannelPermission(int channelId, String permName, int permValue) { + asyncApi.addChannelPermission(channelId, permName, permValue).getUninterruptibly(); + } + + /** + * Adds a specified permission to a channel. + * + * @param clientDBId + * the database ID of the client to grant the permission + * @param permName + * the name of the permission to grant + * @param value + * the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false) + * @param skipped + * if set to {@code true}, the permission will not be overridden by channel group permissions + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see Permission + */ + public void addClientPermission(int clientDBId, String permName, int value, boolean skipped) { + asyncApi.addClientPermission(clientDBId, permName, value, skipped).getUninterruptibly(); + } + + /** + * Adds a client to the specified server group. + *

+ * Please note that a client cannot be added to default groups or template groups. + *

+ * + * @param groupId + * the ID of the server group to add the client to + * @param clientDatabaseId + * the database ID of the client to add + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + * @see Client#getDatabaseId() + */ + public void addClientToServerGroup(int groupId, int clientDatabaseId) { + asyncApi.addClientToServerGroup(groupId, clientDatabaseId).getUninterruptibly(); + } + + /** + * Submits a complaint about the specified client. + * The length of the message is limited to 200 UTF-8 bytes and BB codes in it will be ignored. + * + * @param clientDBId + * the database ID of the client + * @param message + * the message of the complaint, may not contain BB codes + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see Complaint#getMessage() + */ + public void addComplaint(int clientDBId, String message) { + asyncApi.addComplaint(clientDBId, message).getUninterruptibly(); + } + + /** + * Adds a specified permission to all server groups of the type specified by {@code type} on all virtual servers. + * + * @param type + * the kind of server group this permission should be added to + * @param permName + * the name of the permission to be granted + * @param value + * the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false) + * @param negated + * if set to true, the lowest permission value will be selected instead of the highest + * @param skipped + * if set to true, this permission will not be overridden by client or channel group permissions + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroupType + * @see Permission + */ + public void addPermissionToAllServerGroups(ServerGroupType type, String permName, int value, boolean negated, boolean skipped) { + asyncApi.addPermissionToAllServerGroups(type, permName, value, negated, skipped).getUninterruptibly(); + } + + /** + * Create a new privilege key that allows one client to join a server or channel group. + *
    + *
  • If {@code type} is set to {@linkplain PrivilegeKeyType#SERVER_GROUP SERVER_GROUP}, + * {@code groupId} is used as a server group ID and {@code channelId} is ignored.
  • + *
  • If {@code type} is set to {@linkplain PrivilegeKeyType#CHANNEL_GROUP CHANNEL_GROUP}, + * {@code groupId} is used as a channel group ID and {@code channelId} is used as the channel in which the group should be set.
  • + *
+ * + * @param type + * the type of token that should be created + * @param groupId + * the ID of the server or channel group + * @param channelId + * the ID of the channel, in case the token is channel group token + * @param description + * the description for the token, can be null + * + * @return the created token for a client to use + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see PrivilegeKeyType + * @see #addPrivilegeKeyServerGroup(int, String) + * @see #addPrivilegeKeyChannelGroup(int, int, String) + */ + public String addPrivilegeKey(PrivilegeKeyType type, int groupId, int channelId, String description) { + return asyncApi.addPrivilegeKey(type, groupId, channelId, description).getUninterruptibly(); + } + + /** + * Creates a new privilege key for a channel group. + * + * @param channelGroupId + * the ID of the channel group + * @param channelId + * the ID of the channel in which the channel group should be set + * @param description + * the description for the token, can be null + * + * @return the created token for a client to use + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + * @see Channel#getId() + * @see #addPrivilegeKey(PrivilegeKeyType, int, int, String) + * @see #addPrivilegeKeyServerGroup(int, String) + */ + public String addPrivilegeKeyChannelGroup(int channelGroupId, int channelId, String description) { + return asyncApi.addPrivilegeKeyChannelGroup(channelGroupId, channelId, description).getUninterruptibly(); + } + + /** + * Creates a new privilege key for a server group. + * + * @param serverGroupId + * the ID of the server group + * @param description + * the description for the token, can be null + * + * @return the created token for a client to use + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + * @see #addPrivilegeKey(PrivilegeKeyType, int, int, String) + * @see #addPrivilegeKeyChannelGroup(int, int, String) + */ + public String addPrivilegeKeyServerGroup(int serverGroupId, String description) { + return asyncApi.addPrivilegeKeyServerGroup(serverGroupId, description).getUninterruptibly(); + } + + /** + * Creates a new server group for clients using a given name and returns its ID. + *

+ * To create server group templates or ones for server queries, + * use {@link #addServerGroup(String, PermissionGroupDatabaseType)}. + *

+ * + * @param name + * the name of the new server group + * + * @return the ID of the newly created server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup + */ + public int addServerGroup(String name) { + return asyncApi.addServerGroup(name).getUninterruptibly(); + } + + /** + * Creates a new server group using a given name and returns its ID. + * + * @param name + * the name of the new server group + * @param type + * the desired type of server group + * + * @return the ID of the newly created server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup + * @see PermissionGroupDatabaseType + */ + public int addServerGroup(String name, PermissionGroupDatabaseType type) { + return asyncApi.addServerGroup(name, type).getUninterruptibly(); + } + + /** + * Adds a specified permission to a server group. + * + * @param groupId + * the ID of the channel group to which the permission should be added + * @param permName + * the name of the permission to add + * @param value + * the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false) + * @param negated + * if set to true, the lowest permission value will be selected instead of the highest + * @param skipped + * if set to true, this permission will not be overridden by client or channel group permissions + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + * @see Permission + */ + public void addServerGroupPermission(int groupId, String permName, int value, boolean negated, boolean skipped) { + asyncApi.addServerGroupPermission(groupId, permName, value, negated, skipped).getUninterruptibly(); + } + + /** + * Adds one or more {@link TS3Listener}s to the event manager of the query. + * These listeners will be notified when the TS3 server fires an event. + *

+ * Note that for the TS3 server to fire events, you must first also register + * the event types you want to listen to. + *

+ * + * @param listeners + * one or more listeners to register + * + * @see #registerAllEvents() + * @see #registerEvent(TS3EventType, int) + * @see TS3Listener + * @see TS3EventType + */ + public void addTS3Listeners(TS3Listener... listeners) { + asyncApi.addTS3Listeners(listeners); + } + + /** + * Bans a client with a given client ID for a given time. + *

+ * Please note that this will create 2 or 3 separate ban rules, + * one for the targeted client's IP address, one for their unique identifier, + * and potentially one more for their "myTeamSpeak" ID. + *

+ * Exception: If the banned client connects via a loopback address + * (i.e. {@code 127.0.0.1} or {@code localhost}), no IP ban is created + * and the returned array will only have 1 entry. + *

+ * + * @param clientId + * the ID of the client + * @param timeInSeconds + * the duration of the ban in seconds. 0 equals a permanent ban + * + * @return an array containing the IDs of the created ban entries + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see #addBan(String, String, String, long, String) + */ + public int[] banClient(int clientId, long timeInSeconds) { + return asyncApi.banClient(clientId, timeInSeconds).getUninterruptibly(); + } + + /** + * Bans a client with a given client ID for a given time for the specified reason. + *

+ * Please note that this will create two separate ban rules, + * one for the targeted client's IP address and their unique identifier. + *

+ * Exception: If the banned client connects via a loopback address + * (i.e. {@code 127.0.0.1} or {@code localhost}), no IP ban is created + * and the returned array will only have 1 entry. + *

+ * + * @param clientId + * the ID of the client + * @param timeInSeconds + * the duration of the ban in seconds. 0 equals a permanent ban + * @param reason + * the reason for the ban, can be null + * + * @return an array containing the IDs of the first and the second ban entry + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see #addBan(String, String, String, long, String) + */ + public int[] banClient(int clientId, long timeInSeconds, String reason) { + return asyncApi.banClient(clientId, timeInSeconds, reason).getUninterruptibly(); + } + + /** + * Bans a client with a given client ID permanently for the specified reason. + *

+ * Please note that this will create two separate ban rules, + * one for the targeted client's IP address and their unique identifier. + *

+ * Exception: If the banned client connects via a loopback address + * (i.e. {@code 127.0.0.1} or {@code localhost}), no IP ban is created + * and the returned array will only have 1 entry. + *

+ * + * @param clientId + * the ID of the client + * @param reason + * the reason for the ban, can be null + * + * @return an array containing the IDs of the first and the second ban entry + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see #addBan(String, String, String, long, String) + */ + public int[] banClient(int clientId, String reason) { + return asyncApi.banClient(clientId, reason).getUninterruptibly(); + } + + /** + * Sends a text message to all clients on all virtual servers. + * These messages will appear to clients in the tab for server messages. + * + * @param message + * the message to be sent + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public void broadcast(String message) { + asyncApi.broadcast(message).getUninterruptibly(); + } + + /** + * Creates a copy of the channel group specified by {@code sourceGroupId}, + * overwriting any other channel group specified by {@code targetGroupId}. + *

+ * The parameter {@code type} can be used to create server query and template groups. + *

+ * + * @param sourceGroupId + * the ID of the channel group to copy + * @param targetGroupId + * the ID of another channel group to overwrite + * @param type + * the desired type of channel group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + */ + public void copyChannelGroup(int sourceGroupId, int targetGroupId, PermissionGroupDatabaseType type) { + asyncApi.copyChannelGroup(sourceGroupId, targetGroupId, type).getUninterruptibly(); + } + + /** + * Creates a copy of the channel group specified by {@code sourceGroupId} with a given name + * and returns the ID of the newly created channel group. + * + * @param sourceGroupId + * the ID of the channel group to copy + * @param targetName + * the name for the copy of the channel group + * @param type + * the desired type of channel group + * + * @return the ID of the newly created channel group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + */ + public int copyChannelGroup(int sourceGroupId, String targetName, PermissionGroupDatabaseType type) { + return asyncApi.copyChannelGroup(sourceGroupId, targetName, type).getUninterruptibly(); + } + + /** + * Creates a copy of the server group specified by {@code sourceGroupId}, + * overwriting another server group specified by {@code targetGroupId}. + *

+ * The parameter {@code type} can be used to create server query and template groups. + *

+ * + * @param sourceGroupId + * the ID of the server group to copy + * @param targetGroupId + * the ID of another server group to overwrite + * @param type + * the desired type of server group + * + * @return the ID of the newly created server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + */ + public int copyServerGroup(int sourceGroupId, int targetGroupId, PermissionGroupDatabaseType type) { + return asyncApi.copyServerGroup(sourceGroupId, targetGroupId, type).getUninterruptibly(); + } + + /** + * Creates a copy of the server group specified by {@code sourceGroupId} with a given name + * and returns the ID of the newly created server group. + * + * @param sourceGroupId + * the ID of the server group to copy + * @param targetName + * the name for the copy of the server group + * @param type + * the desired type of server group + * + * @return the ID of the newly created server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + */ + public int copyServerGroup(int sourceGroupId, String targetName, PermissionGroupDatabaseType type) { + return asyncApi.copyServerGroup(sourceGroupId, targetName, type).getUninterruptibly(); + } + + /** + * Creates a new channel with a given name using the given properties and returns its ID. + * + * @param name + * the name for the new channel + * @param options + * a map of options that should be set for the channel + * + * @return the ID of the newly created channel + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel + */ + public int createChannel(String name, Map options) { + return asyncApi.createChannel(name, options).getUninterruptibly(); + } + + /** + * Creates a new directory on the file repository in the specified channel. + * + * @param directoryPath + * the path to the directory that should be created + * @param channelId + * the ID of the channel the directory should be created in + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public void createFileDirectory(String directoryPath, int channelId) { + asyncApi.createFileDirectory(directoryPath, channelId).getUninterruptibly(); + } + + /** + * Creates a new directory on the file repository in the specified channel. + * + * @param directoryPath + * the path to the directory that should be created + * @param channelId + * the ID of the channel the directory should be created in + * @param channelPassword + * the password of that channel + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public void createFileDirectory(String directoryPath, int channelId, String channelPassword) { + asyncApi.createFileDirectory(directoryPath, channelId, channelPassword).getUninterruptibly(); + } + + /** + * Creates a new virtual server with the given name and returns an object containing the ID of the newly + * created virtual server, the default server admin token and the virtual server's voice port. Usually, + * the virtual server is also automatically started. This can be turned off on the TS3 server, though. + *

+ * If {@link VirtualServerProperty#VIRTUALSERVER_PORT} is not specified in the virtual server properties, + * the server will test for the first unused UDP port. + *

+ * Please also note that creating virtual servers usually requires the server query admin account + * and that there is a limit to how many virtual servers can be created, which is dependent on your license. + * Unlicensed TS3 server instances are limited to 1 virtual server with up to 32 client slots. + *

+ * + * @param name + * the name for the new virtual server + * @param options + * a map of options that should be set for the virtual server + * + * @return information about the newly created virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see VirtualServer + */ + public CreatedVirtualServer createServer(String name, Map options) { + return asyncApi.createServer(name, options).getUninterruptibly(); + } + + /** + * Creates a {@link Snapshot} of the selected virtual server containing all settings, + * groups and known client identities. The data from a server snapshot can be + * used to restore a virtual servers configuration. + * + * @return a snapshot of the virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #deployServerSnapshot(Snapshot) + */ + public Snapshot createServerSnapshot() { + return asyncApi.createServerSnapshot().getUninterruptibly(); + } + + /** + * Deletes all active ban rules from the server. Use with caution. + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public void deleteAllBans() { + asyncApi.deleteAllBans().getUninterruptibly(); + } + + /** + * Deletes all complaints about the client with specified database ID from the server. + * + * @param clientDBId + * the database ID of the client + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see Complaint + */ + public void deleteAllComplaints(int clientDBId) { + asyncApi.deleteAllComplaints(clientDBId).getUninterruptibly(); + } + + /** + * Deletes the ban rule with the specified ID from the server. + * + * @param banId + * the ID of the ban to delete + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Ban#getId() + */ + public void deleteBan(int banId) { + asyncApi.deleteBan(banId).getUninterruptibly(); + } + + /** + * Deletes an existing channel specified by its ID, kicking all clients out of the channel. + * + * @param channelId + * the ID of the channel to delete + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see #deleteChannel(int, boolean) + * @see #kickClientFromChannel(String, int...) + */ + public void deleteChannel(int channelId) { + asyncApi.deleteChannel(channelId).getUninterruptibly(); + } + + /** + * Deletes an existing channel with a given ID. + * If {@code force} is true, the channel will be deleted even if there are clients within, + * else the command will fail in this situation. + * + * @param channelId + * the ID of the channel to delete + * @param force + * whether clients should be kicked out of the channel + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see #kickClientFromChannel(String, int...) + */ + public void deleteChannel(int channelId, boolean force) { + asyncApi.deleteChannel(channelId, force).getUninterruptibly(); + } + + /** + * Removes a specified permission from a client in a specific channel. + * + * @param channelId + * the ID of the channel wherein the permission should be removed + * @param clientDBId + * the database ID of the client + * @param permName + * the name of the permission to revoke + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see Client#getDatabaseId() + * @see Permission#getName() + */ + public void deleteChannelClientPermission(int channelId, int clientDBId, String permName) { + asyncApi.deleteChannelClientPermission(channelId, clientDBId, permName).getUninterruptibly(); + } + + /** + * Removes the channel group with the given ID. + * + * @param groupId + * the ID of the channel group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + */ + public void deleteChannelGroup(int groupId) { + asyncApi.deleteChannelGroup(groupId).getUninterruptibly(); + } + + /** + * Removes the channel group with the given ID. + * If {@code force} is true, the channel group will be deleted even if it still contains clients, + * else the command will fail in this situation. + * + * @param groupId + * the ID of the channel group + * @param force + * whether the channel group should be deleted even if it still contains clients + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + */ + public void deleteChannelGroup(int groupId, boolean force) { + asyncApi.deleteChannelGroup(groupId, force).getUninterruptibly(); + } + + /** + * Removes a permission from the channel group with the given ID. + * + * @param groupId + * the ID of the channel group + * @param permName + * the name of the permission to revoke + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + * @see Permission#getName() + */ + public void deleteChannelGroupPermission(int groupId, String permName) { + asyncApi.deleteChannelGroupPermission(groupId, permName).getUninterruptibly(); + } + + /** + * Removes a permission from the channel with the given ID. + * + * @param channelId + * the ID of the channel + * @param permName + * the name of the permission to revoke + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see Permission#getName() + */ + public void deleteChannelPermission(int channelId, String permName) { + asyncApi.deleteChannelPermission(channelId, permName).getUninterruptibly(); + } + + /** + * Removes a permission from a client. + * + * @param clientDBId + * the database ID of the client + * @param permName + * the name of the permission to revoke + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see Permission#getName() + */ + public void deleteClientPermission(int clientDBId, String permName) { + asyncApi.deleteClientPermission(clientDBId, permName).getUninterruptibly(); + } + + /** + * Deletes the complaint about the client with database ID {@code targetClientDBId} submitted by + * the client with database ID {@code fromClientDBId} from the server. + * + * @param targetClientDBId + * the database ID of the client the complaint is about + * @param fromClientDBId + * the database ID of the client who added the complaint + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Complaint + * @see Client#getDatabaseId() + */ + public void deleteComplaint(int targetClientDBId, int fromClientDBId) { + asyncApi.deleteComplaint(targetClientDBId, fromClientDBId).getUninterruptibly(); + } + + /** + * Removes the {@code key} custom client property from a client. + * + * @param clientDBId + * the database ID of the target client + * @param key + * the key of the custom property to delete, cannot be {@code null} + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + */ + public void deleteCustomClientProperty(int clientDBId, String key) { + asyncApi.deleteCustomClientProperty(clientDBId, key).getUninterruptibly(); + } + + /** + * Removes all stored database information about the specified client. + * Please note that this data is also automatically removed after a configured time (usually 90 days). + *

+ * See {@link DatabaseClientInfo} for a list of stored information about a client. + *

+ * + * @param clientDBId + * the database ID of the client + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see #getDatabaseClientInfo(int) + * @see DatabaseClientInfo + */ + public void deleteDatabaseClientProperties(int clientDBId) { + asyncApi.deleteDatabaseClientProperties(clientDBId).getUninterruptibly(); + } + + /** + * Deletes a file or directory from the file repository in the specified channel. + * + * @param filePath + * the path to the file or directory + * @param channelId + * the ID of the channel the file or directory resides in + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public void deleteFile(String filePath, int channelId) { + asyncApi.deleteFile(filePath, channelId).getUninterruptibly(); + } + + /** + * Deletes a file or directory from the file repository in the specified channel. + * + * @param filePath + * the path to the file or directory + * @param channelId + * the ID of the channel the file or directory resides in + * @param channelPassword + * the password of that channel + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public void deleteFile(String filePath, int channelId, String channelPassword) { + asyncApi.deleteFile(filePath, channelId, channelPassword).getUninterruptibly(); + } + + /** + * Deletes multiple files or directories from the file repository in the specified channel. + * + * @param filePaths + * the paths to the files or directories + * @param channelId + * the ID of the channel the file or directory resides in + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public void deleteFiles(String[] filePaths, int channelId) { + asyncApi.deleteFiles(filePaths, channelId).getUninterruptibly(); + } + + /** + * Deletes multiple files or directories from the file repository in the specified channel. + * + * @param filePaths + * the paths to the files or directories + * @param channelId + * the ID of the channel the file or directory resides in + * @param channelPassword + * the password of that channel + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public void deleteFiles(String[] filePaths, int channelId, String channelPassword) { + asyncApi.deleteFiles(filePaths, channelId, channelPassword).getUninterruptibly(); + } + + /** + * Deletes an icon from the icon directory in the file repository. + * + * @param iconId + * the ID of the icon to delete + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see IconFile#getIconId() + */ + public void deleteIcon(long iconId) { + asyncApi.deleteIcon(iconId).getUninterruptibly(); + } + + /** + * Deletes multiple icons from the icon directory in the file repository. + * + * @param iconIds + * the IDs of the icons to delete + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see IconFile#getIconId() + */ + public void deleteIcons(long... iconIds) { + asyncApi.deleteIcons(iconIds).getUninterruptibly(); + } + + /** + * Deletes the offline message with the specified ID. + * + * @param messageId + * the ID of the offline message to delete + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Message#getId() + */ + public void deleteOfflineMessage(int messageId) { + asyncApi.deleteOfflineMessage(messageId).getUninterruptibly(); + } + + /** + * Removes a specified permission from all server groups of the type specified by {@code type} on all virtual servers. + * + * @param type + * the kind of server group this permission should be removed from + * @param permName + * the name of the permission to remove + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroupType + * @see Permission#getName() + */ + public void deletePermissionFromAllServerGroups(ServerGroupType type, String permName) { + asyncApi.deletePermissionFromAllServerGroups(type, permName).getUninterruptibly(); + } + + /** + * Deletes the privilege key with the given token. + * + * @param token + * the token of the privilege key + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see PrivilegeKey + */ + public void deletePrivilegeKey(String token) { + asyncApi.deletePrivilegeKey(token).getUninterruptibly(); + } + + /** + * Deletes the virtual server with the specified ID. + *

+ * Only stopped virtual servers can be deleted. + *

+ * + * @param serverId + * the ID of the virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see VirtualServer#getId() + * @see #stopServer(int) + */ + public void deleteServer(int serverId) { + asyncApi.deleteServer(serverId).getUninterruptibly(); + } + + /** + * Deletes the server group with the specified ID, even if the server group still contains clients. + * + * @param groupId + * the ID of the server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + */ + public void deleteServerGroup(int groupId) { + asyncApi.deleteServerGroup(groupId).getUninterruptibly(); + } + + /** + * Deletes a server group with the specified ID. + *

+ * If {@code force} is true, the server group will be deleted even if it contains clients, + * else the command will fail in this situation. + *

+ * + * @param groupId + * the ID of the server group + * @param force + * whether the server group should be deleted if it still contains clients + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + */ + public void deleteServerGroup(int groupId, boolean force) { + asyncApi.deleteServerGroup(groupId, force).getUninterruptibly(); + } + + /** + * Removes a permission from the server group with the given ID. + * + * @param groupId + * the ID of the server group + * @param permName + * the name of the permission to revoke + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + * @see Permission#getName() + */ + public void deleteServerGroupPermission(int groupId, String permName) { + asyncApi.deleteServerGroupPermission(groupId, permName).getUninterruptibly(); + } + + /** + * Restores the selected virtual servers configuration using the data from a + * previously created server snapshot. + * + * @param snapshot + * the snapshot to restore + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #createServerSnapshot() + */ + public void deployServerSnapshot(Snapshot snapshot) { + asyncApi.deployServerSnapshot(snapshot).getUninterruptibly(); + } + + /** + * Restores the configuration of the selected virtual server using the data from a + * previously created server snapshot. + * + * @param snapshot + * the snapshot to restore + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #createServerSnapshot() + */ + public void deployServerSnapshot(String snapshot) { + asyncApi.deployServerSnapshot(snapshot).getUninterruptibly(); + } + + /** + * Downloads a file from the file repository at a given path and channel + * and writes the file's bytes to an open {@link OutputStream}. + *

+ * It is the user's responsibility to ensure that the given {@code OutputStream} is + * open and to close the stream again once the download has finished. + *

+ * Note that this method will not read the entire file to memory and can thus + * download arbitrarily sized files from the file repository. + *

+ * + * @param dataOut + * a stream that the downloaded data should be written to + * @param filePath + * the path of the file on the file repository + * @param channelId + * the ID of the channel to download the file from + * + * @return how many bytes were downloaded + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #downloadFileDirect(String, int) + */ + public long downloadFile(OutputStream dataOut, String filePath, int channelId) { + return asyncApi.downloadFile(dataOut, filePath, channelId).getUninterruptibly(); + } + + /** + * Downloads a file from the file repository at a given path and channel + * and writes the file's bytes to an open {@link OutputStream}. + *

+ * It is the user's responsibility to ensure that the given {@code OutputStream} is + * open and to close the stream again once the download has finished. + *

+ * Note that this method will not read the entire file to memory and can thus + * download arbitrarily sized files from the file repository. + *

+ * + * @param dataOut + * a stream that the downloaded data should be written to + * @param filePath + * the path of the file on the file repository + * @param channelId + * the ID of the channel to download the file from + * @param channelPassword + * that channel's password + * + * @return how many bytes were downloaded + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #downloadFileDirect(String, int, String) + */ + public long downloadFile(OutputStream dataOut, String filePath, int channelId, String channelPassword) { + return asyncApi.downloadFile(dataOut, filePath, channelId, channelPassword).getUninterruptibly(); + } + + /** + * Downloads a file from the file repository at a given path and channel + * and returns the file's bytes as a byte array. + *

+ * Note that this method will read the entire file to memory. + * That means that if a file is larger than 231-1 bytes in size, + * the download will fail. + *

+ * + * @param filePath + * the path of the file on the file repository + * @param channelId + * the ID of the channel to download the file from + * + * @return a byte array containing the file's data + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #downloadFile(OutputStream, String, int) + */ + public byte[] downloadFileDirect(String filePath, int channelId) { + return asyncApi.downloadFileDirect(filePath, channelId).getUninterruptibly(); + } + + /** + * Downloads a file from the file repository at a given path and channel + * and returns the file's bytes as a byte array. + *

+ * Note that this method will read the entire file to memory. + * That means that if a file is larger than 231-1 bytes in size, + * the download will fail. + *

+ * + * @param filePath + * the path of the file on the file repository + * @param channelId + * the ID of the channel to download the file from + * @param channelPassword + * that channel's password + * + * @return a byte array containing the file's data + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #downloadFile(OutputStream, String, int, String) + */ + public byte[] downloadFileDirect(String filePath, int channelId, String channelPassword) { + return asyncApi.downloadFileDirect(filePath, channelId, channelPassword).getUninterruptibly(); + } + + /** + * Downloads an icon from the icon directory in the file repository + * and writes the file's bytes to an open {@link OutputStream}. + *

+ * It is the user's responsibility to ensure that the given {@code OutputStream} is + * open and to close the stream again once the download has finished. + *

+ * + * @param dataOut + * a stream that the downloaded data should be written to + * @param iconId + * the ID of the icon that should be downloaded + * + * @return a byte array containing the icon file's data + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see IconFile#getIconId() + * @see #downloadIconDirect(long) + * @see #uploadIcon(InputStream, long) + */ + public long downloadIcon(OutputStream dataOut, long iconId) { + return asyncApi.downloadIcon(dataOut, iconId).getUninterruptibly(); + } + + /** + * Downloads an icon from the icon directory in the file repository + * and returns the file's bytes as a byte array. + *

+ * Note that this method will read the entire file to memory. + *

+ * + * @param iconId + * the ID of the icon that should be downloaded + * + * @return a byte array containing the icon file's data + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see IconFile#getIconId() + * @see #downloadIcon(OutputStream, long) + * @see #uploadIconDirect(byte[]) + */ + public byte[] downloadIconDirect(long iconId) { + return asyncApi.downloadIconDirect(iconId).getUninterruptibly(); + } + + /** + * Changes a channel's configuration using the given properties. + * + * @param channelId + * the ID of the channel to edit + * @param options + * the map of properties to modify + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + */ + public void editChannel(int channelId, Map options) { + asyncApi.editChannel(channelId, options).getUninterruptibly(); + } + + /** + * Changes a single property of the given channel. + *

+ * Note that one can set many properties at once with the overloaded method that + * takes a map of channel properties and strings. + *

+ * + * @param channelId + * the ID of the channel to edit + * @param property + * the channel property to modify, make sure it is editable + * @param value + * the new value of the property + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see #editChannel(int, Map) + */ + public void editChannel(int channelId, ChannelProperty property, String value) { + asyncApi.editChannel(channelId, property, value).getUninterruptibly(); + } + + /** + * Changes a client's configuration using given properties. + *

+ * Only {@link ClientProperty#CLIENT_DESCRIPTION} can be changed for other clients. + * To update the current client's properties, use {@link #updateClient(Map)} + * or {@link #updateClient(ClientProperty, String)}. + *

+ * + * @param clientId + * the ID of the client to edit + * @param options + * the map of properties to modify + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see #updateClient(Map) + */ + public void editClient(int clientId, Map options) { + asyncApi.editClient(clientId, options).getUninterruptibly(); + } + + /** + * Changes a single property of the given client. + *

+ * Only {@link ClientProperty#CLIENT_DESCRIPTION} can be changed for other clients. + * To update the current client's properties, use {@link #updateClient(Map)} + * or {@link #updateClient(ClientProperty, String)}. + *

+ * + * @param clientId + * the ID of the client to edit + * @param property + * the client property to modify, make sure it is editable + * @param value + * the new value of the property + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see #editClient(int, Map) + * @see #updateClient(Map) + */ + public void editClient(int clientId, ClientProperty property, String value) { + asyncApi.editClient(clientId, property, value).getUninterruptibly(); + } + + /** + * Changes a client's database settings using given properties. + * + * @param clientDBId + * the database ID of the client to edit + * @param options + * the map of properties to modify + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see DatabaseClientInfo + * @see Client#getDatabaseId() + */ + public void editDatabaseClient(int clientDBId, Map options) { + asyncApi.editDatabaseClient(clientDBId, options).getUninterruptibly(); + } + + /** + * Changes the server instance configuration using given properties. + * If the given property is not changeable, {@code IllegalArgumentException} will be thrown. + * + * @param property + * the property to edit, must be changeable + * @param value + * the new value for the edit + * + * @throws IllegalArgumentException + * if {@code property} is not changeable + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerInstanceProperty#isChangeable() + */ + public void editInstance(ServerInstanceProperty property, String value) { + asyncApi.editInstance(property, value).getUninterruptibly(); + } + + /** + * Changes the configuration of the selected virtual server using given properties. + * + * @param options + * the map of properties to edit + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see VirtualServerProperty + */ + public void editServer(Map options) { + asyncApi.editServer(options).getUninterruptibly(); + } + + /** + * Gets a list of all bans on the selected virtual server. + * + * @return a list of all bans on the virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Ban + */ + public List getBans() { + return asyncApi.getBans().getUninterruptibly(); + } + + /** + * Gets a list of IP addresses used by the server instance. + * + * @return the list of bound IP addresses + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Binding + */ + public List getBindings() { + return asyncApi.getBindings().getUninterruptibly(); + } + + /** + * Finds and returns the channel matching the given name exactly. + * + * @param name + * the name of the channel + * @param ignoreCase + * whether the case of the name should be ignored + * + * @return the found channel or {@code null} if no channel was found + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel + * @see #getChannelsByName(String) + */ + public Channel getChannelByNameExact(String name, boolean ignoreCase) { + return asyncApi.getChannelByNameExact(name, ignoreCase).getUninterruptibly(); + } + + /** + * Gets a list of channels whose names contain the given search string. + * + * @param name + * the name to search + * + * @return a list of all channels with names matching the search pattern + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 2 + * @see Channel + * @see #getChannelByNameExact(String, boolean) + */ + public List getChannelsByName(String name) { + return asyncApi.getChannelsByName(name).getUninterruptibly(); + } + + /** + * Displays a list of permissions defined for a client in a specific channel. + * + * @param channelId + * the ID of the channel + * @param clientDBId + * the database ID of the client + * + * @return a list of permissions for the user in the specified channel + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see Client#getDatabaseId() + * @see Permission + */ + public List getChannelClientPermissions(int channelId, int clientDBId) { + return asyncApi.getChannelClientPermissions(channelId, clientDBId).getUninterruptibly(); + } + + /** + * Gets all client / channel ID combinations currently assigned to channel groups. + * All three parameters are optional and can be turned off by setting it to {@code -1}. + * + * @param channelId + * restricts the search to the channel with a specified ID. Set to {@code -1} to ignore. + * @param clientDBId + * restricts the search to the client with a specified database ID. Set to {@code -1} to ignore. + * @param groupId + * restricts the search to the channel group with the specified ID. Set to {@code -1} to ignore. + * + * @return a list of combinations of channel ID, client database ID and channel group ID + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see Client#getDatabaseId() + * @see ChannelGroup#getId() + * @see ChannelGroupClient + */ + public List getChannelGroupClients(int channelId, int clientDBId, int groupId) { + return asyncApi.getChannelGroupClients(channelId, clientDBId, groupId).getUninterruptibly(); + } + + /** + * Gets all client / channel ID combinations currently assigned to the specified channel group. + * + * @param groupId + * the ID of the channel group whose client / channel assignments should be returned. + * + * @return a list of combinations of channel ID, client database ID and channel group ID + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + * @see ChannelGroupClient + * @see #getChannelGroupClients(int, int, int) + */ + public List getChannelGroupClientsByChannelGroupId(int groupId) { + return asyncApi.getChannelGroupClientsByChannelGroupId(groupId).getUninterruptibly(); + } + + /** + * Gets all channel group assignments in the specified channel. + * + * @param channelId + * the ID of the channel whose channel group assignments should be returned. + * + * @return a list of combinations of channel ID, client database ID and channel group ID + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see ChannelGroupClient + * @see #getChannelGroupClients(int, int, int) + */ + public List getChannelGroupClientsByChannelId(int channelId) { + return asyncApi.getChannelGroupClientsByChannelId(channelId).getUninterruptibly(); + } + + /** + * Gets all channel group assignments for the specified client. + * + * @param clientDBId + * the database ID of the client whose channel group + * + * @return a list of combinations of channel ID, client database ID and channel group ID + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see ChannelGroupClient + * @see #getChannelGroupClients(int, int, int) + */ + public List getChannelGroupClientsByClientDBId(int clientDBId) { + return asyncApi.getChannelGroupClientsByClientDBId(clientDBId).getUninterruptibly(); + } + + /** + * Gets a list of all permissions assigned to the specified channel group. + * + * @param groupId + * the ID of the channel group. + * + * @return a list of permissions assigned to the channel group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + * @see Permission + */ + public List getChannelGroupPermissions(int groupId) { + return asyncApi.getChannelGroupPermissions(groupId).getUninterruptibly(); + } + + /** + * Gets a list of all channel groups on the selected virtual server. + * + * @return a list of all channel groups on the virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup + */ + public List getChannelGroups() { + return asyncApi.getChannelGroups().getUninterruptibly(); + } + + /** + * Gets detailed configuration information about the channel specified channel. + * + * @param channelId + * the ID of the channel + * + * @return information about the channel + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see ChannelInfo + */ + public ChannelInfo getChannelInfo(int channelId) { + return asyncApi.getChannelInfo(channelId).getUninterruptibly(); + } + + /** + * Gets a list of all permissions assigned to the specified channel. + * + * @param channelId + * the ID of the channel + * + * @return a list of all permissions assigned to the channel + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see Permission + */ + public List getChannelPermissions(int channelId) { + return asyncApi.getChannelPermissions(channelId).getUninterruptibly(); + } + + /** + * Gets a list of all channels on the selected virtual server. + * + * @return a list of all channels on the virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel + */ + public List getChannels() { + return asyncApi.getChannels().getUninterruptibly(); + } + + /** + * Finds and returns the client whose nickname matches the given name exactly. + * + * @param name + * the name of the client + * @param ignoreCase + * whether the case of the name should be ignored + * + * @return the found client or {@code null} if no client was found + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client + * @see #getClientsByName(String) + */ + public Client getClientByNameExact(String name, boolean ignoreCase) { + return asyncApi.getClientByNameExact(name, ignoreCase).getUninterruptibly(); + } + + /** + * Gets a list of clients whose nicknames contain the given search string. + * + * @param name + * the name to search + * + * @return a list of all clients with nicknames matching the search pattern + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 2 + * @see Client + * @see #getClientByNameExact(String, boolean) + */ + public List getClientsByName(String name) { + return asyncApi.getClientsByName(name).getUninterruptibly(); + } + + /** + * Gets information about the client with the specified unique identifier. + * + * @param clientUId + * the unique identifier of the client + * + * @return information about the client + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 2 + * @see Client#getUniqueIdentifier() + * @see ClientInfo + */ + public ClientInfo getClientByUId(String clientUId) { + return asyncApi.getClientByUId(clientUId).getUninterruptibly(); + } + + /** + * Gets information about the client with the specified client ID. + * + * @param clientId + * the client ID of the client + * + * @return information about the client + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see ClientInfo + */ + public ClientInfo getClientInfo(int clientId) { + return asyncApi.getClientInfo(clientId).getUninterruptibly(); + } + + /** + * Gets a list of all permissions assigned to the specified client. + * + * @param clientDBId + * the database ID of the client + * + * @return a list of all permissions assigned to the client + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see Permission + */ + public List getClientPermissions(int clientDBId) { + return asyncApi.getClientPermissions(clientDBId).getUninterruptibly(); + } + + /** + * Gets a list of all clients on the selected virtual server. + * + * @return a list of all clients on the virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client + */ + public List getClients() { + return asyncApi.getClients().getUninterruptibly(); + } + + /** + * Gets a list of all complaints on the selected virtual server. + * + * @return a list of all complaints on the virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Complaint + * @see #getComplaints(int) + */ + public List getComplaints() { + return asyncApi.getComplaints().getUninterruptibly(); + } + + /** + * Gets a list of all complaints about the specified client. + * + * @param clientDBId + * the database ID of the client + * + * @return a list of all complaints about the specified client + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see Complaint + */ + public List getComplaints(int clientDBId) { + return asyncApi.getComplaints(clientDBId).getUninterruptibly(); + } + + /** + * Gets detailed connection information about the selected virtual server. + * + * @return connection information about the selected virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ConnectionInfo + * @see #getServerInfo() + */ + public ConnectionInfo getConnectionInfo() { + return asyncApi.getConnectionInfo().getUninterruptibly(); + } + + /** + * Gets a map of all custom client properties and their values + * assigned to the client with database ID {@code clientDBId}. + * + * @param clientDBId + * the database ID of the target client + * + * @return a map of the client's custom client property assignments + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see #searchCustomClientProperty(String) + * @see #searchCustomClientProperty(String, String) + */ + public Map getCustomClientProperties(int clientDBId) { + return asyncApi.getCustomClientProperties(clientDBId).getUninterruptibly(); + } + + /** + * Gets all clients in the database whose last nickname matches the specified name exactly. + * + * @param name + * the nickname for the clients to match + * + * @return a list of all clients with a matching nickname + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + n, + * where n is the amount of database clients with a matching nickname + * @see Client#getNickname() + */ + public List getDatabaseClientsByName(String name) { + return asyncApi.getDatabaseClientsByName(name).getUninterruptibly(); + } + + /** + * Gets information about the client with the specified unique identifier in the server database. + * + * @param clientUId + * the unique identifier of the client + * + * @return the database client or {@code null} if no client was found + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 2 + * @see Client#getUniqueIdentifier() + * @see DatabaseClientInfo + */ + public DatabaseClientInfo getDatabaseClientByUId(String clientUId) { + return asyncApi.getDatabaseClientByUId(clientUId).getUninterruptibly(); + } + + /** + * Gets information about the client with the specified database ID in the server database. + * + * @param clientDBId + * the database ID of the client + * + * @return the database client or {@code null} if no client was found + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see DatabaseClientInfo + */ + public DatabaseClientInfo getDatabaseClientInfo(int clientDBId) { + return asyncApi.getDatabaseClientInfo(clientDBId).getUninterruptibly(); + } + + /** + * Gets information about all clients in the server database. + *

+ * As this method uses internal commands which can only return 200 clients at once, + * this method can take quite some time to execute. + *

+ * Also keep in mind that the client database can easily accumulate several thousand entries. + *

+ * + * @return a {@link List} of all database clients + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + n, + * where n = Math.ceil([amount of database clients] / 200) + * @see DatabaseClient + */ + public List getDatabaseClients() { + return asyncApi.getDatabaseClients().getUninterruptibly(); + } + + /** + * Gets information about a set number of clients in the server database, starting at {@code offset}. + * + * @param offset + * the index of the first database client to be returned. + * Note that this is not a database ID, but an arbitrary, 0-based index. + * @param count + * the number of database clients that should be returned. + * Any integer greater than 200 might cause problems with the connection + * + * @return a {@link List} of database clients + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see DatabaseClient + */ + public List getDatabaseClients(int offset, int count) { + return asyncApi.getDatabaseClients(offset, count).getUninterruptibly(); + } + + /** + * Gets information about a file on the file repository in the specified channel. + *

+ * Note that this method does not work on directories and the information returned by this + * method is identical to the one returned by {@link #getFileList(String, int, String)} + *

+ * + * @param filePath + * the path to the file + * @param channelId + * the ID of the channel the file resides in + * + * @return some information about the file + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public FileInfo getFileInfo(String filePath, int channelId) { + return asyncApi.getFileInfo(filePath, channelId).getUninterruptibly(); + } + + /** + * Gets information about a file on the file repository in the specified channel. + *

+ * Note that this method does not work on directories and the information returned by this + * method is identical to the one returned by {@link #getFileList(String, int, String)} + *

+ * + * @param filePath + * the path to the file + * @param channelId + * the ID of the channel the file resides in + * @param channelPassword + * the password of that channel + * + * @return some information about the file + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public FileInfo getFileInfo(String filePath, int channelId, String channelPassword) { + return asyncApi.getFileInfo(filePath, channelId, channelPassword).getUninterruptibly(); + } + + /** + * Gets information about multiple files on the file repository in the specified channel. + *

+ * Note that this method does not work on directories and the information returned by this + * method is identical to the one returned by {@link #getFileList(String, int, String)} + *

+ * + * @param filePaths + * the paths to the files + * @param channelId + * the ID of the channel the file resides in + * + * @return some information about the file + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public List getFileInfos(String[] filePaths, int channelId) { + return asyncApi.getFileInfos(filePaths, channelId).getUninterruptibly(); + } + + /** + * Gets information about multiple files on the file repository in the specified channel. + *

+ * Note that this method does not work on directories and the information returned by this + * method is identical to the one returned by {@link #getFileList(String, int, String)} + *

+ * + * @param filePaths + * the paths to the files + * @param channelId + * the ID of the channel the file resides in + * @param channelPassword + * the password of that channel + * + * @return some information about the file + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public List getFileInfos(String[] filePaths, int channelId, String channelPassword) { + return asyncApi.getFileInfos(filePaths, channelId, channelPassword).getUninterruptibly(); + } + + /** + * Gets information about multiple files on the file repository in multiple channels. + *

+ * Note that this method does not work on directories and the information returned by this + * method is identical to the one returned by {@link #getFileList(String, int, String)} + *

+ * + * @param filePaths + * the paths to the files, may not be {@code null} and may not contain {@code null} elements + * @param channelIds + * the IDs of the channels the file resides in, may not be {@code null} + * @param channelPasswords + * the passwords of those channels, may be {@code null} and may contain {@code null} elements + * + * @return some information about the files + * + * @throws IllegalArgumentException + * if the dimensions of {@code filePaths}, {@code channelIds} and {@code channelPasswords} don't match + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public List getFileInfos(String[] filePaths, int[] channelIds, String[] channelPasswords) { + return asyncApi.getFileInfos(filePaths, channelIds, channelPasswords).getUninterruptibly(); + } + + /** + * Gets a list of files and directories in the specified parent directory and channel. + * + * @param directoryPath + * the path to the parent directory + * @param channelId + * the ID of the channel the directory resides in + * + * @return the files and directories in the parent directory + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public List getFileList(String directoryPath, int channelId) { + return asyncApi.getFileList(directoryPath, channelId).getUninterruptibly(); + } + + /** + * Gets a list of files and directories in the specified parent directory and channel. + * + * @param directoryPath + * the path to the parent directory + * @param channelId + * the ID of the channel the directory resides in + * @param channelPassword + * the password of that channel + * + * @return the files and directories in the parent directory + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public List getFileList(String directoryPath, int channelId, String channelPassword) { + return asyncApi.getFileList(directoryPath, channelId, channelPassword).getUninterruptibly(); + } + + /** + * Gets a list of active or recently active file transfers. + * + * @return a list of file transfers + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public List getFileTransfers() { + return asyncApi.getFileTransfers().getUninterruptibly(); + } + + /** + * Displays detailed configuration information about the server instance including + * uptime, number of virtual servers online, traffic information, etc. + * + * @return information about the host + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public HostInfo getHostInfo() { + return asyncApi.getHostInfo().getUninterruptibly(); + } + + /** + * Gets a list of all icon files on this virtual server. + * + * @return a list of all icons + */ + public List getIconList() { + return asyncApi.getIconList().getUninterruptibly(); + } + + /** + * Displays the server instance configuration including database revision number, + * the file transfer port, default group IDs, etc. + * + * @return information about the TeamSpeak server instance. + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public InstanceInfo getInstanceInfo() { + return asyncApi.getInstanceInfo().getUninterruptibly(); + } + + /** + * Fetches the specified amount of log entries from the server log. + * + * @param lines + * the amount of log entries to fetch, in the range between 1 and 100. + * Returns 100 entries if the argument is not in range + * + * @return a list of the latest log entries + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public List getInstanceLogEntries(int lines) { + return asyncApi.getInstanceLogEntries(lines).getUninterruptibly(); + } + + /** + * Fetches the last 100 log entries from the server log. + * + * @return a list of up to 100 log entries + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public List getInstanceLogEntries() { + return asyncApi.getInstanceLogEntries().getUninterruptibly(); + } + + /** + * Reads the message body of a message. This will not set the read flag, though. + * + * @param messageId + * the ID of the message to be read + * + * @return the body of the message with the specified ID or {@code null} if there was no message with that ID + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Message#getId() + * @see #setMessageRead(int) + */ + public String getOfflineMessage(int messageId) { + return asyncApi.getOfflineMessage(messageId).getUninterruptibly(); + } + + /** + * Reads the message body of a message. This will not set the read flag, though. + * + * @param message + * the message to be read + * + * @return the body of the message with the specified ID or {@code null} if there was no message with that ID + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Message#getId() + * @see #setMessageRead(Message) + */ + public String getOfflineMessage(Message message) { + return asyncApi.getOfflineMessage(message).getUninterruptibly(); + } + + /** + * Gets a list of all offline messages for the server query. + * The returned messages lack their message body, though. + * To read the actual message, use {@link #getOfflineMessage(int)} or {@link #getOfflineMessage(Message)}. + * + * @return a list of all offline messages this server query has received + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public List getOfflineMessages() { + return asyncApi.getOfflineMessages().getUninterruptibly(); + } + + /** + * Displays detailed information about all assignments of the permission specified + * with {@code permName}. The output includes the type and the ID of the client, + * channel or group associated with the permission. + * + * @param permName + * the name of the permission + * + * @return a list of permission assignments + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #getPermissionOverview(int, int) + */ + public List getPermissionAssignments(String permName) { + return asyncApi.getPermissionAssignments(permName).getUninterruptibly(); + } + + /** + * Gets the ID of the permission specified by {@code permName}. + *

+ * Note that the use of numeric permission IDs is deprecated + * and that this API only uses the string variant of the IDs. + *

+ * + * @param permName + * the name of the permission + * + * @return the numeric ID of the specified permission + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public int getPermissionIdByName(String permName) { + return asyncApi.getPermissionIdByName(permName).getUninterruptibly(); + } + + /** + * Gets the IDs of the permissions specified by {@code permNames}. + *

+ * Note that the use of numeric permission IDs is deprecated + * and that this API only uses the string variant of the IDs. + *

+ * + * @param permNames + * the names of the permissions + * + * @return the numeric IDs of the specified permission + * + * @throws IllegalArgumentException + * if {@code permNames} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public int[] getPermissionIdsByName(String... permNames) { + return asyncApi.getPermissionIdsByName(permNames).getUninterruptibly(); + } + + /** + * Gets a list of all assigned permissions for a client in a specified channel. + * If you do not care about channel permissions, set {@code channelId} to {@code 0}. + * + * @param channelId + * the ID of the channel + * @param clientDBId + * the database ID of the client to create the overview for + * + * @return a list of all permission assignments for the client in the specified channel + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see Client#getDatabaseId() + */ + public List getPermissionOverview(int channelId, int clientDBId) { + return asyncApi.getPermissionOverview(channelId, clientDBId).getUninterruptibly(); + } + + /** + * Displays a list of all permissions, including ID, name and description. + * + * @return a list of all permissions + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public List getPermissions() { + return asyncApi.getPermissions().getUninterruptibly(); + } + + /** + * Displays the current value of the specified permission for this server query instance. + * + * @param permName + * the name of the permission + * + * @return the permission value, usually ranging from 0 to 100 + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public int getPermissionValue(String permName) { + return asyncApi.getPermissionValue(permName).getUninterruptibly(); + } + + /** + * Displays the current values of the specified permissions for this server query instance. + * + * @param permNames + * the names of the permissions + * + * @return the permission values, usually ranging from 0 to 100 + * + * @throws IllegalArgumentException + * if {@code permNames} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public int[] getPermissionValues(String... permNames) { + return asyncApi.getPermissionValues(permNames).getUninterruptibly(); + } + + /** + * Gets a list of all available tokens to join channel or server groups, + * including their type and group IDs. + * + * @return a list of all generated, but still unclaimed privilege keys + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #addPrivilegeKey(PrivilegeKeyType, int, int, String) + * @see #usePrivilegeKey(String) + */ + public List getPrivilegeKeys() { + return asyncApi.getPrivilegeKeys().getUninterruptibly(); + } + + /** + * Gets a list of all clients in the specified server group. + * + * @param serverGroupId + * the ID of the server group for which the clients should be looked up + * + * @return a list of all clients in the server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public List getServerGroupClients(int serverGroupId) { + return asyncApi.getServerGroupClients(serverGroupId).getUninterruptibly(); + } + + /** + * Gets a list of all clients in the specified server group. + * + * @param serverGroup + * the server group for which the clients should be looked up + * + * @return a list of all clients in the server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public List getServerGroupClients(ServerGroup serverGroup) { + return asyncApi.getServerGroupClients(serverGroup).getUninterruptibly(); + } + + /** + * Gets a list of all permissions assigned to the specified server group. + * + * @param serverGroupId + * the ID of the server group for which the permissions should be looked up + * + * @return a list of all permissions assigned to the server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + * @see #getServerGroupPermissions(ServerGroup) + */ + public List getServerGroupPermissions(int serverGroupId) { + return asyncApi.getServerGroupPermissions(serverGroupId).getUninterruptibly(); + } + + /** + * Gets a list of all permissions assigned to the specified server group. + * + * @param serverGroup + * the server group for which the permissions should be looked up + * + * @return a list of all permissions assigned to the server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public List getServerGroupPermissions(ServerGroup serverGroup) { + return asyncApi.getServerGroupPermissions(serverGroup).getUninterruptibly(); + } + + /** + * Gets a list of all server groups on the virtual server. + *

+ * Depending on your permissions, the output may also contain + * global server query groups and template groups. + *

+ * + * @return a list of all server groups + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public List getServerGroups() { + return asyncApi.getServerGroups().getUninterruptibly(); + } + + /** + * Gets a list of all server groups set for a client. + * + * @param clientDatabaseId + * the database ID of the client for which the server groups should be looked up + * + * @return a list of all server groups set for the client + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 2 + * @see Client#getDatabaseId() + * @see #getServerGroupsByClient(Client) + */ + public List getServerGroupsByClientId(int clientDatabaseId) { + return asyncApi.getServerGroupsByClientId(clientDatabaseId).getUninterruptibly(); + } + + /** + * Gets a list of all server groups set for a client. + * + * @param client + * the client for which the server groups should be looked up + * + * @return a list of all server group set for the client + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 2 + * @see #getServerGroupsByClientId(int) + */ + public List getServerGroupsByClient(Client client) { + return asyncApi.getServerGroupsByClient(client).getUninterruptibly(); + } + + /** + * Gets the ID of a virtual server by its port. + * + * @param port + * the port of a virtual server + * + * @return the ID of the virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see VirtualServer#getPort() + * @see VirtualServer#getId() + */ + public int getServerIdByPort(int port) { + return asyncApi.getServerIdByPort(port).getUninterruptibly(); + } + + /** + * Gets detailed information about the virtual server the server query is currently in. + * + * @return information about the current virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public VirtualServerInfo getServerInfo() { + return asyncApi.getServerInfo().getUninterruptibly(); + } + + /** + * Gets the version, build number and platform of the TeamSpeak3 server. + * + * @return the version information of the server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public Version getVersion() { + return asyncApi.getVersion().getUninterruptibly(); + } + + /** + * Gets a list of all virtual servers including their ID, status, number of clients online, etc. + * + * @return a list of all virtual servers + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public List getVirtualServers() { + return asyncApi.getVirtualServers().getUninterruptibly(); + } + + /** + * Fetches the specified amount of log entries from the currently selected virtual server. + * If no virtual server is selected, the entries will be read from the server log instead. + * + * @param lines + * the amount of log entries to fetch, in the range between 1 and 100. + * Returns 100 entries if the argument is not in range + * + * @return a list of the latest log entries + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public List getVirtualServerLogEntries(int lines) { + return asyncApi.getVirtualServerLogEntries(lines).getUninterruptibly(); + } + + /** + * Fetches the last 100 log entries from the currently selected virtual server. + * If no virtual server is selected, the entries will be read from the server log instead. + * + * @return a list of up to 100 log entries + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public List getVirtualServerLogEntries() { + return asyncApi.getVirtualServerLogEntries().getUninterruptibly(); + } + + /** + * Checks whether the client with the specified client ID is online. + *

+ * Please note that there is no guarantee that the client will still be + * online by the time the next command is executed. + *

+ * + * @param clientId + * the ID of the client + * + * @return {@code true} if the client is online, {@code false} otherwise + * + * @querycommands 1 + * @see #getClientInfo(int) + */ + public boolean isClientOnline(int clientId) { + return asyncApi.isClientOnline(clientId).getUninterruptibly(); + } + + /** + * Checks whether the client with the specified unique identifier is online. + *

+ * Please note that there is no guarantee that the client will still be + * online by the time the next command is executed. + *

+ * + * @param clientUId + * the unique ID of the client + * + * @return {@code true} if the client is online, {@code false} otherwise + * + * @querycommands 1 + * @see #getClientByUId(String) + */ + public boolean isClientOnline(String clientUId) { + return asyncApi.isClientOnline(clientUId).getUninterruptibly(); + } + + /** + * Kicks one or more clients from their current channels. + * This will move the kicked clients into the default channel and + * won't do anything if the clients are already in the default channel. + * + * @param clientIds + * the IDs of the clients to kick + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #kickClientFromChannel(Client...) + * @see #kickClientFromChannel(String, int...) + */ + public void kickClientFromChannel(int... clientIds) { + asyncApi.kickClientFromChannel(clientIds).getUninterruptibly(); + } + + /** + * Kicks one or more clients from their current channels. + * This will move the kicked clients into the default channel and + * won't do anything if the clients are already in the default channel. + * + * @param clients + * the clients to kick + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #kickClientFromChannel(int...) + * @see #kickClientFromChannel(String, Client...) + */ + public void kickClientFromChannel(Client... clients) { + asyncApi.kickClientFromChannel(clients).getUninterruptibly(); + } + + /** + * Kicks one or more clients from their current channels for the specified reason. + * This will move the kicked clients into the default channel and + * won't do anything if the clients are already in the default channel. + * + * @param message + * the reason message to display to the clients + * @param clientIds + * the IDs of the clients to kick + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see #kickClientFromChannel(int...) + * @see #kickClientFromChannel(String, Client...) + */ + public void kickClientFromChannel(String message, int... clientIds) { + asyncApi.kickClientFromChannel(message, clientIds).getUninterruptibly(); + } + + /** + * Kicks one or more clients from their current channels for the specified reason. + * This will move the kicked clients into the default channel and + * won't do anything if the clients are already in the default channel. + * + * @param message + * the reason message to display to the clients + * @param clients + * the clients to kick + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #kickClientFromChannel(Client...) + * @see #kickClientFromChannel(String, int...) + */ + public void kickClientFromChannel(String message, Client... clients) { + asyncApi.kickClientFromChannel(message, clients).getUninterruptibly(); + } + + /** + * Kicks one or more clients from the server. + * + * @param clientIds + * the IDs of the clients to kick + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see #kickClientFromServer(Client...) + * @see #kickClientFromServer(String, int...) + */ + public void kickClientFromServer(int... clientIds) { + asyncApi.kickClientFromServer(clientIds).getUninterruptibly(); + } + + /** + * Kicks one or more clients from the server. + * + * @param clients + * the clients to kick + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #kickClientFromServer(int...) + * @see #kickClientFromServer(String, Client...) + */ + public void kickClientFromServer(Client... clients) { + asyncApi.kickClientFromServer(clients).getUninterruptibly(); + } + + /** + * Kicks one or more clients from the server for the specified reason. + * + * @param message + * the reason message to display to the clients + * @param clientIds + * the IDs of the clients to kick + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see #kickClientFromServer(int...) + * @see #kickClientFromServer(String, Client...) + */ + public void kickClientFromServer(String message, int... clientIds) { + asyncApi.kickClientFromServer(message, clientIds).getUninterruptibly(); + } + + /** + * Kicks one or more clients from the server for the specified reason. + * + * @param message + * the reason message to display to the clients + * @param clients + * the clients to kick + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #kickClientFromServer(Client...) + * @see #kickClientFromServer(String, int...) + */ + public void kickClientFromServer(String message, Client... clients) { + asyncApi.kickClientFromServer(message, clients).getUninterruptibly(); + } + + /** + * Logs the server query in using the specified username and password. + *

+ * Note that you can also set the login in the {@link TS3Config}, + * so that you will be logged in right after the connection is established. + *

+ * + * @param username + * the username of the server query + * @param password + * the password to use + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #logout() + */ + public void login(String username, String password) { + asyncApi.login(username, password).getUninterruptibly(); + } + + /** + * Logs the server query out and deselects the current virtual server. + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #login(String, String) + */ + public void logout() { + asyncApi.logout().getUninterruptibly(); + } + + /** + * Moves a channel to a new parent channel specified by its ID. + * To move a channel to root level, set {@code channelTargetId} to {@code 0}. + *

+ * This will move the channel right below the specified parent channel, above all other child channels. + * This command will fail if the channel already has the specified target channel as the parent channel. + *

+ * + * @param channelId + * the channel to move + * @param channelTargetId + * the new parent channel for the specified channel + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see #moveChannel(int, int, int) + */ + public void moveChannel(int channelId, int channelTargetId) { + asyncApi.moveChannel(channelId, channelTargetId).getUninterruptibly(); + } + + /** + * Moves a channel to a new parent channel specified by its ID. + * To move a channel to root level, set {@code channelTargetId} to {@code 0}. + *

+ * The channel will be ordered below the channel with the ID specified by {@code order}. + * To move the channel right below the parent channel, set {@code order} to {@code 0}. + *

+ * Note that you can't re-order a channel without also changing its parent channel with this method. + * Use {@link #editChannel(int, ChannelProperty, String)} to change {@link ChannelProperty#CHANNEL_ORDER} instead. + *

+ * + * @param channelId + * the channel to move + * @param channelTargetId + * the new parent channel for the specified channel + * @param order + * the channel to sort the specified channel below + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see #moveChannel(int, int) + */ + public void moveChannel(int channelId, int channelTargetId, int order) { + asyncApi.moveChannel(channelId, channelTargetId, order).getUninterruptibly(); + } + + /** + * Moves a single client into a channel. + *

+ * Consider using {@link #moveClients(int[], int)} to move multiple clients. + *

+ * + * @param clientId + * the ID of the client to move + * @param channelId + * the ID of the channel to move the client into + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see Channel#getId() + */ + public void moveClient(int clientId, int channelId) { + asyncApi.moveClient(clientId, channelId).getUninterruptibly(); + } + + /** + * Moves multiple clients into a channel. + * Immediately returns {@code true} for an empty client ID array. + *

+ * Use this method instead of {@link #moveClient(int, int)} for moving + * several clients as this will only send 1 command to the server and thus complete faster. + *

+ * + * @param clientIds + * the IDs of the clients to move, cannot be {@code null} + * @param channelId + * the ID of the channel to move the clients into + * + * @throws IllegalArgumentException + * if {@code clientIds} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see Channel#getId() + */ + public void moveClients(int[] clientIds, int channelId) { + asyncApi.moveClients(clientIds, channelId).getUninterruptibly(); + } + + /** + * Moves a single client into a channel. + *

+ * Consider using {@link #moveClients(Client[], ChannelBase)} to move multiple clients. + *

+ * + * @param client + * the client to move, cannot be {@code null} + * @param channel + * the channel to move the client into, cannot be {@code null} + * + * @throws IllegalArgumentException + * if {@code client} or {@code channel} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public void moveClient(Client client, ChannelBase channel) { + asyncApi.moveClient(client, channel).getUninterruptibly(); + } + + /** + * Moves multiple clients into a channel. + * Immediately returns {@code true} for an empty client array. + *

+ * Use this method instead of {@link #moveClient(Client, ChannelBase)} for moving + * several clients as this will only send 1 command to the server and thus complete faster. + *

+ * + * @param clients + * the clients to move, cannot be {@code null} + * @param channel + * the channel to move the clients into, cannot be {@code null} + * + * @throws IllegalArgumentException + * if {@code clients} or {@code channel} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public void moveClients(Client[] clients, ChannelBase channel) { + asyncApi.moveClients(clients, channel).getUninterruptibly(); + } + + /** + * Moves a single client into a channel using the specified password. + *

+ * Consider using {@link #moveClients(int[], int, String)} to move multiple clients. + *

+ * + * @param clientId + * the ID of the client to move + * @param channelId + * the ID of the channel to move the client into + * @param channelPassword + * the password of the channel, can be {@code null} + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see Channel#getId() + */ + public void moveClient(int clientId, int channelId, String channelPassword) { + asyncApi.moveClient(clientId, channelId, channelPassword).getUninterruptibly(); + } + + /** + * Moves multiple clients into a channel using the specified password. + * Immediately returns {@code true} for an empty client ID array. + *

+ * Use this method instead of {@link #moveClient(int, int, String)} for moving + * several clients as this will only send 1 command to the server and thus complete faster. + *

+ * + * @param clientIds + * the IDs of the clients to move, cannot be {@code null} + * @param channelId + * the ID of the channel to move the clients into + * @param channelPassword + * the password of the channel, can be {@code null} + * + * @throws IllegalArgumentException + * if {@code clientIds} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see Channel#getId() + */ + public void moveClients(int[] clientIds, int channelId, String channelPassword) { + asyncApi.moveClients(clientIds, channelId, channelPassword).getUninterruptibly(); + } + + /** + * Moves a single client into a channel using the specified password. + *

+ * Consider using {@link #moveClients(Client[], ChannelBase, String)} to move multiple clients. + *

+ * + * @param client + * the client to move, cannot be {@code null} + * @param channel + * the channel to move the client into, cannot be {@code null} + * @param channelPassword + * the password of the channel, can be {@code null} + * + * @throws IllegalArgumentException + * if {@code client} or {@code channel} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public void moveClient(Client client, ChannelBase channel, String channelPassword) { + asyncApi.moveClient(client, channel, channelPassword).getUninterruptibly(); + } + + /** + * Moves multiple clients into a channel using the specified password. + * Immediately returns {@code true} for an empty client array. + *

+ * Use this method instead of {@link #moveClient(Client, ChannelBase, String)} for moving + * several clients as this will only send 1 command to the server and thus complete faster. + *

+ * + * @param clients + * the clients to move, cannot be {@code null} + * @param channel + * the channel to move the clients into, cannot be {@code null} + * @param channelPassword + * the password of the channel, can be {@code null} + * + * @throws IllegalArgumentException + * if {@code clients} or {@code channel} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public void moveClients(Client[] clients, ChannelBase channel, String channelPassword) { + asyncApi.moveClients(clients, channel, channelPassword).getUninterruptibly(); + } + + /** + * Moves and renames a file on the file repository within the same channel. + * + * @param oldPath + * the current path to the file + * @param newPath + * the desired new path + * @param channelId + * the ID of the channel the file resides in + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #moveFile(String, String, int, int) moveFile to a different channel + */ + public void moveFile(String oldPath, String newPath, int channelId) { + asyncApi.moveFile(oldPath, newPath, channelId).getUninterruptibly(); + } + + /** + * Renames a file on the file repository and moves it to a new path in a different channel. + * + * @param oldPath + * the current path to the file + * @param newPath + * the desired new path + * @param oldChannelId + * the ID of the channel the file currently resides in + * @param newChannelId + * the ID of the channel the file should be moved to + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #moveFile(String, String, int) moveFile within the same channel + */ + public void moveFile(String oldPath, String newPath, int oldChannelId, int newChannelId) { + asyncApi.moveFile(oldPath, newPath, oldChannelId, newChannelId).getUninterruptibly(); + } + + /** + * Moves and renames a file on the file repository within the same channel. + * + * @param oldPath + * the current path to the file + * @param newPath + * the desired new path + * @param channelId + * the ID of the channel the file resides in + * @param channelPassword + * the password of the channel + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #moveFile(String, String, int, String, int, String) moveFile to a different channel + */ + public void moveFile(String oldPath, String newPath, int channelId, String channelPassword) { + asyncApi.moveFile(oldPath, newPath, channelId, channelPassword).getUninterruptibly(); + } + + /** + * Renames a file on the file repository and moves it to a new path in a different channel. + * + * @param oldPath + * the current path to the file + * @param newPath + * the desired new path + * @param oldChannelId + * the ID of the channel the file currently resides in + * @param oldPassword + * the password of the current channel + * @param newChannelId + * the ID of the channel the file should be moved to + * @param newPassword + * the password of the new channel + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #moveFile(String, String, int, String) moveFile within the same channel + */ + public void moveFile(String oldPath, String newPath, int oldChannelId, String oldPassword, int newChannelId, String newPassword) { + asyncApi.moveFile(oldPath, newPath, oldChannelId, oldPassword, newChannelId, newPassword).getUninterruptibly(); + } + + /** + * Moves the server query into a channel. + * + * @param channelId + * the ID of the channel to move the server query into + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + */ + public void moveQuery(int channelId) { + asyncApi.moveQuery(channelId).getUninterruptibly(); + } + + /** + * Moves the server query into a channel. + * + * @param channel + * the channel to move the server query into, cannot be {@code null} + * + * @throws IllegalArgumentException + * if {@code channel} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public void moveQuery(ChannelBase channel) { + asyncApi.moveQuery(channel).getUninterruptibly(); + } + + /** + * Moves the server query into a channel using the specified password. + * + * @param channelId + * the ID of the channel to move the client into + * @param channelPassword + * the password of the channel, can be {@code null} + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + */ + public void moveQuery(int channelId, String channelPassword) { + asyncApi.moveQuery(channelId, channelPassword).getUninterruptibly(); + } + + /** + * Moves the server query into a channel using the specified password. + * + * @param channel + * the channel to move the client into, cannot be {@code null} + * @param channelPassword + * the password of the channel, can be {@code null} + * + * @throws IllegalArgumentException + * if {@code channel} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public void moveQuery(ChannelBase channel, String channelPassword) { + asyncApi.moveQuery(channel, channelPassword).getUninterruptibly(); + } + + /** + * Pokes the client with the specified client ID. + * This opens up a small popup window for the client containing your message and plays a sound. + * The displayed message will be formatted like this:
+ * {@code hh:mm:ss - "Your Nickname" poked you: } + *

+ * The displayed message length is limited to 100 UTF-8 bytes. + * If a client has already received a poke message, all subsequent pokes will simply add a line + * to the already opened popup window and will still play a sound. + *

+ * + * @param clientId + * the ID of the client to poke + * @param message + * the message to send, may contain BB codes + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + */ + public void pokeClient(int clientId, String message) { + asyncApi.pokeClient(clientId, message).getUninterruptibly(); + } + + /** + * Terminates the connection with the TeamSpeak3 server. + *

+ * This command should never be executed by a user of this API, + * as it leaves the query in an undefined state. To terminate + * a connection regularly, use {@link TS3Query#exit()}. + *

+ * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + void quit() { + asyncApi.quit().getUninterruptibly(); + } + + /** + * Registers the server query to receive notifications about all server events. + *

+ * This means that the following actions will trigger event notifications: + *

+ *
    + *
  • A client joins the server or disconnects from it
  • + *
  • A client switches channels
  • + *
  • A client sends a server message
  • + *
  • A client sends a channel message in the channel the query is in
  • + *
  • A client sends a private message to the server query
  • + *
  • A client uses a privilege key
  • + *
+ *

+ * The limitations to when the query receives notifications about chat events cannot be circumvented. + *

+ * To be able to process these events in your application, register an event listener. + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 6 + * @see #addTS3Listeners(TS3Listener...) + */ + public void registerAllEvents() { + asyncApi.registerAllEvents().getUninterruptibly(); + } + + /** + * Registers the server query to receive notifications about a given event type. + *

+ * If used with {@link TS3EventType#TEXT_CHANNEL}, this will listen to chat events in the current channel. + * If used with {@link TS3EventType#CHANNEL}, this will listen to all channel events. + * To specify a different channel for channel events, use {@link #registerEvent(TS3EventType, int)}. + *

+ * + * @param eventType + * the event type to be notified about + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #addTS3Listeners(TS3Listener...) + * @see #registerEvent(TS3EventType, int) + * @see #registerAllEvents() + */ + public void registerEvent(TS3EventType eventType) { + asyncApi.registerEvent(eventType).getUninterruptibly(); + } + + /** + * Registers the server query to receive notifications about a given event type. + * + * @param eventType + * the event type to be notified about + * @param channelId + * the ID of the channel to listen to, will be ignored if set to {@code -1}. + * Can be set to {@code 0} for {@link TS3EventType#CHANNEL} to receive notifications about all channel switches. + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see #addTS3Listeners(TS3Listener...) + * @see #registerAllEvents() + */ + public void registerEvent(TS3EventType eventType, int channelId) { + asyncApi.registerEvent(eventType, channelId).getUninterruptibly(); + } + + /** + * Registers the server query to receive notifications about multiple given event types. + *

+ * If used with {@link TS3EventType#TEXT_CHANNEL}, this will listen to chat events in the current channel. + * If used with {@link TS3EventType#CHANNEL}, this will listen to all channel events. + * To specify a different channel for channel events, use {@link #registerEvent(TS3EventType, int)}. + *

+ * + * @param eventTypes + * the event types to be notified about + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands n, one command per TS3EventType + * @see #addTS3Listeners(TS3Listener...) + * @see #registerEvent(TS3EventType, int) + * @see #registerAllEvents() + */ + public void registerEvents(TS3EventType... eventTypes) { + asyncApi.registerEvents(eventTypes).getUninterruptibly(); + } + + /** + * Removes the client specified by its database ID from the specified server group. + * + * @param serverGroupId + * the ID of the server group + * @param clientDatabaseId + * the database ID of the client + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + * @see Client#getDatabaseId() + * @see #removeClientFromServerGroup(ServerGroup, Client) + */ + public void removeClientFromServerGroup(int serverGroupId, int clientDatabaseId) { + asyncApi.removeClientFromServerGroup(serverGroupId, clientDatabaseId).getUninterruptibly(); + } + + /** + * Removes the specified client from the specified server group. + * + * @param serverGroup + * the server group to remove the client from + * @param client + * the client to remove from the server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #removeClientFromServerGroup(int, int) + */ + public void removeClientFromServerGroup(ServerGroup serverGroup, Client client) { + asyncApi.removeClientFromServerGroup(serverGroup, client).getUninterruptibly(); + } + + /** + * Removes one or more {@link TS3Listener}s to the event manager of the query. + *

+ * If a listener was not actually registered, it will be ignored and no exception will be thrown. + *

+ * + * @param listeners + * one or more listeners to remove + * + * @see #addTS3Listeners(TS3Listener...) + * @see TS3Listener + * @see TS3EventType + */ + public void removeTS3Listeners(TS3Listener... listeners) { + asyncApi.removeTS3Listeners(listeners); + } + + /** + * Renames the channel group with the specified ID. + * + * @param channelGroupId + * the ID of the channel group to rename + * @param name + * the new name for the channel group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + * @see #renameChannelGroup(ChannelGroup, String) + */ + public void renameChannelGroup(int channelGroupId, String name) { + asyncApi.renameChannelGroup(channelGroupId, name).getUninterruptibly(); + } + + /** + * Renames the specified channel group. + * + * @param channelGroup + * the channel group to rename + * @param name + * the new name for the channel group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #renameChannelGroup(int, String) + */ + public void renameChannelGroup(ChannelGroup channelGroup, String name) { + asyncApi.renameChannelGroup(channelGroup, name).getUninterruptibly(); + } + + /** + * Renames the server group with the specified ID. + * + * @param serverGroupId + * the ID of the server group to rename + * @param name + * the new name for the server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + * @see #renameServerGroup(ServerGroup, String) + */ + public void renameServerGroup(int serverGroupId, String name) { + asyncApi.renameServerGroup(serverGroupId, name).getUninterruptibly(); + } + + /** + * Renames the specified server group. + * + * @param serverGroup + * the server group to rename + * @param name + * the new name for the server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #renameServerGroup(int, String) + */ + public void renameServerGroup(ServerGroup serverGroup, String name) { + asyncApi.renameServerGroup(serverGroup, name).getUninterruptibly(); + } + + /** + * Resets all permissions and deletes all server / channel groups. Use carefully. + * + * @return a token for a new administrator account + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public String resetPermissions() { + return asyncApi.resetPermissions().getUninterruptibly(); + } + + /** + * Finds all clients that have any value associated with the {@code key} custom client property, + * and returns the client's database ID and the key and value of the matching custom property. + * + * @param key + * the key to search for, cannot be {@code null} + * + * @return a list of client database IDs and their matching custom client properties + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see #searchCustomClientProperty(String, String) + * @see #getCustomClientProperties(int) + */ + public List searchCustomClientProperty(String key) { + return asyncApi.searchCustomClientProperty(key).getUninterruptibly(); + } + + /** + * Finds all clients whose value associated with the {@code key} custom client property matches the + * SQL-like pattern {@code valuePattern}, and returns the client's database ID and the key and value + * of the matching custom property. + *

+ * Patterns are case insensitive. They support the wildcard characters {@code %}, which matches any sequence of + * zero or more characters, and {@code _}, which matches exactly one arbitrary character. + *

+ * + * @param key + * the key to search for, cannot be {@code null} + * @param valuePattern + * the pattern that values need to match to be included + * + * @return a list of client database IDs and their matching custom client properties + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see #searchCustomClientProperty(String) + * @see #getCustomClientProperties(int) + */ + public List searchCustomClientProperty(String key, String valuePattern) { + return asyncApi.searchCustomClientProperty(key, valuePattern).getUninterruptibly(); + } + + /** + * Moves the server query into the virtual server with the specified ID. + * + * @param id + * the ID of the virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see VirtualServer#getId() + * @see #selectVirtualServerById(int, String) + * @see #selectVirtualServerByPort(int) + * @see #selectVirtualServer(VirtualServer) + */ + public void selectVirtualServerById(int id) { + asyncApi.selectVirtualServerById(id).getUninterruptibly(); + } + + /** + * Moves the server query into the virtual server with the specified ID + * and sets the server query's nickname. + *

+ * The nickname must be between 3 and 30 UTF-8 bytes long. BB codes will be ignored. + *

+ * + * @param id + * the ID of the virtual server + * @param nickname + * the nickname, or {@code null} if the nickname should not be set + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see VirtualServer#getId() + * @see #selectVirtualServerById(int) + * @see #selectVirtualServerByPort(int, String) + * @see #selectVirtualServer(VirtualServer, String) + */ + public void selectVirtualServerById(int id, String nickname) { + asyncApi.selectVirtualServerById(id, nickname).getUninterruptibly(); + } + + /** + * Moves the server query into the virtual server with the specified voice port. + * + * @param port + * the voice port of the virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see VirtualServer#getPort() + * @see #selectVirtualServerById(int) + * @see #selectVirtualServerByPort(int, String) + * @see #selectVirtualServer(VirtualServer) + */ + public void selectVirtualServerByPort(int port) { + asyncApi.selectVirtualServerByPort(port).getUninterruptibly(); + } + + /** + * Moves the server query into the virtual server with the specified voice port + * and sets the server query's nickname. + *

+ * The nickname must be between 3 and 30 UTF-8 bytes long. BB codes will be ignored. + *

+ * + * @param port + * the voice port of the virtual server + * @param nickname + * the nickname, or {@code null} if the nickname should not be set + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see VirtualServer#getPort() + * @see #selectVirtualServerById(int, String) + * @see #selectVirtualServerByPort(int) + * @see #selectVirtualServer(VirtualServer, String) + */ + public void selectVirtualServerByPort(int port, String nickname) { + asyncApi.selectVirtualServerByPort(port, nickname).getUninterruptibly(); + } + + /** + * Moves the server query into the specified virtual server. + * + * @param server + * the virtual server to move into + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #selectVirtualServerById(int) + * @see #selectVirtualServerByPort(int) + * @see #selectVirtualServer(VirtualServer, String) + */ + public void selectVirtualServer(VirtualServer server) { + asyncApi.selectVirtualServer(server).getUninterruptibly(); + } + + /** + * Moves the server query into the specified virtual server + * and sets the server query's nickname. + *

+ * The nickname must be between 3 and 30 UTF-8 bytes long. BB codes will be ignored. + *

+ * + * @param server + * the virtual server to move into + * @param nickname + * the nickname, or {@code null} if the nickname should not be set + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #selectVirtualServerById(int, String) + * @see #selectVirtualServerByPort(int, String) + * @see #selectVirtualServer(VirtualServer) + */ + public void selectVirtualServer(VirtualServer server, String nickname) { + asyncApi.selectVirtualServer(server, nickname).getUninterruptibly(); + } + + /** + * Sends an offline message to the client with the given unique identifier. + *

+ * The message subject's length is limited to 200 UTF-8 bytes and BB codes in it will be ignored. + * The message body's length is limited to 4096 UTF-8 bytes and accepts BB codes + *

+ * + * @param clientUId + * the unique identifier of the client to send the message to + * @param subject + * the subject for the message, may not contain BB codes + * @param message + * the actual message body, may contain BB codes + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getUniqueIdentifier() + * @see Message + */ + public void sendOfflineMessage(String clientUId, String subject, String message) { + asyncApi.sendOfflineMessage(clientUId, subject, message).getUninterruptibly(); + } + + /** + * Sends a text message either to the whole virtual server, a channel or specific client. + * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes. + *

+ * To send a message to all virtual servers, use {@link #broadcast(String)}. + * To send an offline message, use {@link #sendOfflineMessage(String, String, String)}. + *

+ * + * @param targetMode + * where the message should be sent to + * @param targetId + * the client ID of the recipient of this message. This value is ignored unless {@code targetMode} is {@code CLIENT} + * @param message + * the text message to send + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + */ + public void sendTextMessage(TextMessageTargetMode targetMode, int targetId, String message) { + asyncApi.sendTextMessage(targetMode, targetId, message).getUninterruptibly(); + } + + /** + * Sends a text message to the channel with the specified ID. + * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes. + *

+ * This will move the client into the channel with the specified channel ID, + * but will not move it back to the original channel! + *

+ * + * @param channelId + * the ID of the channel to which the message should be sent to + * @param message + * the text message to send + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #sendChannelMessage(String) + * @see Channel#getId() + */ + public void sendChannelMessage(int channelId, String message) { + asyncApi.sendChannelMessage(channelId, message).getUninterruptibly(); + } + + /** + * Sends a text message to the channel the server query is currently in. + * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes. + * + * @param message + * the text message to send + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public void sendChannelMessage(String message) { + asyncApi.sendChannelMessage(message).getUninterruptibly(); + } + + /** + * Sends a text message to the virtual server with the specified ID. + * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes. + *

+ * This will move the client to the virtual server with the specified server ID, + * but will not move it back to the original virtual server! + *

+ * + * @param serverId + * the ID of the virtual server to which the message should be sent to + * @param message + * the text message to send + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #sendServerMessage(String) + * @see VirtualServer#getId() + */ + public void sendServerMessage(int serverId, String message) { + asyncApi.sendServerMessage(serverId, message).getUninterruptibly(); + } + + /** + * Sends a text message to the virtual server the server query is currently in. + * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes. + * + * @param message + * the text message to send + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public void sendServerMessage(String message) { + asyncApi.sendServerMessage(message).getUninterruptibly(); + } + + /** + * Sends a private message to the client with the specified client ID. + * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes. + * + * @param clientId + * the ID of the client to send the message to + * @param message + * the text message to send + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + */ + public void sendPrivateMessage(int clientId, String message) { + asyncApi.sendPrivateMessage(clientId, message).getUninterruptibly(); + } + + /** + * Sets a channel group for a client in a specific channel. + * + * @param groupId + * the ID of the group the client should join + * @param channelId + * the ID of the channel where the channel group should be assigned + * @param clientDBId + * the database ID of the client for which the channel group should be set + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + * @see Channel#getId() + * @see Client#getDatabaseId() + */ + public void setClientChannelGroup(int groupId, int channelId, int clientDBId) { + asyncApi.setClientChannelGroup(groupId, channelId, clientDBId).getUninterruptibly(); + } + + /** + * Sets the value of the multiple custom client properties for a client. + *

+ * If any key present in the map already has a value assigned for this client, + * the existing value will be overwritten. + * This method does not delete keys not present in the map. + *

+ * If {@code properties} contains an entry with {@code null} as its key, + * that entry will be ignored and no exception will be thrown. + *

+ * + * @param clientDBId + * the database ID of the target client + * @param properties + * the map of properties to set, cannot be {@code null} + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands properties.size() + * @see Client#getDatabaseId() + * @see #setCustomClientProperty(int, String, String) + * @see #deleteCustomClientProperty(int, String) + */ + public void setCustomClientProperties(int clientDBId, Map properties) { + asyncApi.setCustomClientProperties(clientDBId, properties).getUninterruptibly(); + } + + /** + * Sets the value of the {@code key} custom client property for a client. + *

+ * If there is already an assignment of the {@code key} custom client property + * for this client, the existing value will be overwritten. + *

+ * + * @param clientDBId + * the database ID of the target client + * @param key + * the key of the custom property to set, cannot be {@code null} + * @param value + * the (new) value of the custom property to set + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see #setCustomClientProperties(int, Map) + * @see #deleteCustomClientProperty(int, String) + */ + public void setCustomClientProperty(int clientDBId, String key, String value) { + asyncApi.setCustomClientProperty(clientDBId, key, value).getUninterruptibly(); + } + + /** + * Sets the read flag to {@code true} for a given message. This will not delete the message. + * + * @param messageId + * the ID of the message for which the read flag should be set + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #setMessageReadFlag(int, boolean) + */ + public void setMessageRead(int messageId) { + asyncApi.setMessageRead(messageId).getUninterruptibly(); + } + + /** + * Sets the read flag to {@code true} for a given message. This will not delete the message. + * + * @param message + * the message for which the read flag should be set + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #setMessageRead(int) + * @see #setMessageReadFlag(Message, boolean) + * @see #deleteOfflineMessage(int) + */ + public void setMessageRead(Message message) { + asyncApi.setMessageRead(message).getUninterruptibly(); + } + + /** + * Sets the read flag for a given message. This will not delete the message. + * + * @param messageId + * the ID of the message for which the read flag should be set + * @param read + * the boolean value to which the read flag should be set + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #setMessageRead(int) + * @see #setMessageReadFlag(Message, boolean) + * @see #deleteOfflineMessage(int) + */ + public void setMessageReadFlag(int messageId, boolean read) { + asyncApi.setMessageReadFlag(messageId, read).getUninterruptibly(); + } + + /** + * Sets the read flag for a given message. This will not delete the message. + * + * @param message + * the message for which the read flag should be set + * @param read + * the boolean value to which the read flag should be set + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #setMessageRead(Message) + * @see #setMessageReadFlag(int, boolean) + * @see #deleteOfflineMessage(int) + */ + public void setMessageReadFlag(Message message, boolean read) { + asyncApi.setMessageReadFlag(message, read).getUninterruptibly(); + } + + /** + * Sets the nickname of the server query client. + *

+ * The nickname must be between 3 and 30 UTF-8 bytes long. BB codes will be ignored. + *

+ * + * @param nickname + * the new nickname, may not be {@code null} + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #updateClient(Map) + */ + public void setNickname(String nickname) { + asyncApi.setNickname(nickname).getUninterruptibly(); + } + + /** + * Starts the virtual server with the specified ID. + * + * @param serverId + * the ID of the virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public void startServer(int serverId) { + asyncApi.startServer(serverId).getUninterruptibly(); + } + + /** + * Starts the specified virtual server. + * + * @param virtualServer + * the virtual server to start + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public void startServer(VirtualServer virtualServer) { + asyncApi.startServer(virtualServer).getUninterruptibly(); + } + + /** + * Stops the virtual server with the specified ID. + * + * @param serverId + * the ID of the virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public void stopServer(int serverId) { + asyncApi.stopServer(serverId).getUninterruptibly(); + } + + /** + * Stops the virtual server with the specified ID. + * + * @param serverId + * the ID of the virtual server + * @param reason + * the reason message to display to clients when they are disconnected + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public void stopServer(int serverId, String reason) { + asyncApi.stopServer(serverId, reason).getUninterruptibly(); + } + + /** + * Stops the specified virtual server. + * + * @param virtualServer + * the virtual server to stop + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public void stopServer(VirtualServer virtualServer) { + asyncApi.stopServer(virtualServer).getUninterruptibly(); + } + + /** + * Stops the specified virtual server. + * + * @param virtualServer + * the virtual server to stop + * @param reason + * the reason message to display to clients when they are disconnected + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public void stopServer(VirtualServer virtualServer, String reason) { + asyncApi.stopServer(virtualServer, reason).getUninterruptibly(); + } + + /** + * Stops the entire TeamSpeak 3 Server instance by shutting down the process. + *

+ * To have permission to use this command, you need to use the server query admin login. + *

+ * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public void stopServerProcess() { + asyncApi.stopServerProcess().getUninterruptibly(); + } + + /** + * Stops the entire TeamSpeak 3 Server instance by shutting down the process. + *

+ * To have permission to use this command, you need to use the server query admin login. + *

+ * + * @param reason + * the reason message to display to clients when they are disconnected + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public void stopServerProcess(String reason) { + asyncApi.stopServerProcess(reason).getUninterruptibly(); + } + + /** + * Unregisters the server query from receiving any event notifications. + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public void unregisterAllEvents() { + asyncApi.unregisterAllEvents().getUninterruptibly(); + } + + /** + * Updates several client properties for this server query instance. + * + * @param options + * the map of properties to update + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #updateClient(ClientProperty, String) + * @see #editClient(int, Map) + */ + public void updateClient(Map options) { + asyncApi.updateClient(options).getUninterruptibly(); + } + + /** + * Changes a single client property for this server query instance. + *

+ * Note that one can set many properties at once with the overloaded method that + * takes a map of client properties and strings. + *

+ * + * @param property + * the client property to modify, make sure it is editable + * @param value + * the new value of the property + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #updateClient(Map) + * @see #editClient(int, Map) + */ + public void updateClient(ClientProperty property, String value) { + asyncApi.updateClient(property, value).getUninterruptibly(); + } + + /** + * Generates new login credentials for the currently connected server query instance, using the given name. + *

+ * This will remove the current login credentials! You won't be logged out, but after disconnecting, + * the old credentials will no longer work. Make sure to not lock yourselves out! + *

+ * + * @param loginName + * the name for the server query login + * + * @return the generated password for the server query login + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public String updateServerQueryLogin(String loginName) { + return asyncApi.updateServerQueryLogin(loginName).getUninterruptibly(); + } + + /** + * Uploads a file to the file repository at a given path and channel + * by reading {@code dataLength} bytes from an open {@link InputStream}. + *

+ * It is the user's responsibility to ensure that the given {@code InputStream} is + * open and that {@code dataLength} bytes can eventually be read from it. The user is + * also responsible for closing the stream once the upload has finished. + *

+ * Note that this method will not read the entire file to memory and can thus + * upload arbitrarily sized files to the file repository. + *

+ * + * @param dataIn + * a stream that contains the data that should be uploaded + * @param dataLength + * how many bytes should be read from the stream + * @param filePath + * the path the file should have after being uploaded + * @param overwrite + * if {@code false}, fails if there's already a file at {@code filePath} + * @param channelId + * the ID of the channel to upload the file to + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #uploadFileDirect(byte[], String, boolean, int, String) + */ + public void uploadFile(InputStream dataIn, long dataLength, String filePath, boolean overwrite, int channelId) { + asyncApi.uploadFile(dataIn, dataLength, filePath, overwrite, channelId).getUninterruptibly(); + } + + /** + * Uploads a file to the file repository at a given path and channel + * by reading {@code dataLength} bytes from an open {@link InputStream}. + *

+ * It is the user's responsibility to ensure that the given {@code InputStream} is + * open and that {@code dataLength} bytes can eventually be read from it. The user is + * also responsible for closing the stream once the upload has finished. + *

+ * Note that this method will not read the entire file to memory and can thus + * upload arbitrarily sized files to the file repository. + *

+ * + * @param dataIn + * a stream that contains the data that should be uploaded + * @param dataLength + * how many bytes should be read from the stream + * @param filePath + * the path the file should have after being uploaded + * @param overwrite + * if {@code false}, fails if there's already a file at {@code filePath} + * @param channelId + * the ID of the channel to upload the file to + * @param channelPassword + * that channel's password + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #uploadFileDirect(byte[], String, boolean, int, String) + */ + public void uploadFile(InputStream dataIn, long dataLength, String filePath, boolean overwrite, int channelId, String channelPassword) { + asyncApi.uploadFile(dataIn, dataLength, filePath, overwrite, channelId, channelPassword).getUninterruptibly(); + } + + /** + * Uploads a file that is already stored in memory to the file repository + * at a given path and channel. + * + * @param data + * the file's data as a byte array + * @param filePath + * the path the file should have after being uploaded + * @param overwrite + * if {@code false}, fails if there's already a file at {@code filePath} + * @param channelId + * the ID of the channel to upload the file to + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #uploadFile(InputStream, long, String, boolean, int) + */ + public void uploadFileDirect(byte[] data, String filePath, boolean overwrite, int channelId) { + asyncApi.uploadFileDirect(data, filePath, overwrite, channelId).getUninterruptibly(); + } + + /** + * Uploads a file that is already stored in memory to the file repository + * at a given path and channel. + * + * @param data + * the file's data as a byte array + * @param filePath + * the path the file should have after being uploaded + * @param overwrite + * if {@code false}, fails if there's already a file at {@code filePath} + * @param channelId + * the ID of the channel to upload the file to + * @param channelPassword + * that channel's password + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #uploadFile(InputStream, long, String, boolean, int, String) + */ + public void uploadFileDirect(byte[] data, String filePath, boolean overwrite, int channelId, String channelPassword) { + asyncApi.uploadFileDirect(data, filePath, overwrite, channelId, channelPassword).getUninterruptibly(); + } + + /** + * Uploads an icon to the icon directory in the file repository + * by reading {@code dataLength} bytes from an open {@link InputStream}. + *

+ * It is the user's responsibility to ensure that the given {@code InputStream} is + * open and that {@code dataLength} bytes can eventually be read from it. The user is + * also responsible for closing the stream once the upload has finished. + *

+ * Note that unlike the file upload methods, this will read the entire file to memory. + * This is because the CRC32 hash must be calculated before the icon can be uploaded. + * That means that all icon files must be less than 231-1 bytes in size. + *

+ * Uploads that is already stored in memory to the icon directory + * in the file repository. If this icon has already been uploaded or + * if a hash collision occurs (CRC32), this command will fail. + * + * @param dataIn + * a stream that contains the data that should be uploaded + * @param dataLength + * how many bytes should be read from the stream + * + * @return the ID of the uploaded icon + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see IconFile#getIconId() + * @see #uploadIconDirect(byte[]) + * @see #downloadIcon(OutputStream, long) + */ + public long uploadIcon(InputStream dataIn, long dataLength) { + return asyncApi.uploadIcon(dataIn, dataLength).getUninterruptibly(); + } + + /** + * Uploads an icon that is already stored in memory to the icon directory + * in the file repository. If this icon has already been uploaded or + * if a CRC32 hash collision occurs, this command will fail. + * + * @param data + * the icon's data as a byte array + * + * @return the ID of the uploaded icon + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see IconFile#getIconId() + * @see #uploadIcon(InputStream, long) + * @see #downloadIconDirect(long) + */ + public long uploadIconDirect(byte[] data) { + return asyncApi.uploadIconDirect(data).getUninterruptibly(); + } + + /** + * Uses an existing privilege key to join a server or channel group. + * + * @param token + * the privilege key to use + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see PrivilegeKey + * @see #addPrivilegeKey(PrivilegeKeyType, int, int, String) + * @see #usePrivilegeKey(PrivilegeKey) + */ + public void usePrivilegeKey(String token) { + asyncApi.usePrivilegeKey(token).getUninterruptibly(); + } + + /** + * Uses an existing privilege key to join a server or channel group. + * + * @param privilegeKey + * the privilege key to use + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see PrivilegeKey + * @see #addPrivilegeKey(PrivilegeKeyType, int, int, String) + * @see #usePrivilegeKey(String) + */ + public void usePrivilegeKey(PrivilegeKey privilegeKey) { + asyncApi.usePrivilegeKey(privilegeKey).getUninterruptibly(); + } + + /** + * Gets information about the current server query instance. + * + * @return information about the server query instance + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #getClientInfo(int) + */ + public ServerQueryInfo whoAmI() { + return asyncApi.whoAmI().getUninterruptibly(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/TS3ApiAsync.java b/src/com/github/theholywaffle/teamspeak3/TS3ApiAsync.java new file mode 100644 index 0000000..edecb26 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/TS3ApiAsync.java @@ -0,0 +1,5541 @@ +package com.github.theholywaffle.teamspeak3; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.*; +import com.github.theholywaffle.teamspeak3.api.event.TS3EventType; +import com.github.theholywaffle.teamspeak3.api.event.TS3Listener; +import com.github.theholywaffle.teamspeak3.api.exception.TS3CommandFailedException; +import com.github.theholywaffle.teamspeak3.api.exception.TS3Exception; +import com.github.theholywaffle.teamspeak3.api.exception.TS3FileTransferFailedException; +import com.github.theholywaffle.teamspeak3.api.wrapper.*; +import com.github.theholywaffle.teamspeak3.commands.*; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * Asynchronous version of {@link TS3Api} to interact with the {@link TS3Query}. + *

+ * This class is used to easily interact with a {@link TS3Query}. It constructs commands, + * sends them to the TeamSpeak3 server, processes the response and returns the result. + *

+ * All methods in this class are asynchronous (so they won't block) and + * will return a {@link CommandFuture} of the corresponding return type in {@link TS3Api}. + * If a command fails, no exception will be thrown directly. It will however be rethrown in + * {@link CommandFuture#get()} and {@link CommandFuture#get(long, TimeUnit)}. + * Usually, the thrown exception is a {@link TS3CommandFailedException}, which will get you + * access to the {@link QueryError} from which more information about the error can be obtained. + *

+ * Also note that while these methods are asynchronous, the commands will still be sent through a + * synchronous command pipeline. That means if an asynchronous method is called immediately + * followed by a synchronous method, the synchronous method will first have to wait until the + * asynchronous method completed until it its command is sent. + *

+ * You won't be able to execute most commands while you're not logged in due to missing permissions. + * Make sure to either pass your login credentials to the {@link TS3Config} object when + * creating the {@code TS3Query} or to call {@link #login(String, String)} to log in. + *

+ * After that, most commands also require you to select a {@linkplain VirtualServer virtual server}. + * To do so, call either {@link #selectVirtualServerByPort(int)} or {@link #selectVirtualServerById(int)}. + *

+ * + * @see TS3Api The synchronous version of the API + */ +public class TS3ApiAsync { + + /** + * The TS3 query to which this API sends its commands. + */ + private final TS3Query query; + + /** + * Creates a new asynchronous API object for the given {@code TS3Query}. + *

+ * Usually, this constructor should not be called. Use {@link TS3Query#getAsyncApi()} instead. + *

+ * + * @param query + * the TS3Query to call + */ + public TS3ApiAsync(TS3Query query) { + this.query = query; + } + + /** + * Adds a new ban entry. At least one of the parameters {@code ip}, {@code name} or {@code uid} needs to be non-null. + * Returns the ID of the newly created ban entry. + * + * @param ip + * a RegEx pattern to match a client's IP against, can be {@code null} + * @param name + * a RegEx pattern to match a client's name against, can be {@code null} + * @param uid + * the unique identifier of a client, can be {@code null} + * @param timeInSeconds + * the duration of the ban in seconds. 0 equals a permanent ban + * @param reason + * the reason for the ban, can be {@code null} + * + * @return the ID of the newly created ban entry + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Pattern RegEx Pattern + * @see #addBan(String, String, String, String, long, String) + * @see Client#getId() + * @see Client#getUniqueIdentifier() + * @see ClientInfo#getIp() + */ + public CommandFuture addBan(String ip, String name, String uid, long timeInSeconds, String reason) { + return addBan(ip, name, uid, null, timeInSeconds, reason); + } + + /** + * Adds a new ban entry. At least one of the parameters {@code ip}, {@code name}, {@code uid}, or + * {@code myTSId} needs to be non-null. Returns the ID of the newly created ban entry. + *

+ * Note that creating a ban entry for the {@code "empty"} "myTeamSpeak" ID will ban all clients who + * don't have a linked "myTeamSpeak" account. + *

+ * + * @param ip + * a RegEx pattern to match a client's IP against, can be {@code null} + * @param name + * a RegEx pattern to match a client's name against, can be {@code null} + * @param uid + * the unique identifier of a client, can be {@code null} + * @param myTSId + * the "myTeamSpeak" ID of a client, the string {@code "empty"}, or {@code null} + * @param timeInSeconds + * the duration of the ban in seconds. 0 equals a permanent ban + * @param reason + * the reason for the ban, can be {@code null} + * + * @return the ID of the newly created ban entry + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Pattern RegEx Pattern + * @see Client#getId() + * @see Client#getUniqueIdentifier() + * @see ClientInfo#getIp() + */ + public CommandFuture addBan(String ip, String name, String uid, String myTSId, long timeInSeconds, String reason) { + Command cmd = BanCommands.banAdd(ip, name, uid, myTSId, timeInSeconds, reason); + return executeAndReturnIntProperty(cmd, "banid"); + } + + /** + * Adds a specified permission to a client in a specific channel. + * + * @param channelId + * the ID of the channel wherein the permission should be granted + * @param clientDBId + * the database ID of the client to add a permission to + * @param permName + * the name of the permission to grant + * @param permValue + * the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false) + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see Client#getDatabaseId() + * @see Permission + */ + public CommandFuture addChannelClientPermission(int channelId, int clientDBId, String permName, int permValue) { + Command cmd = PermissionCommands.channelClientAddPerm(channelId, clientDBId, permName, permValue); + return executeAndReturnError(cmd); + } + + /** + * Creates a new channel group for clients using a given name and returns its ID. + *

+ * To create channel group templates or ones for server queries, + * use {@link #addChannelGroup(String, PermissionGroupDatabaseType)}. + *

+ * + * @param name + * the name of the new channel group + * + * @return the ID of the newly created channel group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup + */ + public CommandFuture addChannelGroup(String name) { + return addChannelGroup(name, null); + } + + /** + * Creates a new channel group using a given name and returns its ID. + * + * @param name + * the name of the new channel group + * @param type + * the desired type of channel group + * + * @return the ID of the newly created channel group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup + */ + public CommandFuture addChannelGroup(String name, PermissionGroupDatabaseType type) { + Command cmd = ChannelGroupCommands.channelGroupAdd(name, type); + return executeAndReturnIntProperty(cmd, "cgid"); + } + + /** + * Adds a specified permission to a channel group. + * + * @param groupId + * the ID of the channel group to grant the permission + * @param permName + * the name of the permission to be granted + * @param permValue + * the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false) + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + * @see Permission + */ + public CommandFuture addChannelGroupPermission(int groupId, String permName, int permValue) { + Command cmd = PermissionCommands.channelGroupAddPerm(groupId, permName, permValue); + return executeAndReturnError(cmd); + } + + /** + * Adds a specified permission to a channel. + * + * @param channelId + * the ID of the channel wherein the permission should be granted + * @param permName + * the name of the permission to grant + * @param permValue + * the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false) + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see Permission + */ + public CommandFuture addChannelPermission(int channelId, String permName, int permValue) { + Command cmd = PermissionCommands.channelAddPerm(channelId, permName, permValue); + return executeAndReturnError(cmd); + } + + /** + * Adds a specified permission to a channel. + * + * @param clientDBId + * the database ID of the client to grant the permission + * @param permName + * the name of the permission to grant + * @param value + * the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false) + * @param skipped + * if set to {@code true}, the permission will not be overridden by channel group permissions + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see Permission + */ + public CommandFuture addClientPermission(int clientDBId, String permName, int value, boolean skipped) { + Command cmd = PermissionCommands.clientAddPerm(clientDBId, permName, value, skipped); + return executeAndReturnError(cmd); + } + + /** + * Adds a client to the specified server group. + *

+ * Please note that a client cannot be added to default groups or template groups. + *

+ * + * @param groupId + * the ID of the server group to add the client to + * @param clientDatabaseId + * the database ID of the client to add + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + * @see Client#getDatabaseId() + */ + public CommandFuture addClientToServerGroup(int groupId, int clientDatabaseId) { + Command cmd = ServerGroupCommands.serverGroupAddClient(groupId, clientDatabaseId); + return executeAndReturnError(cmd); + } + + /** + * Submits a complaint about the specified client. + * The length of the message is limited to 200 UTF-8 bytes and BB codes in it will be ignored. + * + * @param clientDBId + * the database ID of the client + * @param message + * the message of the complaint, may not contain BB codes + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see Complaint#getMessage() + */ + public CommandFuture addComplaint(int clientDBId, String message) { + Command cmd = ComplaintCommands.complainAdd(clientDBId, message); + return executeAndReturnError(cmd); + } + + /** + * Adds a specified permission to all server groups of the type specified by {@code type} on all virtual servers. + * + * @param type + * the kind of server group this permission should be added to + * @param permName + * the name of the permission to be granted + * @param value + * the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false) + * @param negated + * if set to true, the lowest permission value will be selected instead of the highest + * @param skipped + * if set to true, this permission will not be overridden by client or channel group permissions + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroupType + * @see Permission + */ + public CommandFuture addPermissionToAllServerGroups(ServerGroupType type, String permName, int value, boolean negated, boolean skipped) { + Command cmd = PermissionCommands.serverGroupAutoAddPerm(type, permName, value, negated, skipped); + return executeAndReturnError(cmd); + } + + /** + * Create a new privilege key that allows one client to join a server or channel group. + *
    + *
  • If {@code type} is set to {@linkplain PrivilegeKeyType#SERVER_GROUP SERVER_GROUP}, + * {@code groupId} is used as a server group ID and {@code channelId} is ignored.
  • + *
  • If {@code type} is set to {@linkplain PrivilegeKeyType#CHANNEL_GROUP CHANNEL_GROUP}, + * {@code groupId} is used as a channel group ID and {@code channelId} is used as the channel in which the group should be set.
  • + *
+ * + * @param type + * the type of token that should be created + * @param groupId + * the ID of the server or channel group + * @param channelId + * the ID of the channel, in case the token is channel group token + * @param description + * the description for the token, can be null + * + * @return the created token for a client to use + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see PrivilegeKeyType + * @see #addPrivilegeKeyServerGroup(int, String) + * @see #addPrivilegeKeyChannelGroup(int, int, String) + */ + public CommandFuture addPrivilegeKey(PrivilegeKeyType type, int groupId, int channelId, String description) { + Command cmd = PrivilegeKeyCommands.privilegeKeyAdd(type, groupId, channelId, description); + return executeAndReturnStringProperty(cmd, "token"); + } + + /** + * Creates a new privilege key for a channel group. + * + * @param channelGroupId + * the ID of the channel group + * @param channelId + * the ID of the channel in which the channel group should be set + * @param description + * the description for the token, can be null + * + * @return the created token for a client to use + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + * @see Channel#getId() + * @see #addPrivilegeKey(PrivilegeKeyType, int, int, String) + * @see #addPrivilegeKeyServerGroup(int, String) + */ + public CommandFuture addPrivilegeKeyChannelGroup(int channelGroupId, int channelId, String description) { + return addPrivilegeKey(PrivilegeKeyType.CHANNEL_GROUP, channelGroupId, channelId, description); + } + + /** + * Creates a new privilege key for a server group. + * + * @param serverGroupId + * the ID of the server group + * @param description + * the description for the token, can be null + * + * @return the created token for a client to use + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + * @see #addPrivilegeKey(PrivilegeKeyType, int, int, String) + * @see #addPrivilegeKeyChannelGroup(int, int, String) + */ + public CommandFuture addPrivilegeKeyServerGroup(int serverGroupId, String description) { + return addPrivilegeKey(PrivilegeKeyType.SERVER_GROUP, serverGroupId, 0, description); + } + + /** + * Creates a new server group for clients using a given name and returns its ID. + *

+ * To create server group templates or ones for server queries, + * use {@link #addServerGroup(String, PermissionGroupDatabaseType)}. + *

+ * + * @param name + * the name of the new server group + * + * @return the ID of the newly created server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup + */ + public CommandFuture addServerGroup(String name) { + return addServerGroup(name, PermissionGroupDatabaseType.REGULAR); + } + + /** + * Creates a new server group using a given name and returns its ID. + * + * @param name + * the name of the new server group + * @param type + * the desired type of server group + * + * @return the ID of the newly created server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup + * @see PermissionGroupDatabaseType + */ + public CommandFuture addServerGroup(String name, PermissionGroupDatabaseType type) { + Command cmd = ServerGroupCommands.serverGroupAdd(name, type); + return executeAndReturnIntProperty(cmd, "sgid"); + } + + /** + * Adds a specified permission to a server group. + * + * @param groupId + * the ID of the channel group to which the permission should be added + * @param permName + * the name of the permission to add + * @param value + * the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false) + * @param negated + * if set to true, the lowest permission value will be selected instead of the highest + * @param skipped + * if set to true, this permission will not be overridden by client or channel group permissions + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + * @see Permission + */ + public CommandFuture addServerGroupPermission(int groupId, String permName, int value, boolean negated, boolean skipped) { + Command cmd = PermissionCommands.serverGroupAddPerm(groupId, permName, value, negated, skipped); + return executeAndReturnError(cmd); + } + + /** + * Adds one or more {@link TS3Listener}s to the event manager of the query. + * These listeners will be notified when the TS3 server fires an event. + *

+ * Note that for the TS3 server to fire events, you must first also register + * the event types you want to listen to. + *

+ * + * @param listeners + * one or more listeners to register + * + * @see #registerAllEvents() + * @see #registerEvent(TS3EventType, int) + * @see TS3Listener + * @see TS3EventType + */ + public void addTS3Listeners(TS3Listener... listeners) { + query.getEventManager().addListeners(listeners); + } + + /** + * Bans a client with a given client ID for a given time. + *

+ * Please note that this will create 2 or 3 separate ban rules, + * one for the targeted client's IP address, one for their unique identifier, + * and potentially one more for their "myTeamSpeak" ID. + *

+ * Exception: If the banned client connects via a loopback address + * (i.e. {@code 127.0.0.1} or {@code localhost}), no IP ban is created + * and the returned array will only have 1 entry. + *

+ * + * @param clientId + * the ID of the client + * @param timeInSeconds + * the duration of the ban in seconds. 0 equals a permanent ban + * + * @return an array containing the IDs of the created ban entries + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see #addBan(String, String, String, long, String) + */ + public CommandFuture banClient(int clientId, long timeInSeconds) { + return banClient(clientId, timeInSeconds, null); + } + + /** + * Bans a client with a given client ID for a given time for the specified reason. + *

+ * Please note that this will create two separate ban rules, + * one for the targeted client's IP address and their unique identifier. + *

+ * Exception: If the banned client connects via a loopback address + * (i.e. {@code 127.0.0.1} or {@code localhost}), no IP ban is created + * and the returned array will only have 1 entry. + *

+ * + * @param clientId + * the ID of the client + * @param timeInSeconds + * the duration of the ban in seconds. 0 equals a permanent ban + * @param reason + * the reason for the ban, can be null + * + * @return an array containing the IDs of the first and the second ban entry + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see #addBan(String, String, String, long, String) + */ + public CommandFuture banClient(int clientId, long timeInSeconds, String reason) { + Command cmd = BanCommands.banClient(clientId, timeInSeconds, reason); + return executeAndReturnIntArray(cmd, "banid"); + } + + /** + * Bans a client with a given client ID permanently for the specified reason. + *

+ * Please note that this will create two separate ban rules, + * one for the targeted client's IP address and their unique identifier. + *

+ * Exception: If the banned client connects via a loopback address + * (i.e. {@code 127.0.0.1} or {@code localhost}), no IP ban is created + * and the returned array will only have 1 entry. + *

+ * + * @param clientId + * the ID of the client + * @param reason + * the reason for the ban, can be null + * + * @return an array containing the IDs of the first and the second ban entry + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see #addBan(String, String, String, long, String) + */ + public CommandFuture banClient(int clientId, String reason) { + return banClient(clientId, 0, reason); + } + + /** + * Sends a text message to all clients on all virtual servers. + * These messages will appear to clients in the tab for server messages. + * + * @param message + * the message to be sent + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture broadcast(String message) { + Command cmd = ServerCommands.gm(message); + return executeAndReturnError(cmd); + } + + /** + * Creates a copy of the channel group specified by {@code sourceGroupId}, + * overwriting any other channel group specified by {@code targetGroupId}. + *

+ * The parameter {@code type} can be used to create server query and template groups. + *

+ * + * @param sourceGroupId + * the ID of the channel group to copy + * @param targetGroupId + * the ID of another channel group to overwrite + * @param type + * the desired type of channel group + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + */ + public CommandFuture copyChannelGroup(int sourceGroupId, int targetGroupId, PermissionGroupDatabaseType type) { + if (targetGroupId <= 0) { + throw new IllegalArgumentException("To create a new channel group, use the method with a String argument"); + } + + Command cmd = ChannelGroupCommands.channelGroupCopy(sourceGroupId, targetGroupId, type); + return executeAndReturnError(cmd); + } + + /** + * Creates a copy of the channel group specified by {@code sourceGroupId} with a given name + * and returns the ID of the newly created channel group. + * + * @param sourceGroupId + * the ID of the channel group to copy + * @param targetName + * the name for the copy of the channel group + * @param type + * the desired type of channel group + * + * @return the ID of the newly created channel group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + */ + public CommandFuture copyChannelGroup(int sourceGroupId, String targetName, PermissionGroupDatabaseType type) { + Command cmd = ChannelGroupCommands.channelGroupCopy(sourceGroupId, targetName, type); + return executeAndReturnIntProperty(cmd, "cgid"); + } + + /** + * Creates a copy of the server group specified by {@code sourceGroupId}, + * overwriting another server group specified by {@code targetGroupId}. + *

+ * The parameter {@code type} can be used to create server query and template groups. + *

+ * + * @param sourceGroupId + * the ID of the server group to copy + * @param targetGroupId + * the ID of another server group to overwrite + * @param type + * the desired type of server group + * + * @return the ID of the newly created server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + */ + public CommandFuture copyServerGroup(int sourceGroupId, int targetGroupId, PermissionGroupDatabaseType type) { + if (targetGroupId <= 0) { + throw new IllegalArgumentException("To create a new server group, use the method with a String argument"); + } + + Command cmd = ServerGroupCommands.serverGroupCopy(sourceGroupId, targetGroupId, type); + return executeAndReturnIntProperty(cmd, "sgid"); + } + + /** + * Creates a copy of the server group specified by {@code sourceGroupId} with a given name + * and returns the ID of the newly created server group. + * + * @param sourceGroupId + * the ID of the server group to copy + * @param targetName + * the name for the copy of the server group + * @param type + * the desired type of server group + * + * @return the ID of the newly created server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + */ + public CommandFuture copyServerGroup(int sourceGroupId, String targetName, PermissionGroupDatabaseType type) { + Command cmd = ServerGroupCommands.serverGroupCopy(sourceGroupId, targetName, type); + return executeAndReturnIntProperty(cmd, "sgid"); + } + + /** + * Creates a new channel with a given name using the given properties and returns its ID. + * + * @param name + * the name for the new channel + * @param options + * a map of options that should be set for the channel + * + * @return the ID of the newly created channel + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel + */ + public CommandFuture createChannel(String name, Map options) { + Command cmd = ChannelCommands.channelCreate(name, options); + return executeAndReturnIntProperty(cmd, "cid"); + } + + /** + * Creates a new directory on the file repository in the specified channel. + * + * @param directoryPath + * the path to the directory that should be created + * @param channelId + * the ID of the channel the directory should be created in + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public CommandFuture createFileDirectory(String directoryPath, int channelId) { + return createFileDirectory(directoryPath, channelId, null); + } + + /** + * Creates a new directory on the file repository in the specified channel. + * + * @param directoryPath + * the path to the directory that should be created + * @param channelId + * the ID of the channel the directory should be created in + * @param channelPassword + * the password of that channel + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public CommandFuture createFileDirectory(String directoryPath, int channelId, String channelPassword) { + Command cmd = FileCommands.ftCreateDir(directoryPath, channelId, channelPassword); + return executeAndReturnError(cmd); + } + + /** + * Creates a new virtual server with the given name and returns an object containing the ID of the newly + * created virtual server, the default server admin token and the virtual server's voice port. Usually, + * the virtual server is also automatically started. This can be turned off on the TS3 server, though. + *

+ * If {@link VirtualServerProperty#VIRTUALSERVER_PORT} is not specified in the virtual server properties, + * the server will test for the first unused UDP port. + *

+ * Please also note that creating virtual servers usually requires the server query admin account + * and that there is a limit to how many virtual servers can be created, which is dependent on your license. + * Unlicensed TS3 server instances are limited to 1 virtual server with up to 32 client slots. + *

+ * + * @param name + * the name for the new virtual server + * @param options + * a map of options that should be set for the virtual server + * + * @return information about the newly created virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see VirtualServer + */ + public CommandFuture createServer(String name, Map options) { + Command cmd = VirtualServerCommands.serverCreate(name, options); + return executeAndTransformFirst(cmd, CreatedVirtualServer::new); + } + + /** + * Creates a {@link Snapshot} of the selected virtual server containing all settings, + * groups and known client identities. The data from a server snapshot can be + * used to restore a virtual servers configuration. + * + * @return a snapshot of the virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #deployServerSnapshot(Snapshot) + */ + public CommandFuture createServerSnapshot() { + Command cmd = VirtualServerCommands.serverSnapshotCreate(); + CommandFuture future = cmd.getFuture() + .map(result -> new Snapshot(result.getRawResponse())); + + query.doCommandAsync(cmd); + return future; + } + + /** + * Deletes all active ban rules from the server. Use with caution. + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture deleteAllBans() { + Command cmd = BanCommands.banDelAll(); + return executeAndReturnError(cmd); + } + + /** + * Deletes all complaints about the client with specified database ID from the server. + * + * @param clientDBId + * the database ID of the client + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see Complaint + */ + public CommandFuture deleteAllComplaints(int clientDBId) { + Command cmd = ComplaintCommands.complainDelAll(clientDBId); + return executeAndReturnError(cmd); + } + + /** + * Deletes the ban rule with the specified ID from the server. + * + * @param banId + * the ID of the ban to delete + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Ban#getId() + */ + public CommandFuture deleteBan(int banId) { + Command cmd = BanCommands.banDel(banId); + return executeAndReturnError(cmd); + } + + /** + * Deletes an existing channel specified by its ID, kicking all clients out of the channel. + * + * @param channelId + * the ID of the channel to delete + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see #deleteChannel(int, boolean) + * @see #kickClientFromChannel(String, int...) + */ + public CommandFuture deleteChannel(int channelId) { + return deleteChannel(channelId, true); + } + + /** + * Deletes an existing channel with a given ID. + * If {@code force} is true, the channel will be deleted even if there are clients within, + * else the command will fail in this situation. + * + * @param channelId + * the ID of the channel to delete + * @param force + * whether clients should be kicked out of the channel + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see #kickClientFromChannel(String, int...) + */ + public CommandFuture deleteChannel(int channelId, boolean force) { + Command cmd = ChannelCommands.channelDelete(channelId, force); + return executeAndReturnError(cmd); + } + + /** + * Removes a specified permission from a client in a specific channel. + * + * @param channelId + * the ID of the channel wherein the permission should be removed + * @param clientDBId + * the database ID of the client + * @param permName + * the name of the permission to revoke + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see Client#getDatabaseId() + * @see Permission#getName() + */ + public CommandFuture deleteChannelClientPermission(int channelId, int clientDBId, String permName) { + Command cmd = PermissionCommands.channelClientDelPerm(channelId, clientDBId, permName); + return executeAndReturnError(cmd); + } + + /** + * Removes the channel group with the given ID. + * + * @param groupId + * the ID of the channel group + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + */ + public CommandFuture deleteChannelGroup(int groupId) { + return deleteChannelGroup(groupId, true); + } + + /** + * Removes the channel group with the given ID. + * If {@code force} is true, the channel group will be deleted even if it still contains clients, + * else the command will fail in this situation. + * + * @param groupId + * the ID of the channel group + * @param force + * whether the channel group should be deleted even if it still contains clients + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + */ + public CommandFuture deleteChannelGroup(int groupId, boolean force) { + Command cmd = ChannelGroupCommands.channelGroupDel(groupId, force); + return executeAndReturnError(cmd); + } + + /** + * Removes a permission from the channel group with the given ID. + * + * @param groupId + * the ID of the channel group + * @param permName + * the name of the permission to revoke + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + * @see Permission#getName() + */ + public CommandFuture deleteChannelGroupPermission(int groupId, String permName) { + Command cmd = PermissionCommands.channelGroupDelPerm(groupId, permName); + return executeAndReturnError(cmd); + } + + /** + * Removes a permission from the channel with the given ID. + * + * @param channelId + * the ID of the channel + * @param permName + * the name of the permission to revoke + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see Permission#getName() + */ + public CommandFuture deleteChannelPermission(int channelId, String permName) { + Command cmd = PermissionCommands.channelDelPerm(channelId, permName); + return executeAndReturnError(cmd); + } + + /** + * Removes a permission from a client. + * + * @param clientDBId + * the database ID of the client + * @param permName + * the name of the permission to revoke + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see Permission#getName() + */ + public CommandFuture deleteClientPermission(int clientDBId, String permName) { + Command cmd = PermissionCommands.clientDelPerm(clientDBId, permName); + return executeAndReturnError(cmd); + } + + /** + * Deletes the complaint about the client with database ID {@code targetClientDBId} submitted by + * the client with database ID {@code fromClientDBId} from the server. + * + * @param targetClientDBId + * the database ID of the client the complaint is about + * @param fromClientDBId + * the database ID of the client who added the complaint + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Complaint + * @see Client#getDatabaseId() + */ + public CommandFuture deleteComplaint(int targetClientDBId, int fromClientDBId) { + Command cmd = ComplaintCommands.complainDel(targetClientDBId, fromClientDBId); + return executeAndReturnError(cmd); + } + + /** + * Removes the {@code key} custom client property from a client. + * + * @param clientDBId + * the database ID of the target client + * @param key + * the key of the custom property to delete, cannot be {@code null} + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + */ + public CommandFuture deleteCustomClientProperty(int clientDBId, String key) { + if (key == null) throw new IllegalArgumentException("Key cannot be null"); + + Command cmd = CustomPropertyCommands.customDelete(clientDBId, key); + return executeAndReturnError(cmd); + } + + /** + * Removes all stored database information about the specified client. + * Please note that this data is also automatically removed after a configured time (usually 90 days). + *

+ * See {@link DatabaseClientInfo} for a list of stored information about a client. + *

+ * + * @param clientDBId + * the database ID of the client + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see #getDatabaseClientInfo(int) + * @see DatabaseClientInfo + */ + public CommandFuture deleteDatabaseClientProperties(int clientDBId) { + Command cmd = DatabaseClientCommands.clientDBDelete(clientDBId); + return executeAndReturnError(cmd); + } + + /** + * Deletes a file or directory from the file repository in the specified channel. + * + * @param filePath + * the path to the file or directory + * @param channelId + * the ID of the channel the file or directory resides in + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public CommandFuture deleteFile(String filePath, int channelId) { + return deleteFile(filePath, channelId, null); + } + + /** + * Deletes a file or directory from the file repository in the specified channel. + * + * @param filePath + * the path to the file or directory + * @param channelId + * the ID of the channel the file or directory resides in + * @param channelPassword + * the password of that channel + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public CommandFuture deleteFile(String filePath, int channelId, String channelPassword) { + Command cmd = FileCommands.ftDeleteFile(channelId, channelPassword, filePath); + return executeAndReturnError(cmd); + } + + /** + * Deletes multiple files or directories from the file repository in the specified channel. + * + * @param filePaths + * the paths to the files or directories + * @param channelId + * the ID of the channel the file or directory resides in + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public CommandFuture deleteFiles(String[] filePaths, int channelId) { + return deleteFiles(filePaths, channelId, null); + } + + /** + * Deletes multiple files or directories from the file repository in the specified channel. + * + * @param filePaths + * the paths to the files or directories + * @param channelId + * the ID of the channel the file or directory resides in + * @param channelPassword + * the password of that channel + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public CommandFuture deleteFiles(String[] filePaths, int channelId, String channelPassword) { + Command cmd = FileCommands.ftDeleteFile(channelId, channelPassword, filePaths); + return executeAndReturnError(cmd); + } + + /** + * Deletes an icon from the icon directory in the file repository. + * + * @param iconId + * the ID of the icon to delete + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see IconFile#getIconId() + */ + public CommandFuture deleteIcon(long iconId) { + String iconPath = "/icon_" + iconId; + return deleteFile(iconPath, 0); + } + + /** + * Deletes multiple icons from the icon directory in the file repository. + * + * @param iconIds + * the IDs of the icons to delete + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see IconFile#getIconId() + */ + public CommandFuture deleteIcons(long... iconIds) { + String[] iconPaths = new String[iconIds.length]; + for (int i = 0; i < iconIds.length; ++i) { + iconPaths[i] = "/icon_" + iconIds[i]; + } + return deleteFiles(iconPaths, 0); + } + + /** + * Deletes the offline message with the specified ID. + * + * @param messageId + * the ID of the offline message to delete + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Message#getId() + */ + public CommandFuture deleteOfflineMessage(int messageId) { + Command cmd = MessageCommands.messageDel(messageId); + return executeAndReturnError(cmd); + } + + /** + * Removes a specified permission from all server groups of the type specified by {@code type} on all virtual servers. + * + * @param type + * the kind of server group this permission should be removed from + * @param permName + * the name of the permission to remove + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroupType + * @see Permission#getName() + */ + public CommandFuture deletePermissionFromAllServerGroups(ServerGroupType type, String permName) { + Command cmd = PermissionCommands.serverGroupAutoDelPerm(type, permName); + return executeAndReturnError(cmd); + } + + /** + * Deletes the privilege key with the given token. + * + * @param token + * the token of the privilege key + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see PrivilegeKey + */ + public CommandFuture deletePrivilegeKey(String token) { + Command cmd = PrivilegeKeyCommands.privilegeKeyDelete(token); + return executeAndReturnError(cmd); + } + + /** + * Deletes the virtual server with the specified ID. + *

+ * Only stopped virtual servers can be deleted. + *

+ * + * @param serverId + * the ID of the virtual server + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see VirtualServer#getId() + * @see #stopServer(int) + */ + public CommandFuture deleteServer(int serverId) { + Command cmd = VirtualServerCommands.serverDelete(serverId); + return executeAndReturnError(cmd); + } + + /** + * Deletes the server group with the specified ID, even if the server group still contains clients. + * + * @param groupId + * the ID of the server group + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + */ + public CommandFuture deleteServerGroup(int groupId) { + return deleteServerGroup(groupId, true); + } + + /** + * Deletes a server group with the specified ID. + *

+ * If {@code force} is true, the server group will be deleted even if it contains clients, + * else the command will fail in this situation. + *

+ * + * @param groupId + * the ID of the server group + * @param force + * whether the server group should be deleted if it still contains clients + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + */ + public CommandFuture deleteServerGroup(int groupId, boolean force) { + Command cmd = ServerGroupCommands.serverGroupDel(groupId, force); + return executeAndReturnError(cmd); + } + + /** + * Removes a permission from the server group with the given ID. + * + * @param groupId + * the ID of the server group + * @param permName + * the name of the permission to revoke + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + * @see Permission#getName() + */ + public CommandFuture deleteServerGroupPermission(int groupId, String permName) { + Command cmd = PermissionCommands.serverGroupDelPerm(groupId, permName); + return executeAndReturnError(cmd); + } + + /** + * Restores the selected virtual servers configuration using the data from a + * previously created server snapshot. + * + * @param snapshot + * the snapshot to restore + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #createServerSnapshot() + */ + public CommandFuture deployServerSnapshot(Snapshot snapshot) { + return deployServerSnapshot(snapshot.get()); + } + + /** + * Restores the configuration of the selected virtual server using the data from a + * previously created server snapshot. + * + * @param snapshot + * the snapshot to restore + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #createServerSnapshot() + */ + public CommandFuture deployServerSnapshot(String snapshot) { + Command cmd = VirtualServerCommands.serverSnapshotDeploy(snapshot); + return executeAndReturnError(cmd); + } + + /** + * Downloads a file from the file repository at a given path and channel + * and writes the file's bytes to an open {@link OutputStream}. + *

+ * It is the user's responsibility to ensure that the given {@code OutputStream} is + * open and to close the stream again once the download has finished. + *

+ * Note that this method will not read the entire file to memory and can thus + * download arbitrarily sized files from the file repository. + *

+ * + * @param dataOut + * a stream that the downloaded data should be written to + * @param filePath + * the path of the file on the file repository + * @param channelId + * the ID of the channel to download the file from + * + * @return how many bytes were downloaded + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #downloadFileDirect(String, int) + */ + public CommandFuture downloadFile(OutputStream dataOut, String filePath, int channelId) { + return downloadFile(dataOut, filePath, channelId, null); + } + + /** + * Downloads a file from the file repository at a given path and channel + * and writes the file's bytes to an open {@link OutputStream}. + *

+ * It is the user's responsibility to ensure that the given {@code OutputStream} is + * open and to close the stream again once the download has finished. + *

+ * Note that this method will not read the entire file to memory and can thus + * download arbitrarily sized files from the file repository. + *

+ * + * @param dataOut + * a stream that the downloaded data should be written to + * @param filePath + * the path of the file on the file repository + * @param channelId + * the ID of the channel to download the file from + * @param channelPassword + * that channel's password + * + * @return how many bytes were downloaded + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #downloadFileDirect(String, int, String) + */ + public CommandFuture downloadFile(OutputStream dataOut, String filePath, int channelId, String channelPassword) { + FileTransferHelper helper = query.getFileTransferHelper(); + int transferId = helper.getClientTransferId(); + Command cmd = FileCommands.ftInitDownload(transferId, filePath, channelId, channelPassword); + CommandFuture future = new CommandFuture<>(); + + executeAndTransformFirst(cmd, FileTransferParameters::new).onSuccess(params -> { + QueryError error = params.getQueryError(); + if (!error.isSuccessful()) { + future.fail(new TS3CommandFailedException(error, cmd.getName())); + return; + } + + try { + query.getFileTransferHelper().downloadFile(dataOut, params); + } catch (IOException e) { + future.fail(new TS3FileTransferFailedException("Download failed", e)); + return; + } + future.set(params.getFileSize()); + }).forwardFailure(future); + + return future; + } + + /** + * Downloads a file from the file repository at a given path and channel + * and returns the file's bytes as a byte array. + *

+ * Note that this method will read the entire file to memory. + * That means that if a file is larger than 231-1 bytes in size, + * the download will fail. + *

+ * + * @param filePath + * the path of the file on the file repository + * @param channelId + * the ID of the channel to download the file from + * + * @return a byte array containing the file's data + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #downloadFile(OutputStream, String, int) + */ + public CommandFuture downloadFileDirect(String filePath, int channelId) { + return downloadFileDirect(filePath, channelId, null); + } + + /** + * Downloads a file from the file repository at a given path and channel + * and returns the file's bytes as a byte array. + *

+ * Note that this method will read the entire file to memory. + * That means that if a file is larger than 231-1 bytes in size, + * the download will fail. + *

+ * + * @param filePath + * the path of the file on the file repository + * @param channelId + * the ID of the channel to download the file from + * @param channelPassword + * that channel's password + * + * @return a byte array containing the file's data + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #downloadFile(OutputStream, String, int, String) + */ + public CommandFuture downloadFileDirect(String filePath, int channelId, String channelPassword) { + FileTransferHelper helper = query.getFileTransferHelper(); + int transferId = helper.getClientTransferId(); + Command cmd = FileCommands.ftInitDownload(transferId, filePath, channelId, channelPassword); + CommandFuture future = new CommandFuture<>(); + + executeAndTransformFirst(cmd, FileTransferParameters::new).onSuccess(params -> { + QueryError error = params.getQueryError(); + if (!error.isSuccessful()) { + future.fail(new TS3CommandFailedException(error, cmd.getName())); + return; + } + + long fileSize = params.getFileSize(); + if (fileSize > Integer.MAX_VALUE) { + future.fail(new TS3FileTransferFailedException("File too big for byte array")); + return; + } + ByteArrayOutputStream dataOut = new ByteArrayOutputStream((int) fileSize); + + try { + query.getFileTransferHelper().downloadFile(dataOut, params); + } catch (IOException e) { + future.fail(new TS3FileTransferFailedException("Download failed", e)); + return; + } + future.set(dataOut.toByteArray()); + }).forwardFailure(future); + + return future; + } + + /** + * Downloads an icon from the icon directory in the file repository + * and writes the file's bytes to an open {@link OutputStream}. + *

+ * It is the user's responsibility to ensure that the given {@code OutputStream} is + * open and to close the stream again once the download has finished. + *

+ * + * @param dataOut + * a stream that the downloaded data should be written to + * @param iconId + * the ID of the icon that should be downloaded + * + * @return a byte array containing the icon file's data + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see IconFile#getIconId() + * @see #downloadIconDirect(long) + * @see #uploadIcon(InputStream, long) + */ + public CommandFuture downloadIcon(OutputStream dataOut, long iconId) { + String iconPath = "/icon_" + iconId; + return downloadFile(dataOut, iconPath, 0); + } + + /** + * Downloads an icon from the icon directory in the file repository + * and returns the file's bytes as a byte array. + *

+ * Note that this method will read the entire file to memory. + *

+ * + * @param iconId + * the ID of the icon that should be downloaded + * + * @return a byte array containing the icon file's data + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see IconFile#getIconId() + * @see #downloadIcon(OutputStream, long) + * @see #uploadIconDirect(byte[]) + */ + public CommandFuture downloadIconDirect(long iconId) { + String iconPath = "/icon_" + iconId; + return downloadFileDirect(iconPath, 0); + } + + /** + * Changes a channel's configuration using the given properties. + * + * @param channelId + * the ID of the channel to edit + * @param options + * the map of properties to modify + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + */ + public CommandFuture editChannel(int channelId, Map options) { + Command cmd = ChannelCommands.channelEdit(channelId, options); + return executeAndReturnError(cmd); + } + + /** + * Changes a single property of the given channel. + *

+ * Note that one can set many properties at once with the overloaded method that + * takes a map of channel properties and strings. + *

+ * + * @param channelId + * the ID of the channel to edit + * @param property + * the channel property to modify, make sure it is editable + * @param value + * the new value of the property + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see #editChannel(int, Map) + */ + public CommandFuture editChannel(int channelId, ChannelProperty property, String value) { + return editChannel(channelId, Collections.singletonMap(property, value)); + } + + /** + * Changes a client's configuration using given properties. + *

+ * Only {@link ClientProperty#CLIENT_DESCRIPTION} can be changed for other clients. + * To update the current client's properties, use {@link #updateClient(Map)} + * or {@link #updateClient(ClientProperty, String)}. + *

+ * + * @param clientId + * the ID of the client to edit + * @param options + * the map of properties to modify + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see #updateClient(Map) + */ + public CommandFuture editClient(int clientId, Map options) { + Command cmd = ClientCommands.clientEdit(clientId, options); + return executeAndReturnError(cmd); + } + + /** + * Changes a single property of the given client. + *

+ * Only {@link ClientProperty#CLIENT_DESCRIPTION} can be changed for other clients. + * To update the current client's properties, use {@link #updateClient(Map)} + * or {@link #updateClient(ClientProperty, String)}. + *

+ * + * @param clientId + * the ID of the client to edit + * @param property + * the client property to modify, make sure it is editable + * @param value + * the new value of the property + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see #editClient(int, Map) + * @see #updateClient(Map) + */ + public CommandFuture editClient(int clientId, ClientProperty property, String value) { + return editClient(clientId, Collections.singletonMap(property, value)); + } + + /** + * Changes a client's database settings using given properties. + * + * @param clientDBId + * the database ID of the client to edit + * @param options + * the map of properties to modify + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see DatabaseClientInfo + * @see Client#getDatabaseId() + */ + public CommandFuture editDatabaseClient(int clientDBId, Map options) { + Command cmd = DatabaseClientCommands.clientDBEdit(clientDBId, options); + return executeAndReturnError(cmd); + } + + /** + * Changes the server instance configuration using given properties. + * If the given property is not changeable, {@code IllegalArgumentException} will be thrown. + * + * @param property + * the property to edit, must be changeable + * @param value + * the new value for the edit + * + * @return a future to track the progress of this command + * + * @throws IllegalArgumentException + * if {@code property} is not changeable + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerInstanceProperty#isChangeable() + */ + public CommandFuture editInstance(ServerInstanceProperty property, String value) { + Command cmd = ServerCommands.instanceEdit(Collections.singletonMap(property, value)); + return executeAndReturnError(cmd); + } + + /** + * Changes the configuration of the selected virtual server using given properties. + * + * @param options + * the map of properties to edit + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see VirtualServerProperty + */ + public CommandFuture editServer(Map options) { + Command cmd = VirtualServerCommands.serverEdit(options); + return executeAndReturnError(cmd); + } + + /** + * Gets a list of all bans on the selected virtual server. + * + * @return a list of all bans on the virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Ban + */ + public CommandFuture> getBans() { + Command cmd = BanCommands.banList(); + return executeAndTransform(cmd, Ban::new); + } + + /** + * Gets a list of IP addresses used by the server instance. + * + * @return the list of bound IP addresses + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Binding + */ + public CommandFuture> getBindings() { + Command cmd = ServerCommands.bindingList(); + return executeAndTransform(cmd, Binding::new); + } + + /** + * Finds and returns the channel matching the given name exactly. + * + * @param name + * the name of the channel + * @param ignoreCase + * whether the case of the name should be ignored + * + * @return the found channel or {@code null} if no channel was found + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel + * @see #getChannelsByName(String) + */ + public CommandFuture getChannelByNameExact(String name, boolean ignoreCase) { + String caseName = ignoreCase ? name.toLowerCase(Locale.ROOT) : name; + + return getChannels().map(allChannels -> { + for (Channel c : allChannels) { + String channelName = ignoreCase ? c.getName().toLowerCase(Locale.ROOT) : c.getName(); + if (caseName.equals(channelName)) return c; + } + return null; // Not found + }); + } + + /** + * Gets a list of channels whose names contain the given search string. + * + * @param name + * the name to search + * + * @return a list of all channels with names matching the search pattern + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 2 + * @see Channel + * @see #getChannelByNameExact(String, boolean) + */ + public CommandFuture> getChannelsByName(String name) { + Command cmd = ChannelCommands.channelFind(name); + CommandFuture> future = new CommandFuture<>(); + + CommandFuture> channelIds = executeAndMap(cmd, response -> response.getInt("cid")); + CommandFuture> allChannels = getChannels(); + + findByKey(channelIds, allChannels, Channel::getId) + .forwardSuccess(future) + .onFailure(transformError(future, 768, Collections.emptyList())); + + return future; + } + + /** + * Displays a list of permissions defined for a client in a specific channel. + * + * @param channelId + * the ID of the channel + * @param clientDBId + * the database ID of the client + * + * @return a list of permissions for the user in the specified channel + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see Client#getDatabaseId() + * @see Permission + */ + public CommandFuture> getChannelClientPermissions(int channelId, int clientDBId) { + Command cmd = PermissionCommands.channelClientPermList(channelId, clientDBId); + return executeAndTransform(cmd, Permission::new); + } + + /** + * Gets all client / channel ID combinations currently assigned to channel groups. + * All three parameters are optional and can be turned off by setting it to {@code -1}. + * + * @param channelId + * restricts the search to the channel with a specified ID. Set to {@code -1} to ignore. + * @param clientDBId + * restricts the search to the client with a specified database ID. Set to {@code -1} to ignore. + * @param groupId + * restricts the search to the channel group with the specified ID. Set to {@code -1} to ignore. + * + * @return a list of combinations of channel ID, client database ID and channel group ID + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see Client#getDatabaseId() + * @see ChannelGroup#getId() + * @see ChannelGroupClient + */ + public CommandFuture> getChannelGroupClients(int channelId, int clientDBId, int groupId) { + Command cmd = ChannelGroupCommands.channelGroupClientList(channelId, clientDBId, groupId); + return executeAndTransform(cmd, ChannelGroupClient::new); + } + + /** + * Gets all client / channel ID combinations currently assigned to the specified channel group. + * + * @param groupId + * the ID of the channel group whose client / channel assignments should be returned. + * + * @return a list of combinations of channel ID, client database ID and channel group ID + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + * @see ChannelGroupClient + * @see #getChannelGroupClients(int, int, int) + */ + public CommandFuture> getChannelGroupClientsByChannelGroupId(int groupId) { + return getChannelGroupClients(-1, -1, groupId); + } + + /** + * Gets all channel group assignments in the specified channel. + * + * @param channelId + * the ID of the channel whose channel group assignments should be returned. + * + * @return a list of combinations of channel ID, client database ID and channel group ID + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see ChannelGroupClient + * @see #getChannelGroupClients(int, int, int) + */ + public CommandFuture> getChannelGroupClientsByChannelId(int channelId) { + return getChannelGroupClients(channelId, -1, -1); + } + + /** + * Gets all channel group assignments for the specified client. + * + * @param clientDBId + * the database ID of the client whose channel group + * + * @return a list of combinations of channel ID, client database ID and channel group ID + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see ChannelGroupClient + * @see #getChannelGroupClients(int, int, int) + */ + public CommandFuture> getChannelGroupClientsByClientDBId(int clientDBId) { + return getChannelGroupClients(-1, clientDBId, -1); + } + + /** + * Gets a list of all permissions assigned to the specified channel group. + * + * @param groupId + * the ID of the channel group. + * + * @return a list of permissions assigned to the channel group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + * @see Permission + */ + public CommandFuture> getChannelGroupPermissions(int groupId) { + Command cmd = PermissionCommands.channelGroupPermList(groupId); + return executeAndTransform(cmd, Permission::new); + } + + /** + * Gets a list of all channel groups on the selected virtual server. + * + * @return a list of all channel groups on the virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup + */ + public CommandFuture> getChannelGroups() { + Command cmd = ChannelGroupCommands.channelGroupList(); + return executeAndTransform(cmd, ChannelGroup::new); + } + + /** + * Gets detailed configuration information about the channel specified channel. + * + * @param channelId + * the ID of the channel + * + * @return information about the channel + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see ChannelInfo + */ + public CommandFuture getChannelInfo(int channelId) { + Command cmd = ChannelCommands.channelInfo(channelId); + return executeAndTransformFirst(cmd, map -> new ChannelInfo(channelId, map)); + } + + /** + * Gets a list of all permissions assigned to the specified channel. + * + * @param channelId + * the ID of the channel + * + * @return a list of all permissions assigned to the channel + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see Permission + */ + public CommandFuture> getChannelPermissions(int channelId) { + Command cmd = PermissionCommands.channelPermList(channelId); + return executeAndTransform(cmd, Permission::new); + } + + /** + * Gets a list of all channels on the selected virtual server. + * + * @return a list of all channels on the virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel + */ + public CommandFuture> getChannels() { + Command cmd = ChannelCommands.channelList(); + return executeAndTransform(cmd, Channel::new); + } + + /** + * Finds and returns the client whose nickname matches the given name exactly. + * + * @param name + * the name of the client + * @param ignoreCase + * whether the case of the name should be ignored + * + * @return the found client or {@code null} if no client was found + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client + * @see #getClientsByName(String) + */ + public CommandFuture getClientByNameExact(String name, boolean ignoreCase) { + String caseName = ignoreCase ? name.toLowerCase(Locale.ROOT) : name; + + return getClients().map(allClients -> { + for (Client c : allClients) { + String clientName = ignoreCase ? c.getNickname().toLowerCase(Locale.ROOT) : c.getNickname(); + if (caseName.equals(clientName)) return c; + } + return null; // Not found + }); + } + + /** + * Gets a list of clients whose nicknames contain the given search string. + * + * @param name + * the name to search + * + * @return a list of all clients with nicknames matching the search pattern + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 2 + * @see Client + * @see #getClientByNameExact(String, boolean) + */ + public CommandFuture> getClientsByName(String name) { + Command cmd = ClientCommands.clientFind(name); + CommandFuture> future = new CommandFuture<>(); + + CommandFuture> clientIds = executeAndMap(cmd, response -> response.getInt("clid")); + CommandFuture> allClients = getClients(); + + findByKey(clientIds, allClients, Client::getId) + .forwardSuccess(future) + .onFailure(transformError(future, 512, Collections.emptyList())); + + return future; + } + + /** + * Gets information about the client with the specified unique identifier. + * + * @param clientUId + * the unique identifier of the client + * + * @return information about the client + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 2 + * @see Client#getUniqueIdentifier() + * @see ClientInfo + */ + public CommandFuture getClientByUId(String clientUId) { + Command cmd = ClientCommands.clientGetIds(clientUId); + return executeAndReturnIntProperty(cmd, "clid") + .then(this::getClientInfo); + } + + /** + * Gets information about the client with the specified client ID. + * + * @param clientId + * the client ID of the client + * + * @return information about the client + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see ClientInfo + */ + public CommandFuture getClientInfo(int clientId) { + Command cmd = ClientCommands.clientInfo(clientId); + return executeAndTransformFirst(cmd, map -> new ClientInfo(clientId, map)); + } + + /** + * Gets a list of all permissions assigned to the specified client. + * + * @param clientDBId + * the database ID of the client + * + * @return a list of all permissions assigned to the client + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see Permission + */ + public CommandFuture> getClientPermissions(int clientDBId) { + Command cmd = PermissionCommands.clientPermList(clientDBId); + return executeAndTransform(cmd, Permission::new); + } + + /** + * Gets a list of all clients on the selected virtual server. + * + * @return a list of all clients on the virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client + */ + public CommandFuture> getClients() { + Command cmd = ClientCommands.clientList(); + return executeAndTransform(cmd, Client::new); + } + + /** + * Gets a list of all complaints on the selected virtual server. + * + * @return a list of all complaints on the virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Complaint + * @see #getComplaints(int) + */ + public CommandFuture> getComplaints() { + return getComplaints(-1); + } + + /** + * Gets a list of all complaints about the specified client. + * + * @param clientDBId + * the database ID of the client + * + * @return a list of all complaints about the specified client + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see Complaint + */ + public CommandFuture> getComplaints(int clientDBId) { + Command cmd = ComplaintCommands.complainList(clientDBId); + return executeAndTransform(cmd, Complaint::new); + } + + /** + * Gets detailed connection information about the selected virtual server. + * + * @return connection information about the selected virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ConnectionInfo + * @see #getServerInfo() + */ + public CommandFuture getConnectionInfo() { + Command cmd = VirtualServerCommands.serverRequestConnectionInfo(); + return executeAndTransformFirst(cmd, ConnectionInfo::new); + } + + /** + * Gets a map of all custom client properties and their values + * assigned to the client with database ID {@code clientDBId}. + * + * @param clientDBId + * the database ID of the target client + * + * @return a map of the client's custom client property assignments + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see #searchCustomClientProperty(String) + * @see #searchCustomClientProperty(String, String) + */ + public CommandFuture> getCustomClientProperties(int clientDBId) { + Command cmd = CustomPropertyCommands.customInfo(clientDBId); + CommandFuture> future = cmd.getFuture() + .map(result -> { + List response = result.getResponses(); + Map properties = new HashMap<>(response.size()); + for (Wrapper wrapper : response) { + properties.put(wrapper.get("ident"), wrapper.get("value")); + } + + return properties; + }); + + query.doCommandAsync(cmd); + return future; + } + + /** + * Gets all clients in the database whose last nickname matches the specified name exactly. + * + * @param name + * the nickname for the clients to match + * + * @return a list of all clients with a matching nickname + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + n, + * where n is the amount of database clients with a matching nickname + * @see Client#getNickname() + */ + public CommandFuture> getDatabaseClientsByName(String name) { + Command cmd = DatabaseClientCommands.clientDBFind(name, false); + + return executeAndMap(cmd, response -> response.getInt("cldbid")) + .then(dbClientIds -> { + Collection> infoFutures = new ArrayList<>(dbClientIds.size()); + for (int dbClientId : dbClientIds) { + infoFutures.add(getDatabaseClientInfo(dbClientId)); + } + return CommandFuture.ofAll(infoFutures); + }); + } + + /** + * Gets information about the client with the specified unique identifier in the server database. + * + * @param clientUId + * the unique identifier of the client + * + * @return the database client or {@code null} if no client was found + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 2 + * @see Client#getUniqueIdentifier() + * @see DatabaseClientInfo + */ + public CommandFuture getDatabaseClientByUId(String clientUId) { + Command cmd = DatabaseClientCommands.clientDBFind(clientUId, true); + CommandFuture future = cmd.getFuture() + .then(result -> { + if (result.getResponses().isEmpty()) { + return null; + } else { + int databaseId = result.getFirstResponse().getInt("cldbid"); + return getDatabaseClientInfo(databaseId); + } + }); + + query.doCommandAsync(cmd); + return future; + } + + /** + * Gets information about the client with the specified database ID in the server database. + * + * @param clientDBId + * the database ID of the client + * + * @return the database client or {@code null} if no client was found + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see DatabaseClientInfo + */ + public CommandFuture getDatabaseClientInfo(int clientDBId) { + Command cmd = DatabaseClientCommands.clientDBInfo(clientDBId); + return executeAndTransformFirst(cmd, DatabaseClientInfo::new); + } + + /** + * Gets information about all clients in the server database. + *

+ * As this method uses internal commands which can only return 200 clients at once, + * this method can take quite some time to execute. + *

+ * Also keep in mind that the client database can easily accumulate several thousand entries. + *

+ * + * @return a {@link List} of all database clients + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + n, + * where n = Math.ceil([amount of database clients] / 200) + * @see DatabaseClient + */ + public CommandFuture> getDatabaseClients() { + Command cmd = DatabaseClientCommands.clientDBList(0, 1, true); + + return executeAndReturnIntProperty(cmd, "count") + .then(count -> { + Collection>> futures = new ArrayList<>((count + 199) / 200); + for (int i = 0; i < count; i += 200) { + futures.add(getDatabaseClients(i, 200)); + } + return CommandFuture.ofAll(futures); + }).map(listOfLists -> listOfLists.stream() + .flatMap(List::stream) + .collect(Collectors.toList())); + } + + /** + * Gets information about a set number of clients in the server database, starting at {@code offset}. + * + * @param offset + * the index of the first database client to be returned. + * Note that this is not a database ID, but an arbitrary, 0-based index. + * @param count + * the number of database clients that should be returned. + * Any integer greater than 200 might cause problems with the connection + * + * @return a {@link List} of database clients + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see DatabaseClient + */ + public CommandFuture> getDatabaseClients(int offset, int count) { + Command cmd = DatabaseClientCommands.clientDBList(offset, count, false); + return executeAndTransform(cmd, DatabaseClient::new); + } + + /** + * Gets information about a file on the file repository in the specified channel. + *

+ * Note that this method does not work on directories and the information returned by this + * method is identical to the one returned by {@link #getFileList(String, int, String)} + *

+ * + * @param filePath + * the path to the file + * @param channelId + * the ID of the channel the file resides in + * + * @return some information about the file + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public CommandFuture getFileInfo(String filePath, int channelId) { + return getFileInfo(filePath, channelId, null); + } + + /** + * Gets information about a file on the file repository in the specified channel. + *

+ * Note that this method does not work on directories and the information returned by this + * method is identical to the one returned by {@link #getFileList(String, int, String)} + *

+ * + * @param filePath + * the path to the file + * @param channelId + * the ID of the channel the file resides in + * @param channelPassword + * the password of that channel + * + * @return some information about the file + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public CommandFuture getFileInfo(String filePath, int channelId, String channelPassword) { + Command cmd = FileCommands.ftGetFileInfo(channelId, channelPassword, filePath); + return executeAndTransformFirst(cmd, FileInfo::new); + } + + /** + * Gets information about multiple files on the file repository in the specified channel. + *

+ * Note that this method does not work on directories and the information returned by this + * method is identical to the one returned by {@link #getFileList(String, int, String)} + *

+ * + * @param filePaths + * the paths to the files + * @param channelId + * the ID of the channel the file resides in + * + * @return some information about the file + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public CommandFuture> getFileInfos(String[] filePaths, int channelId) { + return getFileInfos(filePaths, channelId, null); + } + + /** + * Gets information about multiple files on the file repository in the specified channel. + *

+ * Note that this method does not work on directories and the information returned by this + * method is identical to the one returned by {@link #getFileList(String, int, String)} + *

+ * + * @param filePaths + * the paths to the files + * @param channelId + * the ID of the channel the file resides in + * @param channelPassword + * the password of that channel + * + * @return some information about the file + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public CommandFuture> getFileInfos(String[] filePaths, int channelId, String channelPassword) { + Command cmd = FileCommands.ftGetFileInfo(channelId, channelPassword, filePaths); + return executeAndTransform(cmd, FileInfo::new); + } + + /** + * Gets information about multiple files on the file repository in multiple channels. + *

+ * Note that this method does not work on directories and the information returned by this + * method is identical to the one returned by {@link #getFileList(String, int, String)} + *

+ * + * @param filePaths + * the paths to the files, may not be {@code null} and may not contain {@code null} elements + * @param channelIds + * the IDs of the channels the file resides in, may not be {@code null} + * @param channelPasswords + * the passwords of those channels, may be {@code null} and may contain {@code null} elements + * + * @return some information about the files + * + * @throws IllegalArgumentException + * if the dimensions of {@code filePaths}, {@code channelIds} and {@code channelPasswords} don't match + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public CommandFuture> getFileInfos(String[] filePaths, int[] channelIds, String[] channelPasswords) { + Command cmd = FileCommands.ftGetFileInfo(channelIds, channelPasswords, filePaths); + return executeAndTransform(cmd, FileInfo::new); + } + + /** + * Gets a list of files and directories in the specified parent directory and channel. + * + * @param directoryPath + * the path to the parent directory + * @param channelId + * the ID of the channel the directory resides in + * + * @return the files and directories in the parent directory + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public CommandFuture> getFileList(String directoryPath, int channelId) { + return getFileList(directoryPath, channelId, null); + } + + /** + * Gets a list of files and directories in the specified parent directory and channel. + * + * @param directoryPath + * the path to the parent directory + * @param channelId + * the ID of the channel the directory resides in + * @param channelPassword + * the password of that channel + * + * @return the files and directories in the parent directory + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + */ + public CommandFuture> getFileList(String directoryPath, int channelId, String channelPassword) { + Command cmd = FileCommands.ftGetFileList(directoryPath, channelId, channelPassword); + return executeAndTransform(cmd, FileListEntry::new); + } + + /** + * Gets a list of active or recently active file transfers. + * + * @return a list of file transfers + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture> getFileTransfers() { + Command cmd = FileCommands.ftList(); + return executeAndTransform(cmd, FileTransfer::new); + } + + /** + * Displays detailed configuration information about the server instance including + * uptime, number of virtual servers online, traffic information, etc. + * + * @return information about the host + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture getHostInfo() { + Command cmd = ServerCommands.hostInfo(); + return executeAndTransformFirst(cmd, HostInfo::new); + } + + /** + * Gets a list of all icon files on this virtual server. + * + * @return a list of all icons + */ + public CommandFuture> getIconList() { + return getFileList("/icons/", 0) + .map(result -> { + List icons = new ArrayList<>(result.size()); + for (FileListEntry file : result) { + if (file.isDirectory() || file.isStillUploading()) continue; + icons.add(new IconFile(file.getMap())); + } + return icons; + }); + } + + /** + * Displays the server instance configuration including database revision number, + * the file transfer port, default group IDs, etc. + * + * @return information about the TeamSpeak server instance. + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture getInstanceInfo() { + Command cmd = ServerCommands.instanceInfo(); + return executeAndTransformFirst(cmd, InstanceInfo::new); + } + + /** + * Fetches the specified amount of log entries from the server log. + * + * @param lines + * the amount of log entries to fetch, in the range between 1 and 100. + * Returns 100 entries if the argument is not in range + * + * @return a list of the latest log entries + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture> getInstanceLogEntries(int lines) { + Command cmd = ServerCommands.logView(lines, true); + return executeAndMap(cmd, response -> response.get("l")); + } + + /** + * Fetches the last 100 log entries from the server log. + * + * @return a list of up to 100 log entries + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture> getInstanceLogEntries() { + return getInstanceLogEntries(100); + } + + /** + * Reads the message body of a message. This will not set the read flag, though. + * + * @param messageId + * the ID of the message to be read + * + * @return the body of the message with the specified ID or {@code null} if there was no message with that ID + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Message#getId() + * @see #setMessageRead(int) + */ + public CommandFuture getOfflineMessage(int messageId) { + Command cmd = MessageCommands.messageGet(messageId); + return executeAndReturnStringProperty(cmd, "message"); + } + + /** + * Reads the message body of a message. This will not set the read flag, though. + * + * @param message + * the message to be read + * + * @return the body of the message with the specified ID or {@code null} if there was no message with that ID + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Message#getId() + * @see #setMessageRead(Message) + */ + public CommandFuture getOfflineMessage(Message message) { + return getOfflineMessage(message.getId()); + } + + /** + * Gets a list of all offline messages for the server query. + * The returned messages lack their message body, though. + * To read the actual message, use {@link #getOfflineMessage(int)} or {@link #getOfflineMessage(Message)}. + * + * @return a list of all offline messages this server query has received + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture> getOfflineMessages() { + Command cmd = MessageCommands.messageList(); + return executeAndTransform(cmd, Message::new); + } + + /** + * Displays detailed information about all assignments of the permission specified + * with {@code permName}. The output includes the type and the ID of the client, + * channel or group associated with the permission. + * + * @param permName + * the name of the permission + * + * @return a list of permission assignments + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #getPermissionOverview(int, int) + */ + public CommandFuture> getPermissionAssignments(String permName) { + Command cmd = PermissionCommands.permFind(permName); + CommandFuture> future = new CommandFuture<>(); + + executeAndTransform(cmd, PermissionAssignment::new) + .forwardSuccess(future) + .onFailure(transformError(future, 2562, Collections.emptyList())); + + return future; + } + + /** + * Gets the ID of the permission specified by {@code permName}. + *

+ * Note that the use of numeric permission IDs is deprecated + * and that this API only uses the string variant of the IDs. + *

+ * + * @param permName + * the name of the permission + * + * @return the numeric ID of the specified permission + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture getPermissionIdByName(String permName) { + Command cmd = PermissionCommands.permIdGetByName(permName); + return executeAndReturnIntProperty(cmd, "permid"); + } + + /** + * Gets the IDs of the permissions specified by {@code permNames}. + *

+ * Note that the use of numeric permission IDs is deprecated + * and that this API only uses the string variant of the IDs. + *

+ * + * @param permNames + * the names of the permissions + * + * @return the numeric IDs of the specified permission + * + * @throws IllegalArgumentException + * if {@code permNames} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture getPermissionIdsByName(String... permNames) { + Command cmd = PermissionCommands.permIdGetByName(permNames); + return executeAndReturnIntArray(cmd, "permid"); + } + + /** + * Gets a list of all assigned permissions for a client in a specified channel. + * If you do not care about channel permissions, set {@code channelId} to {@code 0}. + * + * @param channelId + * the ID of the channel + * @param clientDBId + * the database ID of the client to create the overview for + * + * @return a list of all permission assignments for the client in the specified channel + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see Client#getDatabaseId() + */ + public CommandFuture> getPermissionOverview(int channelId, int clientDBId) { + Command cmd = PermissionCommands.permOverview(channelId, clientDBId); + return executeAndTransform(cmd, PermissionAssignment::new); + } + + /** + * Displays a list of all permissions, including ID, name and description. + * + * @return a list of all permissions + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture> getPermissions() { + Command cmd = PermissionCommands.permissionList(); + return executeAndTransform(cmd, PermissionInfo::new); + } + + /** + * Displays the current value of the specified permission for this server query instance. + * + * @param permName + * the name of the permission + * + * @return the permission value, usually ranging from 0 to 100 + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture getPermissionValue(String permName) { + Command cmd = PermissionCommands.permGet(permName); + return executeAndReturnIntProperty(cmd, "permvalue"); + } + + /** + * Displays the current values of the specified permissions for this server query instance. + * + * @param permNames + * the names of the permissions + * + * @return the permission values, usually ranging from 0 to 100 + * + * @throws IllegalArgumentException + * if {@code permNames} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture getPermissionValues(String... permNames) { + Command cmd = PermissionCommands.permGet(permNames); + return executeAndReturnIntArray(cmd, "permvalue"); + } + + /** + * Gets a list of all available tokens to join channel or server groups, + * including their type and group IDs. + * + * @return a list of all generated, but still unclaimed privilege keys + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #addPrivilegeKey(PrivilegeKeyType, int, int, String) + * @see #usePrivilegeKey(String) + */ + public CommandFuture> getPrivilegeKeys() { + Command cmd = PrivilegeKeyCommands.privilegeKeyList(); + return executeAndTransform(cmd, PrivilegeKey::new); + } + + /** + * Gets a list of all clients in the specified server group. + * + * @param serverGroupId + * the ID of the server group for which the clients should be looked up + * + * @return a list of all clients in the server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture> getServerGroupClients(int serverGroupId) { + Command cmd = ServerGroupCommands.serverGroupClientList(serverGroupId); + return executeAndTransform(cmd, ServerGroupClient::new); + } + + /** + * Gets a list of all clients in the specified server group. + * + * @param serverGroup + * the server group for which the clients should be looked up + * + * @return a list of all clients in the server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture> getServerGroupClients(ServerGroup serverGroup) { + return getServerGroupClients(serverGroup.getId()); + } + + /** + * Gets a list of all permissions assigned to the specified server group. + * + * @param serverGroupId + * the ID of the server group for which the permissions should be looked up + * + * @return a list of all permissions assigned to the server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + * @see #getServerGroupPermissions(ServerGroup) + */ + public CommandFuture> getServerGroupPermissions(int serverGroupId) { + Command cmd = PermissionCommands.serverGroupPermList(serverGroupId); + return executeAndTransform(cmd, Permission::new); + } + + /** + * Gets a list of all permissions assigned to the specified server group. + * + * @param serverGroup + * the server group for which the permissions should be looked up + * + * @return a list of all permissions assigned to the server group + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture> getServerGroupPermissions(ServerGroup serverGroup) { + return getServerGroupPermissions(serverGroup.getId()); + } + + /** + * Gets a list of all server groups on the virtual server. + *

+ * Depending on your permissions, the output may also contain + * global server query groups and template groups. + *

+ * + * @return a list of all server groups + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture> getServerGroups() { + Command cmd = ServerGroupCommands.serverGroupList(); + return executeAndTransform(cmd, ServerGroup::new); + } + + /** + * Gets a list of all server groups set for a client. + * + * @param clientDatabaseId + * the database ID of the client for which the server groups should be looked up + * + * @return a list of all server groups set for the client + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 2 + * @see Client#getDatabaseId() + * @see #getServerGroupsByClient(Client) + */ + public CommandFuture> getServerGroupsByClientId(int clientDatabaseId) { + Command cmd = ServerGroupCommands.serverGroupsByClientId(clientDatabaseId); + + CommandFuture> serverGroupIds = executeAndMap(cmd, response -> response.getInt("sgid")); + CommandFuture> allServerGroups = getServerGroups(); + + return findByKey(serverGroupIds, allServerGroups, ServerGroup::getId); + } + + /** + * Gets a list of all server groups set for a client. + * + * @param client + * the client for which the server groups should be looked up + * + * @return a list of all server group set for the client + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 2 + * @see #getServerGroupsByClientId(int) + */ + public CommandFuture> getServerGroupsByClient(Client client) { + return getServerGroupsByClientId(client.getDatabaseId()); + } + + /** + * Gets the ID of a virtual server by its port. + * + * @param port + * the port of a virtual server + * + * @return the ID of the virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see VirtualServer#getPort() + * @see VirtualServer#getId() + */ + public CommandFuture getServerIdByPort(int port) { + Command cmd = VirtualServerCommands.serverIdGetByPort(port); + return executeAndReturnIntProperty(cmd, "server_id"); + } + + /** + * Gets detailed information about the virtual server the server query is currently in. + * + * @return information about the current virtual server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture getServerInfo() { + Command cmd = VirtualServerCommands.serverInfo(); + return executeAndTransformFirst(cmd, VirtualServerInfo::new); + } + + /** + * Gets the version, build number and platform of the TeamSpeak3 server. + * + * @return the version information of the server + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture getVersion() { + Command cmd = ServerCommands.version(); + return executeAndTransformFirst(cmd, Version::new); + } + + /** + * Gets a list of all virtual servers including their ID, status, number of clients online, etc. + * + * @return a list of all virtual servers + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture> getVirtualServers() { + Command cmd = VirtualServerCommands.serverList(); + return executeAndTransform(cmd, VirtualServer::new); + } + + /** + * Fetches the specified amount of log entries from the currently selected virtual server. + * If no virtual server is selected, the entries will be read from the server log instead. + * + * @param lines + * the amount of log entries to fetch, in the range between 1 and 100. + * Returns 100 entries if the argument is not in range + * + * @return a list of the latest log entries + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture> getVirtualServerLogEntries(int lines) { + Command cmd = ServerCommands.logView(lines, false); + return executeAndMap(cmd, response -> response.get("l")); + } + + /** + * Fetches the last 100 log entries from the currently selected virtual server. + * If no virtual server is selected, the entries will be read from the server log instead. + * + * @return a list of up to 100 log entries + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture> getVirtualServerLogEntries() { + return getVirtualServerLogEntries(100); + } + + /** + * Checks whether the client with the specified client ID is online. + *

+ * Please note that there is no guarantee that the client will still be + * online by the time the next command is executed. + *

+ * + * @param clientId + * the ID of the client + * + * @return {@code true} if the client is online, {@code false} otherwise + * + * @querycommands 1 + * @see #getClientInfo(int) + */ + public CommandFuture isClientOnline(int clientId) { + Command cmd = ClientCommands.clientInfo(clientId); + CommandFuture future = new CommandFuture<>(); + + cmd.getFuture() + .onSuccess(__ -> future.set(true)) + .onFailure(transformError(future, 512, false)); + + query.doCommandAsync(cmd); + return future; + } + + /** + * Checks whether the client with the specified unique identifier is online. + *

+ * Please note that there is no guarantee that the client will still be + * online by the time the next command is executed. + *

+ * + * @param clientUId + * the unique ID of the client + * + * @return {@code true} if the client is online, {@code false} otherwise + * + * @querycommands 1 + * @see #getClientByUId(String) + */ + public CommandFuture isClientOnline(String clientUId) { + Command cmd = ClientCommands.clientGetIds(clientUId); + CommandFuture future = cmd.getFuture() + .map(result -> !result.getResponses().isEmpty()); + + query.doCommandAsync(cmd); + return future; + } + + /** + * Kicks one or more clients from their current channels. + * This will move the kicked clients into the default channel and + * won't do anything if the clients are already in the default channel. + * + * @param clientIds + * the IDs of the clients to kick + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #kickClientFromChannel(Client...) + * @see #kickClientFromChannel(String, int...) + */ + public CommandFuture kickClientFromChannel(int... clientIds) { + return kickClients(ReasonIdentifier.REASON_KICK_CHANNEL, null, clientIds); + } + + /** + * Kicks one or more clients from their current channels. + * This will move the kicked clients into the default channel and + * won't do anything if the clients are already in the default channel. + * + * @param clients + * the clients to kick + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #kickClientFromChannel(int...) + * @see #kickClientFromChannel(String, Client...) + */ + public CommandFuture kickClientFromChannel(Client... clients) { + return kickClients(ReasonIdentifier.REASON_KICK_CHANNEL, null, clients); + } + + /** + * Kicks one or more clients from their current channels for the specified reason. + * This will move the kicked clients into the default channel and + * won't do anything if the clients are already in the default channel. + * + * @param message + * the reason message to display to the clients + * @param clientIds + * the IDs of the clients to kick + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see #kickClientFromChannel(int...) + * @see #kickClientFromChannel(String, Client...) + */ + public CommandFuture kickClientFromChannel(String message, int... clientIds) { + return kickClients(ReasonIdentifier.REASON_KICK_CHANNEL, message, clientIds); + } + + /** + * Kicks one or more clients from their current channels for the specified reason. + * This will move the kicked clients into the default channel and + * won't do anything if the clients are already in the default channel. + * + * @param message + * the reason message to display to the clients + * @param clients + * the clients to kick + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #kickClientFromChannel(Client...) + * @see #kickClientFromChannel(String, int...) + */ + public CommandFuture kickClientFromChannel(String message, Client... clients) { + return kickClients(ReasonIdentifier.REASON_KICK_CHANNEL, message, clients); + } + + /** + * Kicks one or more clients from the server. + * + * @param clientIds + * the IDs of the clients to kick + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see #kickClientFromServer(Client...) + * @see #kickClientFromServer(String, int...) + */ + public CommandFuture kickClientFromServer(int... clientIds) { + return kickClients(ReasonIdentifier.REASON_KICK_SERVER, null, clientIds); + } + + /** + * Kicks one or more clients from the server. + * + * @param clients + * the clients to kick + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #kickClientFromServer(int...) + * @see #kickClientFromServer(String, Client...) + */ + public CommandFuture kickClientFromServer(Client... clients) { + return kickClients(ReasonIdentifier.REASON_KICK_SERVER, null, clients); + } + + /** + * Kicks one or more clients from the server for the specified reason. + * + * @param message + * the reason message to display to the clients + * @param clientIds + * the IDs of the clients to kick + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see #kickClientFromServer(int...) + * @see #kickClientFromServer(String, Client...) + */ + public CommandFuture kickClientFromServer(String message, int... clientIds) { + return kickClients(ReasonIdentifier.REASON_KICK_SERVER, message, clientIds); + } + + /** + * Kicks one or more clients from the server for the specified reason. + * + * @param message + * the reason message to display to the clients + * @param clients + * the clients to kick + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #kickClientFromServer(Client...) + * @see #kickClientFromServer(String, int...) + */ + public CommandFuture kickClientFromServer(String message, Client... clients) { + return kickClients(ReasonIdentifier.REASON_KICK_SERVER, message, clients); + } + + /** + * Kicks a list of clients from either the channel or the server for a given reason. + * + * @param reason + * where to kick the clients from + * @param message + * the reason message to display to the clients + * @param clients + * the clients to kick + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + private CommandFuture kickClients(ReasonIdentifier reason, String message, Client... clients) { + int[] clientIds = new int[clients.length]; + for (int i = 0; i < clients.length; ++i) { + clientIds[i] = clients[i].getId(); + } + return kickClients(reason, message, clientIds); + } + + /** + * Kicks a list of clients from either the channel or the server for a given reason. + * + * @param reason + * where to kick the clients from + * @param message + * the reason message to display to the clients + * @param clientIds + * the IDs of the clients to kick + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + */ + private CommandFuture kickClients(ReasonIdentifier reason, String message, int... clientIds) { + Command cmd = ClientCommands.clientKick(reason, message, clientIds); + return executeAndReturnError(cmd); + } + + /** + * Logs the server query in using the specified username and password. + *

+ * Note that you can also set the login in the {@link TS3Config}, + * so that you will be logged in right after the connection is established. + *

+ * + * @param username + * the username of the server query + * @param password + * the password to use + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #logout() + */ + public CommandFuture login(String username, String password) { + Command cmd = QueryCommands.logIn(username, password); + return executeAndReturnError(cmd); + } + + /** + * Logs the server query out and deselects the current virtual server. + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #login(String, String) + */ + public CommandFuture logout() { + Command cmd = QueryCommands.logOut(); + return executeAndReturnError(cmd); + } + + /** + * Moves a channel to a new parent channel specified by its ID. + * To move a channel to root level, set {@code channelTargetId} to {@code 0}. + *

+ * This will move the channel right below the specified parent channel, above all other child channels. + * This command will fail if the channel already has the specified target channel as the parent channel. + *

+ * + * @param channelId + * the channel to move + * @param channelTargetId + * the new parent channel for the specified channel + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see #moveChannel(int, int, int) + */ + public CommandFuture moveChannel(int channelId, int channelTargetId) { + return moveChannel(channelId, channelTargetId, 0); + } + + /** + * Moves a channel to a new parent channel specified by its ID. + * To move a channel to root level, set {@code channelTargetId} to {@code 0}. + *

+ * The channel will be ordered below the channel with the ID specified by {@code order}. + * To move the channel right below the parent channel, set {@code order} to {@code 0}. + *

+ * Note that you can't re-order a channel without also changing its parent channel with this method. + * Use {@link #editChannel(int, ChannelProperty, String)} to change {@link ChannelProperty#CHANNEL_ORDER} instead. + *

+ * + * @param channelId + * the channel to move + * @param channelTargetId + * the new parent channel for the specified channel + * @param order + * the channel to sort the specified channel below + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see #moveChannel(int, int) + */ + public CommandFuture moveChannel(int channelId, int channelTargetId, int order) { + Command cmd = ChannelCommands.channelMove(channelId, channelTargetId, order); + return executeAndReturnError(cmd); + } + + /** + * Moves a single client into a channel. + *

+ * Consider using {@link #moveClients(int[], int)} to move multiple clients. + *

+ * + * @param clientId + * the ID of the client to move + * @param channelId + * the ID of the channel to move the client into + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see Channel#getId() + */ + public CommandFuture moveClient(int clientId, int channelId) { + return moveClient(clientId, channelId, null); + } + + /** + * Moves multiple clients into a channel. + * Immediately returns {@code true} for an empty client ID array. + *

+ * Use this method instead of {@link #moveClient(int, int)} for moving + * several clients as this will only send 1 command to the server and thus complete faster. + *

+ * + * @param clientIds + * the IDs of the clients to move, cannot be {@code null} + * @param channelId + * the ID of the channel to move the clients into + * + * @return a future to track the progress of this command + * + * @throws IllegalArgumentException + * if {@code clientIds} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see Channel#getId() + */ + public CommandFuture moveClients(int[] clientIds, int channelId) { + return moveClients(clientIds, channelId, null); + } + + /** + * Moves a single client into a channel. + *

+ * Consider using {@link #moveClients(Client[], ChannelBase)} to move multiple clients. + *

+ * + * @param client + * the client to move, cannot be {@code null} + * @param channel + * the channel to move the client into, cannot be {@code null} + * + * @return a future to track the progress of this command + * + * @throws IllegalArgumentException + * if {@code client} or {@code channel} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture moveClient(Client client, ChannelBase channel) { + return moveClient(client, channel, null); + } + + /** + * Moves multiple clients into a channel. + * Immediately returns {@code true} for an empty client array. + *

+ * Use this method instead of {@link #moveClient(Client, ChannelBase)} for moving + * several clients as this will only send 1 command to the server and thus complete faster. + *

+ * + * @param clients + * the clients to move, cannot be {@code null} + * @param channel + * the channel to move the clients into, cannot be {@code null} + * + * @return a future to track the progress of this command + * + * @throws IllegalArgumentException + * if {@code clients} or {@code channel} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture moveClients(Client[] clients, ChannelBase channel) { + return moveClients(clients, channel, null); + } + + /** + * Moves a single client into a channel using the specified password. + *

+ * Consider using {@link #moveClients(int[], int, String)} to move multiple clients. + *

+ * + * @param clientId + * the ID of the client to move + * @param channelId + * the ID of the channel to move the client into + * @param channelPassword + * the password of the channel, can be {@code null} + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see Channel#getId() + */ + public CommandFuture moveClient(int clientId, int channelId, String channelPassword) { + Command cmd = ClientCommands.clientMove(clientId, channelId, channelPassword); + return executeAndReturnError(cmd); + } + + /** + * Moves multiple clients into a channel using the specified password. + * Immediately returns {@code true} for an empty client ID array. + *

+ * Use this method instead of {@link #moveClient(int, int, String)} for moving + * several clients as this will only send 1 command to the server and thus complete faster. + *

+ * + * @param clientIds + * the IDs of the clients to move, cannot be {@code null} + * @param channelId + * the ID of the channel to move the clients into + * @param channelPassword + * the password of the channel, can be {@code null} + * + * @return a future to track the progress of this command + * + * @throws IllegalArgumentException + * if {@code clientIds} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + * @see Channel#getId() + */ + public CommandFuture moveClients(int[] clientIds, int channelId, String channelPassword) { + if (clientIds == null) throw new IllegalArgumentException("Client ID array was null"); + if (clientIds.length == 0) return CommandFuture.immediate(null); // Success + + Command cmd = ClientCommands.clientMove(clientIds, channelId, channelPassword); + return executeAndReturnError(cmd); + } + + /** + * Moves a single client into a channel using the specified password. + *

+ * Consider using {@link #moveClients(Client[], ChannelBase, String)} to move multiple clients. + *

+ * + * @param client + * the client to move, cannot be {@code null} + * @param channel + * the channel to move the client into, cannot be {@code null} + * @param channelPassword + * the password of the channel, can be {@code null} + * + * @return a future to track the progress of this command + * + * @throws IllegalArgumentException + * if {@code client} or {@code channel} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture moveClient(Client client, ChannelBase channel, String channelPassword) { + if (client == null) throw new IllegalArgumentException("Client cannot be null"); + if (channel == null) throw new IllegalArgumentException("Channel cannot be null"); + + return moveClient(client.getId(), channel.getId(), channelPassword); + } + + /** + * Moves multiple clients into a channel using the specified password. + * Immediately returns {@code true} for an empty client array. + *

+ * Use this method instead of {@link #moveClient(Client, ChannelBase, String)} for moving + * several clients as this will only send 1 command to the server and thus complete faster. + *

+ * + * @param clients + * the clients to move, cannot be {@code null} + * @param channel + * the channel to move the clients into, cannot be {@code null} + * @param channelPassword + * the password of the channel, can be {@code null} + * + * @return a future to track the progress of this command + * + * @throws IllegalArgumentException + * if {@code clients} or {@code channel} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture moveClients(Client[] clients, ChannelBase channel, String channelPassword) { + if (clients == null) throw new IllegalArgumentException("Client array cannot be null"); + if (channel == null) throw new IllegalArgumentException("Channel cannot be null"); + + int[] clientIds = new int[clients.length]; + for (int i = 0; i < clients.length; i++) { + clientIds[i] = clients[i].getId(); + } + return moveClients(clientIds, channel.getId(), channelPassword); + } + + /** + * Moves and renames a file on the file repository within the same channel. + * + * @param oldPath + * the current path to the file + * @param newPath + * the desired new path + * @param channelId + * the ID of the channel the file resides in + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #moveFile(String, String, int, int) moveFile to a different channel + */ + public CommandFuture moveFile(String oldPath, String newPath, int channelId) { + return moveFile(oldPath, newPath, channelId, null); + } + + /** + * Renames a file on the file repository and moves it to a new path in a different channel. + * + * @param oldPath + * the current path to the file + * @param newPath + * the desired new path + * @param oldChannelId + * the ID of the channel the file currently resides in + * @param newChannelId + * the ID of the channel the file should be moved to + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #moveFile(String, String, int) moveFile within the same channel + */ + public CommandFuture moveFile(String oldPath, String newPath, int oldChannelId, int newChannelId) { + return moveFile(oldPath, newPath, oldChannelId, null, newChannelId, null); + } + + /** + * Moves and renames a file on the file repository within the same channel. + * + * @param oldPath + * the current path to the file + * @param newPath + * the desired new path + * @param channelId + * the ID of the channel the file resides in + * @param channelPassword + * the password of the channel + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #moveFile(String, String, int, String, int, String) moveFile to a different channel + */ + public CommandFuture moveFile(String oldPath, String newPath, int channelId, String channelPassword) { + Command cmd = FileCommands.ftRenameFile(oldPath, newPath, channelId, channelPassword); + return executeAndReturnError(cmd); + } + + /** + * Renames a file on the file repository and moves it to a new path in a different channel. + * + * @param oldPath + * the current path to the file + * @param newPath + * the desired new path + * @param oldChannelId + * the ID of the channel the file currently resides in + * @param oldPassword + * the password of the current channel + * @param newChannelId + * the ID of the channel the file should be moved to + * @param newPassword + * the password of the new channel + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #moveFile(String, String, int, String) moveFile within the same channel + */ + public CommandFuture moveFile(String oldPath, String newPath, int oldChannelId, String oldPassword, int newChannelId, String newPassword) { + Command cmd = FileCommands.ftRenameFile(oldPath, newPath, oldChannelId, oldPassword, newChannelId, newPassword); + return executeAndReturnError(cmd); + } + + /** + * Moves the server query into a channel. + * + * @param channelId + * the ID of the channel to move the server query into + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + */ + public CommandFuture moveQuery(int channelId) { + return moveClient(0, channelId, null); + } + + /** + * Moves the server query into a channel. + * + * @param channel + * the channel to move the server query into, cannot be {@code null} + * + * @return a future to track the progress of this command + * + * @throws IllegalArgumentException + * if {@code channel} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture moveQuery(ChannelBase channel) { + if (channel == null) throw new IllegalArgumentException("Channel cannot be null"); + + return moveClient(0, channel.getId(), null); + } + + /** + * Moves the server query into a channel using the specified password. + * + * @param channelId + * the ID of the channel to move the client into + * @param channelPassword + * the password of the channel, can be {@code null} + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + */ + public CommandFuture moveQuery(int channelId, String channelPassword) { + return moveClient(0, channelId, channelPassword); + } + + /** + * Moves the server query into a channel using the specified password. + * + * @param channel + * the channel to move the client into, cannot be {@code null} + * @param channelPassword + * the password of the channel, can be {@code null} + * + * @return a future to track the progress of this command + * + * @throws IllegalArgumentException + * if {@code channel} is {@code null} + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture moveQuery(ChannelBase channel, String channelPassword) { + if (channel == null) throw new IllegalArgumentException("Channel cannot be null"); + + return moveClient(0, channel.getId(), channelPassword); + } + + /** + * Pokes the client with the specified client ID. + * This opens up a small popup window for the client containing your message and plays a sound. + * The displayed message will be formatted like this:
+ * {@code hh:mm:ss - "Your Nickname" poked you: } + *

+ * The displayed message length is limited to 100 UTF-8 bytes. + * If a client has already received a poke message, all subsequent pokes will simply add a line + * to the already opened popup window and will still play a sound. + *

+ * + * @param clientId + * the ID of the client to poke + * @param message + * the message to send, may contain BB codes + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + */ + public CommandFuture pokeClient(int clientId, String message) { + Command cmd = ClientCommands.clientPoke(clientId, message); + return executeAndReturnError(cmd); + } + + /** + * Terminates the connection with the TeamSpeak3 server. + *

+ * This command should never be executed by a user of this API, + * as it leaves the query in an undefined state. To terminate + * a connection regularly, use {@link TS3Query#exit()}. + *

+ * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + CommandFuture quit() { + Command cmd = QueryCommands.quit(); + return executeAndReturnError(cmd); + } + + /** + * Registers the server query to receive notifications about all server events. + *

+ * This means that the following actions will trigger event notifications: + *

+ *
    + *
  • A client joins the server or disconnects from it
  • + *
  • A client switches channels
  • + *
  • A client sends a server message
  • + *
  • A client sends a channel message in the channel the query is in
  • + *
  • A client sends a private message to the server query
  • + *
  • A client uses a privilege key
  • + *
+ *

+ * The limitations to when the query receives notifications about chat events cannot be circumvented. + *

+ * To be able to process these events in your application, register an event listener. + * + * @return whether all commands succeeded or not + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 6 + * @see #addTS3Listeners(TS3Listener...) + */ + public CommandFuture registerAllEvents() { + Collection> eventFutures = Arrays.asList( + registerEvent(TS3EventType.SERVER), + registerEvent(TS3EventType.TEXT_SERVER), + registerEvent(TS3EventType.CHANNEL, 0), + registerEvent(TS3EventType.TEXT_CHANNEL, 0), + registerEvent(TS3EventType.TEXT_PRIVATE), + registerEvent(TS3EventType.PRIVILEGE_KEY_USED) + ); + + return CommandFuture.ofAll(eventFutures) + .map(__ -> null); // Return success as Void, not List + } + + /** + * Registers the server query to receive notifications about a given event type. + *

+ * If used with {@link TS3EventType#TEXT_CHANNEL}, this will listen to chat events in the current channel. + * If used with {@link TS3EventType#CHANNEL}, this will listen to all channel events. + * To specify a different channel for channel events, use {@link #registerEvent(TS3EventType, int)}. + *

+ * + * @param eventType + * the event type to be notified about + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #addTS3Listeners(TS3Listener...) + * @see #registerEvent(TS3EventType, int) + * @see #registerAllEvents() + */ + public CommandFuture registerEvent(TS3EventType eventType) { + if (eventType == TS3EventType.CHANNEL || eventType == TS3EventType.TEXT_CHANNEL) { + return registerEvent(eventType, 0); + } else { + return registerEvent(eventType, -1); + } + } + + /** + * Registers the server query to receive notifications about a given event type. + * + * @param eventType + * the event type to be notified about + * @param channelId + * the ID of the channel to listen to, will be ignored if set to {@code -1}. + * Can be set to {@code 0} for {@link TS3EventType#CHANNEL} to receive notifications about all channel switches. + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Channel#getId() + * @see #addTS3Listeners(TS3Listener...) + * @see #registerAllEvents() + */ + public CommandFuture registerEvent(TS3EventType eventType, int channelId) { + Command cmd = QueryCommands.serverNotifyRegister(eventType, channelId); + return executeAndReturnError(cmd); + } + + /** + * Registers the server query to receive notifications about multiple given event types. + *

+ * If used with {@link TS3EventType#TEXT_CHANNEL}, this will listen to chat events in the current channel. + * If used with {@link TS3EventType#CHANNEL}, this will listen to all channel events. + * To specify a different channel for channel events, use {@link #registerEvent(TS3EventType, int)}. + *

+ * + * @param eventTypes + * the event types to be notified about + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands n, one command per TS3EventType + * @see #addTS3Listeners(TS3Listener...) + * @see #registerEvent(TS3EventType, int) + * @see #registerAllEvents() + */ + public CommandFuture registerEvents(TS3EventType... eventTypes) { + if (eventTypes.length == 0) return CommandFuture.immediate(null); // Success + + Collection> registerFutures = new ArrayList<>(eventTypes.length); + for (TS3EventType type : eventTypes) { + registerFutures.add(registerEvent(type)); + } + + return CommandFuture.ofAll(registerFutures) + .map(__ -> null); // Return success as Void, not List + } + + /** + * Removes the client specified by its database ID from the specified server group. + * + * @param serverGroupId + * the ID of the server group + * @param clientDatabaseId + * the database ID of the client + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + * @see Client#getDatabaseId() + * @see #removeClientFromServerGroup(ServerGroup, Client) + */ + public CommandFuture removeClientFromServerGroup(int serverGroupId, int clientDatabaseId) { + Command cmd = ServerGroupCommands.serverGroupDelClient(serverGroupId, clientDatabaseId); + return executeAndReturnError(cmd); + } + + /** + * Removes the specified client from the specified server group. + * + * @param serverGroup + * the server group to remove the client from + * @param client + * the client to remove from the server group + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #removeClientFromServerGroup(int, int) + */ + public CommandFuture removeClientFromServerGroup(ServerGroup serverGroup, Client client) { + return removeClientFromServerGroup(serverGroup.getId(), client.getDatabaseId()); + } + + /** + * Removes one or more {@link TS3Listener}s to the event manager of the query. + *

+ * If a listener was not actually registered, it will be ignored and no exception will be thrown. + *

+ * + * @param listeners + * one or more listeners to remove + * + * @see #addTS3Listeners(TS3Listener...) + * @see TS3Listener + * @see TS3EventType + */ + public void removeTS3Listeners(TS3Listener... listeners) { + query.getEventManager().removeListeners(listeners); + } + + /** + * Renames the channel group with the specified ID. + * + * @param channelGroupId + * the ID of the channel group to rename + * @param name + * the new name for the channel group + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + * @see #renameChannelGroup(ChannelGroup, String) + */ + public CommandFuture renameChannelGroup(int channelGroupId, String name) { + Command cmd = ChannelGroupCommands.channelGroupRename(channelGroupId, name); + return executeAndReturnError(cmd); + } + + /** + * Renames the specified channel group. + * + * @param channelGroup + * the channel group to rename + * @param name + * the new name for the channel group + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #renameChannelGroup(int, String) + */ + public CommandFuture renameChannelGroup(ChannelGroup channelGroup, String name) { + return renameChannelGroup(channelGroup.getId(), name); + } + + /** + * Renames the server group with the specified ID. + * + * @param serverGroupId + * the ID of the server group to rename + * @param name + * the new name for the server group + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ServerGroup#getId() + * @see #renameServerGroup(ServerGroup, String) + */ + public CommandFuture renameServerGroup(int serverGroupId, String name) { + Command cmd = ServerGroupCommands.serverGroupRename(serverGroupId, name); + return executeAndReturnError(cmd); + } + + /** + * Renames the specified server group. + * + * @param serverGroup + * the server group to rename + * @param name + * the new name for the server group + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #renameServerGroup(int, String) + */ + public CommandFuture renameServerGroup(ServerGroup serverGroup, String name) { + return renameChannelGroup(serverGroup.getId(), name); + } + + /** + * Resets all permissions and deletes all server / channel groups. Use carefully. + * + * @return a token for a new administrator account + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture resetPermissions() { + Command cmd = PermissionCommands.permReset(); + return executeAndReturnStringProperty(cmd, "token"); + } + + /** + * Finds all clients that have any value associated with the {@code key} custom client property, + * and returns the client's database ID and the key and value of the matching custom property. + * + * @param key + * the key to search for, cannot be {@code null} + * + * @return a list of client database IDs and their matching custom client properties + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see #searchCustomClientProperty(String, String) + * @see #getCustomClientProperties(int) + */ + public CommandFuture> searchCustomClientProperty(String key) { + return searchCustomClientProperty(key, "%"); + } + + /** + * Finds all clients whose value associated with the {@code key} custom client property matches the + * SQL-like pattern {@code valuePattern}, and returns the client's database ID and the key and value + * of the matching custom property. + *

+ * Patterns are case insensitive. They support the wildcard characters {@code %}, which matches any sequence of + * zero or more characters, and {@code _}, which matches exactly one arbitrary character. + *

+ * + * @param key + * the key to search for, cannot be {@code null} + * @param valuePattern + * the pattern that values need to match to be included + * + * @return a list of client database IDs and their matching custom client properties + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see #searchCustomClientProperty(String) + * @see #getCustomClientProperties(int) + */ + public CommandFuture> searchCustomClientProperty(String key, String valuePattern) { + if (key == null) throw new IllegalArgumentException("Key cannot be null"); + + Command cmd = CustomPropertyCommands.customSearch(key, valuePattern); + return executeAndTransform(cmd, CustomPropertyAssignment::new); + } + + /** + * Moves the server query into the virtual server with the specified ID. + * + * @param id + * the ID of the virtual server + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see VirtualServer#getId() + * @see #selectVirtualServerById(int, String) + * @see #selectVirtualServerByPort(int) + * @see #selectVirtualServer(VirtualServer) + */ + public CommandFuture selectVirtualServerById(int id) { + return selectVirtualServerById(id, null); + } + + /** + * Moves the server query into the virtual server with the specified ID + * and sets the server query's nickname. + *

+ * The nickname must be between 3 and 30 UTF-8 bytes long. BB codes will be ignored. + *

+ * + * @param id + * the ID of the virtual server + * @param nickname + * the nickname, or {@code null} if the nickname should not be set + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see VirtualServer#getId() + * @see #selectVirtualServerById(int) + * @see #selectVirtualServerByPort(int, String) + * @see #selectVirtualServer(VirtualServer, String) + */ + public CommandFuture selectVirtualServerById(int id, String nickname) { + Command cmd = QueryCommands.useId(id, nickname); + return executeAndReturnError(cmd); + } + + /** + * Moves the server query into the virtual server with the specified voice port. + * + * @param port + * the voice port of the virtual server + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see VirtualServer#getPort() + * @see #selectVirtualServerById(int) + * @see #selectVirtualServerByPort(int, String) + * @see #selectVirtualServer(VirtualServer) + */ + public CommandFuture selectVirtualServerByPort(int port) { + return selectVirtualServerByPort(port, null); + } + + /** + * Moves the server query into the virtual server with the specified voice port + * and sets the server query's nickname. + *

+ * The nickname must be between 3 and 30 UTF-8 bytes long. BB codes will be ignored. + *

+ * + * @param port + * the voice port of the virtual server + * @param nickname + * the nickname, or {@code null} if the nickname should not be set + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see VirtualServer#getPort() + * @see #selectVirtualServerById(int, String) + * @see #selectVirtualServerByPort(int) + * @see #selectVirtualServer(VirtualServer, String) + */ + public CommandFuture selectVirtualServerByPort(int port, String nickname) { + Command cmd = QueryCommands.usePort(port, nickname); + return executeAndReturnError(cmd); + } + + /** + * Moves the server query into the specified virtual server. + * + * @param server + * the virtual server to move into + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #selectVirtualServerById(int) + * @see #selectVirtualServerByPort(int) + * @see #selectVirtualServer(VirtualServer, String) + */ + public CommandFuture selectVirtualServer(VirtualServer server) { + return selectVirtualServerById(server.getId()); + } + + /** + * Moves the server query into the specified virtual server + * and sets the server query's nickname. + *

+ * The nickname must be between 3 and 30 UTF-8 bytes long. BB codes will be ignored. + *

+ * + * @param server + * the virtual server to move into + * @param nickname + * the nickname, or {@code null} if the nickname should not be set + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #selectVirtualServerById(int, String) + * @see #selectVirtualServerByPort(int, String) + * @see #selectVirtualServer(VirtualServer) + */ + public CommandFuture selectVirtualServer(VirtualServer server, String nickname) { + return selectVirtualServerById(server.getId(), nickname); + } + + /** + * Sends an offline message to the client with the given unique identifier. + *

+ * The message subject's length is limited to 200 UTF-8 bytes and BB codes in it will be ignored. + * The message body's length is limited to 4096 UTF-8 bytes and accepts BB codes + *

+ * + * @param clientUId + * the unique identifier of the client to send the message to + * @param subject + * the subject for the message, may not contain BB codes + * @param message + * the actual message body, may contain BB codes + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getUniqueIdentifier() + * @see Message + */ + public CommandFuture sendOfflineMessage(String clientUId, String subject, String message) { + Command cmd = MessageCommands.messageAdd(clientUId, subject, message); + return executeAndReturnError(cmd); + } + + /** + * Sends a text message either to the whole virtual server, a channel or specific client. + * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes. + *

+ * To send a message to all virtual servers, use {@link #broadcast(String)}. + * To send an offline message, use {@link #sendOfflineMessage(String, String, String)}. + *

+ * + * @param targetMode + * where the message should be sent to + * @param targetId + * the client ID of the recipient of this message. This value is ignored unless {@code targetMode} is {@code CLIENT} + * @param message + * the text message to send + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + */ + public CommandFuture sendTextMessage(TextMessageTargetMode targetMode, int targetId, String message) { + Command cmd = ClientCommands.sendTextMessage(targetMode.getIndex(), targetId, message); + return executeAndReturnError(cmd); + } + + /** + * Sends a text message to the channel with the specified ID. + * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes. + *

+ * This will move the client into the channel with the specified channel ID, + * but will not move it back to the original channel! + *

+ * + * @param channelId + * the ID of the channel to which the message should be sent to + * @param message + * the text message to send + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #sendChannelMessage(String) + * @see Channel#getId() + */ + public CommandFuture sendChannelMessage(int channelId, String message) { + return moveQuery(channelId) + .then(__ -> sendTextMessage(TextMessageTargetMode.CHANNEL, 0, message)); + } + + /** + * Sends a text message to the channel the server query is currently in. + * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes. + * + * @param message + * the text message to send + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture sendChannelMessage(String message) { + return sendTextMessage(TextMessageTargetMode.CHANNEL, 0, message); + } + + /** + * Sends a text message to the virtual server with the specified ID. + * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes. + *

+ * This will move the client to the virtual server with the specified server ID, + * but will not move it back to the original virtual server! + *

+ * + * @param serverId + * the ID of the virtual server to which the message should be sent to + * @param message + * the text message to send + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #sendServerMessage(String) + * @see VirtualServer#getId() + */ + public CommandFuture sendServerMessage(int serverId, String message) { + return selectVirtualServerById(serverId) + .then(__ -> sendTextMessage(TextMessageTargetMode.SERVER, 0, message)); + } + + /** + * Sends a text message to the virtual server the server query is currently in. + * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes. + * + * @param message + * the text message to send + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture sendServerMessage(String message) { + return sendTextMessage(TextMessageTargetMode.SERVER, 0, message); + } + + /** + * Sends a private message to the client with the specified client ID. + * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes. + * + * @param clientId + * the ID of the client to send the message to + * @param message + * the text message to send + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getId() + */ + public CommandFuture sendPrivateMessage(int clientId, String message) { + return sendTextMessage(TextMessageTargetMode.CLIENT, clientId, message); + } + + /** + * Sets a channel group for a client in a specific channel. + * + * @param groupId + * the ID of the group the client should join + * @param channelId + * the ID of the channel where the channel group should be assigned + * @param clientDBId + * the database ID of the client for which the channel group should be set + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see ChannelGroup#getId() + * @see Channel#getId() + * @see Client#getDatabaseId() + */ + public CommandFuture setClientChannelGroup(int groupId, int channelId, int clientDBId) { + Command cmd = ChannelGroupCommands.setClientChannelGroup(groupId, channelId, clientDBId); + return executeAndReturnError(cmd); + } + + /** + * Sets the value of the multiple custom client properties for a client. + *

+ * If any key present in the map already has a value assigned for this client, + * the existing value will be overwritten. + * This method does not delete keys not present in the map. + *

+ * If {@code properties} contains an entry with {@code null} as its key, + * that entry will be ignored and no exception will be thrown. + *

+ * + * @param clientDBId + * the database ID of the target client + * @param properties + * the map of properties to set, cannot be {@code null} + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands properties.size() + * @see Client#getDatabaseId() + * @see #setCustomClientProperty(int, String, String) + * @see #deleteCustomClientProperty(int, String) + */ + public CommandFuture setCustomClientProperties(int clientDBId, Map properties) { + Collection> futures = new ArrayList<>(properties.size()); + + for (Map.Entry entry : properties.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + + if (key != null) { + futures.add(setCustomClientProperty(clientDBId, key, value)); + } + } + + return CommandFuture.ofAll(futures) + .map(__ -> null); // Return success as Void, not List + } + + /** + * Sets the value of the {@code key} custom client property for a client. + *

+ * If there is already an assignment of the {@code key} custom client property + * for this client, the existing value will be overwritten. + *

+ * + * @param clientDBId + * the database ID of the target client + * @param key + * the key of the custom property to set, cannot be {@code null} + * @param value + * the (new) value of the custom property to set + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see Client#getDatabaseId() + * @see #setCustomClientProperties(int, Map) + * @see #deleteCustomClientProperty(int, String) + */ + public CommandFuture setCustomClientProperty(int clientDBId, String key, String value) { + if (key == null) throw new IllegalArgumentException("Key cannot be null"); + + Command cmd = CustomPropertyCommands.customSet(clientDBId, key, value); + return executeAndReturnError(cmd); + } + + /** + * Sets the read flag to {@code true} for a given message. This will not delete the message. + * + * @param messageId + * the ID of the message for which the read flag should be set + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #setMessageReadFlag(int, boolean) + */ + public CommandFuture setMessageRead(int messageId) { + return setMessageReadFlag(messageId, true); + } + + /** + * Sets the read flag to {@code true} for a given message. This will not delete the message. + * + * @param message + * the message for which the read flag should be set + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #setMessageRead(int) + * @see #setMessageReadFlag(Message, boolean) + * @see #deleteOfflineMessage(int) + */ + public CommandFuture setMessageRead(Message message) { + return setMessageReadFlag(message.getId(), true); + } + + /** + * Sets the read flag for a given message. This will not delete the message. + * + * @param messageId + * the ID of the message for which the read flag should be set + * @param read + * the boolean value to which the read flag should be set + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #setMessageRead(int) + * @see #setMessageReadFlag(Message, boolean) + * @see #deleteOfflineMessage(int) + */ + public CommandFuture setMessageReadFlag(int messageId, boolean read) { + Command cmd = MessageCommands.messageUpdateFlag(messageId, read); + return executeAndReturnError(cmd); + } + + /** + * Sets the read flag for a given message. This will not delete the message. + * + * @param message + * the message for which the read flag should be set + * @param read + * the boolean value to which the read flag should be set + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #setMessageRead(Message) + * @see #setMessageReadFlag(int, boolean) + * @see #deleteOfflineMessage(int) + */ + public CommandFuture setMessageReadFlag(Message message, boolean read) { + return setMessageReadFlag(message.getId(), read); + } + + /** + * Sets the nickname of the server query client. + *

+ * The nickname must be between 3 and 30 UTF-8 bytes long. BB codes will be ignored. + *

+ * + * @param nickname + * the new nickname, may not be {@code null} + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #updateClient(Map) + */ + public CommandFuture setNickname(String nickname) { + Map options = Collections.singletonMap(ClientProperty.CLIENT_NICKNAME, nickname); + return updateClient(options); + } + + /** + * Starts the virtual server with the specified ID. + * + * @param serverId + * the ID of the virtual server + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture startServer(int serverId) { + Command cmd = VirtualServerCommands.serverStart(serverId); + return executeAndReturnError(cmd); + } + + /** + * Starts the specified virtual server. + * + * @param virtualServer + * the virtual server to start + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture startServer(VirtualServer virtualServer) { + return startServer(virtualServer.getId()); + } + + /** + * Stops the virtual server with the specified ID. + * + * @param serverId + * the ID of the virtual server + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture stopServer(int serverId) { + return stopServer(serverId, null); + } + + /** + * Stops the virtual server with the specified ID. + * + * @param serverId + * the ID of the virtual server + * @param reason + * the reason message to display to clients when they are disconnected + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture stopServer(int serverId, String reason) { + Command cmd = VirtualServerCommands.serverStop(serverId, reason); + return executeAndReturnError(cmd); + } + + /** + * Stops the specified virtual server. + * + * @param virtualServer + * the virtual server to stop + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture stopServer(VirtualServer virtualServer) { + return stopServer(virtualServer.getId(), null); + } + + /** + * Stops the specified virtual server. + * + * @param virtualServer + * the virtual server to stop + * @param reason + * the reason message to display to clients when they are disconnected + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture stopServer(VirtualServer virtualServer, String reason) { + return stopServer(virtualServer.getId(), reason); + } + + /** + * Stops the entire TeamSpeak 3 Server instance by shutting down the process. + *

+ * To have permission to use this command, you need to use the server query admin login. + *

+ * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture stopServerProcess() { + return stopServerProcess(null); + } + + /** + * Stops the entire TeamSpeak 3 Server instance by shutting down the process. + *

+ * To have permission to use this command, you need to use the server query admin login. + *

+ * + * @param reason + * the reason message to display to clients when they are disconnected + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture stopServerProcess(String reason) { + Command cmd = ServerCommands.serverProcessStop(reason); + return executeAndReturnError(cmd); + } + + /** + * Unregisters the server query from receiving any event notifications. + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture unregisterAllEvents() { + Command cmd = QueryCommands.serverNotifyUnregister(); + return executeAndReturnError(cmd); + } + + /** + * Updates several client properties for this server query instance. + * + * @param options + * the map of properties to update + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #updateClient(ClientProperty, String) + * @see #editClient(int, Map) + */ + public CommandFuture updateClient(Map options) { + Command cmd = ClientCommands.clientUpdate(options); + return executeAndReturnError(cmd); + } + + /** + * Changes a single client property for this server query instance. + *

+ * Note that one can set many properties at once with the overloaded method that + * takes a map of client properties and strings. + *

+ * + * @param property + * the client property to modify, make sure it is editable + * @param value + * the new value of the property + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #updateClient(Map) + * @see #editClient(int, Map) + */ + public CommandFuture updateClient(ClientProperty property, String value) { + return updateClient(Collections.singletonMap(property, value)); + } + + /** + * Generates new login credentials for the currently connected server query instance, using the given name. + *

+ * This will remove the current login credentials! You won't be logged out, but after disconnecting, + * the old credentials will no longer work. Make sure to not lock yourselves out! + *

+ * + * @param loginName + * the name for the server query login + * + * @return the generated password for the server query login + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + */ + public CommandFuture updateServerQueryLogin(String loginName) { + Command cmd = ClientCommands.clientSetServerQueryLogin(loginName); + return executeAndReturnStringProperty(cmd, "client_login_password"); + } + + /** + * Uploads a file to the file repository at a given path and channel + * by reading {@code dataLength} bytes from an open {@link InputStream}. + *

+ * It is the user's responsibility to ensure that the given {@code InputStream} is + * open and that {@code dataLength} bytes can eventually be read from it. The user is + * also responsible for closing the stream once the upload has finished. + *

+ * Note that this method will not read the entire file to memory and can thus + * upload arbitrarily sized files to the file repository. + *

+ * + * @param dataIn + * a stream that contains the data that should be uploaded + * @param dataLength + * how many bytes should be read from the stream + * @param filePath + * the path the file should have after being uploaded + * @param overwrite + * if {@code false}, fails if there's already a file at {@code filePath} + * @param channelId + * the ID of the channel to upload the file to + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #uploadFileDirect(byte[], String, boolean, int, String) + */ + public CommandFuture uploadFile(InputStream dataIn, long dataLength, String filePath, boolean overwrite, int channelId) { + return uploadFile(dataIn, dataLength, filePath, overwrite, channelId, null); + } + + /** + * Uploads a file to the file repository at a given path and channel + * by reading {@code dataLength} bytes from an open {@link InputStream}. + *

+ * It is the user's responsibility to ensure that the given {@code InputStream} is + * open and that {@code dataLength} bytes can eventually be read from it. The user is + * also responsible for closing the stream once the upload has finished. + *

+ * Note that this method will not read the entire file to memory and can thus + * upload arbitrarily sized files to the file repository. + *

+ * + * @param dataIn + * a stream that contains the data that should be uploaded + * @param dataLength + * how many bytes should be read from the stream + * @param filePath + * the path the file should have after being uploaded + * @param overwrite + * if {@code false}, fails if there's already a file at {@code filePath} + * @param channelId + * the ID of the channel to upload the file to + * @param channelPassword + * that channel's password + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #uploadFileDirect(byte[], String, boolean, int, String) + */ + public CommandFuture uploadFile(InputStream dataIn, long dataLength, String filePath, boolean overwrite, int channelId, String channelPassword) { + FileTransferHelper helper = query.getFileTransferHelper(); + int transferId = helper.getClientTransferId(); + Command cmd = FileCommands.ftInitUpload(transferId, filePath, channelId, channelPassword, dataLength, overwrite); + CommandFuture future = new CommandFuture<>(); + + executeAndTransformFirst(cmd, FileTransferParameters::new).onSuccess(params -> { + QueryError error = params.getQueryError(); + if (!error.isSuccessful()) { + future.fail(new TS3CommandFailedException(error, cmd.getName())); + return; + } + + try { + query.getFileTransferHelper().uploadFile(dataIn, dataLength, params); + } catch (IOException e) { + future.fail(new TS3FileTransferFailedException("Upload failed", e)); + return; + } + future.set(null); // Mark as successful + }).forwardFailure(future); + + return future; + } + + /** + * Uploads a file that is already stored in memory to the file repository + * at a given path and channel. + * + * @param data + * the file's data as a byte array + * @param filePath + * the path the file should have after being uploaded + * @param overwrite + * if {@code false}, fails if there's already a file at {@code filePath} + * @param channelId + * the ID of the channel to upload the file to + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #uploadFile(InputStream, long, String, boolean, int) + */ + public CommandFuture uploadFileDirect(byte[] data, String filePath, boolean overwrite, int channelId) { + return uploadFileDirect(data, filePath, overwrite, channelId, null); + } + + /** + * Uploads a file that is already stored in memory to the file repository + * at a given path and channel. + * + * @param data + * the file's data as a byte array + * @param filePath + * the path the file should have after being uploaded + * @param overwrite + * if {@code false}, fails if there's already a file at {@code filePath} + * @param channelId + * the ID of the channel to upload the file to + * @param channelPassword + * that channel's password + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see FileInfo#getPath() + * @see Channel#getId() + * @see #uploadFile(InputStream, long, String, boolean, int, String) + */ + public CommandFuture uploadFileDirect(byte[] data, String filePath, boolean overwrite, int channelId, String channelPassword) { + return uploadFile(new ByteArrayInputStream(data), data.length, filePath, overwrite, channelId, channelPassword); + } + + /** + * Uploads an icon to the icon directory in the file repository + * by reading {@code dataLength} bytes from an open {@link InputStream}. + *

+ * It is the user's responsibility to ensure that the given {@code InputStream} is + * open and that {@code dataLength} bytes can eventually be read from it. The user is + * also responsible for closing the stream once the upload has finished. + *

+ * Note that unlike the file upload methods, this will read the entire file to memory. + * This is because the CRC32 hash must be calculated before the icon can be uploaded. + * That means that all icon files must be less than 231-1 bytes in size. + *

+ * Uploads that is already stored in memory to the icon directory + * in the file repository. If this icon has already been uploaded or + * if a hash collision occurs (CRC32), this command will fail. + * + * @param dataIn + * a stream that contains the data that should be uploaded + * @param dataLength + * how many bytes should be read from the stream + * + * @return the ID of the uploaded icon + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see IconFile#getIconId() + * @see #uploadIconDirect(byte[]) + * @see #downloadIcon(OutputStream, long) + */ + public CommandFuture uploadIcon(InputStream dataIn, long dataLength) { + FileTransferHelper helper = query.getFileTransferHelper(); + byte[] data; + try { + data = helper.readFully(dataIn, dataLength); + } catch (IOException e) { + throw new TS3FileTransferFailedException("Reading stream failed", e); + } + return uploadIconDirect(data); + } + + /** + * Uploads an icon that is already stored in memory to the icon directory + * in the file repository. If this icon has already been uploaded or + * if a CRC32 hash collision occurs, this command will fail. + * + * @param data + * the icon's data as a byte array + * + * @return the ID of the uploaded icon + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @throws TS3FileTransferFailedException + * if the file transfer fails for any reason + * @querycommands 1 + * @see IconFile#getIconId() + * @see #uploadIcon(InputStream, long) + * @see #downloadIconDirect(long) + */ + public CommandFuture uploadIconDirect(byte[] data) { + FileTransferHelper helper = query.getFileTransferHelper(); + CommandFuture future = new CommandFuture<>(); + + long iconId = helper.getIconId(data); + String path = "/icon_" + iconId; + + uploadFileDirect(data, path, false, 0) + .onSuccess(__ -> future.set(iconId)) + .onFailure(transformError(future, 2050, iconId)); + + return future; + } + + /** + * Uses an existing privilege key to join a server or channel group. + * + * @param token + * the privilege key to use + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see PrivilegeKey + * @see #addPrivilegeKey(PrivilegeKeyType, int, int, String) + * @see #usePrivilegeKey(PrivilegeKey) + */ + public CommandFuture usePrivilegeKey(String token) { + Command cmd = PrivilegeKeyCommands.privilegeKeyUse(token); + return executeAndReturnError(cmd); + } + + /** + * Uses an existing privilege key to join a server or channel group. + * + * @param privilegeKey + * the privilege key to use + * + * @return a future to track the progress of this command + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see PrivilegeKey + * @see #addPrivilegeKey(PrivilegeKeyType, int, int, String) + * @see #usePrivilegeKey(String) + */ + public CommandFuture usePrivilegeKey(PrivilegeKey privilegeKey) { + return usePrivilegeKey(privilegeKey.getToken()); + } + + /** + * Gets information about the current server query instance. + * + * @return information about the server query instance + * + * @throws TS3CommandFailedException + * if the execution of a command fails + * @querycommands 1 + * @see #getClientInfo(int) + */ + public CommandFuture whoAmI() { + Command cmd = QueryCommands.whoAmI(); + return executeAndTransformFirst(cmd, ServerQueryInfo::new); + } + + /** + * Checks whether a given {@link TS3Exception} is a {@link TS3CommandFailedException} with the + * specified error ID. + * + * @param exception + * the exception to check + * @param errorId + * the error ID to match + * + * @return whether {@code exception} is a {@code TS3CommandFailedException} with error ID {@code errorId}. + */ + private static boolean isQueryError(TS3Exception exception, int errorId) { + if (exception instanceof TS3CommandFailedException) { + TS3CommandFailedException cfe = (TS3CommandFailedException) exception; + return (cfe.getError().getId() == errorId); + } else { + return false; + } + } + + /** + * Creates a {@code FailureListener} that checks whether the caught exception is + * a {@code TS3CommandFailedException} with error ID {@code errorId}. + *

+ * If so, the listener makes {@code future} succeed by setting its result value to an empty + * list with element type {@code T}. Else, the caught exception is forwarded to {@code future}. + *

+ * + * @param future + * the future to forward the result to + * @param errorId + * the error ID to catch + * @param replacement + * the value to + * @param + * the type of {@code replacement} and element type of {@code future} + * + * @return a {@code FailureListener} with the described properties + */ + private static CommandFuture.FailureListener transformError(CommandFuture future, int errorId, T replacement) { + return exception -> { + if (isQueryError(exception, errorId)) { + future.set(replacement); + } else { + future.fail(exception); + } + }; + } + + /** + * Executes a command and sets the returned future to true if the command succeeded. + * + * @param command + * the command to execute + * + * @return a future to track the progress of this command + */ + private CommandFuture executeAndReturnError(Command command) { + CommandFuture future = command.getFuture() + .map(__ -> null); // Mark as successful + + query.doCommandAsync(command); + return future; + } + + /** + * Executes a command, checking for failure and returning a single + * {@code String} property from the first response map. + * + * @param command + * the command to execute + * @param property + * the name of the property to return + * + * @return the value of the specified {@code String} property + */ + private CommandFuture executeAndReturnStringProperty(Command command, String property) { + CommandFuture future = command.getFuture() + .map(result -> result.getFirstResponse().get(property)); + + query.doCommandAsync(command); + return future; + } + + /** + * Executes a command and returns a single {@code Integer} property from the first response map. + * + * @param command + * the command to execute + * @param property + * the name of the property to return + * + * @return the value of the specified {@code Integer} property + */ + private CommandFuture executeAndReturnIntProperty(Command command, String property) { + CommandFuture future = command.getFuture() + .map(result -> result.getFirstResponse().getInt(property)); + + query.doCommandAsync(command); + return future; + } + + private CommandFuture executeAndReturnIntArray(Command command, String property) { + CommandFuture future = command.getFuture() + .map(result -> { + List responses = result.getResponses(); + int[] values = new int[responses.size()]; + int i = 0; + + for (Wrapper response : responses) { + values[i++] = response.getInt(property); + } + return values; + }); + + query.doCommandAsync(command); + return future; + } + + /** + * Executes a command, checks for failure and transforms the first + * response map by invoking {@code fn}. + * + * @param command + * the command to execute + * @param fn + * the function that creates a new wrapper of type {@code T} + * @param + * the wrapper class the map should be wrapped with + * + * @return a future of a {@code T} wrapper of the first response map + */ + private CommandFuture executeAndTransformFirst(Command command, Function, T> fn) { + return executeAndMapFirst(command, wrapper -> fn.apply(wrapper.getMap())); + } + + /** + * Executes a command, checks for failure and maps the first + * response wrapper by using {@code fn}. + * + * @param command + * the command to execute + * @param fn + * a mapping function from {@code Wrapper} to {@code T} + * @param + * the result type of the mapping function {@code fn} + * + * @return a future of a {@code T} + */ + private CommandFuture executeAndMapFirst(Command command, Function fn) { + CommandFuture future = command.getFuture() + .map(result -> fn.apply(result.getFirstResponse())); + + query.doCommandAsync(command); + return future; + } + + /** + * Executes a command, checks for failure and transforms all + * response maps to a wrapper by invoking {@code fn} on each map. + * + * @param command + * the command to execute + * @param fn + * the function that creates the new wrappers of type {@code T} + * @param + * the wrapper class the maps should be wrapped with + * + * @return a future of a list of wrapped response maps + */ + private CommandFuture> executeAndTransform(Command command, Function, T> fn) { + return executeAndMap(command, wrapper -> fn.apply(wrapper.getMap())); + } + + /** + * Executes a command, checks for failure and maps all response + * wrappers by using {@code fn}. + * + * @param command + * the command to execute + * @param fn + * a mapping function from {@code Wrapper} to {@code T} + * @param + * the result type of the mapping function {@code fn} + * + * @return a future of a list of {@code T} + */ + private CommandFuture> executeAndMap(Command command, Function fn) { + CommandFuture> future = command.getFuture() + .map(result -> { + List response = result.getResponses(); + List transformed = new ArrayList<>(response.size()); + for (Wrapper wrapper : response) { + transformed.add(fn.apply(wrapper)); + } + + return transformed; + }); + + query.doCommandAsync(command); + return future; + } + + /** + * Computes a sub-list of the list of values produced by {@code valuesFuture} where + * each value matches a key in the list of keys produced by {@code keysFuture}. + *

+ * The returned future succeeds if {@code keysFuture} and {@code valuesFuture} succeed and + * fails if {@code keysFuture} or {@code valuesFuture} fails. + *

+ * {@code null} keys, {@code null} values, and keys without a matching value are ignored. + * If multiple values map to the same key, only the first value is used. + *

+ * The order of values in the resulting list follows the order of matching keys, + * not the order of the original value list. + *

+ * + * @param keysFuture + * the future producing a list of keys of type {@code K} + * @param valuesFuture + * the future producing a list of values of type {@code V} + * @param keyMapper + * a function extracting keys from the value type + * @param + * the key type + * @param + * the value type + * + * @return a future of a list of values of type {@code V} + */ + private static CommandFuture> findByKey(CommandFuture> keysFuture, CommandFuture> valuesFuture, + Function keyMapper) { + CommandFuture> future = new CommandFuture<>(); + + keysFuture.onSuccess(keys -> + valuesFuture.onSuccess(values -> { + Map valueMap = values.stream().collect(Collectors.toMap(keyMapper, Function.identity(), (l, r) -> l)); + List foundValues = new ArrayList<>(keys.size()); + + for (K key : keys) { + if (key == null) continue; + V value = valueMap.get(key); + if (value == null) continue; + foundValues.add(value); + } + + future.set(foundValues); + }).forwardFailure(future) + ).forwardFailure(future); + + return future; + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/TS3Config.java b/src/com/github/theholywaffle/teamspeak3/TS3Config.java new file mode 100644 index 0000000..df43515 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/TS3Config.java @@ -0,0 +1,139 @@ +package com.github.theholywaffle.teamspeak3; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.TS3Query.FloodRate; +import com.github.theholywaffle.teamspeak3.api.reconnect.ConnectionHandler; +import com.github.theholywaffle.teamspeak3.api.reconnect.ReconnectStrategy; + +public class TS3Config { + + private String host = null; + private int queryPort = 10011; + private FloodRate floodRate = FloodRate.DEFAULT; + private boolean enableCommunicationsLogging = false; + private int commandTimeout = 4000; + private ReconnectStrategy reconnectStrategy = ReconnectStrategy.disconnect(); + private ConnectionHandler connectionHandler = null; + + public TS3Config setHost(String host) { + this.host = host; + return this; + } + + String getHost() { + return host; + } + + public TS3Config setQueryPort(int queryPort) { + if (queryPort <= 0 || queryPort > 65535) { + throw new IllegalArgumentException("Port out of range: " + queryPort); + } + this.queryPort = queryPort; + return this; + } + + int getQueryPort() { + return queryPort; + } + + public TS3Config setFloodRate(FloodRate rate) { + if (rate == null) throw new IllegalArgumentException("rate cannot be null!"); + this.floodRate = rate; + return this; + } + + FloodRate getFloodRate() { + return floodRate; + } + + /** + * Setting this value to {@code true} will log the communication between the + * query client and the TS3 server at the {@code DEBUG} level. + *

+ * By default, this is turned off to prevent leaking IPs, tokens, passwords, etc. + * into the console and / or log files. + *

+ * + * @param enable + * whether to log query commands + * + * @return this TS3Config object for chaining + */ + public TS3Config setEnableCommunicationsLogging(boolean enable) { + enableCommunicationsLogging = enable; + return this; + } + + boolean getEnableCommunicationsLogging() { + return enableCommunicationsLogging; + } + + /** + * Sets how many milliseconds a call in {@link TS3Api} should block at most until a command + * without response fails. By default, this timeout is 4000 milliseconds. + * + * @param commandTimeout + * the maximum time to wait for a response until a synchronous command call fails + * + * @return this TS3Config object for chaining + * + * @throws IllegalArgumentException + * if the timeout value is smaller than or equal to {@code 0} + */ + public TS3Config setCommandTimeout(int commandTimeout) { + if (commandTimeout <= 0) { + throw new IllegalArgumentException("Timeout value must be greater than 0"); + } + + this.commandTimeout = commandTimeout; + return this; + } + + int getCommandTimeout() { + return commandTimeout; + } + + public TS3Config setReconnectStrategy(ReconnectStrategy reconnectStrategy) { + if (reconnectStrategy == null) throw new IllegalArgumentException("reconnectStrategy cannot be null!"); + this.reconnectStrategy = reconnectStrategy; + return this; + } + + ReconnectStrategy getReconnectStrategy() { + return reconnectStrategy; + } + + public TS3Config setConnectionHandler(ConnectionHandler connectionHandler) { + this.connectionHandler = connectionHandler; + return this; + } + + ConnectionHandler getConnectionHandler() { + return connectionHandler; + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/TS3Query.java b/src/com/github/theholywaffle/teamspeak3/TS3Query.java new file mode 100644 index 0000000..9fda730 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/TS3Query.java @@ -0,0 +1,296 @@ +package com.github.theholywaffle.teamspeak3; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.exception.TS3Exception; +import com.github.theholywaffle.teamspeak3.api.exception.TS3QueryShutDownException; +import com.github.theholywaffle.teamspeak3.api.reconnect.ConnectionHandler; +import com.github.theholywaffle.teamspeak3.api.reconnect.ReconnectStrategy; +import com.github.theholywaffle.teamspeak3.commands.Command; +import net.md_5.bungee.api.ProxyServer; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class TS3Query { + + private static final Logger log = ProxyServer.getInstance().getLogger(); + + public static class FloodRate { + + public static final FloodRate DEFAULT = new FloodRate(350); + public static final FloodRate UNLIMITED = new FloodRate(0); + + public static FloodRate custom(int milliseconds) { + if (milliseconds < 0) throw new IllegalArgumentException("Timeout must be positive"); + return new FloodRate(milliseconds); + } + + private final int ms; + + private FloodRate(int ms) { + this.ms = ms; + } + + public int getMs() { + return ms; + } + } + + private final ConnectionHandler connectionHandler; + private final EventManager eventManager; + private final ExecutorService userThreadPool; + private final FileTransferHelper fileTransferHelper; + private final TS3Api api; + private final TS3ApiAsync asyncApi; + private final TS3Config config; + + private final AtomicBoolean connected = new AtomicBoolean(false); + private final AtomicBoolean shuttingDown = new AtomicBoolean(false); + + private QueryIO io; + + /** + * Creates a TS3Query that connects to a TS3 server at + * {@code localhost:10011} using default settings. + */ + public TS3Query() { + this(new TS3Config()); + } + + /** + * Creates a customized TS3Query that connects to a server + * specified by {@code config}. + * + * @param config + * configuration for this TS3Query + */ + public TS3Query(TS3Config config) { + this.config = config; + this.eventManager = new EventManager(this); + this.userThreadPool = Executors.newCachedThreadPool(); + this.fileTransferHelper = new FileTransferHelper(config.getHost()); + this.connectionHandler = config.getReconnectStrategy().create(config.getConnectionHandler()); + + this.asyncApi = new TS3ApiAsync(this); + this.api = new TS3Api(asyncApi); + } + + /* + * Copy constructor only used for ReconnectQuery + */ + private TS3Query(TS3Query query) { + this.config = query.config; + this.eventManager = query.eventManager; + this.userThreadPool = query.userThreadPool; + this.fileTransferHelper = query.fileTransferHelper; + this.connectionHandler = null; + + this.asyncApi = new TS3ApiAsync(this); + this.api = new TS3Api(asyncApi); + } + + // PUBLIC + + public void connect() { + synchronized (this) { + if (userThreadPool.isShutdown()) { + throw new IllegalStateException("The query has already been shut down"); + } + } + + QueryIO newIO = new QueryIO(this, config); + + try { + connectionHandler.onConnect(new ReconnectQuery(this, newIO)); + } catch (Exception e) { + log.log(Level.SEVERE, "ConnectionHandler threw exception in connect handler", e); + } + + synchronized (this) { + QueryIO oldIO = io; + io = newIO; + if (oldIO != null) { + oldIO.disconnect(); + newIO.continueFrom(oldIO); + } + connected.set(true); + } + } + + /** + * Removes and closes all used resources to the TeamSpeak server. + */ + public void exit() { + if (shuttingDown.compareAndSet(false, true)) { + try { + // Sending this command will guarantee that all previously sent commands have been processed + api.quit(); + } catch (TS3Exception e) { + log.log(Level.WARNING, "Could not send a quit command to terminate the connection", e); + } finally { + shutDown(); + } + } else { + // Only 1 thread shall ever send quit, the rest of the threads wait for the first thread to finish + try { + synchronized (this) { + while (connected.get()) { + wait(); + } + } + } catch (InterruptedException e) { + // Restore interrupt, then bail out + Thread.currentThread().interrupt(); + } + } + } + + private synchronized void shutDown() { + if (userThreadPool.isShutdown()) return; + + if (io != null) { + io.disconnect(); + io.failRemainingCommands(); + } + + userThreadPool.shutdown(); + connected.set(false); + notifyAll(); + } + + /** + * Returns {@code true} if the query is likely connected, + * {@code false} if the query is disconnected or currently trying to reconnect. + *

+ * Note that the only way to really determine whether the query is connected or not + * is to send a command and check whether it succeeds. + * Thus this method could return {@code true} almost a minute after the connection + * has been lost, when the last keep-alive command was sent. + *

+ * Please do not use this method to write your own connection handler. + * Instead, use the built-in classes in the {@code api.reconnect} package. + *

+ * + * @return whether the query is connected or not + * + * @see TS3Config#setReconnectStrategy(ReconnectStrategy) + * @see TS3Config#setConnectionHandler(ConnectionHandler) + */ + public boolean isConnected() { + return connected.get(); + } + + public TS3Api getApi() { + return api; + } + + public TS3ApiAsync getAsyncApi() { + return asyncApi; + } + + // INTERNAL + + synchronized void doCommandAsync(Command c) { + if (userThreadPool.isShutdown()) { + c.getFuture().fail(new TS3QueryShutDownException()); + return; + } + + io.enqueueCommand(c); + } + + void submitUserTask(final String name, final Runnable task) { + userThreadPool.submit(() -> { + try { + task.run(); + } catch (Throwable throwable) { + log.log(Level.SEVERE, name + " threw an exception", throwable); + } + }); + } + + EventManager getEventManager() { + return eventManager; + } + + FileTransferHelper getFileTransferHelper() { + return fileTransferHelper; + } + + void fireDisconnect() { + connected.set(false); + + submitUserTask("ConnectionHandler disconnect task", this::handleDisconnect); + } + + private void handleDisconnect() { + try { + connectionHandler.onDisconnect(this); + } finally { + synchronized (this) { + if (!connected.get()) { + shuttingDown.set(true); // Try to prevent extraneous exit commands + shutDown(); + } + } + } + } + + private static class ReconnectQuery extends TS3Query { + + private final TS3Query parent; + + private ReconnectQuery(TS3Query query, QueryIO io) { + super(query); + super.io = io; + this.parent = query; + } + + @Override + public void connect() { + throw new UnsupportedOperationException("Can't call connect from onConnect handler"); + } + + @Override + public void exit() { + parent.exit(); + } + + @Override + synchronized void fireDisconnect() { + // If a reconnect query fails, we do not want this to affect the parent query. + // Instead, simply fail all remaining onConnect commands. + QueryIO io = super.io; + io.disconnect(); + io.failRemainingCommands(); + } + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/ChannelProperty.java b/src/com/github/theholywaffle/teamspeak3/api/ChannelProperty.java new file mode 100644 index 0000000..034b8a2 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/ChannelProperty.java @@ -0,0 +1,79 @@ +package com.github.theholywaffle.teamspeak3.api; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Locale; + +public enum ChannelProperty implements Property { + + CHANNEL_CODEC(true), + CHANNEL_CODEC_IS_UNENCRYPTED(true), + CHANNEL_CODEC_LATENCY_FACTOR(false), + CHANNEL_CODEC_QUALITY(true), + CHANNEL_DELETE_DELAY(true), + CHANNEL_DESCRIPTION(true), + CHANNEL_FILEPATH(false), + CHANNEL_FLAG_DEFAULT(true), + CHANNEL_FLAG_MAXCLIENTS_UNLIMITED(true), + CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED(true), + CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED(true), + CHANNEL_FLAG_PASSWORD(false), + CHANNEL_FLAG_PERMANENT(true), + CHANNEL_FLAG_SEMI_PERMANENT(true), + CHANNEL_FLAG_TEMPORARY(true), + CHANNEL_FORCED_SILENCE(false), + CHANNEL_ICON_ID(true), + CHANNEL_MAXCLIENTS(true), + CHANNEL_MAXFAMILYCLIENTS(true), + CHANNEL_NAME(true), + CHANNEL_NAME_PHONETIC(true), + CHANNEL_NEEDED_SUBSCRIBE_POWER(false), + CHANNEL_NEEDED_TALK_POWER(true), + CHANNEL_ORDER(true), + CHANNEL_PASSWORD(true), + CHANNEL_TOPIC(true), + SECONDS_EMPTY(false), + CID(false), + PID(false), + CPID(true); + + private final boolean changeable; + + ChannelProperty(boolean changeable) { + this.changeable = changeable; + } + + @Override + public String getName() { + return name().toLowerCase(Locale.ROOT); + } + + @Override + public boolean isChangeable() { + return changeable; + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/ClientProperty.java b/src/com/github/theholywaffle/teamspeak3/api/ClientProperty.java new file mode 100644 index 0000000..c1fb7cd --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/ClientProperty.java @@ -0,0 +1,118 @@ +package com.github.theholywaffle.teamspeak3.api; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.TS3Api; + +import java.util.Locale; +import java.util.Map; + +public enum ClientProperty implements Property { + + CID(false), + CLIENT_AWAY(false), + CLIENT_AWAY_MESSAGE(false), + /** + * Property for the Overwolf badge and any myTeamSpeak badges. + * Can be changed only for the own client by using {@link TS3Api#updateClient(Map)}. + *

+ * String format: {@code overwolf=n:badges=guid,guid,guid(,guid...)}
+ * where {@code n} is 0 or 1 and {@code guid} is 128-bit badge GUIDs + *

+ */ + CLIENT_BADGES(true), + CLIENT_BASE64HASHCLIENTUID(false), + CLIENT_CHANNEL_GROUP_ID(false), + CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID(false), + CLIENT_COUNTRY(false), + CLIENT_CREATED(false), + CLIENT_DATABASE_ID(false), + CLIENT_DEFAULT_CHANNEL(false), + CLIENT_DEFAULT_TOKEN(false), + CLIENT_DESCRIPTION(true), + CLIENT_FLAG_AVATAR(false), + CLIENT_FLAG_TALKING(false), + CLIENT_ICON_ID(true), + CLIENT_IDLE_TIME(false), + CLIENT_INPUT_HARDWARE(false), + CLIENT_INPUT_MUTED(false), + CLIENT_IS_CHANNEL_COMMANDER(true), + CLIENT_IS_PRIORITY_SPEAKER(false), + CLIENT_IS_RECORDING(false), + CLIENT_IS_TALKER(true), + CLIENT_LASTCONNECTED(false), + CLIENT_LOGIN_NAME(false), + CLIENT_META_DATA(false), + CLIENT_MONTH_BYTES_DOWNLOADED(false), + CLIENT_MONTH_BYTES_UPLOADED(false), + CLIENT_NEEDED_SERVERQUERY_VIEW_POWER(false), + CLIENT_NICKNAME(true), + CLIENT_NICKNAME_PHONETIC(false), + CLIENT_OUTPUT_HARDWARE(false), + CLIENT_OUTPUT_MUTED(false), + CLIENT_OUTPUTONLY_MUTED(false), + CLIENT_PLATFORM(false), + CLIENT_SERVERGROUPS(false), + CLIENT_TALK_POWER(false), + CLIENT_TALK_REQUEST(false), + CLIENT_TALK_REQUEST_MSG(false), + CLIENT_TOTAL_BYTES_DOWNLOADED(false), + CLIENT_TOTAL_BYTES_UPLOADED(false), + CLIENT_TOTALCONNECTIONS(false), + CLIENT_TYPE(false), + CLIENT_UNIQUE_IDENTIFIER(false), + CLIENT_UNREAD_MESSAGES(false), + CLIENT_VERSION(false), + CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_TOTAL(false), + CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_TOTAL(false), + CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_TOTAL(false), + CONNECTION_BANDWIDTH_SENT_LAST_SECOND_TOTAL(false), + CONNECTION_BYTES_RECEIVED_TOTAL(false), + CONNECTION_BYTES_SENT_TOTAL(false), + CONNECTION_CLIENT_IP(false), + CONNECTION_CONNECTED_TIME(false), + CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED(false), + CONNECTION_FILETRANSFER_BANDWIDTH_SENT(false), + CONNECTION_PACKETS_RECEIVED_TOTAL(false), + CONNECTION_PACKETS_SENT_TOTAL(false); + + private final boolean changeable; + + ClientProperty(boolean changeable) { + this.changeable = changeable; + } + + @Override + public String getName() { + return name().toLowerCase(Locale.ROOT); + } + + @Override + public boolean isChangeable() { + return changeable; + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/Codec.java b/src/com/github/theholywaffle/teamspeak3/api/Codec.java new file mode 100644 index 0000000..df8861f --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/Codec.java @@ -0,0 +1,132 @@ +package com.github.theholywaffle.teamspeak3.api; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.TS3Api; + +import java.util.Map; + +/** + * Voice codecs currently used by TeamSpeak3. + *

+ * A channel's codec may be edited by using {@link TS3Api#editChannel(int, Map)}. + * All voice data may also be encrypted. + *

+ */ +public enum Codec { + + /** + * The Speex narrowband codec with a sampling rate of 8kHz. + *

+ * All Speex codecs are basically obsolete as Opus is better + * in every aspect and has a comparable required bandwidth. + *

    + *
  • Bandwidth per client with codec quality 0: 2.49 KiB/s
  • + *
  • Bandwidth per client with codec quality 10: 5.22 KiB/s
  • + *
+ */ + SPEEX_NARROWBAND(0), + + /** + * The Speex wideband codec with a sampling rate of 16kHz. + *

+ * All Speex codecs are basically obsolete as Opus is better + * in every aspect and has a comparable required bandwidth. + *

    + *
  • Bandwidth per client with codec quality 0: 2.69 KiB/s
  • + *
  • Bandwidth per client with codec quality 10: 7.37 KiB/s
  • + *
+ */ + SPEEX_WIDEBAND(1), + + /** + * The Speex ultra-wideband codec with a sampling rate of 32kHz. + *

+ * All Speex codecs are basically obsolete as Opus is better + * in every aspect and has a comparable required bandwidth. + *

    + *
  • Bandwidth per client with codec quality 0: 2.73 KiB/s
  • + *
  • Bandwidth per client with codec quality 10: 7.57 KiB/s
  • + *
+ */ + SPEEX_ULTRAWIDEBAND(2), + + /** + * The CELT Mono codec. It is optimised for music but still generates very good + * results in voice transmission. + *

+ * Note that this codec requires the most bandwidth of all currently available codecs. + *

    + *
  • Bandwidth per client with codec quality 0: 6.10 KiB/s
  • + *
  • Bandwidth per client with codec quality 10: 13.92 KiB/s
  • + *
+ */ + CELT_MONO(3), + + /** + * The Opus codec optimised for voice transmission. + *

+ * This is the default codec used by TeamSpeak and usually achieves the + * best results for voice transmission. + *

    + *
  • Bandwidth per client with codec quality 0: 2.73 KiB/s
  • + *
  • Bandwidth per client with codec quality 10: 7.71 KiB/s
  • + *
+ */ + OPUS_VOICE(4), + + /** + * The Opus codec optimised for music transmission. + *

+ * Please note that if used only to transmit speech, this codec can + * sound worse than Opus Voice despite a higher bandwidth. + *

    + *
  • Bandwidth per client with codec quality 0: 3.08 KiB/s
  • + *
  • Bandwidth per client with codec quality 10: 11.87 KiB/s
  • + *
+ */ + OPUS_MUSIC(5), + + /** + * An unknown codec. + *

+ * If you ever encounter an unknown codec, please tell us! + * We may have to update the API. + *

+ */ + UNKNOWN(-1); + + private final int i; + + Codec(int i) { + this.i = i; + } + + public int getIndex() { + return i; + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/CodecEncryptionMode.java b/src/com/github/theholywaffle/teamspeak3/api/CodecEncryptionMode.java new file mode 100644 index 0000000..e7301d2 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/CodecEncryptionMode.java @@ -0,0 +1,76 @@ +package com.github.theholywaffle.teamspeak3.api; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.TS3Api; + +import java.util.Map; + +/** + * Describes how the virtual server manages audio encryption. + *

+ * Can be edited with {@link TS3Api#editServer(Map)}. + *

+ */ +public enum CodecEncryptionMode { + + /** + * Each channel manages audio encryption on its own. + * + * @see TS3Api#editChannel(int, Map) + */ + CODEC_CRYPT_INDIVIDUAL(0), + + /** + * Audio encryption is globally disabled on this virtual server. + */ + CODEC_CRYPT_DISABLED(1), + + /** + * Audio encryption is globally enabled on this virtual server. + */ + CODEC_CRYPT_ENABLED(2), + + /** + * An unknown codec encryption mode. + *

+ * If you ever encounter an unknown mode, please tell us! + * We may have to update the API. + *

+ */ + UNKNOWN(-1); + + private final int i; + + CodecEncryptionMode(int i) { + this.i = i; + } + + public int getIndex() { + return i; + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/CommandFuture.java b/src/com/github/theholywaffle/teamspeak3/api/CommandFuture.java new file mode 100644 index 0000000..f392f52 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/CommandFuture.java @@ -0,0 +1,891 @@ +package com.github.theholywaffle.teamspeak3.api; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.TS3ApiAsync; +import com.github.theholywaffle.teamspeak3.api.exception.TS3Exception; +import net.md_5.bungee.api.ProxyServer; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.CancellationException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Represents the result of an asynchronous execution of a query command. + *

+ * Basically, this class is a container for a server response which will + * arrive at some time in the future. It also accounts for the possibility + * that a command might fail and that a future might be cancelled by a user. + *

+ * A {@code CommandFuture} can therefore have 4 different states: + *
    + *
  • Waiting - No response from the server has arrived yet
  • + *
  • Cancelled - A user cancelled this future before a response from the server could arrive
  • + *
  • Failed - The server received the command but responded with an error message
  • + *
  • Succeeded - The server successfully processed the command and sent back a result
  • + *
+ * You can check the state of the future using the methods {@link #isDone()}, + * {@link #isSuccessful()}, {@link #hasFailed()} and {@link #isCancelled()}. + *

+ * A {@code CommandFuture}'s value can be retrieved by calling {@link #get()} + * or {@link #get(long, TimeUnit)}, which block the current thread until the + * server response arrives. The method with a timeout should be preferred + * as there's no guarantee that a proper response (or an error message) + * will ever arrive, e.g. in case of a permanent disconnect. + * There are also variations of these methods which ignore thread interrupts, + * {@link #getUninterruptibly()} and {@link #getUninterruptibly(long, TimeUnit)}. + *

+ * Note that these methods all wait for the response to arrive and thereby + * revert to synchronous execution. If you want to handle the server response + * asynchronously, you need to register success and failure listeners. + * These listeners will be called in a separate thread once a response arrives. + *

+ * Each {@code CommandFuture} can only ever have one {@link SuccessListener} and + * one {@link FailureListener} registered. All {@link TS3ApiAsync} methods are + * guaranteed to return a {@code CommandFuture} with no listeners registered. + *

+ * To set the value of a {@code CommandFuture}, the {@link #set(Object)} method is used; + * to notify it of a failure, {@link #fail(TS3Exception)} is used. You usually + * shouldn't call these methods yourself, however. That's the job of the API. + *

+ * {@code CommandFuture}s are thread-safe. All state-changing methods are synchronized. + *

+ * + * @param + * the type of the value + * + * @see TS3ApiAsync + */ +public class CommandFuture implements Future { + + private static final Logger log = ProxyServer.getInstance().getLogger(); + + private enum FutureState { + WAITING, + CANCELLED, + FAILED, + SUCCEEDED + } + + /** + * Just a plain object used for its monitor to synchronize access to the + * critical sections of this future and to signal state changes to any + * threads waiting in {@link #get()} and {@link #getUninterruptibly()} methods. + */ + private final Object monitor = new Object(); + + /** + * The current state of the future. Marked as volatile so {@link #isDone()} + * and similar functions can work without synchronization. + * State transitions and check-then-acts must be guarded by monitor. + */ + private volatile FutureState state = FutureState.WAITING; + + // All guarded by monitor + private V value = null; + private TS3Exception exception = null; + private SuccessListener successListener = null; + private FailureListener failureListener = null; + + /** + * Waits indefinitely until the command completes. + *

+ * If the thread is interrupted while waiting for the command + * to complete, this method will throw an {@code InterruptedException} + * and the thread's interrupt flag will be cleared. + *

+ * Please note that this method is blocking and thus negates + * the advantage of the asynchronous nature of this class. + * Consider using {@link #onSuccess(SuccessListener)} and + * {@link #onFailure(FailureListener)} instead. + *

+ * + * @throws InterruptedException + * if the method is interrupted by calling {@link Thread#interrupt()}. + * The interrupt flag will be cleared + */ + public void await() throws InterruptedException { + synchronized (monitor) { + while (state == FutureState.WAITING) { + monitor.wait(); + } + } + } + + /** + * Waits for at most the given time until the command completes. + *

+ * If the thread is interrupted while waiting for the command + * to complete, this method will throw an {@code InterruptedException} + * and the thread's interrupt flag will be cleared. + *

+ * Please note that this method is blocking and thus negates + * the advantage of the asynchronous nature of this class. + * Consider using {@link #onSuccess(SuccessListener)} and + * {@link #onFailure(FailureListener)} instead. + *

+ * + * @param timeout + * the maximum amount of the given time unit to wait + * @param unit + * the time unit of the timeout argument + * + * @throws InterruptedException + * if the method is interrupted by calling {@link Thread#interrupt()}. + * The interrupt flag will be cleared + * @throws TimeoutException + * if the given time elapsed without the command completing + */ + public void await(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { + synchronized (monitor) { + final long end = System.currentTimeMillis() + unit.toMillis(timeout); + while (state == FutureState.WAITING && System.currentTimeMillis() < end) { + monitor.wait(end - System.currentTimeMillis()); + } + + if (state == FutureState.WAITING) throw new TimeoutException(); + } + } + + /** + * Waits indefinitely until the command completes. + *

+ * If the thread is interrupted while waiting for the command + * to complete, the interrupt is simply ignored and no + * {@link InterruptedException} is thrown. + *

+ * Please note that this method is blocking and thus negates + * the advantage of the asynchronous nature of this class. + * Consider using {@link #onSuccess(SuccessListener)} and + * {@link #onFailure(FailureListener)} instead. + *

+ */ + public void awaitUninterruptibly() { + synchronized (monitor) { + boolean interrupted = false; + while (state == FutureState.WAITING) { + try { + monitor.wait(); + } catch (InterruptedException e) { + interrupted = true; + } + } + + if (interrupted) { + // Restore the interrupt for the caller + Thread.currentThread().interrupt(); + } + } + } + + /** + * Waits for at most the given time until the command completes. + *

+ * If the thread is interrupted while waiting for the command + * to complete, the interrupt is simply ignored and no + * {@link InterruptedException} is thrown. + *

+ * Please note that this method is blocking and thus negates + * the advantage of the asynchronous nature of this class. + * Consider using {@link #onSuccess(SuccessListener)} and + * {@link #onFailure(FailureListener)} instead. + *

+ * + * @param timeout + * the maximum amount of the given time unit to wait + * @param unit + * the time unit of the timeout argument + * + * @throws TimeoutException + * if the given time elapsed without the command completing + */ + public void awaitUninterruptibly(long timeout, TimeUnit unit) throws TimeoutException { + synchronized (monitor) { + final long end = System.currentTimeMillis() + unit.toMillis(timeout); + boolean interrupted = false; + + while (state == FutureState.WAITING && System.currentTimeMillis() < end) { + try { + monitor.wait(end - System.currentTimeMillis()); + } catch (InterruptedException e) { + interrupted = true; + } + } + + if (interrupted) { + // Restore the interrupt for the caller + Thread.currentThread().interrupt(); + } + + if (state == FutureState.WAITING) throw new TimeoutException(); + } + } + + /** + * Waits indefinitely until the command completes + * and returns the result of the command. + *

+ * If the thread is interrupted while waiting for the command + * to complete, this method will throw an {@code InterruptedException} + * and the thread's interrupt flag will be cleared. + *

+ * Please note that this method is blocking and thus negates + * the advantage of the asynchronous nature of this class. + * Consider using {@link #onSuccess(SuccessListener)} and + * {@link #onFailure(FailureListener)} instead. + *

+ * + * @return the server response to the command + * + * @throws InterruptedException + * if the method is interrupted by calling {@link Thread#interrupt()}. + * The interrupt flag will be cleared + * @throws CancellationException + * if the {@code CommandFuture} was cancelled before the command completed + * @throws TS3Exception + * if the command fails + */ + @Override + public V get() throws InterruptedException { + synchronized (monitor) { + await(); + + checkForFailure(); + return value; + } + } + + /** + * Waits for at most the given time until the command completes + * and returns the result of the command. + *

+ * If the thread is interrupted while waiting for the command + * to complete, this method will throw an {@code InterruptedException} + * and the thread's interrupt flag will be cleared. + *

+ * Please note that this method is blocking and thus negates + * the advantage of the asynchronous nature of this class. + * Consider using {@link #onSuccess(SuccessListener)} and + * {@link #onFailure(FailureListener)} instead. + *

+ * + * @param timeout + * the maximum amount of the given time unit to wait + * @param unit + * the time unit of the timeout argument + * + * @return the server response to the command + * + * @throws InterruptedException + * if the method is interrupted by calling {@link Thread#interrupt()}. + * The interrupt flag will be cleared + * @throws TimeoutException + * if the given time elapsed without the command completing + * @throws CancellationException + * if the {@code CommandFuture} was cancelled before the command completed + * @throws TS3Exception + * if the command fails + */ + @Override + public V get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { + synchronized (monitor) { + await(timeout, unit); + + checkForFailure(); + return value; + } + } + + /** + * Waits indefinitely until the command completes + * and returns the result of the command. + *

+ * If the thread is interrupted while waiting for the command + * to complete, the interrupt is simply ignored and no + * {@link InterruptedException} is thrown. + *

+ * Please note that this method is blocking and thus negates + * the advantage of the asynchronous nature of this class. + * Consider using {@link #onSuccess(SuccessListener)} and + * {@link #onFailure(FailureListener)} instead. + *

+ * + * @return the server response to the command + * + * @throws CancellationException + * if the {@code CommandFuture} was cancelled before the command completed + * @throws TS3Exception + * if the command fails + */ + public V getUninterruptibly() { + synchronized (monitor) { + awaitUninterruptibly(); + + checkForFailure(); + return value; + } + } + + /** + * Waits for at most the given time until the command completes + * and returns the result of the command. + *

+ * If the thread is interrupted while waiting for the command + * to complete, the interrupt is simply ignored and no + * {@link InterruptedException} is thrown. + *

+ * Please note that this method is blocking and thus negates + * the advantage of the asynchronous nature of this class. + * Consider using {@link #onSuccess(SuccessListener)} and + * {@link #onFailure(FailureListener)} instead. + *

+ * + * @param timeout + * the maximum amount of the given time unit to wait + * @param unit + * the time unit of the timeout argument + * + * @return the server response to the command + * + * @throws TimeoutException + * if the given time elapsed without the command completing + * @throws CancellationException + * if the {@code CommandFuture} was cancelled before the command completed + * @throws TS3Exception + * if the command fails + */ + public V getUninterruptibly(long timeout, TimeUnit unit) throws TimeoutException { + synchronized (monitor) { + awaitUninterruptibly(timeout, unit); + + checkForFailure(); + return value; + } + } + + /** + * Throws an exception if the future was either cancelled or the command failed. + *

+ * Must be called with the monitor lock held! + *

+ * + * @throws CancellationException + * if the future was cancelled + * @throws TS3Exception + * if the command failed + */ + private void checkForFailure() { + if (state == FutureState.CANCELLED) { + throw new CancellationException(); + } else if (state == FutureState.FAILED) { + // Make the stack trace of the exception point to this method and not + // SocketReader#run -> TS3ApiAsync#hasFailed, which wouldn't be helpful + exception.fillInStackTrace(); + throw exception; + } + } + + @Override + public boolean isDone() { + return state != FutureState.WAITING; + } + + /** + * Returns {@code true} if this command completed successfully, + * i.e. the future wasn't cancelled and the command completed without throwing an exception. + * + * @return {@code true} if the command completed successfully + */ + public boolean isSuccessful() { + return state == FutureState.SUCCEEDED; + } + + @Override + public boolean isCancelled() { + return state == FutureState.CANCELLED; + } + + /** + * Returns {@code true} if the command failed and threw a {@link TS3Exception}. + * + * @return {@code true} if the command failed + */ + public boolean hasFailed() { + return state == FutureState.FAILED; + } + + /** + * Sets the value of this future. This will mark the future as successful. + *

+ * Furthermore, this will run the {@link SuccessListener}, if one is registered. + * All exceptions thrown from the body of the {@code SuccessListener} are caught + * so no exceptions can leak into user code. + *

+ * Note that a future's value can only be set once. Subsequent calls to + * this method will be ignored. + *

+ * + * @param value + * the value to set this future to + * + * @return {@code true} if the command was marked as successful + */ + public boolean set(V value) { + SuccessListener listener; + + synchronized (monitor) { + if (isDone()) return false; // Ignore + + this.state = FutureState.SUCCEEDED; + this.value = value; + listener = successListener; + monitor.notifyAll(); + } + + if (listener != null) { + try { + listener.handleSuccess(value); + } catch (Exception e) { + // Whatever happens, we do not want a user error to leak into our logic + log.log(Level.SEVERE, "SuccessListener threw an exception", e); + } + } + return true; + } + + /** + * Notifies this future that the command has failed. + *

+ * Furthermore, this will run the {@link FailureListener}, if one is registered. + * All exceptions thrown from the body of the {@code FailureListener} are caught + * so no exceptions can leak into user code. + *

+ * Note that a future can only fail once. Subsequent calls to this method will be ignored. + *

+ * + * @param exception + * the exception that occurred while executing this command + * + * @return {@code true} if the command was marked as failed + */ + public boolean fail(TS3Exception exception) { + FailureListener listener; + + synchronized (monitor) { + if (isDone()) return false; // Ignore + + this.state = FutureState.FAILED; + this.exception = exception; + listener = failureListener; + monitor.notifyAll(); + } + + if (listener != null) { + try { + listener.handleFailure(exception); + } catch (Exception e) { + // Whatever happens, we do not want a user error to leak into our logic + log.log(Level.SEVERE, "FailureListener threw an exception", e); + } + } + return true; + } + + /** + * {@inheritDoc} + *

+ * Cancelling a {@code CommandFuture} will not actually cancel the + * execution of the command which was sent to the TeamSpeak server. + *

+ * It will, however, prevent the {@link SuccessListener} and the + * {@link FailureListener} from firing, provided a response from the + * server has not yet arrived. + *

+ */ + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + synchronized (monitor) { + if (isDone()) return false; // Ignore + + this.state = FutureState.CANCELLED; + monitor.notifyAll(); + } + + return true; + } + + /** + * Sets a {@link SuccessListener} which will be notified when this future + * succeeded and a value has been set. + *

+ * If this future has already succeeded, this method will immediately call + * the listener method, which will be executed synchronously. + *

+ * + * @param listener + * the listener to notify of a success + * + * @return this object for chaining + */ + public CommandFuture onSuccess(SuccessListener listener) { + boolean runSuccessListener; + V successValue; + + synchronized (monitor) { + if (successListener != null) { + throw new IllegalStateException("Listener already set"); + } + successListener = listener; + + runSuccessListener = isSuccessful(); + successValue = value; + } + + if (runSuccessListener) { + listener.handleSuccess(successValue); + } + + return this; + } + + /** + * Sets a {@link FailureListener} which will be notified when this future + * fails because of a error returned by the TeamSpeak server. + *

+ * If this future has already failed, this method will immediately call + * the listener method, which will be executed synchronously. + *

+ * + * @param listener + * the listener to notify of a failure + * + * @return this object for chaining + */ + public CommandFuture onFailure(FailureListener listener) { + boolean runFailureListener; + TS3Exception failureException; + + synchronized (monitor) { + if (failureListener != null) { + throw new IllegalStateException("Listener already set"); + } + failureListener = listener; + + runFailureListener = hasFailed(); + failureException = exception; + } + + if (runFailureListener) { + listener.handleFailure(failureException); + } + + return this; + } + + /** + * Forwards a success to another future by calling {@link #set(Object)} on + * that future with the value this future was set to. + *

+ * This will register a {@link SuccessListener}, meaning that you will not + * be able to register another {@code SuccessListener}. + *

+ * + * @param otherFuture + * the future to forward a success to + * + * @return this object for chaining + */ + public CommandFuture forwardSuccess(final CommandFuture otherFuture) { + return onSuccess(otherFuture::set); + } + + /** + * Forwards a failure to another future by calling {@link #fail(TS3Exception)} + * on that future with the error that caused this future to fail. + *

+ * This will register a {@link FailureListener}, meaning that you will not + * be able to register another {@code FailureListener}. + *

+ * + * @param otherFuture + * the future to forward a failure to + * + * @return this object for chaining + */ + public CommandFuture forwardFailure(final CommandFuture otherFuture) { + return onFailure(otherFuture::fail); + } + + /** + * Forwards both a success as well as a failure to another {@code CommandFuture}. + * This method just calls both {@link #forwardSuccess(CommandFuture)} and + * {@link #forwardFailure(CommandFuture)}. + *

+ * This will set both a {@link SuccessListener} as well as a {@link FailureListener}, + * so no other listeners can be registered. + *

+ * + * @param otherFuture + * the future which should be notified about + */ + public void forwardResult(final CommandFuture otherFuture) { + forwardSuccess(otherFuture).forwardFailure(otherFuture); + } + + /** + * Creates a new {@code CommandFuture} that succeeds with {@code fn(result)} + * if the original future succeeded with a value {@code result}, and fails + * if the original future failed or if the mapping function {@code fn} threw + * an exception. + * + * @param fn + * a function that maps the result value of type {@code V} to a value of type {@code F} + * @param + * the result type of {@code fn} + * + * @return a new {@code CommandFuture} that will hold the return value of {@code fn} + */ + public CommandFuture map(Function fn) { + CommandFuture target = new CommandFuture<>(); + onSuccess(result -> { + F output; + try { + output = fn.apply(result); + } catch (Exception ex) { + target.fail(new TS3Exception("CommandFuture 'map' function threw an exception", ex)); + return; + } + target.set(output); + }).forwardFailure(target); + return target; + } + + /** + * Creates a new {@code CommandFuture} that succeeds with the result value of + * the {@code CommandFuture} returned by {@code fn} if both the original future + * and the future returned by {@code fn} succeed. + *

+ * The created {@code CommandFuture} fails if the original future failed, + * the future returned by {@code fn} fails, or if {@code fn} throws an exception. + *

+ * If {@code fn} returns {@code null}, the created {@code CommandFuture} + * will immediately succeed with a value of {@code null}. To create this effect + * with non-null values, return an {@link #immediate(Object)} future instead. + *

+ * + * @param fn + * a function that maps the result value of type {@code V} to a {@code CommandFuture} + * @param + * the result type of the future returned by {@code fn} + * + * @return a new {@code CommandFuture} that will hold the result of the future returned by {@code fn} + */ + public CommandFuture then(Function> fn) { + CommandFuture target = new CommandFuture<>(); + onSuccess(result -> { + CommandFuture nextFuture; + try { + nextFuture = fn.apply(result); + } catch (Exception ex) { + target.fail(new TS3Exception("CommandFuture 'then' function threw an exception", ex)); + return; + } + + if (nextFuture == null) { + target.set(null); // Propagate null shortcut + } else { + nextFuture.forwardResult(target); + } + }).forwardFailure(target); + return target; + } + + /** + * Returns a new {@code CommandFuture} that already has a value set. + * + * @param value + * the default value for the new {@code CommandFuture} + * @param + * the dynamic type of the value, will usually be inferred + * + * @return a new {@code CommandFuture} with a default value + */ + public static CommandFuture immediate(V value) { + final CommandFuture future = new CommandFuture<>(); + future.set(value); + return future; + } + + /** + * Combines multiple {@code CommandFuture}s into a single future, which will + * succeed if all futures succeed and fail as soon as one future fails. + * + * @param futures + * the futures to combine + * @param + * the common return type of the futures + * + * @return a future which succeeds if all supplied futures succeed + */ + @SafeVarargs + public static CommandFuture> ofAll(CommandFuture... futures) { + return ofAll(Arrays.asList(futures)); + } + + /** + * Combines a collection of {@code CommandFuture}s into a single future, which will + * succeed if all futures succeed and fail as soon as one future fails. + * + * @param futures + * the futures to combine + * @param + * the common return type of the futures + * + * @return a future which succeeds if all supplied futures succeed + */ + public static CommandFuture> ofAll(final Collection> futures) { + if (futures.isEmpty()) throw new IllegalArgumentException("Requires at least 1 future"); + + @SuppressWarnings("unchecked") final F[] results = (F[]) new Object[futures.size()]; + final AtomicInteger successCounter = new AtomicInteger(futures.size()); + final CommandFuture> combined = new CommandFuture<>(); + + final Iterator> iterator = futures.iterator(); + for (int i = 0; iterator.hasNext(); ++i) { + final int index = i; + final CommandFuture future = iterator.next(); + + future.forwardFailure(combined).onSuccess(result -> { + results[index] = result; + + if (successCounter.decrementAndGet() == 0) { + combined.set(Arrays.asList(results)); + } + }); + } + + return combined; + } + + /** + * Combines multiple {@code CommandFuture}s into a single future, which will + * succeed if any of the futures succeeds and fail if all of the futures fail. + * + * @param futures + * the futures to combine + * @param + * the common return type of the futures + * + * @return a future which succeeds if one of the supplied futures succeeds + */ + @SafeVarargs + public static CommandFuture ofAny(CommandFuture... futures) { + return ofAny(Arrays.asList(futures)); + } + + /** + * Combines a collection of {@code CommandFuture}s into a single future, which will + * succeed as soon as one of the futures succeeds and fail if all futures fail. + * + * @param futures + * the futures to combine + * @param + * the common return type of the futures + * + * @return a future which succeeds if one of the supplied futures succeeds + */ + public static CommandFuture ofAny(final Collection> futures) { + if (futures.isEmpty()) throw new IllegalArgumentException("Requires at least 1 future"); + + final CommandFuture any = new CommandFuture<>(); + final AtomicInteger failureCounter = new AtomicInteger(futures.size()); + + for (CommandFuture future : futures) { + future.forwardSuccess(any).onFailure(exception -> { + if (failureCounter.decrementAndGet() == 0) { + any.fail(exception); + } + }); + } + + return any; + } + + /** + * A listener which will be notified if the {@link CommandFuture} succeeded. + * In that case, {@link #handleSuccess(Object)} will be called with the value + * the future has been set to. + *

+ * A {@code CommandFuture}'s {@code SuccessListener} can be set by calling + * {@link #onSuccess(SuccessListener)}. + *

+ * + * @param + * the type of the value + */ + @FunctionalInterface + public interface SuccessListener { + + /** + * The method to be executed when the command succeeds. + * + * @param result + * the result of the command + */ + void handleSuccess(V result); + } + + /** + * A listener which will be notified if the {@link CommandFuture} failed. + * In that case, {@link #handleFailure(TS3Exception)} will be called with + * the exception that occurred while executing this command. + *

+ * A {@code CommandFuture}'s {@code FailureListener} can be set by calling + * {@link #onFailure(FailureListener)}. + *

+ */ + @FunctionalInterface + public interface FailureListener { + + /** + * The method to be executed when the command failed. + * + * @param exception + * the exception that occurred while executing this command + */ + void handleFailure(TS3Exception exception); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/HostBannerMode.java b/src/com/github/theholywaffle/teamspeak3/api/HostBannerMode.java new file mode 100644 index 0000000..f353960 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/HostBannerMode.java @@ -0,0 +1,45 @@ +package com.github.theholywaffle.teamspeak3.api; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public enum HostBannerMode { + NO_ADJUST(0), + IGNORE_ASPECT(1), + KEEP_ASPECT(2), + UNKNOWN(-1); + + private final int i; + + HostBannerMode(int i) { + this.i = i; + } + + public int getIndex() { + return i; + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/HostMessageMode.java b/src/com/github/theholywaffle/teamspeak3/api/HostMessageMode.java new file mode 100644 index 0000000..493ab0c --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/HostMessageMode.java @@ -0,0 +1,46 @@ +package com.github.theholywaffle.teamspeak3.api; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public enum HostMessageMode { + + LOG(1), + MODAL(2), + MODAL_QUIT(3), + UNKNOWN(-1); + + private final int i; + + HostMessageMode(int i) { + this.i = i; + } + + public int getIndex() { + return i; + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/LogLevel.java b/src/com/github/theholywaffle/teamspeak3/api/LogLevel.java new file mode 100644 index 0000000..e92f03b --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/LogLevel.java @@ -0,0 +1,46 @@ +package com.github.theholywaffle.teamspeak3.api; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public enum LogLevel { + + ERROR(1), + WARNING(2), + DEBUG(3), + INFO(4); + + private final int i; + + LogLevel(int i) { + this.i = i; + } + + public int getIndex() { + return i; + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/PermissionGroupDatabaseType.java b/src/com/github/theholywaffle/teamspeak3/api/PermissionGroupDatabaseType.java new file mode 100644 index 0000000..62f1eba --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/PermissionGroupDatabaseType.java @@ -0,0 +1,45 @@ +package com.github.theholywaffle.teamspeak3.api; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public enum PermissionGroupDatabaseType { + + TEMPLATE(0), + REGULAR(1), + QUERY(2); + + private final int i; + + PermissionGroupDatabaseType(int i) { + this.i = i; + } + + public int getIndex() { + return i; + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/PermissionGroupType.java b/src/com/github/theholywaffle/teamspeak3/api/PermissionGroupType.java new file mode 100644 index 0000000..a7d8caf --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/PermissionGroupType.java @@ -0,0 +1,78 @@ +package com.github.theholywaffle.teamspeak3.api; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.wrapper.PermissionAssignment; + +/** + * An enum describing where a {@link PermissionAssignment} originates from. + */ +public enum PermissionGroupType { + + /** + * A permission assigned to a server group. + */ + SERVER_GROUP(0), + + /** + * A permission assigned to a client on a server-wide level. + */ + GLOBAL_CLIENT(1), + + /** + * A permission requirement for certain actions in a channel. + *

+ * Examples: Join, talk, subscribe and file upload permission requirements + *

+ */ + CHANNEL(2), + + /** + * A permission assigned to a channel group. + */ + CHANNEL_GROUP(3), + + /** + * A permission assigned to a client in a specific channel. + *

+ * This is mostly used for the priority speaker feature by granting + * {@code b_client_is_priority_speaker} to a client in a channel. + *

+ */ + CHANNEL_CLIENT(4); + + private final int i; + + PermissionGroupType(int i) { + this.i = i; + } + + public int getIndex() { + return i; + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/PrivilegeKeyType.java b/src/com/github/theholywaffle/teamspeak3/api/PrivilegeKeyType.java new file mode 100644 index 0000000..363596f --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/PrivilegeKeyType.java @@ -0,0 +1,44 @@ +package com.github.theholywaffle.teamspeak3.api; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public enum PrivilegeKeyType { + + SERVER_GROUP(0), + CHANNEL_GROUP(1); + + private final int i; + + PrivilegeKeyType(int i) { + this.i = i; + } + + public int getIndex() { + return i; + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/Property.java b/src/com/github/theholywaffle/teamspeak3/api/Property.java new file mode 100644 index 0000000..141e415 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/Property.java @@ -0,0 +1,34 @@ +package com.github.theholywaffle.teamspeak3.api; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public interface Property { + + String getName(); + + boolean isChangeable(); +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/ReasonIdentifier.java b/src/com/github/theholywaffle/teamspeak3/api/ReasonIdentifier.java new file mode 100644 index 0000000..d4a79b8 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/ReasonIdentifier.java @@ -0,0 +1,44 @@ +package com.github.theholywaffle.teamspeak3.api; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public enum ReasonIdentifier { + + REASON_KICK_CHANNEL(4), + REASON_KICK_SERVER(5); + + private final int i; + + ReasonIdentifier(int i) { + this.i = i; + } + + public int getIndex() { + return i; + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/ServerGroupType.java b/src/com/github/theholywaffle/teamspeak3/api/ServerGroupType.java new file mode 100644 index 0000000..bcb0610 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/ServerGroupType.java @@ -0,0 +1,51 @@ +package com.github.theholywaffle.teamspeak3.api; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public enum ServerGroupType { + + CHANNEL_GUEST(10), + SERVER_GUEST(15), + QUERY_GUEST(20), + CHANNEL_VOICE(25), + SERVER_NORMAL(30), + CHANNEL_OPERATOR(35), + CHANNEL_ADMIN(40), + SERVER_ADMIN(45), + QUERY_ADMIN(50); + + private final int i; + + ServerGroupType(int i) { + this.i = i; + } + + public int getIndex() { + return i; + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/ServerInstanceProperty.java b/src/com/github/theholywaffle/teamspeak3/api/ServerInstanceProperty.java new file mode 100644 index 0000000..63f4225 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/ServerInstanceProperty.java @@ -0,0 +1,80 @@ +package com.github.theholywaffle.teamspeak3.api; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Locale; + +public enum ServerInstanceProperty implements Property { + + CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_TOTAL(false), + CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_TOTAL(false), + CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_TOTAL(false), + CONNECTION_BANDWIDTH_SENT_LAST_SECOND_TOTAL(false), + CONNECTION_BYTES_RECEIVED_TOTAL(false), + CONNECTION_BYTES_SENT_TOTAL(false), + CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED(false), + CONNECTION_FILETRANSFER_BANDWIDTH_SENT(false), + CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL(false), + CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL(false), + CONNECTION_PACKETS_RECEIVED_TOTAL(false), + CONNECTION_PACKETS_SENT_TOTAL(false), + HOST_TIMESTAMP_UTC(false), + INSTANCE_UPTIME(false), + SERVERINSTANCE_DATABASE_VERSION(false), + SERVERINSTANCE_FILETRANSFER_PORT(true), + SERVERINSTANCE_GUEST_SERVERQUERY_GROUP(true), + SERVERINSTANCE_MAX_DOWNLOAD_TOTAL_BANDWIDTH(true), + SERVERINSTANCE_MAX_UPLOAD_TOTAL_BANDWIDTH(true), + SERVERINSTANCE_PERMISSIONS_VERSION(false), + SERVERINSTANCE_SERVERQUERY_BAN_TIME(true), + SERVERINSTANCE_SERVERQUERY_FLOOD_COMMANDS(true), + SERVERINSTANCE_SERVERQUERY_FLOOD_TIME(true), + SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP(true), + SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP(true), + SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP(true), + SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP(true), + VIRTUALSERVERS_RUNNING_TOTAL(false), + VIRTUALSERVERS_TOTAL_CHANNELS_ONLINE(false), + VIRTUALSERVERS_TOTAL_CLIENTS_ONLINE(false), + VIRTUALSERVERS_TOTAL_MAXCLIENTS(false); + + private final boolean changeable; + + ServerInstanceProperty(boolean changeable) { + this.changeable = changeable; + } + + @Override + public String getName() { + return name().toLowerCase(Locale.ROOT); + } + + @Override + public boolean isChangeable() { + return changeable; + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/Snapshot.java b/src/com/github/theholywaffle/teamspeak3/api/Snapshot.java new file mode 100644 index 0000000..3d74ed0 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/Snapshot.java @@ -0,0 +1,41 @@ +package com.github.theholywaffle.teamspeak3.api; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public class Snapshot { + + private final String snapshot; + + public Snapshot(String snapshot) { + this.snapshot = snapshot; + } + + public String get() { + return snapshot; + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/TextMessageTargetMode.java b/src/com/github/theholywaffle/teamspeak3/api/TextMessageTargetMode.java new file mode 100644 index 0000000..f131353 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/TextMessageTargetMode.java @@ -0,0 +1,45 @@ +package com.github.theholywaffle.teamspeak3.api; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public enum TextMessageTargetMode { + + CLIENT(1), + CHANNEL(2), + SERVER(3); + + private final int i; + + TextMessageTargetMode(int i) { + this.i = i; + } + + public int getIndex() { + return i; + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/VirtualServerProperty.java b/src/com/github/theholywaffle/teamspeak3/api/VirtualServerProperty.java new file mode 100644 index 0000000..269b51b --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/VirtualServerProperty.java @@ -0,0 +1,142 @@ +package com.github.theholywaffle.teamspeak3.api; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Locale; + +public enum VirtualServerProperty implements Property { + + CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_TOTAL(false), + CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_TOTAL(false), + CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_TOTAL(false), + CONNECTION_BANDWIDTH_SENT_LAST_SECOND_TOTAL(false), + CONNECTION_BYTES_RECEIVED_CONTROL(false), + CONNECTION_BYTES_RECEIVED_KEEPALIVE(false), + CONNECTION_BYTES_RECEIVED_SPEECH(false), + CONNECTION_BYTES_RECEIVED_TOTAL(false), + CONNECTION_BYTES_SENT_CONTROL(false), + CONNECTION_BYTES_SENT_KEEPALIVE(false), + CONNECTION_BYTES_SENT_SPEECH(false), + CONNECTION_BYTES_SENT_TOTAL(false), + CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED(false), + CONNECTION_FILETRANSFER_BANDWIDTH_SENT(false), + CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL(false), + CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL(false), + CONNECTION_PACKETS_RECEIVED_CONTROL(false), + CONNECTION_PACKETS_RECEIVED_KEEPALIVE(false), + CONNECTION_PACKETS_RECEIVED_SPEECH(false), + CONNECTION_PACKETS_RECEIVED_TOTAL(false), + CONNECTION_PACKETS_SENT_CONTROL(false), + CONNECTION_PACKETS_SENT_KEEPALIVE(false), + CONNECTION_PACKETS_SENT_SPEECH(false), + CONNECTION_PACKETS_SENT_TOTAL(false), + VIRTUALSERVER_ANTIFLOOD_POINTS_NEEDED_COMMAND_BLOCK(true), + VIRTUALSERVER_ANTIFLOOD_POINTS_NEEDED_IP_BLOCK(true), + VIRTUALSERVER_ANTIFLOOD_POINTS_TICK_REDUCE(true), + VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY(false), + VIRTUALSERVER_AUTOSTART(true), + VIRTUALSERVER_CHANNELSONLINE(false), + VIRTUALSERVER_CLIENT_CONNECTIONS(false), + VIRTUALSERVER_CLIENTSONLINE(false), + VIRTUALSERVER_CODEC_ENCRYPTION_MODE(true), + VIRTUALSERVER_COMPLAIN_AUTOBAN_COUNT(true), + VIRTUALSERVER_COMPLAIN_AUTOBAN_TIME(true), + VIRTUALSERVER_COMPLAIN_REMOVE_TIME(true), + VIRTUALSERVER_CREATED(false), + VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP(true), + VIRTUALSERVER_DEFAULT_CHANNEL_GROUP(true), + VIRTUALSERVER_DEFAULT_SERVER_GROUP(true), + VIRTUALSERVER_DOWNLOAD_QUOTA(true), + VIRTUALSERVER_FILEBASE(false), + VIRTUALSERVER_FLAG_PASSWORD(false), + VIRTUALSERVER_HOSTBANNER_GFX_INTERVAL(true), + VIRTUALSERVER_HOSTBANNER_GFX_URL(true), + VIRTUALSERVER_HOSTBANNER_MODE(true), + VIRTUALSERVER_HOSTBANNER_URL(true), + VIRTUALSERVER_HOSTBUTTON_GFX_URL(true), + VIRTUALSERVER_HOSTBUTTON_TOOLTIP(true), + VIRTUALSERVER_HOSTBUTTON_URL(true), + VIRTUALSERVER_HOSTMESSAGE(true), + VIRTUALSERVER_HOSTMESSAGE_MODE(true), + VIRTUALSERVER_ICON_ID(true), + VIRTUALSERVER_ID(false), + VIRTUALSERVER_IP(false), + VIRTUALSERVER_LOG_CHANNEL(true), + VIRTUALSERVER_LOG_CLIENT(true), + VIRTUALSERVER_LOG_FILETRANSFER(true), + VIRTUALSERVER_LOG_PERMISSIONS(true), + VIRTUALSERVER_LOG_QUERY(true), + VIRTUALSERVER_LOG_SERVER(true), + VIRTUALSERVER_MACHINE_ID(true), + VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH(true), + VIRTUALSERVER_MAX_UPLOAD_TOTAL_BANDWIDTH(true), + VIRTUALSERVER_MAXCLIENTS(true), + VIRTUALSERVER_MIN_CLIENT_VERSION(true), + VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE(true), + VIRTUALSERVER_MONTH_BYTES_DOWNLOADED(false), + VIRTUALSERVER_MONTH_BYTES_UPLOADED(false), + VIRTUALSERVER_NAME(true), + VIRTUALSERVER_NAME_PHONETIC(true), + VIRTUALSERVER_NEEDED_IDENTITY_SECURITY_LEVEL(true), + VIRTUALSERVER_PASSWORD(true), + VIRTUALSERVER_PLATFORM(false), + VIRTUALSERVER_PORT(true), + VIRTUALSERVER_PRIORITY_SPEAKER_DIMM_MODIFICATOR(true), + VIRTUALSERVER_QUERY_CLIENT_CONNECTIONS(false), + VIRTUALSERVER_QUERYCLIENTSONLINE(false), + VIRTUALSERVER_RESERVED_SLOTS(true), + VIRTUALSERVER_STATUS(true), + VIRTUALSERVER_TOTAL_BYTES_DOWNLOADED(false), + VIRTUALSERVER_TOTAL_BYTES_UPLOADED(false), + VIRTUALSERVER_TOTAL_PACKETLOSS_CONTROL(false), + VIRTUALSERVER_TOTAL_PACKETLOSS_KEEPALIVE(false), + VIRTUALSERVER_TOTAL_PACKETLOSS_SPEECH(false), + VIRTUALSERVER_TOTAL_PACKETLOSS_TOTAL(false), + VIRTUALSERVER_TOTAL_PING(false), + VIRTUALSERVER_UNIQUE_IDENTIFIER(false), + VIRTUALSERVER_UPLOAD_QUOTA(true), + VIRTUALSERVER_UPTIME(false), + VIRTUALSERVER_VERSION(false), + VIRTUALSERVER_WEBLIST_ENABLED(true), + VIRTUALSERVER_WELCOMEMESSAGE(true); + + private final boolean changeable; + + VirtualServerProperty(boolean changeable) { + this.changeable = changeable; + } + + @Override + public String getName() { + return name().toLowerCase(Locale.ROOT); + } + + @Override + public boolean isChangeable() { + return changeable; + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/VirtualServerStatus.java b/src/com/github/theholywaffle/teamspeak3/api/VirtualServerStatus.java new file mode 100644 index 0000000..763e1b4 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/VirtualServerStatus.java @@ -0,0 +1,48 @@ +package com.github.theholywaffle.teamspeak3.api; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public enum VirtualServerStatus { + + ONLINE("online"), + OFFLINE("offline"), + DEPLOY_RUNNING("deploy running"), + BOOTING_UP("booting up"), + SHUTTING_DOWN("shutting down"), + VIRTUAL_ONLINE("virtual online"), + UNKNOWN("unknown"); + + private final String name; + + VirtualServerStatus(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/event/BaseEvent.java b/src/com/github/theholywaffle/teamspeak3/api/event/BaseEvent.java new file mode 100644 index 0000000..a9c0308 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/event/BaseEvent.java @@ -0,0 +1,131 @@ +package com.github.theholywaffle.teamspeak3.api.event; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 - 2015 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.wrapper.Wrapper; + +import java.util.Collections; +import java.util.Map; + +public abstract class BaseEvent extends Wrapper implements TS3Event { + + protected BaseEvent(Map map) { + super(Collections.unmodifiableMap(map)); + } + + /** + * Gets the ID of the client who caused this event to happen. + *

+ * Because not all events are caused by clients, not all events have an invoker.
+ * Most importantly, client events (i.e. {@link ClientJoinEvent}, {@link ClientLeaveEvent} + * {@link ClientMovedEvent}) do not have an invoker if a client joined, + * quit or changed channels themselves. These values are only present if another client + * caused the event to happen by moving, kicking or banning a client. + *

+ * If the event response does not contain an invoker, this method will return {@code -1}. + *

+ * + * @return the client ID of the invoker or {@code -1} if the event does not contain an invoker + */ + public int getInvokerId() { + return getInt("invokerid"); + } + + /** + * Gets the nickname of the client who caused this event to happen. + *

+ * Because not all events are caused by clients, not all events have an invoker.
+ * Most importantly, client events (i.e. {@link ClientJoinEvent}, {@link ClientLeaveEvent} + * {@link ClientMovedEvent}) do not have an invoker if a client joined, + * quit or changed channels themselves. These values are only present if another client + * caused the event to happen by moving, kicking or banning a client. + *

+ * If the event response does not contain an invoker, this method will return an empty string. + *

+ * + * @return the nickname of the invoker or an empty string if the event does not contain an invoker + */ + public String getInvokerName() { + return get("invokername"); + } + + /** + * Gets the unique identifier of the client who caused this event to happen. + *

+ * Because not all events are caused by clients, not all events have an invoker.
+ * Most importantly, client events (i.e. {@link ClientJoinEvent}, {@link ClientLeaveEvent} + * {@link ClientMovedEvent}) do not have an invoker if a client joined, + * quit or changed channels themselves. These values are only present if another client + * caused the event to happen by moving, kicking or banning a client. + *

+ * If the event response does not contain an invoker, this method will return an empty string. + *

+ * + * @return the unique ID the invoker or an empty string if the event does not contain an invoker + */ + public String getInvokerUniqueId() { + return get("invokeruid"); + } + + /** + * Gets the reason ID of the cause of this event. + *

+ * Here's an incomplete list of known reason IDs: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + *
IDDescription
-1No reason present
0No external cause
1Client was moved (by other client / by creating a new channel)
3Client timed out
4Client kicked from a channel or channel deleted
5Client kicked from the server
6Client banned from the server
8Client left the server (no external cause)
10Virtual server or channel edited
11Server shut down
+ * + * @return the reason ID for this event + */ + public int getReasonId() { + return getInt("reasonid"); + } + + /** + * Gets the reason for this event. + * This field is rarely present, even if the reason ID is non-zero. + *

+ * In case of a client getting kicked or banned, this will return the user-provided reason. + *

+ * + * @return the reason for this event or an empty string if no reason message is present + */ + public String getReasonMessage() { + return get("reasonmsg"); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/event/ChannelCreateEvent.java b/src/com/github/theholywaffle/teamspeak3/api/event/ChannelCreateEvent.java new file mode 100644 index 0000000..2dad4bd --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/event/ChannelCreateEvent.java @@ -0,0 +1,47 @@ +package com.github.theholywaffle.teamspeak3.api.event; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.ChannelProperty; + +import java.util.Map; + +public class ChannelCreateEvent extends BaseEvent { + + public ChannelCreateEvent(Map map) { + super(map); + } + + public int getChannelId() { + return getInt(ChannelProperty.CID); + } + + @Override + public void fire(TS3Listener listener) { + listener.onChannelCreate(this); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/event/ChannelDeletedEvent.java b/src/com/github/theholywaffle/teamspeak3/api/event/ChannelDeletedEvent.java new file mode 100644 index 0000000..5db953e --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/event/ChannelDeletedEvent.java @@ -0,0 +1,47 @@ +package com.github.theholywaffle.teamspeak3.api.event; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.ChannelProperty; + +import java.util.Map; + +public class ChannelDeletedEvent extends BaseEvent { + + public ChannelDeletedEvent(Map map) { + super(map); + } + + public int getChannelId() { + return getInt(ChannelProperty.CID); + } + + @Override + public void fire(TS3Listener listener) { + listener.onChannelDeleted(this); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/event/ChannelDescriptionEditedEvent.java b/src/com/github/theholywaffle/teamspeak3/api/event/ChannelDescriptionEditedEvent.java new file mode 100644 index 0000000..2d7f122 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/event/ChannelDescriptionEditedEvent.java @@ -0,0 +1,47 @@ +package com.github.theholywaffle.teamspeak3.api.event; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.ChannelProperty; + +import java.util.Map; + +public class ChannelDescriptionEditedEvent extends BaseEvent { + + public ChannelDescriptionEditedEvent(Map map) { + super(map); + } + + public int getChannelId() { + return getInt(ChannelProperty.CID); + } + + @Override + public void fire(TS3Listener listener) { + listener.onChannelDescriptionChanged(this); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/event/ChannelEditedEvent.java b/src/com/github/theholywaffle/teamspeak3/api/event/ChannelEditedEvent.java new file mode 100644 index 0000000..d8a2990 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/event/ChannelEditedEvent.java @@ -0,0 +1,47 @@ +package com.github.theholywaffle.teamspeak3.api.event; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.ChannelProperty; + +import java.util.Map; + +public class ChannelEditedEvent extends BaseEvent { + + public ChannelEditedEvent(Map map) { + super(map); + } + + public int getChannelId() { + return getInt(ChannelProperty.CID); + } + + @Override + public void fire(TS3Listener listener) { + listener.onChannelEdit(this); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/event/ChannelMovedEvent.java b/src/com/github/theholywaffle/teamspeak3/api/event/ChannelMovedEvent.java new file mode 100644 index 0000000..00c43b9 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/event/ChannelMovedEvent.java @@ -0,0 +1,55 @@ +package com.github.theholywaffle.teamspeak3.api.event; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.ChannelProperty; + +import java.util.Map; + +public class ChannelMovedEvent extends BaseEvent { + + public ChannelMovedEvent(Map map) { + super(map); + } + + public int getChannelId() { + return getInt(ChannelProperty.CID); + } + + public int getChannelParentId() { + return getInt(ChannelProperty.CPID); + } + + public int getChannelOrder() { + return getInt(ChannelProperty.CHANNEL_ORDER); + } + + @Override + public void fire(TS3Listener listener) { + listener.onChannelMoved(this); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/event/ChannelPasswordChangedEvent.java b/src/com/github/theholywaffle/teamspeak3/api/event/ChannelPasswordChangedEvent.java new file mode 100644 index 0000000..cba4d0a --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/event/ChannelPasswordChangedEvent.java @@ -0,0 +1,47 @@ +package com.github.theholywaffle.teamspeak3.api.event; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.ChannelProperty; + +import java.util.Map; + +public class ChannelPasswordChangedEvent extends BaseEvent { + + public ChannelPasswordChangedEvent(Map map) { + super(map); + } + + public int getChannelId() { + return getInt(ChannelProperty.CID); + } + + @Override + public void fire(TS3Listener listener) { + listener.onChannelPasswordChanged(this); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/event/ClientJoinEvent.java b/src/com/github/theholywaffle/teamspeak3/api/event/ClientJoinEvent.java new file mode 100644 index 0000000..c91d990 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/event/ClientJoinEvent.java @@ -0,0 +1,179 @@ +package com.github.theholywaffle.teamspeak3.api.event; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.ClientProperty; + +import java.util.Map; + +public class ClientJoinEvent extends BaseEvent { + + public ClientJoinEvent(Map map) { + super(map); + } + + public int getClientFromId() { + return getInt("cfid"); + } + + public int getClientTargetId() { + return getInt("ctid"); + } + + public int getClientId() { + return getInt("clid"); + } + + public String getUniqueClientIdentifier() { + return get(ClientProperty.CLIENT_UNIQUE_IDENTIFIER); + } + + public String getClientNickname() { + return get(ClientProperty.CLIENT_NICKNAME); + } + + public boolean isClientInputMuted() { + return getBoolean(ClientProperty.CLIENT_INPUT_MUTED); + } + + public boolean isClientOutputMuted() { + return getBoolean(ClientProperty.CLIENT_OUTPUT_MUTED); + } + + public boolean isClientOutputOnlyMuted() { + return getBoolean(ClientProperty.CLIENT_OUTPUTONLY_MUTED); + } + + public boolean isClientUsingHardwareInput() { + return getBoolean(ClientProperty.CLIENT_INPUT_HARDWARE); + } + + public boolean isClientUsingHardwareOutput() { + return getBoolean(ClientProperty.CLIENT_OUTPUT_HARDWARE); + } + + public String getClientMetadata() { + return get(ClientProperty.CLIENT_META_DATA); + } + + public boolean isClientRecording() { + return getBoolean(ClientProperty.CLIENT_IS_RECORDING); + } + + public int getClientDatabaseId() { + return getInt(ClientProperty.CLIENT_DATABASE_ID); + } + + public int getClientChannelGroupId() { + return getInt(ClientProperty.CLIENT_CHANNEL_GROUP_ID); + } + + public int getAmountOfServerGroups() { + //getInt was turning the String 1,2,3,.. to a int which wasn't right. + //Now it gets the Amount even if the ID is <=10 + String[] split = get(ClientProperty.CLIENT_SERVERGROUPS).split(","); + return split.length; + } + + public String getClientServerGroups() { + //getClientServerGroups returns a string containing the server group ID for example 1,2,3,... + return get(ClientProperty.CLIENT_SERVERGROUPS); + } + + public boolean isClientAway() { + return getBoolean(ClientProperty.CLIENT_AWAY); + } + + public String getClientAwayMessage() { + return get(ClientProperty.CLIENT_AWAY_MESSAGE); + } + + public int getClientType() { + return getInt(ClientProperty.CLIENT_TYPE); + } + + public String getClientFlagAvatarId() { + return get(ClientProperty.CLIENT_FLAG_AVATAR); + } + + public int getClientTalkPower() { + return getInt(ClientProperty.CLIENT_TALK_POWER); + } + + public boolean isClientRequestingToTalk() { + return getBoolean(ClientProperty.CLIENT_TALK_REQUEST); + } + + public String getClientTalkRequestMessage() { + return get(ClientProperty.CLIENT_TALK_REQUEST_MSG); + } + + public String getClientDescription() { + return get(ClientProperty.CLIENT_DESCRIPTION); + } + + public boolean isClientTalking() { + return getBoolean(ClientProperty.CLIENT_IS_TALKER); + } + + public boolean isClientPrioritySpeaker() { + return getBoolean(ClientProperty.CLIENT_IS_PRIORITY_SPEAKER); + } + + public int getClientUnreadMessages() { + return getInt(ClientProperty.CLIENT_UNREAD_MESSAGES); + } + + public String getClientPhoneticNickname() { + return get(ClientProperty.CLIENT_NICKNAME_PHONETIC); + } + + public int getClientNeededServerQueryViewPower() { + return getInt(ClientProperty.CLIENT_NEEDED_SERVERQUERY_VIEW_POWER); + } + + public long getClientIconId() { + return getLong(ClientProperty.CLIENT_ICON_ID); + } + + public boolean isClientChannelCommander() { + return getBoolean(ClientProperty.CLIENT_IS_CHANNEL_COMMANDER); + } + + public String getClientCountry() { + return get(ClientProperty.CLIENT_COUNTRY); + } + + public int getClientInheritedChannelGroupId() { + return getInt(ClientProperty.CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID); + } + + @Override + public void fire(TS3Listener listener) { + listener.onClientJoin(this); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/event/ClientLeaveEvent.java b/src/com/github/theholywaffle/teamspeak3/api/event/ClientLeaveEvent.java new file mode 100644 index 0000000..92cf968 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/event/ClientLeaveEvent.java @@ -0,0 +1,53 @@ +package com.github.theholywaffle.teamspeak3.api.event; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +public class ClientLeaveEvent extends BaseEvent { + + public ClientLeaveEvent(Map map) { + super(map); + } + + public int getClientFromId() { + return getInt("cfid"); + } + + public int getClientTargetId() { + return getInt("ctid"); + } + + public int getClientId() { + return getInt("clid"); + } + + @Override + public void fire(TS3Listener listener) { + listener.onClientLeave(this); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/event/ClientMovedEvent.java b/src/com/github/theholywaffle/teamspeak3/api/event/ClientMovedEvent.java new file mode 100644 index 0000000..aa254fd --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/event/ClientMovedEvent.java @@ -0,0 +1,58 @@ +package com.github.theholywaffle.teamspeak3.api.event; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.wrapper.Channel; + +import java.util.Map; + +public class ClientMovedEvent extends BaseEvent { + + public ClientMovedEvent(Map map) { + super(map); + } + + /** + * Gets the ID of the channel the client has moved to. + * + * @return the ID of the target channel + * + * @see Channel#getId() + */ + public int getTargetChannelId() { + return getInt("ctid"); + } + + public int getClientId() { + return getInt("clid"); + } + + @Override + public void fire(TS3Listener listener) { + listener.onClientMoved(this); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/event/PrivilegeKeyUsedEvent.java b/src/com/github/theholywaffle/teamspeak3/api/event/PrivilegeKeyUsedEvent.java new file mode 100644 index 0000000..166ee04 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/event/PrivilegeKeyUsedEvent.java @@ -0,0 +1,75 @@ +package com.github.theholywaffle.teamspeak3.api.event; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2016 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.PrivilegeKeyType; + +import java.util.Map; + +public class PrivilegeKeyUsedEvent extends BaseEvent { + + public PrivilegeKeyUsedEvent(Map map) { + super(map); + } + + public int getClientId() { + return getInt("clid"); + } + + public int getClientDatabaseId() { + return getInt("cldbid"); + } + + public String getClientUniqueIdentifier() { + return get("cluid"); + } + + public String getPrivilegeKey() { + return get("token"); + } + + public PrivilegeKeyType getPrivilegeKeyType() { + if (getPrivilegeKeyChannelId() == 0) { + return PrivilegeKeyType.SERVER_GROUP; + } else { + return PrivilegeKeyType.CHANNEL_GROUP; + } + } + + public int getPrivilegeKeyGroupId() { + return getInt("token1"); + } + + public int getPrivilegeKeyChannelId() { + return getInt("token2"); + } + + @Override + public void fire(TS3Listener listener) { + listener.onPrivilegeKeyUsed(this); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/event/ServerEditedEvent.java b/src/com/github/theholywaffle/teamspeak3/api/event/ServerEditedEvent.java new file mode 100644 index 0000000..0f14620 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/event/ServerEditedEvent.java @@ -0,0 +1,41 @@ +package com.github.theholywaffle.teamspeak3.api.event; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +public class ServerEditedEvent extends BaseEvent { + + public ServerEditedEvent(Map map) { + super(map); + } + + @Override + public void fire(TS3Listener listener) { + listener.onServerEdit(this); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/event/TS3Event.java b/src/com/github/theholywaffle/teamspeak3/api/event/TS3Event.java new file mode 100644 index 0000000..21be951 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/event/TS3Event.java @@ -0,0 +1,32 @@ +package com.github.theholywaffle.teamspeak3.api.event; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public interface TS3Event { + + void fire(TS3Listener listener); +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/event/TS3EventAdapter.java b/src/com/github/theholywaffle/teamspeak3/api/event/TS3EventAdapter.java new file mode 100644 index 0000000..08621fb --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/event/TS3EventAdapter.java @@ -0,0 +1,73 @@ +package com.github.theholywaffle.teamspeak3.api.event; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +/** + * A template class implementing {@link TS3Listener} similar to Swing's event adapters. + *

+ * All method in this class do nothing, so the user only has to override the interface + * methods for the events they want to take action on. + *

+ */ +public abstract class TS3EventAdapter implements TS3Listener { + + @Override + public void onTextMessage(TextMessageEvent e) {} + + @Override + public void onClientJoin(ClientJoinEvent e) {} + + @Override + public void onClientLeave(ClientLeaveEvent e) {} + + @Override + public void onServerEdit(ServerEditedEvent e) {} + + @Override + public void onChannelEdit(ChannelEditedEvent e) {} + + @Override + public void onChannelDescriptionChanged(ChannelDescriptionEditedEvent e) {} + + @Override + public void onClientMoved(ClientMovedEvent e) {} + + @Override + public void onChannelCreate(ChannelCreateEvent e) {} + + @Override + public void onChannelDeleted(ChannelDeletedEvent e) {} + + @Override + public void onChannelMoved(ChannelMovedEvent e) {} + + @Override + public void onChannelPasswordChanged(ChannelPasswordChangedEvent e) {} + + @Override + public void onPrivilegeKeyUsed(PrivilegeKeyUsedEvent e) {} +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/event/TS3EventType.java b/src/com/github/theholywaffle/teamspeak3/api/event/TS3EventType.java new file mode 100644 index 0000000..10870a4 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/event/TS3EventType.java @@ -0,0 +1,49 @@ +package com.github.theholywaffle.teamspeak3.api.event; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public enum TS3EventType { + + SERVER("server"), + CHANNEL("channel"), + TEXT_SERVER("textserver"), + TEXT_CHANNEL("textchannel"), + TEXT_PRIVATE("textprivate"), + PRIVILEGE_KEY_USED("tokenused"); + + private final String name; + + TS3EventType(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/event/TS3Listener.java b/src/com/github/theholywaffle/teamspeak3/api/event/TS3Listener.java new file mode 100644 index 0000000..3cd5d2b --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/event/TS3Listener.java @@ -0,0 +1,54 @@ +package com.github.theholywaffle.teamspeak3.api.event; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public interface TS3Listener { + + void onTextMessage(TextMessageEvent e); + + void onClientJoin(ClientJoinEvent e); + + void onClientLeave(ClientLeaveEvent e); + + void onServerEdit(ServerEditedEvent e); + + void onChannelEdit(ChannelEditedEvent e); + + void onChannelDescriptionChanged(ChannelDescriptionEditedEvent e); + + void onClientMoved(ClientMovedEvent e); + + void onChannelCreate(ChannelCreateEvent e); + + void onChannelDeleted(ChannelDeletedEvent e); + + void onChannelMoved(ChannelMovedEvent e); + + void onChannelPasswordChanged(ChannelPasswordChangedEvent e); + + void onPrivilegeKeyUsed(PrivilegeKeyUsedEvent e); +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/event/TextMessageEvent.java b/src/com/github/theholywaffle/teamspeak3/api/event/TextMessageEvent.java new file mode 100644 index 0000000..00fed90 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/event/TextMessageEvent.java @@ -0,0 +1,71 @@ +package com.github.theholywaffle.teamspeak3.api.event; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.TextMessageTargetMode; + +import java.util.Map; + +public class TextMessageEvent extends BaseEvent { + + public TextMessageEvent(Map map) { + super(map); + } + + public TextMessageTargetMode getTargetMode() { + int mode = getInt("targetmode"); + for (TextMessageTargetMode m : TextMessageTargetMode.values()) { + if (m.getIndex() == mode) { + return m; + } + } + return null; + } + + public String getMessage() { + return get("msg"); + } + + /** + * Gets the client ID of the recipient of a private message. + *
    + *
  • If the private message was sent to the query, the target ID will be the query's client ID
  • + *
  • If the private message was sent by the query, the target ID will be the recipient's client ID
  • + *
  • If this is not an event for a private message, this method will return {@code -1}
  • + *
+ * + * @return the client ID of the recipient + */ + public int getTargetClientId() { + return getInt("target"); + } + + @Override + public void fire(TS3Listener listener) { + listener.onTextMessage(this); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/exception/TS3CommandFailedException.java b/src/com/github/theholywaffle/teamspeak3/api/exception/TS3CommandFailedException.java new file mode 100644 index 0000000..a2ce2be --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/exception/TS3CommandFailedException.java @@ -0,0 +1,63 @@ +package com.github.theholywaffle.teamspeak3.api.exception; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.wrapper.QueryError; + +public class TS3CommandFailedException extends TS3Exception { + + private static final long serialVersionUID = 8179203326662268882L; + + private final QueryError queryError; + + public TS3CommandFailedException(QueryError error, String commandName) { + super(buildMessage(error, commandName)); + queryError = error; + } + + private static String buildMessage(QueryError error, String cmdName) { + final StringBuilder msg = new StringBuilder(); + msg.append("A \"").append(cmdName).append("\" command returned with a server error.\n"); + msg.append(">> ").append(error.getMessage()).append(" (ID ").append(error.getId()).append(')'); + + final String extra = error.getExtraMessage(); + if (extra != null && !extra.isEmpty()) { + msg.append(": ").append(extra); + } + + final int failedPermissionId = error.getFailedPermissionId(); + if (failedPermissionId > 0) { + msg.append(", failed permission with ID ").append(failedPermissionId); + } + + return msg.toString(); + } + + public QueryError getError() { + return queryError; + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/exception/TS3ConnectionFailedException.java b/src/com/github/theholywaffle/teamspeak3/api/exception/TS3ConnectionFailedException.java new file mode 100644 index 0000000..c5603df --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/exception/TS3ConnectionFailedException.java @@ -0,0 +1,37 @@ +package com.github.theholywaffle.teamspeak3.api.exception; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public class TS3ConnectionFailedException extends TS3Exception { + + private static final long serialVersionUID = 6849777544299282019L; + + public TS3ConnectionFailedException(Throwable c) { + super("Could not connect to the TeamSpeak3 server", c); + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/exception/TS3Exception.java b/src/com/github/theholywaffle/teamspeak3/api/exception/TS3Exception.java new file mode 100644 index 0000000..1833421 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/exception/TS3Exception.java @@ -0,0 +1,41 @@ +package com.github.theholywaffle.teamspeak3.api.exception; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public class TS3Exception extends RuntimeException { + + private static final long serialVersionUID = 7167169981592989359L; + + public TS3Exception(String msg) { + super(msg); + } + + public TS3Exception(String msg, Throwable cause) { + super(msg, cause); + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/exception/TS3FileTransferFailedException.java b/src/com/github/theholywaffle/teamspeak3/api/exception/TS3FileTransferFailedException.java new file mode 100644 index 0000000..ba60dde --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/exception/TS3FileTransferFailedException.java @@ -0,0 +1,42 @@ +package com.github.theholywaffle.teamspeak3.api.exception; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.io.IOException; + +public class TS3FileTransferFailedException extends TS3Exception { + + private static final long serialVersionUID = 2819130214534186875L; + + public TS3FileTransferFailedException(String msg) { + super(msg); + } + + public TS3FileTransferFailedException(String msg, IOException cause) { + super(msg, cause); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/exception/TS3QueryShutDownException.java b/src/com/github/theholywaffle/teamspeak3/api/exception/TS3QueryShutDownException.java new file mode 100644 index 0000000..ca6965f --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/exception/TS3QueryShutDownException.java @@ -0,0 +1,36 @@ +package com.github.theholywaffle.teamspeak3.api.exception; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2018 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public class TS3QueryShutDownException extends TS3Exception { + + private static final long serialVersionUID = -6727279731231409306L; + + public TS3QueryShutDownException() { + super("The query was shut down or disconnected."); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/exception/TS3UnknownErrorException.java b/src/com/github/theholywaffle/teamspeak3/api/exception/TS3UnknownErrorException.java new file mode 100644 index 0000000..e82fca7 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/exception/TS3UnknownErrorException.java @@ -0,0 +1,37 @@ +package com.github.theholywaffle.teamspeak3.api.exception; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public class TS3UnknownErrorException extends TS3Exception { + + private static final long serialVersionUID = -8458508268312921713L; + + public TS3UnknownErrorException(String error) { + super(error + " [Please report this exception to the developer!]"); + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/exception/TS3UnknownEventException.java b/src/com/github/theholywaffle/teamspeak3/api/exception/TS3UnknownEventException.java new file mode 100644 index 0000000..7ced336 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/exception/TS3UnknownEventException.java @@ -0,0 +1,37 @@ +package com.github.theholywaffle.teamspeak3.api.exception; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public class TS3UnknownEventException extends TS3Exception { + + private static final long serialVersionUID = -9179157153557357715L; + + public TS3UnknownEventException(String event) { + super(event + " [Please report this exception to the developer!]"); + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/reconnect/ConnectionHandler.java b/src/com/github/theholywaffle/teamspeak3/api/reconnect/ConnectionHandler.java new file mode 100644 index 0000000..2bfd076 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/reconnect/ConnectionHandler.java @@ -0,0 +1,36 @@ +package com.github.theholywaffle.teamspeak3.api.reconnect; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2015 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.TS3Query; + +public interface ConnectionHandler { + + void onConnect(TS3Query ts3Query); + + void onDisconnect(TS3Query ts3Query); +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/reconnect/DisconnectingConnectionHandler.java b/src/com/github/theholywaffle/teamspeak3/api/reconnect/DisconnectingConnectionHandler.java new file mode 100644 index 0000000..32cefce --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/reconnect/DisconnectingConnectionHandler.java @@ -0,0 +1,59 @@ +package com.github.theholywaffle.teamspeak3.api.reconnect; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2015 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.TS3Query; +import net.md_5.bungee.api.ProxyServer; + +import java.util.logging.Level; +import java.util.logging.Logger; + +public class DisconnectingConnectionHandler implements ConnectionHandler { + + private static final Logger log = ProxyServer.getInstance().getLogger(); + private final ConnectionHandler userConnectionHandler; + + public DisconnectingConnectionHandler(ConnectionHandler userConnectionHandler) { + this.userConnectionHandler = userConnectionHandler; + } + + @Override + public void onConnect(TS3Query ts3Query) { + if (userConnectionHandler != null) { + userConnectionHandler.onConnect(ts3Query); + } + } + + @Override + public void onDisconnect(TS3Query ts3Query) { + log.log(Level.SEVERE, "[Connection] Disconnected from TS3 server"); + + if (userConnectionHandler != null) { + userConnectionHandler.onDisconnect(ts3Query); + } + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/reconnect/ReconnectStrategy.java b/src/com/github/theholywaffle/teamspeak3/api/reconnect/ReconnectStrategy.java new file mode 100644 index 0000000..d9bdede --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/reconnect/ReconnectStrategy.java @@ -0,0 +1,156 @@ +package com.github.theholywaffle.teamspeak3.api.reconnect; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 - 2016 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public abstract class ReconnectStrategy { + + private static final int CONSTANT_BACKOFF = 10000; + private static final int START_TIMEOUT = 1000; + private static final int TIMEOUT_CAP = 60000; + private static final int ADDEND = 2000; + private static final double MULTIPLIER = 1.5; + + private ReconnectStrategy() {} + + public abstract ConnectionHandler create(ConnectionHandler userConnectionHandler); + + public static ReconnectStrategy userControlled() { + return new UserControlled(); + } + + public static ReconnectStrategy disconnect() { + return new Disconnect(); + } + + public static ReconnectStrategy constantBackoff() { + return constantBackoff(CONSTANT_BACKOFF); + } + + public static ReconnectStrategy constantBackoff(int timeout) { + return new Constant(timeout); + } + + public static ReconnectStrategy linearBackoff() { + return linearBackoff(START_TIMEOUT, ADDEND, TIMEOUT_CAP); + } + + public static ReconnectStrategy linearBackoff(int startTimeout, int addend) { + return linearBackoff(startTimeout, addend, TIMEOUT_CAP); + } + + public static ReconnectStrategy linearBackoff(int startTimeout, int addend, int timeoutCap) { + return new Linear(startTimeout, addend, timeoutCap); + } + + public static ReconnectStrategy exponentialBackoff() { + return exponentialBackoff(START_TIMEOUT, MULTIPLIER, TIMEOUT_CAP); + } + + public static ReconnectStrategy exponentialBackoff(int startTimeout, double multiplier) { + return exponentialBackoff(startTimeout, multiplier, TIMEOUT_CAP); + } + + public static ReconnectStrategy exponentialBackoff(int startTimeout, double multiplier, int timeoutCap) { + return new Exponential(startTimeout, multiplier, timeoutCap); + } + + private static class UserControlled extends ReconnectStrategy { + + @Override + public ConnectionHandler create(ConnectionHandler userConnectionHandler) { + String message = "userConnectionHandler cannot be null when using strategy UserControlled!"; + if (userConnectionHandler == null) throw new IllegalArgumentException(message); + return userConnectionHandler; + } + } + + private static class Disconnect extends ReconnectStrategy { + + @Override + public ConnectionHandler create(ConnectionHandler userConnectionHandler) { + return new DisconnectingConnectionHandler(userConnectionHandler); + } + } + + private static class Constant extends ReconnectStrategy { + + private final int timeout; + + public Constant(int timeout) { + if (timeout <= 0) throw new IllegalArgumentException("Timeout must be greater than 0"); + + this.timeout = timeout; + } + + @Override + public ConnectionHandler create(ConnectionHandler userConnectionHandler) { + return new ReconnectingConnectionHandler(userConnectionHandler, timeout, timeout, 0, 1.0); + } + } + + private static class Linear extends ReconnectStrategy { + + private final int startTimeout; + private final int addend; + private final int timeoutCap; + + private Linear(int startTimeout, int addend, int timeoutCap) { + if (startTimeout <= 0) throw new IllegalArgumentException("Starting timeout must be greater than 0"); + if (addend <= 0) throw new IllegalArgumentException("Addend must be greater than 0"); + + this.startTimeout = startTimeout; + this.addend = addend; + this.timeoutCap = timeoutCap; + } + + @Override + public ConnectionHandler create(ConnectionHandler userConnectionHandler) { + return new ReconnectingConnectionHandler(userConnectionHandler, startTimeout, timeoutCap, addend, 1.0); + } + } + + private static class Exponential extends ReconnectStrategy { + + private final int startTimeout; + private final double multiplier; + private final int timeoutCap; + + private Exponential(int startTimeout, double multiplier, int timeoutCap) { + if (startTimeout <= 0) throw new IllegalArgumentException("Starting timeout must be greater than 0"); + if (multiplier <= 1.0) throw new IllegalArgumentException("Multiplier must be greater than 1"); + + this.startTimeout = startTimeout; + this.multiplier = multiplier; + this.timeoutCap = timeoutCap; + } + + @Override + public ConnectionHandler create(ConnectionHandler userConnectionHandler) { + return new ReconnectingConnectionHandler(userConnectionHandler, startTimeout, timeoutCap, 0, multiplier); + } + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/reconnect/ReconnectingConnectionHandler.java b/src/com/github/theholywaffle/teamspeak3/api/reconnect/ReconnectingConnectionHandler.java new file mode 100644 index 0000000..6a6ec48 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/reconnect/ReconnectingConnectionHandler.java @@ -0,0 +1,91 @@ +package com.github.theholywaffle.teamspeak3.api.reconnect; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2015 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.TS3Query; +import com.github.theholywaffle.teamspeak3.api.exception.TS3ConnectionFailedException; +import net.md_5.bungee.api.ProxyServer; + +import java.util.logging.Level; +import java.util.logging.Logger; + +public class ReconnectingConnectionHandler implements ConnectionHandler { + + private static final Logger log = ProxyServer.getInstance().getLogger(); + + private final ConnectionHandler userConnectionHandler; + private final int startTimeout; + private final int timeoutCap; + private final int addend; + private final double multiplier; + + public ReconnectingConnectionHandler(ConnectionHandler userConnectionHandler, int startTimeout, + int timeoutCap, int addend, double multiplier) { + this.userConnectionHandler = userConnectionHandler; + this.startTimeout = startTimeout; + this.timeoutCap = timeoutCap; + this.addend = addend; + this.multiplier = multiplier; + } + + @Override + public void onConnect(TS3Query ts3Query) { + if (userConnectionHandler != null) { + userConnectionHandler.onConnect(ts3Query); + } + } + + @Override + public void onDisconnect(TS3Query ts3Query) { + // Announce disconnect and run user connection handler + log.log(Level.INFO, "[Connection] Disconnected from TS3 server - reconnecting in {}ms", startTimeout); + if (userConnectionHandler != null) { + userConnectionHandler.onDisconnect(ts3Query); + } + + int timeout = startTimeout; + + while (true) { + try { + Thread.sleep(timeout); + } catch (InterruptedException e) { + return; + } + + timeout = (int) Math.ceil(timeout * multiplier) + addend; + if (timeoutCap > 0) timeout = Math.min(timeout, timeoutCap); + + try { + ts3Query.connect(); + return; // Successfully reconnected, return + } catch (TS3ConnectionFailedException conFailed) { + // Ignore exception, announce reconnect failure + log.log(Level.FINE, "[Connection] Failed to reconnect - waiting {}ms until next attempt", timeout); + } + } + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/Ban.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/Ban.java new file mode 100644 index 0000000..dc5e37e --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/Ban.java @@ -0,0 +1,85 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Date; +import java.util.Map; + +public class Ban extends Wrapper { + + public Ban(Map map) { + super(map); + } + + public int getId() { + return getInt("banid"); + } + + public String getBannedIp() { + return get("ip"); + } + + public String getBannedName() { + return get("name"); + } + + public String getBannedUId() { + return get("uid"); + } + + public String getLastNickname() { + return get("lastnickname"); + } + + public Date getCreatedDate() { + return new Date(getLong("created") * 1000); + } + + public long getDuration() { + return getLong("duration"); + } + + public String getInvokerName() { + return get("invokername"); + } + + public int getInvokerClientDBId() { + return getInt("invokercldbid"); + } + + public String getInvokerUId() { + return get("invokeruid"); + } + + public String getReason() { + return get("reason"); + } + + public int getEnforcements() { + return getInt("enforcements"); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/Binding.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/Binding.java new file mode 100644 index 0000000..0faffb2 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/Binding.java @@ -0,0 +1,41 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +public class Binding extends Wrapper { + + public Binding(Map map) { + super(map); + } + + public String getIp() { + return get("ip"); + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/Channel.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/Channel.java new file mode 100644 index 0000000..eaac925 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/Channel.java @@ -0,0 +1,67 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +import com.github.theholywaffle.teamspeak3.api.ChannelProperty; + +public class Channel extends ChannelBase { + + public Channel(Map map) { + super(map); + } + + @Override + public int getId() { + return getInt(ChannelProperty.CID); + } + + public int getTotalClientsFamily() { + return getInt("total_clients_family"); + } + + public int getTotalClients() { + return getInt("total_clients"); + } + + public int getNeededSubscribePower() { + return getInt(ChannelProperty.CHANNEL_NEEDED_SUBSCRIBE_POWER); + } + + /** + * @return {@code true}, if the channel is empty, {@code false} otherwise. + */ + public boolean isEmpty() { + return (getTotalClients() == 0); + } + + @Override + public boolean isFamilyEmpty() { + return (getTotalClientsFamily() == 0); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/ChannelBase.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/ChannelBase.java new file mode 100644 index 0000000..586759f --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/ChannelBase.java @@ -0,0 +1,112 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 - 2015 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.ChannelProperty; +import com.github.theholywaffle.teamspeak3.api.Codec; + +import java.util.Map; + +public abstract class ChannelBase extends Wrapper { + + protected ChannelBase(Map map) { + super(map); + } + + public abstract int getId(); + + public int getParentChannelId() { + return getInt(ChannelProperty.PID); + } + + public int getOrder() { + return getInt(ChannelProperty.CHANNEL_ORDER); + } + + public String getName() { + return get(ChannelProperty.CHANNEL_NAME); + } + + public String getTopic() { + return get(ChannelProperty.CHANNEL_TOPIC); + } + + public boolean isDefault() { + return getBoolean(ChannelProperty.CHANNEL_FLAG_DEFAULT); + } + + public boolean hasPassword() { + return getBoolean(ChannelProperty.CHANNEL_FLAG_PASSWORD); + } + + public boolean isPermanent() { + return getBoolean(ChannelProperty.CHANNEL_FLAG_PERMANENT); + } + + public boolean isSemiPermanent() { + return getBoolean(ChannelProperty.CHANNEL_FLAG_SEMI_PERMANENT); + } + + public Codec getCodec() { + final int codec = getInt(ChannelProperty.CHANNEL_CODEC); + for (final Codec c : Codec.values()) { + if (c.getIndex() == codec) { + return c; + } + } + return Codec.UNKNOWN; + } + + public int getCodecQuality() { + return getInt(ChannelProperty.CHANNEL_CODEC_QUALITY); + } + + public int getNeededTalkPower() { + return getInt(ChannelProperty.CHANNEL_NEEDED_TALK_POWER); + } + + public long getIconId() { + return getLong(ChannelProperty.CHANNEL_ICON_ID); + } + + public int getMaxClients() { + return getInt(ChannelProperty.CHANNEL_MAXCLIENTS); + } + + public int getMaxFamilyClients() { + return getInt(ChannelProperty.CHANNEL_MAXFAMILYCLIENTS); + } + + public int getSecondsEmpty() { + return getInt(ChannelProperty.SECONDS_EMPTY); + } + + /** + * @return {@code true}, if the channel and all child channels are empty, {@code false} otherwise. + */ + abstract public boolean isFamilyEmpty(); +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/ChannelGroup.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/ChannelGroup.java new file mode 100644 index 0000000..be948a5 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/ChannelGroup.java @@ -0,0 +1,85 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.PermissionGroupDatabaseType; + +import java.util.Map; + +public class ChannelGroup extends Wrapper { + + public ChannelGroup(Map map) { + super(map); + } + + public int getId() { + return getInt("cgid"); + } + + public String getName() { + return get("name"); + } + + public PermissionGroupDatabaseType getType() { + final int type = getInt("type"); + for (final PermissionGroupDatabaseType t : PermissionGroupDatabaseType.values()) { + if (t.getIndex() == type) { + return t; + } + } + return null; + } + + public long getIconId() { + return getLong("iconid"); + } + + public boolean isSavedInDatabase() { + return getBoolean("savedb"); + } + + public int getSortId() { + return getInt("sortid"); + } + + public int getNameMode() { + return getInt("namemode"); + } + + public int getModifyPower() { + return getInt("n_modifyp"); + } + + public int getMemberAddPower() { + return getInt("n_member_addp"); + } + + public int getMemberRemovePower() { + return getInt("n_member_removep"); + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/ChannelGroupClient.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/ChannelGroupClient.java new file mode 100644 index 0000000..7853a44 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/ChannelGroupClient.java @@ -0,0 +1,49 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +public class ChannelGroupClient extends Wrapper { + + public ChannelGroupClient(Map map) { + super(map); + } + + public int getChannelId() { + return getInt("cid"); + } + + public int getClientDatabaseId() { + return getInt("cldbid"); + } + + public int getChannelGroupId() { + return getInt("cgid"); + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/ChannelInfo.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/ChannelInfo.java new file mode 100644 index 0000000..a5474fa --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/ChannelInfo.java @@ -0,0 +1,91 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +import com.github.theholywaffle.teamspeak3.api.ChannelProperty; + +public class ChannelInfo extends ChannelBase { + + private final int channelId; + + public ChannelInfo(int channelId, Map map) { + super(map); + this.channelId = channelId; + } + + @Override + public int getId() { + return channelId; + } + + public String getDescription() { + return get(ChannelProperty.CHANNEL_DESCRIPTION); + } + + public String getPassword() { + return get(ChannelProperty.CHANNEL_PASSWORD); + } + + public int getCodecLatencyFactor() { + return getInt(ChannelProperty.CHANNEL_CODEC_LATENCY_FACTOR); + } + + public boolean isEncrypted() { + return !getBoolean(ChannelProperty.CHANNEL_CODEC_IS_UNENCRYPTED); + } + + public boolean hasUnlimitedClients() { + return getBoolean(ChannelProperty.CHANNEL_FLAG_MAXCLIENTS_UNLIMITED); + } + + public boolean hasUnlimitedFamilyClients() { + return getBoolean(ChannelProperty.CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED); + } + + public boolean hasInheritedMaxFamilyClients() { + return getBoolean(ChannelProperty.CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED); + } + + public String getFilePath() { + return get(ChannelProperty.CHANNEL_FILEPATH); + } + + public boolean isForcedSilence() { + return getBoolean(ChannelProperty.CHANNEL_FORCED_SILENCE); + } + + public String getPhoneticName() { + return get(ChannelProperty.CHANNEL_NAME_PHONETIC); + } + + @Override + public boolean isFamilyEmpty() { + return (getSecondsEmpty() >= 0); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/Client.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/Client.java new file mode 100644 index 0000000..79ac5dd --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/Client.java @@ -0,0 +1,245 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.ClientProperty; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.Date; +import java.util.Map; + +public class Client extends Wrapper { + + public Client(Map map) { + super(map); + } + + public boolean canTalk() { + return getBoolean(ClientProperty.CLIENT_IS_TALKER); + } + + public String getAwayMessage() { + return get(ClientProperty.CLIENT_AWAY_MESSAGE); + } + + public String[] getBadgeGUIDs() { + String raw = get(ClientProperty.CLIENT_BADGES); + String[] properties = raw.split("[\\s:;]"); + for (String property : properties) { + if (!property.startsWith("badges=")) continue; + String commaSepBadges = property.substring("badges=".length()); + return commaSepBadges.split(","); + } + return new String[0]; + } + + public int getChannelGroupId() { + return getInt(ClientProperty.CLIENT_CHANNEL_GROUP_ID); + } + + public int getChannelId() { + return getInt(ClientProperty.CID); + } + + /** + * Utility method. This method will return a client URI. + * A client URI can be used to reference a client in descriptions or just via chat. + * Example: {@code client:///~} + * + * @return Client's URI + */ + public String getClientURI() { + StringBuilder sb = new StringBuilder(); + sb.append("client://").append(getId()).append('/'); + sb.append(getUniqueIdentifier()).append('~'); + try { + // We will encode the nickname, so characters like spaces work with this. + sb.append(URLEncoder.encode(getNickname(), "UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException("JVM doesn't support UTF-8", e); + } + return sb.toString(); + } + + public String getCountry() { + return get(ClientProperty.CLIENT_COUNTRY); + } + + public Date getCreatedDate() { + return new Date(getLong(ClientProperty.CLIENT_CREATED) * 1000); + } + + public int getDatabaseId() { + return getInt(ClientProperty.CLIENT_DATABASE_ID); + } + + public long getIconId() { + return getLong(ClientProperty.CLIENT_ICON_ID); + } + + public int getId() { + return getInt("clid"); + } + + public long getIdleTime() { + return getLong(ClientProperty.CLIENT_IDLE_TIME); + } + + public int getInheritedChannelGroupId() { + return getInt(ClientProperty.CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID); + } + + public String getIp() { + return get(ClientProperty.CONNECTION_CLIENT_IP); + } + + public Date getLastConnectedDate() { + return new Date(getLong(ClientProperty.CLIENT_LASTCONNECTED) * 1000); + } + + public String getNickname() { + return get(ClientProperty.CLIENT_NICKNAME); + } + + public String getPlatform() { + return get(ClientProperty.CLIENT_PLATFORM); + } + + public int[] getServerGroups() { + final String str = get(ClientProperty.CLIENT_SERVERGROUPS); + final String[] arr = str.split(","); + final int[] groups = new int[arr.length]; + for (int i = 0; i < groups.length; i++) { + groups[i] = Integer.parseInt(arr[i]); + } + return groups; + } + + public int getTalkPower() { + return getInt(ClientProperty.CLIENT_TALK_POWER); + } + + public int getType() { + return getInt(ClientProperty.CLIENT_TYPE); + } + + public String getUniqueIdentifier() { + return get(ClientProperty.CLIENT_UNIQUE_IDENTIFIER); + } + + public String getVersion() { + return get(ClientProperty.CLIENT_VERSION); + } + + public boolean hasOverwolf() { + String raw = get(ClientProperty.CLIENT_BADGES); + String[] properties = raw.split("[\\s:;]"); + for (String property : properties) { + if (!(property.startsWith("overwolf=") || property.startsWith("Overwolf="))) continue; + String overwolfValue = property.substring("overwolf=".length()); + return overwolfValue.equals("1"); + } + return false; + } + + public boolean isAway() { + return getBoolean(ClientProperty.CLIENT_AWAY); + } + + public boolean isChannelCommander() { + return getBoolean(ClientProperty.CLIENT_IS_CHANNEL_COMMANDER); + } + + /** + * Utility method that does a linear search on the array of server group IDs returned + * by {@link #getServerGroups()} and returns {@code true} if that array contains + * the given server group ID. + * + * @param serverGroupId + * the ID of the server group to search for + * + * @return whether this client is a member of the given server group + */ + public boolean isInServerGroup(int serverGroupId) { + int[] serverGroupIds = getServerGroups(); + for (int s : serverGroupIds) { + if (s == serverGroupId) return true; + } + return false; + } + + /** + * Utility method that does a linear search on the array of server group IDs returned + * by {@link #getServerGroups()} and returns {@code true} if that array contains + * the ID of the given server group. + * + * @param serverGroup + * the server group to search for + * + * @return whether this client is a member of the given server group + */ + public boolean isInServerGroup(ServerGroup serverGroup) { + return isInServerGroup(serverGroup.getId()); + } + + public boolean isInputHardware() { + return getBoolean(ClientProperty.CLIENT_INPUT_HARDWARE); + } + + public boolean isInputMuted() { + return getBoolean(ClientProperty.CLIENT_INPUT_MUTED); + } + + public boolean isOutputHardware() { + return getBoolean(ClientProperty.CLIENT_OUTPUT_HARDWARE); + } + + public boolean isOutputMuted() { + return getBoolean(ClientProperty.CLIENT_OUTPUT_MUTED); + } + + public boolean isPrioritySpeaker() { + return getBoolean(ClientProperty.CLIENT_IS_PRIORITY_SPEAKER); + } + + public boolean isRecording() { + return getBoolean(ClientProperty.CLIENT_IS_RECORDING); + } + + public boolean isRegularClient() { + return getType() == 0; + } + + public boolean isServerQueryClient() { + return getType() == 1; + } + + public boolean isTalking() { + return getBoolean(ClientProperty.CLIENT_FLAG_TALKING); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/ClientInfo.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/ClientInfo.java new file mode 100644 index 0000000..888d55b --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/ClientInfo.java @@ -0,0 +1,170 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.ClientProperty; + +import java.util.Map; + +public class ClientInfo extends Client { + + private final int clientId; + + public ClientInfo(int clientId, Map map) { + super(map); + this.clientId = clientId; + } + + @Override + public int getId() { + return clientId; + } + + public String getAvatar() { + return get(ClientProperty.CLIENT_FLAG_AVATAR); + } + + public long getBandwidthReceivedLastMinute() { + return getLong(ClientProperty.CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_TOTAL); + } + + public long getBandwidthReceivedLastSecond() { + return getLong(ClientProperty.CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_TOTAL); + } + + public long getBandwidthSentlastMinute() { + return getLong(ClientProperty.CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_TOTAL); + } + + public long getBandwidthSentLastSecond() { + return getLong(ClientProperty.CONNECTION_BANDWIDTH_SENT_LAST_SECOND_TOTAL); + } + + public String getBase64ClientUId() { + return get("client_base64HashClientUID"); + } + + public int getDefaultChannel() { + // TeamSpeak decided to prefix the channel ID with a forward slash (/)... + final String channelId = get(ClientProperty.CLIENT_DEFAULT_CHANNEL); + if (channelId.isEmpty()) return -1; + return Integer.parseInt(channelId.substring(1)); + } + + public String getDefaultToken() { + return get(ClientProperty.CLIENT_DEFAULT_TOKEN); + } + + public String getDescription() { + return get(ClientProperty.CLIENT_DESCRIPTION); + } + + public long getFiletransferBandwidthReceived() { + return getLong(ClientProperty.CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED); + } + + public long getFiletransferBandwidthSent() { + return getLong(ClientProperty.CONNECTION_FILETRANSFER_BANDWIDTH_SENT); + } + + public String getLoginName() { + return get(ClientProperty.CLIENT_LOGIN_NAME); + } + + public String getMetaData() { + return get(ClientProperty.CLIENT_META_DATA); + } + + public long getMonthlyBytesDownloaded() { + return getLong(ClientProperty.CLIENT_MONTH_BYTES_DOWNLOADED); + } + + public long getMonthlyBytesUploaded() { + return getLong(ClientProperty.CLIENT_MONTH_BYTES_UPLOADED); + } + + public int getNeededServerQueryViewPower() { + return getInt(ClientProperty.CLIENT_NEEDED_SERVERQUERY_VIEW_POWER); + } + + public String getPhoneticNickname() { + return get(ClientProperty.CLIENT_NICKNAME_PHONETIC); + } + + public String getTalkRequestMessage() { + return get(ClientProperty.CLIENT_TALK_REQUEST_MSG); + } + + public long getTimeConnected() { // milliseconds + return getLong(ClientProperty.CONNECTION_CONNECTED_TIME); + } + + public long getTotalBytesDownloaded() { + return getLong(ClientProperty.CLIENT_TOTAL_BYTES_DOWNLOADED); + } + + public long getTotalBytesReceived() { + return getLong(ClientProperty.CONNECTION_BYTES_RECEIVED_TOTAL); + } + + public long getTotalBytesSent() { + return getLong(ClientProperty.CONNECTION_BYTES_SENT_TOTAL); + } + + public long getTotalBytesUploaded() { + return getLong(ClientProperty.CLIENT_TOTAL_BYTES_UPLOADED); + } + + public int getTotalConnections() { + return getInt(ClientProperty.CLIENT_TOTALCONNECTIONS); + } + + public long getTotalPacketsReceived() { + return getLong(ClientProperty.CONNECTION_PACKETS_RECEIVED_TOTAL); + } + + public long getTotalPacketsSent() { + return getLong(ClientProperty.CONNECTION_PACKETS_SENT_TOTAL); + } + + public int getUnreadMessages() { + return getInt(ClientProperty.CLIENT_UNREAD_MESSAGES); + } + + public boolean isOutputOnlyMuted() { + return getBoolean(ClientProperty.CLIENT_OUTPUTONLY_MUTED); + } + + public boolean isRequestingToTalk() { + return getBoolean(ClientProperty.CLIENT_TALK_REQUEST); + } + + @Override + public boolean isTalking() { + throw new UnsupportedOperationException(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/Complaint.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/Complaint.java new file mode 100644 index 0000000..2da8c2b --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/Complaint.java @@ -0,0 +1,62 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Date; +import java.util.Map; + +public class Complaint extends Wrapper { + + public Complaint(Map map) { + super(map); + } + + public int getTargetClientDatabaseId() { + return getInt("tcldbid"); + } + + public String getTargetName() { + return get("tname"); + } + + public int getSourceClientDatabaseId() { + return getInt("fcldbid"); + } + + public String getSourceName() { + return get("fname"); + } + + public String getMessage() { + return get("message"); + } + + public Date getTimestamp() { + return new Date(getLong("timestamp") * 1000); + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/ConnectionInfo.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/ConnectionInfo.java new file mode 100644 index 0000000..97b1416 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/ConnectionInfo.java @@ -0,0 +1,99 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +import com.github.theholywaffle.teamspeak3.api.VirtualServerProperty; + +public class ConnectionInfo extends Wrapper { + + public ConnectionInfo(Map map) { + super(map); + } + + public long getFiletransferBandwidthSent() { + return getLong(VirtualServerProperty.CONNECTION_FILETRANSFER_BANDWIDTH_SENT); + } + + public long getFiletransferBandwidthReceived() { + return getLong(VirtualServerProperty.CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED); + } + + public long getFiletransferBytesSent() { + return getLong(VirtualServerProperty.CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL); + } + + public long getFiletransferBytesReceived() { + return getLong(VirtualServerProperty.CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL); + } + + public long getTotalPacketsSent() { + return getLong(VirtualServerProperty.CONNECTION_PACKETS_SENT_TOTAL); + } + + public long getTotalBytesSent() { + return getLong(VirtualServerProperty.CONNECTION_BYTES_SENT_TOTAL); + } + + public long getTotalPacketsReceived() { + return getLong(VirtualServerProperty.CONNECTION_PACKETS_RECEIVED_TOTAL); + } + + public long getTotalBytesReceived() { + return getLong(VirtualServerProperty.CONNECTION_BYTES_RECEIVED_TOTAL); + } + + public long getBandwidthSentLastSecond() { + return getLong(VirtualServerProperty.CONNECTION_BANDWIDTH_SENT_LAST_SECOND_TOTAL); + } + + public long getBandwidthSentLastMinute() { + return getLong(VirtualServerProperty.CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_TOTAL); + } + + public long getBandwidthReceivedLastSecond() { + return getLong(VirtualServerProperty.CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_TOTAL); + } + + public long getBandwidthReceivedLastMinute() { + return getLong(VirtualServerProperty.CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_TOTAL); + } + + public long getConnectedTime() { + return getLong("connection_connected_time"); + } + + public double getPacketLoss() { + return getDouble("connection_packetloss_total"); + } + + public double getPing() { + return getDouble("connection_ping"); + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/CreatedVirtualServer.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/CreatedVirtualServer.java new file mode 100644 index 0000000..fea3e53 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/CreatedVirtualServer.java @@ -0,0 +1,48 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +public class CreatedVirtualServer extends Wrapper { + + public CreatedVirtualServer(Map map) { + super(map); + } + + public int getId() { + return getInt("sid"); + } + + public int getPort() { + return getInt("virtualserver_port"); + } + + public String getServerAdminToken() { + return get("token"); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/CustomPropertyAssignment.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/CustomPropertyAssignment.java new file mode 100644 index 0000000..882f979 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/CustomPropertyAssignment.java @@ -0,0 +1,68 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2018 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +/** + * Wrapper class for the result to a {@code customsearch} command. + */ +public class CustomPropertyAssignment extends Wrapper { + + public CustomPropertyAssignment(Map map) { + super(map); + } + + /** + * Gets the database ID of the client that matched the custom client property search. + * + * @return the client's database ID + * + * @see Client#getDatabaseId() + */ + public int getClientDatabaseId() { + return getInt("cldbid"); + } + + /** + * Gets the key of the matched custom client property. + * + * @return the key of the property + */ + public String getKey() { + return get("ident"); + } + + /** + * Gets the value of the matched custom client property. + * + * @return the value of the property + */ + public String getValue() { + return get("value"); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/DatabaseClient.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/DatabaseClient.java new file mode 100644 index 0000000..9104a34 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/DatabaseClient.java @@ -0,0 +1,71 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.ClientProperty; + +import java.util.Date; +import java.util.Map; + +public class DatabaseClient extends Wrapper { + + public DatabaseClient(Map map) { + super(map); + } + + public int getDatabaseId() { + return getInt("cldbid"); + } + + public String getUniqueIdentifier() { + return get(ClientProperty.CLIENT_UNIQUE_IDENTIFIER); + } + + public String getNickname() { + return get(ClientProperty.CLIENT_NICKNAME); + } + + public Date getCreatedDate() { + return new Date(getLong(ClientProperty.CLIENT_CREATED) * 1000); + } + + public Date getLastConnectedDate() { + return new Date(getLong(ClientProperty.CLIENT_LASTCONNECTED) * 1000); + } + + public int getTotalConnections() { + return getInt(ClientProperty.CLIENT_TOTALCONNECTIONS); + } + + public String getDescription() { + return get(ClientProperty.CLIENT_DESCRIPTION); + } + + public String getLastIp() { + return get("client_lastip"); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/DatabaseClientInfo.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/DatabaseClientInfo.java new file mode 100644 index 0000000..43b007d --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/DatabaseClientInfo.java @@ -0,0 +1,71 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.ClientProperty; + +import java.util.Map; + +public class DatabaseClientInfo extends DatabaseClient { + + public DatabaseClientInfo(Map map) { + super(map); + } + + @Override + public int getDatabaseId() { + return getInt(ClientProperty.CLIENT_DATABASE_ID); + } + + public String getAvatar() { + return get(ClientProperty.CLIENT_FLAG_AVATAR); + } + + public long getMonthlyBytesUploaded() { + return getLong(ClientProperty.CLIENT_MONTH_BYTES_UPLOADED); + } + + public long getMonthlyBytesDownloaded() { + return getLong(ClientProperty.CLIENT_MONTH_BYTES_DOWNLOADED); + } + + public long getTotalBytesUploaded() { + return getLong(ClientProperty.CLIENT_TOTAL_BYTES_UPLOADED); + } + + public long getTotalBytesDownloaded() { + return getLong(ClientProperty.CLIENT_TOTAL_BYTES_DOWNLOADED); + } + + public long getIconId() { + return getLong(ClientProperty.CLIENT_ICON_ID); + } + + public String getBase64HashClientUID() { + return get("client_base64HashClientUID"); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/FileInfo.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/FileInfo.java new file mode 100644 index 0000000..6bc553d --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/FileInfo.java @@ -0,0 +1,134 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2016 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Date; +import java.util.Map; + +public class FileInfo extends Wrapper { + + public FileInfo(Map map) { + super(map); + } + + /** + * Gets the ID of the channel this file is stored on. + * + * @return the ID of the file's channel + */ + public int getChannelId() { + return getInt("cid"); + } + + /** + * Gets the path to this file or directory, including its name. + * This path can be used in other file transfer commands. + * + * @return the path to the file or directory + */ + public String getPath() { + return get("name"); + } + + /** + * Gets the name of this file or directory. + * + * @return the name of this file or directory + */ + public String getName() { + String fullPath = getPath(); + int slashPos = fullPath.lastIndexOf('/'); + if (slashPos < 0) return fullPath; + return fullPath.substring(slashPos + 1); + } + + /** + * Gets the path of the directory containing this file or directory. + * + * @return the path to the parent directory + * + * @see #getPath() + */ + public String getParentPath() { + String fullPath = getPath(); + int slashPos = fullPath.lastIndexOf('/'); + if (slashPos < 0) return "/"; + return fullPath.substring(0, slashPos + 1); + } + + /** + * Gets the size of the file in bytes. + * Note that this can return wrong values if the file is still being uploaded + * or if the upload has been paused by the uploading client. + * + * @return the file's size + */ + public long getFileSize() { + return getLong("size"); + } + + /** + * Gets the date of the last modification to this file. + * The date has a one-second resolution. + * + * @return the file's last modification date + */ + public Date getLastModifiedDate() { + return new Date(getLong("datetime") * 1000); + } + + /** + * Gets whether this entry is a directory or a file. + * {@code 0} stands for a directory, {@code 1} for a file. + *

+ * Consider using {@link #isFile} and {@link #isDirectory} instead. + *

+ * + * @return the type of this file entry + */ + public int getType() { + return 1; + } + + /** + * Returns {@code true} if this entry is a file and not a directory. + * + * @return whether this is a file + */ + public boolean isFile() { + return true; + } + + /** + * Returns {@code true} if this entry is a directory and not a file. + * + * @return whether this is a directory + */ + public boolean isDirectory() { + return false; + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/FileListEntry.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/FileListEntry.java new file mode 100644 index 0000000..9b8b7de --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/FileListEntry.java @@ -0,0 +1,94 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2016 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +public class FileListEntry extends FileInfo { + + public FileListEntry(Map map) { + super(map); + } + + @Override + public String getPath() { + return getParentPath() + getName(); + } + + @Override + public String getName() { + return get("name"); + } + + @Override + public String getParentPath() { + return get("path"); + } + + @Override + public long getFileSize() { + // Present if still uploading and returns + final long finishedSize = getLong("incompletesize"); + return (finishedSize > 0) ? finishedSize : super.getFileSize(); + } + + @Override + public int getType() { + return getInt("type"); + } + + @Override + public boolean isFile() { + return getType() == 1; + } + + @Override + public boolean isDirectory() { + return getType() == 0; + } + + /** + * Returns {@code true} if this file was actively being uploaded at the time + * this object was created. Note that this will return {@code false} if a + * client has paused an upload. + * + * @return whether this file is actively being uploaded + */ + public boolean isStillUploading() { + return getLong("incompletesize") > 0; + } + + /** + * If this file is still uploading, this method will return how many bytes have already + * been uploaded. Otherwise the normal file size is returned. + * + * @return how many bytes of this file have been uploaded + */ + public long getUploadedBytes() { + return super.getFileSize(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/FileTransfer.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/FileTransfer.java new file mode 100644 index 0000000..59f7508 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/FileTransfer.java @@ -0,0 +1,166 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2016 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +public class FileTransfer extends Wrapper { + + public FileTransfer(Map map) { + super(map); + } + + /** + * Gets the ID of the client who started this file transfer. + * + * @return the ID of the involved client + */ + public int getClientId() { + return getInt("clid"); + } + + /** + * Gets the path of the folder that the file which is being transferred is stored in on disk. + * This path seems to be relative to the folder the TS3 server is installed in. + * + * @return the disk path of the folder containing the transferring file + */ + public String getDiskFilePath() { + return get("path"); + } + + /** + * Returns the name of the file on the TS3 server without its full path. + * + * @return the file's name + */ + public String getFileName() { + return get("name"); + } + + /** + * Gets the size in bytes the file will have once it is fully transferred. + * + * @return the final file size in bytes + */ + public long getTotalFileSize() { + return getLong("size"); + } + + /** + * Gets the amount of bytes that have already been transferred. + * + * @return the amount of transferred bytes + */ + public long getTransferredFileSize() { + return getLong("sizedone"); + } + + /** + * Returns the key that the client has sent to the server to identify their file transfer. + * + * @return the client's file transfer ID + */ + public int getClientTransferId() { + return getInt("clientftfid"); + } + + /** + * Returns an internal ID that the server uses to identify their file transfer. + * This is not the key that can be used to request a file from the file server. + * + * @return the server's file transfer ID + */ + public int getServerTransferId() { + return getInt("serverftfid"); + } + + /** + * Returns the current status of the file transfer. + *

+ * Currently known status codes: + *

+ *
    + *
  • 0 - Not started
  • + *
  • 1 - Active
  • + *
  • 2 - Inactive (paused, stalled or done)
  • + *
+ * + * @return the current transfer status + */ + public int getStatus() { + return getInt("status"); + } + + /** + * Returns {@code true} if the file transfer has started. + * + * @return whether the file transfer has started + */ + public boolean hasStarted() { + return getStatus() > 0; + } + + /** + * Returns {@code true} if the file transfer is still active. + * This is the case when the transfer has started and is not done or paused. + * + * @return whether the file transfer is active + */ + public boolean isActive() { + return getStatus() == 1; + } + + /** + * Returns the current file transfer speed in bytes per second. + * + * @return the current transfer speed + */ + public double getCurrentSpeed() { + return getDouble("current_speed"); + } + + /** + * Returns the current file transfer speed in bytes per second. + * + * @return the current transfer speed + */ + public double getAverageSpeed() { + return getDouble("average_speed"); + } + + /** + * Returns how many milliseconds have elapsed since the file transfer was first announced to + * the server. This value will count up even if the file transfer has not yet been started, + * but will stop if the transfer has been paused or is complete. + * + * @return the transfer runtime in milliseconds + */ + public long getRuntime() { + return getLong("runtime"); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/FileTransferParameters.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/FileTransferParameters.java new file mode 100644 index 0000000..4f1edc7 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/FileTransferParameters.java @@ -0,0 +1,127 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2016 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.HashMap; +import java.util.Map; + +/** + * Represents an internally started file transfer and encapsulates the command response. + */ +public class FileTransferParameters extends Wrapper { + + public FileTransferParameters(Map map) { + super(map); + } + + /** + * Returns the key that the client has sent to the server to identify their file transfer. + * + * @return the client's file transfer ID + */ + public int getClientTransferId() { + return getInt("clientftfid"); + } + + /** + * Returns an internal ID that the server uses to identify their file transfer. + * This is not the key that can be used to request a file from the file server. + * + * @return the server's file transfer ID + */ + public int getServerTransferId() { + return getInt("serverftfid"); + } + + /** + * Gets the key needed identify ourselves to the file server and to start the file transfer. + * + * @return the file transfer key + */ + public String getFileTransferKey() { + return get("ftkey"); + } + + /** + * Gets the IP address or hostname of the file server the TS3 instance wants us to send our data to. + * + * @return the IP address of the file server + */ + public String getFileServerHost() { + return get("ip"); + } + + /** + * Gets the port of the file server the TS3 instance wants us to send our data to. + * + * @return the port of the file server + */ + public int getFileServerPort() { + return getInt("port"); + } + + /** + * Gets the size of the file being downloaded in bytes. + * Only present if this is a successfully started download. + * + * @return the size of the file being downloaded. + */ + public long getFileSize() { + return getLong("size"); + } + + /** + * Gets a {@link QueryError} that can be used when throwing an exception. + * + * @return a query error for this command's success value + */ + public QueryError getQueryError() { + Map errorMap = new HashMap<>(2); + errorMap.put("id", String.valueOf(getStatus())); + errorMap.put("msg", getMessage()); + return new QueryError(errorMap); + } + + /** + * Gets the status / error code of this command. Only present if the command failed. + * + * @return the command's status value + */ + public int getStatus() { + final int status = getInt("status"); + return status == -1 ? 0 : status; + } + + /** + * Gets the message that is provided in case the command fails. + * + * @return the failure message + */ + public String getMessage() { + return get("msg"); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/HostInfo.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/HostInfo.java new file mode 100644 index 0000000..f3d8437 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/HostInfo.java @@ -0,0 +1,112 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Date; +import java.util.Map; + +import com.github.theholywaffle.teamspeak3.api.ServerInstanceProperty; + +public class HostInfo extends Wrapper { + + public HostInfo(Map map) { + super(map); + } + + public long getUptime() { + return getLong(ServerInstanceProperty.INSTANCE_UPTIME); + } + + public Date getTimeStamp() { + return new Date(getLong(ServerInstanceProperty.HOST_TIMESTAMP_UTC) * 1000); + } + + public int getTotalRunningServers() { + return getInt(ServerInstanceProperty.VIRTUALSERVERS_RUNNING_TOTAL); + } + + public int getTotalMaxClients() { + return getInt(ServerInstanceProperty.VIRTUALSERVERS_TOTAL_MAXCLIENTS); + } + + public int getTotalClientsOnline() { + return getInt(ServerInstanceProperty.VIRTUALSERVERS_TOTAL_CLIENTS_ONLINE); + } + + public int getTotalChannels() { + return getInt(ServerInstanceProperty.VIRTUALSERVERS_TOTAL_CHANNELS_ONLINE); + } + + public long getFileTransferBandwidthSent() { + return getLong(ServerInstanceProperty.CONNECTION_FILETRANSFER_BANDWIDTH_SENT); + } + + public long getFileTransferBandwidthReceived() { + return getLong(ServerInstanceProperty.CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED); + } + + public long getFileTransferBytesSent() { + return getLong(ServerInstanceProperty.CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL); + } + + public long getFileTransferBytesReceived() { + return getLong(ServerInstanceProperty.CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL); + } + + public long getPacketsSentTotal() { + return getLong(ServerInstanceProperty.CONNECTION_PACKETS_SENT_TOTAL); + } + + public long getBytesSentTotal() { + return getLong(ServerInstanceProperty.CONNECTION_BYTES_SENT_TOTAL); + } + + public long getPacketsReceivedTotal() { + return getLong(ServerInstanceProperty.CONNECTION_PACKETS_RECEIVED_TOTAL); + } + + public long getBytesReceivedTotal() { + return getLong(ServerInstanceProperty.CONNECTION_BYTES_RECEIVED_TOTAL); + } + + public long getBandwidthSentLastSecond() { + return getLong(ServerInstanceProperty.CONNECTION_BANDWIDTH_SENT_LAST_SECOND_TOTAL); + } + + public long getBandwidthSentLastMinute() { + return getLong(ServerInstanceProperty.CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_TOTAL); + } + + public long getBandwidthReceivedLastSecond() { + return getLong(ServerInstanceProperty.CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_TOTAL); + } + + public long getBandwidthReceivedLastMinute() { + return getLong(ServerInstanceProperty.CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_TOTAL); + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/IconFile.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/IconFile.java new file mode 100644 index 0000000..6b2bb1c --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/IconFile.java @@ -0,0 +1,50 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2016 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +public class IconFile extends FileListEntry { + + public IconFile(Map map) { + super(map); + } + + /** + * Gets the icon ID corresponding to this file, or {@code -1} if the file name + * doesn't follow the standard format. + * + * @return this file's icon ID + */ + public long getIconId() { + String fileName = getName(); + if (!fileName.matches("icon_\\d{1,10}")) return -1L; // Doesn't match standard pattern + long id = Long.parseLong(fileName.substring("icon_".length())); + if ((id & 0xFFFF_FFFF_0000_0000L) != 0) return -1L; // CRC32 is 32 bits, so needs to fit into an int + return id; + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/InstanceInfo.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/InstanceInfo.java new file mode 100644 index 0000000..2bf1037 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/InstanceInfo.java @@ -0,0 +1,90 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +import com.github.theholywaffle.teamspeak3.api.ServerInstanceProperty; + +public class InstanceInfo extends Wrapper { + + public InstanceInfo(Map map) { + super(map); + } + + public int getDatabaseVersion() { + return getInt(ServerInstanceProperty.SERVERINSTANCE_DATABASE_VERSION); + } + + public int getFileTransferPort() { + return getInt(ServerInstanceProperty.SERVERINSTANCE_FILETRANSFER_PORT); + } + + public long getMaxDownloadBandwidth() { + return getLong(ServerInstanceProperty.SERVERINSTANCE_MAX_DOWNLOAD_TOTAL_BANDWIDTH); + } + + public long getMaxUploadBandwidth() { + return getLong(ServerInstanceProperty.SERVERINSTANCE_MAX_UPLOAD_TOTAL_BANDWIDTH); + } + + public int getGuestServerQueryGroup() { + return getInt(ServerInstanceProperty.SERVERINSTANCE_GUEST_SERVERQUERY_GROUP); + } + + public int getMaxFloodCommands() { + return getInt(ServerInstanceProperty.SERVERINSTANCE_SERVERQUERY_FLOOD_COMMANDS); + } + + public int getMaxFloodTime() { // SECONDS + return getInt(ServerInstanceProperty.SERVERINSTANCE_SERVERQUERY_FLOOD_TIME); + } + + public int getFloodBanTime() {// SECONDS + return getInt(ServerInstanceProperty.SERVERINSTANCE_SERVERQUERY_BAN_TIME); + } + + public int getServerAdminGroup() { + return getInt(ServerInstanceProperty.SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP); + } + + public int getDefaultServerGroup() { + return getInt(ServerInstanceProperty.SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP); + } + + public int getChannelAdminGroup() { + return getInt(ServerInstanceProperty.SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP); + } + + public int getDefaultChannelGroup() { + return getInt(ServerInstanceProperty.SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP); + } + + public int getPermissionsVersion() { + return getInt(ServerInstanceProperty.SERVERINSTANCE_PERMISSIONS_VERSION); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/Message.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/Message.java new file mode 100644 index 0000000..daf1871 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/Message.java @@ -0,0 +1,57 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Date; +import java.util.Map; + +public class Message extends Wrapper { + + public Message(Map map) { + super(map); + } + + public int getId() { + return getInt("msgid"); + } + + public String getSenderUniqueIdentifier() { + return get("cluid"); + } + + public String getSubject() { + return get("subject"); + } + + public Date getReceivedDate() { + return new Date(getLong("timestamp") * 1000); + } + + public boolean hasBeenRead() { + return getBoolean("flag_read"); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/Permission.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/Permission.java new file mode 100644 index 0000000..8eb69fe --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/Permission.java @@ -0,0 +1,110 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +/** + * Describes a permission that has been assigned to a client, + * a channel group or a server group. + *

+ * For a complete description of the TS3 permission system, refer to + * + * this post on the TeamSpeak forums. + *

+ */ +public class Permission extends Wrapper { + + public Permission(Map map) { + super(map); + } + + /** + * Gets the name of this permission. + *

+ * Boolean permissions are prefixed with {@code b_}
+ * Integer permissions are prefixed with {@code i_} + *

+ * + * @return this permission's name + */ + public String getName() { + return get("permsid"); + } + + /** + * Gets the value of this permission assignment. + *

+ * Please note that this value doesn't necessarily have to be + * the effective permission value for a client, as this assignment + * can be overridden by another assignment. + *

+ * Integer permissions usually have values between 0 and 100, + * but any integer value is theoretically valid. + *

+ * Boolean permissions have a value of {@code 0} to represent + * {@code false} and {@code 1} to represent {@code true}. + *

+ * + * @return the value of this permission assignment + */ + public int getValue() { + return getInt("permvalue"); + } + + /** + * Returns {@code true} if this permission is negated. + *

+ * Negated means that instead of the highest value, the lowest + * value will be selected for this permission instead. + *

+ * + * @return whether this permission is negated or not + */ + public boolean isNegated() { + return getBoolean("permnegated"); + } + + /** + * Returns {@code true} if this permission is skipped. + *

+ * Skipped only exists for server group and client permissions, + * therefore this value will always be false for channel group permissions. + *

+ * If a client permission is skipped, it won't be overridden by channel + * group permissions.
+ * If a server group permission is skipped, it won't be overridden by + * channel group or client permissions. + *

+ * + * @return whether this permission is negated or not + */ + public boolean isSkipped() { + return getBoolean("permskip"); + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/PermissionAssignment.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/PermissionAssignment.java new file mode 100644 index 0000000..69a01a7 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/PermissionAssignment.java @@ -0,0 +1,162 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.PermissionGroupType; + +import java.util.Map; + +/** + * Describes a single permission that is assigned to a varying target. + *

+ * This class is used when a lot of permissions are sent at once. + * To reduce bandwidth usage, the TS3 server only transmit the numeric + * permission ID and not the permission name. + *

+ * For a complete description of the TS3 permission system, refer to + * + * this post on the TeamSpeak forums. + *

+ */ +public class PermissionAssignment extends Wrapper { + + public PermissionAssignment(Map map) { + super(map); + } + + /** + * Where this permission assignment originates from. + * + * @return the type of this permission assignment + */ + public PermissionGroupType getType() { + final int type = getInt("t"); + for (final PermissionGroupType p : PermissionGroupType.values()) { + if (p.getIndex() == type) { + return p; + } + } + return null; + } + + /** + * Specifies where this permission assignment originates from, + * depending on the {@linkplain #getType() type} of this assignment. + *

+ * {@code x -> y} := In case {@code type} is {@code x}, {@code majorId} means {@code y} + *

+ * {@linkplain PermissionGroupType#SERVER_GROUP SERVER_GROUP} {@code ->} {@linkplain ServerGroup#getId() Server group ID}
+ * {@linkplain PermissionGroupType#GLOBAL_CLIENT GLOBAL_CLIENT} {@code ->} {@linkplain Client#getDatabaseId() Client database ID}
+ * {@linkplain PermissionGroupType#CHANNEL CHANNEL} {@code ->} {@linkplain Channel#getId() Channel ID}
+ * {@linkplain PermissionGroupType#CHANNEL_GROUP CHANNEL_GROUP} {@code ->} {@linkplain Channel#getId() Channel ID}
+ * {@linkplain PermissionGroupType#CHANNEL_CLIENT CHANNEL_CLIENT} {@code ->} {@linkplain Channel#getId() Channel ID} + *

+ * + * @return the major ID of the source of this assignment as described above + */ + public int getMajorId() { + return getInt("id1"); + } + + /** + * Specifies where this permission assignment originates from, + * depending on the {@linkplain #getType() type} of this assignment. + *

+ * {@code x -> y} := In case {@code type} is {@code x}, {@code minorId} means {@code y} + *

+ * {@linkplain PermissionGroupType#CHANNEL_GROUP CHANNEL_GROUP} {@code ->} {@linkplain ChannelGroup#getId() Channel group ID}
+ * {@linkplain PermissionGroupType#CHANNEL_CLIENT CHANNEL_CLIENT} {@code ->} {@linkplain Client#getDatabaseId() Client database ID} + *

+ * Otherwise {@code getMinorId()} is undefined should return {@code 0}. + *

+ * + * @return the minor ID of the source of this assignment as described above + */ + public int getMinorId() { + return getInt("id2"); + } + + /** + * Gets the numerical ID of this permission. + * + * @return this permission's numerical ID + */ + public int getId() { + return getInt("p"); + } + + /** + * Gets the value of this permission assignment. + *

+ * Please note that this value doesn't necessarily have to be + * the effective permission value for a client, as this assignment + * can be overridden by another assignment. + *

+ * Integer permissions usually have values between 0 and 100, + * but any integer value is theoretically valid. + *

+ * Boolean permissions have a value of {@code 0} to represent + * {@code false} and {@code 1} to represent {@code true}. + *

+ * + * @return the value of this permission assignment + */ + public int getValue() { + return getInt("v"); + } + + /** + * Returns {@code true} if this permission is negated. + *

+ * Negated means that instead of the highest value, the lowest + * value will be selected for this permission instead. + *

+ * + * @return whether this permission is negated or not + */ + public boolean isNegated() { + return getBoolean("n"); + } + + /** + * Returns {@code true} if this permission is skipped. + *

+ * Skipped only exists for server group and client permissions, + * therefore this value will always be false for channel group permissions. + *

+ * If a client permission is skipped, it won't be overridden by channel + * group permissions.
+ * If a server group permission is skipped, it won't be overridden by + * channel group or client permissions. + *

+ * + * @return whether this permission is negated or not + */ + public boolean isSkipped() { + return getBoolean("s"); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/PermissionInfo.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/PermissionInfo.java new file mode 100644 index 0000000..45d0df4 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/PermissionInfo.java @@ -0,0 +1,88 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +/** + * Describes a single permission on the TS3 server. + *

+ * Includes the numerical ID and the name of the permission, as well + * as an optional short description of this permission. Does not + * include any information about any possible permission assignments. + *

+ * For a complete description of the TS3 permission system, refer to + * + * this post on the TeamSpeak forums. + *

+ */ +public class PermissionInfo extends Wrapper { + + public PermissionInfo(Map map) { + super(map); + } + + /** + * Gets the numerical ID of this permission. + *

+ * In most cases, the name of the permission should be + * preferred over the numerical ID. + *

+ * + * @return this permission's numerical ID + */ + public int getId() { + return getInt("permid"); + } + + /** + * Gets the name of this permission. + *

+ * Boolean permissions are prefixed with {@code b_}
+ * Integer permissions are prefixed with {@code i_} + *

+ * + * @return this permission's name + */ + public String getName() { + return get("permname"); + } + + /** + * A short description about what this permission does. + *

+ * Does not exist for all permissions. In that case, an + * empty String will be returned instead. + *

+ * + * @return a short description of this permission + */ + public String getDescription() { + return get("permdesc"); + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/PrivilegeKey.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/PrivilegeKey.java new file mode 100644 index 0000000..62009bf --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/PrivilegeKey.java @@ -0,0 +1,69 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Date; +import java.util.Map; + +public class PrivilegeKey extends Wrapper { + + public PrivilegeKey(Map map) { + super(map); + } + + public String getToken() { + return get("token"); + } + + public int getType() { + return getInt("token_type"); + } + + public boolean isServerGroupToken() { + return getType() == 0; + } + + public boolean isChannelGroupToken() { + return !isServerGroupToken(); + } + + public int getGroupId() { + return getInt("token_id1"); + } + + public int getChannelId() { + return getInt("token_id2"); + } + + public Date getCreated() { + return new Date(getLong("token_created") * 1000); + } + + public String getDescription() { + return get("token_description"); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/QueryError.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/QueryError.java new file mode 100644 index 0000000..c387892 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/QueryError.java @@ -0,0 +1,60 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +public class QueryError extends Wrapper { + + private static final int ERROR_ID_OK = 0; + private static final int ERROR_ID_EMPTY_RESULT_SET = 1281; + + public QueryError(Map map) { + super(map); + } + + public int getId() { + return getInt("id"); + } + + public String getMessage() { + return get("msg"); + } + + public String getExtraMessage() { + return get("extra_msg"); + } + + public int getFailedPermissionId() { + return getInt("failed_permid"); + } + + public boolean isSuccessful() { + final int id = getId(); + return (id == ERROR_ID_OK || id == ERROR_ID_EMPTY_RESULT_SET); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/ServerGroup.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/ServerGroup.java new file mode 100644 index 0000000..f85a0c3 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/ServerGroup.java @@ -0,0 +1,85 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +import com.github.theholywaffle.teamspeak3.api.PermissionGroupDatabaseType; + +public class ServerGroup extends Wrapper { + + public ServerGroup(Map map) { + super(map); + } + + public int getId() { + return getInt("sgid"); + } + + public String getName() { + return get("name"); + } + + public PermissionGroupDatabaseType getType() { + final int type = getInt("type"); + for (final PermissionGroupDatabaseType p : PermissionGroupDatabaseType.values()) { + if (p.getIndex() == type) { + return p; + } + } + return null; + } + + public long getIconId() { + return getLong("iconid"); + } + + public int getSaveDb() { + return getInt("savedb"); + } + + public int getSortId() { + return getInt("sortid"); + } + + public int getNameMode() { + return getInt("namemode"); + } + + public int getModifyPower() { + return getInt("n_modifyp"); + } + + public int getMemberAddPower() { + return getInt("n_member_addp"); + } + + public int getMemberRemovePower() { + return getInt("n_member_removep"); + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/ServerGroupClient.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/ServerGroupClient.java new file mode 100644 index 0000000..64517c9 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/ServerGroupClient.java @@ -0,0 +1,49 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +public class ServerGroupClient extends Wrapper { + + public ServerGroupClient(Map map) { + super(map); + } + + public int getClientDatabaseId() { + return getInt("cldbid"); + } + + public String getNickname() { + return get("client_nickname"); + } + + public String getUniqueIdentifier() { + return get("client_unique_identifier"); + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/ServerQueryInfo.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/ServerQueryInfo.java new file mode 100644 index 0000000..1274556 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/ServerQueryInfo.java @@ -0,0 +1,198 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.TS3Api; +import com.github.theholywaffle.teamspeak3.TS3ApiAsync; +import com.github.theholywaffle.teamspeak3.api.ClientProperty; +import com.github.theholywaffle.teamspeak3.api.VirtualServerProperty; +import com.github.theholywaffle.teamspeak3.api.VirtualServerStatus; + +import java.util.Map; + +/** + * A class containing information about a server query, returned by the + * API methods {@link TS3Api#whoAmI()} and {@link TS3ApiAsync#whoAmI()}. + */ +public class ServerQueryInfo extends Wrapper { + + /** + * Creates a new {@code ServerQueryInfo} from the information present in the provided map. + * + * @param map + * the map containing the key-value pairs abstracted by this object + */ + public ServerQueryInfo(Map map) { + super(map); + } + + /** + * Gets the ID of the channel the server query is currently in. + * + * @return the ID of the current channel + */ + public int getChannelId() { + return getInt("client_channel_id"); + } + + /** + * Gets the ID of the server query in the database. + *

+ * In case this server query account was created by a client, + * its database ID will be identical to the client's database ID. + *

+ * + * @return the server query's database ID + */ + public int getDatabaseId() { + return getInt(ClientProperty.CLIENT_DATABASE_ID); + } + + /** + * Gets the client ID of the server query. + * + * @return the server query's client ID + */ + public int getId() { + return getInt("client_id"); + } + + /** + * Gets the username that was used when logging the query in (using a username-password combination). + *

+ * This username was set when creating the server query login and doesn't have to be related to + * the client who created the server query login. + *
+ * In case a server query is not logged in yet, this method will return an empty string. + *

+ * + * @return the username used when logging this query in + * + * @see TS3Api#login(String, String) TS3Api#login(username, password) - logging in server queries + */ + public String getLoginName() { + return get(ClientProperty.CLIENT_LOGIN_NAME); + } + + /** + * Gets the nickname currently used by the server query. + * Unless explicitly set, the nickname will be formatted like + *
username from ip:port
+ *

+ * Nicknames are only assigned after a virtual server has been selected. + * Until then, this method will return an empty string. + *

+ * + * @return the current nickname of the server query + */ + public String getNickname() { + return get(ClientProperty.CLIENT_NICKNAME); + } + + /** + * Gets the ID of the virtual server on which the server query login was created. + *

+ * This method will return {@code 0} (the ID of the template server) if a server query is + * not logged in or using the {@code serveradmin} login. + *

+ * + * @return the ID of the virtual server this server query belongs to + */ + public int getOriginServerId() { + return getInt("client_origin_server_id"); + } + + /** + * Gets the unique identifier of the server query. + *

+ * In case this server query account was created by a client, + * its unique ID will be identical to the client's unique ID. + *

+ * + * @return the server query's unique ID + */ + public String getUniqueIdentifier() { + return get(ClientProperty.CLIENT_UNIQUE_IDENTIFIER); + } + + /** + * Gets the ID of the currently selected virtual server. + *

+ * If used on a non-commercial TeamSpeak instance which can only host 1 virtual server, + * this ID will always be 1. + *
+ * If no virtual server has been selected yet, this method will return {@code 0}. + *

+ * + * @return the ID of the current virtual server or {@code 0} if none is selected + */ + public int getVirtualServerId() { + return getInt(VirtualServerProperty.VIRTUALSERVER_ID); + } + + /** + * Gets the port used by the currently selected virtual server. + *

+ * If no virtual server has been selected yet, this method will return {@code 0}. + *

+ * + * @return the port used by the current virtual server or {@code 0} if none is selected + */ + public int getVirtualServerPort() { + return getInt(VirtualServerProperty.VIRTUALSERVER_PORT); + } + + /** + * Gets the status of the currently selected virtual server. + *

+ * If no virtual server has been selected yet, this method will return {@link VirtualServerStatus#UNKNOWN}. + *

+ * + * @return the status of the current virtual server + */ + public VirtualServerStatus getVirtualServerStatus() { + final String status = get(VirtualServerProperty.VIRTUALSERVER_STATUS); + for (final VirtualServerStatus s : VirtualServerStatus.values()) { + if (s.getName().equals(status)) { + return s; + } + } + return VirtualServerStatus.UNKNOWN; + } + + /** + * Gets the unique identifier of the currently selected virtual server. + *

+ * If no virtual server has been selected yet, this method will return an empty string. + *

+ * + * @return the unique ID of the current virtual server + */ + public String getVirtualServerUniqueIdentifier() { + return get(VirtualServerProperty.VIRTUALSERVER_UNIQUE_IDENTIFIER); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/Version.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/Version.java new file mode 100644 index 0000000..1a24a16 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/Version.java @@ -0,0 +1,49 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +public class Version extends Wrapper { + + public Version(Map map) { + super(map); + } + + public String getVersion() { + return get("version"); + } + + public String getBuild() { + return get("build"); + } + + public String getPlatform() { + return get("platform"); + } + +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/VirtualServer.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/VirtualServer.java new file mode 100644 index 0000000..52b69d4 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/VirtualServer.java @@ -0,0 +1,85 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.VirtualServerProperty; +import com.github.theholywaffle.teamspeak3.api.VirtualServerStatus; + +import java.util.Map; + +public class VirtualServer extends Wrapper { + + public VirtualServer(Map map) { + super(map); + } + + public int getId() { + return getInt(VirtualServerProperty.VIRTUALSERVER_ID); + } + + public int getPort() { + return getInt(VirtualServerProperty.VIRTUALSERVER_PORT); + } + + public VirtualServerStatus getStatus() { + final String status = get(VirtualServerProperty.VIRTUALSERVER_STATUS); + for (final VirtualServerStatus s : VirtualServerStatus.values()) { + if (status.equals(s.getName())) { + return s; + } + } + return VirtualServerStatus.UNKNOWN; + } + + public int getClientsOnline() { + return getInt(VirtualServerProperty.VIRTUALSERVER_CLIENTSONLINE); + } + + public int getQueryClientsOnline() { + return getInt(VirtualServerProperty.VIRTUALSERVER_QUERYCLIENTSONLINE); + } + + public int getMaxClients() { + return getInt(VirtualServerProperty.VIRTUALSERVER_MAXCLIENTS); + } + + public String getUniqueIdentifier() { + return get(VirtualServerProperty.VIRTUALSERVER_UNIQUE_IDENTIFIER); + } + + public long getUptime() { + return getLong(VirtualServerProperty.VIRTUALSERVER_UPTIME); // in seconds + } + + public String getName() { + return get(VirtualServerProperty.VIRTUALSERVER_NAME); + } + + public boolean isAutoStart() { + return getBoolean(VirtualServerProperty.VIRTUALSERVER_AUTOSTART); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/VirtualServerInfo.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/VirtualServerInfo.java new file mode 100644 index 0000000..b378a9e --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/VirtualServerInfo.java @@ -0,0 +1,392 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.CodecEncryptionMode; +import com.github.theholywaffle.teamspeak3.api.HostBannerMode; +import com.github.theholywaffle.teamspeak3.api.HostMessageMode; +import com.github.theholywaffle.teamspeak3.api.VirtualServerProperty; + +import java.util.Date; +import java.util.Map; + +public class VirtualServerInfo extends VirtualServer { + + public VirtualServerInfo(Map map) { + super(map); + } + + public int getAntifloodPointsNeededCommandBlock() { + return getInt(VirtualServerProperty.VIRTUALSERVER_ANTIFLOOD_POINTS_NEEDED_COMMAND_BLOCK); + } + + public int getAntifloodPointsNeedIpBlock() { + return getInt(VirtualServerProperty.VIRTUALSERVER_ANTIFLOOD_POINTS_NEEDED_IP_BLOCK); + } + + public int getAntifloodPointsTickReduce() { + return getInt(VirtualServerProperty.VIRTUALSERVER_ANTIFLOOD_POINTS_TICK_REDUCE); + } + + public long getBandwidthReceivedLastMinute() { + return getInt(VirtualServerProperty.CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_TOTAL); + } + + public long getBandwidthReceivedLastSecond() { + return getLong(VirtualServerProperty.CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_TOTAL); + } + + public long getBandwidthSentLastMinute() { + return getLong(VirtualServerProperty.CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_TOTAL); + } + + public long getBandwidthSentLastSecond() { + return getLong(VirtualServerProperty.CONNECTION_BANDWIDTH_SENT_LAST_SECOND_TOTAL); + } + + public int getChannelsOnline() { + return getInt(VirtualServerProperty.VIRTUALSERVER_CHANNELSONLINE); + } + + public CodecEncryptionMode getCodecEncryptionMode() { + final int encryptionMode = getInt(VirtualServerProperty.VIRTUALSERVER_CODEC_ENCRYPTION_MODE); + for (final CodecEncryptionMode m : CodecEncryptionMode.values()) { + if (m.getIndex() == encryptionMode) { + return m; + } + } + return CodecEncryptionMode.UNKNOWN; + } + + public int getComplaintAutobanCount() { + return getInt(VirtualServerProperty.VIRTUALSERVER_COMPLAIN_AUTOBAN_COUNT); + } + + public long getComplaintAutobanTime() {// SEC + return getLong(VirtualServerProperty.VIRTUALSERVER_COMPLAIN_AUTOBAN_TIME); + } + + public long getComplaintRemoveTime() { + return getLong(VirtualServerProperty.VIRTUALSERVER_COMPLAIN_REMOVE_TIME); + } + + public long getControlBytesReceived() { + return getLong(VirtualServerProperty.CONNECTION_BYTES_RECEIVED_CONTROL); + } + + public long getControlBytesSent() { + return getLong(VirtualServerProperty.CONNECTION_BYTES_SENT_CONTROL); + } + + public long getControlPacketsReceived() { + return getLong(VirtualServerProperty.CONNECTION_PACKETS_RECEIVED_CONTROL); + } + + public long getControlPacketsSent() { + return getLong(VirtualServerProperty.CONNECTION_PACKETS_SENT_CONTROL); + } + + public Date getCreatedDate() { + return new Date(getLong(VirtualServerProperty.VIRTUALSERVER_CREATED) * 1000); + } + + public int getDefaultChannelAdminGroup() { + return getInt(VirtualServerProperty.VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP); + } + + public int getDefaultChannelGroup() { + return getInt(VirtualServerProperty.VIRTUALSERVER_DEFAULT_CHANNEL_GROUP); + } + + public int getDefaultServerGroup() { + return getInt(VirtualServerProperty.VIRTUALSERVER_DEFAULT_SERVER_GROUP); + } + + public long getDownloadQuota() { + return getLong(VirtualServerProperty.VIRTUALSERVER_DOWNLOAD_QUOTA); + } + + public String getFileBase() { + return get(VirtualServerProperty.VIRTUALSERVER_FILEBASE); + } + + public long getFiletransferBandwidthReceived() { + return getLong(VirtualServerProperty.CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED); + } + + public long getFiletransferBandwidthSent() { + return getLong(VirtualServerProperty.CONNECTION_FILETRANSFER_BANDWIDTH_SENT); + } + + public long getFiletransferBytesReceived() { + return getLong(VirtualServerProperty.CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL); + } + + public long getFiletransferBytesSent() { + return getLong(VirtualServerProperty.CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL); + } + + public int getHostbannerGfxInterval() { + return getInt(VirtualServerProperty.VIRTUALSERVER_HOSTBANNER_GFX_INTERVAL); + } + + public String getHostbannerGfxUrl() { + return get(VirtualServerProperty.VIRTUALSERVER_HOSTBANNER_GFX_URL); + } + + public HostBannerMode getHostbannerMode() { + final int hostbannerMode = getInt(VirtualServerProperty.VIRTUALSERVER_HOSTBANNER_MODE); + for (final HostBannerMode m : HostBannerMode.values()) { + if (m.getIndex() == hostbannerMode) { + return m; + } + } + return HostBannerMode.UNKNOWN; + } + + public String getHostbannerUrl() { + return get(VirtualServerProperty.VIRTUALSERVER_HOSTBANNER_URL); + } + + public String getHostbuttonGfxUrl() { + return get(VirtualServerProperty.VIRTUALSERVER_HOSTBUTTON_GFX_URL); + } + + public String getHostbuttonTooltip() { + return get(VirtualServerProperty.VIRTUALSERVER_HOSTBUTTON_TOOLTIP); + } + + public String getHostbuttonUrl() { + return get(VirtualServerProperty.VIRTUALSERVER_HOSTBUTTON_URL); + } + + public String getHostMessage() { + return get(VirtualServerProperty.VIRTUALSERVER_HOSTMESSAGE); + } + + public HostMessageMode getHostMessageMode() { + final int hostmessageMode = getInt(VirtualServerProperty.VIRTUALSERVER_HOSTMESSAGE_MODE); + for (final HostMessageMode m : HostMessageMode.values()) { + if (m.getIndex() == hostmessageMode) { + return m; + } + } + return HostMessageMode.UNKNOWN; + } + + public long getIconId() { + return getLong(VirtualServerProperty.VIRTUALSERVER_ICON_ID); + } + + public String getIp() { + return get(VirtualServerProperty.VIRTUALSERVER_IP); + } + + public long getKeepAliveBytesReceived() { + return getLong(VirtualServerProperty.CONNECTION_BYTES_RECEIVED_KEEPALIVE); + } + + public long getKeepAliveBytesSent() { + return getLong(VirtualServerProperty.CONNECTION_BYTES_SENT_KEEPALIVE); + } + + public long getKeepAlivePacketsReceived() { + return getLong(VirtualServerProperty.CONNECTION_PACKETS_RECEIVED_KEEPALIVE); + } + + public long getKeepAlivePacketsSent() { + return getLong(VirtualServerProperty.CONNECTION_PACKETS_SENT_KEEPALIVE); + } + + public String getMachineId() { + return get(VirtualServerProperty.VIRTUALSERVER_MACHINE_ID); + } + + public long getMaxDownloadBandwidth() { + return getLong(VirtualServerProperty.VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH); + } + + public long getMaxUploadBandwidth() { + return getLong(VirtualServerProperty.VIRTUALSERVER_MAX_UPLOAD_TOTAL_BANDWIDTH); + } + + public int getMinClientsInChannelBeforeForcedSilence() { + return getInt(VirtualServerProperty.VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE); + } + + public int getMinimumClientVersion() { + return getInt(VirtualServerProperty.VIRTUALSERVER_MIN_CLIENT_VERSION); + } + + public long getMonthlyBytesDownloaded() { + return getLong(VirtualServerProperty.VIRTUALSERVER_MONTH_BYTES_DOWNLOADED); + } + + public long getMonthlyBytesUploaded() { + return getLong(VirtualServerProperty.VIRTUALSERVER_MONTH_BYTES_UPLOADED); + } + + public int getNeededIdentitySecurityLevel() { + return getInt(VirtualServerProperty.VIRTUALSERVER_NEEDED_IDENTITY_SECURITY_LEVEL); + } + + public String getPassword() { + return get(VirtualServerProperty.VIRTUALSERVER_PASSWORD); + } + + public String getPhoneticName() { + return get(VirtualServerProperty.VIRTUALSERVER_NAME_PHONETIC); + } + + public double getPing() { + return getDouble(VirtualServerProperty.VIRTUALSERVER_TOTAL_PING); + } + + public String getPlatform() { + return get(VirtualServerProperty.VIRTUALSERVER_PLATFORM); + } + + public double getPrioritySpeakerDimmModificator() { + return getDouble(VirtualServerProperty.VIRTUALSERVER_PRIORITY_SPEAKER_DIMM_MODIFICATOR); + } + + public int getReservedSlots() { + return getInt(VirtualServerProperty.VIRTUALSERVER_RESERVED_SLOTS); + } + + public long getSpeechBytesReceived() { + return getLong(VirtualServerProperty.CONNECTION_BYTES_RECEIVED_SPEECH); + } + + public long getSpeechBytesSent() { + return getLong(VirtualServerProperty.CONNECTION_BYTES_SENT_SPEECH); + } + + public long getSpeechPacketsReceived() { + return getLong(VirtualServerProperty.CONNECTION_PACKETS_RECEIVED_SPEECH); + } + + public long getSpeechPacketsSent() { + return getLong(VirtualServerProperty.CONNECTION_PACKETS_SENT_SPEECH); + } + + public long getTotalBytesDownloaded() { + return getLong(VirtualServerProperty.VIRTUALSERVER_TOTAL_BYTES_DOWNLOADED); + } + + public long getTotalBytesReceived() { + return getLong(VirtualServerProperty.CONNECTION_BYTES_RECEIVED_TOTAL); + } + + public long getTotalBytesSent() { + return getLong(VirtualServerProperty.CONNECTION_BYTES_SENT_TOTAL); + } + + public long getTotalBytesUploaded() { + return getLong(VirtualServerProperty.VIRTUALSERVER_TOTAL_BYTES_UPLOADED); + } + + public int getTotalClientConnections() { + return getInt(VirtualServerProperty.VIRTUALSERVER_CLIENT_CONNECTIONS); + } + + public double getTotalControlPacketloss() { + return getDouble(VirtualServerProperty.VIRTUALSERVER_TOTAL_PACKETLOSS_CONTROL); + } + + public double getTotalKeepAlivePacketloss() { + return getDouble(VirtualServerProperty.VIRTUALSERVER_TOTAL_PACKETLOSS_KEEPALIVE); + } + + public double getTotalPacketloss() { + return getDouble(VirtualServerProperty.VIRTUALSERVER_TOTAL_PACKETLOSS_TOTAL); + } + + public long getTotalPacketsReceived() { + return getLong(VirtualServerProperty.CONNECTION_PACKETS_RECEIVED_TOTAL); + } + + public long getTotalPacketsSent() { + return getLong(VirtualServerProperty.CONNECTION_PACKETS_SENT_TOTAL); + } + + public int getTotalQueryClientConnections() { + return getInt(VirtualServerProperty.VIRTUALSERVER_QUERY_CLIENT_CONNECTIONS); + } + + public double getTotalSpeechPacketloss() { + return getDouble(VirtualServerProperty.VIRTUALSERVER_TOTAL_PACKETLOSS_SPEECH); + } + + public long getUploadQuota() { + return getLong(VirtualServerProperty.VIRTUALSERVER_UPLOAD_QUOTA); + } + + public String getVersion() { + return get(VirtualServerProperty.VIRTUALSERVER_VERSION); + } + + public String getWelcomeMessage() { + return get(VirtualServerProperty.VIRTUALSERVER_WELCOMEMESSAGE); + } + + public boolean hasPassword() { + return getBoolean(VirtualServerProperty.VIRTUALSERVER_FLAG_PASSWORD); + } + + public boolean isAskingPrivilegeKey() { + return getBoolean(VirtualServerProperty.VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY); + } + + public boolean isLoggingChannel() { + return getBoolean(VirtualServerProperty.VIRTUALSERVER_LOG_CHANNEL); + } + + public boolean isLoggingClient() { + return getBoolean(VirtualServerProperty.VIRTUALSERVER_LOG_CLIENT); + } + + public boolean isLoggingFileTransfer() { + return getBoolean(VirtualServerProperty.VIRTUALSERVER_LOG_FILETRANSFER); + } + + public boolean isLoggingPermissions() { + return getBoolean(VirtualServerProperty.VIRTUALSERVER_LOG_PERMISSIONS); + } + + public boolean isLoggingQuery() { + return getBoolean(VirtualServerProperty.VIRTUALSERVER_LOG_QUERY); + } + + public boolean isLoggingServer() { + return getBoolean(VirtualServerProperty.VIRTUALSERVER_LOG_SERVER); + } + + public boolean isWeblistEnabled() { + return getBoolean(VirtualServerProperty.VIRTUALSERVER_WEBLIST_ENABLED); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/api/wrapper/Wrapper.java b/src/com/github/theholywaffle/teamspeak3/api/wrapper/Wrapper.java new file mode 100644 index 0000000..6c42b15 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/api/wrapper/Wrapper.java @@ -0,0 +1,217 @@ +package com.github.theholywaffle.teamspeak3.api.wrapper; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.Property; + +import java.util.Collections; +import java.util.Map; + +/** + * A wrapper class around a {@link Map}. + *

+ * We use wrapper classes instead of just passing around plain Maps because + *

    + *
  • it's more descriptive
  • + *
  • there's no confusion between types (e.g. {@code Client} and {@code Channel})
  • + *
  • we can create getters for the specific use-cases
  • + *
  • we don't have to check for {@code null} each time
  • + *
+ */ +public class Wrapper { + + public static final Wrapper EMPTY = new Wrapper(Collections.emptyMap()); + + private final Map map; + + /** + * Creates a new wrapper around the given map. + * + * @param map + * the Map to abstract, cannot be {@code null} + */ + public Wrapper(Map map) { + this.map = map; + } + + /** + * Gets the map underlying this {@code Wrapper}. + * + * @return the underlying map + */ + public Map getMap() { + return map; + } + + /** + * Gets a property as a boolean from the underlying map. + * Returns {@code true} if the property exists in the map and its value is {@code "1"}. + * + * @param propertyName + * the name of the property + * + * @return the boolean value of the property or {@code false} if the property doesn't exist + */ + public boolean getBoolean(String propertyName) { + return getInt(propertyName) == 1; + } + + /** + * Gets a property as a boolean from the underlying map. + * Returns {@code true} if the property exists in the map and its value is {@code "1"}. + * + * @param property + * the property + * + * @return the boolean value of the property or {@code false} if the property doesn't exist + */ + public boolean getBoolean(Property property) { + return getBoolean(property.getName()); + } + + /** + * Gets a property as a double from the underlying map. + * If the property doesn't exist in the underlying map, {@code -1.0} is returned. + * + * @param propertyName + * the name of the property + * + * @return the double value of the property or {@code -1.0} if the property doesn't exist + */ + public double getDouble(String propertyName) { + final String value = get(propertyName); + if (value == null || value.isEmpty()) { + return -1D; + } + return Double.valueOf(value); + } + + /** + * Gets a property as a double from the underlying map. + * If the property doesn't exist in the underlying map, {@code -1.0} is returned. + * + * @param property + * the property + * + * @return the double value of the property or {@code -1.0} if the property doesn't exist + */ + public double getDouble(Property property) { + return getDouble(property.getName()); + } + + /** + * Gets a property as a long from the underlying map. + * If the property doesn't exist in the underlying map, {@code -1} is returned. + * + * @param propertyName + * the name of the property + * + * @return the long value of the property or {@code -1} if the property doesn't exist + */ + public long getLong(String propertyName) { + final String value = get(propertyName); + if (value == null || value.isEmpty()) { + return -1L; + } + return Long.parseLong(value); + } + + /** + * Gets a property as a long from the underlying map. + * If the property doesn't exist in the underlying map, {@code -1} is returned. + * + * @param property + * the property + * + * @return the long value of the property or {@code -1} if the property doesn't exist + */ + public long getLong(Property property) { + return getLong(property.getName()); + } + + /** + * Gets a property as an integer from the underlying map. + * If the property doesn't exist in the underlying map, {@code -1} is returned. + * + * @param propertyName + * the name of the property + * + * @return the integer value of the property or {@code -1} if the property doesn't exist + */ + public int getInt(String propertyName) { + final String value = get(propertyName); + if (value == null || value.isEmpty()) { + return -1; + } + return Integer.parseInt(value); + } + + /** + * Gets a property as an integer from the underlying map. + * If the property doesn't exist in the underlying map, {@code -1} is returned. + * + * @param property + * the property + * + * @return the integer value of the property or {@code -1} if the property doesn't exist + */ + public int getInt(Property property) { + return getInt(property.getName()); + } + + /** + * Gets a property as a String from the underlying map. + * If the property doesn't exist in the underlying map, an empty String is returned. + * + * @param propertyName + * the name of the property + * + * @return the String value of the property or an empty String if the property doesn't exist + */ + public String get(String propertyName) { + final String result = map.get(propertyName); + return result != null ? result : ""; + } + + /** + * Gets a property as a {@code String} from the underlying map. + * If the property doesn't exist in the underlying map, an empty String is returned. + * + * @param property + * the property + * + * @return the String value of the property or an empty String if the property doesn't exist + */ + public String get(Property property) { + return get(property.getName()); + } + + @Override + public String toString() { + return map.toString(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/BanCommands.java b/src/com/github/theholywaffle/teamspeak3/commands/BanCommands.java new file mode 100644 index 0000000..72c4555 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/BanCommands.java @@ -0,0 +1,71 @@ +package com.github.theholywaffle.teamspeak3.commands; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2017 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.commands.parameter.KeyValueParam; + +public final class BanCommands { + + private BanCommands() { + throw new Error("No instances"); + } + + public static Command banAdd(String ip, String name, String uid, String myTSId, long timeInSeconds, String reason) { + if (ip == null && name == null && uid == null) { + throw new IllegalArgumentException("Either IP, name or UId must be non-null"); + } + + CommandBuilder builder = new CommandBuilder("banadd", 6); + builder.addIf(ip != null, new KeyValueParam("ip", ip)); + builder.addIf(name != null, new KeyValueParam("name", name)); + builder.addIf(uid != null, new KeyValueParam("uid", uid)); + builder.addIf(myTSId != null, new KeyValueParam("mytsid", myTSId)); + builder.addIf(timeInSeconds > 0, new KeyValueParam("time", timeInSeconds)); + builder.addIf(reason != null, new KeyValueParam("banreason", reason)); + return builder.build(); + } + + public static Command banClient(int clientId, long timeInSeconds, String reason) { + CommandBuilder builder = new CommandBuilder("banclient", 3); + builder.add(new KeyValueParam("clid", clientId)); + builder.addIf(timeInSeconds > 0, new KeyValueParam("time", timeInSeconds)); + builder.addIf(reason != null, new KeyValueParam("banreason", reason)); + return builder.build(); + } + + public static Command banDel(int banId) { + return new CommandBuilder("bandel", 1).add(new KeyValueParam("banid", banId)).build(); + } + + public static Command banDelAll() { + return new CommandBuilder("bandelall").build(); + } + + public static Command banList() { + return new CommandBuilder("banlist").build(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/ChannelCommands.java b/src/com/github/theholywaffle/teamspeak3/commands/ChannelCommands.java new file mode 100644 index 0000000..62c6c1c --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/ChannelCommands.java @@ -0,0 +1,96 @@ +package com.github.theholywaffle.teamspeak3.commands; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2017 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.ChannelProperty; +import com.github.theholywaffle.teamspeak3.commands.parameter.KeyValueParam; +import com.github.theholywaffle.teamspeak3.commands.parameter.OptionParam; + +import java.util.Map; + +public final class ChannelCommands { + + private ChannelCommands() { + throw new Error("No instances"); + } + + public static Command channelCreate(String name, Map options) { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("Channel name must be a non-empty string"); + } + + CommandBuilder builder = new CommandBuilder("channelcreate", 2); + builder.add(new KeyValueParam("channel_name", name)); + builder.addProperties(options); + return builder.build(); + } + + public static Command channelDelete(int channelId, boolean force) { + CommandBuilder builder = new CommandBuilder("channeldelete", 2); + builder.add(new KeyValueParam("cid", channelId)); + builder.add(new KeyValueParam("force", force)); + return builder.build(); + } + + public static Command channelEdit(int channelId, Map options) { + CommandBuilder builder = new CommandBuilder("channeledit", 2); + builder.add(new KeyValueParam("cid", channelId)); + builder.addProperties(options); + return builder.build(); + } + + public static Command channelFind(String pattern) { + if (pattern == null || pattern.isEmpty()) { + throw new IllegalArgumentException("Channel name pattern must be a non-empty string"); + } + + return new CommandBuilder("channelfind", 1).add(new KeyValueParam("pattern", pattern)).build(); + } + + public static Command channelInfo(int channelId) { + return new CommandBuilder("channelinfo", 1).add(new KeyValueParam("cid", channelId)).build(); + } + + public static Command channelList() { + CommandBuilder builder = new CommandBuilder("channellist", 6); + builder.add(new OptionParam("topic")); + builder.add(new OptionParam("flags")); + builder.add(new OptionParam("voice")); + builder.add(new OptionParam("limits")); + builder.add(new OptionParam("icon")); + builder.add(new OptionParam("secondsempty")); + return builder.build(); + } + + public static Command channelMove(int channelId, int channelParentId, int order) { + CommandBuilder builder = new CommandBuilder("channelmove", 3); + builder.add(new KeyValueParam("cid", channelId)); + builder.add(new KeyValueParam("cpid", channelParentId)); + builder.add(new KeyValueParam("order", order < 0 ? 0 : order)); + return builder.build(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/ChannelGroupCommands.java b/src/com/github/theholywaffle/teamspeak3/commands/ChannelGroupCommands.java new file mode 100644 index 0000000..bac522d --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/ChannelGroupCommands.java @@ -0,0 +1,112 @@ +package com.github.theholywaffle.teamspeak3.commands; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2017 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.PermissionGroupDatabaseType; +import com.github.theholywaffle.teamspeak3.commands.parameter.KeyValueParam; + +public final class ChannelGroupCommands { + + private ChannelGroupCommands() { + throw new Error("No instances"); + } + + public static Command channelGroupAdd(String groupName, PermissionGroupDatabaseType type) { + if (groupName == null || groupName.isEmpty()) { + throw new IllegalArgumentException("Channel group name must be a non-empty string"); + } + + CommandBuilder builder = new CommandBuilder("channelgroupadd", 2); + builder.add(new KeyValueParam("name", groupName)); + if (type != null) { + builder.add(new KeyValueParam("type", type.getIndex())); + } + return builder.build(); + } + + public static Command channelGroupClientList(int channelId, int clientDBId, int groupId) { + CommandBuilder builder = new CommandBuilder("channelgroupclientlist", 3); + builder.addIf(channelId > 0, new KeyValueParam("cid", channelId)); + builder.addIf(clientDBId > 0, new KeyValueParam("cldbid", clientDBId)); + builder.addIf(groupId > 0, new KeyValueParam("cgid", groupId)); + return builder.build(); + } + + public static Command channelGroupCopy(int sourceGroupId, int targetGroupId, PermissionGroupDatabaseType type) { + return channelGroupCopy(sourceGroupId, targetGroupId, "name", type); + } + + public static Command channelGroupCopy(int sourceGroupId, String groupName, PermissionGroupDatabaseType type) { + return channelGroupCopy(sourceGroupId, 0, groupName, type); + } + + private static Command channelGroupCopy(int sourceGroupId, int targetGroupId, String groupName, PermissionGroupDatabaseType type) { + if (type == null) { + throw new IllegalArgumentException("Group type cannot be null"); + } + if (groupName == null || groupName.isEmpty()) { + throw new IllegalArgumentException("Channel group name must be a non-empty string"); + } + + CommandBuilder builder = new CommandBuilder("channelgroupcopy", 4); + builder.add(new KeyValueParam("scgid", sourceGroupId)); + builder.add(new KeyValueParam("tcgid", targetGroupId)); + builder.add(new KeyValueParam("name", groupName)); + builder.add(new KeyValueParam("type", type.getIndex())); + return builder.build(); + } + + public static Command channelGroupDel(int channelGroupId, boolean forced) { + CommandBuilder builder = new CommandBuilder("channelgroupdel", 2); + builder.add(new KeyValueParam("cgid", channelGroupId)); + builder.add(new KeyValueParam("force", forced)); + return builder.build(); + } + + public static Command channelGroupList() { + return new CommandBuilder("channelgrouplist").build(); + } + + public static Command channelGroupRename(int groupId, String groupName) { + if (groupName == null || groupName.isEmpty()) { + throw new IllegalArgumentException("Channel group name must be a non-empty string"); + } + + CommandBuilder builder = new CommandBuilder("channelgrouprename", 2); + builder.add(new KeyValueParam("cgid", groupId)); + builder.add(new KeyValueParam("name", groupName)); + return builder.build(); + } + + public static Command setClientChannelGroup(int groupId, int channelId, int clientDBId) { + CommandBuilder builder = new CommandBuilder("setclientchannelgroup", 3); + builder.add(new KeyValueParam("cgid", groupId)); + builder.add(new KeyValueParam("cid", channelId)); + builder.add(new KeyValueParam("cldbid", clientDBId)); + return builder.build(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/ClientCommands.java b/src/com/github/theholywaffle/teamspeak3/commands/ClientCommands.java new file mode 100644 index 0000000..9197724 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/ClientCommands.java @@ -0,0 +1,161 @@ +package com.github.theholywaffle.teamspeak3.commands; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2017 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.ClientProperty; +import com.github.theholywaffle.teamspeak3.api.ReasonIdentifier; +import com.github.theholywaffle.teamspeak3.commands.parameter.ArrayParameter; +import com.github.theholywaffle.teamspeak3.commands.parameter.KeyValueParam; +import com.github.theholywaffle.teamspeak3.commands.parameter.OptionParam; + +import java.util.Map; + +public final class ClientCommands { + + private ClientCommands() { + throw new Error("No instances"); + } + + public static Command clientEdit(int clientId, Map options) { + CommandBuilder builder = new CommandBuilder("clientedit", 2); + builder.add(new KeyValueParam("clid", clientId)); + builder.addProperties(options); + return builder.build(); + } + + public static Command clientFind(String pattern) { + if (pattern == null || pattern.isEmpty()) { + throw new IllegalArgumentException("Client name pattern must be a non-empty string"); + } + + return new CommandBuilder("clientfind", 1).add(new KeyValueParam("pattern", pattern)).build(); + } + + public static Command clientGetDBIdFromUId(String clientUId) { + if (clientUId == null || clientUId.isEmpty()) { + throw new IllegalArgumentException("Client UId must be a non-empty string"); + } + + return new CommandBuilder("clientgetdbidfromuid", 1).add(new KeyValueParam("cluid", clientUId)).build(); + } + + public static Command clientGetIds(String clientUId) { + if (clientUId == null || clientUId.isEmpty()) { + throw new IllegalArgumentException("Client UId must be a non-empty string"); + } + + return new CommandBuilder("clientgetids", 1).add(new KeyValueParam("cluid", clientUId)).build(); + } + + public static Command clientInfo(int clientId) { + return new CommandBuilder("clientinfo", 1).add(new KeyValueParam("clid", clientId)).build(); + } + + public static Command clientKick(ReasonIdentifier reason, String reasonMessage, int... clientIds) { + if (clientIds == null || clientIds.length == 0) { + throw new IllegalArgumentException("Client ID array cannot be null or empty"); + } + + CommandBuilder builder = new CommandBuilder("clientkick", 3); + builder.add(new KeyValueParam("reasonid", reason.getIndex())); + builder.addIf(reasonMessage != null, new KeyValueParam("reasonmsg", reasonMessage)); + + ArrayParameter clients = new ArrayParameter(clientIds.length); + for (final int id : clientIds) { + clients.add(new KeyValueParam("clid", id)); + } + builder.add(clients); + + return builder.build(); + } + + public static Command clientList() { + CommandBuilder builder = new CommandBuilder("clientlist", 10); + builder.add(new OptionParam("uid")); + builder.add(new OptionParam("away")); + builder.add(new OptionParam("voice")); + builder.add(new OptionParam("times")); + builder.add(new OptionParam("groups")); + builder.add(new OptionParam("info")); + builder.add(new OptionParam("icon")); + builder.add(new OptionParam("country")); + builder.add(new OptionParam("ip")); + builder.add(new OptionParam("badges")); + return builder.build(); + } + + public static Command clientMove(int clientId, int channelId, String channelPassword) { + CommandBuilder builder = new CommandBuilder("clientmove", 3); + builder.add(new KeyValueParam("clid", clientId)); + builder.add(new KeyValueParam("cid", channelId)); + builder.addIf(channelPassword != null, new KeyValueParam("cpw", channelPassword)); + return builder.build(); + } + + public static Command clientMove(int[] clientIds, int channelId, String channelPassword) { + if (clientIds == null || clientIds.length == 0) { + throw new IllegalArgumentException("Client ID array cannot be null or empty"); + } + + CommandBuilder builder = new CommandBuilder("clientmove", 3); + builder.add(new KeyValueParam("cid", channelId)); + builder.addIf(channelPassword != null, new KeyValueParam("cpw", channelPassword)); + + ArrayParameter clients = new ArrayParameter(clientIds.length); + for (final int clientId : clientIds) { + clients.add(new KeyValueParam("clid", clientId)); + } + builder.add(clients); + + return builder.build(); + } + + public static Command clientPoke(int clientId, String message) { + CommandBuilder builder = new CommandBuilder("clientpoke", 2); + builder.add(new KeyValueParam("clid", clientId)); + builder.add(new KeyValueParam("msg", message)); + return builder.build(); + } + + public static Command clientSetServerQueryLogin(String username) { + CommandBuilder builder = new CommandBuilder("clientsetserverquerylogin", 1); + builder.add(new KeyValueParam("client_login_name", username)); + return builder.build(); + } + + public static Command clientUpdate(Map options) { + return new CommandBuilder("clientupdate", 1).addProperties(options).build(); + } + + public static Command sendTextMessage(int targetMode, int targetId, String message) { + CommandBuilder builder = new CommandBuilder("sendtextmessage", 3); + builder.add(new KeyValueParam("targetmode", targetMode)); + builder.add(new KeyValueParam("target", targetId)); + builder.add(new KeyValueParam("msg", message)); + return builder.build(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/Command.java b/src/com/github/theholywaffle/teamspeak3/commands/Command.java new file mode 100644 index 0000000..d5906e8 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/Command.java @@ -0,0 +1,64 @@ +package com.github.theholywaffle.teamspeak3.commands; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2017 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.CommandFuture; +import com.github.theholywaffle.teamspeak3.commands.parameter.Parameter; +import com.github.theholywaffle.teamspeak3.commands.response.DefaultArrayResponse; + +import java.util.Collection; + +public class Command { + + private final String name; + private final Collection parameters; + private final CommandFuture future; + + Command(String commandName, Collection parameters) { + this.name = commandName; + this.parameters = parameters; + this.future = new CommandFuture<>(); + } + + public String getName() { + return name; + } + + public CommandFuture getFuture() { + return future; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(name); + for (Parameter param : parameters) { + builder.append(' '); + param.appendTo(builder); + } + return builder.toString(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/CommandBuilder.java b/src/com/github/theholywaffle/teamspeak3/commands/CommandBuilder.java new file mode 100644 index 0000000..1f7b874 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/CommandBuilder.java @@ -0,0 +1,89 @@ +package com.github.theholywaffle.teamspeak3.commands; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2017 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.Property; +import com.github.theholywaffle.teamspeak3.commands.parameter.ArrayParameter; +import com.github.theholywaffle.teamspeak3.commands.parameter.KeyValueParam; +import com.github.theholywaffle.teamspeak3.commands.parameter.Parameter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +final class CommandBuilder { + + private final String commandName; + private final Collection parameters; + + public CommandBuilder(String commandName) { + this(commandName, 0); + } + + public CommandBuilder(String commandName, int expectedParamCount) { + this.commandName = commandName; + this.parameters = new ArrayList<>(expectedParamCount); + } + + public CommandBuilder add(Parameter parameter) { + parameters.add(parameter); + return this; + } + + public CommandBuilder addIf(boolean test, Parameter parameter) { + if (test) { + parameters.add(parameter); + } + return this; + } + + public CommandBuilder addProperties(Map properties) { + if (properties == null || properties.isEmpty()) return this; + + ArrayParameter parameter = new ArrayParameter(1, properties.size()); + for (Map.Entry entry : properties.entrySet()) { + Property property = entry.getKey(); + String value = entry.getValue(); + + if (property == null) { + throw new IllegalArgumentException("Property cannot be null"); + } else if (!property.isChangeable()) { + String className = property.getClass().getSimpleName(); + throw new IllegalArgumentException(className + " " + property.getName() + " is not changeable"); + } + + parameter.add(new KeyValueParam(property.getName(), value)); + } + parameters.add(parameter); + + return this; + } + + public Command build() { + return new Command(commandName, parameters); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/CommandEncoding.java b/src/com/github/theholywaffle/teamspeak3/commands/CommandEncoding.java new file mode 100644 index 0000000..87a14a3 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/CommandEncoding.java @@ -0,0 +1,66 @@ +package com.github.theholywaffle.teamspeak3.commands; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2015 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public final class CommandEncoding { + + private CommandEncoding() {} + + public static String encode(String str) { + str = str.replace("\\", "\\\\"); + + str = str.replace(" ", "\\s"); + str = str.replace("/", "\\/"); + str = str.replace("|", "\\p"); + str = str.replace("\b", "\\b"); + str = str.replace("\f", "\\f"); + str = str.replace("\n", "\\n"); + str = str.replace("\r", "\\r"); + str = str.replace("\t", "\\t"); + str = str.replace(String.valueOf((char) 7), "\\a"); + str = str.replace(String.valueOf((char) 11), "\\v"); + + return str; + } + + public static String decode(String str) { + str = str.replace("\\s", " "); + str = str.replace("\\/", "/"); + str = str.replace("\\p", "|"); + str = str.replace("\\b", "\b"); + str = str.replace("\\f", "\f"); + str = str.replace("\\n", "\n"); + str = str.replace("\\r", "\r"); + str = str.replace("\\t", "\t"); + str = str.replace("\\a", String.valueOf((char) 7)); // Bell + str = str.replace("\\v", String.valueOf((char) 11)); // Vertical Tab + + str = str.replace("\\\\", "\\"); + + return str; + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/ComplaintCommands.java b/src/com/github/theholywaffle/teamspeak3/commands/ComplaintCommands.java new file mode 100644 index 0000000..2d4e938 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/ComplaintCommands.java @@ -0,0 +1,58 @@ +package com.github.theholywaffle.teamspeak3.commands; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2017 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.commands.parameter.KeyValueParam; + +public final class ComplaintCommands { + + private ComplaintCommands() { + throw new Error("No instances"); + } + + public static Command complainAdd(int clientDBId, String message) { + CommandBuilder builder = new CommandBuilder("complainadd", 2); + builder.add(new KeyValueParam("tcldbid", clientDBId)); + builder.add(new KeyValueParam("message", message)); + return builder.build(); + } + + public static Command complainDel(int clientDBId, int fromClientDBId) { + CommandBuilder builder = new CommandBuilder("complaindel", 2); + builder.add(new KeyValueParam("tcldbid", clientDBId)); + builder.add(new KeyValueParam("fcldbid", fromClientDBId)); + return builder.build(); + } + + public static Command complainDelAll(int clientDBId) { + return new CommandBuilder("complaindelall", 1).add(new KeyValueParam("tcldbid", clientDBId)).build(); + } + + public static Command complainList(int clientDBId) { + return new CommandBuilder("complainlist", 1).addIf(clientDBId > 0, new KeyValueParam("tcldbid", clientDBId)).build(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/CustomPropertyCommands.java b/src/com/github/theholywaffle/teamspeak3/commands/CustomPropertyCommands.java new file mode 100644 index 0000000..17601bf --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/CustomPropertyCommands.java @@ -0,0 +1,62 @@ +package com.github.theholywaffle.teamspeak3.commands; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2018 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.commands.parameter.KeyValueParam; + +public final class CustomPropertyCommands { + + private CustomPropertyCommands() { + throw new Error("No instances"); + } + + public static Command customDelete(int clientDBId, String key) { + CommandBuilder builder = new CommandBuilder("customdelete", 2); + builder.add(new KeyValueParam("cldbid", clientDBId)); + builder.add(new KeyValueParam("ident", key)); + return builder.build(); + } + + public static Command customInfo(int clientDBId) { + return new CommandBuilder("custominfo", 1).add(new KeyValueParam("cldbid", clientDBId)).build(); + } + + public static Command customSet(int clientDBId, String key, String value) { + CommandBuilder builder = new CommandBuilder("customset", 3); + builder.add(new KeyValueParam("cldbid", clientDBId)); + builder.add(new KeyValueParam("ident", key)); + builder.add(new KeyValueParam("value", value)); + return builder.build(); + } + + public static Command customSearch(String key, String pattern) { + CommandBuilder builder = new CommandBuilder("customsearch", 2); + builder.add(new KeyValueParam("ident", key)); + builder.add(new KeyValueParam("pattern", pattern)); + return builder.build(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/DatabaseClientCommands.java b/src/com/github/theholywaffle/teamspeak3/commands/DatabaseClientCommands.java new file mode 100644 index 0000000..6c693b7 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/DatabaseClientCommands.java @@ -0,0 +1,74 @@ +package com.github.theholywaffle.teamspeak3.commands; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2017 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.ClientProperty; +import com.github.theholywaffle.teamspeak3.commands.parameter.KeyValueParam; +import com.github.theholywaffle.teamspeak3.commands.parameter.OptionParam; + +import java.util.Map; + +public final class DatabaseClientCommands { + + private DatabaseClientCommands() { + throw new Error("No instances"); + } + + public static Command clientDBDelete(int clientDBId) { + return new CommandBuilder("clientdbdelete", 1).add(new KeyValueParam("cldbid", clientDBId)).build(); + } + + public static Command clientDBEdit(int clientDBId, Map options) { + CommandBuilder builder = new CommandBuilder("clientdbedit", 2); + builder.add(new KeyValueParam("cldbid", clientDBId)); + builder.addProperties(options); + return builder.build(); + } + + public static Command clientDBFind(String pattern, boolean uid) { + if (pattern == null || pattern.isEmpty()) { + throw new IllegalArgumentException("Pattern must be a non-empty string"); + } + + CommandBuilder builder = new CommandBuilder("clientdbfind", 2); + builder.add(new KeyValueParam("pattern", pattern)); + builder.addIf(uid, new OptionParam("uid")); + return builder.build(); + } + + public static Command clientDBInfo(int clientDBId) { + return new CommandBuilder("clientdbinfo", 1).add(new KeyValueParam("cldbid", clientDBId)).build(); + } + + public static Command clientDBList(int begin, int amount, boolean count) { + CommandBuilder builder = new CommandBuilder("clientdblist", 3); + builder.add(new KeyValueParam("start", begin)); + builder.add(new KeyValueParam("duration", amount)); + builder.addIf(count, new OptionParam("count")); + return builder.build(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/FileCommands.java b/src/com/github/theholywaffle/teamspeak3/commands/FileCommands.java new file mode 100644 index 0000000..e7ecfd5 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/FileCommands.java @@ -0,0 +1,191 @@ +package com.github.theholywaffle.teamspeak3.commands; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2017 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.commands.parameter.ArrayParameter; +import com.github.theholywaffle.teamspeak3.commands.parameter.KeyValueParam; + +public final class FileCommands { + + private FileCommands() { + throw new Error("No instances"); + } + + public static Command ftCreateDir(String path, int channelId, String channelPassword) { + if (path == null) throw new IllegalArgumentException("File path cannot be null"); + + CommandBuilder builder = new CommandBuilder("ftcreatedir", 3); + builder.add(new KeyValueParam("cid", channelId)); + builder.add(new KeyValueParam("cpw", channelPassword)); + builder.add(new KeyValueParam("dirname", path.startsWith("/") ? path : "/" + path)); + return builder.build(); + } + + public static Command ftDeleteFile(int channelId, String channelPassword, String... filePaths) { + if (filePaths == null || filePaths.length == 0) { + throw new IllegalArgumentException("File array cannot be null or empty"); + } + + CommandBuilder builder = new CommandBuilder("ftdeletefile", 3); + builder.add(new KeyValueParam("cid", channelId)); + builder.add(new KeyValueParam("cpw", channelPassword)); + + ArrayParameter files = new ArrayParameter(filePaths.length); + for (String filePath : filePaths) { + if (filePath == null) throw new IllegalArgumentException("File path cannot be null"); + files.add(new KeyValueParam("name", filePath.startsWith("/") ? filePath : "/" + filePath)); + } + builder.add(files); + + return builder.build(); + } + + public static Command ftGetFileInfo(int channelId, String channelPassword, String... filePaths) { + if (filePaths == null || filePaths.length == 0) { + throw new IllegalArgumentException("File array cannot be null or empty"); + } + + CommandBuilder builder = new CommandBuilder("ftgetfileinfo", 1); + ArrayParameter files = new ArrayParameter(filePaths.length, 3); + for (String filePath : filePaths) { + if (filePath == null) throw new IllegalArgumentException("File path cannot be null"); + files.add(new KeyValueParam("cid", channelId)); + files.add(new KeyValueParam("cpw", channelPassword)); + files.add(new KeyValueParam("name", filePath)); + } + builder.add(files); + + return builder.build(); + } + + public static Command ftGetFileInfo(int[] channelIds, String[] channelPasswords, String[] filePaths) { + if (channelIds == null || channelIds.length == 0) { + throw new IllegalArgumentException("Channel ID array cannot be null or empty"); + } + if (filePaths == null || filePaths.length == 0) { + throw new IllegalArgumentException("File array cannot be null or empty"); + } + if (channelIds.length != filePaths.length) { + throw new IllegalArgumentException("Channel IDs length doesn't match file paths length"); + } + if (channelPasswords != null && filePaths.length != channelPasswords.length) { + throw new IllegalArgumentException("Passwords length doesn't match file paths length"); + } + + CommandBuilder builder = new CommandBuilder("ftgetfileinfo", 1); + ArrayParameter files = new ArrayParameter(filePaths.length, 3); + for (int i = 0; i < filePaths.length; ++i) { + final String password = channelPasswords == null ? null : channelPasswords[i]; + final String path = filePaths[i]; + if (path == null) throw new IllegalArgumentException("File path cannot be null"); + + files.add(new KeyValueParam("cid", channelIds[i])); + files.add(new KeyValueParam("cpw", password)); + files.add(new KeyValueParam("name", path.startsWith("/") ? path : "/" + path)); + } + builder.add(files); + + return builder.build(); + } + + public static Command ftGetFileList(String directoryPath, int channelId, String channelPassword) { + if (directoryPath == null) throw new IllegalArgumentException("Directory path cannot be null"); + + CommandBuilder builder = new CommandBuilder("ftgetfilelist", 3); + builder.add(new KeyValueParam("cid", channelId)); + builder.add(new KeyValueParam("cpw", channelPassword)); + + String path = directoryPath; // Make sure path starts and ends with / + if (!path.startsWith("/")) path = "/" + path; + if (!path.endsWith("/")) path += "/"; + builder.add(new KeyValueParam("path", path)); + + return builder.build(); + } + + public static Command ftInitDownload(int transferId, String path, int channelId, String channelPassword) { + if (path == null) throw new IllegalArgumentException("File path cannot be null"); + + CommandBuilder builder = new CommandBuilder("ftinitdownload", 6); + builder.add(new KeyValueParam("clientftfid", transferId)); + builder.add(new KeyValueParam("name", path.startsWith("/") ? path : "/" + path)); + builder.add(new KeyValueParam("cid", channelId)); + builder.add(new KeyValueParam("cpw", channelPassword)); + builder.add(new KeyValueParam("seekpos", 0)); + builder.add(new KeyValueParam("proto", 0)); // Use current (old) protocol for as long as possible + return builder.build(); + } + + public static Command ftInitUpload(int transferId, String path, int channelId, String channelPassword, + long size, boolean overwrite) { + if (path == null) throw new IllegalArgumentException("File path cannot be null"); + + CommandBuilder builder = new CommandBuilder("ftinitupload", 8); + builder.add(new KeyValueParam("clientftfid", transferId)); + builder.add(new KeyValueParam("name", path.startsWith("/") ? path : "/" + path)); + builder.add(new KeyValueParam("cid", channelId)); + builder.add(new KeyValueParam("cpw", channelPassword)); + builder.add(new KeyValueParam("size", size)); + builder.add(new KeyValueParam("overwrite", overwrite)); + builder.add(new KeyValueParam("resume", 0)); + builder.add(new KeyValueParam("proto", 0)); // Use current (old) protocol for as long as possible + return builder.build(); + } + + public static Command ftList() { + return new CommandBuilder("ftlist").build(); + } + + public static Command ftRenameFile(String oldPath, String newPath, + int channelId, String channelPassword) { + if (oldPath == null) throw new IllegalArgumentException("Old file path cannot be null"); + if (newPath == null) throw new IllegalArgumentException("New file path cannot be null"); + + CommandBuilder builder = new CommandBuilder("ftrenamefile", 4); + builder.add(new KeyValueParam("cid", channelId)); + builder.add(new KeyValueParam("cpw", channelPassword)); + builder.add(new KeyValueParam("oldname", oldPath.startsWith("/") ? oldPath : "/" + oldPath)); + builder.add(new KeyValueParam("newname", newPath.startsWith("/") ? newPath : "/" + newPath)); + return builder.build(); + } + + public static Command ftRenameFile(String oldPath, String newPath, + int oldChannelId, String oldChannelPassword, + int newChannelId, String newChannelPassword) { + if (oldPath == null) throw new IllegalArgumentException("Old file path cannot be null"); + if (newPath == null) throw new IllegalArgumentException("New file path cannot be null"); + + CommandBuilder builder = new CommandBuilder("ftrenamefile", 6); + builder.add(new KeyValueParam("cid", oldChannelId)); + builder.add(new KeyValueParam("cpw", oldChannelPassword)); + builder.add(new KeyValueParam("tcid", newChannelId)); + builder.add(new KeyValueParam("tcpw", newChannelPassword)); + builder.add(new KeyValueParam("oldname", oldPath.startsWith("/") ? oldPath : "/" + oldPath)); + builder.add(new KeyValueParam("newname", newPath.startsWith("/") ? newPath : "/" + newPath)); + return builder.build(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/MessageCommands.java b/src/com/github/theholywaffle/teamspeak3/commands/MessageCommands.java new file mode 100644 index 0000000..584ae76 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/MessageCommands.java @@ -0,0 +1,67 @@ +package com.github.theholywaffle.teamspeak3.commands; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2017 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.commands.parameter.KeyValueParam; + +public final class MessageCommands { + + private MessageCommands() { + throw new Error("No instances"); + } + + public static Command messageAdd(String clientUId, String subject, String message) { + if (clientUId == null || clientUId.isEmpty()) { + throw new IllegalArgumentException("Client UId must be a non-empty string"); + } + + CommandBuilder builder = new CommandBuilder("messageadd", 3); + builder.add(new KeyValueParam("cluid", clientUId)); + builder.add(new KeyValueParam("subject", subject)); + builder.add(new KeyValueParam("message", message)); + return builder.build(); + } + + public static Command messageDel(int messageId) { + return new CommandBuilder("messagedel", 1).add(new KeyValueParam("msgid", messageId)).build(); + } + + public static Command messageGet(int messageId) { + return new CommandBuilder("messageget", 1).add(new KeyValueParam("msgid", messageId)).build(); + } + + public static Command messageList() { + return new CommandBuilder("messagelist").build(); + } + + public static Command messageUpdateFlag(int messageId, boolean read) { + CommandBuilder builder = new CommandBuilder("messageupdateflag", 2); + builder.add(new KeyValueParam("msgid", messageId)); + builder.add(new KeyValueParam("flag", read)); + return builder.build(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/PermissionCommands.java b/src/com/github/theholywaffle/teamspeak3/commands/PermissionCommands.java new file mode 100644 index 0000000..09d848d --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/PermissionCommands.java @@ -0,0 +1,304 @@ +package com.github.theholywaffle.teamspeak3.commands; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2017 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.ServerGroupType; +import com.github.theholywaffle.teamspeak3.commands.parameter.ArrayParameter; +import com.github.theholywaffle.teamspeak3.commands.parameter.KeyValueParam; +import com.github.theholywaffle.teamspeak3.commands.parameter.OptionParam; + +public final class PermissionCommands { + + private PermissionCommands() { + throw new Error("No instances"); + } + + /* + * General commands + */ + + public static Command permFind(String permName) { + nonEmptyPermissionName(permName); + + return new CommandBuilder("permfind", 1).add(new KeyValueParam("permsid", permName)).build(); + } + + public static Command permGet(String... permNames) { + nonEmptyPermissionArray(permNames); + + CommandBuilder builder = new CommandBuilder("permget", 1); + + ArrayParameter permissions = new ArrayParameter(permNames.length); + for (String permName : permNames) { + nonEmptyPermissionName(permName); + permissions.add(new KeyValueParam("permsid", permName)); + } + builder.add(permissions); + + return builder.build(); + } + + public static Command permIdGetByName(String permName) { + nonEmptyPermissionName(permName); + + return new CommandBuilder("permidgetbyname", 1).add(new KeyValueParam("permsid", permName)).build(); + } + + public static Command permIdGetByName(String... permNames) { + nonEmptyPermissionArray(permNames); + + CommandBuilder builder = new CommandBuilder("permidgetbyname", 1); + + ArrayParameter permissions = new ArrayParameter(permNames.length); + for (String permName : permNames) { + nonEmptyPermissionName(permName); + permissions.add(new KeyValueParam("permsid", permName)); + } + builder.add(permissions); + + return builder.build(); + } + + public static Command permissionList() { + return new CommandBuilder("permissionlist").build(); + } + + public static Command permOverview(int channelId, int clientDBId) { + CommandBuilder builder = new CommandBuilder("permoverview", 3); + builder.add(new KeyValueParam("cid", channelId)); + builder.add(new KeyValueParam("cldbid", clientDBId)); + builder.add(new KeyValueParam("permid", 0)); + return builder.build(); + } + + public static Command permReset() { + return new CommandBuilder("permreset").build(); + } + + /* + * Client commands + */ + + public static Command clientAddPerm(int clientDBId, String permName, int permValue, boolean skip) { + nonEmptyPermissionName(permName); + + CommandBuilder builder = new CommandBuilder("clientaddperm", 4); + builder.add(new KeyValueParam("cldbid", clientDBId)); + builder.add(new KeyValueParam("permsid", permName)); + builder.add(new KeyValueParam("permvalue", permValue)); + builder.add(new KeyValueParam("permskip", skip)); + return builder.build(); + } + + public static Command clientDelPerm(int clientDBId, String permName) { + nonEmptyPermissionName(permName); + + CommandBuilder builder = new CommandBuilder("clientdelperm", 2); + builder.add(new KeyValueParam("cldbid", clientDBId)); + builder.add(new KeyValueParam("permsid", permName)); + return builder.build(); + } + + public static Command clientPermList(int clientDBId) { + CommandBuilder builder = new CommandBuilder("clientpermlist", 2); + builder.add(new KeyValueParam("cldbid", clientDBId)); + builder.add(new OptionParam("permsid")); + return builder.build(); + } + + /* + * Channel commands + */ + + public static Command channelAddPerm(int channelId, String permName, int permValue) { + nonEmptyPermissionName(permName); + + CommandBuilder builder = new CommandBuilder("channeladdperm", 3); + builder.add(new KeyValueParam("cid", channelId)); + builder.add(new KeyValueParam("permsid", permName)); + builder.add(new KeyValueParam("permvalue", permValue)); + return builder.build(); + } + + public static Command channelDelPerm(int channelId, String permName) { + nonEmptyPermissionName(permName); + + CommandBuilder builder = new CommandBuilder("channeldelperm", 2); + builder.add(new KeyValueParam("cid", channelId)); + builder.add(new KeyValueParam("permsid", permName)); + return builder.build(); + } + + public static Command channelPermList(int channelId) { + CommandBuilder builder = new CommandBuilder("channelpermlist", 2); + builder.add(new KeyValueParam("cid", channelId)); + builder.add(new OptionParam("permsid")); + return builder.build(); + } + + /* + * Channel client commands + */ + + public static Command channelClientAddPerm(int channelId, int clientDBId, String permName, int permValue) { + nonEmptyPermissionName(permName); + + CommandBuilder builder = new CommandBuilder("channelclientaddperm", 4); + builder.add(new KeyValueParam("cid", channelId)); + builder.add(new KeyValueParam("cldbid", clientDBId)); + builder.add(new KeyValueParam("permsid", permName)); + builder.add(new KeyValueParam("permvalue", permValue)); + return builder.build(); + } + + public static Command channelClientDelPerm(int channelId, int clientDBId, String permName) { + nonEmptyPermissionName(permName); + + CommandBuilder builder = new CommandBuilder("channelclientdelperm", 3); + builder.add(new KeyValueParam("cid", channelId)); + builder.add(new KeyValueParam("cldbid", clientDBId)); + builder.add(new KeyValueParam("permsid", permName)); + return builder.build(); + } + + public static Command channelClientPermList(int channelId, int clientDBId) { + CommandBuilder builder = new CommandBuilder("channelclientpermlist", 3); + builder.add(new KeyValueParam("cid", channelId)); + builder.add(new KeyValueParam("cldbid", clientDBId)); + builder.add(new OptionParam("permsid")); + return builder.build(); + } + + /* + * Channel group commands + */ + + public static Command channelGroupAddPerm(int groupId, String permName, int permValue) { + nonEmptyPermissionName(permName); + + CommandBuilder builder = new CommandBuilder("channelgroupaddperm", 3); + builder.add(new KeyValueParam("cgid", groupId)); + builder.add(new KeyValueParam("permsid", permName)); + builder.add(new KeyValueParam("permvalue", permValue)); + return builder.build(); + } + + public static Command channelGroupDelPerm(int groupId, String permName) { + nonEmptyPermissionName(permName); + + CommandBuilder builder = new CommandBuilder("channelgroupdelperm", 2); + builder.add(new KeyValueParam("cgid", groupId)); + builder.add(new KeyValueParam("permsid", permName)); + return builder.build(); + } + + public static Command channelGroupPermList(int groupId) { + CommandBuilder builder = new CommandBuilder("channelgrouppermlist", 2); + builder.add(new KeyValueParam("cgid", groupId)); + builder.add(new OptionParam("permsid")); + return builder.build(); + } + + /* + * Server group commands + */ + + public static Command serverGroupAddPerm(int groupId, String permName, int permValue, + boolean negate, boolean skip) { + nonEmptyPermissionName(permName); + + CommandBuilder builder = new CommandBuilder("servergroupaddperm", 5); + builder.add(new KeyValueParam("sgid", groupId)); + builder.add(new KeyValueParam("permsid", permName)); + builder.add(new KeyValueParam("permvalue", permValue)); + builder.add(new KeyValueParam("permnegated", negate)); + builder.add(new KeyValueParam("permskip", skip)); + return builder.build(); + } + + public static Command serverGroupAutoAddPerm(ServerGroupType type, String permName, int permValue, + boolean negate, boolean skip) { + nonNullServerGroupType(type); + nonEmptyPermissionName(permName); + + CommandBuilder builder = new CommandBuilder("servergroupautoaddperm", 5); + builder.add(new KeyValueParam("sgtype", type.getIndex())); + builder.add(new KeyValueParam("permsid", permName)); + builder.add(new KeyValueParam("permvalue", permValue)); + builder.add(new KeyValueParam("permnegated", negate)); + builder.add(new KeyValueParam("permskip", skip)); + return builder.build(); + } + + public static Command serverGroupDelPerm(int groupId, String permName) { + nonEmptyPermissionName(permName); + + CommandBuilder builder = new CommandBuilder("servergroupdelperm", 2); + builder.add(new KeyValueParam("sgid", groupId)); + builder.add(new KeyValueParam("permsid", permName)); + return builder.build(); + } + + public static Command serverGroupAutoDelPerm(ServerGroupType type, String permName) { + nonNullServerGroupType(type); + nonEmptyPermissionName(permName); + + CommandBuilder builder = new CommandBuilder("servergroupautodelperm", 2); + builder.add(new KeyValueParam("sgtype", type.getIndex())); + builder.add(new KeyValueParam("permsid", permName)); + return builder.build(); + } + + public static Command serverGroupPermList(int groupId) { + CommandBuilder builder = new CommandBuilder("servergrouppermlist", 2); + builder.add(new KeyValueParam("sgid", groupId)); + builder.add(new OptionParam("permsid")); + return builder.build(); + } + + /* + * Validation + */ + + private static void nonEmptyPermissionName(String permName) { + if (permName == null || permName.isEmpty()) { + throw new IllegalArgumentException("Permission name must be a non-empty string"); + } + } + + private static void nonEmptyPermissionArray(String[] permNames) { + if (permNames == null || permNames.length == 0) { + throw new IllegalArgumentException("Permission name array cannot be null or empty"); + } + } + + private static void nonNullServerGroupType(ServerGroupType type) { + if (type == null) { + throw new IllegalArgumentException("Server group type cannot be null"); + } + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/PrivilegeKeyCommands.java b/src/com/github/theholywaffle/teamspeak3/commands/PrivilegeKeyCommands.java new file mode 100644 index 0000000..2fefd1d --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/PrivilegeKeyCommands.java @@ -0,0 +1,70 @@ +package com.github.theholywaffle.teamspeak3.commands; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2017 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.PrivilegeKeyType; +import com.github.theholywaffle.teamspeak3.commands.parameter.KeyValueParam; + +public final class PrivilegeKeyCommands { + + private PrivilegeKeyCommands() { + throw new Error("No instances"); + } + + public static Command privilegeKeyAdd(PrivilegeKeyType type, int groupId, int channelId, String description) { + if (type == null) { + throw new IllegalArgumentException("Privilege key type cannot be null"); + } + + CommandBuilder builder = new CommandBuilder("privilegekeyadd", 4); + builder.add(new KeyValueParam("tokentype", type.getIndex())); + builder.add(new KeyValueParam("tokenid1", groupId)); + builder.add(new KeyValueParam("tokenid2", channelId)); + builder.addIf(description != null, new KeyValueParam("tokendescription", description)); + return builder.build(); + } + + public static Command privilegeKeyDelete(String token) { + if (token == null || token.isEmpty()) { + throw new IllegalArgumentException("Privilege key must be a non-empty string"); + } + + return new CommandBuilder("privilegekeydelete", 1).add(new KeyValueParam("token", token)).build(); + } + + public static Command privilegeKeyList() { + return new CommandBuilder("privilegekeylist").build(); + } + + public static Command privilegeKeyUse(String token) { + if (token == null || token.isEmpty()) { + throw new IllegalArgumentException("Privilege key must be a non-empty string"); + } + + return new CommandBuilder("privilegekeyuse", 1).add(new KeyValueParam("token", token)).build(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/QueryCommands.java b/src/com/github/theholywaffle/teamspeak3/commands/QueryCommands.java new file mode 100644 index 0000000..e379238 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/QueryCommands.java @@ -0,0 +1,93 @@ +package com.github.theholywaffle.teamspeak3.commands; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2017 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.event.TS3EventType; +import com.github.theholywaffle.teamspeak3.commands.parameter.KeyValueParam; +import com.github.theholywaffle.teamspeak3.commands.parameter.OptionParam; +import com.github.theholywaffle.teamspeak3.commands.parameter.ValueParam; + +public final class QueryCommands { + + private QueryCommands() { + throw new Error("No instances"); + } + + public static Command logIn(String username, String password) { + if (username == null || username.isEmpty()) { + throw new IllegalArgumentException("Username must be a non-empty string"); + } + if (password == null || password.isEmpty()) { + throw new IllegalArgumentException("Password must be a non-empty string"); + } + + return new CommandBuilder("login", 2).add(new ValueParam(username)).add(new ValueParam(password)).build(); + } + + public static Command logOut() { + return new CommandBuilder("logout").build(); + } + + public static Command quit() { + return new CommandBuilder("quit").build(); + } + + public static Command serverNotifyRegister(TS3EventType eventType, int channelId) { + if (eventType == null) { + throw new IllegalArgumentException("Event type cannot be null"); + } + + CommandBuilder builder = new CommandBuilder("servernotifyregister", 2); + builder.add(new KeyValueParam("event", eventType.toString())); + builder.addIf(channelId >= 0, new KeyValueParam("id", channelId)); + return builder.build(); + } + + public static Command serverNotifyUnregister() { + return new CommandBuilder("servernotifyunregister").build(); + } + + public static Command useId(int id, String nickname) { + CommandBuilder builder = new CommandBuilder("use", 3); + builder.add(new KeyValueParam("sid", id)); + builder.add(new OptionParam("virtual")); + builder.addIf(nickname != null, new KeyValueParam("client_nickname", nickname)); + return builder.build(); + } + + public static Command usePort(int port, String nickname) { + CommandBuilder builder = new CommandBuilder("use", 3); + builder.add(new KeyValueParam("port", port)); + builder.add(new OptionParam("virtual")); + builder.addIf(nickname != null, new KeyValueParam("client_nickname", nickname)); + return builder.build(); + } + + public static Command whoAmI() { + return new CommandBuilder("whoami").build(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/ServerCommands.java b/src/com/github/theholywaffle/teamspeak3/commands/ServerCommands.java new file mode 100644 index 0000000..0cd1c8b --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/ServerCommands.java @@ -0,0 +1,82 @@ +package com.github.theholywaffle.teamspeak3.commands; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2017 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.ServerInstanceProperty; +import com.github.theholywaffle.teamspeak3.commands.parameter.KeyValueParam; + +import java.util.Map; + +public final class ServerCommands { + + private ServerCommands() { + throw new Error("No instances"); + } + + public static Command bindingList() { + return new CommandBuilder("bindinglist").build(); + } + + public static Command gm(String message) { + if (message == null || message.isEmpty()) { + throw new IllegalArgumentException("Message must be a non-empty string"); + } + + return new CommandBuilder("gm", 1).add(new KeyValueParam("msg", message)).build(); + } + + public static Command hostInfo() { + return new CommandBuilder("hostinfo").build(); + } + + public static Command instanceInfo() { + return new CommandBuilder("instanceinfo").build(); + } + + public static Command instanceEdit(Map options) { + return new CommandBuilder("instanceedit", 1).addProperties(options).build(); + } + + public static Command logView(int lines, boolean instance) { + if (lines > 100) throw new IllegalArgumentException("Can only fetch up to 100 lines at once (" + lines + ")"); + + CommandBuilder builder = new CommandBuilder("logview", 2); + builder.addIf(lines > 0, new KeyValueParam("lines", lines)); + builder.addIf(instance, new KeyValueParam("instance", 1)); + return builder.build(); + } + + public static Command serverProcessStop(String reason) { + CommandBuilder builder = new CommandBuilder("serverprocessstop", 1); + builder.addIf(reason != null, new KeyValueParam("reasonmsg", reason)); + return builder.build(); + } + + public static Command version() { + return new CommandBuilder("version").build(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/ServerGroupCommands.java b/src/com/github/theholywaffle/teamspeak3/commands/ServerGroupCommands.java new file mode 100644 index 0000000..9c1ec0d --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/ServerGroupCommands.java @@ -0,0 +1,122 @@ +package com.github.theholywaffle.teamspeak3.commands; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2017 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.PermissionGroupDatabaseType; +import com.github.theholywaffle.teamspeak3.commands.parameter.KeyValueParam; +import com.github.theholywaffle.teamspeak3.commands.parameter.OptionParam; + +public final class ServerGroupCommands { + + private ServerGroupCommands() { + throw new Error("No instances"); + } + + public static Command serverGroupAdd(String groupName, PermissionGroupDatabaseType type) { + if (groupName == null || groupName.isEmpty()) { + throw new IllegalArgumentException("Server group name must be a non-empty string"); + } + + CommandBuilder builder = new CommandBuilder("servergroupadd", 2); + builder.add(new KeyValueParam("name", groupName)); + if (type != null) { + builder.add(new KeyValueParam("type", type.getIndex())); + } + return builder.build(); + } + + public static Command serverGroupAddClient(int groupId, int clientDBId) { + CommandBuilder builder = new CommandBuilder("servergroupaddclient", 2); + builder.add(new KeyValueParam("sgid", groupId)); + builder.add(new KeyValueParam("cldbid", clientDBId)); + return builder.build(); + } + + public static Command serverGroupClientList(int groupId) { + CommandBuilder builder = new CommandBuilder("servergroupclientlist", 2); + builder.add(new KeyValueParam("sgid", groupId)); + builder.add(new OptionParam("names")); + return builder.build(); + } + + public static Command serverGroupCopy(int sourceGroupId, int targetGroupId, PermissionGroupDatabaseType type) { + return serverGroupCopy(sourceGroupId, targetGroupId, "name", type); + } + + public static Command serverGroupCopy(int sourceGroupId, String groupName, PermissionGroupDatabaseType type) { + return serverGroupCopy(sourceGroupId, 0, groupName, type); + } + + private static Command serverGroupCopy(int sourceGroupId, int targetGroupId, String groupName, PermissionGroupDatabaseType type) { + if (type == null) { + throw new IllegalArgumentException("Group type cannot be null"); + } + if (groupName == null || groupName.isEmpty()) { + throw new IllegalArgumentException("Server group name must be a non-empty string"); + } + + CommandBuilder builder = new CommandBuilder("servergroupcopy", 4); + builder.add(new KeyValueParam("ssgid", sourceGroupId)); + builder.add(new KeyValueParam("tsgid", targetGroupId)); + builder.add(new KeyValueParam("name", groupName)); + builder.add(new KeyValueParam("type", type.getIndex())); + return builder.build(); + } + + public static Command serverGroupDel(int id, boolean force) { + CommandBuilder builder = new CommandBuilder("servergroupdel", 2); + builder.add(new KeyValueParam("sgid", id)); + builder.add(new KeyValueParam("force", force)); + return builder.build(); + } + + public static Command serverGroupDelClient(int groupId, int clientDBId) { + CommandBuilder builder = new CommandBuilder("servergroupdelclient", 2); + builder.add(new KeyValueParam("sgid", groupId)); + builder.add(new KeyValueParam("cldbid", clientDBId)); + return builder.build(); + } + + public static Command serverGroupList() { + return new CommandBuilder("servergrouplist").build(); + } + + public static Command serverGroupRename(int id, String groupName) { + if (groupName == null || groupName.isEmpty()) { + throw new IllegalArgumentException("Server group name must be a non-empty string"); + } + + CommandBuilder builder = new CommandBuilder("servergrouprename", 2); + builder.add(new KeyValueParam("sgid", id)); + builder.add(new KeyValueParam("name", groupName)); + return builder.build(); + } + + public static Command serverGroupsByClientId(int clientDBId) { + return new CommandBuilder("servergroupsbyclientid", 1).add(new KeyValueParam("cldbid", clientDBId)).build(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/VirtualServerCommands.java b/src/com/github/theholywaffle/teamspeak3/commands/VirtualServerCommands.java new file mode 100644 index 0000000..611d1bf --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/VirtualServerCommands.java @@ -0,0 +1,102 @@ +package com.github.theholywaffle.teamspeak3.commands; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2017 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.VirtualServerProperty; +import com.github.theholywaffle.teamspeak3.commands.parameter.KeyValueParam; +import com.github.theholywaffle.teamspeak3.commands.parameter.OptionParam; +import com.github.theholywaffle.teamspeak3.commands.parameter.RawParam; + +import java.util.Map; + +public final class VirtualServerCommands { + + private VirtualServerCommands() { + throw new Error("No instances"); + } + + public static Command serverCreate(String name, Map options) { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("Server name must be a non-empty string"); + } + + CommandBuilder builder = new CommandBuilder("servercreate", 2); + builder.add(new KeyValueParam(VirtualServerProperty.VIRTUALSERVER_NAME.getName(), name)); + builder.addProperties(options); + return builder.build(); + } + + public static Command serverDelete(int id) { + return new CommandBuilder("serverdelete", 1).add(new KeyValueParam("sid", id)).build(); + } + + public static Command serverEdit(Map options) { + return new CommandBuilder("serveredit", 1).addProperties(options).build(); + } + + public static Command serverIdGetByPort(int port) { + return new CommandBuilder("serveridgetbyport", 1).add(new KeyValueParam("virtualserver_port", port)).build(); + } + + public static Command serverInfo() { + return new CommandBuilder("serverinfo").build(); + } + + public static Command serverList() { + CommandBuilder builder = new CommandBuilder("serverlist", 2); + builder.add(new OptionParam("uid")); + builder.add(new OptionParam("all")); + return builder.build(); + } + + public static Command serverRequestConnectionInfo() { + return new CommandBuilder("serverrequestconnectioninfo").build(); + } + + public static Command serverSnapshotCreate() { + return new CommandBuilder("serversnapshotcreate").build(); + } + + public static Command serverSnapshotDeploy(String snapshot) { + if (snapshot == null || snapshot.isEmpty()) { + throw new IllegalArgumentException("Server snapshot must be a non-empty string"); + } + + return new CommandBuilder("serversnapshotdeploy", 1).add(new RawParam(snapshot)).build(); + } + + public static Command serverStart(int id) { + return new CommandBuilder("serverstart", 1).add(new KeyValueParam("sid", id)).build(); + } + + public static Command serverStop(int id, String reason) { + CommandBuilder builder = new CommandBuilder("serverstop", 2); + builder.add(new KeyValueParam("sid", id)); + builder.addIf(reason != null, new KeyValueParam("reasonmsg", reason)); + return builder.build(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/parameter/ArrayParameter.java b/src/com/github/theholywaffle/teamspeak3/commands/parameter/ArrayParameter.java new file mode 100644 index 0000000..e0593dc --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/parameter/ArrayParameter.java @@ -0,0 +1,72 @@ +package com.github.theholywaffle.teamspeak3.commands.parameter; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.ArrayList; +import java.util.List; + +public class ArrayParameter extends Parameter { + + private final int parametersPerEntry; + private final List parameters; + + public ArrayParameter(int numberOfEntries) { + this(numberOfEntries, 1); + } + + public ArrayParameter(int numberOfEntries, int parametersPerEntry) { + this.parametersPerEntry = parametersPerEntry; + this.parameters = new ArrayList<>(numberOfEntries * parametersPerEntry); + } + + public ArrayParameter add(Parameter p) { + parameters.add(p); + return this; + } + + @Override + public void appendTo(StringBuilder str) { + if (parameters.isEmpty()) return; + + // First entry without | + parameters.get(0).appendTo(str); + for (int i = 1; i < parametersPerEntry; ++i) { + str.append(' '); + parameters.get(i).appendTo(str); + } + + // Remaining entries separated with | + for (int offset = parametersPerEntry; offset < parameters.size(); offset += parametersPerEntry) { + str.append('|'); + parameters.get(offset).appendTo(str); + for (int i = 1; i < parametersPerEntry; ++i) { + str.append(' '); + parameters.get(offset + i).appendTo(str); + } + } + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/parameter/KeyValueParam.java b/src/com/github/theholywaffle/teamspeak3/commands/parameter/KeyValueParam.java new file mode 100644 index 0000000..fc19eec --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/parameter/KeyValueParam.java @@ -0,0 +1,61 @@ +package com.github.theholywaffle.teamspeak3.commands.parameter; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.commands.CommandEncoding; + +public class KeyValueParam extends Parameter { + + private final String key; + private final String value; + + public KeyValueParam(String key, String value) { + if (key == null) throw new IllegalArgumentException("Key was null"); + + this.key = key; + this.value = (value != null) ? value : ""; + } + + public KeyValueParam(String key, int value) { + this(key, String.valueOf(value)); + } + + public KeyValueParam(String key, long value) { + this(key, String.valueOf(value)); + } + + public KeyValueParam(String key, boolean value) { + this(key, value ? "1" : "0"); + } + + @Override + public void appendTo(StringBuilder str) { + str.append(CommandEncoding.encode(key)); + str.append('='); + str.append(CommandEncoding.encode(value)); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/parameter/OptionParam.java b/src/com/github/theholywaffle/teamspeak3/commands/parameter/OptionParam.java new file mode 100644 index 0000000..60506e3 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/parameter/OptionParam.java @@ -0,0 +1,43 @@ +package com.github.theholywaffle.teamspeak3.commands.parameter; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.commands.CommandEncoding; + +public class OptionParam extends Parameter { + + private final String option; + + public OptionParam(String option) { + this.option = option; + } + + @Override + public void appendTo(StringBuilder str) { + str.append('-').append(CommandEncoding.encode(option)); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/parameter/Parameter.java b/src/com/github/theholywaffle/teamspeak3/commands/parameter/Parameter.java new file mode 100644 index 0000000..0d48c72 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/parameter/Parameter.java @@ -0,0 +1,39 @@ +package com.github.theholywaffle.teamspeak3.commands.parameter; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public abstract class Parameter { + + public abstract void appendTo(StringBuilder str); + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder(); + appendTo(stringBuilder); + return stringBuilder.toString(); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/parameter/RawParam.java b/src/com/github/theholywaffle/teamspeak3/commands/parameter/RawParam.java new file mode 100644 index 0000000..b586846 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/parameter/RawParam.java @@ -0,0 +1,41 @@ +package com.github.theholywaffle.teamspeak3.commands.parameter; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +public class RawParam extends Parameter { + + private final String raw; + + public RawParam(String raw) { + this.raw = raw; + } + + @Override + public void appendTo(StringBuilder str) { + str.append(raw); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/parameter/ValueParam.java b/src/com/github/theholywaffle/teamspeak3/commands/parameter/ValueParam.java new file mode 100644 index 0000000..d32bafb --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/parameter/ValueParam.java @@ -0,0 +1,43 @@ +package com.github.theholywaffle.teamspeak3.commands.parameter; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.commands.CommandEncoding; + +public class ValueParam extends Parameter { + + private final String value; + + public ValueParam(String value) { + this.value = value; + } + + @Override + public void appendTo(StringBuilder str) { + str.append(CommandEncoding.encode(value)); + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/response/DefaultArrayResponse.java b/src/com/github/theholywaffle/teamspeak3/commands/response/DefaultArrayResponse.java new file mode 100644 index 0000000..8e42bd7 --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/response/DefaultArrayResponse.java @@ -0,0 +1,120 @@ +package com.github.theholywaffle.teamspeak3.commands.response; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2017 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.api.wrapper.QueryError; +import com.github.theholywaffle.teamspeak3.api.wrapper.Wrapper; +import com.github.theholywaffle.teamspeak3.commands.CommandEncoding; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class DefaultArrayResponse { + + public static DefaultArrayResponse parse(String rawResponse) { + if (rawResponse == null || rawResponse.isEmpty()) return EMPTY; + + String[] rawMaps = rawResponse.split("\\|"); + List responses = new ArrayList<>(rawMaps.length); + if (rawMaps.length == 0) return new DefaultArrayResponse(responses, rawResponse); + + // First response, just use the parsed map + Map firstResponse = parseMap(rawMaps[0]); + responses.add(new Wrapper(firstResponse)); + + // Array response: use "default" values from the first response, then add the parsed map + for (int i = 1; i < rawMaps.length; ++i) { + final Map responseMap = new HashMap<>(firstResponse); + final Map ithResponse = parseMap(rawMaps[i]); + responseMap.putAll(ithResponse); + + responses.add(new Wrapper(responseMap)); + } + + return new DefaultArrayResponse(responses, rawResponse); + } + + public static QueryError parseError(String rawError) { + String error = rawError.substring("error ".length()); + Map errorMap = parseMap(error); + return new QueryError(errorMap); + } + + private static Map parseMap(String rawMap) { + if (rawMap == null || rawMap.isEmpty()) return Collections.emptyMap(); + + final String[] parameters = rawMap.split(" "); + final Map map = new HashMap<>(parameters.length); + + for (String param : parameters) { + if (param.isEmpty()) continue; + final int pos = param.indexOf("="); + + if (pos == -1) { + final String valuelessKey = CommandEncoding.decode(param); + map.put(valuelessKey, ""); + } else { + final String key = CommandEncoding.decode(param.substring(0, pos)); + final String value = CommandEncoding.decode(param.substring(pos + 1)); + map.put(key, value); + } + } + + return map; + } + + private static final DefaultArrayResponse EMPTY = new DefaultArrayResponse(new ArrayList(0), ""); + + private final List responses; + private final String rawResponse; + + private DefaultArrayResponse(List responses, String rawResponse) { + this.responses = Collections.unmodifiableList(responses); + this.rawResponse = rawResponse; + } + + public List getResponses() { + return responses; + } + + public Wrapper getFirstResponse() { + if (responses.isEmpty()) return Wrapper.EMPTY; + return responses.get(0); + } + + public String getRawResponse() { + return rawResponse; + } + + @Override + public String toString() { + return rawResponse; + } +} diff --git a/src/com/github/theholywaffle/teamspeak3/commands/response/ResponseBuilder.java b/src/com/github/theholywaffle/teamspeak3/commands/response/ResponseBuilder.java new file mode 100644 index 0000000..33d4baf --- /dev/null +++ b/src/com/github/theholywaffle/teamspeak3/commands/response/ResponseBuilder.java @@ -0,0 +1,57 @@ +package com.github.theholywaffle.teamspeak3.commands.response; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2017 Bert De Geyter, Roger Baumgartner + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.github.theholywaffle.teamspeak3.commands.Command; + +public class ResponseBuilder { + + private final Command command; + private final StringBuilder rawResponseBuilder; + + public ResponseBuilder(Command command) { + this.command = command; + this.rawResponseBuilder = new StringBuilder(); + } + + public Command getCommand() { + return command; + } + + public DefaultArrayResponse buildResponse() { + // Erase trailing '|' + if (rawResponseBuilder.length() > 0) { + rawResponseBuilder.setLength(rawResponseBuilder.length() - 1); + } + + return DefaultArrayResponse.parse(rawResponseBuilder.toString()); + } + + public void appendResponse(String rawArrayResponse) { + rawResponseBuilder.append(rawArrayResponse).append('|'); + } +}