mirror of
				https://github.com/continew-org/continew-starter.git
				synced 2025-10-31 22:57:19 +08:00 
			
		
		
		
	Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
		| @@ -593,6 +593,13 @@ | |||||||
|                 <version>${revision}</version> |                 <version>${revision}</version> | ||||||
|             </dependency> |             </dependency> | ||||||
|  |  | ||||||
|  |             <!-- 敏感词模块 --> | ||||||
|  |             <dependency> | ||||||
|  |                 <groupId>top.continew</groupId> | ||||||
|  |                 <artifactId>continew-starter-sensitive-words</artifactId> | ||||||
|  |                 <version>${revision}</version> | ||||||
|  |             </dependency> | ||||||
|  |  | ||||||
|         </dependencies> |         </dependencies> | ||||||
|     </dependencyManagement> |     </dependencyManagement> | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										42
									
								
								continew-starter-sensitive-words/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								continew-starter-sensitive-words/pom.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project xmlns="http://maven.apache.org/POM/4.0.0" | ||||||
|  |          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||||
|  |          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||||
|  |     <modelVersion>4.0.0</modelVersion> | ||||||
|  |     <parent> | ||||||
|  |         <groupId>top.continew</groupId> | ||||||
|  |         <artifactId>continew-starter</artifactId> | ||||||
|  |         <version>${revision}</version> | ||||||
|  |     </parent> | ||||||
|  |  | ||||||
|  |     <artifactId>continew-starter-sensitive-words</artifactId> | ||||||
|  |     <description>Continew starter sensitive words 模块</description> | ||||||
|  |  | ||||||
|  |     <dependencies> | ||||||
|  |         <!-- Spring Boot Starter(自动配置相关依赖) --> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>org.springframework.boot</groupId> | ||||||
|  |             <artifactId>spring-boot-starter</artifactId> | ||||||
|  |         </dependency> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>org.springframework.boot</groupId> | ||||||
|  |             <artifactId>spring-boot-configuration-processor</artifactId> | ||||||
|  |         </dependency> | ||||||
|  |  | ||||||
|  |         <!-- Hutool(工具类库) --> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>cn.hutool</groupId> | ||||||
|  |             <artifactId>hutool-dfa</artifactId> | ||||||
|  |         </dependency> | ||||||
|  |  | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>org.springframework.boot</groupId> | ||||||
|  |             <artifactId>spring-boot-starter-validation</artifactId> | ||||||
|  |         </dependency> | ||||||
|  |  | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>org.projectlombok</groupId> | ||||||
|  |             <artifactId>lombok</artifactId> | ||||||
|  |         </dependency> | ||||||
|  |     </dependencies> | ||||||
|  | </project> | ||||||
| @@ -0,0 +1,39 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||||
|  |  * <p> | ||||||
|  |  * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * <p> | ||||||
|  |  * http://www.gnu.org/licenses/lgpl.html | ||||||
|  |  * <p> | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package top.continew.starter.sensitive.words.autoconfigure; | ||||||
|  |  | ||||||
|  | import jakarta.annotation.PostConstruct; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  | import org.springframework.boot.autoconfigure.AutoConfiguration; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * JSR 303 校验器自动配置 | ||||||
|  |  * | ||||||
|  |  * @author Charles7c | ||||||
|  |  * @since 2.3.0 | ||||||
|  |  */ | ||||||
|  | @AutoConfiguration | ||||||
|  | public class SensitiveWordsAutoConfiguration { | ||||||
|  |  | ||||||
|  |     private static final Logger log = LoggerFactory.getLogger(SensitiveWordsAutoConfiguration.class); | ||||||
|  |  | ||||||
|  |     @PostConstruct | ||||||
|  |     public void postConstruct() { | ||||||
|  |         log.debug("[ContiNew Starter] - Auto Configuration 'sensitive words service' completed initialization."); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,32 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||||
|  |  * <p> | ||||||
|  |  * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * <p> | ||||||
|  |  * http://www.gnu.org/licenses/lgpl.html | ||||||
|  |  * <p> | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package top.continew.starter.sensitive.words.autoconfigure; | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  | import org.springframework.boot.context.properties.ConfigurationProperties; | ||||||
|  | import org.springframework.stereotype.Component; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | @Data | ||||||
|  | @Component | ||||||
|  | @ConfigurationProperties(prefix = "continew.sensitive-words") | ||||||
|  | public class SensitiveWordsProperties { | ||||||
|  |     // 敏感词注入类型 | ||||||
|  |     private String type; | ||||||
|  |     private List<String> values; | ||||||
|  | } | ||||||
| @@ -0,0 +1,45 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||||
|  |  * <p> | ||||||
|  |  * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * <p> | ||||||
|  |  * http://www.gnu.org/licenses/lgpl.html | ||||||
|  |  * <p> | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package top.continew.starter.sensitive.words.service; | ||||||
|  |  | ||||||
|  | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||||||
|  | import org.springframework.stereotype.Component; | ||||||
|  | import top.continew.starter.sensitive.words.autoconfigure.SensitiveWordsProperties; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 默认敏感词配置 | ||||||
|  |  */ | ||||||
|  | @Component | ||||||
|  | @ConditionalOnProperty(prefix = "continew.sensitive-words", name = "type", havingValue = "default", matchIfMissing = true) | ||||||
|  | public class DefaultSensitiveWordsConfig implements SensitiveWordsConfig { | ||||||
|  |  | ||||||
|  |     private final SensitiveWordsProperties properties; | ||||||
|  |  | ||||||
|  |     public DefaultSensitiveWordsConfig(SensitiveWordsProperties properties) { | ||||||
|  |         this.properties = properties; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public List<String> getWords() { | ||||||
|  |         if (properties.getValues() != null) { | ||||||
|  |             return properties.getValues(); | ||||||
|  |         } | ||||||
|  |         return List.of(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,49 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||||
|  |  * <p> | ||||||
|  |  * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * <p> | ||||||
|  |  * http://www.gnu.org/licenses/lgpl.html | ||||||
|  |  * <p> | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package top.continew.starter.sensitive.words.service; | ||||||
|  |  | ||||||
|  | import cn.hutool.dfa.WordTree; | ||||||
|  | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; | ||||||
|  | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | ||||||
|  | import org.springframework.stereotype.Component; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 默认敏感词服务 | ||||||
|  |  */ | ||||||
|  | @Component | ||||||
|  | @ConditionalOnBean(SensitiveWordsConfig.class) | ||||||
|  | @ConditionalOnMissingBean(SensitiveWordsService.class) | ||||||
|  | public class DefaultSensitiveWordsService implements SensitiveWordsService { | ||||||
|  |  | ||||||
|  |     private final SensitiveWordsConfig sensitiveWordsConfig; | ||||||
|  |  | ||||||
|  |     private WordTree tree = new WordTree(); | ||||||
|  |  | ||||||
|  |     public DefaultSensitiveWordsService(SensitiveWordsConfig sensitiveWordsConfig) { | ||||||
|  |         this.sensitiveWordsConfig = sensitiveWordsConfig; | ||||||
|  |         if (sensitiveWordsConfig != null && sensitiveWordsConfig.getWords() != null) { | ||||||
|  |             tree.addWords(sensitiveWordsConfig.getWords()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public List<String> check(String content) { | ||||||
|  |         return tree.matchAll(content, -1, false, true); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,28 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||||
|  |  * <p> | ||||||
|  |  * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * <p> | ||||||
|  |  * http://www.gnu.org/licenses/lgpl.html | ||||||
|  |  * <p> | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package top.continew.starter.sensitive.words.service; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 敏感词配置 | ||||||
|  |  */ | ||||||
|  | public interface SensitiveWordsConfig { | ||||||
|  |  | ||||||
|  |     List<String> getWords(); | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,29 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||||
|  |  * <p> | ||||||
|  |  * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * <p> | ||||||
|  |  * http://www.gnu.org/licenses/lgpl.html | ||||||
|  |  * <p> | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package top.continew.starter.sensitive.words.service; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | public interface SensitiveWordsService { | ||||||
|  |     /** | ||||||
|  |      * 检查敏感词 | ||||||
|  |      * | ||||||
|  |      * @param content 待检测字符串 | ||||||
|  |      * @return 敏感词列表 | ||||||
|  |      */ | ||||||
|  |     List<String> check(String content); | ||||||
|  | } | ||||||
| @@ -0,0 +1,35 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||||
|  |  * <p> | ||||||
|  |  * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * <p> | ||||||
|  |  * http://www.gnu.org/licenses/lgpl.html | ||||||
|  |  * <p> | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package top.continew.starter.sensitive.words.validate; | ||||||
|  |  | ||||||
|  | import jakarta.validation.Constraint; | ||||||
|  | import jakarta.validation.Payload; | ||||||
|  |  | ||||||
|  | import java.lang.annotation.*; | ||||||
|  |  | ||||||
|  | @Target({ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) | ||||||
|  | @Retention(RetentionPolicy.RUNTIME) | ||||||
|  | @Documented | ||||||
|  | @Constraint(validatedBy = {SensitiveWordValidator.class}) | ||||||
|  | public @interface SensitiveWord { | ||||||
|  |  | ||||||
|  |     String message() default "有敏感词,请检测!"; | ||||||
|  |  | ||||||
|  |     Class<?>[] groups() default {}; | ||||||
|  |  | ||||||
|  |     Class<? extends Payload>[] payload() default {}; | ||||||
|  | } | ||||||
| @@ -0,0 +1,60 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||||
|  |  * <p> | ||||||
|  |  * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * <p> | ||||||
|  |  * http://www.gnu.org/licenses/lgpl.html | ||||||
|  |  * <p> | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package top.continew.starter.sensitive.words.validate; | ||||||
|  |  | ||||||
|  | import jakarta.annotation.Resource; | ||||||
|  | import jakarta.validation.ConstraintValidator; | ||||||
|  | import jakarta.validation.ConstraintValidatorContext; | ||||||
|  | import top.continew.starter.sensitive.words.service.SensitiveWordsService; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | public class SensitiveWordValidator implements ConstraintValidator<SensitiveWord, String> { | ||||||
|  |  | ||||||
|  |     @Resource | ||||||
|  |     private SensitiveWordsService sensitiveWordsService; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 初始化方法,可以用自定义注解中获取值进行初始化 | ||||||
|  |      * | ||||||
|  |      * @param {@link SensitiveWord } constraintAnnotation 注解值内容 | ||||||
|  |      */ | ||||||
|  |     @Override | ||||||
|  |     public void initialize(SensitiveWord constraintAnnotation) { | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 实际校验自定义注解 value 值 | ||||||
|  |      * | ||||||
|  |      * @param {@link String} value 待检测字符串 | ||||||
|  |      * @param {@link ConstraintValidatorContext } constraintValidatorContext 检测的上下文 | ||||||
|  |      * @return boolean 是否通过检测 | ||||||
|  |      */ | ||||||
|  |     @Override | ||||||
|  |     public boolean isValid(String value, ConstraintValidatorContext context) { | ||||||
|  |         List<String> res = sensitiveWordsService.check(value); | ||||||
|  |         if (!res.isEmpty()) { | ||||||
|  |             // 动态设置错误消息 | ||||||
|  |             context.disableDefaultConstraintViolation(); // 禁用默认消息 | ||||||
|  |             context.buildConstraintViolationWithTemplate("包含敏感词: " + String.join(",", res)) | ||||||
|  |                     .addConstraintViolation(); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,4 @@ | |||||||
|  | top.continew.starter.sensitive.words.autoconfigure.SensitiveWordsAutoConfiguration | ||||||
|  | top.continew.starter.sensitive.words.autoconfigure.SensitiveWordsProperties | ||||||
|  | top.continew.starter.sensitive.words.service.DefaultSensitiveWordsConfig | ||||||
|  | top.continew.starter.sensitive.words.service.DefaultSensitiveWordsService | ||||||
| @@ -0,0 +1,49 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||||
|  |  * <p> | ||||||
|  |  * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * <p> | ||||||
|  |  * http://www.gnu.org/licenses/lgpl.html | ||||||
|  |  * <p> | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package top.continew.starter.sensitive.words; | ||||||
|  |  | ||||||
|  | import cn.hutool.dfa.FoundWord; | ||||||
|  | import cn.hutool.dfa.WordTree; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | public class SensitiveWordsTest { | ||||||
|  |     public static void main(String[] args) { | ||||||
|  |         WordTree tree = new WordTree(); | ||||||
|  |         tree.addWord("大"); | ||||||
|  |         tree.addWord("大土豆"); | ||||||
|  |         tree.addWord("土豆"); | ||||||
|  |         tree.addWord("刚出锅"); | ||||||
|  |         tree.addWord("出锅"); | ||||||
|  |         //正文 | ||||||
|  |         String text = "我有一颗大土豆,刚出锅的"; | ||||||
|  |  | ||||||
|  |         // 匹配到【大】,由于非密集匹配,因此从下一个字符开始查找,匹配到【土豆】接着被匹配 | ||||||
|  |         // 由于【刚出锅】被匹配,由于非密集匹配,【出锅】被跳过 | ||||||
|  |         List<String> matchAll = tree.matchAll(text, -1, false, true); | ||||||
|  |         for (String s : matchAll) { | ||||||
|  |             System.out.println(s); | ||||||
|  |         } | ||||||
|  |         System.out.println("-------------------"); | ||||||
|  |         String match = tree.match(text); | ||||||
|  |         System.out.println(match); | ||||||
|  |  | ||||||
|  |         System.out.println("-------------------"); | ||||||
|  |         FoundWord matchText = tree.matchWord(text); | ||||||
|  |         System.out.println(matchText.getFoundWord()); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								pom.xml
									
									
									
									
									
								
							| @@ -72,6 +72,7 @@ | |||||||
|         <module>continew-starter-auth</module> |         <module>continew-starter-auth</module> | ||||||
|         <module>continew-starter-messaging</module> |         <module>continew-starter-messaging</module> | ||||||
|         <module>continew-starter-extension</module> |         <module>continew-starter-extension</module> | ||||||
|  |         <module>continew-starter-sensitive-words</module> | ||||||
|     </modules> |     </modules> | ||||||
|  |  | ||||||
|     <build> |     <build> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user