├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src └── main ├── kotlin └── cn │ └── booling │ └── bakahdt │ ├── CommandManager.kt │ ├── Main.kt │ ├── Secret.kt │ ├── TextFields.kt │ ├── Utils.kt │ ├── command │ ├── Command.kt │ └── Permission.kt │ └── service │ ├── CrtVersionChecker.kt │ └── MCMODHandler.kt └── resources ├── commands.json └── permissions.json /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | test 3 | # whitelist 4 | !src/*.kt 5 | !gradle 6 | !build.gradle.kts 7 | !gradle.properties 8 | !settings.gradle.kts 9 | !gradlew 10 | !gradlew.bat 11 | !LICENSE 12 | !README.md 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU AFFERO GENERAL PUBLIC LICENSE 2 | Version 3, 19 November 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU Affero General Public License is a free, copyleft license for 11 | software and other kinds of works, specifically designed to ensure 12 | cooperation with the community in the case of network server software. 13 | 14 | The licenses for most software and other practical works are designed 15 | to take away your freedom to share and change the works. By contrast, 16 | our General Public Licenses are intended to guarantee your freedom to 17 | share and change all versions of a program--to make sure it remains free 18 | software for all its users. 19 | 20 | When we speak of free software, we are referring to freedom, not 21 | price. Our General Public Licenses are designed to make sure that you 22 | have the freedom to distribute copies of free software (and charge for 23 | them if you wish), that you receive source code or can get it if you 24 | want it, that you can change the software or use pieces of it in new 25 | free programs, and that you know you can do these things. 26 | 27 | Developers that use our General Public Licenses protect your rights 28 | with two steps: (1) assert copyright on the software, and (2) offer 29 | you this License which gives you legal permission to copy, distribute 30 | and/or modify the software. 31 | 32 | A secondary benefit of defending all users' freedom is that 33 | improvements made in alternate versions of the program, if they 34 | receive widespread use, become available for other developers to 35 | incorporate. Many developers of free software are heartened and 36 | encouraged by the resulting cooperation. However, in the case of 37 | software used on network servers, this result may fail to come about. 38 | The GNU General Public License permits making a modified version and 39 | letting the public access it on a server without ever releasing its 40 | source code to the public. 41 | 42 | The GNU Affero General Public License is designed specifically to 43 | ensure that, in such cases, the modified source code becomes available 44 | to the community. It requires the operator of a network server to 45 | provide the source code of the modified version running there to the 46 | users of that server. Therefore, public use of a modified version, on 47 | a publicly accessible server, gives the public access to the source 48 | code of the modified version. 49 | 50 | An older license, called the Affero General Public License and 51 | published by Affero, was designed to accomplish similar goals. This is 52 | a different license, not a version of the Affero GPL, but Affero has 53 | released a new version of the Affero GPL which permits relicensing under 54 | this license. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | TERMS AND CONDITIONS 60 | 61 | 0. Definitions. 62 | 63 | "This License" refers to version 3 of the GNU Affero General Public License. 64 | 65 | "Copyright" also means copyright-like laws that apply to other kinds of 66 | works, such as semiconductor masks. 67 | 68 | "The Program" refers to any copyrightable work licensed under this 69 | License. Each licensee is addressed as "you". "Licensees" and 70 | "recipients" may be individuals or organizations. 71 | 72 | To "modify" a work means to copy from or adapt all or part of the work 73 | in a fashion requiring copyright permission, other than the making of an 74 | exact copy. The resulting work is called a "modified version" of the 75 | earlier work or a work "based on" the earlier work. 76 | 77 | A "covered work" means either the unmodified Program or a work based 78 | on the Program. 79 | 80 | To "propagate" a work means to do anything with it that, without 81 | permission, would make you directly or secondarily liable for 82 | infringement under applicable copyright law, except executing it on a 83 | computer or modifying a private copy. Propagation includes copying, 84 | distribution (with or without modification), making available to the 85 | public, and in some countries other activities as well. 86 | 87 | To "convey" a work means any kind of propagation that enables other 88 | parties to make or receive copies. Mere interaction with a user through 89 | a computer network, with no transfer of a copy, is not conveying. 90 | 91 | An interactive user interface displays "Appropriate Legal Notices" 92 | to the extent that it includes a convenient and prominently visible 93 | feature that (1) displays an appropriate copyright notice, and (2) 94 | tells the user that there is no warranty for the work (except to the 95 | extent that warranties are provided), that licensees may convey the 96 | work under this License, and how to view a copy of this License. If 97 | the interface presents a list of user commands or options, such as a 98 | menu, a prominent item in the list meets this criterion. 99 | 100 | 1. Source Code. 101 | 102 | The "source code" for a work means the preferred form of the work 103 | for making modifications to it. "Object code" means any non-source 104 | form of a work. 105 | 106 | A "Standard Interface" means an interface that either is an official 107 | standard defined by a recognized standards body, or, in the case of 108 | interfaces specified for a particular programming language, one that 109 | is widely used among developers working in that language. 110 | 111 | The "System Libraries" of an executable work include anything, other 112 | than the work as a whole, that (a) is included in the normal form of 113 | packaging a Major Component, but which is not part of that Major 114 | Component, and (b) serves only to enable use of the work with that 115 | Major Component, or to implement a Standard Interface for which an 116 | implementation is available to the public in source code form. A 117 | "Major Component", in this context, means a major essential component 118 | (kernel, window system, and so on) of the specific operating system 119 | (if any) on which the executable work runs, or a compiler used to 120 | produce the work, or an object code interpreter used to run it. 121 | 122 | The "Corresponding Source" for a work in object code form means all 123 | the source code needed to generate, install, and (for an executable 124 | work) run the object code and to modify the work, including scripts to 125 | control those activities. However, it does not include the work's 126 | System Libraries, or general-purpose tools or generally available free 127 | programs which are used unmodified in performing those activities but 128 | which are not part of the work. For example, Corresponding Source 129 | includes interface definition files associated with source files for 130 | the work, and the source code for shared libraries and dynamically 131 | linked subprograms that the work is specifically designed to require, 132 | such as by intimate data communication or control flow between those 133 | subprograms and other parts of the work. 134 | 135 | The Corresponding Source need not include anything that users 136 | can regenerate automatically from other parts of the Corresponding 137 | Source. 138 | 139 | The Corresponding Source for a work in source code form is that 140 | same work. 141 | 142 | 2. Basic Permissions. 143 | 144 | All rights granted under this License are granted for the term of 145 | copyright on the Program, and are irrevocable provided the stated 146 | conditions are met. This License explicitly affirms your unlimited 147 | permission to run the unmodified Program. The output from running a 148 | covered work is covered by this License only if the output, given its 149 | content, constitutes a covered work. This License acknowledges your 150 | rights of fair use or other equivalent, as provided by copyright law. 151 | 152 | You may make, run and propagate covered works that you do not 153 | convey, without conditions so long as your license otherwise remains 154 | in force. You may convey covered works to others for the sole purpose 155 | of having them make modifications exclusively for you, or provide you 156 | with facilities for running those works, provided that you comply with 157 | the terms of this License in conveying all material for which you do 158 | not control copyright. Those thus making or running the covered works 159 | for you must do so exclusively on your behalf, under your direction 160 | and control, on terms that prohibit them from making any copies of 161 | your copyrighted material outside their relationship with you. 162 | 163 | Conveying under any other circumstances is permitted solely under 164 | the conditions stated below. Sublicensing is not allowed; section 10 165 | makes it unnecessary. 166 | 167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 168 | 169 | No covered work shall be deemed part of an effective technological 170 | measure under any applicable law fulfilling obligations under article 171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 172 | similar laws prohibiting or restricting circumvention of such 173 | measures. 174 | 175 | When you convey a covered work, you waive any legal power to forbid 176 | circumvention of technological measures to the extent such circumvention 177 | is effected by exercising rights under this License with respect to 178 | the covered work, and you disclaim any intention to limit operation or 179 | modification of the work as a means of enforcing, against the work's 180 | users, your or third parties' legal rights to forbid circumvention of 181 | technological measures. 182 | 183 | 4. Conveying Verbatim Copies. 184 | 185 | You may convey verbatim copies of the Program's source code as you 186 | receive it, in any medium, provided that you conspicuously and 187 | appropriately publish on each copy an appropriate copyright notice; 188 | keep intact all notices stating that this License and any 189 | non-permissive terms added in accord with section 7 apply to the code; 190 | keep intact all notices of the absence of any warranty; and give all 191 | recipients a copy of this License along with the Program. 192 | 193 | You may charge any price or no price for each copy that you convey, 194 | and you may offer support or warranty protection for a fee. 195 | 196 | 5. Conveying Modified Source Versions. 197 | 198 | You may convey a work based on the Program, or the modifications to 199 | produce it from the Program, in the form of source code under the 200 | terms of section 4, provided that you also meet all of these conditions: 201 | 202 | a) The work must carry prominent notices stating that you modified 203 | it, and giving a relevant date. 204 | 205 | b) The work must carry prominent notices stating that it is 206 | released under this License and any conditions added under section 207 | 7. This requirement modifies the requirement in section 4 to 208 | "keep intact all notices". 209 | 210 | c) You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | 218 | d) If the work has interactive user interfaces, each must display 219 | Appropriate Legal Notices; however, if the Program has interactive 220 | interfaces that do not display Appropriate Legal Notices, your 221 | work need not make them do so. 222 | 223 | A compilation of a covered work with other separate and independent 224 | works, which are not by their nature extensions of the covered work, 225 | and which are not combined with it such as to form a larger program, 226 | in or on a volume of a storage or distribution medium, is called an 227 | "aggregate" if the compilation and its resulting copyright are not 228 | used to limit the access or legal rights of the compilation's users 229 | beyond what the individual works permit. Inclusion of a covered work 230 | in an aggregate does not cause this License to apply to the other 231 | parts of the aggregate. 232 | 233 | 6. Conveying Non-Source Forms. 234 | 235 | You may convey a covered work in object code form under the terms 236 | of sections 4 and 5, provided that you also convey the 237 | machine-readable Corresponding Source under the terms of this License, 238 | in one of these ways: 239 | 240 | a) Convey the object code in, or embodied in, a physical product 241 | (including a physical distribution medium), accompanied by the 242 | Corresponding Source fixed on a durable physical medium 243 | customarily used for software interchange. 244 | 245 | b) Convey the object code in, or embodied in, a physical product 246 | (including a physical distribution medium), accompanied by a 247 | written offer, valid for at least three years and valid for as 248 | long as you offer spare parts or customer support for that product 249 | model, to give anyone who possesses the object code either (1) a 250 | copy of the Corresponding Source for all the software in the 251 | product that is covered by this License, on a durable physical 252 | medium customarily used for software interchange, for a price no 253 | more than your reasonable cost of physically performing this 254 | conveying of source, or (2) access to copy the 255 | Corresponding Source from a network server at no charge. 256 | 257 | c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | 263 | d) Convey the object code by offering access from a designated 264 | place (gratis or for a charge), and offer equivalent access to the 265 | Corresponding Source in the same way through the same place at no 266 | further charge. You need not require recipients to copy the 267 | Corresponding Source along with the object code. If the place to 268 | copy the object code is a network server, the Corresponding Source 269 | may be on a different server (operated by you or a third party) 270 | that supports equivalent copying facilities, provided you maintain 271 | clear directions next to the object code saying where to find the 272 | Corresponding Source. Regardless of what server hosts the 273 | Corresponding Source, you remain obligated to ensure that it is 274 | available for as long as needed to satisfy these requirements. 275 | 276 | e) Convey the object code using peer-to-peer transmission, provided 277 | you inform other peers where the object code and Corresponding 278 | Source of the work are being offered to the general public at no 279 | charge under subsection 6d. 280 | 281 | A separable portion of the object code, whose source code is excluded 282 | from the Corresponding Source as a System Library, need not be 283 | included in conveying the object code work. 284 | 285 | A "User Product" is either (1) a "consumer product", which means any 286 | tangible personal property which is normally used for personal, family, 287 | or household purposes, or (2) anything designed or sold for incorporation 288 | into a dwelling. In determining whether a product is a consumer product, 289 | doubtful cases shall be resolved in favor of coverage. For a particular 290 | product received by a particular user, "normally used" refers to a 291 | typical or common use of that class of product, regardless of the status 292 | of the particular user or of the way in which the particular user 293 | actually uses, or expects or is expected to use, the product. A product 294 | is a consumer product regardless of whether the product has substantial 295 | commercial, industrial or non-consumer uses, unless such uses represent 296 | the only significant mode of use of the product. 297 | 298 | "Installation Information" for a User Product means any methods, 299 | procedures, authorization keys, or other information required to install 300 | and execute modified versions of a covered work in that User Product from 301 | a modified version of its Corresponding Source. The information must 302 | suffice to ensure that the continued functioning of the modified object 303 | code is in no case prevented or interfered with solely because 304 | modification has been made. 305 | 306 | If you convey an object code work under this section in, or with, or 307 | specifically for use in, a User Product, and the conveying occurs as 308 | part of a transaction in which the right of possession and use of the 309 | User Product is transferred to the recipient in perpetuity or for a 310 | fixed term (regardless of how the transaction is characterized), the 311 | Corresponding Source conveyed under this section must be accompanied 312 | by the Installation Information. But this requirement does not apply 313 | if neither you nor any third party retains the ability to install 314 | modified object code on the User Product (for example, the work has 315 | been installed in ROM). 316 | 317 | The requirement to provide Installation Information does not include a 318 | requirement to continue to provide support service, warranty, or updates 319 | for a work that has been modified or installed by the recipient, or for 320 | the User Product in which it has been modified or installed. Access to a 321 | network may be denied when the modification itself materially and 322 | adversely affects the operation of the network or violates the rules and 323 | protocols for communication across the network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders of 351 | that material) supplement the terms of this License with terms: 352 | 353 | a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | 356 | b) Requiring preservation of specified reasonable legal notices or 357 | author attributions in that material or in the Appropriate Legal 358 | Notices displayed by works containing it; or 359 | 360 | c) Prohibiting misrepresentation of the origin of that material, or 361 | requiring that modified versions of such material be marked in 362 | reasonable ways as different from the original version; or 363 | 364 | d) Limiting the use for publicity purposes of names of licensors or 365 | authors of the material; or 366 | 367 | e) Declining to grant rights under trademark law for use of some 368 | trade names, trademarks, or service marks; or 369 | 370 | f) Requiring indemnification of licensors and authors of that 371 | material by anyone who conveys the material (or modified versions of 372 | it) with contractual assumptions of liability to the recipient, for 373 | any liability that these contractual assumptions directly impose on 374 | those licensors and authors. 375 | 376 | All other non-permissive additional terms are considered "further 377 | restrictions" within the meaning of section 10. If the Program as you 378 | received it, or any part of it, contains a notice stating that it is 379 | governed by this License along with a term that is a further 380 | restriction, you may remove that term. If a license document contains 381 | a further restriction but permits relicensing or conveying under this 382 | License, you may add to a covered work material governed by the terms 383 | of that license document, provided that the further restriction does 384 | not survive such relicensing or conveying. 385 | 386 | If you add terms to a covered work in accord with this section, you 387 | must place, in the relevant source files, a statement of the 388 | additional terms that apply to those files, or a notice indicating 389 | where to find the applicable terms. 390 | 391 | Additional terms, permissive or non-permissive, may be stated in the 392 | form of a separately written license, or stated as exceptions; 393 | the above requirements apply either way. 394 | 395 | 8. Termination. 396 | 397 | You may not propagate or modify a covered work except as expressly 398 | provided under this License. Any attempt otherwise to propagate or 399 | modify it is void, and will automatically terminate your rights under 400 | this License (including any patent licenses granted under the third 401 | paragraph of section 11). 402 | 403 | However, if you cease all violation of this License, then your 404 | license from a particular copyright holder is reinstated (a) 405 | provisionally, unless and until the copyright holder explicitly and 406 | finally terminates your license, and (b) permanently, if the copyright 407 | holder fails to notify you of the violation by some reasonable means 408 | prior to 60 days after the cessation. 409 | 410 | Moreover, your license from a particular copyright holder is 411 | reinstated permanently if the copyright holder notifies you of the 412 | violation by some reasonable means, this is the first time you have 413 | received notice of violation of this License (for any work) from that 414 | copyright holder, and you cure the violation prior to 30 days after 415 | your receipt of the notice. 416 | 417 | Termination of your rights under this section does not terminate the 418 | licenses of parties who have received copies or rights from you under 419 | this License. If your rights have been terminated and not permanently 420 | reinstated, you do not qualify to receive new licenses for the same 421 | material under section 10. 422 | 423 | 9. Acceptance Not Required for Having Copies. 424 | 425 | You are not required to accept this License in order to receive or 426 | run a copy of the Program. Ancillary propagation of a covered work 427 | occurring solely as a consequence of using peer-to-peer transmission 428 | to receive a copy likewise does not require acceptance. However, 429 | nothing other than this License grants you permission to propagate or 430 | modify any covered work. These actions infringe copyright if you do 431 | not accept this License. Therefore, by modifying or propagating a 432 | covered work, you indicate your acceptance of this License to do so. 433 | 434 | 10. Automatic Licensing of Downstream Recipients. 435 | 436 | Each time you convey a covered work, the recipient automatically 437 | receives a license from the original licensors, to run, modify and 438 | propagate that work, subject to this License. You are not responsible 439 | for enforcing compliance by third parties with this License. 440 | 441 | An "entity transaction" is a transaction transferring control of an 442 | organization, or substantially all assets of one, or subdividing an 443 | organization, or merging organizations. If propagation of a covered 444 | work results from an entity transaction, each party to that 445 | transaction who receives a copy of the work also receives whatever 446 | licenses to the work the party's predecessor in interest had or could 447 | give under the previous paragraph, plus a right to possession of the 448 | Corresponding Source of the work from the predecessor in interest, if 449 | the predecessor has it or can get it with reasonable efforts. 450 | 451 | You may not impose any further restrictions on the exercise of the 452 | rights granted or affirmed under this License. For example, you may 453 | not impose a license fee, royalty, or other charge for exercise of 454 | rights granted under this License, and you may not initiate litigation 455 | (including a cross-claim or counterclaim in a lawsuit) alleging that 456 | any patent claim is infringed by making, using, selling, offering for 457 | sale, or importing the Program or any portion of it. 458 | 459 | 11. Patents. 460 | 461 | A "contributor" is a copyright holder who authorizes use under this 462 | License of the Program or a work on which the Program is based. The 463 | work thus licensed is called the contributor's "contributor version". 464 | 465 | A contributor's "essential patent claims" are all patent claims 466 | owned or controlled by the contributor, whether already acquired or 467 | hereafter acquired, that would be infringed by some manner, permitted 468 | by this License, of making, using, or selling its contributor version, 469 | but do not include claims that would be infringed only as a 470 | consequence of further modification of the contributor version. For 471 | purposes of this definition, "control" includes the right to grant 472 | patent sublicenses in a manner consistent with the requirements of 473 | this License. 474 | 475 | Each contributor grants you a non-exclusive, worldwide, royalty-free 476 | patent license under the contributor's essential patent claims, to 477 | make, use, sell, offer for sale, import and otherwise run, modify and 478 | propagate the contents of its contributor version. 479 | 480 | In the following three paragraphs, a "patent license" is any express 481 | agreement or commitment, however denominated, not to enforce a patent 482 | (such as an express permission to practice a patent or covenant not to 483 | sue for patent infringement). To "grant" such a patent license to a 484 | party means to make such an agreement or commitment not to enforce a 485 | patent against the party. 486 | 487 | If you convey a covered work, knowingly relying on a patent license, 488 | and the Corresponding Source of the work is not available for anyone 489 | to copy, free of charge and under the terms of this License, through a 490 | publicly available network server or other readily accessible means, 491 | then you must either (1) cause the Corresponding Source to be so 492 | available, or (2) arrange to deprive yourself of the benefit of the 493 | patent license for this particular work, or (3) arrange, in a manner 494 | consistent with the requirements of this License, to extend the patent 495 | license to downstream recipients. "Knowingly relying" means you have 496 | actual knowledge that, but for the patent license, your conveying the 497 | covered work in a country, or your recipient's use of the covered work 498 | in a country, would infringe one or more identifiable patents in that 499 | country that you have reason to believe are valid. 500 | 501 | If, pursuant to or in connection with a single transaction or 502 | arrangement, you convey, or propagate by procuring conveyance of, a 503 | covered work, and grant a patent license to some of the parties 504 | receiving the covered work authorizing them to use, propagate, modify 505 | or convey a specific copy of the covered work, then the patent license 506 | you grant is automatically extended to all recipients of the covered 507 | work and works based on it. 508 | 509 | A patent license is "discriminatory" if it does not include within 510 | the scope of its coverage, prohibits the exercise of, or is 511 | conditioned on the non-exercise of one or more of the rights that are 512 | specifically granted under this License. You may not convey a covered 513 | work if you are a party to an arrangement with a third party that is 514 | in the business of distributing software, under which you make payment 515 | to the third party based on the extent of your activity of conveying 516 | the work, and under which the third party grants, to any of the 517 | parties who would receive the covered work from you, a discriminatory 518 | patent license (a) in connection with copies of the covered work 519 | conveyed by you (or copies made from those copies), or (b) primarily 520 | for and in connection with specific products or compilations that 521 | contain the covered work, unless you entered into that arrangement, 522 | or that patent license was granted, prior to 28 March 2007. 523 | 524 | Nothing in this License shall be construed as excluding or limiting 525 | any implied license or other defenses to infringement that may 526 | otherwise be available to you under applicable patent law. 527 | 528 | 12. No Surrender of Others' Freedom. 529 | 530 | If conditions are imposed on you (whether by court order, agreement or 531 | otherwise) that contradict the conditions of this License, they do not 532 | excuse you from the conditions of this License. If you cannot convey a 533 | covered work so as to satisfy simultaneously your obligations under this 534 | License and any other pertinent obligations, then as a consequence you may 535 | not convey it at all. For example, if you agree to terms that obligate you 536 | to collect a royalty for further conveying from those to whom you convey 537 | the Program, the only way you could satisfy both those terms and this 538 | License would be to refrain entirely from conveying the Program. 539 | 540 | 13. Remote Network Interaction; Use with the GNU General Public License. 541 | 542 | Notwithstanding any other provision of this License, if you modify the 543 | Program, your modified version must prominently offer all users 544 | interacting with it remotely through a computer network (if your version 545 | supports such interaction) an opportunity to receive the Corresponding 546 | Source of your version by providing access to the Corresponding Source 547 | from a network server at no charge, through some standard or customary 548 | means of facilitating copying of software. This Corresponding Source 549 | shall include the Corresponding Source for any work covered by version 3 550 | of the GNU General Public License that is incorporated pursuant to the 551 | following paragraph. 552 | 553 | Notwithstanding any other provision of this License, you have 554 | permission to link or combine any covered work with a work licensed 555 | under version 3 of the GNU General Public License into a single 556 | combined work, and to convey the resulting work. The terms of this 557 | License will continue to apply to the part which is the covered work, 558 | but the work with which it is combined will remain governed by version 559 | 3 of the GNU General Public License. 560 | 561 | 14. Revised Versions of this License. 562 | 563 | The Free Software Foundation may publish revised and/or new versions of 564 | the GNU Affero General Public License from time to time. Such new versions 565 | will be similar in spirit to the present version, but may differ in detail to 566 | address new problems or concerns. 567 | 568 | Each version is given a distinguishing version number. If the 569 | Program specifies that a certain numbered version of the GNU Affero General 570 | Public License "or any later version" applies to it, you have the 571 | option of following the terms and conditions either of that numbered 572 | version or of any later version published by the Free Software 573 | Foundation. If the Program does not specify a version number of the 574 | GNU Affero General Public License, you may choose any version ever published 575 | by the Free Software Foundation. 576 | 577 | If the Program specifies that a proxy can decide which future 578 | versions of the GNU Affero General Public License can be used, that proxy's 579 | public statement of acceptance of a version permanently authorizes you 580 | to choose that version for the Program. 581 | 582 | Later license versions may give you additional or different 583 | permissions. However, no additional obligations are imposed on any 584 | author or copyright holder as a result of your choosing to follow a 585 | later version. 586 | 587 | 15. Disclaimer of Warranty. 588 | 589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 597 | 598 | 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 608 | SUCH DAMAGES. 609 | 610 | 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these terms. 626 | 627 | To do so, attach the following notices to the program. It is safest 628 | to attach them to the start of each source file to most effectively 629 | state the exclusion of warranty; and each file should have at least 630 | the "copyright" line and a pointer to where the full notice is found. 631 | 632 | 633 | Copyright (C) 634 | 635 | This program is free software: you can redistribute it and/or modify 636 | it under the terms of the GNU Affero General Public License as published by 637 | the Free Software Foundation, either version 3 of the License, or 638 | (at your option) any later version. 639 | 640 | This program is distributed in the hope that it will be useful, 641 | but WITHOUT ANY WARRANTY; without even the implied warranty of 642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 643 | GNU Affero General Public License for more details. 644 | 645 | You should have received a copy of the GNU Affero General Public License 646 | along with this program. If not, see . 647 | 648 | Also add information on how to contact you by electronic and paper mail. 649 | 650 | If your software can interact with users remotely through a computer 651 | network, you should also make sure that it provides a way for users to 652 | get its source. For example, if your program is a web application, its 653 | interface could display a "Source" link that leads users to an archive 654 | of the code. There are many ways you could offer source, and different 655 | solutions will be better for different programs; see section 13 for the 656 | specific requirements. 657 | 658 | You should also get your employer (if you work as a programmer) or school, 659 | if any, to sign a "copyright disclaimer" for the program, if necessary. 660 | For more information on this, and how to apply and follow the GNU AGPL, see 661 | . -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 这是甚么 | What's this 2 | 3 | 为Minecraft魔改交流群定制的QQ bot 4 | ~~拯救大佬们的血压~~ 5 | 6 | ## 贡献 | Contribute 7 | 8 | ### 配置开发环境 | Setup Dev Env 9 | 10 | 关于本项目的环境搭建问题,如下几点: 11 | 12 | - 请使用 JDK11+ & Kotlin1.5.21+ 13 | - 请自行配置 Secret 内容 14 | 15 | > Secrets 内容格式: 16 | > BOT_ID: Long (bot账号QQ号) 17 | > BOT_PWD: String (bot账号QQ密码) 18 | > BOT_NAME: String (自定义bot名称) 19 | > ADMIN: Mut ableList (bot最高管理员列表,填入管理员QQ号) 20 | 21 | ### 做出更改 | Make Changes 22 | 23 | > `permission.json` 格式:直接填写 QQ 号(MutableList\) 24 | 25 | > `commands.json` 格式: 26 | > - `name` 必填 27 | > - `info` 可选(不填将不会出现在 /help 内) 28 | > - `permission` 可选(默认为 Member ) 29 | > - `message` 可选(不填用于为内建指令添加 info )(MutableList 类型) 30 | 31 | 要构建可执行 jar,请执行`gradlew shadowJar` 32 | 33 | 完成配置后可直接运行 `Main()` 方法测试项目。 34 | 35 | ## 待办 | TODO 36 | 37 | - [x] Move To Kotlin 38 | - [x] Permission System 39 | - [x] Rebuild Command Register System 40 | - [x] Commands Completion 41 | - [x] Member Join Tips 42 | - [x] `https://` Link Resolve 43 | - [ ] CrT Version Checker Fix 44 | - [ ] `/jrrp` Ranking 45 | - [ ] And More... 46 | 47 | ## 许可 | License 48 | 49 | 本项目继承 Mirai 的 AGPL 证书。 50 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar 2 | 3 | plugins { 4 | java 5 | kotlin("jvm") version "1.6.10" 6 | kotlin("plugin.serialization") version "1.6.10" 7 | id("com.github.johnrengelman.shadow") version "7.0.0" 8 | } 9 | 10 | group = "cn.booling.bakahdt" 11 | version = "0.3.5-SNAPSHOT" 12 | 13 | tasks.withType { 14 | manifest.attributes["Main-Class"] = "cn.booling.bakahdt.MainKt" 15 | } 16 | 17 | repositories { 18 | maven { 19 | setUrl("https://maven.aliyun.com/repository/public/") 20 | } 21 | maven { 22 | setUrl("https://maven.github.com/") 23 | } 24 | mavenCentral() 25 | } 26 | 27 | dependencies { 28 | // libs 29 | // implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar")))) 30 | 31 | val miraiVersion = "2.9.0" 32 | api("net.mamoe:mirai-core-api:$miraiVersion") 33 | runtimeOnly("net.mamoe:mirai-core:$miraiVersion") 34 | api("net.mamoe:mirai-console:$miraiVersion") 35 | 36 | implementation("org.jsoup:jsoup:1.14.3") 37 | implementation("com.squareup.okhttp3:okhttp:4.9.3") 38 | implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") 39 | implementation("org.apache.logging.log4j:log4j-core:2.17.0") 40 | implementation("com.google.code.gson:gson:2.8.9") 41 | } 42 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or 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 UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /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 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "bakahdt" 2 | -------------------------------------------------------------------------------- /src/main/kotlin/cn/booling/bakahdt/CommandManager.kt: -------------------------------------------------------------------------------- 1 | package cn.booling.bakahdt 2 | 3 | import cn.booling.bakahdt.command.* 4 | import kotlinx.coroutines.delay 5 | import net.mamoe.mirai.event.* 6 | import net.mamoe.mirai.event.events.MemberJoinEvent 7 | import net.mamoe.mirai.event.events.MessageEvent 8 | import net.mamoe.mirai.message.data.At 9 | import net.mamoe.mirai.message.data.MessageChain 10 | import net.mamoe.mirai.message.data.MessageChainBuilder 11 | import net.mamoe.mirai.message.data.firstIsInstanceOrNull 12 | import java.text.SimpleDateFormat 13 | import java.util.* 14 | import kotlin.random.Random 15 | 16 | typealias CommandHandler = MessageSubscribersBuilder, Unit, Unit> 17 | 18 | suspend fun MessageEvent.simpleReply(message: String) = this.subject.sendMessage(message) 19 | suspend fun MessageEvent.simpleReply(message: MessageChain) = this.subject.sendMessage(message) 20 | 21 | @MessageDsl 22 | fun CommandHandler.onAdminCommand(cmd: String) = content { 23 | this.sender.id in ADMINS && "$IDENTIFIER$cmd" in this.message.contentToString() 24 | } 25 | 26 | @MessageDsl 27 | fun CommandHandler.onOPCommand(cmd: String) = content { 28 | this.sender.hasPermission(Permission.OP) && "$IDENTIFIER$cmd" in this.message.contentToString() 29 | } 30 | 31 | @MessageDsl 32 | fun CommandHandler.onMemberCommand(cmd: String) = content { 33 | this.sender.hasPermission(Permission.MEMBER) && "$IDENTIFIER$cmd" in this.message.contentToString() 34 | } 35 | 36 | val subscribeMessages = BAKA.eventChannel.subscribeMessages { 37 | // simple commands 38 | for ((cmd, ret) in simpleCommands) { 39 | contains("$IDENTIFIER$cmd") { 40 | if (this.sender.hasPermission(Permission.MEMBER) && !this.hasUrl()) { 41 | this.simpleReply(ret.messages.toMessage()) 42 | } 43 | } 44 | } 45 | 46 | // admin commands 47 | onAdminCommand("op").invoke { 48 | this.message.firstIsInstanceOrNull()?.target?.let { it1 -> 49 | permissionMap.op(it1) 50 | val ret = "Added $it1 to operator list." 51 | this.simpleReply(ret) 52 | logger.info(ret) 53 | } 54 | } 55 | 56 | onAdminCommand("deop").invoke { 57 | this.message.firstIsInstanceOrNull()?.target?.let { it1 -> 58 | permissionMap.deop(it1) 59 | val ret = "Removed $it1 from operator list." 60 | this.simpleReply(ret) 61 | logger.info(ret) 62 | } 63 | } 64 | 65 | onOPCommand("ban").invoke { 66 | this.message.firstIsInstanceOrNull()?.target?.let { it1 -> 67 | permissionMap.ban(it1) 68 | val ret = "Added $it1 to blacklist." 69 | this.simpleReply(ret) 70 | logger.info(ret) 71 | } 72 | } 73 | 74 | onOPCommand("unban").invoke { 75 | this.message.firstIsInstanceOrNull()?.target?.let { it1 -> 76 | permissionMap.unban(it1) 77 | val ret = "Removed $it1 from blacklist." 78 | this.simpleReply(ret) 79 | logger.info(ret) 80 | } 81 | } 82 | 83 | onOPCommand("reloadPermission").invoke { 84 | permissionMap.clear() 85 | loadPermissionMap() 86 | val ret = "Permission Reloaded." 87 | this.simpleReply(ret) 88 | logger.info(ret) 89 | } 90 | 91 | onOPCommand("reloadCommands").invoke { 92 | simpleCommands.clear() 93 | loadSimpleCommands() 94 | val ret = "Commands Reloaded!" 95 | this.simpleReply(ret) 96 | logger.info(ret) 97 | } 98 | 99 | onMemberCommand("info").invoke { 100 | this.simpleReply(TextFields.INFO) 101 | } 102 | 103 | onMemberCommand("help").invoke { 104 | this.simpleReply(TextFields.HELP) 105 | } 106 | 107 | onMemberCommand("jrrp").invoke { 108 | this.simpleReply(this.jrrp()) 109 | } 110 | 111 | } 112 | 113 | class TweakerListener : SimpleListenerHost() { 114 | @EventHandler 115 | suspend fun onMemberJoin(event: MemberJoinEvent) { 116 | delay(2000) 117 | val message = MessageChainBuilder() 118 | .append(At(event.member.id)) 119 | .append(TextFields.MEMBER_JOIN_TIP) 120 | .asMessageChain() 121 | event.group.sendMessage(message) 122 | } 123 | } 124 | 125 | fun cmdInit() { 126 | TWEAKER_CHANNEL.registerListenerHost(TweakerListener()) 127 | subscribeMessages 128 | } 129 | 130 | fun MessageEvent.jrrp(): String { 131 | val random = Random( 132 | (SimpleDateFormat("yyMMdd").format(Date()).toInt() xor this.sender.id.toInt()).toLong() 133 | ) 134 | return "${this.sender.nick} 今天的人品值是:${random.nextInt(101)}" 135 | } 136 | 137 | -------------------------------------------------------------------------------- /src/main/kotlin/cn/booling/bakahdt/Main.kt: -------------------------------------------------------------------------------- 1 | package cn.booling.bakahdt 2 | 3 | import cn.booling.bakahdt.command.loadPermissionMap 4 | import cn.booling.bakahdt.command.loadSimpleCommands 5 | import net.mamoe.mirai.BotFactory 6 | import net.mamoe.mirai.event.GlobalEventChannel 7 | import net.mamoe.mirai.event.events.GroupEvent 8 | import net.mamoe.mirai.utils.BotConfiguration 9 | import java.io.File 10 | 11 | const val IDENTIFIER = "/" 12 | 13 | val BAKA = BotFactory.newBot(BOT_ID, BOT_PWD) { 14 | enableContactCache() 15 | fileBasedDeviceInfo() 16 | autoReconnectOnForceOffline() 17 | protocol = BotConfiguration.MiraiProtocol.ANDROID_WATCH 18 | // protocol = BotConfiguration.MiraiProtocol.ANDROID_PHONE 19 | } 20 | val TWEAKER_CHANNEL = 21 | GlobalEventChannel.filter { ev -> ev is GroupEvent && ev.group == BAKA.getGroupOrFail(624487948L) } 22 | val logger = BAKA.logger 23 | var permissionsFile: File = File("permissions.json") 24 | 25 | suspend fun main() { 26 | init() 27 | BAKA.login() 28 | cmdInit() 29 | } 30 | 31 | fun init() { 32 | generateFile("permissions.json") 33 | generateFile("commands.json") 34 | loadPermissionMap() 35 | loadSimpleCommands() 36 | } 37 | -------------------------------------------------------------------------------- /src/main/kotlin/cn/booling/bakahdt/Secret.kt: -------------------------------------------------------------------------------- 1 | package cn.booling.bakahdt 2 | 3 | const val BOT_ID = 123456789L 4 | const val BOT_PWD = "1145141919810" 5 | const val BOT_NAME = "Diavolo" 6 | -------------------------------------------------------------------------------- /src/main/kotlin/cn/booling/bakahdt/TextFields.kt: -------------------------------------------------------------------------------- 1 | package cn.booling.bakahdt 2 | 3 | import net.mamoe.mirai.message.data.MessageChainBuilder 4 | 5 | object TextFields { 6 | // do not move these to [commands.json] 7 | val INFO = MessageChainBuilder() 8 | .append( 9 | """ 10 | ${BOT_NAME}是由[冰凌sama]为Minecraft魔改交流群开发的QQ bot,由[GBL]友情提供Server 11 | GitHub仓库地址:https://github.com/bingling-sama/BakaHDT/ 12 | 有任何意见或建议(或整活的idea)都可以来发issues。 13 | """.trimIndent() 14 | ) 15 | .asMessageChain() 16 | var HELP = MessageChainBuilder() 17 | .append("${BOT_NAME}指令一览:") 18 | .asMessageChain() 19 | val MEMBER_JOIN_TIP = MessageChainBuilder() 20 | .append( 21 | """ 22 | 欢迎加入Minecraft魔改交流群,进群请先阅读所有置顶公告。提问请携带尽可能多的相关信息 23 | Discord群:https://discord.gg/sB9PhGcutE/ 24 | CRT等魔改类模组错误还需附带脚本内容和输出LOG。 25 | 详见/ask/pastebin/log 26 | ----------------- 27 | 能解决你大部分疑惑的视频: 28 | https://b23.tv/Qu6aAY 29 | https://b23.tv/d2brHg 30 | ----------------- 31 | 本群会不定期清理长期不发言的人。 32 | 本群允许分享整合包(私人整合包自建整合包领域服个人服务器包)。 33 | ----------------- 34 | 群内分享的代码片段、音效、材质等资源,使用协议和最终解释权归[发布者],[商业使用]请提前咨询以避免踩雷。 35 | ----------------- 36 | """.trimIndent() 37 | ) 38 | .asMessageChain() 39 | } 40 | -------------------------------------------------------------------------------- /src/main/kotlin/cn/booling/bakahdt/Utils.kt: -------------------------------------------------------------------------------- 1 | package cn.booling.bakahdt 2 | 3 | import net.mamoe.mirai.event.events.MessageEvent 4 | import net.mamoe.mirai.utils.ExternalResource 5 | import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource 6 | import okhttp3.OkHttpClient 7 | import okhttp3.Request 8 | import org.jsoup.Jsoup 9 | import org.jsoup.nodes.Document 10 | import org.jsoup.select.Elements 11 | import java.io.File 12 | import java.io.InputStream 13 | import java.util.* 14 | 15 | fun MessageEvent.hasUrl(): Boolean { 16 | return this.message.contentToString().contains("http") 17 | } 18 | 19 | fun readImage(url: String): ExternalResource { 20 | val base64Prefix = "data:image/png;base64," 21 | return if (url.startsWith(base64Prefix)) { 22 | Base64.getDecoder().decode(url.substring(base64Prefix.length)).toExternalResource() 23 | } else { 24 | getImage( 25 | when { 26 | url.startsWith('/') -> "https://www.mcmod.cn$url" 27 | url.startsWith("//") -> "https:$url" 28 | else -> url 29 | } 30 | ).toExternalResource() 31 | } 32 | } 33 | 34 | fun getImage(url: String): InputStream { 35 | val okHttpClient = OkHttpClient() 36 | val request = Request.Builder().url(url).build() 37 | return okHttpClient.newCall(request).execute().body!!.byteStream() 38 | } 39 | 40 | fun getRequest(url: String): String { 41 | val okHttpClient = OkHttpClient() 42 | val request = Request.Builder().url(url).build() 43 | return okHttpClient.newCall(request).execute().body!!.string() 44 | } 45 | 46 | fun parseWebsiteBody(parseWebBody: String): Document { 47 | return Jsoup.parse(parseWebBody) 48 | } 49 | 50 | fun getDocument(url: String): Document { 51 | return parseWebsiteBody(getRequest(url)) 52 | } 53 | 54 | fun documentElementSelect(document: Document, cssQuery: String): Elements { 55 | return document.select(cssQuery) 56 | } 57 | 58 | fun String.substringBetween(open: String, close: String): String { 59 | val start = this.indexOf(open) 60 | if (start != -1) { 61 | val end = this.indexOf(close, start + open.length) 62 | if (end != -1) { 63 | return this.substring(start + open.length, end) 64 | } 65 | } 66 | return "" 67 | } 68 | 69 | fun checkFileExists(fileName: String): Boolean { 70 | val file = File(fileName) 71 | if (!file.exists()) { 72 | file.createNewFile() 73 | return false 74 | } 75 | return true 76 | } 77 | 78 | fun generateFile(fileName: String) { 79 | if (checkFileExists(fileName)) { 80 | logger.info("$fileName found, loading...") 81 | } else { 82 | logger.info("Can not find ${fileName}, generating...") 83 | getResourceAsText(fileName).let { File(fileName).writeText(it, Charsets.UTF_8) } 84 | } 85 | } 86 | 87 | fun getResourceAsText(path: String): String { 88 | return object {}::class.java.getResourceAsStream("/${path}")!!.bufferedReader().readText() 89 | } 90 | -------------------------------------------------------------------------------- /src/main/kotlin/cn/booling/bakahdt/command/Command.kt: -------------------------------------------------------------------------------- 1 | package cn.booling.bakahdt.command 2 | 3 | import cn.booling.bakahdt.* 4 | import kotlinx.serialization.* 5 | import kotlinx.serialization.Serializable 6 | import kotlinx.serialization.json.* 7 | import java.io.* 8 | 9 | var simpleCommands = mutableMapOf() 10 | 11 | @Serializable 12 | data class SimpleCommand( 13 | val name: String, 14 | val info: String? = null, 15 | val permission: String = "member", 16 | val messages: MutableList = mutableListOf() 17 | ) 18 | 19 | @Serializable 20 | data class CommandMap(var commands: List = mutableListOf()) 21 | 22 | fun loadSimpleCommands() { 23 | val commandMap = Json.decodeFromString(File("commands.json").readText()) 24 | commandMap.commands.forEach { cmd -> 25 | if (cmd.messages.isNotEmpty()) simpleCommands[cmd.name] = cmd 26 | if (cmd.info != null) TextFields.HELP = TextFields.HELP.plus("\n$IDENTIFIER${cmd.name} ${cmd.info}") 27 | logger.info("Loaded command: ${cmd.name} ${cmd.info}") 28 | } 29 | logger.info("Simple Commands Reloaded") 30 | } 31 | 32 | fun List.toMessage(): String { 33 | var message = "" 34 | this.forEach { 35 | message += "$it\n" 36 | } 37 | message = message.trimEnd('\n') 38 | return message 39 | } 40 | -------------------------------------------------------------------------------- /src/main/kotlin/cn/booling/bakahdt/command/Permission.kt: -------------------------------------------------------------------------------- 1 | package cn.booling.bakahdt.command 2 | 3 | import cn.booling.bakahdt.* 4 | import cn.booling.bakahdt.command.Permission.* 5 | import kotlinx.serialization.* 6 | import kotlinx.serialization.Serializable 7 | import kotlinx.serialization.json.* 8 | import net.mamoe.mirai.contact.* 9 | import java.io.* 10 | 11 | var permissionMap = PermissionMap() 12 | 13 | enum class Permission(val displayName: String) { 14 | OP("operator"), 15 | MEMBER("member"), 16 | BANNED("banned"); 17 | } 18 | 19 | fun String.getPermission(): Permission { 20 | return when (this) { 21 | OP.displayName -> OP 22 | MEMBER.displayName -> MEMBER 23 | BANNED.displayName -> BANNED 24 | else -> MEMBER 25 | } 26 | } 27 | 28 | @Serializable 29 | data class PermissionMap( 30 | var op: MutableList = mutableListOf(), 31 | var banned: MutableList = mutableListOf() 32 | ) { 33 | fun clear() { 34 | op = mutableListOf() 35 | banned = mutableListOf() 36 | } 37 | 38 | private fun save() { 39 | permissionsFile.writeText(Json.encodeToString(this)) 40 | logger.info(this.toString()) 41 | } 42 | 43 | fun op(id: Long) { 44 | if (!op.contains(id)) op.add(id) 45 | save() 46 | } 47 | 48 | fun deop(id: Long) { 49 | if (op.contains(id)) op.remove(id) 50 | save() 51 | } 52 | 53 | fun ban(id: Long) { 54 | if (!op.contains(id) && !banned.contains(id)) banned.add(id) 55 | save() 56 | } 57 | 58 | fun unban(id: Long) { 59 | if (banned.contains(id)) banned.remove(id) 60 | save() 61 | } 62 | } 63 | 64 | fun loadPermissionMap() { 65 | permissionMap = Json.decodeFromString(File("permissions.json").readText()) 66 | logger.info("Permissions Reloaded") 67 | } 68 | 69 | 70 | fun User.getPermission(): Permission { 71 | return when (this.id) { 72 | in permissionMap.op -> OP 73 | in permissionMap.banned -> BANNED 74 | else -> MEMBER 75 | } 76 | } 77 | 78 | fun User.hasPermission(permission: Permission): Boolean { 79 | return when (this.getPermission()) { 80 | OP -> return true 81 | MEMBER -> this.getPermission() == permission || this.getPermission() == OP 82 | BANNED -> return false 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/kotlin/cn/booling/bakahdt/service/CrtVersionChecker.kt: -------------------------------------------------------------------------------- 1 | package cn.booling.bakahdt.service 2 | 3 | import cn.booling.bakahdt.CF_API_KEY 4 | import com.google.gson.JsonParser 5 | import kotlinx.serialization.Serializable 6 | import kotlinx.serialization.decodeFromString 7 | import kotlinx.serialization.json.Json 8 | import okhttp3.* 9 | import java.io.IOException 10 | import java.net.URL 11 | import java.text.DateFormat 12 | import java.text.SimpleDateFormat 13 | import java.util.* 14 | 15 | private const val BASE_URL = "https://api.curseforge.com" 16 | 17 | object CrtVersionChecker { 18 | private var client = OkHttpClient() 19 | fun checkCrtVersion() { 20 | val request = Request.Builder() 21 | .get() 22 | .url("$BASE_URL/v1/mods/239197") 23 | .addHeader("Accept", "application/json") 24 | .addHeader("x-api-key", CF_API_KEY) 25 | .build() 26 | client.newCall(request).enqueue(object : Callback { 27 | override fun onResponse(call: Call, response: Response) { 28 | val data = JsonParser 29 | .parseString(response.body?.string()) 30 | .asJsonObject["data"] 31 | .asJsonObject["latestFiles"] 32 | .asJsonArray 33 | for (jsonElement in data) { 34 | when (jsonElement.asJsonObject["gameVersions"].asJsonArray[0].asString) { 35 | "1.12.2" -> Json.decodeFromString(jsonElement.asString) 36 | "1.16.5" -> Json.decodeFromString(jsonElement.asString) 37 | "1.18.1" -> Json.decodeFromString(jsonElement.asString) 38 | } 39 | } 40 | } 41 | 42 | override fun onFailure(call: Call, e: IOException) { 43 | } 44 | }) 45 | } 46 | 47 | fun updateVersion(data: String) { 48 | var version = Json.decodeFromString(data) 49 | 50 | } 51 | } 52 | 53 | @Serializable 54 | data class CrtVersionSerialization( 55 | var gameVersion: String, 56 | var fileName: String, 57 | var fileDate: String, 58 | var downloadUrl: String 59 | ) { 60 | fun parseToCrtVersion(): CrtVersion { 61 | return CrtVersion( 62 | this.gameVersion.getGameVersion(), 63 | this.fileName, 64 | SimpleDateFormat("yyyy-MM-ddTHH:mm:ss.SS").parse(this.fileDate), 65 | URL(this.downloadUrl) 66 | ) 67 | } 68 | } 69 | 70 | class CrtVersion( 71 | var gameVersion: GameVersion, 72 | var fileName: String, 73 | var fileDate: Date, 74 | var downloadUrl: URL 75 | ) 76 | 77 | enum class GameVersion(var version: String) { 78 | V1122("1.12.2"), 79 | V1165("1.16.5"), 80 | V1181("1.18.1"), 81 | OTHER("other") 82 | } 83 | 84 | fun String.getGameVersion(): GameVersion { 85 | return when (this) { 86 | "1.12.2" -> GameVersion.V1122 87 | "1.16.5" -> GameVersion.V1165 88 | "1.18.1" -> GameVersion.V1181 89 | else -> GameVersion.OTHER 90 | } 91 | } -------------------------------------------------------------------------------- /src/main/kotlin/cn/booling/bakahdt/service/MCMODHandler.kt: -------------------------------------------------------------------------------- 1 | package cn.booling.bakahdt 2 | 3 | import net.mamoe.mirai.contact.* 4 | import net.mamoe.mirai.event.events.* 5 | import net.mamoe.mirai.message.data.* 6 | import org.jsoup.* 7 | import org.jsoup.safety.* 8 | import org.jsoup.select.* 9 | 10 | private const val DOMAIN = "https://www.mcmod.cn" 11 | private const val RESULT = ".result-item > .head > a" 12 | private const val INTRODUCTION_TEXT = "[class=text-area common-text font14]" 13 | private const val INTRODUCTION_ITEM = "[class=item-content common-text font14]" 14 | private const val INTRODUCTION_POST = "[class=post-content common-text font14]" 15 | private const val NAME = ".name" 16 | private const val SHORT_NAME = ".short-name" 17 | private const val CN_NAME = ".class-title > h3" 18 | private const val EN_NAME = ".class-title > h4" 19 | private const val ICON_URL = ".class-cover-image > img" 20 | private const val TAB_URL = "[class=common-icon-text item-table] > a" 21 | 22 | enum class Filter { 23 | ALL, 24 | MOD, 25 | DATA, 26 | TUTORIAL 27 | } 28 | 29 | fun String.getFilter(): Filter { 30 | return when (this) { 31 | "ALL" -> Filter.ALL 32 | "MOD" -> Filter.MOD 33 | "DATA" -> Filter.DATA 34 | "TUTORIAL" -> Filter.TUTORIAL 35 | else -> Filter.ALL 36 | } 37 | } 38 | 39 | data class SearchResult( 40 | val title: String, 41 | val url: String, 42 | val filter: Filter 43 | ) 44 | 45 | data class Mod( 46 | val iconUrl: String = "", 47 | val shortName: String = "", 48 | val cnName: String = "", 49 | val enName: String = "", 50 | val introduction: String = "" 51 | ) 52 | 53 | data class Tutorial( 54 | val name: String = "", 55 | val introduction: String = "", 56 | ) 57 | 58 | data class Item( 59 | val iconUrl: String = "", 60 | val name: String = "", 61 | val introduction: String = "", 62 | val tabUrl: String = "" 63 | ) 64 | 65 | //fun search(key: String, filer: Filter): MutableList { 66 | // 67 | //} 68 | 69 | fun MessageEvent.mcmod() { 70 | 71 | } 72 | 73 | private fun search(key: String, filer: Filter, page: Int): MutableList { 74 | val searchResultsList = mutableListOf() 75 | val url = "$DOMAIN/s?key=$key&filter=${filer.ordinal}&page=$page" 76 | val elements = documentElementSelect(getDocument(url), RESULT) 77 | elements.forEach { 78 | searchResultsList.add(SearchResult(it.text(), it.attr("href"), filer)) 79 | } 80 | return searchResultsList 81 | } 82 | 83 | private fun parseMod(url: String): Mod { 84 | val document = getDocument(url) 85 | return Mod( 86 | document.select(ICON_URL).attr("src").run { 87 | if (this.contains("https")) this 88 | else "https:$this" 89 | }, 90 | document.select(SHORT_NAME).text(), 91 | document.select(CN_NAME).text(), 92 | document.select(EN_NAME).text(), 93 | labelReplacement(document.select(INTRODUCTION_TEXT)) 94 | ) 95 | } 96 | 97 | private fun parseItem(url: String): Item { 98 | val document = getDocument(url) 99 | return Item( 100 | document.select("td > img").attr("src").run { 101 | if (this.isNotEmpty()) "https:$this" 102 | else document.select("td > a > img").attr("src") 103 | }, 104 | document.select(NAME).text(), 105 | labelReplacement(document.select(INTRODUCTION_ITEM)), 106 | DOMAIN + document.select(TAB_URL).attr("href") 107 | ) 108 | } 109 | 110 | private fun parseTutorial(url: String): Tutorial { 111 | val document = getDocument(url) 112 | return Tutorial( 113 | document.select(NAME).text(), 114 | labelReplacement(document.select(INTRODUCTION_POST)) 115 | ) 116 | } 117 | 118 | private fun labelReplacement(elements: Elements): String { 119 | val whitelist = Whitelist() 120 | whitelist.addTags("p") 121 | whitelist.addAttributes("img", "data-src") 122 | var body = Jsoup.clean(elements.html(), whitelist) 123 | body = body.replace("

