diff --git a/plugins/CopyUrlInsteadOfShare/README.md b/plugins/CopyUrlInsteadOfShare/README.md new file mode 100755 index 0000000..edfc3b9 --- /dev/null +++ b/plugins/CopyUrlInsteadOfShare/README.md @@ -0,0 +1,2 @@ +# Replaces the share message function with a copy url one + diff --git a/plugins/CopyUrlInsteadOfShare/build.gradle.kts b/plugins/CopyUrlInsteadOfShare/build.gradle.kts new file mode 100755 index 0000000..c934edc --- /dev/null +++ b/plugins/CopyUrlInsteadOfShare/build.gradle.kts @@ -0,0 +1,2 @@ +version = "1.1.1" +description = "Replaces share message function with a copy url one" diff --git a/plugins/CopyUrlInsteadOfShare/src/main/AndroidManifest.xml b/plugins/CopyUrlInsteadOfShare/src/main/AndroidManifest.xml new file mode 100755 index 0000000..e54b5d9 --- /dev/null +++ b/plugins/CopyUrlInsteadOfShare/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/plugins/CopyUrlInsteadOfShare/src/main/java/dev/nope/plugins/copyurlinsteadofshare/CopyUrlInsteadOfShareMessages.kt b/plugins/CopyUrlInsteadOfShare/src/main/java/dev/nope/plugins/copyurlinsteadofshare/CopyUrlInsteadOfShareMessages.kt new file mode 100755 index 0000000..9f2dc4c --- /dev/null +++ b/plugins/CopyUrlInsteadOfShare/src/main/java/dev/nope/plugins/copyurlinsteadofshare/CopyUrlInsteadOfShareMessages.kt @@ -0,0 +1,131 @@ +package dev.nope.plugins.copyurlinsteadofshare + +import android.annotation.SuppressLint +import android.content.Context +import android.os.Bundle +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.content.ContextCompat +import androidx.core.content.res.ResourcesCompat +import androidx.core.widget.NestedScrollView +import com.aliucord.Constants +import com.aliucord.Utils +import com.aliucord.Utils.showToast +import com.aliucord.annotations.AliucordPlugin +import com.aliucord.entities.Plugin +import com.aliucord.patcher.Hook +import com.discord.app.AppBottomSheet +import com.discord.databinding.WidgetChatListActionsBinding +import com.discord.utilities.color.ColorCompat +import com.discord.widgets.chat.list.actions.WidgetChatListActions +import com.lytefast.flexinput.R +import java.lang.reflect.InvocationTargetException + + +@AliucordPlugin(requiresRestart = false) +class MessageLinkContext : Plugin() { + + /* No settings tab for you because Cannot access 'com.discord.app.AppLogger$a' which is a supertype of 'dev.nope.plugins.copyurlinsteadofshare.HelpMeeee'. Check your module classpath for missing or conflicting dependencies + init { + settingsTab = SettingsTab( + Halp::class.java, + SettingsTab.Type.BOTTOM_SHEET + ).withArgs(settings) + } + */ + + @SuppressLint("SetTextI18n") + override fun start(context: Context) { + + val icon = ContextCompat.getDrawable(context, R.e.ic_diag_link_24dp)!! + .mutate() + + val copyMessageUrlViewId = View.generateViewId() + + with(WidgetChatListActions::class.java) { + val getBinding = getDeclaredMethod("getBinding").apply { isAccessible = true } + + val replaceShare = settings.getBool("replaceShare", true) + + patcher.patch( //creating the option + getDeclaredMethod("onViewCreated", View::class.java, Bundle::class.java), + Hook { callFrame -> + val shareMessagesViewId = Utils.getResId("dialog_chat_actions_share", "id") + val binding = + getBinding.invoke(callFrame.thisObject) as WidgetChatListActionsBinding + val shareMessageView = + binding.a.findViewById(shareMessagesViewId).apply { + visibility = View.VISIBLE + } + val linearLayout = + (callFrame.args[0] as NestedScrollView).getChildAt(0) as LinearLayout + val ctx = linearLayout.context + icon.setTint(ColorCompat.getThemedColor(ctx, R.b.colorInteractiveNormal)) + val copyMessageUrl = + TextView(ctx, null, 0, R.i.UiKit_Settings_Item_Icon).apply { + + text = ctx.getString(R.h.copy_link) + id = copyMessageUrlViewId + typeface = ResourcesCompat.getFont(ctx, Constants.Fonts.whitney_medium) + setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null) + } + var replacementId = linearLayout.indexOfChild(shareMessageView) + linearLayout.removeView(shareMessageView) // poof + + linearLayout.addView( + copyMessageUrl, + replacementId + ) + }) + + patcher.patch( //setting onClickListener + getDeclaredMethod("configureUI", WidgetChatListActions.Model::class.java), + Hook { callFrame -> + try { + val binding = + getBinding.invoke(callFrame.thisObject) as WidgetChatListActionsBinding + val shareMessageView = + binding.a.findViewById(copyMessageUrlViewId).apply { + visibility = View.VISIBLE + } + + shareMessageView.setOnClickListener { + try { + val msg = (callFrame.args[0] as WidgetChatListActions.Model).message + val guild = + (callFrame.args[0] as WidgetChatListActions.Model).guild // because msg.guildId is null + val messageUri = String.format( + "https://discord.com/channels/%s/%s/%s", + try { + guild.id + } catch (e: Throwable) { // for DMs + "@me" + }, + msg.channelId, + msg.id + ) + Utils.setClipboard( + "message link", + messageUri + ) + showToast("Copied url", showLonger = false) + + val bottomSheetDismisser = + AppBottomSheet::class.java.getDeclaredMethod("dismiss") // because cannot access shit again + bottomSheetDismisser.invoke((callFrame.thisObject as WidgetChatListActions)) + } catch (e: IllegalAccessException) { + e.printStackTrace() + } catch (e: InvocationTargetException) { + e.printStackTrace() + } + } + } catch (e: Exception) { //yes generic maybe works idk + e.printStackTrace() + } + }) + } + } + + override fun stop(context: Context) = patcher.unpatchAll() +} diff --git a/plugins/Find/README.md b/plugins/Find/README.md new file mode 100755 index 0000000..c324813 --- /dev/null +++ b/plugins/Find/README.md @@ -0,0 +1,5 @@ +# Find + +A slashcommand to find what the ids you give are related to +Usage: /find id1,id2,id3... +Will not find messages or uncached servers/channels/users diff --git a/plugins/Find/build.gradle.kts b/plugins/Find/build.gradle.kts new file mode 100755 index 0000000..adf424c --- /dev/null +++ b/plugins/Find/build.gradle.kts @@ -0,0 +1,2 @@ +version = "1.0.4" +description = "/find command" diff --git a/plugins/Find/src/main/AndroidManifest.xml b/plugins/Find/src/main/AndroidManifest.xml new file mode 100755 index 0000000..72eebf8 --- /dev/null +++ b/plugins/Find/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/plugins/Find/src/main/java/dev/nope/plugins/find/Find.kt b/plugins/Find/src/main/java/dev/nope/plugins/find/Find.kt new file mode 100755 index 0000000..ae88902 --- /dev/null +++ b/plugins/Find/src/main/java/dev/nope/plugins/find/Find.kt @@ -0,0 +1,147 @@ +package dev.nope.plugins.find + +import android.content.Context +import com.aliucord.Http +import com.aliucord.Utils +import com.aliucord.annotations.AliucordPlugin +import com.aliucord.api.CommandsAPI.CommandResult +import com.aliucord.entities.Plugin +import com.aliucord.utils.RxUtils.await +import com.aliucord.wrappers.ChannelWrapper.Companion.guildId +import com.aliucord.wrappers.ChannelWrapper.Companion.parentId +import com.discord.api.commands.ApplicationCommandType +import com.discord.models.user.User +import com.discord.stores.StoreStream +import com.discord.utilities.rest.RestAPI +import com.discord.api.user.UserProfile +import com.discord.models.user.CoreUser + + +@AliucordPlugin(requiresRestart = false) +class Find : Plugin() { + + private fun timestampToUnixTime(x: Long): Long { + val discordEpoch = 1420070400000 + val dateBits = x shr 22 + val unixTimes1000 = (dateBits + discordEpoch) + return unixTimes1000 / 1000 + } + + override fun start(context: Context) { + + commands.registerCommand( + "find", "Tries fo find what a timestamp or a list of timestamps refers to", listOf( + Utils.createCommandOption( + ApplicationCommandType.STRING, + "timestampList", + "Timestamps you want answers on. Separate with simple spaces.", + null, + required = true, + default = true + ) + ) + ) { ctx -> + val ids = ctx.getRequiredString("timestampList") + val input = findStringtoList(ids) + val results: MutableMap = mutableMapOf() + + input.forEach { + StoreStream.getUsers() + Utils + val colit: Collection = listOf(it) + val tempUser: User? = StoreStream.getUsers().getUsers(colit, false)[it] + val tempChannel = StoreStream.getChannels().getChannel(it) + val tempServer = StoreStream.getGuilds().getGuild(it) + // val tempMessage = StoreStream.getMessages().getMe i need to test every channel lolilol + try { + if (tempUser?.username == null) { + if (tempChannel?.guildId == null) { + if (tempServer?.id == null) { + results.put(it, "is neither a user, a channel nor guild ID, or is not cached.") + } else { + results.put(it, "is a server.\nName: ${tempServer.name}.") + } + } else { + results.put( + it, + "is the channel: <#${it}> in category ${tempChannel.parentId} in server ${ + StoreStream.getGuilds().getGuild(tempChannel.guildId).name + }." + ) + } + } else { + results.put(it, "is the user <@$it>.") + } + } catch (throwable: Throwable) { + return@registerCommand CommandResult( + "oopsie doopsie, an error happened. Sorry ! Please check the logs !", + null, + false + ) + } + + +// welp i want to avoid making requests like that so i have to see what to do with the uncached things + if (false && results[it] == "is neither a user, a channel nor guild ID.") { //Checks if the user exists. + try { + + val directuser = RestAPI.api.userGet(it).await().first ?: return@forEach + val userinfo = + UserProfile(null, null, directuser, null, null, null, null) + val user = CoreUser(userinfo.g()) + results[it] = + "is a user that is not cached. Name: ${user.username}#${user.discriminator}, created on . Avatar id: ${user.avatar}" + + + } catch (throwable: Throwable) { + return@forEach + } + + } + } + + var finalList = "" + results.forEach { (t, u) -> finalList += "\n**$t** $u" } + + CommandResult( + finalList, + null, + false + ) + } + } + + + private fun findStringtoList(ids: String): MutableList { + val result = ids.split(" ").map { it.trim() } + val result2: MutableList = result as MutableList + val result3: MutableList = mutableListOf() + val counter = 0 + result.forEach { + try { + it.toLong().takeIf { that -> that.toString().length in 17..19 } ?: (result2.set( + result.indexOf(it), + "0" + )) + } catch (throwable: Throwable) { + result2[result.indexOf(it)] = "0" + } + } + result2.forEach { + if (it.toLong() != 0L) { + result3.add(it.toLong()) + } else { + counter + 1 + } + + } + return result3 + } + + override fun stop(context: Context) { + // Unregister our commands + commands.unregisterAll() + } +} diff --git a/plugins/MoreMoreSlashCommands/README.md b/plugins/MoreMoreSlashCommands/README.md new file mode 100755 index 0000000..426e54d --- /dev/null +++ b/plugins/MoreMoreSlashCommands/README.md @@ -0,0 +1,3 @@ +# MoreMoreSlashCommands + +Adds more useless slash commands for text editing : full width, bold, small, smaller, plus a morse encoder and decoder diff --git a/plugins/MoreMoreSlashCommands/build.gradle.kts b/plugins/MoreMoreSlashCommands/build.gradle.kts new file mode 100755 index 0000000..4e88f75 --- /dev/null +++ b/plugins/MoreMoreSlashCommands/build.gradle.kts @@ -0,0 +1,14 @@ +version = "1.0.2" +description = "Adds a few charcter modification slash commands" + +aliucord.changelog.set(""" +#1.0.2 +fixed morse translation issues + # 1.0.1 + * Now with morse, bold, and small yay + # 1.0.0 + * Initial release +""".trimIndent() + +) + diff --git a/plugins/MoreMoreSlashCommands/src/main/AndroidManifest.xml b/plugins/MoreMoreSlashCommands/src/main/AndroidManifest.xml new file mode 100755 index 0000000..d46bfb2 --- /dev/null +++ b/plugins/MoreMoreSlashCommands/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/plugins/MoreMoreSlashCommands/src/main/java/dev/nope/plugins/moremoreslashcommands/MoreMoreSlashCommands.kt b/plugins/MoreMoreSlashCommands/src/main/java/dev/nope/plugins/moremoreslashcommands/MoreMoreSlashCommands.kt new file mode 100755 index 0000000..49c2509 --- /dev/null +++ b/plugins/MoreMoreSlashCommands/src/main/java/dev/nope/plugins/moremoreslashcommands/MoreMoreSlashCommands.kt @@ -0,0 +1,407 @@ +package dev.nope.plugins.moremoreslashcommands +/* + * Copyright (c) 2022 nope and the CutTheCord project on gitdab + * Licensed under the Open Software License version 3.0 + */ + + +import android.content.Context +import android.text.style.SubscriptSpan +import com.aliucord.annotations.AliucordPlugin +import com.aliucord.api.CommandsAPI +import com.aliucord.entities.Plugin + +@AliucordPlugin +@Suppress("unused") +class MoreSlashCommands : Plugin() { + override fun start(context: Context?) { + commands.registerCommand("fw", "Makes text full width ([a-Z][0-9][!-~])", listOf(CommandsAPI.requiredMessageOption)) { ctx -> + CommandsAPI.CommandResult(fullwidthify(ctx.getRequiredString("message").trim())) + } + + commands.registerCommand("flip", "Flips characters ([a-z])", listOf(CommandsAPI.requiredMessageOption)) { ctx -> + CommandsAPI.CommandResult(flipify(ctx.getRequiredString("message").trim())) + } + commands.registerCommand("morse", "Makes your text morse([a-Z][0-9][.-=])", listOf(CommandsAPI.requiredMessageOption)) { ctx -> + CommandsAPI.CommandResult(morseify(ctx.getRequiredString("message").trim())) + } + + commands.registerCommand("unmorse", "decrypts morse text", listOf(CommandsAPI.requiredMessageOption)) { ctx -> + CommandsAPI.CommandResult(unmorseify(ctx.getRequiredString("message").trim()), null, false) + } //maybe an activable option to put it in message menu would be better ? idk. May make every single command activable + + commands.registerCommand("bolder", "Makes your text more bold ([a-Z][0-9])", listOf(CommandsAPI.requiredMessageOption)) { ctx -> + CommandsAPI.CommandResult(bolderify(ctx.getRequiredString("message").trim())) + } + + commands.registerCommand("small", "Makes your text more small ([a-z])", listOf(CommandsAPI.requiredMessageOption)) { ctx -> + CommandsAPI.CommandResult(smallify(ctx.getRequiredString("message").trim())) + } + commands.registerCommand("smaller", "Makes your text more more small ([a-z])", listOf(CommandsAPI.requiredMessageOption)) { ctx -> + CommandsAPI.CommandResult(smallerify(ctx.getRequiredString("message").trim())) + } + + + } + + + + override fun stop(context: Context?) = commands.unregisterAll() + + + private fun fullwidthify(text: String): String { + return text + .replace(" ", " ") + .replace("!", "!") + .replace("#", "#") + .replace("$", "$") + .replace("%", "%") + .replace("&", "&") + .replace("'", "'") + .replace("(", "(") + .replace(")", ")") + .replace("*", "*") + .replace("+", "+") + .replace(",", ",") + .replace("-", "-") + .replace(".", ".") + .replace("/", "/") + .replace("0", "0") + .replace("1", "1") + .replace("2", "2") + .replace("3", "3") + .replace("4", "4") + .replace("5", "5") + .replace("6", "6") + .replace("7", "7") + .replace("8", "8") + .replace("9", "9") + .replace(":", ":") + .replace(";", ";") + .replace("<", "<") + .replace("=", "=") + .replace(">", ">") + .replace("?", "?") + .replace("@", "@") + .replace("A", "A") + .replace("B", "B") + .replace("C", "C") + .replace("D", "D") + .replace("E", "E") + .replace("F", "F") + .replace("G", "G") + .replace("H", "H") + .replace("I", "I") + .replace("J", "J") + .replace("K", "K") + .replace("L", "L") + .replace("M", "M") + .replace("N", "N") + .replace("O", "O") + .replace("P", "P") + .replace("Q", "Q") + .replace("R", "R") + .replace("S", "S") + .replace("T", "T") + .replace("U", "U") + .replace("V", "V") + .replace("W", "W") + .replace("X", "X") + .replace("Y", "Y") + .replace("Z", "Z") + .replace("[", "[") + .replace("]", "]") + .replace("^", "^") + .replace("_", "_") + .replace("`", "`") + .replace("a", "a") + .replace("b", "b") + .replace("c", "c") + .replace("d", "d") + .replace("e", "e") + .replace("f", "f") + .replace("g", "g") + .replace("h", "h") + .replace("i", "i") + .replace("j", "j") + .replace("k", "k") + .replace("l", "l") + .replace("m", "m") + .replace("n", "n") + .replace("o", "o") + .replace("p", "p") + .replace("q", "q") + .replace("r", "r") + .replace("s", "s") + .replace("t", "t") + .replace("u", "u") + .replace("v", "v") + .replace("w", "w") + .replace("x", "x") + .replace("y", "y") + .replace("z", "z") + .replace("{", "{") + .replace("|", "|") + .replace("}", "}") + .replace("~", "~") + + + + } + + private fun flipify(text :String): String { + return text.toLowerCase() + + + .replace("a", "ɐ") + .replace("b", "q") + .replace("c", "ɔ") + .replace("d", "p") + .replace("e", "ǝ") + .replace("f", "ɟ") + .replace("g", "ƃ") + .replace("h", "ɥ") + .replace("i", "ı") + .replace("j", "ɾ") + .replace("k", "ʞ") + .replace("l", "ן") + .replace("m", "ɯ") + .replace("n", "u") + .replace("p", "d") + .replace("q", "b") + .replace("r", "ɹ") + .replace("t", "ʇ") + .replace("u", "n") + .replace("v", "ʌ") + .replace("w", "ʍ") + .replace("y", "ʎ") + } + + private fun morseify(text: String): String { + return text.toUpperCase() + .replace(" ", "/ ") + .replace(".", ".-.-.- ") + .replace(",", "--..-- ") + .replace(":", "---... ") + .replace("?", "..--.. ") + .replace("'", ".----. ") + // .replace("-", "-....- ") user should not use - + .replace("/", "-..-. ") + .replace("@", ".--.-. ") + .replace("=", "-...- ") + .replace("A", ".- ") + .replace("B", "-... ") + .replace("C", "-.-. ") + .replace("D", "-.. ") + .replace("E", ". ") + .replace("F", "..-. ") + .replace("G", "--. ") + .replace("H", ".... ") + .replace("I", ".. ") + .replace("J", ".--- ") + .replace("K", "-.- ") + .replace("L", ".-.. ") + .replace("M", "-- ") + .replace("N", "-. ") + .replace("O", "--- ") + .replace("P", ".--. ") + .replace("Q", "--.- ") + .replace("R", ".-. ") + .replace("S", "... ") + .replace("T", "- ") + .replace("U", "..- ") + .replace("V", "...- ") + .replace("W", ".-- ") + .replace("X", "-..- ") + .replace("Y", "-.-- ") + .replace("Z", "--.. ") + .replace("0", "----- ") + .replace("1", ".---- ") + .replace("2", "..--- ") + .replace("3", "...-- ") + .replace("4", "....- ") + .replace("5", "..... ") + .replace("6", "-.... ") + .replace("7", "--... ") + .replace("8", "---.. ") + .replace("9", "----. ") + } + + private fun unmorseify(text: String): String { + return ("$text ") + .replace("..--.. ", "?") + .replace(".-.-.- ", ".") + .replace(".--.-. ", "@") + .replace(".----. ", "'") + .replace("--..-- ", ",") + .replace("---... ", ":") + .replace("..... ", "5") + .replace("....- ", "4") + .replace("...-- ", "3") + .replace("..--- ", "2") + .replace(".---- ", "1") + .replace("-.... ", "6") + .replace("-...- ", "=") + .replace("-..-. ", "/") + .replace("--... ", "7") + .replace("---.. ", "8") + .replace("----. ", "9") + .replace("----- ", "0") + .replace(".... ", "H") + .replace("...- ", "V") + .replace("..-. ", "F") + .replace(".-.. ", "L") + .replace(".--. ", "P") + .replace(".--- ", "J") + .replace("-... ", "B") + .replace("-..- ", "X") + .replace("-.-. ", "C") + .replace("-.-- ", "Y") + .replace("--.. ", "Z") + .replace("--.- ", "Q") + .replace("... ", "S") + .replace("..- ", "U") + .replace(".-. ", "R") + .replace(".-- ", "W") + .replace("-.. ", "D") + .replace("-.- ", "K") + .replace("--. ", "G") + .replace("--- ", "O") + .replace(".. ", "I") + .replace(".- ", "A") + .replace("-. ", "N") + .replace("-- ", "M") + .replace(". ", "E") + .replace("- ", "T") + .replace("/ ", " ") + } + + private fun bolderify(text: String): String { + return text + .replace("a", "𝗮") + .replace("b", "𝗯") + .replace("c", "𝗰") + .replace("d", "𝗱") + .replace("e", "𝗲") + .replace("f", "𝗳") + .replace("g", "𝗴") + .replace("h", "𝗵") + .replace("i", "𝗶") + .replace("j", "𝗷") + .replace("k", "𝗸") + .replace("l", "𝗹") + .replace("m", "𝗺") + .replace("n", "𝗻") + .replace("o", "𝗼") + .replace("p", "𝗽") + .replace("q", "𝗾") + .replace("r", "𝗿") + .replace("s", "𝘀") + .replace("t", "𝘁") + .replace("u", "𝘂") + .replace("v", "𝘃") + .replace("w", "𝘄") + .replace("x", "𝘅") + .replace("y", "𝘆") + .replace("z", "𝘇") + .replace("A", "𝗔") + .replace("B", "𝗕") + .replace("C", "𝗖") + .replace("D", "𝗗") + .replace("E", "𝗘") + .replace("F", "𝗙") + .replace("G", "𝗚") + .replace("H", "𝗛") + .replace("I", "𝗜") + .replace("J", "𝗝") + .replace("K", "𝗞") + .replace("L", "𝗟") + .replace("M", "𝗠") + .replace("N", "𝗡") + .replace("O", "𝗢") + .replace("P", "𝗣") + .replace("Q", "𝗤") + .replace("R", "𝗥") + .replace("S", "𝗦") + .replace("T", "𝗧") + .replace("U", "𝗨") + .replace("V", "𝗩") + .replace("W", "𝗪") + .replace("X", "𝗫") + .replace("Y", "𝗬") + .replace("Z", "𝗭") + .replace("0", "𝟬") + .replace("1", "𝟭") + .replace("2", "𝟮") + .replace("3", "𝟯") + .replace("4", "𝟰") + .replace("5", "𝟱") + .replace("6", "𝟲") + .replace("7", "𝟳") + .replace("8", "𝟴") + .replace("9", "𝟵") + } + + private fun smallify(text: String): String { + return text.toLowerCase() + .replace("a", "ᴀ") + .replace("b", "ʙ") + .replace("c", "ᴄ") + .replace("d", "ᴅ") + .replace("e", "ᴇ") + .replace("f", "ꜰ") + .replace("g", "ɢ") + .replace("h", "ʜ") + .replace("i", "ɪ") + .replace("j", "ᴊ") + .replace("k", "ᴋ") + .replace("l", "ʟ") + .replace("m", "ᴍ") + .replace("n", "ɴ") + .replace("o", "ᴏ") + .replace("p", "ᴘ") + .replace("q", "ǫ") + .replace("r", "ʀ") + .replace("t", "ᴛ") + .replace("u", "ᴜ") + .replace("v", "ᴠ") + .replace("w", "ᴡ") + .replace("y", "ʏ") + .replace("z", "ᴢ") + } + + private fun smallerify(text: String): String { + return text + .replace("a", "ᵃ") + .replace("b", "ᵇ") + .replace("c", "ᶜ") + .replace("d", "ᵈ") + .replace("e", "ᵉ") + .replace("f", "ᶠ") + .replace("g", "ᵍ") + .replace("h", "ʰ") + .replace("i", "ᶦ") + .replace("j", "ʲ") + .replace("k", "ᵏ") + .replace("l", "ˡ") + .replace("m", "ᵐ") + .replace("n", "ⁿ") + .replace("o", "ᵒ") + .replace("p", "ᵖ") + .replace("q", "ᑫ") + .replace("r", "ʳ") + .replace("s", "ˢ") + .replace("t", "ᵗ") + .replace("u", "ᵘ") + .replace("v", "ᵛ") + .replace("w", "ʷ") + .replace("x", "ˣ") + .replace("y", "ʸ") + .replace("z", "ᶻ") + + + } + + + + } diff --git a/plugins/SnowflakeUtilities/README.md b/plugins/SnowflakeUtilities/README.md new file mode 100755 index 0000000..3e61196 --- /dev/null +++ b/plugins/SnowflakeUtilities/README.md @@ -0,0 +1,4 @@ +# SnowflakeUtilities + +Converts Discord snowflakes to time. + diff --git a/plugins/SnowflakeUtilities/build.gradle.kts b/plugins/SnowflakeUtilities/build.gradle.kts new file mode 100755 index 0000000..bdf6482 --- /dev/null +++ b/plugins/SnowflakeUtilities/build.gradle.kts @@ -0,0 +1,10 @@ +version = "1.0.6" +description = "Adds a /snowflake command to translate discord snowflakes to readable date and time" + +aliucord.changelog.set(""" + # 1.0.6 Changed the /timestamp to /snowflake because conflicted with existing plugin + # 1.0.4 + * Created a separate plugin for Whois +""".trimIndent() + +) diff --git a/plugins/SnowflakeUtilities/src/main/AndroidManifest.xml b/plugins/SnowflakeUtilities/src/main/AndroidManifest.xml new file mode 100755 index 0000000..77e4f0f --- /dev/null +++ b/plugins/SnowflakeUtilities/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/plugins/SnowflakeUtilities/src/main/java/dev/nope/plugins/snowflakeutilities/SnowflakeUtilities.kt b/plugins/SnowflakeUtilities/src/main/java/dev/nope/plugins/snowflakeutilities/SnowflakeUtilities.kt new file mode 100755 index 0000000..550d1b7 --- /dev/null +++ b/plugins/SnowflakeUtilities/src/main/java/dev/nope/plugins/snowflakeutilities/SnowflakeUtilities.kt @@ -0,0 +1,64 @@ +package dev.nope.plugins.snowflakeutilities + +import android.content.Context +import com.aliucord.Http +import com.aliucord.Utils +import com.aliucord.annotations.AliucordPlugin +import com.aliucord.api.CommandsAPI +import com.aliucord.api.CommandsAPI.CommandResult +import com.aliucord.entities.MessageEmbedBuilder +import com.aliucord.entities.Plugin +import com.discord.api.commands.ApplicationCommandType +import java.util.* + + +@AliucordPlugin(requiresRestart = false ) +class SnowflakeUtilities : Plugin() { + + + private fun timestampToUnixTime(x: Long): Long { + val discordEpoch = 1420070400000 + val dateBits = x shr 22 + val unixTimes1000 = (dateBits + discordEpoch) + return unixTimes1000 / 1000 + // val time = Date(unix) less precise and adapting to timezones is harder + + } + + override fun start(context: Context) { + + + commands.registerCommand( + "snowflake", "Converts discord ID to date", listOf( + Utils.createCommandOption( + ApplicationCommandType.STRING, + "snowflake", + "The snowflake to convert", + null, + required = true, + default = true + ) + ) + ) { ctx -> + val id = ctx.getRequiredString("snowflake") + val unixTime = timestampToUnixTime(id.toLong()) + + + CommandsAPI.CommandResult( + "Message/User was created on ().", + null, + false + ) + + } + + + } + + override fun stop(context: Context) { + // Unregister our commands + commands.unregisterAll() + } + + +} diff --git a/plugins/Whois/README.md b/plugins/Whois/README.md new file mode 100755 index 0000000..0424fc6 --- /dev/null +++ b/plugins/Whois/README.md @@ -0,0 +1,3 @@ +# MoreMoreSlashCommands + +Adds more slash commands ig diff --git a/plugins/Whois/build.gradle.kts b/plugins/Whois/build.gradle.kts new file mode 100755 index 0000000..2fa05e7 --- /dev/null +++ b/plugins/Whois/build.gradle.kts @@ -0,0 +1,10 @@ +version = "1.0.0" +description = "Adds a /whois that inputs a mention or a userid and returns the most info it can test about that user" + +aliucord.changelog.set(""" + # 1.0.0 + * Initial release. Adds /fw (fullwidth) +""".trimIndent() + +) + diff --git a/plugins/Whois/src/main/AndroidManifest.xml b/plugins/Whois/src/main/AndroidManifest.xml new file mode 100755 index 0000000..0facbe0 --- /dev/null +++ b/plugins/Whois/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/plugins/Whois/src/main/java/dev/nope/plugins/whois/whois.kt b/plugins/Whois/src/main/java/dev/nope/plugins/whois/whois.kt new file mode 100755 index 0000000..42a2c46 --- /dev/null +++ b/plugins/Whois/src/main/java/dev/nope/plugins/whois/whois.kt @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2022 Vendicated & nope + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 +*/ + +package dev.nope.plugins.whois + +import android.content.Context +import android.graphics.Color +import com.aliucord.Utils +import com.aliucord.annotations.AliucordPlugin +import com.aliucord.api.CommandsAPI +import com.aliucord.entities.MessageEmbedBuilder +import com.aliucord.entities.Plugin +import com.aliucord.utils.RxUtils.await +import com.aliucord.wrappers.GuildRoleWrapper.Companion.permissions +import com.discord.api.commands.ApplicationCommandType +import com.discord.api.permission.Permission +import com.discord.api.user.UserFlags +import com.discord.api.user.UserProfile +import com.discord.models.member.GuildMember +import com.discord.models.user.CoreUser +import com.discord.stores.StoreStream +import com.discord.utilities.SnowflakeUtils +import com.discord.utilities.icon.IconUtils +import com.discord.utilities.rest.RestAPI +import com.discord.utilities.time.ClockFactory +import com.discord.utilities.time.TimeUtils +import com.discord.utilities.user.UserProfileUtilsKt +import com.discord.utilities.user.UserUtils +import java.util.* + +fun Long.snowflakeToDateString() = SnowflakeUtils.toTimestamp(this).toDateString() +fun Long.toDateString(): String = TimeUtils.INSTANCE.toReadableTimeStringEN(Locale.ENGLISH, this, ClockFactory.get()) + +@AliucordPlugin +class UserLookup : Plugin() { + private val flagToEmoji = mapOf( + UserFlags.BUG_HUNTER_LEVEL_1 to "<:bughunter:585765206769139723>", + UserFlags.BUG_HUNTER_LEVEL_2 to "<:goldbughunter:853274684337946648>", + UserFlags.CERTIFIED_MODERATOR to "<:certifiedmod:853274382339670046>", + UserFlags.HYPESQUAD_HOUSE1 to "<:bravery:585763004218343426>", + UserFlags.HYPESQUAD_HOUSE2 to "<:brilliance:585763004495298575>", + UserFlags.HYPESQUAD_HOUSE3 to "<:balance:585763004574859273>", + UserFlags.PARTNER to "<:partnernew:754032603081998336>", + UserFlags.STAFF to "<:stafftools:314348604095594498>", + UserFlags.VERIFIED_DEVELOPER to "<:verifiedbotdev:853277205264859156>", + UserFlags.PREMIUM_EARLY_SUPPORTER to "<:supporter:585763690868113455>", + ) + + override fun start(_ctx: Context) { + val mentionOrId = arrayListOf( + Utils.createCommandOption( + ApplicationCommandType.SUBCOMMAND, "user-id", "Look up a user by ID", subCommandOptions = arrayListOf( + Utils.createCommandOption( + ApplicationCommandType.STRING, "id", "The id of the user to look up", required = true + ) + ) + ), Utils.createCommandOption( + ApplicationCommandType.SUBCOMMAND, "mention", "Get info on a user by mention", subCommandOptions = arrayListOf( + Utils.createCommandOption( + ApplicationCommandType.USER, "user", "The user", required = true + ) + ) + ) + ) + + commands.registerCommand("whois", "Look up a user", mentionOrId) { ctx -> + val userId = when { + ctx.containsArg("user-id") -> ctx.getRequiredSubCommandArgs("user-id")["id"] + else -> ctx.getRequiredSubCommandArgs("mention")["user"] + }.let { + (it as String).toLongOrNull().takeIf { _ -> it.length in 17..19 } ?: return@registerCommand fail("Invalid input: $it") + } + val profile = getUserProfile(userId, ctx.currentChannel.guildId) ?: run { + val user = RestAPI.api.userGet(userId).await().first ?: return@registerCommand fail("No such user") + UserProfile(null, null, user, null, null, null, null) + } + val user = CoreUser(profile.g()) + + + val guildMember = profile.c()?.let { (GuildMember.Companion).from(it, ctx.currentChannel.guildId, emptyMap(), StoreStream.getGuilds()) } + val embed = MessageEmbedBuilder().run { + setAuthor( + "${if (user.isBot || user.isSystemUser) "\uD83E\uDD16 " else ""}${user.username}${ + UserUtils.INSTANCE.getDiscriminatorWithPadding( + user + ) + }" + ) + IconUtils.INSTANCE.getForGuildMemberOrUser( + user, guildMember, 512, true + ).let { + setThumbnail(it, it, 512, 512) + } + + user.bannerColor?.let { setColor(Color.parseColor(it)) } + user.banner?.let { hash -> + val icon = if (guildMember?.hasBanner() == true) IconUtils.INSTANCE.getForGuildMemberBanner( + guildMember.bannerHash, ctx.currentChannel.guildId, user.id, 2048, true + ) + else IconUtils.INSTANCE.getForUserBanner(user.id, hash, 2048, true) + setImage(icon, icon, 818, 2048) + } + setDescription(user.bio) + addField( + "Created At", user.id.snowflakeToDateString(), false + ) + guildMember?.let { + addField("Joined At", it.joinedAt.g().toDateString(), true) + addField("Colour", "#${it.color.toString(16)}", true) + + addField("Permissions", getPermissions(it.roles, ctx.currentChannel.guildId), true) + } + getBadgeEmojis(user.flags, profile).run { + if (isNotEmpty()) addField("Badges", this, true) + } + if (profile.d().isNotEmpty()) { + val guilds = StoreStream.getGuilds().guilds + val mutualGuilds = profile.d() + addField("Mutual Servers (${mutualGuilds.size})", mutualGuilds.joinToString("\n") { "• ${guilds[it.a()]!!.name}" }, true) + } + listOf(this.build()) + } + CommandsAPI.CommandResult(null, embed, false) + } + + + } + + override fun stop(context: Context) { + patcher.unpatchAll() + commands.unregisterAll() + } + + private fun getUserProfile(id: Long, guildId: Long?) = RestAPI.api.userProfileGet(id, true, guildId).await().first + + private fun getBadgeEmojis(flags: Int, profile: UserProfile) = StringBuilder().run { + flagToEmoji.forEach { (flag, emoji) -> + if (flags and flag != 0) append(emoji).append(' ') + } + if (UserProfileUtilsKt.isPremium(profile)) append("<:nitro:314068430611415041> ") + UserProfileUtilsKt.getGuildBoostMonthsSubscribed(profile)?.let { + append( + when (it) { + 0 -> "<:NitroBoost:699715144862662666>" + 1 -> "<:booster:585764032162562058>" + 2 -> "<:booster2:585764446253744128>" + 3 -> "<:booster3:585764446220189716>" + else -> "<:booster4:585764446178246657>" + } + ) + } + toString().trimEnd() + } + + @Suppress("NOTHING_TO_INLINE") + private inline fun fail(msg: String) = CommandsAPI.CommandResult(msg, null, false) + + private fun getPermissions(roleIds: List, guildId: Long): String { + val roles = StoreStream.getGuilds().roles[guildId]!! + val perms = roleIds.fold(roles[guildId]!!.permissions) { acc, curr -> + acc or roles[curr]!!.permissions + } + val re = "_\\w".toRegex() + return Permission::class.java.declaredFields.mapNotNull { f -> + when (f.name) { + "DEFAULT", "ALL", "NONE", "ELEVATED", "MODERATOR_PERMISSIONS", "MANAGEMENT_PERMISSIONS" -> return@mapNotNull null + } + if (f.type == Long::class.java && perms and f.get(null) as Long != 0L) + f.name.lowercase() + .replaceFirstChar { it.uppercase() } + .replace(re) { " ${it.value[1].uppercase()}" } + else null + }.joinToString(", ") + } + +} +