last commit before migration from GitHub

This commit is contained in:
particle-box
2026-01-31 07:55:39 +05:30
parent c9052cba26
commit c5f164ccc8
38 changed files with 720 additions and 261 deletions

View File

@@ -100,7 +100,7 @@ class ModContext(
runCatching {
runnable()
}.onFailure {
longToast("Async task failed: " + it.message)
longToast(translation.format("toast_async_task_failed", "message" to (it.message ?: "")))
log.error("Async task failed", it)
}
}
@@ -139,7 +139,7 @@ class ModContext(
fun logCritical(message: Any?, throwable: Throwable = Throwable()) {
log.error(message ?: "Snapchat crash", throwable)
longToast(message ?: "Snapchat has crashed! Please check logs for more details.")
longToast(message ?: translation["toast_snapchat_crashed"])
}
private fun delayForceCloseApp(delay: Long) = Handler(Looper.getMainLooper()).postDelayed({

View File

@@ -198,7 +198,7 @@ class PurrfectSnap {
log.verbose("Features initialized successfully")
}.onFailure { throwable ->
log.error("Failed to initialize features", throwable)
longToast("Failed to initialize features! Some functionality may not work properly.")
longToast(appContext.translation["toast_init_features_failed"])
// Continue with other initializations even if features fail
}
@@ -209,7 +209,7 @@ class PurrfectSnap {
log.verbose("Script runtime initialized successfully")
}.onFailure { throwable ->
log.error("Failed to initialize script runtime", throwable)
longToast("Failed to initialize script runtime!")
longToast(appContext.translation["toast_init_script_runtime_failed"])
}
}
}

View File

@@ -299,13 +299,15 @@ class ExportMemories : AbstractAction() {
val exportedPath = runCatching { outputTarget.finalize(outputZip) }
.getOrElse { error ->
context.log.error("Failed to finalize memories export", error)
context.longToast("Failed to export memories")
context.longToast(context.translation["toast_export_memories_failed"])
return
}
if (outputZip.parentFile == context.androidContext.cacheDir) {
outputZip.delete()
}
context.longToast("Exported to $exportedPath")
context.longToast(
context.translation.format("toast_exported_to_path", "path" to exportedPath)
)
}
@OptIn(ExperimentalMaterial3Api::class)
@@ -863,7 +865,7 @@ class ExportMemories : AbstractAction() {
}.getOrNull()
if (database == null) {
context.longToast("Failed to open memories database")
context.longToast(context.translation["toast_open_memories_db_failed"])
return@launch
}

View File

@@ -87,7 +87,7 @@ class ManageFriendList : AbstractAction() {
private fun addFriend(userId: String) {
val friendRelationshipChangerInstance = context.feature(AddFriendSourceSpoof::class).friendRelationshipChangerInstance
?: run {
context.longToast("Failed to add friend: FriendRelationshipChanger instance not available")
context.longToast(context.translation["toast_friend_add_unavailable"])
return
}
@@ -153,7 +153,9 @@ class ManageFriendList : AbstractAction() {
}
}.onFailure {
context.log.error("Failed to add friend $userId", it)
context.longToast("Failed to add friend: ${it.message}")
context.longToast(
context.translation.format("toast_friend_add_failed", "message" to (it.message ?: ""))
)
}
}
}
@@ -173,7 +175,9 @@ class ManageFriendList : AbstractAction() {
context.androidContext.contentResolver.openOutputStream(data)?.bufferedWriter()?.use { writer ->
userIds.forEach { writer.write(it); writer.newLine() }
}
context.longToast("Exported ${userIds.size} friends!")
context.longToast(
context.translation.format("toast_friends_exported", "count" to userIds.size.toString())
)
}
context.mainActivity?.startActivityForResult(
Intent.createChooser(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
@@ -303,7 +307,12 @@ class ManageFriendList : AbstractAction() {
fetchedFriends = context.androidContext.contentResolver.openInputStream(data)?.bufferedReader()?.readLines()?.filter { it.matches(uuidRegex) }?.map { it.trim() }?.toMutableList() ?: mutableListOf()
}.onFailure {
context.log.error("Failed to import friends", it)
context.longToast("Failed to import friends: ${it.message}")
context.longToast(
context.translation.format(
"toast_friends_import_failed",
"message" to (it.message ?: "")
)
)
}
}
context.mainActivity?.startActivityForResult(Intent.createChooser(Intent(Intent.ACTION_GET_CONTENT).apply { type = "*/*" }, "Select a file"), pendingPickerAction!!.first)

View File

@@ -99,7 +99,12 @@ class DatabaseAccess(
context.log.error("Failed to execute query $query", it)
return@onFailure
}
context.longToast("Database ${this.path} is corrupted! Restarting ...")
context.longToast(
context.translation.format(
"toast_database_corrupted",
"path" to this.path
)
)
context.androidContext.deleteDatabase(this.path)
context.crash("Database ${this.path} is corrupted!", it)
}.getOrNull()
@@ -616,4 +621,4 @@ class DatabaseAccess(
}
}
}
}
}

