diff --git a/build-logic/src/main/kotlin/com/aliucord/gradle/repo/Authors.kt b/build-logic/src/main/kotlin/com/aliucord/gradle/repo/Authors.kt index 2ef9381..17c19f6 100644 --- a/build-logic/src/main/kotlin/com/aliucord/gradle/repo/Authors.kt +++ b/build-logic/src/main/kotlin/com/aliucord/gradle/repo/Authors.kt @@ -44,12 +44,13 @@ object Authors { val Nyako = Author("nyakowint", 118437263754395652L) val Juby210 = Author("Juby210", 324622488644616195L) val LampDelivery = Author("LampDelivery", 650805815623680030L) - val Ty = Author("Ty", 487443883127472129L) val miaaaa0a = Author("miaaaa0a", 435750383491481602L) + val Vendicated = Author("Vendicated", 343383572805058560L) + val mantikafasi = Author("mantikafasi", 287555395151593473L) + val Ty = Author("Ty", 487443883127472129L) val Cloudburst = Author("Cloudburst", 295186738085756929L) val HalalKing = Author("HalalKing", 261634919980204033L) val Accelerator = Author("Accelerator", 150234173024501762L) - val Vendicated = Author("Vendicated", 343383572805058560L) val Derlan = Author("Derlan", 821545900807028757L) } diff --git a/plugins/AutoShowSpoilers/build.gradle.kts b/plugins/AutoShowSpoilers/build.gradle.kts new file mode 100755 index 0000000..ab3b149 --- /dev/null +++ b/plugins/AutoShowSpoilers/build.gradle.kts @@ -0,0 +1,13 @@ +import com.aliucord.gradle.repo.Authors +import com.aliucord.gradle.repo.author + +plugins { + id("com.aliucord.plugins-repo") +} + +version = "1.1.0" +description = "Automatically clicks on spoilers for you" + +aliucord { + author(Authors.Nyako) +} \ No newline at end of file diff --git a/plugins/AutoShowSpoilers/src/main/kotlin/me/aniimalz/plugins/AutoShowSpoilers.kt b/plugins/AutoShowSpoilers/src/main/kotlin/me/aniimalz/plugins/AutoShowSpoilers.kt new file mode 100755 index 0000000..d0c0110 --- /dev/null +++ b/plugins/AutoShowSpoilers/src/main/kotlin/me/aniimalz/plugins/AutoShowSpoilers.kt @@ -0,0 +1,35 @@ +package me.aniimalz.plugins + +import android.content.Context +import com.aliucord.annotations.AliucordPlugin +import com.aliucord.entities.Plugin +import com.aliucord.patcher.InsteadHook +import com.aliucord.patcher.after +import com.discord.simpleast.core.node.Node +import com.discord.utilities.textprocessing.MessagePreprocessor +import com.discord.utilities.textprocessing.node.SpoilerNode +import com.discord.widgets.chat.list.adapter.WidgetChatListAdapterItemAttachment +import com.discord.widgets.chat.list.entries.AttachmentEntry + +@AliucordPlugin +class AutoShowSpoilers : Plugin() { + override fun start(ctx: Context) { + patcher.after("processNode", Node::class.java) { + val node = it.args[0] + if (node !is SpoilerNode<*>) return@after + node.updateState(spoilers.size, true) + } + patcher.after>("isRevealed") { this.isRevealed = true } + + // Embed spoiler patch + with(WidgetChatListAdapterItemAttachment.Model::class.java) { + val rev = getDeclaredMethod("isSpoilerEmbedRevealed", AttachmentEntry::class.java) + rev.apply { isAccessible = true } + patcher.patch(rev, InsteadHook { return@InsteadHook true }) + } + } + + override fun stop(ctx: Context) { + patcher.unpatchAll() + } +} \ No newline at end of file diff --git a/plugins/ChannelLocker/build.gradle.kts b/plugins/ChannelLocker/build.gradle.kts new file mode 100755 index 0000000..c732d72 --- /dev/null +++ b/plugins/ChannelLocker/build.gradle.kts @@ -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.3" +description = "Lets you lock channels so you cant type in them anymore" + +aliucord { + author(Authors.Nyako) +} \ No newline at end of file diff --git a/plugins/ChannelLocker/src/main/kotlin/me/aniimalz/plugins/ChannelCard.kt b/plugins/ChannelLocker/src/main/kotlin/me/aniimalz/plugins/ChannelCard.kt new file mode 100755 index 0000000..febfb00 --- /dev/null +++ b/plugins/ChannelLocker/src/main/kotlin/me/aniimalz/plugins/ChannelCard.kt @@ -0,0 +1,34 @@ +package me.aniimalz.plugins + +import android.content.Context +import android.view.Gravity +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.content.ContextCompat +import com.aliucord.utils.DimenUtils.dpToPx +import com.lytefast.flexinput.R + +@Suppress("Deprecation") +class ChannelCard(ctx: Context?) : LinearLayout(ctx) { + val name: TextView + val delete: com.aliucord.views.ToolbarButton + + init { + orientation = HORIZONTAL + gravity = Gravity.CENTER_VERTICAL + layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) + name = TextView(ctx, null, 0, R.i.UiKit_Settings_Item_Icon) + val buttons = LinearLayout(ctx) + buttons.orientation = HORIZONTAL + val params = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) + params.marginEnd = dpToPx(16) + buttons.layoutParams = params + buttons.setHorizontalGravity(Gravity.END) + buttons.setVerticalGravity(Gravity.CENTER_VERTICAL) + delete = com.aliucord.views.ToolbarButton(ctx) + delete.setImageDrawable(ContextCompat.getDrawable(ctx!!, R.e.ic_close_primary_200_24dp)) + buttons.addView(delete) + addView(name) + addView(buttons) + } +} \ No newline at end of file diff --git a/plugins/ChannelLocker/src/main/kotlin/me/aniimalz/plugins/ChannelListAdapter.kt b/plugins/ChannelLocker/src/main/kotlin/me/aniimalz/plugins/ChannelListAdapter.kt new file mode 100755 index 0000000..1a6eb07 --- /dev/null +++ b/plugins/ChannelLocker/src/main/kotlin/me/aniimalz/plugins/ChannelListAdapter.kt @@ -0,0 +1,60 @@ +package me.aniimalz.plugins + +import android.annotation.SuppressLint +import android.content.Context +import android.view.View +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import com.aliucord.PluginManager +import com.aliucord.Utils +import com.aliucord.Utils.getResId +import com.aliucord.fragments.SettingsPage +import com.discord.utilities.color.ColorCompat +import com.lytefast.flexinput.R +import java.util.* + +class ChannelListAdapter( + page: SettingsPage, + private val channels: MutableMap +) : + RecyclerView.Adapter() { + inner class ChannelListHolder(private val adapter: ChannelListAdapter, val item: ChannelCard) : + RecyclerView.ViewHolder(item), View.OnClickListener { + override fun onClick(view: View) { + adapter.onClick() + } + + } + + private val ctx: Context? = page.context + override fun getItemCount(): Int { + return ArrayList(channels.keys).size + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChannelListHolder { + return ChannelListHolder(this, ChannelCard(ctx)) + } + + @SuppressLint("NotifyDataSetChanged") + override fun onBindViewHolder(holder: ChannelListHolder, position: Int) { + val name = ArrayList(channels.keys)[position] + holder.item.name.text = name + holder.item.delete.setOnClickListener { + channels.remove(name) + PluginManager.plugins["ChannelLocker"]!!.settings.setObject("channels", channels) + notifyDataSetChanged() + Utils.showToast("Channel removed: unlocked") + } + val icon = + ContextCompat.getDrawable(ctx!!, getResId("ic_text_channel_white_24dp", "drawable"))!! + .mutate() + icon.setTint(ColorCompat.getThemedColor(ctx, R.b.colorInteractiveNormal)) + holder.item.name.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null) + } + + fun onClick() { + + } + +} \ No newline at end of file diff --git a/plugins/ChannelLocker/src/main/kotlin/me/aniimalz/plugins/ChannelLocker.kt b/plugins/ChannelLocker/src/main/kotlin/me/aniimalz/plugins/ChannelLocker.kt new file mode 100755 index 0000000..466969a --- /dev/null +++ b/plugins/ChannelLocker/src/main/kotlin/me/aniimalz/plugins/ChannelLocker.kt @@ -0,0 +1,200 @@ +package me.aniimalz.plugins + +import android.annotation.SuppressLint +import android.content.Context +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.RelativeLayout +import android.widget.TextView +import androidx.core.content.ContextCompat +import androidx.core.content.res.ResourcesCompat +import com.aliucord.Constants +import com.aliucord.Utils +import com.aliucord.annotations.AliucordPlugin +import com.aliucord.api.CommandsAPI +import com.aliucord.entities.Plugin +import com.aliucord.patcher.Hook +import com.aliucord.patcher.InsteadHook +import com.aliucord.patcher.after +import com.aliucord.utils.DimenUtils +import com.aliucord.utils.ReflectUtils +import com.aliucord.wrappers.ChannelWrapper.Companion.id +import com.aliucord.wrappers.ChannelWrapper.Companion.name +import com.discord.databinding.WidgetChannelsListItemActionsBinding +import com.discord.databinding.WidgetChatInputBinding +import com.discord.utilities.color.ColorCompat +import com.discord.widgets.channels.list.WidgetChannelsListItemChannelActions +import com.discord.widgets.chat.input.ChatInputViewModel +import com.discord.widgets.chat.input.WidgetChatInput +import com.google.gson.reflect.TypeToken +import com.lytefast.flexinput.R +import java.util.* + +@AliucordPlugin +class ChannelLocker : Plugin() { + init { + settingsTab = SettingsTab( + PluginSettings::class.java, + SettingsTab.Type.PAGE + ).withArgs(settings) + } + + + @SuppressLint("SetTextI18n") + override fun start(ctx: Context) { + val icon = Utils.tintToTheme( + ContextCompat.getDrawable( + Utils.appContext, + R.e.ic_lock_dark_a60_24dp + ) + ) // diamong makes me husk + val channelLockId = View.generateViewId() + val channels = settings.getObject( + "channels", HashMap(), TypeToken.getParameterized( + HashMap::class.java, String::class.javaObjectType, Long::class.javaObjectType + ).type + ) + + commands.registerCommand( + "lockchannel", + "Use this to lock the channel so you cannot type in it anymore" + ) { + if (channels.containsValue(it.channelId)) return@registerCommand CommandsAPI.CommandResult( + "Channel is already locked! how did you do this lol", + null, + false + ) + channels[it.currentChannel.name] = it.channelId + settings.setObject("channels", channels) + CommandsAPI.CommandResult("Channel has been locked!", null, false) + } + + + val bindingMethod = WidgetChatInput::class.java.getDeclaredMethod("getBinding") + .apply { isAccessible = true } + patcher.after( + "configureChatGuard", ChatInputViewModel.ViewState.Loaded::class.java + ) { + val loaded = it.args[0] as ChatInputViewModel.ViewState.Loaded + + if (channels.containsValue(loaded.channelId) && !loaded.shouldShowVerificationGate) { + val binding = bindingMethod(it.thisObject) as WidgetChatInputBinding + + val gateButtonText = binding.root.findViewById( + Utils.getResId( + "chat_input_member_verification_guard_text", + "id" + ) + ) + val chatWrap = binding.root.findViewById( + Utils.getResId( + "chat_input_wrap", + "id" + ) + ) + val gateButtonImage = binding.root.findViewById( + Utils.getResId( + "chat_input_member_verification_guard_icon", + "id" + ) + ) + val gateButtonArrow = binding.root.findViewById( + Utils.getResId( + "chat_input_member_verification_guard_action", + "id" + ) + ) + val gateButtonLayout = binding.root.findViewById( + Utils.getResId( + "guard_member_verification", + "id" + ) + ) + + gateButtonLayout.visibility = View.VISIBLE + chatWrap.visibility = View.GONE + + gateButtonImage.setImageResource(R.e.ic_channel_text_locked) + gateButtonText.text = + "Channel is locked" + gateButtonArrow.setImageResource(R.e.ic_check_white_24dp) + val pd = DimenUtils.defaultPadding + gateButtonArrow.setPadding(pd, pd, pd, pd) + gateButtonArrow.layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ) + gateButtonArrow.setOnLongClickListener { + channels.remove(loaded.channel.name) + settings.setObject("channels", channels) + + gateButtonLayout.visibility = View.GONE + chatWrap.visibility = View.VISIBLE + return@setOnLongClickListener true + } + gateButtonArrow.setOnClickListener { + InsteadHook.DO_NOTHING + } + gateButtonArrow.visibility = + if (settings.getBool("showUnlock", true)) View.VISIBLE else View.GONE + } + } + + + // just adding stupid buttons to channel settings and channel list actions + + patcher.patch( + WidgetChannelsListItemChannelActions::class.java, "configureUI", arrayOf>( + WidgetChannelsListItemChannelActions.Model::class.java + ), Hook { + val model = it.args[0] as WidgetChannelsListItemChannelActions.Model + val actions = it.thisObject as WidgetChannelsListItemChannelActions + try { + val binding = ReflectUtils.invokeMethod( + actions, + "getBinding" + ) as WidgetChannelsListItemActionsBinding? + val root = (binding?.root as ViewGroup).getChildAt(0) as ViewGroup + TextView(root.context, null, 0, R.i.UiKit_Settings_Item_Icon).apply { + text = + if (!channels.containsValue(model.channel.id)) "Lock Channel" else "Unlock Channel" + setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null) + id = channelLockId + typeface = ResourcesCompat.getFont( + Utils.appContext, + Constants.Fonts.whitney_medium + ) + setOnClickListener { + if (channels.containsValue(model.channel.id)) { + channels.remove(model.channel.name) + actions.dismiss() + Utils.showToast("Channel unlocked") + settings.setObject("channels", channels) + return@setOnClickListener + } + channels[model.channel.name] = model.channel.id + actions.dismiss() + Utils.showToast("Channel locked") + settings.setObject("channels", channels) + } + root.addView(this) + } + icon?.setTint( + ColorCompat.getThemedColor( + root.context, + R.b.colorInteractiveNormal + ) + ) + } catch (e: Throwable) { + logger.error("Error adding lock button to channel actions", e) + } + }) + } + + override fun stop(ctx: Context) { + patcher.unpatchAll() + commands.unregisterAll() + } +} \ No newline at end of file diff --git a/plugins/ChannelLocker/src/main/kotlin/me/aniimalz/plugins/PluginSettings.kt b/plugins/ChannelLocker/src/main/kotlin/me/aniimalz/plugins/PluginSettings.kt new file mode 100755 index 0000000..b7e93ce --- /dev/null +++ b/plugins/ChannelLocker/src/main/kotlin/me/aniimalz/plugins/PluginSettings.kt @@ -0,0 +1,77 @@ +package me.aniimalz.plugins + +import android.annotation.SuppressLint +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.aliucord.Utils +import com.aliucord.api.SettingsAPI +import com.aliucord.fragments.InputDialog +import com.aliucord.fragments.SettingsPage +import com.aliucord.views.Button +import com.aliucord.views.Divider +import com.aliucord.wrappers.ChannelWrapper.Companion.name +import com.discord.stores.StoreStream +import com.discord.views.CheckedSetting +import com.google.gson.reflect.TypeToken +import java.util.* + +class PluginSettings(private val settings: SettingsAPI) : SettingsPage() { + @SuppressLint("SetTextI18n", "NotifyDataSetChanged") + override fun onViewBound(view: View) { + super.onViewBound(view) + val ctx = view.context + val layout = linearLayout + setActionBarTitle("Channel Locker") + val fm = parentFragmentManager + + val channels = settings.getObject( + "channels", + HashMap(), + TypeToken.getParameterized( + HashMap::class.java, + String::class.javaObjectType, + Long::class.javaObjectType + ).type + ) + + val recycler = RecyclerView(ctx).apply { + layoutManager = LinearLayoutManager(ctx) + adapter = ChannelListAdapter(this@PluginSettings, channels) + } + + addView(Utils.createCheckedSetting(requireContext(), CheckedSetting.ViewType.SWITCH, "Show unlock button", "Whether to show the checkmark or not. If hidden you will have to unlock the channel through the settings").apply { + isChecked = settings.getBool("showUnlock", true) + setOnCheckedListener { settings.setBool("showUnlock", it) } + }) + + Button(ctx).run { + text = "Add Channel" + setOnClickListener { + InputDialog().run { + setTitle("Add channel") + setDescription("Enter the id of the channel you want to lock") + setOnCancelListener { dismiss() } + setOnOkListener { + if (channels.containsValue(input.toLong())) { + dismiss() + return@setOnOkListener + } + channels[StoreStream.getChannels().getChannel(input.toLong()).name] = + input.toLong() + dismiss() + Utils.showToast("Channel added: locked") + settings.setObject("channels", channels) + recycler.adapter?.notifyItemInserted(recycler.adapter!!.itemCount) + } + show(fm, "fsdkjlbgxjkhdlbfghfjkmugyhkl") + } + } + layout.addView(this) + } + + layout.addView(Divider(ctx)) + + layout.addView(recycler) + } +} \ No newline at end of file diff --git a/plugins/HomeIconSwitcher/build.gradle.kts b/plugins/HomeIconSwitcher/build.gradle.kts new file mode 100755 index 0000000..32a57e7 --- /dev/null +++ b/plugins/HomeIconSwitcher/build.gradle.kts @@ -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.3" +description = "switch the home icon above guild list also fresco sucks" + +aliucord { + author(Authors.Nyako) +} \ No newline at end of file diff --git a/plugins/HomeIconSwitcher/src/main/kotlin/me/aniimalz/plugins/HomeIconSwitcher.kt b/plugins/HomeIconSwitcher/src/main/kotlin/me/aniimalz/plugins/HomeIconSwitcher.kt new file mode 100755 index 0000000..2d38d12 --- /dev/null +++ b/plugins/HomeIconSwitcher/src/main/kotlin/me/aniimalz/plugins/HomeIconSwitcher.kt @@ -0,0 +1,113 @@ +package me.aniimalz.plugins + +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.Drawable +import android.graphics.drawable.ShapeDrawable +import android.graphics.drawable.shapes.OvalShape +import android.net.Uri +import android.view.Gravity +import android.view.View +import android.widget.FrameLayout +import android.widget.ImageView +import androidx.appcompat.widget.AppCompatImageView +import androidx.core.content.ContextCompat +import com.aliucord.Utils +import com.aliucord.annotations.AliucordPlugin +import com.aliucord.entities.Plugin +import com.aliucord.patcher.after +import com.discord.widgets.guilds.list.GuildListItem +import com.discord.widgets.guilds.list.GuildListViewHolder +import com.facebook.drawee.view.SimpleDraweeView +import com.lytefast.flexinput.R +import b.f.g.a.a.b as bruh + + +@AliucordPlugin +class HomeIconSwitcher : Plugin() { + init { + settingsTab = SettingsTab( + PluginSettings::class.java, + SettingsTab.Type.BOTTOM_SHEET + ).withArgs(settings) + } + + private lateinit var homeView: AppCompatImageView + + var pluginIcon: Drawable? = null + + override fun start(ctx: Context) { + val viewId = View.generateViewId() + pluginIcon = ContextCompat.getDrawable(Utils.appContext, R.e.ic_tab_home) + patcher.after( + "configure", + GuildListItem.FriendsItem::class.java + ) { + if (!settings.getBool("enabled", true)) return@after + homeView = itemView.findViewById( + Utils.getResId( + "guilds_item_profile_avatar", + "id" + ) + ) + val layout = homeView.parent as FrameLayout + val homeIcon = settings.getString("homeIcon", null).takeIf { it != null } + try { + if (homeIcon?.let { it1 -> Utils.getResId(it1, "drawable") } != 0) { + val field = homeIcon?.let { R.e::class.java.getField(homeIcon) } + val icon = homeIcon?.let { + field?.let { it1 -> + ContextCompat.getDrawable(ctx, it1.getInt(field))?.apply { + mutate() + Utils.tintToTheme(this) + } + } + } + homeView.setImageDrawable(icon) + return@after + } + if (settings.getBool("removeBg", false)) { + layout.apply { + backgroundTintList = null + setBackgroundColor(Color.TRANSPARENT) + } + homeView.apply { + backgroundTintList = null + imageTintList = null + setBackgroundColor(Color.TRANSPARENT) + } + } + if (layout.findViewById(viewId) != null) return@after + SimpleDraweeView(ctx).apply { + id = viewId + imageTintList = null + clipToOutline = true + setImageURI(homeIcon) + layout.addView(this) + homeView.setImageDrawable(null) + layoutParams = FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT + ).apply { + gravity = Gravity.CENTER + } + background = ShapeDrawable(OvalShape()).apply { paint.color = Color.TRANSPARENT } + controller = bruh.a().run { + f(Uri.parse(homeIcon)) + m = true + j = true + a() + } + } + } catch (t: Throwable) { + logger.error(t) + } + } + } + + override fun stop(ctx: Context) { + patcher.unpatchAll() + commands.unregisterAll() + } + +} \ No newline at end of file diff --git a/plugins/HomeIconSwitcher/src/main/kotlin/me/aniimalz/plugins/PluginSettings.kt b/plugins/HomeIconSwitcher/src/main/kotlin/me/aniimalz/plugins/PluginSettings.kt new file mode 100755 index 0000000..0a8e2cb --- /dev/null +++ b/plugins/HomeIconSwitcher/src/main/kotlin/me/aniimalz/plugins/PluginSettings.kt @@ -0,0 +1,78 @@ +package me.aniimalz.plugins + +import android.annotation.SuppressLint +import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher +import android.view.View +import android.widget.TextView +import androidx.core.content.res.ResourcesCompat +import com.aliucord.Constants +import com.aliucord.Logger +import com.aliucord.Utils +import com.aliucord.api.SettingsAPI +import com.aliucord.utils.DimenUtils +import com.aliucord.views.Divider +import com.aliucord.views.TextInput +import com.aliucord.widgets.BottomSheet +import com.discord.views.CheckedSetting +import com.lytefast.flexinput.R + +class PluginSettings(private val settings: SettingsAPI) : BottomSheet() { + + @SuppressLint("SetTextI18n") + override fun onViewCreated(view: View, bundle: Bundle?) { + super.onViewCreated(view, bundle) + val ctx = requireContext() + val logger = Logger("HomeIconSwitcher") + + addView(TextView(ctx, null, 0, R.i.UiKit_Settings_Item_Header).apply { + typeface = ResourcesCompat.getFont(ctx, Constants.Fonts.whitney_bold) + text = "Home Icon Switcher" + isAllCaps = true + setPadding(DimenUtils.defaultPadding) + }) + + addView(Utils.createCheckedSetting( + ctx, CheckedSetting.ViewType.SWITCH, "Enable plugin", null + ).apply { + isChecked = settings.getBool("enabled", true) + setOnCheckedListener { + settings.setBool("enabled", it) + promptRestart() + } + }) + addView(Utils.createCheckedSetting( + ctx, CheckedSetting.ViewType.SWITCH, "Remove colored background", "Remove discord's colored background behind the image" + ).apply { + isChecked = settings.getBool("removeBg", true) + setOnCheckedListener { + settings.setBool("removeBg", it) + promptRestart() + } + }) + addView(Divider(ctx)) + + val icon = settings.getString("homeIcon", null).takeIf { it != null } + + addView(TextInput(ctx, "Home icon URL (supports drawables)", icon?.let { icon } ?: "", object : TextWatcher { + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} + + override fun afterTextChanged(s: Editable?) { + try { + val txt = s.toString() + settings.setString("homeIcon", txt) + promptRestart("Restart to apply icon change.") + } catch (n: Throwable) { + logger.error(n) + } + } + })) + + } + + private fun promptRestart(t: String = "Restart to apply changes") { + Utils.promptRestart(t) + } +} \ No newline at end of file diff --git a/plugins/HopOnBloonsWhen/build.gradle.kts b/plugins/HopOnBloonsWhen/build.gradle.kts new file mode 100755 index 0000000..43366da --- /dev/null +++ b/plugins/HopOnBloonsWhen/build.gradle.kts @@ -0,0 +1,13 @@ +import com.aliucord.gradle.repo.Authors +import com.aliucord.gradle.repo.author + +plugins { + id("com.aliucord.plugins-repo") +} + +version = "1.1.4" +description = "hop on bloons when" + +aliucord { + author(Authors.Nyako) +} \ No newline at end of file diff --git a/plugins/HopOnBloonsWhen/src/main/kotlin/me/aniimalz/plugins/HopOnBloonsWhen.kt b/plugins/HopOnBloonsWhen/src/main/kotlin/me/aniimalz/plugins/HopOnBloonsWhen.kt new file mode 100755 index 0000000..74135c9 --- /dev/null +++ b/plugins/HopOnBloonsWhen/src/main/kotlin/me/aniimalz/plugins/HopOnBloonsWhen.kt @@ -0,0 +1,61 @@ +package me.aniimalz.plugins + +import android.content.Context +import android.graphics.drawable.Drawable +import android.view.View +import androidx.core.content.ContextCompat +import com.aliucord.Main +import com.aliucord.Utils +import com.aliucord.annotations.AliucordPlugin +import com.aliucord.entities.Plugin +import com.aliucord.patcher.after +import com.aliucord.utils.RxUtils.onBackpressureBuffer +import com.aliucord.utils.RxUtils.subscribe +import com.discord.models.domain.ModelUserSettings +import com.discord.models.message.Message +import com.discord.models.user.CoreUser +import com.discord.stores.StoreStream +import com.discord.widgets.settings.WidgetSettingsAppearance +import com.discord.widgets.settings.`WidgetSettingsAppearance$updateTheme$1` +import com.lytefast.flexinput.R +import rx.Subscription +import java.util.* +import java.util.concurrent.ThreadLocalRandom +import kotlin.system.exitProcess + + +@AliucordPlugin +class HopOnBloonsWhen : Plugin() { + private var observable: Subscription? = null + var pluginIcon: Drawable? = null + override fun start(ctx: Context) { + pluginIcon = ContextCompat.getDrawable(Utils.appContext, R.e.ic_emoji_24dp) + observable = StoreStream.getGatewaySocket().messageCreate.onBackpressureBuffer().subscribe { + if (this == null) return@subscribe + val message = Message(this) + if (CoreUser(message.author).id == StoreStream.getUsers().me.id) return@subscribe + val content = message.content.lowercase() + if (content.contains("hop on bloons") || content.contains("get on bloons") || + content.contains("com.ninjakiwi.bloonstd6") + ) { + hopOnBloons() + } + } + } + + private fun hopOnBloons() { + try { + val bloons = + Utils.appContext.packageManager.getLaunchIntentForPackage("com.ninjakiwi.bloonstd6") + Utils.appContext.startActivity(bloons) + } catch (ignored: Throwable) { + // nop nop nop nop + } + } + + override fun stop(ctx: Context) { + patcher.unpatchAll() + commands.unregisterAll() + observable?.unsubscribe() + } +} \ No newline at end of file diff --git a/plugins/MoarConfirm/build.gradle.kts b/plugins/MoarConfirm/build.gradle.kts new file mode 100755 index 0000000..08ef5be --- /dev/null +++ b/plugins/MoarConfirm/build.gradle.kts @@ -0,0 +1,16 @@ +import com.aliucord.gradle.repo.Authors +import com.aliucord.gradle.repo.author + +plugins { + id("com.aliucord.plugins-repo") +} + +version = "1.0.6" +description = "More confirmations on shit." + +aliucord { + changelog.set(""" + ## 1.0.4 + - Added confirmation for deleting a role + """.trimIndent()) +} \ No newline at end of file diff --git a/plugins/MoarConfirm/src/main/kotlin/me/aniimalz/plugins/MoarConfirm.kt b/plugins/MoarConfirm/src/main/kotlin/me/aniimalz/plugins/MoarConfirm.kt new file mode 100755 index 0000000..617cfa0 --- /dev/null +++ b/plugins/MoarConfirm/src/main/kotlin/me/aniimalz/plugins/MoarConfirm.kt @@ -0,0 +1,201 @@ +package me.aniimalz.plugins + +import android.content.Context +import android.view.MenuItem +import android.widget.Button +import androidx.fragment.app.FragmentManager +import com.aliucord.Utils +import com.aliucord.annotations.AliucordPlugin +import com.aliucord.entities.Plugin +import com.aliucord.fragments.ConfirmDialog +import com.aliucord.patcher.* +import com.aliucord.wrappers.GuildRoleWrapper.Companion.name +import com.discord.app.AppComponent +import com.discord.app.AppPermissionsRequests +import com.discord.databinding.WidgetUserSheetBinding +import com.discord.widgets.servers.`WidgetServerSettingsEditRole$setupMenu$1` +import com.discord.widgets.user.calls.PrivateCallLauncher +import com.discord.widgets.user.usersheet.WidgetUserSheet +import com.discord.widgets.user.usersheet.WidgetUserSheetViewModel +import com.discord.widgets.voice.call.PrivateCallLaunchUtilsKt +import de.robv.android.xposed.XposedBridge + +@AliucordPlugin +class MoarConfirm : Plugin() { + init { + settingsTab = + SettingsTab(MoarSettings::class.java, SettingsTab.Type.PAGE).withArgs(settings) + } + + private var permRequests: AppPermissionsRequests? = null + private var appComponent: AppComponent? = null + private var callFragManager: FragmentManager? = null + + override fun start(ctx: Context) { + patchCallButtons() + patchUserSheet() + patchRoleContext() + } + + private fun patchRoleContext() { + // confirmation for deleting roles + if (!settings.getBool("deleteRoleConfirm", true)) return + val args = arrayOf(MenuItem::class.java, Context::class.java) + patcher.before<`WidgetServerSettingsEditRole$setupMenu$1`>( + "call", + *args + ) { + val uh = this.`$data`.role + val cf = ConfirmDialog().apply { + setTitle("Delete Role") + setDescription("Do you want to delete ${uh.name}?") + setOnOkListener { _ -> + XposedBridge.invokeOriginalMethod( + `WidgetServerSettingsEditRole$setupMenu$1`::class.java.getDeclaredMethod( + "call", + *args + ), + it.thisObject, + arrayOf(it.args[0], it.args[1]) + ) + } + } + cf.show(Utils.appActivity.supportFragmentManager, "confirm_delete_role") + it.result = null + } + } + + private fun patchCallButtons() { + val permReqField = + PrivateCallLauncher::class.java.getDeclaredField("appPermissionsRequests") + .apply { isAccessible = true } + val appComponentField = PrivateCallLauncher::class.java.getDeclaredField("appComponent") + .apply { isAccessible = true } + val fragManangerField = PrivateCallLauncher::class.java.getDeclaredField("fragmentManager") + .apply { isAccessible = true } + + patcher.patch( + PrivateCallLauncher::class.java.getDeclaredMethod( + "launchVoiceCall", + Long::class.javaPrimitiveType + ), PreHook { + if (!settings.getBool("callConfirm", true)) return@PreHook + permRequests = permReqField.get(it.thisObject) as AppPermissionsRequests + appComponent = appComponentField.get(it.thisObject) as AppComponent + callFragManager = fragManangerField.get(it.thisObject) as FragmentManager + val userId = it.args[0] as Long + val confirmCall = ConfirmDialog().apply { + setTitle("Confirm Call") + setOnOkListener { + dismiss() + callUser(Utils.appContext, userId, false) + } + setDescription("Do you really want to call this user?") + setOnCancelListener { dismiss() } + } + confirmCall.show(callFragManager!!, "call_confirmation") + @Suppress("UsePropertyAccessSyntax") + it.setResult(null) + }) + + patcher.patch( + PrivateCallLauncher::class.java.getDeclaredMethod( + "launchVideoCall", + Long::class.javaPrimitiveType + ), PreHook { + if (!settings.getBool("callConfirm", true)) return@PreHook + permRequests = permReqField.get(it.thisObject) as AppPermissionsRequests + appComponent = appComponentField.get(it.thisObject) as AppComponent + callFragManager = fragManangerField.get(it.thisObject) as FragmentManager + val userId = it.args[0] as Long + val confirmCall = ConfirmDialog().apply { + setTitle("Confirm Video Call") + setOnOkListener { + dismiss() + callUser(Utils.appContext, userId, true) + } + setDescription("Do you really want to video call this user?") + setOnCancelListener { dismiss() } + } + confirmCall.show(callFragManager!!, "call_confirmation") + @Suppress("UsePropertyAccessSyntax") + it.setResult(null) + }) + } + + private fun patchUserSheet() { + with(WidgetUserSheet::class.java) { + val actionButtons = getDeclaredMethod( + "configureProfileActionButtons", + WidgetUserSheetViewModel.ViewState.Loaded::class.java + ) + actionButtons.apply { isAccessible = true } + patcher.patch(actionButtons, Hook { + + val addFriendId = Utils.getResId("user_sheet_add_friend_action_button", "id") + val userSheet = it.thisObject as WidgetUserSheet + + val root = userSheet.javaClass.getDeclaredMethod("getBinding").let { m -> + m.isAccessible = true + m.invoke(userSheet) as WidgetUserSheetBinding + }.root + val loaded = it.args[0] as WidgetUserSheetViewModel.ViewState.Loaded + val confirmBtn = root.findViewById