Java tutorial
/*The MIT License (MIT) Copyright (c) 2016 Merlin von Rssing 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.*/ package com.github.flawedbliss.dicebotr4; import com.github.theholywaffle.teamspeak3.TS3Api; import com.github.theholywaffle.teamspeak3.TS3Config; import com.github.theholywaffle.teamspeak3.TS3Query; import com.github.theholywaffle.teamspeak3.TS3Query.FloodRate; import com.github.theholywaffle.teamspeak3.api.event.ChannelCreateEvent; import com.github.theholywaffle.teamspeak3.api.event.ChannelDeletedEvent; import com.github.theholywaffle.teamspeak3.api.event.ChannelDescriptionEditedEvent; import com.github.theholywaffle.teamspeak3.api.event.ChannelEditedEvent; import com.github.theholywaffle.teamspeak3.api.event.ChannelMovedEvent; import com.github.theholywaffle.teamspeak3.api.event.ChannelPasswordChangedEvent; import com.github.theholywaffle.teamspeak3.api.event.ClientJoinEvent; import com.github.theholywaffle.teamspeak3.api.event.ClientLeaveEvent; import com.github.theholywaffle.teamspeak3.api.event.ClientMovedEvent; import com.github.theholywaffle.teamspeak3.api.event.ServerEditedEvent; import com.github.theholywaffle.teamspeak3.api.event.TS3EventType; import com.github.theholywaffle.teamspeak3.api.event.TS3Listener; import com.github.theholywaffle.teamspeak3.api.event.TextMessageEvent; import com.github.theholywaffle.teamspeak3.api.wrapper.Client; import com.github.theholywaffle.teamspeak3.api.wrapper.ClientInfo; import java.io.IOException; import java.net.InetAddress; import java.util.ArrayList; import java.util.Date; import java.util.Objects; import java.util.Random; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; import org.joda.time.DateTime; import org.joda.time.Days; /** * * @author Daiya */ public class DicebotR4 { TS3Api api; TS3Query query; DConfig config; MySqlManager sqlManager; private static final Logger log = Logger.getLogger(DicebotR4.class.getName()); /** * * @param path Path to the folder containing the configuration files. * @throws com.github.flawedbliss.dicebotr4.DicebotException * @throws com.github.flawedbliss.dicebotr4.FatalDicebotException */ public DicebotR4(String path) throws DicebotException, FatalDicebotException { Handler[] handlers = log.getHandlers(); for (Handler h : handlers) { log.removeHandler(h); } log.setUseParentHandlers(false); LogHandler h = new LogHandler(true); log.addHandler(h); log.setLevel(Level.ALL); this.config = new DConfig(path); config.addHandler(h); init(); sqlManager.addHandler(h); } /** * @description Gets the bot ready for work * @throws DicebotException * @throws FatalDicebotException */ private void init() throws DicebotException, FatalDicebotException { try { /* ts3config: config of TheHolyWaffles API * config: config of the dicebot */ TS3Config ts3config = new TS3Config(); config.initFromFile(); String ts3server = config.getTs3Server(); if (isIP(ts3server)) { ts3config.setHost(ts3server); } else { ts3config.setHost(InetAddress.getByName(config.getTs3Server()).getHostAddress()); } ts3config.setLoginCredentials(config.getTs3QueryUser(), config.getTs3QueryPassword()); ts3config.setQueryPort(config.getTs3QueryPort()); /* todo: add floodrate to the config */ ts3config.setFloodRate(FloodRate.UNLIMITED); query = new TS3Query(ts3config); query.connect(); api = query.getApi(); if (!api.selectVirtualServerByPort(config.getTs3Port())) { throw new FatalDicebotException("Could not select the virtual server. Is the port correct?"); } /* If anyone is using my nickname, kick them */ for (Client c : api.getClients()) { if (c.getNickname().equals(config.getTs3Nickname())) { api.kickClientFromServer("This nickname is reserved!", c.getId()); } } api.setNickname(config.getTs3Nickname()); api.sendServerMessage(config.getTs3Nickname() + " is now online! Version " + config.getVersion()); api.registerEvent(TS3EventType.SERVER); api.registerEvent(TS3EventType.CHANNEL); api.registerEvent(TS3EventType.TEXT_CHANNEL); api.registerEvent(TS3EventType.TEXT_PRIVATE); api.registerEvent(TS3EventType.TEXT_SERVER); addListeners(); } catch (IOException ex) { throw new DicebotException("Could not access the configuration file. Startup failed."); } sqlManager = new MySqlManager(config); } private void addListeners() { api.addTS3Listeners(new TS3Listener() { @Override public void onChannelPasswordChanged(ChannelPasswordChangedEvent e) { } @Override public void onChannelMoved(ChannelMovedEvent e) { } @Override public void onChannelDeleted(ChannelDeletedEvent e) { } @Override public void onChannelCreate(ChannelCreateEvent e) { } @Override public void onClientMoved(ClientMovedEvent e) { } @Override public void onChannelDescriptionChanged(ChannelDescriptionEditedEvent e) { } @Override public void onChannelEdit(ChannelEditedEvent e) { } @Override public void onServerEdit(ServerEditedEvent e) { } @Override public void onClientLeave(ClientLeaveEvent e) { } @Override public void onClientJoin(ClientJoinEvent e) { int clientId = e.getClientId(); ClientInfo CI = api.getClientInfo(clientId); if (e.getClientType() == 0) { for (int motdId : config.getMotd().keySet()) { for (int motdGroup : config.getMotd().get(motdId)) { if (motdGroup == -1) { api.sendPrivateMessage(clientId, config.getMotdText().get(motdId)); } else { for (int clientGroup : CI.getServerGroups()) { if (clientGroup == motdGroup) { api.sendPrivateMessage(clientId, config.getMotdText().get(motdId)); } } } } } } try { if (clientHasServerGroup(config.getMemberGroupIds(), CI)) { //Client is a member if (isUserInactive(CI, config.getInactiveDays())) { if (!clientHasServerGroup(config.getInactiveGroupId(), CI)) { api.addClientToServerGroup(config.getInactiveGroupId(), CI.getDatabaseId()); } } sqlManager.updateLastLogin(CI.getUniqueIdentifier()); } else if (clientHasServerGroup(config.getFriendlyGroupId(), CI)) { //Client is a friendly if (isUserInactive(CI, config.getFriendlyInactiveDays())) { api.removeClientFromServerGroup(config.getFriendlyGroupId(), e.getClientDatabaseId()); api.sendPrivateMessage(clientId, "Because of your recent inactivity, your friendly " + "status has been revoked. If you believe you still deserve it, please " + "message a staff member."); } sqlManager.updateLastLogin(CI.getUniqueIdentifier()); } //Else: Is a guest, do nothing. /* Strip the client of his premium group if it expired */ if (clientHasServerGroup(config.getPremiumGroupId(), CI)) { if (isPremiumExpired(CI)) { api.removeClientFromServerGroup(config.getPremiumGroupId(), CI.getDatabaseId()); api.sendPrivateMessage(e.getClientId(), "Your premium membership has expired."); } } } catch (DicebotException ex) { log.warning(ex.getMessage()); } } @Override public void onTextMessage(TextMessageEvent e) { switch (e.getTargetMode()) { case SERVER: handleServerTextMessage(e); break; case CHANNEL: handleChannelTextMessage(e); break; case CLIENT: handleClientTextMessage(e); break; } } }); } /** * @desc Using methods for this so it stays cleaner. * @param e */ private void handleServerTextMessage(TextMessageEvent e) { String args[] = e.getMessage().split(" "); Client client = api.getClientByUId(e.getInvokerUniqueId()); switch (args[0].toLowerCase()) { case "!enabledice": if (!config.isSticky()) { api.moveClient(client.getChannelId()); api.sendChannelMessage("I am now watching this channel."); } else { Client me = api.getClientByNameExact(config.getTs3Nickname(), false); api.sendPrivateMessage(client.getId(), "Sorry, I am currently stuck to channel " + me.getChannelId() + "."); } break; case "!dicehelp": api.sendPrivateMessage(client.getId(), config.getHelpMessage()); break; default: break; } } private void handleChannelTextMessage(TextMessageEvent e) { String args[] = e.getMessage().split(" "); Client client = api.getClientByUId(e.getInvokerUniqueId()); switch (args[0].toLowerCase()) { case "!roll": if (args.length != 3) { api.sendChannelMessage("Invalid number of arguments. Usage: !roll <sides> <times>"); return; } if (!isInteger(args[1]) || !isInteger(args[2])) { api.sendChannelMessage("Invalid arguments: Not a number"); return; } int sides = Integer.parseInt(args[1]); int times = Integer.parseInt(args[2]); if (sides <= 0) { api.sendChannelMessage("Invalid arguments: You cannot roll with zero or less sides."); return; } if (times > 20) { api.sendChannelMessage("You may only roll up to 20 times at once."); return; } Random rand = new Random(); rand.setSeed(new Date().getTime()); for (int i = 0; i < times; i++) { api.sendChannelMessage((i + 1) + ": " + rand.nextInt(sides)); } break; case "!stick": config.setSticky(!config.isSticky()); if (config.isSticky()) { api.sendChannelMessage("I am now stuck to this channel"); } else { api.sendChannelMessage("I am no longer stuck to this channel."); } break; case "!dicehelp": api.sendChannelMessage(config.getHelpMessage()); break; case "!adminhelp": api.sendPrivateMessage(client.getId(), config.getAdminHelpMessage()); break; case "!mirror": config.setMirror(!config.isMirror()); api.sendChannelMessage("Mirror mode toggled."); break; default: if (config.isMirror()) { api.sendChannelMessage(e.getMessage()); } break; } } private void handleClientTextMessage(TextMessageEvent e) { String args[] = e.getMessage().split(" "); Client client = api.getClientByUId(e.getInvokerUniqueId()); switch (args[0].toLowerCase()) { case "!enabledice": if (!config.isSticky()) { api.moveClient(client.getChannelId()); api.sendChannelMessage("I am now watching this channel."); } else { Client me = api.getClientByNameExact(config.getTs3Nickname(), false); api.sendPrivateMessage(client.getId(), "Sorry, I am currently stuck to channel " + me.getChannelId() + "."); } break; case "!dicehelp": api.sendPrivateMessage(client.getId(), config.getHelpMessage()); break; case "!adminhelp": api.sendPrivateMessage(client.getId(), config.getAdminHelpMessage()); break; case "!shutdown": shutdown(); break; case "!diceinfo": api.sendPrivateMessage(client.getId(), config.getInfo()); break; case "!premium": if (clientHasServerGroup(config.getPremiumGroupId(), api.getClientInfo(client.getId()))) { api.sendPrivateMessage(client.getId(), "You are premium already."); return; } if (args.length != 2) { api.sendPrivateMessage(client.getId(), "Invalid arguments. Usage: !premium <code>"); return; } try { if (!sqlManager.isCodeUsed(args[1])) { sqlManager.setCodeUsed(args[1], client.getUniqueIdentifier()); api.addClientToServerGroup(config.getPremiumGroupId(), client.getDatabaseId()); api.sendPrivateMessage(client.getId(), "You have been added to the premium group.\n" + "Your membership will expire in " + config.getPremiumDays() + " days."); } else { api.sendPrivateMessage(client.getId(), "The code you entered was used already."); } } catch (DicebotException ex) { api.sendPrivateMessage(client.getId(), "I failed to retrieve the information about this code." + "Please try again later. If the issue persists, please message WG Flawedbliss or WG Xar."); log.warning(ex.getMessage()); } break; } } public void shutdown() { sqlManager.close(); api.sendServerMessage("Shutting down..."); api.logout(); query.exit(); } /** * * @param str The string to be checked for a valid IP * @return Returns true if the string matches the regex pattern of an IPv4 * Address. */ private boolean isIP(String str) { return str.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"); } /** * * @param str String to be checked for an Integer * @return Returns true if the string is a valid Integer */ private boolean isInteger(String str) { try { Integer.parseInt(str); return true; } catch (NumberFormatException e) { return false; } } /** * * @param serverGroup The ServerGroup the client should be checked for * @param CI A ClientInfo object of the client to be checked * @return Returns true if the client has the serverGroup, false if not. */ private boolean clientHasServerGroup(int serverGroup, ClientInfo CI) { for (int clientGroup : CI.getServerGroups()) { if (clientGroup == serverGroup) { return true; } } return false; } /** * * @param serverGroups A list of the Server Groups the client should be * checked for * @param CI A ClientInfo object of the client to be checked * @return Returns true of the client has any of serverGroups groups, false * if not. */ private boolean clientHasServerGroup(ArrayList<Integer> serverGroups, ClientInfo CI) { for (int clientGroup : CI.getServerGroups()) { if (serverGroups.contains(clientGroup)) { return true; } } return false; } /** * * @param uid Unique Id of the user you want to check * @return returns the number of days since a user last connected * @throws DicebotException */ private int getClientInactivityDays(String uid) throws DicebotException { DateTime lastlogin = sqlManager.getLastLogin(uid); if (lastlogin != null) { return Days.daysBetween(new DateTime(new Date()), lastlogin).getDays(); } else { return 0; } } /** * * @param CI ClientInfo of the Client you want to be checked * @param maxDays Maximum days between their last login and now * @return returns if true if the clients last login was more than maxDays * ago, false if not * @throws DicebotException */ private boolean isUserInactive(ClientInfo CI, int maxDays) throws DicebotException { return (getClientInactivityDays(CI.getUniqueIdentifier()) > config.getInactiveDays()); } /** * * @param CI ClientInfo of the client to be checked * @return Returns true if it was more days ago than set in the * configuration. */ private boolean isPremiumExpired(ClientInfo CI) { try { return Days.daysBetween(sqlManager.getPremiumBegin(CI.getUniqueIdentifier()), new DateTime(new Date())) .getDays() > config.getPremiumDays(); } catch (DicebotException ex) { log.warning(ex.getMessage()); return false; } } public void sendServerMessage(String msg) { api.sendServerMessage(msg); } public String getInstanceName() { return config.getInstanceName(); } @Override public boolean equals(Object o) { if (o == null || !(o instanceof DicebotR4)) return false; DicebotR4 other = (DicebotR4) o; if (o == this) return true; return this.getInstanceName().equals(other.getInstanceName()); } @Override public int hashCode() { int hash = 7; hash = 53 * hash + Objects.hashCode(this.api); hash = 53 * hash + Objects.hashCode(this.query); hash = 53 * hash + Objects.hashCode(this.config); hash = 53 * hash + Objects.hashCode(this.sqlManager); return hash; } }