View File

@@ -164,7 +164,12 @@ class FeatureManager(
}
}.onFailure {
context.log.error("Failed to init feature ${feature.key}", it)
context.longToast("Failed to init feature ${feature.key}! Check logcat for more details.")
context.longToast(
context.translation.format(
"toast_feature_init_failed",
"feature" to feature.key
)
)
}
}
}

View File

@@ -48,6 +48,7 @@ import java.util.zip.ZipOutputStream
import kotlin.random.Random
class AccountSwitcher: Feature("Account Switcher") {
private val translation by lazy { context.translation.getCategory("account_switcher_ui") }
private var exportCallback: Pair<Int, String>? = null // requestCode -> userId
private var importRequestCode: Int? = null
@@ -107,7 +108,9 @@ class AccountSwitcher: Feature("Account Switcher") {
onClick = {
runCatching {
if (!isLoginActivity && context.database.myUserId == user.first) {
context.shortToast("Already logged in as ${user.second}")
context.shortToast(
translation.format("already_logged_in", "username" to user.second)
)
return@runCatching
}
@@ -117,7 +120,7 @@ class AccountSwitcher: Feature("Account Switcher") {
login(userId = user.first, username = user.second)
}.onFailure {
context.shortToast("Failed to login. Check logs for more info.")
context.shortToast(translation["login_failed_toast"])
context.log.error("Failed to login", it)
}
}
@@ -259,7 +262,7 @@ class AccountSwitcher: Feature("Account Switcher") {
private fun logout() {
context.androidContext.dataDir.resolve( "shared_prefs/user_session_shared_pref.xml").takeIf { it.exists() }?.delete()
context.shortToast("Logged out")
context.shortToast(translation["logged_out_toast"])
context.softRestartApp()
}
@@ -268,7 +271,7 @@ class AccountSwitcher: Feature("Account Switcher") {
ParcelFileDescriptor.AutoCloseInputStream(pfd).use { it.readBytes() }
}
if (accountData == null) {
context.shortToast("Account data not found")
context.shortToast(translation["data_not_found_toast"])
return
}
@@ -312,12 +315,12 @@ class AccountSwitcher: Feature("Account Switcher") {
zipInputStream.close()
} catch (e: Exception) {
context.log.error("Failed to restore account data", e)
context.shortToast("Failed to restore account data")
context.shortToast(translation["restore_failed_toast"])
return
}
context.log.debug("Account data restored")
context.shortToast("Logged in as $username")
context.shortToast(translation.format("logged_in_as_toast", "username" to username))
context.softRestartApp()
}
@@ -390,9 +393,9 @@ class AccountSwitcher: Feature("Account Switcher") {
context.database.getFriendInfo(context.database.myUserId)?.mutableUsername ?: "Unknown username",
getCurrentAccountData()
)
context.shortToast("Account backed up!")
context.shortToast(translation["backup_success_toast"])
}.onFailure {
context.shortToast("Failed to backup account. Check logs for more info.")
context.shortToast(translation["backup_failure_toast"])
context.log.error("Failed to backup account", it)
}
}
@@ -465,11 +468,13 @@ class AccountSwitcher: Feature("Account Switcher") {
it.toParcelFileDescriptor(context.coroutineScope)
)
}
context.shortToast("Imported $username!")
context.shortToast(translation.format("import_success_toast", "username" to username))
updateUsers()
}
}.onFailure {
context.shortToast("Failed to import account: ${it.message}")
context.shortToast(
translation.format("import_failure_toast", "message" to (it.message ?: ""))
)
context.log.error("Failed to import account", it)
}
@@ -522,10 +527,10 @@ class AccountSwitcher: Feature("Account Switcher") {
it.copyTo(outputStream)
}
}
context.shortToast("Account exported!")
context.shortToast(translation["export_success_toast"])
}
}.onFailure {
context.shortToast("Failed to export account. Check logs for more info.")
context.shortToast(translation["export_failed_toast"])
context.log.error("Failed to export account", it)
}
}
@@ -539,10 +544,10 @@ class AccountSwitcher: Feature("Account Switcher") {
runCatching {
val accountStorage = context.bridgeClient.getAccountStorage()
if (accountStorage.isAccountExists(context.database.myUserId)) {
accountStorage.removeAccount(context.database.myUserId)
context.shortToast("Removed account due to forced logout")
}
if (accountStorage.isAccountExists(context.database.myUserId)) {
accountStorage.removeAccount(context.database.myUserId)
context.shortToast(translation["forced_logout_toast"])
}
}
return@hook
}

View File