".toRegex(), "") 124 | body = body.replace("

".toRegex(), "") 125 | body = body.replace(" ".toRegex(), " ") 126 | return body 127 | } 128 | 129 | class MCMODProvider { 130 | 131 | private val searchResultsList = mutableListOf() 132 | private var page: Int = 1 133 | private var nextPage: Boolean = false 134 | 135 | private fun search(key: String, filer: Filter) { 136 | searchResultsList.addAll(search(key, filer, page)) 137 | nextPage = searchResultsList.size == 30 138 | } 139 | 140 | private fun getSearchList(key: String, filer: Filter, entry: Int): List { 141 | if (page == 1) { 142 | search(key, filer) 143 | page++ 144 | } 145 | val subList = if (entry <= searchResultsList.size) { 146 | searchResultsList.subList(0, entry) 147 | } else { 148 | if (nextPage) { 149 | search(key, filer) 150 | return getSearchList(key, filer, entry) 151 | } else { 152 | page = 1 153 | searchResultsList.subList(0, searchResultsList.size) 154 | } 155 | } 156 | val toList = subList.toList() 157 | subList.clear() 158 | return toList 159 | } 160 | 161 | fun searchListToString(list: List, group: Group, sender: Member): Message { 162 | if (list.isEmpty()) return PlainText("未查询到此内容...\n") 163 | val builder = ForwardMessageBuilder(group) 164 | 165 | builder.add(sender, PlainText("30秒内回复编号查看")) 166 | 167 | for (i in list.indices) { 168 | val title = list[i].title 169 | .replace(Regex("\\([^()]*\\)"), "") 170 | .replace(Regex("\\[[^\\[\\]]*]"), "") 171 | .replace(Regex("\\s*-\\s*"), "-") 172 | builder.add(sender.id, i.toString(), PlainText(title)) 173 | } 174 | if (searchResultsList.size > 0) builder.add(sender, PlainText("回复[P]下一页")) 175 | return builder.build() 176 | } 177 | 178 | fun getNextPage() = nextPage 179 | 180 | fun getSearchResultsListSize() = searchResultsList.size 181 | 182 | fun clear() { 183 | searchResultsList.clear() 184 | page = 1 185 | nextPage = false 186 | } 187 | 188 | } 189 | 190 | 191 | private suspend fun getSearchResults(serialNumber: Int, list: List, event: GroupMessageEvent): Any { 192 | if (serialNumber >= list.size) return "输入序号大于可查询数据,此 次查询已取消, 请重新查询。" 193 | 194 | val searchResults = list[serialNumber] 195 | return when (searchResults.filter) { 196 | Filter.MOD -> modMessageHandler(searchResults.url, event) 197 | Filter.DATA -> dataMessageHandler(searchResults.url, event) 198 | Filter.TUTORIAL -> tutorialMessageHandler(searchResults.url, event) 199 | else -> Unit 200 | } 201 | } 202 | 203 | private suspend fun modMessageHandler(url: String, event: GroupMessageEvent) { 204 | val mod = parseMod(url) 205 | val forwardMessageBuilder = ForwardMessageBuilder(event.group) 206 | forwardMessageBuilder.add(event.bot, event.group.uploadImage(readImage(mod.iconUrl))) 207 | 208 | var name = "" 209 | if (mod.shortName.isNotEmpty()) name += "缩写:${mod.shortName}\n" 210 | if (mod.cnName.isNotEmpty()) name += "中文:${mod.cnName}\n" 211 | if (mod.enName.isNotEmpty()) name += "英文:${mod.enName}" 212 | forwardMessageBuilder.add(event.bot, PlainText(name)) 213 | forwardMessageBuilder.add(event.bot, PlainText(url)) 214 | introductionMessage(forwardMessageBuilder, mod.introduction, event) 215 | event.group.sendMessage(forwardMessageBuilder.build()) 216 | } 217 | 218 | private suspend fun dataMessageHandler(url: String, event: GroupMessageEvent) { 219 | val item = parseItem(url) 220 | 221 | val forwardMessageBuilder = ForwardMessageBuilder(event.group) 222 | forwardMessageBuilder.add(event.bot, event.group.uploadImage(readImage(item.iconUrl))) 223 | forwardMessageBuilder.add(event.bot, PlainText(item.name)) 224 | forwardMessageBuilder.add(event.bot, PlainText(url)) 225 | introductionMessage(forwardMessageBuilder, item.introduction, event) 226 | forwardMessageBuilder.add(event.bot, PlainText("合成表:${item.tabUrl}")) 227 | 228 | event.group.sendMessage(forwardMessageBuilder.build()) 229 | } 230 | 231 | private suspend fun tutorialMessageHandler(url: String, event: GroupMessageEvent) { 232 | val tutorial = parseTutorial(url) 233 | 234 | val forwardMessageBuilder = ForwardMessageBuilder(event.group) 235 | forwardMessageBuilder.add(event.bot, PlainText(tutorial.name)) 236 | forwardMessageBuilder.add(event.bot, PlainText(url)) 237 | introductionMessage(forwardMessageBuilder, tutorial.introduction, event) 238 | 239 | event.group.sendMessage(forwardMessageBuilder.build()) 240 | } 241 | 242 | private suspend fun introductionMessage( 243 | forwardMessageBuilder: ForwardMessageBuilder, 244 | introductionHtml: String, 245 | event: GroupMessageEvent 246 | ) { 247 | var introduction = introductionHtml 248 | val strList: MutableList = ArrayList() 249 | val imgList: MutableList = ArrayList() 250 | var start: Int 251 | while (introduction.indexOf("") 255 | imgList.add(event.group.uploadImage(readImage(imgUrl))) 256 | introduction = introduction.substring(imgUrl.length + 17) 257 | } 258 | strList.add(introduction) 259 | var i = 0 260 | while (strList.size > i) { 261 | strList[i].split("\n\n").forEach { 262 | val maxLength = 1500 263 | val n = it.length / maxLength + if (it.length % maxLength != 0) 1 else 0 264 | for (j in 0 until n) 265 | forwardMessageBuilder.add( 266 | event.bot, 267 | PlainText(it.substring(j * maxLength, Integer.min((j + 1) * maxLength, it.length))) 268 | ) 269 | } 270 | if (i < imgList.size) { 271 | forwardMessageBuilder.add(event.bot, imgList[i]) 272 | } 273 | i++ 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /src/main/resources/commands.json: -------------------------------------------------------------------------------- 1 | { 2 | "commands": [ 3 | { 4 | "name": "ping", 5 | "info": "Bot存活测试", 6 | "messages": [ 7 | "Pong!" 8 | ] 9 | }, 10 | { 11 | "name": "info", 12 | "info": "显示Bot资料" 13 | }, 14 | { 15 | "name": "help", 16 | "info": "显示Bot帮助信息" 17 | }, 18 | { 19 | "name": "jrrp", 20 | "info": "获取今日随机人品值[roll 0~100]" 21 | }, 22 | { 23 | "name": "ask", 24 | "info": "教你提问", 25 | "messages": [ 26 | "提问的正确方式:", 27 | "简明清晰地描述你的游戏版本&遇到的问题", 28 | "魔改问题请将[crafttweaker.log]和log中提到/你认为可能出错的代码使用pastebin发送到群内", 29 | "(详见/pastebin)" 30 | ] 31 | }, 32 | { 33 | "name": "pastebin", 34 | "info": "pastebin 使用方法", 35 | "messages": [ 36 | "Ubuntu Pastebin使用介绍:", 37 | "1.打开https://paste.ubuntu.com.cn/", 38 | "2.填写Poster(随便写)", 39 | "3.将信息复制到Content框内", 40 | "4.点击Paste!", 41 | "5.复制网址栏网址发送至群内" 42 | ] 43 | }, 44 | { 45 | "name": "log", 46 | "info": "log相关", 47 | "messages": [ 48 | "关于log:", 49 | "首先在[.minecraft]文件夹找到[crafttweaker.log]和[.minecraft/logs]文件夹下的[debug.log]文件", 50 | "如果自己百度/Bing/Google无果实在无法解决,请使用 pastebin 将文件内容发送到群内等待群友/管理解答(详见 &ask 和 &pastebin)" 51 | ] 52 | }, 53 | { 54 | "name": "rules", 55 | "info": "显示群规", 56 | "messages": [ 57 | "《Minecraft魔改交流群 群规》请熟读并背诵()", 58 | "1.不允许多次加群,超过[3次]将被加入本群黑名单;", 59 | "2.禁止以任何形式发布有关 [键政/商业广告/非私人服务器宣传] 的消息;", 60 | "3.禁止以任何形式发布R18等[限制级]内容,擦边球也不行;", 61 | "4.禁止以任何形式发布付费资源;", 62 | "5.看Wiki(真的有很多问题看Wiki就能解决);", 63 | "6.看Wiki(第一次警告,第二次十分钟,第三次飞机票);", 64 | "7.看Wiki(重要的事情说三遍);", 65 | "8.群内大佬不是神,拒绝伸手党和随意@;", 66 | "更多群规详见群公告..." 67 | ] 68 | }, 69 | { 70 | "name": "crtcmds", 71 | "info": "CraftTweaker 部分常用指令使用方法", 72 | "messages": [ 73 | "CraftTweaker 可用的部分实用指令:", 74 | "/ct hand 输出玩家手上物品的 ID / 矿辞 (1.12-) / 标签 (1.14+) 等信息,你可以点击有关信息将其复制到剪贴板里。", 75 | "/ct syntax 检查脚本的语法是否准确。注意语法正确也不代表一定能运行的符合预期,此指令仅能检查语法问题,无法检查逻辑问题。", 76 | "/ct log 打开 CrT 日志文件,请配合 &pastebin 发送。", 77 | "/ct inventory 输出玩家物品栏的所有物品的 ID。", 78 | "/ct conflict 打印所有冲突配方和其总数。 (仅限工作台和熔炉)", 79 | "/reload (1.14+ 可用) 重载脚本。但 CoT 脚本不可重载。" 80 | ] 81 | }, 82 | { 83 | "name": "mtcmds", 84 | "info": "MineTweaker 部分常用指令使用方法", 85 | "messages": [ 86 | "MineTweaker 可用的部分实用指令: ", 87 | "/mt hand 输出玩家手上物品的 ID 等信息,你可以点击有关信息将其复制到剪贴板里。", 88 | "/mt inventory 输出玩家物品栏的所有物品的 ID。", 89 | "/mt oredict 打印所有游戏内存在的矿辞。", 90 | "/mt liquids 输出游戏内所有流体的注册 ID。", 91 | "/mt blocks 可以输出游戏内所有方块的注册ID。", 92 | "/mt blockinfo 开启时左右键可查看方块数据,再次输入指令以关闭。", 93 | "/mt reload 重载脚本。" 94 | ] 95 | }, 96 | { 97 | "name": "whyvsc", 98 | "info": "为什么要使用 VisualStudio Code ?", 99 | "messages": [ 100 | "VisualStudio Code(以下简称VSCode) 是当下编写 ZenScript 代码的最佳选择之一, 也是本群极力推荐的 文本编辑工具。", 101 | "为什么? 请花半分钟阅读以下理由:", 102 | "VSCode 是一款由微软维护的, 功能十分丰富, 用户社区和插件社区极其发达的文本编辑器。", 103 | "对于 ZenScript 这种脚本语言, 是最佳的编写工具之一, 安装了插件后, 可以提供代码高亮和语法纠错, NBT 预览和编辑, Language 文件高亮等等功能。", 104 | "就算对于其他脚本语言, 甚至程序设计语言, 其都是一个值得考虑的选择。", 105 | "想要下载并安装 VSCode, 前往 https://code.visualstudio.com/download, 下载适合自己系统的安装包安装即可。", 106 | "如果需要安装 ZenScript 高亮插件, 请参考 https://github.com/GBLodb/GBLodb/blob/master/personalModpackDevExperience.md。" 107 | ] 108 | }, 109 | { 110 | "name": "links", 111 | "info": "实用链接", 112 | "messages": [ 113 | "实用链接:", 114 | "CraftTweaker *官方英文文档*: https://docs.blamejared.com/", 115 | "MineTweaker 官方 Wiki: http://minetweaker3.powerofbytes.com/wiki/Main_Page/", 116 | "TweakerGroup Discord: https://discord.gg/4XsUtUfDFt/", 117 | "BlameJared Discord: https://discord.blamejared.com/", 118 | "Ubuntu Pastebin: https://paste.ubuntu.com.cn/", 119 | "Hastebin (DimDev): http://paste.dimdev.org/", 120 | "CurseForge Minecraft Mods: https://www.curseforge.com/minecraft/mc-mods/" 121 | ] 122 | } 123 | ] 124 | } 125 | -------------------------------------------------------------------------------- /src/main/resources/permissions.json: -------------------------------------------------------------------------------- 1 | { 2 | "op": [ 3 | 2082152212 4 | ], 5 | "banned": [ 6 | 114514810 7 | ] 8 | } --------------------------------------------------------------------------------