Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Add mau_appservice_trial_days config #12619

Merged
merged 10 commits into from
May 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/12619.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add new `mau_appservice_trial_days` configuration option to specify a different trial period for users registered via an appservice.
7 changes: 7 additions & 0 deletions docs/sample_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,11 @@ manhole_settings:
# sign up in a short space of time never to return after their initial
# 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
# 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
Expand All @@ -417,6 +422,8 @@ manhole_settings:
#max_mau_value: 50
#mau_trial_days: 2
#mau_limit_alerting: false
#mau_appservice_trial_days:
# "appservice-id": 1

# 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
Expand Down
14 changes: 14 additions & 0 deletions docs/usage/configuration/config_documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,20 @@ Example configuration:
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`

The option `mau_limit_alerting` is a means of limiting client-side alerting
Expand Down
8 changes: 8 additions & 0 deletions synapse/config/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@ def read_config(self, config: JsonDict, **kwargs: Any) -> None:
)

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)

# How long to keep redacted events in the database in unredacted form
Expand Down Expand Up @@ -1105,6 +1106,11 @@ def generate_config_section(
# sign up in a short space of time never to return after their initial
# 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
# 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
Expand All @@ -1115,6 +1121,8 @@ def generate_config_section(
#max_mau_value: 50
#mau_trial_days: 2
#mau_limit_alerting: false
#mau_appservice_trial_days:
# "appservice-id": 1

# 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
Expand Down
8 changes: 6 additions & 2 deletions synapse/storage/databases/main/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ async def get_userinfo_by_id(self, user_id: str) -> Optional[UserInfo]:

async def is_trial_user(self, user_id: str) -> bool:
"""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:
user_id: The user to check for trial status.
Expand All @@ -226,7 +227,10 @@ async def is_trial_user(self, user_id: str) -> bool:
return False

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
return is_trial

Expand Down
74 changes: 74 additions & 0 deletions tests/test_mau.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

"""Tests REST events for /rooms paths."""

from typing import List

from synapse.api.constants import APP_SERVICE_REGISTRATION_TYPE, LoginType
from synapse.api.errors import Codes, HttpResponseException, SynapseError
from synapse.appservice import ApplicationService
Expand Down Expand Up @@ -229,6 +231,78 @@ def test_tracked_but_not_limited(self):
self.reactor.advance(100)
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):
request_data = {
"username": localpart,
Expand Down