diff --git a/STATS.md b/STATS.md index 242ca4c1c53..673d04f3438 100644 --- a/STATS.md +++ b/STATS.md @@ -1,7 +1,175 @@ # Download Stats -| Date | GitHub Downloads | npm Downloads | Total | -| ---------- | ---------------- | --------------- | --------------- | -| 2025-06-29 | 18,789 (+0) | 39,420 (+0) | 58,209 (+0) | -| 2025-06-30 | 20,127 (+1,338) | 41,059 (+1,639) | 61,186 (+2,977) | -| 2025-07-01 | 22,108 (+1,981) | 43,745 (+2,686) | 65,853 (+4,667) | +| Date | GitHub Downloads | npm Downloads | Total | +| ---------- | ------------------- | ------------------- | ------------------- | +| 2025-06-29 | 18,789 (+0) | 39,420 (+0) | 58,209 (+0) | +| 2025-06-30 | 20,127 (+1,338) | 41,059 (+1,639) | 61,186 (+2,977) | +| 2025-07-01 | 22,108 (+1,981) | 43,745 (+2,686) | 65,853 (+4,667) | +| 2025-07-02 | 24,788 (+2,680) | 46,168 (+2,423) | 70,956 (+5,103) | +| 2025-07-03 | 27,814 (+3,026) | 49,955 (+3,787) | 77,769 (+6,813) | +| 2025-07-04 | 30,599 (+2,785) | 54,758 (+4,803) | 85,357 (+7,588) | +| 2025-07-05 | 32,539 (+1,940) | 58,371 (+3,613) | 90,910 (+5,553) | +| 2025-07-06 | 33,775 (+1,236) | 59,694 (+1,323) | 93,469 (+2,559) | +| 2025-07-07 | 35,466 (+1,691) | 61,847 (+2,153) | 97,313 (+3,844) | +| 2025-07-08 | 38,170 (+2,704) | 64,468 (+2,621) | 102,638 (+5,325) | +| 2025-07-09 | 40,909 (+2,739) | 67,872 (+3,404) | 108,781 (+6,143) | +| 2025-07-10 | 43,821 (+2,912) | 71,402 (+3,530) | 115,223 (+6,442) | +| 2025-07-11 | 47,003 (+3,182) | 77,462 (+6,060) | 124,465 (+9,242) | +| 2025-07-12 | 49,311 (+2,308) | 82,177 (+4,715) | 131,488 (+7,023) | +| 2025-07-13 | 50,815 (+1,504) | 86,394 (+4,217) | 137,209 (+5,721) | +| 2025-07-14 | 53,303 (+2,488) | 87,860 (+1,466) | 141,163 (+3,954) | +| 2025-07-15 | 57,618 (+4,315) | 91,036 (+3,176) | 148,654 (+7,491) | +| 2025-07-16 | 62,343 (+4,725) | 95,258 (+4,222) | 157,601 (+8,947) | +| 2025-07-17 | 66,382 (+4,039) | 100,048 (+4,790) | 166,430 (+8,829) | +| 2025-07-18 | 70,407 (+4,025) | 102,587 (+2,539) | 172,994 (+6,564) | +| 2025-07-19 | 73,500 (+3,093) | 105,904 (+3,317) | 179,404 (+6,410) | +| 2025-07-20 | 76,466 (+2,966) | 109,044 (+3,140) | 185,510 (+6,106) | +| 2025-07-21 | 80,024 (+3,558) | 113,537 (+4,493) | 193,561 (+8,051) | +| 2025-07-22 | 84,278 (+4,254) | 118,073 (+4,536) | 202,351 (+8,790) | +| 2025-07-23 | 88,614 (+4,336) | 121,436 (+3,363) | 210,050 (+7,699) | +| 2025-07-24 | 92,491 (+3,877) | 124,091 (+2,655) | 216,582 (+6,532) | +| 2025-07-25 | 96,440 (+3,949) | 126,985 (+2,894) | 223,425 (+6,843) | +| 2025-07-26 | 100,661 (+4,221) | 131,411 (+4,426) | 232,072 (+8,647) | +| 2025-07-27 | 102,645 (+1,984) | 134,736 (+3,325) | 237,381 (+5,309) | +| 2025-07-28 | 105,468 (+2,823) | 136,016 (+1,280) | 241,484 (+4,103) | +| 2025-07-29 | 109,022 (+3,554) | 137,542 (+1,526) | 246,564 (+5,080) | +| 2025-07-30 | 113,571 (+4,549) | 140,317 (+2,775) | 253,888 (+7,324) | +| 2025-07-31 | 118,359 (+4,788) | 143,344 (+3,027) | 261,703 (+7,815) | +| 2025-08-01 | 123,573 (+5,214) | 146,680 (+3,336) | 270,253 (+8,550) | +| 2025-08-02 | 127,890 (+4,317) | 149,236 (+2,556) | 277,126 (+6,873) | +| 2025-08-03 | 131,423 (+3,533) | 150,451 (+1,215) | 281,874 (+4,748) | +| 2025-08-04 | 136,294 (+4,871) | 153,260 (+2,809) | 289,554 (+7,680) | +| 2025-08-05 | 141,624 (+5,330) | 155,752 (+2,492) | 297,376 (+7,822) | +| 2025-08-06 | 147,103 (+5,479) | 158,309 (+2,557) | 305,412 (+8,036) | +| 2025-08-07 | 152,622 (+5,519) | 160,889 (+2,580) | 313,511 (+8,099) | +| 2025-08-08 | 158,230 (+5,608) | 163,448 (+2,559) | 321,678 (+8,167) | +| 2025-08-09 | 162,786 (+4,556) | 165,721 (+2,273) | 328,507 (+6,829) | +| 2025-08-10 | 165,710 (+2,924) | 167,109 (+1,388) | 332,819 (+4,312) | +| 2025-08-11 | 169,327 (+3,617) | 167,953 (+844) | 337,280 (+4,461) | +| 2025-08-12 | 176,348 (+7,021) | 171,876 (+3,923) | 348,224 (+10,944) | +| 2025-08-13 | 183,027 (+6,679) | 177,182 (+5,306) | 360,209 (+11,985) | +| 2025-08-14 | 189,095 (+6,068) | 179,741 (+2,559) | 368,836 (+8,627) | +| 2025-08-15 | 193,633 (+4,538) | 181,792 (+2,051) | 375,425 (+6,589) | +| 2025-08-16 | 198,134 (+4,501) | 184,558 (+2,766) | 382,692 (+7,267) | +| 2025-08-17 | 201,315 (+3,181) | 186,269 (+1,711) | 387,584 (+4,892) | +| 2025-08-18 | 204,587 (+3,272) | 187,399 (+1,130) | 391,986 (+4,402) | +| 2025-08-19 | 209,846 (+5,259) | 189,668 (+2,269) | 399,514 (+7,528) | +| 2025-08-20 | 214,552 (+4,706) | 191,481 (+1,813) | 406,033 (+6,519) | +| 2025-08-21 | 220,499 (+5,947) | 194,784 (+3,303) | 415,283 (+9,250) | +| 2025-08-22 | 225,917 (+5,418) | 197,204 (+2,420) | 423,121 (+7,838) | +| 2025-08-23 | 229,007 (+3,090) | 199,238 (+2,034) | 428,245 (+5,124) | +| 2025-08-24 | 232,108 (+3,101) | 201,157 (+1,919) | 433,265 (+5,020) | +| 2025-08-25 | 236,650 (+4,542) | 202,650 (+1,493) | 439,300 (+6,035) | +| 2025-08-26 | 242,828 (+6,178) | 205,242 (+2,592) | 448,070 (+8,770) | +| 2025-08-27 | 248,448 (+5,620) | 205,242 (+0) | 453,690 (+5,620) | +| 2025-08-28 | 252,815 (+4,367) | 205,242 (+0) | 458,057 (+4,367) | +| 2025-08-29 | 256,063 (+3,248) | 211,075 (+5,833) | 467,138 (+9,081) | +| 2025-08-30 | 258,889 (+2,826) | 212,397 (+1,322) | 471,286 (+4,148) | +| 2025-08-31 | 262,016 (+3,127) | 213,944 (+1,547) | 475,960 (+4,674) | +| 2025-09-01 | 265,389 (+3,373) | 215,115 (+1,171) | 480,504 (+4,544) | +| 2025-09-02 | 270,519 (+5,130) | 217,075 (+1,960) | 487,594 (+7,090) | +| 2025-09-03 | 274,814 (+4,295) | 219,755 (+2,680) | 494,569 (+6,975) | +| 2025-09-04 | 280,463 (+5,649) | 222,103 (+2,348) | 502,566 (+7,997) | +| 2025-09-05 | 283,785 (+3,322) | 223,793 (+1,690) | 507,578 (+5,012) | +| 2025-09-06 | 286,248 (+2,463) | 225,036 (+1,243) | 511,284 (+3,706) | +| 2025-09-07 | 288,623 (+2,375) | 225,866 (+830) | 514,489 (+3,205) | +| 2025-09-08 | 293,380 (+4,757) | 227,073 (+1,207) | 520,453 (+5,964) | +| 2025-09-09 | 300,092 (+6,712) | 229,788 (+2,715) | 529,880 (+9,427) | +| 2025-09-10 | 307,341 (+7,249) | 233,435 (+3,647) | 540,776 (+10,896) | +| 2025-09-11 | 314,122 (+6,781) | 237,356 (+3,921) | 551,478 (+10,702) | +| 2025-09-12 | 321,124 (+7,002) | 240,728 (+3,372) | 561,852 (+10,374) | +| 2025-09-13 | 324,928 (+3,804) | 245,539 (+4,811) | 570,467 (+8,615) | +| 2025-09-14 | 328,898 (+3,970) | 248,245 (+2,706) | 577,143 (+6,676) | +| 2025-09-15 | 334,250 (+5,352) | 250,983 (+2,738) | 585,233 (+8,090) | +| 2025-09-16 | 342,674 (+8,424) | 255,264 (+4,281) | 597,938 (+12,705) | +| 2025-09-17 | 351,167 (+8,493) | 260,970 (+5,706) | 612,137 (+14,199) | +| 2025-09-18 | 358,759 (+7,592) | 266,922 (+5,952) | 625,681 (+13,544) | +| 2025-09-19 | 365,450 (+6,691) | 271,859 (+4,937) | 637,309 (+11,628) | +| 2025-09-20 | 372,109 (+6,659) | 276,917 (+5,058) | 649,026 (+11,717) | +| 2025-09-21 | 377,116 (+5,007) | 280,261 (+3,344) | 657,377 (+8,351) | +| 2025-09-22 | 382,540 (+5,424) | 284,009 (+3,748) | 666,549 (+9,172) | +| 2025-09-23 | 387,022 (+4,482) | 289,129 (+5,120) | 676,151 (+9,602) | +| 2025-09-24 | 393,369 (+6,347) | 294,927 (+5,798) | 688,296 (+12,145) | +| 2025-09-25 | 398,914 (+5,545) | 301,663 (+6,736) | 700,577 (+12,281) | +| 2025-09-26 | 404,469 (+5,555) | 306,713 (+5,050) | 711,182 (+10,605) | +| 2025-09-27 | 411,649 (+7,180) | 317,763 (+11,050) | 729,412 (+18,230) | +| 2025-09-28 | 414,928 (+3,279) | 322,522 (+4,759) | 737,450 (+8,038) | +| 2025-09-29 | 419,991 (+5,063) | 328,033 (+5,511) | 748,024 (+10,574) | +| 2025-09-30 | 428,029 (+8,038) | 336,472 (+8,439) | 764,501 (+16,477) | +| 2025-10-01 | 433,670 (+5,641) | 341,742 (+5,270) | 775,412 (+10,911) | +| 2025-10-02 | 440,912 (+7,242) | 348,099 (+6,357) | 789,011 (+13,599) | +| 2025-10-03 | 446,907 (+5,995) | 359,937 (+11,838) | 806,844 (+17,833) | +| 2025-10-04 | 452,569 (+5,662) | 370,386 (+10,449) | 822,955 (+16,111) | +| 2025-10-05 | 455,603 (+3,034) | 374,745 (+4,359) | 830,348 (+7,393) | +| 2025-10-06 | 460,975 (+5,372) | 379,489 (+4,744) | 840,464 (+10,116) | +| 2025-10-07 | 467,386 (+6,411) | 385,438 (+5,949) | 852,824 (+12,360) | +| 2025-10-08 | 474,680 (+7,294) | 394,139 (+8,701) | 868,819 (+15,995) | +| 2025-10-09 | 479,230 (+4,550) | 400,526 (+6,387) | 879,756 (+10,937) | +| 2025-10-10 | 484,419 (+5,189) | 406,015 (+5,489) | 890,434 (+10,678) | +| 2025-10-11 | 488,439 (+4,020) | 414,699 (+8,684) | 903,138 (+12,704) | +| 2025-10-12 | 492,161 (+3,722) | 418,745 (+4,046) | 910,906 (+7,768) | +| 2025-10-13 | 497,581 (+5,420) | 423,531 (+4,786) | 921,112 (+10,206) | +| 2025-10-14 | 505,192 (+7,611) | 429,286 (+5,755) | 934,478 (+13,366) | +| 2025-10-15 | 512,770 (+7,578) | 439,290 (+10,004) | 952,060 (+17,582) | +| 2025-10-16 | 517,739 (+4,969) | 447,137 (+7,847) | 964,876 (+12,816) | +| 2025-10-17 | 526,295 (+8,556) | 457,467 (+10,330) | 983,762 (+18,886) | +| 2025-10-18 | 531,582 (+5,287) | 465,272 (+7,805) | 996,854 (+13,092) | +| 2025-10-19 | 536,237 (+4,655) | 469,078 (+3,806) | 1,005,315 (+8,461) | +| 2025-10-20 | 541,314 (+5,077) | 472,952 (+3,874) | 1,014,266 (+8,951) | +| 2025-10-21 | 548,786 (+7,472) | 479,703 (+6,751) | 1,028,489 (+14,223) | +| 2025-10-22 | 557,999 (+9,213) | 491,395 (+11,692) | 1,049,394 (+20,905) | +| 2025-10-23 | 564,777 (+6,778) | 498,736 (+7,341) | 1,063,513 (+14,119) | +| 2025-10-24 | 572,761 (+7,984) | 506,905 (+8,169) | 1,079,666 (+16,153) | +| 2025-10-25 | 578,962 (+6,201) | 516,129 (+9,224) | 1,095,091 (+15,425) | +| 2025-10-26 | 584,462 (+5,500) | 521,179 (+5,050) | 1,105,641 (+10,550) | +| 2025-10-27 | 590,045 (+5,583) | 526,001 (+4,822) | 1,116,046 (+10,405) | +| 2025-10-28 | 595,860 (+5,815) | 532,438 (+6,437) | 1,128,298 (+12,252) | +| 2025-10-29 | 606,316 (+10,456) | 542,064 (+9,626) | 1,148,380 (+20,082) | +| 2025-10-30 | 617,917 (+11,601) | 555,026 (+12,962) | 1,172,943 (+24,563) | +| 2025-10-31 | 626,688 (+8,771) | 564,579 (+9,553) | 1,191,267 (+18,324) | +| 2025-11-01 | 636,152 (+9,464) | 581,806 (+17,227) | 1,217,958 (+26,691) | +| 2025-11-02 | 644,118 (+7,966) | 590,004 (+8,198) | 1,234,122 (+16,164) | +| 2025-11-03 | 653,190 (+9,072) | 597,139 (+7,135) | 1,250,329 (+16,207) | +| 2025-11-05 | 675,140 (+21,950) | 619,690 (+22,551) | 1,294,830 (+44,501) | +| 2025-11-06 | 686,320 (+11,180) | 630,885 (+11,195) | 1,317,205 (+22,375) | +| 2025-11-07 | 696,702 (+10,382) | 642,146 (+11,261) | 1,338,848 (+21,643) | +| 2025-11-08 | 706,079 (+9,377) | 653,489 (+11,343) | 1,359,568 (+20,720) | +| 2025-11-09 | 713,521 (+7,442) | 660,459 (+6,970) | 1,373,980 (+14,412) | +| 2025-11-10 | 722,366 (+8,845) | 668,225 (+7,766) | 1,390,591 (+16,611) | +| 2025-11-11 | 729,814 (+7,448) | 677,501 (+9,276) | 1,407,315 (+16,724) | +| 2025-11-12 | 740,245 (+10,431) | 686,454 (+8,953) | 1,426,699 (+19,384) | +| 2025-11-13 | 749,951 (+9,706) | 696,157 (+9,703) | 1,446,108 (+19,409) | +| 2025-11-14 | 759,985 (+10,034) | 705,237 (+9,080) | 1,465,222 (+19,114) | +| 2025-11-15 | 765,982 (+5,997) | 712,870 (+7,633) | 1,478,852 (+13,630) | +| 2025-11-16 | 771,118 (+5,136) | 716,596 (+3,726) | 1,487,714 (+8,862) | +| 2025-11-17 | 780,240 (+9,122) | 723,339 (+6,743) | 1,503,579 (+15,865) | +| 2025-11-18 | 791,652 (+11,412) | 732,544 (+9,205) | 1,524,196 (+20,617) | +| 2025-11-19 | 804,486 (+12,834) | 747,624 (+15,080) | 1,552,110 (+27,914) | +| 2025-11-20 | 814,678 (+10,192) | 757,907 (+10,283) | 1,572,585 (+20,475) | +| 2025-11-21 | 826,364 (+11,686) | 769,307 (+11,400) | 1,595,671 (+23,086) | +| 2025-11-22 | 837,318 (+10,954) | 780,996 (+11,689) | 1,618,314 (+22,643) | +| 2025-11-23 | 846,655 (+9,337) | 795,069 (+14,073) | 1,641,724 (+23,410) | +| 2025-11-24 | 856,802 (+10,147) | 804,033 (+8,964) | 1,660,835 (+19,111) | +| 2025-11-25 | 869,503 (+12,701) | 817,339 (+13,306) | 1,686,842 (+26,007) | +| 2025-11-26 | 881,477 (+11,974) | 832,518 (+15,179) | 1,713,995 (+27,153) | +| 2025-11-27 | 894,031 (+12,554) | 846,180 (+13,662) | 1,740,211 (+26,216) | +| 2025-11-28 | 901,771 (+7,740) | 856,482 (+10,302) | 1,758,253 (+18,042) | +| 2025-11-29 | 908,738 (+6,967) | 863,361 (+6,879) | 1,772,099 (+13,846) | +| 2025-11-30 | 916,456 (+7,718) | 870,194 (+6,833) | 1,786,650 (+14,551) | +| 2025-12-01 | 925,975 (+9,519) | 876,500 (+6,306) | 1,802,475 (+15,825) | +| 2025-12-02 | 939,313 (+13,338) | 890,919 (+14,419) | 1,830,232 (+27,757) | +| 2025-12-03 | 952,384 (+13,071) | 903,713 (+12,794) | 1,856,097 (+25,865) | +| 2025-12-04 | 965,693 (+13,309) | 916,471 (+12,758) | 1,882,164 (+26,067) | +| 2025-12-05 | 978,068 (+12,375) | 930,616 (+14,145) | 1,908,684 (+26,520) | +| 2025-12-06 | 987,936 (+9,868) | 943,773 (+13,157) | 1,931,709 (+23,025) | +| 2025-12-07 | 994,080 (+6,144) | 951,425 (+7,652) | 1,945,505 (+13,796) | +| 2025-12-08 | 1,000,954 (+6,874) | 957,149 (+5,724) | 1,958,103 (+12,598) | +| 2025-12-09 | 1,011,588 (+10,634) | 973,922 (+16,773) | 1,985,510 (+27,407) | +| 2025-12-10 | 1,025,967 (+14,379) | 991,708 (+17,786) | 2,017,675 (+32,165) | +| 2025-12-11 | 1,045,227 (+19,260) | 1,010,559 (+18,851) | 2,055,786 (+38,111) | +| 2025-12-12 | 1,061,451 (+16,224) | 1,030,838 (+20,279) | 2,092,289 (+36,503) | +| 2025-12-13 | 1,073,646 (+12,195) | 1,044,608 (+13,770) | 2,118,254 (+25,965) | +| 2025-12-14 | 1,082,084 (+8,438) | 1,052,425 (+7,817) | 2,134,509 (+16,255) | +| 2025-12-15 | 1,093,741 (+11,657) | 1,059,078 (+6,653) | 2,152,819 (+18,310) | +| 2025-12-16 | 1,120,661 (+26,920) | 1,078,022 (+18,944) | 2,198,683 (+45,864) | +| 2025-12-17 | 1,151,268 (+30,607) | 1,097,661 (+19,639) | 2,248,929 (+50,246) | diff --git a/bun.lock b/bun.lock index 5457a140872..9dedd9920cc 100644 --- a/bun.lock +++ b/bun.lock @@ -30,6 +30,7 @@ "@hono/zod-validator": "0.5.0", "@openauthjs/openauth": "0.4.3", "@standard-schema/spec": "1.0.0", + "@xhayper/discord-rpc": "1.2.2", "ai": "catalog:", "decimal.js": "10.5.0", "diff": "8.0.2", @@ -214,6 +215,12 @@ "@ctrl/tinycolor": ["@ctrl/tinycolor@4.1.0", "", {}, "sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ=="], + "@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="], + + "@discordjs/rest": ["@discordjs/rest@2.5.1", "", { "dependencies": { "@discordjs/collection": "^2.1.1", "@discordjs/util": "^1.1.1", "@sapphire/async-queue": "^1.5.3", "@sapphire/snowflake": "^3.5.3", "@vladfrangu/async_event_emitter": "^2.4.6", "discord-api-types": "^0.38.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-Tg9840IneBcbrAjcGaQzHUJWFNq1MMWZjTdjJ0WS/89IffaNKc++iOvffucPxQTF/gviO9+9r8kEPea1X5J2Dw=="], + + "@discordjs/util": ["@discordjs/util@1.1.1", "", {}, "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g=="], + "@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="], "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA=="], @@ -416,6 +423,10 @@ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.41.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw=="], + "@sapphire/async-queue": ["@sapphire/async-queue@1.5.5", "", {}, "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg=="], + + "@sapphire/snowflake": ["@sapphire/snowflake@3.5.5", "", {}, "sha512-xzvBr1Q1c4lCe7i6sRnrofxeO1QTP/LKQ6A6qy0iB4x5yfiSfARMEQEghojzTNALDTcv8En04qYNIco9/K9eZQ=="], + "@shikijs/core": ["@shikijs/core@3.4.2", "", { "dependencies": { "@shikijs/types": "3.4.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-AG8vnSi1W2pbgR2B911EfGqtLE9c4hQBYkv/x7Z+Kt0VxhgQKcW7UNDVYsu9YxwV6u+OJrvdJrMq6DNWoBjihQ=="], "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.4.2", "", { "dependencies": { "@shikijs/types": "3.4.2", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-1/adJbSMBOkpScCE/SB6XkjJU17ANln3Wky7lOmrnpl+zBdQ1qXUJg2GXTYVHRq+2j3hd1DesmElTXYDgtfSOQ=="], @@ -504,6 +515,10 @@ "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + "@vladfrangu/async_event_emitter": ["@vladfrangu/async_event_emitter@2.4.6", "", {}, "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA=="], + + "@xhayper/discord-rpc": ["@xhayper/discord-rpc@1.2.2", "", { "dependencies": { "@discordjs/rest": "^2.5.0", "@vladfrangu/async_event_emitter": "^2.4.6", "discord-api-types": "^0.37.120", "ws": "^8.18.2" } }, "sha512-P3+uF2Hb7zVNEtk2ZleV7FF2cj1lCZ0Caa82RECwa5oaPNCF12CDhsx8GdBqFaD0zRRVrkheP3LJn0dmbd0KoA=="], + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], "acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="], @@ -736,6 +751,8 @@ "direction": ["direction@2.0.1", "", { "bin": { "direction": "cli.js" } }, "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA=="], + "discord-api-types": ["discord-api-types@0.37.120", "", {}, "sha512-7xpNK0EiWjjDFp2nAhHXezE4OUWm7s1zhc/UXXN6hnFFU8dfoPHgV0Hx0RPiCa3ILRpdeh152icc68DGCyXYIw=="], + "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="], "dset": ["dset@3.1.4", "", {}, "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA=="], @@ -1040,6 +1057,8 @@ "luxon": ["luxon@3.6.1", "", {}, "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ=="], + "magic-bytes.js": ["magic-bytes.js@1.12.1", "", {}, "sha512-ThQLOhN86ZkJ7qemtVRGYM+gRgR8GEXNli9H/PMvpnZsE44Xfh3wx9kGJaldg314v85m+bFW6WBMaVHJc/c3zA=="], + "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], "magicast": ["magicast@0.3.5", "", { "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ=="], @@ -1578,7 +1597,7 @@ "uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="], - "undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], + "undici": ["undici@6.21.3", "", {}, "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw=="], "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], @@ -1672,7 +1691,7 @@ "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - "ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], "xdg-basedir": ["xdg-basedir@5.1.0", "", {}, "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ=="], @@ -1732,6 +1751,10 @@ "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "@discordjs/rest/discord-api-types": ["discord-api-types@0.38.14", "", {}, "sha512-5qknrPxvIzAiX2tDb7dB55A4ydb/nCKxhlO48RrMNeyEGsBgk/88qIAsUm7K7nDnNkkOgJYt/wufybEPYWRMJQ=="], + + "@discordjs/rest/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "@jridgewell/gen-mapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], "@openauthjs/openauth/@standard-schema/spec": ["@standard-schema/spec@1.0.0-beta.3", "", {}, "sha512-0ifF3BjA1E8SY9C+nUew8RefNOIq0cDlYALPty4rhUm8Rrl6tCM8hBT4bhGhx7I7iXD0uAgt50lgo8dD73ACMw=="], @@ -1786,6 +1809,10 @@ "miniflare/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], + "miniflare/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], + + "miniflare/ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], + "opencontrol/hono": ["hono@4.7.4", "", {}, "sha512-Pst8FuGqz3L7tFF+u9Pu70eI0xa5S3LPUmrNd5Jm8nTHze9FxLTK9Kaj5g/k4UcwuJSXTP65SyHOPLrffpcAJg=="], "opencontrol/zod-to-json-schema": ["zod-to-json-schema@3.24.3", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A=="], diff --git a/packages/opencode/config.schema.json b/packages/opencode/config.schema.json index 6ee406c01bb..07fd738d5e1 100644 --- a/packages/opencode/config.schema.json +++ b/packages/opencode/config.schema.json @@ -355,6 +355,46 @@ } }, "additionalProperties": false + }, + "discord": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "description": "Enable Discord Rich Presence" + }, + "applicationId": { + "type": "string", + "description": "Discord application ID for Rich Presence" + }, + "showModel": { + "type": "boolean", + "default": true, + "description": "Show current AI model in presence" + }, + "showProject": { + "type": "boolean", + "default": true, + "description": "Show current project name in presence" + }, + "showSession": { + "type": "boolean", + "default": true, + "description": "Show session information in presence" + }, + "showShareUrl": { + "type": "boolean", + "default": true, + "description": "Show share URL button when session is shared" + }, + "customStatus": { + "type": "string", + "description": "Custom status text to display" + } + }, + "additionalProperties": false, + "description": "Discord Rich Presence configuration" } }, "additionalProperties": false, diff --git a/packages/opencode/package.json b/packages/opencode/package.json index fae2e3425b1..08332761199 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -31,6 +31,7 @@ "@hono/zod-validator": "0.5.0", "@openauthjs/openauth": "0.4.3", "@standard-schema/spec": "1.0.0", + "@xhayper/discord-rpc": "1.2.2", "ai": "catalog:", "decimal.js": "10.5.0", "diff": "8.0.2", diff --git a/packages/opencode/src/cli/bootstrap.ts b/packages/opencode/src/cli/bootstrap.ts index 9ae274edb07..ea1e1bbbcad 100644 --- a/packages/opencode/src/cli/bootstrap.ts +++ b/packages/opencode/src/cli/bootstrap.ts @@ -1,5 +1,6 @@ import { App } from "../app/app" import { ConfigHooks } from "../config/hooks" +import { Discord } from "../discord/discord" import { FileWatcher } from "../file/watch" import { Format } from "../format" import { LSP } from "../lsp" @@ -14,6 +15,7 @@ export async function bootstrap( Format.init() ConfigHooks.init() LSP.init() + Discord.init() FileWatcher.init() return cb(app) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index cf2f3479038..a5a0797eeae 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -10,6 +10,7 @@ import fs from "fs/promises" import { lazy } from "../util/lazy" import { NamedError } from "../util/error" + export namespace Config { const log = Log.create({ service: "config" }) @@ -202,6 +203,33 @@ export namespace Config { .optional(), }) .optional(), + discord: z + .object({ + enabled: z.boolean().default(false).describe("Enable Discord Rich Presence"), + applicationId: z + .string() + .optional() + .describe("Discord application ID for Rich Presence"), + showModel: z + .boolean() + .default(true) + .describe("Show current AI model in presence"), + showProject: z + .boolean() + .default(true) + .describe("Show current project name in presence"), + showSession: z + .boolean() + .default(true) + .describe("Show session information in presence"), + customStatus: z + .string() + .optional() + .describe("Custom status text to display"), + }) + .strict() + .optional() + .describe("Discord Rich Presence configuration"), }) .strict() .openapi({ diff --git a/packages/opencode/src/discord/discord.ts b/packages/opencode/src/discord/discord.ts new file mode 100644 index 00000000000..8f8a35897bb --- /dev/null +++ b/packages/opencode/src/discord/discord.ts @@ -0,0 +1,320 @@ +import { z } from "zod" +import { App } from "../app/app" +import { Bus } from "../bus" +import { Session } from "../session" +import { Message } from "../session/message" +import { Log } from "../util/log" +import { NamedError } from "../util/error" +import { Config } from "../config/config" +import { ActivitySupportedPlatform, Client } from "@xhayper/discord-rpc" +import path from "path" + +export namespace Discord { + const log = Log.create({ service: "discord" }) + const DEFAULT_APPLICATION_ID = "1388540337164910712" + + export type Config = NonNullable + + interface PresenceData { + details?: string + state?: string + startTimestamp?: number + largeImageKey?: string + largeImageText?: string + smallImageKey?: string + smallImageText?: string + supportedPlatforms?: ( + | ActivitySupportedPlatform + | `${ActivitySupportedPlatform}` + )[] + instance?: boolean + } + + const state = App.state( + "discord", + async () => { + const configData = await Config.get() + const config = configData.discord + + if (!config?.enabled) { + return { + client: null as Client | null, + isConnected: false, + currentSession: null as Session.Info | null, + currentModel: null as string | null, + sessionStartTime: null as number | null, + retryTimeout: null as NodeJS.Timeout | null, + retryCount: 0, + config: null as Config | null, + activityInterval: null as NodeJS.Timeout | null, + } + } + + log.info("Initializing Discord Rich Presence", { + enabled: config.enabled, + applicationId: config.applicationId || DEFAULT_APPLICATION_ID, + showModel: config.showModel, + showProject: config.showProject, + }) + + const appState = { + client: null as Client | null, + isConnected: false, + currentSession: null as Session.Info | null, + currentModel: null as string | null, + sessionStartTime: null as number | null, + retryTimeout: null as NodeJS.Timeout | null, + retryCount: 0, + config, + activityInterval: null as NodeJS.Timeout | null, + } + + attemptConnection(appState, config) + return appState + }, + async (state) => { + if (state.retryTimeout) { + clearTimeout(state.retryTimeout) + state.retryTimeout = null + } + + if (state.activityInterval) { + clearInterval(state.activityInterval) + state.activityInterval = null + } + + if (state.client) { + try { + await state.client.destroy() + log.info("Discord Rich Presence disconnected") + } catch (error) { + log.error("Error disconnecting Discord client", { + error: error instanceof Error ? error.message : String(error), + }) + } + state.client = null + state.isConnected = false + } + }, + ) + + export const ConnectionError = NamedError.create( + "DiscordConnectionError", + z.object({ + message: z.string(), + }), + ) + + export async function init() { + return state() + } + + async function attemptConnection( + appState: Awaited>, + config: Config, + ) { + const applicationId = config.applicationId || DEFAULT_APPLICATION_ID + + log.info("Attempting Discord Rich Presence connection", { + applicationId, + attempt: appState.retryCount + 1, + transport: "ipc", + }) + + try { + if (appState.client) { + try { + await appState.client.destroy() + } catch (e) { + // Ignore cleanup errors + } + appState.client = null + } + + appState.client = new Client({ + clientId: applicationId, + transport: { type: "ipc" }, + }) + + appState.client.on("ready", () => { + log.info("Discord Rich Presence connected successfully", { + user: appState.client?.user?.username || "unknown", + }) + appState.isConnected = true + appState.retryCount = 0 + + if (!appState.sessionStartTime) { + appState.sessionStartTime = Date.now() + } + + startActivityUpdates(appState, config) + setupEventListeners(appState, config) + }) + + appState.client.on("join", (secret: string) => { + log.info("Discord join request received", { secret }) + if (secret.startsWith("http")) { + import("open") + .then((open) => open.default(secret)) + .catch(() => { + log.warn("Failed to open share URL", { url: secret }) + }) + } + }) + appState.client.on("disconnected", () => { + log.info("Discord Rich Presence disconnected") + appState.isConnected = false + scheduleRetry(appState, config) + }) + + appState.client.on("error", (error: Error) => { + log.error("Discord RPC client error", { + error: error.message, + code: (error as any).code, + stack: error.stack, + }) + appState.isConnected = false + scheduleRetry(appState, config) + }) + + const loginPromise = appState.client.login() + const timeoutPromise = new Promise((_, reject) => { + setTimeout( + () => reject(new Error("Login timeout after 10 seconds")), + 10000, + ) + }) + + await Promise.race([loginPromise, timeoutPromise]) + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error) + const errorCode = (error as any)?.code + + log.warn("Discord Rich Presence connection failed", { + error: errorMessage, + code: errorCode, + attempt: appState.retryCount + 1, + applicationId, + }) + + if (appState.client) { + try { + await appState.client.destroy() + } catch (e) { + } + appState.client = null + } + + scheduleRetry(appState, config) + } + } + + function startActivityUpdates( + appState: Awaited>, + config: Config, + ) { + if (appState.activityInterval) { + clearInterval(appState.activityInterval) + } + + setActivity(appState, config) + + appState.activityInterval = setInterval(() => { + setActivity(appState, config) + }, 15000) + } + + async function setActivity( + appState: Awaited>, + config: Config, + ) { + if (!appState.client || !appState.isConnected) return + + const presence = buildPresence(appState, config) + await appState.client?.user?.setActivity(presence) + } + function setupEventListeners( + appState: Awaited>, + _config: Config, + ) { + if (appState.retryCount > 0) return + + Bus.subscribe(Session.Event.Updated, (event) => { + appState.currentSession = event.properties.info + if (!appState.sessionStartTime) { + appState.sessionStartTime = Date.now() + } + setActivity(appState, _config) + }) + + Bus.subscribe(Message.Event.Updated, (event) => { + const message = event.properties.info + if (message.role === "assistant" && message.metadata?.assistant) { + const newModel = `${message.metadata.assistant.providerID}/${message.metadata.assistant.modelID}` + if (newModel !== appState.currentModel) { + appState.currentModel = newModel + } + } + }) + } + + function scheduleRetry( + appState: Awaited>, + config: Config, + ) { + if (appState.retryCount >= 3) { + log.info( + "Discord Rich Presence: Maximum retry attempts reached, giving up", + ) + return + } + + appState.retryCount++ + const delay = Math.min(5000 * appState.retryCount, 30000) + + log.info( + `Discord Rich Presence: Retrying in ${delay / 1000}s (attempt ${appState.retryCount + 1}/4)`, + ) + + appState.retryTimeout = setTimeout(() => { + attemptConnection(appState, config) + }, delay) + } + + function buildPresence( + appState: Awaited>, + config: Config, + ): PresenceData { + const app = App.info() + + const presence: PresenceData = { + startTimestamp: appState.sessionStartTime || Date.now(), + largeImageKey: "opencode", + largeImageText: "opencode AI Assistant", + instance: false, + supportedPlatforms: ["web", "ios", "android"], + } + + if (config.customStatus) { + presence.details = config.customStatus + } else if (config.showProject) { + const projectName = path.basename(app.path.cwd) + presence.details = `Working on: ${projectName}` + } else { + presence.details = "Using opencode" + } + + if (config.showModel && appState.currentModel) { + const [, model] = appState.currentModel.split("/") + presence.state = `Using ${model || appState.currentModel}` + } else { + presence.state = "Using opencode.ai" + } + + presence.smallImageKey = "terminal" + presence.smallImageText = "Coding" + + return presence + } +}