diff --git a/lib/src/model/game/chat_controller.dart b/lib/src/model/game/chat_controller.dart index 6ec13b78a6..8c0f294945 100644 --- a/lib/src/model/game/chat_controller.dart +++ b/lib/src/model/game/chat_controller.dart @@ -125,14 +125,8 @@ class ChatController extends _$ChatController { } } else if (event.topic == 'message') { final data = event.data as Map; - final message = data['t'] as String; - final username = data['u'] as String?; - _addMessage( - ( - message: message, - username: username, - ), - ); + final message = _messageFromPick(RequiredPick(data)); + _addMessage(message); } } } @@ -147,11 +141,58 @@ class ChatState with _$ChatState { }) = _ChatState; } -typedef Message = ({String? username, String message}); +typedef Message = ({ + String? username, + String message, + bool troll, + bool deleted, +}); Message _messageFromPick(RequiredPick pick) { return ( message: pick('t').asStringOrThrow(), username: pick('u').asStringOrNull(), + troll: pick('r').asBoolOrNull() ?? false, + deleted: pick('d').asBoolOrNull() ?? false, ); } + +bool isSpam(Message message) { + return spamRegex.hasMatch(message.message) || + followMeRegex.hasMatch(message.message); +} + +final RegExp spamRegex = RegExp( + [ + 'xcamweb.com', + '(^|[^i])chess-bot', + 'chess-cheat', + 'coolteenbitch', + 'letcafa.webcam', + 'tinyurl.com/', + 'wooga.info/', + 'bit.ly/', + 'wbt.link/', + 'eb.by/', + '001.rs/', + 'shr.name/', + 'u.to/', + '.3-a.net', + '.ssl443.org', + '.ns02.us', + '.myftp.info', + '.flinkup.com', + '.serveusers.com', + 'badoogirls.com', + 'hide.su', + 'wyon.de', + 'sexdatingcz.club', + 'qps.ru', + 'tiny.cc/', + 'trasderk.blogspot.com', + 't.ly/', + 'shorturl.at/', + ].map((url) => url.replaceAll('.', '\\.').replaceAll('/', '\\/')).join('|'), +); + +final followMeRegex = RegExp('follow me|join my team', caseSensitive: false); diff --git a/lib/src/view/game/message_screen.dart b/lib/src/view/game/message_screen.dart index d684f855c0..00187cc040 100644 --- a/lib/src/view/game/message_screen.dart +++ b/lib/src/view/game/message_screen.dart @@ -83,29 +83,34 @@ class _Body extends ConsumerWidget { child: GestureDetector( onTap: () => FocusScope.of(context).unfocus(), child: chatStateAsync.when( - data: (chatState) => ListView.builder( - // remove the automatic bottom padding of the ListView, which on iOS - // corresponds to the safe area insets - // and which is here taken care of by the _ChatBottomBar - padding: MediaQuery.of(context).padding.copyWith(bottom: 0), - reverse: true, - itemCount: chatState.messages.length, - itemBuilder: (context, index) { - final message = - chatState.messages[chatState.messages.length - index - 1]; - return (message.username == 'lichess') - ? _MessageAction(message: message.message) - : (message.username == me?.name) - ? _MessageBubble( - you: true, - message: message.message, - ) - : _MessageBubble( - you: false, - message: message.message, - ); - }, - ), + data: (chatState) { + final selectedMessages = chatState.messages + .where((m) => !m.troll && !m.deleted && !isSpam(m)) + .toList(); + final messagesCount = selectedMessages.length; + return ListView.builder( + // remove the automatic bottom padding of the ListView, which on iOS + // corresponds to the safe area insets + // and which is here taken care of by the _ChatBottomBar + padding: MediaQuery.of(context).padding.copyWith(bottom: 0), + reverse: true, + itemCount: messagesCount, + itemBuilder: (context, index) { + final message = selectedMessages[messagesCount - index - 1]; + return (message.username == 'lichess') + ? _MessageAction(message: message.message) + : (message.username == me?.name) + ? _MessageBubble( + you: true, + message: message.message, + ) + : _MessageBubble( + you: false, + message: message.message, + ); + }, + ); + }, loading: () => const Center( child: CircularProgressIndicator(), ),