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(", ")
+ }
+
+}
+