Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ object Authors {
val Accelerator = Author("Accelerator", 150234173024501762L)
val Derlan = Author("Derlan", 821545900807028757L)
val Link = Author("Link", 725923756555501658L)
val ArjixWasTaken = Author("ArjixWasTaken", 674710789138939916L)
}

/**
Expand Down
13 changes: 13 additions & 0 deletions plugins/TextReact/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import com.aliucord.gradle.repo.Authors
import com.aliucord.gradle.repo.author

plugins {
id("com.aliucord.plugins-repo")
}

version = "1.0.41"
description = "A plugin that allows you to write text using reactions."

aliucord {
author(Authors.ArjixWasTaken)
}
133 changes: 133 additions & 0 deletions plugins/TextReact/src/main/kotlin/TextReact.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
@file:Suppress("MISSING_DEPENDENCY_CLASS", "MISSING_DEPENDENCY_SUPERCLASS")

import textreact.PluginSettings
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.annotations.AliucordPlugin
import com.aliucord.entities.Plugin
import com.aliucord.fragments.ConfirmDialog
import com.aliucord.fragments.InputDialog
import com.aliucord.patcher.Hook
import com.discord.databinding.WidgetChatListActionsBinding
import com.discord.models.domain.emoji.Emoji
import com.discord.stores.StoreStream
import com.discord.utilities.color.ColorCompat
import com.discord.widgets.chat.list.actions.WidgetChatListActions
import com.lytefast.flexinput.R
import textreact.TextToEmoji
import java.lang.reflect.InvocationTargetException

@AliucordPlugin
class TextReact : Plugin() {
init {
settingsTab = SettingsTab(PluginSettings::class.java, SettingsTab.Type.BOTTOM_SHEET)
.withArgs(settings)
}

override fun start(context: Context) {
val icon = ContextCompat.getDrawable(context, R.e.ic_keyboard_black_24dp)
val textReactId = View.generateViewId()

with(WidgetChatListActions::class.java) {
val getBinding = getDeclaredMethod("getBinding").apply { isAccessible = true }
val addReaction = getDeclaredMethod("addReaction", Emoji::class.java).apply { isAccessible = true }

patcher.patch(
getDeclaredMethod("configureUI", WidgetChatListActions.Model::class.java),
Hook { callFrame ->
try {
val binding =
getBinding.invoke(callFrame.thisObject) as WidgetChatListActionsBinding
val textReact = binding.a.findViewById<TextView>(textReactId).apply {
visibility =
if ((callFrame.args[0] as WidgetChatListActions.Model).manageMessageContext.canAddReactions) View.VISIBLE else View.GONE
}

if (!textReact.hasOnClickListeners()) textReact.setOnClickListener {
try {
val fragmentManager =
(callFrame.thisObject as WidgetChatListActions).parentFragmentManager
val inDialog = InputDialog()
.setTitle("Text React!")
.setDescription("Enter some text to send as reactions.")
.setPlaceholderText("Enter text...")
inDialog.setOnOkListener {
val result = TextToEmoji.generateEmojiArray(inDialog.input.toString().trim())
inDialog.dismiss()

if (result.second) {
// show prompt that the text is incomplete
val coDialog = ConfirmDialog()
.setTitle("Text React!")
.setDescription("Warning: The given input could not be 100% translated to reactions, do you still want to continue?")
coDialog.setOnOkListener {
coDialog.dismiss()
Utils.threadPool.execute {
result.first.forEach { emoji ->
addReaction.invoke(
callFrame.thisObject,
StoreStream.getEmojis().unicodeEmojiSurrogateMap[emoji]!!
)
Thread.sleep(1000)
}
}

}
coDialog.setOnCancelListener {
coDialog.dismiss()
Utils.showToast("Action cancelled by the user.")
}
coDialog.show(fragmentManager, "bbbbbb")
} else {
Utils.threadPool.execute {
result.first.forEach { emoji ->
addReaction.invoke(
callFrame.thisObject,
StoreStream.getEmojis().unicodeEmojiSurrogateMap[emoji]!!
)
Thread.sleep(1000)
}
}
}
}
(callFrame.thisObject as WidgetChatListActions).dismiss()
inDialog.show(fragmentManager, "aaaaaa")
} catch (e: IllegalAccessException) {
e.printStackTrace()
} catch (e: InvocationTargetException) {
e.printStackTrace()
}
}
} catch (ignored: Throwable) {
}
})

patcher.patch(
getDeclaredMethod("onViewCreated", View::class.java, Bundle::class.java),
Hook { callFrame ->
val linearLayout = (callFrame.args[0] as NestedScrollView).getChildAt(0) as LinearLayout
val ctx = linearLayout.context

icon?.setTint(ColorCompat.getThemedColor(ctx, R.b.colorInteractiveNormal))

val textReact = TextView(ctx, null, 0, R.i.UiKit_Settings_Item_Icon).apply {
text = "Text React"
id = textReactId
typeface = ResourcesCompat.getFont(ctx, Constants.Fonts.whitney_medium)
setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null)
}

linearLayout.addView(textReact, 1)
})
}
}
override fun stop(context: Context) = patcher.unpatchAll()
}
34 changes: 34 additions & 0 deletions plugins/TextReact/src/main/kotlin/textreact/PluginSettings.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package textreact

import android.os.Bundle
import android.view.View
import com.aliucord.PluginManager
import com.aliucord.Utils
import com.aliucord.api.SettingsAPI
import com.aliucord.widgets.BottomSheet
import com.discord.views.CheckedSetting

@Suppress("MISSING_DEPENDENCY_SUPERCLASS")
class PluginSettings(private val settings: SettingsAPI) : BottomSheet() {
override fun onViewCreated(view: View, bundle: Bundle?) {
super.onViewCreated(view, bundle)

val ctx = requireContext()

fun createCheckedSetting(title: String, setting: String, checked: Boolean = true) {
return addView(
Utils.createCheckedSetting(ctx, CheckedSetting.ViewType.SWITCH, title, null).apply {
isChecked = settings.getBool(setting, checked)

setOnCheckedListener {
settings.setBool(setting, it)
PluginManager.stopPlugin("TextReact")
PluginManager.startPlugin("TextReact")
}
}
)
}

createCheckedSetting("Foo", "foo")
}
}
127 changes: 127 additions & 0 deletions plugins/TextReact/src/main/kotlin/textreact/TextToEmoji.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package textreact

// huge thanks to https://github.com/Juby210/text-react/blob/master/index.js

class TextToEmoji {
companion object {
private data class ReactionsType(
val single: MutableMap<String, ArrayList<String>>,
val multiple: MutableMap<String, ArrayList<String>>,
)

private val reactions: ReactionsType = ReactionsType(
single=mutableMapOf(
"a" to arrayListOf("\uD83C\uDDE6", "\uD83C\uDD70"),
"b" to arrayListOf("\uD83C\uDDE7", "\uD83C\uDD71"),
"c" to arrayListOf("\uD83C\uDDE8", "©"),
"d" to arrayListOf("\uD83C\uDDE9"),
"e" to arrayListOf("\uD83C\uDDEA", "\uD83D\uDCE7", "🎼"),
"f" to arrayListOf("\uD83C\uDDEB"),
"g" to arrayListOf("\uD83C\uDDEC"),
"h" to arrayListOf("\uD83C\uDDED", "♓"),
"i" to arrayListOf("\uD83C\uDDEE", "ℹ"),
"j" to arrayListOf("\uD83C\uDDEF"),
"k" to arrayListOf("\uD83C\uDDF0"),
"l" to arrayListOf("\uD83C\uDDF1"),
"m" to arrayListOf("\uD83C\uDDF2", "Ⓜ", "♏", "♍"),
"n" to arrayListOf("\uD83C\uDDF3", "♑"),
"o" to arrayListOf("\uD83C\uDDF4", "\uD83C\uDD7E", "⭕"),
"p" to arrayListOf("\uD83C\uDDF5", "\uD83C\uDD7F"),
"q" to arrayListOf("\uD83C\uDDF6"),
"r" to arrayListOf("\uD83C\uDDF7", "®"),
"s" to arrayListOf("\uD83C\uDDF8"),
"t" to arrayListOf("\uD83C\uDDF9", "✝"),
"u" to arrayListOf("\uD83C\uDDFA"),
"v" to arrayListOf("\uD83C\uDDFB", "♈"),
"w" to arrayListOf("\uD83C\uDDFC"),
"x" to arrayListOf("\uD83C\uDDFD", "❎", "❌", "✖"),
"y" to arrayListOf("\uD83C\uDDFE"),
"z" to arrayListOf("\uD83C\uDDFF"),
"0" to arrayListOf("0️⃣"),
"1" to arrayListOf("1️⃣"),
"2" to arrayListOf("2️⃣"),
"3" to arrayListOf("3️⃣"),
"4" to arrayListOf("4️⃣"),
"5" to arrayListOf("5️⃣"),
"6" to arrayListOf("6️⃣"),
"7" to arrayListOf("7️⃣"),
"8" to arrayListOf("8️⃣"),
"9" to arrayListOf("9️⃣"),
"?" to arrayListOf("❔", "❓"),
"+" to arrayListOf("➕"),
"-" to arrayListOf("➖", "⛔", "\uD83D\uDCDB"),
"!" to arrayListOf("❕", "❗"),
"*" to arrayListOf("*️⃣"),
"$" to arrayListOf("\uD83D\uDCB2"),
"#" to arrayListOf("#️⃣"),
" " to arrayListOf("▪", "◾", "➖", "◼", "⬛", "⚫", "\uD83D\uDDA4", "\uD83D\uDD76")
),
multiple=mutableMapOf(
"wc" to arrayListOf("\uD83D\uDEBE"),
"back" to arrayListOf("\uD83D\uDD19"),
"end" to arrayListOf("\uD83D\uDD1A"),
"on!" to arrayListOf("\uD83D\uDD1B"),
"soon" to arrayListOf("\uD83D\uDD1C"),
"top" to arrayListOf("\uD83D\uDD1D"),
"!!" to arrayListOf("‼"),
"!?" to arrayListOf("⁉"),
"tm" to arrayListOf("™"),
"10" to arrayListOf("\uD83D\uDD1F"),
"cl" to arrayListOf("\uD83C\uDD91"),
"cool" to arrayListOf("\uD83C\uDD92"),
"free" to arrayListOf("\uD83C\uDD93"),
"id" to arrayListOf("\uD83C\uDD94"),
"new" to arrayListOf("\uD83C\uDD95"),
"ng" to arrayListOf("\uD83C\uDD96"),
"ok" to arrayListOf("\uD83C\uDD97"),
"sos" to arrayListOf("\uD83C\uDD98"),
"up!" to arrayListOf("\uD83C\uDD99"),
"vs" to arrayListOf("\uD83C\uDD9A"),
"abc" to arrayListOf("\uD83D\uDD24"),
"ab" to arrayListOf("\uD83C\uDD8E"),
"18" to arrayListOf("\uD83D\uDD1E"),
"100" to arrayListOf("\uD83D\uDCAF"),
"atm" to arrayListOf("\uD83C\uDFE7")
)
)

fun generateEmojiArray(string: String): Pair<List<String>, Boolean> {
val unusedReactions = reactions
var msg = string.lowercase()
val newReactions = ArrayList<String>()

val allReactions = ArrayList((unusedReactions.single + unusedReactions.multiple).keys)
var incomplete = false

while (msg.isNotEmpty()) {
if (!allReactions.contains(msg[0].toString())) {
msg = msg.slice(1 until msg.length)
incomplete = true
}
for (reactionName in unusedReactions.multiple.keys) {
if (msg.isNotEmpty() && msg.startsWith(reactionName) && unusedReactions.multiple[reactionName]!!.isNotEmpty()) {
val reactionValue = unusedReactions.multiple[reactionName]!!.first()
newReactions.add(reactionValue)
unusedReactions.multiple[reactionName] = ArrayList(unusedReactions.multiple[reactionName]!!.filter { value -> value != reactionValue })
msg = msg.replace(reactionName, "")
} else if (msg.isNotEmpty() && msg.startsWith(reactionName) && unusedReactions.multiple[reactionName]!!.isEmpty()) {
msg = msg.replaceFirst(reactionName, "")
incomplete = true
}
}
for (reactionName in unusedReactions.single.keys) {
if (msg.isNotEmpty() && msg.startsWith(reactionName) && unusedReactions.single[reactionName]!!.isNotEmpty()) {
val reactionValue = unusedReactions.single[reactionName]!!.first()
newReactions.add(reactionValue)
unusedReactions.single[reactionName] = ArrayList(unusedReactions.single[reactionName]!!.filter { value -> value != reactionValue })
msg = msg.replaceFirst(reactionName, "")
} else if (msg.isNotEmpty() && msg.startsWith(reactionName) && unusedReactions.single[reactionName]!!.isEmpty()) {
msg = msg.replaceFirst(reactionName, "")
incomplete = true
}
}
}
return Pair(newReactions, incomplete)
}
}
}