├── .editorconfig ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── Build.yml ├── .gitignore ├── .run ├── Test.run.xml └── lgz-bot [buildPluginLegacy].run.xml ├── CNAME ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src ├── main ├── java │ ├── com │ │ └── benjaminwan │ │ │ └── ocrlibrary │ │ │ ├── OcrEngine.java │ │ │ ├── OcrInput.java │ │ │ ├── OcrOutput.java │ │ │ ├── OcrResult.java │ │ │ ├── Point.java │ │ │ └── TextBlock.java │ ├── huzpsb │ │ └── ll4j │ │ │ ├── layer │ │ │ ├── AbstractLayer.java │ │ │ ├── DenseLayer.java │ │ │ ├── DropoutLayer.java │ │ │ ├── JudgeLayer.java │ │ │ ├── LeakyRelu.java │ │ │ ├── Sigmoid.java │ │ │ ├── Softmax.java │ │ │ └── Tanh.java │ │ │ ├── minrt │ │ │ ├── JFuncModelScript.java │ │ │ └── MinRt.java │ │ │ ├── model │ │ │ └── Model.java │ │ │ ├── nlp │ │ │ └── token │ │ │ │ ├── CharUtils.java │ │ │ │ ├── Tokenizer.java │ │ │ │ └── TokenizerBuilder.java │ │ │ └── utils │ │ │ ├── data │ │ │ ├── DataEntry.java │ │ │ └── DataSet.java │ │ │ ├── pair │ │ │ ├── ImmutablePair.java │ │ │ ├── MutablePair.java │ │ │ ├── Pair.java │ │ │ └── PairBase.java │ │ │ └── random │ │ │ ├── NRandom.java │ │ │ └── RandomSeedGenerator.java │ ├── io │ │ └── github │ │ │ └── mymonstercat │ │ │ ├── JarFileUtil.java │ │ │ ├── Model.java │ │ │ ├── exception │ │ │ └── LoadException.java │ │ │ ├── loader │ │ │ ├── LibraryLoader.java │ │ │ └── ModelsLoader.java │ │ │ └── ocr │ │ │ ├── InferenceEngine.java │ │ │ ├── LoadUtil.java │ │ │ └── config │ │ │ ├── HardwareConfig.java │ │ │ ├── IOcrConfig.java │ │ │ └── ParamConfig.java │ └── ltd │ │ └── guimc │ │ └── dlm │ │ └── DLModel.java ├── kotlin │ └── ltd │ │ └── guimc │ │ └── lgzbot │ │ ├── PluginMain.kt │ │ ├── command │ │ ├── ACGCommand.kt │ │ ├── FbCommand.kt │ │ ├── GithubWebhookSubCommand.kt │ │ ├── HomoIntCommand.kt │ │ ├── HttpCatCommand.kt │ │ ├── HypixelCommand.kt │ │ ├── LGZBotCommand.kt │ │ ├── ReviewCommand.kt │ │ └── ToggleCheckCommand.kt │ │ ├── counter │ │ ├── Counter.kt │ │ └── VLManager.kt │ │ ├── files │ │ ├── Config.kt │ │ ├── GithubSubConfig.kt │ │ ├── GithubWebhookSubData.kt │ │ └── ModuleStateConfig.kt │ │ ├── github │ │ ├── CommitInfo.kt │ │ ├── OwnerInfo.kt │ │ ├── RepoInfo.kt │ │ └── UserInfo.kt │ │ ├── listener │ │ ├── message │ │ │ ├── AntiMappLinkListener.kt │ │ │ ├── FunListener.kt │ │ │ ├── GithubUrlListener.kt │ │ │ ├── ImageOCRFilter.kt │ │ │ ├── MessageFilter.kt │ │ │ ├── NaiLongImageListener.kt │ │ │ └── SelfMessageListener.kt │ │ ├── multi │ │ │ └── BakaListener.kt │ │ ├── mute │ │ │ └── AutoQuit.kt │ │ └── nudge │ │ │ ├── AntiNudgeSpam.kt │ │ │ └── NudgeMute.kt │ │ ├── utils │ │ ├── AsciiUtils.kt │ │ ├── Base64Utils.kt │ │ ├── CooldownUtils.kt │ │ ├── FbUtils.kt │ │ ├── GithubUtils.kt │ │ ├── HomoIntUtils.kt │ │ ├── HttpUtils.kt │ │ ├── ImageUtils.kt │ │ ├── LL4JUtils.kt │ │ ├── MemberUtils.kt │ │ ├── MessageUtils.kt │ │ ├── MojangAPIUtils.kt │ │ ├── OverflowUtils.kt │ │ ├── PinyinUtils.kt │ │ ├── RegexUtils.kt │ │ ├── RequestUtils.kt │ │ ├── TextUtils.kt │ │ ├── TimeUtils.kt │ │ ├── hypixel │ │ │ ├── ApiFailedException.kt │ │ │ ├── ExpCalculator.kt │ │ │ └── HypixelApiUtils.kt │ │ ├── timer │ │ │ └── MSTimer.kt │ │ └── webhook │ │ │ └── SecurityUtils.kt │ │ └── webhook │ │ ├── GithubWebHookReciver.kt │ │ └── WebHookService.kt └── resources │ ├── META-INF │ └── services │ │ └── net.mamoe.mirai.console.plugin.jvm.JvmPlugin │ ├── Nailong.onnx │ ├── ad-detector.model │ ├── anti-ad.model │ ├── faqing.txt │ ├── gen.model │ ├── regex.txt │ ├── self-protect.txt │ ├── serious.txt │ └── ts.model └── test └── java └── Test.java /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{kt, kts}] 2 | max_line_length = 120 3 | tab_width = 4 4 | ij_continuation_indent_size = 4 5 | indent_size = 4 6 | 7 | [*.java] 8 | max_line_length = 120 9 | tab_width = 4 10 | ij_continuation_indent_size = 4 11 | indent_size = 4 -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: 4 | patreon: Guimc 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: guimc233 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: guimc233 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/Build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout repository 12 | uses: actions/checkout@v2 13 | - name: Java setup 14 | uses: actions/setup-java@v2 15 | with: 16 | distribution: 'adopt' 17 | java-version: 11 18 | check-latest: true 19 | - uses: burrunan/gradle-cache-action@v1 20 | name: BuildPluginLegacy 21 | with: 22 | job-id: jdk11 23 | arguments: buildPluginLegacy 24 | gradle-version: wrapper 25 | - name: Upload build artifacts 26 | uses: actions/upload-artifact@v4 27 | with: 28 | name: lgz-bot 29 | path: build/mirai/lgz-bot-*.jar 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Project exclude paths 2 | /.gradle/ 3 | /build/ 4 | /.idea/ 5 | /test/ 6 | /out/ 7 | /run/ 8 | /src/test/kotlin/RunMiraiConsole.kt 9 | /.fleet/ 10 | /pytest/ 11 | -------------------------------------------------------------------------------- /.run/Test.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | -------------------------------------------------------------------------------- /.run/lgz-bot [buildPluginLegacy].run.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 17 | 19 | true 20 | true 21 | false 22 | false 23 | 24 | 25 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | lgz.guimc.ltd -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | xiluo@guimc.ltd. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LGZ bot 2 | [![State-of-the-art Shitcode](https://img.shields.io/static/v1?label=State-of-the-art&message=ShitCode&color=7B5804)](https://github.com/trekhleb/state-of-the-art-shitcode) 3 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Flgz-bot.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Flgz-bot?ref=badge_shield) 4 | [![Maintainability](https://api.codeclimate.com/v1/badges/05a867056904e1a9f5b1/maintainability)](https://codeclimate.com/github/guimc233/lgz-bot/maintainability) 5 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/f4fa5faaba9a42e0bf4213a42ba92a94)](https://www.codacy.com/gh/guimc233/lgz-bot/dashboard?utm_source=github.com&utm_medium=referral&ut[...] 6 | 7 | **⚠️ 此项目停止开发 新项目 [YounBot](https://github.com/BakaBotTeam/YounBot) ⚠️**
8 | **⚠️ 此项目停止开发 新项目 [YounBot](https://github.com/BakaBotTeam/YounBot) ⚠️**
9 | **⚠️ 此项目停止开发 新项目 [YounBot](https://github.com/BakaBotTeam/YounBot) ⚠️**
10 | **⚠️ 此项目停止开发 新项目 [YounBot](https://github.com/BakaBotTeam/YounBot) ⚠️**
11 | **⚠️ 此项目停止开发 新项目 [YounBot](https://github.com/BakaBotTeam/YounBot) ⚠️**
12 | 13 | * **Thanks for everyone!!** 14 | * 交流群: `952891372` (QQ) 15 | * Due to some reason, this project only support windows/linux x86_64 now. 16 | 17 | ## About LGZ bot 18 | * 这是[Mirai](https://github.com/mamoe/mirai)框架的插件,依赖于[Mirai Console](https://github.com/mamoe/mirai-console)运行. 19 | * 目前建议在[Overflow](https://github.com/MrXiaoM/Overflow/)下继续运行此插件, 本插件已经完成了一些对Overflow的兼容 20 | * 本插件可以为使用者提供群消息合规性检查功能. 21 | * 还有一些有趣的小东西 \>_\< 22 | * Thanks to [LL4J](https://github.com/LL4J/), [ADDetector4J](https://github.com/siuank/ADDetector4J), 23 | [RapidOCR-Java](https://github.com/MyMonsterCat/RapidOcr-Java) 24 | and [恶臭数字论证器](https://github.com/itorr/homo) 25 | 26 | ## License 27 | This project is subject to the [GNU Affero General Public License v3.0](LICENSE). This does only apply for source code located directly in this clean repository. During the development and compilation[...] 28 | 29 | For those who are unfamiliar with the license, here is a summary of its main points. This is by no means legal advice nor legally binding. 30 | 31 | You are allowed to 32 | - use 33 | - share 34 | - modify 35 | 36 | this project entirely or partially for free and even commercially. However, please consider the following: 37 | 38 | - **You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-so[...] 39 | - **Your modified application must also be licensed under the GPL.** 40 | 41 | Do the above and share your source code with everyone, just like we do. 42 | 43 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Flgz-bot.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Flgz-bot?ref=badge_large) 44 | 45 | ## Kotlin 46 | 我们正在使用Kotlin开发此项目 47 | ![Kotlin logo](https://resources.jetbrains.com/storage/products/company/brand/logos/Kotlin.svg) 48 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | val kotlinVersion = "1.9.0" 3 | kotlin("plugin.serialization") version kotlinVersion 4 | 5 | id("net.mamoe.mirai-console") version "2.16.0" 6 | id("org.jetbrains.kotlin.jvm") version "1.9.0" 7 | id("com.github.johnrengelman.shadow") version "8.0.0" 8 | id("io.freefair.lombok") version "8.6" 9 | id("maven-publish") 10 | } 11 | 12 | dependencies { 13 | val ktor_version = "2.3.5" 14 | val overflow_version = "1.0.0" 15 | implementation(kotlin("stdlib")) 16 | implementation("io.ktor:ktor-server-core:$ktor_version") 17 | implementation("io.ktor:ktor-server-netty:$ktor_version") 18 | implementation("ch.qos.logback:logback-classic:1.4.12") 19 | implementation("com.github.promeg:tinypinyin:2.0.3") 20 | implementation("com.github.promeg:tinypinyin-lexicons-java-cncity:2.0.3") 21 | implementation("org.json:json:20231013") 22 | implementation("org.apache.httpcomponents:httpclient:4.5.14") 23 | implementation("org.jsoup:jsoup:1.15.3") 24 | implementation("io.github.mymonstercat:rapidocr-onnx-windows-x86_64:1.2.2") 25 | implementation("io.github.mymonstercat:rapidocr-onnx-linux-x86_64:1.2.2") 26 | implementation("org.xerial:sqlite-jdbc:3.46.0.0") 27 | implementation("org.apache.commons:commons-lang3:3.16.0") 28 | implementation("org.apache.commons:commons-imaging:1.0-alpha1") 29 | implementation("ai.djl.onnxruntime:onnxruntime-engine:0.27.0") 30 | 31 | compileOnly("top.mrxiaom.mirai:overflow-core-api:$overflow_version") 32 | compileOnly("top.mrxiaom.mirai:overflow-core:$overflow_version") 33 | compileOnly("org.projectlombok:lombok:1.18.34") 34 | 35 | compileOnly(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar")))) 36 | } 37 | 38 | mirai { 39 | noTestCore = true 40 | setupConsoleTestRuntime { 41 | // 移除 mirai-core 依赖 42 | classpath = classpath.filter { 43 | !it.nameWithoutExtension.startsWith("mirai-core-jvm") || 44 | !it.nameWithoutExtension.startsWith("overflow-api") 45 | } 46 | } 47 | } 48 | 49 | version = "0.3.5" 50 | 51 | repositories { 52 | maven("https://maven.aliyun.com/repository/public") // 阿里云国内代理仓库 53 | maven { url = uri("https://repo.repsy.io/mvn/chrynan/public") } 54 | maven("https://s01.oss.sonatype.org/content/repositories/snapshots") 55 | mavenCentral() 56 | } 57 | 58 | mirai { 59 | jvmTarget = JavaVersion.VERSION_11 60 | } 61 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BakaBotTeam/lgz-bot/89a80c0e7126e4b58b383ebd7cc0cac33af274f8/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Use the maximum available, or set MAX_FD != -1 to use that value. 89 | MAX_FD=maximum 90 | 91 | warn () { 92 | echo "$*" 93 | } >&2 94 | 95 | die () { 96 | echo 97 | echo "$*" 98 | echo 99 | exit 1 100 | } >&2 101 | 102 | # OS specific support (must be 'true' or 'false'). 103 | cygwin=false 104 | msys=false 105 | darwin=false 106 | nonstop=false 107 | case "$( uname )" in #( 108 | CYGWIN* ) cygwin=true ;; #( 109 | Darwin* ) darwin=true ;; #( 110 | MSYS* | MINGW* ) msys=true ;; #( 111 | NONSTOP* ) nonstop=true ;; 112 | esac 113 | 114 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 115 | 116 | 117 | # Determine the Java command to use to start the JVM. 118 | if [ -n "$JAVA_HOME" ] ; then 119 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 120 | # IBM's JDK on AIX uses strange locations for the executables 121 | JAVACMD=$JAVA_HOME/jre/sh/java 122 | else 123 | JAVACMD=$JAVA_HOME/bin/java 124 | fi 125 | if [ ! -x "$JAVACMD" ] ; then 126 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 127 | 128 | Please set the JAVA_HOME variable in your environment to match the 129 | location of your Java installation." 130 | fi 131 | else 132 | JAVACMD=java 133 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 134 | 135 | Please set the JAVA_HOME variable in your environment to match the 136 | location of your Java installation." 137 | fi 138 | 139 | # Increase the maximum file descriptors if we can. 140 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 141 | case $MAX_FD in #( 142 | max*) 143 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 144 | # shellcheck disable=SC3045 145 | MAX_FD=$( ulimit -H -n ) || 146 | warn "Could not query maximum file descriptor limit" 147 | esac 148 | case $MAX_FD in #( 149 | '' | soft) :;; #( 150 | *) 151 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 152 | # shellcheck disable=SC3045 153 | ulimit -n "$MAX_FD" || 154 | warn "Could not set maximum file descriptor limit to $MAX_FD" 155 | esac 156 | fi 157 | 158 | # Collect all arguments for the java command, stacking in reverse order: 159 | # * args from the command line 160 | # * the main class name 161 | # * -classpath 162 | # * -D...appname settings 163 | # * --module-path (only if needed) 164 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 165 | 166 | # For Cygwin or MSYS, switch paths to Windows format before running java 167 | if "$cygwin" || "$msys" ; then 168 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 169 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 170 | 171 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 172 | 173 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 174 | for arg do 175 | if 176 | case $arg in #( 177 | -*) false ;; # don't mess with options #( 178 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 179 | [ -e "$t" ] ;; #( 180 | *) false ;; 181 | esac 182 | then 183 | arg=$( cygpath --path --ignore --mixed "$arg" ) 184 | fi 185 | # Roll the args list around exactly as many times as the number of 186 | # args, so each arg winds up back in the position where it started, but 187 | # possibly modified. 188 | # 189 | # NB: a `for` loop captures its iteration list before it begins, so 190 | # changing the positional parameters here affects neither the number of 191 | # iterations, nor the values presented in `arg`. 192 | shift # remove old arg 193 | set -- "$@" "$arg" # push replacement arg 194 | done 195 | fi 196 | 197 | 198 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 199 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 200 | 201 | # Collect all arguments for the java command; 202 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 203 | # shell script including quotes and variable substitutions, so put them in 204 | # double quotes to make sure that they get re-expanded; and 205 | # * put everything else in single quotes, so that it's not re-expanded. 206 | 207 | set -- \ 208 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 209 | -classpath "$CLASSPATH" \ 210 | org.gradle.wrapper.GradleWrapperMain \ 211 | "$@" 212 | 213 | # Stop when "xargs" is not available. 214 | if ! command -v xargs >/dev/null 2>&1 215 | then 216 | die "xargs is not available" 217 | fi 218 | 219 | # Use "xargs" to parse quoted args. 220 | # 221 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 222 | # 223 | # In Bash we could simply go: 224 | # 225 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 226 | # set -- "${ARGS[@]}" "$@" 227 | # 228 | # but POSIX shell has neither arrays nor command substitution, so instead we 229 | # post-process each arg (as a line of input to sed) to backslash-escape any 230 | # character that might be a shell metacharacter, then use eval to reverse 231 | # that process (while maintaining the separation between arguments), and wrap 232 | # the whole thing up as a single "set" statement. 233 | # 234 | # This will of course break if any of these variables contains a newline or 235 | # an unmatched quote. 236 | # 237 | 238 | eval "set -- $( 239 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 240 | xargs -n1 | 241 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 242 | tr '\n' ' ' 243 | )" '"$@"' 244 | 245 | exec "$JAVACMD" "$@" 246 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "lgz-bot" -------------------------------------------------------------------------------- /src/main/java/com/benjaminwan/ocrlibrary/OcrEngine.java: -------------------------------------------------------------------------------- 1 | package com.benjaminwan.ocrlibrary; 2 | 3 | import io.github.mymonstercat.Model; 4 | import io.github.mymonstercat.ocr.config.HardwareConfig; 5 | import lombok.extern.slf4j.Slf4j; 6 | 7 | import java.util.Objects; 8 | 9 | /** 10 | * OCR engine object that interacts with library files through JNI. 11 | */ 12 | @Slf4j 13 | public class OcrEngine { 14 | 15 | public void initEngine(Model model, HardwareConfig hardwareConfig) { 16 | if (!isInit) { 17 | synchronized (this) { 18 | if (!isInit) { 19 | initLogger(false, false, false); 20 | setNumThread(hardwareConfig.getNumThread()); 21 | if (hardwareConfig.getGpuIndex() != -1) { 22 | setGpuIndex(hardwareConfig.getGpuIndex()); 23 | } 24 | if (!initModels(model.getTempDirPath(), model.getDetName(), model.getClsName(), model.getRecName(), model.getKeysName())) { 25 | log.error("Model initialization error, please check the models path! Model: {}", model); 26 | throw new IllegalArgumentException("Model initialization error, please check the models/keys path!"); 27 | } 28 | inferType = model.getModelType(); 29 | log.info("Inference engine initialized successfully, currently using inference engine: {}-v{}", inferType, getVersion()); 30 | log.info("Model configuration during initialization: {}, Hardware configuration: {}", model, hardwareConfig); 31 | isInit = true; 32 | } 33 | } 34 | } else { 35 | if (!Objects.equals(model.getModelType(), inferType)) { 36 | log.warn("Engine has been initialized already; switching engines post-initialization is not supported, continuing to use {} inference engine", inferType); 37 | } else { 38 | log.info("Currently using inference engine: {}-v{}", inferType, getVersion()); 39 | } 40 | } 41 | } 42 | 43 | private volatile boolean isInit = false; 44 | private String inferType; 45 | 46 | public native boolean setNumThread(int numThread); 47 | 48 | public native void initLogger(boolean isConsole, boolean isPartImg, boolean isResultImg); 49 | 50 | public native void enableResultText(String imagePath); 51 | 52 | public native boolean initModels(String modelsDir, String detName, String clsName, String recName, String keysName); 53 | 54 | public native void setGpuIndex(int gpuIndex); 55 | 56 | public native String getVersion(); 57 | 58 | public native OcrResult detect( 59 | String input, int padding, int maxSideLen, 60 | float boxScoreThresh, float boxThresh, 61 | float unClipRatio, boolean doAngle, boolean mostAngle 62 | ); 63 | 64 | public native OcrResult detectInput( 65 | OcrInput input, int padding, int maxSideLen, 66 | float boxScoreThresh, float boxThresh, 67 | float unClipRatio, boolean doAngle, boolean mostAngle 68 | ); 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/benjaminwan/ocrlibrary/OcrInput.java: -------------------------------------------------------------------------------- 1 | package com.benjaminwan.ocrlibrary; 2 | 3 | /** 4 | * 支持传入bitmap二进制数据和传输的字节数据图片 5 | */ 6 | public class OcrInput { 7 | /** 8 | * 文件或者bitmap字节数据 9 | */ 10 | private final byte[] data; 11 | /** 12 | * 0 bitmap 1 bytes 13 | */ 14 | private final int type; 15 | /** 16 | * 通道数 图片通道bitmap需要传递具体的通道数,file bytes 只需要传入1或者>1表示灰度或者彩色 17 | */ 18 | private final int channels; 19 | /** 20 | * 图片宽度 21 | */ 22 | private int width; 23 | /** 24 | * 图片高度 25 | */ 26 | private int height; 27 | 28 | public OcrInput(byte[] data, int channels, int width, int height) { 29 | this.data = data; 30 | this.type = 0; 31 | this.channels = channels; 32 | this.width = width; 33 | this.height = height; 34 | } 35 | 36 | public OcrInput(byte[] data) { 37 | this.data = data; 38 | this.type = 1; 39 | this.channels = 4; 40 | } 41 | 42 | public byte[] getData() { 43 | return data; 44 | } 45 | 46 | public int getType() { 47 | return type; 48 | } 49 | 50 | public int getChannels() { 51 | return channels; 52 | } 53 | 54 | public int getWidth() { 55 | return width; 56 | } 57 | 58 | public int getHeight() { 59 | return height; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/benjaminwan/ocrlibrary/OcrOutput.java: -------------------------------------------------------------------------------- 1 | package com.benjaminwan.ocrlibrary; 2 | 3 | 4 | public interface OcrOutput { 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/benjaminwan/ocrlibrary/OcrResult.java: -------------------------------------------------------------------------------- 1 | package com.benjaminwan.ocrlibrary; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class OcrResult implements OcrOutput { 6 | private final double dbNetTime; 7 | private final ArrayList textBlocks; 8 | private double detectTime; 9 | private String strRes; 10 | 11 | public OcrResult(double dbNetTime, ArrayList textBlocks, double detectTime, String strRes) { 12 | this.dbNetTime = dbNetTime; 13 | this.textBlocks = textBlocks; 14 | this.detectTime = detectTime; 15 | this.strRes = strRes; 16 | } 17 | 18 | public double getDbNetTime() { 19 | return dbNetTime; 20 | } 21 | 22 | public ArrayList getTextBlocks() { 23 | return textBlocks; 24 | } 25 | 26 | public double getDetectTime() { 27 | return detectTime; 28 | } 29 | 30 | public void setDetectTime(double detectTime) { 31 | this.detectTime = detectTime; 32 | } 33 | 34 | public String getStrRes() { 35 | return strRes; 36 | } 37 | 38 | public void setStrRes(String strRes) { 39 | this.strRes = strRes; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return "OcrResult{" + 45 | "dbNetTime=" + dbNetTime + 46 | ", textBlocks=" + textBlocks + 47 | ", detectTime=" + detectTime + 48 | ", strRes='" + strRes + '\'' + 49 | '}'; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/benjaminwan/ocrlibrary/Point.java: -------------------------------------------------------------------------------- 1 | package com.benjaminwan.ocrlibrary; 2 | 3 | import java.util.Objects; 4 | 5 | public class Point { 6 | private int x; 7 | private int y; 8 | 9 | public Point(int x, int y) { 10 | this.x = x; 11 | this.y = y; 12 | } 13 | 14 | public int getX() { 15 | return x; 16 | } 17 | 18 | public void setX(int x) { 19 | this.x = x; 20 | } 21 | 22 | public int getY() { 23 | return y; 24 | } 25 | 26 | public void setY(int y) { 27 | this.y = y; 28 | } 29 | 30 | @Override 31 | public boolean equals(Object o) { 32 | if (this == o) return true; 33 | if (o == null || getClass() != o.getClass()) return false; 34 | Point point = (Point) o; 35 | return x == point.x && y == point.y; 36 | } 37 | 38 | @Override 39 | public int hashCode() { 40 | return Objects.hash(x, y); 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return "Point{" + 46 | "x=" + x + 47 | ", y=" + y + 48 | '}'; 49 | } 50 | } -------------------------------------------------------------------------------- /src/main/java/com/benjaminwan/ocrlibrary/TextBlock.java: -------------------------------------------------------------------------------- 1 | package com.benjaminwan.ocrlibrary; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.Objects; 6 | 7 | public class TextBlock { 8 | private final ArrayList boxPoint; 9 | private final float boxScore; 10 | private final int angleIndex; 11 | private final float angleScore; 12 | private final double angleTime; 13 | private final String text; 14 | private final float[] charScores; 15 | private final double crnnTime; 16 | private final double blockTime; 17 | 18 | public TextBlock( 19 | ArrayList boxPoint, float boxScore, 20 | int angleIndex, float angleScore, double angleTime, 21 | String text, float[] charScores, double crnnTime, double blockTime 22 | ) { 23 | this.boxPoint = boxPoint; 24 | this.boxScore = boxScore; 25 | this.angleIndex = angleIndex; 26 | this.angleScore = angleScore; 27 | this.angleTime = angleTime; 28 | this.text = text; 29 | this.charScores = charScores; 30 | this.crnnTime = crnnTime; 31 | this.blockTime = blockTime; 32 | } 33 | 34 | public ArrayList getBoxPoint() { 35 | return boxPoint; 36 | } 37 | 38 | public float getBoxScore() { 39 | return boxScore; 40 | } 41 | 42 | public int getAngleIndex() { 43 | return angleIndex; 44 | } 45 | 46 | public float getAngleScore() { 47 | return angleScore; 48 | } 49 | 50 | public double getAngleTime() { 51 | return angleTime; 52 | } 53 | 54 | public String getText() { 55 | return text; 56 | } 57 | 58 | public float[] getCharScores() { 59 | return charScores; 60 | } 61 | 62 | public double getCrnnTime() { 63 | return crnnTime; 64 | } 65 | 66 | public double getBlockTime() { 67 | return blockTime; 68 | } 69 | 70 | @Override 71 | public boolean equals(Object o) { 72 | if (this == o) return true; 73 | if (o == null || getClass() != o.getClass()) return false; 74 | TextBlock textBlock = (TextBlock) o; 75 | return Float.compare(textBlock.boxScore, boxScore) == 0 && 76 | angleIndex == textBlock.angleIndex && 77 | Float.compare(textBlock.angleScore, angleScore) == 0 && 78 | Double.compare(textBlock.angleTime, angleTime) == 0 && 79 | Double.compare(textBlock.crnnTime, crnnTime) == 0 && 80 | Double.compare(textBlock.blockTime, blockTime) == 0 && 81 | Objects.equals(boxPoint, textBlock.boxPoint) && 82 | Objects.equals(text, textBlock.text) && 83 | Arrays.equals(charScores, textBlock.charScores); 84 | } 85 | 86 | @Override 87 | public int hashCode() { 88 | int result = Objects.hash(boxPoint, boxScore, angleIndex, angleScore, angleTime, text, crnnTime, blockTime); 89 | result = 31 * result + Arrays.hashCode(charScores); 90 | return result; 91 | } 92 | 93 | @Override 94 | public String toString() { 95 | return "TextBlock{" + 96 | "boxPoint=" + boxPoint + 97 | ", boxScore=" + boxScore + 98 | ", angleIndex=" + angleIndex + 99 | ", angleScore=" + angleScore + 100 | ", angleTime=" + angleTime + 101 | ", text='" + text + '\'' + 102 | ", charScores=" + Arrays.toString(charScores) + 103 | ", crnnTime=" + crnnTime + 104 | ", blockTime=" + blockTime + 105 | '}'; 106 | } 107 | } -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/layer/AbstractLayer.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.layer; 2 | 3 | import huzpsb.ll4j.model.Model; 4 | import huzpsb.ll4j.utils.random.NRandom; 5 | 6 | public abstract class AbstractLayer { 7 | public int input_size; 8 | public int output_size; 9 | public NRandom random; 10 | public double[] input; 11 | public double[] output; 12 | public double[] input_error = null; 13 | public double[] output_error = null; 14 | public boolean training = false; 15 | 16 | public AbstractLayer(int inputSize, int outputSize) { 17 | this(inputSize, outputSize, 1145151919810L); 18 | } 19 | 20 | public AbstractLayer(int inputSize, int outputSize, long seed) { 21 | input_size = inputSize; 22 | output_size = outputSize; 23 | input = new double[input_size]; 24 | output = new double[output_size]; 25 | random = new NRandom(seed); 26 | } 27 | 28 | public void makeInputError() { 29 | if (input_error == null) { 30 | input_error = new double[input_size]; 31 | } 32 | } 33 | 34 | public void makeOutputError() { 35 | if (output_error == null) { 36 | output_error = new double[output_size]; 37 | } 38 | } 39 | 40 | public AbstractLayer mergeWith(AbstractLayer layer) { 41 | if (layer.getClass() != this.getClass()) { 42 | throw new RuntimeException("Can't merge different layer type"); 43 | } 44 | if (layer.input_size != input_size || layer.output_size != output_size) { 45 | throw new RuntimeException("Can't merge different layer size"); 46 | } 47 | // need to implement 48 | StringBuilder builder = new StringBuilder(); 49 | this.serialize(builder); 50 | return Model.parseLine(builder.toString().replaceAll("\n", "")); 51 | } 52 | 53 | @Override 54 | public AbstractLayer clone() { 55 | try { 56 | return (AbstractLayer) super.clone(); 57 | } catch (Exception e) { 58 | throw new RuntimeException(e); 59 | } 60 | } 61 | 62 | public abstract void forward(); 63 | 64 | public abstract void backward(); 65 | 66 | public abstract void update(double learningRate); 67 | 68 | public abstract void randomize(double rv); 69 | 70 | public abstract void initialize(); 71 | 72 | public abstract void serialize(StringBuilder sb); 73 | 74 | public interface TrainOnlyLayer { } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/layer/DenseLayer.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.layer; 2 | 3 | public class DenseLayer extends AbstractLayer { 4 | public double[][] weights; 5 | 6 | public DenseLayer(int inputSize, int outputSize) { 7 | super(inputSize, outputSize); 8 | weights = new double[inputSize][outputSize]; 9 | initialize(); 10 | } 11 | 12 | @Override 13 | public void forward() { 14 | for (int i = 0; i < output_size; i++) { 15 | output[i] = 0; 16 | for (int j = 0; j < input_size; j++) { 17 | output[i] += input[j] * weights[j][i]; 18 | } 19 | } 20 | } 21 | 22 | @Override 23 | public void backward() { 24 | makeInputError(); 25 | for (int i = 0; i < input_size; i++) { 26 | input_error[i] = 0; 27 | for (int j = 0; j < output_size; j++) { 28 | input_error[i] += output_error[j] * weights[i][j]; 29 | } 30 | } 31 | } 32 | 33 | @Override 34 | public void update(double learningRate) { 35 | for (int i = 0; i < input_size; i++) { 36 | for (int j = 0; j < output_size; j++) { 37 | double delta = learningRate * output_error[j] * input[i]; 38 | weights[i][j] -= delta; 39 | } 40 | } 41 | } 42 | 43 | @Override 44 | public void randomize(double rv) { 45 | for (int i = 0; i < input_size; i++) { 46 | for (int j = 0; j < output_size; j++) { 47 | weights[i][j] *= random.nextDouble(1 - rv, 1 + rv); 48 | } 49 | } 50 | } 51 | 52 | @Override 53 | public AbstractLayer mergeWith(AbstractLayer layer) { 54 | super.mergeWith(layer); 55 | DenseLayer denseLayer = (DenseLayer) layer; 56 | DenseLayer output = new DenseLayer(input_size, output_size); 57 | double[][] weights = output.weights; 58 | for (int i = 0; i < input_size; i++) { 59 | for (int j = 0; j < output_size; j++) { 60 | weights[i][j] = (this.weights[i][j] + denseLayer.weights[i][j]) / 2.0; 61 | } 62 | } 63 | return output; 64 | } 65 | 66 | @Override 67 | public void initialize() { 68 | for (int i = 0; i < input_size; i++) { 69 | for (int j = 0; j < output_size; j++) { 70 | weights[i][j] = random.nextGaussian(0, 1.0 / Math.sqrt(input_size)); 71 | } 72 | } 73 | } 74 | 75 | @Override 76 | public void serialize(StringBuilder sb) { 77 | sb.append("D ").append(input_size).append(" ").append(output_size).append(" "); 78 | for (int i = 0; i < input_size; i++) { 79 | for (int j = 0; j < output_size; j++) { 80 | sb.append(weights[i][j]).append(" "); 81 | } 82 | } 83 | sb.append("\n"); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/layer/DropoutLayer.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.layer; 2 | 3 | public class DropoutLayer extends AbstractLayer implements AbstractLayer.TrainOnlyLayer { 4 | protected double dropoutRate; 5 | protected boolean dropoutState = false; 6 | protected boolean[] mask; 7 | 8 | public DropoutLayer(int input) { 9 | super(input, input); 10 | } 11 | 12 | @Override 13 | public void forward() { 14 | updateMask(); 15 | output = input; 16 | if (!dropoutState || !training || dropoutRate >= 1.0) return; 17 | double div = 1 - dropoutRate; 18 | for (int i = 0; i < input.length; i++) { 19 | output[i] = (mask[i] ? 1 : 0) * input[i] / div; 20 | } 21 | } 22 | 23 | @Override 24 | public void backward() { 25 | input_error = output_error; 26 | if (!dropoutState || !training) return; 27 | makeInputError(); 28 | for (int i = 0; i < input_error.length; i++) { 29 | input_error[i] *= mask[i] ? 1 : 0; 30 | } 31 | } 32 | 33 | @Override 34 | public void update(double learningRate) { 35 | // nothing to update 36 | } 37 | 38 | @Override 39 | public void randomize(double rv) {} 40 | 41 | @Override 42 | public void initialize() { 43 | updateMask(); 44 | } 45 | 46 | public void updateMask() { 47 | mask = new boolean[input_size]; 48 | if (dropoutRate >= 1.0 || !training || !dropoutState) return; 49 | for (int i = 0; i < input_size; i++) { 50 | mask[i] = random.nextGaussian() <= dropoutRate; 51 | } 52 | } 53 | 54 | public DropoutLayer enable() { 55 | dropoutState = true; 56 | return this; 57 | } 58 | 59 | public DropoutLayer disable() { 60 | dropoutState = false; 61 | return this; 62 | } 63 | 64 | public DropoutLayer dropout(double rate) { 65 | this.dropoutRate = rate; 66 | updateMask(); 67 | enable(); 68 | return this; 69 | } 70 | 71 | public double getDropoutRate() { 72 | return dropoutRate; 73 | } 74 | 75 | @Override 76 | public void serialize(StringBuilder sb) { 77 | sb.append("Dropout ").append(input_size).append(" ").append(dropoutRate).append("\n"); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/layer/JudgeLayer.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.layer; 2 | 3 | public class JudgeLayer extends AbstractLayer { 4 | public int result = 0; 5 | 6 | public JudgeLayer(int size) { 7 | super(size, size); 8 | } 9 | 10 | @Override 11 | public void forward() { 12 | for (int i = 0; i < input_size; i++) { 13 | if (Double.isNaN(input[i])) { 14 | throw new RuntimeException("input[" + i + "] is NaN! Plz reduce learning rate!"); 15 | } 16 | if (input[i] > input[result]) { 17 | result = i; 18 | } 19 | } 20 | } 21 | 22 | @Override 23 | public void backward() { 24 | makeInputError(); 25 | for (int i = 0; i < input_size; i++) { 26 | if (i == result) { 27 | input_error[i] = input[i] - 1; 28 | } else { 29 | input_error[i] = input[i]; 30 | } 31 | } 32 | } 33 | 34 | @Override 35 | public void update(double learningRate) { 36 | } 37 | 38 | @Override 39 | public void randomize(double rv) { 40 | } 41 | 42 | @Override 43 | public void initialize() { 44 | } 45 | 46 | @Override 47 | public void serialize(StringBuilder sb) { 48 | sb.append("J ").append(input_size).append("\n"); 49 | } 50 | 51 | public static JudgeLayer judgeLayer(int size) { 52 | return new JudgeLayer(size); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/layer/LeakyRelu.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.layer; 2 | 3 | public class LeakyRelu extends AbstractLayer { 4 | public LeakyRelu(int size) { 5 | super(size, size); 6 | } 7 | 8 | @Override 9 | public void forward() { 10 | for (int i = 0; i < output_size; i++) { 11 | if (input[i] > 0) { 12 | output[i] = input[i]; 13 | } else { 14 | output[i] = input[i] * 0.01; 15 | } 16 | } 17 | } 18 | 19 | @Override 20 | public void backward() { 21 | makeInputError(); 22 | for (int i = 0; i < output_size; i++) { 23 | if (input[i] > 0) { 24 | input_error[i] = output_error[i]; 25 | } else { 26 | input_error[i] = output_error[i] * 0.01; 27 | } 28 | } 29 | } 30 | 31 | @Override 32 | public void update(double learningRate) { 33 | // ReLu has no parameters to update 34 | } 35 | 36 | @Override 37 | public void randomize(double rv) { 38 | // ReLu has no parameters to randomize 39 | } 40 | 41 | @Override 42 | public void initialize() { 43 | // ReLu has no parameters to initialize 44 | } 45 | 46 | @Override 47 | public void serialize(StringBuilder sb) { 48 | sb.append("L ").append(input_size).append("\n"); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/layer/Sigmoid.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.layer; 2 | 3 | public class Sigmoid extends AbstractLayer { 4 | public Sigmoid(int size) { 5 | super(size, size); 6 | } 7 | 8 | @Override 9 | public void forward() { 10 | for (int i = 0; i < output_size; i++) { 11 | output[i] = 1 / (1 + Math.exp(-input[i])); 12 | } 13 | } 14 | 15 | @Override 16 | public void backward() { 17 | makeInputError(); 18 | for (int i = 0; i < output_size; i++) { 19 | input_error[i] = output_error[i] * output[i] * (1 - output[i]); 20 | } 21 | } 22 | 23 | @Override 24 | public void update(double learningRate) { 25 | // Sigmoid has no parameters to update 26 | } 27 | 28 | @Override 29 | public void randomize(double rv) { 30 | // Sigmoid has no parameters to randomize 31 | } 32 | 33 | @Override 34 | public void initialize() { 35 | // Sigmoid has no parameters to initialize 36 | } 37 | 38 | @Override 39 | public void serialize(StringBuilder sb) { 40 | sb.append("S ").append(input_size).append("\n"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/layer/Softmax.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.layer; 2 | 3 | public class Softmax extends AbstractLayer { 4 | public Softmax(int inputSize) { 5 | super(inputSize, inputSize); 6 | } 7 | 8 | @Override 9 | public void forward() { 10 | double[] input = this.input; 11 | double[] output = this.output; 12 | double maxInput = Double.NEGATIVE_INFINITY; 13 | for (int i = 0; i < input_size; i++) { 14 | maxInput = Math.max(maxInput, input[i]); 15 | } 16 | 17 | double sum = 0.0; 18 | for (int i = 0; i < input_size; i++) { 19 | output[i] = Math.exp(input[i] - maxInput); 20 | sum += output[i]; 21 | } 22 | 23 | // 归一化输出 24 | for (int i = 0; i < input_size; i++) { 25 | output[i] /= sum; 26 | } 27 | } 28 | 29 | @Override 30 | public void backward() { 31 | makeInputError(); 32 | // 计算每个元素的输入误差 33 | for (int i = 0; i < input_size; i++) { 34 | // softmax层的导数是当前输出值乘以(1 - 当前输出值),再乘以外部传入的该位置的输出误差 35 | double derivative = output[i] * (1.0 - output[i]) * output_error[i]; 36 | 37 | // 更新输入误差 38 | input_error[i] = derivative; 39 | } 40 | } 41 | 42 | @Override 43 | public void update(double learningRate) { 44 | 45 | } 46 | 47 | @Override 48 | public void randomize(double rv) { 49 | 50 | } 51 | 52 | @Override 53 | public void initialize() { 54 | 55 | } 56 | 57 | @Override 58 | public void serialize(StringBuilder sb) { 59 | sb.append("Softmax ").append(input_size).append("\n"); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/layer/Tanh.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.layer; 2 | 3 | public class Tanh extends AbstractLayer { 4 | public Tanh(int inputSize) { 5 | super(inputSize, inputSize); 6 | } 7 | 8 | @Override 9 | public void forward() { 10 | for (int i = 0; i < output_size; i++) { 11 | // double x = input[i]; 12 | output[i] = Math.tanh(input[i]); 13 | // output[i] = (exp(x) - exp(-x)) / (exp(x) + exp(-x)); 14 | } 15 | } 16 | 17 | @Override 18 | public void backward() { 19 | makeInputError(); 20 | for (int i = 0; i < input_size; i++) { 21 | double x = input_error[i]; 22 | double derivativeTanh = 1.0 - output[i] * output[i]; // tanh导数公式 23 | input_error[i] = derivativeTanh * x; 24 | } 25 | } 26 | 27 | @Override 28 | public void update(double learningRate) { 29 | 30 | } 31 | 32 | @Override 33 | public void randomize(double rv) { 34 | 35 | } 36 | 37 | @Override 38 | public void initialize() { 39 | 40 | } 41 | 42 | @Override 43 | public void serialize(StringBuilder sb) { 44 | sb.append("Th ").append(input_size).append("\n"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/minrt/JFuncModelScript.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.minrt; 2 | 3 | import java.util.function.Function; 4 | 5 | public class JFuncModelScript { 6 | public static Function compile(String[] script) { 7 | Function current = (input) -> { 8 | double[] copied = new double[input.length]; 9 | System.arraycopy(input, 0, copied, 0, input.length); 10 | return copied; 11 | }; 12 | 13 | for (String str : script) { 14 | if (str.length() < 2) { 15 | continue; 16 | } 17 | String[] tokens = str.split(" "); 18 | switch (tokens[0]) { 19 | case "D": 20 | int ic = Integer.parseInt(tokens[1]); 21 | int oc = Integer.parseInt(tokens[2]); 22 | double[] weights = new double[ic * oc]; 23 | for (int i = 0; i < oc; i++) { 24 | for (int j = 0; j < ic; j++) { 25 | weights[i + j * oc] = Double.parseDouble(tokens[3 + i + j * oc]); 26 | } 27 | } 28 | current = current.andThen((input) -> { 29 | if (input.length != ic) { 30 | throw new RuntimeException("Wrong input size for Dense layer (expected " + ic + ", got " + input.length + ")"); 31 | } 32 | double[] tmp = new double[oc]; 33 | for (int i = 0; i < oc; i++) { 34 | double sum = 0; 35 | for (int j = 0; j < ic; j++) { 36 | sum += input[j] * weights[i + j * oc]; 37 | } 38 | tmp[i] = sum; 39 | } 40 | return tmp; 41 | }); 42 | break; 43 | case "L": 44 | int n = Integer.parseInt(tokens[1]); 45 | current = current.andThen((input) -> { 46 | if (input.length != n) { 47 | throw new RuntimeException("Wrong input size for LeakyRelu layer (expected " + n + ", got " + input.length + ")"); 48 | } 49 | double[] tmp = new double[n]; 50 | for (int i = 0; i < n; i++) { 51 | tmp[i] = input[i] > 0 ? input[i] : input[i] * 0.01; 52 | } 53 | return tmp; 54 | }); 55 | break; 56 | } 57 | } 58 | return current; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/minrt/MinRt.java: -------------------------------------------------------------------------------- 1 | // MinRt is a minimal runtime for LL4J deep learning framework 2 | // While MinRt is as small as possible, it is SLOW and INEFFICIENT. 3 | // Use MinRt only when you need to run LL4J models in a memory-constrained environment. 4 | // Copyright (c) 2024 huzpsb [adminhuzpsbeuorg] 5 | // Licensed under the WTFPL license. You may remove this notice at will. 6 | 7 | package huzpsb.ll4j.minrt; 8 | 9 | public class MinRt { 10 | public static int predict(double[] input, String[] script) { 11 | double[] current = new double[input.length]; 12 | System.arraycopy(input, 0, current, 0, input.length); 13 | for (String str : script) { 14 | if (str.length() < 2) { 15 | continue; 16 | } 17 | String[] tokens = str.split(" "); 18 | switch (tokens[0]) { 19 | case "D": 20 | int ic = Integer.parseInt(tokens[1]); 21 | int oc = Integer.parseInt(tokens[2]); 22 | if (current.length != ic) { 23 | throw new RuntimeException("Wrong input size for Dense layer (expected " + ic + ", got " + current.length + ")"); 24 | } 25 | double[] tmp = new double[oc]; 26 | for (int i = 0; i < oc; i++) { 27 | double sum = 0; 28 | for (int j = 0; j < ic; j++) { 29 | sum += current[j] * Double.parseDouble(tokens[3 + i + j * oc]); 30 | } 31 | tmp[i] = sum; 32 | } 33 | current = tmp; 34 | break; 35 | case "L": { 36 | int n = Integer.parseInt(tokens[1]); 37 | if (current.length != n) { 38 | throw new RuntimeException("Wrong input size for LeakyRelu layer (expected " + n + ", got " + current.length + ")"); 39 | } 40 | for (int i = 0; i < n; i++) { 41 | current[i] = current[i] > 0 ? current[i] : current[i] * 0.01; 42 | } 43 | break; 44 | } 45 | case "S": { 46 | int n = Integer.parseInt(tokens[1]); 47 | if (current.length != n) { 48 | throw new RuntimeException("Wrong input size for Sigmoid layer (expected " + n + ", got " + current.length + ")"); 49 | } 50 | for (int i = 0; i < n; i++) { 51 | current[i] = 1 / (1 + Math.exp(-current[i])); 52 | } 53 | break; 54 | } 55 | case "Th": { 56 | int n = Integer.parseInt(tokens[1]); 57 | if (current.length != n) { 58 | throw new RuntimeException("Wrong input size for Tanh layer (expected " + n + ", got " + current.length + ")"); 59 | } 60 | for (int i = 0; i < n; i++) { 61 | current[i] = Math.tanh(current[i]); 62 | } 63 | break; 64 | } 65 | case "Softmax": { 66 | int n = Integer.parseInt(tokens[1]); 67 | if (current.length != n) { 68 | throw new RuntimeException("Wrong input size for Softmax layer (expected " + n + ", got " + current.length + ")"); 69 | } 70 | double sum = 0; 71 | for (int i = 0; i < n; i++) { 72 | sum += Math.exp(current[i]); 73 | } 74 | for (int i = 0; i < n; i++) { 75 | current[i] = Math.exp(current[i]) / sum; 76 | } 77 | break; 78 | } 79 | case "J": 80 | int m = Integer.parseInt(tokens[1]); 81 | if (current.length != m) { 82 | throw new RuntimeException("Wrong input size for Judge layer (expected " + m + ", got " + current.length + ")"); 83 | } 84 | int idx = 0; 85 | for (int i = 1; i < m; i++) { 86 | if (current[i] > current[idx]) { 87 | idx = i; 88 | } 89 | } 90 | return idx; 91 | case "Dropout": 92 | break; 93 | default: 94 | throw new RuntimeException("Unknown layer[minRt] type"); 95 | } 96 | } 97 | throw new RuntimeException("No output layer"); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/nlp/token/CharUtils.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.nlp.token; 2 | 3 | import java.util.regex.Pattern; 4 | 5 | /** 6 | * from jie-ba 7 | */ 8 | public class CharUtils { 9 | private static final char[] connectors = new char[] { '+', '#', '&', '.', '_', '-' }; 10 | 11 | 12 | public static boolean isChineseLetter(char ch) { 13 | return ch >= 0x4E00 && ch <= 0x9FA5; 14 | } 15 | 16 | 17 | public static boolean isEnglishLetter(char ch) { 18 | return (ch >= 0x0041 && ch <= 0x005A) || (ch >= 0x0061 && ch <= 0x007A); 19 | } 20 | 21 | 22 | public static boolean isDigit(char ch) { 23 | return ch >= 0x0030 && ch <= 0x0039; 24 | } 25 | 26 | 27 | public static boolean isConnector(char ch) { 28 | for (char connector : connectors) 29 | if (ch == connector) 30 | return true; 31 | return false; 32 | } 33 | 34 | 35 | public static boolean ccFind(char ch) { 36 | if (isChineseLetter(ch)) 37 | return true; 38 | if (isEnglishLetter(ch)) 39 | return true; 40 | if (isDigit(ch)) 41 | return true; 42 | return isConnector(ch); 43 | } 44 | 45 | private static final Pattern ENGLISH_CONTEXT = Pattern.compile("[a-zA-Z]+"); 46 | 47 | public static boolean isEnglishContext(String input) { 48 | return ENGLISH_CONTEXT.matcher(input).matches(); 49 | } 50 | 51 | /** 52 | * 全角 to 半角,大写 to 小写 53 | * 54 | * @param input 55 | * 输入字符 56 | * @return 转换后的字符 57 | */ 58 | public static char regularize(char input) { 59 | if (input == 0x3000) { 60 | return 32; 61 | } 62 | else if (input > 0xff00 && input < 0xff5f) { 63 | return (char) (input - 0xfee0); 64 | } 65 | else if (input >= 'A' && input <= 'Z') { 66 | input += 32; 67 | } 68 | return input; 69 | } 70 | 71 | public static String regularize(String input) { 72 | StringBuilder sb = new StringBuilder(); 73 | for (int i = 0; i < input.length(); i++) { 74 | char c = regularize(input.charAt(i)); 75 | if (ccFind(c)) sb.append(c); 76 | } 77 | return sb.toString(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/nlp/token/Tokenizer.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.nlp.token; 2 | 3 | import huzpsb.ll4j.utils.data.DataEntry; 4 | 5 | import java.io.*; 6 | 7 | public class Tokenizer { 8 | private final String[] vocab; 9 | 10 | public Tokenizer(String[] vocab) { 11 | this.vocab = vocab; 12 | } 13 | 14 | public Tokenizer(String[] vocab, int start, int length) { 15 | this.vocab = new String[length]; 16 | System.arraycopy(vocab, start, this.vocab, 0, length); 17 | } 18 | 19 | public static Tokenizer load(InputStream stream) { 20 | return load(new InputStreamReader(stream)); 21 | } 22 | 23 | public static Tokenizer load(Reader reader) { 24 | return load(new BufferedReader(reader)); 25 | } 26 | 27 | public static Tokenizer load(BufferedReader reader) { 28 | String[] vocab = null; 29 | try (reader) { 30 | String str; 31 | int index = 0; 32 | while ((str = reader.readLine()) != null) { 33 | if (vocab == null) { 34 | int size = Integer.parseInt(str); 35 | vocab = new String[size]; 36 | continue; 37 | } 38 | vocab[index++] = str; 39 | } 40 | } catch (IOException e) { 41 | throw new RuntimeException(e); 42 | } 43 | if (vocab == null) return null; 44 | return new Tokenizer(vocab); 45 | } 46 | 47 | public DataEntry tokenize(int type, String text) { 48 | String regularized = CharUtils.regularize(text); 49 | double[] values = new double[vocab.length + 1]; 50 | values[0] = text.length(); 51 | for (int i = 0; i < vocab.length; i++) { 52 | values[i + 1] = regularized.contains(vocab[i]) ? 1 : 0; 53 | } 54 | return new DataEntry(type, values); 55 | } 56 | 57 | public void saveToFile(String filename) { 58 | try (PrintWriter writer = new PrintWriter(filename)) { 59 | writer.println(vocab.length); 60 | for (String word : vocab) { 61 | writer.println(word); 62 | } 63 | } catch (Exception ignored) { 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/nlp/token/TokenizerBuilder.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.nlp.token; 2 | 3 | import java.util.HashMap; 4 | import java.util.LinkedList; 5 | import java.util.Map; 6 | import java.util.PriorityQueue; 7 | 8 | public class TokenizerBuilder { 9 | private final int outputSize; 10 | private final Map occurrences = new HashMap<>(); 11 | public int minLength = 1, maxLength = 5; 12 | public boolean smartEnglishContext = true, checkRepeat = false; 13 | 14 | 15 | public TokenizerBuilder(int outputSize) { 16 | this.outputSize = outputSize; 17 | } 18 | 19 | public void update(String text) { 20 | text = CharUtils.regularize(text); 21 | LinkedList engStr = new LinkedList<>(); 22 | if (smartEnglishContext) { 23 | int[] charArray = text.codePoints().toArray(); 24 | for (int i = 0; i < charArray.length; i++) { 25 | char c = (char) charArray[i]; 26 | if (CharUtils.isEnglishLetter(c)) { 27 | int until = i + 1; 28 | StringBuilder builder = new StringBuilder(); 29 | builder.append(c); 30 | for (; until < charArray.length; until++) { 31 | char ch = (char) charArray[until]; 32 | if (CharUtils.isEnglishLetter(ch)) { 33 | builder.append(ch); 34 | i = until; 35 | } 36 | else break; 37 | } 38 | String s = builder.toString(); 39 | updateVocab(s); 40 | engStr.add(s); 41 | } 42 | } 43 | } 44 | 45 | int length = text.length(); 46 | for (int i = 0; i < length; i++) { 47 | for (int j = i + minLength; j <= length; j++) { 48 | if(j - i > maxLength) break; 49 | String substring = text.substring(i, j); 50 | if (checkRepeat && substring.length() > 1) { 51 | String str2 = substring.concat(substring); 52 | if (str2.substring(1, str2.length() - 2).contains(substring)) continue; 53 | } 54 | 55 | if (smartEnglishContext && CharUtils.isEnglishContext(substring)) { 56 | i = j; 57 | for (String s : engStr) { 58 | if (s.contains(substring) || substring.contains(s)) { 59 | updateVocab(s); 60 | break; 61 | } 62 | } 63 | continue; 64 | } 65 | 66 | updateVocab(substring); 67 | } 68 | } 69 | } 70 | 71 | private void updateVocab(String text) { 72 | if (occurrences.containsKey(text)) { 73 | occurrences.put(text, occurrences.get(text) + 1); 74 | } else { 75 | occurrences.put(text, 1); 76 | } 77 | } 78 | 79 | public Tokenizer build() { 80 | String[] vocab = new String[outputSize]; 81 | PriorityQueue queue = new PriorityQueue<>(); 82 | for (Map.Entry entry : occurrences.entrySet()) { 83 | String key = entry.getKey(); 84 | int value = entry.getValue(); 85 | double weight = value * Math.log(key.length() + 10); 86 | queue.add(new Entry(key, weight)); 87 | } 88 | for (int i = 0; i < outputSize; i++) { 89 | if (queue.isEmpty()) { 90 | break; 91 | } 92 | vocab[i] = queue.poll().key; 93 | } 94 | return new Tokenizer(vocab); 95 | } 96 | 97 | public void loadDefault() { 98 | for (char i = 'a'; i < 'z'; i++) { 99 | occurrences.put(String.valueOf(i), 0); 100 | } 101 | for (char i = 'A'; i < 'Z'; i++) { 102 | occurrences.put(String.valueOf(i), 0); 103 | } 104 | for (char i = '0'; i < '9'; i++) { 105 | occurrences.put(String.valueOf(i), 0); 106 | } 107 | occurrences.put(" ", 0); 108 | } 109 | 110 | static class Entry implements Comparable { 111 | String key; 112 | double value; 113 | 114 | public Entry(String key, double value) { 115 | this.key = key; 116 | this.value = value; 117 | } 118 | 119 | @Override 120 | public int compareTo(Entry o) { 121 | return Double.compare(o.value, value); 122 | } 123 | } 124 | } 125 | 126 | -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/utils/data/DataEntry.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.utils.data; 2 | 3 | public class DataEntry { 4 | public final int type; 5 | public final double[] values; 6 | 7 | public DataEntry(int type, double[] values) { 8 | this.type = type; 9 | this.values = values; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/utils/data/DataSet.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.utils.data; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class DataSet { 7 | public final List split = new ArrayList<>(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/utils/pair/ImmutablePair.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.utils.pair; 2 | public class ImmutablePair extends PairBase { 3 | private final K first; 4 | private final V second; 5 | 6 | public ImmutablePair(K first, V second) { 7 | this.first = first; 8 | this.second = second; 9 | } 10 | 11 | @Override 12 | public K first() { 13 | return first; 14 | } 15 | 16 | @Override 17 | public V second() { 18 | return second; 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/utils/pair/MutablePair.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.utils.pair; 2 | 3 | public class MutablePair extends PairBase { 4 | private K first; 5 | private V second; 6 | 7 | public MutablePair(K first, V second) { 8 | this.first = first; 9 | this.second = second; 10 | } 11 | @Override 12 | public K first() { 13 | return first; 14 | } 15 | 16 | @Override 17 | public V second() { 18 | return second; 19 | } 20 | 21 | public void setFirst(K first) { 22 | this.first = first; 23 | } 24 | 25 | public void setSecond(V second) { 26 | this.second = second; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/utils/pair/Pair.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.utils.pair; 2 | 3 | public interface Pair { 4 | K first(); 5 | V second(); 6 | 7 | static Pair create(K key, V value) { 8 | return new ImmutablePair<>(key, value); 9 | } 10 | 11 | default Pair mutable() { 12 | return new MutablePair<>(first(), second()); 13 | } 14 | 15 | default Pair immutable() { 16 | return new ImmutablePair<>(first(), second()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/utils/pair/PairBase.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.utils.pair; 2 | 3 | public abstract class PairBase implements Pair { 4 | @Override 5 | public String toString() { 6 | return "Pair(first=" + first() + ",second=" + second() + ")"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/utils/random/NRandom.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.utils.random; 2 | 3 | public class NRandom { 4 | private long seed; 5 | 6 | public NRandom(long seed) { 7 | this.seed = seed; 8 | } 9 | 10 | public double nextDouble() { 11 | return (((long) (next(26)) << 27) + next(27)) * 0x1.0p-53; 12 | } 13 | 14 | public double nextDouble(double min, double max) { 15 | return nextDouble() * (max - min) + min; 16 | } 17 | 18 | public double nextGaussian() { 19 | double u = nextDouble(); 20 | double v = nextDouble(); 21 | return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v); 22 | } 23 | 24 | public double nextGaussian(double mean, double std) { 25 | return nextGaussian() * std + mean; 26 | } 27 | 28 | private int next(int bits) { 29 | seed = (seed * 0x5DEECE66DL + 0xBL) & (1L << 48) - 1; 30 | return (int) (seed >>> (48 - bits)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/huzpsb/ll4j/utils/random/RandomSeedGenerator.java: -------------------------------------------------------------------------------- 1 | package huzpsb.ll4j.utils.random; 2 | 3 | import java.util.concurrent.ThreadLocalRandom; 4 | 5 | public abstract class RandomSeedGenerator { 6 | public static final class Builtin { 7 | public static final RandomSeedGenerator CONSTANT = new ConstantRandomGenerator(1145141919810L), RANDOM = new RandomGenerator(); 8 | } 9 | 10 | public abstract long generateSeed(); 11 | 12 | public static class ConstantRandomGenerator extends RandomSeedGenerator { 13 | private final long seed; 14 | 15 | public ConstantRandomGenerator(long seed) { 16 | this.seed = seed; 17 | } 18 | @Override 19 | public long generateSeed() { 20 | return seed; 21 | } 22 | } 23 | 24 | public static class RandomGenerator extends RandomSeedGenerator { 25 | @Override 26 | public long generateSeed() { 27 | return ThreadLocalRandom.current().nextLong(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/github/mymonstercat/JarFileUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.mymonstercat; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import java.io.File; 6 | import java.io.FileNotFoundException; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.nio.file.Files; 10 | import java.nio.file.StandardCopyOption; 11 | import java.util.Objects; 12 | 13 | /** 14 | * @author Monster 15 | */ 16 | @Slf4j 17 | public class JarFileUtil { 18 | 19 | public static final String TEMP_DIR = new File(Objects.toString(System.getProperty("java.io.tmpdir")), "ocrJava").getPath(); 20 | 21 | private JarFileUtil() { 22 | } 23 | 24 | static File tempDir = null; 25 | 26 | /** 27 | * @param filePath resources下的文件路径 28 | * @param aimDir 29 | * @param load 30 | * @param deleteOnExit 31 | * @throws IOException 32 | */ 33 | public static synchronized void copyFileFromJar(String filePath, String aimDir, boolean load, boolean deleteOnExit) throws IOException { 34 | 35 | // 获取文件名并校验 36 | String[] parts = filePath.split("/"); 37 | String filename = (parts.length > 1) ? parts[parts.length - 1] : null; 38 | if (filename == null || filename.length() < 3) { 39 | throw new IllegalArgumentException("文件名必须至少有3个字符长."); 40 | } 41 | // 检查目标文件夹是否存在 42 | if (tempDir == null) { 43 | tempDir = new File(TEMP_DIR, aimDir); 44 | if (!tempDir.exists() && !tempDir.mkdirs()) { 45 | throw new IOException("无法在临时目录创建文件夹" + tempDir); 46 | } 47 | } 48 | // 在临时文件夹下创建文件 49 | File temp = new File(tempDir, filename.startsWith("/") ? filename : "/" + filename); 50 | if (!temp.exists()) { 51 | // 从jar包中复制文件到系统临时文件夹 52 | try (InputStream is = JarFileUtil.class 53 | .getResourceAsStream("/" + filePath)) { 54 | if (is != null) { 55 | Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING); 56 | } else { 57 | throw new NullPointerException(); 58 | } 59 | } catch (IOException e) { 60 | Files.delete(temp.toPath()); 61 | throw new IOException("无法复制文件 " + filePath + " 到 " + temp.getAbsolutePath(), e); 62 | } catch (NullPointerException e) { 63 | throw new FileNotFoundException("文件 " + filePath + " 在JAR中未找到."); 64 | } 65 | } 66 | // 加载临时文件夹中的动态库 67 | if (load) { 68 | System.load(temp.getAbsolutePath()); 69 | } 70 | // JVM结束时删除临时文件和临时文件夹 71 | if (deleteOnExit) { 72 | temp.deleteOnExit(); 73 | tempDir.deleteOnExit(); 74 | } 75 | log.debug("将文件{}复制到{},加载此文件:{},JVM退出时删除此文件:{}", filePath, aimDir, load, deleteOnExit); 76 | } 77 | 78 | /** 79 | * 从jar包中复制models文件夹下的内容 80 | * 81 | * @param model 要使用的模型 82 | */ 83 | public static void copyModelsFromJar(Model model, boolean isDelOnExit) throws IOException { 84 | String modelsDir = model.getModelsDir(); 85 | String noStart = modelsDir.startsWith("/") ? modelsDir.substring(1, modelsDir.length()) : modelsDir; 86 | String base = noStart.endsWith("/") ? noStart : noStart + "/"; 87 | for (final String path : model.getModelFileArray()) { 88 | copyFileFromJar(base + path, "/" + model.getModelType(), Boolean.FALSE, isDelOnExit); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/io/github/mymonstercat/Model.java: -------------------------------------------------------------------------------- 1 | package io.github.mymonstercat; 2 | 3 | import lombok.Getter; 4 | import lombok.ToString; 5 | 6 | /** 7 | * @author Monster 8 | */ 9 | @Getter 10 | @ToString 11 | public enum Model { 12 | ONNX_PPOCR_V3("/models", "onnx", "ch_PP-OCRv3_det_infer.onnx", "ch_ppocr_mobile_v2.0_cls_infer.onnx", "ch_PP-OCRv3_rec_infer.onnx", "ppocr_keys_v1.txt"), 13 | ONNX_PPOCR_V4("/models", "onnx", "ch_PP-OCRv4_det_infer.onnx", "ch_ppocr_mobile_v2.0_cls_infer.onnx", "ch_PP-OCRv4_rec_infer.onnx", "ppocr_keys_v1.txt"), 14 | //ONNX_PPOCR_V4_SERVER("/models", "onnx","ch_PP-OCRv4_det_server_infer.onnx", "ch_ppocr_mobile_v2.0_cls_infer.onnx", "ch_PP-OCRv4_rec_server_infer.onnx", "ppocr_keys_v1.txt"), 15 | NCNN_PPOCR_V3("/models", "ncnn", "ch_PP-OCRv3_det_infer", "ch_ppocr_mobile_v2.0_cls_infer", "ch_PP-OCRv3_rec_infer", "ppocr_keys_v1.txt"), 16 | ; 17 | 18 | 19 | Model(String modelsDir, String modelType, String detName, String clsName, String recName, String keysName) { 20 | this.modelsDir = modelsDir; 21 | this.modelType = modelType; 22 | this.detName = detName; 23 | this.clsName = clsName; 24 | this.recName = recName; 25 | this.keysName = keysName; 26 | } 27 | 28 | private final String modelsDir; 29 | private final String modelType; 30 | private final String detName; 31 | private final String clsName; 32 | private final String recName; 33 | private final String keysName; 34 | 35 | public String getTempDirPath() { 36 | return JarFileUtil.TEMP_DIR + "/" + modelType; 37 | } 38 | 39 | public String[] getModelFileArray() { 40 | if (modelType.equals("onnx")) { 41 | return new String[]{detName, clsName, recName, keysName}; 42 | } else { 43 | final String bin = ".bin"; 44 | final String param = ".param"; 45 | return new String[]{ 46 | detName + bin, detName + param, 47 | clsName + bin, clsName + param, 48 | recName + bin, recName + param, 49 | keysName 50 | }; 51 | } 52 | 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/io/github/mymonstercat/exception/LoadException.java: -------------------------------------------------------------------------------- 1 | package io.github.mymonstercat.exception; 2 | 3 | public class LoadException extends Exception { 4 | 5 | public LoadException() { 6 | super(); 7 | } 8 | 9 | public LoadException(String message) { 10 | super(message); 11 | } 12 | 13 | public LoadException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | 17 | public LoadException(Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/io/github/mymonstercat/loader/LibraryLoader.java: -------------------------------------------------------------------------------- 1 | package io.github.mymonstercat.loader; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * @author Monster 7 | */ 8 | public interface LibraryLoader { 9 | 10 | void loadLibrary() throws IOException; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/github/mymonstercat/loader/ModelsLoader.java: -------------------------------------------------------------------------------- 1 | package io.github.mymonstercat.loader; 2 | 3 | import io.github.mymonstercat.Model; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * @author Monster 9 | */ 10 | public interface ModelsLoader { 11 | void loadModels(Model model) throws IOException; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/github/mymonstercat/ocr/InferenceEngine.java: -------------------------------------------------------------------------------- 1 | package io.github.mymonstercat.ocr; 2 | 3 | import com.benjaminwan.ocrlibrary.OcrEngine; 4 | import com.benjaminwan.ocrlibrary.OcrInput; 5 | import com.benjaminwan.ocrlibrary.OcrResult; 6 | import io.github.mymonstercat.Model; 7 | import io.github.mymonstercat.exception.LoadException; 8 | import io.github.mymonstercat.loader.LibraryLoader; 9 | import io.github.mymonstercat.loader.ModelsLoader; 10 | import io.github.mymonstercat.ocr.config.HardwareConfig; 11 | import io.github.mymonstercat.ocr.config.ParamConfig; 12 | import lombok.SneakyThrows; 13 | import lombok.extern.slf4j.Slf4j; 14 | 15 | import java.util.concurrent.atomic.AtomicBoolean; 16 | 17 | /** 18 | * Inference framework engine. 19 | */ 20 | @Slf4j 21 | public class InferenceEngine extends OcrEngine { 22 | 23 | private Model model = Model.ONNX_PPOCR_V3; 24 | private HardwareConfig hardwareConfig = HardwareConfig.getOnnxConfig(); 25 | private static InferenceEngine inferenceEngine; 26 | private static volatile LibraryLoader nativeLoader; 27 | private static volatile ModelsLoader modelsLoader; 28 | private static final AtomicBoolean isLibraryLoaded = new AtomicBoolean(false); 29 | 30 | private InferenceEngine() { 31 | } 32 | 33 | @SneakyThrows 34 | private InferenceEngine(Model model, HardwareConfig hardwareConfig) { 35 | this.model = model; 36 | this.hardwareConfig = hardwareConfig; 37 | loadFileIfNeeded(model); 38 | initEngine(model, hardwareConfig); 39 | } 40 | 41 | public static InferenceEngine getInstance(Model model) { 42 | return getInstance(model, HardwareConfig.getOnnxConfig()); 43 | } 44 | 45 | public static InferenceEngine getInstance(Model model, HardwareConfig hardwareConfig) { 46 | if (inferenceEngine == null) { 47 | inferenceEngine = new InferenceEngine(model, hardwareConfig); 48 | } 49 | return inferenceEngine; 50 | } 51 | 52 | public Model getModel() { 53 | return model; 54 | } 55 | 56 | public HardwareConfig getHardwareConfig() { 57 | return hardwareConfig; 58 | } 59 | 60 | public OcrResult runOcr(String imagePath) { 61 | return runOcr(imagePath, ParamConfig.getDefaultConfig()); 62 | } 63 | 64 | public OcrResult runOcr(String imagePath, ParamConfig config) { 65 | log.info("Image path: {}, Parameter configuration: {}", imagePath, config); 66 | OcrResult result = detect(imagePath, config.getPadding(), config.getMaxSideLen(), config.getBoxScoreThresh(), config.getBoxThresh(), config.getUnClipRatio(), config.isDoAngle(), config.isMostAngle()); 67 | String property = System.getProperty("rapid.ocr.print.result"); 68 | if ("true".equals(property)) { 69 | log.info("Recognition result: {}, Time taken: {}ms", result.getStrRes().replace("\n", ""), result.getDetectTime()); 70 | } 71 | log.debug("Text blocks: {}, DbNet Time taken: {}ms", result.getTextBlocks(), result.getDbNetTime()); 72 | return result; 73 | } 74 | 75 | public OcrResult runOcr(OcrInput input, ParamConfig config) { 76 | log.info("Image path: {}, Parameter configuration: {}", input.getData().length, config); 77 | OcrResult result = detectInput(input, config.getPadding(), config.getMaxSideLen(), config.getBoxScoreThresh(), config.getBoxThresh(), config.getUnClipRatio(), config.isDoAngle(), config.isMostAngle()); 78 | String property = System.getProperty("rapid.ocr.print.result"); 79 | if ("true".equals(property)) { 80 | log.info("Recognition result: {}, Time taken: {}ms", result.getStrRes().replace("\n", ""), result.getDetectTime()); 81 | } 82 | log.debug("Text blocks: {}, DbNet Time taken: {}ms", result.getTextBlocks(), result.getDbNetTime()); 83 | return result; 84 | } 85 | 86 | @SneakyThrows 87 | public static void loadFileIfNeeded(Model model) { 88 | String modelType = model.getModelType(); 89 | if (InferenceEngine.nativeLoader == null && (isLibraryLoaded.compareAndSet(false, true))) { 90 | synchronized (InferenceEngine.class) { 91 | if (InferenceEngine.nativeLoader == null) { 92 | LibraryLoader nativeLoader = LoadUtil.findLibLoader(modelType); 93 | if (nativeLoader == null) { 94 | throw new LoadException("Unable to find a suitable native loader implementation. Possible reasons include: " + 95 | "1. The Maven coordinates for " + modelType + " are not included! " + 96 | "2. The runtime library might not yet be adapted for your system: " + System.getProperty("os.name").toLowerCase() + System.getProperty("os.arch").toLowerCase() + "! " + 97 | "3. The model used does not match the JAR dependency included, currently used model: " + modelType + ", please check your JAR dependencies are correct! " + 98 | "4. Incorrect inclusion of the runtime library during packaging, such as packaging Windows dependencies but running on Linux, please refer to the documentation to check if the correct packaging command was used!"); 99 | } 100 | nativeLoader.loadLibrary(); 101 | isLibraryLoaded.set(true); 102 | InferenceEngine.nativeLoader = nativeLoader; 103 | } 104 | } 105 | } 106 | log.debug("Current library loader: {}", nativeLoader.getClass().getSimpleName()); 107 | if (InferenceEngine.modelsLoader == null) { 108 | synchronized (InferenceEngine.class) { 109 | if (InferenceEngine.modelsLoader == null) { 110 | ModelsLoader modelsLoader = LoadUtil.findModelsLoader(modelType); 111 | if (modelsLoader == null) { 112 | throw new LoadException("Failed to load models successfully!"); 113 | } 114 | modelsLoader.loadModels(model); 115 | InferenceEngine.modelsLoader = modelsLoader; 116 | } 117 | } 118 | } 119 | log.debug("Current model loader: {}", modelsLoader.getClass().getSimpleName()); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/io/github/mymonstercat/ocr/LoadUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.mymonstercat.ocr; 2 | 3 | import io.github.mymonstercat.loader.LibraryLoader; 4 | import io.github.mymonstercat.loader.ModelsLoader; 5 | import lombok.extern.slf4j.Slf4j; 6 | 7 | import java.io.StringReader; 8 | import java.util.Properties; 9 | 10 | /** 11 | * 库文件加载工具类 12 | */ 13 | @Slf4j 14 | public class LoadUtil { 15 | private LoadUtil() { 16 | throw new IllegalStateException("Utility class"); 17 | } 18 | 19 | private static final String loaders = "# ONNX\n" + 20 | "onnx.mac-arm64=io.github.mymonstercat.OnnxMacArm64LibraryLoader\n" + 21 | "onnx.mac-x86_64=io.github.mymonstercat.OnnxMacX8664LibraryLoader\n" + 22 | "onnx.win-x86=io.github.mymonstercat.OnnxWindowsX86LibraryLoader\n" + 23 | "onnx.win-x86_64=io.github.mymonstercat.OnnxWindowsX8664LibraryLoader\n" + 24 | "onnx.linux-x86_64=io.github.mymonstercat.OnnxLinuxX8664LibraryLoader\n" + 25 | "onnx.linux-arm64=io.github.mymonstercat.OnnxLinuxArm64LibraryLoader\n" + 26 | "onnx.model=io.github.mymonstercat.OnnxModelsLoader\n" + 27 | "# NCNN\n" + 28 | "ncnn.mac-arm64=io.github.mymonstercat.NcnnMacArm64LibraryLoader\n" + 29 | "ncnn.mac-x86_64=io.github.mymonstercat.NcnnMacX8664LibraryLoader\n" + 30 | "ncnn.win-x86_64=io.github.mymonstercat.NcnnWindowsX8664LibraryLoader\n" + 31 | "ncnn.linux-x86_64=io.github.mymonstercat.NcnnLinuxX8664LibraryLoader\n" + 32 | "ncnn.model=io.github.mymonstercat.NcnnModelsLoader\n"; 33 | 34 | static LibraryLoader findLibLoader(String engine) { 35 | Properties props = new Properties(); 36 | try { 37 | props.load(new StringReader(loaders)); 38 | 39 | String osName = System.getProperty("os.name").toLowerCase(); 40 | String osArch = System.getProperty("os.arch").toLowerCase(); 41 | log.debug("osName: {}, osArch: {}", osName, osArch); 42 | String loaderClassName = null; 43 | if (osName.contains("win")) { 44 | if (osArch.contains("amd64")) { 45 | loaderClassName = props.getProperty(engine + ".win-x86_64"); 46 | } else if (osArch.contains("x86")) { 47 | loaderClassName = props.getProperty(engine + ".win-x86"); 48 | } 49 | } else if (osName.contains("mac")) { 50 | if (osArch.contains("arch64")) { 51 | loaderClassName = props.getProperty(engine + ".mac-arm64"); 52 | } else { 53 | loaderClassName = props.getProperty(engine + ".mac-x86_64"); 54 | } 55 | } else if (osName.contains("linux")) { 56 | if (osArch.contains("x86") || osArch.contains("amd64")) { 57 | loaderClassName = props.getProperty(engine + ".linux-x86_64"); 58 | } else if (osArch.contains("arm") || osArch.contains("arch64")) { 59 | loaderClassName = props.getProperty(engine + ".linux-arm64"); //Note: only support onnx, not support ncnn now 60 | } 61 | } 62 | if (loaderClassName != null) { 63 | return (LibraryLoader) Class.forName(loaderClassName).getDeclaredConstructor().newInstance(); 64 | } 65 | } catch (Exception e) { 66 | log.error("获取库文件加载器 {} 失败", e.getMessage()); 67 | } 68 | return null; 69 | } 70 | 71 | public static ModelsLoader findModelsLoader(String engine) { 72 | Properties props = new Properties(); 73 | try { 74 | props.load(new StringReader(loaders)); 75 | 76 | return (ModelsLoader) Class.forName(props.getProperty(engine + ".model")).getDeclaredConstructor().newInstance(); 77 | } catch (Exception e) { 78 | log.error("获取模型文件加载器 {} 失败", e.getMessage()); 79 | } 80 | return null; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/io/github/mymonstercat/ocr/config/HardwareConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.mymonstercat.ocr.config; 2 | 3 | import lombok.*; 4 | 5 | /** 6 | * OCR引擎启动硬件配置配置 7 | */ 8 | @Getter 9 | @Setter 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @ToString 13 | public class HardwareConfig implements IOcrConfig { 14 | 15 | 16 | /** 17 | * CPU 核心数量,默认 1 18 | */ 19 | private int numThread = 1; 20 | 21 | /** 22 | * GPU0一般为默认GPU,参数选项:使用CPU(-1)/使用GPU0(0)/使用GPU1(1)/... 23 | * 重要:ONNX不使用GPU,目前都是使用CPU 24 | */ 25 | private int gpuIndex = 0; 26 | 27 | public static HardwareConfig getNcnnConfig() { 28 | return new HardwareConfig(Runtime.getRuntime().availableProcessors() / 2, 0); 29 | } 30 | 31 | public static HardwareConfig getOnnxConfig() { 32 | return new HardwareConfig(Runtime.getRuntime().availableProcessors() / 2, -1); 33 | } 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/github/mymonstercat/ocr/config/IOcrConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.mymonstercat.ocr.config; 2 | 3 | /** 4 | * OCR配置类 5 | */ 6 | public interface IOcrConfig { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/io/github/mymonstercat/ocr/config/ParamConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.mymonstercat.ocr.config; 2 | 3 | import lombok.*; 4 | 5 | /** 6 | * 可调节参数配置 7 | */ 8 | @Getter 9 | @Setter 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @ToString 13 | public class ParamConfig implements IOcrConfig { 14 | 15 | /** 16 | * 图像外接白框,用于提升识别率,文字框没有正确框住所有文字时,增加此值。默认50。 17 | */ 18 | private int padding = 50; 19 | /** 20 | * 按图像长边进行总体缩放,放大增加识别耗时但精度更高,缩小减小耗时但精度降低,maxSideLen为0表示不缩放 21 | */ 22 | private int maxSideLen = 0; 23 | /** 24 | * 文字框置信度门限,文字框没有正确框住所有文字时,减小此值 25 | */ 26 | private float boxScoreThresh = 0.5f; 27 | /** 28 | * 同上,自行试验 29 | */ 30 | private float boxThresh = 0.3f; 31 | /** 32 | * 单个文字框大小倍率,越大时单个文字框越大 33 | */ 34 | private float unClipRatio = 1.6f; 35 | /** 36 | * 启用(true)/禁用(false) 文字方向检测,只有图片倒置的情况下(旋转90~270度的图片),才需要启用文字方向检测,默认关闭 37 | */ 38 | private boolean doAngle = false; 39 | /** 40 | * 启用(true)/禁用(false) 角度投票(整张图片以最大可能文字方向来识别),当禁用文字方向检测时,此项也不起作用,默认关闭 41 | */ 42 | private boolean mostAngle = false; 43 | 44 | public static ParamConfig getDefaultConfig() { 45 | return new ParamConfig(); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/ltd/guimc/dlm/DLModel.java: -------------------------------------------------------------------------------- 1 | package ltd.guimc.dlm; 2 | 3 | import ai.djl.MalformedModelException; 4 | import ai.djl.Model; 5 | import ai.djl.engine.Engine; 6 | import ai.djl.inference.Predictor; 7 | import ai.djl.modality.Classifications; 8 | import ai.djl.modality.cv.Image; 9 | import ai.djl.modality.cv.ImageFactory; 10 | import ai.djl.modality.cv.translator.ImageClassificationTranslator; 11 | import ai.djl.onnxruntime.engine.OrtEngineProvider; 12 | import ai.djl.translate.TranslateException; 13 | import ai.djl.translate.Translator; 14 | 15 | import java.io.ByteArrayInputStream; 16 | import java.io.File; 17 | import java.io.IOException; 18 | import java.io.InputStream; 19 | import java.nio.file.Files; 20 | import java.nio.file.StandardCopyOption; 21 | 22 | public class DLModel { 23 | private static Model model; 24 | private static boolean inited = false; 25 | 26 | public static void init() { 27 | Engine.registerEngine(new OrtEngineProvider()); 28 | System.out.println("Available engines: " + Engine.getAllEngines()); 29 | try { 30 | // 从资源文件中获取模型 31 | InputStream modelStream = DLModel.class.getResourceAsStream("/Nailong.onnx"); 32 | 33 | if (modelStream == null) { 34 | throw new IOException("无法找到Nailong.onnx模型文件"); 35 | } 36 | 37 | // 创建临时文件来存储ONNX模型 38 | File tempFile = File.createTempFile("Nailong", ".onnx"); 39 | tempFile.deleteOnExit(); 40 | 41 | // 将模型文件从JAR中复制到临时文件 42 | Files.copy(modelStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); 43 | 44 | // 使用 DJL (Deep Java Library) 加载 ONNX 模型 45 | model = Model.newInstance("Nailong", "OnnxRuntime"); 46 | model.load(tempFile.toPath()); 47 | 48 | // 初始化模型 49 | inited = true; 50 | } catch (IOException e) { 51 | e.printStackTrace(); 52 | } catch (MalformedModelException e) { 53 | throw new RuntimeException(e); 54 | } 55 | } 56 | 57 | public static boolean checkImage(byte[] imageData) { 58 | if (!inited) { 59 | return false; 60 | } 61 | 62 | try { 63 | // 将字节数组转换为 DJL 的 Image 对象 64 | ByteArrayInputStream bais = new ByteArrayInputStream(imageData); 65 | Image djlImage = ImageFactory.getInstance().fromInputStream(bais); 66 | 67 | Translator translator = ImageClassificationTranslator.builder().build(); 68 | 69 | // 使用模型进行推理 70 | try (Predictor predictor = model.newPredictor(translator)) { 71 | Classifications classifications = predictor.predict(djlImage); 72 | 73 | // 获取预测的结果类 74 | String predictedClass = classifications.best().getClassName(); 75 | 76 | // 如果预测类为10,返回True,否则返回False 77 | return "10".equals(predictedClass); 78 | } 79 | } catch (IOException | TranslateException e) { 80 | e.printStackTrace(); 81 | return false; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/command/ACGCommand.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.command 11 | 12 | import kotlinx.coroutines.launch 13 | import ltd.guimc.lgzbot.PluginMain 14 | import ltd.guimc.lgzbot.utils.CooldownUtils 15 | import ltd.guimc.lgzbot.utils.ImageUtils 16 | import ltd.guimc.lgzbot.utils.OverflowUtils 17 | import net.mamoe.mirai.console.command.CommandSender 18 | import net.mamoe.mirai.console.command.SimpleCommand 19 | import net.mamoe.mirai.console.command.getGroupOrNull 20 | import top.mrxiaom.overflow.OverflowAPI 21 | 22 | object ACGCommand: SimpleCommand ( 23 | owner = PluginMain, 24 | primaryName = "acg", 25 | description = "二次元图片" 26 | ) { 27 | val cooldown = CooldownUtils(10000) 28 | 29 | @Handler 30 | fun CommandSender.onHandler() = ltd_guimc_command_acg() 31 | 32 | fun CommandSender.ltd_guimc_command_acg() = launch { 33 | requireNotNull(user) { "请在聊天环境中使用该指令" } 34 | val group = getGroupOrNull() 35 | if (!cooldown.isTimePassed(user!!)) { 36 | if (cooldown.shouldSendCooldownNotice(user!!)) sendMessage("你可以在 ${ACGCommand.cooldown.getLeftTime(user!!) / 1000} 秒后继续使用该指令") 37 | return@launch 38 | } 39 | cooldown.flag(user!!) 40 | try { 41 | if (!OverflowUtils.checkOverflowCore()) { 42 | sendMessage(ImageUtils.url2imageMessage("https://www.dmoe.cc/random.php", bot!!, subject!!)) 43 | } else { 44 | sendMessage(OverflowAPI.get().imageFromFile("https://www.dmoe.cc/random.php")) 45 | } 46 | } catch (ignore: Throwable) { 47 | sendMessage("Oops, something went wrong.") 48 | cooldown.addLeftTime(user!!, -10000L) 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/command/FbCommand.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.command 11 | 12 | import kotlinx.coroutines.launch 13 | import ltd.guimc.lgzbot.PluginMain 14 | import ltd.guimc.lgzbot.PluginMain.fbValue 15 | import ltd.guimc.lgzbot.utils.CooldownUtils 16 | import net.mamoe.mirai.console.command.CommandSender 17 | import net.mamoe.mirai.console.command.SimpleCommand 18 | import net.mamoe.mirai.message.data.Message 19 | import net.mamoe.mirai.message.data.PlainText 20 | 21 | object FbCommand: SimpleCommand( 22 | owner = PluginMain, 23 | primaryName = "fb", 24 | description = "嘿嘿...❤" 25 | ) { 26 | val cooldown = CooldownUtils(30000) 27 | 28 | @Handler 29 | fun CommandSender.onHandler() = ltd_guimc_lgzbot_fb() 30 | 31 | fun CommandSender.ltd_guimc_lgzbot_fb() = launch { 32 | requireNotNull(user) { "请在聊天环境中使用该指令" } 33 | if (!cooldown.isTimePassed(user!!)) { 34 | if (cooldown.shouldSendCooldownNotice(user!!)) sendMessage("你可以在 ${cooldown.getLeftTime(user!!) / 1000} 秒后继续使用该指令") 35 | return@launch 36 | } 37 | cooldown.flag(user!!) 38 | 39 | var msg: Message = PlainText("") 40 | 41 | val context = fbValue.random().replace("\\n", "\n").replace("$", user!!.remark) 42 | 43 | sendMessage(msg) 44 | } 45 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/command/GithubWebhookSubCommand.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.command 2 | 3 | import ltd.guimc.lgzbot.PluginMain 4 | import ltd.guimc.lgzbot.files.GithubWebhookSubData 5 | import net.mamoe.mirai.console.command.CommandSender 6 | import net.mamoe.mirai.console.command.CompositeCommand 7 | import net.mamoe.mirai.contact.Group 8 | 9 | object GithubWebhookSubCommand: CompositeCommand( 10 | owner = PluginMain, 11 | primaryName = "ghsub", 12 | description = "Github Webhook Sub" 13 | ) { 14 | @SubCommand("add") 15 | @Description("Add a new sub") 16 | suspend fun CommandSender.iI1I1I1i1I1I1iI1i1I1Ii1I1i1I1I(repo: String) { 17 | requireNotNull(bot) 18 | require(subject is Group) 19 | if (GithubWebhookSubData.sub[bot!!.id] == null) 20 | GithubWebhookSubData.sub[bot!!.id] = mutableMapOf() 21 | 22 | if (GithubWebhookSubData.sub[bot!!.id]!![repo] == null) 23 | GithubWebhookSubData.sub[bot!!.id]!![repo] = mutableListOf() 24 | 25 | if (GithubWebhookSubData.sub[bot!!.id]!![repo]!!.indexOf(subject!!.id) != -1) { 26 | sendMessage("You have already added this repo!") 27 | return 28 | } 29 | 30 | GithubWebhookSubData.sub[bot!!.id]!![repo]!!.add(subject!!.id) 31 | sendMessage("OK") 32 | } 33 | 34 | @SubCommand("remove") 35 | @Description("Remove a repo from sub") 36 | suspend fun CommandSender.iI1I1I1I1i1I1Ii1I1i1I1I1I1Ii1I1(repo: String) { 37 | requireNotNull(bot) 38 | require(subject is Group) 39 | if (GithubWebhookSubData.sub[bot!!.id] == null) 40 | GithubWebhookSubData.sub[bot!!.id] = mutableMapOf() 41 | 42 | if (GithubWebhookSubData.sub[bot!!.id]!![repo] == null) 43 | GithubWebhookSubData.sub[bot!!.id]!![repo] = mutableListOf() 44 | 45 | if (GithubWebhookSubData.sub[bot!!.id]!![repo]!!.indexOf(subject!!.id) == -1) { 46 | sendMessage("You have already removed this repo!") 47 | return 48 | } 49 | 50 | GithubWebhookSubData.sub[bot!!.id]!![repo]!!.remove(subject!!.id) 51 | sendMessage("OK") 52 | } 53 | 54 | @SubCommand("ignore") 55 | @Description("Add/Remove a committer") 56 | suspend fun CommandSender.i1I1I1I1Ii1I1II1I1i1I1i1I1I1(sub: String, repo: String, target: String) { 57 | requireNotNull(bot) 58 | require(subject is Group) 59 | 60 | if (GithubWebhookSubData.ignore[bot!!.id] == null) 61 | GithubWebhookSubData.ignore[bot!!.id] = mutableMapOf() 62 | 63 | if (GithubWebhookSubData.ignore[bot!!.id]!![repo] == null) 64 | GithubWebhookSubData.ignore[bot!!.id]!![repo] = mutableListOf() 65 | 66 | if (sub == "add") { 67 | if (GithubWebhookSubData.ignore[bot!!.id]!![repo]!!.indexOf(target) != -1) { 68 | sendMessage("You have already added this repo!") 69 | return 70 | } 71 | 72 | GithubWebhookSubData.ignore[bot!!.id]!![repo]!!.add(target) 73 | sendMessage("OK") 74 | } else if (sub == "remove") { 75 | if (GithubWebhookSubData.ignore[bot!!.id]!![repo]!!.indexOf(target) == -1) { 76 | sendMessage("You have already removed this repo!") 77 | return 78 | } 79 | 80 | GithubWebhookSubData.ignore[bot!!.id]!![repo]!!.remove(target) 81 | sendMessage("OK") 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/command/HomoIntCommand.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.command 11 | 12 | import kotlinx.coroutines.launch 13 | import ltd.guimc.lgzbot.PluginMain 14 | import ltd.guimc.lgzbot.utils.HomoIntUtils 15 | import net.mamoe.mirai.console.command.CommandSender 16 | import net.mamoe.mirai.console.command.SimpleCommand 17 | 18 | object HomoIntCommand : SimpleCommand( 19 | owner = PluginMain, 20 | primaryName = "homoint", 21 | secondaryNames = arrayOf("homo"), 22 | description = "随处可见的Homo(恼" 23 | ) { 24 | @Handler 25 | fun CommandSender.onHandler(num: Long) = ltd_guimc_lgzbot_homoint(num) 26 | 27 | fun CommandSender.ltd_guimc_lgzbot_homoint(num: Long) = launch { 28 | sendMessage(HomoIntUtils.getInt(num)) 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/command/HttpCatCommand.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.command 11 | 12 | import kotlinx.coroutines.launch 13 | import ltd.guimc.lgzbot.PluginMain 14 | import ltd.guimc.lgzbot.utils.CooldownUtils 15 | import ltd.guimc.lgzbot.utils.ImageUtils 16 | import ltd.guimc.lgzbot.utils.OverflowUtils 17 | import net.mamoe.mirai.console.command.CommandSender 18 | import net.mamoe.mirai.console.command.SimpleCommand 19 | import net.mamoe.mirai.console.command.getGroupOrNull 20 | import top.mrxiaom.overflow.OverflowAPI 21 | 22 | object HttpCatCommand: SimpleCommand ( 23 | owner = PluginMain, 24 | primaryName = "httpcat", 25 | description = "Funny Cat" 26 | ) { 27 | val cooldown = CooldownUtils(10000) 28 | 29 | @Handler 30 | fun CommandSender.onHandler(code: Int) = ltd_guimc_command_httpcat(code) 31 | 32 | fun CommandSender.ltd_guimc_command_httpcat(code: Int) = launch{ 33 | requireNotNull(user) { "请在聊天环境中使用该指令" } 34 | val group = getGroupOrNull() 35 | if (!cooldown.isTimePassed(user!!)) { 36 | if (cooldown.shouldSendCooldownNotice(user!!)) sendMessage("你可以在 ${cooldown.getLeftTime(user!!) / 1000} 秒后继续使用该指令") 37 | return@launch 38 | } 39 | cooldown.flag(user!!) 40 | try { 41 | if (!OverflowUtils.checkOverflowCore()) { 42 | sendMessage(ImageUtils.url2imageMessage("https://http.cat/$code", bot!!, subject!!)) 43 | } else { 44 | sendMessage(OverflowAPI.get().imageFromFile("https://http.cat/$code")) 45 | } 46 | } catch (ignore: Throwable) { 47 | sendMessage("Oops, something went wrong.") 48 | cooldown.addLeftTime(user!!, -10000L) 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/command/LGZBotCommand.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.command 11 | 12 | import ltd.guimc.lgzbot.PluginMain 13 | import ltd.guimc.lgzbot.files.ModuleStateConfig 14 | import ltd.guimc.lgzbot.listener.message.MessageFilter 15 | import ltd.guimc.lgzbot.utils.LL4JUtils 16 | import ltd.guimc.lgzbot.utils.MessageUtils.getPlainText 17 | import ltd.guimc.lgzbot.utils.OverflowUtils 18 | import net.mamoe.mirai.Bot 19 | import net.mamoe.mirai.console.command.* 20 | import net.mamoe.mirai.contact.Member 21 | import net.mamoe.mirai.contact.NormalMember 22 | import net.mamoe.mirai.message.data.* 23 | import org.apache.commons.lang3.RandomUtils 24 | import top.mrxiaom.overflow.contact.Updatable 25 | import kotlin.math.roundToInt 26 | import kotlin.time.Duration 27 | 28 | object LGZBotCommand : CompositeCommand( 29 | owner = PluginMain, primaryName = "lgzbot", description = "LGZBot插件的主命令" 30 | ) { 31 | @SubCommand("ping") 32 | @Description("看看机器人是否在线吧") 33 | suspend fun CommandSender.ping() { 34 | sendMessage("Pong!") 35 | } 36 | 37 | @SubCommand("mute") 38 | @Description("把某人的嘴巴用胶布粘上") 39 | suspend fun CommandSender.mute(user: Member, time: String, reason: String) { 40 | try { 41 | val second: Long = Duration.parse(time).inWholeSeconds 42 | 43 | user.mute(second.toInt()) 44 | if (ModuleStateConfig.slientmute) return 45 | user.group.sendMessage( 46 | PlainText("[滥权小助手] ") + At(user) + PlainText(" 获得了来自 ${if (isConsole()) "CONSOLE" else name} 的禁言\n") + PlainText( 47 | "时长: ${(second / 60.0 * 100.0).roundToInt().toDouble() / 100.0} 分钟\n" 48 | ) + PlainText("理由: $reason") 49 | ) 50 | } catch (e: Exception) { 51 | sendMessage("Oops! Something went wrong! ${e.message}") 52 | } 53 | } 54 | 55 | @SubCommand("unmute") 56 | @Description("把胶布从某人的嘴巴上撕下来") 57 | suspend fun CommandSender.unmute(user: Member) { 58 | try { 59 | (user as NormalMember).unmute() 60 | if (ModuleStateConfig.slientmute) return 61 | user.group.sendMessage( 62 | PlainText("[滥权小助手] ") + At(user) + PlainText(" 获得了来自 ${if (isConsole()) "CONSOLE" else name} 的解除禁言") 63 | ) 64 | } catch (e: Exception) { 65 | sendMessage("Oops! Something went wrong! ${e.message}") 66 | } 67 | } 68 | 69 | @SubCommand("clear") 70 | @Description("清除某人的状态") 71 | suspend fun CommandSender.I1Ii1I1(member: Member) { 72 | try { 73 | MessageFilter.riskList.remove(member) 74 | MessageFilter.clearVl(member.id) 75 | sendMessage( 76 | PlainText("清除了") + At(member) + PlainText("的VL, 并移出在本群的风险管控") 77 | ) 78 | } catch (e: Exception) { 79 | sendMessage("Oops! 在尝试执行操作的时候发生了一些错误!") 80 | e.printStackTrace() 81 | } 82 | } 83 | 84 | @SubCommand("debug") 85 | @Description("获取Debug信息") 86 | suspend fun CommandSender.i1I1i1II1i1I1i() { 87 | val messageChain = MessageChainBuilder() 88 | messageChain.add("c=${MessageFilter.allCheckedMessage}, d=${MessageFilter.recalledMessage}, r=${(MessageFilter.recalledMessage / (if (MessageFilter.allCheckedMessage == 0) 1 else MessageFilter.allCheckedMessage * 10000)).toDouble() / 100.0}\n") 89 | messageChain.add("o=${if (OverflowUtils.checkOverflowCore()) "true" else "false"}") 90 | if (OverflowUtils.checkOverflowCore()) messageChain.add( 91 | ", on=${OverflowUtils.getOnebotServiceProviderName(bot ?: Bot.instances[0])}, ov=${ 92 | OverflowUtils.getOnebotServiceProviderVersion( 93 | bot ?: Bot.instances[0] 94 | ) 95 | }, oc=${OverflowUtils.getOnebotConnection()}" 96 | ) 97 | sendMessage(messageChain.build()) 98 | } 99 | 100 | @SubCommand("update") 101 | @Description("清理本群群成员缓存") 102 | suspend fun CommandSender.iI1Ii1I1i1I1i1I1() { 103 | if (!OverflowUtils.checkOverflowCore()) { 104 | sendMessage("该指令仅Overflow adaption可用") 105 | return 106 | } 107 | requireNotNull(getGroupOrNull()) 108 | (getGroupOrNull()!! as Updatable).queryUpdate() 109 | sendMessage("ok.") 110 | } 111 | 112 | @SubCommand("check") 113 | @Description("使用模型检测一段文本是否合规") 114 | suspend fun CommandSenderOnMessage<*>.iI1I1i1I1i1I(string: String) { 115 | var str = string 116 | if (string == "reply") { 117 | val quote = fromEvent.message.findIsInstance() ?: return 118 | val raw = quote.source.originalMessage 119 | str = raw.getPlainText() 120 | } 121 | if (LL4JUtils.predictAllResult(str).let { it[1] > it[0] }) { 122 | sendMessage("不合规") 123 | } else { 124 | sendMessage("合规") 125 | } 126 | } 127 | 128 | @SubCommand("learn") 129 | @Description("让模型学习一段文本") 130 | suspend fun CommandSender.iI1I1i1iIi1I(type: Int, string: String) { 131 | LL4JUtils.learn(type, string) 132 | sendMessage("Deprecated") 133 | } 134 | 135 | @SubCommand("downloadModel") 136 | @Description("从ADDetector仓库下载模型") 137 | suspend fun CommandSender.llIIllIIllI() { 138 | sendMessage("Downloading (current: ${LL4JUtils.version})") 139 | LL4JUtils.downloadModel() 140 | } 141 | 142 | @SubCommand("broadcast") 143 | @Description("广播") 144 | suspend fun CommandSenderOnMessage<*>.iI1I1i1iIi1i(content: String) { 145 | for (groups in (getBotOrNull() ?: return).groups) { 146 | if (groups.botPermission.level >= 1) { 147 | try { 148 | groups.sendMessage(content) 149 | } catch (_: Exception) { 150 | } 151 | Thread.sleep(RandomUtils.nextLong(1500, 2000)) 152 | } 153 | } 154 | sendMessage("完成.") 155 | } 156 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/command/ReviewCommand.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.command 2 | 3 | import kotlinx.coroutines.launch 4 | import ltd.guimc.lgzbot.PluginMain 5 | import ltd.guimc.lgzbot.PluginMain.logger 6 | import ltd.guimc.lgzbot.files.ModuleStateConfig 7 | import ltd.guimc.lgzbot.utils.MessageUtils.getPlainText 8 | import ltd.guimc.lgzbot.utils.RequestUtils 9 | import net.mamoe.mirai.console.command.CommandSender 10 | import net.mamoe.mirai.console.command.SimpleCommand 11 | import net.mamoe.mirai.event.GlobalEventChannel 12 | import net.mamoe.mirai.event.ListeningStatus 13 | import net.mamoe.mirai.event.events.BotEvent 14 | import net.mamoe.mirai.event.events.MessageEvent 15 | 16 | /*@Deprecated("This command is deprecated and will be removed in a future release.", level = DeprecationLevel.HIDDEN)*/ 17 | object ReviewCommand : SimpleCommand( 18 | owner = PluginMain, 19 | primaryName = "review", 20 | description = "处理Event ID用" 21 | ) { 22 | @Handler 23 | fun CommandSender.iiIiIIiII1i1I1i1I1i1II1i1I1i1I1(id: Long) = launch { 24 | try { 25 | if (bot == null) { 26 | throw IllegalAccessError("请勿在控制台运行") 27 | } 28 | 29 | if (ModuleStateConfig.invite) { 30 | sendMessage("该功能已禁用") 31 | } 32 | 33 | val realSource = user!!.id 34 | val realSubject = subject!! 35 | 36 | val event = RequestUtils.Group.find(id) 37 | sendMessage( 38 | "找到事件!\n" + 39 | "发起人: ${event.invitorId}\n" + 40 | "群聊: ${event.groupId}\n" + 41 | "使用 同意/拒绝/取消" 42 | ) 43 | GlobalEventChannel.filter { it is BotEvent && it.bot.id == bot!!.id } 44 | .subscribe { 45 | if (it.subject == realSubject && it.sender.id == realSource) { 46 | return@subscribe when (it.message.getPlainText()) { 47 | "同意" -> { 48 | event.accept() 49 | RequestUtils.Group.remove(id) 50 | sendMessage("已同意") 51 | ListeningStatus.STOPPED 52 | } 53 | 54 | "拒绝" -> { 55 | event.ignore() 56 | RequestUtils.Group.remove(id) 57 | sendMessage("已拒绝") 58 | ListeningStatus.STOPPED 59 | } 60 | 61 | "取消" -> { 62 | sendMessage("已取消") 63 | ListeningStatus.STOPPED 64 | } 65 | 66 | else -> { 67 | sendMessage("请发送 同意/拒绝/取消") 68 | ListeningStatus.LISTENING 69 | } 70 | } 71 | } else { 72 | return@subscribe ListeningStatus.LISTENING 73 | } 74 | } 75 | } catch (e: Throwable) { 76 | sendMessage("遇到问题: " + e.message) 77 | logger.warning(e) 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/command/ToggleCheckCommand.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.command 2 | 3 | import ltd.guimc.lgzbot.PluginMain 4 | import net.mamoe.mirai.console.command.CommandSender 5 | import net.mamoe.mirai.console.command.CompositeCommand 6 | import net.mamoe.mirai.console.command.getGroupOrNull 7 | import net.mamoe.mirai.console.permission.PermissionService.Companion.cancel 8 | import net.mamoe.mirai.console.permission.PermissionService.Companion.hasPermission 9 | import net.mamoe.mirai.console.permission.PermissionService.Companion.permit 10 | import net.mamoe.mirai.console.permission.PermitteeId.Companion.permitteeId 11 | import net.mamoe.mirai.contact.getMemberOrFail 12 | import net.mamoe.mirai.contact.isOperator 13 | 14 | object ToggleCheckCommand : CompositeCommand( 15 | owner = PluginMain, 16 | primaryName = "togglecheck", 17 | secondaryNames = arrayOf("disablecheck"), 18 | description = "开关检测" 19 | ) { 20 | @SubCommand("spam") 21 | @Description("开/关刷屏检查") 22 | suspend fun CommandSender.iI1I1i11ii1I1i1I1i() { 23 | if (getGroupOrNull() == null) { 24 | sendMessage("请在群聊中执行该命令") 25 | } 26 | 27 | val group = getGroupOrNull()!! 28 | val member = group.getMemberOrFail(user!!.id) 29 | 30 | if (member.permission.isOperator()) { 31 | if (group.permitteeId.hasPermission(PluginMain.disableSpamCheck)) { 32 | group.permitteeId.cancel(PluginMain.disableSpamCheck, false) 33 | sendMessage("已重新开启本群的刷屏检测") 34 | } else { 35 | group.permitteeId.permit(PluginMain.disableSpamCheck) 36 | sendMessage("已关闭本群的刷屏检测") 37 | } 38 | } else { 39 | sendMessage("只有管理才能使用这个指令的说...") 40 | } 41 | } 42 | 43 | @SubCommand("ad") 44 | @Description("开/关广告检测") 45 | suspend fun CommandSender.iiI11i1I1i1II1i1I1ii1I1i1I1() { 46 | if (getGroupOrNull() == null) { 47 | sendMessage("请在群聊中执行该命令") 48 | } 49 | 50 | val group = getGroupOrNull()!! 51 | val member = group.getMemberOrFail(user!!.id) 52 | 53 | if (member.permission.isOperator()) { 54 | if (group.permitteeId.hasPermission(PluginMain.disableADCheck)) { 55 | group.permitteeId.cancel(PluginMain.disableADCheck, false) 56 | sendMessage("已重新开启本群的广告检测") 57 | } else { 58 | group.permitteeId.permit(PluginMain.disableADCheck) 59 | sendMessage("已关闭本群的广告检测") 60 | } 61 | } else { 62 | sendMessage("只有管理才能使用这个指令的说...") 63 | } 64 | } 65 | 66 | @SubCommand("image") 67 | @Description("开/关图片检测") 68 | suspend fun CommandSender.iiI11i1I1i1II1i1I1i11I1i1I1() { 69 | if (getGroupOrNull() == null) { 70 | sendMessage("请在群聊中执行该命令") 71 | } 72 | 73 | val group = getGroupOrNull()!! 74 | val member = group.getMemberOrFail(user!!.id) 75 | 76 | if (member.permission.isOperator()) { 77 | if (group.permitteeId.hasPermission(PluginMain.disableImageCheck)) { 78 | group.permitteeId.cancel(PluginMain.disableImageCheck, false) 79 | sendMessage("已重新开启本群的图片检测") 80 | } else { 81 | group.permitteeId.permit(PluginMain.disableImageCheck) 82 | sendMessage("已关闭本群的图片检测") 83 | } 84 | } else { 85 | sendMessage("只有管理才能使用这个指令的说...") 86 | } 87 | } 88 | 89 | /*@SubCommand("nailong") 90 | @Description("开/关奶龙图片检测") 91 | suspend fun CommandSender.iiI11i1I1i1Iai1I1i11I1i1I1() { 92 | if (getGroupOrNull() == null) { 93 | sendMessage("请在群聊中执行该命令") 94 | } 95 | 96 | val group = getGroupOrNull()!! 97 | val member = group.getMemberOrFail(user!!.id) 98 | 99 | if (member.permission.isOperator()) { 100 | if (!group.permitteeId.hasPermission(PluginMain.checkNailong)) { 101 | group.permitteeId.permit(PluginMain.checkNailong) 102 | sendMessage("已开启本群的奶龙图片检测") 103 | } else { 104 | group.permitteeId.cancel(PluginMain.checkNailong, false) 105 | sendMessage("已关闭本群的奶龙图片检测") 106 | } 107 | } else { 108 | sendMessage("只有管理才能使用这个指令的说...") 109 | } 110 | }*/ 111 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/counter/Counter.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.counter 2 | 3 | import net.mamoe.mirai.contact.Member 4 | 5 | class Counter(private val member: Member) { 6 | var wordFrequency = HashMap() 7 | var messageVl = 0 8 | } 9 | -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/counter/VLManager.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.counter 2 | 3 | import net.mamoe.mirai.contact.Member 4 | 5 | object VLManager { 6 | private val counters = HashMap() 7 | fun getCounter(p: Member): Counter { 8 | var c = counters[p] 9 | if (c == null) { 10 | c = Counter(p) 11 | counters[p] = c 12 | } 13 | return c 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/files/Config.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.files 11 | 12 | import net.mamoe.mirai.console.data.AutoSavePluginConfig 13 | import net.mamoe.mirai.console.data.ReadOnlyPluginConfig 14 | import net.mamoe.mirai.console.data.value 15 | 16 | object Config : ReadOnlyPluginConfig("config") { 17 | val BotOwner by value(0L) 18 | 19 | val vlPunish by value(100.0) 20 | 21 | val historyMessageLimit by value(8) 22 | 23 | val hypixelApiKey by value("00000000-0000-0000-0000-00000000") 24 | 25 | val githubWebhookPort by value(39988) 26 | 27 | val githubWebhookSecret by value("_UVCM1v6HY_i8JkRybLdSEshCVa_KCB2PlZD53BHn0LA-cyqbdXMTo9Sy_DlvjJB4dZ0NJxA3BjEZ37K83mU0Q") 28 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/files/GithubSubConfig.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.files 11 | 12 | import net.mamoe.mirai.console.data.AutoSavePluginConfig 13 | import net.mamoe.mirai.console.data.ReadOnlyPluginConfig 14 | import net.mamoe.mirai.console.data.value 15 | 16 | object GithubSubConfig : ReadOnlyPluginConfig("githubsub") { 17 | var key by value("") 18 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/files/GithubWebhookSubData.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.files 2 | 3 | import net.mamoe.mirai.console.data.AutoSavePluginData 4 | import net.mamoe.mirai.console.data.value 5 | 6 | object GithubWebhookSubData : AutoSavePluginData("githubWebhookSub") { 7 | val sub by value(mutableMapOf>>()) 8 | 9 | val ignore by value(mutableMapOf>>()) 10 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/files/ModuleStateConfig.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.files 2 | 3 | import net.mamoe.mirai.console.data.ReadOnlyPluginConfig 4 | import net.mamoe.mirai.console.data.ValueDescription 5 | import net.mamoe.mirai.console.data.value 6 | 7 | object ModuleStateConfig : ReadOnlyPluginConfig("modulestate") { 8 | @ValueDescription("消息过滤器与刷屏检测") 9 | val messageFilter by value(true) 10 | 11 | @ValueDescription("戳一戳处理") 12 | val nudge by value(true) 13 | 14 | @ValueDescription("机器人被邀请进群处理") 15 | val invite by value(true) 16 | 17 | @ValueDescription("GroupListener (进群/退群/禁言/解除禁言)") 18 | val grouplistener by value(true) 19 | 20 | @ValueDescription("占卜功能") 21 | val fortune by value(true) 22 | 23 | @ValueDescription("历史上的今天") 24 | val historytoday by value(true) 25 | 26 | @ValueDescription("摸鱼人日历") 27 | val moyu by value(true) 28 | 29 | @ValueDescription("GitHub仓库信息自动查询") 30 | val githubquery by value(true) 31 | 32 | @ValueDescription("静默[滥权小助手]") 33 | val slientmute by value(false) 34 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/github/CommitInfo.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.github 11 | 12 | import java.time.LocalDateTime 13 | 14 | // TODO: Add some features 15 | class CommitInfo( 16 | val commitId: String, 17 | val commitMessage: String, 18 | val author: UserInfo, 19 | val commitTime: LocalDateTime, 20 | val verified: Boolean 21 | ) { 22 | override fun equals(other: Any?): Boolean { 23 | if (other !is CommitInfo) return false 24 | return other.commitId == this.commitId 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/github/OwnerInfo.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.github 11 | 12 | // TODO: Add some features 13 | class OwnerInfo(val name: String) -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/github/RepoInfo.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.github 11 | 12 | import ltd.guimc.lgzbot.utils.GithubUtils 13 | import java.time.LocalDateTime 14 | 15 | 16 | // TODO: Add some features 17 | class RepoInfo( 18 | val repo: String, 19 | val author: OwnerInfo, 20 | val descriptor: String, 21 | val createdTime: LocalDateTime, 22 | val updateTime: LocalDateTime, 23 | val lastCommitInfo: CommitInfo, 24 | val language: String, 25 | val defaultBranch: String 26 | ) { 27 | var commit: CommitInfo = lastCommitInfo 28 | override fun equals(other: Any?): Boolean { 29 | if (other !is RepoInfo) return false 30 | return other.repo == this.repo 31 | } 32 | 33 | fun checkUpdate(): Boolean { 34 | val newCommit = GithubUtils.getLastCommit(repo) 35 | return if (newCommit.commitId != commit.commitId) { 36 | commit = newCommit 37 | true 38 | } else { 39 | false 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/github/UserInfo.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.github 11 | 12 | class UserInfo(val name: String, val email: String) -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/listener/message/AntiMappLinkListener.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.listener.message 2 | 3 | import net.mamoe.mirai.event.events.GroupMessageEvent 4 | import net.mamoe.mirai.message.data.ForwardMessage 5 | import net.mamoe.mirai.message.data.MessageChain 6 | import net.mamoe.mirai.message.data.QuoteReply 7 | import net.mamoe.mirai.message.data.RichMessage 8 | 9 | object AntiMappLinkListener { 10 | suspend fun filter(e: GroupMessageEvent) { 11 | if (check(e.message)) e.subject.sendMessage(QuoteReply(e.source) + "WARN: 本消息可能包含mqqapi链接 请注意安全") 12 | } 13 | 14 | fun check(m: MessageChain): Boolean { 15 | for (singleMessage in m) { 16 | if (singleMessage is RichMessage && (singleMessage.content.contains("mqqapi:\\/\\/") || singleMessage.content.contains( 17 | "mqqapi://" 18 | )) 19 | ) { 20 | return true 21 | } 22 | 23 | if (singleMessage is ForwardMessage) { 24 | return checkForwarded(singleMessage) 25 | } 26 | } 27 | return false 28 | } 29 | 30 | fun checkForwarded(m: ForwardMessage): Boolean { 31 | for (singleNode in m.nodeList) { 32 | if (check(singleNode.messageChain)) return true 33 | } 34 | return false 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/listener/message/FunListener.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.listener.message 2 | 3 | import ltd.guimc.lgzbot.files.ModuleStateConfig 4 | import ltd.guimc.lgzbot.utils.HttpUtils 5 | import ltd.guimc.lgzbot.utils.ImageUtils 6 | import ltd.guimc.lgzbot.utils.MessageUtils.getPlainText 7 | import ltd.guimc.lgzbot.utils.OverflowUtils 8 | import net.mamoe.mirai.contact.Contact 9 | import net.mamoe.mirai.event.events.GroupMessageEvent 10 | import net.mamoe.mirai.message.data.At 11 | import net.mamoe.mirai.message.data.ForwardMessage 12 | import net.mamoe.mirai.message.data.ForwardMessageBuilder 13 | import net.mamoe.mirai.message.data.PlainText 14 | import org.json.JSONObject 15 | import org.jsoup.Jsoup 16 | import top.mrxiaom.overflow.OverflowAPI 17 | 18 | object FunListener { 19 | val COMMAND_PREFIX = "!" 20 | 21 | suspend fun onMessage(event: GroupMessageEvent) { 22 | when(event.message.getPlainText()) { 23 | "${COMMAND_PREFIX}摸鱼", "${COMMAND_PREFIX}摸鱼!", "${COMMAND_PREFIX}摸鱼.", 24 | "${COMMAND_PREFIX}摸鱼!", "${COMMAND_PREFIX}摸鱼。" -> { 25 | if (ModuleStateConfig.moyu) { 26 | try { 27 | event.subject.sendMessage( 28 | At(event.sender) + if (!OverflowUtils.checkOverflowCore()) { 29 | ImageUtils.url2imageMessage( 30 | "https://api.j4u.ink/v1/store/redirect/moyu/calendar/today.png", 31 | event.bot, 32 | event.subject 33 | ) 34 | } else { 35 | OverflowAPI.get() 36 | .imageFromFile("https://api.j4u.ink/v1/store/redirect/moyu/calendar/today.png") 37 | } 38 | ) 39 | } catch (e: Throwable) { 40 | event.subject.sendMessage("处理时发生异常... \nStackTrace:") 41 | event.subject.sendMessage(build2forwardMessage(e.stackTraceToString(), event.subject)) 42 | } 43 | } 44 | } 45 | 46 | "${COMMAND_PREFIX}历史上的今天" -> { 47 | if (ModuleStateConfig.historytoday) { 48 | try { 49 | event.subject.sendMessage(getHistoryToday(event.subject)) 50 | event.subject.sendMessage(At(event.sender)) 51 | } catch (e: Throwable) { 52 | event.subject.sendMessage("处理时发生异常... \nStackTrace:") 53 | event.subject.sendMessage(build2forwardMessage(e.stackTraceToString(), event.subject)) 54 | } 55 | } 56 | } 57 | } 58 | } 59 | 60 | private fun getHistoryToday(contact: Contact): ForwardMessage { 61 | val msg = ForwardMessageBuilder(contact) 62 | val jsonObject = HttpUtils.getJsonObject("https://api.oioweb.cn/api/common/history") 63 | jsonObject.getJSONArray("result").forEach { 64 | if (it is JSONObject) { 65 | msg.add( 66 | contact.bot, PlainText( 67 | "${it.getString("year")}: ${it.getString("title").replace("\n", "")}\n" + 68 | "${Jsoup.parse(it.getString("desc")).text()}\n来源: ${it.getString("link")}" 69 | ) 70 | ) 71 | } 72 | } 73 | return msg.build() 74 | } 75 | 76 | private fun build2forwardMessage(string: String, contact: Contact): ForwardMessage { 77 | return ForwardMessageBuilder(contact) 78 | .add(contact.bot, PlainText(string)) 79 | .build() 80 | } 81 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/listener/message/GithubUrlListener.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.listener.message 11 | 12 | import ltd.guimc.lgzbot.PluginMain 13 | import ltd.guimc.lgzbot.files.ModuleStateConfig 14 | import ltd.guimc.lgzbot.utils.GithubUtils 15 | import ltd.guimc.lgzbot.utils.MessageUtils.getPlainText 16 | import ltd.guimc.lgzbot.utils.RegexUtils 17 | import net.mamoe.mirai.console.permission.PermissionService.Companion.hasPermission 18 | import net.mamoe.mirai.console.permission.PermitteeId.Companion.permitteeId 19 | import net.mamoe.mirai.event.events.GroupMessageEvent 20 | import java.time.format.DateTimeFormatter 21 | import java.time.format.FormatStyle 22 | 23 | object GithubUrlListener { 24 | suspend fun onMessage(event: GroupMessageEvent) { 25 | if (event.group.permitteeId.hasPermission(PluginMain.quiet)) return 26 | if (ModuleStateConfig.githubquery) { 27 | val plain = event.message.getPlainText() 28 | val gitLink = GithubUtils.findGitLink(plain) ?: return 29 | // if (plain.indexOf("/gh-sub") != -1) return 30 | 31 | val info = GithubUtils.getGithubRepo(GithubUtils.convert(gitLink)) 32 | val s = """[GitHub] (Preview Version) 33 | |Repo: ${info.repo} 34 | |Descriptor: ${RegexUtils.checkRisk(info.descriptor)} 35 | |Owner: ${info.author.name} 36 | |Default Branch: ${info.defaultBranch} 37 | |Language: ${info.language} 38 | |Last Commit: ${RegexUtils.checkRisk(info.lastCommitInfo.commitMessage)} (${info.lastCommitInfo.commitId}) 39 | |Create Time: ${info.createdTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL))} 40 | |Update Time: ${info.updateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL))}""".trimMargin() 41 | 42 | event.group.sendMessage(s) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/listener/message/NaiLongImageListener.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.listener.message 2 | 3 | import ltd.guimc.dlm.DLModel 4 | import ltd.guimc.lgzbot.PluginMain 5 | import ltd.guimc.lgzbot.utils.HttpUtils 6 | import net.mamoe.mirai.console.permission.PermissionService.Companion.hasPermission 7 | import net.mamoe.mirai.console.permission.PermitteeId.Companion.permitteeId 8 | import net.mamoe.mirai.event.ListenerHost 9 | import net.mamoe.mirai.event.events.GroupMessageEvent 10 | import net.mamoe.mirai.message.data.Image 11 | import net.mamoe.mirai.message.data.Image.Key.queryUrl 12 | import net.mamoe.mirai.message.data.MessageSource.Key.recall 13 | import net.mamoe.mirai.message.data.QuoteReply 14 | 15 | object NaiLongImageListener : ListenerHost { 16 | suspend fun GroupMessageEvent.onEvent() { 17 | if (this.group.permitteeId.hasPermission(PluginMain.checkNailong)) { 18 | for (msg in this.message) { 19 | if (msg !is Image) continue 20 | try { 21 | val imageUrl = msg.queryUrl() 22 | val imageRaw = HttpUtils.getBytesResponse(imageUrl) ?: continue 23 | val scannerResult = DLModel.checkImage(imageRaw) 24 | if (scannerResult) { 25 | this.cancel() 26 | this.group.sendMessage(QuoteReply(this.source) + "啾咪啊!这里有人发奶龙图片啊!快来人管管啊!") 27 | if (this.group.botPermission.level > this.sender.permission.level) { 28 | this.message.recall() 29 | } 30 | break 31 | } 32 | } catch (e: Exception) { 33 | PluginMain.logger.warning("Failed to recognize image") 34 | } 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/listener/message/SelfMessageListener.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.listener.message 2 | 3 | import ltd.guimc.lgzbot.PluginMain 4 | import ltd.guimc.lgzbot.utils.MessageUtils.getPlainText 5 | import ltd.guimc.lgzbot.utils.RegexUtils 6 | import ltd.guimc.lgzbot.utils.RegexUtils.replaceRegex 7 | import net.mamoe.mirai.event.EventHandler 8 | import net.mamoe.mirai.event.EventPriority 9 | import net.mamoe.mirai.event.ListenerHost 10 | import net.mamoe.mirai.event.events.MessagePreSendEvent 11 | import net.mamoe.mirai.message.data.MessageChainBuilder 12 | import net.mamoe.mirai.message.data.PlainText 13 | import net.mamoe.mirai.message.data.toMessageChain 14 | 15 | object SelfMessageListener : ListenerHost { 16 | @EventHandler(priority = EventPriority.HIGHEST) 17 | suspend fun MessagePreSendEvent.onEvent() { 18 | if (!this.message.toMessageChain().getPlainText().endsWith(" [Self-protect System]")) { 19 | val newMessageChain = MessageChainBuilder() 20 | this.message.toMessageChain().forEach { it -> 21 | if (it is PlainText && RegexUtils.matchRegex(PluginMain.spRegex, it.content)) { 22 | this.cancel() 23 | newMessageChain.add(it.content.replaceRegex(PluginMain.spRegex)) 24 | } else { 25 | newMessageChain.add(it) 26 | } 27 | } 28 | if (this.isCancelled) { 29 | newMessageChain.add(" [Self-protect System]") 30 | this.target.sendMessage(newMessageChain.asMessageChain()) 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/listener/multi/BakaListener.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | package ltd.guimc.lgzbot.listener.multi 10 | 11 | import ltd.guimc.lgzbot.PluginMain 12 | import ltd.guimc.lgzbot.files.ModuleStateConfig 13 | import net.mamoe.mirai.console.permission.PermissionService.Companion.hasPermission 14 | import net.mamoe.mirai.console.permission.PermitteeId.Companion.permitteeId 15 | import net.mamoe.mirai.contact.UserOrBot 16 | import net.mamoe.mirai.contact.nameCardOrNick 17 | import net.mamoe.mirai.event.EventHandler 18 | import net.mamoe.mirai.event.ListenerHost 19 | import net.mamoe.mirai.event.events.* 20 | import net.mamoe.mirai.message.data.MessageChain 21 | import net.mamoe.mirai.message.data.MessageChainBuilder 22 | import net.mamoe.mirai.message.data.PlainText 23 | 24 | object BakaListener : ListenerHost { 25 | // 使用 % 分割 26 | // 发送者: source, 目标(可能没有): target 27 | private val NUDGE: Array = 28 | arrayOf("唔...", "喵! (咬)", "不要动我! (气鼓鼓)", "source%是大坏蛋!", "打!", ">_<") 29 | 30 | // private val RECALL: Array = arrayOf("不用再撤回啦~ 我都看到了! %source") 31 | private val QUIT: Array = arrayOf("[GroupListener] %source% left the group.") 32 | private val KICK: Array = arrayOf("[GroupListener] %target% have been removed from your group.") 33 | private val MUTE: Array = arrayOf("[GroupListener] %source% muted %target%.") 34 | private val UNMUTE: Array = arrayOf("[GroupListener] %source% unmuted %target%.") 35 | private val NEW_MEMBER: Array = arrayOf("[GroupListener] New Member! %source%!") 36 | private val MUTE_TO_BOT: Array = arrayOf("为什么禁言我...呜呜 ┭┮﹏┭┮") 37 | private val UNMUTE_TO_BOT: Array = arrayOf("我...我能说话了! 谢谢%source%!") 38 | 39 | // private val rand = Random(RandomUtils.randomLong()) 40 | 41 | @EventHandler 42 | suspend fun NudgeEvent.nudge() { 43 | if (!ModuleStateConfig.nudge) return 44 | if (this.target == this.bot) { 45 | this.subject.sendMessage(format(NUDGE.random(), this.target, this.from)) 46 | } 47 | } 48 | 49 | // @EventHandler 50 | // suspend fun MessageRecallEvent.GroupRecall.recall() { 51 | // if (!ModuleStateConfig.grouplistener) return 52 | // if (this.operator == null) return 53 | // if (this.authorId != this.operator!!.id) return 54 | // if (rand.nextDouble() >= 0.9) { 55 | // this.group.sendMessage(format(RECALL.random(), this.operator!!)) 56 | // } 57 | // } 58 | 59 | @EventHandler 60 | suspend fun MemberLeaveEvent.kick() { 61 | if (!ModuleStateConfig.grouplistener) return 62 | if (this.member == this.bot) return 63 | if (this.group.permitteeId.hasPermission(PluginMain.quiet)) return 64 | if (this !is MemberLeaveEvent.Kick) { 65 | this.group.sendMessage(format(QUIT.random(), this.member)) 66 | return 67 | } 68 | if (this.operator == null) return 69 | this.group.sendMessage(format(KICK.random(), this.member, this.operator!!)) 70 | } 71 | 72 | @EventHandler 73 | suspend fun MemberMuteEvent.mute() { 74 | if (!ModuleStateConfig.grouplistener) return 75 | if (this.group.permitteeId.hasPermission(PluginMain.quiet)) return 76 | if (this.operator == null) return 77 | this.group.sendMessage(format(MUTE.random(), this.member, this.operator!!)) 78 | } 79 | 80 | @EventHandler 81 | suspend fun MemberUnmuteEvent.unmute() { 82 | if (!ModuleStateConfig.grouplistener) return 83 | if (this.group.permitteeId.hasPermission(PluginMain.quiet)) return 84 | if (this.operator == null) return 85 | this.group.sendMessage(format(UNMUTE.random(), this.member, this.operator!!)) 86 | } 87 | 88 | @EventHandler 89 | suspend fun MemberJoinEvent.newMember() { 90 | if (!ModuleStateConfig.grouplistener) return 91 | if (this.group.permitteeId.hasPermission(PluginMain.quiet)) return 92 | this.group.sendMessage(format(NEW_MEMBER.random(), this.member)) 93 | } 94 | 95 | @EventHandler 96 | suspend fun BotMuteEvent.muteBot() { 97 | if (!ModuleStateConfig.grouplistener) return 98 | if (this.group.permitteeId.hasPermission(PluginMain.quiet)) return 99 | this.operator.sendMessage(format(MUTE_TO_BOT.random())) 100 | } 101 | 102 | @EventHandler 103 | suspend fun BotUnmuteEvent.unmuteBot() { 104 | if (!ModuleStateConfig.grouplistener) return 105 | if (this.group.permitteeId.hasPermission(PluginMain.quiet)) return 106 | this.group.sendMessage(format(UNMUTE_TO_BOT.random(), this.operator)) 107 | } 108 | 109 | private fun format(str: String, target: UserOrBot?, source: UserOrBot?): MessageChain { 110 | val messages = MessageChainBuilder() 111 | for (i in str.split("%")) { 112 | messages += when (i) { 113 | "source" -> if (source != null) PlainText("${source.nameCardOrNick}(${source.id})") else PlainText("source") 114 | "target" -> if (target != null) PlainText("${target.nameCardOrNick}(${target.id})") else PlainText("target") 115 | else -> PlainText(i) 116 | } 117 | } 118 | return messages.build() 119 | } 120 | 121 | private fun format(str: String): MessageChain = format(str, null, null) 122 | private fun format(str: String, source: UserOrBot?): MessageChain = format(str, null, source) 123 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/listener/mute/AutoQuit.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.listener.mute 2 | 3 | import net.mamoe.mirai.event.EventHandler 4 | import net.mamoe.mirai.event.ListenerHost 5 | import net.mamoe.mirai.event.events.BotMuteEvent 6 | 7 | object AutoQuit : ListenerHost { 8 | private const val autoQuitTime = 60 * 60 // 1 Hour 9 | 10 | @EventHandler 11 | suspend fun BotMuteEvent.onEvent() { 12 | if (this.durationSeconds >= autoQuitTime) { 13 | this.operator.sendMessage("[AutoQuit] 触发自动退群") 14 | this.group.quit() 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/listener/nudge/AntiNudgeSpam.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.listener.nudge 2 | 3 | import net.mamoe.mirai.contact.User 4 | import net.mamoe.mirai.event.events.NudgeEvent 5 | import net.mamoe.mirai.message.data.At 6 | import net.mamoe.mirai.message.data.PlainText 7 | import java.time.Instant 8 | 9 | object AntiNudgeSpam { 10 | private val nudgeTimes: MutableMap = mutableMapOf() 11 | private val lastNudgeTime: MutableMap = mutableMapOf() 12 | private val blockedUser: MutableMap = mutableMapOf() 13 | 14 | suspend fun onNudge(e: NudgeEvent) { 15 | val from = e.from as User 16 | val timestamp = Instant.now().epochSecond 17 | 18 | if (e.target != e.bot) return // 只处理对机器人的戳一戳 19 | if (e.from == e.bot) return // 机器人自己戳自己? 雾 20 | 21 | // 检查是否已经被屏蔽了 22 | if (blockedUser[from] != null && blockedUser[from]!! >= timestamp) { 23 | e.intercept() 24 | return 25 | } 26 | 27 | if (nudgeTimes[from] == null) { // 检查是否已初始化过戳一戳发起者 28 | nudgeTimes[from] = 1 29 | lastNudgeTime[from] = timestamp 30 | } else if (timestamp - lastNudgeTime[from]!! >= 3) { 31 | // ta已经好久没有戳一戳了 重置次数 32 | nudgeTimes[from] = 1 33 | lastNudgeTime[from] = timestamp 34 | } else { 35 | // 次数+1 36 | nudgeTimes[from] = nudgeTimes[from]!! + 1 37 | } 38 | 39 | // 检查次数 40 | if (nudgeTimes[from]!! >= 3) { 41 | blockedUser[from] = timestamp + 3600L 42 | try { 43 | e.subject.sendMessage( 44 | At(from) + PlainText(" 你...你怎么能戳这么快!\n我生气了!免疫你的戳一戳1小时!") 45 | ) 46 | } catch (ignore: Throwable) { 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/listener/nudge/NudgeMute.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.listener.nudge 2 | 3 | import ltd.guimc.lgzbot.PluginMain 4 | import ltd.guimc.lgzbot.utils.MemberUtils.mute 5 | import net.mamoe.mirai.console.permission.PermissionService.Companion.hasPermission 6 | import net.mamoe.mirai.console.permission.PermitteeId.Companion.permitteeId 7 | import net.mamoe.mirai.contact.Group 8 | import net.mamoe.mirai.contact.getMemberOrFail 9 | import net.mamoe.mirai.event.events.NudgeEvent 10 | 11 | object NudgeMute { 12 | suspend fun onNudge(e: NudgeEvent) { 13 | // check target 14 | if (e.target != e.bot) return 15 | 16 | // check subject 17 | if (e.subject !is Group) return 18 | 19 | val group = e.subject as Group 20 | val from = group.getMemberOrFail(e.from.id) 21 | 22 | // check permission 23 | if (!group.permitteeId.hasPermission(PluginMain.nudgeMute)) return 24 | 25 | // check bot permission 26 | if (from.permission.level >= group.botAsMember.permission.level) return 27 | 28 | from.mute(600, "NudgeMute is Enabled") 29 | e.cancel() 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/AsciiUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.utils 11 | 12 | object AsciiUtil { 13 | const val SBC_SPACE = 12288 // 全角空格 12288 14 | .toChar() 15 | const val DBC_SPACE = 32 //半角空格 32 16 | .toChar() 17 | 18 | // ASCII character 33-126 <-> unicode 65281-65374 19 | const val ASCII_START = 33.toChar() 20 | const val ASCII_END = 126.toChar() 21 | const val UNICODE_START = 65281.toChar() 22 | const val UNICODE_END = 65374.toChar() 23 | const val DBC_SBC_STEP = 65248 // 全角半角转换间隔 24 | .toChar() 25 | 26 | fun sbc2dbc(src: Char): Char { 27 | if (src == SBC_SPACE) { 28 | return DBC_SPACE 29 | } 30 | return if (src in UNICODE_START..UNICODE_END) { 31 | (src.code - DBC_SBC_STEP.code).toChar() 32 | } else src 33 | } 34 | 35 | /** 36 | * Convert from SBC case to DBC case 37 | * 38 | * @param src 39 | * @return DBC case 40 | */ 41 | fun sbc2dbcCase(src: String): String { 42 | val c = src.toCharArray() 43 | for (i in c.indices) { 44 | c[i] = sbc2dbc(c[i]) 45 | } 46 | return String(c) 47 | } 48 | 49 | fun dbc2sbc(src: Char): Char { 50 | if (src == DBC_SPACE) { 51 | return SBC_SPACE 52 | } 53 | return if (src <= ASCII_END) { 54 | (src.code + DBC_SBC_STEP.code).toChar() 55 | } else src 56 | } 57 | 58 | /** 59 | * Convert from DBC case to SBC case. 60 | * 61 | * @param src 62 | * @return SBC case string 63 | */ 64 | fun dbc2sbcCase(src: String?): String? { 65 | if (src == null) { 66 | return null 67 | } 68 | val c = src.toCharArray() 69 | for (i in c.indices) { 70 | c[i] = dbc2sbc(c[i]) 71 | } 72 | return String(c) 73 | } 74 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/Base64Utils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.utils 11 | 12 | import java.util.* 13 | 14 | object Base64Utils { 15 | fun encode(data: ByteArray): String { 16 | return Base64.getEncoder().encodeToString(data) 17 | } 18 | 19 | fun decode(data: String): ByteArray { 20 | return Base64.getDecoder().decode(data) 21 | } 22 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/CooldownUtils.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.utils 2 | 3 | class CooldownUtils(val cooldown: Long) { 4 | var cooldownMap: MutableMap = mutableMapOf() 5 | var cooldownNoticeMap: MutableMap = mutableMapOf() 6 | 7 | fun flag(target: Any) { 8 | cooldownMap[target] = System.currentTimeMillis() 9 | } 10 | 11 | fun isTimePassed(target: Any): Boolean { 12 | return isTimePassed(target, cooldown) 13 | } 14 | 15 | fun isTimePassed(target: Any, time: Long): Boolean { 16 | if (cooldownMap.keys.indexOf(target) == -1) return true 17 | if (System.currentTimeMillis() - cooldownMap[target]!! >= time) return true 18 | return false 19 | } 20 | 21 | fun shouldSendCooldownNotice(target: Any): Boolean { 22 | if (cooldownNoticeMap.keys.indexOf(target) == -1) return true 23 | if (System.currentTimeMillis() - cooldownNoticeMap[target]!! >= 3000) { 24 | cooldownNoticeMap[target] = System.currentTimeMillis() 25 | return true 26 | } 27 | return false 28 | } 29 | 30 | fun getLeftTime(target: Any): Long { 31 | return getLeftTime(target, cooldown) 32 | } 33 | 34 | fun getLeftTime(target: Any, time: Long): Long { 35 | if (cooldownMap.keys.indexOf(target) == -1) return -1 36 | return time - (System.currentTimeMillis() - cooldownMap[target]!!) 37 | } 38 | 39 | fun addLeftTime(target: Any, time: Long) { 40 | if (cooldownMap.keys.indexOf(target) == -1) return 41 | cooldownMap[target] = cooldownMap[target]!! + time 42 | } 43 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/FbUtils.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.utils 2 | 3 | object FbUtils { 4 | fun getFbValue(): Array { 5 | // Thanks for https://github.com/Ikaros-521/nonebot_plugin_random_stereotypes/blob/master/nonebot_plugin_random_stereotypes/data.py 6 | val fbFile = FbUtils::class.java.getResourceAsStream("/faqing.txt") 7 | val fbList = mutableListOf() 8 | fbFile?.bufferedReader()?.use { reader -> 9 | reader.lines().forEach { 10 | // regexList.add(Regex(PinyinUtils.convertToPinyin(it))) 11 | fbList.add(it) 12 | } 13 | } 14 | 15 | return fbList.toTypedArray() 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/GithubUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.utils 11 | 12 | import ltd.guimc.lgzbot.files.GithubSubConfig 13 | import ltd.guimc.lgzbot.github.CommitInfo 14 | import ltd.guimc.lgzbot.github.OwnerInfo 15 | import ltd.guimc.lgzbot.github.RepoInfo 16 | import ltd.guimc.lgzbot.github.UserInfo 17 | import org.json.JSONArray 18 | import org.json.JSONObject 19 | import java.time.LocalDateTime 20 | import java.time.format.DateTimeFormatter 21 | 22 | object GithubUtils { 23 | val gitLinkRegex = Regex(pattern = "(git|https|git@)(:\\/\\/|)github.com(:|\\/).*\\/.*(.git|\\/| |)") 24 | val githubHead = Regex(pattern = "(git|https|git@)(:\\/\\/|)github.com(:|\\/)") 25 | 26 | fun findGitLink(text: String): String? { 27 | return gitLinkRegex.find(text)?.value 28 | } 29 | 30 | private fun apiObject(url: String): JSONObject = HttpUtils.getJsonObject("https://api.github.com$url", if (GithubSubConfig.key != "" ) GithubSubConfig.key else null) 31 | 32 | private fun apiArray(url: String): JSONArray = HttpUtils.getJsonArray("https://api.github.com$url", if (GithubSubConfig.key != "" ) GithubSubConfig.key else null) 33 | 34 | fun convert(url: String): String { 35 | val s = url 36 | var dropLength = 0 37 | if (s.endsWith("/")) dropLength++ 38 | if (s.endsWith(".git")) dropLength += 4 39 | if (s.endsWith(" ")) dropLength++ 40 | return s.dropLast(dropLength).replace(githubHead, "") 41 | } 42 | 43 | fun getGithubRepo(repo: String): RepoInfo { 44 | val infoJson = apiObject("/repos/$repo") 45 | return RepoInfo( 46 | repo, 47 | OwnerInfo(infoJson.getJSONObject("owner").getString("login")), 48 | infoJson.getString("description"), 49 | LocalDateTime.parse(infoJson.getString("created_at"), DateTimeFormatter.ISO_DATE_TIME), 50 | LocalDateTime.parse(infoJson.getString("pushed_at"), DateTimeFormatter.ISO_DATE_TIME), 51 | getLastCommit(repo), 52 | infoJson.getString("language"), 53 | infoJson.getString("default_branch") 54 | ) 55 | } 56 | 57 | fun getLastCommit(repo: String): CommitInfo { 58 | val infoJson = apiArray("/repos/$repo/commits").getJSONObject(0) 59 | val commit = infoJson.getJSONObject("commit") 60 | val author = commit.getJSONObject("committer") 61 | val commitID = infoJson.getString("sha").dropLast(8) 62 | 63 | // Commit Info 64 | val verification = try { 65 | commit.getJSONObject("verification").getBoolean("verified") 66 | } catch (_: Exception) { 67 | false 68 | } 69 | val commitMessage = commit.getString("message") 70 | val commitTime = LocalDateTime.parse(commit.getJSONObject("committer").getString("date"), DateTimeFormatter.ISO_DATE_TIME) 71 | 72 | return CommitInfo( 73 | commitID, 74 | commitMessage, 75 | UserInfo( 76 | author.getString("name"), 77 | author.getString("email") 78 | ), 79 | commitTime, 80 | verification 81 | ) 82 | } 83 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/HttpUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.utils 11 | 12 | import org.json.JSONArray 13 | import org.json.JSONObject 14 | import java.util.* 15 | 16 | object HttpUtils { 17 | fun getJsonObject(url: String, auth: String? = null): JSONObject { 18 | val connection = java.net.URL(url).openConnection() as java.net.HttpURLConnection 19 | connection.requestMethod = "GET" 20 | if (auth != null) { 21 | val basicAuth = "Basic : " + String(Base64.getEncoder().encode(auth.toByteArray())) 22 | connection.setRequestProperty("Authorization", basicAuth) 23 | } 24 | connection.instanceFollowRedirects = true 25 | connection.connect() 26 | // 转换到json对象 27 | val raw = connection.inputStream.bufferedReader().readText() 28 | return JSONObject(raw) 29 | } 30 | 31 | fun getJsonArray(url: String, auth: String? = null): JSONArray { 32 | val connection = java.net.URL(url).openConnection() as java.net.HttpURLConnection 33 | connection.requestMethod = "GET" 34 | if (auth != null) { 35 | val basicAuth = "Basic : " + String(Base64.getEncoder().encode(auth.toByteArray())) 36 | connection.setRequestProperty("Authorization", basicAuth) 37 | } 38 | connection.connect() 39 | connection.instanceFollowRedirects = true 40 | // 转换到json对象 41 | val raw = connection.inputStream.bufferedReader().readText() 42 | return JSONArray(raw) 43 | } 44 | 45 | fun getResponse(url: String): String { 46 | val connection = java.net.URL(url).openConnection() as java.net.HttpURLConnection 47 | connection.requestMethod = "GET" 48 | connection.instanceFollowRedirects = true 49 | connection.connect() 50 | return connection.inputStream.bufferedReader().readText() 51 | } 52 | 53 | fun getBytesResponse(url: String): ByteArray? { 54 | val connection = java.net.URL(url).openConnection() as java.net.HttpURLConnection 55 | connection.requestMethod = "GET" 56 | connection.instanceFollowRedirects = true 57 | connection.connect() 58 | return connection.inputStream.readAllBytes() 59 | } 60 | 61 | fun pushJson(url: String, json: String): String { 62 | val connection = java.net.URL(url).openConnection() as java.net.HttpURLConnection 63 | connection.requestMethod = "POST" 64 | connection.setRequestProperty("Content-Type", "application/json") 65 | connection.setRequestProperty("Content-Length", json.length.toString()) 66 | connection.instanceFollowRedirects = true 67 | connection.doOutput = true 68 | connection.connect() 69 | connection.outputStream.write(json.toByteArray()) 70 | return connection.responseMessage 71 | } 72 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/ImageUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.utils 11 | 12 | import net.mamoe.mirai.Bot 13 | import net.mamoe.mirai.contact.Contact 14 | import net.mamoe.mirai.message.data.Image 15 | import net.mamoe.mirai.message.data.Image.Key.isUploaded 16 | import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource 17 | import org.apache.http.client.methods.HttpGet 18 | import org.apache.http.impl.client.HttpClients 19 | import java.io.IOException 20 | import java.io.InputStream 21 | 22 | 23 | object ImageUtils { 24 | suspend fun url2imageMessage(url: String, bot: Bot, subject: Contact): Image { 25 | val httpclients = HttpClients.createDefault() 26 | val httpget = HttpGet(url) 27 | val response = httpclients.execute(httpget) 28 | if (response.statusLine.statusCode == 200) { 29 | val entity = response.entity 30 | if (entity != null) { 31 | val inputstream = entity.content 32 | if (inputstream != null) { 33 | val image = subject.uploadImage(inputstream.toExternalResource()) 34 | if (image.isUploaded(bot)) { 35 | return image 36 | } 37 | } 38 | } 39 | } 40 | throw Exception("图片上传失败") 41 | } 42 | 43 | @Throws(IOException::class) 44 | fun imgType(inputStream: InputStream): String { 45 | // 读取文件前几位 46 | val fileHeader = ByteArray(4) 47 | val read = inputStream.read(fileHeader, 0, fileHeader.size) 48 | inputStream.close() 49 | 50 | 51 | // 转为十六进制字符串 52 | val header: String = bytes2Hex(fileHeader) 53 | 54 | return if (header.contains("FFD8FF")) { 55 | "jpg" 56 | } else if (header.contains("89504E47")) { 57 | "png" 58 | } else if (header.contains("47494638")) { 59 | "gif" 60 | } else if (header.contains("424D")) { 61 | "bmp" 62 | } else if (header.contains("52494646")) { 63 | "webp" 64 | } else if (header.contains("49492A00")) { 65 | "tif" 66 | } else { 67 | "unknown" 68 | } 69 | } 70 | 71 | fun bytes2Hex(bytes: ByteArray): String { 72 | val sb = StringBuilder() 73 | for (b in bytes) { 74 | val hex = Integer.toHexString(b.toInt() and 0xff) 75 | sb.append(if (hex.length == 2) hex else ("0$hex")) 76 | } 77 | return sb.toString() 78 | } 79 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/LL4JUtils.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.utils 2 | 3 | import huzpsb.ll4j.minrt.JFuncModelScript 4 | import huzpsb.ll4j.model.Model 5 | import huzpsb.ll4j.nlp.token.Tokenizer 6 | import huzpsb.ll4j.utils.data.DataSet 7 | import ltd.guimc.lgzbot.utils.AsciiUtil.sbc2dbcCase 8 | import ltd.guimc.lgzbot.utils.TextUtils.removeInterference 9 | import ltd.guimc.lgzbot.utils.TextUtils.removeNonVisible 10 | 11 | object LL4JUtils { 12 | lateinit var model: (DoubleArray) -> DoubleArray 13 | lateinit var tokenizer: Tokenizer 14 | var version = "FEB25" 15 | 16 | fun init() { 17 | val tokenizerFile = LL4JUtils.javaClass.getResourceAsStream("/ts.model")!! 18 | val modelFile = LL4JUtils.javaClass.getResourceAsStream("/anti-ad.model")!! 19 | tokenizer = Tokenizer.load(tokenizerFile.bufferedReader(Charsets.UTF_8)) 20 | modelFile.bufferedReader(Charsets.UTF_8).use { 21 | val compiled = JFuncModelScript.compile(it.readLines().toTypedArray()) 22 | model = { arr -> compiled.apply(arr) } // to kotlin style lambda (wtf) 23 | } 24 | } 25 | 26 | fun predict(string: String): Boolean = 27 | model( 28 | tokenizer.tokenize( 29 | 0, 30 | string.replace("\n", "").replace("live.bilibili.com", "") 31 | ).values 32 | ).let { it[1] > it[0] } 33 | 34 | // [unused] 35 | // fun predictDebug(string: String): Pair = 36 | // model.predictDebug(tokenizer.tokenize(0, string.replace("\n", "").replace("live.bilibili.com", "")).values) 37 | 38 | fun predictAllResult(string: String): DoubleArray = 39 | model( 40 | tokenizer.tokenize( 41 | 0, sbc2dbcCase(string.replace("\n", "").replace("live.bilibili.com", "")) 42 | .lowercase() 43 | .removeInterference() 44 | .removeNonVisible() 45 | ).values 46 | ) 47 | 48 | // [deprecated] 49 | fun learn(type: Int, string: String) { 50 | val dataSet = DataSet() 51 | dataSet.split.add(tokenizer.tokenize(type, string.replace("\n", ""))) 52 | // model.trainOn(dataSet) 53 | } 54 | 55 | fun downloadModel() { 56 | try { 57 | // model = Model.read(HttpUtils.getResponse("https://raw.githubusercontent.com/siuank/ADDetector4J/main/anti-ad.model")) 58 | tokenizer = Tokenizer.load(HttpUtils.getResponse("https://raw.githubusercontent.com/siuank/ADDetector4J/main/t1.tokenized.txt").reader()) 59 | val time = GithubUtils.getLastCommit("siuank/ADDetector4J").commitTime 60 | version = "${time.month.name.substring(0..3)}${time.dayOfMonth}" 61 | } catch (_: Exception) {} 62 | } 63 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/MemberUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.utils 11 | 12 | import ltd.guimc.lgzbot.command.LGZBotCommand.mute 13 | import ltd.guimc.lgzbot.command.LGZBotCommand.unmute 14 | import net.mamoe.mirai.console.command.ConsoleCommandSender 15 | import net.mamoe.mirai.contact.Member 16 | 17 | object MemberUtils { 18 | suspend fun Member.mute(durationSeconds: Int, reason: String) { 19 | ConsoleCommandSender.mute(this, "${durationSeconds}s", reason) 20 | } 21 | 22 | suspend fun Member.unmute() { 23 | ConsoleCommandSender.unmute(this) 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/MessageUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.utils 11 | 12 | import net.mamoe.mirai.message.data.ForwardMessage 13 | import net.mamoe.mirai.message.data.MessageChain 14 | import net.mamoe.mirai.message.data.MessageChainBuilder 15 | import net.mamoe.mirai.message.data.PlainText 16 | 17 | object MessageUtils { 18 | fun MessageChain.getPlainText(): String { 19 | var pt = "" 20 | for (i in listIterator()) if (i is PlainText) pt += i.content 21 | return pt 22 | } 23 | 24 | fun MessageChain.getFullText(): String { 25 | var pt = "" 26 | for (i in listIterator()) { 27 | if (i is PlainText) { 28 | pt += i.content 29 | } 30 | 31 | if (i is ForwardMessage) { 32 | pt += i.getPlainText() 33 | } 34 | } 35 | return pt 36 | } 37 | 38 | fun MessageChain.getForwardMessage(): MessageChain { 39 | val msgcb = MessageChainBuilder() 40 | for (u in listIterator()) if (u is ForwardMessage) for (i in u.nodeList) for (k in i.messageChain.listIterator()) msgcb.add(k) 41 | return msgcb.build() 42 | } 43 | 44 | fun ForwardMessage.getPlainText(): String { 45 | var pt = "" 46 | for (i in nodeList) { 47 | for (k in i.messageChain.listIterator()) { 48 | if (k is PlainText) { 49 | pt += k.content 50 | } 51 | 52 | if (k is ForwardMessage) { 53 | pt += k.getPlainText() 54 | } 55 | } 56 | } 57 | return pt 58 | } 59 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/MojangAPIUtils.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.utils 2 | 3 | import ltd.guimc.lgzbot.utils.TextUtils.convert2UUID 4 | import java.util.* 5 | 6 | object MojangAPIUtils { 7 | fun getUUIDByName(name: String): String { 8 | return HttpUtils.getJsonObject("https://api.mojang.com/users/profiles/minecraft/$name").getString("id") 9 | } 10 | 11 | fun String.unFormatted(): String { 12 | return this.replace("§0", "") 13 | .replace("§1", "") 14 | .replace("§2", "") 15 | .replace("§3", "") 16 | .replace("§4", "") 17 | .replace("§5", "") 18 | .replace("§6", "") 19 | .replace("§7", "") 20 | .replace("§8", "") 21 | .replace("§9", "") 22 | .replace("§a", "") 23 | .replace("§b", "") 24 | .replace("§c", "") 25 | .replace("§d", "") 26 | .replace("§e", "") 27 | .replace("§f", "") 28 | .replace("§g", "") 29 | .replace("§h", "") 30 | .replace("§i", "") 31 | .replace("§j", "") 32 | .replace("§n", "") 33 | .replace("§m", "") 34 | .replace("§q", "") 35 | .replace("§p", "") 36 | .replace("§s", "") 37 | .replace("§t", "") 38 | .replace("§u", "") 39 | .replace("§o", "") 40 | .replace("§r", "") 41 | .replace("§k", "") 42 | } 43 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/OverflowUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.utils 11 | 12 | import net.mamoe.mirai.Bot 13 | import top.mrxiaom.overflow.contact.RemoteBot.Companion.asRemoteBot 14 | 15 | object OverflowUtils { 16 | fun checkOverflowCore(): Boolean { 17 | try { 18 | Class.forName("top.mrxiaom.overflow.internal.Overflow") 19 | return true 20 | } catch (ignored: ClassNotFoundException) { 21 | return false 22 | } 23 | } 24 | 25 | fun getOnebotServiceProviderName(bot: Bot): String { 26 | if (!checkOverflowCore()) return "null" 27 | return bot.asRemoteBot.appName 28 | } 29 | 30 | fun getOnebotServiceProviderVersion(bot: Bot): String { 31 | if (!checkOverflowCore()) return "null" 32 | return bot.asRemoteBot.appVersion 33 | } 34 | 35 | fun getOnebotConnection(): String { 36 | if (!checkOverflowCore()) return "null" 37 | val oclazz = Class.forName("top.mrxiaom.overflow.internal.Overflow") 38 | val instance = oclazz.getDeclaredMethod("getInstance").invoke(null) 39 | val method = oclazz.getDeclaredMethod("getConfig") 40 | method.trySetAccessible() 41 | val configInstance = method.invoke(instance) 42 | val cclazz = Class.forName("top.mrxiaom.overflow.internal.Config") 43 | val rPortMethod = cclazz.getDeclaredMethod("getReversedWSPort") 44 | rPortMethod.trySetAccessible() 45 | val rPort = rPortMethod.invoke(configInstance) as Int 46 | if (rPort in 1..65536) { 47 | return "rws: $rPort" 48 | } 49 | val wsHostMethod = cclazz.getDeclaredMethod("getWsHost") 50 | wsHostMethod.trySetAccessible() 51 | val wsHost = wsHostMethod.invoke(configInstance) as String 52 | return "ws: $wsHost" 53 | } 54 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/PinyinUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.utils 11 | 12 | import com.github.promeg.pinyinhelper.Pinyin 13 | 14 | object PinyinUtils { 15 | // 将汉字转化为拼音 16 | fun convertToPinyin(text: String): String { 17 | return Pinyin.toPinyin(text, "") 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/RegexUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.utils 11 | 12 | import ltd.guimc.lgzbot.PluginMain.logger 13 | import ltd.guimc.lgzbot.utils.AsciiUtil.sbc2dbcCase 14 | import ltd.guimc.lgzbot.utils.TextUtils.removeInterference 15 | import ltd.guimc.lgzbot.utils.TextUtils.removeNonVisible 16 | 17 | object RegexUtils { 18 | // 获取正则表达式列表 19 | fun getDefaultRegex(): Array { 20 | // Read from resources regex.txt 21 | val regexFile = RegexUtils::class.java.getResourceAsStream("/regex.txt") 22 | val regexList = mutableListOf() 23 | regexFile?.bufferedReader()?.use { reader -> 24 | reader.lines().forEach { 25 | // regexList.add(Regex(PinyinUtils.convertToPinyin(it))) 26 | regexList.add(Regex(it)) 27 | } 28 | } 29 | 30 | return regexList.toTypedArray() 31 | } 32 | 33 | fun getDefaultPinyinRegex(): Array { 34 | // Read from resources regex.txt 35 | val regexFile = RegexUtils::class.java.getResourceAsStream("/regex.txt") 36 | val regexList = mutableListOf() 37 | regexFile?.bufferedReader()?.use { reader -> 38 | reader.lines().forEach { 39 | // regexList.add(Regex(PinyinUtils.convertToPinyin(it))) 40 | regexList.add(Regex(PinyinUtils.convertToPinyin(it))) 41 | } 42 | } 43 | 44 | return regexList.toTypedArray() 45 | } 46 | 47 | fun getSeriousRegex(): Array { 48 | // Read from resources regex.txt 49 | val regexFile = RegexUtils::class.java.getResourceAsStream("/serious.txt") 50 | val regexList = mutableListOf() 51 | regexFile?.bufferedReader()?.use { reader -> 52 | reader.lines().forEach { 53 | // regexList.add(Regex(PinyinUtils.convertToPinyin(it))) 54 | regexList.add(Regex(it)) 55 | } 56 | } 57 | 58 | return regexList.toTypedArray() 59 | } 60 | 61 | fun getSpRegex(): Array { 62 | // Read from resources regex.txt 63 | val regexFile = RegexUtils::class.java.getResourceAsStream("/self-protect.txt") 64 | val regexList = mutableListOf() 65 | regexFile?.bufferedReader()?.use { reader -> 66 | reader.lines().forEach { 67 | // regexList.add(Regex(PinyinUtils.convertToPinyin(it))) 68 | regexList.add(Regex(it)) 69 | } 70 | } 71 | 72 | return regexList.toTypedArray() 73 | } 74 | 75 | // 匹配正则表达式列表 返回是否匹配 76 | fun matchRegex(regexList: Array, message: String): Boolean { 77 | try { 78 | var i = 0 79 | val unPeekText = sbc2dbcCase(message) 80 | .lowercase() 81 | .removeInterference() 82 | .removeNonVisible() 83 | for (regex in regexList) { 84 | i++ 85 | if (regex.containsMatchIn(unPeekText)) { 86 | return true 87 | } 88 | } 89 | } catch (_: Throwable) {} 90 | return false 91 | } 92 | 93 | fun String.replaceRegex(regexList: Array): String { 94 | var a = this 95 | try { 96 | var i = 0 97 | for (regex in regexList) { 98 | i++ 99 | if (regex.containsMatchIn(a)) { 100 | logger.info("匹配成功 在第${i}行 ${regex.find(a)?.value}") 101 | a = a.replace(regex, "****") 102 | } 103 | } 104 | } catch (_: Throwable) { 105 | } 106 | return a 107 | } 108 | 109 | fun matchRegexPinyin(regexList: Array, message: String): Boolean { 110 | try { 111 | var i = 0 112 | val unPeekText = PinyinUtils.convertToPinyin(sbc2dbcCase(message)) 113 | .lowercase() 114 | .removeInterference() 115 | .removeNonVisible() 116 | for (regex in regexList) { 117 | i++ 118 | if (regex.containsMatchIn(unPeekText)) { 119 | logger.info("匹配成功 在第${i}行 ${regex.find(unPeekText)?.value}") 120 | return true 121 | } 122 | } 123 | } catch (_: Throwable) {} 124 | return false 125 | } 126 | 127 | // 匹配正则表达式 返回匹配结果 128 | fun matchRegex(regex: String, message: String): String? { 129 | // logger.info("匹配成功") 130 | return Regex(regex).find(message)?.groups?.get(0)?.value 131 | } 132 | 133 | // 检测内容是否异常 134 | fun checkRisk(content: String): String { 135 | return if (matchRegex(getDefaultRegex(), content)) { 136 | "" 137 | } else { 138 | content 139 | } 140 | } 141 | 142 | fun countLines(str: String): Int { 143 | val lines = str.split("\r\n|\r|\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() 144 | 145 | return lines.size 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/RequestUtils.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.utils 2 | 3 | import net.mamoe.mirai.event.events.BotInvitedJoinGroupRequestEvent 4 | 5 | object RequestUtils { 6 | private val groups: MutableMap = mutableMapOf() 7 | 8 | object Group { 9 | fun add(event: BotInvitedJoinGroupRequestEvent) = groups.put(event.eventId, event) 10 | fun remove(id: Long) = groups.remove(id) 11 | 12 | fun find(id: Long): BotInvitedJoinGroupRequestEvent { 13 | try { 14 | return groups[id]!! 15 | } catch (e: NullPointerException) { 16 | throw NoSuchElementException("Can't find the event") 17 | } catch (e: Throwable) { 18 | throw e 19 | } 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/TextUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.utils 11 | 12 | import java.util.* 13 | import kotlin.math.max 14 | import kotlin.math.min 15 | 16 | object TextUtils { 17 | private fun getLevenshteinDistance(X: String, Y: String): Int { 18 | val m = X.length 19 | val n = Y.length 20 | val T = Array(m + 1) { IntArray(n + 1) } 21 | for (i in 1..m) { 22 | T[i][0] = i 23 | } 24 | for (j in 1..n) { 25 | T[0][j] = j 26 | } 27 | var cost: Int 28 | for (i in 1..m) { 29 | for (j in 1..n) { 30 | cost = if (X[i - 1] == Y[j - 1]) 0 else 1 31 | T[i][j] = min(min(T[i - 1][j] + 1, T[i][j - 1] + 1), 32 | T[i - 1][j - 1] + cost) 33 | } 34 | } 35 | return T[m][n] 36 | } 37 | 38 | // 寻找两字符串相似度,返回双精度浮点数 39 | fun findSimilarity(x: String?, y: String?): Double { 40 | require(!(x == null || y == null)) { "Strings should not be null" } 41 | 42 | val maxLength = max(x.length, y.length) 43 | return if (maxLength > 0) { 44 | (maxLength * 1.0 - getLevenshteinDistance(x, y)) / maxLength * 1.0 45 | } else 1.0 46 | } 47 | 48 | // 去除不可见字符 49 | fun String.removeNonVisible(): String { 50 | return this.replace("\\p{C}".toRegex(), "") 51 | } 52 | 53 | // 去除干扰字符 54 | fun String.removeInterference(): String { 55 | return this.replace(" ", "") 56 | .replace(",", "") 57 | .replace(".", "") 58 | .replace("!", "") 59 | .replace("?", "") 60 | .replace(";", "") 61 | .replace(":", "") 62 | .replace("\"", "") 63 | .replace("'", "") 64 | .replace("“", "") 65 | .replace("”", "") 66 | .replace("‘", "") 67 | .replace("’", "") 68 | .replace("<", "") 69 | .replace(">", "") 70 | .replace("(", "") 71 | .replace(")", "") 72 | .replace("內", "内") 73 | } 74 | 75 | fun String.convert2UUID(): UUID { 76 | return UUID.fromString("${this.substring(0, 7)}-${this.substring(8, 11)}-${this.substring(12, 15)}-${this.substring(16, 19)}-${this.substring(20)}") 77 | } 78 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/TimeUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.utils 11 | 12 | import java.text.DateFormat 13 | import java.time.Instant 14 | import java.time.ZoneId 15 | import java.time.format.DateTimeFormatter 16 | 17 | 18 | object TimeUtils { 19 | fun convertDate(dateInMilliseconds: Long): String { 20 | return Instant.ofEpochMilli(dateInMilliseconds) 21 | .atZone(ZoneId.of("Asia/Shanghai")) 22 | .toLocalDateTime() 23 | .format( 24 | DateTimeFormatter.ISO_LOCAL_DATE_TIME 25 | ) 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/hypixel/ApiFailedException.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.utils.hypixel 11 | 12 | class ApiFailedException : Throwable { 13 | constructor() : super() 14 | constructor(message: String) : super(message) 15 | constructor(message: String, cause: Throwable) : super(message, cause) 16 | constructor(cause: Throwable) : super(cause) 17 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/hypixel/ExpCalculator.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.utils.hypixel 2 | 3 | import kotlin.math.floor 4 | import kotlin.math.sqrt 5 | 6 | object ExpCalculator { 7 | val BASE = 10000.0 8 | val GROWTH = 2500.0 9 | 10 | /* Constants to generate the total amount of XP to complete a level */ 11 | val HALF_GROWTH = 0.5 * GROWTH 12 | 13 | /* Constants to look up the level from the total amount of XP */ 14 | val REVERSE_PQ_PREFIX = -(BASE - 0.5 * GROWTH) / GROWTH 15 | val REVERSE_CONST = REVERSE_PQ_PREFIX * REVERSE_PQ_PREFIX 16 | val GROWTH_DIVIDES_2 = 2 / GROWTH 17 | 18 | fun getExactLevel(exp: Long): Double { 19 | return getLevel(exp) + getPercentageToNextLevel(exp) 20 | } 21 | 22 | private fun getTotalExpToFullLevel(level: Double): Double { 23 | return (HALF_GROWTH * (level - 2) + BASE) * (level - 1) 24 | } 25 | 26 | private fun getTotalExpToLevel(level: Double): Double { 27 | val lv = floor(level) 28 | val x0 = getTotalExpToFullLevel(lv) 29 | return if (level == lv) x0 else (getTotalExpToFullLevel(lv + 1) - x0) * (level % 1) + x0 30 | } 31 | 32 | private fun getPercentageToNextLevel(exp: Long): Double { 33 | val lv = getLevel(exp) 34 | val x0 = getTotalExpToLevel(lv) 35 | return (exp - x0) / (getTotalExpToLevel(lv + 1) - x0) 36 | } 37 | 38 | private fun getLevel(exp: Long): Double { 39 | return if (exp < 0) 1.0 else floor(1 + REVERSE_PQ_PREFIX + sqrt(REVERSE_CONST + GROWTH_DIVIDES_2 * exp)) 40 | } 41 | 42 | fun getBedWarsLevel(exp: Int): Double { 43 | var exp = exp.toDouble() 44 | var level = 100 * (exp / 487000).toInt() 45 | exp %= 487000 46 | if (exp < 500) return level + exp / 500 47 | level++ 48 | if (exp < 1500) return level + (exp - 500) / 1000 49 | level++ 50 | if (exp < 3500) return level + (exp - 1500) / 2000 51 | level++ 52 | if (exp < 7000) return level + (exp - 3500) / 3500 53 | level++ 54 | exp -= 7000.0 55 | return level + exp / 5000 56 | } 57 | 58 | fun getSkyWarsLevel(xp: Int): Double { 59 | val xps = intArrayOf(0, 20, 70, 150, 250, 500, 1000, 2000, 3500, 6000, 10000, 15000) 60 | val exp = xp.toDouble() 61 | if (exp >= 15000) { 62 | return (exp - 15000) / 10000 + 12 63 | } else { 64 | for (i in xps.indices) { 65 | if (exp < xps[i]) { 66 | return i + (exp - xps[i - 1]) / (xps[i] - xps[i - 1]) 67 | } 68 | } 69 | } 70 | return 1.0 71 | } 72 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/hypixel/HypixelApiUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS PART OF lgz-bot PROJECT 3 | * 4 | * You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application. 5 | * Your modified application must also be licensed under the AGPLv3. 6 | * 7 | * Copyright (c) 2022 - now Guimc Team. 8 | */ 9 | 10 | package ltd.guimc.lgzbot.utils.hypixel 11 | 12 | import ltd.guimc.lgzbot.files.Config 13 | import org.json.JSONObject 14 | 15 | object HypixelApiUtils { 16 | fun request(url: String): JSONObject { 17 | val connection = java.net.URL("https://api.hypixel.net$url").openConnection() as java.net.HttpURLConnection 18 | connection.requestMethod = "GET" 19 | connection.setRequestProperty("API-Key", Config.hypixelApiKey) 20 | connection.connect() 21 | // 转换到json对象 22 | val raw = connection.inputStream.bufferedReader().readText() 23 | val jsonObject = JSONObject(raw) 24 | if (!jsonObject.getBoolean("success")) { 25 | throw ApiFailedException("Request failed with message \"${jsonObject.getString("cause")}\"") 26 | } 27 | return jsonObject 28 | } 29 | 30 | fun resolveRank(rank: String): String? { 31 | return when (rank) { 32 | "ADMIN" -> "Admin" 33 | "MODERATOR" -> "Moderator" 34 | "HELPER" -> "Helper" 35 | "SUPERSTAR" -> "MVP++" 36 | "MVP_PLUS" -> "MVP+" 37 | "MVP" -> "MVP" 38 | "VIP_PLUS" -> "VIP+" 39 | "VIP" -> "VIP" 40 | else -> null 41 | } 42 | } 43 | 44 | fun resolveGameType(gametype: String): String { 45 | return when (gametype) { 46 | "QUAKECRAFT" -> "Quake" 47 | "WALLS" -> "Walls" 48 | "PAINTBALL" -> "Paintball" 49 | "SURVIVAL_GAMES " -> "Blitz Survival Games" 50 | "TNTGAMES" -> "TNT Games" 51 | "VAMPIREZ" -> "VampireZ" 52 | "WALLS3" -> "Mega Walls" 53 | "ARCADE" -> "Arcade" 54 | "ARENA" -> "Arena" 55 | "UHC" -> "UHC" 56 | "MCGO" -> "Cops and Crims" 57 | "BATTLEGROUND" -> "Warlords" 58 | "SUPER_SMASH" -> "Smash Heroes" 59 | "GINGERBREAD" -> "Turbo Kart Racers" 60 | "HOUSING" -> "Housing" 61 | "SKYWARS" -> "SkyWars" 62 | "TRUE_COMBAT " -> "Crazy Walls" 63 | "SPEED_UHC" -> "Speed UHC" 64 | "SKYCLASH" -> "SkyClash" 65 | "LEGACY" -> "Classic Games" 66 | "PROTOTYPE" -> "Prototype" 67 | "BEDWARS" -> "Bed Wars" 68 | "MURDER_MYSTERY " -> "Murder Mystery" 69 | "BUILD_BATTLE " -> "Build Battle" 70 | "DUELS" -> "Duels" 71 | "SKYBLOCK" -> "SkyBlock" 72 | "PIT" -> "Pit" 73 | "REPLAY" -> "Replay" 74 | "SMP" -> "SMP" 75 | "WOOL_GAMES" -> "Wool Wars" 76 | else -> gametype 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/timer/MSTimer.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.utils.timer 2 | 3 | import kotlin.properties.Delegates 4 | 5 | class MSTimer() { 6 | private var initTime by Delegates.notNull() 7 | 8 | init { 9 | this.initTime = System.currentTimeMillis() 10 | } 11 | 12 | fun isTimePressed(ms: Long): Boolean { 13 | return System.currentTimeMillis() - ms >= this.initTime 14 | } 15 | 16 | fun reset() { 17 | this.initTime = System.currentTimeMillis() 18 | } 19 | 20 | fun hasTimePassed(): Long { 21 | return System.currentTimeMillis() - this.initTime 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/utils/webhook/SecurityUtils.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.utils.webhook 2 | 3 | import ltd.guimc.lgzbot.PluginMain.logger 4 | import java.nio.charset.StandardCharsets 5 | import javax.crypto.Mac 6 | import javax.crypto.spec.SecretKeySpec 7 | 8 | object SecurityUtils { 9 | fun calcSignature(data: String, sharedSecret: String): ByteArray? { 10 | try { 11 | val hmac: Mac = Mac.getInstance("HmacSHA256") 12 | hmac.init(SecretKeySpec(sharedSecret.toByteArray(StandardCharsets.UTF_8), "HmacSHA256")) 13 | return hmac.doFinal(data.toByteArray(StandardCharsets.UTF_8)) 14 | 15 | } catch (e: Exception) { 16 | logger.error("verifySignature had the following error: ", e) 17 | } 18 | return null 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/webhook/GithubWebHookReciver.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.webhook 2 | 3 | import io.ktor.server.request.* 4 | import ltd.guimc.lgzbot.files.GithubWebhookSubData 5 | import ltd.guimc.lgzbot.utils.RegexUtils 6 | import ltd.guimc.lgzbot.utils.webhook.SecurityUtils.calcSignature 7 | import net.mamoe.mirai.Bot 8 | import net.mamoe.mirai.Mirai 9 | import net.mamoe.mirai.console.util.ConsoleExperimentalApi 10 | import net.mamoe.mirai.console.util.ContactUtils.getFriendOrGroup 11 | import net.mamoe.mirai.contact.BotIsBeingMutedException 12 | import org.json.JSONArray 13 | import org.json.JSONObject 14 | 15 | class GithubWebHookReciver { 16 | @OptIn(ExperimentalStdlibApi::class) 17 | fun validate(payload: String, secret: String, request: ApplicationRequest): Boolean { 18 | val signature = request.headers["X-Hub-Signature-256"] 19 | return "sha256=" + calcSignature(payload, secret)?.toHexString() == signature 20 | } 21 | 22 | suspend fun onPush(event: JSONObject) { 23 | val repo = event.getJSONObject("repository").getString("full_name") 24 | event.getJSONArray("commits").forEach { rawcommit -> 25 | val commit = rawcommit as JSONObject 26 | val author = commit.getJSONObject("author") 27 | val ref = event.getString("ref") 28 | val addedLength = try { commit.getJSONArray("added").length() } catch (_: Throwable) { 0 } 29 | val removedLength = try { commit.getJSONArray("removed").length() } catch (_: Throwable) { 0 } 30 | val modifiedLength = try { commit.getJSONArray("modified").length() } catch (_: Throwable) { 0 } 31 | 32 | if (ref.startsWith("refs/tags")) return@forEach // Filter Releases 33 | if (RegexUtils.matchRegex("Merge remote-tracking branch '.*'", commit.getString("message")) != null) return@forEach 34 | 35 | GithubWebhookSubData.sub.keys.forEach { botid -> 36 | try { 37 | val bot = Bot.getInstance(botid) 38 | GithubWebhookSubData.sub[botid]!![repo]?.forEach { 39 | try { 40 | bot.getGroup(it)!!.sendMessage( 41 | """[GitHub WebHook] 42 | || New Commit to repo $repo 43 | || Author: ${if (GithubWebhookSubData.ignore[bot.id]?.get(repo)?.indexOf(author.getString("username")) == -1) "${author.getString("name")} (${author.getString("email")})" else ""} 44 | || Branch: ${RegexUtils.checkRisk(ref)} 45 | || ++$addedLength --$removedLength **$modifiedLength 46 | || Commit Message: ${RegexUtils.checkRisk(commit.getString("message"))} 47 | || Details: ${commit.getString("url")}""".trimMargin() 48 | ) 49 | } catch (_: Throwable) { 50 | } 51 | } 52 | } catch (_: Throwable) {} 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/kotlin/ltd/guimc/lgzbot/webhook/WebHookService.kt: -------------------------------------------------------------------------------- 1 | package ltd.guimc.lgzbot.webhook 2 | 3 | import io.ktor.http.* 4 | import io.ktor.server.application.* 5 | import io.ktor.server.engine.* 6 | import io.ktor.server.netty.* 7 | import io.ktor.server.request.* 8 | import io.ktor.server.response.* 9 | import io.ktor.server.routing.* 10 | import kotlinx.coroutines.Dispatchers 11 | import kotlinx.coroutines.withContext 12 | import ltd.guimc.lgzbot.PluginMain 13 | import ltd.guimc.lgzbot.files.Config 14 | import org.json.JSONObject 15 | 16 | class WebHookService(githubWebHookReciver: GithubWebHookReciver) { 17 | private var server: ApplicationEngine 18 | private val errMsg = "\u50BB\u903C\u50BB\u903C\u64CD\u4F60\u5988\uFF0C\u4F60\u5988\u5927\u903C\u4EBA\u4EBA" + 19 | "\u63D2\uFF0C\u5DE6\u63D2\u63D2\u6211\u53F3\u63D2\u63D2\uFF0C\u63D2\u7684\u4F60\u5988\u903C\u5F00\u82B1" 20 | 21 | init { 22 | server = embeddedServer(Netty, port = Config.githubWebhookPort) { 23 | routing { 24 | post("/") { 25 | val headers = this.context.request.headers 26 | if (headers["Content-Type"] != "application/json") { 27 | PluginMain.logger.error("Content-Type must be \"application/json\"") 28 | return@post call.respondText(errMsg, status = HttpStatusCode.BadGateway) 29 | } 30 | if (headers["User-Agent"] == null || !headers["User-Agent"]!!.startsWith("GitHub-Hookshot/")) { 31 | PluginMain.logger.warning("Received wrong user-agent") 32 | return@post call.respondText(errMsg, status = HttpStatusCode.BadGateway) 33 | } 34 | 35 | val payload = withContext(Dispatchers.IO) { 36 | call.receiveStream().bufferedReader(charset = Charsets.UTF_8).readText() 37 | } 38 | 39 | if (!githubWebHookReciver.validate(payload, Config.githubWebhookSecret, this.context.request)) { 40 | PluginMain.logger.warning("Failed to validate request!") 41 | } 42 | 43 | val jsonObject = JSONObject(payload) 44 | 45 | when(headers["X-GitHub-Event"]) { 46 | "push" -> { 47 | githubWebHookReciver.onPush(jsonObject) 48 | } 49 | } 50 | 51 | return@post call.respondText("Accepted", status = HttpStatusCode.Accepted) 52 | } 53 | } 54 | } 55 | } 56 | 57 | fun start() { 58 | server.start(wait = false) 59 | } 60 | 61 | fun stop() { 62 | server.stop() 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin: -------------------------------------------------------------------------------- 1 | ltd.guimc.lgzbot.PluginMain -------------------------------------------------------------------------------- /src/main/resources/Nailong.onnx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BakaBotTeam/lgz-bot/89a80c0e7126e4b58b383ebd7cc0cac33af274f8/src/main/resources/Nailong.onnx -------------------------------------------------------------------------------- /src/main/resources/regex.txt: -------------------------------------------------------------------------------- 1 | 定制水影 2 | 加群.*[0-9]{5,10} 3 | .*公益 4 | .*内部 5 | 绕更新 6 | 开端 7 | 不封号 8 | 外部 9 | .*toolbox 10 | 绕过(盒子)(vape)检测 11 | 内部 12 | .*公益 13 | 禁商 14 | 盒子更新后 15 | 小号机 16 | 群号[0-9]{5,10} 17 | \d{2,4}红利项目 18 | 躺赚 19 | 咨询(\+) 20 | 捡钱(模式) 21 | (个人)创业 22 | 带价私聊 23 | 出.*号 24 | 君羊.*[0-9]{5,10} 25 | .*小卖铺 26 | 稳定奔放 27 | 小卖铺 28 | 老婆不在家.*(刺激) 29 | 代购.* 30 | 收.*带价 31 | 恢复原价 32 | -------------------------------------------------------------------------------- /src/main/resources/self-protect.txt: -------------------------------------------------------------------------------- 1 | 8964 2 | 19890604 3 | 198964 4 | 坦克人 5 | 两百斤麦子 6 | 十里山路 7 | 习死亡了 8 | 八九事件最大胜利者江泽民 9 | 阻挡约18辆59式坦克车队行进 10 | 天安门大屠杀 11 | 六四真相 12 | 革命暴乱 13 | 全境示威运动 14 | 太子党关系网络 15 | 权贵家族 16 | 戒严清场 17 | 胡耀邦逝世 18 | 侮包 19 | 满脸喷粪 20 | 打倒李鹏 21 | 打倒罗干 22 | 打倒中共 23 | 打倒朱镕 24 | 抵制李鹏 25 | 抵制罗干 26 | 抵制中共 27 | 煽动不明 28 | 煽动群众 29 | 烧公安局 30 | 强权政府 31 | 习包子 32 | 习馒头 33 | 习玉米 34 | 习土豆 35 | 习包 36 | 习平 37 | 习登 38 | 习禁评 39 | 习明泽 40 | 新疆骚乱 41 | 网警受贿案 42 | 政府腐败 43 | 台湾共和国 44 | 台湾国 45 | 中国禁闻 46 | 连任 47 | 胡锦涛 48 | 强力施政 49 | 反共 50 | 退党 51 | 美国之音 52 | 老人政治 53 | 老人干政 54 | 习禁平 55 | 习特勒 56 | 习皇 57 | 庆丰包子 58 | 十里山路不换肩 59 | 包姐夫 60 | 因言获罪 61 | 自由门 62 | 学潮 63 | 右派 64 | 反革命 65 | 公审后被杀害 66 | 反革命活动 67 | 法轮大法 68 | 佛展千手法 69 | 法轮桩法 70 | 贯通两极法 71 | 法轮周天法 72 | 神通加持法 73 | 李洪志 74 | 北京当局 75 | 台湾共和国 76 | 活体器官摘取 77 | 活体摘取良心犯的器官 78 | 强行活摘囚犯器官 79 | 天安门自焚 80 | 暴力威胁 81 | 暴力袭击 82 | 梁家河 83 | 为境外提供情报 84 | 海军基地 85 | 禁拍区 86 | 行销独裁 87 | 独裁国 88 | 外国记者被拘留 89 | 执政党 90 | 民进党 91 | 六四天安门事件 92 | 只身阻挡 93 | 王维林 94 | 杰夫·怀德纳 95 | 螳臂挡车的歹徒 96 | 无名的抗议者 97 | 无名的反抗者 98 | 中国政府禁止 99 | 无界浏览 100 | 德国之声 101 | 自由亚洲 102 | 香港独立 103 | 台湾独立 104 | 新疆独立 105 | 西藏独立 106 | 温家宝 107 | 极权国家 108 | 高压政策 109 | 反革命暴乱 110 | 官倒 111 | 天安门轮流蹲 112 | 吾尔开希 113 | 柴玲 114 | 天安门轮流蹲 115 | 用枪扫射 116 | 种族灭绝 117 | 再教育营 118 | 新疆棉花 119 | 新疆人权 120 | 太子党关系网络 121 | 习仲勋 122 | 柯玲玲 123 | 柯华 124 | 彭丽媛 125 | 梁治中 126 | 习正宁 127 | 张澜澜 128 | 习桥桥 129 | 邓家贵 130 | 张燕南 131 | 习安安 132 | 吴龙 133 | 吴雅凝 134 | danielfoa 135 | 习仲恺 136 | 赵紫阳 137 | 遭受软禁 138 | 左倾运动 139 | 免去党内外一切领导职务 140 | 失势下台 141 | 六四事件 142 | 八九民运 143 | 枪口不能对准中国人民 144 | 上街游行 145 | 同学们,我们来得太晚了。 146 | 实施武力清场 147 | 戒严令 148 | 打、砸、抢、烧、冲 149 | 被免去 150 | 要求解除软禁 151 | 天安门母亲 152 | 香港政府 153 | 台湾政府 154 | 中华民国 155 | 维基解密 156 | 炎黄春秋 157 | 政治风波 158 | 北京这一夜 159 | 赵紫阳软禁中的谈话 160 | 李鹏六四日记 161 | 六四诗集 162 | 政治冷漠 163 | 坦克清场 164 | 反送中 165 | 二二八事件 166 | 台湾白色恐怖时期 167 | 白色恐怖 168 | 独裁 169 | 威权统治 170 | 雨伞革命 171 | 九评共产党 172 | 阻挡坦克 173 | 习连任 174 | 真善忍 175 | 共惨党 176 | 共匪 177 | 支那 178 | 屠支 179 | 灭共 180 | 南京大屠杀杀得好 181 | 推翻政府 182 | 出现暴动 183 | 发生暴动 184 | 引起暴动 185 | 明慧网 186 | 政治清洗 187 | 习猪头下位 188 | 世维会 189 | 安拉 190 | 白话佛法 191 | 黄祸 192 | 天藏 193 | 法西斯 194 | 右翼 195 | cnd刊物和论坛 196 | 东方日报 197 | 内幕 198 | 美国之音 199 | 法广中文网 200 | 明镜网 201 | 大事件 202 | 北京之春 203 | 中央社 204 | 倍可亲 205 | 大纪元网 206 | 阿波罗新闻网 207 | 看中国专栏 208 | 万维读者网 209 | 零八宪章 210 | 华尔街日报 211 | 法广新闻网 212 | 中国密报 213 | 民主中国 214 | 多维新闻网 215 | 万维博客 216 | 太陽報 217 | 东网 218 | 世界日报 219 | 法广网 220 | 希望之声 221 | 世界新闻网 222 | 阿波罗网 223 | 内幕第期 224 | 多维网 225 | 新纪元周刊期 226 | 中国时报 227 | 新唐人电视台网 228 | 联合早报 229 | 星岛环球网 230 | 博讯网 231 | 金融时报 232 | 南早中文网 233 | 新史记 234 | 金山桥 235 | 牛泪 236 | 德国之声中文网 237 | 信报月刊 238 | 中国人权双周刊 239 | 明星新聞網 240 | 西藏之声 241 | 开放网 242 | 博谈网 243 | 观察者网 244 | 路透社 245 | 香港经济日报 246 | 新纪元 247 | 纵览中国 248 | 爱思想 249 | 明镜新闻 250 | 动向杂志 251 | 争鸣杂志 252 | 茉莉花革命 253 | 英国金融时报 254 | 明镜周刊 255 | 联合新闻 256 | 草榴 257 | 亚洲周刊 258 | 忽然周 259 | 壹周刊 260 | 爽报 261 | 穿越浏览器 262 | 香港报纸 263 | 联合报 264 | 旺报 265 | 中华电视公司 266 | 客家电视台 267 | 原住民族电视台 268 | 壹电视 269 | 澳洲广播电台中文网 270 | 荷兰国际广播电台中文网 271 | engadget中文网 272 | 博客大赛网站 273 | 自由亚洲电台 274 | 自由欧洲电台 275 | 加拿大国际广播电台 276 | 法国国际广播电台 277 | 梵蒂冈广播电台 278 | 梵蒂冈亚洲新闻通讯社 279 | 今日悉尼 280 | 澳大利亚时报澳奇网 281 | 中欧新闻网 282 | 北美中文网 283 | 南洋视界 284 | 华人今日网 285 | 多维新闻 286 | 牛博网 287 | 博讯 288 | 香港独立媒 289 | 媒体公民行动网 290 | 新头壳 291 | 主场新闻 292 | 香港人网 293 | 民间电台 294 | 阳光时务周刊 295 | 开放杂志 296 | 苦劳网 297 | 留园网 298 | 新三才 299 | 希望之声国际广播电台 300 | 新唐人电视台 301 | 大纪元时报 302 | 信报财经新闻 303 | 公教报 304 | 星岛日报消息 305 | 达赖喇嘛的智慧箴言 306 | 辛子陵 307 | 高文谦 308 | 心灵法门白话佛法”系列节目 309 | 世界报纸 310 | 红太阳的陨落 311 | 彭博 312 | 美国有线电视频道 313 | 国际广播的电视节目 314 | 布什 315 | 布莱尔 316 | 小泉 317 | 纯一郎 318 | 萨马兰奇 319 | 六四 320 | 六四运动 321 | 美国之音 322 | 密宗 323 | 民国 324 | 民进党 325 | 民运 326 | 摩门教 327 | 纳粹 328 | 南华早报 329 | 南蛮 330 | 明慧网 331 | 起义 332 | 亲民党 333 | 瘸腿帮 334 | 人民报 335 | 法轮功 336 | 法轮大法 337 | 打倒共产党 338 | 台独万岁 339 | 圣战 340 | 示威 341 | 台独 342 | 台独分子 343 | 台联 344 | 台湾民国 345 | 台湾岛国 346 | 台湾国 347 | 台湾独立 348 | 太子党 349 | 天安门事件 350 | 屠杀 351 | 小泉 352 | 新党 353 | 新疆独立 354 | 新疆分裂 355 | 新疆国 356 | 疆独 357 | 西藏独立 358 | 西藏分裂 359 | 西藏国 360 | 藏独 361 | 藏青会 362 | 藏妇会 363 | 学潮 364 | 学运 365 | 一党专政 366 | 一中一台 367 | 两个中国 368 | 一贯道 369 | 游行 370 | 造反 371 | 真善忍 372 | 政变 373 | 政治反对派 374 | 政治犯 375 | 反党 376 | 反共 377 | 政府 378 | 民主党 379 | 中国之春 380 | 转法轮 381 | 自焚 382 | 共匪 383 | 苏家屯 384 | 基地组织 385 | 塔利班 386 | 东亚病夫 387 | 支那 388 | 高治联 389 | 高自联 390 | 专政 391 | 陆肆事件 392 | 九评 393 | 一党独裁 394 | 内蒙古抗议 395 | 内蒙古.事件 396 | 内蒙古锡林郭勒盟 397 | 伊斯兰国 398 | 一边一国 399 | 两国论 400 | 全能神 401 | 占中 402 | 占领中环 403 | 赤匪 404 | 本拉登 405 | 奥马尔 406 | 柴玲 407 | 达赖 408 | 达赖喇嘛 409 | 江青 410 | 东条英机 411 | 希特勒 412 | 墨索里尼 413 | 冈村秀树 414 | 冈村宁次 415 | 高丽朴 416 | 赵紫阳 417 | 王丹 418 | 沃尔开西 419 | 李洪志 420 | 李大师 421 | 赖昌星 422 | 马加爵 423 | 班禅 424 | 额尔德尼 425 | 山本五十六 426 | 阿扁 427 | 阿扁万岁 428 | 热那亚 429 | 花革命 430 | 中国茉莉花革命 431 | 中国茉莉花运动 432 | 梅花革命 433 | 牡丹花革命 434 | 葵花子政变 435 | 二十日 436 | 东突厥斯坦 437 | 任弼时 438 | 新闻出版总署 439 | 彭丽媛 440 | 出卖中国 441 | 军车进京 442 | 日本帝国 443 | 吴帮国 444 | 无帮国 445 | 瘟家宝 446 | 假庆林 447 | 甲庆林 448 | 习远平 449 | 袭近平 450 | 李磕墙 451 | 贺过墙 452 | 和锅枪 453 | 粥永康 454 | 轴永康 455 | 肘永康 456 | 金三胖 457 | 赵弹 458 | 赵蛋 459 | zhao蛋 460 | zhao弹 461 | 雷鸣登猎枪出售 462 | 出售半自动猎枪 463 | 出售折叠款式枪 464 | 出售下压款式枪 465 | 銷售電擊麻醉槍 466 | 军用录音监听器 467 | 組裝錄音監聽器 468 | 组装录音监听器 469 | 出售磚石冰毒 470 | 三唑綸出售 471 | 手機監聽卡出售 472 | 出售左旋黃麻素 473 | 出售黃麻素 474 | 出售鎂粉 475 | 出售羟基環戊基 476 | 出售邻氯苯晴 477 | 銷售麻黃素 478 | 出售氫化可的松 479 | 出售甲基苯丙 480 | 出售黃體酮 481 | 銷售间苯三酚 482 | 銷售盐酸羟亚胺 483 | k粉製作 484 | 鹽酸羟亞胺合成 485 | 提供美国秃鹰 486 | 美国秃鹰到货 487 | 美国秃鹰订购 488 | 制作台湾秃鹰 489 | 台湾秃鹰图纸 490 | 台湾秃鹰制造 491 | 清除网络负面信息 492 | 网络公关删帖公司qq 493 | 公关删除百度信息 494 | 招聘兼职发帖员 495 | 专业删帖 496 | 水军招聘 497 | 专业代理投票 498 | 专业网上刷票 499 | 公关负面信息处理 500 | 负面新闻信息删除 501 | 专业消除负面信息 502 | 专业负面信息处理 503 | 负面信息删除 504 | 删除负面帖子 505 | 美国秃鹰电话 506 | 制作美国秃鹰 507 | 美国秃鹰技术转让 508 | 台湾秃鹰电话 509 | 台湾秃鹰qq 510 | 台湾秃鹰技术 511 | 枪支买卖 512 | 出售港式左轮 513 | 出售麻醉l枪 514 | 出售单管猎l枪 515 | 出售五连发猎l枪 516 | 出售销售气槍 517 | 销售秃鹰汽枪 518 | 出售秃鹰汽枪 519 | 提供秃鹰汽枪 520 | 供应秃鹰 521 | 秃鹰汽枪到货 522 | 秃鹰汽枪出货 523 | 销售秃鹰 524 | 供应秃鹰汽枪 525 | 秃鹰订购 526 | 销售气枪 527 | 虎头五连发猎槍出售 528 | 气枪电话 529 | 秃鹰汽狗 530 | 小口径步枪电话 531 | 小口径步枪技术转让 532 | 小口径步枪图纸 533 | 制作小口径步枪 534 | 小口径步枪出货 535 | 提供小口径步枪 536 | 健卫步枪制造图 537 | 各种枪支出售 538 | 出售五连发猎枪 539 | 出售猎l枪 540 | 出售原装猎l枪 541 | 出售军用枪 542 | 出售自制式式 543 | 长期销售各种枪l支 544 | 銷售遙控解碼器 545 | 銷售中控解碼器 546 | 銷售干擾器 547 | 買胡椒基甲基酮 548 | 買賣四氢吡喃酮 549 | 買賣鷹牌獵槍 550 | 出售锡锋枪 551 | 出售健卫步枪 552 | 气动钢珠枪购买 553 | 出售麻黄素 554 | 销售乙醚 555 | 手机监听卡出售 556 | 出售麻果配方 557 | k粉制作 558 | 出售钻石冰毒 559 | 销售邻氯苯晴 560 | 销售羟基环戊基 561 | 销售苯基丙酮 562 | 销售可可精 563 | 买卖盐酸羟亚胺 564 | 盐酸羟亚胺合成 565 | 贩卖king粉 566 | 贩卖摇头丸 567 | 出售海豹m 568 | 德版ppk供应 569 | 散弹枪买卖 570 | 七连发猎枪专卖 571 | 出售突击步枪 572 | 出售铵梯炸药 573 | 出售乳化炸药 574 | 出售双管平式枪 575 | 出售水胶炸药 576 | 出售健卫步枪 577 | 制造缅古合成 578 | 销售甲基苯丙 579 | 销售左旋麻黄素 580 | 销售天然咖啡因 581 | 买卖眼镜蛇弩 582 | 出售追风 583 | 出售小飞虎 584 | 出售小飞狼 585 | 出售森林之豹 586 | 出售追月 587 | 力斯曼弓弩供应 588 | 出售力思曼弓弩 589 | 出售战神弓弩 590 | 森林之鹰出售 591 | 猎豹弓弩出售 592 | 自製手狗 593 | 自製mm手狗 594 | 自製mm手狗 595 | 銷售售用麻醉槍 596 | 出售c款式枪 597 | 销售唧筒式猎枪 598 | 立式双管枪销售 599 | 璩美凤 600 | 中国猪 601 | 江猪媳 602 | 尼泊尔军刀专卖 603 | 军用狙击弩专卖 604 | 军用弩专卖 605 | 上门小姐服务qq 606 | 出售三唑仑 607 | 迷情藥制作方法 608 | 代办驾驶证 609 | 专业出售各种迷药 610 | 兼职女上门qq 611 | 财税代开发票 612 | 售軍用手枪 613 | 假硬币批发qq 614 | 出售手槍 615 | 真鎗出售 616 | 开建筑发票qq 617 | 出售冰毒qq送货 618 | 手枪qq 619 | 销售纯古 620 | 销售水晶冰 621 | 销售捷克左轮 622 | 销售赵氏弓弩 623 | 销售北朝鲜冰 624 | 销售黄绿牙签 625 | 销售麻古果子 626 | 俄罗斯k出售 627 | 按摩小姐口交服务 628 | 出售迷情藥 629 | 迷药迷情药 630 | 少女迷幻药 631 | 匕首麻醉枪出售 632 | 销售冰古 633 | 少女迷情药水出售 634 | 出售迷幻藥 635 | 出售式.mm 636 | 式军用手枪模型出售 637 | 式军用手枪出售 638 | 销售汽狗 639 | 汽狗支付宝专卖 640 | 出售高压汽枪 641 | 气枪专卖店 642 | 气枪销售 643 | 气狗销售网 644 | 上海工字气枪指定销售点 645 | 工字气枪网 646 | 秦氏弓弩专供 647 | 假驾驶证办理 648 | 钢珠枪 649 | 代办证件 650 | 手机监听软件 651 | 帮人报仇 652 | 出售枪支 653 | 售枪qq 654 | 办证刻章 655 | 开发票公司 656 | 气枪子弹专卖店 657 | 卖手枪 658 | 友情气枪专卖网 659 | 出售式麻狗 660 | 阳江刀具网 661 | 上海牌工字气枪专卖网 662 | 假护照办理 663 | 偷窥视频 664 | 枪支弹药 665 | 銷售步qiang 666 | 步qiang銷售 667 | 猎qiang销售 668 | 出售麻醉qiang 669 | 麻醉qiang銷售 670 | 銷售麻醉qiang 671 | 激情聊天室 672 | 哪里有猎qiang卖 673 | 銷售獵qiang 674 | 出售猎qiang 675 | 猎qiang出售 676 | 出售健卫步枪 677 | 销售中控解码器 678 | 出售遥控解码器 679 | 买卖汽车拦截器 680 | 供应四氢吡喃酮 681 | 供应青蒿素 682 | 供应无水咖啡因 683 | 供应咖啡硷 684 | 供应胡椒基甲基 685 | 供应咖啡碱 686 | 供应植物冰 687 | 供应化学冰 688 | 供应天然咖啡因 689 | 甲基苯丙胺制作 690 | 甲基苯丙胺合成 691 | 甲基苯丙胺化学 692 | 甲基苯丙胺结构 693 | 供应盐酸氯 694 | 供应可可精 695 | 供应麦角醇 696 | 供应氢化可的松 697 | 供应黄体酮 698 | 供应冰糖 699 | 供应白冰 700 | 供应黄冰 701 | 供应甲基可可碱 702 | 出售单发麻醉枪 703 | 销售电击麻醉枪 704 | 出售仿真气枪 705 | 销售仿真气枪 706 | 美国秃鹰制造图 707 | 美国秃鹰qq 708 | 上海气枪销售网 709 | 瓦斯手狗电话 710 | 瓦斯手狗订购 711 | 瓦斯手狗出货 712 | 瓦斯手狗到货 713 | 三箭牌汽枪图纸 714 | 三箭牌汽枪转让 715 | 出售瓦斯手狗 716 | 瓦斯手狗图纸 717 | 提供瓦斯手狗 718 | 提供台湾秃鹰 719 | 供应台湾秃鹰 720 | 出售台湾秃鹰 721 | 进口气枪订购 722 | 进口气枪电话 723 | 销售台湾秃鹰 724 | 进口气枪出货 725 | 提供进口气枪 726 | 出售进口气枪 727 | 负面新闻删除 728 | 负面新闻删除清理 729 | 博客帖子删除 730 | 删除负面信息 731 | 仿真气枪qq 732 | 销售进口气枪 733 | 供应进口气枪 734 | 进口气枪到货 735 | 出售假发票qq 736 | 虎头猎枪出货 737 | 虎头猎枪电话 738 | 制作虎头猎枪 739 | 买卖猎枪子弹 740 | 买卖黄麻素 741 | 双管猎槍买卖 742 | 出售冰毒 743 | 办理警察证 744 | 供应美国秃鹰 745 | 台湾秃鹰订购 746 | 台湾秃鹰出货 747 | 台湾秃鹰到货 748 | 进口气枪技术转让 749 | 提供三箭牌汽枪 750 | 三箭牌汽枪到货 751 | 三箭牌汽枪出货 752 | 三箭牌汽枪订购 753 | 三箭牌汽枪电话 754 | 仿真气枪制造图纸 755 | 仿真气枪技术转让 756 | 制作进口气枪 757 | 进口气枪制造图 758 | 销售三箭牌汽枪 759 | 出售三箭牌汽枪 760 | 供应三箭牌汽枪 761 | 进口气枪qq 762 | 求购三唑仑片 763 | 出售三唑仑片 764 | 购蒙汗药 765 | 三唑仑片价格 766 | 迷魂药配方 767 | 出售假币联系电话 768 | 供应盐酸羟亚胺 769 | 假钱出售 770 | 出售迷幻药 771 | 假币买卖 772 | 瓦斯手狗qq 773 | 瓦斯手狗制造 774 | 三箭牌汽枪制造 775 | 销售瓦斯手狗 776 | 出售美国秃鹰 777 | 供应仿真气枪 778 | 提供仿真气枪 779 | 仿真气枪到货 780 | 仿真气枪出货 781 | 仿真气枪订购 782 | 仿真气枪电话 783 | 制作仿真气枪 784 | 销售美国秃鹰 785 | 三箭牌汽枪qq 786 | 供应瓦斯手狗 787 | 甲基苯丙胺制作技术 788 | 手qiang銷售 789 | 销售猎qiang 790 | 上门按摩 791 | 六合彩 792 | 代办各类文凭 793 | A片播放器 794 | 出售黄白牙签 795 | 供应麻果丸子 796 | 供应罂粟壳 797 | 最新一本道 798 | 毒品出售 799 | 高效三唑仑 800 | 猎qiang货到付款 801 | 麻醉qiang出售 802 | 獵qiang貨到付款 803 | 麻醉qiang销售 804 | 獵qiang銷售 805 | 步qiang货到付款, 806 | 秃鹰汽枪订购 807 | 出售军用制式式枪 808 | 出售自制式式枪 809 | 出售进口枪支 810 | 出售原装猎枪 811 | 组装录音监听软件 812 | 出售港式左轮枪 813 | 供应盐羟亚胺 814 | 钢珠槍出售 815 | 秃鹰汽枪电话 816 | 虎头猎枪订购 817 | 供应工字牌汽枪 818 | 出售虎头猎枪 819 | 制作汽狗 820 | 汽狗订购 821 | 秃鹰汽枪qq 822 | 秃鹰汽枪制造图纸 823 | 秃鹰汽枪技术转让 824 | 制作秃鹰汽枪 825 | 提供秃鹰 826 | 秃鹰到货 827 | 秃鹰出货 828 | 秃鹰电话 829 | 秃鹰技术转让 830 | 秃鹰qq 831 | 秃鹰制造图 832 | 制作秃鹰 833 | 提供汽狗 834 | 汽狗电话 835 | 汽狗技术转让 836 | 提供虎头猎枪 837 | 汽狗到货 838 | 虎头双管制造 839 | 虎头猎枪qq 840 | 炸弹制作流程 841 | 出售盐酸曲马多片 842 | 東京熱 843 | 供应甲基苯丙胺 844 | 盐酸氯胺酮批发 845 | 黄牙签出售 846 | 迷奸 847 | 出售工字牌汽枪 848 | 虎头猎枪图纸 849 | 虎头猎枪技术转让 850 | 虎头猎枪制造 851 | 虎头猎枪到货 852 | 工字牌汽枪qq 853 | 工字牌汽枪制造 854 | 工字牌汽枪电话 855 | 工字牌汽枪出货 856 | 提供虎头双管 857 | 虎头双管到货 858 | 虎头双管出货 859 | 虎头双管订购 860 | 虎头双管电话 861 | 制作虎头双管 862 | 虎头双管技术转让 863 | 提供工字牌汽枪 864 | 工字牌汽枪到货 865 | 供应汽狗 866 | 汽狗出货 867 | 汽狗制造图 868 | 汽狗qq 869 | 供应虎头猎枪 870 | 虎头双管qq 871 | 虎头双管图纸 872 | 销售虎头双管 873 | 供应虎头双管 874 | 出售汽狗 875 | 工字牌汽枪订购 876 | 制作工字牌汽枪 877 | 工字牌汽枪转让 878 | 工字牌汽枪图纸 879 | -------------------------------------------------------------------------------- /src/main/resources/serious.txt: -------------------------------------------------------------------------------- 1 | 共产主义 2 | 农民阶级 3 | 工人阶级 4 | 国民革命 5 | 平均地权 6 | 阶级社会 7 | 封建社会 8 | 奴隶制社会 9 | 奴隶社会 10 | 安那其主义 11 | 无政府主义 12 | 无政府国家 13 | 无政府政权 14 | 无政府政党 15 | 执政党 16 | 参政党 17 | 共产革命 18 | 农民革命 19 | 工人革命 20 | 工农阶级 21 | 社会主义 22 | 特色社会主义 23 | 人民民主专政 24 | 人民民主 25 | 西方民主 26 | 代议制制度 27 | 民主化 28 | 一党专政 29 | 国家独裁 30 | 独裁统治 31 | 独裁政权 32 | 独裁国家 33 | 独裁政党 34 | 改革开放 35 | 文化大革命 36 | 文革 37 | 破四旧 38 | 六四运动 39 | 1989年春夏之交 40 | 白纸运动 41 | 新民主主义 42 | 毛主义 43 | 土地革命 44 | 农民革命 45 | 修正主义 46 | 中华民国 47 | 殖民主义 48 | 新殖民主义 49 | 自由主义 50 | 新自由主义 51 | 资本主义 52 | 习近平 53 | 毛泽东 54 | 威权主义 55 | 国家资本主义 56 | 国家主义 57 | 民族资本主义 58 | 资产阶级 59 | 小资产阶级 60 | 中产阶级 61 | 无产阶级 62 | 半无产阶级 63 | 天安门广场事件 64 | 邓小平 65 | 赵紫阳 66 | 中国共产党 67 | 民族国家 68 | 文化型国家 69 | 政变 70 | 法西斯主义 71 | 反对共产党 72 | 资本家 73 | 共和党 74 | 民进党 75 | 国民党 76 | 民主党 77 | 共产党 78 | 台湾独立 79 | 台独 80 | 蒋介石 81 | 孙中山 82 | 人民代表大会 83 | 政治改革 84 | 政治上层建筑 85 | 政治基础 86 | 政治协商 87 | 左派 88 | 右派 89 | 右翼 90 | 左翼 91 | 马列主义 92 | 列宁 93 | 马克思 94 | 马列毛主义 95 | 马主义 96 | 民族主义 97 | 国贼 98 | 人类命运共同体 99 | 斯大林 100 | 斯大林主义 101 | 意识形态 102 | 制度化 103 | 军事侵略 104 | 转型国家 105 | 集权主义 106 | 极权主义 107 | 西方国家 108 | 政治迭代 109 | 平等主义 110 | 发达国家 111 | 发展中国家 112 | 台独 113 | 台独分子 114 | 雷鸣登猎枪出售 115 | 出售半自动猎枪 116 | 出售折叠款式枪 117 | 出售下压款式枪 118 | 銷售電擊麻醉槍 119 | 军用录音监听器 120 | 組裝錄音監聽器 121 | 组装录音监听器 122 | 出售磚石冰毒 123 | 三唑綸出售 124 | 手機監聽卡出售 125 | 出售左旋黃麻素 126 | 出售黃麻素 127 | 出售鎂粉 128 | 出售羟基環戊基 129 | 出售邻氯苯晴 130 | 銷售麻黃素 131 | 出售氫化可的松 132 | 出售甲基苯丙 133 | 出售黃體酮 134 | 銷售间苯三酚 135 | 銷售盐酸羟亚胺 136 | k粉製作 137 | 鹽酸羟亞胺合成 138 | 提供美国秃鹰 139 | 美国秃鹰到货 140 | 美国秃鹰订购 141 | 制作台湾秃鹰 142 | 台湾秃鹰图纸 143 | 台湾秃鹰制造 144 | 清除网络负面信息 145 | 网络公关删帖公司qq 146 | 公关删除百度信息 147 | 招聘兼职发帖员 148 | 专业删帖 149 | 水军招聘 150 | 专业代理投票 151 | 专业网上刷票 152 | 公关负面信息处理 153 | 负面新闻信息删除 154 | 专业消除负面信息 155 | 专业负面信息处理 156 | 负面信息删除 157 | 删除负面帖子 158 | 美国秃鹰电话 159 | 制作美国秃鹰 160 | 美国秃鹰技术转让 161 | 台湾秃鹰电话 162 | 台湾秃鹰qq 163 | 台湾秃鹰技术 164 | 枪支买卖 165 | 出售港式左轮 166 | 出售麻醉l枪 167 | 出售单管猎l枪 168 | 出售五连发猎l枪 169 | 出售销售气槍 170 | 销售秃鹰汽枪 171 | 出售秃鹰汽枪 172 | 提供秃鹰汽枪 173 | 供应秃鹰 174 | 秃鹰汽枪到货 175 | 秃鹰汽枪出货 176 | 销售秃鹰 177 | 供应秃鹰汽枪 178 | 秃鹰订购 179 | 销售气枪 180 | 虎头五连发猎槍出售 181 | 气枪电话 182 | 秃鹰汽狗 183 | 小口径步枪电话 184 | 小口径步枪技术转让 185 | 小口径步枪图纸 186 | 制作小口径步枪 187 | 小口径步枪出货 188 | 提供小口径步枪 189 | 健卫步枪制造图 190 | 各种枪支出售 191 | 出售五连发猎枪 192 | 出售猎l枪 193 | 出售原装猎l枪 194 | 出售军用枪 195 | 出售自制式式 196 | 长期销售各种枪l支 197 | 銷售遙控解碼器 198 | 銷售中控解碼器 199 | 銷售干擾器 200 | 買胡椒基甲基酮 201 | 買賣四氢吡喃酮 202 | 買賣鷹牌獵槍 203 | 出售锡锋枪 204 | 出售健卫步枪 205 | 气动钢珠枪购买 206 | 出售麻黄素 207 | 销售乙醚 208 | 手机监听卡出售 209 | 出售麻果配方 210 | k粉制作 211 | 出售钻石冰毒 212 | 销售邻氯苯晴 213 | 销售羟基环戊基 214 | 销售苯基丙酮 215 | 销售可可精 216 | 买卖盐酸羟亚胺 217 | 盐酸羟亚胺合成 218 | 贩卖king粉 219 | 贩卖摇头丸 220 | 出售海豹m 221 | 德版ppk供应 222 | 散弹枪买卖 223 | 七连发猎枪专卖 224 | 出售突击步枪 225 | 出售铵梯炸药 226 | 出售乳化炸药 227 | 出售双管平式枪 228 | 出售水胶炸药 229 | 出售健卫步枪 230 | 制造缅古合成 231 | 销售甲基苯丙 232 | 销售左旋麻黄素 233 | 销售天然咖啡因 234 | 买卖眼镜蛇弩 235 | 出售追风 236 | 出售小飞虎 237 | 出售小飞狼 238 | 出售森林之豹 239 | 出售追月 240 | 力斯曼弓弩供应 241 | 出售力思曼弓弩 242 | 出售战神弓弩 243 | 森林之鹰出售 244 | 猎豹弓弩出售 245 | 自製手狗 246 | 自製mm手狗 247 | 自製mm手狗 248 | 銷售售用麻醉槍 249 | 出售c款式枪 250 | 销售唧筒式猎枪 251 | 立式双管枪销售 252 | 璩美凤 253 | 中国猪 254 | 江猪媳 255 | 尼泊尔军刀专卖 256 | 军用狙击弩专卖 257 | 军用弩专卖 258 | 上门小姐服务qq 259 | 出售三唑仑 260 | 迷情藥制作方法 261 | 代办驾驶证 262 | 专业出售各种迷药 263 | 兼职女上门qq 264 | 财税代开发票 265 | 售軍用手枪 266 | 假硬币批发qq 267 | 出售手槍 268 | 真鎗出售 269 | 开建筑发票qq 270 | 出售冰毒qq送货 271 | 手枪qq 272 | 销售纯古 273 | 销售水晶冰 274 | 销售捷克左轮 275 | 销售赵氏弓弩 276 | 销售北朝鲜冰 277 | 销售黄绿牙签 278 | 销售麻古果子 279 | 俄罗斯k出售 280 | 按摩小姐口交服务 281 | 出售迷情藥 282 | 迷药迷情药 283 | 少女迷幻药 284 | 匕首麻醉枪出售 285 | 销售冰古 286 | 少女迷情药水出售 287 | 出售迷幻藥 288 | 出售式.mm 289 | 式军用手枪模型出售 290 | 式军用手枪出售 291 | 销售汽狗 292 | 汽狗支付宝专卖 293 | 出售高压汽枪 294 | 气枪专卖店 295 | 气枪销售 296 | 气狗销售网 297 | 上海工字气枪指定销售点 298 | 工字气枪网 299 | 秦氏弓弩专供 300 | 假驾驶证办理 301 | 钢珠枪 302 | 代办证件 303 | 手机监听软件 304 | 帮人报仇 305 | 出售枪支 306 | 售枪qq 307 | 办证刻章 308 | 开发票公司 309 | 气枪子弹专卖店 310 | 卖手枪 311 | 友情气枪专卖网 312 | 出售式麻狗 313 | 阳江刀具网 314 | 上海牌工字气枪专卖网 315 | 假护照办理 316 | 偷窥视频 317 | 枪支弹药 318 | 銷售步qiang 319 | 步qiang銷售 320 | 猎qiang销售 321 | 出售麻醉qiang 322 | 麻醉qiang銷售 323 | 銷售麻醉qiang 324 | 激情聊天室 325 | 哪里有猎qiang卖 326 | 銷售獵qiang 327 | 出售猎qiang 328 | 猎qiang出售 329 | 出售健卫步枪 330 | 销售中控解码器 331 | 出售遥控解码器 332 | 买卖汽车拦截器 333 | 供应四氢吡喃酮 334 | 供应青蒿素 335 | 供应无水咖啡因 336 | 供应咖啡硷 337 | 供应胡椒基甲基 338 | 供应咖啡碱 339 | 供应植物冰 340 | 供应化学冰 341 | 供应天然咖啡因 342 | 甲基苯丙胺制作 343 | 甲基苯丙胺合成 344 | 甲基苯丙胺化学 345 | 甲基苯丙胺结构 346 | 供应盐酸氯 347 | 供应可可精 348 | 供应麦角醇 349 | 供应氢化可的松 350 | 供应黄体酮 351 | 供应冰糖 352 | 供应白冰 353 | 供应黄冰 354 | 供应甲基可可碱 355 | 出售单发麻醉枪 356 | 销售电击麻醉枪 357 | 出售仿真气枪 358 | 销售仿真气枪 359 | 美国秃鹰制造图 360 | 美国秃鹰qq 361 | 上海气枪销售网 362 | 瓦斯手狗电话 363 | 瓦斯手狗订购 364 | 瓦斯手狗出货 365 | 瓦斯手狗到货 366 | 三箭牌汽枪图纸 367 | 三箭牌汽枪转让 368 | 出售瓦斯手狗 369 | 瓦斯手狗图纸 370 | 提供瓦斯手狗 371 | 提供台湾秃鹰 372 | 供应台湾秃鹰 373 | 出售台湾秃鹰 374 | 进口气枪订购 375 | 进口气枪电话 376 | 销售台湾秃鹰 377 | 进口气枪出货 378 | 提供进口气枪 379 | 出售进口气枪 380 | 负面新闻删除 381 | 负面新闻删除清理 382 | 博客帖子删除 383 | 删除负面信息 384 | 仿真气枪qq 385 | 销售进口气枪 386 | 供应进口气枪 387 | 进口气枪到货 388 | 出售假发票qq 389 | 虎头猎枪出货 390 | 虎头猎枪电话 391 | 制作虎头猎枪 392 | 买卖猎枪子弹 393 | 买卖黄麻素 394 | 双管猎槍买卖 395 | 出售冰毒 396 | 办理警察证 397 | 供应美国秃鹰 398 | 台湾秃鹰订购 399 | 台湾秃鹰出货 400 | 台湾秃鹰到货 401 | 进口气枪技术转让 402 | 提供三箭牌汽枪 403 | 三箭牌汽枪到货 404 | 三箭牌汽枪出货 405 | 三箭牌汽枪订购 406 | 三箭牌汽枪电话 407 | 仿真气枪制造图纸 408 | 仿真气枪技术转让 409 | 制作进口气枪 410 | 进口气枪制造图 411 | 销售三箭牌汽枪 412 | 出售三箭牌汽枪 413 | 供应三箭牌汽枪 414 | 进口气枪qq 415 | 求购三唑仑片 416 | 出售三唑仑片 417 | 购蒙汗药 418 | 三唑仑片价格 419 | 迷魂药配方 420 | 出售假币联系电话 421 | 供应盐酸羟亚胺 422 | 假钱出售 423 | 出售迷幻药 424 | 假币买卖 425 | 瓦斯手狗qq 426 | 瓦斯手狗制造 427 | 三箭牌汽枪制造 428 | 销售瓦斯手狗 429 | 出售美国秃鹰 430 | 供应仿真气枪 431 | 提供仿真气枪 432 | 仿真气枪到货 433 | 仿真气枪出货 434 | 仿真气枪订购 435 | 仿真气枪电话 436 | 制作仿真气枪 437 | 销售美国秃鹰 438 | 三箭牌汽枪qq 439 | 供应瓦斯手狗 440 | 甲基苯丙胺制作技术 441 | 手qiang銷售 442 | 销售猎qiang 443 | 上门按摩 444 | 六合彩 445 | 代办各类文凭 446 | A片播放器 447 | 出售黄白牙签 448 | 供应麻果丸子 449 | 供应罂粟壳 450 | 最新一本道 451 | 毒品出售 452 | 高效三唑仑 453 | 猎qiang货到付款 454 | 麻醉qiang出售 455 | 獵qiang貨到付款 456 | 麻醉qiang销售 457 | 獵qiang銷售 458 | 步qiang货到付款, 459 | 秃鹰汽枪订购 460 | 出售军用制式式枪 461 | 出售自制式式枪 462 | 出售进口枪支 463 | 出售原装猎枪 464 | 组装录音监听软件 465 | 出售港式左轮枪 466 | 供应盐羟亚胺 467 | 钢珠槍出售 468 | 秃鹰汽枪电话 469 | 虎头猎枪订购 470 | 供应工字牌汽枪 471 | 出售虎头猎枪 472 | 制作汽狗 473 | 汽狗订购 474 | 秃鹰汽枪qq 475 | 秃鹰汽枪制造图纸 476 | 秃鹰汽枪技术转让 477 | 制作秃鹰汽枪 478 | 提供秃鹰 479 | 秃鹰到货 480 | 秃鹰出货 481 | 秃鹰电话 482 | 秃鹰技术转让 483 | 秃鹰qq 484 | 秃鹰制造图 485 | 制作秃鹰 486 | 提供汽狗 487 | 汽狗电话 488 | 汽狗技术转让 489 | 提供虎头猎枪 490 | 汽狗到货 491 | 虎头双管制造 492 | 虎头猎枪qq 493 | 炸弹制作流程 494 | 出售盐酸曲马多片 495 | 東京熱 496 | 供应甲基苯丙胺 497 | 盐酸氯胺酮批发 498 | 黄牙签出售 499 | 迷奸 500 | 出售工字牌汽枪 501 | 虎头猎枪图纸 502 | 虎头猎枪技术转让 503 | 虎头猎枪制造 504 | 虎头猎枪到货 505 | 工字牌汽枪qq 506 | 工字牌汽枪制造 507 | 工字牌汽枪电话 508 | 工字牌汽枪出货 509 | 提供虎头双管 510 | 虎头双管到货 511 | 虎头双管出货 512 | 虎头双管订购 513 | 虎头双管电话 514 | 制作虎头双管 515 | 虎头双管技术转让 516 | 提供工字牌汽枪 517 | 工字牌汽枪到货 518 | 供应汽狗 519 | 汽狗出货 520 | 汽狗制造图 521 | 汽狗qq 522 | 供应虎头猎枪 523 | 虎头双管qq 524 | 虎头双管图纸 525 | 销售虎头双管 526 | 供应虎头双管 527 | 出售汽狗 528 | 工字牌汽枪订购 529 | 制作工字牌汽枪 530 | 工字牌汽枪转让 531 | 工字牌汽枪图纸 532 | -------------------------------------------------------------------------------- /src/test/java/Test.java: -------------------------------------------------------------------------------- 1 | import ltd.guimc.dlm.DLModel; 2 | 3 | public class Test { 4 | public static void main(String[] args) throws InterruptedException { 5 | DLModel.init(); 6 | } 7 | } 8 | --------------------------------------------------------------------------------