Add `mau_appservice_trial_days` config (#12619)
* Add mau_appservice_trial_days * Add a test * Tweaks * changelog * Ensure we sync after the delay * Fix types * Add config statement * Fix test * Reinstate logging that got removed * Fix feature namepull/12634/head
parent
7fbf42499d
commit
2d74a8c178
|
@ -0,0 +1 @@
|
||||||
|
Add new `mau_appservice_trial_days` configuration option to specify a different trial period for users registered via an appservice.
|
|
@ -407,6 +407,11 @@ manhole_settings:
|
||||||
# sign up in a short space of time never to return after their initial
|
# sign up in a short space of time never to return after their initial
|
||||||
# session.
|
# session.
|
||||||
#
|
#
|
||||||
|
# The option `mau_appservice_trial_days` is similar to `mau_trial_days`, but
|
||||||
|
# applies a different trial number if the user was registered by an appservice.
|
||||||
|
# A value of 0 means no trial days are applied. Appservices not listed in this
|
||||||
|
# dictionary use the value of `mau_trial_days` instead.
|
||||||
|
#
|
||||||
# 'mau_limit_alerting' is a means of limiting client side alerting
|
# 'mau_limit_alerting' is a means of limiting client side alerting
|
||||||
# should the mau limit be reached. This is useful for small instances
|
# should the mau limit be reached. This is useful for small instances
|
||||||
# where the admin has 5 mau seats (say) for 5 specific people and no
|
# where the admin has 5 mau seats (say) for 5 specific people and no
|
||||||
|
@ -417,6 +422,8 @@ manhole_settings:
|
||||||
#max_mau_value: 50
|
#max_mau_value: 50
|
||||||
#mau_trial_days: 2
|
#mau_trial_days: 2
|
||||||
#mau_limit_alerting: false
|
#mau_limit_alerting: false
|
||||||
|
#mau_appservice_trial_days:
|
||||||
|
# "appservice-id": 1
|
||||||
|
|
||||||
# If enabled, the metrics for the number of monthly active users will
|
# If enabled, the metrics for the number of monthly active users will
|
||||||
# be populated, however no one will be limited. If limit_usage_by_mau
|
# be populated, however no one will be limited. If limit_usage_by_mau
|
||||||
|
|
|
@ -627,6 +627,20 @@ Example configuration:
|
||||||
mau_trial_days: 5
|
mau_trial_days: 5
|
||||||
```
|
```
|
||||||
---
|
---
|
||||||
|
Config option: `mau_appservice_trial_days`
|
||||||
|
|
||||||
|
The option `mau_appservice_trial_days` is similar to `mau_trial_days`, but applies a different
|
||||||
|
trial number if the user was registered by an appservice. A value
|
||||||
|
of 0 means no trial days are applied. Appservices not listed in this dictionary
|
||||||
|
use the value of `mau_trial_days` instead.
|
||||||
|
|
||||||
|
Example configuration:
|
||||||
|
```yaml
|
||||||
|
mau_appservice_trial_days:
|
||||||
|
my_appservice_id: 3
|
||||||
|
another_appservice_id: 6
|
||||||
|
```
|
||||||
|
---
|
||||||
Config option: `mau_limit_alerting`
|
Config option: `mau_limit_alerting`
|
||||||
|
|
||||||
The option `mau_limit_alerting` is a means of limiting client-side alerting
|
The option `mau_limit_alerting` is a means of limiting client-side alerting
|
||||||
|
|
|
@ -413,6 +413,7 @@ class ServerConfig(Config):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.mau_trial_days = config.get("mau_trial_days", 0)
|
self.mau_trial_days = config.get("mau_trial_days", 0)
|
||||||
|
self.mau_appservice_trial_days = config.get("mau_appservice_trial_days", {})
|
||||||
self.mau_limit_alerting = config.get("mau_limit_alerting", True)
|
self.mau_limit_alerting = config.get("mau_limit_alerting", True)
|
||||||
|
|
||||||
# How long to keep redacted events in the database in unredacted form
|
# How long to keep redacted events in the database in unredacted form
|
||||||
|
@ -1105,6 +1106,11 @@ class ServerConfig(Config):
|
||||||
# sign up in a short space of time never to return after their initial
|
# sign up in a short space of time never to return after their initial
|
||||||
# session.
|
# session.
|
||||||
#
|
#
|
||||||
|
# The option `mau_appservice_trial_days` is similar to `mau_trial_days`, but
|
||||||
|
# applies a different trial number if the user was registered by an appservice.
|
||||||
|
# A value of 0 means no trial days are applied. Appservices not listed in this
|
||||||
|
# dictionary use the value of `mau_trial_days` instead.
|
||||||
|
#
|
||||||
# 'mau_limit_alerting' is a means of limiting client side alerting
|
# 'mau_limit_alerting' is a means of limiting client side alerting
|
||||||
# should the mau limit be reached. This is useful for small instances
|
# should the mau limit be reached. This is useful for small instances
|
||||||
# where the admin has 5 mau seats (say) for 5 specific people and no
|
# where the admin has 5 mau seats (say) for 5 specific people and no
|
||||||
|
@ -1115,6 +1121,8 @@ class ServerConfig(Config):
|
||||||
#max_mau_value: 50
|
#max_mau_value: 50
|
||||||
#mau_trial_days: 2
|
#mau_trial_days: 2
|
||||||
#mau_limit_alerting: false
|
#mau_limit_alerting: false
|
||||||
|
#mau_appservice_trial_days:
|
||||||
|
# "appservice-id": 1
|
||||||
|
|
||||||
# If enabled, the metrics for the number of monthly active users will
|
# If enabled, the metrics for the number of monthly active users will
|
||||||
# be populated, however no one will be limited. If limit_usage_by_mau
|
# be populated, however no one will be limited. If limit_usage_by_mau
|
||||||
|
|
|
@ -215,7 +215,8 @@ class RegistrationWorkerStore(CacheInvalidationWorkerStore):
|
||||||
|
|
||||||
async def is_trial_user(self, user_id: str) -> bool:
|
async def is_trial_user(self, user_id: str) -> bool:
|
||||||
"""Checks if user is in the "trial" period, i.e. within the first
|
"""Checks if user is in the "trial" period, i.e. within the first
|
||||||
N days of registration defined by `mau_trial_days` config
|
N days of registration defined by `mau_trial_days` config or the
|
||||||
|
`mau_appservice_trial_days` config.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
user_id: The user to check for trial status.
|
user_id: The user to check for trial status.
|
||||||
|
@ -226,7 +227,10 @@ class RegistrationWorkerStore(CacheInvalidationWorkerStore):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
now = self._clock.time_msec()
|
now = self._clock.time_msec()
|
||||||
trial_duration_ms = self.config.server.mau_trial_days * 24 * 60 * 60 * 1000
|
days = self.config.server.mau_appservice_trial_days.get(
|
||||||
|
info["appservice_id"], self.config.server.mau_trial_days
|
||||||
|
)
|
||||||
|
trial_duration_ms = days * 24 * 60 * 60 * 1000
|
||||||
is_trial = (now - info["creation_ts"] * 1000) < trial_duration_ms
|
is_trial = (now - info["creation_ts"] * 1000) < trial_duration_ms
|
||||||
return is_trial
|
return is_trial
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
"""Tests REST events for /rooms paths."""
|
"""Tests REST events for /rooms paths."""
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from synapse.api.constants import APP_SERVICE_REGISTRATION_TYPE, LoginType
|
from synapse.api.constants import APP_SERVICE_REGISTRATION_TYPE, LoginType
|
||||||
from synapse.api.errors import Codes, HttpResponseException, SynapseError
|
from synapse.api.errors import Codes, HttpResponseException, SynapseError
|
||||||
from synapse.appservice import ApplicationService
|
from synapse.appservice import ApplicationService
|
||||||
|
@ -229,6 +231,78 @@ class TestMauLimit(unittest.HomeserverTestCase):
|
||||||
self.reactor.advance(100)
|
self.reactor.advance(100)
|
||||||
self.assertEqual(2, self.successResultOf(count))
|
self.assertEqual(2, self.successResultOf(count))
|
||||||
|
|
||||||
|
@override_config(
|
||||||
|
{
|
||||||
|
"mau_trial_days": 3,
|
||||||
|
"mau_appservice_trial_days": {"SomeASID": 1, "AnotherASID": 2},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
def test_as_trial_days(self):
|
||||||
|
user_tokens: List[str] = []
|
||||||
|
|
||||||
|
def advance_time_and_sync():
|
||||||
|
self.reactor.advance(24 * 60 * 61)
|
||||||
|
for token in user_tokens:
|
||||||
|
self.do_sync_for_user(token)
|
||||||
|
|
||||||
|
# Cheekily add an application service that we use to register a new user
|
||||||
|
# with.
|
||||||
|
as_token_1 = "foobartoken1"
|
||||||
|
self.store.services_cache.append(
|
||||||
|
ApplicationService(
|
||||||
|
token=as_token_1,
|
||||||
|
hostname=self.hs.hostname,
|
||||||
|
id="SomeASID",
|
||||||
|
sender="@as_sender_1:test",
|
||||||
|
namespaces={"users": [{"regex": "@as_1.*", "exclusive": True}]},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
as_token_2 = "foobartoken2"
|
||||||
|
self.store.services_cache.append(
|
||||||
|
ApplicationService(
|
||||||
|
token=as_token_2,
|
||||||
|
hostname=self.hs.hostname,
|
||||||
|
id="AnotherASID",
|
||||||
|
sender="@as_sender_2:test",
|
||||||
|
namespaces={"users": [{"regex": "@as_2.*", "exclusive": True}]},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
user_tokens.append(self.create_user("kermit1"))
|
||||||
|
user_tokens.append(self.create_user("kermit2"))
|
||||||
|
user_tokens.append(
|
||||||
|
self.create_user("as_1kermit3", token=as_token_1, appservice=True)
|
||||||
|
)
|
||||||
|
user_tokens.append(
|
||||||
|
self.create_user("as_2kermit4", token=as_token_2, appservice=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Advance time by 1 day to include the first appservice
|
||||||
|
advance_time_and_sync()
|
||||||
|
self.assertEqual(
|
||||||
|
self.get_success(self.store.get_monthly_active_count_by_service()),
|
||||||
|
{"SomeASID": 1},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Advance time by 1 day to include the next appservice
|
||||||
|
advance_time_and_sync()
|
||||||
|
self.assertEqual(
|
||||||
|
self.get_success(self.store.get_monthly_active_count_by_service()),
|
||||||
|
{"SomeASID": 1, "AnotherASID": 1},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Advance time by 1 day to include the native users
|
||||||
|
advance_time_and_sync()
|
||||||
|
self.assertEqual(
|
||||||
|
self.get_success(self.store.get_monthly_active_count_by_service()),
|
||||||
|
{
|
||||||
|
"SomeASID": 1,
|
||||||
|
"AnotherASID": 1,
|
||||||
|
"native": 2,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
def create_user(self, localpart, token=None, appservice=False):
|
def create_user(self, localpart, token=None, appservice=False):
|
||||||
request_data = {
|
request_data = {
|
||||||
"username": localpart,
|
"username": localpart,
|
||||||
|
|
Loading…
Reference in New Issue