diff --git a/service/chat/__init__.py b/service/chat/__init__.py
index e61b478..f28f96a 100644
--- a/service/chat/__init__.py
+++ b/service/chat/__init__.py
@@ -88,6 +88,19 @@
LIMIT 1
"""
+# Accounts are trusted after they've been around for a day. Verified accounts
+# are trusted a bit sooner.
+Q_IS_TRUSTED_ACCOUNT = """
+SELECT
+ 1
+FROM
+ person
+WHERE
+ id = %(from_id)s
+AND
+ sign_up_time < now() - (interval '1 day') / power(verification_level_id, 2)
+"""
+
Q_IMMEDIATE_DATA = """
WITH to_notification AS (
SELECT
@@ -306,6 +319,16 @@ async def fetch_is_intro(from_id: int, to_id: int) -> bool:
return not bool(row)
+@AsyncLruCache(maxsize=1024, cache_condition=lambda x: not x)
+async def fetch_is_trusted_account(from_id: int) -> bool:
+ async with api_tx('read committed') as tx:
+ await tx.execute(
+ Q_IS_TRUSTED_ACCOUNT,
+ dict(from_id=from_id))
+ row = await tx.fetchone()
+
+ return bool(row)
+
@AsyncLruCache(ttl=2 * 60) # 2 minutes
async def fetch_push_token(username: str) -> str | None:
async with chat_tx('read committed') as tx:
@@ -385,7 +408,10 @@ async def process_duo_message(
if is_intro and is_offensive(maybe_message_body):
return [f''], []
- if is_intro and is_spam(maybe_message_body):
+ if \
+ is_intro and \
+ is_spam(maybe_message_body) and \
+ not await fetch_is_trusted_account(from_id=from_id):
return [f''], []
immediate_data = await fetch_immediate_data(
diff --git a/test/functionality4/xmpp-spam.sh b/test/functionality4/xmpp-spam.sh
index eb9467a..5afc3fd 100755
--- a/test/functionality4/xmpp-spam.sh
+++ b/test/functionality4/xmpp-spam.sh
@@ -22,15 +22,19 @@ q "delete from intro_hash" duo_chat
../util/create-user.sh user1 0 0
../util/create-user.sh user2 0 0
+../util/create-user.sh user3 0 0
assume_role user1 ; user1token=$SESSION_TOKEN
assume_role user2 ; user2token=$SESSION_TOKEN
+assume_role user3 ; user3token=$SESSION_TOKEN
user1uuid=$(get_uuid 'user1@example.com')
user2uuid=$(get_uuid 'user2@example.com')
+user3uuid=$(get_uuid 'user3@example.com')
user1id=$(get_id 'user1@example.com')
user2id=$(get_id 'user2@example.com')
+user3id=$(get_id 'user3@example.com')
@@ -60,7 +64,6 @@ curl -X POST http://localhost:3000/send -H "Content-Type: application/xml" -d "
from='$user1uuid@duolicious.app'
to='$user2uuid@duolicious.app'
id='id1'
- check_uniqueness='false'
xmlns='jabber:client'>
You should join discord.gg/spaghetti
@@ -90,7 +93,6 @@ curl -X POST http://localhost:3000/send -H "Content-Type: application/xml" -d "
from='$user1uuid@duolicious.app'
to='$user2uuid@duolicious.app'
id='id1'
- check_uniqueness='false'
xmlns='jabber:client'>
damn I want to volunteer to walk puppies
@@ -134,7 +136,6 @@ curl -X POST http://localhost:3000/send -H "Content-Type: application/xml" -d "
from='$user2uuid@duolicious.app'
to='$user1uuid@duolicious.app'
id='id1'
- check_uniqueness='false'
xmlns='jabber:client'>
You should join discord.gg/spaghetti
@@ -151,3 +152,44 @@ sleep 3 # MongooseIM takes some time to flush messages to the DB
curl -sX GET http://localhost:3000/pop | grep -qF ''
[[ "$(q "select count(*) from mam_message" duo_chat)" = 4 ]]
+
+
+
+curl -X POST http://localhost:3000/config -H "Content-Type: application/json" -d '{
+ "service": "ws://chat:5443",
+ "domain": "duolicious.app",
+ "resource": "testresource",
+ "username": "'$user3uuid'",
+ "password": "'$user3token'"
+}'
+
+
+
+sleep 3
+
+echo A potential spam message is allowed for trusted accounts
+
+q "update person set sign_up_time = now() - interval '7 days' where uuid = '$user3uuid'"
+
+curl -X POST http://localhost:3000/send -H "Content-Type: application/xml" -d "
+
+ You should join discord.gg/spaghetti
+
+
+"
+
+sleep 3 # MongooseIM takes some time to flush messages to the DB
+
+[[ "$(q "select count(*) from messaged where \
+ subject_person_id = $user3id and \
+ object_person_id = $user2id")" = 1 ]]
+[[ "$(q "select count(*) from messaged")" = 1 ]]
+
+curl -sX GET http://localhost:3000/pop | grep -qF ''
+
+[[ "$(q "select count(*) from mam_message" duo_chat)" = 6 ]]