@@ -75,12 +75,22 @@ class EndToEndEncryption : MessagingRuleFeature(
private fun askForKeys(conversationId: String) {
val friendId = context.database.getDMOtherParticipant(conversationId) ?: run {
context.longToast("Can't find friendId for conversationId $conversationId")
context.longToast(
translation.format(
"missing_friend_id_toast",
"conversationId" to conversationId
)
)
return
}
val publicKey = e2eeInterface.createKeyExchange(friendId) ?: run {
context.longToast("Can't create key exchange for friendId $friendId")
context.longToast(
translation.format(
"key_exchange_failed_toast",
"friendId" to friendId
)
)
return
}
@@ -131,7 +141,12 @@ class EndToEndEncryption : MessagingRuleFeature(
private fun handlePublicKeyRequest(conversationId: String, publicKey: ByteArray) {
val friendId = context.database.getDMOtherParticipant(conversationId) ?: run {
context.longToast("Can't find friendId for conversationId $conversationId")
context.longToast(
translation.format(
"missing_friend_id_toast",
"conversationId" to conversationId
)
)
return
}
warnKeyOverwrite(friendId) {
@@ -151,7 +166,12 @@ class EndToEndEncryption : MessagingRuleFeature(
private fun handleSecretResponse(conversationId: String, secret: ByteArray) {
val friendId = context.database.getDMOtherParticipant(conversationId) ?: run {
context.longToast("Can't find friendId for conversationId $conversationId")
context.longToast(
translation.format(
"missing_friend_id_toast",
"conversationId" to conversationId
)
)
return
}
warnKeyOverwrite(friendId) {
@@ -561,4 +581,4 @@ class EndToEndEncryption : MessagingRuleFeature(
}
override fun getRuleState() = RuleState.WHITELIST
}
}

View File

@@ -396,7 +396,7 @@ class AutoDeleteSentMessages : MessagingRuleFeature("Auto Delete Sent Messages",
notificationManager.cancel(9999)
Handler(Looper.getMainLooper()).post {
context.shortToast("Auto delete queue cleared")
context.shortToast(translation["queue_cleared_toast"])
}
}
}

View File

@@ -91,7 +91,7 @@ class MessageTranslator : Feature("Instant Translation") {
if (config.pauseOnError.get()) {
isPaused = true
context.log.warn("Translation paused due to errors")
context.shortToast("Translation service temporarily unavailable")
context.shortToast(context.translation["toast_translation_service_unavailable"])
}
}
}

View File

@@ -192,7 +192,12 @@ class Notifications : Feature("Notifications") {
val myUser = context.database.myUserId.let { context.database.getFriendInfo(it) } ?: return@subscribe
context.messageSender.sendChatMessage(listOf(SnapUUID(conversationId)), input, onError = {
context.longToast("Failed to send message: $it")
context.longToast(
context.translation.format(
"toast_send_message_failed",
"error" to it.toString()
)
)
context.coroutineScope.launch(coroutineDispatcher) {
appendNotificationText("Failed to send message: $it")
}
@@ -221,7 +226,7 @@ class Notifications : Feature("Notifications") {
onResult = {
if (it != null) {
context.log.error("Failed to mark conversation as read: $it")
context.shortToast("Failed to mark conversation as read")
context.shortToast(context.translation["toast_mark_conversation_read_failed"])
}
}
)
@@ -245,7 +250,7 @@ class Notifications : Feature("Notifications") {
},
onError = {
context.log.error("Failed to fetch conversation: $it")
context.shortToast("Failed to fetch conversation")
context.shortToast(context.translation["toast_fetch_conversation_failed"])
}
)
}
@@ -257,13 +262,13 @@ class Notifications : Feature("Notifications") {
conversationManager.updateMessage(conversationId, clientMessageId, MessageUpdate.READ) {
if (it != null) {
context.log.error("Failed to open snap: $it")
context.shortToast("Failed to open snap")
context.shortToast(context.translation["toast_open_snap_failed"])
}
}
}
}.onFailure {
context.log.error("Failed to mark message as read", it)
context.shortToast("Failed to mark message as read. Check logs for more details")
context.shortToast(context.translation["toast_mark_message_read_failed"])
}
notificationManager.cancel(notificationId)
}

View File

@@ -113,7 +113,7 @@ class ConversationToolbox : Feature("Conversation Toolbox") {
private fun openToolbox() {
val openedConversationId = context.feature(Messaging::class).openedConversationUUID?.toString() ?: run {
context.shortToast("You must open a conversation first")
context.shortToast(context.translation["toast_open_conversation_first"])
return
}

View File

@@ -59,19 +59,19 @@ object LSPatchUpdater {
}
context.log.verbose("updating", TAG)
context.shortToast("Updating PurrfectSnap. Please wait...")
context.shortToast(context.translation["toast_updating_purrfectsnap"])
// copy embedded module to cache
runCatching {
seAppApk.copyTo(embeddedModule, overwrite = true)
}.onFailure {
seAppApk.delete()
context.log.error("Failed to copy embedded module", it, TAG)
context.longToast("Failed to update PurrfectSnap. Please check logcat for more details.")
context.longToast(context.translation["toast_update_purrfectsnap_failed"])
context.forceCloseApp()
return
}
context.longToast("PurrfectSnap updated!")
context.longToast(context.translation["toast_purrfectsnap_updated"])
context.log.verbose("updated", TAG)
context.softRestartApp()
}