Words containing my name should not trigger notifications (Fixes #1781)

It adds a specific behavior for rule with id RuleIds.RULE_ID_CONTAIN_USER_NAME
This commit is contained in:
Benoit Marty 2020-08-25 17:26:16 +02:00 committed by Benoit Marty
parent bf7a096a18
commit 2e618999d9
6 changed files with 49 additions and 25 deletions

View file

@ -9,6 +9,7 @@ Improvements 🙌:
Bugfix 🐛:
- Display name not shown under Settings/General (#1926)
- Words containing my name should not trigger notifications (#1781)
Translations 🗣:
-

View file

@ -18,6 +18,7 @@ package org.matrix.android.sdk.api.pushrules
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.internal.di.MoshiProvider
import org.matrix.android.sdk.internal.util.caseInsensitiveFind
import org.matrix.android.sdk.internal.util.hasSpecialGlobChar
import org.matrix.android.sdk.internal.util.simpleGlobToRegExp
import timber.log.Timber
@ -31,14 +32,18 @@ class EventMatchCondition(
* The glob-style pattern to match against. Patterns with no special glob characters should
* be treated as having asterisks prepended and appended when testing the condition.
*/
val pattern: String
val pattern: String,
/**
* true to match only words. In this case pattern will not be considered as a glob
*/
val wordsOnly: Boolean
) : Condition {
override fun isSatisfied(event: Event, conditionResolver: ConditionResolver): Boolean {
return conditionResolver.resolveEventMatchCondition(event, this)
}
override fun technicalDescription() = "'$key' Matches '$pattern'"
override fun technicalDescription() = "'$key' Matches '$pattern', words only '$wordsOnly'"
fun isSatisfied(event: Event): Boolean {
// TODO encrypted events?
@ -48,14 +53,18 @@ class EventMatchCondition(
// Patterns with no special glob characters should be treated as having asterisks prepended
// and appended when testing the condition.
try {
val modPattern = if (pattern.hasSpecialGlobChar()) pattern.simpleGlobToRegExp() else "*$pattern*".simpleGlobToRegExp()
val regex = Regex(modPattern, RegexOption.DOT_MATCHES_ALL)
return regex.containsMatchIn(value)
return try {
if (wordsOnly) {
value.caseInsensitiveFind(pattern)
} else {
val modPattern = if (pattern.hasSpecialGlobChar()) pattern.simpleGlobToRegExp() else "*$pattern*".simpleGlobToRegExp()
val regex = Regex(modPattern, RegexOption.DOT_MATCHES_ALL)
regex.containsMatchIn(value)
}
} catch (e: Throwable) {
// e.g PatternSyntaxException
Timber.e(e, "Failed to evaluate push condition")
return false
false
}
}

View file

@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.pushrules.ContainsDisplayNameCondition
import org.matrix.android.sdk.api.pushrules.EventMatchCondition
import org.matrix.android.sdk.api.pushrules.Kind
import org.matrix.android.sdk.api.pushrules.RoomMemberCountCondition
import org.matrix.android.sdk.api.pushrules.RuleIds
import org.matrix.android.sdk.api.pushrules.SenderNotificationPermissionCondition
import timber.log.Timber
@ -59,11 +60,11 @@ data class PushCondition(
val iz: String? = null
) {
fun asExecutableCondition(): Condition? {
fun asExecutableCondition(rule: PushRule): Condition? {
return when (Kind.fromString(kind)) {
Kind.EventMatch -> {
if (key != null && pattern != null) {
EventMatchCondition(key, pattern)
EventMatchCondition(key, pattern, rule.ruleId == RuleIds.RULE_ID_CONTAIN_USER_NAME)
} else {
Timber.e("Malformed Event match condition")
null

View file

@ -100,7 +100,7 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
return rules.firstOrNull { rule ->
// All conditions must hold true for an event in order to apply the action for the event.
rule.enabled && rule.conditions?.all {
it.asExecutableCondition()?.isSatisfied(event, conditionResolver) ?: false
it.asExecutableCondition(rule)?.isSatisfied(event, conditionResolver) ?: false
} ?: false
}
}

View file

@ -16,6 +16,12 @@
package org.matrix.android.sdk.api.pushrules
import io.mockk.every
import io.mockk.mockk
import org.amshove.kluent.shouldBe
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.matrix.android.sdk.MatrixTest
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.toContent
@ -24,12 +30,6 @@ import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
import org.matrix.android.sdk.internal.session.room.RoomGetter
import io.mockk.every
import io.mockk.mockk
import org.amshove.kluent.shouldBe
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
class PushrulesConditionTest: MatrixTest {
@ -39,7 +39,7 @@ class PushrulesConditionTest: MatrixTest {
@Test
fun test_eventmatch_type_condition() {
val condition = EventMatchCondition("type", "m.room.message")
val condition = EventMatchCondition("type", "m.room.message", false)
val simpleTextEvent = Event(
type = "m.room.message",
@ -65,7 +65,7 @@ class PushrulesConditionTest: MatrixTest {
@Test
fun test_eventmatch_path_condition() {
val condition = EventMatchCondition("content.msgtype", "m.text")
val condition = EventMatchCondition("content.msgtype", "m.text", false)
val simpleTextEvent = Event(
type = "m.room.message",
@ -86,13 +86,13 @@ class PushrulesConditionTest: MatrixTest {
).toContent(),
originServerTs = 0
).apply {
assert(EventMatchCondition("content.membership", "invite").isSatisfied(this))
assert(EventMatchCondition("content.membership", "invite", false).isSatisfied(this))
}
}
@Test
fun test_eventmatch_cake_condition() {
val condition = EventMatchCondition("content.body", "cake")
val condition = EventMatchCondition("content.body", "cake", false)
Event(
type = "m.room.message",
@ -115,7 +115,7 @@ class PushrulesConditionTest: MatrixTest {
@Test
fun test_eventmatch_cakelie_condition() {
val condition = EventMatchCondition("content.body", "cake*lie")
val condition = EventMatchCondition("content.body", "cake*lie", false)
val simpleTextEvent = Event(
type = "m.room.message",
@ -126,9 +126,22 @@ class PushrulesConditionTest: MatrixTest {
assert(condition.isSatisfied(simpleTextEvent))
}
@Test
fun test_eventmatch_words_only_condition() {
val condition = EventMatchCondition("content.body", "ben", true)
val simpleTextEvent = Event(
type = "m.room.message",
eventId = "mx0",
content = MessageTextContent("m.text", "benoit").toContent(),
originServerTs = 0)
assertFalse(condition.isSatisfied(simpleTextEvent))
}
@Test
fun test_notice_condition() {
val conditionEqual = EventMatchCondition("content.msgtype", "m.notice")
val conditionEqual = EventMatchCondition("content.msgtype", "m.notice", false)
Event(
type = "m.room.message",

View file

@ -26,11 +26,11 @@ import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import com.airbnb.epoxy.EpoxyModelWithHolder
import org.matrix.android.sdk.api.pushrules.getActions
import org.matrix.android.sdk.api.pushrules.rest.PushRule
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.features.notifications.toNotificationAction
import org.matrix.android.sdk.api.pushrules.getActions
import org.matrix.android.sdk.api.pushrules.rest.PushRule
@EpoxyModelClass(layout = R.layout.item_pushrule_raw)
abstract class PushRuleItem : EpoxyModelWithHolder<PushRuleItem.Holder>() {
@ -68,7 +68,7 @@ abstract class PushRuleItem : EpoxyModelWithHolder<PushRuleItem.Holder>() {
val description = StringBuffer()
pushRule.conditions?.forEachIndexed { i, condition ->
if (i > 0) description.append("\n")
description.append(condition.asExecutableCondition()?.technicalDescription()
description.append(condition.asExecutableCondition(pushRule)?.technicalDescription()
?: "UNSUPPORTED")
}
if (description.isBlank()) {