diff --git a/.well-known/discord b/.well-known/discord new file mode 100644 index 0000000..3162025 --- /dev/null +++ b/.well-known/discord @@ -0,0 +1 @@ +dh=1f526289182e6f75ac4606f01aa656ae9b2a14a2 \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bae94e1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. \ No newline at end of file diff --git a/_headers b/_headers new file mode 100644 index 0000000..431180a --- /dev/null +++ b/_headers @@ -0,0 +1,2 @@ +/ + Link: ; rel=preload; as=style; type=text/css, ; rel=preload; as=image; type=image/avif, ; rel=preload; as=image; type=image/avif, ; rel=preload; as=image; type=image/svg+xml, ; rel=preload; as=script; type=application/javascript, ; rel=preload; as=script; type=application/javascript, ; rel=preload; as=font; type=font/woff2; crossorigin, ; rel=preload; as=fetch; type=application/json; crossorigin diff --git a/_redirects b/_redirects new file mode 100644 index 0000000..e833435 --- /dev/null +++ b/_redirects @@ -0,0 +1,23 @@ +/akatsuki https://akatsuki.gg/u/102756 301 +/anilist https://anilist.co/user/AutumnVN 301 +/bsky https://bsky.app/profile/chino.id.vn 301 +/bot https://discord.com/oauth2/authorize?client_id=833707580593143841&permissions=8&scope=bot 301 +/codeberg https://codeberg.org/AutumnVN 301 +/crowdin https://crowdin.com/profile/AutumnVN 301 +/discord https://discord.com/users/393694671383166998 301 +/dm https://discord.com/users/393694671383166998 301 +/facebook https://www.facebook.com/autumnvnchino 301 +/genshin https://enka.network/u/829753707 301 +/github https://github.com/AutumnVN 301 +/lastfm https://www.last.fm/user/AutumnVN 301 +/osu https://osu.ppy.sh/users/26086345 301 +/pixiv https://www.pixiv.net/users/54279342 301 +/reddit https://www.reddit.com/user/AutumnVN 301 +/rickroll https://www.youtube.com/watch?v=dQw4w9WgXcQ 301 +/ripple https://ripple.moe/u/128582 301 +/spotify https://open.spotify.com/user/31x3ktny3mzvdu5kob47r6oceyiq 301 +/steam https://steamcommunity.com/id/AutumnVN 301 +/twitch https://www.twitch.tv/autumnvn 301 +/twitter https://twitter.com/autumnvnchino 301 +/youtube https://www.youtube.com/@AutumnVN 301 +/wysi https://www.youtube.com/watch?v=AaAF51Gwbxo&t=62s 301 diff --git a/autumnvn.svg b/autumnvn.svg new file mode 100644 index 0000000..e843985 --- /dev/null +++ b/autumnvn.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/avatar.avif b/avatar.avif new file mode 100644 index 0000000..e90be24 Binary files /dev/null and b/avatar.avif differ diff --git a/bg.avif b/bg.avif new file mode 100644 index 0000000..945bd3a Binary files /dev/null and b/bg.avif differ diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..b48e728 Binary files /dev/null and b/favicon.ico differ diff --git a/font.woff2 b/font.woff2 new file mode 100644 index 0000000..87fd56f Binary files /dev/null and b/font.woff2 differ diff --git a/functions/chino.js b/functions/chino.js new file mode 100644 index 0000000..515ef61 --- /dev/null +++ b/functions/chino.js @@ -0,0 +1,9 @@ +export async function onRequestGet() { + const max = 267; + return new Response(await fetch(`https://raw.githubusercontent.com/AutumnVN/chino.webp/main/${Math.ceil(Math.random() * max)}.webp`).then(r => r.blob()), { + headers: { + 'content-type': 'image/webp', + 'cache-control': 'no-cache, no-store, must-revalidate, max-age=0' + } + }); +} diff --git a/functions/count.js b/functions/count.js new file mode 100644 index 0000000..a17546c --- /dev/null +++ b/functions/count.js @@ -0,0 +1,25 @@ +export async function onRequestGet({ env, waitUntil }) { + const value = + await env.COUNT.get('count') + 1 || 1; + + waitUntil(env.COUNT.put('count', value)); + + return new Response(generateSvg(String(value).padStart(7, '0')), { + headers: { + 'content-type': 'image/svg+xml', + 'cache-control': 'no-cache, no-store, must-revalidate, max-age=0' + } + }); +} + +function generateSvg(str) { + const font = 24; + const size = 29; + const margin = 32; + let text = ''; + + for (let i = 0; i < str.length; i++) { + text += `${str[i]}`; + } + + return `${text}`; +} diff --git a/functions/fetchanilist.js b/functions/fetchanilist.js new file mode 100644 index 0000000..4a1038c --- /dev/null +++ b/functions/fetchanilist.js @@ -0,0 +1,65 @@ +export async function onRequestGet({ request, waitUntil }) { + let res = await caches.default.match(request.url); + + if (res) { + res = await res.json(); + waitUntil(fetchData().then(r => caches.default.put(request.url, new Response(JSON.stringify(r), { + headers: { + 'content-type': 'application/json', + 'cache-control': 'public, max-age=86400' + } + })))); + } else { + res = await fetchData(); + waitUntil(caches.default.put(request.url, new Response(JSON.stringify(res), { + headers: { + 'content-type': 'application/json', + 'cache-control': 'public, max-age=86400' + } + }))); + } + + return new Response(JSON.stringify(res), { + headers: { + 'content-type': 'application/json', + 'cache-control': 'no-cache, no-store, must-revalidate, max-age=0' + } + }); +} + +async function fetchData() { + const query = `query { + Page(page: 1, perPage: 4) { + activities(userId: 6851565, sort: ID_DESC) { + ... on ListActivity { + createdAt + status + progress + media { + coverImage { + medium + } + title { + userPreferred + } + siteUrl + } + } + } + } + }`; + + const { data } = await fetch('https://graphql.anilist.co', { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ query }) + }).then(r => r.json()); + + return data.Page.activities.map(i => ([ + i.media.siteUrl, + i.media.coverImage.medium, + `${i.status} ${i.progress}`, + i.media.title.userPreferred, + i.createdAt + ])); +} diff --git a/functions/fetchgithub.js b/functions/fetchgithub.js new file mode 100644 index 0000000..567ec4a --- /dev/null +++ b/functions/fetchgithub.js @@ -0,0 +1,93 @@ +export async function onRequestGet({ env, request, waitUntil }) { + let res = await caches.default.match(request.url); + + if (res) { + res = await res.json(); + waitUntil(fetchData(env).then(r => caches.default.put(request.url, new Response(JSON.stringify(r), { + headers: { + 'content-type': 'application/json', + 'cache-control': 'public, max-age=86400' + } + })))); + } else { + res = await fetchData(env); + waitUntil(caches.default.put(request.url, new Response(JSON.stringify(res), { + headers: { + 'content-type': 'application/json', + 'cache-control': 'public, max-age=86400' + } + }))); + } + + return new Response(JSON.stringify(res), { + headers: { + 'content-type': 'application/json', + 'cache-control': 'no-cache, no-store, must-revalidate, max-age=0' + } + }); +} + +async function fetchData(env) { + const query = `{ + a: repository(owner: "AutumnVN", name: "chino.pages.dev") { stargazers { totalCount } forks { totalCount } } + b: repository(owner: "AutumnVN", name: "autumn") { stargazers { totalCount } forks { totalCount } } + c: repository(owner: "AutumnVN", name: "highlight") { stargazers { totalCount } forks { totalCount } } + d: repository(owner: "AutumnVN", name: "osu-igdl") { stargazers { totalCount } forks { totalCount } } + e: repository(owner: "AutumnVN", name: "osu-mpbd") { stargazers { totalCount } forks { totalCount } } + f: repository(owner: "AutumnVN", name: "osu-ubu") { stargazers { totalCount } forks { totalCount } } + g: repository(owner: "AutumnVN", name: "bot") { stargazers { totalCount } forks { totalCount } } + h: repository(owner: "Vendicated", name: "Vencord") { stargazers { totalCount } forks { totalCount } } + + z: user(login: "AutumnVN") { + repositories(first: 100, ownerAffiliations: OWNER) { + nodes { + stargazerCount + forkCount + } + } + contributionsCollection { + totalCommitContributions + } + pullRequests(first: 1) { + totalCount + } + issues(first: 1) { + totalCount + } + repositoriesContributedTo(first: 1, contributionTypes: [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]) { + totalCount + } + } + }`; + + const { data } = await fetch('https://api.github.com/graphql', { + method: 'POST', + headers: { + 'accept': 'application/json', + 'authorization': `bearer ${env.GITHUB_TOKEN}`, + 'user-agent': '' + }, + body: JSON.stringify({ query }) + }).then(r => r.json()); + + const repos = Object.values(data); + const stats = repos.pop(); + + const statsArr = [ + stats.repositories.nodes.reduce((a, b) => a + b.stargazerCount, 0), + stats.repositories.nodes.reduce((a, b) => a + b.forkCount, 0), + stats.contributionsCollection.totalCommitContributions, + stats.pullRequests.totalCount, + stats.issues.totalCount, + stats.repositoriesContributedTo.totalCount + ]; + + const res = repos.map(i => ([ + i.stargazers.totalCount, + i.forks.totalCount + ])); + + res.push(statsArr); + + return res; +} diff --git a/functions/fetchlanyard.js b/functions/fetchlanyard.js new file mode 100644 index 0000000..a0231c6 --- /dev/null +++ b/functions/fetchlanyard.js @@ -0,0 +1,38 @@ +export async function onRequestGet({ request, waitUntil }) { + let res = await caches.default.match(request.url); + + if (res) { + res = await res.json(); + waitUntil(fetchData().then(r => caches.default.put(request.url, new Response(JSON.stringify(r), { + headers: { + 'content-type': 'application/json', + 'cache-control': 'public, max-age=86400' + } + })))); + } else { + res = await fetchData(); + waitUntil(caches.default.put(request.url, new Response(JSON.stringify(res), { + headers: { + 'content-type': 'application/json', + 'cache-control': 'public, max-age=86400' + } + }))); + } + + return new Response(JSON.stringify(res), { + headers: { + 'content-type': 'application/json', + 'cache-control': 'no-cache, no-store, must-revalidate, max-age=0' + } + }); +} + +async function fetchData() { + const { data } = await fetch('https://api.lanyard.rest/v1/users/393694671383166998').then(r => r.json()); + + return { + discord_user: data.discord_user, + discord_status: data.discord_status, + activities: data.activities + } +} diff --git a/functions/lanyard.js b/functions/lanyard.js new file mode 100644 index 0000000..37515b1 --- /dev/null +++ b/functions/lanyard.js @@ -0,0 +1,35 @@ +export async function onRequestGet({ env, waitUntil }) { + const value = Number((await env.COUNT.get('count')) || 0) + 1; + + const originSvg = await fetch('https://lanyard.cnrad.dev/api/393694671383166998?showDisplayName=true&bg=0d1117&idleMessage=https://chino.pages.dev').then(r => r.text()); + + const newSvg = originSvg.replace(/( width=")410((px)?" )/g, '$1350$2') + .replace(/width: 400px;/g, 'width: 340px;') + .replace(/width: 279px;/, 'width: 219px;') + .replace(/border: solid 0.5px #222;/, '$& object-fit: cover;') + .replace(/height="210px">/, 'height="265px">') + .replace(/<\/svg>/, generateCountSvg(String(value).padStart(7, '0')) + '$&'); + + waitUntil(env.COUNT.put('count', value)); + + return new Response(newSvg, { + headers: { + 'content-type': 'image/svg+xml', + 'cache-control': 'no-cache, no-store, must-revalidate, max-age=0' + } + }); +} + +function generateCountSvg(str) { + const y = 215; + const font = 37; + const size = 45; + const margin = 50; + let text = ''; + + for (let i = 0; i < str.length; i++) { + text += `${str[i]}`; + } + + return `${text}`; +} diff --git a/functions/osuapiv2/[[osuapiv2]].js b/functions/osuapiv2/[[osuapiv2]].js new file mode 100644 index 0000000..82772c7 --- /dev/null +++ b/functions/osuapiv2/[[osuapiv2]].js @@ -0,0 +1,24 @@ +export async function onRequestGet({ env, request }) { + const { access_token } = await fetch('https://osu.ppy.sh/oauth/token', { + method: 'POST', + headers: { + 'accept': 'application/json', + 'content-Type': 'application/x-www-form-urlencoded', + }, + body: `client_id=23616&client_secret=${env.OSU_CLIENT_SECRET}&grant_type=client_credentials&scope=public` + }).then(res => res.json()); + + const response = await fetch(`https://osu.ppy.sh/api/v2/${request.url.replace(/^.+?osuapiv2\//, '')}`, { + headers: { + 'accept': 'application/json', + 'content-type': 'application/json', + 'authorization': `Bearer ${access_token}` + }, + body: request.body + }); + + return new Response(await response.text(), { + status: response.status, + headers: response.headers + }); +} diff --git a/index.css b/index.css new file mode 100644 index 0000000..ad47ea5 --- /dev/null +++ b/index.css @@ -0,0 +1,723 @@ +:root { + --background: url('/bg.avif'); + --widget: #def9; + --widget-hover: #defb; + font-weight: 500; + line-height: 1.4; + font-family: Inter, system-ui, sans-serif; +} + +body { + margin: 0 auto; + min-width: 320px; + max-width: min(1280px, 100% - 100px); +} + +@media (width < 880px) { + body { + max-width: calc(100% - 50px); + } +} + +.background { + --multiplier: clamp(0, calc(-5 * var(--scroll, 0) + 1.5), 1); + position: fixed; + transform: translate(calc(var(--tx, 0) * var(--multiplier, 1)), calc(var(--ty, 0) * var(--multiplier, 1))); + z-index: -1; + filter: blur(clamp(0px, calc(50px * var(--scroll, 0) + -5px), 10px)) brightness(clamp(.7, calc(-.5 * var(--scroll, 0) + .85), .8)); + inset: -30px; + background: #9af var(--background) center/cover no-repeat; +} + +header { + display: flex; + flex-direction: row; + align-items: center; + gap: 50px; + transform: translateY(-50%); + margin-top: 45vh; +} + +@media (width < 1300px) { + header { + margin-right: auto; + margin-left: auto; + max-width: 950px; + } +} + +@media (width < 880px) { + header { + flex-direction: column; + gap: 20px; + margin-right: auto; + margin-left: auto; + max-width: 480px; + } +} + +header img { + animation: 1s ease-in-out .2s both avatar; + border-radius: 50%; + width: clamp(120px, 26vmin, 200px); + height: clamp(120px, 26vmin, 200px); + pointer-events: none; +} + +@keyframes avatar { + 0% { + transform: scale(.8); + opacity: 0; + } + + 100% { + transform: scale(1); + opacity: 1; + } +} + +header .name-container { + display: flex; + flex-direction: column; + gap: 20px; +} + +header svg { + -webkit-mask-image:url('data:image/svg+xml,'); + backdrop-filter: saturate(2) brightness(2.5) blur(12px); + width: min(500px, 100%); +} + +header .bio { + -webkit-mask: linear-gradient(to right, #fff 45%, transparent 55%) 0 0/220% 100% no-repeat; + opacity: clamp(.7, .75 * var(--scroll, 0) + .7, .85); + animation: 2s ease-in-out 1.9s both bio; + color: #fff; + font-weight: normal; + font-size: clamp(18px, 3vmin, 25px); +} + +@keyframes bio { + 0% { + -webkit-mask-position: 100% 0; + } + + 100% { + -webkit-mask-position: 0% 0; + } +} + +.arrow { + display: flex; + position: fixed; + right: 0; + bottom: 0; + left: 0; + justify-content: center; + transition: opacity .2s; +} + +.arrow svg { + opacity: .5; + transition: opacity .2s; + cursor: pointer; + padding: 0 15px; + width: 60px; + height: 90px; + color: #fff; +} + +.arrow svg:hover { + opacity: .8; +} + +body[style*='.'] .arrow { + opacity: 0; + pointer-events: none; +} + +.main-container { + display: grid; + grid-template-columns: repeat(12, 1fr); + gap: 60px; + opacity: calc(var(--scroll) / .15); + margin-top: max(0px, calc(-140vh * var(--scroll) + 35vh)); + margin-bottom: calc(35vh - max(0px, calc(-140vh * var(--scroll) + 35vh))); +} + +@media (width < 1300px) { + .main-container { + display: flex; + flex-direction: column; + gap: 40px; + margin-right: auto; + margin-left: auto; + max-width: 950px; + } +} + +@media (width < 880px) { + .main-container { + margin-right: auto; + margin-left: auto; + max-width: 480px; + } +} + +main { + --template-columns: 8; + --gap: 40px; + display: grid; + grid-template-columns: repeat(var(--template-columns), 1fr); + grid-column: span 8; + gap: var(--gap); + height: fit-content; +} + +@media (width < 880px) { + main { + --template-columns: 4; + --gap: 25px; + } +} + +.widget { + --square-size: calc((((min(1280px, 100vw - 100px) - (60px * 11)) / 12) * 8 + (60px - var(--gap)) * 7) / 8); + --c: min(var(--columns, 2), var(--template-columns)); + --r: var(--rows, 2); + grid-row: span var(--r); + grid-column: span var(--c); + transition: transform .2s linear, background-color .2s linear; + border-radius: 24px; + background-color: var(--widget); + height: calc(var(--square-size) * var(--r) + var(--gap) * (var(--r) - 1)); + overflow: hidden; + color: #000; +} + +@media (width < 1300px) { + .widget { + --square-size: calc((min(950px, 100vw - 100px) - (var(--gap) * 7)) / 8); + } +} + +@media (width < 880px) { + .widget { + --square-size: calc((max(320px, min(480px, calc(100vw - 50px))) - (var(--gap) * 3)) / 4); + } +} + +.widget:hover { + background-color: var(--widget-hover); +} + +.widget:active { + transform: perspective(300px) translateZ(-10px); +} + +.widget>a { + display: flex; + flex-direction: row; + box-sizing: border-box; + padding: 24px; + width: 100%; + height: 100%; + color: #000; +} + +.widget>a[href='#'] { + pointer-events: none; +} + +.widget>a[aria-label] { + box-sizing: border-box; + padding: 0; +} + +.widget>a[aria-label] .image { + background: var(--bg) center/contain no-repeat; + width: 100%; + height: 100%; +} + +.widget .content { + display: flex; + flex-direction: column; +} + +.widget .icon { + display: flex; + justify-content: center; + align-items: center; + border-radius: 10px; + width: 40px; + height: 40px; + overflow: hidden; + color: #fff; +} + +.widget .icon svg { + width: var(--size, 70%); + height: var(--size, 70%); +} + +.widget .icon img { + width: 100%; + height: 100%; +} + +.widget .meta { + margin-top: 10px; +} + +.widget[style*='--rows: 1;'] .content { + flex-direction: row; + align-items: center; +} + +.widget[style*='--rows: 1;'] .meta { + margin-top: 0; + margin-left: 20px; + white-space: nowrap; +} + +@media (width < 880px) { + .widget[style*='--m-'] { + --c: min(var(--m-columns, 2), var(--template-columns)); + --r: var(--m-rows, 2); + } +} + +.github { + display: flex; + flex-direction: column; + box-sizing: border-box; + padding: 19px 24px; + width: 100%; + height: 100%; +} + +.github .line { + display: flex; + align-items: center; + gap: 6px; + color: #000b; +} + +.github svg { + fill: #07c; + flex-shrink: 0; +} + +.github .title { + flex: 0 0 14em; +} + +.discord { + display: flex; + flex-direction: column; + gap: 8px; + box-sizing: border-box; + padding: 24px; + width: 100%; + height: 100%; +} + +.discord .content { + flex-direction: row; + align-items: center; +} + +.discord .content .icon { + position: relative; + flex-shrink: 0; + border-radius: 0; +} + +.discord .content .avatar { + border-radius: 10px; +} + +.discord .meta { + align-self: center; + margin-top: 0; + margin-left: 14px; + width: 100%; + min-width: 0; +} + +.discord .color-dot { + position: absolute; + right: 1px; + bottom: 1px; +} + +.discord .rpc { + display: flex; + flex-direction: row; +} + +.discord .image-container { + display: flex; + position: relative; +} + +.discord .large-image { + border-radius: 10px; + background-position: center; + background-size: cover; + background-repeat: no-repeat; + width: 72px; + height: 72px; +} + +.discord .small-image { + position: absolute; + right: -4px; + bottom: -4px; + border-radius: 50%; + background-position: center; + background-size: cover; + background-repeat: no-repeat; + width: 28px; + height: 28px; +} + +.discord .timebar-container { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + font-family: Consolas, monospace; +} + +.discord .timebar-background { + flex: 1; + border-radius: 2px; + background-color: #0004; + height: 4px; +} + +.discord .timebar-progress { + border-radius: inherit; + background-color: #000; + height: inherit; +} + +.anilist { + display: flex; + flex-direction: column !important; + gap: 10px; + box-sizing: border-box; + padding: 24px; + width: 100%; + height: 100%; +} + +.anilist .content { + flex-direction: row; + align-items: center; + color: #000; +} + +.anilist .content .icon { + flex-shrink: 0; +} + +.anilist .meta { + margin-top: 0; + margin-left: 14px; + width: 100%; + min-width: 0; +} + +.anilist .activity-container { + display: flex; + flex-direction: column; + justify-content: space-around; + height: 100%; +} + +.anilist .activity { + display: flex; + flex-direction: row; +} + +.anilist .image { + flex-shrink: 0; + border-radius: 4px; + background-position: center; + background-size: cover; + background-repeat: no-repeat; + width: 50px; + height: 65px; +} + +.anilist time { + font-size: 0.9em; +} + +.clock { + display: flex; + flex-direction: row; + justify-content: space-around; + align-items: center; + gap: 10px; + box-sizing: border-box; + padding: 20px; + width: 100%; + height: 100%; +} + +.clock .face { + position: relative; + aspect-ratio: 1/1; + height: 100%; +} + +.clock .face div, +.clock .face svg { + position: absolute; + width: 100%; + height: 100%; +} + +.clock .face svg { + fill: #adf; +} + +.clock .hour-hand::before, +.clock .minute-hand::before, +.clock .second-hand::before { + position: absolute; + bottom: 46%; + left: 46%; + border-radius: 50px; + background-color: var(--color); + width: 8%; + height: var(--h); + content: ''; +} + +.clock .hour-hand { + --h: 30%; + --color: #36b; +} + +.clock .minute-hand { + --h: 40%; + --color: #48d; +} + +.clock .second-hand { + --h: 8%; + --color: #48d; + transition: transform .2s; +} + +.clock .second-hand::before { + top: 7%; +} + +.clock .date-container { + color: #000b; + font-size: 1.2em; +} + +.clock .time-container { + min-width: 7ch; + font-weight: bold; + font-size: 2.5em; +} + +.clock .minute~span { + color: #0006; +} + +.clock .timezone-diff { + color: #000b; +} + +.clock .utc-offset { + color: #0009; +} + +aside { + display: flex; + grid-column: span 4; + flex-direction: column; + gap: 20px; +} + +@media (width < 1300px) { + aside { + display: grid; + grid-template-columns: repeat(2, 1fr); + } +} + +@media (width < 880px) { + aside { + display: flex; + } +} + +.project { + transition: transform .2s linear, background-color .2s linear; + border-radius: 16px; + background: var(--widget); + overflow: hidden; +} + +.project:hover { + background-color: var(--widget-hover); +} + +.project:active { + transform: perspective(300px) translateZ(-10px); +} + +.project>a { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 5px; + box-sizing: border-box; + padding: 15px 20px; + width: 100%; + height: 100%; + color: #000; +} + +.project .bold { + color: #06b; + font-size: 1.1em; +} + +.project .meta { + display: flex; + flex-direction: row; + gap: 10px; + width: 100%; + color: #000b; +} + +.project .meta svg { + fill: #000b; +} + +.project .meta>div { + display: flex; + flex-direction: row; + align-items: center; + gap: 5px; +} + +.project .lang { + flex: 1; + justify-content: flex-end; +} + +.color-dot { + border-radius: 50%; + width: .6em; + height: .6em; +} + +footer { + display: flex; + justify-content: center; + align-items: center; + margin-top: -35vh; + height: 15vh; + font-size: 20px; +} + +footer .footer-container { + opacity: .65; + transition: opacity .2s; + color: #fff; + line-height: 2; + text-align: center; +} + +footer .footer-container:hover { + opacity: .8; +} + +footer a { + transition: color .2s; + color: #fff; +} + +footer a:hover { + color: #7df; +} + +.scrollbar { + position: fixed; + box-sizing: border-box; + inset: 0 0 0 auto; + padding: 4px; + width: 16px; +} + +@media (width < 880px) { + .scrollbar { + display: none; + } +} + +.track { + height: 100%; +} + +.thumb { + transition: background-color .2s; + border-radius: 4px; + background: #def6; +} + +.thumb:before { + position: absolute; + inset: -4px; + content: ''; +} + +.thumb:hover { + background: #defa; +} + +.bold { + font-weight: bold; +} + +.opaque { + color: #000b; +} + +.overflow { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +a { + text-decoration: none; +} + +h1 { + margin: 0; +} + +html { + -webkit-tap-highlight-color: transparent; + scrollbar-width: none; + user-select: none; +} + +@font-face { + src: url('/font.woff2') format('woff2'); + font-family: Inter; + font-display: swap; +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..e853598 --- /dev/null +++ b/index.html @@ -0,0 +1,593 @@ + + + + + AutumnVN + + + + + + + + + + + + + +
+ +
+ avatar +
+

