├── .gitignore
├── LICENSE
├── README.md
├── build.gradle
├── cli.md
├── docs
├── .conf
│ └── nav.js
├── Module.md
├── Plugin.md
└── README.md
├── mcl
├── mcl.cmd
├── settings.gradle
└── src
└── main
├── java
└── org
│ └── itxtech
│ └── mcl
│ ├── Agent.java
│ ├── Loader.java
│ ├── Utility.java
│ ├── component
│ ├── Config.java
│ ├── DownloadObserver.java
│ ├── Downloader.java
│ ├── Logger.java
│ ├── Repository.java
│ └── SemVer.java
│ ├── impl
│ ├── DefaultDownloader.java
│ └── DefaultLogger.java
│ ├── module
│ ├── MclModule.java
│ ├── ModuleManager.java
│ └── builtin
│ │ ├── Addon.java
│ │ ├── Announcement.java
│ │ ├── Boot.java
│ │ ├── Conf.java
│ │ ├── MDownloader.java
│ │ ├── Mrm.java
│ │ ├── OracleJdk.java
│ │ ├── PkgAnn.java
│ │ ├── Repo.java
│ │ ├── RepoCache.java
│ │ └── Updater.java
│ └── pkg
│ ├── MclPackage.java
│ └── PackageManager.java
└── resources
└── META-INF
└── services
└── org.itxtech.mcl.module.MclModule
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .gradle
3 | build
4 | out
5 |
6 | /testing
7 |
8 |
9 | /gradle
10 | gradlew
11 | gradlew.bat
--------------------------------------------------------------------------------
/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 | # Mirai Console Loader
2 |
3 | [](https://github.com/iTXTech/mirai-console-loader/releases)
4 | [](https://repo.maven.apache.org/maven2/org/itxtech/mcl/)
5 | [](https://mirai.mamoe.net/topic/177)
6 |
7 | 模块化、轻量级且支持完全自定义的 [mirai](https://github.com/mamoe/mirai) 加载器。
8 |
9 | 开发者请参见 [MCL 开发文档](docs/README.md)。
10 |
11 | ## 简介
12 |
13 | `iTX Technologies Mirai Console Loader`(下简称`MCL`)采用模块化设计,包含以下几个基础模块:
14 |
15 | * `Module` 模块管理器,用于加载和执行模块,`MCL`的主要功能均由模块实现。模块执行有各个阶段,详见开发文档。
16 | * `Config` 配置文件模块,用于配置的持久化。
17 | * `Package` 包管理器。
18 | * `Downloader` 下载器模块,用于下载文件,并实时返回进度。
19 | * `Logger` 日志模块,用于向控制台输出日志。
20 |
21 | ## [`MCL` 命令行文档](cli.md)
22 |
23 | 该文档将教会您如何`安装插件`,`禁用和启用脚本`,`修改包的更新频道`等操作。
24 |
25 | ## 使用 `iTXTech MCL`
26 |
27 | ### 一键安装
28 |
29 | [iTXTech MCL Installer](https://github.com/iTXTech/mcl-installer) 能在所有操作系统上一键安装 `iTXTech MCL`。
30 |
31 | ### 手动安装
32 |
33 | 1. 安装 Java 运行时(版本必须 >= 11)
34 | 2. 从 [Releases](https://github.com/iTXTech/mirai-console-loader/releases) 下载最新版本的`MCL`
35 | 3. 解压到某处
36 | 4. 在命令行中执行`.\mcl`以启动`MCL`
37 |
38 | #### 在`*nix`下通过命令行安装
39 |
40 | ```bash
41 | mkdir mcl
42 | cd mcl
43 | wget https://github.com/iTXTech/mirai-console-loader/releases/download/v2.1.2/mcl-2.1.2.zip
44 | unzip mcl-2.1.2.zip
45 | chmod +x mcl
46 | ./mcl
47 | ```
48 |
49 | ## `Mirai Repo` 列表
50 |
51 | * [iTXTech](https://repo.itxtech.org) - **默认** - Cloudflare Pages
52 | * [Mamoe](https://mcl.repo.mamoe.net) - GitHub Pages
53 | * [GitHub](https://github.com/project-mirai/mirai-repo-mirror) - 源仓库
54 |
55 | ## `Maven Repo` 列表
56 |
57 | * [Maven Central](https://repo1.maven.org/maven2/) - `Maven Central`上游
58 | * [Aliyun](https://maven.aliyun.com/repository/public) - **默认**,阿里云`Maven`镜像,国内访问速度快
59 | * [HuaweiCloud](https://mirrors.huaweicloud.com/repository/maven) - 华为云`Maven`镜像,阿里云不可用时的备选方案
60 |
61 | ## 安装`MCL Module`扩展组件
62 |
63 | 1. 在 `mcl` 运行目录下新建 `modules` 目录
64 | 2. 将 目标Jar 放入该目录
65 | 3. ~~编辑 `config.json` 中 `module_packages` 字段,添加入 `jar文件名(不带扩展名):包名`~~
66 |
67 | 新版 MCL Module 加载将使用 Java SPI Service 的加载方式,不需要再配置 `module_packages` 字段
68 |
69 | ## `MCL` 默认支持 `Mirai 2.11` 及以上插件格式
70 |
71 | 若需要默认使用旧版插件格式,请移除`config.json`的`archiveSuffix`中的`.mirai2.jar`字段。
72 |
73 | ## 开源许可证
74 |
75 | iTXTech Mirai Console Loader
76 | Copyright (C) 2020-2022 iTX Technologies
77 |
78 | This program is free software: you can redistribute it and/or modify
79 | it under the terms of the GNU Affero General Public License as
80 | published by the Free Software Foundation, either version 3 of the
81 | License, or (at your option) any later version.
82 |
83 | This program is distributed in the hope that it will be useful,
84 | but WITHOUT ANY WARRANTY; without even the implied warranty of
85 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
86 | GNU Affero General Public License for more details.
87 |
88 | You should have received a copy of the GNU Affero General Public License
89 | along with this program. If not, see .
90 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'me.him188.maven-central-publish' version "1.0.0-dev-3"
3 | id 'java'
4 | }
5 |
6 | group 'org.itxtech'
7 | version '2.1.2'
8 | description '模块化、轻量级且支持完全自定义的 mirai 加载器。'
9 |
10 | repositories {
11 | mavenCentral()
12 | }
13 |
14 | dependencies {
15 | implementation('commons-cli:commons-cli:1.5.0')
16 | implementation('com.google.code.gson:gson:2.10')
17 | implementation('org.fusesource.jansi:jansi:2.4.0')
18 | }
19 |
20 | def getGitHash = { ->
21 | def stdout = new ByteArrayOutputStream()
22 | exec {
23 | commandLine 'git', 'rev-parse', '--short', 'HEAD'
24 | standardOutput = stdout
25 | }
26 | return stdout.toString().trim()
27 | }
28 |
29 | task fatJar(type: Jar) {
30 | from sourceSets.main.output
31 | archiveClassifier.set("all")
32 | archiveFileName = "mcl.jar"
33 |
34 | manifest {
35 | attributes "Main-Class": "org.itxtech.mcl.Loader"
36 | attributes "Version": project.version + "-" + getGitHash()
37 | attributes "Launcher-Agent-Class": "org.itxtech.mcl.Agent"
38 | attributes "Agent-Class": "org.itxtech.mcl.Agent"
39 | attributes "Premain-Class": "org.itxtech.mcl.Agent"
40 | }
41 |
42 | from {
43 | configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
44 | }
45 |
46 | exclude("META-INF/LICENSE.txt", "META-INF/NOTICE.txt", "META-INF/NOTICE-tools.txt")
47 | }
48 |
49 | task launchTest(type: JavaExec) {
50 | dependsOn(fatJar)
51 |
52 | jvmArgs(
53 | '-Dmcl.no-ansi-console-init=true',
54 | )
55 |
56 | mainClass.set('-jar')
57 | args(fatJar.outputs.files.singleFile)
58 |
59 | def wk = project.file('testing')
60 | workingDir(wk)
61 | doFirst { wk.mkdirs() }
62 | }
63 |
64 | task zipAll(type: Zip) {
65 | dependsOn(fatJar)
66 |
67 | from fileTree(dir: ".", includes: ["README.md", "LICENSE", "mcl", "mcl.cmd"])
68 | from("$buildDir/libs") {
69 | include "mcl.jar"
70 | rename { "mcl.jar" }
71 | }
72 | destinationDirectory = file("$buildDir/libs")
73 | archiveFileName = "mcl-${project.version}.zip"
74 | }
75 |
76 | assemble.dependsOn(fatJar)
77 | assemble.dependsOn(zipAll)
78 |
79 | mavenCentralPublish {
80 | singleDevGithubProject("iTXTech", "mirai-console-loader")
81 | licenseAGplV3()
82 | useCentralS01()
83 |
84 | publication {
85 | artifacts.artifact(tasks.getByName("zipAll"))
86 | artifacts.artifact(tasks.getByName("fatJar"))
87 | }
88 | }
89 |
90 | java {
91 | sourceCompatibility = JavaVersion.VERSION_11
92 | targetCompatibility = JavaVersion.VERSION_11
93 | }
94 |
95 | tasks.withType(JavaCompile) {
96 | options.encoding = 'UTF-8'
97 | }
98 |
--------------------------------------------------------------------------------
/cli.md:
--------------------------------------------------------------------------------
1 | # MCL 命令行参数
2 |
3 | ## 在`updater`中使用`maven`更新频道
4 |
5 | 此功能可以自动从`Maven Repo`获取最新版本。
6 |
7 | ```
8 | ./mcl --update-package 包名 --channel maven
9 | ```
10 |
11 | ### `maven` 支持两个子频道
12 |
13 | * `stable` - 稳定版 - `./mcl --update-package 包名 --channel maven-stable`
14 | * `prerelease` - 预发行版 - `./mcl --update-package 包名 --channel maven-prerelease`
15 | * 留空或其他,则默认为最新版
16 |
17 | ## 禁用控制台颜色
18 |
19 | 配置`mcl.disable-ansi`环境变量为`true`。
20 |
21 | ```bash
22 | $ java "-Dmcl.disable-ansi=true" -jar mcl.jar
23 | ```
24 |
25 | 仅禁用 `Windows CMD` 下 `ANSI` 初始化,请配置 `mcl.no-ansi-console-init` 环境变量为 `true`。
26 |
27 | ## 切换 `Mirai Repo`
28 |
29 | `MCL` 内置 `Mirai Repo Manager`,可通过以下命令调用。
30 |
31 | ```bash
32 | ./mcl --mrm-list # 列出内置 Mirai Repo
33 | ./mcl --mrm-use forum # 使用 Mirai Forum 提供的 Mirai Repo 镜像
34 | ./mcl --set-mirai-repo https://repo.example.org # 使用自定义的 Mirai Repo
35 | ```
36 |
37 | ## 使用样例
38 |
39 | * 修改某个包的更新频道
40 |
41 | ```
42 | ./mcl --update-package 包名 --channel 频道名
43 | ```
44 |
45 | * 安装 `Mirai Native`
46 |
47 | ```
48 | ./mcl --update-package org.itxtech:mirai-native
49 | ```
50 |
51 | * 安装 `Chat Command`
52 |
53 | ```
54 | ./mcl --update-package net.mamoe:chat-command
55 | ```
56 |
57 | * 指定 `mirai-console` 版本(指定的版本必须为该`Channel`中存在的版本)
58 |
59 | ```
60 | ./mcl --update-package net.mamoe:mirai-console --channel stable --version 1.0.0
61 | ```
62 |
63 | * 执行包更新
64 |
65 | ```
66 | ./mcl -u
67 | ```
68 |
69 | * 禁用 `updater` 模块
70 |
71 | ```
72 | ./mcl --disable-module updater
73 | ```
74 |
75 | * 启用 `updater` 模块
76 |
77 | ```
78 | ./mcl --enable-module updater
79 | ```
80 |
81 | * 更新运行库但不启动
82 |
83 | ```
84 | ./mcl --dry-run
85 | ```
86 |
87 | * 查看帮助
88 |
89 | ```bash
90 | ./mcl -h
91 |
92 | usage: mcl
93 | -a,--update-package Add or update package
94 | -b,--show-boot-props Show Mirai Console boot properties
95 | --boot-only Execute boot phase only
96 | -c,--log-level Set log level
97 | -d,--disable-module Disable module
98 | --disable-auto-clear Disable Repo Cache auto clear
99 | -e,--enable-module Enable module
100 | --enable-auto-clear Enable Repo Cache auto clear
101 | -f,--set-boot-entry Set Mirai Console boot entry
102 | -g,--set-boot-args Set Mirai Console boot arguments
103 | -i,--package-info Fetch info for specified package
104 | -j,--list-repo-packages List available packages in Mirai Repo
105 | -k,--disable-progress-bar Disable progress bar
106 | -l,--list-disabled-modules List disabled modules
107 | -m,--set-mirai-repo Set Mirai Repo address
108 | --mrm-list List all builtin Mirai Repo
109 | --mrm-use Change Mirai Repo
110 | -n,--channel Set update channel of package
111 | -o,--show-repos Show Mirai Repo and Maven Repo
112 | -p,--proxy Set HTTP proxy
113 | -q,--delete Remove outdated files while updating
114 | -r,--remove-package Remove package
115 | -s,--list-packages List configured packages
116 | --set-max-threads Set Max Threads of Multithreading
117 | Downloader
118 | -t,--type Set type of package
119 | -u,--update Update packages
120 | -w,--version Set version of package
121 | -x,--lock Lock version of package
122 | -y,--unlock Unlock version of package
123 | -z,--dry-run Skip boot phase
124 | ```
125 |
126 | ## 开源许可证
127 |
128 | Copyright (C) 2020-2022 iTX Technologies
129 |
130 | This program is free software: you can redistribute it and/or modify
131 | it under the terms of the GNU Affero General Public License as
132 | published by the Free Software Foundation, either version 3 of the
133 | License, or (at your option) any later version.
134 |
135 | This program is distributed in the hope that it will be useful,
136 | but WITHOUT ANY WARRANTY; without even the implied warranty of
137 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
138 | GNU Affero General Public License for more details.
139 |
140 | You should have received a copy of the GNU Affero General Public License
141 | along with this program. If not, see .
142 |
--------------------------------------------------------------------------------
/docs/.conf/nav.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | text: 'mirai-console-loader',
3 | link: '/',
4 | items: [
5 | {text: '主页', link: '/'},
6 | {text: 'MCL模块', link: '/Module.html'},
7 | {text: 'MCL插件', link: '/Plugin.html'},
8 | ],
9 | };
10 |
--------------------------------------------------------------------------------
/docs/Module.md:
--------------------------------------------------------------------------------
1 | # MCL Module
2 |
3 | MCL 模块开发文档。
4 |
5 | ## 模块架构
6 |
7 | `MCL Module` 中提供的基本对象:
8 |
9 | * [Loader](../src/main/java/org/itxtech/mcl/Loader.java) - MCL 实例,可通过其访问各个组件
10 |
11 | ## `MclModule` 类
12 |
13 | * 每个模块类都需继承 `org.itxtech.mcl.module.MclModule`
14 | * 新版 MCL Module 加载将使用 Java SPI Service 的加载方式,
15 | 可参考 [META-INF.services](../src/main/resources/META-INF/services/org.itxtech.mcl.module.MclModule) 将类注册
16 |
17 | ### `prepare`
18 |
19 | 模块最先执行的方法,可用于:
20 |
21 | 1. 添加命令行选项
22 | 2. 修改配置文件
23 |
24 | ### `cli`
25 |
26 | 脚本处理命令行参数。
27 |
28 | ### `load`
29 |
30 | 各脚本都处理完命令行参数后被调用。
31 |
32 | ### `boot`
33 |
34 | 启动`mirai`,应有且只有一个脚本实现此阶段。
35 |
36 | ## 示例
37 |
38 | ```java
39 | package com.test.module;
40 |
41 | import org.apache.commons.cli.Option;
42 | import org.itxtech.mcl.module.MclModule;
43 |
44 | public class Test extends MclModule {
45 | @Override
46 | public String getName() {
47 | return "Test"; // 此方法必须实现,名称是插件的唯一标识
48 | }
49 |
50 | @Override
51 | public void prepare() {
52 | loader //脚本中可直接访问 Loader 实例
53 | .options //命令行参数实例
54 | .addOption( //添加命令行参数
55 | Option //该包已在最上面导入了
56 | .builder("t")
57 | .desc("Example")
58 | .longOpt("example")
59 | .build()
60 | );
61 | // MCL 使用 Apache Commons CLI,见 https://commons.apache.org/proper/commons-cli/
62 | }
63 |
64 | @Override
65 | public void cli() {
66 | if (loader.cli.hasOption("t")) { //如果存在-t参数,或--exmaple参数
67 | loader.logger.info("示例!!!");
68 | }
69 | }
70 |
71 | @Override
72 | public void load() {
73 | loader.logger.warning("示例:Load");
74 | }
75 |
76 | @Override
77 | public void boot() {
78 | loader.logger.warning("示例:Boot");
79 | }
80 | }
81 | ```
82 |
83 | ## 注意事项
84 |
85 | 1. `Jar` 会直接加载入 `MCL` 的 `SystemClassLoader` 中,被所有包共享
86 | 2. `META-INF/services/org.itxtech.mcl.module.MclModule` 中一行对应一个 MclModule, 行的内容是 模块类 的完整类名
87 |
--------------------------------------------------------------------------------
/docs/Plugin.md:
--------------------------------------------------------------------------------
1 | # MCL Plugin
2 |
3 | 在`Mirai Console`插件中使用`MCL API`,本文档采用`Kotlin`编写。
4 |
5 | ## 在`build.gradle(.kts)` 中添加`MCL`依赖
6 |
7 | ```groovy
8 | dependencies {
9 | //打包时排除mcl包,如果您的插件打包时不打包依赖,则可使用implementation
10 | compileOnly("org.itxtech:mcl:2.0.0")
11 | }
12 | ```
13 |
14 | ## 在插件主类中检查`MCL`
15 |
16 | 如果不检查会导致加载调用了`MCL API`的类时导致崩溃。
17 |
18 | ```kotlin
19 | try {
20 | Class.forName("org.itxtech.mcl.Loader")
21 | } catch (e: Exception) {
22 | logger.error("Mirai Console 并未通过 iTXTech Mirai Console Loader 加载。")
23 | logger.error("请访问 https://github.com/iTXTech/mirai-console-loader")
24 | return
25 | }
26 | //载入调用了MCL API的类,切记不要用子类,不然会自动加载
27 | ```
28 |
29 | ## 执行`MCL`命令行命令
30 |
31 | ```kotlin
32 | import org.itxtech.mcl.Loader
33 |
34 | val mcl = Loader.getInstance()
35 |
36 | fun runMclCommand(args: Array) {
37 | mcl.parseCli(args, true) //调用mcl解析参数
38 | mcl.manager.phaseCli() //调用模块管理器执行cli阶段
39 | }
40 |
41 | //执行添加包指令
42 | runMclCommand(arrayOf("--update-package", "包名", "--type", "plugin", "--channel", "stable"))
43 | ```
44 |
45 | ## 调用`MCL`包管理器
46 |
47 | ```kotlin
48 | import org.itxtech.mcl.Loader
49 | import org.itxtech.mcl.component.Config
50 |
51 | val mcl = Loader.getInstance();
52 |
53 | //添加 Mirai Native
54 | mcl.config.packages.add(
55 | Config.Package("org.itxtech:mirai-native", "stable")
56 | )
57 |
58 | //执行 updater 模块,如果updater被禁用则无法调用
59 | mcl.manager.getModule("updater")?.load() //执行模块的 load 阶段
60 | ```
61 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Mirai Console Loader
2 |
3 | 欢迎来到 [iTXTech MCL](https://github.com/iTXTech/mirai-console-loader) 开发文档!
4 |
5 | ## 将插件发布到 MCL
6 |
7 | 1. 发布到 [Maven Central](https://search.maven.org) -
8 | 参见 [maven-central-publish - him188](https://github.com/Him188/maven-central-publish/blob/main/UseInLocalProject.md)
9 | 2. 发布到 [Mirai Repo](https://github.com/project-mirai/mirai-repo-mirror) - 参照模板提交`Pull Request` (可选步骤,如不添加,可使用`maven`提供的三种更新频道)
10 |
11 | ## 开发 `MCL Module`
12 |
13 | * [MCL Module](Module.md)
14 |
15 | ## 在`Mirai Console`插件中调用 `MCL API`
16 |
17 | * [Plugin](Plugin.md)
18 |
--------------------------------------------------------------------------------
/mcl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | export JAVA_BINARY=java
3 | $JAVA_BINARY -jar mcl.jar $*
4 |
--------------------------------------------------------------------------------
/mcl.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 | setlocal
3 | set JAVA_BINARY=java
4 | %JAVA_BINARY% -jar mcl.jar %*
5 |
6 | set EL=%ERRORLEVEL%
7 | if %EL% NEQ 0 (
8 | echo Process exited with %EL%
9 | pause
10 | )
11 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | maven { url 'https://maven.aliyun.com/repository/public' }
4 | gradlePluginPortal()
5 | }
6 | }
7 | rootProject.name = 'mcl'
8 |
9 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/Agent.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl;
2 |
3 | import java.io.IOException;
4 | import java.lang.instrument.Instrumentation;
5 | import java.util.jar.JarFile;
6 |
7 | /*
8 | *
9 | * Mirai Console Loader
10 | *
11 | * Copyright (C) 2020-2022 iTX Technologies
12 | *
13 | * This program is free software: you can redistribute it and/or modify
14 | * it under the terms of the GNU Affero General Public License as
15 | * published by the Free Software Foundation, either version 3 of the
16 | * License, or (at your option) any later version.
17 | *
18 | * This program is distributed in the hope that it will be useful,
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 | * GNU Affero General Public License for more details.
22 | *
23 | * You should have received a copy of the GNU Affero General Public License
24 | * along with this program. If not, see .
25 | *
26 | * @author PeratX
27 | * @website https://github.com/iTXTech/mirai-console-loader
28 | *
29 | */
30 | public class Agent {
31 | public static Instrumentation instrumentation;
32 |
33 | public static void premain(String args, Instrumentation instrumentation) {
34 | Agent.instrumentation = instrumentation;
35 | }
36 |
37 | public static void agentmain(String args, Instrumentation instrumentation) {
38 | Agent.instrumentation = instrumentation;
39 | }
40 |
41 | public static void appendJarFile(JarFile file) throws IOException {
42 | if (instrumentation != null) {
43 | instrumentation.appendToSystemClassLoaderSearch(file);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/Loader.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl;
2 |
3 | import org.apache.commons.cli.*;
4 | import org.fusesource.jansi.Ansi;
5 | import org.fusesource.jansi.AnsiConsole;
6 | import org.itxtech.mcl.component.Config;
7 | import org.itxtech.mcl.component.Downloader;
8 | import org.itxtech.mcl.component.Logger;
9 | import org.itxtech.mcl.component.Repository;
10 | import org.itxtech.mcl.impl.DefaultDownloader;
11 | import org.itxtech.mcl.impl.DefaultLogger;
12 | import org.itxtech.mcl.module.ModuleManager;
13 | import org.itxtech.mcl.pkg.PackageManager;
14 |
15 | import java.io.File;
16 | import java.io.PrintWriter;
17 | import java.io.StringWriter;
18 | import java.net.InetSocketAddress;
19 | import java.util.jar.Manifest;
20 |
21 | /*
22 | *
23 | * Mirai Console Loader
24 | *
25 | * Copyright (C) 2020-2022 iTX Technologies
26 | *
27 | * This program is free software: you can redistribute it and/or modify
28 | * it under the terms of the GNU Affero General Public License as
29 | * published by the Free Software Foundation, either version 3 of the
30 | * License, or (at your option) any later version.
31 | *
32 | * This program is distributed in the hope that it will be useful,
33 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
34 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 | * GNU Affero General Public License for more details.
36 | *
37 | * You should have received a copy of the GNU Affero General Public License
38 | * along with this program. If not, see .
39 | *
40 | * @author PeratX
41 | * @website https://github.com/iTXTech/mirai-console-loader
42 | *
43 | */
44 | public class Loader {
45 | private static Loader instance;
46 |
47 | public static Loader getInstance() {
48 | return instance;
49 | }
50 |
51 | public Downloader downloader;
52 | public Logger logger = new DefaultLogger();
53 | public File configFile = new File("config.json");
54 | public Config config;
55 | public ModuleManager manager;
56 | public PackageManager packageManager;
57 | public Repository repo;
58 | public Options options = new Options();
59 | public CommandLine cli;
60 |
61 | public boolean boot = false;
62 |
63 | public Loader() {
64 | instance = this;
65 | }
66 |
67 | public static void main(String[] args) {
68 | var loader = new Loader();
69 | try {
70 | if (!Boolean.getBoolean("mcl.disable-ansi")) {
71 | if (!Boolean.getBoolean("mcl.no-ansi-console-init")) {
72 | try {
73 | AnsiConsole.systemInstall();
74 | } catch (Exception ansiException) {
75 | loader.logger.error("Fail to initialize JAnsi, set env mcl.no-ansi-console-init to true to disable the initialization.");
76 | loader.logger.logException(ansiException);
77 | }
78 | }
79 | Ansi.setEnabled(true);
80 | } else {
81 | Ansi.setEnabled(false);
82 | }
83 | loader.loadConfig();
84 | loader.start(args);
85 | } catch (Exception e) {
86 | loader.logger.logException(e);
87 | }
88 | }
89 |
90 | public void exit(int code) {
91 | if (!boot) {
92 | System.exit(code);
93 | }
94 | }
95 |
96 | public void parseCli(String[] args, boolean help) {
97 | try {
98 | cli = new DefaultParser().parse(options, args);
99 | } catch (ParseException e) {
100 | if (help) {
101 | logger.error(e.getMessage());
102 | var stringWriter = new StringWriter();
103 | var printWriter = new PrintWriter(stringWriter);
104 | var formatter = new HelpFormatter();
105 | formatter.printHelp(printWriter, formatter.getWidth(), "mcl", null,
106 | options, formatter.getLeftPadding(), formatter.getDescPadding(),
107 | null, false);
108 | printWriter.flush();
109 | logger.info(stringWriter.toString());
110 | exit(1);
111 | }
112 | cli = new CommandLine.Builder().build();
113 | }
114 | }
115 |
116 | public void loadConfig() {
117 | config = Config.load(configFile);
118 | logger.setLogLevel(config.logLevel);
119 | }
120 |
121 | public InetSocketAddress getProxy() {
122 | var p = config.proxy.split(":");
123 | try {
124 | return new InetSocketAddress(p[0], Integer.parseInt(p[1]));
125 | } catch (Exception e) {
126 | if (!"".equals(config.proxy)) {
127 | logger.error("Invalid proxy setting: " + config.proxy);
128 | }
129 | }
130 | return null;
131 | }
132 |
133 | public String getVersion() throws Exception {
134 | var version = "unknown";
135 | var mf = getClass().getClassLoader().getResources("META-INF/MANIFEST.MF");
136 | while (mf.hasMoreElements()) {
137 | var manifest = new Manifest(mf.nextElement().openStream());
138 | if ("org.itxtech.mcl.Loader".equals(manifest.getMainAttributes().getValue("Main-Class"))) {
139 | version = manifest.getMainAttributes().getValue("Version");
140 | }
141 | }
142 | return version;
143 | }
144 |
145 | public boolean saveConfig() {
146 | return tryCatching(() -> config.save(configFile));
147 | }
148 |
149 | private boolean tryCatching(UnsafeRunnable r) {
150 | try {
151 | r.run();
152 | return true;
153 | } catch (Throwable e) {
154 | logger.logException(e);
155 | return false;
156 | }
157 | }
158 |
159 | private interface UnsafeRunnable {
160 | void run() throws Exception;
161 | }
162 |
163 | /**
164 | * 启动 Mirai Console Loader,并加载脚本
165 | */
166 | public void start(String[] args) throws Exception {
167 | logger.info(Ansi.ansi().fgBrightCyan().a("iTX Technologies Mirai Console Loader").reset()
168 | .a(" version ").fgBrightYellow().a(getVersion()));
169 | logger.info(Ansi.ansi().a("Runtime: ").fgBrightCyan().a(System.getProperty("java.vm.name")).a(" ")
170 | .fgBrightYellow().a(System.getProperty("java.version")).reset().a(" (arch: ").a(System.getProperty("sun.arch.data.model")).a(")"));
171 | logger.info("https://github.com/iTXTech/mirai-console-loader");
172 | logger.info(Ansi.ansi().a("This program is licensed under ").fgBrightMagenta().a("GNU AGPL v3"));
173 |
174 | var bootGroup = new OptionGroup();
175 | bootGroup.addOption(Option.builder("z").desc("Skip boot phase").longOpt("dry-run").build());
176 | bootGroup.addOption(Option.builder().desc("Execute boot phase only").longOpt("boot-only").build());
177 | options.addOptionGroup(bootGroup);
178 |
179 | packageManager = new PackageManager(this);
180 | repo = new Repository(this);
181 | manager = new ModuleManager(this);
182 | downloader = new DefaultDownloader(this);
183 |
184 | parseCli(args, false);
185 | tryCatching(() -> manager.loadAllModules()); //此阶段脚本只能修改loader中变量
186 | parseCli(args, true);
187 |
188 | if (!cli.hasOption("boot-only")) {
189 | tryCatching(() -> manager.phaseCli()); //此阶段脚本处理命令行参数
190 | tryCatching(() -> manager.phaseLoad()); //此阶段脚本下载包
191 | saveConfig();
192 | }
193 |
194 | boot = true;
195 | if (!cli.hasOption("z")) {
196 | tryCatching(() -> manager.phaseBoot()); //此阶段脚本启动mirai,且应该只有一个脚本实现
197 | }
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/Utility.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl;
2 |
3 | import org.itxtech.mcl.pkg.MclPackage;
4 |
5 | import java.io.File;
6 | import java.io.FileInputStream;
7 | import java.lang.reflect.Method;
8 | import java.math.BigInteger;
9 | import java.nio.file.Files;
10 | import java.security.MessageDigest;
11 | import java.text.StringCharacterIterator;
12 | import java.util.ArrayList;
13 | import java.util.List;
14 | import java.util.jar.JarFile;
15 |
16 | /*
17 | *
18 | * Mirai Console Loader
19 | *
20 | * Copyright (C) 2020-2022 iTX Technologies
21 | *
22 | * This program is free software: you can redistribute it and/or modify
23 | * it under the terms of the GNU Affero General Public License as
24 | * published by the Free Software Foundation, either version 3 of the
25 | * License, or (at your option) any later version.
26 | *
27 | * This program is distributed in the hope that it will be useful,
28 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 | * GNU Affero General Public License for more details.
31 | *
32 | * You should have received a copy of the GNU Affero General Public License
33 | * along with this program. If not, see .
34 | *
35 | * @author PeratX
36 | * @website https://github.com/iTXTech/mirai-console-loader
37 | *
38 | */
39 | public class Utility {
40 | public static String fileSha1(File file) throws Exception {
41 | var fis = new FileInputStream(file);
42 | var buffer = new byte[1024];
43 | var digest = MessageDigest.getInstance("SHA");
44 | int numRead;
45 |
46 | do {
47 | numRead = fis.read(buffer);
48 | if (numRead > 0) {
49 | digest.update(buffer, 0, numRead);
50 | }
51 | } while (numRead != -1);
52 |
53 | fis.close();
54 | byte[] bytes = digest.digest();
55 | BigInteger b = new BigInteger(1, bytes);
56 | return String.format("%0" + (bytes.length << 1) + "x", b);
57 | }
58 |
59 | public static boolean check(File baseFile, File checksumFile) throws Exception {
60 | if (!baseFile.exists() || !checksumFile.exists()) {
61 | return false;
62 | }
63 | var checksum = Files.readString(checksumFile.toPath()).trim().replace(" ", "").toLowerCase();
64 | return fileSha1(baseFile).equals(checksum);
65 | }
66 |
67 | public static boolean checkLocalFile(MclPackage pkg) throws Exception {
68 | return Utility.check(pkg.getJarFile(), pkg.getSha1File());
69 | }
70 |
71 | public interface GetMain {
72 | Method run() throws Exception;
73 | }
74 |
75 | public static void bootJars(File[] files, String entry, String args) throws Exception {
76 | bootJars(files, entry, args, () ->
77 | Utility.class.getClassLoader().loadClass(entry)
78 | .getMethod("main", String[].class));
79 | }
80 |
81 | public static void bootJars(File[] files, String entry, String args, GetMain getMain) throws Exception {
82 | for (var file : files) {
83 | Agent.appendJarFile(new JarFile(file));
84 | }
85 | var method = getMain.run();
86 | method.invoke(null, (Object) (args.trim().equals("") ? new String[0] : args.split(" ")));
87 | }
88 |
89 | public static void bootMirai(ArrayList files, String entry, String args) throws Exception {
90 | var f = new StringBuilder();
91 | var arr = new ArrayList();
92 | for (var file : files) {
93 | arr.add(file);
94 | f.append(file.getName()).append(", ");
95 | }
96 | f.delete(f.length() - 2, f.length());
97 | Loader.getInstance().logger.debug("Boot Mirai Files: " + f + "; Args: \"" + args + "\"");
98 | bootJars(arr.toArray(new File[0]), entry, args);
99 | }
100 |
101 | public static String humanReadableFileSize(int bytes) {
102 | var absB = bytes == Integer.MIN_VALUE ? Integer.MAX_VALUE : Math.abs(bytes);
103 | if (absB < 1024) {
104 | return bytes + " B";
105 | }
106 | var value = absB;
107 | var ci = new StringCharacterIterator("KMGTPE");
108 | for (var i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) {
109 | value >>= 10;
110 | ci.next();
111 | }
112 | value *= Integer.signum(bytes);
113 | return String.format("%.2f %cB", value / 1024.0, ci.current());
114 | }
115 |
116 | public static String join(String d, List t) {
117 | return String.join(d, t);
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/component/Config.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.component;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.GsonBuilder;
5 | import com.google.gson.annotations.SerializedName;
6 | import com.google.gson.reflect.TypeToken;
7 | import com.google.gson.stream.JsonReader;
8 | import org.itxtech.mcl.Loader;
9 | import org.itxtech.mcl.pkg.MclPackage;
10 |
11 | import java.io.File;
12 | import java.io.FileReader;
13 | import java.io.FileWriter;
14 | import java.io.IOException;
15 | import java.nio.file.Files;
16 | import java.util.ArrayList;
17 | import java.util.HashMap;
18 | import java.util.LinkedHashMap;
19 |
20 | /*
21 | *
22 | * Mirai Console Loader
23 | *
24 | * Copyright (C) 2020-2022 iTX Technologies
25 | *
26 | * This program is free software: you can redistribute it and/or modify
27 | * it under the terms of the GNU Affero General Public License as
28 | * published by the Free Software Foundation, either version 3 of the
29 | * License, or (at your option) any later version.
30 | *
31 | * This program is distributed in the hope that it will be useful,
32 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
33 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34 | * GNU Affero General Public License for more details.
35 | *
36 | * You should have received a copy of the GNU Affero General Public License
37 | * along with this program. If not, see .
38 | *
39 | * @author PeratX
40 | * @website https://github.com/iTXTech/mirai-console-loader
41 | *
42 | */
43 | public class Config {
44 | @SerializedName("module_packages")
45 | public ArrayList modulePackages = new ArrayList<>() {{
46 | add("mcl:org.itxtech.mcl.module.builtin");
47 | }};
48 |
49 | @SerializedName("mirai_repo")
50 | public String miraiRepo = "https://repo.mirai.mamoe.net/keep/mcl";
51 |
52 | @SerializedName("maven_repo")
53 | public ArrayList mavenRepo = new ArrayList<>() {{
54 | add("https://maven.aliyun.com/repository/public");
55 | }};
56 |
57 | public LinkedHashMap packages = new LinkedHashMap<>() {{
58 | new MclPackage("net.mamoe:mirai-console", "maven-stable", MclPackage.TYPE_CORE).addToMap(this);
59 | new MclPackage("net.mamoe:mirai-console-terminal", "maven-stable", MclPackage.TYPE_CORE).addToMap(this);
60 | new MclPackage("net.mamoe:mirai-core-all", "maven-stable", MclPackage.TYPE_CORE).addToMap(this);
61 | }};
62 |
63 | public ArrayList archiveSuffix = new ArrayList<>() {{
64 | add(".zip");
65 | add(".mirai2.jar");
66 | add(".mirai.jar");
67 | add("-all.jar");
68 | add(".jar");
69 | }};
70 |
71 | @SerializedName("disabled_modules")
72 | public ArrayList disabledModules = new ArrayList<>();
73 |
74 | public String proxy = "";
75 |
76 | @SerializedName("log_level")
77 | public int logLevel = Logger.LOG_INFO;
78 |
79 | @SerializedName("modules_props")
80 | public HashMap moduleProps = new HashMap<>();
81 |
82 | public static Config load(File file) {
83 | try {
84 | Config conf = new Gson().fromJson(new JsonReader(new FileReader(file)), new TypeToken() {
85 | }.getType());
86 | if (conf != null) {
87 | for (var entry : conf.packages.entrySet()) {
88 | entry.getValue().id = entry.getKey();
89 | }
90 | return conf;
91 | }
92 | } catch (Exception e) {
93 | if (file.isFile() && file.exists()) {
94 | Loader.getInstance().logger.logException(e);
95 | var bak = new File(file.getAbsolutePath() + "." + System.currentTimeMillis() + ".bak");
96 | try {
97 | Files.copy(file.toPath(), bak.toPath());
98 | } catch (Exception ee) {
99 | Loader.getInstance().logger.logException(ee);
100 | }
101 | Loader.getInstance().logger.error("Invalid configuration file. It has been renamed to " + bak.getAbsolutePath());
102 | }
103 | }
104 | return new Config();
105 | }
106 |
107 | public void save(File file) throws IOException {
108 | var writer = new FileWriter(file);
109 | new GsonBuilder().setPrettyPrinting().create().toJson(this, writer);
110 | writer.close();
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/component/DownloadObserver.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.component;
2 |
3 | /*
4 | *
5 | * Mirai Console Loader
6 | *
7 | * Copyright (C) 2020-2022 iTX Technologies
8 | *
9 | * This program is free software: you can redistribute it and/or modify
10 | * it under the terms of the GNU Affero General Public License as
11 | * published by the Free Software Foundation, either version 3 of the
12 | * License, or (at your option) any later version.
13 | *
14 | * This program is distributed in the hope that it will be useful,
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | * GNU Affero General Public License for more details.
18 | *
19 | * You should have received a copy of the GNU Affero General Public License
20 | * along with this program. If not, see .
21 | *
22 | * @author PeratX
23 | * @website https://github.com/iTXTech/mirai-console-loader
24 | *
25 | */
26 | public interface DownloadObserver {
27 | void updateProgress(int total, int current);
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/component/Downloader.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.component;
2 |
3 | import java.io.File;
4 |
5 | /*
6 | *
7 | * Mirai Console Loader
8 | *
9 | * Copyright (C) 2020-2022 iTX Technologies
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU Affero General Public License as
13 | * published by the Free Software Foundation, either version 3 of the
14 | * License, or (at your option) any later version.
15 | *
16 | * This program is distributed in the hope that it will be useful,
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | * GNU Affero General Public License for more details.
20 | *
21 | * You should have received a copy of the GNU Affero General Public License
22 | * along with this program. If not, see .
23 | *
24 | * @author PeratX
25 | * @website https://github.com/iTXTech/mirai-console-loader
26 | *
27 | */
28 | public interface Downloader {
29 | void download(String url, File file, DownloadObserver observer);
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/component/Logger.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.component;
2 |
3 | /*
4 | *
5 | * Mirai Console Loader
6 | *
7 | * Copyright (C) 2020-2022 iTX Technologies
8 | *
9 | * This program is free software: you can redistribute it and/or modify
10 | * it under the terms of the GNU Affero General Public License as
11 | * published by the Free Software Foundation, either version 3 of the
12 | * License, or (at your option) any later version.
13 | *
14 | * This program is distributed in the hope that it will be useful,
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | * GNU Affero General Public License for more details.
18 | *
19 | * You should have received a copy of the GNU Affero General Public License
20 | * along with this program. If not, see .
21 | *
22 | * @author PeratX
23 | * @website https://github.com/iTXTech/mirai-console-loader
24 | *
25 | */
26 | public interface Logger {
27 | int LOG_DEBUG = 0;
28 | int LOG_INFO = 1;
29 | int LOG_WARNING = 2;
30 | int LOG_ERROR = 3;
31 |
32 | void setLogLevel(int level);
33 |
34 | void log(Object info, int level);
35 |
36 | void debug(Object info);
37 |
38 | void info(Object info);
39 |
40 | void warning(Object warning);
41 |
42 | void error(Object error);
43 |
44 | void println(Object s);
45 |
46 | void print(Object s);
47 |
48 | void logException(Object e);
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/component/Repository.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.component;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.reflect.TypeToken;
5 | import org.itxtech.mcl.Loader;
6 | import org.itxtech.mcl.pkg.MclPackage;
7 | import org.w3c.dom.Document;
8 | import org.xml.sax.InputSource;
9 |
10 | import javax.xml.XMLConstants;
11 | import javax.xml.parsers.DocumentBuilderFactory;
12 | import java.io.File;
13 | import java.io.StringReader;
14 | import java.net.ProxySelector;
15 | import java.net.URI;
16 | import java.net.http.HttpClient;
17 | import java.net.http.HttpRequest;
18 | import java.net.http.HttpResponse;
19 | import java.nio.file.Files;
20 | import java.time.Duration;
21 | import java.util.*;
22 |
23 | /*
24 | *
25 | * Mirai Console Loader
26 | *
27 | * Copyright (C) 2020-2023 iTX Technologies
28 | *
29 | * This program is free software: you can redistribute it and/or modify
30 | * it under the terms of the GNU Affero General Public License as
31 | * published by the Free Software Foundation, either version 3 of the
32 | * License, or (at your option) any later version.
33 | *
34 | * This program is distributed in the hope that it will be useful,
35 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
36 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37 | * GNU Affero General Public License for more details.
38 | *
39 | * You should have received a copy of the GNU Affero General Public License
40 | * along with this program. If not, see .
41 | *
42 | * @author PeratX
43 | * @website https://github.com/iTXTech/mirai-console-loader
44 | *
45 | */
46 | public class Repository {
47 | private static final String USER_AGENT = "iTX Technologies Mirai Console Loader";
48 |
49 | public HttpClient client;
50 |
51 | public Loader loader;
52 |
53 | public Repository(Loader loader) {
54 | this.loader = loader;
55 | client = (loader.getProxy() == null ?
56 | HttpClient.newBuilder() :
57 | HttpClient.newBuilder().proxy(ProxySelector.of(loader.getProxy())))
58 | .followRedirects(HttpClient.Redirect.NORMAL)
59 | .build();
60 | if (loader.getProxy() != null) {
61 | loader.logger.debug("HTTP client initialized with HTTP proxy " + loader.config.proxy);
62 | }
63 | }
64 |
65 | public MclPackageIndex fetchPackageIndex() throws Exception {
66 | return new Gson().fromJson(httpGet("/packages.json"), new TypeToken() {
67 | }.getType());
68 | }
69 |
70 | private static String transformId(String id) {
71 | var arr = id.split(":", 2);
72 | var group = arr[0];
73 | var name = arr[1];
74 | return group.replace(".", "/") + "/" + name;
75 | }
76 |
77 | private static String getPackageFromId(String id) {
78 | return id.split(":", 2)[1];
79 | }
80 |
81 | public PackageInfo fetchPackage(String id) throws Exception {
82 | return new Gson().fromJson(httpGet("/" + transformId(id) + "/package.json"), new TypeToken() {
83 | }.getType());
84 | }
85 |
86 | public Document fetchMavenMetadata(String id) throws Exception {
87 | for (var repo : loader.config.mavenRepo) {
88 | try {
89 | var content = httpGet("/" + transformId(id) + "/maven-metadata.xml", repo);
90 | var factory = DocumentBuilderFactory.newInstance();
91 | factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
92 | return factory.newDocumentBuilder().parse(new InputSource(new StringReader(content)));
93 | } catch (Exception e) {
94 | loader.logger.logException(e);
95 | }
96 | }
97 | throw new Exception("Cannot find valid maven metadata");
98 | }
99 |
100 | public String getLatestVersionFromMaven(String id, String channel) throws Exception {
101 | var data = fetchMavenMetadata(id);
102 | if (channel.contains("-")) {
103 | var kind = SemVer.getVersionKindFromChannel(channel.split("-")[1]);
104 | if (kind != SemVer.VersionKind.Nightly) {
105 | var vers = data.getElementsByTagName("versions").item(0).getChildNodes();
106 | var map = new TreeMap();
107 | for (var i = 0; i < vers.getLength(); i++) {
108 | var ver = vers.item(i).getTextContent().trim();
109 | if (ver.length() > 0 && SemVer.isKind(ver, kind)) {
110 | var semVer = SemVer.parseFromText(ver);
111 | if (semVer != null) {
112 | map.put(semVer, ver);
113 | continue;
114 | }
115 |
116 | loader.logger.warning("Failed to parse version \"" + ver + "\" for \"" + id + "\"");
117 | }
118 | }
119 | if (map.size() == 0) {
120 | loader.logger.error("Cannot find any version matches channel \"" + channel + "`\" for \"" + id + "\", using default version.");
121 | } else {
122 | return map.lastEntry().getValue();
123 | }
124 | }
125 | }
126 | return data.getElementsByTagName("release").item(0).getTextContent();
127 | }
128 |
129 | public Metadata getMetadataFromFile(File file) throws Exception {
130 | return new Gson().fromJson(Files.readString(file.toPath()), new TypeToken() {
131 | }.getType());
132 | }
133 |
134 | public String getSha1Url(MclPackage pkg, PackageInfo info, String jarUrl) {
135 | if (info != null && info.repo != null) {
136 | var repoInfo = info.repo.get(pkg.version);
137 | if (repoInfo != null && repoInfo.sha1 != null && !repoInfo.sha1.isBlank()) {
138 | return repoInfo.sha1;
139 | }
140 | }
141 | return jarUrl + ".sha1";
142 | }
143 |
144 | public String getJarUrl(MclPackage pkg, PackageInfo info) {
145 | if (info != null && info.repo != null) {
146 | var repoInfo = info.repo.get(pkg.version);
147 | if (repoInfo != null && repoInfo.archive != null && !repoInfo.archive.isBlank()) {
148 | return repoInfo.archive;
149 | }
150 | }
151 | for (var repo : loader.config.mavenRepo) {
152 | var base = repo + "/" + transformId(pkg.id) + "/" + pkg.version + "/"
153 | + getPackageFromId(pkg.id) + "-" + pkg.version;
154 | for (var suf : loader.config.archiveSuffix) {
155 | var real = base + suf;
156 | try {
157 | if (httpHead(real).statusCode() == 200) {
158 | return real;
159 | }
160 | } catch (Exception e) {
161 | loader.logger.logException(e);
162 | }
163 | }
164 | }
165 | return "";
166 | }
167 |
168 | public String getMetadataUrl(MclPackage pkg, PackageInfo info) {
169 | if (info != null && info.repo != null) {
170 | var repoInfo = info.repo.get(pkg.version);
171 | if (repoInfo != null && repoInfo.metadata != null && !repoInfo.metadata.isBlank()) {
172 | return repoInfo.metadata;
173 | }
174 | }
175 | for (var repo : loader.config.mavenRepo) {
176 | var url = repo + "/" + transformId(pkg.id) + "/" + pkg.version + "/"
177 | + getPackageFromId(pkg.id) + "-" + pkg.version + ".mirai.metadata";
178 | try {
179 | if (httpHead(url).statusCode() == 200) {
180 | return url;
181 | }
182 | } catch (Exception e) {
183 | loader.logger.logException(e);
184 | }
185 | }
186 | return "";
187 | }
188 |
189 | public HttpResponse httpHead(String url) throws Exception {
190 | loader.logger.debug("HTTP HEAD " + url);
191 | return client.send(
192 | HttpRequest.newBuilder(URI.create(url))
193 | .method("HEAD", HttpRequest.BodyPublishers.noBody())
194 | .timeout(Duration.ofSeconds(30))
195 | .setHeader("User-Agent", USER_AGENT)
196 | .build(),
197 | HttpResponse.BodyHandlers.discarding()
198 | );
199 | }
200 |
201 | public String httpGet(String url) throws Exception {
202 | return httpGet(url, loader.config.miraiRepo);
203 | }
204 |
205 | public String httpGet(String url, String server) throws Exception {
206 | loader.logger.debug("HTTP GET " + server + url);
207 | return client.send(
208 | HttpRequest.newBuilder(URI.create(server + url))
209 | .timeout(Duration.ofSeconds(30))
210 | .setHeader("User-Agent", USER_AGENT)
211 | .build(),
212 | HttpResponse.BodyHandlers.ofString()
213 | ).body();
214 | }
215 |
216 | public static class MclPackageIndex {
217 | public MclPackageIndexMetadata metadata;
218 | public Map packages;
219 | }
220 |
221 | public static class MclPackageIndexMetadata {
222 | public String name;
223 | public long timestamp;
224 | public String commit;
225 | }
226 |
227 | public static class MclPackageIndexInfo {
228 | public String name;
229 | public String description;
230 | public String website;
231 | public String type;
232 | public String defaultChannel;
233 | }
234 |
235 | public static class PackageInfo {
236 | public String name;
237 | public String announcement;
238 | public String type;
239 | public String defaultChannel;
240 | public Map> channels;
241 | public Map repo;
242 |
243 | public String getLatestVersion(String chan) {
244 | var c = channels.get(chan);
245 | return c.get(c.size() - 1);
246 | }
247 |
248 | public String getName(String id) {
249 | return name == null ? id : name;
250 | }
251 | }
252 |
253 | public static class RepoInfo {
254 | public String archive;
255 | public String metadata;
256 | public String sha1;
257 | }
258 |
259 | public static class Metadata {
260 | public String groupId;
261 | public String artifactId;
262 | public String version;
263 | public List dependencies;
264 | }
265 | }
266 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/component/SemVer.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.component;
2 |
3 | import java.util.Objects;
4 |
5 | /*
6 | *
7 | * Mirai Console Loader
8 | *
9 | * Copyright (C) 2020-2022 iTX Technologies
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU Affero General Public License as
13 | * published by the Free Software Foundation, either version 3 of the
14 | * License, or (at your option) any later version.
15 | *
16 | * This program is distributed in the hope that it will be useful,
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | * GNU Affero General Public License for more details.
20 | *
21 | * You should have received a copy of the GNU Affero General Public License
22 | * along with this program. If not, see .
23 | *
24 | * @author JetBrains, PeratX
25 | * @website https://github.com/iTXTech/mirai-console-loader
26 | *
27 | */
28 | public final class SemVer implements Comparable {
29 |
30 | private final String myRawVersion;
31 | private final int myMajor;
32 | private final int myMinor;
33 | private final int myPatch;
34 |
35 | private final String myPreRelease;
36 |
37 | public SemVer(String rawVersion, int major, int minor, int patch) {
38 | this(rawVersion, major, minor, patch, null);
39 | }
40 |
41 | public SemVer(String rawVersion, int major, int minor, int patch, String preRelease) {
42 | myRawVersion = rawVersion;
43 | myMajor = major;
44 | myMinor = minor;
45 | myPatch = patch;
46 | myPreRelease = preRelease;
47 | }
48 |
49 | public String getRawVersion() {
50 | return myRawVersion;
51 | }
52 |
53 | public int getMajor() {
54 | return myMajor;
55 | }
56 |
57 | public int getMinor() {
58 | return myMinor;
59 | }
60 |
61 | public int getPatch() {
62 | return myPatch;
63 | }
64 |
65 | public String getPreRelease() {
66 | return myPreRelease;
67 | }
68 |
69 | public String getParsedVersion() {
70 | return myMajor + "." + myMinor + "." + myPatch + (myPreRelease != null ? "-" + myPreRelease : "");
71 | }
72 |
73 | @Override
74 | public int compareTo(SemVer other) {
75 | int diff = myMajor - other.myMajor;
76 | if (diff != 0) return diff;
77 |
78 | diff = myMinor - other.myMinor;
79 | if (diff != 0) return diff;
80 |
81 | diff = myPatch - other.myPatch;
82 | if (diff != 0) return diff;
83 |
84 | return comparePrerelease(myPreRelease, other.myPreRelease);
85 | }
86 |
87 | public boolean isGreaterOrEqualThan(int major, int minor, int patch) {
88 | if (myMajor != major) return myMajor > major;
89 | if (myMinor != minor) return myMinor > minor;
90 | return myPatch >= patch;
91 | }
92 |
93 | public boolean isGreaterOrEqualThan(SemVer version) {
94 | return compareTo(version) >= 0;
95 | }
96 |
97 | @Override
98 | public boolean equals(Object o) {
99 | if (this == o) return true;
100 | if (o == null || getClass() != o.getClass()) return false;
101 |
102 | SemVer semVer = (SemVer) o;
103 | return myMajor == semVer.myMajor
104 | && myMinor == semVer.myMinor
105 | && myPatch == semVer.myPatch
106 | && Objects.equals(myPreRelease, semVer.myPreRelease);
107 | }
108 |
109 | @Override
110 | public int hashCode() {
111 | int result = myMajor;
112 | result = 31 * result + myMinor;
113 | result = 31 * result + myPatch;
114 | if (myPreRelease != null) {
115 | result = 31 * result + myPreRelease.hashCode();
116 | }
117 | return result;
118 | }
119 |
120 | @Override
121 | public String toString() {
122 | return myRawVersion;
123 | }
124 |
125 | private static int comparePrerelease(String pre1, String pre2) {
126 | if (pre1 == null) {
127 | return pre2 == null ? 0 : 1;
128 | } else if (pre2 == null) {
129 | return -1;
130 | }
131 | int length1 = pre1.length();
132 | int length2 = pre2.length();
133 |
134 | if (length1 == length2 && pre1.equals(pre2)) return 0;
135 |
136 | int start1 = 0;
137 | int start2 = 0;
138 | int diff;
139 |
140 | // compare each segment separately
141 | do {
142 | int end1 = pre1.indexOf('.', start1);
143 | int end2 = pre2.indexOf('.', start2);
144 |
145 | if (end1 < 0) end1 = length1;
146 | if (end2 < 0) end2 = length2;
147 |
148 |
149 | CharSequence segment1 = pre1.subSequence(start1, end1);
150 | CharSequence segment2 = pre2.subSequence(start2, end2);
151 | if (isNotNegativeNumber(segment1)) {
152 | if (!isNotNegativeNumber(segment2)) {
153 | // According to SemVer specification numeric segments has lower precedence
154 | // than non-numeric segments
155 | return -1;
156 | }
157 | diff = compareNumeric(segment1, segment2);
158 | } else if (isNotNegativeNumber(segment2)) {
159 | return 1;
160 | } else {
161 | diff = compare(segment1, segment2, false);
162 | }
163 | start1 = end1 + 1;
164 | start2 = end2 + 1;
165 | }
166 | while (diff == 0 && start1 < length1 && start2 < length2);
167 |
168 | if (diff != 0) return diff;
169 |
170 | return start1 < length1 ? 1 : -1;
171 | }
172 |
173 | private static int compareNumeric(CharSequence segment1, CharSequence segment2) {
174 | int length1 = segment1.length();
175 | int length2 = segment2.length();
176 | int diff = Integer.compare(length1, length2);
177 | for (int i = 0; i < length1 && diff == 0; i++) {
178 | diff = segment1.charAt(i) - segment2.charAt(i);
179 | }
180 | return diff;
181 | }
182 |
183 |
184 | public static SemVer parseFromText(String text) {
185 | if (text == null) return null;
186 |
187 | int majorEndIdx = text.indexOf('.');
188 | if (majorEndIdx < 0) return null;
189 | int major = parseInt(text.substring(0, majorEndIdx), -1);
190 |
191 | int preReleaseIdx = text.indexOf('-');
192 | var hasPreRelease = (preReleaseIdx != -1);
193 | String preRelease = hasPreRelease ? text.substring(preReleaseIdx + 1) : null;
194 | preReleaseIdx = hasPreRelease ? preReleaseIdx : text.length();
195 |
196 | int minorEndIdx = text.indexOf('.', majorEndIdx + 1);
197 | var hasPatch = (minorEndIdx != -1);
198 | minorEndIdx = hasPatch ? minorEndIdx : preReleaseIdx;
199 | int minor = parseInt(text.substring(majorEndIdx + 1, minorEndIdx), -1);
200 | int patch = hasPatch ? parseInt(text.substring(minorEndIdx + 1, preReleaseIdx), -1) : 0;
201 |
202 | if (major >= 0 && minor >= 0 && patch >= 0) {
203 | return new SemVer(text, major, minor, patch, preRelease);
204 | }
205 |
206 | return null;
207 | }
208 |
209 | public static int parseInt(String string, int defaultValue) {
210 | if (string != null) {
211 | try {
212 | return Integer.parseInt(string);
213 | } catch (NumberFormatException ignored) {
214 | }
215 | }
216 | return defaultValue;
217 | }
218 |
219 | public static int compare(CharSequence s1, CharSequence s2, boolean ignoreCase) {
220 | if (s1 == s2) return 0;
221 | if (s1 == null) return -1;
222 | if (s2 == null) return 1;
223 |
224 | int length1 = s1.length();
225 | int length2 = s2.length();
226 | int i = 0;
227 | for (; i < length1 && i < length2; i++) {
228 | int diff = compare(s1.charAt(i), s2.charAt(i), ignoreCase);
229 | if (diff != 0) {
230 | return diff;
231 | }
232 | }
233 | return length1 - length2;
234 | }
235 |
236 | public static int compare(char c1, char c2, boolean ignoreCase) {
237 | // duplicating String.equalsIgnoreCase logic
238 | int d = c1 - c2;
239 | if (d == 0 || !ignoreCase) {
240 | return d;
241 | }
242 | // If characters don't match but case may be ignored,
243 | // try converting both characters to uppercase.
244 | // If the results match, then the comparison scan should
245 | // continue.
246 | char u1 = toUpperCase(c1);
247 | char u2 = toUpperCase(c2);
248 | d = u1 - u2;
249 | if (d != 0) {
250 | // Unfortunately, conversion to uppercase does not work properly
251 | // for the Georgian alphabet, which has strange rules about case
252 | // conversion. So we need to make one last check before
253 | // exiting.
254 | d = toLowerCase(u1) - toLowerCase(u2);
255 | }
256 | return d;
257 | }
258 |
259 | public static char toLowerCase(char a) {
260 | if (a <= 'z') {
261 | return a >= 'A' && a <= 'Z' ? (char) (a + ('a' - 'A')) : a;
262 | }
263 | return Character.toLowerCase(a);
264 | }
265 |
266 | public static char toUpperCase(char a) {
267 | if (a < 'a') return a;
268 | if (a <= 'z') return (char) (a + ('A' - 'a'));
269 | return Character.toUpperCase(a);
270 | }
271 |
272 | public static boolean isNotNegativeNumber(CharSequence s) {
273 | if (s == null) {
274 | return false;
275 | }
276 | for (int i = 0; i < s.length(); i++) {
277 | if (!isDecimalDigit(s.charAt(i))) {
278 | return false;
279 | }
280 | }
281 | return true;
282 | }
283 |
284 | public static boolean isDecimalDigit(char c) {
285 | return c >= '0' && c <= '9';
286 | }
287 |
288 | public enum VersionKind {
289 | Stable(0),
290 | PreRelease(1),
291 | Nightly(2);
292 |
293 | public final int id;
294 |
295 | VersionKind(int id) {
296 | this.id = id;
297 | }
298 | }
299 |
300 | public static VersionKind getVersionKindFromChannel(String kind) {
301 | if (kind.equals("stable")) {
302 | return VersionKind.Stable;
303 | }
304 | if (kind.equals("beta") || kind.equals("prerelease")) {
305 | return VersionKind.PreRelease;
306 | }
307 | return VersionKind.Nightly;
308 | }
309 |
310 | public static VersionKind getVersionKind(String ver) {
311 | ver = ver.toLowerCase();
312 | if (ver.matches("^\\d+\\.\\d+(?:\\.\\d+)?$")) {
313 | return VersionKind.Stable;
314 | }
315 | if ((ver.contains("-m") || ver.contains("-rc") || ver.contains("-beta")) && !ver.contains("-dev")) {
316 | return VersionKind.PreRelease;
317 | }
318 | return VersionKind.Nightly;
319 | }
320 |
321 | public static boolean isKind(String ver, VersionKind kind) {
322 | return getVersionKind(ver).id <= kind.id;
323 | }
324 | }
325 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/impl/DefaultDownloader.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.impl;
2 |
3 | import org.itxtech.mcl.Loader;
4 | import org.itxtech.mcl.component.DownloadObserver;
5 | import org.itxtech.mcl.component.Downloader;
6 |
7 | import java.io.BufferedInputStream;
8 | import java.io.File;
9 | import java.io.FileOutputStream;
10 | import java.net.Proxy;
11 | import java.net.URL;
12 |
13 | /*
14 | *
15 | * Mirai Console Loader
16 | *
17 | * Copyright (C) 2020-2022 iTX Technologies
18 | *
19 | * This program is free software: you can redistribute it and/or modify
20 | * it under the terms of the GNU Affero General Public License as
21 | * published by the Free Software Foundation, either version 3 of the
22 | * License, or (at your option) any later version.
23 | *
24 | * This program is distributed in the hope that it will be useful,
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 | * GNU Affero General Public License for more details.
28 | *
29 | * You should have received a copy of the GNU Affero General Public License
30 | * along with this program. If not, see .
31 | *
32 | * @author PeratX
33 | * @website https://github.com/iTXTech/mirai-console-loader
34 | *
35 | */
36 | public class DefaultDownloader implements Downloader {
37 | private final Loader loader;
38 |
39 | public DefaultDownloader(Loader loader) {
40 | this.loader = loader;
41 | }
42 |
43 | @Override
44 | public void download(String url, File file, DownloadObserver observer) {
45 | try {
46 | var proxy = loader.getProxy();
47 | var connection = proxy == null ? new URL(url).openConnection() : new URL(url).openConnection(new Proxy(Proxy.Type.HTTP, proxy));
48 | var totalLen = connection.getContentLength();
49 | var is = new BufferedInputStream(connection.getInputStream());
50 | var os = new FileOutputStream(file);
51 | var len = 0;
52 | var buff = new byte[1024];
53 | var current = 0;
54 | while ((len = is.read(buff)) != -1) {
55 | os.write(buff, 0, len);
56 | current += len;
57 | if (observer != null) {
58 | observer.updateProgress(totalLen, current);
59 | }
60 | }
61 | os.close();
62 | is.close();
63 | } catch (Throwable e) {
64 | loader.logger.logException(e);
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/impl/DefaultLogger.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.impl;
2 |
3 | import org.fusesource.jansi.Ansi;
4 | import org.itxtech.mcl.component.Logger;
5 |
6 | import java.io.PrintWriter;
7 | import java.io.StringWriter;
8 | import java.text.SimpleDateFormat;
9 | import java.util.Date;
10 |
11 | /*
12 | *
13 | * Mirai Console Loader
14 | *
15 | * Copyright (C) 2020-2022 iTX Technologies
16 | *
17 | * This program is free software: you can redistribute it and/or modify
18 | * it under the terms of the GNU Affero General Public License as
19 | * published by the Free Software Foundation, either version 3 of the
20 | * License, or (at your option) any later version.
21 | *
22 | * This program is distributed in the hope that it will be useful,
23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 | * GNU Affero General Public License for more details.
26 | *
27 | * You should have received a copy of the GNU Affero General Public License
28 | * along with this program. If not, see .
29 | *
30 | * @author PeratX
31 | * @website https://github.com/iTXTech/mirai-console-loader
32 | *
33 | */
34 | public class DefaultLogger implements Logger {
35 | protected int logLevel = LOG_DEBUG;
36 |
37 | @Override
38 | public void setLogLevel(int logLevel) {
39 | this.logLevel = logLevel;
40 | }
41 |
42 | @Override
43 | public void info(Object info) {
44 | log(info, LOG_INFO);
45 | }
46 |
47 | @Override
48 | public void debug(Object info) {
49 | log(info, LOG_DEBUG);
50 | }
51 |
52 | @Override
53 | public void warning(Object info) {
54 | log(info, LOG_WARNING);
55 | }
56 |
57 | @Override
58 | public void error(Object info) {
59 | log(info, LOG_ERROR);
60 | }
61 |
62 | @Override
63 | public void logException(Object e) {
64 | Object oe = e;
65 | if (e == null) e = oe;
66 | if (e instanceof Throwable) {
67 | error(getExceptionMessage((Throwable) e));
68 | } else {
69 | error(String.valueOf(e));
70 | }
71 | }
72 |
73 | public static String getExceptionMessage(Throwable e) {
74 | var stringWriter = new StringWriter();
75 | var printWriter = new PrintWriter(stringWriter);
76 | e.printStackTrace(printWriter);
77 | return stringWriter.toString();
78 | }
79 |
80 | @Override
81 | public void log(Object info, int level) {
82 | if (level < logLevel) {
83 | return;
84 | }
85 | var ansi = Ansi.ansi().a(" ");
86 | String prefix;
87 | var date = new SimpleDateFormat("HH:mm:ss").format(new Date());
88 | switch (level) {
89 | case LOG_DEBUG:
90 | ansi = ansi.fgBrightBlack();
91 | prefix = "DEBUG";
92 | break;
93 | case LOG_WARNING:
94 | ansi = ansi.fgBrightYellow();
95 | prefix = "WARN";
96 | break;
97 | case LOG_ERROR:
98 | ansi = ansi.fgBrightRed();
99 | prefix = "ERROR";
100 | break;
101 | case LOG_INFO:
102 | default:
103 | ansi = ansi.fgBrightGreen();
104 | prefix = "INFO";
105 | break;
106 | }
107 | ansi.a(" ").a(date).a(" [").a(prefix).a("] ");
108 | if (level == LOG_INFO) ansi.reset();
109 | ansi.a(info);
110 | ansi.reset();
111 | if (level == LOG_ERROR) {
112 | System.err.println(ansi);
113 | } else {
114 | System.out.println(ansi);
115 | }
116 | }
117 |
118 | @Override
119 | public void print(Object s) {
120 | System.out.print(s);
121 | }
122 |
123 | @Override
124 | public void println(Object s) {
125 | System.out.println(s);
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/module/MclModule.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.module;
2 |
3 | import org.itxtech.mcl.Loader;
4 |
5 | /*
6 | *
7 | * Mirai Console Loader
8 | *
9 | * Copyright (C) 2020-2022 iTX Technologies
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU Affero General Public License as
13 | * published by the Free Software Foundation, either version 3 of the
14 | * License, or (at your option) any later version.
15 | *
16 | * This program is distributed in the hope that it will be useful,
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | * GNU Affero General Public License for more details.
20 | *
21 | * You should have received a copy of the GNU Affero General Public License
22 | * along with this program. If not, see .
23 | *
24 | * @author PeratX
25 | * @website https://github.com/iTXTech/mirai-console-loader
26 | *
27 | */
28 | public abstract class MclModule {
29 | protected Loader loader;
30 |
31 | public final void init(Loader l) {
32 | loader = l;
33 | }
34 |
35 | public abstract String getName();
36 |
37 | public void prepare() {
38 | }
39 |
40 | public void load() {
41 | }
42 |
43 | public void cli() {
44 | }
45 |
46 | public void boot() {
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/module/ModuleManager.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.module;
2 |
3 | import org.apache.commons.cli.Option;
4 | import org.apache.commons.cli.OptionGroup;
5 | import org.itxtech.mcl.Agent;
6 | import org.itxtech.mcl.Loader;
7 |
8 | import java.io.File;
9 | import java.util.HashMap;
10 | import java.util.ServiceLoader;
11 | import java.util.jar.JarFile;
12 |
13 | /*
14 | *
15 | * Mirai Console Loader
16 | *
17 | * Copyright (C) 2020-2022 iTX Technologies
18 | *
19 | * This program is free software: you can redistribute it and/or modify
20 | * it under the terms of the GNU Affero General Public License as
21 | * published by the Free Software Foundation, either version 3 of the
22 | * License, or (at your option) any later version.
23 | *
24 | * This program is distributed in the hope that it will be useful,
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 | * GNU Affero General Public License for more details.
28 | *
29 | * You should have received a copy of the GNU Affero General Public License
30 | * along with this program. If not, see .
31 | *
32 | * @author PeratX
33 | * @website https://github.com/iTXTech/mirai-console-loader
34 | *
35 | */
36 | public class ModuleManager {
37 | private final Loader loader;
38 | private final HashMap modules = new HashMap<>();
39 |
40 | public ModuleManager(Loader loader) {
41 | this.loader = loader;
42 |
43 | var group = new OptionGroup();
44 | group.addOption(Option.builder("l").longOpt("list-disabled-modules")
45 | .desc("List disabled modules").build());
46 | group.addOption(Option.builder("e").longOpt("enable-module")
47 | .desc("Enable module").hasArg().argName("ModuleName").build());
48 | group.addOption(Option.builder("d").longOpt("disable-module")
49 | .desc("Disable module").hasArg().argName("ModuleName").build());
50 | loader.options.addOptionGroup(group);
51 | }
52 |
53 | public MclModule getModule(String name) {
54 | return modules.get(name);
55 | }
56 |
57 | public void loadAllModules() throws Exception {
58 | if (loader.cli.hasOption("l")) {
59 | loader.logger.info("Disabled modules: " + String.join(", ", loader.config.disabledModules));
60 | return;
61 | }
62 | if (loader.cli.hasOption("d")) {
63 | var name = loader.cli.getOptionValue("d");
64 | if (!loader.config.disabledModules.contains(name)) {
65 | loader.config.disabledModules.add(name);
66 | }
67 | loader.logger.info("Module \"" + name + "\" has been disabled.");
68 | return;
69 | }
70 | if (loader.cli.hasOption("e")) {
71 | var name = loader.cli.getOptionValue("e");
72 | loader.config.disabledModules.remove(name);
73 | loader.logger.info("Module \"" + name + "\" has been enabled.");
74 | return;
75 | }
76 |
77 | var folder = new File("modules");
78 | folder.mkdir();
79 |
80 | var list = folder.listFiles(file -> file.getName().endsWith(".jar"));
81 | if (list != null) {
82 | for (var file : list) {
83 | var jar = new JarFile(file);
84 | Agent.appendJarFile(jar);
85 | }
86 | }
87 |
88 | var serviceLoader = ServiceLoader.load(MclModule.class);
89 |
90 | serviceLoader.stream().forEach(provider -> {
91 | try {
92 | var module = provider.get();
93 | if (!loader.config.disabledModules.contains(module.getName())) {
94 | loader.logger.debug("Loading module: \"" + module.getName() + "\". Class: " + module.getClass().getCanonicalName());
95 | modules.put(module.getName(), module);
96 |
97 | module.init(loader);
98 | module.prepare();
99 | }
100 | } catch (Exception e) {
101 | loader.logger.logException(e);
102 | }
103 | });
104 | if (modules.size() == 0) {
105 | loader.logger.warning("No module has been loaded. Exiting.");
106 | }
107 | }
108 |
109 | public void phaseCli() {
110 | for (var module : modules.values()) {
111 | module.cli();
112 | }
113 | }
114 |
115 | public void phaseLoad() {
116 | for (var module : modules.values()) {
117 | module.load();
118 | }
119 | }
120 |
121 | public void phaseBoot() {
122 | for (var module : modules.values()) {
123 | module.boot();
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/module/builtin/Addon.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.module.builtin;
2 |
3 | import org.itxtech.mcl.module.MclModule;
4 | import org.itxtech.mcl.pkg.MclPackage;
5 |
6 | /*
7 | *
8 | * Mirai Console Loader
9 | *
10 | * Copyright (C) 2020-2022 iTX Technologies
11 | *
12 | * This program is free software: you can redistribute it and/or modify
13 | * it under the terms of the GNU Affero General Public License as
14 | * published by the Free Software Foundation, either version 3 of the
15 | * License, or (at your option) any later version.
16 | *
17 | * This program is distributed in the hope that it will be useful,
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 | * GNU Affero General Public License for more details.
21 | *
22 | * You should have received a copy of the GNU Affero General Public License
23 | * along with this program. If not, see .
24 | *
25 | * @author PeratX
26 | * @website https://github.com/iTXTech/mirai-console-loader
27 | *
28 | */
29 | public class Addon extends MclModule {
30 | private static final String CURRENT_CHANNEL = "maven-stable";
31 | private static final String ADDON_ID = "org.itxtech:mcl-addon";
32 |
33 | @Override
34 | public String getName() {
35 | return "addon";
36 | }
37 |
38 | @Override
39 | public void prepare() {
40 | if (loader.packageManager.hasPackage(ADDON_ID)) {
41 | loader.packageManager.getPackage(ADDON_ID).channel = CURRENT_CHANNEL;
42 | } else {
43 | var p = new MclPackage("org.itxtech:mcl-addon", CURRENT_CHANNEL);
44 | p.type = MclPackage.TYPE_PLUGIN;
45 | loader.packageManager.addPackage(p);
46 | loader.logger.info("MCL Addon is installed! Website: https://github.com/iTXTech/mcl-addon");
47 | loader.logger.warning("To remove MCL Addon, run \"./mcl --disable-module addon\" and \"./mcl --remove-package org.itxtech:mcl-addon\"");
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/module/builtin/Announcement.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.module.builtin;
2 |
3 | import org.itxtech.mcl.module.MclModule;
4 |
5 | /*
6 | *
7 | * Mirai Console Loader
8 | *
9 | * Copyright (C) 2020-2022 iTX Technologies
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU Affero General Public License as
13 | * published by the Free Software Foundation, either version 3 of the
14 | * License, or (at your option) any later version.
15 | *
16 | * This program is distributed in the hope that it will be useful,
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | * GNU Affero General Public License for more details.
20 | *
21 | * You should have received a copy of the GNU Affero General Public License
22 | * along with this program. If not, see .
23 | *
24 | * @author PeratX
25 | * @website https://github.com/iTXTech/mirai-console-loader
26 | *
27 | */
28 | public class Announcement extends MclModule {
29 | @Override
30 | public String getName() {
31 | return "announcement";
32 | }
33 |
34 | @Override
35 | public void load() {
36 | loader.logger.debug("Fetching MCL Announcement...");
37 | try {
38 | var pkg = loader.repo.fetchPackage("org.itxtech:mcl");
39 | loader.logger.info("Mirai Console Loader Announcement:");
40 | loader.logger.println(pkg.announcement);
41 | } catch (Exception e) {
42 | loader.logger.error("Failed to fetch MCL announcement.");
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/module/builtin/Boot.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.module.builtin;
2 |
3 | import org.apache.commons.cli.Option;
4 | import org.itxtech.mcl.Utility;
5 | import org.itxtech.mcl.module.MclModule;
6 | import org.itxtech.mcl.pkg.MclPackage;
7 |
8 | import java.io.File;
9 | import java.util.ArrayList;
10 | import java.util.HashMap;
11 |
12 | /*
13 | *
14 | * Mirai Console Loader
15 | *
16 | * Copyright (C) 2020-2022 iTX Technologies
17 | *
18 | * This program is free software: you can redistribute it and/or modify
19 | * it under the terms of the GNU Affero General Public License as
20 | * published by the Free Software Foundation, either version 3 of the
21 | * License, or (at your option) any later version.
22 | *
23 | * This program is distributed in the hope that it will be useful,
24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 | * GNU Affero General Public License for more details.
27 | *
28 | * You should have received a copy of the GNU Affero General Public License
29 | * along with this program. If not, see .
30 | *
31 | * @author PeratX
32 | * @website https://github.com/iTXTech/mirai-console-loader
33 | *
34 | */
35 | public class Boot extends MclModule {
36 | public static final HashMap depMap = new HashMap<>() {{
37 | put("net.mamoe:mirai-core", "net.mamoe:mirai-core-all");
38 | }};
39 |
40 | @Override
41 | public String getName() {
42 | return "boot";
43 | }
44 |
45 | public String getBootEntry() {
46 | return loader.config.moduleProps.getOrDefault("boot.entry",
47 | "net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader");
48 | }
49 |
50 | public String getBootArgs() {
51 | return loader.config.moduleProps.getOrDefault("boot.args", "");
52 | }
53 |
54 | @Override
55 | public void prepare() {
56 | loader.options.addOption(Option.builder("b").desc("Show Mirai Console boot properties")
57 | .longOpt("show-boot-props").build());
58 | loader.options.addOption(Option.builder("f").desc("Set Mirai Console boot entry")
59 | .longOpt("set-boot-entry").hasArg().argName("EntryClass").build());
60 | loader.options.addOption(Option.builder("g").desc("Set Mirai Console boot arguments")
61 | .longOpt("set-boot-args").optionalArg(true).hasArg().argName("Arguments").build());
62 | }
63 |
64 | @Override
65 | public void cli() {
66 | if (loader.cli.hasOption("f")) {
67 | loader.config.moduleProps.put("boot.entry", loader.cli.getOptionValue("f"));
68 | loader.saveConfig();
69 | }
70 | if (loader.cli.hasOption("g")) {
71 | loader.config.moduleProps.put("boot.args", loader.cli.getOptionValue("g", ""));
72 | loader.saveConfig();
73 | }
74 | if (loader.cli.hasOption("b")) {
75 | loader.logger.info("Mirai Console boot entry: " + getBootEntry());
76 | loader.logger.info("Mirai Console boot arguments: " + getBootArgs());
77 | loader.exit(0);
78 | }
79 | }
80 |
81 | @Override
82 | public void boot() {
83 | try {
84 | var files = new ArrayList();
85 | var pkgMap = new HashMap();
86 | for (var pkg : loader.packageManager.getPackages()) {
87 | if (pkg.type.equals(MclPackage.TYPE_CORE)) {
88 | if (pkg.id.equals("org.bouncycastle:bcprov-jdk15on")) {
89 | files.add(0, pkg.getJarFile());
90 | } else {
91 | files.add(pkg.getJarFile());
92 | }
93 | pkgMap.put(pkg.id, pkg.version);
94 | }
95 | if (pkg.type.equals(MclPackage.TYPE_PLUGIN)) {
96 | var metadata = pkg.getMetadataFile();
97 | if (metadata.exists()) {
98 | for (var s : loader.repo.getMetadataFromFile(metadata).dependencies) {
99 | var dep = s.split(":");
100 | var name = dep[0] + ":" + dep[1];
101 | var version = dep[2];
102 | var realPkg = depMap.getOrDefault(name, name);
103 | for (var corePkg : pkgMap.entrySet()) {
104 | if (corePkg.getKey().equals(realPkg) && !corePkg.getValue().equals(version)) {
105 | loader.logger.warning("Package \"" + pkg.id + "\" requires \"" + name + "\" version " + version + ". Current version is " + corePkg.getValue());
106 | }
107 | }
108 | }
109 | }
110 | }
111 | }
112 |
113 | Utility.bootMirai(files, getBootEntry(), getBootArgs());
114 | } catch (Exception e) {
115 | loader.logger.logException(e);
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/module/builtin/Conf.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.module.builtin;
2 |
3 | import org.apache.commons.cli.Option;
4 | import org.apache.commons.cli.OptionGroup;
5 | import org.itxtech.mcl.module.MclModule;
6 | import org.itxtech.mcl.pkg.MclPackage;
7 |
8 | /*
9 | *
10 | * Mirai Console Loader
11 | *
12 | * Copyright (C) 2020-2022 iTX Technologies
13 | *
14 | * This program is free software: you can redistribute it and/or modify
15 | * it under the terms of the GNU Affero General Public License as
16 | * published by the Free Software Foundation, either version 3 of the
17 | * License, or (at your option) any later version.
18 | *
19 | * This program is distributed in the hope that it will be useful,
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 | * GNU Affero General Public License for more details.
23 | *
24 | * You should have received a copy of the GNU Affero General Public License
25 | * along with this program. If not, see .
26 | *
27 | * @author PeratX
28 | * @website https://github.com/iTXTech/mirai-console-loader
29 | *
30 | */
31 | public class Conf extends MclModule {
32 | @Override
33 | public String getName() {
34 | return "config";
35 | }
36 |
37 | @Override
38 | public void prepare() {
39 | loader.options.addOption(Option.builder("p").desc("Set HTTP proxy")
40 | .longOpt("proxy").optionalArg(true).hasArg().argName("address").build());
41 | loader.options.addOption(Option.builder("o").desc("Show Mirai Repo and Maven Repo")
42 | .longOpt("show-repos").build());
43 | loader.options.addOption(Option.builder("m").desc("Set Mirai Repo address")
44 | .longOpt("set-mirai-repo").hasArg().argName("Address").build());
45 | loader.options.addOption(Option.builder("c").desc("Set log level")
46 | .longOpt("log-level").hasArg().argName("level").build());
47 | var group = new OptionGroup();
48 | group.addOption(Option.builder("s").desc("List configured packages")
49 | .longOpt("list-packages").build());
50 | group.addOption(Option.builder("r").desc("Remove package")
51 | .longOpt("remove-package").hasArg().argName("PackageName").build());
52 | group.addOption(Option.builder("a").desc("Add or update package")
53 | .longOpt("update-package").hasArg().argName("PackageName").build());
54 | loader.options.addOptionGroup(group);
55 | loader.options.addOption(Option.builder("n").desc("Set update channel of package")
56 | .longOpt("channel").hasArg().argName("Channel").build());
57 | loader.options.addOption(Option.builder("t").desc("Set type of package")
58 | .longOpt("type").hasArg().argName("Type").build());
59 | loader.options.addOption(Option.builder("w").desc("Set version of package")
60 | .longOpt("version").hasArg().argName("Version").build());
61 | var lockGroup = new OptionGroup();
62 | lockGroup.addOption(Option.builder("x").desc("Lock version of package")
63 | .longOpt("lock").build());
64 | lockGroup.addOption(Option.builder("y").desc("Unlock version of package")
65 | .longOpt("unlock").build());
66 | loader.options.addOptionGroup(lockGroup);
67 | }
68 |
69 | @Override
70 | public void cli() {
71 | if (loader.cli.hasOption("p")) {
72 | loader.config.proxy = loader.cli.getOptionValue("p", "");
73 | loader.saveConfig();
74 | }
75 | if (loader.cli.hasOption("o")) {
76 | loader.logger.info("Mirai Repo: " + loader.config.miraiRepo);
77 | loader.logger.info("Maven Repo: " + loader.config.mavenRepo);
78 | loader.exit(0);
79 | return;
80 | }
81 | if (loader.cli.hasOption("m")) {
82 | loader.config.miraiRepo = loader.cli.getOptionValue("m");
83 | loader.saveConfig();
84 | }
85 | if (loader.cli.hasOption("c")) {
86 | var lvl = Integer.parseInt(loader.cli.getOptionValue("c"));
87 | loader.logger.setLogLevel(lvl);
88 | loader.config.logLevel = lvl;
89 | loader.saveConfig();
90 | }
91 | if (loader.cli.hasOption("s")) {
92 | for (var pkg : loader.packageManager.getPackages()) {
93 | loader.logger.info("Package: " + pkg.id + " Channel: " + pkg.channel + " Type: " + pkg.type +
94 | " Version: " + pkg.version + " Locked: " + (pkg.versionLocked ? "true" : "false"));
95 | }
96 | loader.exit(0);
97 | return;
98 | }
99 | if (loader.cli.hasOption("r")) {
100 | var name = loader.cli.getOptionValue("r");
101 | var pkg = loader.packageManager.getPackage(name);
102 | if (pkg != null) {
103 | pkg.removeFiles();
104 | loader.packageManager.removePackage(name);
105 | loader.logger.info("Package \"" + pkg.id + "\" has been removed.");
106 | loader.saveConfig();
107 | loader.exit(0);
108 | return;
109 | }
110 | loader.logger.error("Package \"" + name + "\" not found.");
111 | loader.exit(1);
112 | return;
113 | }
114 | if (loader.cli.hasOption("a")) {
115 | var name = loader.cli.getOptionValue("a");
116 | if (!name.contains(":")) {
117 | loader.logger.error("Invalid package \"" + name + "\"");
118 | } else {
119 | var pkg = loader.packageManager.getPackage(name);
120 | if (pkg != null) {
121 | updatePackage(pkg);
122 | loader.logger.info("Package \"" + pkg.id + "\" has been updated.");
123 | loader.saveConfig();
124 | loader.exit(0);
125 | return;
126 | }
127 | pkg = new MclPackage(name);
128 | updatePackage(pkg);
129 | loader.packageManager.addPackage(pkg);
130 | loader.logger.info("Package \"" + pkg.id + "\" has been added.");
131 | loader.saveConfig();
132 | }
133 | loader.exit(0);
134 | }
135 | }
136 |
137 | public void updatePackage(MclPackage pkg) {
138 | if (loader.cli.hasOption("n")) {
139 | pkg.channel = loader.cli.getOptionValue("n");
140 | }
141 | if (loader.cli.hasOption("t")) {
142 | pkg.type = MclPackage.getType(loader.cli.getOptionValue("t"));
143 | }
144 | if (loader.cli.hasOption("w")) {
145 | pkg.version = loader.cli.getOptionValue("w");
146 | }
147 | if (loader.cli.hasOption("x")) {
148 | if (pkg.version.trim().equals("")) {
149 | loader.logger.warning("Invalid version \"" + pkg.version + "\" for \"" + pkg.id + "\".");
150 | }
151 | pkg.versionLocked = true;
152 | }
153 | if (loader.cli.hasOption("y")) {
154 | pkg.versionLocked = false;
155 | }
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/module/builtin/MDownloader.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.module.builtin;
2 |
3 | import org.apache.commons.cli.Option;
4 | import org.itxtech.mcl.Loader;
5 | import org.itxtech.mcl.component.DownloadObserver;
6 | import org.itxtech.mcl.component.Downloader;
7 | import org.itxtech.mcl.module.MclModule;
8 |
9 | import java.io.*;
10 | import java.net.Proxy;
11 | import java.net.URL;
12 | import java.util.ArrayList;
13 | import java.util.concurrent.Executors;
14 | import java.util.concurrent.ThreadPoolExecutor;
15 |
16 | /*
17 | *
18 | * Mirai Console Loader
19 | *
20 | * Copyright (C) 2020-2022 iTX Technologies
21 | *
22 | * This program is free software: you can redistribute it and/or modify
23 | * it under the terms of the GNU Affero General Public License as
24 | * published by the Free Software Foundation, either version 3 of the
25 | * License, or (at your option) any later version.
26 | *
27 | * This program is distributed in the hope that it will be useful,
28 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 | * GNU Affero General Public License for more details.
31 | *
32 | * You should have received a copy of the GNU Affero General Public License
33 | * along with this program. If not, see .
34 | *
35 | * @author PeratX
36 | * @website https://github.com/iTXTech/mirai-console-loader
37 | *
38 | */
39 | public class MDownloader extends MclModule {
40 | private static final String MAX_THREADS_KEY = "mdownloader.max-threads";
41 |
42 | @Override
43 | public String getName() {
44 | return "mdownloader";
45 | }
46 |
47 | @Override
48 | public void prepare() {
49 | loader.options.addOption(Option.builder().desc("Set Max Threads of Multithreading Downloader")
50 | .longOpt("set-max-threads").hasArg().argName("MaxThreads").build());
51 | }
52 |
53 | @Override
54 | public void cli() {
55 | if (loader.cli.hasOption("set-max-threads")) {
56 | try {
57 | var t = loader.cli.getOptionValue("set-max-threads");
58 | Integer.parseInt(t);
59 | loader.config.moduleProps.put(MAX_THREADS_KEY, t);
60 | } catch (Exception ignored) {
61 | loader.logger.error("Invalid Max Threads value");
62 | }
63 | }
64 | loader.downloader = new MultithreadingDownloaderImpl(loader.downloader,
65 | Integer.parseInt(loader.config.moduleProps.getOrDefault(MAX_THREADS_KEY, "16")));
66 | }
67 |
68 | public static class MultithreadingDownloaderImpl implements Downloader {
69 | private static final int MIN_SIZE = 2 * 1024 * 1024; // < 2MB
70 |
71 | private int maxThreads;
72 | private Downloader defaultDownloader;
73 |
74 | public MultithreadingDownloaderImpl(Downloader defaultDownloader, int maxThreads) {
75 | this.maxThreads = maxThreads;
76 | this.defaultDownloader = defaultDownloader;
77 | }
78 |
79 | @Override
80 | public void download(String url, File file, DownloadObserver observer) {
81 | var loader = Loader.getInstance();
82 | try {
83 | var header = loader.repo.httpHead(url);
84 | var len = header.headers().firstValueAsLong("Content-Length").getAsLong();
85 | if (len < MIN_SIZE) {
86 | defaultDownloader.download(url, file, observer);
87 | } else {
88 | var executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(maxThreads);
89 | var start = 0L;
90 | var end = 0L;
91 | var part = len / maxThreads;
92 | var list = new ArrayList();
93 | while (start < len) {
94 | end = Math.min(len - 1, start + part);
95 | var task = new DownloadTask(start, end, url);
96 | list.add(task);
97 | executor.submit(task);
98 | start = end + 1;
99 | }
100 | while (executor.getActiveCount() > 0) {
101 | var sum = 0;
102 | for (var task : list) {
103 | sum += task.read;
104 | }
105 | if (observer != null) {
106 | observer.updateProgress((int) len, sum);
107 | }
108 | Thread.sleep(100);
109 | }
110 | var os = new BufferedOutputStream(new FileOutputStream(file));
111 | for (var task : list) {
112 | os.write(task.out.toByteArray());
113 | }
114 | os.flush();
115 | os.close();
116 | executor.shutdownNow();
117 | }
118 | } catch (Exception e) {
119 | loader.logger.error(e);
120 | }
121 | }
122 | }
123 |
124 | private static class DownloadTask implements Runnable {
125 | private long start;
126 | private long end;
127 | private long contentLen;
128 | private String url;
129 | public long read;
130 | public ByteArrayOutputStream out;
131 |
132 | public DownloadTask(long start, long end, String url) {
133 | this.start = start;
134 | this.end = end;
135 | contentLen = end - start + 1;
136 | this.url = url;
137 | this.read = 0;
138 | out = new ByteArrayOutputStream();
139 | }
140 |
141 | @Override
142 | public void run() {
143 | try {
144 | var proxy = Loader.getInstance().getProxy();
145 | var connection = proxy == null ? new URL(url).openConnection() :
146 | new URL(url).openConnection(new Proxy(Proxy.Type.HTTP, proxy));
147 | connection.setRequestProperty("Range", "bytes=" + start + "-" + end);
148 | connection.connect();
149 | var is = new BufferedInputStream(connection.getInputStream());
150 | var os = new BufferedOutputStream(out);
151 | var len = 0;
152 | var buff = new byte[1024];
153 | while (read < contentLen && (len = is.read(buff)) != -1) {
154 | os.write(buff, 0, len);
155 | read += len;
156 | }
157 | os.flush();
158 | os.close();
159 | is.close();
160 | } catch (Throwable e) {
161 | Loader.getInstance().logger.logException(e);
162 | }
163 | }
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/module/builtin/Mrm.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.module.builtin;
2 |
3 | import org.apache.commons.cli.Option;
4 | import org.fusesource.jansi.Ansi;
5 | import org.itxtech.mcl.module.MclModule;
6 |
7 | import java.util.HashMap;
8 |
9 | /*
10 | *
11 | * Mirai Console Loader
12 | *
13 | * Copyright (C) 2020-2022 iTX Technologies
14 | *
15 | * This program is free software: you can redistribute it and/or modify
16 | * it under the terms of the GNU Affero General Public License as
17 | * published by the Free Software Foundation, either version 3 of the
18 | * License, or (at your option) any later version.
19 | *
20 | * This program is distributed in the hope that it will be useful,
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | * GNU Affero General Public License for more details.
24 | *
25 | * You should have received a copy of the GNU Affero General Public License
26 | * along with this program. If not, see .
27 | *
28 | * @author PeratX
29 | * @website https://github.com/iTXTech/mirai-console-loader
30 | *
31 | */
32 | public class Mrm extends MclModule {
33 |
34 | // Mirai Repo Manager
35 |
36 | static class Repo {
37 | public String url;
38 | public String desc;
39 |
40 | public Repo(String u, String d) {
41 | url = u;
42 | desc = d;
43 | }
44 | }
45 |
46 | public final static HashMap repos = new HashMap<>() {{
47 | put("itx", new Repo("https://repo.itxtech.org", "Hosted by iTX Technologies"));
48 | put("github", new Repo("https://mcl.repo.mamoe.net", "Hosted on GitHub Pages"));
49 | put("mamoeRepo", new Repo("https://repo.mirai.mamoe.net/keep/mcl", "Hosted by Mamoe Technologies; Mamoe Repo Server"));
50 | put("forum", new Repo("https://mirai.mamoe.net/assets/mcl", "Hosted by Mamoe Technologies; Mirai Forum"));
51 | }};
52 |
53 | @Override
54 | public String getName() {
55 | return "mrm";
56 | }
57 |
58 | @Override
59 | public void prepare() {
60 | loader.options.addOption(Option.builder().desc("List all builtin Mirai Repo")
61 | .longOpt("mrm-list").build());
62 |
63 | loader.options.addOption(Option.builder().desc("Change Mirai Repo")
64 | .longOpt("mrm-use").hasArg().argName("RepoId").build());
65 | }
66 |
67 | @Override
68 | public void cli() {
69 | if (loader.cli.hasOption("mrm-list")) {
70 | loader.logger.info("");
71 | for (var repo : repos.entrySet()) {
72 | loader.logger.info(Ansi.ansi().a(repo.getKey()).a(" - ").fgBrightCyan()
73 | .a(repo.getValue().url).reset().a(" - ").a(repo.getValue().desc));
74 | }
75 | loader.exit(0);
76 | return;
77 | }
78 | if (loader.cli.hasOption("mrm-use")) {
79 | var id = loader.cli.getOptionValue("mrm-use");
80 | var r = repos.get(id);
81 | if (r == null) {
82 | loader.logger.error("Fail to find Mirai Repo \"" + id + "\"");
83 | } else {
84 | loader.config.miraiRepo = r.url;
85 | loader.saveConfig();
86 | }
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/module/builtin/OracleJdk.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.module.builtin;
2 |
3 | import org.itxtech.mcl.module.MclModule;
4 | import org.itxtech.mcl.pkg.MclPackage;
5 |
6 | /*
7 | *
8 | * Mirai Console Loader
9 | *
10 | * Copyright (C) 2020-2022 iTX Technologies
11 | *
12 | * This program is free software: you can redistribute it and/or modify
13 | * it under the terms of the GNU Affero General Public License as
14 | * published by the Free Software Foundation, either version 3 of the
15 | * License, or (at your option) any later version.
16 | *
17 | * This program is distributed in the hope that it will be useful,
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 | * GNU Affero General Public License for more details.
21 | *
22 | * You should have received a copy of the GNU Affero General Public License
23 | * along with this program. If not, see .
24 | *
25 | * @author PeratX
26 | * @website https://github.com/iTXTech/mirai-console-loader
27 | *
28 | */
29 | public class OracleJdk extends MclModule {
30 | private static final String BC_ID = "org.bouncycastle:bcprov-jdk15on";
31 |
32 | @Override
33 | public String getName() {
34 | return "oraclejdk";
35 | }
36 |
37 | @Override
38 | public void prepare() {
39 | if (System.getProperty("java.vm.vendor").contains("Oracle")) {
40 | var pkgs = loader.config.packages;
41 | if (!loader.packageManager.hasPackage(BC_ID)) {
42 | var p = new MclPackage("org.bouncycastle:bcprov-jdk15on");
43 | p.type = MclPackage.TYPE_CORE;
44 | loader.packageManager.addPackage(p);
45 | loader.logger.info("BouncyCastle is installed because OracleJDK is detected.");
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/module/builtin/PkgAnn.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.module.builtin;
2 |
3 | import org.fusesource.jansi.Ansi;
4 | import org.itxtech.mcl.module.MclModule;
5 |
6 | /*
7 | *
8 | * Mirai Console Loader
9 | *
10 | * Copyright (C) 2020-2022 iTX Technologies
11 | *
12 | * This program is free software: you can redistribute it and/or modify
13 | * it under the terms of the GNU Affero General Public License as
14 | * published by the Free Software Foundation, either version 3 of the
15 | * License, or (at your option) any later version.
16 | *
17 | * This program is distributed in the hope that it will be useful,
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 | * GNU Affero General Public License for more details.
21 | *
22 | * You should have received a copy of the GNU Affero General Public License
23 | * along with this program. If not, see .
24 | *
25 | * @author PeratX
26 | * @website https://github.com/iTXTech/mirai-console-loader
27 | *
28 | */
29 | public class PkgAnn extends MclModule {
30 | @Override
31 | public String getName() {
32 | return "pkgann";
33 | }
34 |
35 | @Override
36 | public void load() {
37 | for (var pkg : loader.packageManager.getPackages()) {
38 | try {
39 | if (!pkg.channel.startsWith("maven")) {
40 | var info = loader.repo.fetchPackage(pkg.id);
41 | if (info.announcement != null) {
42 | loader.logger.info(Ansi.ansi().fgBrightYellow().a(info.getName(pkg.id)).reset().a(" Announcement:"));
43 | loader.logger.println(info.announcement);
44 | }
45 | }
46 | } catch (Exception e) {
47 | loader.logger.error("Failed to fetch announcement for \"" + pkg.id + "\"");
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/module/builtin/Repo.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.module.builtin;
2 |
3 | import org.apache.commons.cli.Option;
4 | import org.apache.commons.cli.OptionGroup;
5 | import org.itxtech.mcl.Utility;
6 | import org.itxtech.mcl.module.MclModule;
7 |
8 | import java.sql.Timestamp;
9 | import java.time.format.DateTimeFormatter;
10 |
11 | /*
12 | *
13 | * Mirai Console Loader
14 | *
15 | * Copyright (C) 2020-2022 iTX Technologies
16 | *
17 | * This program is free software: you can redistribute it and/or modify
18 | * it under the terms of the GNU Affero General Public License as
19 | * published by the Free Software Foundation, either version 3 of the
20 | * License, or (at your option) any later version.
21 | *
22 | * This program is distributed in the hope that it will be useful,
23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 | * GNU Affero General Public License for more details.
26 | *
27 | * You should have received a copy of the GNU Affero General Public License
28 | * along with this program. If not, see .
29 | *
30 | * @author PeratX
31 | * @website https://github.com/iTXTech/mirai-console-loader
32 | *
33 | */
34 | public class Repo extends MclModule {
35 | @Override
36 | public String getName() {
37 | return "repo";
38 | }
39 |
40 | @Override
41 | public void prepare() {
42 | var group = new OptionGroup();
43 | group.addOption(Option.builder("i").desc("Fetch info for specified package")
44 | .longOpt("package-info").hasArg().argName("PackageName").build());
45 | group.addOption(Option.builder("j").desc("List available packages in Mirai Repo")
46 | .longOpt("list-repo-packages").build());
47 | loader.options.addOptionGroup(group);
48 | }
49 |
50 | @Override
51 | public void cli() {
52 | try {
53 | if (loader.cli.hasOption("j")) {
54 | loader.logger.info("Fetching packages from " + loader.config.miraiRepo);
55 | var index = loader.repo.fetchPackageIndex();
56 |
57 | loader.logger.info("---------- Mirai Repo Index Metadata ----------");
58 | loader.logger.info("Name: " + index.metadata.name);
59 | loader.logger.info("Revision: " + index.metadata.commit);
60 | loader.logger.info("Time: " + DateTimeFormatter.ISO_LOCAL_DATE_TIME
61 | .format(new Timestamp(index.metadata.timestamp).toLocalDateTime()));
62 | loader.logger.info("");
63 |
64 | for (var pkg : index.packages.entrySet()) {
65 | var info = pkg.getValue();
66 | loader.logger.info("---------- Package: " + pkg.getKey() + " ----------");
67 | loader.logger.info("Name: " + info.name);
68 | loader.logger.info("Description: " + info.description);
69 | loader.logger.info("Website: " + info.website);
70 | loader.logger.info("Type: " + info.type);
71 | loader.logger.info("Default Channel: " + info.defaultChannel);
72 | loader.logger.info("");
73 | }
74 | loader.exit(0);
75 | return;
76 | }
77 |
78 | if (loader.cli.hasOption("i")) {
79 | var pkg = loader.cli.getOptionValue("i");
80 |
81 | loader.logger.info("Fetching channel info for package \"" + pkg + "\"");
82 | var fetchedPackageInfo = loader.repo.fetchPackage(pkg);
83 | if (null == fetchedPackageInfo) {
84 | loader.logger.error("Package \"" + pkg + "\" is not found in MiraiRepo");
85 | loader.exit(1);
86 | return;
87 | }
88 |
89 | for (var chan : fetchedPackageInfo.channels.entrySet()) {
90 | loader.logger.info("---------- Channel: " + chan.getKey() + " ----------");
91 | loader.logger.info("Version: " + Utility.join(", ", chan.getValue()));
92 | loader.logger.info("");
93 | }
94 | loader.exit(0);
95 | }
96 | } catch (Exception e) {
97 | loader.logger.logException(e);
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/module/builtin/RepoCache.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.module.builtin;
2 |
3 | import org.apache.commons.cli.Option;
4 | import org.apache.commons.cli.OptionGroup;
5 | import org.itxtech.mcl.component.Repository;
6 | import org.itxtech.mcl.module.MclModule;
7 |
8 | import java.util.HashMap;
9 |
10 | /*
11 | *
12 | * Mirai Console Loader
13 | *
14 | * Copyright (C) 2020-2022 iTX Technologies
15 | *
16 | * This program is free software: you can redistribute it and/or modify
17 | * it under the terms of the GNU Affero General Public License as
18 | * published by the Free Software Foundation, either version 3 of the
19 | * License, or (at your option) any later version.
20 | *
21 | * This program is distributed in the hope that it will be useful,
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 | * GNU Affero General Public License for more details.
25 | *
26 | * You should have received a copy of the GNU Affero General Public License
27 | * along with this program. If not, see .
28 | *
29 | * @author PeratX
30 | * @website https://github.com/iTXTech/mirai-console-loader
31 | *
32 | */
33 | public class RepoCache extends MclModule {
34 | private static final String AUTO_CLEAR_KEY = "repocache.auto-clear";
35 |
36 | @Override
37 | public String getName() {
38 | return "repocache";
39 | }
40 |
41 | @Override
42 | public void prepare() {
43 | var clearGroup = new OptionGroup();
44 | clearGroup.addOption(Option.builder().desc("Disable Repo Cache auto clear")
45 | .longOpt("disable-auto-clear").build());
46 | clearGroup.addOption(Option.builder().desc("Enable Repo Cache auto clear")
47 | .longOpt("enable-auto-clear").build());
48 | loader.options.addOptionGroup(clearGroup);
49 |
50 | loader.repo = new RepoWithCache(loader.repo);
51 | }
52 |
53 | @Override
54 | public void cli() {
55 | if (loader.cli.hasOption("enable-auto-clear")) {
56 | loader.config.moduleProps.put(AUTO_CLEAR_KEY, "true");
57 | }
58 | if (loader.cli.hasOption("disable-auto-clear")) {
59 | loader.config.moduleProps.put(AUTO_CLEAR_KEY, "false");
60 | }
61 | }
62 |
63 | @Override
64 | public void boot() {
65 | if (loader.config.moduleProps.getOrDefault(AUTO_CLEAR_KEY, "true").equals("true") &&
66 | loader.repo instanceof RepoWithCache) {
67 | ((RepoWithCache) loader.repo).clearCache();
68 | loader.logger.debug("RepoCache has been cleared");
69 | }
70 | }
71 |
72 | public static class RepoWithCache extends Repository {
73 | private final HashMap packageInfoCache = new HashMap<>();
74 | private MclPackageIndex indexCache = null;
75 |
76 | public RepoWithCache(Repository base) {
77 | super(base.loader);
78 | }
79 |
80 | public void clearCache() {
81 | indexCache = null;
82 | packageInfoCache.clear();
83 | }
84 |
85 | @Override
86 | public PackageInfo fetchPackage(String id) throws Exception {
87 | if (!packageInfoCache.containsKey(id)) {
88 | packageInfoCache.put(id, super.fetchPackage(id));
89 | }
90 | return packageInfoCache.get(id);
91 | }
92 |
93 | @Override
94 | public MclPackageIndex fetchPackageIndex() throws Exception {
95 | if (indexCache == null) {
96 | indexCache = super.fetchPackageIndex();
97 | }
98 | return indexCache;
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/module/builtin/Updater.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.module.builtin;
2 |
3 | import org.apache.commons.cli.Option;
4 | import org.fusesource.jansi.Ansi;
5 | import org.itxtech.mcl.Utility;
6 | import org.itxtech.mcl.component.Repository;
7 | import org.itxtech.mcl.module.MclModule;
8 | import org.itxtech.mcl.pkg.MclPackage;
9 |
10 | import java.io.File;
11 |
12 | /*
13 | *
14 | * Mirai Console Loader
15 | *
16 | * Copyright (C) 2020-2022 iTX Technologies
17 | *
18 | * This program is free software: you can redistribute it and/or modify
19 | * it under the terms of the GNU Affero General Public License as
20 | * published by the Free Software Foundation, either version 3 of the
21 | * License, or (at your option) any later version.
22 | *
23 | * This program is distributed in the hope that it will be useful,
24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 | * GNU Affero General Public License for more details.
27 | *
28 | * You should have received a copy of the GNU Affero General Public License
29 | * along with this program. If not, see .
30 | *
31 | * @author PeratX
32 | * @website https://github.com/iTXTech/mirai-console-loader
33 | *
34 | */
35 | public class Updater extends MclModule {
36 | private boolean showNotice = false;
37 |
38 | private int size = 0;
39 | private String ttl = "";
40 |
41 | @Override
42 | public String getName() {
43 | return "updater";
44 | }
45 |
46 | @Override
47 | public void prepare() {
48 | loader.options.addOption(Option.builder("u").desc("Update packages")
49 | .longOpt("update").build());
50 | loader.options.addOption(Option.builder("k").desc("Disable progress bar")
51 | .longOpt("disable-progress-bar").build());
52 | // loader.options.addOption(Option.builder("q").desc("Remove outdated files while updating")
53 | // .longOpt("delete").build());
54 | }
55 |
56 | @Override
57 | public void load() {
58 | for (var pkg : loader.packageManager.getPackages()) {
59 | try {
60 | check(pkg);
61 | } catch (Exception e) {
62 | loader.logger.error("Failed to verify package \"" + pkg.id + "\"");
63 | loader.logger.logException(e);
64 | }
65 | }
66 | if (showNotice) {
67 | loader.logger.warning(Ansi.ansi()
68 | .fgYellow()
69 | .a("Run ")
70 | .reset().fgBrightYellow()
71 | .a("./mcl -u")
72 | .reset().fgYellow()
73 | .a(" to update packages.")
74 | );
75 | }
76 | }
77 |
78 | public void check(MclPackage pack) throws Exception {
79 | var baseInfo = Ansi.ansi()
80 | .a("Verifying ")
81 | .fgBrightYellow()
82 | .a("\"").a(pack.id).a("\"");
83 | if (!"".equals(pack.version)) {
84 | baseInfo = baseInfo.reset().a(" v").fgBrightYellow().a(pack.version);
85 | }
86 | if (!"".equals(pack.channel)) {
87 | baseInfo = baseInfo.reset().a(" from ").fgBrightYellow().a(pack.channel);
88 | }
89 | loader.logger.info(baseInfo);
90 | var update = loader.cli.hasOption("u");
91 | var force = pack.isVersionLocked();
92 | var down = false;
93 | if (!Utility.checkLocalFile(pack)) {
94 | if (!"".equals(pack.version)) {
95 | loader.logger.error("\"" + pack.id + "\" is corrupted.");
96 | }
97 | down = true;
98 | }
99 | var ver = "";
100 | Repository.PackageInfo info = null;
101 | if (pack.channel.startsWith("maven")) {
102 | ver = loader.repo.getLatestVersionFromMaven(pack.id, pack.channel);
103 | } else {
104 | info = loader.repo.fetchPackage(pack.id);
105 | if (pack.type.equals("")) {
106 | pack.type = MclPackage.getType(info.type);
107 | }
108 | if (pack.channel.equals("")) {
109 | pack.channel = MclPackage.getChannel(info.defaultChannel);
110 | }
111 | if (!info.channels.containsKey(pack.channel)) {
112 | loader.logger.error(Ansi.ansi()
113 | .fgBrightRed()
114 | .a("Invalid update channel ")
115 | .fgBrightBlue().append("\"").a(pack.channel).a("\"")
116 | .fgBrightRed()
117 | .a(" for package ")
118 | .fgBrightYellow().a("\"").a(pack.id).a("\"")
119 | );
120 | loader.saveConfig();
121 | return;
122 | }
123 | ver = info.getLatestVersion(pack.channel);
124 | }
125 |
126 | if ((update && !pack.version.equals(ver) && !force) || pack.version.trim().equals("")) {
127 | // if (loader.cli.hasOption("q")) {
128 | pack.removeFiles();
129 | // } else if (pack.type.equals(MclPackage.TYPE_PLUGIN)) {
130 | // var dir = new File(pack.type);
131 | // pack.getJarFile().renameTo(new File(dir, pack.getBasename() + ".jar.bak"));
132 | // }
133 | pack.version = ver;
134 | down = true;
135 | }
136 | if (!down && !pack.version.equals(ver)) {
137 | loader.logger.warning(Ansi.ansi()
138 | .fgBrightRed()
139 | .a("Package ")
140 | .reset().fgBrightYellow().a("\"").a(pack.id).a("\"")
141 | .reset().fgBrightRed().a(" has newer version ")
142 | .reset().fgBrightYellow().a("\"").a(ver).a("\"")
143 | );
144 | showNotice = true;
145 | }
146 | if (down) {
147 | loader.logger.info(Ansi.ansi()
148 | .a("Updating ")
149 | .fgBrightYellow()
150 | .a("\"").a(pack.id).a("\"").reset()
151 | .a(" to v").fgBrightYellow().a(pack.version)
152 | );
153 | if (!Utility.checkLocalFile(pack)) {
154 | downloadFile(pack, info);
155 | }
156 | if (!Utility.checkLocalFile(pack)) {
157 | loader.logger.error(Ansi.ansi()
158 | .fgBrightRed()
159 | .a("The local file ")
160 | .fgBrightYellow().a("\"").a(pack.id).a("\"")
161 | .fgBrightRed()
162 | .a(" is still corrupted, please check the network.")
163 | );
164 | }
165 | }
166 | loader.saveConfig();
167 | }
168 |
169 | public void downloadFile(MclPackage pack, Repository.PackageInfo info) {
170 | var dir = new File(pack.type);
171 | dir.mkdirs();
172 | var name = pack.getName();
173 | var jar = name + "-" + pack.version + ".jar";
174 | var metadata = name + "-" + pack.version + ".mirai.metadata";
175 |
176 | var jarUrl = loader.repo.getJarUrl(pack, info);
177 | if (jarUrl.isEmpty()) {
178 | loader.logger.error(Ansi.ansi()
179 | .a("Cannot download package ")
180 | .fgBrightYellow().a("\"").a(pack.id).a("\"")
181 | );
182 | return;
183 | }
184 | var index = jarUrl.lastIndexOf(name);
185 | if (index != -1) {
186 | jar = jarUrl.substring(index);
187 | }
188 | down(jarUrl, new File(dir, jar));
189 |
190 | var sha1Url = loader.repo.getSha1Url(pack, info, jarUrl);
191 | var sha1 = jar + ".sha1";
192 | down(sha1Url, new File(dir, sha1));
193 |
194 | var metadataUrl = loader.repo.getMetadataUrl(pack, info);
195 | if (metadataUrl.isEmpty()) return;
196 | down(metadataUrl, new File(dir, metadata));
197 | }
198 |
199 | public String alignRight(String current, String total) {
200 | var max = Math.max(current.length(), total.length());
201 | return " ".repeat(max - current.length()) + current;
202 | }
203 |
204 | public String buildDownloadBar(int total, int current) {
205 | var length = 30;
206 | var bar = Math.floor((current / 1.0 / total) * length);
207 | var buffer = new StringBuilder("[");
208 | for (var i = 0; i < bar; i++) {
209 | buffer.append('=');
210 | }
211 | if (bar < length) {
212 | buffer.append('>');
213 | for (var i = bar; i < length; i++) {
214 | buffer.append(' ');
215 | }
216 | }
217 | return buffer + "]";
218 | }
219 |
220 | public void down(String url, File file) {
221 | var name = file.getName();
222 | size = 0;
223 | ttl = "";
224 | loader.downloader.download(url, file, loader.cli.hasOption("k") ? null : (total, current) -> {
225 | ttl = Utility.humanReadableFileSize(total);
226 | var cur = Utility.humanReadableFileSize(current);
227 |
228 | var line = " Downloading " + name + " " + buildDownloadBar(total, current) + " " +
229 | (alignRight(cur, ttl) + " / " + ttl) + " (" + (Math.floor(current * 1000.0 / total) / 10) + "%)" + " \r";
230 | loader.logger.print(line);
231 | size = line.length();
232 | });
233 | if (!loader.cli.hasOption("k")) {
234 | loader.logger.print(" ".repeat(size) + '\r');
235 | }
236 | loader.logger.println(" Downloading " + name + " " + buildDownloadBar(1, 1) + " " + ttl);
237 | }
238 | }
239 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/pkg/MclPackage.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.pkg;
2 |
3 | import org.itxtech.mcl.Loader;
4 |
5 | import java.io.File;
6 | import java.util.HashMap;
7 | import java.util.LinkedHashMap;
8 |
9 | /*
10 | *
11 | * Mirai Console Loader
12 | *
13 | * Copyright (C) 2020-2022 iTX Technologies
14 | *
15 | * This program is free software: you can redistribute it and/or modify
16 | * it under the terms of the GNU Affero General Public License as
17 | * published by the Free Software Foundation, either version 3 of the
18 | * License, or (at your option) any later version.
19 | *
20 | * This program is distributed in the hope that it will be useful,
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | * GNU Affero General Public License for more details.
24 | *
25 | * You should have received a copy of the GNU Affero General Public License
26 | * along with this program. If not, see .
27 | *
28 | * @author PeratX
29 | * @website https://github.com/iTXTech/mirai-console-loader
30 | *
31 | */
32 | public class MclPackage {
33 | public static final HashMap TYPE_ALIAS = new HashMap<>() {{
34 | put("core", TYPE_CORE);
35 | put("plugin", TYPE_PLUGIN);
36 | put("mcl-module", TYPE_MODULE);
37 | }};
38 |
39 | public static final String TYPE_CORE = "libs";
40 | public static final String TYPE_PLUGIN = "plugins";
41 | public static final String TYPE_MODULE = "modules";
42 |
43 | public static final String CHAN_STABLE = "stable";
44 | public static final String CHAN_BETA = "beta";
45 | public static final String CHAN_NIGHTLY = "nightly";
46 |
47 | public static String getType(String t) {
48 | if (t == null || "".equals(t)) {
49 | return TYPE_PLUGIN;
50 | }
51 | var alias = TYPE_ALIAS.get(t);
52 | if (alias == null) {
53 | if (t.contains("-")) {
54 | t = t.split("-")[0];
55 | }
56 | return TYPE_ALIAS.getOrDefault(t, t);
57 | }
58 | return alias;
59 | }
60 |
61 | public static String getChannel(String c) {
62 | return c == null ? CHAN_STABLE : c;
63 | }
64 |
65 | public transient String id;
66 | public String channel;
67 | public String version = "";
68 | public String type = "";
69 | public boolean versionLocked = false;
70 |
71 | public MclPackage(String id) {
72 | this(id, "");
73 | }
74 |
75 | public MclPackage(String id, String channel) {
76 | this.id = id;
77 | this.channel = channel;
78 | }
79 |
80 | public MclPackage(String id, String channel, String type) {
81 | this.id = id;
82 | this.channel = channel;
83 | this.type = type;
84 | }
85 |
86 | public boolean isVersionLocked() {
87 | return versionLocked;
88 | }
89 |
90 | public void addToMap(LinkedHashMap map) {
91 | map.put(id, this);
92 | }
93 |
94 | public String getName() {
95 | return id.split(":", 2)[1];
96 | }
97 |
98 | public File getJarFile() {
99 | var name = getName();
100 | var suffix = Loader.getInstance().config.archiveSuffix;
101 | for (String end : suffix) {
102 | var file = new File(type, name + "-" + version + end);
103 | if (file.exists()) return file;
104 | }
105 | return new File(type, name + "-" + version + suffix.get(0));
106 | }
107 |
108 | public File getSha1File() {
109 | var jar = getJarFile();
110 | return new File(jar.getParent(), jar.getName() + ".sha1");
111 | }
112 |
113 | public File getMetadataFile() {
114 | var name = getName();
115 | return new File(type, name + "-" + version + ".mirai.metadata");
116 | }
117 |
118 | public void removeFiles() {
119 | var dir = new File(type);
120 | var name = getName();
121 | deleteFile(dir, name, "jar");
122 | deleteFile(dir, name, "zip");
123 | deleteFile(dir, name, "sha1");
124 | deleteFile(dir, name, "metadata");
125 | }
126 |
127 | public void deleteFile(File dir, String name, String type) {
128 | var list = dir.listFiles((d, f) -> f.startsWith(name) && f.endsWith(type));
129 | if (list == null) return;
130 | for (File source : list) {
131 | if (source.delete()) {
132 | Loader.getInstance().logger.info("File \"" + source.getName() + "\" has been deleted.");
133 | } else {
134 | Loader.getInstance().logger.error("Failed to delete \"" + source.getName() + "\". Please delete it manually.");
135 | }
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/main/java/org/itxtech/mcl/pkg/PackageManager.java:
--------------------------------------------------------------------------------
1 | package org.itxtech.mcl.pkg;
2 |
3 | import org.itxtech.mcl.Loader;
4 |
5 | import java.util.Collection;
6 |
7 | /*
8 | *
9 | * Mirai Console Loader
10 | *
11 | * Copyright (C) 2020-2022 iTX Technologies
12 | *
13 | * This program is free software: you can redistribute it and/or modify
14 | * it under the terms of the GNU Affero General Public License as
15 | * published by the Free Software Foundation, either version 3 of the
16 | * License, or (at your option) any later version.
17 | *
18 | * This program is distributed in the hope that it will be useful,
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 | * GNU Affero General Public License for more details.
22 | *
23 | * You should have received a copy of the GNU Affero General Public License
24 | * along with this program. If not, see .
25 | *
26 | * @author PeratX
27 | * @website https://github.com/iTXTech/mirai-console-loader
28 | *
29 | */
30 | public class PackageManager {
31 | private final Loader loader;
32 |
33 | public PackageManager(Loader loader) {
34 | this.loader = loader;
35 | }
36 |
37 | public boolean hasPackage(String id) {
38 | return loader.config.packages.containsKey(id);
39 | }
40 |
41 | public Collection getPackages() {
42 | return loader.config.packages.values();
43 | }
44 |
45 | public MclPackage getPackage(String id) {
46 | return loader.config.packages.get(id);
47 | }
48 |
49 | public void removePackage(String id) {
50 | loader.config.packages.remove(id);
51 | }
52 |
53 | public void addPackage(MclPackage pkg) {
54 | loader.config.packages.put(pkg.id, pkg);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/services/org.itxtech.mcl.module.MclModule:
--------------------------------------------------------------------------------
1 | org.itxtech.mcl.module.builtin.Addon
2 | org.itxtech.mcl.module.builtin.Announcement
3 | org.itxtech.mcl.module.builtin.Boot
4 | org.itxtech.mcl.module.builtin.Conf
5 | org.itxtech.mcl.module.builtin.MDownloader
6 | org.itxtech.mcl.module.builtin.Mrm
7 | org.itxtech.mcl.module.builtin.OracleJdk
8 | org.itxtech.mcl.module.builtin.PkgAnn
9 | org.itxtech.mcl.module.builtin.Repo
10 | org.itxtech.mcl.module.builtin.RepoCache
11 | org.itxtech.mcl.module.builtin.Updater
--------------------------------------------------------------------------------