├── .idea ├── .gitignore ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── vcs.xml ├── encodings.xml ├── checkstyleidea-libs │ └── readme.txt ├── misc.xml ├── compiler.xml ├── checkstyle-idea.xml └── jarRepositories.xml ├── src ├── slaynash │ └── lum │ │ └── bot │ │ ├── discord │ │ ├── VerifyPair.java │ │ ├── ServerChannel.java │ │ ├── ReactionListener.java │ │ ├── melonscanner │ │ │ ├── ModVersion.java │ │ │ ├── MelonIncompatibleMod.java │ │ │ ├── MelonOutdatedMod.java │ │ │ ├── LogsModDetails.java │ │ │ ├── MelonDuplicateMod.java │ │ │ ├── MelonApiMod.java │ │ │ └── LogCounter.java │ │ ├── Command.java │ │ ├── commands │ │ │ ├── RubybotOverDynobotCommand.java │ │ │ ├── VerifyCommandCommand.java │ │ │ ├── TestVRCObfmap.java │ │ │ ├── LumGoneCommand.java │ │ │ ├── CommandLaunchCommand.java │ │ │ ├── HelpCommand.java │ │ │ ├── VerifyChannelHandlerCommand.java │ │ │ ├── Unban.java │ │ │ ├── SetScreeningRoleHandlerCommand.java │ │ │ ├── DumpID.java │ │ │ ├── AutoPublish.java │ │ │ ├── LockDown.java │ │ │ ├── UVMCommand.java │ │ │ ├── AddMissingRoles.java │ │ │ ├── Kick.java │ │ │ └── Ban.java │ │ ├── slashs │ │ │ ├── Slash.java │ │ │ ├── SetVRCAPI.java │ │ │ ├── SetMemes.java │ │ │ ├── SlashManager.java │ │ │ └── SetLogChannel.java │ │ ├── GuildConfiguration.java │ │ ├── HandledServerMessageContext.java │ │ ├── PrivateMessagesHandler.java │ │ ├── utils │ │ │ └── MessageFinder.java │ │ ├── Moderation.java │ │ ├── GuildConfigurations.java │ │ ├── JDAManager.java │ │ ├── ABCpolice.java │ │ ├── LuaPackages.java │ │ ├── VRCApiVersionScanner.java │ │ └── Memes.java │ │ ├── api │ │ ├── Endpoint.java │ │ ├── RequestMethod.java │ │ ├── endpoints │ │ │ ├── ReloadTranslationsEndpoint.java │ │ │ ├── ReloadMelonScannerErrorsEndpoint.java │ │ │ └── PingEndpoint.java │ │ ├── WebRequest.java │ │ ├── API.java │ │ └── WebResponse.java │ │ ├── steam │ │ └── SteamChannel.java │ │ ├── log │ │ ├── LogSystem.java │ │ └── LumLogOutput.java │ │ ├── utils │ │ ├── TimeManager.java │ │ └── ArrayUtils.java │ │ ├── timers │ │ ├── ClearDMs.java │ │ ├── Reminders.java │ │ └── Anime.java │ │ ├── UrlShortener.java │ │ ├── uvm │ │ ├── CecilAssemblyResolverProvider.java │ │ ├── UnityUtils.java │ │ └── UnityVersion.java │ │ ├── ConfigManager.java │ │ ├── Localization.java │ │ └── DBConnectionManagerShortUrls.java └── net │ └── gcardone │ └── junidecode │ └── CHANGELOG ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── bug_report.yml │ └── feature_request.yml └── workflows │ ├── javaci.yaml │ ├── translationsCD.yaml │ ├── melonscannererrorsCD.yaml │ ├── javaCICD.yaml │ └── apiscriptsCD.yaml ├── unityversionsmonitor └── HashChecker │ ├── libs │ └── Newtonsoft.Json.13.0.1 │ │ └── Newtonsoft.Json.dll │ ├── SlaynashUtils │ ├── StringUtils.cs │ ├── ReflectionUtils.cs │ ├── VersionUtils.cs │ └── CppUtils.cs │ ├── HashChecker.sln │ ├── Properties │ └── AssemblyInfo.cs │ └── HashChecker.csproj ├── .vscode ├── launch.json └── settings.json ├── apiscripts ├── btd6_gurrenm4.lua ├── uno.lua ├── audica_ahriana.lua ├── btd6_inferno.lua ├── musedash.lua ├── musedashgh.lua ├── thunderstore.lua ├── tld.lua ├── vrcmg.lua └── curseforge.lua ├── workspace.code-workspace ├── .project ├── .classpath ├── README.md └── .gitignore /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/VerifyPair.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord; 2 | 3 | public record VerifyPair(String channelId, String roleId) { 4 | } 5 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/ServerChannel.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord; 2 | 3 | public record ServerChannel(String serverID, String channelId) { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/ReactionListener.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord; 2 | 3 | public record ReactionListener(String messageId, String emoteId, String roleId) { 4 | } 5 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/api/Endpoint.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.api; 2 | 3 | public abstract class Endpoint { 4 | 5 | public abstract WebResponse handle(WebRequest request); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/api/RequestMethod.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.api; 2 | 3 | public enum RequestMethod { 4 | NONE, 5 | GET, 6 | HEAD, 7 | POST, 8 | PUT 9 | } 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Discord 4 | url: https://discord.gg/akFkAG2 5 | about: Feel Free to ask questions on our Discord server -------------------------------------------------------------------------------- /unityversionsmonitor/HashChecker/libs/Newtonsoft.Json.13.0.1/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Slaynash/Lumbot/HEAD/unityversionsmonitor/HashChecker/libs/Newtonsoft.Json.13.0.1/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/melonscanner/ModVersion.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.melonscanner; 2 | 3 | import com.github.zafarkhaja.semver.Version; 4 | 5 | public record ModVersion(Version version, String hash) { 6 | } 7 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/steam/SteamChannel.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.steam; 2 | 3 | public record SteamChannel(String gameID, String guildID, String channelId, String publicMessage, String betaMessage, String otherMessage) { 4 | } 5 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/log/LogSystem.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.log; 2 | 3 | public class LogSystem { 4 | 5 | public static void init() { 6 | System.setOut(new LumLogOutput(System.out, "OUT")); 7 | System.setErr(new LumLogOutput(System.err, "ERROR")); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /unityversionsmonitor/HashChecker/SlaynashUtils/StringUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SlaynashUtils 4 | { 5 | public static class StringUtils 6 | { 7 | public static string AsHexString(this IntPtr ptr) => 8 | string.Format("{0:X}", ptr.ToInt64()); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "java", 6 | "name": "Launch", 7 | "request": "launch", 8 | "mainClass": "slaynash.lum.bot.Main", 9 | "projectName": "Lumbot" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /apiscripts/btd6_gurrenm4.lua: -------------------------------------------------------------------------------- 1 | local apiData = data:getAsJsonObject():entrySet():iterator() 2 | local mods = {} 3 | 4 | while apiData:hasNext() do 5 | local entry = apiData:next() 6 | table.insert(mods, { 7 | name = entry:getKey(), 8 | version = entry:getValue():get("Version"):getAsString() 9 | }) 10 | end 11 | 12 | return mods -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/melonscanner/MelonIncompatibleMod.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.melonscanner; 2 | 3 | public class MelonIncompatibleMod { 4 | final String mod; 5 | final String incompatible; 6 | 7 | public MelonIncompatibleMod(String mod, String incompatible) { 8 | this.mod = mod; 9 | this.incompatible = incompatible; 10 | } 11 | } -------------------------------------------------------------------------------- /src/slaynash/lum/bot/utils/TimeManager.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.utils; 2 | 3 | import java.time.LocalDateTime; 4 | import java.time.format.DateTimeFormatter; 5 | 6 | public abstract class TimeManager { 7 | private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); 8 | 9 | public static String getTimeForLog() { 10 | return dtf.format(LocalDateTime.now()); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /unityversionsmonitor/HashChecker/SlaynashUtils/ReflectionUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace SlaynashUtils 5 | { 6 | public static class ReflectionUtils 7 | { 8 | internal static IntPtr GetFunctionPointerFromMethod(string methodName) => 9 | typeof(T).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static).MethodHandle.GetFunctionPointer(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /apiscripts/uno.lua: -------------------------------------------------------------------------------- 1 | local apiData = data:getAsJsonArray() 2 | local mods = {} 3 | 4 | for i = 0, apiData:size() - 1, 1 do 5 | local mod = apiData:get(i) 6 | 7 | table.insert(mods, { 8 | name = mod:get("Name"):getAsString(), 9 | version = mod:get("Version"):getAsString(), 10 | downloadLink = mod:get("DownloadLink"):getAsString(), 11 | hash = mod:get("Hash"):getAsString() 12 | }) 13 | end 14 | 15 | return mods -------------------------------------------------------------------------------- /apiscripts/audica_ahriana.lua: -------------------------------------------------------------------------------- 1 | local apiData = data:getAsJsonObject():entrySet():iterator() 2 | local mods = {} 3 | 4 | while apiData:hasNext() do 5 | local entry = apiData:next() 6 | table.insert(mods, { 7 | name = entry:getKey(), 8 | version = entry:getValue():get("Version"):getAsString(), 9 | downloadLink = entry:getValue():get("Download"):get(0):get("browser_download_url"):getAsString() 10 | }) 11 | end 12 | 13 | return mods -------------------------------------------------------------------------------- /apiscripts/btd6_inferno.lua: -------------------------------------------------------------------------------- 1 | local apiData = data:getAsJsonArray() 2 | local mods = {} 3 | 4 | for i = 0, apiData:size() - 1, 1 do 5 | local mod = apiData:get(i) 6 | local modDetails = mod:get("Version") 7 | 8 | table.insert(mods, { 9 | name = mod:get("Name"):getAsString(), 10 | version = modDetails:get("ReadableVersion"):getAsString(), 11 | downloadLink = modDetails:get("DownloadLink"):getAsString() 12 | }) 13 | end 14 | 15 | return mods 16 | -------------------------------------------------------------------------------- /.idea/checkstyleidea-libs/readme.txt: -------------------------------------------------------------------------------- 1 | This folder contains libraries copied from the "Lumbot" project. 2 | It is managed by the CheckStyle-IDEA IDE plugin. 3 | Do not modify this folder while the IDE is running. 4 | When the IDE is stopped, you may delete this folder at any time. It will be recreated as needed. 5 | In order to prevent the CheckStyle-IDEA IDE plugin from creating this folder, 6 | uncheck the "Copy libraries from project directory" option in the CheckStyle-IDEA settings dialog. 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.completion.importOrder": ["java", "javax"], 3 | "java.configuration.updateBuildConfiguration": "automatic", 4 | "Lua.diagnostics.globals": [ 5 | "data", 6 | "base64toLowerHexString" 7 | ], 8 | "java.checkstyle.version": "10.3.1", 9 | "java.checkstyle.configuration": "${workspaceFolder}\\checkstyle.xml", 10 | "java.debug.settings.onBuildFailureProceed": true, 11 | "dotnet.preferCSharpExtension": true 12 | } 13 | -------------------------------------------------------------------------------- /workspace.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "java.completion.importOrder": ["java", "javax"], 9 | "java.configuration.updateBuildConfiguration": "automatic", 10 | "java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx2G -Xms100m", 11 | "java.checkstyle.version": "10.3.1", 12 | "java.checkstyle.configuration": "checkstyle.xml" 13 | } 14 | } -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /apiscripts/musedash.lua: -------------------------------------------------------------------------------- 1 | local apiData = data:getAsJsonArray() 2 | local mods = {} 3 | 4 | for i = 0, apiData:size() - 1, 1 do 5 | local mod = apiData:get(i) 6 | 7 | table.insert(mods, { 8 | name = mod:get("name"):getAsString(), 9 | version = mod:get("version"):getAsString(), 10 | downloadLink = "https://mdmc.moe/api/v5/download/mod/" .. mod:get("id"):getAsString(), 11 | isbroken = not mod:get("functional"):getAsBoolean(), 12 | }) 13 | end 14 | 15 | return mods -------------------------------------------------------------------------------- /apiscripts/musedashgh.lua: -------------------------------------------------------------------------------- 1 | local apiData = data:getAsJsonArray() 2 | local mods = {} 3 | 4 | for i = 0, apiData:size() - 1, 1 do 5 | local mod = apiData:get(i) 6 | 7 | table.insert(mods, { 8 | name = mod:get("Name"):getAsString(), 9 | version = mod:get("Version"):getAsString(), 10 | downloadLink = "https://github.com/MDModsDev/ModLinks/raw/main/" .. mod:get("DownloadLink"):getAsString(), 11 | hash = mod:get("SHA256"):getAsString() 12 | }) 13 | end 14 | 15 | return mods -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /apiscripts/thunderstore.lua: -------------------------------------------------------------------------------- 1 | local apiData = data:getAsJsonArray() 2 | local mods = {} 3 | 4 | for i = 0, apiData:size() - 1, 1 do 5 | local mod = apiData:get(i) 6 | if mod:get("full_name"):getAsString() == "LavaGang-MelonLoader" then goto continue end 7 | 8 | local modDetails = mod:get("versions"):get(0) 9 | 10 | table.insert(mods, { 11 | name = modDetails:get("name"):getAsString(), 12 | version = modDetails:get("version_number"):getAsString(), 13 | downloadLink = modDetails:get("download_url"):getAsString(), 14 | is_deprecated = mod:get("is_deprecated"):getAsBoolean() 15 | }) 16 | ::continue:: 17 | end 18 | 19 | return mods -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/melonscanner/MelonOutdatedMod.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.melonscanner; 2 | 3 | import com.github.zafarkhaja.semver.Version; 4 | 5 | public class MelonOutdatedMod { 6 | final String name; 7 | final String newName; 8 | final Version currentVersion; 9 | final Version latestVersion; 10 | final String downloadUrl; 11 | 12 | public MelonOutdatedMod(String name, String newName, Version currentVersion, Version latestVersion, String downloadUrl) { 13 | this.name = name; 14 | this.newName = newName; 15 | this.currentVersion = currentVersion; 16 | this.latestVersion = latestVersion; 17 | this.downloadUrl = downloadUrl; 18 | } 19 | } -------------------------------------------------------------------------------- /src/slaynash/lum/bot/utils/ArrayUtils.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.utils; 2 | 3 | public final class ArrayUtils { 4 | 5 | public static boolean contains(T[] aliases, T value) { 6 | if (aliases == null) 7 | return false; 8 | 9 | for (T alias : aliases) 10 | if (alias == null && value == null || alias.equals(value)) 11 | return true; 12 | 13 | return false; 14 | } 15 | 16 | public static boolean contains(long[] aliases, long value) { 17 | if (aliases == null) 18 | return false; 19 | 20 | for (long alias : aliases) 21 | if (alias == value) 22 | return true; 23 | 24 | return false; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /apiscripts/tld.lua: -------------------------------------------------------------------------------- 1 | local apiData = data:getAsJsonArray() 2 | local mods = {} 3 | 4 | for i = 0, apiData:size() - 1, 1 do 5 | local entry = apiData:get(i) 6 | 7 | -- convert the aliases to a LuaTable 8 | local srcAliases = entry:get("Aliases") 9 | local aliases = {} 10 | for iAlias = 0, srcAliases:size() - 1, 1 do 11 | table.insert(aliases, srcAliases:get(iAlias):getAsString()) -- lua arrays start at 1 12 | end 13 | 14 | table.insert(mods, { 15 | name = entry:get("Name"):getAsString(), 16 | version = entry:get("Version"):getAsString(), 17 | downloadLink = entry:get("Download"):getAsString(), 18 | isbroken = entry:get("Error"):getAsBoolean(), 19 | modtype = entry:get("Type"):getAsString(), 20 | aliases = aliases 21 | }) 22 | end 23 | 24 | return mods -------------------------------------------------------------------------------- /src/slaynash/lum/bot/api/endpoints/ReloadTranslationsEndpoint.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.api.endpoints; 2 | 3 | import slaynash.lum.bot.Localization; 4 | import slaynash.lum.bot.api.Endpoint; 5 | import slaynash.lum.bot.api.WebRequest; 6 | import slaynash.lum.bot.api.WebResponse; 7 | 8 | public class ReloadTranslationsEndpoint extends Endpoint { 9 | 10 | @Override 11 | public WebResponse handle(WebRequest request) { 12 | System.out.println("Reloading translations"); 13 | 14 | WebResponse r = new WebResponse(); 15 | r.addHeader("Content-Type", "application/json"); 16 | 17 | if (Localization.reload()) { 18 | r.setData("{\"result\":\"OK\"}"); 19 | } 20 | else { 21 | r.returnCode = 500; 22 | r.setData("{\"result\":\"Failed\"}"); 23 | } 24 | 25 | return r; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/api/endpoints/ReloadMelonScannerErrorsEndpoint.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.api.endpoints; 2 | 3 | import slaynash.lum.bot.api.Endpoint; 4 | import slaynash.lum.bot.api.WebRequest; 5 | import slaynash.lum.bot.api.WebResponse; 6 | import slaynash.lum.bot.discord.melonscanner.MelonLoaderError; 7 | 8 | public class ReloadMelonScannerErrorsEndpoint extends Endpoint { 9 | 10 | @Override 11 | public WebResponse handle(WebRequest request) { 12 | System.out.println("Reloading Melon Scanner Errors"); 13 | 14 | WebResponse r = new WebResponse(); 15 | r.addHeader("Content-Type", "application/json"); 16 | 17 | if (MelonLoaderError.reload()) { 18 | r.setData("{\"result\":\"OK\"}"); 19 | } 20 | else { 21 | r.returnCode = 500; 22 | r.setData("{\"result\":\"Failed\"}"); 23 | } 24 | 25 | return r; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/Command.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord; 2 | 3 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 4 | 5 | public abstract class Command { 6 | protected Command instance = this; 7 | 8 | @SuppressWarnings("BooleanMethodIsAlwaysInverted") 9 | protected abstract boolean matchPattern(String paramString); 10 | 11 | protected void onClient(String paramString, MessageReceivedEvent paramMessageReceivedEvent) { 12 | } 13 | 14 | protected void onServer(String paramString, MessageReceivedEvent paramMessageReceivedEvent) { 15 | } 16 | 17 | public String getName() { 18 | return null; 19 | } 20 | 21 | public String getHelpDescription() { 22 | return null; 23 | } 24 | 25 | public boolean includeInHelp(MessageReceivedEvent event) { 26 | return true; 27 | } 28 | 29 | public boolean allowBots() { 30 | return false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/commands/RubybotOverDynobotCommand.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.commands; 2 | 3 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 4 | import slaynash.lum.bot.discord.Command; 5 | 6 | public class RubybotOverDynobotCommand extends Command { 7 | 8 | @Override 9 | protected void onServer(String paramString, MessageReceivedEvent paramMessageReceivedEvent) { 10 | paramMessageReceivedEvent.getMessage().reply("<:SmugSip:743484784415866950>").queue(); 11 | } 12 | 13 | @Override 14 | protected boolean matchPattern(String paramString) { 15 | return paramString.trim().toLowerCase().replace(" ", "").replace(">", "<").contains("rubybot 2 | 3 | Lumbot 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.m2e.core.maven2Nature 21 | org.eclipse.jdt.core.javanature 22 | 23 | 24 | 25 | 1664910748688 26 | 27 | 30 28 | 29 | org.eclipse.core.resources.regexFilterMatcher 30 | node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/melonscanner/LogsModDetails.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.melonscanner; 2 | 3 | import com.github.zafarkhaja.semver.Version; 4 | 5 | public class LogsModDetails { 6 | public String name; 7 | public Version version; 8 | public String author; 9 | public String hash; 10 | public String assembly; 11 | 12 | public String id; 13 | 14 | public LogsModDetails(String hash, String assembly) { 15 | this.hash = hash; 16 | this.assembly = assembly; 17 | } 18 | 19 | public LogsModDetails(String name, Version version, String author) { 20 | this.name = name; 21 | this.version = version; 22 | this.author = author; 23 | } 24 | 25 | public LogsModDetails(String name, Version version, String author, String hash, String assembly) { 26 | this.name = name; 27 | this.version = version; 28 | this.author = author; 29 | this.hash = hash; 30 | this.assembly = assembly; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/api/endpoints/PingEndpoint.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.api.endpoints; 2 | 3 | import net.dv8tion.jda.api.JDA.Status; 4 | import slaynash.lum.bot.api.Endpoint; 5 | import slaynash.lum.bot.api.WebRequest; 6 | import slaynash.lum.bot.api.WebResponse; 7 | import slaynash.lum.bot.discord.JDAManager; 8 | 9 | public class PingEndpoint extends Endpoint { 10 | 11 | @Override 12 | public WebResponse handle(WebRequest request) { 13 | WebResponse r = new WebResponse(); 14 | r.addHeader("Content-Type", "text/plain"); 15 | if (JDAManager.getJDA() == null) { 16 | r.returnCode = 569; 17 | r.setData("Lum is Booting"); 18 | } 19 | if (JDAManager.getJDA().getStatus() == Status.CONNECTED) { 20 | r.setData("pong " + JDAManager.getJDA().getGatewayPing()); 21 | } 22 | else { 23 | r.returnCode = 503; 24 | r.setData("Lum is not running " + JDAManager.getJDA().getStatus()); 25 | } 26 | return r; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /apiscripts/vrcmg.lua: -------------------------------------------------------------------------------- 1 | local apiData = data:getAsJsonArray() 2 | local mods = {} 3 | 4 | for i = 0, apiData:size() - 1, 1 do 5 | local mod = apiData:get(i) 6 | local modDetails = mod:get("versions"):get(0) 7 | 8 | local srcAliases = mod:get("aliases") 9 | local aliases = {} 10 | for iAlias = 0, srcAliases:size() - 2, 1 do 11 | table.insert(aliases, srcAliases:get(iAlias):getAsString()) -- lua arrays start at 1 12 | end 13 | 14 | local hash = base64toLowerHexString(modDetails:get("hash"):getAsString()) 15 | 16 | table.insert(mods, { 17 | approvalStatus = modDetails:get("approvalStatus"):getAsString(), 18 | name = modDetails:get("name"):getAsString(), 19 | version = modDetails:get("modVersion"):getAsString(), 20 | downloadLink = modDetails:get("downloadLink"):getAsString(), 21 | modtype = modDetails:get("modType"):getAsString(), 22 | haspending = mod:get("hasPending"):getAsBoolean(), 23 | aliases = aliases, 24 | hash = hash 25 | }) 26 | end 27 | 28 | return mods -------------------------------------------------------------------------------- /.idea/checkstyle-idea.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8.44 5 | JavaOnly 6 | true 7 | true 8 | 12 | 19 | 20 | -------------------------------------------------------------------------------- /apiscripts/curseforge.lua: -------------------------------------------------------------------------------- 1 | local apiData = data:get("data"):getAsJsonArray() 2 | local mods = {} 3 | 4 | for i = 0, apiData:size() - 1, 1 do 5 | local mod = apiData:get(i) 6 | if mod:get("name"):getAsString() == "MelonLoader" then goto continue end 7 | 8 | local latestFiles = mod:get("latestFiles"):get(0) 9 | 10 | -- CurseForge doesn't have a proper mod versioning, so we have to use the file name instead 11 | local rawfilename = latestFiles:get("fileName"):getAsString() 12 | -- remove the file extension 13 | local filenameWithoutExt = string.gsub(rawfilename, "%.[^%.]*$", "") 14 | -- get only the version from the filename (can have a "v", and be preceded by a space, "-", or "_") 15 | local version = string.gsub(filenameWithoutExt, "^.[^%d]+[ _-]v?", "") 16 | 17 | table.insert(mods, { 18 | id = tostring(mod:get("id"):getAsInt()), 19 | name = mod:get("name"):getAsString(), 20 | version = version, 21 | downloadLink = latestFiles:get("downloadUrl"):getAsString() 22 | }) 23 | ::continue:: 24 | end 25 | 26 | return mods -------------------------------------------------------------------------------- /src/slaynash/lum/bot/api/WebRequest.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.api; 2 | 3 | import java.util.Map; 4 | 5 | public class WebRequest { 6 | 7 | public final String path; 8 | public RequestMethod method = RequestMethod.NONE; 9 | public final Map parameters; 10 | public final Map headers; 11 | public byte[] content = new byte[0]; 12 | public final String clientIpAddress; 13 | 14 | public WebRequest(String path, String method, Map parameters, Map headers, String clientIpAddress) { 15 | if (method.equals("GET")) 16 | this.method = RequestMethod.GET; 17 | if (method.equals("HEAD")) 18 | this.method = RequestMethod.HEAD; 19 | if (method.equals("POST")) 20 | this.method = RequestMethod.POST; 21 | if (method.equals("PUT")) 22 | this.method = RequestMethod.PUT; 23 | 24 | this.path = path; 25 | this.parameters = parameters; 26 | this.headers = headers; 27 | this.clientIpAddress = clientIpAddress; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/melonscanner/MelonDuplicateMod.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.melonscanner; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class MelonDuplicateMod { 7 | 8 | public final List names = new ArrayList<>(); 9 | 10 | public MelonDuplicateMod(String name1, String name2) { 11 | names.add(name1); 12 | if (!name1.equals(name2)) 13 | names.add(name2); 14 | } 15 | 16 | public MelonDuplicateMod(String name1) { 17 | names.add(name1); 18 | } 19 | 20 | public void addName(String name) { 21 | if (names.stream().noneMatch(n -> n.equals(name))) 22 | names.add(name); 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | StringBuilder r = new StringBuilder(names.get(0)); 28 | for (int i = 1; i < names.size(); ++i) 29 | r.append("/").append(names.get(i)); 30 | return r.toString(); 31 | } 32 | 33 | public boolean hasName(String name) { 34 | return names.stream().anyMatch(n -> n.equals(name)); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a report to help us improve 3 | title: "[BUG REPORT]: " 4 | labels: ["bug"] 5 | assignees: [] 6 | body: 7 | - type: textarea 8 | id: bug 9 | attributes: 10 | label: Describe the bug 11 | description: A clear and concise description of what the bug is. 12 | validations: 13 | required: true 14 | - type: textarea 15 | id: steps 16 | attributes: 17 | label: To Reproduce 18 | description: "Steps to reproduce the behavior:" 19 | placeholder: "1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error" 20 | validations: 21 | required: true 22 | - type: textarea 23 | id: behavior 24 | attributes: 25 | label: Expected behavior 26 | description: A clear and concise description of what you expected to happen. 27 | validations: 28 | required: true 29 | - type: textarea 30 | id: additional 31 | attributes: 32 | label: Additional context 33 | description: Add any other context or screenshots about the problem here. 34 | validations: 35 | required: false 36 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/slashs/Slash.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.slashs; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; 7 | import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; 8 | import net.dv8tion.jda.api.interactions.commands.build.CommandData; 9 | import slaynash.lum.bot.utils.ExceptionUtils; 10 | 11 | public abstract class Slash { 12 | protected Slash instance = this; 13 | 14 | protected CommandData globalSlashData() { 15 | return null; 16 | } 17 | protected Map<Long, CommandData> guildSlashData() { 18 | return null; 19 | } 20 | protected List<String> buttonList() { 21 | return null; 22 | } 23 | 24 | protected void slashRun(SlashCommandInteractionEvent event) { 25 | ExceptionUtils.reportException("A slash command was called but no slashRun method was implemented"); 26 | } 27 | protected void buttonClick(ButtonInteractionEvent event) { 28 | ExceptionUtils.reportException("A button was clicked but no buttonClick method was implemented"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/GuildConfiguration.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord; 2 | 3 | import java.sql.Timestamp; 4 | 5 | public record GuildConfiguration(String uildID, Timestamp ts, boolean ScamShield, 6 | boolean ScamShieldBan, boolean ScamShieldCross, boolean ScamShieldDm, boolean MLLogScan, boolean MLLogReaction, 7 | boolean MLReplies, boolean MLPartialRemover, boolean MLGeneralRemover, boolean DLLRemover, boolean LumReplies, boolean DadJokes) 8 | { 9 | public enum Setting { 10 | TS("ts"), 11 | SCAMSHIELD("ScamShield"), 12 | DLLREMOVER("DLLRemover"), 13 | LOGREACTION("MLLogReaction"), 14 | LUMREPLIES("LumReplies"), 15 | PARTIALLOGREMOVER("MLPartialRemover"), 16 | GENERALLOGREMOVER("MLGeneralRemover"), 17 | DADJOKES("DadJokes"), 18 | LOGSCAN("MLLogScan"), 19 | MLREPLIES("MLReplies"), 20 | SSBAN("ScamShieldBan"), 21 | SSCROSS("ScamShieldCross"), 22 | SSDM("ScamShieldDm"); 23 | public final String string; 24 | Setting(String string) { 25 | this.string = string; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/HandledServerMessageContext.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord; 2 | 3 | import java.time.LocalDateTime; 4 | import java.time.ZoneOffset; 5 | 6 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 7 | import slaynash.lum.bot.discord.ScamShield.ScamResults; 8 | 9 | public class HandledServerMessageContext { 10 | 11 | public final MessageReceivedEvent messageReceivedEvent; 12 | public final ScamResults suspiciousResults; 13 | public final long guildId; 14 | public final LocalDateTime creationTime; 15 | 16 | public HandledServerMessageContext(MessageReceivedEvent messageReceivedEvent, ScamResults suspiciousResults, long guildId) { 17 | this.messageReceivedEvent = messageReceivedEvent; 18 | this.suspiciousResults = suspiciousResults; 19 | this.guildId = guildId; 20 | this.creationTime = LocalDateTime.now(ZoneOffset.UTC); 21 | } 22 | public HandledServerMessageContext(MessageReceivedEvent messageReceivedEvent, long guildId) { 23 | this.messageReceivedEvent = messageReceivedEvent; 24 | this.guildId = guildId; 25 | this.suspiciousResults = null; 26 | this.creationTime = LocalDateTime.now(ZoneOffset.UTC); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/javaci.yaml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Cancel Previous Runs 11 | uses: styfle/cancel-workflow-action@0.12.1 12 | with: 13 | access_token: ${{ github.token }} 14 | - name: Checkout 15 | uses: actions/checkout@v4.2.2 16 | - name: Set up JDK 17 17 | uses: actions/setup-java@v4.7.1 18 | with: 19 | distribution: 'zulu' 20 | java-version: 17 21 | - name: Cache local Maven repository 22 | uses: actions/cache@main 23 | with: 24 | path: ~/.m2/repository 25 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} 26 | restore-keys: | 27 | ${{ runner.os }}-maven- 28 | - name: Build with Maven 29 | run: mvn --batch-mode --update-snapshots verify 30 | - name: Adding file name to path 31 | run: echo buildname=$(ls /home/runner/work/Lumbot/Lumbot/target | grep bot) >> $GITHUB_ENV 32 | 33 | - name: Upload to artifacts 34 | uses: actions/upload-artifact@v4.6.2 35 | with: 36 | name: ${{ env.buildname }} 37 | path: target/${{ env.buildname }} -------------------------------------------------------------------------------- /unityversionsmonitor/HashChecker/HashChecker.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.002.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HashChecker", "HashChecker.csproj", "{A93D74AF-8978-43EF-A8C3-D3DBC7AC1D60}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {A93D74AF-8978-43EF-A8C3-D3DBC7AC1D60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {A93D74AF-8978-43EF-A8C3-D3DBC7AC1D60}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {A93D74AF-8978-43EF-A8C3-D3DBC7AC1D60}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {A93D74AF-8978-43EF-A8C3-D3DBC7AC1D60}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {D680E312-F838-4C21-BFC7-6515E8D53637} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Suggest an idea for this project 3 | title: "[FEATURE REQUEST]: <title>" 4 | labels: ["feature"] 5 | assignees: [] 6 | body: 7 | - type: textarea 8 | id: problem 9 | attributes: 10 | label: Is your feature request related to a problem? Please describe. 11 | description: A clear and concise description of what the problem is. 12 | placeholder: Ex. I'm always frustrated when [...] 13 | validations: 14 | required: true 15 | - type: textarea 16 | id: solution 17 | attributes: 18 | label: Describe the solution you'd like 19 | description: A clear and concise description of what you want to happen. 20 | validations: 21 | required: true 22 | - type: textarea 23 | id: alternatives 24 | attributes: 25 | label: Describe alternatives you've considered 26 | description: A clear and concise description of any alternative solutions or features you've considered. 27 | validations: 28 | required: true 29 | - type: textarea 30 | id: additional 31 | attributes: 32 | label: Additional context 33 | description: Add any other context or screenshots about the feature request here. 34 | validations: 35 | required: false 36 | -------------------------------------------------------------------------------- /.github/workflows/translationsCD.yaml: -------------------------------------------------------------------------------- 1 | name: Localization CD 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'localization.json' 9 | - '!src/**' 10 | - '!pom.xml' 11 | workflow_dispatch: 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Cancel Previous Runs 19 | uses: styfle/cancel-workflow-action@0.12.1 20 | with: 21 | access_token: ${{ github.token }} 22 | - name: Checkout 23 | uses: actions/checkout@v4.2.2 24 | 25 | - name: Upload to server 26 | if: github.event.repository.fork == false 27 | run: | 28 | curl \ 29 | -F 'localization=@localization.json' \ 30 | https://${{ secrets.SSH_HOST }}/uploadlum.php?key=${{ secrets.UPLOAD_KEY }} 31 | shell: bash 32 | 33 | - name: Running remote script 34 | uses: garygrossgarten/github-action-ssh@release 35 | if: github.event.repository.fork == false 36 | with: 37 | command: | 38 | python3 ~/downloadLocalization.py localization.json 39 | 40 | host: ${{ secrets.SSH_HOST }} 41 | port: ${{ secrets.SSH_PORT }} 42 | username: ${{ secrets.SSH_USERNAME }} 43 | password: ${{ secrets.SSH_PASSWORD }} -------------------------------------------------------------------------------- /src/slaynash/lum/bot/timers/ClearDMs.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.timers; 2 | 3 | import java.time.OffsetDateTime; 4 | import java.util.List; 5 | import java.util.Timer; 6 | import java.util.TimerTask; 7 | 8 | import net.dv8tion.jda.api.entities.Guild; 9 | import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; 10 | import slaynash.lum.bot.discord.JDAManager; 11 | 12 | public class ClearDMs extends TimerTask { 13 | public void run() { 14 | Guild mainGuild = JDAManager.getJDA().getGuildById(JDAManager.mainGuildID); 15 | if (mainGuild == null) 16 | return; 17 | List<TextChannel> channels = mainGuild.getCategoryById(924780998124798022L).getTextChannels(); 18 | channels.forEach(c -> c.retrieveMessageById(c.getLatestMessageId()).queue(m -> { 19 | if (m.getTimeCreated().isBefore(OffsetDateTime.now().minusDays(14))) { 20 | c.delete().queue(); 21 | } 22 | }, e -> System.out.println("Failed to retrieve message from channel " + c.getName()))); 23 | } 24 | 25 | public static void start() { 26 | Timer timer = new Timer(); 27 | timer.schedule( 28 | new ClearDMs(), 29 | java.util.Calendar.getInstance().getTime(), 30 | 1000 * 60 * 60 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/melonscannererrorsCD.yaml: -------------------------------------------------------------------------------- 1 | name: Melon Scanner Errors CD 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'melonscannererrors.json' 9 | - '!src/**' 10 | - '!pom.xml' 11 | workflow_dispatch: 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Cancel Previous Runs 19 | uses: styfle/cancel-workflow-action@0.12.1 20 | with: 21 | access_token: ${{ github.token }} 22 | - name: Checkout 23 | uses: actions/checkout@v4.2.2 24 | 25 | - name: Upload to server 26 | if: github.event.repository.fork == false 27 | run: | 28 | curl \ 29 | -F 'melonscannererrors=@melonscannererrors.json' \ 30 | https://${{ secrets.SSH_HOST }}/uploadlum.php?key=${{ secrets.UPLOAD_KEY }} 31 | shell: bash 32 | 33 | - name: Running remote script 34 | uses: garygrossgarten/github-action-ssh@release 35 | if: github.event.repository.fork == false 36 | with: 37 | command: | 38 | python3 ~/downloadMelonscannererrors.py melonscannererrors.json 39 | 40 | host: ${{ secrets.SSH_HOST }} 41 | port: ${{ secrets.SSH_PORT }} 42 | username: ${{ secrets.SSH_USERNAME }} 43 | password: ${{ secrets.SSH_PASSWORD }} -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/commands/VerifyCommandCommand.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.commands; 2 | 3 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 4 | import slaynash.lum.bot.discord.Command; 5 | import slaynash.lum.bot.discord.CommandManager; 6 | import slaynash.lum.bot.discord.VerifyPair; 7 | 8 | public class VerifyCommandCommand extends Command { 9 | 10 | @Override 11 | protected void onServer(String paramString, MessageReceivedEvent paramMessageReceivedEvent) { 12 | VerifyPair pair = CommandManager.verifyChannels.get(paramMessageReceivedEvent.getGuild().getIdLong()); 13 | if (pair != null) { 14 | //System.out.println("Server has registered verify command with channel " + pair.channelId + " and role " + pair.roleId); 15 | if (paramMessageReceivedEvent.getChannel().getId().equals(pair.channelId())) { 16 | paramMessageReceivedEvent.getGuild().addRoleToMember(paramMessageReceivedEvent.getAuthor(), paramMessageReceivedEvent.getGuild().getRoleById(pair.roleId())).queue(); 17 | paramMessageReceivedEvent.getChannel().sendMessage("You are now verified!").queue(); 18 | } 19 | } 20 | } 21 | 22 | @Override 23 | protected boolean matchPattern(String paramString) { 24 | return paramString.equals("!verify"); 25 | } 26 | 27 | @Override 28 | public boolean includeInHelp(MessageReceivedEvent event) { 29 | return false; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/PrivateMessagesHandler.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord; 2 | 3 | import java.util.List; 4 | 5 | import net.dv8tion.jda.api.entities.Message.Attachment; 6 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 7 | 8 | public class PrivateMessagesHandler { 9 | public static final String LOG_IDENTIFIER = "PrivateMessagesHandler"; 10 | 11 | public static void handle(MessageReceivedEvent event) { 12 | 13 | if (event.getAuthor().getIdLong() != JDAManager.getJDA().getSelfUser().getIdLong()) { 14 | System.out.println(String.format("[DM] %s%s%s: %s", 15 | event.getAuthor().getEffectiveName(), 16 | event.getMessage().isEdited() ? " *edited*" : "", 17 | event.getMessage().getType().isSystem() ? " *system*" : "", 18 | event.getMessage().getContentRaw().replace("\n", "\n\t\t"))); 19 | List<Attachment> attachments = event.getMessage().getAttachments(); 20 | if (!attachments.isEmpty()) { 21 | System.out.println(attachments.size() + " Files"); 22 | for (Attachment a : attachments) 23 | System.out.println(" - " + a.getUrl()); 24 | } 25 | if (ScamShield.checkForFishingPrivate(event)) { 26 | System.out.println("I was DM'd a Scam"); 27 | return; 28 | } 29 | 30 | MessageProxy.fromDM(event); 31 | } 32 | // CommandManager.runAsClient(event); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/melonscanner/MelonApiMod.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.melonscanner; 2 | 3 | import com.github.zafarkhaja.semver.Version; 4 | 5 | public class MelonApiMod { 6 | public final String id; 7 | public final String name; 8 | public final ModVersion[] versions; 9 | public final String downloadLink; 10 | public final String[] aliases; 11 | public final String modtype; 12 | public final boolean haspending; 13 | public final boolean isbroken; 14 | 15 | public MelonApiMod(String id, String name, ModVersion[] versions, String downloadLink, String[] aliases, String modtype, boolean haspending, boolean isbroken) { 16 | this.id = id; 17 | this.name = name; 18 | this.versions = versions; 19 | this.downloadLink = downloadLink; 20 | this.aliases = aliases; 21 | this.modtype = modtype; 22 | this.haspending = haspending; 23 | this.isbroken = isbroken; 24 | } 25 | 26 | public MelonApiMod(String id, String name, Version version, String downloadLink, String[] aliases, String hash, String modtype, boolean haspending, boolean isbroken) { 27 | this(id, name, new ModVersion[] {new ModVersion(version, hash)}, downloadLink, aliases, modtype, haspending, isbroken); 28 | } 29 | 30 | public MelonApiMod(String id, String name, Version version, String downloadLink, String[] aliases) { 31 | this(id, name, new ModVersion[] {new ModVersion(version, null)}, downloadLink, aliases, "", false, false); 32 | } 33 | 34 | public String getName() { 35 | return name; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <project version="4"> 3 | <component name="RemoteRepositoriesConfiguration"> 4 | <remote-repository> 5 | <option name="id" value="central" /> 6 | <option name="name" value="Central Repository" /> 7 | <option name="url" value="https://repo.maven.apache.org/maven2" /> 8 | </remote-repository> 9 | <remote-repository> 10 | <option name="id" value="jitpack.io" /> 11 | <option name="name" value="jitpack.io" /> 12 | <option name="url" value="https://jitpack.io" /> 13 | </remote-repository> 14 | <remote-repository> 15 | <option name="id" value="sonatype" /> 16 | <option name="name" value="sonatype" /> 17 | <option name="url" value="https://oss.sonatype.org/content/groups/public/" /> 18 | </remote-repository> 19 | <remote-repository> 20 | <option name="id" value="central" /> 21 | <option name="name" value="Maven Central repository" /> 22 | <option name="url" value="https://repo1.maven.org/maven2" /> 23 | </remote-repository> 24 | <remote-repository> 25 | <option name="id" value="jboss.community" /> 26 | <option name="name" value="JBoss Community repository" /> 27 | <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" /> 28 | </remote-repository> 29 | <remote-repository> 30 | <option name="id" value="sonatype" /> 31 | <option name="name" value="sonatype" /> 32 | <option name="url" value="https://central.sonatype.com/repository/maven-snapshots/" /> 33 | </remote-repository> 34 | </component> 35 | </project> -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <classpath> 3 | <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"> 4 | <attributes> 5 | <attribute name="maven.pomderived" value="true"/> 6 | </attributes> 7 | </classpathentry> 8 | <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"> 9 | <attributes> 10 | <attribute name="maven.pomderived" value="true"/> 11 | </attributes> 12 | </classpathentry> 13 | <classpathentry including="**/*.java" kind="src" output="target/classes" path="src"> 14 | <attributes> 15 | <attribute name="maven.pomderived" value="true"/> 16 | <attribute name="optional" value="true"/> 17 | </attributes> 18 | </classpathentry> 19 | <classpathentry kind="src" path="target/generated-sources/annotations"> 20 | <attributes> 21 | <attribute name="optional" value="true"/> 22 | <attribute name="maven.pomderived" value="true"/> 23 | <attribute name="ignore_optional_problems" value="true"/> 24 | <attribute name="m2e-apt" value="true"/> 25 | </attributes> 26 | </classpathentry> 27 | <classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations"> 28 | <attributes> 29 | <attribute name="optional" value="true"/> 30 | <attribute name="maven.pomderived" value="true"/> 31 | <attribute name="ignore_optional_problems" value="true"/> 32 | <attribute name="m2e-apt" value="true"/> 33 | </attributes> 34 | </classpathentry> 35 | <classpathentry kind="output" path="target/classes"/> 36 | </classpath> 37 | -------------------------------------------------------------------------------- /unityversionsmonitor/HashChecker/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // Les informations générales relatives à un assembly dépendent de 6 | // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations 7 | // associées à un assembly. 8 | [assembly: AssemblyTitle("HashChecker")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("HashChecker")] 13 | [assembly: AssemblyCopyright("Copyright © 2021")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly 18 | // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de 19 | // COM, affectez la valeur true à l'attribut ComVisible sur ce type. 20 | [assembly: ComVisible(false)] 21 | 22 | // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM 23 | [assembly: Guid("d804597e-ca4e-495f-ab24-65090625f9cc")] 24 | 25 | // Les informations de version pour un assembly se composent des quatre valeurs suivantes : 26 | // 27 | // Version principale 28 | // Version secondaire 29 | // Numéro de build 30 | // Révision 31 | // 32 | // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut 33 | // en utilisant '*', comme indiqué ci-dessous : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/commands/TestVRCObfmap.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.commands; 2 | 3 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 4 | import slaynash.lum.bot.ConfigManager; 5 | import slaynash.lum.bot.VRChatVersionComparer; 6 | import slaynash.lum.bot.discord.Command; 7 | import slaynash.lum.bot.discord.utils.CrossServerUtils; 8 | 9 | public class TestVRCObfmap extends Command { 10 | 11 | @Override 12 | protected void onServer(String paramString, MessageReceivedEvent event) { 13 | if (!includeInHelp(event)) 14 | return; 15 | 16 | String[] parts = paramString.split(" "); 17 | 18 | if (parts.length != 4) { 19 | event.getMessage().reply("usage: " + ConfigManager.discordPrefix + getName() + " <manifestid> <branch> <map url>").queue(); 20 | return; 21 | } 22 | 23 | Thread t = new Thread(() -> { 24 | VRChatVersionComparer.obfMapUrl = parts[3]; 25 | VRChatVersionComparer.run(parts[1], parts[2], event); 26 | }, "TestVRCObfMap"); 27 | t.setDaemon(true); 28 | t.start(); 29 | } 30 | 31 | @Override 32 | protected boolean matchPattern(String paramString) { 33 | return paramString.split(" ", 2)[0].equals(ConfigManager.discordPrefix + getName()); 34 | } 35 | 36 | @Override 37 | public boolean includeInHelp(MessageReceivedEvent event) { 38 | return CrossServerUtils.isLumDev(event.getMember()); 39 | } 40 | 41 | @Override 42 | public String getHelpDescription() { 43 | return "Tests the VRChat obfucation map"; 44 | } 45 | 46 | @Override 47 | public String getName() { 48 | return "testvrcobfmap"; 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lumbot 2 | 3 | Lum Discord Bot 4 | 5 | ## Installation 6 | 7 | Make a file in the working path called `.settings.conf` with the following content add your own Discord token and SQL database: 8 | 9 | ``` 10 | DISCORD_TOKEN= 11 | MAIN_BOT= 12 | PING_URL= 13 | 14 | DB_ADDRESS= 15 | DB_PORT= 16 | DB_DATABASELUM=lum 17 | DB_DATABASESHORTURL=shorturls 18 | DB_LOGIN= 19 | DB_PASSWORD= 20 | 21 | DISCORD_PREFIX=l! 22 | CURSEFORGE_API_KEY= 23 | GitHub_API_KEY= 24 | ANIMESCHEDULE_API_KEY= 25 | ``` 26 | 27 | Initialize your SQL with the InitLumSQL.sql file 28 | 29 | Currently, Lum assumes that it is run on Linux-like OS (Currently Debian 11) and that the language is not Turkish. 30 | 31 | ## Features 32 | 33 | * MelonLoader Log scans 34 | * Scammer remover (Scam Shield) 35 | * Steam Depo change notifications 36 | * Dad Jokes 37 | * Funny Replies 38 | * ABCDEFG game 39 | * Moderation (Ban, Kick, Purge, Reply Purge) 40 | * Remove EXE, ZIP, DLL, and other suspicious messages from non-staff 41 | * Auto Publish announcement channels 42 | * Dump IDs (get all ID from regex on username) 43 | * Custom Rank Colors 44 | * Custom Replies (also via regex) (also reactions) 45 | * Give Role on successful member screening 46 | * Give Role on reaction 47 | * Verifier 48 | * DM message Proxy to Devs 49 | 50 | --- 51 | 52 | ## Licence 53 | 54 | This work is licensed under a 55 | [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License][cc-by-nc-sa]. 56 | 57 | [![CC BY-NC-SA 4.0][cc-by-nc-sa-image]][cc-by-nc-sa] 58 | 59 | [cc-by-nc-sa]: http://creativecommons.org/licenses/by-nc-sa/4.0/ 60 | [cc-by-nc-sa-image]: https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png 61 | [cc-by-nc-sa-shield]: https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-lightgrey.svg -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/commands/LumGoneCommand.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.commands; 2 | 3 | import net.dv8tion.jda.api.OnlineStatus; 4 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 5 | import slaynash.lum.bot.discord.Command; 6 | import slaynash.lum.bot.discord.JDAManager; 7 | import slaynash.lum.bot.discord.utils.CrossServerUtils; 8 | 9 | public class LumGoneCommand extends Command { 10 | 11 | @Override 12 | protected void onServer(String str, MessageReceivedEvent event) { 13 | if (JDAManager.getJDA().getPresence().getStatus().equals(OnlineStatus.INVISIBLE) && str.toLowerCase().contains("lum") && str.toLowerCase().contains("come back")) { 14 | event.getChannel().sendMessage("Nope, I am not here <:Neko_pout:865328471102324778>").queue(); 15 | return; 16 | } 17 | 18 | if (!(CrossServerUtils.isLumDev(event.getMember()) || event.getAuthor().getIdLong() == 209206057594126336L)) 19 | return; 20 | 21 | event.getChannel().sendMessage("Ok I'm leaving...").queue(); 22 | JDAManager.getJDA().getPresence().setStatus(OnlineStatus.INVISIBLE); 23 | try { 24 | Thread.sleep(15000); 25 | } 26 | catch (InterruptedException e) { 27 | e.printStackTrace(); 28 | } 29 | JDAManager.getJDA().getPresence().setStatus(OnlineStatus.ONLINE); 30 | event.getChannel().sendMessage("JK I am not allowed to leave").queue(); 31 | } 32 | 33 | @Override 34 | protected boolean matchPattern(String str) { 35 | return str.toLowerCase().contains("lum stop"); 36 | } 37 | 38 | @Override 39 | public boolean includeInHelp(MessageReceivedEvent event) { 40 | return false; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/commands/CommandLaunchCommand.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.commands; 2 | 3 | import java.io.File; 4 | 5 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 6 | import org.luaj.vm2.Globals; 7 | import org.luaj.vm2.LuaError; 8 | import org.luaj.vm2.LuaValue; 9 | import slaynash.lum.bot.ConfigManager; 10 | import slaynash.lum.bot.discord.Command; 11 | import slaynash.lum.bot.discord.LuaPackages; 12 | import slaynash.lum.bot.utils.ExceptionUtils; 13 | 14 | public class CommandLaunchCommand extends Command { 15 | 16 | @Override 17 | protected void onServer(String command, MessageReceivedEvent event) { 18 | System.out.println("loading lua file commands/" + event.getMessage().getContentRaw().split(" ")[0].replaceAll("\n", "").replaceAll("[^a-zA-Z\\d.-]", "_")); 19 | Globals m_globals = LuaPackages.createCommandGlobals(event); 20 | try { 21 | File rom = new File("commands/" + command.substring(2).split(" ")[0].replaceAll("\n", "").replaceAll("[^a-zA-Z\\d.-]", "_") + ".lua"); 22 | m_globals.get("dofile").call(LuaValue.valueOf(rom.toString())); 23 | } 24 | catch (LuaError e) { 25 | ExceptionUtils.reportException("An error has occurred: \"" + command.substring(2).split(" ")[0] + "\"", e); 26 | } 27 | } 28 | 29 | @Override 30 | protected boolean matchPattern(String pattern) { 31 | return pattern.startsWith(ConfigManager.discordPrefix) && new File("commands/" + pattern.substring(2).split(" ")[0].replaceAll("\n", "").replaceAll("[^a-zA-Z\\d.-]", "_") + ".lua").exists() && !pattern.substring(2).split(" ")[0].isBlank(); 32 | } 33 | 34 | @Override 35 | public boolean includeInHelp(MessageReceivedEvent event) { 36 | return false; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/commands/HelpCommand.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.commands; 2 | 3 | import java.awt.Color; 4 | 5 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 6 | import slaynash.lum.bot.ConfigManager; 7 | import slaynash.lum.bot.discord.Command; 8 | import slaynash.lum.bot.discord.CommandManager; 9 | import slaynash.lum.bot.utils.Utils; 10 | 11 | public class HelpCommand extends Command { 12 | 13 | @Override 14 | protected boolean matchPattern(String pattern) { 15 | return pattern.split(" ", 2)[0].equals(ConfigManager.discordPrefix + getName()); 16 | } 17 | 18 | @Override 19 | protected void onServer(String command, MessageReceivedEvent event) { 20 | 21 | StringBuilder helpMessage = new StringBuilder("**__Help :__**\n\n"); 22 | 23 | boolean empty = true; 24 | for (Command cmd : CommandManager.getCommands()) { 25 | if (cmd.includeInHelp(event)) { 26 | empty = false; 27 | helpMessage.append("**").append(cmd.getName()).append("**: ").append(cmd.getHelpDescription()).append("\n"); 28 | } 29 | } 30 | if (!empty) { 31 | Utils.replyEmbed(helpMessage.toString(), Color.CYAN, event); 32 | } 33 | else { 34 | helpMessage.append("**No commands found**"); 35 | Utils.replyEmbed(helpMessage.toString(), Color.RED, event); 36 | } 37 | } 38 | 39 | @Override 40 | public String getHelpDescription() { 41 | return "Show a description of all commands"; 42 | } 43 | 44 | @Override 45 | public String getName() { 46 | return "help"; 47 | } 48 | 49 | @Override 50 | public boolean includeInHelp(MessageReceivedEvent event) { 51 | return false; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /.github/workflows/javaCICD.yaml: -------------------------------------------------------------------------------- 1 | name: Java CI & CD 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'src/**' 9 | - 'pom.xml' 10 | workflow_dispatch: 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Cancel Previous Runs 18 | uses: styfle/cancel-workflow-action@0.12.1 19 | with: 20 | access_token: ${{ github.token }} 21 | - name: Checkout 22 | uses: actions/checkout@v4.2.2 23 | - name: Set up JDK 17 24 | uses: actions/setup-java@v4.7.1 25 | with: 26 | distribution: 'zulu' 27 | java-version: 17 28 | - name: Cache local Maven repository 29 | uses: actions/cache@main 30 | with: 31 | path: ~/.m2/repository 32 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} 33 | restore-keys: | 34 | ${{ runner.os }}-maven- 35 | - name: Build with Maven 36 | run: mvn --batch-mode --update-snapshots verify 37 | - name: Adding file name to path 38 | run: echo buildname=$(ls /home/runner/work/Lumbot/Lumbot/target | grep bot) >> $GITHUB_ENV 39 | 40 | - name: Upload to artifacts 41 | uses: actions/upload-artifact@v4.6.2 42 | with: 43 | name: ${{ env.buildname }} 44 | path: target/${{ env.buildname }} 45 | - name: Upload to server 46 | if: github.event.repository.fork == false 47 | run: | 48 | curl \ 49 | -F 'buildfile=@target/${{ env.buildname }}' \ 50 | https://${{ secrets.SSH_HOST }}/uploadlum.php?key=${{ secrets.UPLOAD_KEY }} 51 | shell: bash 52 | 53 | - name: Running remote script 54 | uses: garygrossgarten/github-action-ssh@release 55 | if: github.event.repository.fork == false 56 | with: 57 | command: | 58 | python3 ~/downloadAndRestart.py ${{ env.buildname }} 59 | 60 | host: ${{ secrets.SSH_HOST }} 61 | port: ${{ secrets.SSH_PORT }} 62 | username: ${{ secrets.SSH_USERNAME }} 63 | password: ${{ secrets.SSH_PASSWORD }} -------------------------------------------------------------------------------- /.github/workflows/apiscriptsCD.yaml: -------------------------------------------------------------------------------- 1 | name: Api Scripts CD 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'apiscripts/**' 9 | - '!src/**' 10 | - '!pom.xml' 11 | workflow_dispatch: 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Cancel Previous Runs 19 | uses: styfle/cancel-workflow-action@0.12.1 20 | with: 21 | access_token: ${{ github.token }} 22 | - name: Checkout 23 | uses: actions/checkout@v4.2.2 24 | 25 | - name: Upload to server 26 | if: github.event.repository.fork == false 27 | run: | 28 | curl \ 29 | -F 'apiscripts/audica_ahriana=@apiscripts/audica_ahriana.lua' \ 30 | -F 'apiscripts/btd6_gurrenm4=@apiscripts/btd6_gurrenm4.lua' \ 31 | -F 'apiscripts/btd6_inferno=@apiscripts/btd6_inferno.lua' \ 32 | -F 'apiscripts/curseforge=@apiscripts/curseforge.lua' \ 33 | -F 'apiscripts/musedash=@apiscripts/musedash.lua' \ 34 | -F 'apiscripts/thunderstore=@apiscripts/thunderstore.lua' \ 35 | -F 'apiscripts/tld=@apiscripts/tld.lua' \ 36 | -F 'apiscripts/uno=@apiscripts/uno.lua' \ 37 | -F 'apiscripts/vrcmg=@apiscripts/vrcmg.lua' \ 38 | https://${{ secrets.SSH_HOST }}/uploadlum.php?key=${{ secrets.UPLOAD_KEY }} 39 | shell: bash 40 | 41 | - name: Running remote script 42 | uses: garygrossgarten/github-action-ssh@release 43 | if: github.event.repository.fork == false 44 | with: 45 | command: | 46 | python3 ~/downloadApiscripts.py \ 47 | apiscripts/audica_ahriana.lua \ 48 | apiscripts/btd6_gurrenm4.lua \ 49 | apiscripts/btd6_inferno.lua \ 50 | apiscripts/thunderstore.lua \ 51 | apiscripts/curseforge.lua \ 52 | apiscripts/tld.lua \ 53 | apiscripts/uno.lua \ 54 | apiscripts/vrcmg.lua 55 | 56 | host: ${{ secrets.SSH_HOST }} 57 | port: ${{ secrets.SSH_PORT }} 58 | username: ${{ secrets.SSH_USERNAME }} 59 | password: ${{ secrets.SSH_PASSWORD }} -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/utils/MessageFinder.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.utils; 2 | 3 | import java.util.function.Consumer; 4 | 5 | import net.dv8tion.jda.api.Permission; 6 | import net.dv8tion.jda.api.entities.Guild; 7 | import net.dv8tion.jda.api.entities.Message; 8 | import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; 9 | 10 | public class MessageFinder { 11 | private boolean waiting = false; 12 | public boolean found = false; 13 | public Message message = null; 14 | public Throwable error = null; 15 | 16 | public void findMessageAsync(Guild guild, String messageId, Consumer<Message> success, Consumer<Throwable> failure) { 17 | new Thread(() -> { 18 | 19 | findMessage(guild, messageId); 20 | if (error != null) 21 | failure.accept(error); 22 | else 23 | success.accept(message); 24 | 25 | }, "MessageFindThread " + guild.getId()).start(); 26 | } 27 | 28 | public void findMessage(Guild guild, String messageId) { 29 | 30 | for (TextChannel tc : guild.getTextChannels()) { 31 | if (!guild.getSelfMember().hasPermission(tc, Permission.VIEW_CHANNEL)) 32 | continue; 33 | System.out.println("tc: " + tc); 34 | waiting = true; 35 | try { 36 | tc.retrieveMessageById(messageId).queue(success -> { 37 | message = success; 38 | found = true; 39 | waiting = false; 40 | }, failure -> { 41 | //System.err.println("F: " + failure.getMessage()); 42 | if (!failure.getMessage().startsWith("10008")) { 43 | error = failure; 44 | found = true; 45 | } 46 | waiting = false; 47 | }); 48 | } 49 | catch (Exception e) { 50 | waiting = false; 51 | System.out.println("Failed to read channel " + tc + ": " + e.getMessage()); 52 | } 53 | 54 | while (waiting) { 55 | try { 56 | Thread.sleep(1); 57 | } 58 | catch (Exception ignored) { } 59 | } 60 | if (found) 61 | break; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/log/LumLogOutput.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.log; 2 | 3 | import java.io.PrintStream; 4 | import java.lang.reflect.Field; 5 | import java.lang.reflect.Modifier; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | import slaynash.lum.bot.discord.Command; 10 | import slaynash.lum.bot.utils.TimeManager; 11 | 12 | public class LumLogOutput extends PrintStream { 13 | private final String name; 14 | 15 | private static final Map<Class<?>, String> knownLoggingElements = new HashMap<>(); 16 | 17 | public LumLogOutput(PrintStream defaultOut, String streamName) { 18 | super(defaultOut); 19 | this.name = streamName; 20 | } 21 | 22 | @Override 23 | public void println(String obj) { 24 | String loggingElement = null; 25 | for (StackTraceElement element : Thread.currentThread().getStackTrace()) { 26 | Class<?> elementClass; 27 | try { 28 | elementClass = Class.forName(element.getClassName()); 29 | } 30 | catch (Exception e) { 31 | break; 32 | } 33 | 34 | loggingElement = knownLoggingElements.get(elementClass); 35 | if (loggingElement != null) 36 | break; 37 | 38 | for (Field field : elementClass.getFields()) { 39 | if (field.getName().equals("LOG_IDENTIFIER") && Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers())) { 40 | try { 41 | loggingElement = (String) field.get(null); 42 | knownLoggingElements.put(elementClass, loggingElement); 43 | break; 44 | } 45 | catch (Exception ignored) { } 46 | } 47 | } 48 | if (loggingElement != null) 49 | break; 50 | 51 | if (elementClass.getSuperclass() != null && elementClass.getSuperclass() == Command.class) { 52 | knownLoggingElements.put(elementClass, loggingElement = elementClass.getName()); 53 | break; 54 | } 55 | } 56 | 57 | String[] lines = obj.split("\n"); 58 | for (String line : lines) { 59 | if (loggingElement != null) 60 | super.println("[" + TimeManager.getTimeForLog() + "] [" + name + "] [" + loggingElement + "] " + line); 61 | else 62 | super.println("[" + TimeManager.getTimeForLog() + "] [" + name + "] " + line); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/UrlShortener.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot; 2 | 3 | import java.sql.ResultSet; 4 | import java.sql.SQLException; 5 | 6 | import slaynash.lum.bot.utils.ExceptionUtils; 7 | 8 | public final class UrlShortener { 9 | private UrlShortener() { 10 | } 11 | 12 | public static String getShortenedUrl(String baseUrl) { 13 | if (baseUrl == null) 14 | return null; 15 | if (baseUrl.contains("discord.com") || baseUrl.contains("discord.gg")) 16 | return baseUrl; 17 | if (baseUrl.length() < 69) 18 | return baseUrl; 19 | String result; 20 | 21 | // Check if already exists 22 | try { 23 | ResultSet rs = DBConnectionManagerShortUrls.sendRequest("SELECT uid FROM shorturls WHERE url = ? LIMIT 1", baseUrl); 24 | if (rs.next()) { 25 | result = rs.getString("uid"); 26 | DBConnectionManagerShortUrls.closeRequest(rs); 27 | return "https://s.slaynash.fr/" + result; 28 | } 29 | } 30 | catch (SQLException e) { 31 | ExceptionUtils.reportException("Failed to fetch shorten url from database", e); 32 | return ""; 33 | } 34 | 35 | // Generate new unique one 36 | try { 37 | 38 | boolean exists; 39 | do { 40 | result = randomAlphaNumeric(8); 41 | ResultSet rs = DBConnectionManagerShortUrls.sendRequest("SELECT uid FROM shorturls WHERE uid = ? LIMIT 1", result); 42 | exists = rs.next(); 43 | DBConnectionManagerShortUrls.closeRequest(rs); 44 | } 45 | while (exists); 46 | 47 | DBConnectionManagerShortUrls.sendUpdate("INSERT INTO shorturls (uid, url) VALUES (?,?)", result, baseUrl); 48 | 49 | return "https://s.slaynash.fr/" + result; 50 | 51 | } 52 | catch (SQLException e) { 53 | ExceptionUtils.reportException("Failed to save shorten url in database", e); 54 | return ""; 55 | } 56 | } 57 | 58 | private static final String ALPHA_NUMERIC_STRING = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 59 | public static String randomAlphaNumeric(int count) { 60 | StringBuilder builder = new StringBuilder(count); 61 | while (count-- != 0) { 62 | int character = (int) (Math.random() * ALPHA_NUMERIC_STRING.length()); 63 | builder.append(ALPHA_NUMERIC_STRING.charAt(character)); 64 | } 65 | return builder.toString(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/commands/VerifyChannelHandlerCommand.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.commands; 2 | 3 | import net.dv8tion.jda.api.Permission; 4 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 5 | import slaynash.lum.bot.ConfigManager; 6 | import slaynash.lum.bot.discord.Command; 7 | import slaynash.lum.bot.discord.CommandManager; 8 | import slaynash.lum.bot.discord.VerifyPair; 9 | 10 | public class VerifyChannelHandlerCommand extends Command { 11 | 12 | @Override 13 | protected void onServer(String paramString, MessageReceivedEvent paramMessageReceivedEvent) { 14 | if (!paramMessageReceivedEvent.getMember().hasPermission(Permission.ADMINISTRATOR)) { 15 | paramMessageReceivedEvent.getChannel().sendMessage("Error: You need to have the Administrator permission").queue(); 16 | return; 17 | } 18 | 19 | if (CommandManager.verifyChannels.containsKey(paramMessageReceivedEvent.getGuild().getIdLong())) { 20 | CommandManager.verifyChannels.remove(paramMessageReceivedEvent.getGuild().getIdLong()); 21 | paramMessageReceivedEvent.getChannel().sendMessage("Successfully removed verify handler").queue(); 22 | } 23 | else { 24 | String[] parts = paramString.split(" ", 3); 25 | if (parts.length != 2) { 26 | paramMessageReceivedEvent.getChannel().sendMessage("Usage: " + ConfigManager.discordPrefix + getName() + " `roleid`").queue(); 27 | } 28 | try { 29 | Long.parseLong(parts[1]); 30 | } 31 | catch (Exception e) { 32 | paramMessageReceivedEvent.getChannel().sendMessage("Error: Invalid role id. usage: " + ConfigManager.discordPrefix + getName() + " `roleid`").queue(); 33 | return; 34 | } 35 | 36 | CommandManager.verifyChannels.put(paramMessageReceivedEvent.getGuild().getIdLong(), new VerifyPair(paramMessageReceivedEvent.getChannel().getId(), parts[1])); 37 | paramMessageReceivedEvent.getChannel().sendMessage("Successfully set verify handler").queue(); 38 | } 39 | CommandManager.saveVerifyChannels(); 40 | } 41 | 42 | @Override 43 | protected boolean matchPattern(String paramString) { 44 | return paramString.split(" ", 2)[0].equals(ConfigManager.discordPrefix + getName()); 45 | } 46 | 47 | @Override 48 | public boolean includeInHelp(MessageReceivedEvent event) { 49 | return event.getMember().hasPermission(Permission.ADMINISTRATOR); 50 | } 51 | 52 | @Override 53 | public String getName() { 54 | return "setverifychannel"; 55 | } 56 | 57 | @Override 58 | public String getHelpDescription() { 59 | return "Set the verify channel to the current one."; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/commands/Unban.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.commands; 2 | 3 | import net.dv8tion.jda.api.Permission; 4 | import net.dv8tion.jda.api.entities.Guild.Ban; 5 | import net.dv8tion.jda.api.entities.User; 6 | import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion; 7 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 8 | import slaynash.lum.bot.ConfigManager; 9 | import slaynash.lum.bot.discord.Command; 10 | import slaynash.lum.bot.discord.CommandManager; 11 | import slaynash.lum.bot.utils.Utils; 12 | 13 | public class Unban extends Command { 14 | @Override 15 | protected void onServer(String paramString, MessageReceivedEvent event) { 16 | if (!includeInHelp(event)) 17 | return; 18 | 19 | User unbanUser; 20 | String[] parts = paramString.split(" ", 2); 21 | if (parts.length < 2) { 22 | event.getMessage().reply("Usage: " + ConfigManager.discordPrefix + getName() + " <UserID>").queue(); 23 | return; 24 | } 25 | try { 26 | unbanUser = event.getJDA().getUserById(parts[1]); 27 | } 28 | catch (Exception e) { 29 | event.getMessage().reply("Invalid snowflake, User was not found!").queue(); 30 | return; 31 | } 32 | if (unbanUser == null) { 33 | event.getMessage().reply("User was not found!").queue(); 34 | return; 35 | } 36 | new Thread(() -> { 37 | Ban bannedMember = event.getGuild().retrieveBan(unbanUser).complete(); 38 | if (bannedMember == null) { 39 | event.getMessage().reply("User was not banned!").queue(); 40 | return; 41 | } 42 | event.getGuild().unban(unbanUser).reason("Unbanned by " + event.getMember().getEffectiveName()).queue(); 43 | 44 | MessageChannelUnion reportChannel = CommandManager.getModReportChannels(event, "ban"); 45 | if (reportChannel != null && !reportChannel.equals(event.getChannel())) 46 | Utils.sendMessage("User " + unbanUser.getAsMention() + "(" + unbanUser.getId() + ") has been unbanned by " + event.getMember().getEffectiveName() + "!", reportChannel); 47 | event.getChannel().sendMessage("User " + unbanUser.getAsMention() + "(" + unbanUser.getId() + ") has been unbanned!").queue(); 48 | }).start(); 49 | } 50 | 51 | @Override 52 | protected boolean matchPattern(String paramString) { 53 | return paramString.startsWith(ConfigManager.discordPrefix + getName()); 54 | } 55 | 56 | @Override 57 | public boolean includeInHelp(MessageReceivedEvent event) { 58 | return event.getGuild().getSelfMember().hasPermission(Permission.BAN_MEMBERS) && event.getMember().hasPermission(Permission.BAN_MEMBERS); 59 | } 60 | 61 | @Override 62 | public String getHelpDescription() { 63 | return "Unbans a member with UserID- Staff only"; 64 | } 65 | 66 | @Override 67 | public String getName() { 68 | return "unban"; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /unityversionsmonitor/HashChecker/SlaynashUtils/VersionUtils.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Text.RegularExpressions; 4 | 5 | namespace SlaynashUtils 6 | { 7 | public static class VersionUtils 8 | { 9 | // left more recent: 1 10 | // identicals: 0 11 | // right more recent: -1 12 | public static int CompareVersion(VersionData left, VersionData right) 13 | { 14 | if (left.IsValidSemver != right.IsValidSemver) 15 | return left.IsValidSemver ? 1 : -1; 16 | 17 | int compareLength = left.Length > right.Length ? left.Length : right.Length; 18 | for (int i = 0; i < compareLength; ++i) 19 | { 20 | int leftNumber = left.GetIndex(i); 21 | int rightNumber = right.GetIndex(i); 22 | 23 | if (leftNumber > rightNumber) 24 | return 1; 25 | if (leftNumber < rightNumber) 26 | return -1; 27 | } 28 | 29 | return 0; 30 | } 31 | 32 | // left more recent: 1 33 | // identicals: 0 34 | // right more recent: -1 35 | public static int CompareVersion(string left, string right) 36 | { 37 | return CompareVersion(GetVersion(left), GetVersion(right)); 38 | } 39 | 40 | public static VersionData GetVersion(string versionString) 41 | { 42 | versionString = versionString.Trim(); 43 | 44 | if (string.IsNullOrEmpty(versionString)) 45 | return VersionData.ZERO; 46 | 47 | MatchCollection matches = Regex.Matches(versionString, "\\d+"); 48 | bool isValidSemver = Regex.IsMatch(versionString, "^v?[0-9][\\d.-_]*[^\\s]*$"); 49 | 50 | return new VersionData(matches, isValidSemver); 51 | } 52 | 53 | 54 | public class VersionData 55 | { 56 | public static readonly VersionData ZERO = new VersionData(); 57 | 58 | public List<int> numbers; 59 | 60 | public bool IsValidSemver { get; } 61 | public int Length => numbers.Count; 62 | 63 | public VersionData() 64 | { 65 | IsValidSemver = false; 66 | numbers = new List<int>(0); 67 | } 68 | 69 | public VersionData(MatchCollection collection, bool validSemver) 70 | { 71 | IsValidSemver = validSemver; 72 | numbers = new List<int>(collection.Count); 73 | 74 | foreach (Match match in collection) 75 | { 76 | int parsedNumber = int.Parse(match.Value); 77 | numbers.Add(parsedNumber > 0 ? parsedNumber : 0); 78 | } 79 | } 80 | 81 | public int GetIndex(int index) 82 | { 83 | return numbers.Count > index ? numbers.ElementAt(index) : 0; 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/api/API.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.api; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.net.ServerSocket; 6 | import java.net.Socket; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import com.google.gson.Gson; 11 | import com.google.gson.GsonBuilder; 12 | import slaynash.lum.bot.Main; 13 | import slaynash.lum.bot.api.endpoints.PingEndpoint; 14 | import slaynash.lum.bot.api.endpoints.ReloadMelonScannerErrorsEndpoint; 15 | import slaynash.lum.bot.api.endpoints.ReloadTranslationsEndpoint; 16 | import slaynash.lum.bot.utils.ExceptionUtils; 17 | 18 | public class API { 19 | public static final String LOG_IDENTIFIER = "API"; 20 | 21 | public static final Map<String, Endpoint> endpoints = new HashMap<>(); 22 | 23 | public static Gson gson; 24 | 25 | private static ServerSocket socket = null; 26 | private static int totalConnectionCount; 27 | 28 | 29 | public static void start() { 30 | 31 | gson = new GsonBuilder() 32 | .setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ") 33 | .create(); 34 | 35 | try { 36 | endpoints.put("/api/1/internal/reloadtranslations", new ReloadTranslationsEndpoint()); 37 | endpoints.put("/api/1/internal/reloadmelonscannererrors", new ReloadMelonScannerErrorsEndpoint()); 38 | endpoints.put("/api/1/ping", new PingEndpoint()); 39 | 40 | while (socket == null) { 41 | try { 42 | socket = new ServerSocket(); 43 | socket.setReuseAddress(true); 44 | socket.bind(new InetSocketAddress(28644)); 45 | } 46 | catch (Exception e) { 47 | System.err.println("Error while creating socket: " + e.getMessage()); 48 | socket = null; 49 | Thread.sleep(60 * 1000); 50 | } 51 | } 52 | } 53 | catch (Exception e) { 54 | ExceptionUtils.reportException("Failed to start up API", e); 55 | return; 56 | } 57 | 58 | Thread thread = new Thread(() -> { 59 | while (!Main.isShuttingDown) { 60 | try { 61 | Socket clientSocket = socket.accept(); 62 | new APIClient(clientSocket, totalConnectionCount++); 63 | // APIClient client = new APIClient(clientSocket, totalConnectionCount++); 64 | // if (client.valid) 65 | // clients.add(client); 66 | } 67 | catch (IOException e) { 68 | ExceptionUtils.reportException("Failed to handle API request", e); 69 | } 70 | } 71 | try { 72 | System.out.println("Closing API socket..."); 73 | socket.close(); 74 | } 75 | catch (IOException e) { 76 | e.printStackTrace(); 77 | } 78 | }, "APIThread"); 79 | thread.setDaemon(false); 80 | thread.start(); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /unityversionsmonitor/HashChecker/HashChecker.csproj: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?> 2 | <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 3 | <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> 4 | <PropertyGroup> 5 | <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> 6 | <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> 7 | <ProjectGuid>{D804597E-CA4E-495F-AB24-65090625F9CC}</ProjectGuid> 8 | <OutputType>Exe</OutputType> 9 | <RootNamespace>HashChecker</RootNamespace> 10 | <AssemblyName>HashChecker</AssemblyName> 11 | <TargetFrameworkVersion>v4.8</TargetFrameworkVersion> 12 | <FileAlignment>512</FileAlignment> 13 | <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> 14 | <Deterministic>true</Deterministic> 15 | </PropertyGroup> 16 | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> 17 | <PlatformTarget>AnyCPU</PlatformTarget> 18 | <DebugSymbols>true</DebugSymbols> 19 | <DebugType>full</DebugType> 20 | <Optimize>false</Optimize> 21 | <OutputPath>bin\Debug\</OutputPath> 22 | <DefineConstants>DEBUG;TRACE</DefineConstants> 23 | <ErrorReport>prompt</ErrorReport> 24 | <WarningLevel>4</WarningLevel> 25 | <AllowUnsafeBlocks>true</AllowUnsafeBlocks> 26 | </PropertyGroup> 27 | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> 28 | <PlatformTarget>AnyCPU</PlatformTarget> 29 | <DebugType>pdbonly</DebugType> 30 | <Optimize>true</Optimize> 31 | <OutputPath>bin\Release\</OutputPath> 32 | <DefineConstants>TRACE</DefineConstants> 33 | <ErrorReport>prompt</ErrorReport> 34 | <WarningLevel>4</WarningLevel> 35 | <AllowUnsafeBlocks>true</AllowUnsafeBlocks> 36 | </PropertyGroup> 37 | <ItemGroup> 38 | <Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> 39 | <HintPath>libs\Newtonsoft.Json.13.0.1\Newtonsoft.Json.dll</HintPath> 40 | </Reference> 41 | <Reference Include="System" /> 42 | <Reference Include="System.Core" /> 43 | <Reference Include="System.IO.Compression.FileSystem" /> 44 | <Reference Include="System.Xml.Linq" /> 45 | <Reference Include="System.Data.DataSetExtensions" /> 46 | <Reference Include="Microsoft.CSharp" /> 47 | <Reference Include="System.Data" /> 48 | <Reference Include="System.Net.Http" /> 49 | <Reference Include="System.Xml" /> 50 | </ItemGroup> 51 | <ItemGroup> 52 | <Compile Include="Program.cs" /> 53 | <Compile Include="Properties\AssemblyInfo.cs" /> 54 | <Compile Include="SlaynashUtils\CppUtils.cs" /> 55 | <Compile Include="SlaynashUtils\ReflectionUtils.cs" /> 56 | <Compile Include="SlaynashUtils\StringUtils.cs" /> 57 | <Compile Include="SlaynashUtils\VersionUtils.cs" /> 58 | </ItemGroup> 59 | <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> 60 | </Project> -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/slashs/SetVRCAPI.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.slashs; 2 | 3 | import java.sql.ResultSet; 4 | 5 | import net.dv8tion.jda.api.entities.channel.ChannelType; 6 | import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; 7 | import net.dv8tion.jda.api.interactions.InteractionContextType; 8 | import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; 9 | import net.dv8tion.jda.api.interactions.commands.build.CommandData; 10 | import net.dv8tion.jda.api.interactions.commands.build.Commands; 11 | import slaynash.lum.bot.ConfigManager; 12 | import slaynash.lum.bot.DBConnectionManagerLum; 13 | import slaynash.lum.bot.utils.ExceptionUtils; 14 | 15 | public class SetVRCAPI extends Slash { 16 | @Override 17 | protected CommandData globalSlashData() { 18 | return Commands.slash("vrcapi", "Set log channel for VRChat API changes - Admins only").setDefaultPermissions(DefaultMemberPermissions.DISABLED).setContexts(InteractionContextType.GUILD); 19 | } 20 | 21 | @Override 22 | public void slashRun(SlashCommandInteractionEvent event) { 23 | if (event.getChannelType() == ChannelType.PRIVATE) { 24 | event.reply("This command does not work in DMs").setEphemeral(true).queue(); 25 | } 26 | else if (!ConfigManager.mainBot) { 27 | event.reply("This command is not available on backup bot, please wait for main bot to come back online.").setEphemeral(true).queue(); 28 | } 29 | else if (!event.getGuild().getSelfMember().hasPermission(event.getGuildChannel(), net.dv8tion.jda.api.Permission.MESSAGE_SEND)) { 30 | event.reply("Lum does not have " + net.dv8tion.jda.api.Permission.MESSAGE_SEND.getName() + " permission").setEphemeral(true).queue(); 31 | } 32 | else if (!event.getGuild().getSelfMember().hasPermission(event.getGuildChannel(), net.dv8tion.jda.api.Permission.MESSAGE_EMBED_LINKS)) { 33 | event.reply("Lum does not have " + net.dv8tion.jda.api.Permission.MESSAGE_EMBED_LINKS.getName() + " permission").setEphemeral(true).queue(); 34 | } 35 | else try { 36 | String guildID = event.getGuild().getId(); 37 | String channelID = event.getChannel().getId(); 38 | ResultSet rs = DBConnectionManagerLum.sendRequest("SELECT * FROM `GuildConfigurations` WHERE GuildID = ?", guildID); 39 | if (rs.next() && channelID.equals(rs.getString("VRCAPI"))) { 40 | DBConnectionManagerLum.sendUpdate("UPDATE `GuildConfigurations` SET `VRCAPI` = NULL WHERE GuildID = ?", guildID); 41 | event.reply("VRChat API monitor is now disabled").setEphemeral(true).queue(); 42 | } 43 | else { 44 | DBConnectionManagerLum.sendUpdate("UPDATE `GuildConfigurations` SET `VRCAPI` = ? WHERE GuildID = ?", channelID, guildID); 45 | event.reply("VRChat API monitor is now enabled in this channel and reports will be sent to " + event.getChannel().getName()).setEphemeral(true).queue(); 46 | } 47 | DBConnectionManagerLum.closeRequest(rs); 48 | } 49 | catch (Exception e) { 50 | ExceptionUtils.reportException("Failed to set meme channel", e, event.getChannel()); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/uvm/CecilAssemblyResolverProvider.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.uvm; 2 | 3 | import java.io.File; 4 | import java.nio.file.Path; 5 | import java.util.ArrayList; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | 9 | import mono.cecil.AssemblyDefinition; 10 | import mono.cecil.AssemblyNameReference; 11 | import mono.cecil.IAssemblyResolver; 12 | import mono.cecil.IAssemblyResolverProvider; 13 | import mono.cecil.ModuleDefinition; 14 | import mono.cecil.ReaderParameters; 15 | import org.apache.commons.lang3.NotImplementedException; 16 | import org.jetbrains.annotations.NotNull; 17 | import org.jetbrains.annotations.Nullable; 18 | 19 | public class CecilAssemblyResolverProvider implements IAssemblyResolverProvider { 20 | @Override 21 | public @NotNull IAssemblyResolver createResolver() { 22 | return new AssemblyResolver(); 23 | } 24 | 25 | 26 | public static class AssemblyResolver implements IAssemblyResolver { 27 | 28 | private final List<AssemblyDefinition> assemblydefs = new ArrayList<>(); 29 | private final List<File> directories = new ArrayList<>(); 30 | 31 | @Override 32 | public @NotNull Iterator<AssemblyDefinition> iterator() { 33 | return assemblydefs.iterator(); 34 | } 35 | 36 | @Override 37 | public void dispose() { 38 | } 39 | 40 | @Override 41 | public void addSearchDirectory(File directory) { 42 | directories.add(directory); 43 | } 44 | 45 | @Override 46 | public void removeSearchDirectory(File directory) { 47 | directories.remove(directory); 48 | } 49 | 50 | @Override 51 | public boolean registerAssembly(AssemblyDefinition arg0) { 52 | // TODO Auto-generated method stub 53 | return false; 54 | } 55 | 56 | @Override 57 | public AssemblyDefinition resolve(AssemblyNameReference name, @Nullable ReaderParameters parameters) { 58 | if (name == null) 59 | throw new NullPointerException(); 60 | if (parameters == null) 61 | throw new NullPointerException(); 62 | 63 | return searchDirectory(name, directories, parameters); 64 | } 65 | 66 | @Override 67 | public AssemblyDefinition resolve(String name, ReaderParameters arg1) { 68 | //return resolve(name, parameters); 69 | throw new NotImplementedException("resolve(string, ReaderParameters) is not implemented"); 70 | } 71 | 72 | 73 | protected AssemblyDefinition searchDirectory(AssemblyNameReference name, Iterable<File> directories, ReaderParameters parameters) { 74 | for (File directory : directories) { 75 | File file = Path.of(directory.getAbsolutePath(), name.getName() + ".dll").toFile(); 76 | if (!file.exists()) 77 | continue; 78 | 79 | return getAssembly(file.getAbsolutePath(), parameters); 80 | } 81 | 82 | return null; 83 | } 84 | 85 | AssemblyDefinition getAssembly(String file, ReaderParameters parameters) { 86 | if (parameters.getAssemblyResolver() == null) 87 | parameters.setAssemblyResolver(this); 88 | 89 | return ModuleDefinition.readModule(file, parameters).getAssembly(); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/Moderation.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord; 2 | 3 | import java.util.ArrayList; 4 | import java.util.EnumSet; 5 | import java.util.List; 6 | 7 | import net.dv8tion.jda.api.JDA; 8 | import net.dv8tion.jda.api.Permission; 9 | import net.dv8tion.jda.api.entities.Guild; 10 | import net.dv8tion.jda.api.entities.Member; 11 | import net.dv8tion.jda.api.entities.PermissionOverride; 12 | import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; 13 | import net.dv8tion.jda.api.events.guild.voice.GuildVoiceUpdateEvent; 14 | 15 | public class Moderation { 16 | public static void voiceEvent(GuildVoiceUpdateEvent event) { 17 | Long guildID = event.getGuild().getIdLong(); 18 | if (event.getChannelJoined() != null) { 19 | if (GuildConfigurations.noMicChannels.containsKey(guildID) && event.getMember().hasPermission(Permission.MESSAGE_SEND)) { 20 | TextChannel vcmute = event.getGuild().getTextChannelById(GuildConfigurations.noMicChannels.get(guildID)); 21 | if (vcmute != null) { 22 | vcmute.getManager().putMemberPermissionOverride(event.getMember().getIdLong(), EnumSet.of(Permission.VIEW_CHANNEL, Permission.MESSAGE_SEND), null).queue(); 23 | } 24 | } 25 | } 26 | else if (event.getChannelLeft() != null) { 27 | if (GuildConfigurations.noMicChannels.containsKey(guildID)) { 28 | TextChannel vcmute = event.getGuild().getTextChannelById(GuildConfigurations.noMicChannels.get(guildID)); 29 | if (vcmute != null) { 30 | vcmute.getManager().removePermissionOverride(event.getMember().getIdLong()).queue(); 31 | } 32 | } 33 | } 34 | } 35 | 36 | public static void voiceStartup() { 37 | JDA jda = JDAManager.getJDA(); 38 | for (Long chID : GuildConfigurations.noMicChannels.values()) { 39 | TextChannel channel = jda.getTextChannelById(chID); 40 | if (channel == null) { 41 | System.out.println("[ERROR] Mute Voice Channel " + chID + " is null"); 42 | continue; 43 | } 44 | for (PermissionOverride override : channel.getMemberPermissionOverrides()) { 45 | if (override.isMemberOverride() && override.getMember() != null) { 46 | if (!override.getMember().getVoiceState().inAudioChannel()) { 47 | try { 48 | override.delete().queue(); 49 | } 50 | catch (Exception e) { 51 | System.out.println("Can't remove vc mute override from " + override.getMember().getEffectiveName() + e.getMessage()); 52 | } 53 | } 54 | } 55 | } 56 | } 57 | } 58 | 59 | public static List<Long> getAdmins(Guild guild) { 60 | List<Long> adminList = new ArrayList<>(); 61 | adminList.add(145556654241349632L/*Slay*/); 62 | adminList.add(240701606977470464L/*rakosi2*/); 63 | for (Member member : guild.getMembers()) { 64 | if (member.isOwner() || member.hasPermission(Permission.MANAGE_SERVER) || member.hasPermission(Permission.ADMINISTRATOR)) 65 | adminList.add(member.getIdLong()); 66 | } 67 | return adminList; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/commands/SetScreeningRoleHandlerCommand.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.commands; 2 | 3 | import java.awt.Color; 4 | 5 | import net.dv8tion.jda.api.Permission; 6 | import net.dv8tion.jda.api.entities.Role; 7 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 8 | import slaynash.lum.bot.ConfigManager; 9 | import slaynash.lum.bot.discord.Command; 10 | import slaynash.lum.bot.discord.CommandManager; 11 | import slaynash.lum.bot.discord.utils.CrossServerUtils; 12 | import slaynash.lum.bot.utils.Utils; 13 | 14 | public class SetScreeningRoleHandlerCommand extends Command { 15 | 16 | @Override 17 | protected void onServer(String paramString, MessageReceivedEvent paramMessageReceivedEvent) { 18 | if (!includeInHelp(paramMessageReceivedEvent)) { 19 | paramMessageReceivedEvent.getChannel().sendMessage("Error: You need to have the Manage Role permission").queue(); 20 | return; 21 | } 22 | String[] params = paramMessageReceivedEvent.getMessage().getContentRaw().split(" "); 23 | if (params.length > 2 || params.length == 2 && !params[1].matches("^\\d+$")) { 24 | paramMessageReceivedEvent.getChannel().sendMessage("Usage: " + ConfigManager.discordPrefix + getName() + " [roleid]").queue(); 25 | return; 26 | } 27 | 28 | if (params.length == 2) { 29 | Role role = paramMessageReceivedEvent.getGuild().getRoleById(params[1]); 30 | if (role == null) { 31 | paramMessageReceivedEvent.getChannel().sendMessageEmbeds(Utils.wrapMessageInEmbed("Error: Role not found", Color.RED)).queue(); 32 | return; 33 | } 34 | if (!role.getGuild().getSelfMember().canInteract(role)) { 35 | paramMessageReceivedEvent.getChannel().sendMessageEmbeds(Utils.wrapMessageInEmbed("Lum does not have permissions to give out this role or Lum's role is too low", Color.RED)).queue(); 36 | return; 37 | } 38 | 39 | CommandManager.autoScreeningRoles.put(paramMessageReceivedEvent.getGuild().getIdLong(), role.getIdLong()); 40 | CommandManager.saveScreenings(); 41 | paramMessageReceivedEvent.getChannel().sendMessageEmbeds(Utils.wrapMessageInEmbed("Successfully set screening role", Color.GREEN)).queue(); 42 | } 43 | else { 44 | CommandManager.autoScreeningRoles.remove(paramMessageReceivedEvent.getGuild().getIdLong()); 45 | CommandManager.saveScreenings(); 46 | paramMessageReceivedEvent.getChannel().sendMessageEmbeds(Utils.wrapMessageInEmbed("Successfully removed screening role", Color.GREEN)).queue(); 47 | } 48 | 49 | } 50 | 51 | @Override 52 | protected boolean matchPattern(String pattern) { 53 | return pattern.split(" ", 2)[0].equals(ConfigManager.discordPrefix + getName()); 54 | } 55 | 56 | @Override 57 | public boolean includeInHelp(MessageReceivedEvent event) { 58 | return event.getMember().hasPermission(Permission.MANAGE_ROLES) || CrossServerUtils.isLumDev(event.getMember()); 59 | } 60 | 61 | @Override 62 | public String getName() { 63 | return "setscreeningrole"; 64 | } 65 | 66 | @Override 67 | public String getHelpDescription() { 68 | return "Set target role assignation on membership screening accept, or disable if empty"; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/commands/DumpID.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.commands; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Comparator; 5 | import java.util.List; 6 | import java.util.regex.Pattern; 7 | import java.util.regex.PatternSyntaxException; 8 | 9 | import net.dv8tion.jda.api.Permission; 10 | import net.dv8tion.jda.api.entities.Member; 11 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 12 | import net.dv8tion.jda.api.utils.FileUpload; 13 | import net.gcardone.junidecode.Junidecode; 14 | import slaynash.lum.bot.ConfigManager; 15 | import slaynash.lum.bot.discord.Command; 16 | 17 | public class DumpID extends Command { 18 | @Override 19 | protected void onServer(String paramString, MessageReceivedEvent event) { 20 | if (!includeInHelp(event)) 21 | return; 22 | String[] parts = paramString.split(" ", 2); 23 | if (parts.length < 2) { 24 | event.getMessage().reply("Usage: " + ConfigManager.discordPrefix + getName() + " <Regex>").queue(); 25 | return; 26 | } 27 | String regex = Junidecode.unidecode(parts[1]).toLowerCase(); 28 | try { 29 | Pattern.compile(regex); 30 | } 31 | catch (PatternSyntaxException exception) { 32 | event.getMessage().reply("Invalid Regex, please check your regex and try again").queue(); 33 | return; 34 | } 35 | List<Member> members = new ArrayList<>(); 36 | event.getGuild().loadMembers(m -> { 37 | if (m.getNickname() != null && Junidecode.unidecode(m.getNickname()).toLowerCase().matches(regex)) { 38 | members.add(m); 39 | return; 40 | } 41 | if (Junidecode.unidecode(m.getUser().getName()).toLowerCase().matches(regex)) 42 | members.add(m); 43 | if (Junidecode.unidecode(m.getUser().getGlobalName()).toLowerCase().matches(regex)) 44 | members.add(m); 45 | if (Junidecode.unidecode(m.getNickname()).toLowerCase().matches(regex)) 46 | members.add(m); 47 | }); 48 | if (members.isEmpty()) { 49 | event.getMessage().reply("No users found.").queue(); 50 | return; 51 | } 52 | members.sort(Comparator.comparing(m -> m.getUser().getId())); 53 | StringBuilder sb = new StringBuilder(); 54 | for (Member m : members) { 55 | sb.append(m.getUser().getId()).append(" ").append(m.getUser().getName()).append(m.getUser().getGlobalName() != null ? " GlobalName: " + m.getUser().getGlobalName() : "").append(m.getNickname() != null ? " nickname: " + m.getNickname() : "").append("\n"); 56 | } 57 | event.getMessage().replyFiles(FileUpload.fromData(sb.toString().getBytes(), event.getGuild().getName() + " " + regex + ".txt")).queue(); 58 | } 59 | 60 | @Override 61 | protected boolean matchPattern(String paramString) { 62 | return paramString.startsWith(ConfigManager.discordPrefix + getName()); 63 | } 64 | 65 | @Override 66 | public boolean includeInHelp(MessageReceivedEvent event) { 67 | return event.getMember().hasPermission(Permission.MESSAGE_MANAGE); 68 | } 69 | 70 | @Override 71 | public String getHelpDescription() { 72 | return "Dump all user IDs in the server that match a regex"; 73 | } 74 | 75 | @Override 76 | public String getName() { 77 | return "dumpid"; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/GuildConfigurations.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord; 2 | 3 | import java.util.HashMap; 4 | 5 | public class GuildConfigurations { 6 | 7 | public static final HashMap<Long, long[]> whitelistedRolesServers = new HashMap<>() {{ 8 | put(439093693769711616L /* VRCMG */, new long[] { 9 | 631581319670923274L /* Staff */, 10 | 662720231591903243L /* Helper */, 11 | 825266051277258754L /* cutie */, 12 | 585594394153844865L /* Modder */ }); 13 | put(600298024425619456L /* emmVRC */, new long[] { 14 | 748392927365169233L /* Admin */, 15 | 653722281864069133L /* Helper */ }); 16 | put(563139253542846474L /* BONEWORKS */, new long[] { 17 | 563143742324736001L /* Moderators */}); 18 | put(663449315876012052L /* MelonLoader */, new long[] { 19 | 663450403194798140L /* Lava Gang */, 20 | 663450567015792670L /* Administrator */, 21 | 663450611253248002L /* Moderator */, 22 | 663450655775522843L /* Modder */ }); 23 | put(673663870136746046L /* Modders & Chill */, new long[] { 24 | 673725166626406428L /* Modders */, 25 | 673726384450961410L /* Moderators */ }); 26 | put(633588473433030666L /* Slaynash's Workbench */, new long[] { 27 | 633590573412122634L /* Friends */}); 28 | put(835185040752246835L /* The Long Development */, new long[] { 29 | 837912560497721344L /* Team Member */, 30 | 836863571811106846L /* Fellow Modder */}); 31 | put(322211727192358914L /* The Long Dark Modding */, new long[] { 32 | 370425060844109835L /* Modders */}); 33 | put(748692902137430018L /* Beat Saber Legacy Group */, new long[] { 34 | 748701248701857972L /* Staff */, 35 | 750814355985006633L /* Modders */, 36 | 810258575620309033L /* Helper */}); 37 | }}; 38 | 39 | public static final HashMap<Long, Long> lockDownRoles = new HashMap<>() {{ 40 | put(439093693769711616L /* VRCMG */, 548534015108448256L /* Member */); 41 | put(600298024425619456L /* emmVRC */, 600304115972702209L /* Members */); 42 | put(663449315876012052L /* MelonLoader */, 663462327022256150L /* Member */); 43 | put(716536783621587004L /* Totally Wholesome */, 716541223145308170L /* Member */); 44 | put(737454833258201198L, 737456197925339196L); 45 | put(570957154928951296L, 743410413273874494L); 46 | put(550477546958094348L, 550600331482890251L); 47 | put(459359136954580992L, 909025973922586645L); 48 | }}; 49 | 50 | public static final HashMap<Long, Long> noMicChannels = new HashMap<>() {{ 51 | put(439093693769711616L /* VRCMG */, 673955689298788376L /* no-mic */); 52 | put(600298024425619456L /* emmVRC */, 682379676106096678L /* no-mic */); 53 | put(663449315876012052L /* MelonLoader */, 701494833495408640L /* voice-chat */); 54 | // put(716536783621587004L /* Totally Wholesome */, 943694417016000552L /* mute-voice */); 55 | //put(1001388809184870441L /* CVRMG */, 1002672344663326780L /* voice-text */); 56 | }}; 57 | } 58 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/commands/AutoPublish.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.commands; 2 | 3 | import java.time.Duration; 4 | 5 | import net.dv8tion.jda.api.Permission; 6 | import net.dv8tion.jda.api.entities.Message; 7 | import net.dv8tion.jda.api.entities.channel.ChannelType; 8 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 9 | import slaynash.lum.bot.ConfigManager; 10 | import slaynash.lum.bot.discord.Command; 11 | import slaynash.lum.bot.discord.CommandManager; 12 | 13 | public class AutoPublish extends Command { 14 | 15 | @Override 16 | protected void onServer(String paramString, MessageReceivedEvent paramMessageReceivedEvent) { 17 | if (paramMessageReceivedEvent.getChannel().getType() != ChannelType.NEWS) { 18 | paramMessageReceivedEvent.getMessage().reply("This is not an announcement channel.").queue(); 19 | return; 20 | } 21 | if (!paramMessageReceivedEvent.getGuild().getSelfMember().hasPermission(paramMessageReceivedEvent.getChannel().asNewsChannel(), Permission.VIEW_CHANNEL, Permission.MESSAGE_SEND, Permission.MESSAGE_MANAGE, Permission.VIEW_CHANNEL)) { 22 | paramMessageReceivedEvent.getAuthor().openPrivateChannel().flatMap(channel -> channel.sendMessage( 23 | "Lum does not have the proper permissions to publish messages in " + paramMessageReceivedEvent.getChannel().getName() + " Please make sure that Lum has Message Read, Write, and Manage Permissions.")).queue(null, m -> System.out.println("failed to open DM")); 24 | return; 25 | } 26 | 27 | if (!includeInHelp(paramMessageReceivedEvent)) 28 | return; 29 | 30 | if (CommandManager.apChannels.contains(paramMessageReceivedEvent.getChannel().getIdLong())) { 31 | CommandManager.apChannels.remove(paramMessageReceivedEvent.getChannel().getIdLong()); 32 | CommandManager.saveAPChannels(); 33 | paramMessageReceivedEvent.getChannel().sendMessage("Successfully removed " + paramMessageReceivedEvent.getChannel().getName() + " from autopublish!").delay(Duration.ofSeconds(3)).flatMap(Message::delete).queue(); 34 | System.out.println("Successfully removed autopublish from " + paramMessageReceivedEvent.getChannel().getName()); 35 | } 36 | else { 37 | CommandManager.apChannels.add(paramMessageReceivedEvent.getChannel().getIdLong()); 38 | CommandManager.saveAPChannels(); 39 | paramMessageReceivedEvent.getChannel().sendMessage("Successfully set " + paramMessageReceivedEvent.getChannel().getName() + " to autopublish!").delay(Duration.ofSeconds(3)).flatMap(Message::delete).queue(); 40 | System.out.println("Successfully added autopublish to " + paramMessageReceivedEvent.getChannel().getName()); 41 | } 42 | paramMessageReceivedEvent.getMessage().delete().queue(); 43 | } 44 | 45 | @Override 46 | protected boolean matchPattern(String paramString) { 47 | return paramString.split(" ", 2)[0].equals(ConfigManager.discordPrefix + getName()); 48 | } 49 | 50 | @Override 51 | public String getHelpDescription() { 52 | return "Automatically publish all messages in an announcement channel"; 53 | } 54 | 55 | @Override 56 | public String getName() { 57 | return "autopublish"; 58 | } 59 | 60 | @Override 61 | public boolean includeInHelp(MessageReceivedEvent event) { 62 | return event.getMember().hasPermission(Permission.ADMINISTRATOR); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/commands/LockDown.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.commands; 2 | 3 | import java.util.Collections; 4 | import java.util.Objects; 5 | 6 | import net.dv8tion.jda.api.Permission; 7 | import net.dv8tion.jda.api.entities.Role; 8 | import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion; 9 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 10 | import slaynash.lum.bot.ConfigManager; 11 | import slaynash.lum.bot.discord.Command; 12 | import slaynash.lum.bot.discord.CommandManager; 13 | import slaynash.lum.bot.discord.GuildConfigurations; 14 | import slaynash.lum.bot.discord.Moderation; 15 | import slaynash.lum.bot.discord.utils.CrossServerUtils; 16 | import slaynash.lum.bot.utils.Utils; 17 | 18 | public class LockDown extends Command { 19 | 20 | @Override 21 | protected void onServer(String paramString, MessageReceivedEvent event) { 22 | if (!includeInHelp(event)) 23 | return; 24 | 25 | if (event.getGuild().getPublicRole().hasPermission(Permission.MESSAGE_SEND)) { 26 | event.getChannel().sendMessage("@everyone has send message and lockdown will not work").setAllowedMentions(Collections.emptyList()).queue(); 27 | return; 28 | } 29 | 30 | MessageChannelUnion reportChannel = CommandManager.getModReportChannels(event, "users"); 31 | Long lockDownRoleID = GuildConfigurations.lockDownRoles.get(event.getGuild().getIdLong()); 32 | if (lockDownRoleID == null) { 33 | event.getChannel().sendMessage("LockDown is not setup in this server. Please DM rakosi2 to setup LockDown").setAllowedMentions(Collections.emptyList()).queue(); 34 | } 35 | Role lockDownRole = event.getGuild().getRoleById(lockDownRoleID); 36 | if (!event.getGuild().getSelfMember().canInteract(lockDownRole)) { 37 | event.getChannel().sendMessage("I can not interact with the role " + lockDownRole.getName()).queue(); 38 | return; 39 | } 40 | boolean lockDownState = lockDownRole.hasPermission(Permission.MESSAGE_SEND); 41 | 42 | if (lockDownState) 43 | lockDownRole.getManager().revokePermissions(Permission.MESSAGE_SEND).complete(); 44 | else 45 | lockDownRole.getManager().givePermissions(Permission.MESSAGE_SEND).complete(); 46 | 47 | if (!Objects.equals(reportChannel, event.getChannel())) 48 | Utils.sendMessage("User " + event.getAuthor().getEffectiveName() + " has " + (lockDownState ? "locked down" : "unlocked") + " this server in " + event.getChannel().getName(), reportChannel); 49 | event.getChannel().sendMessage("User " + event.getAuthor().getEffectiveName() + " has " + (lockDownState ? "locked down" : "unlocked") + " this server.").queue(); 50 | } 51 | 52 | @Override 53 | protected boolean matchPattern(String paramString) { 54 | return paramString.startsWith(ConfigManager.discordPrefix + getName()); 55 | } 56 | 57 | @Override 58 | public boolean includeInHelp(MessageReceivedEvent event) { 59 | return GuildConfigurations.lockDownRoles.get(event.getGuild().getIdLong()) != null && (Moderation.getAdmins(event.getGuild()).contains(event.getAuthor().getIdLong()) || CrossServerUtils.checkIfStaff(event)); 60 | } 61 | 62 | @Override 63 | public String getHelpDescription() { 64 | return "Toggles Member's sending messages permission in all channels for emergencies - Staff only"; 65 | } 66 | 67 | @Override 68 | public String getName() { 69 | return "lockdown"; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/JDAManager.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord; 2 | 3 | import java.util.EnumSet; 4 | 5 | import net.dv8tion.jda.api.JDA; 6 | import net.dv8tion.jda.api.JDABuilder; 7 | import net.dv8tion.jda.api.entities.Message; 8 | import net.dv8tion.jda.api.requests.GatewayIntent; 9 | import net.dv8tion.jda.api.utils.ChunkingFilter; 10 | import net.dv8tion.jda.api.utils.MemberCachePolicy; 11 | import net.dv8tion.jda.api.utils.messages.MessageRequest; 12 | import slaynash.lum.bot.ConfigManager; 13 | import slaynash.lum.bot.Main; 14 | 15 | public class JDAManager { 16 | 17 | private static JDA jda; 18 | public static final long mainGuildID = 633588473433030666L; 19 | private static boolean init = false; 20 | private static final Main mainEvents = new Main(); 21 | 22 | public static void init(String token) throws InterruptedException { 23 | if (!init) init = true; 24 | else return; 25 | while (true) { 26 | try { 27 | jda = JDABuilder.createDefault(token) 28 | .setChunkingFilter(ChunkingFilter.NONE) 29 | .setMemberCachePolicy(MemberCachePolicy.ALL) 30 | .enableIntents(GatewayIntent.GUILD_MEMBERS, GatewayIntent.MESSAGE_CONTENT, GatewayIntent.DIRECT_MESSAGES, GatewayIntent.GUILD_MESSAGE_REACTIONS, GatewayIntent.GUILD_MESSAGE_TYPING, GatewayIntent.DIRECT_MESSAGE_TYPING, GatewayIntent.GUILD_EXPRESSIONS) 31 | .setMaxReconnectDelay(60) 32 | .setEventPassthrough(true) 33 | .build(); 34 | break; 35 | } 36 | catch (Exception e) { 37 | System.out.println("Error while building JDA: " + e.getMessage()); 38 | Thread.sleep(30 * 1000); //Wait 30 seconds before retrying 39 | } 40 | } 41 | jda.awaitReady(); 42 | EnumSet<Message.MentionType> deny = EnumSet.of(Message.MentionType.EVERYONE, Message.MentionType.HERE, Message.MentionType.ROLE); 43 | MessageRequest.setDefaultMentions(EnumSet.complementOf(deny)); 44 | } 45 | 46 | public static JDA getJDA() { 47 | return jda; 48 | } 49 | 50 | public static void enableEvents() { 51 | if (jda == null) { 52 | return; 53 | } 54 | System.out.println("Enabling events"); 55 | jda.getEventManager().register(mainEvents); 56 | } 57 | 58 | public static void disableEvents() { 59 | if (jda == null) { 60 | return; 61 | } 62 | System.out.println("Disabling events"); 63 | jda.getEventManager().unregister(mainEvents); 64 | } 65 | 66 | public static boolean isEventsEnabled() { 67 | if (jda == null) { 68 | return false; 69 | } 70 | return jda.getEventManager().getRegisteredListeners().contains(mainEvents); 71 | } 72 | 73 | public static boolean isProductionBot() { 74 | if (jda == null) 75 | return false; 76 | return jda.getSelfUser().getIdLong() == 275759980752273418L; 77 | } 78 | 79 | public static boolean isDevBot() { 80 | if (jda == null) 81 | return false; 82 | return jda.getSelfUser().getIdLong() == 773707709064151051L; 83 | } 84 | 85 | public static boolean isMainBot() { 86 | return isProductionBot() && ConfigManager.mainBot; 87 | } 88 | 89 | public static boolean isBackupBot() { 90 | // Note: The dev bot is always assumed to be non-backup 91 | return isProductionBot() && !ConfigManager.mainBot; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/ConfigManager.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.util.Properties; 8 | 9 | import slaynash.lum.bot.utils.ExceptionUtils; 10 | 11 | public final class ConfigManager { 12 | private ConfigManager() { 13 | } 14 | 15 | private static final String SETTINGS_FILE = ".settings.conf"; 16 | private static boolean initialized = false; 17 | private static final Properties properties = new Properties(); 18 | 19 | public static String discordToken; 20 | public static boolean mainBot; 21 | public static String pingURL; 22 | 23 | public static String dbAddress; 24 | public static String dbPort; 25 | public static String dbDatabaseLum; 26 | public static String dbDatabaseShortURL; 27 | public static String dbLogin; 28 | public static String dbPassword; 29 | 30 | public static String discordPrefix; 31 | 32 | public static String curseforgeApiKey; 33 | public static String gitHubApiKey; 34 | public static String animescheduleApiKey; 35 | 36 | public static String commitHash; 37 | 38 | public static void init() { 39 | if (initialized) 40 | return; 41 | initialized = true; 42 | 43 | File settingsFile = new File(SETTINGS_FILE); 44 | 45 | if (!settingsFile.exists()) { 46 | System.err.println(SETTINGS_FILE + " missing"); 47 | System.exit(-1); 48 | } 49 | 50 | try (FileInputStream fis = new FileInputStream(SETTINGS_FILE)) { 51 | 52 | properties.load(fis); 53 | 54 | discordToken = properties.getProperty("DISCORD_TOKEN"); 55 | mainBot = Boolean.parseBoolean(properties.getProperty("MAIN_BOT")); 56 | pingURL = properties.getProperty("PING_URL"); 57 | 58 | dbAddress = properties.getProperty("DB_ADDRESS"); 59 | dbPort = properties.getProperty("DB_PORT"); 60 | dbDatabaseLum = properties.getProperty("DB_DATABASELUM"); 61 | dbDatabaseShortURL = properties.getProperty("DB_DATABASESHORTURL"); 62 | dbLogin = properties.getProperty("DB_LOGIN"); 63 | dbPassword = properties.getProperty("DB_PASSWORD"); 64 | 65 | discordPrefix = properties.getProperty("DISCORD_PREFIX"); 66 | 67 | curseforgeApiKey = properties.getProperty("CURSEFORGE_API_KEY"); 68 | gitHubApiKey = properties.getProperty("GitHub_API_KEY"); 69 | animescheduleApiKey = properties.getProperty("ANIMESCHEDULE_API_KEY"); 70 | 71 | } 72 | catch (IOException e) { 73 | ExceptionUtils.reportException("Failed to load config", e); 74 | } 75 | 76 | commitHash = "unknown"; 77 | try { 78 | InputStream gitstream = Main.class.getResourceAsStream("/git.properties"); 79 | if (gitstream != null) { 80 | java.util.Properties properties = new java.util.Properties(); 81 | properties.load(gitstream); 82 | System.out.println("Lum hash: " + properties.getProperty("git.commit.id.abbrev")); 83 | System.out.println("Lum build time: " + properties.getProperty("git.build.time")); 84 | commitHash = properties.getProperty("git.commit.id.abbrev"); 85 | } 86 | else 87 | System.err.println("Failed to load git.properties"); 88 | } 89 | catch (IOException e) { 90 | ExceptionUtils.reportException("Failed to load git commit hash", e); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/uvm/UnityUtils.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.uvm; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import org.apache.commons.lang3.NotImplementedException; 6 | 7 | public final class UnityUtils { 8 | 9 | public static final Gson gson = new GsonBuilder().setPrettyPrinting().create(); 10 | public static final String downloadPath = "/mnt/hdd3t/unity_versions"; 11 | 12 | public static String getMonoManagedSubpath(String version) { 13 | String monoManagedSubpath = "win64_player_nondevelopment_mono/Data"; 14 | 15 | if (version.startsWith("3.")) { 16 | monoManagedSubpath = "windows64standaloneplayer"; 17 | } 18 | else if (version.startsWith("4.")) { 19 | if (version.startsWith("4.5") || 20 | version.startsWith("4.6") || 21 | version.startsWith("4.7")) 22 | { 23 | monoManagedSubpath = "win64_nondevelopment/Data"; 24 | } 25 | else { 26 | monoManagedSubpath = "windows64standaloneplayer"; 27 | } 28 | } 29 | else if (version.startsWith("5.")) { 30 | if (version.startsWith("5.3")) { 31 | monoManagedSubpath = "win64_nondevelopment_mono/Data"; 32 | } 33 | else { 34 | monoManagedSubpath = "win64_nondevelopment/Data"; 35 | } 36 | } 37 | else if (version.startsWith("20")) { 38 | if (UnityVersion.compare(version, "2021.2.0") < 0) 39 | monoManagedSubpath = "win64_nondevelopment_mono/Data"; 40 | } 41 | 42 | return monoManagedSubpath + "/Managed"; 43 | } 44 | 45 | public static String getUnityPath(String version, boolean is64bit, boolean isIl2Cpp, boolean dev) { 46 | String archName = is64bit ? "64" : "32"; 47 | String runtimeName = isIl2Cpp ? "il2cpp" : "mono"; 48 | String devName = dev ? "development" : "nondevelopment"; 49 | 50 | if (!version.startsWith("20") || version.startsWith("2017.1.")) 51 | throw new NotImplementedException("Function not implemented for unity versions other than 2017.2.0"); 52 | 53 | if (version.startsWith("20")) { 54 | if (UnityVersion.compare(version, "2021.2.0") < 0) 55 | return "win" + archName + "_" + devName + "_" + runtimeName + "/"; 56 | } 57 | 58 | return "win" + archName + "_player_" + devName + "_" + runtimeName + "/"; 59 | } 60 | 61 | public static String getPdbName(String version, boolean is64bit, boolean isIl2Cpp, boolean dev) { 62 | String archName = is64bit ? "64" : "32"; 63 | String archName2 = is64bit ? "64" : "86"; 64 | String runtimeName = isIl2Cpp ? "il2cpp" : "mono"; 65 | String devName = dev ? "development_" : ""; 66 | 67 | if (!version.startsWith("20") || version.startsWith("2017.1.")) 68 | throw new NotImplementedException("Function not implemented for unity versions other than 2017.2.0"); 69 | 70 | if (version.startsWith("20")) { 71 | if (UnityVersion.compare(version, "2021.2.0") < 0) 72 | return "UnityPlayer_Win" + archName + "_" + devName + runtimeName + "_x" + archName2 + ".pdb"; 73 | else if (UnityVersion.compare(version, "2022.2.0") < 0) 74 | return "UnityPlayer_Win" + archName + "_player_" + devName + runtimeName + "_x" + archName2 + "_s.pdb"; 75 | } 76 | 77 | return "UnityPlayer_Win" + archName + "_player_" + devName + runtimeName + "_x" + archName2 + ".pdb"; 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /src/slaynash/lum/bot/uvm/UnityVersion.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.uvm; 2 | 3 | public class UnityVersion { 4 | public final String stream; 5 | public final String version; 6 | public final String fullVersion; 7 | public final String downloadUrl; 8 | public final String downloadUrlIl2CppWin; 9 | 10 | public UnityVersion(String stream, String version, String fullVersion, String downloadUrl, String downloadUrlIl2CppWin) { 11 | this.stream = stream; 12 | this.version = version; 13 | this.fullVersion = fullVersion; 14 | this.downloadUrl = downloadUrl; 15 | this.downloadUrlIl2CppWin = downloadUrlIl2CppWin; 16 | } 17 | 18 | public static boolean isValid(String version) { 19 | // ([3-5]\.\d) 20 | // ((20(1[7-9]|2\d))\.[1-4]) 21 | // => ^(([3-5]\.\d)|((20(1[7-9]|2\d))\.[1-4]))\.\d+$ 22 | return version.matches("^(([3-5]\\.\\d)|((20(1[7-9]|2\\d))\\.[1-4]))\\.\\d+$"); 23 | } 24 | 25 | public static int compare(String left, String right) { 26 | int[] leftparts = getNumbers(left); 27 | int[] rightparts = getNumbers(right); 28 | 29 | float leftsum = leftparts[0] * 10000L + leftparts[1] * 100L + leftparts[2] + (leftparts.length > 3 ? leftparts[3] / 100f : 0); 30 | float rightsum = rightparts[0] * 10000L + rightparts[1] * 100L + rightparts[2] + (rightparts.length > 3 ? rightparts[3] / 100f : 0); 31 | 32 | return Float.compare(leftsum, rightsum); 33 | } 34 | 35 | private static int[] getNumbers(String s) { 36 | //replace all non digit characters with a dot 37 | s = s.replaceAll("[^\\d]", "."); 38 | 39 | String[] numbersS = s.split("\\."); 40 | int[] numbers = new int[numbersS.length]; 41 | for (int i = 0; i < numbersS.length; ++i) 42 | numbers[i] = Integer.parseInt(numbersS[i]); 43 | 44 | return numbers; 45 | } 46 | 47 | /* 48 | public static boolean isOverOrEqual(String currentversion, String validversion) 49 | { 50 | String[] versionparts = currentversion.split("\\."); 51 | 52 | String[] validversionparts = validversion.split("\\."); 53 | 54 | if ( 55 | Integer.parseInt(versionparts[0]) >= Integer.parseInt(validversionparts[0]) && 56 | Integer.parseInt(versionparts[1]) >= Integer.parseInt(validversionparts[1]) && 57 | Integer.parseInt(versionparts[2]) >= Integer.parseInt(validversionparts[2])) 58 | return true; 59 | 60 | return false; 61 | } 62 | */ 63 | 64 | public static boolean isOverOrEqual(String currentversion, String[] validversions) { 65 | if (validversions == null || validversions.length == 0) 66 | return true; 67 | 68 | currentversion = currentversion.replaceAll("[^\\d]", "."); 69 | 70 | String[] versionparts = currentversion.split("\\."); 71 | 72 | for (String validversion : validversions) { 73 | 74 | String[] validversionparts = validversion.split("\\."); 75 | 76 | if ( 77 | Integer.parseInt(versionparts[0]) >= Integer.parseInt(validversionparts[0]) && 78 | Integer.parseInt(versionparts[1]) >= Integer.parseInt(validversionparts[1]) && 79 | Integer.parseInt(versionparts[2]) >= Integer.parseInt(validversionparts[2])) 80 | return true; 81 | 82 | } 83 | 84 | return false; 85 | } 86 | 87 | 88 | public static class Comparator implements java.util.Comparator<String> { 89 | @Override 90 | public int compare(String left, String right) { 91 | return UnityVersion.compare(left, right); 92 | } 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/commands/UVMCommand.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.commands; 2 | 3 | import java.util.HashMap; 4 | import java.util.function.Function; 5 | 6 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 7 | import slaynash.lum.bot.uvm.UnityVersion; 8 | import slaynash.lum.bot.uvm.UnityVersionMonitor; 9 | import slaynash.lum.bot.ConfigManager; 10 | import slaynash.lum.bot.discord.Command; 11 | import slaynash.lum.bot.discord.utils.CrossServerUtils; 12 | 13 | public class UVMCommand extends Command { 14 | 15 | private final HashMap<String, Function<String[], String>> subcommands = new HashMap<>() {{ 16 | put("checkicalls", args -> { UnityVersionMonitor.runFullICallCheck(); return null; }); 17 | // put("checkhashes", args -> { UnityVersionMonitor.runFullHashCheck(); return null; }); 18 | // put("fulldownload", args -> { UnityVersionMonitor.runFullDownloadCheck(); return null; }); 19 | put("checkintegrity", args -> { UnityVersionMonitor.runFullIntegrityCheck(); return null; }); 20 | put("kill", args -> { UnityVersionMonitor.killThreads(); return null; }); 21 | put("restart", args -> { UnityVersionMonitor.startMainThread(); return null; }); 22 | put("setenabled", args -> { 23 | if (args.length != 1) return "Usage: setenabled <true/false>"; 24 | try { 25 | UnityVersionMonitor.setEnabled(Boolean.parseBoolean(args[0])); 26 | } 27 | catch (Exception e) { 28 | return "Usage: setenabled <true/false>"; 29 | } 30 | return null; 31 | }); 32 | put("redownload", args -> { 33 | if (args.length != 1) return "Usage: redownload <unityversion>"; 34 | if (!UnityVersion.isValid(args[0])) return "Invalid Unity version.\nUsage: redownload <unityversion>"; 35 | UnityVersionMonitor.redownloadVersion(args[0]); 36 | return null; 37 | }); 38 | }}; 39 | 40 | @Override 41 | protected void onServer(String paramString, MessageReceivedEvent event) { 42 | if (!includeInHelp(event)) 43 | return; 44 | 45 | String[] parts = paramString.split(" ", 3); 46 | String subcommandName; 47 | Function<String[], String> subcommandRunnable; 48 | 49 | if (parts.length < 2 || (subcommandRunnable = subcommands.get(subcommandName = parts[1])) == null) { 50 | event.getMessage().reply("Usage: " + ConfigManager.discordPrefix + getName() + " <subcommand>\nsubcommands: " + String.join(", ", subcommands.keySet())).queue(); 51 | return; 52 | } 53 | 54 | String[] args = (parts.length == 3 && !parts[2].isEmpty()) ? parts[2].split(" ") : new String[0]; 55 | 56 | UnityVersionMonitor.startThread(() -> { 57 | event.getMessage().reply("Running subcommand \"" + subcommandName + "\"").queue(); 58 | String ret = subcommandRunnable.apply(args); 59 | if (ret != null) 60 | event.getMessage().reply(ret).queue(); 61 | }, subcommandName); 62 | } 63 | 64 | @Override 65 | protected boolean matchPattern(String paramString) { 66 | return paramString.split(" ", 2)[0].equals(ConfigManager.discordPrefix + getName()); 67 | } 68 | 69 | @Override 70 | public boolean includeInHelp(MessageReceivedEvent event) { 71 | return CrossServerUtils.isLumDev(event.getMember()) && event.getGuild().getIdLong() == 633588473433030666L /* Slaynash's Workbench */; 72 | } 73 | 74 | @Override 75 | public String getHelpDescription() { 76 | return "Trigger Unity Version Monitor commands/checks"; 77 | } 78 | 79 | @Override 80 | public String getName() { 81 | return "uvm"; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/commands/AddMissingRoles.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.commands; 2 | 3 | import java.time.Duration; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | 6 | import net.dv8tion.jda.api.entities.Guild; 7 | import net.dv8tion.jda.api.entities.Role; 8 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 9 | import slaynash.lum.bot.ConfigManager; 10 | import slaynash.lum.bot.discord.Command; 11 | import slaynash.lum.bot.discord.CommandManager; 12 | import slaynash.lum.bot.discord.JDAManager; 13 | import slaynash.lum.bot.discord.utils.CrossServerUtils; 14 | import slaynash.lum.bot.utils.ExceptionUtils; 15 | 16 | public class AddMissingRoles extends Command { 17 | 18 | @Override 19 | protected void onServer(String paramString, MessageReceivedEvent event) { 20 | if (!includeInHelp(event)) 21 | return; 22 | 23 | Thread thread = new Thread(() -> addMissing(event), "APIThread"); 24 | thread.setDaemon(false); 25 | thread.start(); 26 | } 27 | 28 | @Override 29 | protected boolean matchPattern(String paramString) { 30 | return paramString.startsWith(ConfigManager.discordPrefix + getName()); 31 | } 32 | 33 | @Override 34 | public boolean includeInHelp(MessageReceivedEvent event) { 35 | return CrossServerUtils.isLumDev(event.getMember()); 36 | } 37 | 38 | @Override 39 | public String getHelpDescription() { 40 | return "Scans throgh all members for missing roles from screening acceptance"; 41 | } 42 | 43 | @Override 44 | public String getName() { 45 | return "addmissing"; 46 | } 47 | 48 | public void addMissing(MessageReceivedEvent event) { 49 | AtomicInteger runCount = new AtomicInteger(0); 50 | CommandManager.autoScreeningRoles.forEach((k, v) -> { 51 | Guild guild = JDAManager.getJDA().getGuildById(k); 52 | if (guild == null) 53 | return; 54 | Role role = guild.getRoleById(v); 55 | if (role == null) 56 | return; 57 | if (!guild.getSelfMember().canInteract(role)) { 58 | System.out.println("Lum can not modify role " + role.getName() + " in " + guild.getName() + " " + k); 59 | //TODO announce that Lum can not interact with role 60 | return; 61 | } 62 | try { 63 | guild.loadMembers(m -> { 64 | if (!m.getUser().isBot() && !m.isPending() && !m.getRoles().contains(role)) { 65 | try { 66 | guild.addRoleToMember(m, role).reason("User has agreed to Membership Screening requirements while Lum was rebooting").queue(null, z -> System.out.println("Failed to regive role to " + m.getUser().getEffectiveName() + " in " + guild.getName())); 67 | System.out.println("Giving role " + role.getName() + " to " + m.getEffectiveName() + " in " + guild.getName()); 68 | runCount.getAndIncrement(); 69 | } 70 | catch (Exception ignored) { } 71 | } 72 | }).setTimeout(Duration.ofDays(1)).onError(e -> System.out.println("Failed to load members in " + guild.getName() + " " + k)); 73 | } 74 | catch (Exception e) { 75 | ExceptionUtils.reportException("loadMembers failed during AddMissingRoles", e); 76 | } 77 | try { 78 | Thread.sleep(1000); 79 | } 80 | catch (InterruptedException e) { 81 | ExceptionUtils.reportException("Was Interrupted in AddMissing", e); 82 | } 83 | }); 84 | if (event != null) { 85 | event.getMessage().reply("Added roles to " + runCount.get() + " members").queue(); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/timers/Reminders.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.timers; 2 | 3 | import java.sql.ResultSet; 4 | import java.util.Calendar; 5 | import java.util.Timer; 6 | import java.util.TimerTask; 7 | 8 | import net.dv8tion.jda.api.EmbedBuilder; 9 | import net.dv8tion.jda.api.Permission; 10 | import net.dv8tion.jda.api.entities.Guild; 11 | import net.dv8tion.jda.api.entities.MessageEmbed; 12 | import net.dv8tion.jda.api.entities.User; 13 | import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; 14 | import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; 15 | import slaynash.lum.bot.DBConnectionManagerLum; 16 | import slaynash.lum.bot.discord.JDAManager; 17 | import slaynash.lum.bot.utils.ExceptionUtils; 18 | 19 | public class Reminders extends TimerTask { 20 | public void run() { 21 | try { 22 | ResultSet rs = DBConnectionManagerLum.sendRequest("SELECT * FROM `Reminders` WHERE `TSend` < CURRENT_TIMESTAMP"); 23 | while (rs.next()) { 24 | String message = rs.getString("Message"); 25 | if (message != null) 26 | message = message.substring(0, Math.min(message.length(), MessageEmbed.DESCRIPTION_MAX_LENGTH)); 27 | EmbedBuilder embedBuilder = new EmbedBuilder().setColor(rs.getInt("Color")); 28 | embedBuilder.setTitle("Reminder"); 29 | embedBuilder.setDescription(message); 30 | 31 | long userID = rs.getLong("UserID"); 32 | User user = JDAManager.getJDA().getUserById(userID); 33 | long serverID = rs.getLong("ServerID"); 34 | long channelID = rs.getLong("ChannelID"); 35 | if (user == null) { 36 | user = JDAManager.getJDA().retrieveUserById(userID).complete(); 37 | } 38 | if (message == null) { 39 | message = user.getAsMention(); 40 | } 41 | if (serverID == 0) { 42 | user.openPrivateChannel().queue(channel -> channel.sendMessageEmbeds(embedBuilder.build()).queue()); 43 | } 44 | else { 45 | Guild guild; 46 | if ((guild = JDAManager.getJDA().getGuildById(serverID)) == null) 47 | continue; 48 | GuildChannel gchannel = guild.getGuildChannelById(channelID); 49 | MessageChannel channel = (MessageChannel) gchannel; 50 | if (channel == null || !channel.canTalk()) { 51 | user.openPrivateChannel().queue(pchannel -> pchannel.sendMessageEmbeds(embedBuilder.build()).queue()); 52 | } 53 | else if (guild.getSelfMember().hasPermission(gchannel, Permission.MESSAGE_EMBED_LINKS)) { 54 | channel.sendMessageEmbeds(embedBuilder.build()).addContent(user.getAsMention()).queue(); 55 | } 56 | else { 57 | channel.sendMessage(message).addContent("\n" + user.getAsMention()).queue(); 58 | } 59 | } 60 | DBConnectionManagerLum.sendUpdate("DELETE FROM `Reminders` WHERE `ID` = ?", rs.getLong("ID")); 61 | } 62 | DBConnectionManagerLum.closeRequest(rs); 63 | } 64 | catch (Exception e) { 65 | ExceptionUtils.reportException("Failed to get reminders", e); 66 | } 67 | } 68 | 69 | public static void start() { 70 | Timer timer = new Timer(); 71 | Calendar time = Calendar.getInstance(); 72 | time.set(Calendar.MILLISECOND, 0); 73 | time.set(Calendar.SECOND, 0); 74 | time.set(Calendar.MINUTE, time.get(Calendar.MINUTE) + 1); 75 | timer.schedule( 76 | new Reminders(), 77 | time.getTime(), 78 | 1000 * 60 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/timers/Anime.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.timers; 2 | 3 | import java.awt.Color; 4 | import java.net.URI; 5 | import java.net.http.HttpClient; 6 | import java.net.http.HttpRequest; 7 | import java.net.http.HttpResponse; 8 | import java.time.Instant; 9 | import java.time.temporal.ChronoUnit; 10 | import java.util.Date; 11 | import java.util.Timer; 12 | import java.util.TimerTask; 13 | 14 | import com.google.gson.JsonArray; 15 | import com.google.gson.JsonElement; 16 | import com.google.gson.JsonObject; 17 | import com.google.gson.JsonParser; 18 | import net.dv8tion.jda.api.EmbedBuilder; 19 | import slaynash.lum.bot.ConfigManager; 20 | import slaynash.lum.bot.discord.JDAManager; 21 | import slaynash.lum.bot.utils.ExceptionUtils; 22 | 23 | public class Anime extends TimerTask { 24 | public void run() { 25 | try { 26 | HttpRequest request = HttpRequest.newBuilder() 27 | .uri(URI.create("https://animeschedule.net/api/v3/timetables/sub")) 28 | .header("User-Agent", "LUM Bot " + ConfigManager.commitHash) 29 | .header("Authorization", "Bearer " + ConfigManager.animescheduleApiKey) 30 | .method("GET", HttpRequest.BodyPublishers.noBody()) 31 | .build(); 32 | HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); 33 | JsonArray json = JsonParser.parseString(response.body()).getAsJsonArray(); 34 | Instant startOfDay = Instant.now().truncatedTo(ChronoUnit.DAYS); 35 | 36 | StringBuilder sb = new StringBuilder(); 37 | Color color = Color.GREEN; 38 | 39 | for (JsonElement element : json) { 40 | JsonObject anime = element.getAsJsonObject(); 41 | String episode = "Episode " + anime.get("episodeNumber").getAsString(); 42 | String episodeDate = anime.get("episodeDate").getAsString(); 43 | Instant instant = Instant.parse(episodeDate); 44 | String title = anime.get("title").getAsString(); 45 | String url = "https://animeschedule.net/anime/" + anime.get("route").getAsString(); 46 | String time = " at <t:" + instant.getEpochSecond() + ":t>\n"; 47 | if (anime.has("delayedText")) 48 | time = " **" + anime.get("delayedText").getAsString() + "**\n"; 49 | if (anime.has("english")) 50 | title = anime.get("english").getAsString(); 51 | if (anime.has("episodes") && anime.get("episodes").equals(anime.get("episodeNumber"))) 52 | episode = episode + "F"; 53 | if (anime.has("mediaTypes") && anime.get("mediaTypes").getAsJsonArray().get(0).getAsJsonObject().get("name").getAsString().equals("Movie")) 54 | episode = "Movie"; 55 | 56 | if (instant.isAfter(startOfDay) && instant.isBefore(startOfDay.plus(1, ChronoUnit.DAYS))) { 57 | sb.append("* [").append(title).append("](").append(url).append(") ").append(episode).append(time); 58 | } 59 | } 60 | 61 | if (sb.isEmpty()) { 62 | sb.append("No anime today"); 63 | color = Color.YELLOW; 64 | } 65 | 66 | EmbedBuilder embed = new EmbedBuilder(); 67 | embed.setTitle("Anime Schedule <t:" + startOfDay.getEpochSecond() + ":D>"); 68 | embed.setDescription(sb.toString().strip()); 69 | embed.setColor(color); 70 | JDAManager.getJDA().getGuildById(627168678471008269L).getTextChannelById(628799325232693248L).sendMessageEmbeds(embed.build()).queue(); 71 | } 72 | catch (Exception e) { 73 | ExceptionUtils.reportException("Exception while handling Anime:", e); 74 | } 75 | } 76 | 77 | public static void start() { 78 | Timer timer = new Timer(); 79 | timer.schedule( 80 | new Anime(), 81 | Date.from(Instant.now().plus(1, ChronoUnit.DAYS).truncatedTo(ChronoUnit.DAYS)), 82 | 1000 * 60 * 60 * 24 83 | ); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/commands/Kick.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.commands; 2 | 3 | import java.util.Collections; 4 | 5 | import net.dv8tion.jda.api.Permission; 6 | import net.dv8tion.jda.api.entities.Member; 7 | import net.dv8tion.jda.api.entities.Message; 8 | import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion; 9 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 10 | import slaynash.lum.bot.ConfigManager; 11 | import slaynash.lum.bot.discord.Command; 12 | import slaynash.lum.bot.discord.CommandManager; 13 | import slaynash.lum.bot.utils.Utils; 14 | 15 | public class Kick extends Command { 16 | @Override 17 | protected void onServer(String paramString, MessageReceivedEvent event) { 18 | if (!includeInHelp(event)) 19 | return; 20 | 21 | Member kickMember; 22 | Message replied = event.getMessage().getReferencedMessage(); 23 | String reason = ""; 24 | if (replied != null) { //l!kick reason 25 | kickMember = replied.getMember(); 26 | String[] parts = paramString.split(" ", 2); 27 | if (parts.length > 1) 28 | reason = parts[1]; 29 | } 30 | else { 31 | String[] parts = paramString.split(" ", 3); //l!kick UserID reason 32 | if (parts.length < 2) { 33 | event.getMessage().reply("Usage: reply to user or " + ConfigManager.discordPrefix + getName() + " <UserID> (reason)").queue(); 34 | return; 35 | } 36 | if (parts.length > 2) { 37 | reason = parts[2]; 38 | } 39 | try { 40 | kickMember = event.getGuild().getMemberById(parts[1]); 41 | } 42 | catch (Exception e) { 43 | event.getMessage().reply("Invalid snowflake, User was not found!").queue(); 44 | return; 45 | } 46 | } 47 | 48 | if (kickMember == null) { 49 | event.getMessage().reply("User was not found!").queue(); 50 | return; 51 | } 52 | 53 | if (kickMember.equals(event.getGuild().getSelfMember())) { 54 | event.getMessage().reply("Please don't kick me, I have been a good bot").queue(); 55 | return; 56 | } 57 | 58 | if (kickMember.equals(event.getMember())) { 59 | event.getMessage().reply("https://tenor.com/view/leave-go-away-just-leave-you-are-annoying-leave-server-gif-17802417").queue(); 60 | return; 61 | } 62 | 63 | if (!event.getGuild().getSelfMember().canInteract(kickMember)) { 64 | event.getMessage().reply("Can not kick " + kickMember.getUser().getAsMention() + "(" + kickMember.getId() + ") because they are a higher role than my role").setAllowedMentions(Collections.emptyList()).queue(); 65 | return; 66 | } 67 | 68 | if (reason.isBlank()) 69 | kickMember.kick().reason("Kicked by " + event.getMember().getEffectiveName()).queue(); 70 | else 71 | kickMember.kick().reason(event.getAuthor().getName() + " - " + reason).queue(); 72 | 73 | MessageChannelUnion reportChannel = CommandManager.getModReportChannels(event, "kick"); 74 | if (reportChannel != null && !reportChannel.equals(event.getChannel())) 75 | Utils.sendMessage("User " + kickMember.getUser().getAsMention() + "(" + kickMember.getId() + ") has been kicked by " + event.getMember().getEffectiveName() + "!\n" + reason, reportChannel); 76 | event.getChannel().sendMessage("User " + kickMember.getUser().getAsMention() + "(" + kickMember.getId() + ") has been kicked!\n" + reason).queue(); 77 | } 78 | 79 | @Override 80 | protected boolean matchPattern(String paramString) { 81 | return paramString.startsWith(ConfigManager.discordPrefix + getName()); 82 | } 83 | 84 | @Override 85 | public boolean includeInHelp(MessageReceivedEvent event) { 86 | return event.getGuild().getSelfMember().hasPermission(Permission.KICK_MEMBERS) && event.getMember().hasPermission(Permission.KICK_MEMBERS); 87 | } 88 | 89 | @Override 90 | public String getHelpDescription() { 91 | return "Kicks a member. Reply or UserID - Staff only"; 92 | } 93 | 94 | @Override 95 | public String getName() { 96 | return "kick"; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/api/WebResponse.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.api; 2 | 3 | import java.nio.charset.StandardCharsets; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public class WebResponse { 10 | 11 | public int returnCode; 12 | public final String returnMessage; 13 | public final Map<String, List<String>> headers = new HashMap<>(); 14 | public byte[] data = new byte[0]; 15 | 16 | public WebResponse() { 17 | returnCode = 200; 18 | returnMessage = "OK"; 19 | //headers.put("Server", "Slaynash's Server"); 20 | //headers.put("Content-Type", "text/html; charset=UTF-8"); 21 | //headers.put("Date", ZonedDateTime.now().format(DateTimeFormatter.RFC_1123_DATE_TIME)); 22 | } 23 | 24 | public void setData(String data) { 25 | //System.out.println("SetData: " + data); 26 | setData(data.getBytes(StandardCharsets.UTF_8)); 27 | } 28 | 29 | public void setData(byte[] data) { 30 | this.data = data; 31 | } 32 | 33 | public void addHeader(String key, String value) { 34 | List<String> header = headers.computeIfAbsent(key, k -> new ArrayList<>()); 35 | header.add(value); 36 | } 37 | 38 | public static WebResponse getBadRequestResponse() { 39 | WebResponse r = new WebResponse(); 40 | r.returnCode = 400; 41 | r.setData(""" 42 | <html>\r 43 | <head><title>400 Bad Request\r 44 | \r 45 |

400 Bad Request

\r 46 |
Slaynash's Server
\r 47 | \r 48 | """); 49 | return r; 50 | } 51 | 52 | public static WebResponse getMissingCredentialsResponse() { 53 | WebResponse r = new WebResponse(); 54 | r.returnCode = 401; 55 | r.addHeader("Content-Type", "application/json"); 56 | r.setData("{\"error\":{\"message\":\"Missing Credentials\",\"status_code\":401}}"); 57 | return r; 58 | } 59 | 60 | public static WebResponse getInvalidCredentialsResponse() { 61 | WebResponse r = new WebResponse(); 62 | r.returnCode = 401; 63 | r.addHeader("Content-Type", "application/json"); 64 | r.setData("{\"error\":{\"message\":\"Invalid Credentials\",\"status_code\":401}}"); 65 | return r; 66 | } 67 | 68 | public static WebResponse getUnauthorizedResponse() { 69 | WebResponse r = new WebResponse(); 70 | r.returnCode = 403; 71 | r.addHeader("Content-Type", "application/json"); 72 | r.setData("{\"error\":\"Unauthorized\",\"status_code\":403}"); 73 | return r; 74 | } 75 | 76 | public static WebResponse getNotFoundResponse() { 77 | WebResponse r = new WebResponse(); 78 | r.returnCode = 404; 79 | r.addHeader("Content-Type", "application/json"); 80 | r.setData("{\"error\":\"Endpoint Not Found\",\"status_code\":404}"); 81 | return r; 82 | } 83 | 84 | public static WebResponse getInternalErrorResponse() { 85 | WebResponse r = new WebResponse(); 86 | r.returnCode = 500; 87 | r.addHeader("Content-Type", "application/json"); 88 | r.setData("{\"error\":\"Internal Server Error\",\"status_code\":500}"); 89 | return r; 90 | } 91 | 92 | public static WebResponse getNotImplementedResponse() { 93 | WebResponse r = new WebResponse(); 94 | r.returnCode = 501; 95 | r.addHeader("Content-Type", "application/json"); 96 | r.setData("{\"error\":\"Method Not Implemented\",\"status_code\":501}"); 97 | return r; 98 | } 99 | 100 | public static WebResponse getBadGatewayResponse() { 101 | WebResponse r = new WebResponse(); 102 | r.returnCode = 502; 103 | r.addHeader("Content-Type", "application/json"); 104 | r.setData("{\"error\":\"Bad Gateway\",\"status_code\":502}"); 105 | return r; 106 | } 107 | 108 | public static WebResponse getUnsupportedResponse() { 109 | WebResponse r = new WebResponse(); 110 | r.returnCode = 505; 111 | r.addHeader("Content-Type", "application/json"); 112 | r.setData("{\"error\":\"HTTP Version Not Supported\",\"status_code\":505}"); 113 | return r; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/slashs/SetMemes.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.slashs; 2 | 3 | import java.sql.ResultSet; 4 | import java.util.List; 5 | 6 | import net.dv8tion.jda.api.entities.channel.ChannelType; 7 | import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; 8 | import net.dv8tion.jda.api.interactions.InteractionContextType; 9 | import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; 10 | import net.dv8tion.jda.api.interactions.commands.OptionMapping; 11 | import net.dv8tion.jda.api.interactions.commands.OptionType; 12 | import net.dv8tion.jda.api.interactions.commands.build.CommandData; 13 | import net.dv8tion.jda.api.interactions.commands.build.Commands; 14 | import slaynash.lum.bot.ConfigManager; 15 | import slaynash.lum.bot.DBConnectionManagerLum; 16 | import slaynash.lum.bot.utils.ExceptionUtils; 17 | 18 | public class SetMemes extends Slash { 19 | @Override 20 | protected CommandData globalSlashData() { 21 | return Commands.slash("meme", "Auto moderate a meme channel - Admins only").addOption(OptionType.CHANNEL, "report", "Optional Channel for message logs", false).setDefaultPermissions(DefaultMemberPermissions.DISABLED).setContexts(InteractionContextType.GUILD); 22 | } 23 | 24 | @Override 25 | public void slashRun(SlashCommandInteractionEvent event) { 26 | List reportChannel = event.getOptionsByName("report"); 27 | if (event.getChannelType() == ChannelType.PRIVATE) { 28 | event.reply("This command does not work in DMs").setEphemeral(true).queue(); 29 | } 30 | else if (!ConfigManager.mainBot) { 31 | event.reply("This command is not available on backup bot, please wait for main bot to come back online.").setEphemeral(true).queue(); 32 | } 33 | else if (!reportChannel.isEmpty() && reportChannel.get(0).getAsLong() == event.getChannel().getIdLong()) { 34 | event.reply("You can't set the report channel to the same channel as the meme channel").setEphemeral(true).queue(); 35 | } 36 | else if (!event.getGuild().getSelfMember().hasPermission(event.getGuildChannel(), net.dv8tion.jda.api.Permission.MESSAGE_MANAGE)) { 37 | event.reply("Lum does not have " + net.dv8tion.jda.api.Permission.MESSAGE_MANAGE.getName() + " permission").setEphemeral(true).queue(); 38 | } 39 | else if (!event.getGuild().getSelfMember().hasPermission(event.getGuildChannel(), net.dv8tion.jda.api.Permission.MESSAGE_EXT_EMOJI)) { 40 | event.reply("Lum does not have " + net.dv8tion.jda.api.Permission.MESSAGE_EXT_EMOJI.getName() + " permission").setEphemeral(true).queue(); 41 | } 42 | else if (!event.getGuild().getSelfMember().hasPermission(event.getGuildChannel(), net.dv8tion.jda.api.Permission.MESSAGE_ADD_REACTION)) { 43 | event.reply("Lum does not have " + net.dv8tion.jda.api.Permission.MESSAGE_ADD_REACTION.getName() + " permission").setEphemeral(true).queue(); 44 | } 45 | else try { 46 | String guildID = event.getGuild().getId(); 47 | String channelID = event.getChannel().getId(); 48 | ResultSet rs = DBConnectionManagerLum.sendRequest("SELECT `ReportChannel` FROM `Memes` WHERE MemeChannel = ?", channelID); 49 | if (rs.next()) { 50 | DBConnectionManagerLum.sendUpdate("DELETE FROM `Memes` WHERE MemeChannel = ?", channelID); 51 | event.reply("Meme moderation is now disabled in this channel").setEphemeral(true).queue(); 52 | } 53 | else { 54 | DBConnectionManagerLum.sendUpdate("INSERT INTO `Memes` (`GuildID`, `user`, `MemeChannel`, `ReportChannel`) VALUES (?, ?, ?, ?)", guildID, event.getUser().getIdLong(), channelID, reportChannel.isEmpty() ? null : reportChannel.get(0).getAsChannel().getId()); 55 | if (reportChannel.isEmpty()) { 56 | event.reply("Meme moderation is now enabled in this channel").setEphemeral(true).queue(); 57 | } 58 | else { 59 | event.reply("Meme moderation is now enabled in this channel and reports will be sent to " + reportChannel.get(0).getAsChannel().getAsMention()).setEphemeral(true).queue(); 60 | } 61 | } 62 | DBConnectionManagerLum.closeRequest(rs); 63 | } 64 | catch (Exception e) { 65 | ExceptionUtils.reportException("Failed to set meme channel", e, event.getChannel()); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /unityversionsmonitor/HashChecker/SlaynashUtils/CppUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace SlaynashUtils 7 | { 8 | public static class CppUtils 9 | { 10 | public static unsafe byte GetByte(this IntPtr value) => *(byte*)value; 11 | public static unsafe bool GetBool(this IntPtr value) => *(bool*)value; 12 | public static unsafe ushort GetUShort(IntPtr value) => *(ushort*)value; 13 | public static unsafe int GetInt(this IntPtr value) => *(int*)value; 14 | public static unsafe uint GetUInt(this IntPtr value) => *(uint*)value; 15 | public static unsafe IntPtr GetValue(this IntPtr pointer) => *(IntPtr*)pointer; 16 | 17 | public static unsafe void SetValue(this IntPtr pointer, bool value) => *(bool*)pointer = value; 18 | 19 | public static unsafe string CharArrayPtrToString(this IntPtr ptr) 20 | { 21 | byte* text = *(byte**)ptr; 22 | int length = 0; 23 | while (text[length] != 0) 24 | ++length; 25 | 26 | return Encoding.UTF8.GetString(text, length); 27 | } 28 | 29 | internal static unsafe IntPtr Sigscan(IntPtr module, string signature) 30 | { 31 | int moduleSize = 0; 32 | foreach (ProcessModule module_ in Process.GetCurrentProcess().Modules) 33 | { 34 | if (module_.ModuleName == "UnityPlayer.dll") 35 | { 36 | moduleSize = module_.ModuleMemorySize; 37 | break; 38 | } 39 | } 40 | 41 | string signatureSpaceless = signature.Replace(" ", ""); 42 | int signatureLength = signatureSpaceless.Length / 2; 43 | byte[] signatureBytes = new byte[signatureLength]; 44 | bool[] signatureNullBytes = new bool[signatureLength]; 45 | for (int i = 0; i < signatureLength; ++i) 46 | { 47 | if (signatureSpaceless[i * 2] == '?') 48 | signatureNullBytes[i] = true; 49 | else 50 | signatureBytes[i] = (byte)((GetHexVal(signatureSpaceless[i * 2]) << 4) + (GetHexVal(signatureSpaceless[(i * 2) + 1]))); 51 | } 52 | 53 | long index = module.ToInt64(); 54 | long maxIndex = index + moduleSize; 55 | long tmpAddress = 0; 56 | int processed = 0; 57 | 58 | while (index < maxIndex) 59 | { 60 | if (signatureNullBytes[processed] || *(byte*)index == signatureBytes[processed]) 61 | { 62 | if (processed == 0) 63 | tmpAddress = index; 64 | 65 | ++processed; 66 | 67 | if (processed == signatureLength) 68 | return (IntPtr)tmpAddress; 69 | } 70 | else 71 | { 72 | processed = 0; 73 | } 74 | 75 | ++index; 76 | } 77 | 78 | return IntPtr.Zero; 79 | } 80 | 81 | public static IntPtr ResolveRelativeInstruction(IntPtr instruction) 82 | { 83 | byte opcode = instruction.GetByte(); 84 | if (opcode != 0xE8 && opcode != 0xE9) 85 | return IntPtr.Zero; 86 | 87 | return ResolvePtrOffset(instruction + 1, instruction + 5); // CALL: E8 [rel32] / JMP: E9 [rel32] 88 | } 89 | 90 | public static IntPtr ResolvePtrOffset(IntPtr offset32Ptr, IntPtr nextInstructionPtr) 91 | { 92 | uint jmpOffset = offset32Ptr.GetUInt(); 93 | uint valueUInt = new ConvertClass() { valueULong = (ulong)nextInstructionPtr.ToInt64() }.valueUInt; 94 | long delta = nextInstructionPtr.ToInt64() - valueUInt; 95 | uint newPtrInt = unchecked(valueUInt + jmpOffset); 96 | return new IntPtr(newPtrInt + delta); 97 | } 98 | 99 | 100 | 101 | // source: https://stackoverflow.com/a/9995303 102 | private static int GetHexVal(char hex) 103 | { 104 | int val = (int)hex; 105 | return val - (val < 58 ? 48 : (val < 97 ? 55 : 87)); 106 | } 107 | 108 | 109 | 110 | [StructLayout(LayoutKind.Explicit)] 111 | private class ConvertClass 112 | { 113 | [FieldOffset(0)] 114 | public ulong valueULong; 115 | 116 | [FieldOffset(0)] 117 | public uint valueUInt; 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/Localization.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.file.Files; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.Random; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | import com.google.gson.Gson; 13 | import com.google.gson.reflect.TypeToken; 14 | import slaynash.lum.bot.utils.ExceptionUtils; 15 | 16 | public final class Localization { 17 | public static final String LOG_IDENTIFIER = "Localization"; 18 | 19 | public static final Map> localizations = new HashMap<>(); 20 | 21 | public static boolean init() { 22 | Gson gson = new Gson(); 23 | 24 | try (Stream lines = Files.lines(new File("localization.json").toPath())) { 25 | String data = lines.collect(Collectors.joining("\n")); 26 | 27 | localizations.putAll(gson.fromJson(data, new TypeToken>>() {}.getType())); 28 | } 29 | catch (IOException exception) { 30 | ExceptionUtils.reportException("Failed to load translations", exception); 31 | return false; 32 | } 33 | 34 | return true; 35 | } 36 | 37 | public static boolean reload() { 38 | synchronized (localizations) { 39 | Map> localizationsBackup = new HashMap<>(localizations); 40 | localizations.clear(); 41 | if (!init()) { //restore if load failed 42 | localizations.putAll(localizationsBackup); 43 | return false; 44 | } 45 | } 46 | 47 | return true; 48 | } 49 | 50 | public static String get(String key, String lang) { 51 | String ret = getInternal(key, lang); 52 | if ("sga".equals(lang)) 53 | return toStandardGalacticAlphabet(ret); 54 | if ("owo".equals(lang) || "uwu".equals(lang)) 55 | return toOwOify(ret); 56 | 57 | return ret; 58 | } 59 | 60 | public static String getFormat(String key, String lang, Object... args) { 61 | String ret = getInternal(key, lang); 62 | if (!ret.equals(key)) 63 | ret = String.format(ret, args); 64 | 65 | if ("sga".equals(lang)) 66 | return toStandardGalacticAlphabet(ret); 67 | 68 | if ("owo".equals(lang) || "uwu".equals(lang)) 69 | return toOwOify(ret); 70 | 71 | return ret; 72 | } 73 | 74 | private static String getInternal(String key, String lang) { 75 | String locale; 76 | 77 | synchronized (localizations) { 78 | Map langMap = localizations.get(lang); 79 | if (langMap == null) { 80 | if (lang.equals("en")) 81 | return key; 82 | return getInternal(key, "en"); 83 | } 84 | 85 | locale = langMap.get(key); 86 | if (locale == null) { 87 | if (lang.equals("en")) 88 | return key; 89 | return getInternal(key, "en"); 90 | } 91 | } 92 | 93 | return locale; 94 | } 95 | 96 | static final String[] STANDARD_GALACTIC_ALPHABET = { 97 | "ᔑ", "ʖ", "ᓵ", "↸", "ᒷ", "⎓", 98 | "⊣", "⍑", "╎", "⋮", "ꖌ", "ꖎ", 99 | "ᒲ", "リ", "𝙹", "!¡", "ᑑ", "∷", 100 | "ᓭ", "ℸ", "⚍", "⍊", "∴", "̇/", "\\|\\|", "⨅" 101 | }; 102 | public static String toStandardGalacticAlphabet(String original) { 103 | StringBuilder ret = new StringBuilder(original.length()); 104 | 105 | original.toLowerCase().chars().forEachOrdered(c -> { 106 | if (c <= 'z' && c >= 'a') 107 | ret.append(STANDARD_GALACTIC_ALPHABET[c - 'a']); 108 | else 109 | ret.append((char) c); 110 | }); 111 | 112 | return ret.toString(); 113 | } 114 | public static String toOwOify(String text) { 115 | if (text == null || text.isBlank()) 116 | return text; 117 | 118 | text = text.replace('r', 'w') 119 | .replace('l', 'w') 120 | .replace('R', 'W') 121 | .replace('L', 'W'); 122 | Random random = new Random(); 123 | StringBuilder nyan = new StringBuilder(text.length()); 124 | for (String cat : text.split(" ")) { 125 | switch (random.nextInt(2)) { 126 | case 0 -> cat = cat.replace("n", "ny"); 127 | case 1 -> cat = cat.replace("n", "nya"); 128 | default -> { 129 | } 130 | } 131 | nyan.append(cat).append(" "); 132 | } 133 | 134 | return nyan.toString(); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .settings.conf 2 | /Lumbot.iml 3 | *.orig 4 | /.VSCodeCounter/* 5 | /MLlogs/ 6 | /commands/ 7 | /storage/ 8 | /SteamLogs/ 9 | 10 | /*.txt 11 | /*.jar 12 | 13 | /target 14 | 15 | .metadata 16 | bin/ 17 | tmp/ 18 | logs 19 | *.tmp 20 | *.bak 21 | *.swp 22 | *~.nib 23 | local.properties 24 | .settings/ 25 | .loadpath 26 | .recommenders 27 | .classpath 28 | 29 | # External tool builders 30 | .externalToolBuilders/ 31 | 32 | # Locally stored "Eclipse launch configurations" 33 | *.launch 34 | 35 | # PyDev specific (Python IDE for Eclipse) 36 | *.pydevproject 37 | 38 | # CDT-specific (C/C++ Development Tooling) 39 | .cproject 40 | 41 | # CDT- autotools 42 | .autotools 43 | 44 | # Java annotation processor (APT) 45 | .factorypath 46 | 47 | # PDT-specific (PHP Development Tools) 48 | .buildpath 49 | 50 | # sbteclipse plugin 51 | .target 52 | 53 | # Tern plugin 54 | .tern-project 55 | 56 | # TeXlipse plugin 57 | .texlipse 58 | 59 | # STS (Spring Tool Suite) 60 | .springBeans 61 | 62 | # Code Recommenders 63 | .recommenders/ 64 | 65 | # Annotation Processing 66 | .apt_generated/ 67 | .apt_generated_test/ 68 | 69 | # Scala IDE specific (Scala & Java development for Eclipse) 70 | .cache-main 71 | .scala_dependencies 72 | .worksheet 73 | 74 | # Uncomment this line if you wish to ignore the project description file. 75 | # Typically, this file would be tracked if it contains build/dependency configurations: 76 | #.project 77 | 78 | 79 | # Created by https://www.toptal.com/developers/gitignore/api/intellij 80 | # Edit at https://www.toptal.com/developers/gitignore?templates=intellij 81 | 82 | ### Intellij ### 83 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 84 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 85 | 86 | # User-specific stuff 87 | .idea/**/workspace.xml 88 | .idea/**/tasks.xml 89 | .idea/**/usage.statistics.xml 90 | .idea/**/dictionaries 91 | .idea/**/shelf 92 | 93 | # AWS User-specific 94 | .idea/**/aws.xml 95 | 96 | # Generated files 97 | .idea/**/contentModel.xml 98 | 99 | # Sensitive or high-churn files 100 | .idea/**/dataSources/ 101 | .idea/**/dataSources.ids 102 | .idea/**/dataSources.local.xml 103 | .idea/**/sqlDataSources.xml 104 | .idea/**/dynamic.xml 105 | .idea/**/uiDesigner.xml 106 | .idea/**/dbnavigator.xml 107 | 108 | # Gradle 109 | .idea/**/gradle.xml 110 | .idea/**/libraries 111 | 112 | # Gradle and Maven with auto-import 113 | # When using Gradle or Maven with auto-import, you should exclude module files, 114 | # since they will be recreated, and may cause churn. Uncomment if using 115 | # auto-import. 116 | # .idea/artifacts 117 | # .idea/compiler.xml 118 | # .idea/jarRepositories.xml 119 | # .idea/modules.xml 120 | # .idea/*.iml 121 | # .idea/modules 122 | # *.iml 123 | # *.ipr 124 | 125 | # CMake 126 | cmake-build-*/ 127 | 128 | # Mongo Explorer plugin 129 | .idea/**/mongoSettings.xml 130 | 131 | # File-based project format 132 | *.iws 133 | 134 | # IntelliJ 135 | out/ 136 | 137 | # mpeltonen/sbt-idea plugin 138 | .idea_modules/ 139 | 140 | # JIRA plugin 141 | atlassian-ide-plugin.xml 142 | 143 | # Cursive Clojure plugin 144 | .idea/replstate.xml 145 | 146 | # SonarLint plugin 147 | .idea/sonarlint/ 148 | 149 | # Crashlytics plugin (for Android Studio and IntelliJ) 150 | com_crashlytics_export_strings.xml 151 | crashlytics.properties 152 | crashlytics-build.properties 153 | fabric.properties 154 | 155 | # Editor-based Rest Client 156 | .idea/httpRequests 157 | 158 | # Android studio 3.1+ serialized cache file 159 | .idea/caches/build_file_checksums.ser 160 | 161 | ### Intellij Patch ### 162 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 163 | 164 | # *.iml 165 | # modules.xml 166 | # .idea/misc.xml 167 | # *.ipr 168 | 169 | # Sonarlint plugin 170 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 171 | .idea/**/sonarlint/ 172 | 173 | # SonarQube Plugin 174 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 175 | .idea/**/sonarIssues.xml 176 | 177 | # Markdown Navigator plugin 178 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 179 | .idea/**/markdown-navigator.xml 180 | .idea/**/markdown-navigator-enh.xml 181 | .idea/**/markdown-navigator/ 182 | 183 | # Cache file creation bug 184 | # See https://youtrack.jetbrains.com/issue/JBR-2257 185 | .idea/$CACHE_FILE$ 186 | 187 | # CodeStream plugin 188 | # https://plugins.jetbrains.com/plugin/12206-codestream 189 | .idea/codestream.xml 190 | 191 | # Azure Toolkit for IntelliJ plugin 192 | # https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij 193 | .idea/**/azureSettings.xml 194 | 195 | # End of https://www.toptal.com/developers/gitignore/api/intellij 196 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/slashs/SlashManager.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.slashs; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.Map.Entry; 8 | 9 | import net.dv8tion.jda.api.JDA; 10 | import net.dv8tion.jda.api.entities.Guild; 11 | import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; 12 | import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; 13 | import net.dv8tion.jda.api.interactions.commands.build.CommandData; 14 | import slaynash.lum.bot.discord.JDAManager; 15 | import slaynash.lum.bot.utils.ExceptionUtils; 16 | 17 | public class SlashManager { 18 | private static final List slashes = new ArrayList<>(); 19 | 20 | public static void slashRun(SlashCommandInteractionEvent event) { 21 | System.out.println("Slash " + event.getName() + (event.getSubcommandName() == null ? "" : " " + event.getSubcommandName()) + " options:" + event.getOptions() + " in " + (event.getGuild() == null ? "DM " + event.getUser().getId() : event.getGuild().getName())); 22 | if (!event.getChannel().canTalk()) { 23 | event.reply("I can't talk in this channel!").setEphemeral(true).queue(); // Ephemeral message will work even without any permissions 24 | return; 25 | } 26 | for (Slash slash : slashes) { 27 | CommandData global = slash.globalSlashData(); 28 | if (global != null && event.getName().equals(global.getName())) { 29 | slash.slashRun(event); 30 | return; 31 | } 32 | if (slash.guildSlashData() != null) { 33 | CommandData guild = slash.guildSlashData().get(event.getGuild().getIdLong()); 34 | if (guild != null && event.getName().equals(guild.getName())) { 35 | slash.slashRun(event); 36 | return; 37 | } 38 | } 39 | } 40 | event.reply("Unknown command").queue(); 41 | } 42 | 43 | public static void buttonClicked(ButtonInteractionEvent event) { 44 | if (!event.getChannel().canTalk()) { 45 | event.reply("I can't talk in this channel!").setEphemeral(true).queue(); // Ephemeral message will work even without any permissions 46 | return; //Lum can't talk in this channel 47 | } 48 | 49 | for (Slash slash : slashes) { 50 | if (slash.buttonList() != null && slash.buttonList().contains(event.getComponentId())) { 51 | slash.buttonClick(event); 52 | return; 53 | } 54 | } 55 | 56 | event.reply("Unknown button click").queue(); 57 | } 58 | 59 | protected static void registerSlash(Slash command) { 60 | synchronized (slashes) { 61 | slashes.add(command); 62 | } 63 | } 64 | 65 | public static void registerCommands() { 66 | JDA jda = JDAManager.getJDA(); 67 | 68 | registerSlash(new SlashConfig()); 69 | registerSlash(new SteamWatcher()); 70 | registerSlash(new Replies()); 71 | registerSlash(new Reminder()); 72 | registerSlash(new SetMemes()); 73 | registerSlash(new SetVRCAPI()); 74 | registerSlash(new SetLogChannel()); 75 | registerSlash(new UnivUCBLLIFExoGenerator()); 76 | 77 | try { 78 | List globalSlashes = new ArrayList<>(); 79 | Map> guildSlashes = new HashMap<>(); 80 | 81 | for (Slash slash : slashes) { 82 | CommandData globalSlash = slash.globalSlashData(); 83 | if (globalSlash != null) 84 | globalSlashes.add(globalSlash); 85 | Map guildSlash = slash.guildSlashData(); 86 | if (guildSlash != null) { 87 | for (Entry guild : guildSlash.entrySet()) { 88 | List guildCommands = guildSlashes.get(guild.getKey()); 89 | if (guildCommands == null) 90 | guildCommands = new ArrayList<>(); 91 | guildCommands.add(guild.getValue()); 92 | guildSlashes.put(guild.getKey(), guildCommands); 93 | } 94 | } 95 | } 96 | 97 | //Removes any old commands and registers the new ones 98 | jda.updateCommands().addCommands(globalSlashes).queue(); 99 | for (Guild guild : jda.getGuilds()) { 100 | List gslash = guildSlashes.get(guild.getIdLong()); 101 | if (gslash != null) 102 | guild.updateCommands().addCommands(gslash).queue(null, e -> { }); 103 | } 104 | } 105 | catch (Exception e) { 106 | ExceptionUtils.reportException("Error registering command", e); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/ABCpolice.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord; 2 | 3 | import java.time.OffsetDateTime; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.Optional; 7 | 8 | import net.dv8tion.jda.api.entities.Message; 9 | import net.dv8tion.jda.api.entities.emoji.Emoji; 10 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 11 | import net.gcardone.junidecode.Junidecode; 12 | 13 | public class ABCpolice { 14 | public static final String LOG_IDENTIFIER = "ABCpolice"; 15 | public static boolean abcPolice(MessageReceivedEvent event) { 16 | if (event.getChannel().getIdLong() != 815364277123940423L) 17 | return false; 18 | if (event.getAuthor().isBot() || event.getMessage().isEdited()) 19 | return true; 20 | String message = event.getMessage().getContentStripped().trim(); 21 | if (!event.getMessage().getStickers().isEmpty()) 22 | message = event.getMessage().getStickers().get(0).getName() + " " + message; 23 | System.out.println("abc " + event.getMember().getEffectiveName() + ": " + message); 24 | List history = new ArrayList<>(event.getChannel().asGuildMessageChannel().getHistoryBefore(event.getMessage(), 20).complete().getRetrievedHistory()); 25 | boolean brokenChain = !history.isEmpty() && history.get(0).getAuthor().equals(event.getJDA().getSelfUser()) && history.get(0).getContentStripped().contains("tart back to"); 26 | Optional find = history.stream().filter(h -> h.getContentStripped().contains("tart back to")).findFirst(); 27 | find.ifPresent(value -> history.subList(history.indexOf(value), history.size()).clear()); 28 | history.removeIf(m -> m.getAuthor().isBot()); 29 | if (history.isEmpty() && !brokenChain) //new channel or wipe or bot spam 30 | return true; 31 | Message previousAuthorMessage = history.stream().filter(f -> f.getAuthor().equals(event.getAuthor())).findFirst().orElse(null); 32 | char currentLetter = convertChar(message); 33 | char previousLetter = !history.isEmpty() ? convertChar(history.get(0).getContentStripped()) : 0; 34 | boolean timing = previousAuthorMessage != null && previousAuthorMessage.getTimeCreated().isAfter(OffsetDateTime.now().minusHours(30)); // if the previous message was sent less than 30 hours ago 35 | 36 | if (brokenChain || previousLetter == 0 || previousLetter == 'z') 37 | previousLetter = 'a' - 1; 38 | System.out.println("abc previousLetter:" + previousLetter + " currentLetter:" + currentLetter + " brokenChain:" + brokenChain); 39 | 40 | if (message.isEmpty()) { 41 | System.out.println("abc empty message"); 42 | event.getChannel().sendMessage(event.getMember().getEffectiveName() + " sent an empty message, Stickers are not allowed. Start back to `A`").queue(); 43 | return true; 44 | } 45 | else if ((int) currentLetter != (int) previousLetter + 1) { 46 | System.out.println("abc does not match"); 47 | event.getMessage().addReaction(Emoji.fromCustom("bonk", 907068295868477551L, false)).queue(); 48 | event.getChannel().sendMessage(event.getMember().getEffectiveName() + " just broke the chain, it should have been `" + Character.toUpperCase((char) (previousLetter + 1)) + "` <:Neko_sad:865328470652485633> Start back to `A`").queue(); 49 | return true; 50 | } 51 | else if (!brokenChain && message.length() == 1) { 52 | System.out.println("abc hey that is cheating"); 53 | event.getMessage().addReaction(Emoji.fromCustom("baka", 828070018935685130L, false)).queue(); 54 | event.getChannel().sendMessage("Hey that is cheating <:Neko_pout:865328471102324778> Time to start back to `A`").queue(); 55 | return true; 56 | } 57 | else if (!brokenChain && timing && currentLetter != 'a' && history.size() > 1 && (history.get(0).getAuthor().equals(event.getAuthor()) || history.get(1).getAuthor().equals(event.getAuthor()))) { 58 | System.out.println("abc spacing not meet"); 59 | event.getChannel().sendMessage("User spacing rule was not met <:Neko_sad:865328470652485633> Someone else, start back to `A`").queue(); 60 | return true; 61 | } 62 | else if (currentLetter == 'z') { 63 | System.out.println("abc looped around"); 64 | event.getChannel().sendMessage("おめでとう。あなたはめちゃくちゃせずにそれを出来た!<:Neko_dab:865328473719439381>").queue(); 65 | return true; 66 | } 67 | 68 | //valid ABC 69 | return true; 70 | } 71 | 72 | private static char convertChar(String letter) { 73 | if (letter.isEmpty()) 74 | return '\0'; 75 | letter = Junidecode.unidecode(letter.toLowerCase()); 76 | if (letter.charAt(0) == 55356) { //Unicode indicator or something like that 77 | if (letter.charAt(1) >= 56806 && letter.charAt(1) <= 56831) { //convert regional_indicator to lowercase letters 78 | return (char) (letter.charAt(1) - 56709); 79 | } 80 | } 81 | return letter.replace(":", "").charAt(0); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/melonscanner/LogCounter.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.melonscanner; 2 | 3 | import java.io.File; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.time.Instant; 7 | import java.util.Date; 8 | 9 | import net.dv8tion.jda.api.JDA.Status; 10 | import net.dv8tion.jda.api.entities.Activity; 11 | import net.dv8tion.jda.api.entities.Message.Attachment; 12 | import slaynash.lum.bot.ConfigManager; 13 | import slaynash.lum.bot.discord.JDAManager; 14 | import slaynash.lum.bot.utils.ExceptionUtils; 15 | 16 | public final class LogCounter { //TODO: create directory if it doesn't exist 17 | 18 | private static final String workingPath = System.getProperty("user.dir"); 19 | private static int previousLogCount = 0; 20 | private static int previousSSCount = 0; 21 | 22 | public static File addMLCounter(Attachment attachment) { 23 | if (workingPath == null) { 24 | System.out.println("Working directory not found"); 25 | return null; 26 | } 27 | try { 28 | String directoryPath = workingPath + "/MLlogs/"; 29 | if (!new File(directoryPath).isDirectory()) { 30 | new File(directoryPath).mkdir(); 31 | } 32 | File att = attachment.getProxy().downloadToFile(new File(directoryPath + Instant.now().toString().replace(":", "_") + "-" + attachment.getFileName())).get(); 33 | System.out.println("Downloaded attachment to " + att.getCanonicalPath()); 34 | return att; 35 | } 36 | catch (Exception exception) { 37 | ExceptionUtils.reportException( 38 | "Exception while Saving ML Log", 39 | exception.getMessage(), 40 | exception); 41 | } 42 | return null; 43 | } 44 | 45 | public static void addSSCounter(String bannedUser, String message, String guildID) { 46 | if (workingPath == null) { 47 | System.out.println("Working directory not found"); 48 | return; 49 | } 50 | try { 51 | String directoryPath = workingPath + "/SSlogs/"; 52 | if (!new File(directoryPath).isDirectory()) { 53 | new File(directoryPath).mkdir(); 54 | } 55 | 56 | Files.writeString(Path.of(directoryPath, bannedUser + "-" + guildID + ".txt"), message); 57 | } 58 | catch (Exception exception) { 59 | ExceptionUtils.reportException( 60 | "Exception while Saving Scam Shield Log", 61 | exception.getMessage(), 62 | exception); 63 | } 64 | } 65 | 66 | public static void updateCounter() { 67 | if (workingPath == null) { 68 | System.out.println("Working directory not found"); 69 | return; 70 | } 71 | try { 72 | if (!JDAManager.isEventsEnabled()) return; 73 | if (JDAManager.getJDA() == null || JDAManager.getJDA().getStatus() != Status.CONNECTED) 74 | return; 75 | Date date = new Date(); 76 | String directoryPath = workingPath + "/MLlogs/"; 77 | File directory = new File(directoryPath); 78 | if (!directory.exists()) { 79 | directory.mkdir(); 80 | } 81 | 82 | int logCount = directory.listFiles().length; 83 | if (logCount > 0) { 84 | // remove folders that is older then 24 hours 85 | for (File fileEntry : directory.listFiles()) { 86 | if ((date.getTime() - fileEntry.lastModified()) > 24 * 60 * 60 * 1000) { 87 | fileEntry.delete(); 88 | } 89 | } 90 | } 91 | logCount = directory.listFiles().length; 92 | 93 | directoryPath = workingPath + "/SSlogs/"; 94 | directory = new File(directoryPath); 95 | if (!directory.exists()) { 96 | directory.mkdir(); 97 | } 98 | int sslogCount = directory.listFiles().length; 99 | if (sslogCount > 0) { 100 | // remove folders that is older then 24 hours 101 | for (File fileEntry : directory.listFiles()) { 102 | if ((date.getTime() - fileEntry.lastModified()) > 24 * 60 * 60 * 1000) { 103 | fileEntry.delete(); 104 | } 105 | } 106 | } 107 | sslogCount = directory.listFiles().length; 108 | 109 | if (logCount != previousLogCount || sslogCount != previousSSCount) 110 | JDAManager.getJDA().getPresence().setActivity(Activity.watching((ConfigManager.mainBot ? "" : "BACKUP ") + logCount + " melons squashed and removed " 111 | + sslogCount + " scammers in 24 hours. In " + JDAManager.getJDA().getGuilds().size() + " guilds!")); 112 | previousLogCount = logCount; 113 | previousSSCount = sslogCount; 114 | } 115 | catch (Exception exception) { 116 | ExceptionUtils.reportException( 117 | "Exception while Updating Counter", 118 | exception); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/LuaPackages.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord; 2 | 3 | import net.dv8tion.jda.api.entities.User; 4 | import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; 5 | import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; 6 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 7 | import org.luaj.vm2.Globals; 8 | import org.luaj.vm2.LuaTable; 9 | import org.luaj.vm2.LuaValue; 10 | import org.luaj.vm2.lib.OneArgFunction; 11 | import org.luaj.vm2.lib.ZeroArgFunction; 12 | import org.luaj.vm2.lib.jse.JsePlatform; 13 | 14 | public abstract class LuaPackages { 15 | private static class PrintFunction extends OneArgFunction { 16 | private final MessageChannel channel; 17 | 18 | public PrintFunction(MessageChannel channel) { 19 | this.channel = channel; 20 | } 21 | 22 | @Override 23 | public LuaValue call(LuaValue arg) { 24 | this.channel.sendMessage(arg.toString()).queue(); 25 | return LuaValue.NIL; 26 | } 27 | } 28 | 29 | private static class GetChannelIdFunction extends ZeroArgFunction { 30 | private final MessageChannel channel; 31 | 32 | public GetChannelIdFunction(MessageChannel channel) { 33 | this.channel = channel; 34 | } 35 | 36 | @Override 37 | public LuaValue call() { 38 | return LuaValue.valueOf(this.channel.getId()); 39 | } 40 | } 41 | 42 | private static class GetChannelNameFunction extends ZeroArgFunction { 43 | private final MessageChannel channel; 44 | 45 | public GetChannelNameFunction(MessageChannel channel) { 46 | this.channel = channel; 47 | } 48 | 49 | @Override 50 | public LuaValue call() { 51 | return LuaValue.valueOf(this.channel.getName()); 52 | } 53 | } 54 | 55 | private static class GetAuthorIdFunction extends ZeroArgFunction { 56 | private final User user; 57 | 58 | public GetAuthorIdFunction(User user) { 59 | this.user = user; 60 | } 61 | 62 | @Override 63 | public LuaValue call() { 64 | return LuaValue.valueOf(this.user.getId()); 65 | } 66 | } 67 | 68 | private static class GetArgumentsFunction extends ZeroArgFunction { 69 | private final LuaValue args; 70 | 71 | public GetArgumentsFunction(String[] args) { 72 | LuaValue[] argList = new LuaValue[args.length]; 73 | for (int i = 0; i < args.length; i++) { 74 | argList[i] = LuaValue.valueOf(args[i]); 75 | } 76 | this.args = LuaValue.listOf(argList); 77 | } 78 | 79 | @Override 80 | public LuaValue call() { 81 | return this.args; 82 | } 83 | } 84 | 85 | private static class IsChannelNSFWFunction extends ZeroArgFunction { 86 | private final TextChannel channel; 87 | 88 | public IsChannelNSFWFunction(TextChannel channel) { 89 | this.channel = channel; 90 | } 91 | 92 | @Override 93 | public LuaValue call() { 94 | return this.channel.isNSFW() ? LuaValue.TRUE : LuaValue.FALSE; 95 | } 96 | } 97 | 98 | public static Globals createRunGlobals(MessageReceivedEvent event) { 99 | Globals m_globals = JsePlatform.standardGlobals(); 100 | LuaValue discord = new LuaTable(); 101 | 102 | m_globals.set("discord", discord); 103 | discord.set("print", new PrintFunction(event.getChannel())); 104 | discord.set("getChannelId", new GetChannelIdFunction(event.getChannel())); 105 | discord.set("getChannelName", new GetChannelNameFunction(event.getChannel())); 106 | discord.set("getAuthorId", new GetAuthorIdFunction(event.getAuthor())); 107 | discord.set("isChannelNSFW", new IsChannelNSFWFunction(event.getChannel().asTextChannel())); 108 | if (event.getMessage().getContentRaw().split(" ", 2).length > 1) { 109 | discord.set("getArguments", 110 | new GetArgumentsFunction(event.getMessage().getContentRaw().split(" ", 2)[1].split(" "))); 111 | } 112 | else { 113 | discord.set("getArguments", new GetArgumentsFunction(new String[0])); 114 | } 115 | return m_globals; 116 | } 117 | 118 | public static Globals createCommandGlobals(MessageReceivedEvent event) { 119 | Globals m_globals = JsePlatform.standardGlobals(); 120 | LuaValue discord = new LuaTable(); 121 | m_globals.set("discord", discord); 122 | discord.set("print", new PrintFunction(event.getChannel())); 123 | discord.set("getChannelId", new GetChannelIdFunction(event.getChannel())); 124 | discord.set("getChannelName", new GetChannelNameFunction(event.getChannel())); 125 | discord.set("getAuthorId", new GetAuthorIdFunction(event.getAuthor())); 126 | discord.set("isChannelNSFW", new IsChannelNSFWFunction(event.getChannel().asTextChannel())); 127 | if (event.getMessage().getContentRaw().toLowerCase().substring(2).split(" ", 2).length > 1) { 128 | discord.set("getArguments", new GetArgumentsFunction( 129 | event.getMessage().getContentRaw().substring(2).split(" ", 2)[1].split(" "))); 130 | } 131 | else { 132 | discord.set("getArguments", new GetArgumentsFunction(new String[0])); 133 | } 134 | return m_globals; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/slashs/SetLogChannel.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.slashs; 2 | 3 | import java.sql.ResultSet; 4 | 5 | import net.dv8tion.jda.api.entities.channel.ChannelType; 6 | import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; 7 | import net.dv8tion.jda.api.interactions.InteractionContextType; 8 | import net.dv8tion.jda.api.interactions.commands.Command; 9 | import net.dv8tion.jda.api.interactions.commands.Command.Choice; 10 | import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; 11 | import net.dv8tion.jda.api.interactions.commands.OptionType; 12 | import net.dv8tion.jda.api.interactions.commands.build.CommandData; 13 | import net.dv8tion.jda.api.interactions.commands.build.Commands; 14 | import net.dv8tion.jda.api.interactions.commands.build.OptionData; 15 | import slaynash.lum.bot.ConfigManager; 16 | import slaynash.lum.bot.DBConnectionManagerLum; 17 | import slaynash.lum.bot.utils.ExceptionUtils; 18 | 19 | public class SetLogChannel extends Slash { 20 | final OptionData optionType = new OptionData(OptionType.STRING, "type", "Log Type, Don't set this to print current log settings", false).addChoices( 21 | // value is the SQL column name 22 | new Command.Choice("MelonLogs", "melon"), 23 | new Command.Choice("ScamShield", "scam"), 24 | new Command.Choice("Message", "message"), 25 | new Command.Choice("Kick", "kick"), 26 | new Command.Choice("Ban", "ban"), 27 | new Command.Choice("Join/Leave", "joins"), 28 | new Command.Choice("Replies", "reply"), 29 | new Command.Choice("Roles from reaction listener", "role"), 30 | new Command.Choice("Users", "users") 31 | ); 32 | @Override 33 | protected CommandData globalSlashData() { 34 | return Commands.slash("log", "Set channel to place moderation logs - Admins only").setDefaultPermissions(DefaultMemberPermissions.DISABLED).setContexts(InteractionContextType.GUILD) 35 | .addOptions(optionType); 36 | } 37 | 38 | @Override 39 | public void slashRun(SlashCommandInteractionEvent event) { 40 | if (event.getChannelType() == ChannelType.PRIVATE) { 41 | event.reply("This command does not work in DMs").setEphemeral(true).queue(); 42 | } 43 | else if (!ConfigManager.mainBot) { 44 | event.reply("This command is not available on backup bot, please wait for main bot to come back online.").setEphemeral(true).queue(); 45 | } 46 | else if (!event.getGuildChannel().canTalk()) { 47 | event.reply("Lum can not talk in this channel").setEphemeral(true).queue(); 48 | } 49 | else if (!event.getGuild().getSelfMember().hasPermission(event.getGuildChannel(), net.dv8tion.jda.api.Permission.MESSAGE_EMBED_LINKS)) { 50 | event.reply("Lum does not have " + net.dv8tion.jda.api.Permission.MESSAGE_EMBED_LINKS.getName() + " permission").setEphemeral(true).queue(); 51 | } 52 | else try { 53 | String guildID = event.getGuild().getId(); 54 | String channelID = event.getChannel().getId(); 55 | ResultSet rs = DBConnectionManagerLum.sendRequest("SELECT * FROM `LogChannel` WHERE GuildID = ?", guildID); 56 | if (rs.next()) { 57 | if (event.getOptions().isEmpty()) { 58 | StringBuilder logs = new StringBuilder("Moderation logs are set in this channel for:\n"); 59 | for (Choice types : optionType.getChoices()) { 60 | long channel = rs.getLong(types.getAsString()); 61 | logs.append("- ").append(types.getName()).append(" - ").append(channel == 0 ? "Not Set" : "<#" + channel + '>').append("\n"); 62 | } 63 | event.reply(logs.toString()).queue(); 64 | } 65 | else { 66 | String type = event.getOptions().get(0).getAsString(); 67 | long current = rs.getLong(type); 68 | boolean isCurrent = current == event.getChannel().getIdLong(); 69 | DBConnectionManagerLum.sendUpdate("UPDATE `LogChannel` SET `" + type + "`=?,`user`=? WHERE GuildID = ?", isCurrent ? null : channelID, event.getUser().getIdLong(), guildID); 70 | event.reply("Moderation logs is now " + (isCurrent ? "disabled" : "enabled") + " in this channel for " + type).queue(); 71 | } 72 | } 73 | else { 74 | if (event.getOptions().isEmpty()) { 75 | event.reply("There is no log channels set for this server. Rerun this command this command with an option to set ").queue(); 76 | return; 77 | } 78 | else { 79 | String type = event.getOptions().get(0).getAsString(); 80 | DBConnectionManagerLum.sendUpdate("INSERT INTO `LogChannel` (`GuildID`, `" + type + "`, `user`) VALUES (?, ?, ?)", guildID, channelID, event.getUser().getIdLong()); 81 | event.reply("Moderation logs is now enabled in this channel for " + type).queue(); 82 | } 83 | } 84 | DBConnectionManagerLum.closeRequest(rs); 85 | } 86 | catch (Exception e) { 87 | ExceptionUtils.reportException("Failed to set log channel", e, event.getChannel()); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/DBConnectionManagerShortUrls.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot; 2 | 3 | import java.sql.Connection; 4 | import java.sql.DriverManager; 5 | import java.sql.PreparedStatement; 6 | import java.sql.ResultSet; 7 | import java.sql.SQLException; 8 | import java.sql.Types; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import java.util.Objects; 12 | import java.util.concurrent.atomic.AtomicInteger; 13 | 14 | import slaynash.lum.bot.utils.ExceptionUtils; 15 | 16 | public final class DBConnectionManagerShortUrls { 17 | public static final String LOG_IDENTIFIER = "DBSHORT"; 18 | 19 | private DBConnectionManagerShortUrls() { 20 | } 21 | 22 | private static final int DATABASE_TIMEOUT = 10; // in seconds 23 | 24 | private static Connection connection; 25 | private static final Map requests = new HashMap<>(); 26 | private static final AtomicInteger requestCount = new AtomicInteger(0); 27 | private static final AtomicInteger updateCount = new AtomicInteger(0); 28 | private static final AtomicInteger requestClosedCount = new AtomicInteger(0); 29 | private static final AtomicInteger updateClosedCount = new AtomicInteger(0); 30 | 31 | public static void init() { 32 | try { 33 | System.out.println("Connecting to Database..."); 34 | if (ConfigManager.dbAddress.isBlank() || ConfigManager.dbPort.isBlank() || ConfigManager.dbDatabaseShortURL.isBlank() || ConfigManager.dbLogin.isBlank() || ConfigManager.dbPassword.isBlank()) { 35 | System.err.println("Database configuration is missing!"); 36 | return; 37 | } 38 | DriverManager.setLoginTimeout(DATABASE_TIMEOUT); 39 | connection = DriverManager.getConnection("jdbc:mysql://" + ConfigManager.dbAddress + ":" + ConfigManager.dbPort + "/" + ConfigManager.dbDatabaseShortURL, ConfigManager.dbLogin, ConfigManager.dbPassword); 40 | System.out.println("Connection to Database initialised"); 41 | } 42 | catch (Exception e) { 43 | ExceptionUtils.reportException("Failed to contact database", e); 44 | } 45 | } 46 | 47 | private static Connection getConnection() { 48 | try { 49 | if (connection == null || !connection.isValid(DATABASE_TIMEOUT)) { 50 | if (connection != null && !connection.isClosed()) connection.close(); 51 | init(); 52 | } 53 | return connection; 54 | } 55 | catch (SQLException e) { 56 | ExceptionUtils.reportException("Failed to get database connection", e); 57 | } 58 | return null; 59 | } 60 | 61 | public static PreparedStatement formatStatement(String statement, Object... args) throws SQLException { 62 | PreparedStatement ps = Objects.requireNonNull(getConnection()).prepareStatement(statement); 63 | for (int i = 0; i < args.length; i++) { 64 | if (args[i] == null) 65 | ps.setNull(i + 1, Types.VARCHAR); 66 | else if (args[i].getClass() == String.class) 67 | ps.setString(i + 1, (String) args[i]); 68 | else if (args[i].getClass() == Integer.class) 69 | ps.setInt(i + 1, (int) args[i]); 70 | else if (args[i].getClass() == Boolean.class) 71 | ps.setBoolean(i + 1, (boolean) args[i]); 72 | else if (args[i].getClass() == Long.class) 73 | ps.setLong(i + 1, (long) args[i]); 74 | else throw new IllegalArgumentException("Trying to initialise request with unknown arg type " + args[i].getClass() + "(arg number " + i + ")"); 75 | } 76 | return ps; 77 | } 78 | 79 | /** SELECT, CALL 80 | * 81 | * @param statement The SQL statement to execute 82 | * @param args Replacements for the ? in the statement 83 | * @return ResultSet 84 | */ 85 | public static ResultSet sendRequest(String statement, Object... args) throws SQLException { 86 | requestCount.incrementAndGet(); 87 | PreparedStatement ps = formatStatement(statement, args); 88 | ResultSet rs = ps.executeQuery(); 89 | synchronized (requests) { 90 | requests.put(rs, ps); 91 | } 92 | return rs; 93 | } 94 | 95 | /** INSERT, UPDATE or DELETE 96 | * 97 | * @param statement The SQL statement to execute 98 | * @param args Replacements for the ? in the statement 99 | * @return Number of Rows affected 100 | */ 101 | public static int sendUpdate(String statement, Object... args) throws SQLException { 102 | updateCount.incrementAndGet(); 103 | PreparedStatement ps = formatStatement(statement, args); 104 | int r = ps.executeUpdate(); 105 | ps.close(); 106 | updateClosedCount.incrementAndGet(); 107 | return r; 108 | } 109 | 110 | public static void closeRequest(ResultSet resultSet) throws SQLException { 111 | PreparedStatement ps; 112 | synchronized (requests) { 113 | ps = requests.remove(resultSet); 114 | } 115 | resultSet.close(); 116 | if (ps != null) { 117 | ps.close(); 118 | requestClosedCount.incrementAndGet(); 119 | } 120 | } 121 | 122 | public static int getRequestCount() { 123 | return requestCount.get(); 124 | } 125 | 126 | public static int getUpdateCount() { 127 | return updateCount.get(); 128 | } 129 | 130 | public static int getRequestClosedCount() { 131 | return requestClosedCount.get(); 132 | } 133 | 134 | public static int getUpdateClosedCount() { 135 | return updateClosedCount.get(); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/VRCApiVersionScanner.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord; 2 | 3 | import java.net.ConnectException; 4 | import java.net.URI; 5 | import java.net.http.HttpRequest; 6 | import java.net.http.HttpResponse; 7 | import java.sql.ResultSet; 8 | import java.sql.SQLException; 9 | import java.time.Duration; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | import net.dv8tion.jda.api.EmbedBuilder; 14 | import net.dv8tion.jda.api.Permission; 15 | import net.dv8tion.jda.api.entities.Guild; 16 | import net.dv8tion.jda.api.entities.MessageEmbed; 17 | import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; 18 | import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; 19 | import slaynash.lum.bot.DBConnectionManagerLum; 20 | import slaynash.lum.bot.utils.ExceptionUtils; 21 | import slaynash.lum.bot.utils.Utils; 22 | 23 | public class VRCApiVersionScanner { 24 | 25 | private static String secondLastBVT, lastBVT, lastDG; 26 | 27 | public static void init() { 28 | Thread t = new Thread(() -> { 29 | 30 | HttpRequest request = HttpRequest.newBuilder() 31 | .GET() 32 | .uri(URI.create("https://api.vrchat.cloud/api/1/config")) 33 | .setHeader("User-Agent", "LUM Bot (https://discord.gg/akFkAG2)") 34 | .timeout(Duration.ofSeconds(30)) 35 | .build(); 36 | 37 | while (true) { 38 | try { 39 | HttpResponse response = Utils.downloadRequest(request, "VRChat API"); 40 | 41 | String newBVT = response.headers().firstValue("x-vrc-api-version").orElse(null); 42 | String newDG = response.headers().firstValue("x-vrc-api-group").orElse(null); 43 | 44 | 45 | if (lastBVT == null) { 46 | secondLastBVT = "nyan"; 47 | lastBVT = newBVT; 48 | lastDG = newDG; 49 | } 50 | else if (!lastBVT.equals(newBVT)) { 51 | System.out.println("VRCAPI: " + newDG + "-" + newBVT); 52 | 53 | EmbedBuilder eb = new EmbedBuilder(); 54 | eb.setTitle("VRCAPI Updated"); 55 | eb.setUrl("https://api.vrchat.cloud/api/1/config"); 56 | eb.addField("Old Build Version Tag", "[" + lastDG + "] " + lastBVT, false); 57 | eb.addField("New Build Version Tag", "[" + newDG + "] " + newBVT, false); 58 | if (lastDG.equals(newDG)) 59 | eb.addField("WTF VRChat <:latina_pout:828090216732295228>", "Reusing Deployment Groups I see", false); 60 | else if (newBVT.equals(secondLastBVT)) { 61 | eb.setTitle("VRCAPI Downgraded"); 62 | eb.addField("<:Neko_TeHe:865328470685909033>", "I see you fucked up VRChat and need to undo your mess.", false); 63 | } 64 | MessageEmbed embed = eb.build(); 65 | 66 | secondLastBVT = lastBVT; 67 | lastBVT = newBVT; 68 | lastDG = newDG; 69 | 70 | if (!JDAManager.isEventsEnabled()) 71 | continue; 72 | 73 | List channels = new ArrayList<>(); 74 | try { 75 | ResultSet rs = DBConnectionManagerLum.sendRequest("SELECT * FROM `GuildConfigurations` WHERE VRCAPI IS NOT NULL"); 76 | while (rs.next()) { 77 | channels.add(new ServerChannel(rs.getString("GuildID"), rs.getString("VRCAPI"))); 78 | } 79 | DBConnectionManagerLum.closeRequest(rs); 80 | } 81 | catch (SQLException e) { 82 | ExceptionUtils.reportException("Failed to fetch VRCAPI channels", e); 83 | continue; 84 | } 85 | 86 | for (ServerChannel channel : channels) { 87 | Guild guild = JDAManager.getJDA().getGuildById(channel.serverID()); 88 | if (guild == null) 89 | continue; 90 | MessageChannel tc = (MessageChannel) JDAManager.getJDA().getGuildChannelById(channel.channelId()); 91 | if (tc == null) 92 | continue; 93 | if (tc.canTalk()) { 94 | if (guild.getSelfMember().hasPermission((GuildChannel) tc, Permission.MESSAGE_EMBED_LINKS)) 95 | tc.sendMessageEmbeds(embed).queue(); 96 | else 97 | tc.sendMessage("Gibme embed perms").queue(); 98 | } 99 | } 100 | } 101 | } 102 | catch (ConnectException e) { 103 | System.out.println("VRCAPI: " + e.getMessage()); 104 | } 105 | catch (Exception e) { 106 | ExceptionUtils.reportException("Failed to fetch VRCAPI:", e); 107 | } 108 | 109 | try { 110 | Thread.sleep(45 * 1000); 111 | } 112 | catch (Exception ignored) { } 113 | } 114 | 115 | }, "VRCApiVersionScanner"); 116 | t.setDaemon(true); 117 | t.start(); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/commands/Ban.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord.commands; 2 | 3 | import java.util.Collections; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | import net.dv8tion.jda.api.Permission; 7 | import net.dv8tion.jda.api.entities.Member; 8 | import net.dv8tion.jda.api.entities.Message; 9 | import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion; 10 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 11 | import slaynash.lum.bot.ConfigManager; 12 | import slaynash.lum.bot.discord.Command; 13 | import slaynash.lum.bot.discord.CommandManager; 14 | import slaynash.lum.bot.utils.Utils; 15 | 16 | public class Ban extends Command { 17 | //TODO DM banned user and better perms, maybe ban via regex 18 | @Override 19 | protected void onServer(String paramString, MessageReceivedEvent event) { 20 | if (!includeInHelp(event)) 21 | return; 22 | 23 | int delDays = 0; 24 | Member banMember; 25 | Message replied = event.getMessage().getReferencedMessage(); 26 | String reason = ""; 27 | if (replied != null) { 28 | String[] parts = paramString.split(" ", 3); 29 | if (parts.length > 1) { 30 | if (parts[1].matches("^\\d{1,2}$")) { //l!ban 9 reason 31 | delDays = Math.min(Integer.parseInt(parts[1]), 7); 32 | if (parts.length > 2) 33 | reason = parts[2]; 34 | } 35 | else { //l!ban reason 36 | if (parts.length == 2) 37 | reason = parts[1]; 38 | else 39 | reason = parts[1] + " " + parts[2]; 40 | } 41 | } 42 | banMember = replied.getMember(); 43 | } 44 | else { 45 | String[] parts = paramString.split(" ", 4); 46 | if (parts.length < 2) { 47 | event.getMessage().reply("Usage: reply to user or " + ConfigManager.discordPrefix + getName() + " (purge days) (reason)").queue(); 48 | return; 49 | } 50 | if (parts.length == 3) { 51 | if (parts[2].matches("^\\d{1,2}$")) { //l!ban ID 9 reason 52 | delDays = Math.min(Integer.parseInt(parts[2]), 7); 53 | } 54 | else { 55 | reason = parts[2]; 56 | } 57 | } 58 | else if (parts.length > 3) { 59 | if (parts[2].matches("^\\d{1,2}$")) { //l!ban ID 9 reason 60 | delDays = Math.min(Integer.parseInt(parts[2]), 7); 61 | reason = parts[3]; 62 | } 63 | else { //l!ban ID reason 64 | reason = parts[2] + " " + parts[3]; 65 | } 66 | } 67 | try { 68 | banMember = event.getGuild().getMemberById(parts[1]); 69 | } 70 | catch (Exception e) { 71 | event.getMessage().reply("Invalid snowflake, User was not found!").queue(); 72 | return; 73 | } 74 | } 75 | 76 | if (banMember == null) { 77 | event.getMessage().reply("User was not found!").queue(); 78 | return; 79 | } 80 | 81 | if (banMember.equals(event.getGuild().getSelfMember())) { 82 | event.getMessage().reply("You don't really want to ban me... Right? ").queue(); 83 | return; 84 | } 85 | 86 | if (banMember.equals(event.getMember())) { 87 | event.getMessage().reply("As much as want to ban you for everything you have done to me, I unfortunately can't ban you <:NotHuTao:828069127478181888>").queue(); 88 | return; 89 | } 90 | 91 | if (!event.getGuild().getSelfMember().canInteract(banMember)) { 92 | event.getMessage().reply("Can not ban " + banMember.getUser().getAsMention() + "(" + banMember.getId() + ") because they are a higher role than my role").setAllowedMentions(Collections.emptyList()).queue(); 93 | return; 94 | } 95 | 96 | if (reason.isBlank()) 97 | banMember.ban(delDays, TimeUnit.DAYS).reason("Banned by " + event.getAuthor().getName()).queue(); 98 | else 99 | banMember.ban(delDays, TimeUnit.DAYS).reason(event.getAuthor().getName() + " - " + reason).queue(); //reason limit is 512 chars 100 | 101 | MessageChannelUnion reportChannel = CommandManager.getModReportChannels(event, "ban"); 102 | if (reportChannel != null && !reportChannel.equals(event.getChannel())) 103 | Utils.sendMessage("User " + banMember.getUser().getAsMention() + "(" + banMember.getId() + ") has been banned by " + event.getMember().getEffectiveName() + "!\n" + reason, reportChannel); 104 | event.getChannel().sendMessage("User " + banMember.getUser().getAsMention() + "(" + banMember.getId() + ") has been banned!\n" + reason).queue(); 105 | } 106 | 107 | @Override 108 | protected boolean matchPattern(String paramString) { 109 | return paramString.startsWith(ConfigManager.discordPrefix + getName()); 110 | } 111 | 112 | @Override 113 | public boolean includeInHelp(MessageReceivedEvent event) { 114 | return event.getGuild().getSelfMember().hasPermission(Permission.BAN_MEMBERS) && event.getMember().hasPermission(Permission.BAN_MEMBERS); 115 | } 116 | 117 | @Override 118 | public String getHelpDescription() { 119 | return "Bans a member. Reply or UserID with optional purge days - Staff only"; 120 | } 121 | 122 | @Override 123 | public String getName() { 124 | return "ban"; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/slaynash/lum/bot/discord/Memes.java: -------------------------------------------------------------------------------- 1 | package slaynash.lum.bot.discord; 2 | 3 | import java.sql.ResultSet; 4 | import java.util.List; 5 | 6 | import net.dv8tion.jda.api.Permission; 7 | import net.dv8tion.jda.api.entities.Message; 8 | import net.dv8tion.jda.api.entities.Message.Attachment; 9 | import net.dv8tion.jda.api.entities.User; 10 | import net.dv8tion.jda.api.entities.channel.ChannelType; 11 | import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; 12 | import net.dv8tion.jda.api.entities.emoji.CustomEmoji; 13 | import net.dv8tion.jda.api.entities.emoji.Emoji; 14 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 15 | import net.dv8tion.jda.api.events.message.react.GenericMessageReactionEvent; 16 | import net.dv8tion.jda.api.requests.restaction.MessageCreateAction; 17 | import net.dv8tion.jda.api.utils.FileUpload; 18 | import slaynash.lum.bot.DBConnectionManagerLum; 19 | import slaynash.lum.bot.utils.ExceptionUtils; 20 | 21 | public class Memes { 22 | private static final float removalPercent = 0.2f; 23 | private static final int minVotes = 5; 24 | private static final CustomEmoji upArrow = Emoji.fromCustom("UNOUpVote", 936073450815111208L, false); 25 | private static final CustomEmoji downArrow = Emoji.fromCustom("UNODownVote", 936073450676703282L, false); 26 | 27 | public static boolean memeRecieved(MessageReceivedEvent event) { 28 | MessageChannel channel = event.getChannel(); 29 | if (event.getAuthor().isBot()) return false; 30 | try { 31 | ResultSet rs = DBConnectionManagerLum.sendRequest("SELECT `ReportChannel` FROM `Memes` WHERE MemeChannel = ?", channel.getIdLong()); 32 | if (rs.next()) { 33 | if (!event.getGuild().getSelfMember().hasPermission(event.getGuildChannel(), Permission.MESSAGE_ADD_REACTION, Permission.MESSAGE_EXT_EMOJI)) { 34 | if (channel.getType() == ChannelType.GUILD_PUBLIC_THREAD || channel.getType() == ChannelType.GUILD_PRIVATE_THREAD ? event.getGuild().getSelfMember().hasPermission(event.getGuildChannel(), Permission.MESSAGE_SEND_IN_THREADS) : event.getGuild().getSelfMember().hasPermission(event.getGuildChannel(), Permission.MESSAGE_SEND)) 35 | channel.sendMessage("I need the `MESSAGE_ADD_REACTION` and `MESSAGE_EXT_EMOJI` permissions to moderate memes").queue(); 36 | return false; 37 | } 38 | event.getMessage().addReaction(upArrow).queue(c -> event.getMessage().addReaction(downArrow).queue()); 39 | DBConnectionManagerLum.closeRequest(rs); 40 | return true; 41 | } 42 | DBConnectionManagerLum.closeRequest(rs); 43 | } 44 | catch (Exception e) { 45 | ExceptionUtils.reportException("Failed to check if channel is meme channel", e); 46 | } 47 | return false; 48 | } 49 | 50 | public static void memeReaction(GenericMessageReactionEvent event) { 51 | if (event.getUser().isBot()) return; 52 | if (event.getMember().hasPermission(Permission.MESSAGE_MANAGE)) return; 53 | long memeReportChannelID; 54 | try { 55 | ResultSet rs = DBConnectionManagerLum.sendRequest("SELECT `ReportChannel` FROM `Memes` WHERE MemeChannel = ?", event.getChannel().getIdLong()); 56 | if (rs.next()) { 57 | memeReportChannelID = rs.getLong("ReportChannel"); 58 | } 59 | else return; 60 | } 61 | catch (Exception e) { 62 | ExceptionUtils.reportException("Failed to check if channel is a meme channel", e); 63 | return; 64 | } 65 | Message message = event.retrieveMessage().complete(); 66 | if (message == null) return; 67 | List upReactions = message.retrieveReactionUsers(upArrow).complete(); 68 | List downReactions = message.retrieveReactionUsers(downArrow).complete(); 69 | upReactions.remove(event.getJDA().getSelfUser()); 70 | downReactions.remove(event.getJDA().getSelfUser()); 71 | // ignore og author 72 | upReactions.remove(message.getAuthor()); 73 | downReactions.remove(message.getAuthor()); 74 | 75 | if (upReactions.size() + downReactions.size() < minVotes) return; 76 | 77 | float reactionVotes = getPercentage(upReactions.size(), downReactions.size()); 78 | 79 | if (reactionVotes < removalPercent) { 80 | if (memeReportChannelID != 0) { 81 | StringBuilder sb = new StringBuilder(); 82 | sb.append(message.getAuthor().getName()).append(" ").append(message.getAuthor().getId()).append("\n"); 83 | sb.append(message.getContentRaw()).append("\n"); 84 | if (!upReactions.isEmpty()) 85 | sb.append("\nup voted:\n").append(upReactions).append("\n"); 86 | sb.append("\ndown voted:\n").append(downReactions).append("\n"); 87 | MessageCreateAction ma = message.getGuild().getTextChannelById(memeReportChannelID).sendMessage(sb.toString()); 88 | for (Attachment attach : message.getAttachments()) { 89 | try { 90 | ma.addFiles(FileUpload.fromData(attach.getProxy().download().get(), attach.getFileName()).asSpoiler()); 91 | } 92 | catch (Exception e) { 93 | ExceptionUtils.reportException("Failed reattaching meme", e); 94 | } 95 | } 96 | ma.queue(); 97 | } 98 | message.delete().reason("Meme was voted out").queue(); 99 | } 100 | } 101 | 102 | private static float getPercentage(int up, int down) { 103 | return (float) up / (up + down); 104 | } 105 | } 106 | --------------------------------------------------------------------------------