+ + AutumnVN + +

+
Hi! I'm AutumnVN, a dude from Vietnam. Nice to meet you :3
+
+
+ +
+ + + +
+ +
+
+ + + + + + + + +
+
+
+ + + +
Total Stars Earned:
+
-
+
+
+ + + +
Total Forks Earned:
+
-
+
+
+ + + +
Total Commits (this year):
+
-
+
+
+ + + +
Total PRs:
+
-
+
+
+ + + +
Total Issues:
+
-
+
+
+ + + +
Contributed to (last year):
+
-
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+
+
+ + +
+ + +
+
+
+
+ +
+ +
+
+
+ + + + +
+ +
+
+
+ + + + +
+ + +
+ + + + + + + + diff --git a/index.js b/index.js new file mode 100644 index 0000000..da05d3f --- /dev/null +++ b/index.js @@ -0,0 +1,277 @@ +(window.setScroll = () => document.body.style.setProperty('--scroll', scrollY / innerHeight))(); +['scroll', 'resize'].forEach(e => addEventListener(e, setScroll)); + +const bg = document.querySelector('.background'); + +addEventListener('touchstart', () => bg.style.setProperty('--multiplier', '0')); + +addEventListener('mousemove', ({ clientX, clientY }) => { + bg.style.setProperty('--tx', `${20 * (clientX - innerWidth / 2) / innerWidth}px`); + bg.style.setProperty('--ty', `${20 * (clientY - innerHeight / 2) / innerHeight}px`); +}); + +['mouseenter', 'mouseleave'].forEach(e => document.addEventListener(e, () => { + if (e === 'mouseleave') bg.removeAttribute('style'); + bg.style.transition = 'transform .1s linear'; + setTimeout(() => bg.style.transition = '', 100); +})); + +document.querySelectorAll('.overflow').forEach(e => e.addEventListener('mouseenter', () => { + if (e.scrollWidth > e.clientWidth) e.title = e.textContent.trim(); +})); + +document.querySelector('header svg').style = `-webkit-mask-image:url('data:image/svg+xml,');`; + +document.querySelector('.arrow svg').addEventListener('click', () => smoothScrollTo(innerWidth > 880 ? .25 * innerHeight + 30 : .25 * innerHeight + 380)); + +function smoothScrollTo(target) { + const start = document.documentElement.scrollTop; + const startTime = performance.now(); + + !function scroll() { + const time = Math.min(1, (performance.now() - startTime) / 200); + const timeFunction = time * (2 - time); + document.documentElement.scrollTop = (timeFunction * (target - start)) + start; + if (Math.abs(document.documentElement.scrollTop - target) > 1) requestAnimationFrame(scroll); + }(); +} + +const visit = new Date().setSeconds(0, 0); +const cache = new Map(); + +!function setClock() { + const date = new Date(); + const time = date.getTime(); + const { year, month, day, hour, minute, second } = myTime(); + const hourOff = -date.getTimezoneOffset() / 60; + const minuteOff = new Date(time - time % 1000 - hourOff * 60 * 60 * 1000); + const timezoneOff = (new Date(year, month - 1, day, hour, minute, second) - minuteOff) / 1000 / 60 / 60; + const timezoneDiff = timezoneOff - hourOff; + const myDate = new Date(time + timezoneDiff * 60 * 60 * 1000); + const utcOffset = `${(timezoneOff >= 0 ? '+' : '')}${Math.floor(timezoneOff)}:${(timezoneOff % 1 * 60).toString().padStart(2, '0')}`; + + update('.clock .hour-hand', 'style', `transform: rotate(${hour % 12 / 12 * 360 + minute / 60 * 30 + second / 60 / 60 * 30}deg)`); + update('.clock .minute-hand', 'style', `transform: rotate(${minute / 60 * 360 + second / 60 * 6}deg)`); + update('.clock .second-hand', 'style', `transform: rotate(${360 * Math.floor((time - visit) / 60 / 1000) + second / 60 * 360}deg)`); + update('.clock .date', 'textContent', myDate.toLocaleDateString()); + update('.clock .date-container', 'datetime', myDate.toISOString().match(/\d{4}-\d{2}-\d{2}/)[0]); + update('.clock .hour', 'textContent', padZero(hour)); + update('.clock .minute', 'textContent', padZero(minute)); + update('.clock .second', 'textContent', padZero(second)); + update('.clock .time-container', 'datetime', myDate.toISOString().match(/\d{2}:\d{2}:\d{2}/)[0]); + update('.clock .timezone-diff', 'textContent', timezoneDiff === 0 ? 'same time' : (timezoneDiff > 0 ? `${formatTimezone(timezoneDiff)} ahead` : `${formatTimezone(-timezoneDiff)} behind`)); + update('.clock .utc-offset', 'textContent', ` / UTC ${utcOffset}`); + update('.clock .utc-offset', 'datetime', utcOffset); + + setRpcTimestamp(cache.get('timestamp')); + + setTimeout(setClock, 1000 - time % 1000); +}(); + +function myTime() { + const obj = {}; + const options = { timeZone: 'Asia/Ho_Chi_Minh', hour: 'numeric', minute: 'numeric', second: 'numeric', hour12: false, day: 'numeric', month: 'numeric', year: 'numeric' }; + new Intl.DateTimeFormat([], options).formatToParts(new Date()).forEach(({ type, value }) => obj[type] = +value); + return obj; +}; + +function formatTimezone(timezoneDiff) { + if (timezoneDiff < 0) return `-${formatTimezone(-timezoneDiff)}`; + const minute = timezoneDiff % 1 * 60; + timezoneDiff = Math.floor(timezoneDiff); + return minute ? `${timezoneDiff}h ${minute}m` : `${timezoneDiff}h`; +} + +fetch('/fetchgithub').then(r => r.json()).then(repos => { + const stats = repos.pop(); + document.querySelectorAll('.stat').forEach((stat, i) => stat.textContent = stats[i]); + document.querySelectorAll('.star').forEach((star, i) => star.textContent = repos[i][0]); + document.querySelectorAll('.fork').forEach((fork, i) => fork.textContent = repos[i][1]); +}); + +fetch('/fetchanilist').then(r => r.json()).then(activities => { + document.querySelectorAll('.anilist .activity').forEach((a, i) => a.href = activities[i][0]); + document.querySelectorAll('.anilist .activity .image').forEach((i, j) => i.style = `background-image: url(${imageProxy(activities[j][1])})`); + document.querySelectorAll('.anilist .activity .status').forEach((s, i) => s.textContent = activities[i][2]); + document.querySelectorAll('.anilist .activity .title').forEach((t, i) => t.textContent = activities[i][3]); + document.querySelectorAll('.anilist .activity time').forEach((t, i) => { + const now = Math.floor(Date.now() / 1000); + const diff = now - activities[i][4]; + const year = Math.floor(diff / 60 / 60 / 24 / 365); + const month = Math.floor(diff / 60 / 60 / 24 / 30) % 12; + const week = Math.floor(diff / 60 / 60 / 24 / 7) % 4; + const day = Math.floor(diff / 60 / 60 / 24) % 7; + const hour = Math.floor(diff / 60 / 60) % 24; + const minute = Math.floor(diff / 60) % 60; + + if (diff < 60) t.textContent = 'just now'; + else if (diff < 60 * 60) t.textContent = pluralize(minute, 'minute') + ' ago'; + else if (diff < 60 * 60 * 24) t.textContent = pluralize(hour, 'hour') + ' ago'; + else if (diff < 60 * 60 * 24 * 7) t.textContent = pluralize(day, 'day') + ' ago'; + else if (diff < 60 * 60 * 24 * 30) t.textContent = pluralize(week, 'week') + ' ago'; + else if (diff < 60 * 60 * 24 * 365) t.textContent = pluralize(month, 'month') + ' ago'; + else t.textContent = pluralize(year, 'year') + ' ago'; + + t.setAttribute('datetime', new Date(activities[i][3] * 1000).toISOString()); + t.title = new Date(activities[i][4] * 1000).toLocaleString(); + }); +}); + +fetch('/fetchlanyard').then(r => r.json()).then(updateLanyard); + +!function lanyard() { + const ws = new WebSocket('wss://api.lanyard.rest/socket'); + + ws.addEventListener('open', () => ws.send(JSON.stringify({ op: 2, d: { subscribe_to_id: '393694671383166998' } }))); + ws.addEventListener('error', () => ws.close()); + ws.addEventListener('close', () => setTimeout(lanyard, 1000)); + + ws.addEventListener('message', async ({ data }) => { + const { t, d } = JSON.parse(data); + if (t !== 'INIT_STATE' && t !== 'PRESENCE_UPDATE') return; + updateLanyard(d); + }); +}(); + +const ACTIVITY_TYPE = ['Playing', 'Streaming to', 'Listening to', 'Watching', 'Custom status', 'Competing in']; +const STATUS_COLOR = { online: '#4b8', idle: '#fa1', dnd: '#f44', offline: '#778' }; + +function updateLanyard({ discord_user, discord_status, activities }) { + update('.discord .avatar', 'style', `background-image: url(${imageProxy(`https://cdn.discordapp.com/avatars/${discord_user.id}/${discord_user.avatar}.webp?size=80`)})`); + update('.discord .display-name', 'textContent', discord_user.display_name); + update('.discord .color-dot', 'style', `background-color: ${STATUS_COLOR[discord_status]}`); + + activities = activities.filter(a => a.type !== 4); + if (!activities.length) { + update('.discord .status', 'textContent', discord_status); + update('.discord .activity', 'textContent', ''); + update('.discord .details', 'textContent', ''); + update('.discord .state', 'textContent', ''); + update('.discord .large-image', 'style', ''); + update('.discord .large-image', 'title', ''); + update('.discord .small-image', 'style', ''); + update('.discord .small-image', 'title', ''); + setRpcTimestamp('undefined undefined'); + return; + } + + const a = activities[0]; + + ['large', 'small'].forEach(s => { + let imageUrl = a.assets?.[`${s}_image`]; + if (!imageUrl) { + update(`.discord .${s}-image`, 'style', ''); + update(`.discord .${s}-image`, 'title', ''); + if (s === 'small') update(`.discord .image-container foreignObject`, 'mask', ''); + return; + } + if (imageUrl.startsWith('mp:')) imageUrl = `https://media.discordapp.net/${imageUrl.slice(3)}?width=${getSize(s)}&height=${getSize(s)}`; + else if (imageUrl.startsWith('spotify:')) imageUrl = `https://i.scdn.co/image/${imageUrl.slice(8)}`; + else imageUrl = `https://cdn.discordapp.com/app-assets/${a.application_id}/${imageUrl}.png?size=${getSize(s)}`; + update(`.discord .${s}-image`, 'style', `background-image: url(${imageProxy(imageUrl)})`); + update(`.discord .${s}-image`, 'title', a.assets?.[`${s}_text`] || ''); + if (s === 'small') update(`.discord .image-container foreignObject`, 'mask', 'url(#mask-large-image)'); + }); + + update('.discord .status', 'textContent', ACTIVITY_TYPE[a.type]); + update('.discord .activity', 'textContent', a.name); + update('.discord .details', 'textContent', a.details); + update('.discord .state', 'textContent', a.state); + + const timestamp = `${a.timestamps?.start} ${a.timestamps?.end}`; + if (cache.get('timestamp') !== timestamp) setRpcTimestamp(timestamp); +} + +function getSize(s) { + return s === 'large' ? 160 : 60; +} + +function imageProxy(url) { + return `https://chino.id.vn/cdn-cgi/image/format=avif/${url}`; +} + +function pluralize(n, s, p = s + 's') { + return `${n} ${n === 1 ? s : p}`; +} + +function update(selector, property, value) { + const key = `${selector} ${property}`; + if (cache.get(key) === value) return; + + const e = document.querySelector(selector); + if (['mask', 'datetime'].includes(property)) e.setAttribute(property, value); + else e[property] = value; + cache.set(key, value); +} + +function setRpcTimestamp(timestamp = 'undefined undefined') { + cache.set('timestamp', timestamp); + + if (timestamp === 'undefined undefined') { + update('.discord .timestamp', 'textContent', ''); + update('.discord .timebar-container', 'style', 'display: none'); + return; + } + + if (timestamp.includes('undefined')) { + timestamp = Number(timestamp.split(' ')[0] === 'undefined' ? timestamp.split(' ')[1] : timestamp.split(' ')[0]); + const diff = Math.abs(timestamp - Date.now()); + const hour = Math.floor(diff / 1000 / 60 / 60); + const minute = Math.floor(diff / 1000 / 60) % 60; + const second = Math.floor(diff / 1000) % 60; + update('.discord .timestamp', 'textContent', `${hour ? `${padZero(hour)}:` : ''}${padZero(minute)}:${padZero(second)} ${timestamp > Date.now() ? 'left' : 'elapsed'}`); + update('.discord .timebar-container', 'style', 'display: none'); + return; + } + + const [start, end] = timestamp.split(' ').map(t => Number(t)); + const now = Date.now(); + const total = end - start; + const current = now < end ? now - start : total; + const progress = current / total * 100; + const currentHour = Math.floor(current / 1000 / 60 / 60); + const currentMinute = Math.floor(current / 1000 / 60) % 60; + const currentSecond = Math.floor(current / 1000) % 60; + const totalHour = Math.floor(total / 1000 / 60 / 60); + const totalMinute = Math.floor(total / 1000 / 60) % 60; + const totalSecond = Math.floor(total / 1000) % 60; + update('.discord .current-time', 'textContent', `${currentHour ? `${padZero(currentHour)}:` : ''}${padZero(currentMinute)}:${padZero(currentSecond)}`); + update('.discord .total-time', 'textContent', `${totalHour ? `${padZero(totalHour)}:` : ''}${padZero(totalMinute)}:${padZero(totalSecond)}`); + update('.discord .timebar-progress', 'style', `width: ${Math.max(0, Math.min(100, progress))}%`); + update('.discord .timebar-container', 'style', 'display: flex'); +} + +function padZero(n) { + return n.toString().padStart(2, '0'); +} + +let isRecording = false; +let recoder; +let stream; + +document.querySelector('.recorder').addEventListener('click', async () => { + if (!isRecording) { + stream = await navigator.mediaDevices.getDisplayMedia({ audio: true, video: { frameRate: { ideal: 60 } } }); + recoder = new MediaRecorder(stream); + const [video] = stream.getVideoTracks(); + + recoder.start(); + isRecording = true; + + video.addEventListener('ended', () => { + recoder.stop(); + isRecording = false; + }); + + recoder.addEventListener('dataavailable', e => { + const a = document.createElement('a'); + a.href = URL.createObjectURL(e.data); + a.download = 'watch if cute.webm'; + a.click(); + }); + } else { + recoder.stop(); + stream.getTracks().forEach(track => track.stop()); + isRecording = false; + } +}); diff --git a/min.js b/min.js new file mode 100644 index 0000000..7daf206 --- /dev/null +++ b/min.js @@ -0,0 +1 @@ +(()=>{let d=(e,t)=>{for(var r of e)t(r);return e},h=(e,t)=>e.push(t),f=e=>d(e,e=>e()),b=(e,t,r)=>e.setAttribute(t,r),m=(e,t)=>e.insertBefore(t,e.firstChild),v=e=>{var t=document.createElement("div");return b(t,"class",e),t},y=e=>e.offsetHeight,w=e=>e.getBoundingClientRect(),E=(t,e,r)=>{let n=[];return d(e.split(" "),e=>{h(n,((e,t,r)=>e.removeEventListener(t,r)).bind(0,t,e,r)),t.addEventListener(e,r)}),f.bind(0,n)},g=(e,t,r)=>r?r.a/(r.a+r.b):w(e).height/w(t).height;return e=>{let t=(e=>{let[t,r]=(()=>{let n=new Map;return[(e,t)=>{let r=n.get(e)||new Set;return n.set(e,r),d([t],e=>r.add(e))},(e,t)=>d((e=n.get(e),Array.from(e)),e=>e(t))]})(),n={Z:u=(e=e.ownerDocument).documentElement,J:u,d:u,e:e,f:e.defaultView,c:e},o=(s=n,[()=>s,e=>s=e]),i=o[0],l=(()=>{let e=(e=>{let t=o[1];return()=>t({a:e.f.innerHeight,b:w(e.J).height-e.f.innerHeight})})(n);return()=>d([e],e=>e())})(),a=i.bind(0);var s,u;return n,u=n.Z,new ResizeObserver(()=>{l(),r("u")}).observe(u),a.n=e=>t("u",e),a.m=n,a})(e),r=(e=>{let[t]=(e=>{var t,r,n,o;let{c:i,Z:l,d:a}=e,s=(e,n)=>d(e,t=>{var[t,r]=n(t);d(Object.keys(r),e=>t.style[e]=r[e])}),u=[];return r={g:e=v("scrollbar"),h:o=v("track"),i:t=v("thumb")},m(e,o),m(o,t),h(u,r),n=r,b,e=i,o=a,f.bind(0,((e,c,d,h)=>{let{i:b,h:m}=n,v="scrollTop",g="clientY";return E(m,"pointerdown",a=>{var s;let u=Element.prototype.closest.call(a.target,".thumb")===b,p=u?b:m;if(0===a.button&&a.isPrimary&&u&&e.scrollbars.pointers.includes(a.pointerType)){s=d[v];let r=a[g],e=w(b),t=w(m),n=e.top-t.top+e.height/2,o=u?0:r-t.top-n,i=e=>{f(l),p.releasePointerCapture(e.pointerId)},l=[E(c,"pointerup pointerleave pointercancel lostpointercapture",i),E(m,"pointermove",e=>{var t;e=e[g]-r,u&&(e=o+e,t=h().b,e=e/(y(m)-y(b))*t,d[v]=s+e)})];b.setPointerCapture(a.pointerId)}})})(p,e,o,c)),[{k:r=>s(u,e=>{var{i:e,h:t}=e;return[e,{height:(100*g(e,t,r)).toFixed(3)+"%"}]}),l:o=>s(u,e=>{var t,r,{i:e,h:n}=e;return[e,{transform:`translateY(${(100*(r=o.b,t=a.scrollTop/r,1/(r=g(e,n))*(1-r)*t)).toFixed(3)}%)`}]})},m(l,u[0].g)]})(e.m,(p={scrollbars:{dragScroll:!0,pointers:["mouse","touch","pen"]}},c=e)),r=e.m.e,{k:n,l:o}=t;var p,c;return E(r,"scroll",()=>o(e())),()=>{n(e()),o(e())}})(t);return t.n(()=>r())}})()(document.body) \ No newline at end of file diff --git a/nocss.html b/nocss.html new file mode 100644 index 0000000..e2d5738 --- /dev/null +++ b/nocss.html @@ -0,0 +1,86 @@ + + + + + + AutumnVN (no-css) + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +

AutumnVN

+

Hi! I'm AutumnVN, a dude from Vietnam. Nice to meet you :3

+
+
an epic no-css site
+

You can find me on

+ +

Projects

+ + +
+

main sitenocss.club

+
+
+ + + diff --git a/noscript.css b/noscript.css new file mode 100644 index 0000000..46343e9 --- /dev/null +++ b/noscript.css @@ -0,0 +1,14 @@ +.main-container { + opacity: 1; + margin-bottom: 35vh; +} + +.background { + filter: blur(10px) brightness(0.7); +} + +.arrow, +.js, +.project .meta>div:not(.lang) { + display: none; +} diff --git a/osu.png b/osu.png new file mode 100644 index 0000000..65ea4bd Binary files /dev/null and b/osu.png differ diff --git a/preview.png b/preview.png new file mode 100644 index 0000000..55f5dc1 Binary files /dev/null and b/preview.png differ diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000..eb05362 --- /dev/null +++ b/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/skin/Chino.osk b/skin/Chino.osk new file mode 100644 index 0000000..dbca266 Binary files /dev/null and b/skin/Chino.osk differ diff --git a/skin/Mahiru.osk b/skin/Mahiru.osk new file mode 100644 index 0000000..0560d26 Binary files /dev/null and b/skin/Mahiru.osk differ diff --git a/skin/Minimal 3.osk b/skin/Minimal 3.osk new file mode 100644 index 0000000..e6a8cc5 Binary files /dev/null and b/skin/Minimal 3.osk differ diff --git a/skin/Minimal EZ.osk b/skin/Minimal EZ.osk new file mode 100644 index 0000000..ae58161 Binary files /dev/null and b/skin/Minimal EZ.osk differ diff --git a/skin/Minimal FL.osk b/skin/Minimal FL.osk new file mode 100644 index 0000000..86d86b3 Binary files /dev/null and b/skin/Minimal FL.osk differ diff --git a/skin/Minimal Instafade.osk b/skin/Minimal Instafade.osk new file mode 100644 index 0000000..e590be8 Binary files /dev/null and b/skin/Minimal Instafade.osk differ diff --git a/skin/Minimal.osk b/skin/Minimal.osk new file mode 100644 index 0000000..a55d3b6 Binary files /dev/null and b/skin/Minimal.osk differ