initial commit

Changed files
+1107
+688
LICENSE
··· 1 + Copyright (c) 2025 DWN 2 + 3 + Permission is hereby granted, free of charge, to any person 4 + obtaining a copy of this software and associated documentation 5 + files (the "Software"), to deal in the Software without 6 + restriction, including without limitation the rights to use, 7 + copy, modify, merge, publish, distribute, sublicense, and/or sell 8 + copies of the Software, and to permit persons to whom the 9 + Software is furnished to do so, subject to the following 10 + conditions: 11 + 12 + The above copyright notice and this permission notice shall be 13 + included in all copies or substantial portions of the Software. 14 + 15 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 + OTHER DEALINGS IN THE SOFTWARE. 23 + 24 + --- 25 + 26 + Notice that the original project, https://github.com/sparrowhe/bluesky-circle is AGPLv3 licensed, so here's the AGPLv3 text below, notice that no js codes from that project was used. 27 + 28 + GNU AFFERO GENERAL PUBLIC LICENSE 29 + Version 3, 19 November 2007 30 + 31 + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> 32 + Everyone is permitted to copy and distribute verbatim copies 33 + of this license document, but changing it is not allowed. 34 + 35 + Preamble 36 + 37 + The GNU Affero General Public License is a free, copyleft license for 38 + software and other kinds of works, specifically designed to ensure 39 + cooperation with the community in the case of network server software. 40 + 41 + The licenses for most software and other practical works are designed 42 + to take away your freedom to share and change the works. By contrast, 43 + our General Public Licenses are intended to guarantee your freedom to 44 + share and change all versions of a program--to make sure it remains free 45 + software for all its users. 46 + 47 + When we speak of free software, we are referring to freedom, not 48 + price. Our General Public Licenses are designed to make sure that you 49 + have the freedom to distribute copies of free software (and charge for 50 + them if you wish), that you receive source code or can get it if you 51 + want it, that you can change the software or use pieces of it in new 52 + free programs, and that you know you can do these things. 53 + 54 + Developers that use our General Public Licenses protect your rights 55 + with two steps: (1) assert copyright on the software, and (2) offer 56 + you this License which gives you legal permission to copy, distribute 57 + and/or modify the software. 58 + 59 + A secondary benefit of defending all users' freedom is that 60 + improvements made in alternate versions of the program, if they 61 + receive widespread use, become available for other developers to 62 + incorporate. Many developers of free software are heartened and 63 + encouraged by the resulting cooperation. However, in the case of 64 + software used on network servers, this result may fail to come about. 65 + The GNU General Public License permits making a modified version and 66 + letting the public access it on a server without ever releasing its 67 + source code to the public. 68 + 69 + The GNU Affero General Public License is designed specifically to 70 + ensure that, in such cases, the modified source code becomes available 71 + to the community. It requires the operator of a network server to 72 + provide the source code of the modified version running there to the 73 + users of that server. Therefore, public use of a modified version, on 74 + a publicly accessible server, gives the public access to the source 75 + code of the modified version. 76 + 77 + An older license, called the Affero General Public License and 78 + published by Affero, was designed to accomplish similar goals. This is 79 + a different license, not a version of the Affero GPL, but Affero has 80 + released a new version of the Affero GPL which permits relicensing under 81 + this license. 82 + 83 + The precise terms and conditions for copying, distribution and 84 + modification follow. 85 + 86 + TERMS AND CONDITIONS 87 + 88 + 0. Definitions. 89 + 90 + "This License" refers to version 3 of the GNU Affero General Public License. 91 + 92 + "Copyright" also means copyright-like laws that apply to other kinds of 93 + works, such as semiconductor masks. 94 + 95 + "The Program" refers to any copyrightable work licensed under this 96 + License. Each licensee is addressed as "you". "Licensees" and 97 + "recipients" may be individuals or organizations. 98 + 99 + To "modify" a work means to copy from or adapt all or part of the work 100 + in a fashion requiring copyright permission, other than the making of an 101 + exact copy. The resulting work is called a "modified version" of the 102 + earlier work or a work "based on" the earlier work. 103 + 104 + A "covered work" means either the unmodified Program or a work based 105 + on the Program. 106 + 107 + To "propagate" a work means to do anything with it that, without 108 + permission, would make you directly or secondarily liable for 109 + infringement under applicable copyright law, except executing it on a 110 + computer or modifying a private copy. Propagation includes copying, 111 + distribution (with or without modification), making available to the 112 + public, and in some countries other activities as well. 113 + 114 + To "convey" a work means any kind of propagation that enables other 115 + parties to make or receive copies. Mere interaction with a user through 116 + a computer network, with no transfer of a copy, is not conveying. 117 + 118 + An interactive user interface displays "Appropriate Legal Notices" 119 + to the extent that it includes a convenient and prominently visible 120 + feature that (1) displays an appropriate copyright notice, and (2) 121 + tells the user that there is no warranty for the work (except to the 122 + extent that warranties are provided), that licensees may convey the 123 + work under this License, and how to view a copy of this License. If 124 + the interface presents a list of user commands or options, such as a 125 + menu, a prominent item in the list meets this criterion. 126 + 127 + 1. Source Code. 128 + 129 + The "source code" for a work means the preferred form of the work 130 + for making modifications to it. "Object code" means any non-source 131 + form of a work. 132 + 133 + A "Standard Interface" means an interface that either is an official 134 + standard defined by a recognized standards body, or, in the case of 135 + interfaces specified for a particular programming language, one that 136 + is widely used among developers working in that language. 137 + 138 + The "System Libraries" of an executable work include anything, other 139 + than the work as a whole, that (a) is included in the normal form of 140 + packaging a Major Component, but which is not part of that Major 141 + Component, and (b) serves only to enable use of the work with that 142 + Major Component, or to implement a Standard Interface for which an 143 + implementation is available to the public in source code form. A 144 + "Major Component", in this context, means a major essential component 145 + (kernel, window system, and so on) of the specific operating system 146 + (if any) on which the executable work runs, or a compiler used to 147 + produce the work, or an object code interpreter used to run it. 148 + 149 + The "Corresponding Source" for a work in object code form means all 150 + the source code needed to generate, install, and (for an executable 151 + work) run the object code and to modify the work, including scripts to 152 + control those activities. However, it does not include the work's 153 + System Libraries, or general-purpose tools or generally available free 154 + programs which are used unmodified in performing those activities but 155 + which are not part of the work. For example, Corresponding Source 156 + includes interface definition files associated with source files for 157 + the work, and the source code for shared libraries and dynamically 158 + linked subprograms that the work is specifically designed to require, 159 + such as by intimate data communication or control flow between those 160 + subprograms and other parts of the work. 161 + 162 + The Corresponding Source need not include anything that users 163 + can regenerate automatically from other parts of the Corresponding 164 + Source. 165 + 166 + The Corresponding Source for a work in source code form is that 167 + same work. 168 + 169 + 2. Basic Permissions. 170 + 171 + All rights granted under this License are granted for the term of 172 + copyright on the Program, and are irrevocable provided the stated 173 + conditions are met. This License explicitly affirms your unlimited 174 + permission to run the unmodified Program. The output from running a 175 + covered work is covered by this License only if the output, given its 176 + content, constitutes a covered work. This License acknowledges your 177 + rights of fair use or other equivalent, as provided by copyright law. 178 + 179 + You may make, run and propagate covered works that you do not 180 + convey, without conditions so long as your license otherwise remains 181 + in force. You may convey covered works to others for the sole purpose 182 + of having them make modifications exclusively for you, or provide you 183 + with facilities for running those works, provided that you comply with 184 + the terms of this License in conveying all material for which you do 185 + not control copyright. Those thus making or running the covered works 186 + for you must do so exclusively on your behalf, under your direction 187 + and control, on terms that prohibit them from making any copies of 188 + your copyrighted material outside their relationship with you. 189 + 190 + Conveying under any other circumstances is permitted solely under 191 + the conditions stated below. Sublicensing is not allowed; section 10 192 + makes it unnecessary. 193 + 194 + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 195 + 196 + No covered work shall be deemed part of an effective technological 197 + measure under any applicable law fulfilling obligations under article 198 + 11 of the WIPO copyright treaty adopted on 20 December 1996, or 199 + similar laws prohibiting or restricting circumvention of such 200 + measures. 201 + 202 + When you convey a covered work, you waive any legal power to forbid 203 + circumvention of technological measures to the extent such circumvention 204 + is effected by exercising rights under this License with respect to 205 + the covered work, and you disclaim any intention to limit operation or 206 + modification of the work as a means of enforcing, against the work's 207 + users, your or third parties' legal rights to forbid circumvention of 208 + technological measures. 209 + 210 + 4. Conveying Verbatim Copies. 211 + 212 + You may convey verbatim copies of the Program's source code as you 213 + receive it, in any medium, provided that you conspicuously and 214 + appropriately publish on each copy an appropriate copyright notice; 215 + keep intact all notices stating that this License and any 216 + non-permissive terms added in accord with section 7 apply to the code; 217 + keep intact all notices of the absence of any warranty; and give all 218 + recipients a copy of this License along with the Program. 219 + 220 + You may charge any price or no price for each copy that you convey, 221 + and you may offer support or warranty protection for a fee. 222 + 223 + 5. Conveying Modified Source Versions. 224 + 225 + You may convey a work based on the Program, or the modifications to 226 + produce it from the Program, in the form of source code under the 227 + terms of section 4, provided that you also meet all of these conditions: 228 + 229 + a) The work must carry prominent notices stating that you modified 230 + it, and giving a relevant date. 231 + 232 + b) The work must carry prominent notices stating that it is 233 + released under this License and any conditions added under section 234 + 7. This requirement modifies the requirement in section 4 to 235 + "keep intact all notices". 236 + 237 + c) You must license the entire work, as a whole, under this 238 + License to anyone who comes into possession of a copy. This 239 + License will therefore apply, along with any applicable section 7 240 + additional terms, to the whole of the work, and all its parts, 241 + regardless of how they are packaged. This License gives no 242 + permission to license the work in any other way, but it does not 243 + invalidate such permission if you have separately received it. 244 + 245 + d) If the work has interactive user interfaces, each must display 246 + Appropriate Legal Notices; however, if the Program has interactive 247 + interfaces that do not display Appropriate Legal Notices, your 248 + work need not make them do so. 249 + 250 + A compilation of a covered work with other separate and independent 251 + works, which are not by their nature extensions of the covered work, 252 + and which are not combined with it such as to form a larger program, 253 + in or on a volume of a storage or distribution medium, is called an 254 + "aggregate" if the compilation and its resulting copyright are not 255 + used to limit the access or legal rights of the compilation's users 256 + beyond what the individual works permit. Inclusion of a covered work 257 + in an aggregate does not cause this License to apply to the other 258 + parts of the aggregate. 259 + 260 + 6. Conveying Non-Source Forms. 261 + 262 + You may convey a covered work in object code form under the terms 263 + of sections 4 and 5, provided that you also convey the 264 + machine-readable Corresponding Source under the terms of this License, 265 + in one of these ways: 266 + 267 + a) Convey the object code in, or embodied in, a physical product 268 + (including a physical distribution medium), accompanied by the 269 + Corresponding Source fixed on a durable physical medium 270 + customarily used for software interchange. 271 + 272 + b) Convey the object code in, or embodied in, a physical product 273 + (including a physical distribution medium), accompanied by a 274 + written offer, valid for at least three years and valid for as 275 + long as you offer spare parts or customer support for that product 276 + model, to give anyone who possesses the object code either (1) a 277 + copy of the Corresponding Source for all the software in the 278 + product that is covered by this License, on a durable physical 279 + medium customarily used for software interchange, for a price no 280 + more than your reasonable cost of physically performing this 281 + conveying of source, or (2) access to copy the 282 + Corresponding Source from a network server at no charge. 283 + 284 + c) Convey individual copies of the object code with a copy of the 285 + written offer to provide the Corresponding Source. This 286 + alternative is allowed only occasionally and noncommercially, and 287 + only if you received the object code with such an offer, in accord 288 + with subsection 6b. 289 + 290 + d) Convey the object code by offering access from a designated 291 + place (gratis or for a charge), and offer equivalent access to the 292 + Corresponding Source in the same way through the same place at no 293 + further charge. You need not require recipients to copy the 294 + Corresponding Source along with the object code. If the place to 295 + copy the object code is a network server, the Corresponding Source 296 + may be on a different server (operated by you or a third party) 297 + that supports equivalent copying facilities, provided you maintain 298 + clear directions next to the object code saying where to find the 299 + Corresponding Source. Regardless of what server hosts the 300 + Corresponding Source, you remain obligated to ensure that it is 301 + available for as long as needed to satisfy these requirements. 302 + 303 + e) Convey the object code using peer-to-peer transmission, provided 304 + you inform other peers where the object code and Corresponding 305 + Source of the work are being offered to the general public at no 306 + charge under subsection 6d. 307 + 308 + A separable portion of the object code, whose source code is excluded 309 + from the Corresponding Source as a System Library, need not be 310 + included in conveying the object code work. 311 + 312 + A "User Product" is either (1) a "consumer product", which means any 313 + tangible personal property which is normally used for personal, family, 314 + or household purposes, or (2) anything designed or sold for incorporation 315 + into a dwelling. In determining whether a product is a consumer product, 316 + doubtful cases shall be resolved in favor of coverage. For a particular 317 + product received by a particular user, "normally used" refers to a 318 + typical or common use of that class of product, regardless of the status 319 + of the particular user or of the way in which the particular user 320 + actually uses, or expects or is expected to use, the product. A product 321 + is a consumer product regardless of whether the product has substantial 322 + commercial, industrial or non-consumer uses, unless such uses represent 323 + the only significant mode of use of the product. 324 + 325 + "Installation Information" for a User Product means any methods, 326 + procedures, authorization keys, or other information required to install 327 + and execute modified versions of a covered work in that User Product from 328 + a modified version of its Corresponding Source. The information must 329 + suffice to ensure that the continued functioning of the modified object 330 + code is in no case prevented or interfered with solely because 331 + modification has been made. 332 + 333 + If you convey an object code work under this section in, or with, or 334 + specifically for use in, a User Product, and the conveying occurs as 335 + part of a transaction in which the right of possession and use of the 336 + User Product is transferred to the recipient in perpetuity or for a 337 + fixed term (regardless of how the transaction is characterized), the 338 + Corresponding Source conveyed under this section must be accompanied 339 + by the Installation Information. But this requirement does not apply 340 + if neither you nor any third party retains the ability to install 341 + modified object code on the User Product (for example, the work has 342 + been installed in ROM). 343 + 344 + The requirement to provide Installation Information does not include a 345 + requirement to continue to provide support service, warranty, or updates 346 + for a work that has been modified or installed by the recipient, or for 347 + the User Product in which it has been modified or installed. Access to a 348 + network may be denied when the modification itself materially and 349 + adversely affects the operation of the network or violates the rules and 350 + protocols for communication across the network. 351 + 352 + Corresponding Source conveyed, and Installation Information provided, 353 + in accord with this section must be in a format that is publicly 354 + documented (and with an implementation available to the public in 355 + source code form), and must require no special password or key for 356 + unpacking, reading or copying. 357 + 358 + 7. Additional Terms. 359 + 360 + "Additional permissions" are terms that supplement the terms of this 361 + License by making exceptions from one or more of its conditions. 362 + Additional permissions that are applicable to the entire Program shall 363 + be treated as though they were included in this License, to the extent 364 + that they are valid under applicable law. If additional permissions 365 + apply only to part of the Program, that part may be used separately 366 + under those permissions, but the entire Program remains governed by 367 + this License without regard to the additional permissions. 368 + 369 + When you convey a copy of a covered work, you may at your option 370 + remove any additional permissions from that copy, or from any part of 371 + it. (Additional permissions may be written to require their own 372 + removal in certain cases when you modify the work.) You may place 373 + additional permissions on material, added by you to a covered work, 374 + for which you have or can give appropriate copyright permission. 375 + 376 + Notwithstanding any other provision of this License, for material you 377 + add to a covered work, you may (if authorized by the copyright holders of 378 + that material) supplement the terms of this License with terms: 379 + 380 + a) Disclaiming warranty or limiting liability differently from the 381 + terms of sections 15 and 16 of this License; or 382 + 383 + b) Requiring preservation of specified reasonable legal notices or 384 + author attributions in that material or in the Appropriate Legal 385 + Notices displayed by works containing it; or 386 + 387 + c) Prohibiting misrepresentation of the origin of that material, or 388 + requiring that modified versions of such material be marked in 389 + reasonable ways as different from the original version; or 390 + 391 + d) Limiting the use for publicity purposes of names of licensors or 392 + authors of the material; or 393 + 394 + e) Declining to grant rights under trademark law for use of some 395 + trade names, trademarks, or service marks; or 396 + 397 + f) Requiring indemnification of licensors and authors of that 398 + material by anyone who conveys the material (or modified versions of 399 + it) with contractual assumptions of liability to the recipient, for 400 + any liability that these contractual assumptions directly impose on 401 + those licensors and authors. 402 + 403 + All other non-permissive additional terms are considered "further 404 + restrictions" within the meaning of section 10. If the Program as you 405 + received it, or any part of it, contains a notice stating that it is 406 + governed by this License along with a term that is a further 407 + restriction, you may remove that term. If a license document contains 408 + a further restriction but permits relicensing or conveying under this 409 + License, you may add to a covered work material governed by the terms 410 + of that license document, provided that the further restriction does 411 + not survive such relicensing or conveying. 412 + 413 + If you add terms to a covered work in accord with this section, you 414 + must place, in the relevant source files, a statement of the 415 + additional terms that apply to those files, or a notice indicating 416 + where to find the applicable terms. 417 + 418 + Additional terms, permissive or non-permissive, may be stated in the 419 + form of a separately written license, or stated as exceptions; 420 + the above requirements apply either way. 421 + 422 + 8. Termination. 423 + 424 + You may not propagate or modify a covered work except as expressly 425 + provided under this License. Any attempt otherwise to propagate or 426 + modify it is void, and will automatically terminate your rights under 427 + this License (including any patent licenses granted under the third 428 + paragraph of section 11). 429 + 430 + However, if you cease all violation of this License, then your 431 + license from a particular copyright holder is reinstated (a) 432 + provisionally, unless and until the copyright holder explicitly and 433 + finally terminates your license, and (b) permanently, if the copyright 434 + holder fails to notify you of the violation by some reasonable means 435 + prior to 60 days after the cessation. 436 + 437 + Moreover, your license from a particular copyright holder is 438 + reinstated permanently if the copyright holder notifies you of the 439 + violation by some reasonable means, this is the first time you have 440 + received notice of violation of this License (for any work) from that 441 + copyright holder, and you cure the violation prior to 30 days after 442 + your receipt of the notice. 443 + 444 + Termination of your rights under this section does not terminate the 445 + licenses of parties who have received copies or rights from you under 446 + this License. If your rights have been terminated and not permanently 447 + reinstated, you do not qualify to receive new licenses for the same 448 + material under section 10. 449 + 450 + 9. Acceptance Not Required for Having Copies. 451 + 452 + You are not required to accept this License in order to receive or 453 + run a copy of the Program. Ancillary propagation of a covered work 454 + occurring solely as a consequence of using peer-to-peer transmission 455 + to receive a copy likewise does not require acceptance. However, 456 + nothing other than this License grants you permission to propagate or 457 + modify any covered work. These actions infringe copyright if you do 458 + not accept this License. Therefore, by modifying or propagating a 459 + covered work, you indicate your acceptance of this License to do so. 460 + 461 + 10. Automatic Licensing of Downstream Recipients. 462 + 463 + Each time you convey a covered work, the recipient automatically 464 + receives a license from the original licensors, to run, modify and 465 + propagate that work, subject to this License. You are not responsible 466 + for enforcing compliance by third parties with this License. 467 + 468 + An "entity transaction" is a transaction transferring control of an 469 + organization, or substantially all assets of one, or subdividing an 470 + organization, or merging organizations. If propagation of a covered 471 + work results from an entity transaction, each party to that 472 + transaction who receives a copy of the work also receives whatever 473 + licenses to the work the party's predecessor in interest had or could 474 + give under the previous paragraph, plus a right to possession of the 475 + Corresponding Source of the work from the predecessor in interest, if 476 + the predecessor has it or can get it with reasonable efforts. 477 + 478 + You may not impose any further restrictions on the exercise of the 479 + rights granted or affirmed under this License. For example, you may 480 + not impose a license fee, royalty, or other charge for exercise of 481 + rights granted under this License, and you may not initiate litigation 482 + (including a cross-claim or counterclaim in a lawsuit) alleging that 483 + any patent claim is infringed by making, using, selling, offering for 484 + sale, or importing the Program or any portion of it. 485 + 486 + 11. Patents. 487 + 488 + A "contributor" is a copyright holder who authorizes use under this 489 + License of the Program or a work on which the Program is based. The 490 + work thus licensed is called the contributor's "contributor version". 491 + 492 + A contributor's "essential patent claims" are all patent claims 493 + owned or controlled by the contributor, whether already acquired or 494 + hereafter acquired, that would be infringed by some manner, permitted 495 + by this License, of making, using, or selling its contributor version, 496 + but do not include claims that would be infringed only as a 497 + consequence of further modification of the contributor version. For 498 + purposes of this definition, "control" includes the right to grant 499 + patent sublicenses in a manner consistent with the requirements of 500 + this License. 501 + 502 + Each contributor grants you a non-exclusive, worldwide, royalty-free 503 + patent license under the contributor's essential patent claims, to 504 + make, use, sell, offer for sale, import and otherwise run, modify and 505 + propagate the contents of its contributor version. 506 + 507 + In the following three paragraphs, a "patent license" is any express 508 + agreement or commitment, however denominated, not to enforce a patent 509 + (such as an express permission to practice a patent or covenant not to 510 + sue for patent infringement). To "grant" such a patent license to a 511 + party means to make such an agreement or commitment not to enforce a 512 + patent against the party. 513 + 514 + If you convey a covered work, knowingly relying on a patent license, 515 + and the Corresponding Source of the work is not available for anyone 516 + to copy, free of charge and under the terms of this License, through a 517 + publicly available network server or other readily accessible means, 518 + then you must either (1) cause the Corresponding Source to be so 519 + available, or (2) arrange to deprive yourself of the benefit of the 520 + patent license for this particular work, or (3) arrange, in a manner 521 + consistent with the requirements of this License, to extend the patent 522 + license to downstream recipients. "Knowingly relying" means you have 523 + actual knowledge that, but for the patent license, your conveying the 524 + covered work in a country, or your recipient's use of the covered work 525 + in a country, would infringe one or more identifiable patents in that 526 + country that you have reason to believe are valid. 527 + 528 + If, pursuant to or in connection with a single transaction or 529 + arrangement, you convey, or propagate by procuring conveyance of, a 530 + covered work, and grant a patent license to some of the parties 531 + receiving the covered work authorizing them to use, propagate, modify 532 + or convey a specific copy of the covered work, then the patent license 533 + you grant is automatically extended to all recipients of the covered 534 + work and works based on it. 535 + 536 + A patent license is "discriminatory" if it does not include within 537 + the scope of its coverage, prohibits the exercise of, or is 538 + conditioned on the non-exercise of one or more of the rights that are 539 + specifically granted under this License. You may not convey a covered 540 + work if you are a party to an arrangement with a third party that is 541 + in the business of distributing software, under which you make payment 542 + to the third party based on the extent of your activity of conveying 543 + the work, and under which the third party grants, to any of the 544 + parties who would receive the covered work from you, a discriminatory 545 + patent license (a) in connection with copies of the covered work 546 + conveyed by you (or copies made from those copies), or (b) primarily 547 + for and in connection with specific products or compilations that 548 + contain the covered work, unless you entered into that arrangement, 549 + or that patent license was granted, prior to 28 March 2007. 550 + 551 + Nothing in this License shall be construed as excluding or limiting 552 + any implied license or other defenses to infringement that may 553 + otherwise be available to you under applicable patent law. 554 + 555 + 12. No Surrender of Others' Freedom. 556 + 557 + If conditions are imposed on you (whether by court order, agreement or 558 + otherwise) that contradict the conditions of this License, they do not 559 + excuse you from the conditions of this License. If you cannot convey a 560 + covered work so as to satisfy simultaneously your obligations under this 561 + License and any other pertinent obligations, then as a consequence you may 562 + not convey it at all. For example, if you agree to terms that obligate you 563 + to collect a royalty for further conveying from those to whom you convey 564 + the Program, the only way you could satisfy both those terms and this 565 + License would be to refrain entirely from conveying the Program. 566 + 567 + 13. Remote Network Interaction; Use with the GNU General Public License. 568 + 569 + Notwithstanding any other provision of this License, if you modify the 570 + Program, your modified version must prominently offer all users 571 + interacting with it remotely through a computer network (if your version 572 + supports such interaction) an opportunity to receive the Corresponding 573 + Source of your version by providing access to the Corresponding Source 574 + from a network server at no charge, through some standard or customary 575 + means of facilitating copying of software. This Corresponding Source 576 + shall include the Corresponding Source for any work covered by version 3 577 + of the GNU General Public License that is incorporated pursuant to the 578 + following paragraph. 579 + 580 + Notwithstanding any other provision of this License, you have 581 + permission to link or combine any covered work with a work licensed 582 + under version 3 of the GNU General Public License into a single 583 + combined work, and to convey the resulting work. The terms of this 584 + License will continue to apply to the part which is the covered work, 585 + but the work with which it is combined will remain governed by version 586 + 3 of the GNU General Public License. 587 + 588 + 14. Revised Versions of this License. 589 + 590 + The Free Software Foundation may publish revised and/or new versions of 591 + the GNU Affero General Public License from time to time. Such new versions 592 + will be similar in spirit to the present version, but may differ in detail to 593 + address new problems or concerns. 594 + 595 + Each version is given a distinguishing version number. If the 596 + Program specifies that a certain numbered version of the GNU Affero General 597 + Public License "or any later version" applies to it, you have the 598 + option of following the terms and conditions either of that numbered 599 + version or of any later version published by the Free Software 600 + Foundation. If the Program does not specify a version number of the 601 + GNU Affero General Public License, you may choose any version ever published 602 + by the Free Software Foundation. 603 + 604 + If the Program specifies that a proxy can decide which future 605 + versions of the GNU Affero General Public License can be used, that proxy's 606 + public statement of acceptance of a version permanently authorizes you 607 + to choose that version for the Program. 608 + 609 + Later license versions may give you additional or different 610 + permissions. However, no additional obligations are imposed on any 611 + author or copyright holder as a result of your choosing to follow a 612 + later version. 613 + 614 + 15. Disclaimer of Warranty. 615 + 616 + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 617 + APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 618 + HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 619 + OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 620 + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 621 + PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 622 + IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 623 + ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 624 + 625 + 16. Limitation of Liability. 626 + 627 + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 628 + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 629 + THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 630 + GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 631 + USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 632 + DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 633 + PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 634 + EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 635 + SUCH DAMAGES. 636 + 637 + 17. Interpretation of Sections 15 and 16. 638 + 639 + If the disclaimer of warranty and limitation of liability provided 640 + above cannot be given local legal effect according to their terms, 641 + reviewing courts shall apply local law that most closely approximates 642 + an absolute waiver of all civil liability in connection with the 643 + Program, unless a warranty or assumption of liability accompanies a 644 + copy of the Program in return for a fee. 645 + 646 + END OF TERMS AND CONDITIONS 647 + 648 + How to Apply These Terms to Your New Programs 649 + 650 + If you develop a new program, and you want it to be of the greatest 651 + possible use to the public, the best way to achieve this is to make it 652 + free software which everyone can redistribute and change under these terms. 653 + 654 + To do so, attach the following notices to the program. It is safest 655 + to attach them to the start of each source file to most effectively 656 + state the exclusion of warranty; and each file should have at least 657 + the "copyright" line and a pointer to where the full notice is found. 658 + 659 + <one line to give the program's name and a brief idea of what it does.> 660 + Copyright (C) <year> <name of author> 661 + 662 + This program is free software: you can redistribute it and/or modify 663 + it under the terms of the GNU Affero General Public License as published 664 + by the Free Software Foundation, either version 3 of the License, or 665 + (at your option) any later version. 666 + 667 + This program is distributed in the hope that it will be useful, 668 + but WITHOUT ANY WARRANTY; without even the implied warranty of 669 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 670 + GNU Affero General Public License for more details. 671 + 672 + You should have received a copy of the GNU Affero General Public License 673 + along with this program. If not, see <https://www.gnu.org/licenses/>. 674 + 675 + Also add information on how to contact you by electronic and paper mail. 676 + 677 + If your software can interact with users remotely through a computer 678 + network, you should also make sure that it provides a way for users to 679 + get its source. For example, if your program is a web application, its 680 + interface could display a "Source" link that leads users to an archive 681 + of the code. There are many ways you could offer source, and different 682 + solutions will be better for different programs; see section 13 for the 683 + specific requirements. 684 + 685 + You should also get your employer (if you work as a programmer) or school, 686 + if any, to sign a "copyright disclaimer" for the program, if necessary. 687 + For more information on this, and how to apply and follow the GNU AGPL, see 688 + <https://www.gnu.org/licenses/>.
+16
README.md
··· 1 + # 半个贴贴圈 2 + 3 + Sparrow He([~~Bluesky~~](https://bsky.app/profile/sparrow.0x0e.top)、[X](https://x.com/SparrowHe))的[「贴贴圈」](https://circle.sparrowhe.top)(网站不可用,[GitHub](https://github.com/sparrowhe/bluesky-circle),[本人亲自归档](https://tangled.sh/@dwn.dwnfonts.cc/bluesky-circle))的**纯前端**重写。 4 + 5 + [接下来的内容先咕咕。](https://archive.is/yjEZs#:~:text=%E6%8E%A5%E4%B8%8B%E6%9D%A5%E7%9A%84%E5%86%85%E5%AE%B9%E5%85%88%E5%92%95%E5%92%95%E3%80%82) 6 + 7 + # knot 注明 8 + 9 + 本仓库使用 Cloudflare Tunnel 进行 SSH 内网穿透。请[下载 cloudflared](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/) 后将下列文本加到 `~/.ssh/config` 后面: 10 + 11 + ``` 12 + Host ssh.example.com 13 + ProxyCommand /usr/local/bin/cloudflared access ssh --hostname %h 14 + ``` 15 + 16 + 然后替换 `knot.dwnfonts.cc` 为 `knot-ssh.dwnfonts.cc` 进行 SSH 操作。
+403
app.html
··· 1 + <!DOCTYPE html> 2 + <html> 3 + <head> 4 + <meta charset="UTF-8" /> 5 + <title>半个贴贴圈</title> 6 + <link 7 + href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" 8 + rel="stylesheet" 9 + /> 10 + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script> 11 + <script> 12 + async function changeProgress(currentProgress) { 13 + document.getElementById("progress").innerHTML = currentProgress; 14 + } 15 + 16 + // 常量定义 17 + const PI = Math.PI; 18 + const CANVAS_SIZE = 850; // 画布大小 19 + const CENTER_X = CANVAS_SIZE / 2; // 画布中心X坐标 20 + const CENTER_Y = CANVAS_SIZE / 2; // 画布中心Y坐标 21 + 22 + // 头像大小配置 23 + const BASE_RADIUS = Math.floor(35 * 1.25); // 基础半径 24 + const CENTER_AVATAR_SIZE = 150; // 中心头像直径 25 + const SIZE_STEP = 5; // 每一层头像大小递减值 26 + const MIN_AVATAR_SIZE = 15; // 最小头像直径 27 + 28 + // 布局配置 29 + const RADIUS_STEP = 85; // 每一层圆的半径增量 30 + const INITIAL_RADIUS = 140; // 初始圆半径 31 + const GAP_SIZE = 10; // 头像之间的间隙 32 + const MAX_LAYERS = 4; // 最大层数 33 + 34 + /** 35 + * 计算中心头像的位置 36 + * @returns {Object} 包含x, y坐标和size的对象 37 + */ 38 + function calculateCenterAvatarPosition() { 39 + const radius = CENTER_AVATAR_SIZE / 2; 40 + return { 41 + x: Math.round(CENTER_X - radius), 42 + y: Math.round(CENTER_Y - radius), 43 + size: CENTER_AVATAR_SIZE, 44 + }; 45 + } 46 + 47 + /** 48 + * 根据朋友数量计算所有头像的位置和大小 49 + * @param {number} friendCount 朋友数量 50 + * @returns {Array} 包含每个头像位置和大小的对象数组,格式为{"x":xxx,"y":xxx,"size":xxx} 51 + */ 52 + function calculateAvatarPositionsByCount(friendCount) { 53 + const result = []; 54 + let friendIndex = 0; 55 + let currentRadius = INITIAL_RADIUS; 56 + let layer = 0; 57 + 58 + while (friendIndex < friendCount && layer < MAX_LAYERS) { 59 + // 计算当前层的头像半径和直径 60 + const avatarRadius = Math.max( 61 + BASE_RADIUS - layer * SIZE_STEP, 62 + Math.floor(MIN_AVATAR_SIZE / 2) 63 + ); 64 + const avatarSize = avatarRadius * 2; 65 + 66 + // 计算当前层可以放置的头像数量 67 + const circumference = 2 * PI * currentRadius; 68 + const numInCurrentCircle = Math.floor( 69 + circumference / (avatarSize + GAP_SIZE) 70 + ); 71 + 72 + if (numInCurrentCircle <= 0) break; 73 + 74 + // 计算每个头像之间的角度步长 75 + const thetaStep = (2 * PI) / numInCurrentCircle; 76 + // 每层的旋转偏移 77 + const rotationOffset = layer * (PI / 12); 78 + 79 + // 计算当前层每个头像的位置 80 + for (let i = 0; i < numInCurrentCircle; i++) { 81 + if (friendIndex >= friendCount) break; 82 + 83 + // 计算当前头像的角度(包含旋转偏移) 84 + const theta = i * thetaStep + rotationOffset; 85 + 86 + // 计算X和Y坐标(带偏移修正) 87 + const x = CENTER_X + currentRadius * Math.cos(theta) - avatarRadius; 88 + const y = CENTER_Y + currentRadius * Math.sin(theta) - avatarRadius; 89 + 90 + // 添加到结果数组,格式为{"x":xxx,"y":xxx,"size":xxx} 91 + result.push({ 92 + x: Math.round(x), 93 + y: Math.round(y), 94 + size: avatarSize, 95 + }); 96 + 97 + friendIndex++; 98 + } 99 + 100 + // 进入下一层 101 + currentRadius += RADIUS_STEP; 102 + layer++; 103 + } 104 + 105 + return result; 106 + } 107 + 108 + async function getReplyScore(handle) { 109 + const ENDPOINT_URL = "https://public.api.bsky.app/xrpc/"; 110 + 111 + async function handle2DID(handle) { 112 + try { 113 + const response = await fetch( 114 + `${ENDPOINT_URL}com.atproto.identity.resolveHandle?handle=${handle}` 115 + ); 116 + if (!response.ok) { 117 + throw new Error(`http response code: ${response.status}`); 118 + } 119 + const data = await response.json(); 120 + console.log("data:", data); 121 + return data; 122 + } catch (error) { 123 + console.error("request fail:", error); 124 + throw error; 125 + } 126 + } 127 + 128 + async function getPosts(did) { 129 + try { 130 + const response = await fetch( 131 + `${ENDPOINT_URL}app.bsky.feed.getAuthorFeed?actor=${did}&limit=100&includePins=true` 132 + ); 133 + console.log( 134 + `${ENDPOINT_URL}app.bsky.feed.getAuthorFeed?actor=${did}&limit=100&includePins=true` 135 + ); 136 + if (!response.ok) { 137 + throw new Error(`http response code: ${response.status}`); 138 + } 139 + const data = await response.json(); 140 + // console.log("data:", data); 141 + return data; 142 + } catch (error) { 143 + console.error("request fail:", error); 144 + throw error; 145 + } 146 + } 147 + 148 + async function getReplies(uri) { 149 + try { 150 + const response = await fetch( 151 + `${ENDPOINT_URL}app.bsky.feed.getPostThread?uri=${uri}&depth=1` 152 + ); 153 + console.log( 154 + `${ENDPOINT_URL}app.bsky.feed.getPostThread?uri=${uri}&depth=1` 155 + ); 156 + if (!response.ok) { 157 + throw new Error(`http response code: ${response.status}`); 158 + } 159 + const data = await response.json(); 160 + // console.log("data:", data); 161 + return data; 162 + } catch (error) { 163 + console.error("request fail:", error); 164 + throw error; 165 + } 166 + } 167 + 168 + async function getHandle(did) { 169 + try { 170 + const response = await fetch( 171 + `${ENDPOINT_URL}app.bsky.actor.getProfile?actor=${did}` 172 + ); 173 + console.log( 174 + `${ENDPOINT_URL}app.bsky.actor.getProfile?actor=${did}` 175 + ); 176 + if (!response.ok) { 177 + throw new Error(`http response code: ${response.status}`); 178 + } 179 + const data = await response.json(); 180 + // console.log("data:", data); 181 + return data; 182 + } catch (error) { 183 + console.error("request fail:", error); 184 + throw error; 185 + } 186 + } 187 + 188 + function getDidByAtUrl(aturl) { 189 + return aturl.split("/")[2]; 190 + } 191 + 192 + var result = {}; 193 + 194 + await changeProgress(`[0/2] resolving handle ${handle}`); 195 + const did_result = await handle2DID(handle); 196 + var did = did_result.did; 197 + 198 + handle_result = await getHandle(did); 199 + handle = handle_result.handle; 200 + avatar = handle_result.avatar; 201 + 202 + // result_html = '<div class="result">\n'; 203 + result_html = ""; 204 + 205 + // console.log(`${handle}'s reply_count is ${reply_count}, avatar ${handle_result.avatar}`); 206 + result_html += `<a href="https://bsky.app/profile/${handle}">\n`; 207 + 208 + result_html += `<img style="border-radius:50%;left:350px;top:350px;max-height:150px;"\n`; 209 + result_html += ` src="${avatar}"\n`; 210 + result_html += ` data-bs-toggle="popover"\n`; 211 + result_html += ` title="@${handle}"\n`; 212 + result_html += ` data-bs-content="@${handle} shows the handle itself"\n`; 213 + result_html += ` target="_blank" />\n`; 214 + 215 + result_html += `</a>\n\n`; 216 + // console.log(result_html); 217 + 218 + document.getElementById("result").innerHTML += result_html; 219 + 220 + const posts_result = await getPosts(did); 221 + for (const id in posts_result.feed) { 222 + item = posts_result.feed[id]; 223 + await changeProgress( 224 + `[1/2] fetching post ${item.post.uri}, ${id}/${posts_result.feed.length}` 225 + ); 226 + // will recognize posts with no reason, whose replies will + 1.5 227 + // posts has reply.parent, parent + 1 228 + if ("reason" in item) { 229 + console.warn( 230 + `${item.post.uri} has reason ${item.reason.$type}, ignored` 231 + ); 232 + } else { 233 + if ("reply" in item) { 234 + console.log(`${item.post.uri} replies ${item.reply.parent.uri}`); 235 + did_of_the_guy = getDidByAtUrl(item.reply.parent.uri); 236 + if (!(did_of_the_guy in result)) { 237 + result[did_of_the_guy] = 0.0; 238 + } 239 + result[did_of_the_guy] += 1.0; 240 + } else { 241 + main_uri = item.post.uri; 242 + reply_result = await getReplies(main_uri); 243 + if ("thread" in reply_result) { 244 + for (const replypost of reply_result.thread.replies) { 245 + reply_post_uri = replypost.post.uri; 246 + did_of_the_guy = getDidByAtUrl(reply_post_uri); 247 + if (!(did_of_the_guy in result)) { 248 + result[did_of_the_guy] = 0.0; 249 + } 250 + result[did_of_the_guy] += 1.5; 251 + } 252 + } else { 253 + console.warn(`${item.post.uri} has no replies`); 254 + } 255 + } 256 + } 257 + } 258 + 259 + if (did in result) { 260 + delete result.did; 261 + } 262 + 263 + function format_result(input) { 264 + // 反转键值对 265 + const reversed = Object.entries(input).reduce((acc, [key, value]) => { 266 + if (!acc[value]) { 267 + acc[value] = []; 268 + } 269 + acc[value].push(key); 270 + return acc; 271 + }, {}); 272 + 273 + // 对键进行数值降序排序 274 + const sortedKeys = Object.keys(reversed) 275 + .map(Number) 276 + .sort((a, b) => b - a); // 仅保留降序逻辑 277 + 278 + // 生成最终的排序对象,并对每个数组内的did排序 279 + return sortedKeys.reduce((acc, key) => { 280 + acc[key] = reversed[key].sort(); 281 + return acc; 282 + }, {}); 283 + } 284 + 285 + formatted_result = format_result(result); 286 + 287 + avatar_id = 0; 288 + 289 + const sorted_rc = Object.keys(formatted_result).sort((a, b) => b - a); // 降序排序 290 + 291 + total_user = Object.keys(result).length; 292 + 293 + proceed_user = 0; 294 + 295 + avatars_list = calculateAvatarPositionsByCount(total_user); 296 + 297 + console.warn(avatars_list.length); 298 + 299 + for (const reply_count of sorted_rc) { 300 + for (const final_did of formatted_result[reply_count]) { 301 + proceed_user += 1; 302 + await changeProgress( 303 + `[2/2] fetching metadata of ${final_did}, ${proceed_user}/${total_user} or 87` 304 + ); 305 + if (proceed_user < 88) { 306 + handle_result = await getHandle(final_did); 307 + handle = handle_result.handle; 308 + avatar = handle_result.avatar; 309 + 310 + // console.log(`${handle}'s reply_count is ${reply_count}, avatar ${handle_result.avatar}`); 311 + result_html += `<a href="https://bsky.app/profile/${handle}">\n`; 312 + 313 + result_html += `<img style="border-radius:50%;left:${ 314 + avatars_list[proceed_user - 1].x 315 + }px;top:${avatars_list[proceed_user - 1].y}px;max-height:${ 316 + avatars_list[proceed_user - 1].size 317 + }px;"\n`; 318 + result_html += ` src="${avatar}"\n`; 319 + result_html += ` data-bs-toggle="popover"\n`; 320 + result_html += ` title="@${handle}"\n`; 321 + result_html += ` data-bs-content="@${handle} got ${reply_count} reply counts"\n`; 322 + result_html += ` target="_blank" />\n`; 323 + 324 + result_html += `</a>\n\n`; 325 + // console.log(result_html); 326 + 327 + document.getElementById("result").innerHTML = result_html; 328 + } 329 + } 330 + } // result_html += "</div>"; 331 + return result_html; 332 + } 333 + 334 + // 使用示例 335 + async function main() { 336 + try { 337 + document.getElementById("result").innerHTML = ""; 338 + const did = await getReplyScore( 339 + document.getElementById("bruh").value 340 + ); 341 + console.log("result:", did); 342 + } catch (error) { 343 + console.error("fail:", error); 344 + } 345 + } 346 + 347 + main(); 348 + 349 + function changeColor() { 350 + targetElement = document.getElementById("background"); 351 + const colorPicker = document.createElement("input"); 352 + colorPicker.type = "color"; 353 + 354 + colorPicker.addEventListener("input", (e) => { 355 + targetElement.style.backgroundColor = e.target.value; 356 + }); 357 + 358 + // 触发颜色选择对话框 359 + colorPicker.click(); 360 + } 361 + </script> 362 + <style> 363 + #progress::before { 364 + content: "当前进度:"; 365 + } 366 + 367 + #result { 368 + position: absolute; 369 + } 370 + 371 + #result img { 372 + position: absolute; 373 + z-index: 10; 374 + height: auto; 375 + } 376 + 377 + #background { 378 + height: 850px; 379 + width: 850px; 380 + background-color: blueviolet; 381 + } 382 + </style> 383 + </head> 384 + 385 + <body> 386 + <div class="container"> 387 + <label for="bruh" class="form-label">点击输入 handle</label> 388 + <input type="text" id="bruh" class="form-control" /> 389 + <button class="btn btn-primary" onclick="main()">启动</button> 390 + <button class="btn btn-primary" onclick="changeColor()">换背景色</button> 391 + <button class="btn btn-primary" onclick="alert('自己截去😡😡😡')"> 392 + 导出 PNG! 393 + </button> 394 + <div id="progress"></div> 395 + <div id="ssarea"> 396 + <div id="result"></div> 397 + </div> 398 + <div id="background"></div> 399 + </div> 400 + <div id="readme"> 401 + </div> 402 + </body> 403 + </html>