2023-02-04 21:52:26 +01:00
|
|
|
|
package jp.juggler.subwaytooter.push
|
|
|
|
|
|
2023-02-06 10:56:35 +01:00
|
|
|
|
import android.content.Context
|
2023-02-04 21:52:26 +01:00
|
|
|
|
import jp.juggler.crypt.defaultSecurityProvider
|
|
|
|
|
import jp.juggler.crypt.encodeP256Dh
|
|
|
|
|
import jp.juggler.crypt.generateKeyPair
|
|
|
|
|
import jp.juggler.subwaytooter.R
|
|
|
|
|
import jp.juggler.subwaytooter.api.ApiError
|
2023-02-06 10:56:35 +01:00
|
|
|
|
import jp.juggler.subwaytooter.api.TootApiCallback
|
|
|
|
|
import jp.juggler.subwaytooter.api.TootApiClient
|
2023-02-07 13:49:45 +01:00
|
|
|
|
import jp.juggler.subwaytooter.api.entity.*
|
2023-02-04 21:52:26 +01:00
|
|
|
|
import jp.juggler.subwaytooter.api.push.ApiPushMastodon
|
|
|
|
|
import jp.juggler.subwaytooter.pref.PrefDevice
|
|
|
|
|
import jp.juggler.subwaytooter.pref.lazyContext
|
|
|
|
|
import jp.juggler.subwaytooter.pref.prefDevice
|
|
|
|
|
import jp.juggler.subwaytooter.table.*
|
2023-02-05 17:57:44 +01:00
|
|
|
|
import jp.juggler.util.data.*
|
2023-02-04 21:52:26 +01:00
|
|
|
|
import jp.juggler.util.log.LogCategory
|
|
|
|
|
import jp.juggler.util.time.parseTimeIso8601
|
2023-02-06 10:56:35 +01:00
|
|
|
|
import kotlinx.coroutines.isActive
|
2023-02-04 21:52:26 +01:00
|
|
|
|
import java.security.Provider
|
|
|
|
|
import java.security.SecureRandom
|
|
|
|
|
import java.security.interfaces.ECPublicKey
|
2023-02-06 10:56:35 +01:00
|
|
|
|
import kotlin.coroutines.coroutineContext
|
2023-02-04 21:52:26 +01:00
|
|
|
|
|
|
|
|
|
private val log = LogCategory("PushMastodon")
|
|
|
|
|
|
|
|
|
|
class PushMastodon(
|
2023-02-06 10:56:35 +01:00
|
|
|
|
private val context: Context,
|
2023-02-04 21:52:26 +01:00
|
|
|
|
private val api: ApiPushMastodon,
|
|
|
|
|
private val provider: Provider =
|
|
|
|
|
defaultSecurityProvider,
|
|
|
|
|
override val prefDevice: PrefDevice =
|
|
|
|
|
lazyContext.prefDevice,
|
|
|
|
|
override val daoStatus: AccountNotificationStatus.Access =
|
|
|
|
|
AccountNotificationStatus.Access(appDatabase),
|
|
|
|
|
) : PushBase() {
|
|
|
|
|
override suspend fun updateSubscription(
|
|
|
|
|
subLog: SubscriptionLogger,
|
2023-02-06 10:56:35 +01:00
|
|
|
|
account: SavedAccount,
|
2023-02-04 21:52:26 +01:00
|
|
|
|
willRemoveSubscription: Boolean,
|
2023-02-05 16:44:28 +01:00
|
|
|
|
forceUpdate: Boolean,
|
2023-02-04 21:52:26 +01:00
|
|
|
|
) {
|
2023-02-06 10:56:35 +01:00
|
|
|
|
val deviceHash = deviceHash(account)
|
|
|
|
|
val newUrl = snsCallbackUrl(account) // appServerHashを参照する
|
2023-02-04 21:52:26 +01:00
|
|
|
|
if (newUrl.isNullOrEmpty()) {
|
|
|
|
|
if (willRemoveSubscription) {
|
|
|
|
|
val msg =
|
|
|
|
|
lazyContext.getString(R.string.push_subscription_app_server_hash_missing_but_ok)
|
|
|
|
|
subLog.i(msg)
|
|
|
|
|
} else {
|
|
|
|
|
val msg =
|
|
|
|
|
lazyContext.getString(R.string.push_subscription_app_server_hash_missing_error)
|
|
|
|
|
subLog.e(msg)
|
|
|
|
|
daoAccountNotificationStatus.updateSubscriptionError(
|
2023-02-06 10:56:35 +01:00
|
|
|
|
account.acct,
|
2023-02-04 21:52:26 +01:00
|
|
|
|
msg
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val oldSubscription = try {
|
2023-02-06 10:56:35 +01:00
|
|
|
|
api.getPushSubscription(account)
|
2023-02-04 21:52:26 +01:00
|
|
|
|
} catch (ex: Throwable) {
|
|
|
|
|
if ((ex as? ApiError)?.response?.code == 404) {
|
|
|
|
|
null
|
|
|
|
|
} else {
|
|
|
|
|
throw ex
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-02-06 10:56:35 +01:00
|
|
|
|
log.i("${account.acct} oldSubscription=${oldSubscription}")
|
2023-02-04 21:52:26 +01:00
|
|
|
|
|
|
|
|
|
val oldEndpointUrl = oldSubscription?.string("endpoint")
|
|
|
|
|
when (oldEndpointUrl) {
|
|
|
|
|
// 購読がない。作ってもよい
|
|
|
|
|
null -> Unit
|
|
|
|
|
else -> {
|
|
|
|
|
val params = buildMap {
|
|
|
|
|
if (oldEndpointUrl.startsWith(appServerUrlPrefix)) {
|
|
|
|
|
oldEndpointUrl.substring(appServerUrlPrefix.length)
|
|
|
|
|
.split("/")
|
|
|
|
|
.forEach { pair ->
|
|
|
|
|
val cols = pair.split("_", limit = 2)
|
|
|
|
|
cols.elementAtOrNull(0)?.notEmpty()?.let { k ->
|
|
|
|
|
put(k, cols.elementAtOrNull(1) ?: "")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (params["dh"] != deviceHash) {
|
|
|
|
|
// この端末で作成した購読ではない。
|
2023-02-06 10:56:35 +01:00
|
|
|
|
// TODO: 古い形式のURLを移行できないか?
|
|
|
|
|
log.w("deviceHash not match. keep it for other devices. ${account.acct} $oldEndpointUrl")
|
2023-02-05 16:44:28 +01:00
|
|
|
|
subLog.e(R.string.push_subscription_exists_but_not_created_by_this_device)
|
2023-02-04 21:52:26 +01:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (willRemoveSubscription) {
|
|
|
|
|
when (oldSubscription) {
|
|
|
|
|
null -> {
|
2023-02-05 16:44:28 +01:00
|
|
|
|
subLog.i(R.string.push_subscription_is_not_required)
|
2023-02-04 21:52:26 +01:00
|
|
|
|
}
|
|
|
|
|
else -> {
|
2023-02-05 16:44:28 +01:00
|
|
|
|
subLog.i(R.string.push_subscription_delete_current)
|
2023-02-06 10:56:35 +01:00
|
|
|
|
api.deletePushSubscription(account)
|
2023-02-04 21:52:26 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-06 10:56:35 +01:00
|
|
|
|
val newAlerts = account.alerts()
|
|
|
|
|
|
|
|
|
|
val isSameAlert = isSameAlerts(
|
|
|
|
|
subLog = subLog,
|
|
|
|
|
account = account,
|
|
|
|
|
oldSubscriptionJson = oldSubscription,
|
|
|
|
|
newAlerts = newAlerts,
|
|
|
|
|
)
|
2023-02-04 21:52:26 +01:00
|
|
|
|
|
2023-02-06 10:56:35 +01:00
|
|
|
|
// https://github.com/mastodon/mastodon/pull/23210
|
|
|
|
|
// ポリシーの変更をチェックできるようになるのは4.1くらい?
|
|
|
|
|
val isSamePolicy = true // account.pushPolicy == oldSubscription.
|
|
|
|
|
|
|
|
|
|
if (!forceUpdate && isSameAlert && isSamePolicy &&
|
|
|
|
|
newUrl == oldEndpointUrl
|
|
|
|
|
) {
|
|
|
|
|
// 現在の更新を使い続ける
|
2023-02-05 16:44:28 +01:00
|
|
|
|
subLog.i(R.string.push_subscription_keep_using)
|
|
|
|
|
return
|
|
|
|
|
}
|
2023-02-04 21:52:26 +01:00
|
|
|
|
|
2023-02-06 10:56:35 +01:00
|
|
|
|
if (newUrl == oldEndpointUrl) {
|
|
|
|
|
subLog.i(R.string.push_subscription_exists_updateing)
|
|
|
|
|
api.updatePushSubscriptionData(
|
|
|
|
|
a = account,
|
|
|
|
|
alerts = newAlerts,
|
|
|
|
|
policy = account.pushPolicy ?: "all",
|
|
|
|
|
)
|
|
|
|
|
subLog.i(R.string.push_subscription_updated)
|
|
|
|
|
} else {
|
|
|
|
|
subLog.i(R.string.push_subscription_creating)
|
|
|
|
|
val keyPair = provider.generateKeyPair()
|
|
|
|
|
val auth = ByteArray(16).also { SecureRandom().nextBytes(it) }
|
|
|
|
|
val p256dh = encodeP256Dh(keyPair.public as ECPublicKey)
|
|
|
|
|
val response = api.createPushSubscription(
|
|
|
|
|
a = account,
|
|
|
|
|
endpointUrl = newUrl,
|
|
|
|
|
p256dh = p256dh.encodeBase64Url(),
|
|
|
|
|
auth = auth.encodeBase64Url(),
|
|
|
|
|
alerts = newAlerts,
|
|
|
|
|
policy = account.pushPolicy ?: "all",
|
|
|
|
|
)
|
|
|
|
|
val serverKeyStr = response.string("server_key")
|
|
|
|
|
?: error("missing server_key.")
|
|
|
|
|
|
|
|
|
|
val serverKey = serverKeyStr.decodeBase64()
|
|
|
|
|
|
|
|
|
|
// p256dhは65バイトのはず
|
|
|
|
|
// authは16バイトのはず
|
|
|
|
|
// serverKeyは65バイトのはず
|
|
|
|
|
|
|
|
|
|
// 登録できたらアカウントに覚える
|
|
|
|
|
daoStatus.savePushKey(
|
|
|
|
|
acct = account.acct,
|
|
|
|
|
pushKeyPrivate = keyPair.private.encoded,
|
|
|
|
|
pushKeyPublic = p256dh,
|
|
|
|
|
pushAuthSecret = auth,
|
|
|
|
|
pushServerKey = serverKey,
|
|
|
|
|
lastPushEndpoint = newUrl,
|
|
|
|
|
)
|
|
|
|
|
subLog.i(R.string.push_subscription_completed)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private suspend fun isSameAlerts(
|
|
|
|
|
subLog: SubscriptionLogger,
|
|
|
|
|
account: SavedAccount,
|
|
|
|
|
oldSubscriptionJson: JsonObject?,
|
|
|
|
|
newAlerts: JsonObject,
|
|
|
|
|
): Boolean {
|
|
|
|
|
oldSubscriptionJson ?: return false
|
|
|
|
|
val oldSubscription = TootPushSubscription(oldSubscriptionJson)
|
|
|
|
|
|
|
|
|
|
// STがstatus通知に対応した時期に古いサーバでここを通ると
|
|
|
|
|
// flagsの値が変わりendpoint URLも変わった状態で購読を自動更新してしまう
|
|
|
|
|
// しかしそのタイミングではサーバは古いのでサーバ側の購読内容は変化しなかった。
|
|
|
|
|
|
|
|
|
|
// サーバ上の購読アラートのリスト
|
|
|
|
|
var alertsOld = oldSubscription.alerts.entries
|
|
|
|
|
.mapNotNull { if (it.value) it.key else null }
|
|
|
|
|
.sorted()
|
|
|
|
|
|
|
|
|
|
// 期待する購読アラートのリスト
|
|
|
|
|
var alertsNew = newAlerts.entries
|
|
|
|
|
.mapNotNull { pair -> pair.key.takeIf { pair.value == true } }
|
|
|
|
|
.sorted()
|
|
|
|
|
|
|
|
|
|
// 両方に共通するアラートは除去する
|
|
|
|
|
val bothHave = alertsOld.filter { alertsNew.contains(it) }
|
|
|
|
|
alertsOld = alertsOld.filter { !bothHave.contains(it) }
|
|
|
|
|
alertsNew = alertsNew.filter { !bothHave.contains(it) }
|
|
|
|
|
|
|
|
|
|
// サーバのバージョンを調べる前に、この時点でalertsが一致するか確認する
|
|
|
|
|
if (alertsOld.joinToString(",") == alertsNew.joinToString(",")) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// サーバのバージョンを見て、サーバの知らないalertを無視して比較する
|
|
|
|
|
val client = TootApiClient(context, callback = object : TootApiCallback {
|
|
|
|
|
override suspend fun isApiCancelled(): Boolean = !coroutineContext.isActive
|
|
|
|
|
})
|
|
|
|
|
client.account = account
|
|
|
|
|
val ti = TootInstance.getExOrThrow(client)
|
|
|
|
|
|
|
|
|
|
alertsOld = alertsOld.knownOnly(account, ti)
|
|
|
|
|
alertsNew = alertsNew.knownOnly(account, ti)
|
|
|
|
|
return if (alertsOld.joinToString(",") == alertsNew.joinToString(",")) {
|
|
|
|
|
log.i("${account.acct}: same alerts(2)")
|
|
|
|
|
true
|
2023-02-07 13:49:45 +01:00
|
|
|
|
} else {
|
2023-02-06 10:56:35 +01:00
|
|
|
|
log.i("${account.acct}: changed. old=${alertsOld.sorted()}, new=${alertsNew.sorted()}")
|
|
|
|
|
subLog.i("notification type set changed.")
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun SavedAccount.alerts() = JsonObject().apply {
|
|
|
|
|
// Mastodon's Notification::TYPES in
|
|
|
|
|
// in https://github.com/mastodon/mastodon/blob/main/app/models/notification.rb#L30
|
|
|
|
|
put(TootNotification.TYPE_ADMIN_REPORT, notificationFollow)
|
|
|
|
|
put(TootNotification.TYPE_ADMIN_SIGNUP, notificationFollow) // 設定項目不足
|
|
|
|
|
put(TootNotification.TYPE_FAVOURITE, notificationFavourite)
|
|
|
|
|
put(TootNotification.TYPE_FOLLOW, notificationFollow)
|
|
|
|
|
put(TootNotification.TYPE_FOLLOW_REQUEST, notificationFollowRequest)
|
|
|
|
|
put(TootNotification.TYPE_MENTION, notificationMention)
|
|
|
|
|
put(TootNotification.TYPE_POLL, notificationVote)
|
|
|
|
|
put(TootNotification.TYPE_REBLOG, notificationBoost)
|
|
|
|
|
put(TootNotification.TYPE_STATUS, notificationPost)
|
|
|
|
|
put(TootNotification.TYPE_UPDATE, notificationUpdate)
|
|
|
|
|
// fedibird拡張
|
|
|
|
|
// https://github.com/fedibird/mastodon/blob/fedibird/app/controllers/api/v1/push/subscriptions_controller.rb#L55
|
|
|
|
|
// https://github.com/fedibird/mastodon/blob/fedibird/app/models/notification.rb
|
|
|
|
|
put(TootNotification.TYPE_EMOJI_REACTION, notificationReaction)
|
|
|
|
|
put(TootNotification.TYPE_SCHEDULED_STATUS, notificationPost) // 設定項目不足
|
|
|
|
|
put(TootNotification.TYPE_STATUS_REFERENCE, notificationStatusReference)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// サーバが知らないアラート種別は比較対象から除去する
|
|
|
|
|
private fun Iterable<String>.knownOnly(account: SavedAccount, ti: TootInstance) = filter {
|
|
|
|
|
when (it) {
|
|
|
|
|
TootNotification.TYPE_ADMIN_REPORT -> ti.versionGE(TootInstance.VERSION_4_0_0)
|
|
|
|
|
TootNotification.TYPE_ADMIN_SIGNUP -> ti.versionGE(TootInstance.VERSION_3_5_0_rc1)
|
|
|
|
|
TootNotification.TYPE_FAVOURITE -> true
|
|
|
|
|
TootNotification.TYPE_FOLLOW -> true
|
|
|
|
|
TootNotification.TYPE_FOLLOW_REQUEST -> ti.versionGE(TootInstance.VERSION_3_1_0_rc1)
|
|
|
|
|
TootNotification.TYPE_MENTION -> true
|
|
|
|
|
TootNotification.TYPE_POLL -> ti.versionGE(TootInstance.VERSION_2_8_0_rc1)
|
|
|
|
|
TootNotification.TYPE_REBLOG -> true
|
|
|
|
|
TootNotification.TYPE_STATUS -> ti.versionGE(TootInstance.VERSION_3_3_0_rc1)
|
|
|
|
|
TootNotification.TYPE_UPDATE -> ti.versionGE(TootInstance.VERSION_3_5_0_rc1)
|
|
|
|
|
|
|
|
|
|
//////////////////////
|
|
|
|
|
// Fedibird拡張
|
|
|
|
|
|
|
|
|
|
TootNotification.TYPE_EMOJI_REACTION,
|
|
|
|
|
TootNotification.TYPE_EMOJI_REACTION_PLEROMA,
|
|
|
|
|
-> InstanceCapability.emojiReaction(account, ti)
|
|
|
|
|
|
|
|
|
|
TootNotification.TYPE_SCHEDULED_STATUS,
|
|
|
|
|
-> InstanceCapability.scheduledStatus(account, ti)
|
|
|
|
|
|
|
|
|
|
TootNotification.TYPE_STATUS_REFERENCE,
|
|
|
|
|
-> InstanceCapability.statusReference(account, ti)
|
|
|
|
|
|
|
|
|
|
else -> {
|
|
|
|
|
log.w("${account.acct}: unknown alert '$it'. server version='${ti.version}'")
|
|
|
|
|
false // 未知のアラートの差異は比較しない。でないと購読を何度も繰り返すことになる
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-02-04 21:52:26 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override suspend fun formatPushMessage(
|
|
|
|
|
a: SavedAccount,
|
|
|
|
|
pm: PushMessage,
|
|
|
|
|
) {
|
2023-02-07 13:49:45 +01:00
|
|
|
|
val json = pm.messageJson ?: error("missing messageJson")
|
2023-02-04 21:52:26 +01:00
|
|
|
|
|
|
|
|
|
pm.notificationType = json.string("notification_type")
|
2023-02-07 13:49:45 +01:00
|
|
|
|
pm.iconLarge = a.supplyBaseUrl(json.string("icon"))
|
|
|
|
|
|
2023-02-04 21:52:26 +01:00
|
|
|
|
pm.text = arrayOf(
|
|
|
|
|
// あなたのトゥートが tateisu 🤹 さんにお気に入り登録されました
|
|
|
|
|
json.string("title"),
|
2023-02-07 13:49:45 +01:00
|
|
|
|
).mapNotNull { it?.trim()?.notBlank() }
|
|
|
|
|
.joinToString("\n")
|
|
|
|
|
.ellipsizeDot3(128)
|
|
|
|
|
|
2023-02-05 17:34:51 +01:00
|
|
|
|
pm.textExpand = arrayOf(
|
|
|
|
|
// あなたのトゥートが tateisu 🤹 さんにお気に入り登録されました
|
|
|
|
|
json.string("title"),
|
2023-02-04 21:52:26 +01:00
|
|
|
|
// 対象の投稿の本文?
|
|
|
|
|
json.string("body"),
|
|
|
|
|
// 対象の投稿の本文? (古い
|
|
|
|
|
json.jsonObject("data")?.string("content"),
|
2023-02-07 13:49:45 +01:00
|
|
|
|
).mapNotNull { it?.trim()?.notBlank() }
|
|
|
|
|
.joinToString("\n")
|
|
|
|
|
.ellipsizeDot3(400)
|
|
|
|
|
|
2023-02-04 21:52:26 +01:00
|
|
|
|
when {
|
|
|
|
|
pm.notificationType.isNullOrEmpty() -> {
|
|
|
|
|
// old mastodon
|
|
|
|
|
// {
|
|
|
|
|
// "title": "あなたのトゥートが tateisu 🤹 さんにお気に入り登録されました",
|
|
|
|
|
// "image": null,
|
|
|
|
|
// "badge": "https://mastodon2.juggler.jp/badge.png",
|
|
|
|
|
// "tag": 84,
|
|
|
|
|
// "timestamp": "2018-05-11T17:06:42.887Z",
|
|
|
|
|
// "icon": "/system/accounts/avatars/000/000/003/original/72f1da33539be11e.jpg",
|
|
|
|
|
// "data": {
|
|
|
|
|
// "content": ":enemy_bullet:",
|
|
|
|
|
// "nsfw": null,
|
|
|
|
|
// "url": "https://mastodon2.juggler.jp/web/statuses/98793123081777841",
|
|
|
|
|
// "actions": [],
|
|
|
|
|
// "access_token": null,
|
|
|
|
|
// "message": "%{count} 件の通知",
|
|
|
|
|
// "dir": "ltr"
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
json.string("timestamp")?.parseTimeIso8601()
|
|
|
|
|
?.let { pm.timestamp = it }
|
|
|
|
|
|
|
|
|
|
// 重複排除は完全に諦める
|
|
|
|
|
pm.notificationId = pm.timestamp.toString()
|
|
|
|
|
|
2023-02-07 13:49:45 +01:00
|
|
|
|
pm.iconSmall = a.supplyBaseUrl(json.string("badge"))
|
2023-02-04 21:52:26 +01:00
|
|
|
|
}
|
|
|
|
|
else -> {
|
|
|
|
|
// Mastodon 4.0
|
|
|
|
|
// {
|
|
|
|
|
// "access_token": "***",
|
|
|
|
|
// "preferred_locale": "ja",
|
|
|
|
|
// "notification_id": 341897,
|
|
|
|
|
// "notification_type": "favourite",
|
|
|
|
|
// "icon": "https://m1j.zzz.ac/aed1...e5343f2e7b.png",
|
|
|
|
|
// "title": "tateisu⛏️@テスト鯖 :ct080:さんにお気に入りに登録されました",
|
|
|
|
|
// "body": "テスト"
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
pm.notificationId = json.string("notification_id")
|
|
|
|
|
|
|
|
|
|
// - iconSmall は通知タイプに合わせてアプリが用意するらしい
|
|
|
|
|
// - タイムスタンプ情報はない。
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-02-07 13:49:45 +01:00
|
|
|
|
|
|
|
|
|
// 通知のミュートについて:
|
|
|
|
|
// - アプリ名がないのでアプリ名ミュートは使えない
|
|
|
|
|
// - notification.user のfull acct がないのでふぁぼ魔ミュートは行えない
|
|
|
|
|
// - テキスト本文のミュートは…部分的には可能
|
|
|
|
|
|
|
|
|
|
if(pm.textExpand?.let{TootStatus.muted_word?.matchShort(it)}==true){
|
|
|
|
|
error("muted by text word.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// // ふぁぼ魔ミュート
|
|
|
|
|
// when ( pm.notificationType) {
|
|
|
|
|
// TootNotification.TYPE_REBLOG,
|
|
|
|
|
// TootNotification.TYPE_FAVOURITE,
|
|
|
|
|
// TootNotification.TYPE_FOLLOW,
|
|
|
|
|
// TootNotification.TYPE_FOLLOW_REQUEST,
|
|
|
|
|
// TootNotification.TYPE_FOLLOW_REQUEST_MISSKEY,
|
|
|
|
|
// -> {
|
|
|
|
|
// val whoAcct = a.getFullAcct(user)
|
|
|
|
|
// if (TootStatus.favMuteSet?.contains(whoAcct) == true) {
|
|
|
|
|
// error("muted by favMuteSet ${whoAcct.pretty}")
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// }
|
2023-02-04 21:52:26 +01:00
|
|
|
|
}
|
|
|
|
|
}
|