//public static List<String> BannedID = new ArrayList<String>(); //DONE store in DB
public static List<String> ModID = new ArrayList<String>();
public static List<String> logExcemptID = new ArrayList<String>();
//TODO synchronize below or change to concurrent //but why? its thread safe since its read only
public static HashMap<String, Command> commands = new HashMap<String, Command>();
public static HashMap<String, Command> aliases = new HashMap<String, Command>(); //TODO temporary solution
public static TreeMap<String, List<Command>> catCmds = new TreeMap<String, List<Command>>(); //order base on categories?
public static ConcurrentHashMap<String, TextChannel> lastMusicCmd = new ConcurrentHashMap<String, TextChannel>(); //TODO store in guild configs //nah its fine
//public static HashMap<String, Game> guildMemberPresence = new HashMap<String, Game>();
public static final String prefix = "!desp "; //DONE allow guild change prefix?
public static JDA mainJDA = null;
- public static final String BotID = "311086271642599424";
- public static final String OwnerID = "237881229876133888";
+ public static String BotID;
+ public static String OwnerID;
public static Properties tokens = new Properties();
static final Logger logger = LoggerFactory.getLogger(DiscordBot.class); //package private
public static HikariDataSource db;
/*public static FirefoxDriver driver;*/
// DONE SQLite integration; check if program termination will screw up the connection
// TODO SHARDING
// DONE on shutdown alert those playing music or await? //alerted
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> { //no need to be a global variable since i dont need to access it
try {
FileOutputStream out = new FileOutputStream(System.getProperty("user.dir") + File.separator + "testAnimePicCache.bin");
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(EventListener.testAnimePicCache);
oos.flush();
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}, 1, 5, TimeUnit.MINUTES);*/
// DEPRECATED //or is it? make unmodifiable instead
for(String id : tokens.getProperty("botmod").split(",")) {
ModID.add(id);
}
ModID.add(OwnerID);
logExcemptID.add(BotID);
}
private static void initDB() { //init guild settings when do !desp settings for the first time? //dont really need coz im doing upsert for all values anyways
HikariConfig dbConf = new HikariConfig();
dbConf.setJdbcUrl("jdbc:sqlite:data.db");
dbConf.setIdleTimeout(45000);
dbConf.setMaxLifetime(60000);
dbConf.setMaximumPoolSize(10);
//dbConf.setMaximumPoolSize(25); //for deployment in server
db = new HikariDataSource(dbConf);
try (Connection con = db.getConnection()){
Statement s = con.createStatement();
s.execute("CREATE TABLE IF NOT EXISTS settings"
+ "(id INTEGER PRIMARY KEY," //performance problem using text; integer can handle long anyways
+ "prefix TEXT DEFAULT '!desp ',"
+ "premium INTEGER DEFAULT 0,"
+ "mchannel TEXT," //allow people to set this manually? By default, use last music cmd place
+ "locale TEXT DEFAULT 'EN'," //or int? //user specific or guild specific, or user override guild?
+ "shortcuts TEXT," //use another method?
+ "votepct INTEGER DEFAULT 50,"
+ "looplimit INTEGER DEFAULT 1,"
+ "volume INTEGER DEFAULT 100," //premium?, is default actually 100?
+ "helpdm INTEGER DEFAULT 0);"); //send help to dm or not, excluding cmd help(?)
//add logexempt?
PreparedStatement pragma = con.prepareStatement("SELECT name FROM pragma_table_info('perms_' || ?);"); //NOTE: sqlite does not support changing default values, beware when adding new commands
String create = "CREATE TABLE perms_" + entry.getKey().toLowerCase() + " (id INTEGER PRIMARY KEY, _GLOBAL_ TEXT DEFAULT '0\n', "; //perms for a category are always default no restraints
join.add("\"" + node.getKey() + "\" TEXT DEFAULT '" + ((0L << 32) | (Permission.getRaw(node.getValue()) & 0xffffffffL)) + "\n'"); //initialize with no permission deny override, permissions allow //needs to be text so that channel overrides delimiter can be stored
}
create += join.toString() + ");";
s.execute(create);
//DONE? create table
} else {
if(!nodes.isEmpty()) { //which means there is new subCmd/cmd
s.execute("ALTER TABLE perms_" + entry.getKey().toLowerCase() + " ADD COLUMN \"" + node.getKey() + "\" TEXT DEFAULT '" + ((0L << 32) | (Permission.getRaw(node.getValue()) & 0xffffffffL)) + "\n';"); //not able to prep statement coz its column name
s.close();
}
}
}
}
pragma.close();
//DONE TEST generate the nodes, retrieve default from fields? //what about additional commands added, use ALTER TABLE ADD COLUMN DEFAULT? I dont need to sort it according to category at all, i am not and will not be bulk printing permissions anywhere //actually i might be coz of list edited perms
//fit subnodes into sub tables, split by category so its more balanced (category as table name)?
//fields are gonna be sth like <channelid>:<perm>|<perm> etc?
//DONE store the permissions with long merged; <32bit = deny, >32bit = allow
s.execute("CREATE TABLE IF NOT EXISTS users"
+ "(id INTEGER PRIMARY KEY,"
+ "reports TEXT DEFAULT '',"
+ "game TEXT);"); //disposable, i can change it anytime
//add botmod field? if so, i can deprecate the hard coded list, but it would require querying to this table on perm checks //i need to query it to check if the user is banned anyways //subcmd need to query again, so i didnt do it; besides the modlist is gonna be too small to be significant anyways
//add user specific locale? might be confusing
s.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
private static void initCmds() {
Reflections reflections = new Reflections("me.despawningbone.discordbot.command");
public class EventListener extends ListenerAdapter {
public static Multimap<String, String> testAnimePicCache = ArrayListMultimap.create();
@Override
public void onGuildMessageReceived(GuildMessageReceivedEvent event) {
DiscordBot.mainJDA = event.getJDA(); // refresh it? //no need, everything's getJDA() is the same jda basis but audioplayer's still using it; need to migrate to store in guildmusicmanager
//String[] words = event.getMessage().getContentDisplay().split(" ");
User author = event.getAuthor();
TextChannel channel = event.getChannel();
Message msg = event.getMessage();
//checkAnimePics(event);
//DONE use log4j
//logging
if (!DiscordBot.logExcemptID.contains(author.getId())) {
String perms = cmd.hasSubCommand() ? null : MiscUtils.getMissingPerms(perm, cmd.getRequiredBotUserLevel(), event.getMember(), channel); //pass it to the subcommand handler to handle instead
+ msg.getContentDisplay() + (r.getRemarks() == null ? "." : ". (" + r.getRemarks() + ")")); //logging has to be before sendMessage, or else if no permission it will just quit
public void onUserActivityStart(UserActivityStartEvent event) { //store presence for checking osu pp
Activity osu = event.getNewActivity();
if (osu != null && osu.getName().equals("osu!") && osu.isRich() //if need to include other games and details, just remove this if clause and change sGame below
long ms = Math.abs(timesent.until(timeReceived, ChronoUnit.MILLIS));
System.out.println("Time taken: " + ms + "ms");*/
try (Connection con = DiscordBot.db.getConnection()) { //DONE do i need to close the statement?
PreparedStatement s = con.prepareStatement("INSERT INTO users(id, game) VALUES (" + event.getUser().getId() + ", ?) ON CONFLICT(id) DO UPDATE SET game = ? WHERE game <> ?;" ); //prevent blank updates
public void onGuildVoiceLeave(GuildVoiceLeaveEvent event) { // theoretically i dont need to check if lastMusicCmd has the entry or not, as it must have one to trigger this
GuildVoiceState vs = event.getGuild().getMemberById(DiscordBot.BotID).getVoiceState();
"All users have left the music channel, the player is now paused.\nThe queue will be cleared in 1 minute if there is no activity.")
.queue();
waitActivity(event.getGuild());
}
}
} else { //got kicked
AudioTrackHandler ap = ((Music) DiscordBot.commands.get("music")).getAudioTrackHandler();
if(ap != null) { //if not destroyed
ap.stopAndClearQueue(event.getGuild()); //can double fire on normal end; shouldnt be too big of a problem
}
}
}
@Override
public void onGuildVoiceMove(GuildVoiceMoveEvent event) { // theoretically i dont need to check if lastMusicCmd has the entry or not, as it must have one to trigger this
GuildVoiceState vs = event.getGuild().getMemberById(DiscordBot.BotID).getVoiceState();
"All users have left the music channel, the player is now paused.\nThe queue will be cleared in 1 minute if there is no activity.")
.queue();
waitActivity(event.getGuild());
}
} else if (vs.getChannel().equals(event.getChannelJoined())) {
String type = updateActivity(event.getGuild(), event.getChannelJoined());
if(type != null) channel.sendMessage((type.equals("loop") ? "Looping" : "Autoplay") + " mode disabled due to new users joining the music channel.").queue();
"The bot has been moved to an empty channel, the player is now paused.\nThe queue will be cleared in 1 minute if there is no activity.")
.queue();
waitActivity(event.getGuild());
} else { //moved bot to channel with ppl
event.getGuild().getAudioManager().openAudioConnection(event.getChannelJoined()); //seems to need explicit reconnect on bot move, it gets stuck on attempting to reconnect otherwise; probably would be fixed soon but hot patch for now
String type = updateActivity(event.getGuild(), event.getChannelJoined());
if(type != null) channel.sendMessage((type.equals("loop") ? "Looping" : "Autoplay") + " mode disabled due to new users joining the music channel.").queue();
}
}
}
}
@Override
public void onGuildVoiceJoin(GuildVoiceJoinEvent event) {
GuildVoiceState vs = event.getGuild().getMemberById(DiscordBot.BotID).getVoiceState();
if (vs.inVoiceChannel() && !event.getMember().getUser().getId().equals(DiscordBot.BotID)) {
if (vs.getChannel().equals(event.getChannelJoined())) {
String type = updateActivity(event.getGuild(), event.getChannelJoined());
if(type != null) channel.sendMessage((type.equals("loop") ? "Looping" : "Autoplay") + " mode disabled due to new users joining the music channel.").queue();
return new CommandResult(CommandResultType.FAILURE, "There is no such user.");
} else if (pm.size() > 1) {
return new CommandResult(CommandResultType.FAILURE, "Theres more than 1 user with the same name. Please use !desp ID to get the ID of the user you want to report.");
} else {
SID = pm.get(0).getUser().getId();
ID = Long.parseLong(SID);
}
}
if (success == true) {
SID = Long.toString(ID);
}
if (author.getId().equals(SID)) {
return new CommandResult(CommandResultType.FAILURE, "You cannot report yourself you dumbo :stuck_out_tongue:");
} else if (DiscordBot.ModID.contains(SID) || SID.equals(DiscordBot.BotID)) {
return new CommandResult(CommandResultType.FAILURE, "That user cannot be reported.");
}
try (Connection con = DiscordBot.db.getConnection(); Statement s = con.createStatement()) {
String reports = "";
ResultSet uRs = s.executeQuery("SELECT reports FROM users WHERE id = " + SID + ";");
if(uRs.next()) {
reports = uRs.getString(1);
if(reports.contains(author.getId())) {
return new CommandResult(CommandResultType.FAILURE, "You cannot report a user more than once."); //make it so that its toggling the reports instead?
} else if(reports.split("\n").length < 5){
reports += author.getId() + "\n";
} else {
return new CommandResult(CommandResultType.FAILURE, "The user has already been banned!");
}
} else {
reports = author.getId() + "\n";
}
PreparedStatement set = con.prepareStatement("INSERT INTO users(id, reports) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET reports = ?" );