| | 1 | | using System.Collections.Generic; |
| | 2 | | using System.Diagnostics; |
| | 3 | | using System.Linq; |
| | 4 | | using System.Text; |
| | 5 | | using System.Text.RegularExpressions; |
| | 6 | | using System.Threading.Tasks; |
| | 7 | | using Cysharp.Threading.Tasks; |
| | 8 | |
|
| | 9 | | public class ThrottledRegexProfanityFilter : IProfanityFilter |
| | 10 | | { |
| 1 | 11 | | private readonly List<Regex> regexSteps = new List<Regex>(); |
| | 12 | |
|
| 1 | 13 | | public ThrottledRegexProfanityFilter(IProfanityWordProvider wordProvider, int partitionSize = 1) |
| | 14 | | { |
| 1 | 15 | | List<string> explicitWords = wordProvider.GetExplicitWords().ToList(); |
| 1 | 16 | | List<string> nonExplicitWords = wordProvider.GetNonExplicitWords().ToList(); |
| | 17 | |
|
| 1 | 18 | | var explicitWordsChunks = ToChunks(explicitWords, partitionSize); |
| 1 | 19 | | var nonExplicitWordsChunks = ToChunks(nonExplicitWords, partitionSize); |
| | 20 | |
|
| 6 | 21 | | for (int i = 0; i < explicitWordsChunks.Count; i++) |
| | 22 | | { |
| 2 | 23 | | var explicitWordsRegex = ToRegex(explicitWordsChunks[i]); |
| 2 | 24 | | var regex = new Regex(@$"\b({explicitWordsRegex})\b", RegexOptions.IgnoreCase | RegexOptions.Compiled); |
| 2 | 25 | | regexSteps.Add(regex); |
| | 26 | | } |
| | 27 | |
|
| 40 | 28 | | for (int i = 0; i < nonExplicitWordsChunks.Count; i++) |
| | 29 | | { |
| 19 | 30 | | var nonExplicitWordsRegex = ToRegex(nonExplicitWordsChunks[i]); |
| 19 | 31 | | var regex = new Regex(@$"\\b|({nonExplicitWordsRegex})", RegexOptions.IgnoreCase | RegexOptions.Compiled); |
| 19 | 32 | | regexSteps.Add(regex); |
| | 33 | | } |
| 1 | 34 | | } |
| | 35 | |
|
| | 36 | | public async UniTask<string> Filter(string message) |
| | 37 | | { |
| 13 | 38 | | if (string.IsNullOrEmpty(message)) |
| 0 | 39 | | return message; |
| | 40 | |
|
| 13 | 41 | | var stopwatch = new Stopwatch(); |
| 13 | 42 | | stopwatch.Start(); |
| | 43 | |
|
| 572 | 44 | | foreach (Regex regexStep in regexSteps) |
| | 45 | | { |
| 273 | 46 | | await CheckTimerAndSkipFrame(stopwatch); |
| 276 | 47 | | message = regexStep.Replace(message, match => new StringBuilder().Append('*', match.Value.Length).ToString() |
| 273 | 48 | | } |
| | 49 | |
|
| 13 | 50 | | return message; |
| 13 | 51 | | } |
| | 52 | |
|
| | 53 | | private async Task CheckTimerAndSkipFrame(Stopwatch stopwatch) |
| | 54 | | { |
| 273 | 55 | | if (stopwatch.ElapsedMilliseconds > 1) |
| | 56 | | { |
| 0 | 57 | | await UniTask.WaitForEndOfFrame(); |
| 0 | 58 | | stopwatch.Restart(); |
| | 59 | | } |
| 273 | 60 | | } |
| | 61 | |
|
| 21 | 62 | | private string ToRegex(IEnumerable<string> words) => string.Join("|", words); |
| | 63 | |
|
| | 64 | | private List<List<T>> ToChunks<T>(List<T> source, int chunkSize) |
| | 65 | | { |
| 2 | 66 | | return source |
| | 67 | | .Select((value, index) => (index, value)) |
| | 68 | | .GroupBy(x => x.index / chunkSize) |
| | 69 | | .Select(x => x.Select(v => v.value).ToList()) |
| | 70 | | .ToList(); |
| | 71 | | } |
| | 72 | | } |