mirror of
https://github.com/warengroup/eximiabots-radiox.git
synced 2025-07-01 14:23:41 +00:00
Compare commits
1717 Commits
Author | SHA1 | Date | |
---|---|---|---|
cd5f4cf917 | |||
6abbc394b1 | |||
094576876a | |||
a645031008 | |||
a123c1983b | |||
1f2bf40cd3 | |||
4c1ca5f193 | |||
36084a0489 | |||
e394ecc1e2 | |||
997cf63abb | |||
f4dc6fff40 | |||
e81a1d404f | |||
6ce5acb14e | |||
34ffe44b23 | |||
ff8213196e | |||
4aa3914709 | |||
c68f52824c | |||
db21441549 | |||
28b57d577a | |||
729971802b | |||
29d8ad463c | |||
7b5949b68a | |||
b42b19df1a | |||
2034fd7140 | |||
5828c1b970 | |||
848747f697 | |||
6d2663b6c3 | |||
c925e30cd3 | |||
3409053260 | |||
d5e95086ce | |||
f2e2c0b1a3 | |||
4aaf1ca86e | |||
4d64129561 | |||
ae3f1484bf | |||
785e22ebdc | |||
96043476b6 | |||
6a2e784860 | |||
d3f0cf6dc0 | |||
2725ccb875 | |||
d70179aa81 | |||
cd5d94edc5 | |||
83b8a0c2fa | |||
cd17c62d6b | |||
4f10f75049 | |||
ffa54699bf | |||
52aa960c23 | |||
36a7a87913 | |||
702b4bd00f | |||
ad3d1d03e5 | |||
2b2ce3bf14 | |||
855191725d | |||
6b76914735 | |||
02f165b98a | |||
41e297400d | |||
c8cc3d7ba0 | |||
8fb6aa6d52 | |||
b3859f79fd | |||
7214a10ab8 | |||
9446407751 | |||
71a65284e4 | |||
32567881a4 | |||
88ebab22ba | |||
57b5e001f1 | |||
f0c1add3c7 | |||
69d39e1142 | |||
dd39e91dce | |||
f5a617dd6c | |||
3c37a60a8b | |||
4410ba358b | |||
94e2bee6e1 | |||
44839b853d | |||
6d74f82acb | |||
7cd00ed645 | |||
224a1f849d | |||
c9229f3cda | |||
ff034264d7 | |||
64f53c38d7 | |||
6de5e69dc0 | |||
b5caeb7cd3 | |||
2579549f0b | |||
408c5aaab8 | |||
3f1d576c44 | |||
b29624cba3 | |||
938f8ab69b | |||
9e165713ce | |||
d260c6ea4f | |||
cbae9ae38f | |||
24a6e80f26 | |||
16ef8e70e6 | |||
25ac99379b | |||
26a7b2cf25 | |||
dad16da504 | |||
890a25ac80 | |||
c502101319 | |||
ce80bc7508 | |||
40cd3a9ec5 | |||
227e0bcaaa | |||
95216f5acc | |||
7ba5ee97a8 | |||
c181066c67 | |||
78d6b92dbd | |||
3686cd1b0e | |||
bc238d919d | |||
97881f7331 | |||
6c1f0944db | |||
3cec154343 | |||
87cf4b62c8 | |||
348ac90cba | |||
2d17c33d21 | |||
7acabe411b | |||
e911da8732 | |||
aa5f19328a | |||
5be3b29300 | |||
c584e3632e | |||
0e62861e33 | |||
2d0b326721 | |||
fb36a8f890 | |||
3fc7337d0f | |||
9303c4fcc9 | |||
56f0ab5a40 | |||
4d18468e96 | |||
e87d952b96 | |||
b5eb895611 | |||
8c95114ba6 | |||
d6840f307d | |||
9cd35c7c99 | |||
1a3ca3fbc0 | |||
d18bd35b87 | |||
c590574a50 | |||
089c68566f | |||
c11555acdd | |||
26f3aced5c | |||
cede1e632a | |||
c05311ff4d | |||
a8fb12483e | |||
be3f8f992d | |||
bd9fd46b81 | |||
855edcbfdc | |||
10035e6bd2 | |||
f277bec06c | |||
e03a18fae4 | |||
d3be9a3f31 | |||
d03836a064 | |||
f60cd7218a | |||
8b4e4f3a4b | |||
29859e5beb | |||
ecd158f926 | |||
02e0593ff2 | |||
71625360e1 | |||
df601718ab | |||
503f4b7cca | |||
92af0f71b2 | |||
2a251c2208 | |||
f33efc51f0 | |||
16909d19ed | |||
0ea9068c9c | |||
a9060a7783 | |||
f395c08363 | |||
9ef45c70b6 | |||
e4c17778e7 | |||
270a0c128e | |||
d42a713316 | |||
6304865db1 | |||
5802f999cd | |||
30385e79e4 | |||
4e5dc34b62 | |||
ed477c9129 | |||
75554681da | |||
577fd75dcc | |||
4e8a76c2b9 | |||
196781b655 | |||
2bdd0e47c9 | |||
c84d488506 | |||
5a35f69287 | |||
d99dc82950 | |||
731507b65e | |||
1b7851de69 | |||
4ba4dd38d8 | |||
61c8fe1025 | |||
4df5f237b4 | |||
895fa29499 | |||
6aece48b0f | |||
e148a04ff0 | |||
389cd60228 | |||
11b0f8a42d | |||
3d76e02368 | |||
0000c4c98e | |||
aa89bceae8 | |||
f486b45cc7 | |||
6e50960975 | |||
e2c05b8bbf | |||
9c9c9f8192 | |||
48e08f363b | |||
1061a23421 | |||
7a203c79c4 | |||
db2dea57be | |||
8b4e080991 | |||
8617d1e619 | |||
120822db83 | |||
c178ebe2b7 | |||
135e3d369f | |||
dfd5ddc07e | |||
287df152e2 | |||
9762848d2c | |||
70691614a8 | |||
c714737894 | |||
9aaee7b828 | |||
298db1b0d4 | |||
6382ac9e04 | |||
2d8771c2cc | |||
dd39585b15 | |||
958f7a83ec | |||
fbc92c3f53 | |||
650d6cc112 | |||
d2ef59a59d | |||
81a95201ea | |||
d6cf25fdaf | |||
63fb5e7351 | |||
03c09ac310 | |||
b3e4e22df5 | |||
2b72f87425 | |||
47b8cebf1d | |||
e4068a1dec | |||
1ad6aeef7a | |||
a1efe455d5 | |||
58c3cf9d21 | |||
df7f4fba73 | |||
3e1d01c5e9 | |||
4e4a8804ba | |||
1a84242554 | |||
e2b8599c97 | |||
f1830e2eb6 | |||
d848b26dc5 | |||
fa6ad91120 | |||
177cf64f08 | |||
cbd0bb4dfe | |||
1f66400504 | |||
95a0c875c6 | |||
1a4dcd2e03 | |||
2bc2d6a0ce | |||
91ea4d4449 | |||
8b28f598c6 | |||
45286e4926 | |||
7ef27be0bb | |||
7f87b3179a | |||
a7c7acc3b6 | |||
023d83e4c5 | |||
3e2045bdd9 | |||
9bc1331ee9 | |||
61a7f472ee | |||
5bdbffcd47 | |||
c0fb70df7e | |||
78045c7f13 | |||
1b858be8b6 | |||
93103c4ec7 | |||
18bd3ced41 | |||
c3354635fb | |||
0489820f67 | |||
58c552a4c9 | |||
068ccc238d | |||
f215ab6673 | |||
4f42760b2a | |||
ad02cf34dc | |||
bf420462ff | |||
a8477fe3d5 | |||
7206e95c11 | |||
0901bcb750 | |||
727a666a3a | |||
83cca20d22 | |||
8e65a74c21 | |||
a7c3d9d8b5 | |||
35aba42aa2 | |||
aa0af56036 | |||
f80c85edc2 | |||
5d721d132c | |||
fabcb8d9ba | |||
76d4a42c4a | |||
2903237df0 | |||
fa0c354e9a | |||
fa4098c454 | |||
09e03eda28 | |||
c4b7307952 | |||
c57ff6c471 | |||
39abd83e74 | |||
fcf786f6b5 | |||
ad9187d11e | |||
5503362a9d | |||
61f65bbc4a | |||
0c39121b1b | |||
306efca43f | |||
768f33874c | |||
dc82e0cc10 | |||
a7320168f5 | |||
b5c0b4ec05 | |||
d687f2211f | |||
8735861c09 | |||
dfc1d62238 | |||
c7a5664042 | |||
6e090e05e1 | |||
930a7e049d | |||
f19145205b | |||
128c48bc04 | |||
c7f6972bd3 | |||
b493a14f80 | |||
e58067b8c7 | |||
fbd0ccf0fb | |||
97c64c106a | |||
2621f3bd3a | |||
ab6e0bced8 | |||
f904605925 | |||
289a3fa5a4 | |||
aab2b9dc2a | |||
910c50a741 | |||
f8097f303f | |||
8bd9005345 | |||
8a09c36524 | |||
6ee0596326 | |||
ffc7f9168b | |||
50603286c3 | |||
e1f5c3def9 | |||
f4d10c4cfb | |||
f6c0eaa842 | |||
b2cd90444a | |||
e05494b528 | |||
a836672637 | |||
2018d56a20 | |||
0f0feefd6d | |||
2df41d4679 | |||
4bd657738f | |||
9a667d024f | |||
c6e4da1c16 | |||
9545213b3b | |||
27a0fa5872 | |||
8958d19ec8 | |||
c70d6b56bb | |||
5453a165d0 | |||
daabad98f5 | |||
32f7ff9c79 | |||
f8cdf666de | |||
2516e76848 | |||
6edab61800 | |||
d5af4ec2e8 | |||
3ea3941ead | |||
76c22de575 | |||
55483ba4b0 | |||
4ee84865ca | |||
7ea865203c | |||
bffea3ba83 | |||
af019d7210 | |||
5aa3487c63 | |||
d2f4fb6fbb | |||
5c70b78954 | |||
e444bdf21b | |||
0f7cc442be | |||
e65e29b87d | |||
823cfc2f28 | |||
60170fb249 | |||
8c6d068104 | |||
adeccb545c | |||
9c5300f6b3 | |||
b7c8b10d6f | |||
4bfb840712 | |||
f84d2793a8 | |||
f9f034eb07 | |||
3270bf48dc | |||
d25f321737 | |||
8df66ffb2f | |||
bd00992ab8 | |||
f8fc0595e9 | |||
0053b3a2a9 | |||
6ad52a0740 | |||
5e8d8477d0 | |||
aeb4db94a5 | |||
6271637bc6 | |||
a7ce77ee8e | |||
1b4a7c88a8 | |||
5370b18e46 | |||
8e00f1b4a7 | |||
cb75fd8740 | |||
bc5ea51524 | |||
22b71e6f55 | |||
5dfb9a44ee | |||
10ef760a5b | |||
a289425140 | |||
7c2e6861c1 | |||
5990650299 | |||
e55a3b1f0f | |||
f1d1387a1e | |||
a027d2ed4f | |||
af074de5d6 | |||
c7cebb2294 | |||
9d2a7b7367 | |||
50c29a745c | |||
c1b3884af7 | |||
d1d4ae38d8 | |||
c8faae05be | |||
8c936021b2 | |||
165d9c98ae | |||
9bf84a6aff | |||
3054533061 | |||
dd3ad6c037 | |||
8e64ef88e5 | |||
3d9df86d72 | |||
074174af46 | |||
84526e6b09 | |||
33676a76cc | |||
55f220632e | |||
1b57745c2c | |||
151953c53c | |||
cfbb202034 | |||
849ef050a7 | |||
cf747fbde6 | |||
979afa477b | |||
4c146449fa | |||
6ac6d86af8 | |||
61afb5b9a4 | |||
49a0b6a2e8 | |||
9505439940 | |||
74c182eaa1 | |||
b52af5619a | |||
f599bb79a0 | |||
e0be450284 | |||
0501f19e56 | |||
05c403af91 | |||
972b362887 | |||
1f4874ae81 | |||
546bb5f1f6 | |||
04cb95d359 | |||
e0a3743547 | |||
7aba428b8e | |||
9576a22495 | |||
82ac80d543 | |||
83f88cf0fa | |||
0b51f10056 | |||
0089e3b4e0 | |||
d771673bf8 | |||
847abfc605 | |||
7dea200e3a | |||
ebf468d2ef | |||
5ddb8bcaad | |||
cd46eaaa25 | |||
1015b4b69c | |||
986afc7b62 | |||
d528f13164 | |||
ff56827c57 | |||
6b2af89a4d | |||
3e9eaf1d00 | |||
60a822f58f | |||
6018e91fdc | |||
f45fc04778 | |||
561fa0d8dc | |||
daa7bdd3c5 | |||
c5781ee1fc | |||
4e1e7fa083 | |||
686ac91334 | |||
26213d99cf | |||
9a5e358236 | |||
3556ffca39 | |||
3071cf576b | |||
44dd832022 | |||
aa21305f9f | |||
2325763873 | |||
dfdf468cc1 | |||
f7dc43891e | |||
c245008b28 | |||
1d27569a19 | |||
d0d3d83b3a | |||
c02fb1ddc7 | |||
44698cedd6 | |||
0265a4750a | |||
90f42dfb9c | |||
948d9e5fa9 | |||
6f9165c66b | |||
f855a7e43d | |||
b6009b0180 | |||
1066c65718 | |||
ca80e8b78b | |||
a6ecdadeb5 | |||
f5013e87b0 | |||
cda9c6a09a | |||
a5198e1759 | |||
10d115784a | |||
177dae29fe | |||
2eaef02c39 | |||
bb66ca53d0 | |||
fba4c9c252 | |||
6fae72e30a | |||
0b628b1e82 | |||
9cff518959 | |||
e4b4fa2b4d | |||
02686f0672 | |||
1292117c65 | |||
fa6ee33465 | |||
613a8fe37b | |||
a1fb9a880f | |||
fc883a9266 | |||
bdc1c4f73e | |||
a28be0ded5 | |||
c684717409 | |||
b62c415d1d | |||
b6f3b71c72 | |||
378b79739a | |||
6c17edf5a6 | |||
ff02647d92 | |||
8a20194413 | |||
47a9b3e419 | |||
a07454c8a0 | |||
1700318176 | |||
ef581139e9 | |||
a0cde23946 | |||
1e38caeb1d | |||
cf3f321a73 | |||
d554f24125 | |||
5e58eee16e | |||
d72e48fb95 | |||
f39a70b9c2 | |||
5a7adf94ce | |||
de58c03f19 | |||
f841c7134c | |||
5f70a3f814 | |||
14c2a3792c | |||
0a4df4ba97 | |||
ebd4b3a221 | |||
66db405559 | |||
226d0da814 | |||
f0dbb89d27 | |||
4c119a4cde | |||
e8624a2271 | |||
13455ddfa0 | |||
8a786a033c | |||
2334fa9ea2 | |||
bf35a56c66 | |||
6b593fc8d3 | |||
825dbccf81 | |||
3ace38abc1 | |||
ba3c36f771 | |||
8e974aa82d | |||
b369dcb16b | |||
f8c35f7a79 | |||
dadb2d20b2 | |||
ad961ba000 | |||
40c338e330 | |||
91212b125a | |||
cfedf49f7c | |||
d40e095451 | |||
7f85421f80 | |||
ad139de768 | |||
8827d4eded | |||
c9040d0b23 | |||
869ef6663c | |||
37242094c8 | |||
5cfd22a027 | |||
d4e57bbe94 | |||
f1c7fac597 | |||
8facf44e29 | |||
0248450218 | |||
b0749c0598 | |||
e9ba185016 | |||
ba43726d9a | |||
b1821076b0 | |||
37cd8e86b6 | |||
e8b1ed76a3 | |||
a726608fc7 | |||
1600a290c2 | |||
3e106a267c | |||
a550cde6b4 | |||
f6b47a745a | |||
f321cb3a0c | |||
42f19a30e6 | |||
4acd6f76ed | |||
63ecceb636 | |||
24ad89990f | |||
e6caeccdab | |||
3931c2e7fa | |||
f1c2163e74 | |||
1a1c8538fb | |||
08c8d4b396 | |||
f8f8e3bbfc | |||
7d349912e9 | |||
92fcfe0cd6 | |||
110383edc2 | |||
871075a25f | |||
d3a85673f1 | |||
29c1cfad5f | |||
04562f10a9 | |||
9eca0e99b6 | |||
a1efb04fc0 | |||
ab08623e8b | |||
6de6488008 | |||
fc8e491bd0 | |||
7589c54f03 | |||
3f4f693fd8 | |||
2a0161e337 | |||
aac82132ed | |||
630da8a35a | |||
a026f8ff0a | |||
72821699c9 | |||
444725e0c1 | |||
d337f68dac | |||
a17a325712 | |||
c8340c309c | |||
cc0875917a | |||
9b8db985a5 | |||
7415886fe0 | |||
0be6dcb98c | |||
fe5b19d85a | |||
6c8ef818f5 | |||
e85462b20c | |||
8d3a487d84 | |||
830fd7fe93 | |||
612839d4cf | |||
84c52f7cec | |||
2cd5795481 | |||
cc1fa9c7b2 | |||
f2932a2562 | |||
1fb0593c42 | |||
91c28aa75a | |||
bc7124e9af | |||
801af40302 | |||
c587bdae33 | |||
6980b0c0c3 | |||
6586aa77e3 | |||
184aab0b29 | |||
b0f3a6a9c2 | |||
33ed8d49df | |||
e6a7a7dc6a | |||
e14435b5cf | |||
84bd577ecd | |||
2c77026ab8 | |||
0ed7e24de9 | |||
3a099eebe2 | |||
c9941952e7 | |||
f545f9369f | |||
fd5aeaf9cd | |||
7f56fccc19 | |||
3c494b79dd | |||
9ab3e791a7 | |||
c2104c0b7d | |||
905e6ebfce | |||
e90791246a | |||
b655d8017d | |||
6992298bf6 | |||
b8a360a682 | |||
52b2dd7ef9 | |||
6be2f78bfe | |||
bb30353773 | |||
522ea70da9 | |||
449657a214 | |||
0478f07faa | |||
082d50608f | |||
b40bca5277 | |||
3aa0aba42d | |||
5343d1d716 | |||
5d5374a86a | |||
40a52186b0 | |||
676f2903cb | |||
6b00524aa8 | |||
438c4182ff | |||
273dd68bcb | |||
bebdc4d663 | |||
cb2e8339a2 | |||
1855188a6b | |||
4eaa5bffa4 | |||
5d723ecb74 | |||
658fe32012 | |||
c54e81bead | |||
5912fcfe1b | |||
a6c8934451 | |||
b23c00ac1f | |||
4555148f36 | |||
2a445d9470 | |||
03cae1a771 | |||
9b3e59d420 | |||
f14c44c5b0 | |||
05156ec5bf | |||
c90f0a0845 | |||
9a1d67a56b | |||
6bafe6a92e | |||
c68a393c50 | |||
01e7b4d9fe | |||
20e17cef4a | |||
0eca415014 | |||
36621781e5 | |||
6baeee0a68 | |||
9bab5355ae | |||
3ff30a7098 | |||
7d70fe042e | |||
703bd94ffb | |||
4acaaef5cd | |||
4bff401411 | |||
77f1b5437d | |||
9e14d7b460 | |||
a3eee4e69e | |||
ef67a6ba61 | |||
610a750a36 | |||
a282378ff2 | |||
a89c521ffa | |||
ed30db4a53 | |||
254c9aca57 | |||
0e6286df00 | |||
ad9aa5df6a | |||
30c34cf8a2 | |||
e695072213 | |||
da4d51dfd3 | |||
654475e510 | |||
d3006153b4 | |||
c053907039 | |||
521f277acb | |||
c89a99225d | |||
4a411170d6 | |||
bc4d5974bd | |||
1b8fbe95a9 | |||
493a7e515c | |||
8c57c6589a | |||
27a1ed25a4 | |||
8293664911 | |||
fb64dadd70 | |||
5e6857f17b | |||
c0c16b73a0 | |||
5926f901a2 | |||
6db3b11e6f | |||
4882563c61 | |||
8cbb07798e | |||
b2be90f1c5 | |||
ceeb167ee1 | |||
cb44bb92e6 | |||
1c82545706 | |||
86dd067ea8 | |||
56667fe9f0 | |||
3c2ef5693f | |||
64a5ab03bd | |||
37286f71a1 | |||
bd7874a3fb | |||
cde068af71 | |||
a4aeeb9f23 | |||
8961698e80 | |||
b8e199132a | |||
88cd64a6b6 | |||
1c3aa9328e | |||
f28b62a270 | |||
7025cdc65d | |||
b823867072 | |||
f2e403e3f0 | |||
f9196e96eb | |||
af26632d7d | |||
4275564b1a | |||
b3a12eb8ed | |||
3972b5b489 | |||
0e0a03959e | |||
d7baf58b5a | |||
641dd3b7f1 | |||
4d04413044 | |||
a165ced086 | |||
a45e7fa4d2 | |||
c1c3d8dcb6 | |||
c6104b3eb2 | |||
c77741c75b | |||
32cd06b8f6 | |||
456edba9a5 | |||
eb184cb329 | |||
fba5b7feb5 | |||
8dfac88cf7 | |||
c92bae8dea | |||
fc63bf6e2b | |||
cfdb021b0c | |||
f57cb8ed43 | |||
935874313e | |||
2142105ac2 | |||
99df128e38 | |||
9c07bc15db | |||
6d77ca4dd4 | |||
cbe9294361 | |||
4e2c0b7636 | |||
2ce563438c | |||
1976b9ef42 | |||
84814f2ae5 | |||
0d8ddb865c | |||
b74f7d33e4 | |||
d8aaf433f6 | |||
ec6985c636 | |||
3e6f387f91 | |||
097920671e | |||
99d588e55c | |||
527c4ab276 | |||
c2818cc009 | |||
f4a3693b04 | |||
7c67ced16f | |||
16fb293c83 | |||
227267e2d3 | |||
42040e76f4 | |||
cf8e3c3f9e | |||
1712b21537 | |||
df294fc2f5 | |||
4e91ba23e2 | |||
010749131c | |||
ff8b3f5514 | |||
97bc509b46 | |||
cd155613b5 | |||
2a2986c413 | |||
0beb79093f | |||
fada38662c | |||
c15f1b8ac8 | |||
14911a268d | |||
f12883edbe | |||
e79043471c | |||
ef43021423 | |||
4560e7771f | |||
fb81c944c3 | |||
8f3fe5eec5 | |||
d6d4c47895 | |||
8d4cb1ac9a | |||
865206a85e | |||
f6c25279ae | |||
a16727e90a | |||
13b520d8d6 | |||
4c562a5610 | |||
48a391233c | |||
e487fdd582 | |||
99e76f036d | |||
4db4806aa8 | |||
d8548d4415 | |||
5ca55dd5f6 | |||
65b5c149a7 | |||
78e33e7266 | |||
c48e606f8e | |||
ca513d83b6 | |||
fa5133b76c | |||
da85af0902 | |||
413c8256a9 | |||
d7b02965a8 | |||
a25cd3de0f | |||
554cd6355d | |||
64e43ae1ed | |||
35601e62b0 | |||
3d70c826fe | |||
426470d0e4 | |||
c62ddc6b4f | |||
f93b8653ce | |||
06b4b5a595 | |||
6595298bbf | |||
82d89493f7 | |||
897c7fcf81 | |||
030eb2912e | |||
86c507d0d7 | |||
9c4aca5312 | |||
9110c6e262 | |||
4ba995d6d8 | |||
898bd9e2ff | |||
ecd9b5c47f | |||
b1a385aa93 | |||
71208b8fd7 | |||
54e00c3c09 | |||
643a9cad3b | |||
028b350daa | |||
7c5c31236d | |||
5039eb9b54 | |||
b4a870e287 | |||
bf352639c8 | |||
9822de7a5e | |||
74b0157e83 | |||
53bb40356c | |||
d2e98d8955 | |||
a23342ef6f | |||
d467613fe4 | |||
5ec7198fc5 | |||
5c0910a341 | |||
c6a9000b86 | |||
287c8a88dd | |||
c1278d8513 | |||
8471aa93b6 | |||
725a4a4586 | |||
4db1362255 | |||
5b20fa96e9 | |||
7a0cc3ff1b | |||
c388639699 | |||
fcccd84796 | |||
1fafc9c49a | |||
96df4e17be | |||
381bdb5aea | |||
187595b7d6 | |||
75dc492c4e | |||
18b051b16e | |||
fb7dc8737f | |||
a6da330253 | |||
182fc45e1e | |||
1e6e8e2809 | |||
9841e2a165 | |||
1300cdae2c | |||
4c6a38fc27 | |||
49e6aa6431 | |||
787916eeb9 | |||
cfd43bb6ac | |||
5bcad8eb09 | |||
dd1ba25e6e | |||
eb66e9868a | |||
a471180860 | |||
153f3c1eff | |||
f0ad730c89 | |||
e4de71f698 | |||
9cfbeea108 | |||
d5d6a5e594 | |||
b3fa775943 | |||
63c58d408d | |||
2a1401291d | |||
c9d29aace9 | |||
40c9a2d5a4 | |||
28e29a832d | |||
f23e83e72b | |||
f59a139b7c | |||
09689682b4 | |||
19cef59362 | |||
fcb4a3f035 | |||
404adafe15 | |||
a50bdace0f | |||
a93daeb91e | |||
8127f41436 | |||
b8b01bc7ee | |||
6ced327d85 | |||
100d49c081 | |||
2c3b709a08 | |||
0fc172edea | |||
613ea7e1d2 | |||
b95c43ea21 | |||
90a78afb7e | |||
fea03b9070 | |||
e48b8e4d62 | |||
16c7dfe4c2 | |||
a4939ccdb7 | |||
85a21a23be | |||
c6e127a31b | |||
04c080bd04 | |||
01a7435105 | |||
b686fd49f3 | |||
6cfebfa7f4 | |||
02401a9b1a | |||
0a3534da4f | |||
d6f0538328 | |||
54c2c9dc62 | |||
e18daad552 | |||
6f98548a63 | |||
3f64ef0a67 | |||
353224bf28 | |||
8b0cdcdad4 | |||
80f18795f8 | |||
0ae11943b5 | |||
feb6d269b0 | |||
26ed6c363f | |||
3ce79debcc | |||
d209ade933 | |||
31c2cb44b3 | |||
7b1f55be5c | |||
8cd1161c21 | |||
63edaa1b58 | |||
3dd79c8152 | |||
1273e936e6 | |||
af478b6dee | |||
867c4c2e45 | |||
6b5d4fc34f | |||
498fde780c | |||
0c37ef2c10 | |||
f3ee1b9adf | |||
6dd23cdb3f | |||
20fa2a4f30 | |||
c8d560e4f0 | |||
fd8ba70ba4 | |||
09518047b6 | |||
2ef27d11af | |||
ac7e2c2845 | |||
0383639e78 | |||
e3c0386306 | |||
d46f1cece9 | |||
88098c8a8a | |||
40f3f9ac52 | |||
bb9e94c621 | |||
a667ea5e6b | |||
251bce473f | |||
080c02144c | |||
e4f3305870 | |||
9204b48619 | |||
c63da3b8e1 | |||
82761aac06 | |||
bb262a834f | |||
2bef345cee | |||
2804e0858a | |||
dcd312a6fc | |||
2004d242f1 | |||
9b8ab40646 | |||
ed8a8e1296 | |||
d8e4ab70ec | |||
10a61456eb | |||
57141a73c2 | |||
309fd6af2f | |||
3e32773b57 | |||
3e4fec2041 | |||
54b736bdb4 | |||
3856f224b9 | |||
bede0f3ab6 | |||
dafcfa89cb | |||
7d9325a23f | |||
081592db3d | |||
d8a622c5b5 | |||
6244ed0c63 | |||
f6f7b5c63b | |||
7c41fba569 | |||
eed5abfe81 | |||
2e7a586272 | |||
5aa1b9e6d9 | |||
0858309cfd | |||
c413cb7344 | |||
f1d420a2ce | |||
bf7336ae8d | |||
3654dd58ba | |||
8d12a83691 | |||
e38d14363e | |||
ec30dcbfb8 | |||
e6c7a19648 | |||
f2569776df | |||
5be65b7d28 | |||
88b9cce5f4 | |||
a9890f2cf2 | |||
748c964a27 | |||
95ecc5ba4f | |||
4fbc506480 | |||
81747be330 | |||
0203a264e5 | |||
fe2a69205c | |||
9fcfc2874c | |||
041474cdb9 | |||
98d9fd105a | |||
57d340261d | |||
99404a88dd | |||
461140293b | |||
10df6d50f9 | |||
58a53d642d | |||
9ccc89aa4c | |||
78e32a1084 | |||
d8558ca03c | |||
251e7cd945 | |||
6469110741 | |||
c43a8c2f47 | |||
c08a7d3cc6 | |||
bc2f44c6d3 | |||
95e919b07f | |||
993cb667e0 | |||
d86c14dab4 | |||
552f4d8df7 | |||
d01260204a | |||
eede24106e | |||
0f141a0d14 | |||
14e4ad9ac6 | |||
a38952486d | |||
e651986fab | |||
9de411cd9d | |||
c6fa5ea038 | |||
5859a4a3b6 | |||
75db8f917f | |||
f7bfe69f70 | |||
a5d9cd6a63 | |||
9617e7a7c3 | |||
ba0d7183e2 | |||
66bf354dc9 | |||
0f971cbe6a | |||
232fe3e4d1 | |||
e093f4c963 | |||
01c09f2791 | |||
59895310ca | |||
ec1cc6529f | |||
8a79b646fa | |||
3e7c383707 | |||
cc0bdfd80c | |||
08a6447e19 | |||
29b00a309e | |||
740d7d9b99 | |||
8af3094021 | |||
305cac9a09 | |||
d2500b8b11 | |||
cde18d21e1 | |||
3a8d250327 | |||
94b92a9e2e | |||
6b3222f635 | |||
911b49c3b1 | |||
c0c3be9a06 | |||
bbf5e41389 | |||
a7cd768e76 | |||
99b94d5af2 | |||
b69a99c35e | |||
6093e62c3a | |||
bf00617b2b | |||
22db951a6d | |||
286dab2aa8 | |||
39f795d587 | |||
d922c4fb6e | |||
3fd3c03d6c | |||
d837ae1624 | |||
ed59f895b1 | |||
adc926c340 | |||
4266265ce7 | |||
f973101312 | |||
48a1daf770 | |||
0beb79c44d | |||
01eadec59f | |||
c9426ceee2 | |||
2859c050e8 | |||
7295e18135 | |||
4260614c2b | |||
043c36068b | |||
2127403564 | |||
f77b8a91e0 | |||
304896bba2 | |||
b305aa74e3 | |||
b5dad914ec | |||
328001384c | |||
24dbaa1dc0 | |||
165c27ace8 | |||
e3e822c8d8 | |||
32c3904a16 | |||
129c54a81c | |||
24f6172fbd | |||
36157e53e3 | |||
ee38bc7927 | |||
98fb3e4543 | |||
e15e291307 | |||
e3440b75c8 | |||
8e999df245 | |||
65691c3561 | |||
922b353ec6 | |||
8cf4e040b8 | |||
1881971bac | |||
cbab9141de | |||
d859f07e11 | |||
20a80e5ed9 | |||
a6fed5c002 | |||
4ecccaf3f5 | |||
2d7304ab75 | |||
e44efd6a03 | |||
171e866119 | |||
216b4180c5 | |||
565bc3b7bf | |||
df5fe19eb9 | |||
01095279cc | |||
602f5c818a | |||
e2ef746c5f | |||
7ca6ce558d | |||
c2edf17e8e | |||
ca468373de | |||
67cad5169f | |||
90132ffc3d | |||
79c823fbc9 | |||
a11ebfcaa7 | |||
36b9b154de | |||
324ca962ed | |||
3b97e85eb4 | |||
05762582d9 | |||
d222ffdfe8 | |||
0b7fddf963 | |||
60359fe250 | |||
4f737ee118 | |||
59bc49efdf | |||
c60e8e90f3 | |||
08c065d125 | |||
f1209a5dab | |||
80a14eecd4 | |||
bb9dde337e | |||
a25cf2b2a6 | |||
d48866bfe3 | |||
c56af712b0 | |||
10d754a06d | |||
4956bc6d90 | |||
a2961d7667 | |||
e628275ff7 | |||
9ee45efabb | |||
dcaec17699 | |||
938319bbe4 | |||
22c1c976b3 | |||
c76561850b | |||
8182a29956 | |||
67a97ef58b | |||
2462f93263 | |||
8856ba85c5 | |||
b8f018b0d6 | |||
7fc6f55bfb | |||
3e9ebea3e1 | |||
5ac93d2121 | |||
69d2be4596 | |||
2d55076ef1 | |||
a5f4e2a091 | |||
27ae344de8 | |||
c24dc05ff3 | |||
0e08f372fe | |||
d2f9f76f97 | |||
d2b8abdef8 | |||
ad1f200ff0 | |||
50a5593694 | |||
60ba6401b2 | |||
f2c7b74838 | |||
2eab5bc7dc | |||
1a6e1614a5 | |||
d95b8c882f | |||
66d9118d65 | |||
65dd527852 | |||
acf0b95693 | |||
b815799996 | |||
3263a7b641 | |||
dedd99e18b | |||
40376e71ad | |||
e8274a50a1 | |||
542830150f | |||
ebe369f138 | |||
0548739000 | |||
0080c8722a | |||
4dcb3a627d | |||
7ecaeaa13d | |||
9beb0ddfee | |||
474bdc2ca6 | |||
11f3906ef9 | |||
28ae1793e2 | |||
eba5e72915 | |||
59ad092aaf | |||
17b47a9c12 | |||
b79b8a53db | |||
ea0ba75a32 | |||
780ffdc2d4 | |||
c5783ad9dd | |||
156b6982a5 | |||
5b264fcac8 | |||
01c6b9d128 | |||
a1d69c0002 | |||
25416b8193 | |||
f4ade1e876 | |||
207bb8a212 | |||
65943edc36 | |||
54e7ade5dc | |||
06a7135737 | |||
40c13aeec3 | |||
0b2dce3678 | |||
a80ed5a677 | |||
57edad6b7a | |||
60e12c9caf | |||
421be0e8b3 | |||
de61154389 | |||
4e13e753a6 | |||
9024f480e0 | |||
45f4f8857d | |||
85a6d13dd3 | |||
b0c2a41b7f | |||
a39ab7e8b1 | |||
a52baaed98 | |||
1c93958e78 | |||
aa116518b3 | |||
1926f73ce7 | |||
6c657e5720 | |||
f726251645 | |||
c357fa6de8 | |||
c7100742d8 | |||
9662f4fdd9 | |||
6eb0d1727e | |||
dd02531cf0 | |||
135b87e140 | |||
d8224ef93c | |||
23f3d4bb8f | |||
fdf866b9c8 | |||
7225b4f60d | |||
e261e42e0c | |||
d01aed5171 | |||
025dde8f31 | |||
4f4f516678 | |||
8e9b13a1df | |||
ac338d0c54 | |||
31489495a6 | |||
5d13f117c1 | |||
cab7dc5ee1 | |||
36ad8814fe | |||
daef46442b | |||
be97b271cf | |||
3f3aab4c22 | |||
b4f2ece9bf | |||
95a0bdfdd2 | |||
572958aea2 | |||
36a7b5df63 | |||
d467640be9 | |||
a82b91878d | |||
200a494410 | |||
18bbd43858 | |||
5e3e77ba8a | |||
209c661ab7 | |||
670dc3e1d9 | |||
a057f9f6d1 | |||
b937673c31 | |||
54bc8eebf3 | |||
554049ef63 | |||
5eabc03d6b | |||
060c321d96 | |||
9463388b23 | |||
b5b4ce5f86 | |||
35cdea8cb3 | |||
acf6c98f13 | |||
cf51079243 | |||
6069125584 | |||
0762420c86 | |||
0536ec5df6 | |||
0a4e9c7d67 | |||
fe6fd6688e | |||
f8f0b185ac | |||
e0618a6b2d | |||
c945a6ad47 | |||
1d8ddc6084 | |||
1fcbe8dc98 | |||
145e23cc91 | |||
7f791e0075 | |||
e229b91812 | |||
8d66dcb4ae | |||
21743b8383 | |||
d7587f4d71 | |||
eb131a405f | |||
2891ceee0d | |||
3c49d3ccec | |||
88a171d4e5 | |||
a289da13e0 | |||
8f0d73ba29 | |||
0adffbd240 | |||
90600b12f8 | |||
5181a2a419 | |||
c1ee0f4bc6 | |||
4bc4ac65db | |||
b7ae0db444 | |||
ab9b5fc32f | |||
d04610ba48 | |||
26a220b366 | |||
09991a0080 | |||
d67fa68445 | |||
93a4f3e0c4 | |||
3d29a7c2ff | |||
92b262bbd7 | |||
5dfb79e2d7 | |||
524107bc94 | |||
0479e21015 | |||
bf1b2a9d31 | |||
2e077f639b | |||
16f91fa3f1 | |||
db32310b44 | |||
10b94e1d62 | |||
8476c7d484 | |||
8b7747a914 | |||
32ed9edce5 | |||
74298deaf4 | |||
1a36e7481b | |||
5e6c3f0cec | |||
8e7ceac8c2 | |||
c5be6ef112 | |||
45d37c1f39 | |||
8842e8e684 | |||
0da7568f50 | |||
dc9caba870 | |||
d0939c8a95 | |||
d585b1c619 | |||
62096ea2d5 | |||
c1dd67c4a9 | |||
2e82e13e12 | |||
86befc7c59 | |||
688bfb5b0a | |||
7fc1aafb64 | |||
c30e1892f6 | |||
20a2b3829e | |||
ea54541f6a | |||
419f5639bf | |||
b6b96fb7a6 | |||
13fedb1e28 | |||
7b816ce538 | |||
5c8f31dbc0 | |||
9b512a3d27 | |||
eadba47c2c | |||
1c2aae3ea3 | |||
5eb79a77f5 | |||
725ac8a9fc | |||
87e3a58e36 | |||
0cf66571ae | |||
5513ace425 | |||
8ff2013333 | |||
d683e7e0df | |||
dd8551dacc | |||
aa623d6b13 | |||
5708349f67 | |||
14bbd9de7c | |||
5b98a70ce2 | |||
b174cf1456 | |||
8031737e2f | |||
42fa7fed57 | |||
5d3b0e48e4 | |||
9a775b3fe9 | |||
d5316cde06 | |||
39d3b4df42 | |||
66a0e0c80d | |||
2746ecc588 | |||
7c64d0069d | |||
1005a37c48 | |||
58b99daa48 | |||
e04b73ea94 | |||
8359737828 | |||
706b5c0841 | |||
aefc7b2334 | |||
7133fada59 | |||
12289f2493 | |||
a1d5038ab0 | |||
b65388cb68 | |||
8f9220231a | |||
caab59e5f0 | |||
9de0ce42df | |||
fe9cd0d5f4 | |||
cd9dccd766 | |||
ac750996b9 | |||
6854faa66b | |||
d8b1c99132 | |||
3e023f17eb | |||
437a6e9892 | |||
348ee5d7b8 | |||
2b58c25027 | |||
23aa8de334 | |||
1a7af434fa | |||
f16ee0a357 | |||
4255802027 | |||
8680a90f8e | |||
098f874757 | |||
1ad9e93d8f | |||
91b1098c58 | |||
3873e6e8c1 | |||
5945ab95ae | |||
b1e9ebd291 | |||
545bacf25b | |||
2859f8af51 | |||
3541cd1870 | |||
1160895f9e | |||
3db736d3f3 | |||
22bb8c7467 | |||
12db2c5fcc | |||
51a038096a | |||
53fda7c401 | |||
a7f3029be0 | |||
9f373368b3 | |||
0945c7c26c | |||
871cb475f9 | |||
57aaf92947 | |||
dc7b14b11a | |||
0f5022df15 | |||
8f256d9bc3 | |||
b686afba98 | |||
f08eeb435c | |||
7a8c10c6d4 | |||
b38dfa039d | |||
b07f0d4942 | |||
788216c373 | |||
ead52decf2 | |||
7757788f99 | |||
ddff397fe4 | |||
8fc441c1b3 | |||
0ee3ca5e08 | |||
214dff29d5 | |||
cc3b2a50cf | |||
ee08232128 | |||
f9d5be189e | |||
a8513ce514 | |||
9ecdbc2bee | |||
5ee30b1782 | |||
4bf74f15a2 | |||
ccb386e3b3 | |||
dbedb83104 | |||
b34120e688 | |||
90c7c667e4 | |||
062a163a11 | |||
1f4c3a08d0 | |||
acc7ab1727 | |||
c7ddc6ec5b | |||
6d22dda0ae | |||
bfba8304e4 | |||
7d38170ba6 | |||
348b540c06 | |||
330c33b3d7 | |||
dfa9548b59 | |||
b351bbebed | |||
469374f25e | |||
72c1609503 | |||
8d5291cbb0 | |||
ebf1d0759e | |||
c829f70418 | |||
341bddd4cd | |||
460a5415e5 | |||
a4d984f19c | |||
09ae458ff1 | |||
00f6ee71e0 | |||
126d5e9ba2 | |||
d64b0ac0cb | |||
bfb27e7359 | |||
48445afdd5 | |||
4854e622b3 | |||
9d9c8a6a07 | |||
7040300b3e | |||
24478b6320 | |||
a43a788e15 | |||
2892913caa | |||
930d481fcf | |||
81a9d7be7c | |||
45e5019b28 | |||
b76b7a107c | |||
87295d10c1 | |||
d03b8cdb1d | |||
fd8c1ec31b | |||
720d356d1f | |||
4b0efe7491 | |||
a20e7a1c8e | |||
88fdad4ab9 | |||
3ed49f88ab | |||
752bd07c5d | |||
b08de94c64 | |||
892c5f9902 | |||
3029a8a653 | |||
86510a8871 | |||
e85b864170 | |||
727f8f5177 | |||
ad9a2f8f19 | |||
803bc20fb7 | |||
7d479b4c5f | |||
ffa9c8abb0 | |||
49475212a7 | |||
824d4042c1 | |||
0c76c137b9 | |||
accff3fa60 | |||
1cfa62802f | |||
9019741146 | |||
095b6ad775 | |||
a043cde46e | |||
bfec178de7 | |||
12f3948e40 | |||
0bc26f8895 | |||
b66a60ebe9 | |||
b4a24de8cd | |||
9517ba1fa9 | |||
73d330d1e2 | |||
13ae6c2716 | |||
645ea3e197 | |||
fcf6f15be9 | |||
3d066435b4 | |||
26eda76b23 | |||
05905e6e6d | |||
1533ae46a4 | |||
2b72f87256 | |||
8bf79c2744 | |||
45923319a6 | |||
d8daacf80e | |||
24eaf3a7e2 | |||
57f63c3838 | |||
b9e1945719 | |||
0b8fb4977c | |||
a5eec44b8e | |||
e2ebe6a36e | |||
5132e40d2c | |||
1d6361871c | |||
3978f19b2e | |||
26cdef50b2 | |||
fee833d231 | |||
6541287dec | |||
44d81b6fd7 | |||
200f16eac0 | |||
8c9fe03629 | |||
24071eb5df | |||
3b5c241872 | |||
44a708548f | |||
5c1a8f0ef1 | |||
526bb7736a | |||
22c0544d09 | |||
9c3748ad7c | |||
3807ae89a0 | |||
9e2be295c3 | |||
fb257d1c30 | |||
17a075cd29 | |||
93b5663ebe | |||
8399fd5bb1 | |||
364b5d9b3c | |||
e08c20aa3d | |||
65bebb7b13 | |||
13814afebc | |||
b44bc679e5 | |||
c83b90aa20 | |||
1bb8d8d801 | |||
0b1cb03305 | |||
90a93d35f7 | |||
24716dd497 | |||
259b58aa1a | |||
52ec0845a7 | |||
ddbe950a19 | |||
46790b12ff | |||
488d0a6d12 | |||
7699871779 | |||
ac323a5410 | |||
9408d0d66b | |||
5cd3912ff5 | |||
33e674f7c7 | |||
d0c10018f3 | |||
523c94aafb | |||
ac42502883 | |||
704596c371 | |||
4de241ecab | |||
f70818c9f3 | |||
6ad412000d | |||
2804f942b7 | |||
561b365e44 | |||
9475c68682 | |||
34abb66086 | |||
890df2c27c | |||
cb1d2ca6b9 | |||
f31ad5ceca | |||
8b5933749d | |||
ab950352fa | |||
984e8aab76 | |||
cc8b44db9b | |||
1371a83e90 | |||
33e4a416a9 | |||
5bf4bd9a1c | |||
4edfe6848c | |||
884233a938 | |||
3ac284e237 | |||
d1fe9177af | |||
8316723a6a | |||
f9ce2bfaf9 | |||
a9857cb34b | |||
894a1d82f5 | |||
fa4f5e1e09 | |||
12c8b8558f | |||
bb8a36ad5c | |||
ebeeadd5c3 | |||
6e0b2f5092 | |||
2286815485 | |||
2e402de6bf | |||
45d68ff4ab | |||
370701a4b9 | |||
956f1f7b6d | |||
115a2e7589 | |||
f9d195788c | |||
c673ae27f6 | |||
c5d04aed3c | |||
e39a24de2a | |||
aa715aaafa | |||
9303dd1c19 | |||
9a5075f27e | |||
60ef1cd138 | |||
235a02ca60 | |||
05852b99d4 | |||
01730cd08b | |||
6335a4617f | |||
552d3c81f9 | |||
2d9b0ce732 | |||
3b0f8068c1 | |||
c142fc61b4 | |||
dcfde4131f | |||
a1cb21d98e | |||
4c731c1942 | |||
d2a6d70cf6 | |||
f5ef44b12a | |||
a86cf0c769 | |||
7a0a3e0d50 | |||
479c0ee13d | |||
b280981e1c | |||
933140502d | |||
dfada15b00 | |||
8fdcdd0116 | |||
11573a5113 | |||
7e3a83743d | |||
38af503626 | |||
534bc74936 | |||
ea2be5d5fb | |||
38a8571ddc | |||
2d41c7a76b | |||
16bc2aab52 | |||
17132cd51a | |||
ec6ea7e839 | |||
c81dbddab1 | |||
e0226c5cfe | |||
3f12bafee0 | |||
3fc28a79cb | |||
72d0054d25 | |||
25356be323 | |||
e8d90f8a3c | |||
c724ae502f | |||
a3efdbf3cb | |||
2857b6886f | |||
1e24e89568 | |||
3609e6af10 | |||
d5fefcdbbc | |||
8860952c07 | |||
55905e3841 | |||
d27cdf6ee5 | |||
0d4c46cfd4 | |||
190852bc16 | |||
9ef6b3f36f | |||
1a5caf244f | |||
73857c9fc5 | |||
853661ff52 | |||
9040b84050 | |||
1518ff7809 | |||
ba5b142bbe | |||
4ccace9450 | |||
711e6d48fb | |||
83cfec4678 | |||
90f47825b6 | |||
b0270e651e | |||
238a67c267 | |||
2e5f0a781a | |||
d9e57a47e9 | |||
13a085b9de | |||
fddc57d90e | |||
9d9ae80dbe | |||
314c744527 | |||
555b4d14f0 | |||
41fb87fab6 | |||
65bd82054a | |||
40879c85bb | |||
0858f9e49e | |||
a1aa96fc2f | |||
6ed91fe43b |
@ -1,7 +1,8 @@
|
|||||||
|
.env_example
|
||||||
.git
|
.git
|
||||||
|
.github
|
||||||
.gitignore
|
.gitignore
|
||||||
.vscode
|
.vscode
|
||||||
|
build
|
||||||
datastore
|
datastore
|
||||||
Dockerfile
|
node_modules
|
||||||
LICENSE
|
|
||||||
README.md
|
|
||||||
|
@ -1,2 +1,5 @@
|
|||||||
DISCORD_TOKEN=
|
DISCORD_TOKEN=
|
||||||
RADIOX_STATIONSLISTURL=https://gitea.cwinfo.org/cwchristerw/radio/raw/branch/master/playlist.json
|
RADIOX_STATIONSLISTURL=https://git.cwinfo.net/cwchristerw/radio/raw/branch/master/playlist.json
|
||||||
|
DEV_MODE=false
|
||||||
|
DEBUG_MODE=false
|
||||||
|
STREAMER_MODE=manual
|
||||||
|
19
.github/dependabot.yml
vendored
Normal file
19
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
# GitHub Actions
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
target-branch: "develop"
|
||||||
|
labels:
|
||||||
|
- "dependencies"
|
||||||
|
|
||||||
|
# npm
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
target-branch: "develop"
|
||||||
|
labels:
|
||||||
|
- "dependencies"
|
9
.github/labeler.yml
vendored
Normal file
9
.github/labeler.yml
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
dependencies:
|
||||||
|
- package-lock.json
|
||||||
|
|
||||||
|
documentation:
|
||||||
|
- README.md
|
||||||
|
- SECURITY.md
|
||||||
|
- CONTRIBUTING.md
|
||||||
|
- LICENSE
|
||||||
|
- .env_example
|
71
.github/workflows/codeql-analyze.yml
vendored
Normal file
71
.github/workflows/codeql-analyze.yml
vendored
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# For most projects, this workflow file will not need changing; you simply need
|
||||||
|
# to commit it to your repository.
|
||||||
|
#
|
||||||
|
# You may wish to alter this file to override the set of languages analyzed,
|
||||||
|
# or to provide custom queries or build logic.
|
||||||
|
#
|
||||||
|
# ******** NOTE ********
|
||||||
|
# We have attempted to detect the languages in your repository. Please check
|
||||||
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
|
# supported CodeQL languages.
|
||||||
|
#
|
||||||
|
name: "CodeQL Analyze"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ develop ]
|
||||||
|
pull_request:
|
||||||
|
# The branches below must be a subset of the branches above
|
||||||
|
branches: [ master ]
|
||||||
|
schedule:
|
||||||
|
- cron: '24 20 * * 6'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: CodeQL Analyze
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language: [ 'javascript' ]
|
||||||
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||||
|
# Learn more:
|
||||||
|
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
# Initializes the CodeQL tools for scanning.
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v2
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
# By default, queries listed here will override any specified in a config file.
|
||||||
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
|
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||||
|
|
||||||
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
|
- name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
|
||||||
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
|
# 📚 https://git.io/JvXDl
|
||||||
|
|
||||||
|
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||||
|
# and modify them (or add more) to build your code if your project
|
||||||
|
# uses a compiled language
|
||||||
|
|
||||||
|
#- run: |
|
||||||
|
# make bootstrap
|
||||||
|
# make release
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v2
|
20
.github/workflows/dependabot_auto-merge.yml
vendored
Normal file
20
.github/workflows/dependabot_auto-merge.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
name: Dependabot Auto-Merge
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
branches: [ develop ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
dependabot:
|
||||||
|
name: Dependabot Auto-Merge
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.actor == 'dependabot[bot]' }}
|
||||||
|
steps:
|
||||||
|
- name: Enable auto-merge for Dependabot PRs
|
||||||
|
run: gh pr merge --auto --merge "$PR_URL"
|
||||||
|
env:
|
||||||
|
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
9
.github/workflows/docker-build.yml
vendored
9
.github/workflows/docker-build.yml
vendored
@ -2,6 +2,9 @@ name: Docker Build
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened, assigned, edited, ready_for_review]
|
||||||
|
push:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
buildx:
|
buildx:
|
||||||
@ -9,11 +12,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Setup Docker Buildx
|
- name: Setup Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v1.5.1
|
uses: docker/setup-buildx-action@v2.9.0
|
||||||
id: buildx
|
id: buildx
|
||||||
with:
|
with:
|
||||||
install: true
|
install: true
|
||||||
- name: Build
|
- name: Build
|
||||||
run: docker build . # will run buildx
|
run: docker build . # will run buildx
|
||||||
|
17
.github/workflows/labeler.yml
vendored
Normal file
17
.github/workflows/labeler.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
name: Labeler
|
||||||
|
on: [pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
label:
|
||||||
|
name: Labeler
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.actor != 'dependabot[bot]' }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/labeler@v4
|
||||||
|
with:
|
||||||
|
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
sync-labels: true
|
7
.github/workflows/typescript-build.yml
vendored
7
.github/workflows/typescript-build.yml
vendored
@ -1,16 +1,19 @@
|
|||||||
name: TypeScript Build
|
name: TypeScript Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened, assigned, edited, ready_for_review]
|
||||||
push:
|
push:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
tsc:
|
tsc:
|
||||||
name: TypeScript Build
|
name: TypeScript Build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v3
|
||||||
- name: install node v16
|
- name: install node v16
|
||||||
uses: actions/setup-node@v2.3.1
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
- name: npm install
|
- name: npm install
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,6 +1,5 @@
|
|||||||
datastore/
|
datastore/
|
||||||
node_modules/
|
node_modules/
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
.vscode/
|
|
||||||
.env
|
.env
|
||||||
build/
|
build/
|
||||||
|
6
.vscode/settings.json
vendored
Normal file
6
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"files.trimTrailingWhitespace": true,
|
||||||
|
"files.insertFinalNewline": true,
|
||||||
|
"files.trimFinalNewlines": true,
|
||||||
|
"editor.renderFinalNewline": false
|
||||||
|
}
|
27
CONTRIBUTING.md
Normal file
27
CONTRIBUTING.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
|
||||||
|
|
||||||
|
If you haven't already, come find us in [Discord](https://waren.io/r/eximiabots-discord). We want you working on things you're excited about.
|
||||||
|
|
||||||
|
Here are some important resources:
|
||||||
|
|
||||||
|
* [Discord](https://waren.io/r/eximiabots-discord) Join our Discord guild.
|
||||||
|
|
||||||
|
## Coding
|
||||||
|
|
||||||
|
### Pull Requests
|
||||||
|
* Open a new PR from your fork's new branch into develop branch.
|
||||||
|
* Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
|
||||||
|
* Try to fix all merge conflicts.
|
||||||
|
|
||||||
|
### Coding conventions
|
||||||
|
* We indent using four spaces (soft tabs)
|
||||||
|
* We ALWAYS put spaces after list items and method parameters (`[1, 2, 3]`, not `[1,2,3]`), around operators (`x += 1`, not `x+=1`), and around hash arrows.
|
||||||
|
* This is open source software. Consider the people who will read your code, and make it look nice for them. It's sort of like driving a car: Perhaps you love doing donuts when you're alone, but with passengers the goal is to make the ride as smooth as possible.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
We have currently automated testing in Github Workflows, you can suggest new Github Workflows to us by making PR.
|
||||||
|
|
||||||
|
Every release is manually tested by [cwchristerw](https://github.com/cwchristerw) or [MatteZ02](https://github.com/MatteZ02).
|
@ -1,4 +1,4 @@
|
|||||||
FROM node:16-alpine
|
FROM node:18-alpine
|
||||||
|
|
||||||
#Dependencies
|
#Dependencies
|
||||||
RUN apk add --virtual .build-deps python3 make g++ gcc git
|
RUN apk add --virtual .build-deps python3 make g++ gcc git
|
||||||
@ -14,4 +14,4 @@ RUN npm install
|
|||||||
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
CMD [ "npm", "start" ]
|
CMD [ "npm", "start" ]
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2020-2021 EximiaBots by Warén Group
|
Copyright (c) 2020-2023 EximiaBots by Warén Group
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
41
README.md
41
README.md
@ -1,12 +1,45 @@
|
|||||||
# RadioX by EximiaBots
|
# RadioX by EximiaBots
|
||||||
Internet Radio to your Discord guild
|
Internet Radio to your Discord guild
|
||||||
|
|
||||||
## [Radio Stations List](https://gitea.cwinfo.org/cwchristerw/radio)
|
## [Radio Stations List](https://git.cwinfo.net/cwchristerw/radio)
|
||||||
This bot is using Gitea repo to get radio stations from [playlist.json](https://gitea.cwinfo.org/cwchristerw/radio/raw/branch/master/playlist.json) file. List is currently maintained by Christer Warén. You can use alternative list with same format when using RADIOX_STATIONSLISTURL environment variable.
|
This bot is using Gitea repo to get radio stations from [playlist.json](https://git.cwinfo.net/cwchristerw/radio/raw/branch/master/playlist.json) file. List is currently maintained by Christer Warén. You can use alternative list with same format when using RADIOX_STATIONSLISTURL environment variable.
|
||||||
|
|
||||||
## Docker
|
## Docker
|
||||||
1. `docker build -t warengroup/eximiabots-radiox .`
|
|
||||||
2. `docker run --name radiox-dev -d --net host -e DISCORD_TOKEN= -v "$PWD/datastore":/usr/src/app/datastore/ warengroup/eximiabots-radiox`
|
### 1. Build Image
|
||||||
|
|
||||||
|
**Production**
|
||||||
|
```
|
||||||
|
docker build -t warengroup/eximiabots-radiox:latest . --pull
|
||||||
|
```
|
||||||
|
|
||||||
|
**Beta**
|
||||||
|
```
|
||||||
|
docker build -t warengroup/eximiabots-radiox:latest-beta . --pull
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dev**
|
||||||
|
```
|
||||||
|
docker build -t warengroup/eximiabots-radiox:latest-dev . --pull
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Run Container
|
||||||
|
|
||||||
|
**Production**
|
||||||
|
```
|
||||||
|
docker run --name radiox --net host -d -e DISCORD_TOKEN= -e STREAMER_MODE=auto -v "$PWD/datastore":/usr/src/app/datastore/ warengroup/eximiabots-radiox:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
**Beta**
|
||||||
|
```
|
||||||
|
docker run --name radiox --net host -d -e DISCORD_TOKEN= -e STREAMER_MODE=auto -v "$PWD/datastore":/usr/src/app/datastore/ warengroup/eximiabots-radiox:latest-beta
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dev**
|
||||||
|
```
|
||||||
|
docker run --rm --name radiox-dev --net host -e DISCORD_TOKEN= -e DEV_MODE=true -v "$PWD":/usr/src/app/ warengroup/eximiabots-radiox:latest-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Join our Discord Server
|
## Join our Discord Server
|
||||||
https://discord.gg/rRA65Mn
|
https://discord.gg/rRA65Mn
|
||||||
|
19
SECURITY.md
Normal file
19
SECURITY.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| ------- | ------------------ |
|
||||||
|
| 0.5.x | :white_check_mark: |
|
||||||
|
| 0.4.x | :white_check_mark: |
|
||||||
|
| 0.3.x | :x: |
|
||||||
|
| 0.2.x | :x: |
|
||||||
|
| 0.1.x | :x: |
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
Use this section to tell people how to report a vulnerability.
|
||||||
|
|
||||||
|
Tell them where to go, how often they can expect to get an update on a
|
||||||
|
reported vulnerability, what to expect if the vulnerability is accepted or
|
||||||
|
declined, etc.
|
6771
package-lock.json
generated
6771
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
54
package.json
54
package.json
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "eximiabots-radiox",
|
"name": "eximiabots-radiox",
|
||||||
"version": "0.3.1",
|
"version": "0.5.1",
|
||||||
"description": "Internet Radio to your Discord guild",
|
"description": "Internet Radio to your Discord guild",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rimraf ./build && tsc",
|
"build": "rimraf ./build && tsc",
|
||||||
"start": "node build/index.js",
|
"start": "node --no-warnings build/index.js",
|
||||||
"start:dev": "npm run build && node build/index.js"
|
"start:dev": "rimraf ./build && tsc && node --no-warnings build/index.js"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -18,34 +18,32 @@
|
|||||||
"url": "https://github.com/warengroup/eximiabots-radiox/issues"
|
"url": "https://github.com/warengroup/eximiabots-radiox/issues"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@discordjs/builders": "^0.6.0",
|
"@discordjs/builders": "^1.6.3",
|
||||||
"@discordjs/opus": "^0.6.0",
|
"@discordjs/opus": "^0.9.0",
|
||||||
"@discordjs/rest": "^0.1.1-canary.0",
|
"@discordjs/rest": "^1.7.1",
|
||||||
"@discordjs/voice": "^0.6.0",
|
"@discordjs/voice": "^0.16.0",
|
||||||
"discord-api-types": "^0.22.0",
|
"discord-api-types": "^0.37.47",
|
||||||
"discord.js": "^13.1.0",
|
"discord.js": "^14.11.0",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^16.3.1",
|
||||||
"libsodium-wrappers": "^0.7.9",
|
"libsodium-wrappers": "^0.7.11",
|
||||||
"node-fetch": "^2.6.1",
|
|
||||||
"path": "^0.12.7"
|
"path": "^0.12.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^16.7.8",
|
"@types/node": "^20.4.1",
|
||||||
"@types/ws": "^7.4.7",
|
"@types/ws": "^8.5.5",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.29.3",
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||||
"@typescript-eslint/parser": "^4.29.3",
|
"@typescript-eslint/parser": "^6.0.0",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^8.44.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.8.0",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^5.0.0",
|
||||||
"nodemon": "^2.0.12",
|
"prettier": "^3.0.0",
|
||||||
"prettier": "^2.3.2",
|
"rimraf": "^5.0.1",
|
||||||
"rimraf": "^3.0.2",
|
"ts-node": "^10.9.1",
|
||||||
"ts-node": "^10.2.1",
|
"tsc-watch": "^6.0.4",
|
||||||
"tsc-watch": "^4.5.0",
|
"typescript": "^5.1.6"
|
||||||
"typescript": "^4.4.2"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.6.0",
|
"node": ">=18.16.0",
|
||||||
"npm": ">=7.0.0"
|
"npm": ">=8.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,92 +1,61 @@
|
|||||||
import Discord, { Client, Collection } from "discord.js";
|
import { Client, Collection, IntentsBitField } from "discord.js";
|
||||||
import fs from "fs";
|
import Datastore from "./client/classes/Datastore";
|
||||||
import Datastore from "./client/datastore.js";
|
import Radio from "./client/classes/Radio";
|
||||||
import { command, radio } from "./client/utils/typings.js";
|
import Stations from "./client/classes/Stations";
|
||||||
import config from "./config.js";
|
import Streamer from "./client/classes/Streamer";
|
||||||
import messages from "./client/messages.js";
|
import Statistics from "./client/classes/Statistics";
|
||||||
import path from "path";
|
import { command } from "./client/commands";
|
||||||
|
import config from "./config";
|
||||||
|
import events from "./client/events"
|
||||||
|
import { funcs } from "./client/funcs";
|
||||||
|
import { messages } from "./client/messages";
|
||||||
|
|
||||||
const events = "./client/events/";
|
|
||||||
|
|
||||||
const GatewayIntents = new Discord.Intents();
|
const GatewayIntents = new IntentsBitField();
|
||||||
GatewayIntents.add(
|
GatewayIntents.add(
|
||||||
1 << 0, // GUILDS
|
1 << 0, // GUILDS
|
||||||
1 << 7, // GUILD_VOICE_STATES
|
1 << 7, // GUILD_VOICE_STATES
|
||||||
1 << 9 // GUILD_MESSAGES
|
1 << 9 // GUILD_MESSAGES
|
||||||
);
|
);
|
||||||
|
|
||||||
class RadioClient extends Client {
|
export default class RadioClient extends Client {
|
||||||
readonly commands: Collection<string, command>;
|
readonly commands: Collection<string, command>;
|
||||||
readonly radio: Map<string, radio>;
|
readonly funcs = funcs;
|
||||||
public funcs: any;
|
|
||||||
readonly config = config;
|
readonly config = config;
|
||||||
readonly messages = messages;
|
readonly messages = messages;
|
||||||
public datastore: Datastore | null;
|
public datastore: Datastore | null;
|
||||||
|
public stations: Stations | null;
|
||||||
|
public streamer: Streamer | null;
|
||||||
|
public statistics: Statistics | null;
|
||||||
|
public radio: Radio | null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
intents: GatewayIntents
|
intents: GatewayIntents
|
||||||
});
|
});
|
||||||
this.commands = new Collection();
|
this.commands = new Collection();
|
||||||
this.radio = new Map();
|
|
||||||
this.datastore = null;
|
this.datastore = null;
|
||||||
|
this.stations = null;
|
||||||
this.funcs = {};
|
this.streamer = null;
|
||||||
this.funcs.check = require("./client/funcs/check.js");
|
this.statistics = null;
|
||||||
this.funcs.checkFetchStatus = require("./client/funcs/checkFetchStatus.js");
|
this.radio = null;
|
||||||
this.funcs.isDev = require("./client/funcs/isDev.js");
|
|
||||||
this.funcs.logger = require("./client/funcs/logger.js");
|
|
||||||
this.funcs.msToTime = require("./client/funcs/msToTime.js");
|
|
||||||
this.funcs.statisticsUpdate = require("./client/funcs/statisticsUpdate.js");
|
|
||||||
this.funcs.saveState = require("./client/funcs/saveState.js");
|
|
||||||
this.funcs.loadState = require("./client/funcs/loadState.js");
|
|
||||||
|
|
||||||
console.log('RadioX ' + this.config.version);
|
console.log('RadioX ' + this.config.version);
|
||||||
console.log('Internet Radio to your Discord guild');
|
console.log('Internet Radio to your Discord guild');
|
||||||
console.log('(c)2020-2021 EximiaBots by Warén Group');
|
console.log('(c)2020-2022 EximiaBots by Warén Group');
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|
||||||
this.funcs.logger("Bot", "Starting");
|
this.funcs.logger("Bot", "Starting");
|
||||||
|
|
||||||
const commandFiles = fs.readdirSync(path.join("./src/client/commands")).filter(f => f.endsWith(".js"));
|
this.funcs.logger("Maintenance Mode", "Enabled");
|
||||||
for (const file of commandFiles) {
|
this.config.maintenanceMode = true;
|
||||||
const command = require(`./client/commands/${file}`);
|
|
||||||
this.commands.set(command.name, command);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.on("ready", () => {
|
events(this);
|
||||||
require(`${events}ready`).execute(this);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.on("messageCreate", msg => {
|
this.login(this.config.token).catch((err) => {
|
||||||
require(`${events}messageCreate`).execute(this, msg);
|
this.funcs.logger("Discord Client", "Login Error");
|
||||||
|
console.log(err);
|
||||||
|
console.log('');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.on("messageDelete", msg => {
|
|
||||||
require(`${events}messageDelete`).execute(this, msg);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.on("interactionCreate", interaction => {
|
|
||||||
require(`${events}interactionCreate`).execute(this, interaction);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.on("voiceStateUpdate", (oldState, newState) => {
|
|
||||||
require(`${events}voiceStateUpdate`).execute(this, oldState, newState);
|
|
||||||
});
|
|
||||||
|
|
||||||
process.on('SIGINT', () => {
|
|
||||||
require(`${events}SIGINT`).execute(this);
|
|
||||||
});
|
|
||||||
|
|
||||||
process.on('SIGTERM', () => {
|
|
||||||
require(`${events}SIGTERM`).execute(this);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.on("error", error => {
|
|
||||||
console.error(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.login(this.config.token).catch(err => console.log("Failed to login: " + err));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RadioClient
|
|
101
src/client/classes/Datastore.ts
Normal file
101
src/client/classes/Datastore.ts
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import { Guild } from 'discord.js';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { state } from './Radio';
|
||||||
|
import { statistics } from './Statistics';
|
||||||
|
|
||||||
|
export interface datastore {
|
||||||
|
guild: {
|
||||||
|
id: string,
|
||||||
|
name?: string
|
||||||
|
},
|
||||||
|
statistics: statistics,
|
||||||
|
state: state | null,
|
||||||
|
updated?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Datastore {
|
||||||
|
map: Map<string, datastore>;
|
||||||
|
constructor() {
|
||||||
|
this.map = new Map();
|
||||||
|
this.loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadData() {
|
||||||
|
const dir = path.join(path.dirname(__dirname), '../../datastore');
|
||||||
|
if (!fs.existsSync(dir)) {
|
||||||
|
fs.mkdirSync(dir);
|
||||||
|
}
|
||||||
|
//console.log("");
|
||||||
|
const dataFiles = fs.readdirSync(path.join(path.dirname(__dirname), '../../datastore')).filter((f: string) => f.endsWith('.json'));
|
||||||
|
for (const file of dataFiles) {
|
||||||
|
try {
|
||||||
|
const json = require(`../../../datastore/${file}`);
|
||||||
|
this.map.set(json.guild.id, json);
|
||||||
|
//console.log('[LOADED] ' + file + " (" + json.guild.id + ")");
|
||||||
|
//console.log(JSON.stringify(json, null, 4));
|
||||||
|
} catch (error) {
|
||||||
|
//console.log('[ERROR] Loading ' + file + ' failed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//console.log("");
|
||||||
|
}
|
||||||
|
|
||||||
|
checkEntry(id: string | undefined){
|
||||||
|
if(!id) return;
|
||||||
|
this.loadEntry(id);
|
||||||
|
if(!this.map.has(id)){
|
||||||
|
this.createEntry(id);
|
||||||
|
//this.showEntry(this.getEntry(id));
|
||||||
|
} else {
|
||||||
|
//this.showEntry(this.getEntry(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createEntry(id: string){
|
||||||
|
let newData: datastore = {
|
||||||
|
guild: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
statistics: {},
|
||||||
|
state: null
|
||||||
|
};
|
||||||
|
this.map.set(id, newData);
|
||||||
|
this.saveEntry(id, newData);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadEntry(id: string){
|
||||||
|
try {
|
||||||
|
const json = require(`../../../datastore/` + id + '.json');
|
||||||
|
this.map.set(id, json);
|
||||||
|
} catch (error) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getEntry(id: string){
|
||||||
|
return this.map.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateEntry(guild: Guild | { id: string, name: string }, newData: datastore) {
|
||||||
|
newData.guild.name = guild.name;
|
||||||
|
|
||||||
|
let date = new Date();
|
||||||
|
newData.updated = date.toISOString().substring(0, 10)
|
||||||
|
|
||||||
|
this.map.set(guild.id, newData);
|
||||||
|
this.saveEntry(guild.id, newData);
|
||||||
|
//this.showEntry(this.getEntry(guild.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
showEntry(data : datastore){
|
||||||
|
console.log(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
saveEntry(file: string, data: datastore) {
|
||||||
|
fs.writeFile(path.join(path.dirname(__dirname), '../../datastore') + "/" + file + ".json", JSON.stringify(data, null, 4), 'utf8', function(err: NodeJS.ErrnoException | null) {
|
||||||
|
if (err) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
108
src/client/classes/Radio.ts
Normal file
108
src/client/classes/Radio.ts
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import { Channel, Collection, GuildMember, OAuth2Guild, TextBasedChannel, VoiceBasedChannel, VoiceChannel } from "discord.js";
|
||||||
|
import { getVoiceConnection, joinVoiceChannel, VoiceConnection } from "@discordjs/voice";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
import { station } from "./Stations";
|
||||||
|
import { datastore } from "./Datastore";
|
||||||
|
|
||||||
|
export interface radio {
|
||||||
|
textChannel: Channel | TextBasedChannel | undefined | null,
|
||||||
|
voiceChannel: Channel | VoiceBasedChannel | undefined,
|
||||||
|
connection: VoiceConnection | null,
|
||||||
|
message: null,
|
||||||
|
station: station,
|
||||||
|
datastore?: datastore,
|
||||||
|
currentTime?: number,
|
||||||
|
startTime: number,
|
||||||
|
playTime?: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface state {
|
||||||
|
channels: {
|
||||||
|
"text": string | undefined,
|
||||||
|
"voice": string | undefined
|
||||||
|
},
|
||||||
|
date: string,
|
||||||
|
station: {
|
||||||
|
name: string,
|
||||||
|
owner: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Radio extends Map {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
save(client: RadioClient): void {
|
||||||
|
let currentRadios = this.keys();
|
||||||
|
let radio = currentRadios.next();
|
||||||
|
|
||||||
|
while (!radio.done) {
|
||||||
|
let currentRadio = this.get(radio.value);
|
||||||
|
|
||||||
|
if (currentRadio) {
|
||||||
|
currentRadio.guild = client.datastore?.getEntry(radio.value)?.guild;
|
||||||
|
|
||||||
|
client.statistics?.update(client, currentRadio.guild, currentRadio);
|
||||||
|
client.funcs.saveState(client, currentRadio.guild, currentRadio);
|
||||||
|
currentRadio.connection?.destroy();
|
||||||
|
currentRadio.message?.delete();
|
||||||
|
this.delete(radio.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
radio = currentRadios.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
restore(client: RadioClient, guilds: Collection<string, OAuth2Guild>): void {
|
||||||
|
if(!client.stations) return;
|
||||||
|
|
||||||
|
guilds.forEach(async (guild: OAuth2Guild) => {
|
||||||
|
let state = client.funcs.loadState(client, guild);
|
||||||
|
|
||||||
|
if(!state) return;
|
||||||
|
if(state.channels?.text === undefined || state.channels?.voice === undefined) return;
|
||||||
|
|
||||||
|
let voiceChannel = client.channels.cache.get(state.channels.voice);
|
||||||
|
if(!voiceChannel || !(voiceChannel instanceof VoiceChannel)) return;
|
||||||
|
if(voiceChannel.members.filter((member: GuildMember) => !member.user.bot).size === 0) return;
|
||||||
|
|
||||||
|
const sstation = client.stations?.search(state.station.name, "direct");
|
||||||
|
let station = sstation;
|
||||||
|
|
||||||
|
if(!station) return;
|
||||||
|
|
||||||
|
let date = new Date();
|
||||||
|
const construct: radio = {
|
||||||
|
textChannel: client.channels.cache.get(state.channels.text),
|
||||||
|
voiceChannel: client.channels.cache.get(state.channels.voice),
|
||||||
|
connection: null,
|
||||||
|
message: null,
|
||||||
|
station: station,
|
||||||
|
startTime: date.getTime()
|
||||||
|
};
|
||||||
|
this.set(guild.id, construct);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const connection =
|
||||||
|
getVoiceConnection(guild.id) ??
|
||||||
|
joinVoiceChannel({
|
||||||
|
channelId: voiceChannel.id,
|
||||||
|
guildId: voiceChannel.guild.id,
|
||||||
|
adapterCreator: voiceChannel.guild.voiceAdapterCreator
|
||||||
|
});
|
||||||
|
|
||||||
|
construct.connection = connection;
|
||||||
|
let date = new Date();
|
||||||
|
construct.startTime = date.getTime();
|
||||||
|
client.datastore?.checkEntry(guild.id);
|
||||||
|
|
||||||
|
client.funcs.play(client, null, guild, station);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
117
src/client/classes/Stations.ts
Normal file
117
src/client/classes/Stations.ts
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import logger from "../funcs/logger";
|
||||||
|
|
||||||
|
export interface station {
|
||||||
|
name: string,
|
||||||
|
owner: string,
|
||||||
|
logo: string,
|
||||||
|
stream: {
|
||||||
|
[key: string]: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Stations extends Array {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetch(options: { url: string, show?: boolean}){
|
||||||
|
try {
|
||||||
|
logger('Stations', 'Started fetching list - ' + options.url);
|
||||||
|
let stations: station[] = await fetch(options.url)
|
||||||
|
.then(this.checkFetchStatus)
|
||||||
|
.then((response: Response) => response.json());
|
||||||
|
|
||||||
|
for(const station of stations){
|
||||||
|
this.push(station);
|
||||||
|
if(options.show) logger('Stations', station.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger('Stations', 'Successfully fetched list');
|
||||||
|
} catch (error) {
|
||||||
|
logger('Stations', 'Fetching list failed');
|
||||||
|
console.error(error + "\n");
|
||||||
|
|
||||||
|
if(this.length == 0) setTimeout( () => {
|
||||||
|
this.fetch(options)
|
||||||
|
}, 150 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkFetchStatus(response: Response) {
|
||||||
|
if (response.ok) {
|
||||||
|
return response;
|
||||||
|
} else {
|
||||||
|
throw new Error(response.status + " " + response.statusText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
search(key: string, type: string) {
|
||||||
|
if (this === null || !key || !type) return null;
|
||||||
|
|
||||||
|
if(type == "direct"){
|
||||||
|
return this.find(station => station.name === key);
|
||||||
|
} else {
|
||||||
|
let foundStations : { station: string, name: string, probability: number }[] = [];
|
||||||
|
if (key == "radio") return null;
|
||||||
|
|
||||||
|
this
|
||||||
|
.filter(
|
||||||
|
x => x.name.toUpperCase().includes(key.toUpperCase()) || x === key
|
||||||
|
)
|
||||||
|
.forEach(x =>
|
||||||
|
foundStations.push({ station: x, name: x.name, probability: 100 })
|
||||||
|
);
|
||||||
|
|
||||||
|
if (key.startsWith("radio ")) key = key.slice(6);
|
||||||
|
const probabilityIncrement = 100 / key.split(" ").length / 2;
|
||||||
|
for (let i = 0; i < key.split(" ").length; i++) {
|
||||||
|
this
|
||||||
|
.filter(
|
||||||
|
x => x.name.toUpperCase().includes(key.split(" ")[i].toUpperCase()) || x === key
|
||||||
|
)
|
||||||
|
.forEach(x =>
|
||||||
|
foundStations.push({ station: x, name: x.name, probability: probabilityIncrement })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (foundStations.length === 0) return null;
|
||||||
|
for (let i = 0; i < foundStations.length; i++) {
|
||||||
|
for (let j = 0; j < foundStations.length; j++) {
|
||||||
|
if (foundStations[i] === foundStations[j] && i !== j) foundStations.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = 0; i < foundStations.length; i++) {
|
||||||
|
if (foundStations[i].name.length > key.length) {
|
||||||
|
foundStations[i].probability -=
|
||||||
|
(foundStations[i].name.split(" ").length - key.split(" ").length) *
|
||||||
|
(probabilityIncrement * 0.5);
|
||||||
|
} else if (foundStations[i].name.length === key.length) {
|
||||||
|
foundStations[i].probability += probabilityIncrement * 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let j = 0; j < key.split(" ").length; j++) {
|
||||||
|
if (!foundStations[i].name.toUpperCase().includes(key.toUpperCase().split(" ")[j])) {
|
||||||
|
foundStations[i].probability -= probabilityIncrement * 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let highestProbabilityStation : { station: string, name: string, probability: number } | undefined;
|
||||||
|
let stationName = "";
|
||||||
|
|
||||||
|
for (let i = 0; i < foundStations.length; i++) {
|
||||||
|
if (
|
||||||
|
!highestProbabilityStation ||
|
||||||
|
highestProbabilityStation.probability < foundStations[i].probability
|
||||||
|
)
|
||||||
|
highestProbabilityStation = foundStations[i];
|
||||||
|
if (
|
||||||
|
highestProbabilityStation &&
|
||||||
|
highestProbabilityStation.probability === foundStations[i].probability
|
||||||
|
) {
|
||||||
|
stationName = foundStations[i].station;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stationName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
95
src/client/classes/Statistics.ts
Normal file
95
src/client/classes/Statistics.ts
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import { Guild } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
import { radio } from "./Radio";
|
||||||
|
|
||||||
|
export interface statistics {
|
||||||
|
[key: string]: statistic
|
||||||
|
}
|
||||||
|
|
||||||
|
interface statistic {
|
||||||
|
"time": number,
|
||||||
|
"used": number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Statistics {
|
||||||
|
map: Map<string, statistics>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.map = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
update(client: RadioClient, guild: Guild | null, radio: radio) {
|
||||||
|
if(!guild) return;
|
||||||
|
|
||||||
|
client.datastore?.checkEntry(guild.id);
|
||||||
|
|
||||||
|
radio.datastore = client.datastore?.getEntry(guild.id);
|
||||||
|
|
||||||
|
if(radio.datastore === undefined) return;
|
||||||
|
|
||||||
|
if(!radio.datastore.statistics[radio.station.name]){
|
||||||
|
radio.datastore.statistics[radio.station.name] = {
|
||||||
|
time: 0,
|
||||||
|
used: 0
|
||||||
|
};
|
||||||
|
client.datastore?.updateEntry(guild, radio.datastore);
|
||||||
|
}
|
||||||
|
|
||||||
|
let date = new Date();
|
||||||
|
radio.currentTime = date.getTime();
|
||||||
|
radio.playTime = radio.currentTime - radio.startTime;
|
||||||
|
radio.datastore.statistics[radio.station.name] = {
|
||||||
|
time: radio.datastore.statistics[radio.station.name].time + radio.playTime,
|
||||||
|
used: radio.datastore.statistics[radio.station.name].used + 1
|
||||||
|
}
|
||||||
|
client.datastore?.updateEntry(guild, radio.datastore);
|
||||||
|
this.calculateGlobal(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateGlobal(client: RadioClient){
|
||||||
|
if(!client.datastore?.map) return;
|
||||||
|
|
||||||
|
let guilds = client.datastore.map.keys();
|
||||||
|
let statistics : statistics = {};
|
||||||
|
|
||||||
|
if(!client.stations) return;
|
||||||
|
|
||||||
|
let calculation = guilds.next();
|
||||||
|
|
||||||
|
while (!calculation.done) {
|
||||||
|
let currentGuild = client.datastore.getEntry(calculation.value);
|
||||||
|
if(calculation.value != 'global'){
|
||||||
|
if(client.stations){
|
||||||
|
for(const station of client.stations) {
|
||||||
|
if(!currentGuild) return;
|
||||||
|
if(currentGuild.statistics[station.name] && currentGuild.statistics[station.name]?.time && currentGuild.statistics[station.name].time != 0 && currentGuild.statistics[station.name].used && currentGuild.statistics[station.name].used != 0){
|
||||||
|
if(!statistics[station.name]){
|
||||||
|
statistics[station.name] = {
|
||||||
|
time: 0,
|
||||||
|
used: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
statistics[station.name] = {
|
||||||
|
time: statistics[station.name].time + currentGuild.statistics[station.name].time,
|
||||||
|
used: statistics[station.name].used + currentGuild.statistics[station.name].used
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
calculation = guilds.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
let newData = {
|
||||||
|
guild: {
|
||||||
|
id: "global",
|
||||||
|
name: "global"
|
||||||
|
},
|
||||||
|
statistics: statistics,
|
||||||
|
state: null
|
||||||
|
};
|
||||||
|
client.datastore.updateEntry(newData.guild, newData);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
116
src/client/classes/Streamer.ts
Normal file
116
src/client/classes/Streamer.ts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import logger from "../funcs/logger";
|
||||||
|
import { AudioPlayer, AudioPlayerStatus, createAudioPlayer, createAudioResource, NoSubscriberBehavior } from "@discordjs/voice";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
import { station } from "./Stations";
|
||||||
|
|
||||||
|
export default class Streamer {
|
||||||
|
map: Map<string, AudioPlayer>;
|
||||||
|
mode: "auto" | "manual";
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.map = new Map();
|
||||||
|
this.mode = "manual";
|
||||||
|
}
|
||||||
|
|
||||||
|
init(client: RadioClient){
|
||||||
|
if(!client.config.streamerMode) return;
|
||||||
|
|
||||||
|
switch(client.config.streamerMode){
|
||||||
|
case "manual":
|
||||||
|
this.mode = "manual";
|
||||||
|
break;
|
||||||
|
case "auto":
|
||||||
|
this.mode = "auto";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.mode = "manual";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.mode == "auto"){
|
||||||
|
if(!client.stations) return;
|
||||||
|
|
||||||
|
for(const station of client.stations){
|
||||||
|
this.play(station);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh(client: RadioClient){
|
||||||
|
this.init(client);
|
||||||
|
|
||||||
|
for (const streamer of this.map.keys()){
|
||||||
|
if(client.stations?.findIndex((station: station) => station.name == streamer) == -1){
|
||||||
|
this.stop(streamer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
play(station: station) {
|
||||||
|
let audioPlayer = this.map.get(station.name);
|
||||||
|
if(!audioPlayer) {
|
||||||
|
if(this.mode == "auto"){
|
||||||
|
audioPlayer = createAudioPlayer({
|
||||||
|
behaviors: {
|
||||||
|
noSubscriber: NoSubscriberBehavior.Play,
|
||||||
|
maxMissedFrames: Math.round(5000 / 20),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
audioPlayer = createAudioPlayer({
|
||||||
|
behaviors: {
|
||||||
|
noSubscriber: NoSubscriberBehavior.Stop,
|
||||||
|
maxMissedFrames: Math.round(5000 / 20),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
audioPlayer
|
||||||
|
.on(AudioPlayerStatus.Playing, () => {
|
||||||
|
logger('Streamer', station.name + " / " + "Playing");
|
||||||
|
})
|
||||||
|
.on(AudioPlayerStatus.Idle, () => {
|
||||||
|
logger('Streamer', station.name + " / " + "Idle");
|
||||||
|
})
|
||||||
|
.on(AudioPlayerStatus.Paused, () => {
|
||||||
|
logger('Streamer', station.name + " / " + "Paused");
|
||||||
|
})
|
||||||
|
.on(AudioPlayerStatus.Buffering, () => {
|
||||||
|
logger('Streamer', station.name + " / " + "Buffering");
|
||||||
|
})
|
||||||
|
.on(AudioPlayerStatus.AutoPaused, () => {
|
||||||
|
logger('Streamer', station.name + " / " + "AutoPaused");
|
||||||
|
})
|
||||||
|
|
||||||
|
this.map.set(station.name, audioPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = station.stream[station.stream.default];
|
||||||
|
const resource = createAudioResource(url);
|
||||||
|
audioPlayer.play(resource);
|
||||||
|
|
||||||
|
return audioPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
stop(streamer: string){
|
||||||
|
let audioPlayer = this.map.get(streamer);
|
||||||
|
if(audioPlayer){
|
||||||
|
logger('Streamer', streamer + " / " + "Stop");
|
||||||
|
audioPlayer.removeAllListeners();
|
||||||
|
audioPlayer.stop();
|
||||||
|
}
|
||||||
|
this.map.delete(streamer);
|
||||||
|
}
|
||||||
|
|
||||||
|
listen(station: station) {
|
||||||
|
let audioPlayer = this.map.get(station.name);
|
||||||
|
if(!audioPlayer) audioPlayer = this.play(station);
|
||||||
|
return audioPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
leave(client: RadioClient) {
|
||||||
|
if(!client.stations) return;
|
||||||
|
for(const station of client.stations){
|
||||||
|
this.stop(station.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -1,67 +0,0 @@
|
|||||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
|
||||||
const { REST } = require('@discordjs/rest');
|
|
||||||
const { Routes } = require('discord-api-types/v9');
|
|
||||||
const { token, version } = require('../config.js');
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require ('path');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
async execute(client) {
|
|
||||||
const commands = [];
|
|
||||||
const commandFiles = fs.readdirSync(path.join("./src/client/commands")).filter(f => f.endsWith(".js"));
|
|
||||||
|
|
||||||
for (const file of commandFiles) {
|
|
||||||
const command = require(`./commands/${file}`);
|
|
||||||
command.data = new SlashCommandBuilder()
|
|
||||||
.setName(command.name)
|
|
||||||
.setDescription(command.description);
|
|
||||||
|
|
||||||
command.data = command.data.toJSON();
|
|
||||||
if(command.options) {
|
|
||||||
command.options.forEach(function(option) {
|
|
||||||
if(option.type == "STRING") option.type = 3;
|
|
||||||
command.data.options.push(option);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
commands.push(command.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
const rest = new REST({ version: '9' }).setToken(token);
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
try {
|
|
||||||
client.funcs.logger('Slash Commands', 'Started refreshing application (/) commands.');
|
|
||||||
|
|
||||||
if(version.includes("-dev")){
|
|
||||||
await rest.put(
|
|
||||||
Routes.applicationCommands(client.user.id),
|
|
||||||
{ body: [] },
|
|
||||||
);
|
|
||||||
|
|
||||||
let guilds = await client.guilds.fetch();
|
|
||||||
guilds.forEach(async guild => {
|
|
||||||
try {
|
|
||||||
await rest.put(
|
|
||||||
Routes.applicationGuildCommands(client.user.id, guild.id),
|
|
||||||
{ body: commands },
|
|
||||||
);
|
|
||||||
} catch (DiscordAPIError) {
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await rest.put(
|
|
||||||
Routes.applicationCommands(client.user.id),
|
|
||||||
{ body: commands },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
client.funcs.logger('Slash Commands', 'Successfully reloaded application (/) commands.' + "\n");
|
|
||||||
} catch (error) {
|
|
||||||
client.funcs.logger('Slash Commands', 'Reloading application (/) commands failed.' + "\n");
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
}
|
|
72
src/client/commands.ts
Normal file
72
src/client/commands.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { Snowflake } from "discord.js";
|
||||||
|
import RadioClient from "../Client";
|
||||||
|
import bug from "./commands/bug";
|
||||||
|
import help from "./commands/help";
|
||||||
|
import invite from "./commands/invite";
|
||||||
|
import list from "./commands/list";
|
||||||
|
import maintenance from "./commands/maintenance";
|
||||||
|
import next from "./commands/next";
|
||||||
|
import nowplaying from "./commands/nowplaying";
|
||||||
|
import play from "./commands/play";
|
||||||
|
import prev from "./commands/prev";
|
||||||
|
import statistics from "./commands/statistics";
|
||||||
|
import status from "./commands/status";
|
||||||
|
import stop from "./commands/stop";
|
||||||
|
|
||||||
|
export interface command {
|
||||||
|
name: string,
|
||||||
|
description: string,
|
||||||
|
category: string,
|
||||||
|
options?: [],
|
||||||
|
execute: Function
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function commands(client: RadioClient) {
|
||||||
|
const commands : command[] = [ bug, help, invite, list, maintenance, next, nowplaying, play, prev, statistics, status, stop ];
|
||||||
|
|
||||||
|
for(const command of commands){
|
||||||
|
client.commands.set(command.name, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!client.application) return;
|
||||||
|
client.funcs.logger('Application Commands', 'Started refreshing application (/) commands.');
|
||||||
|
if(client.config.devMode){
|
||||||
|
client.application.commands.set([]);
|
||||||
|
for(const command of commands){
|
||||||
|
let guilds = await client.guilds.fetch();
|
||||||
|
guilds.forEach(async (guild: { id: Snowflake; name: string; }) => {
|
||||||
|
try {
|
||||||
|
if(!client.application) return;
|
||||||
|
await client.application.commands.create({
|
||||||
|
name: command.name,
|
||||||
|
description: command.description,
|
||||||
|
options: command.options || []
|
||||||
|
}, guild.id);
|
||||||
|
client.funcs.logger('Application Commands', 'Guild: ' + guild.id + " (" + guild.name + ") \n" + 'Command: ' + command.name);
|
||||||
|
} catch(DiscordAPIError) {
|
||||||
|
client.funcs.logger('Application Commands', 'Guild: ' + guild.id + " (" + guild.name + ") [FAILED] \n" + 'Command: ' + command.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for(const command of commands){
|
||||||
|
await client.application.commands.create({
|
||||||
|
name: command.name,
|
||||||
|
description: command.description,
|
||||||
|
options: command.options || []
|
||||||
|
});
|
||||||
|
|
||||||
|
client.funcs.logger('Application Commands', 'Command: ' + command.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
let guilds = await client.guilds.fetch();
|
||||||
|
guilds.forEach(async (guild: { id: Snowflake; }) => {
|
||||||
|
try {
|
||||||
|
if(!client.application) return;
|
||||||
|
await client.application.commands.set([], guild.id);
|
||||||
|
} catch (DiscordAPIError){
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
client.funcs.logger('Application Commands', 'Successfully reloaded application (/) commands.' + "\n");
|
||||||
|
}
|
@ -1,26 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
name: 'bug',
|
|
||||||
alias: 'none',
|
|
||||||
usage: '',
|
|
||||||
description: 'Report a bug',
|
|
||||||
permission: 'none',
|
|
||||||
category: 'info',
|
|
||||||
async execute(interaction, client, Discord, command) {
|
|
||||||
let message = {};
|
|
||||||
|
|
||||||
message.bugTitle = client.messages.bugTitle.replace("%client.user.username%", client.user.username);
|
|
||||||
message.bugDescription = message.bugDescription.replace("%client.config.supportGuild%", client.config.supportGuild);
|
|
||||||
|
|
||||||
const embed = new Discord.MessageEmbed()
|
|
||||||
.setTitle(message.bugTitle)
|
|
||||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["logo"].replace(/[^0-9]+/g, ''))
|
|
||||||
.setColor(client.config.embedColor)
|
|
||||||
.setDescription(message.bugDescription)
|
|
||||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
|
||||||
|
|
||||||
interaction.reply({
|
|
||||||
embeds: [embed],
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
34
src/client/commands/bug.ts
Normal file
34
src/client/commands/bug.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { ChatInputCommandInteraction, EmbedBuilder } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'bug',
|
||||||
|
description: 'Report a bug',
|
||||||
|
category: 'info',
|
||||||
|
async execute(interaction: ChatInputCommandInteraction, client: RadioClient) {
|
||||||
|
if(!client.user) return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.maintenance,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setTitle(client.messages.replace(client.messages.bugTitle, {
|
||||||
|
"%client.user.username%": client.user.username
|
||||||
|
}))
|
||||||
|
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messages.emojis["logo"].replace(/[^0-9]+/g, ''))
|
||||||
|
.setColor(client.config.embedColor)
|
||||||
|
.setDescription(client.messages.replace(client.messages.bugDescription, {
|
||||||
|
"%client.config.supportGuild%": client.config.supportGuild
|
||||||
|
}))
|
||||||
|
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||||
|
.setFooter({
|
||||||
|
text: client.messages.footerText,
|
||||||
|
iconURL: "https://cdn.discordapp.com/emojis/" + client.messages.emojis["eximiabots"].replace(/[^0-9]+/g, '')
|
||||||
|
});
|
||||||
|
|
||||||
|
interaction.reply({
|
||||||
|
embeds: [embed],
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -1,35 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
name: 'help',
|
|
||||||
alias: 'h',
|
|
||||||
usage: '<command(opt)>',
|
|
||||||
description: 'Get help using bot',
|
|
||||||
permission: 'none',
|
|
||||||
category: 'info',
|
|
||||||
execute(interaction, client, Discord, command) {
|
|
||||||
let message = {};
|
|
||||||
|
|
||||||
const categories = [];
|
|
||||||
for (let i = 0; i < client.commands.size; i++) {
|
|
||||||
if (!categories.includes([...client.commands.values()][i].category)) categories.push([...client.commands.values()][i].category);
|
|
||||||
}
|
|
||||||
let commands = '';
|
|
||||||
for (let i = 0; i < categories.length; i++) {
|
|
||||||
commands += `**» ${categories[i].toUpperCase()}**\n${client.commands.filter(x => x.category === categories[i] && !x.omitFromHelp).map(x => `\`${x.name}\``).join(', ')}\n`;
|
|
||||||
}
|
|
||||||
|
|
||||||
message.helpTitle = client.messages.helpTitle.replace("%client.user.username%", client.user.username);
|
|
||||||
message.helpDescription = client.messages.helpDescription.replace("%commands%", commands);
|
|
||||||
|
|
||||||
const embed = new Discord.MessageEmbed()
|
|
||||||
.setTitle(message.helpTitle)
|
|
||||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["logo"].replace(/[^0-9]+/g, ''))
|
|
||||||
.setColor(client.config.embedColor)
|
|
||||||
.setDescription(message.helpDescription)
|
|
||||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
|
||||||
|
|
||||||
interaction.reply({
|
|
||||||
embeds: [embed],
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
45
src/client/commands/help.ts
Normal file
45
src/client/commands/help.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { ChatInputCommandInteraction, EmbedBuilder } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
import { command } from "../commands";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'help',
|
||||||
|
description: 'Get help using bot',
|
||||||
|
category: 'info',
|
||||||
|
execute(interaction: ChatInputCommandInteraction, client: RadioClient) {
|
||||||
|
|
||||||
|
if(!client.user) return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.maintenance,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const categories: string[] = [];
|
||||||
|
for (let i = 0; i < client.commands.size; i++) {
|
||||||
|
if (!categories.includes([...client.commands.values()][i].category)) categories.push([...client.commands.values()][i].category);
|
||||||
|
}
|
||||||
|
let commands = '';
|
||||||
|
for (let i = 0; i < categories.length; i++) {
|
||||||
|
commands += `**» ${categories[i].toUpperCase()}**\n${client.commands.filter(x => x.category === categories[i]).map((x: command) => `\`${x.name}\``).join(', ')}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setTitle(client.messages.replace(client.messages.helpTitle, {
|
||||||
|
"%client.user.username%": client.user.username
|
||||||
|
}))
|
||||||
|
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messages.emojis["logo"].replace(/[^0-9]+/g, ''))
|
||||||
|
.setColor(client.config.embedColor)
|
||||||
|
.setDescription(client.messages.replace(client.messages.helpDescription, {
|
||||||
|
"%commands%": commands
|
||||||
|
}))
|
||||||
|
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||||
|
.setFooter({
|
||||||
|
text: client.messages.footerText,
|
||||||
|
iconURL: "https://cdn.discordapp.com/emojis/" + client.messages.emojis["eximiabots"].replace(/[^0-9]+/g, '')
|
||||||
|
});
|
||||||
|
|
||||||
|
interaction.reply({
|
||||||
|
embeds: [embed],
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -1,22 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
name: 'invite',
|
|
||||||
alias: 'i',
|
|
||||||
usage: '',
|
|
||||||
description: 'Invite Bot',
|
|
||||||
permission: 'none',
|
|
||||||
category: 'info',
|
|
||||||
execute(interaction, client, Discord, command) {
|
|
||||||
let message = {};
|
|
||||||
message.inviteTitle = client.messages.inviteTitle.replace("%client.user.username%", client.user.username);
|
|
||||||
const embed = new Discord.MessageEmbed()
|
|
||||||
.setTitle(message.inviteTitle)
|
|
||||||
.setColor(client.config.embedColor)
|
|
||||||
.setURL("https://discord.com/api/oauth2/authorize?client_id=" + client.user.id + "&permissions=2184465408&scope=applications.commands%20bot") //View Channels, Send Messages, Embed Links, Use External Emojis, Use Slash Commands, Connect, Speak, Use Voice Activity
|
|
||||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
|
||||||
|
|
||||||
interaction.reply({
|
|
||||||
embeds: [embed],
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
32
src/client/commands/invite.ts
Normal file
32
src/client/commands/invite.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { ChatInputCommandInteraction, EmbedBuilder } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'invite',
|
||||||
|
description: 'Invite Bot',
|
||||||
|
category: 'info',
|
||||||
|
execute(interaction: ChatInputCommandInteraction, client: RadioClient) {
|
||||||
|
|
||||||
|
if(!client.user) return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.maintenance,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setTitle(client.messages.replace(client.messages.inviteTitle, {
|
||||||
|
"%client.user.username%": client.user.username
|
||||||
|
}))
|
||||||
|
.setColor(client.config.embedColor)
|
||||||
|
.setURL("https://discord.com/api/oauth2/authorize?client_id=" + client.user.id + "&permissions=2184465408&scope=applications.commands%20bot") //View Channels, Send Messages, Embed Links, Use External Emojis, Use Slash Commands, Connect, Speak, Use Voice Activity
|
||||||
|
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||||
|
.setFooter({
|
||||||
|
text: client.messages.footerText,
|
||||||
|
iconURL: "https://cdn.discordapp.com/emojis/" + client.messages.emojis["eximiabots"].replace(/[^0-9]+/g, '')
|
||||||
|
});
|
||||||
|
|
||||||
|
interaction.reply({
|
||||||
|
embeds: [embed],
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -1,81 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
name: 'list',
|
|
||||||
alias: 'l',
|
|
||||||
usage: '',
|
|
||||||
description: 'List radio stations',
|
|
||||||
permission: 'none',
|
|
||||||
category: 'radio',
|
|
||||||
execute(interaction, client, Discord, command) {
|
|
||||||
let message = {};
|
|
||||||
if(!client.stations) {
|
|
||||||
message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace("%client.config.supportGuild%", client.config.supportGuild);
|
|
||||||
return interaction.reply(client.messageEmojis["error"] + message.errorToGetPlaylist);
|
|
||||||
}
|
|
||||||
|
|
||||||
const radio = client.radio.get(interaction.guild.id);
|
|
||||||
|
|
||||||
if(radio){
|
|
||||||
let menu = [];
|
|
||||||
|
|
||||||
let stations = new Array();
|
|
||||||
|
|
||||||
let options = new Array();
|
|
||||||
options[1] = new Array();
|
|
||||||
options[2] = new Array();
|
|
||||||
|
|
||||||
stations[1] = client.stations.slice(0,24).forEach(station => {
|
|
||||||
station = {
|
|
||||||
label: station.name,
|
|
||||||
description: station.owner,
|
|
||||||
value: station.name
|
|
||||||
};
|
|
||||||
options[1].push(station);
|
|
||||||
});
|
|
||||||
|
|
||||||
stations[2] = client.stations.slice(25).forEach(station => {
|
|
||||||
station = {
|
|
||||||
label: station.name,
|
|
||||||
description: station.owner,
|
|
||||||
value: station.name
|
|
||||||
};
|
|
||||||
options[2].push(station);
|
|
||||||
});
|
|
||||||
|
|
||||||
menu = new Discord.MessageActionRow()
|
|
||||||
.addComponents(
|
|
||||||
new Discord.MessageSelectMenu()
|
|
||||||
.setCustomId('play')
|
|
||||||
.setPlaceholder('Change station')
|
|
||||||
.addOptions(options[1])
|
|
||||||
.addOptions(options[2])
|
|
||||||
);
|
|
||||||
|
|
||||||
stations = null;
|
|
||||||
options = null;
|
|
||||||
|
|
||||||
interaction.reply({
|
|
||||||
content: '**Select station:**',
|
|
||||||
components: [menu],
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
let stations = `${client.stations.map(s => `**#** ${s.name}`).join('\n')}`
|
|
||||||
const hashs = stations.split('**#**').length;
|
|
||||||
for (let i = 0; i < hashs; i++) {
|
|
||||||
stations = stations.replace('**#**', `**${i + 1}.**`);
|
|
||||||
}
|
|
||||||
|
|
||||||
let embed = new Discord.MessageEmbed()
|
|
||||||
.setTitle(client.messages.listTitle)
|
|
||||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["list"].replace(/[^0-9]+/g, ''))
|
|
||||||
.setColor(client.config.embedColor)
|
|
||||||
.setDescription(stations)
|
|
||||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
|
||||||
|
|
||||||
interaction.reply({
|
|
||||||
embeds: [embed],
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
55
src/client/commands/list.ts
Normal file
55
src/client/commands/list.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { ButtonInteraction, ChatInputCommandInteraction, EmbedBuilder, StringSelectMenuInteraction } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
import { station } from "../classes/Stations";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'list',
|
||||||
|
description: 'List radio stations',
|
||||||
|
category: 'radio',
|
||||||
|
execute(interaction: ButtonInteraction | ChatInputCommandInteraction | StringSelectMenuInteraction, client: RadioClient) {
|
||||||
|
|
||||||
|
if(client.config.maintenanceMode){
|
||||||
|
return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.maintenance,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!client.stations) {
|
||||||
|
return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.replace(client.messages.errorToGetPlaylist, {
|
||||||
|
"%client.config.supportGuild%": client.config.supportGuild
|
||||||
|
}),
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const radio = client.radio?.get(interaction.guild?.id);
|
||||||
|
|
||||||
|
if(radio && !client.config.maintenanceMode){
|
||||||
|
client.funcs.listStations(client, interaction);
|
||||||
|
} else {
|
||||||
|
let stations = `${client.stations.map((s: station) => `**#** ${s.name}`).join('\n')}`
|
||||||
|
const hashs = stations.split('**#**').length;
|
||||||
|
for (let i = 0; i < hashs; i++) {
|
||||||
|
stations = stations.replace('**#**', `**${i + 1}.**`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let embed = new EmbedBuilder()
|
||||||
|
.setTitle(client.messages.listTitle)
|
||||||
|
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messages.emojis["list"].replace(/[^0-9]+/g, ''))
|
||||||
|
.setColor(client.config.embedColor)
|
||||||
|
.setDescription(stations)
|
||||||
|
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||||
|
.setFooter({
|
||||||
|
text: client.messages.footerText,
|
||||||
|
iconURL: "https://cdn.discordapp.com/emojis/" + client.messages.emojis["eximiabots"].replace(/[^0-9]+/g, '')
|
||||||
|
});
|
||||||
|
|
||||||
|
interaction.reply({
|
||||||
|
embeds: [embed],
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -1,51 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
name: 'maintenance',
|
|
||||||
alias: 'm',
|
|
||||||
usage: '',
|
|
||||||
description: 'Bot Maintenance',
|
|
||||||
permission: 'none',
|
|
||||||
category: 'info',
|
|
||||||
execute(interaction, client, Discord, command) {
|
|
||||||
let message = {};
|
|
||||||
|
|
||||||
if(!client.funcs.isDev(client.config.devId, interaction.user.id)) return interaction.reply(client.messageEmojis["error"] + client.messages.notAllowed);
|
|
||||||
|
|
||||||
if(!client.stations) {
|
|
||||||
message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace("%client.config.supportGuild%", client.config.supportGuild);
|
|
||||||
return interaction.reply(client.messageEmojis["error"] + message.errorToGetPlaylist);
|
|
||||||
}
|
|
||||||
|
|
||||||
let currentRadios = client.radio.keys();
|
|
||||||
let radio = currentRadios.next();
|
|
||||||
let stoppedRadios = "";
|
|
||||||
|
|
||||||
client.user.setStatus('dnd');
|
|
||||||
|
|
||||||
while (!radio.done) {
|
|
||||||
let currentRadio = client.radio.get(radio.value);
|
|
||||||
currentRadio.guild = client.datastore.getEntry(radio.value).guild;
|
|
||||||
|
|
||||||
if(currentRadio){
|
|
||||||
client.funcs.statisticsUpdate(client, currentRadio.guild, currentRadio);
|
|
||||||
currentRadio.connection?.destroy();
|
|
||||||
currentRadio.audioPlayer?.stop();
|
|
||||||
currentRadio.message?.delete();
|
|
||||||
client.radio.delete(radio.value);
|
|
||||||
stoppedRadios += "-" + radio.value + ": " + currentRadio.guild.name + "\n";
|
|
||||||
}
|
|
||||||
radio = currentRadios.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
const embed = new Discord.MessageEmbed()
|
|
||||||
.setTitle(client.messages.maintenanceTitle)
|
|
||||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["maintenance"].replace(/[^0-9]+/g, ''))
|
|
||||||
.setColor(client.config.embedColor)
|
|
||||||
.setDescription("Stopped all radios" + "\n" + stoppedRadios)
|
|
||||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
|
||||||
|
|
||||||
interaction.reply({
|
|
||||||
embeds: [embed],
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
232
src/client/commands/maintenance.ts
Normal file
232
src/client/commands/maintenance.ts
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
import { ActionRowBuilder, APISelectMenuOption, ButtonInteraction, ChatInputCommandInteraction, EmbedBuilder, StringSelectMenuBuilder, StringSelectMenuInteraction } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
import Streamer from "../classes/Streamer";
|
||||||
|
import commands from "../commands";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'maintenance',
|
||||||
|
description: 'Bot Maintenance',
|
||||||
|
category: 'info',
|
||||||
|
async execute(interaction: ButtonInteraction | ChatInputCommandInteraction | StringSelectMenuInteraction, client: RadioClient) {
|
||||||
|
|
||||||
|
if(!client.funcs.isDev(client.config.devIDs, interaction.user.id)) return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.notAllowed,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
|
||||||
|
let action : number | string | null = null;
|
||||||
|
|
||||||
|
if(interaction.isChatInputCommand()){
|
||||||
|
action = interaction.options?.getNumber("action");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(interaction.isStringSelectMenu()){
|
||||||
|
action = interaction.values?.[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const options: APISelectMenuOption[] = new Array(
|
||||||
|
{
|
||||||
|
emoji: {
|
||||||
|
"name": "🌀",
|
||||||
|
},
|
||||||
|
label: "Restart Bot",
|
||||||
|
value: "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
emoji: {
|
||||||
|
id: "688541155377414168",
|
||||||
|
name: "RadioXStop",
|
||||||
|
},
|
||||||
|
label: "Save Radios",
|
||||||
|
value: "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
emoji: {
|
||||||
|
id: "688541155712827458",
|
||||||
|
name: "RadioXPlay",
|
||||||
|
},
|
||||||
|
label: "Restore Radios",
|
||||||
|
value: "5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
emoji: {
|
||||||
|
name: "#️⃣",
|
||||||
|
},
|
||||||
|
label: "Reload Commands",
|
||||||
|
value: "6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
emoji: {
|
||||||
|
id: "688541155519889482",
|
||||||
|
name: "RadioXList",
|
||||||
|
},
|
||||||
|
label: "Reload Stations",
|
||||||
|
value: "7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
emoji: {
|
||||||
|
id: "746069698139127831",
|
||||||
|
name: "dnd",
|
||||||
|
},
|
||||||
|
label: "Enable Maintenance Mode",
|
||||||
|
value: "8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
emoji: {
|
||||||
|
id: "746069731836035098",
|
||||||
|
name: "online",
|
||||||
|
},
|
||||||
|
label: "Disable Maintenance Mode",
|
||||||
|
value: "9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
emoji: {
|
||||||
|
name: "💤",
|
||||||
|
},
|
||||||
|
label: "Streamer Mode - Manual",
|
||||||
|
value: "10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
emoji: {
|
||||||
|
name: "📡",
|
||||||
|
},
|
||||||
|
label: "Streamer Mode - Auto",
|
||||||
|
value: "11"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const menu = new ActionRowBuilder<StringSelectMenuBuilder>()
|
||||||
|
.addComponents(
|
||||||
|
new StringSelectMenuBuilder()
|
||||||
|
.setCustomId('maintenance')
|
||||||
|
.setPlaceholder('Select action')
|
||||||
|
.addOptions(options)
|
||||||
|
);
|
||||||
|
|
||||||
|
if(!action){
|
||||||
|
return interaction.reply({
|
||||||
|
content: "**" + client.messages.maintenanceTitle + "**",
|
||||||
|
components: [menu],
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
client.funcs.logger('Maintenance', options.find((option: APISelectMenuOption) => option.value == action)?.label);
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setTitle(client.messages.maintenanceTitle)
|
||||||
|
.setColor(client.config.embedColor)
|
||||||
|
.setDescription(options.find((option: APISelectMenuOption) => option.value == action)?.label || "-")
|
||||||
|
.setFooter({
|
||||||
|
text: client.messages.footerText,
|
||||||
|
iconURL: "https://cdn.discordapp.com/emojis/" + client.messages.emojis["eximiabots"].replace(/[^0-9]+/g, '')
|
||||||
|
});
|
||||||
|
|
||||||
|
interaction.reply({
|
||||||
|
embeds: [embed],
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
|
||||||
|
let guilds = await client.guilds.fetch();
|
||||||
|
|
||||||
|
switch(action){
|
||||||
|
case "0":
|
||||||
|
client.config.maintenanceMode = true;
|
||||||
|
process.emit('SIGINT');
|
||||||
|
break;
|
||||||
|
case "4":
|
||||||
|
client.config.maintenanceMode = true;
|
||||||
|
client.user?.setStatus('idle');
|
||||||
|
client.radio?.save(client);
|
||||||
|
client.user?.setStatus('online');
|
||||||
|
client.config.maintenanceMode = false;
|
||||||
|
break;
|
||||||
|
case "5":
|
||||||
|
client.config.maintenanceMode = true;
|
||||||
|
client.user?.setStatus('idle');
|
||||||
|
client.radio?.restore(client, guilds);
|
||||||
|
client.user?.setStatus('online');
|
||||||
|
client.config.maintenanceMode = false;
|
||||||
|
break;
|
||||||
|
case "6":
|
||||||
|
client.config.maintenanceMode = true;
|
||||||
|
client.user?.setStatus('idle');
|
||||||
|
commands(client);
|
||||||
|
client.user?.setStatus('online');
|
||||||
|
client.config.maintenanceMode = false;
|
||||||
|
break;
|
||||||
|
case "7":
|
||||||
|
try {
|
||||||
|
client.stations?.fetch({
|
||||||
|
url: client.config.stationslistUrl
|
||||||
|
});
|
||||||
|
client.streamer?.refresh(client);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "8":
|
||||||
|
client.user?.setStatus('dnd');
|
||||||
|
client.funcs.logger("Maintenance Mode", "Enabled");
|
||||||
|
client.config.maintenanceMode = true;
|
||||||
|
break;
|
||||||
|
case "9":
|
||||||
|
client.user?.setStatus('online');
|
||||||
|
client.funcs.logger("Maintenance Mode", "Disabled");
|
||||||
|
client.config.maintenanceMode = false;
|
||||||
|
break;
|
||||||
|
case "10":
|
||||||
|
client.config.streamerMode = "manual";
|
||||||
|
client.config.maintenanceMode = true;
|
||||||
|
|
||||||
|
client.user?.setStatus('idle');
|
||||||
|
client.radio?.save(client);
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
if(client.radio?.size == 0 && client.config.streamerMode == "manual" && client.config.maintenanceMode){
|
||||||
|
client.streamer?.leave(client);
|
||||||
|
client.streamer = new Streamer();
|
||||||
|
client.streamer.init(client);
|
||||||
|
|
||||||
|
client.radio?.restore(client, guilds);
|
||||||
|
client.user?.setStatus('online');
|
||||||
|
client.config.maintenanceMode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!client.config.maintenanceMode){
|
||||||
|
clearInterval(undefined);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "11":
|
||||||
|
client.config.streamerMode = "auto";
|
||||||
|
client.config.maintenanceMode = true;
|
||||||
|
|
||||||
|
client.user?.setStatus('idle');
|
||||||
|
client.radio?.save(client);
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
if(client.radio?.size == 0 && client.config.streamerMode == "auto" && client.config.maintenanceMode){
|
||||||
|
client.streamer?.leave(client);
|
||||||
|
client.streamer = new Streamer();
|
||||||
|
client.streamer.init(client);
|
||||||
|
|
||||||
|
client.radio.restore(client, guilds);
|
||||||
|
client.user?.setStatus('online');
|
||||||
|
client.config.maintenanceMode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!client.config.maintenanceMode){
|
||||||
|
clearInterval(undefined);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
56
src/client/commands/next.ts
Normal file
56
src/client/commands/next.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { ButtonInteraction, ChatInputCommandInteraction, StringSelectMenuInteraction } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
import { station } from "../classes/Stations"
|
||||||
|
import { command } from "../commands";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'next',
|
||||||
|
description: 'Next Station',
|
||||||
|
category: 'radio',
|
||||||
|
async execute(interaction: ButtonInteraction | ChatInputCommandInteraction | StringSelectMenuInteraction, client: RadioClient, command: command) {
|
||||||
|
if (client.funcs.check(client, interaction, command)) {
|
||||||
|
const radio = client.radio?.get(interaction.guild?.id);
|
||||||
|
|
||||||
|
if(client.config.maintenanceMode){
|
||||||
|
return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.maintenance,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!client.stations) {
|
||||||
|
return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.replace(client.messages.errorToGetPlaylist, {
|
||||||
|
"%client.config.supportGuild%": client.config.supportGuild
|
||||||
|
}),
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let index: number = client.stations.findIndex((station: station) => station.name == radio.station.name) + 1;
|
||||||
|
if(index == client.stations?.length) index = 0;
|
||||||
|
|
||||||
|
let station = client.stations[index];
|
||||||
|
|
||||||
|
if(!station) return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.noSearchResults,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
|
||||||
|
client.statistics?.update(client, interaction.guild, radio);
|
||||||
|
|
||||||
|
let date = new Date();
|
||||||
|
radio.station = station;
|
||||||
|
radio.textChannel = interaction.channel;
|
||||||
|
radio.startTime = date.getTime();
|
||||||
|
|
||||||
|
if(interaction.isChatInputCommand()) {
|
||||||
|
client.funcs.play(client, interaction, interaction.guild, station);
|
||||||
|
}
|
||||||
|
if(interaction.isButton()) {
|
||||||
|
interaction.deferUpdate();
|
||||||
|
client.funcs.play(client, null, interaction.guild, station);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,38 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
name: 'nowplaying',
|
|
||||||
alias: 'np',
|
|
||||||
usage: '',
|
|
||||||
description: 'Current Radio Station',
|
|
||||||
permission: 'none',
|
|
||||||
category: 'radio',
|
|
||||||
async execute(interaction, client, Discord, command) {
|
|
||||||
let message = {};
|
|
||||||
const radio = client.radio.get(interaction.guild.id);
|
|
||||||
if (!radio) return interaction.reply('There is nothing playing.');
|
|
||||||
if(!client.stations) {
|
|
||||||
message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace("%client.config.supportGuild%", client.config.supportGuild);
|
|
||||||
return interaction.reply(client.messageEmojis["error"] + message.errorToGetPlaylist);
|
|
||||||
}
|
|
||||||
|
|
||||||
let date = new Date();
|
|
||||||
radio.currentTime = date.getTime();
|
|
||||||
radio.playTime = parseInt(radio.currentTime)-parseInt(radio.startTime);
|
|
||||||
const completed = (radio.playTime);
|
|
||||||
|
|
||||||
message.nowplayingDescription = client.messages.nowplayingDescription.replace("%radio.station.name%", radio.station.name);
|
|
||||||
message.nowplayingDescription = message.nowplayingDescription.replace("%radio.station.owner%", radio.station.owner);
|
|
||||||
message.nowplayingDescription = message.nowplayingDescription.replace("%client.funcs.msToTime(completed)%", client.funcs.msToTime(completed));
|
|
||||||
|
|
||||||
const embed = new Discord.MessageEmbed()
|
|
||||||
.setTitle(client.messages.nowplayingTitle)
|
|
||||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["play"].replace(/[^0-9]+/g, ''))
|
|
||||||
.setColor(client.config.embedColor)
|
|
||||||
.setDescription(message.nowplayingDescription)
|
|
||||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
|
||||||
|
|
||||||
interaction.reply({
|
|
||||||
embeds: [embed],
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
40
src/client/commands/nowplaying.ts
Normal file
40
src/client/commands/nowplaying.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { ButtonInteraction, ChatInputCommandInteraction, EmbedBuilder, StringSelectMenuInteraction } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
import { command } from "../commands";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'nowplaying',
|
||||||
|
description: 'Current Radio Station',
|
||||||
|
category: 'radio',
|
||||||
|
async execute(interaction: ButtonInteraction | ChatInputCommandInteraction | StringSelectMenuInteraction, client: RadioClient, command: command) {
|
||||||
|
if(client.funcs.check(client, interaction, command)) {
|
||||||
|
|
||||||
|
const radio = client.radio?.get(interaction.guild?.id);
|
||||||
|
|
||||||
|
let date = new Date();
|
||||||
|
radio.currentTime = date.getTime();
|
||||||
|
radio.playTime = parseInt(radio.currentTime)-parseInt(radio.startTime);
|
||||||
|
const completed = (radio.playTime);
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setTitle(client.messages.nowplayingTitle)
|
||||||
|
.setThumbnail((radio.station.logo || "https://cdn.discordapp.com/emojis/" + client.messages.emojis["play"].replace(/[^0-9]+/g, '')))
|
||||||
|
.setColor(client.config.embedColor)
|
||||||
|
.setDescription(client.messages.replace(client.messages.nowplayingDescription, {
|
||||||
|
"%radio.station.name%": radio.station.name,
|
||||||
|
"%radio.station.owner%\n": radio.station.name != radio.station.owner ? radio.station.owner + "\n" : "",
|
||||||
|
"%client.funcs.msToTime(completed)%": client.funcs.msToTime(completed)
|
||||||
|
}))
|
||||||
|
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||||
|
.setFooter({
|
||||||
|
text: client.messages.footerText,
|
||||||
|
iconURL: "https://cdn.discordapp.com/emojis/" + client.messages.emojis["eximiabots"].replace(/[^0-9]+/g, '')
|
||||||
|
});
|
||||||
|
|
||||||
|
interaction.reply({
|
||||||
|
embeds: [embed],
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -1,340 +0,0 @@
|
|||||||
const {
|
|
||||||
createAudioPlayer,
|
|
||||||
createAudioResource,
|
|
||||||
getVoiceConnection,
|
|
||||||
joinVoiceChannel
|
|
||||||
} = require("@discordjs/voice");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
name: "play",
|
|
||||||
alias: "p",
|
|
||||||
usage: "<song name>",
|
|
||||||
description: "Play radio",
|
|
||||||
options: [
|
|
||||||
{ type: "STRING", name: "query", description: "Select station", required: false}
|
|
||||||
],
|
|
||||||
permission: "none",
|
|
||||||
category: "radio",
|
|
||||||
async execute(interaction, client, Discord, command) {
|
|
||||||
let message = {};
|
|
||||||
let query = interaction.options?.getString("query") ?? interaction.values?.[0];
|
|
||||||
if(!query){
|
|
||||||
if(!client.stations) {
|
|
||||||
message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace("%client.config.supportGuild%", client.config.supportGuild);
|
|
||||||
return interaction.reply(client.messageEmojis["error"] + message.errorToGetPlaylist);
|
|
||||||
}
|
|
||||||
|
|
||||||
let stations = new Array();
|
|
||||||
|
|
||||||
let options = new Array();
|
|
||||||
options[1] = new Array();
|
|
||||||
options[2] = new Array();
|
|
||||||
|
|
||||||
stations[1] = client.stations.slice(0,24).forEach(station => {
|
|
||||||
station = {
|
|
||||||
label: station.name,
|
|
||||||
description: station.owner,
|
|
||||||
value: station.name
|
|
||||||
};
|
|
||||||
options[1].push(station);
|
|
||||||
});
|
|
||||||
|
|
||||||
stations[2] = client.stations.slice(25).forEach(station => {
|
|
||||||
station = {
|
|
||||||
label: station.name,
|
|
||||||
description: station.owner,
|
|
||||||
value: station.name
|
|
||||||
};
|
|
||||||
options[2].push(station);
|
|
||||||
});
|
|
||||||
|
|
||||||
const menu = new Discord.MessageActionRow()
|
|
||||||
.addComponents(
|
|
||||||
new Discord.MessageSelectMenu()
|
|
||||||
.setCustomId('play')
|
|
||||||
.setPlaceholder('Nothing selected')
|
|
||||||
.addOptions(options[1])
|
|
||||||
.addOptions(options[2])
|
|
||||||
);
|
|
||||||
|
|
||||||
stations = null;
|
|
||||||
options = null;
|
|
||||||
|
|
||||||
return interaction.reply({
|
|
||||||
content: '**Select station:**',
|
|
||||||
components: [menu],
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let url = query ? query.replace(/<(.+)>/g, "$1") : "";
|
|
||||||
const radio = client.radio.get(interaction.guild.id);
|
|
||||||
const voiceChannel = interaction.member.voice.channel;
|
|
||||||
if (!radio) {
|
|
||||||
if (!interaction.member.voice.channel)
|
|
||||||
return interaction.reply({
|
|
||||||
content: client.messageEmojis["error"] + client.messages.noVoiceChannel,
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (voiceChannel !== radio.voiceChannel)
|
|
||||||
return interaction.reply({
|
|
||||||
content: client.messageEmojis["error"] + client.messages.wrongVoiceChannel,
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!client.stations) {
|
|
||||||
message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace(
|
|
||||||
"%client.config.supportGuild%",
|
|
||||||
client.config.supportGuild
|
|
||||||
);
|
|
||||||
return interaction.reply({
|
|
||||||
content: client.messageEmojis["error"] + message.errorToGetPlaylist,
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!query) return interaction.reply(client.messages.noQuery);
|
|
||||||
const permissions = voiceChannel.permissionsFor(interaction.client.user);
|
|
||||||
if (!permissions.has("CONNECT")) {
|
|
||||||
return interaction.reply(client.messageEmojis["error"] + client.messages.noPermsConnect);
|
|
||||||
}
|
|
||||||
if (!permissions.has("SPEAK")) {
|
|
||||||
return interaction.reply(client.messageEmojis["error"] + client.messages.noPermsSpeak);
|
|
||||||
}
|
|
||||||
let station;
|
|
||||||
const number = parseInt(query - 1);
|
|
||||||
if (url.startsWith("http")) {
|
|
||||||
return interaction.reply({
|
|
||||||
content: client.messageEmojis["error"] + client.messages.errorStationURL,
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
} else if (!isNaN(number)) {
|
|
||||||
if (number > client.stations.length - 1) {
|
|
||||||
return interaction.reply({
|
|
||||||
content: client.messageEmojis["error"] + client.messages.wrongStationNumber,
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
url = client.stations[number].stream[client.stations[number].stream.default];
|
|
||||||
station = client.stations[number];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (query.length < 3)
|
|
||||||
return interaction.reply({
|
|
||||||
content: client.messageEmojis["error"] + client.messages.tooShortSearch,
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
const sstation = await searchStation(query, client);
|
|
||||||
if (!sstation)
|
|
||||||
return interaction.reply({
|
|
||||||
content: client.messageEmojis["error"] + client.messages.noSearchResults,
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
url = sstation.stream[sstation.stream.default];
|
|
||||||
station = sstation;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (radio) {
|
|
||||||
client.funcs.statisticsUpdate(client, interaction.guild, radio);
|
|
||||||
radio.audioPlayer.stop();
|
|
||||||
|
|
||||||
let date = new Date();
|
|
||||||
radio.station = station;
|
|
||||||
radio.textChannel = interaction.channel;
|
|
||||||
radio.startTime = date.getTime();
|
|
||||||
play(interaction, interaction.guild, client, url, Discord);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const construct = {
|
|
||||||
textChannel: interaction.channel,
|
|
||||||
voiceChannel: voiceChannel,
|
|
||||||
connection: null,
|
|
||||||
message: null,
|
|
||||||
audioPlayer: createAudioPlayer(),
|
|
||||||
station: station
|
|
||||||
};
|
|
||||||
client.radio.set(interaction.guild.id, construct);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const connection =
|
|
||||||
getVoiceConnection(voiceChannel.guild.id) ??
|
|
||||||
joinVoiceChannel({
|
|
||||||
channelId: voiceChannel.id,
|
|
||||||
guildId: voiceChannel.guild.id,
|
|
||||||
adapterCreator: voiceChannel.guild.voiceAdapterCreator
|
|
||||||
});
|
|
||||||
construct.connection = connection;
|
|
||||||
let date = new Date();
|
|
||||||
construct.startTime = date.getTime();
|
|
||||||
play(interaction, interaction.guild, client, url, Discord);
|
|
||||||
|
|
||||||
client.datastore.checkEntry(interaction.guild.id);
|
|
||||||
construct.currentGuild = client.datastore.getEntry(interaction.guild.id);
|
|
||||||
|
|
||||||
if (!construct.currentGuild.statistics[construct.station.name]) {
|
|
||||||
construct.currentGuild.statistics[construct.station.name] = {};
|
|
||||||
construct.currentGuild.statistics[construct.station.name].time = 0;
|
|
||||||
construct.currentGuild.statistics[construct.station.name].used = 0;
|
|
||||||
client.datastore.updateEntry(interaction.guild, construct.currentGuild);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
client.radio.delete(interaction.guild.id);
|
|
||||||
return interaction.reply(client.messageEmojis["error"] + `An error occured: ${error}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async function play(interaction, guild, client, url, Discord) {
|
|
||||||
let message = {};
|
|
||||||
const radio = client.radio.get(guild.id);
|
|
||||||
const resource = createAudioResource(url);
|
|
||||||
radio.connection.subscribe(radio.audioPlayer);
|
|
||||||
radio.audioPlayer.play(resource);
|
|
||||||
resource.playStream
|
|
||||||
.on("readable", () => {
|
|
||||||
client.funcs.logger('Radio', 'Stream started' + " / " + guild.id + " / " + radio.station.name);
|
|
||||||
})
|
|
||||||
.on("finish", () => {
|
|
||||||
client.funcs.logger('Radio', 'Stream finished' + " / " + guild.id);
|
|
||||||
client.funcs.statisticsUpdate(client, guild, radio);
|
|
||||||
radio.connection?.destroy();
|
|
||||||
radio.audioPlayer?.stop();
|
|
||||||
client.radio.delete(guild.id);
|
|
||||||
return;
|
|
||||||
})
|
|
||||||
.on("error", error => {
|
|
||||||
client.funcs.logger('Radio', 'Stream errored');
|
|
||||||
console.error(error);
|
|
||||||
radio.connection?.destroy();
|
|
||||||
radio.audioPlayer?.stop();
|
|
||||||
client.radio.delete(guild.id);
|
|
||||||
return interaction.reply(client.messages.errorPlaying);
|
|
||||||
});
|
|
||||||
|
|
||||||
message.nowplayingDescription = client.messages.nowplayingDescription.replace("%radio.station.name%", radio.station.name);
|
|
||||||
message.nowplayingDescription = message.nowplayingDescription.replace("%radio.station.owner%", radio.station.owner);
|
|
||||||
message.nowplayingDescription = message.nowplayingDescription.replace("%client.funcs.msToTime(completed)%", "");
|
|
||||||
message.nowplayingDescription = message.nowplayingDescription.replace("Owner: ", "");
|
|
||||||
message.nowplayingDescription = message.nowplayingDescription.replace("**", "");
|
|
||||||
message.nowplayingDescription = message.nowplayingDescription.replace("**", "");
|
|
||||||
|
|
||||||
const embed = new Discord.MessageEmbed()
|
|
||||||
.setTitle(client.user.username)
|
|
||||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["play"].replace(/[^0-9]+/g, ''))
|
|
||||||
.setColor(client.config.embedColor)
|
|
||||||
.addField(client.messages.nowplayingTitle, message.nowplayingDescription, true)
|
|
||||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
|
||||||
|
|
||||||
const buttons = new Discord.MessageActionRow()
|
|
||||||
.addComponents(
|
|
||||||
new Discord.MessageButton()
|
|
||||||
.setCustomId('list')
|
|
||||||
.setEmoji(client.messageEmojis["list"])
|
|
||||||
.setStyle('SECONDARY')
|
|
||||||
)
|
|
||||||
.addComponents(
|
|
||||||
new Discord.MessageButton()
|
|
||||||
.setCustomId('prev')
|
|
||||||
.setEmoji(client.messageEmojis["prev"])
|
|
||||||
.setStyle('SECONDARY')
|
|
||||||
.setDisabled(true)
|
|
||||||
)
|
|
||||||
.addComponents(
|
|
||||||
new Discord.MessageButton()
|
|
||||||
.setCustomId('stop')
|
|
||||||
.setEmoji(client.messageEmojis["stop"])
|
|
||||||
.setStyle('SECONDARY')
|
|
||||||
)
|
|
||||||
.addComponents(
|
|
||||||
new Discord.MessageButton()
|
|
||||||
.setCustomId('next')
|
|
||||||
.setEmoji(client.messageEmojis["next"])
|
|
||||||
.setStyle('SECONDARY')
|
|
||||||
.setDisabled(true)
|
|
||||||
)
|
|
||||||
.addComponents(
|
|
||||||
new Discord.MessageButton()
|
|
||||||
.setCustomId('statistics')
|
|
||||||
.setEmoji(client.messageEmojis["statistics"])
|
|
||||||
.setStyle('SECONDARY')
|
|
||||||
);
|
|
||||||
|
|
||||||
if(!radio.message){
|
|
||||||
radio.message = await radio.textChannel.send({ embeds: [embed], components: [buttons] });
|
|
||||||
} else {
|
|
||||||
radio.message.edit({ embeds: [embed], components: [buttons] });
|
|
||||||
}
|
|
||||||
|
|
||||||
message.play = client.messages.play.replace("%radio.station.name%", radio.station.name);
|
|
||||||
|
|
||||||
interaction.reply({
|
|
||||||
content: client.messageEmojis["play"] + message.play,
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function searchStation(key, client) {
|
|
||||||
if (client.stations === null) return false;
|
|
||||||
let foundStations = [];
|
|
||||||
if (!key) return false;
|
|
||||||
if (key == "radio") return false;
|
|
||||||
|
|
||||||
client.stations
|
|
||||||
.filter(
|
|
||||||
x => x.name.toUpperCase().includes(key.toUpperCase()) || x === key
|
|
||||||
)
|
|
||||||
.forEach(x =>
|
|
||||||
foundStations.push({ station: x, name: x.name, probability: 100 })
|
|
||||||
);
|
|
||||||
|
|
||||||
if (key.startsWith("radio ")) key = key.slice(6);
|
|
||||||
const probabilityIncrement = 100 / key.split(" ").length / 2;
|
|
||||||
for (let i = 0; i < key.split(" ").length; i++) {
|
|
||||||
client.stations
|
|
||||||
.filter(
|
|
||||||
x => x.name.toUpperCase().includes(key.split(" ")[i].toUpperCase()) || x === key
|
|
||||||
)
|
|
||||||
.forEach(x =>
|
|
||||||
foundStations.push({ station: x, name: x.name, probability: probabilityIncrement })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (foundStations.length === 0) return false;
|
|
||||||
for (let i = 0; i < foundStations.length; i++) {
|
|
||||||
for (let j = 0; j < foundStations.length; j++) {
|
|
||||||
if (foundStations[i] === foundStations[j] && i !== j) foundStations.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let i = 0; i < foundStations.length; i++) {
|
|
||||||
if (foundStations[i].name.length > key.length) {
|
|
||||||
foundStations[i].probability -=
|
|
||||||
(foundStations[i].name.split(" ").length - key.split(" ").length) *
|
|
||||||
(probabilityIncrement * 0.5);
|
|
||||||
} else if (foundStations[i].name.length === key.length) {
|
|
||||||
foundStations[i].probability += probabilityIncrement * 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let j = 0; j < key.split(" ").length; j++) {
|
|
||||||
if (!foundStations[i].name.toUpperCase().includes(key.toUpperCase().split(" ")[j])) {
|
|
||||||
foundStations[i].probability -= probabilityIncrement * 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let highestProbabilityStation;
|
|
||||||
for (let i = 0; i < foundStations.length; i++) {
|
|
||||||
if (
|
|
||||||
!highestProbabilityStation ||
|
|
||||||
highestProbabilityStation.probability < foundStations[i].probability
|
|
||||||
)
|
|
||||||
highestProbabilityStation = foundStations[i];
|
|
||||||
if (
|
|
||||||
highestProbabilityStation &&
|
|
||||||
highestProbabilityStation.probability === foundStations[i].probability
|
|
||||||
) {
|
|
||||||
highestProbabilityStation = foundStations[i].station;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return highestProbabilityStation;
|
|
||||||
}
|
|
159
src/client/commands/play.ts
Normal file
159
src/client/commands/play.ts
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
import { ApplicationCommandOptionType, ChatInputCommandInteraction, GuildMember, PermissionFlagsBits, StringSelectMenuInteraction } from "discord.js";
|
||||||
|
import { getVoiceConnection, joinVoiceChannel } from "@discordjs/voice";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
import { radio } from "../classes/Radio"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "play",
|
||||||
|
usage: "<song name>",
|
||||||
|
description: "Play radio",
|
||||||
|
options: [
|
||||||
|
{ type: ApplicationCommandOptionType.String, name: "query", description: "Select station", required: false}
|
||||||
|
],
|
||||||
|
category: "radio",
|
||||||
|
async execute(interaction: ChatInputCommandInteraction | StringSelectMenuInteraction, client: RadioClient) {
|
||||||
|
|
||||||
|
if(client.config.maintenanceMode){
|
||||||
|
return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.maintenance,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!client.stations) {
|
||||||
|
return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.replace(client.messages.errorToGetPlaylist, {
|
||||||
|
"%client.config.supportGuild%": client.config.supportGuild
|
||||||
|
}),
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let query: string | null = null;
|
||||||
|
|
||||||
|
if(interaction.isChatInputCommand()){
|
||||||
|
query = interaction.options?.getString("query");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(interaction.isStringSelectMenu()){
|
||||||
|
query = interaction.values?.[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!query){
|
||||||
|
return client.funcs.listStations(client, interaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
const radio = client.radio?.get(interaction.guild?.id);
|
||||||
|
|
||||||
|
if(!(interaction.member instanceof GuildMember)) return;
|
||||||
|
const voiceChannel = interaction.member?.voice.channel;
|
||||||
|
|
||||||
|
if (!voiceChannel) return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.noVoiceChannel,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (radio) {
|
||||||
|
if (voiceChannel !== radio.voiceChannel) return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.wrongVoiceChannel,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!query) return interaction.reply({
|
||||||
|
content: client.messages.noQuery,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const permissions = voiceChannel.permissionsFor(interaction.client.user);
|
||||||
|
if (!permissions?.has(PermissionFlagsBits.Connect)) {
|
||||||
|
return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.noPermsConnect,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!permissions?.has(PermissionFlagsBits.Speak)) {
|
||||||
|
return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.noPermsSpeak,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let station;
|
||||||
|
|
||||||
|
if(!isNaN(parseInt(query) - 1)){
|
||||||
|
let number = parseInt(query) - 1;
|
||||||
|
if(number > client.stations.length - 1) {
|
||||||
|
return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.wrongStationNumber,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
station = client.stations[number];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if(query.length < 3) return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.tooShortSearch,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
|
||||||
|
let type = "text";
|
||||||
|
|
||||||
|
if(interaction.isStringSelectMenu() && interaction.values?.[0]){
|
||||||
|
type = "direct";
|
||||||
|
}
|
||||||
|
|
||||||
|
const sstation = client.stations.search(query, type);
|
||||||
|
if (!sstation) return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.noSearchResults,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
station = sstation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (radio) {
|
||||||
|
client.statistics?.update(client, interaction.guild, radio);
|
||||||
|
|
||||||
|
let date = new Date();
|
||||||
|
radio.station = station;
|
||||||
|
radio.textChannel = interaction.channel;
|
||||||
|
radio.startTime = date.getTime();
|
||||||
|
client.funcs.play(client, interaction, interaction.guild, station);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let date = new Date();
|
||||||
|
const construct: radio = {
|
||||||
|
textChannel: interaction.channel,
|
||||||
|
voiceChannel: voiceChannel,
|
||||||
|
connection: null,
|
||||||
|
message: null,
|
||||||
|
station: station,
|
||||||
|
startTime: date.getTime()
|
||||||
|
};
|
||||||
|
client.radio?.set(interaction.guild?.id, construct);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const connection =
|
||||||
|
getVoiceConnection(voiceChannel.guild.id) ??
|
||||||
|
joinVoiceChannel({
|
||||||
|
channelId: voiceChannel.id,
|
||||||
|
guildId: voiceChannel.guild.id,
|
||||||
|
adapterCreator: voiceChannel.guild.voiceAdapterCreator
|
||||||
|
});
|
||||||
|
construct.connection = connection;
|
||||||
|
let date = new Date();
|
||||||
|
construct.startTime = date.getTime();
|
||||||
|
client.datastore?.checkEntry(interaction.guild?.id);
|
||||||
|
client.funcs.play(client, interaction, interaction.guild, station);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
client.radio?.delete(interaction.guild?.id);
|
||||||
|
return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + `An error occured: ${error}`,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
57
src/client/commands/prev.ts
Normal file
57
src/client/commands/prev.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { ButtonInteraction, ChatInputCommandInteraction, StringSelectMenuInteraction } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
import { command } from "../commands";
|
||||||
|
import { station } from "../classes/Stations"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'prev',
|
||||||
|
description: 'Previous Station',
|
||||||
|
category: 'radio',
|
||||||
|
async execute(interaction: ButtonInteraction | ChatInputCommandInteraction | StringSelectMenuInteraction, client: RadioClient, command: command) {
|
||||||
|
if (client.funcs.check(client, interaction, command)) {
|
||||||
|
const radio = client.radio?.get(interaction.guild?.id);
|
||||||
|
|
||||||
|
if(client.config.maintenanceMode){
|
||||||
|
return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.maintenance,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!client.stations) {
|
||||||
|
return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.replace(client.messages.errorToGetPlaylist, {
|
||||||
|
"%client.config.supportGuild%": client.config.supportGuild
|
||||||
|
}),
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let index = client.stations.findIndex((station: station) => station.name == radio.station.name) - 1;
|
||||||
|
if(index == -1) index = client.stations.length - 1;
|
||||||
|
|
||||||
|
let station = client.stations[index];
|
||||||
|
|
||||||
|
if(!station) return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.noSearchResults,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
|
||||||
|
client.statistics?.update(client, interaction.guild, radio);
|
||||||
|
|
||||||
|
let date = new Date();
|
||||||
|
radio.station = station;
|
||||||
|
radio.textChannel = interaction.channel;
|
||||||
|
radio.startTime = date.getTime();
|
||||||
|
|
||||||
|
if(interaction.isChatInputCommand()) {
|
||||||
|
client.funcs.play(client, interaction, interaction.guild, station);
|
||||||
|
}
|
||||||
|
if(interaction.isButton()) {
|
||||||
|
interaction.deferUpdate();
|
||||||
|
client.funcs.play(client, null, interaction.guild, station);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,43 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
name: 'statistics',
|
|
||||||
alias: 'stats',
|
|
||||||
usage: '',
|
|
||||||
description: 'Show statistics',
|
|
||||||
permission: 'none',
|
|
||||||
category: 'info',
|
|
||||||
execute(interaction, client, Discord, command) {
|
|
||||||
let message = {};
|
|
||||||
let stations = client.stations;
|
|
||||||
let currentGuild = client.datastore.getEntry(interaction.guild.id);
|
|
||||||
let statistics = "";
|
|
||||||
|
|
||||||
if(!client.stations) {
|
|
||||||
message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace("%client.config.supportGuild%", client.config.supportGuild);
|
|
||||||
return interaction.reply(client.messageEmojis["error"] + message.errorToGetPlaylist);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!currentGuild || currentGuild && !currentGuild.statistics){
|
|
||||||
statistics = "You have not listened any radio station";
|
|
||||||
} else {
|
|
||||||
Object.keys(stations).forEach(function(station) {
|
|
||||||
if(currentGuild.statistics[stations[station].name] && currentGuild.statistics[stations[station].name].time && parseInt(currentGuild.statistics[stations[station].name].time) > 0 && currentGuild.statistics[stations[station].name].used && parseInt(currentGuild.statistics[stations[station].name].used) > 0){
|
|
||||||
statistics += `**${parseInt(station) + 1}** ` + stations[station].name + " \n";
|
|
||||||
statistics += "Time: " + client.funcs.msToTime(currentGuild.statistics[stations[station].name].time) + "\n";
|
|
||||||
statistics += "Used: " + currentGuild.statistics[stations[station].name].used + "\n";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const embed = new Discord.MessageEmbed()
|
|
||||||
.setTitle(client.messages.statisticsTitle)
|
|
||||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["statistics"].replace(/[^0-9]+/g, ''))
|
|
||||||
.setColor(client.config.embedColor)
|
|
||||||
.setDescription(statistics)
|
|
||||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
|
||||||
|
|
||||||
interaction.reply({
|
|
||||||
embeds: [embed],
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
51
src/client/commands/statistics.ts
Normal file
51
src/client/commands/statistics.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { ButtonInteraction, ChatInputCommandInteraction, EmbedBuilder, StringSelectMenuInteraction } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'statistics',
|
||||||
|
description: 'Show statistics',
|
||||||
|
category: 'info',
|
||||||
|
execute(interaction: ButtonInteraction | ChatInputCommandInteraction | StringSelectMenuInteraction, client: RadioClient) {
|
||||||
|
|
||||||
|
if(!interaction.guild) return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.maintenance,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
|
||||||
|
let currentGuild = client.datastore?.getEntry(interaction.guild.id);
|
||||||
|
let global = client.datastore?.getEntry("global");
|
||||||
|
let statistics = "";
|
||||||
|
|
||||||
|
if(!client.stations) {
|
||||||
|
return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.replace(client.messages.errorToGetPlaylist, {
|
||||||
|
"%client.config.supportGuild%": client.config.supportGuild
|
||||||
|
}),
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!currentGuild || currentGuild && !currentGuild.statistics){
|
||||||
|
statistics = "You have not listened any radio stations";
|
||||||
|
} else {
|
||||||
|
statistics = "[Open Dashboard](https://eximiabots.waren.io/radiox/" + interaction.guild.id + "/stats?info=" + Buffer.from(JSON.stringify(currentGuild), 'utf8').toString('base64') + "&globalInfo=" + Buffer.from(JSON.stringify(global), 'utf8').toString('base64') + ")" + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setTitle(client.messages.statisticsTitle)
|
||||||
|
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messages.emojis["statistics"].replace(/[^0-9]+/g, ''))
|
||||||
|
.setColor(client.config.embedColor)
|
||||||
|
.setDescription(statistics)
|
||||||
|
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||||
|
.setFooter({
|
||||||
|
text: client.messages.footerText,
|
||||||
|
iconURL: "https://cdn.discordapp.com/emojis/" + client.messages.emojis["eximiabots"].replace(/[^0-9]+/g, '')
|
||||||
|
});
|
||||||
|
|
||||||
|
interaction.reply({
|
||||||
|
embeds: [embed],
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -1,31 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
name: 'status',
|
|
||||||
alias: 'st',
|
|
||||||
usage: '',
|
|
||||||
description: 'Bot Status',
|
|
||||||
permission: 'none',
|
|
||||||
category: 'info',
|
|
||||||
async execute(interaction, client, Discord, command) {
|
|
||||||
let message = {};
|
|
||||||
|
|
||||||
message.statusTitle = client.messages.statusTitle.replace("%client.user.username%", client.user.username);
|
|
||||||
let uptime = client.funcs.msToTime(client.uptime);
|
|
||||||
|
|
||||||
const embed = new Discord.MessageEmbed()
|
|
||||||
.setTitle(message.statusTitle)
|
|
||||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["logo"].replace(/[^0-9]+/g, ''))
|
|
||||||
.setColor(client.config.embedColor)
|
|
||||||
.addField(client.messages.statusField1, Date.now() - interaction.createdTimestamp + "ms", true)
|
|
||||||
.addField(client.messages.statusField2, client.ws.ping + "ms", true)
|
|
||||||
.addField(client.messages.statusField3, uptime, true)
|
|
||||||
.addField(client.messages.statusField4, client.config.version, true)
|
|
||||||
.addField(client.messages.statusField5, client.config.hostedBy, true)
|
|
||||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
|
||||||
|
|
||||||
interaction.reply({
|
|
||||||
embeds: [embed],
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
42
src/client/commands/status.ts
Normal file
42
src/client/commands/status.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { ChatInputCommandInteraction, EmbedBuilder } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'status',
|
||||||
|
description: 'Bot Status',
|
||||||
|
category: 'info',
|
||||||
|
async execute(interaction: ChatInputCommandInteraction, client: RadioClient) {
|
||||||
|
|
||||||
|
if(!client.user) return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.maintenance,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
|
||||||
|
let uptime = client.funcs.msToTime(client.uptime || 0);
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setTitle(client.messages.replace(client.messages.statusTitle, {
|
||||||
|
"%client.user.username%": client.user.username
|
||||||
|
}))
|
||||||
|
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messages.emojis["logo"].replace(/[^0-9]+/g, ''))
|
||||||
|
.setColor(client.config.embedColor)
|
||||||
|
.addFields([
|
||||||
|
{ name: client.messages.statusField1, value: uptime },
|
||||||
|
{ name: client.messages.statusField2, value: client.config.version },
|
||||||
|
{ name: client.messages.statusField3, value: Date.now() - interaction.createdTimestamp + "ms" },
|
||||||
|
{ name: client.messages.statusField4, value: client.ws.ping.toString() },
|
||||||
|
{ name: client.messages.statusField5, value: client.config.hostedBy }
|
||||||
|
])
|
||||||
|
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||||
|
.setFooter({
|
||||||
|
text: client.messages.footerText,
|
||||||
|
iconURL: "https://cdn.discordapp.com/emojis/" + client.messages.emojis["eximiabots"].replace(/[^0-9]+/g, '')
|
||||||
|
});
|
||||||
|
|
||||||
|
interaction.reply({
|
||||||
|
embeds: [embed],
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
@ -1,41 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
name: 'stop',
|
|
||||||
description: 'Stop radio',
|
|
||||||
alias: 's',
|
|
||||||
usage: '',
|
|
||||||
permission: 'none',
|
|
||||||
category: 'radio',
|
|
||||||
async execute(interaction, client, Discord, command) {
|
|
||||||
const radio = client.radio.get(interaction.guild.id);
|
|
||||||
if (client.funcs.check(client, interaction, command)) {
|
|
||||||
client.funcs.statisticsUpdate(client, interaction.guild, radio);
|
|
||||||
radio.connection?.destroy();
|
|
||||||
radio.audioPlayer?.stop();
|
|
||||||
client.funcs.logger('Radio', 'Stream stopped' + " / " + interaction.guild.id);
|
|
||||||
|
|
||||||
const embed = new Discord.MessageEmbed()
|
|
||||||
.setTitle(client.user.username)
|
|
||||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["stop"].replace(/[^0-9]+/g, ''))
|
|
||||||
.setColor(client.config.embedColor)
|
|
||||||
.addField(client.messages.nowplayingTitle, "-", true)
|
|
||||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
|
||||||
|
|
||||||
if(!radio.message){
|
|
||||||
radio.message = radio.textChannel.send({ embeds: [embed], components: [] });
|
|
||||||
} else {
|
|
||||||
radio.message.edit({ embeds: [embed], components: [] });
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(async function() {
|
|
||||||
await radio.message?.delete();
|
|
||||||
}, 5000);
|
|
||||||
|
|
||||||
client.radio.delete(interaction.guild.id);
|
|
||||||
|
|
||||||
interaction.reply({
|
|
||||||
content: client.messageEmojis["stop"] + client.messages.stop,
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
52
src/client/commands/stop.ts
Normal file
52
src/client/commands/stop.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { ButtonInteraction, ChatInputCommandInteraction, EmbedBuilder, StringSelectMenuInteraction } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
import { command } from "../commands";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'stop',
|
||||||
|
description: 'Stop radio',
|
||||||
|
category: 'radio',
|
||||||
|
async execute(interaction: ButtonInteraction | ChatInputCommandInteraction | StringSelectMenuInteraction, client: RadioClient, command: command) {
|
||||||
|
if (client.funcs.check(client, interaction, command)) {
|
||||||
|
const radio = client.radio?.get(interaction.guild?.id);
|
||||||
|
client.statistics?.update(client, interaction.guild, radio);
|
||||||
|
radio.connection?.destroy();
|
||||||
|
client.funcs.logger('Radio', interaction.guild?.id + " / " + 'Stop');
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setTitle(client.user?.username || "-")
|
||||||
|
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messages.emojis["stop"].replace(/[^0-9]+/g, ''))
|
||||||
|
.setColor(client.config.embedColor)
|
||||||
|
.addFields({
|
||||||
|
name: client.messages.nowplayingTitle,
|
||||||
|
value: "-"
|
||||||
|
})
|
||||||
|
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||||
|
.setFooter({
|
||||||
|
text: client.messages.footerText,
|
||||||
|
iconURL: "https://cdn.discordapp.com/emojis/" + client.messages.emojis["eximiabots"].replace(/[^0-9]+/g, '')
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!radio.message){
|
||||||
|
radio.message = radio.textChannel.send({ embeds: [embed], components: [] });
|
||||||
|
} else {
|
||||||
|
if(radio.textChannel.id == radio.message.channel.id){
|
||||||
|
radio.message.edit({ embeds: [embed], components: [] });
|
||||||
|
} else {
|
||||||
|
radio.message?.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(async function() {
|
||||||
|
await radio.message?.delete();
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
client.radio?.delete(interaction.guild?.id);
|
||||||
|
|
||||||
|
interaction.reply({
|
||||||
|
content: client.messages.emojis["stop"] + client.messages.stop,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -1,136 +0,0 @@
|
|||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
module.exports = class {
|
|
||||||
constructor() {
|
|
||||||
this.map = new Map();
|
|
||||||
this.loadData();
|
|
||||||
}
|
|
||||||
|
|
||||||
loadData() {
|
|
||||||
const dir = path.join(path.dirname(__dirname), '../datastore');
|
|
||||||
if (!fs.existsSync(dir)) {
|
|
||||||
fs.mkdirSync(dir);
|
|
||||||
}
|
|
||||||
//console.log("");
|
|
||||||
const dataFiles = fs.readdirSync(path.join(path.dirname(__dirname), '../datastore')).filter(f => f.endsWith('.json'));
|
|
||||||
for (const file of dataFiles) {
|
|
||||||
try {
|
|
||||||
const json = require(`../../datastore/${file}`);
|
|
||||||
this.map.set(json.guild.id, json);
|
|
||||||
//console.log('[LOADED] ' + file + " (" + json.guild.id + ")");
|
|
||||||
//console.log(JSON.stringify(json, null, 4));
|
|
||||||
} catch (error) {
|
|
||||||
//console.log('[ERROR] Loading ' + file + ' failed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//console.log("");
|
|
||||||
}
|
|
||||||
|
|
||||||
calculateGlobal(client){
|
|
||||||
let guilds = this.map.keys();
|
|
||||||
let stations = client.stations;
|
|
||||||
var statistics = {};
|
|
||||||
|
|
||||||
if(!client.stations) return;
|
|
||||||
|
|
||||||
let calculation = guilds.next();
|
|
||||||
|
|
||||||
while (!calculation.done) {
|
|
||||||
let currentGuild = this.getEntry(calculation.value);
|
|
||||||
if(calculation.value != 'global'){
|
|
||||||
if(stations){
|
|
||||||
Object.keys(stations).forEach(function(station) {
|
|
||||||
if(currentGuild.statistics[stations[station].name] && currentGuild.statistics[stations[station].name].time && parseInt(currentGuild.statistics[stations[station].name].time) != 0 && currentGuild.statistics[stations[station].name].used && parseInt(currentGuild.statistics[stations[station].name].used) != 0){
|
|
||||||
if(!statistics[stations[station].name]){
|
|
||||||
statistics[stations[station].name] = {};
|
|
||||||
statistics[stations[station].name].time = 0;
|
|
||||||
statistics[stations[station].name].used = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
statistics[stations[station].name].time = parseInt(statistics[stations[station].name].time)+parseInt(currentGuild.statistics[stations[station].name].time);
|
|
||||||
statistics[stations[station].name].used = parseInt(statistics[stations[station].name].used)+parseInt(currentGuild.statistics[stations[station].name].used);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
calculation = guilds.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
let newData = {};
|
|
||||||
newData.guild = {};
|
|
||||||
newData.guild.id = "global";
|
|
||||||
newData.guild.name = "global";
|
|
||||||
newData.statistics = statistics;
|
|
||||||
this.updateEntry(newData.guild, newData);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
checkEntry(id){
|
|
||||||
if(!this.map.has(id)){
|
|
||||||
this.createEntry(id);
|
|
||||||
//this.showEntry(this.getEntry(id));
|
|
||||||
} else {
|
|
||||||
//this.showEntry(this.getEntry(id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
createEntry(id){
|
|
||||||
let newData = {};
|
|
||||||
newData.guild = {};
|
|
||||||
newData.guild.id = id;
|
|
||||||
newData.statistics = {};
|
|
||||||
newData.state = {};
|
|
||||||
this.map.set(id, newData);
|
|
||||||
this.saveEntry(id, newData);
|
|
||||||
}
|
|
||||||
|
|
||||||
getEntry(id){
|
|
||||||
return this.map.get(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateEntry(guild, newData) {
|
|
||||||
newData.guild.name = guild.name;
|
|
||||||
|
|
||||||
let date = new Date();
|
|
||||||
newData.updated = date.toISOString().substring(0, 10)
|
|
||||||
|
|
||||||
this.map.set(guild.id, newData);
|
|
||||||
this.saveEntry(guild.id, newData);
|
|
||||||
//this.showEntry(this.getEntry(guild.id));
|
|
||||||
}
|
|
||||||
|
|
||||||
showEntry(data){
|
|
||||||
console.log(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
createTestFile () {
|
|
||||||
let newData = {
|
|
||||||
"guild": {
|
|
||||||
"id": "test",
|
|
||||||
"name": "Test"
|
|
||||||
},
|
|
||||||
"statistics": {
|
|
||||||
"test": {
|
|
||||||
"time": 0,
|
|
||||||
"used": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updateEntry(newData.guild, newData);
|
|
||||||
}
|
|
||||||
|
|
||||||
saveEntry(file, data) {
|
|
||||||
data = JSON.stringify(data, null, 4);
|
|
||||||
|
|
||||||
fs.writeFile(path.join(path.dirname(__dirname), '../datastore') + "/" + file + ".json", data, 'utf8', function(err) {
|
|
||||||
if (err) {
|
|
||||||
//console.log(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,42 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
name: 'emojis',
|
|
||||||
async execute(client) {
|
|
||||||
let customEmojis = {
|
|
||||||
logo: "<:RadioX:688765708808487072>",
|
|
||||||
eximiabots: "<:EximiaBots:693277919929303132>",
|
|
||||||
list: "<:RadioXList:688541155519889482>",
|
|
||||||
play: "<:RadioXPlay:688541155712827458>",
|
|
||||||
stop: "<:RadioXStop:688541155377414168>",
|
|
||||||
statistics: "<:RadioXStatistics:694954485507686421>",
|
|
||||||
maintenance: "<:RadioXMaintenance:695043843057254493>",
|
|
||||||
error: "<:RadioXError:688541155792781320>",
|
|
||||||
prev: "<:RadioXPrev:882153637370023957>",
|
|
||||||
next: "<:RadioXNext:882153637474893834>"
|
|
||||||
};
|
|
||||||
|
|
||||||
let fallbackEmojis = {
|
|
||||||
logo: "RadioX",
|
|
||||||
eximiabots: "EximiaBots",
|
|
||||||
list: "📜",
|
|
||||||
play: "▶️",
|
|
||||||
stop: "⏹️",
|
|
||||||
statistics: "📊",
|
|
||||||
maintenance: "🛠️",
|
|
||||||
error: "❌",
|
|
||||||
prev: "⏪",
|
|
||||||
next: "⏩"
|
|
||||||
};
|
|
||||||
|
|
||||||
client.messageEmojis = {};
|
|
||||||
|
|
||||||
for (const customEmojiName in customEmojis) {
|
|
||||||
const customEmojiID = customEmojis[customEmojiName].replace(/[^0-9]+/g, '');
|
|
||||||
const customEmoji = client.emojis.cache.get(customEmojiID);
|
|
||||||
if (customEmoji) {
|
|
||||||
client.messageEmojis[customEmojiName] = customEmojis[customEmojiName];
|
|
||||||
} else {
|
|
||||||
client.messageEmojis[customEmojiName] = fallbackEmojis[customEmojiName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
53
src/client/events.ts
Normal file
53
src/client/events.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import RadioClient from "../Client"
|
||||||
|
import interactionCreate from "./events/interactionCreate"
|
||||||
|
import messageDelete from "./events/messageDelete"
|
||||||
|
import ready from "./events/ready"
|
||||||
|
import SIGINT from "./events/SIGINT"
|
||||||
|
import SIGTERM from "./events/SIGTERM"
|
||||||
|
import uncaughtException from "./events/uncaughtException"
|
||||||
|
import voiceStateUpdate from "./events/voiceStateUpdate"
|
||||||
|
import warning from "./events/warning"
|
||||||
|
|
||||||
|
export default function events(client: RadioClient) {
|
||||||
|
client.on("ready", () => {
|
||||||
|
ready(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("messageDelete", msg => {
|
||||||
|
messageDelete(client, msg);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("interactionCreate", interaction => {
|
||||||
|
interactionCreate(client, interaction);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("voiceStateUpdate", (oldState, newState) => {
|
||||||
|
voiceStateUpdate(client, oldState, newState);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("error", error => {
|
||||||
|
client.funcs.logger("Discord Client", "Error");
|
||||||
|
console.error(error);
|
||||||
|
console.log('');
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('SIGINT', () => {
|
||||||
|
SIGINT(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('SIGTERM', () => {
|
||||||
|
SIGTERM(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('uncaughtException', (error) => {
|
||||||
|
uncaughtException(client, error);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('exit', () => {
|
||||||
|
client.funcs.logger("Bot", "Stopping");
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('warning', (error) => {
|
||||||
|
warning(client, error);
|
||||||
|
});
|
||||||
|
}
|
@ -1,43 +0,0 @@
|
|||||||
import Discord from "discord.js";
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
name: 'SIGINT',
|
|
||||||
async execute(client) {
|
|
||||||
setTimeout(async function () {
|
|
||||||
let message = {};
|
|
||||||
|
|
||||||
if (!client.stations) return process.exit();
|
|
||||||
|
|
||||||
let currentRadios = client.radio.keys();
|
|
||||||
let radio = currentRadios.next();
|
|
||||||
|
|
||||||
while (!radio.done) {
|
|
||||||
let currentRadio = client.radio.get(radio.value);
|
|
||||||
currentRadio.guild = client.datastore.getEntry(radio.value).guild;
|
|
||||||
|
|
||||||
if (currentRadio) {
|
|
||||||
client.funcs.statisticsUpdate(client, currentRadio.guild, currentRadio);
|
|
||||||
client.funcs.saveState(client, currentRadio.guild, currentRadio);
|
|
||||||
currentRadio.connection?.destroy();
|
|
||||||
currentRadio.audioPlayer?.stop();
|
|
||||||
currentRadio.message?.delete();
|
|
||||||
client.radio.delete(radio.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
radio = currentRadios.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("\n");
|
|
||||||
client.funcs.logger("Bot", "Closing");
|
|
||||||
console.log("\n");
|
|
||||||
|
|
||||||
client.user.setStatus('dnd');
|
|
||||||
|
|
||||||
setInterval(() => {
|
|
||||||
if(radio.done){
|
|
||||||
process.exit();
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
}
|
|
14
src/client/events/SIGINT.ts
Normal file
14
src/client/events/SIGINT.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import RadioClient from "../../Client";
|
||||||
|
|
||||||
|
export default function SIGINT(client: RadioClient) {
|
||||||
|
client.user?.setStatus('dnd');
|
||||||
|
|
||||||
|
client.streamer?.leave(client);
|
||||||
|
client.radio?.save(client);
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
if(client.radio?.size == 0){
|
||||||
|
process.exit();
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
@ -1,6 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
name: 'SIGTERM',
|
|
||||||
async execute(client) {
|
|
||||||
process.emit('SIGINT');
|
|
||||||
}
|
|
||||||
}
|
|
5
src/client/events/SIGTERM.ts
Normal file
5
src/client/events/SIGTERM.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import RadioClient from "../../Client";
|
||||||
|
|
||||||
|
export default function SIGTERM(client: RadioClient) {
|
||||||
|
process.emit('SIGINT');
|
||||||
|
}
|
@ -1,40 +0,0 @@
|
|||||||
import Discord from "discord.js";
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
name: 'interactionCreate',
|
|
||||||
async execute(client, interaction) {
|
|
||||||
|
|
||||||
const permissions = interaction.channel.permissionsFor(interaction.client.user);
|
|
||||||
if (!permissions.has('EMBED_LINKS')) return interaction.send(client.messages.noPermsEmbed);
|
|
||||||
|
|
||||||
if(interaction.isCommand()){
|
|
||||||
const commandName = interaction.commandName;
|
|
||||||
const command = client.commands.get(commandName);
|
|
||||||
if (!command) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
command.execute(interaction, client, Discord, command);
|
|
||||||
} catch (error) {
|
|
||||||
interaction.reply({
|
|
||||||
content: client.messages.runningCommandFailed,
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
} else if (interaction.isSelectMenu() || interaction.isButton()){
|
|
||||||
const commandName = interaction.customId;
|
|
||||||
const command = client.commands.get(commandName);
|
|
||||||
if (!command) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
command.execute(interaction, client, Discord, command);
|
|
||||||
} catch (error) {
|
|
||||||
interaction.reply({
|
|
||||||
content: client.messages.runningCommandFailed,
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
46
src/client/events/interactionCreate.ts
Normal file
46
src/client/events/interactionCreate.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { ChannelType, Interaction, PermissionFlagsBits } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
|
||||||
|
export default function interactionCreate(client: RadioClient, interaction: Interaction) {
|
||||||
|
if(!(interaction.isButton()) && !(interaction.isChatInputCommand()) && !(interaction.isStringSelectMenu())) return;
|
||||||
|
|
||||||
|
if(interaction.channel?.type != ChannelType.DM){
|
||||||
|
const permissions = interaction.channel?.permissionsFor(interaction.client.user);
|
||||||
|
if (!permissions?.has(PermissionFlagsBits.ViewChannel)) return;
|
||||||
|
|
||||||
|
if (!permissions?.has(PermissionFlagsBits.EmbedLinks)) return interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.noPermsEmbed,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(interaction.isChatInputCommand()){
|
||||||
|
const commandName = interaction.commandName;
|
||||||
|
const command = client.commands.get(commandName);
|
||||||
|
if (!command) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
command.execute(interaction, client);
|
||||||
|
} catch (error) {
|
||||||
|
interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.runningCommandFailed,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
} else if (interaction.isStringSelectMenu() || interaction.isButton()){
|
||||||
|
const commandName = interaction.customId;
|
||||||
|
const command = client.commands.get(commandName);
|
||||||
|
if (!command) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
command.execute(interaction, client, command);
|
||||||
|
} catch (error) {
|
||||||
|
interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.runningCommandFailed,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,54 +0,0 @@
|
|||||||
import Discord from "discord.js";
|
|
||||||
module.exports = {
|
|
||||||
name: 'messageCreate',
|
|
||||||
async execute(client, msg) {
|
|
||||||
|
|
||||||
if (msg.author.bot || !msg.guild) return;
|
|
||||||
let prefix = "rx$";
|
|
||||||
if(client.user.username == "RadioX"){
|
|
||||||
prefix = "rx>";
|
|
||||||
} else if (client.user.username == "RadioX Beta"){
|
|
||||||
prefix = "rx-";
|
|
||||||
} else if (client.user.username == "RadioX Dev"){
|
|
||||||
prefix = "rx$";
|
|
||||||
} else if(msg.mentions.members.first() && msg.mentions.members.first().user.id === client.user.id){
|
|
||||||
prefix = "<@!" + client.user.id + "> ";
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const args = msg.content.slice(prefix.length).split(' ');
|
|
||||||
if (!msg.content.startsWith(prefix)) return;
|
|
||||||
if (!args[0]) return;
|
|
||||||
const commandName = args[0].toLowerCase();
|
|
||||||
if (commandName === 'none') return;
|
|
||||||
const command = client.commands.get(commandName) || client.commands.find(cmd => cmd.aliases && cmd.aliases.includes(commandName)) || client.commandAliases.get(commandName);
|
|
||||||
if (!command && msg.content !== `${prefix}`) return;
|
|
||||||
const permissions = msg.channel.permissionsFor(msg.client.user);
|
|
||||||
if (!permissions.has('EMBED_LINKS')) return msg.channel.send(client.messages.noPermsEmbed);
|
|
||||||
try {
|
|
||||||
let message = {};
|
|
||||||
|
|
||||||
message.messageCommandsDeprecatedTitle = client.messages.messageCommandsDeprecatedTitle.replace("%client.user.username%", client.user.username);
|
|
||||||
|
|
||||||
const embed = new Discord.MessageEmbed()
|
|
||||||
.setTitle(message.messageCommandsDeprecatedTitle)
|
|
||||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["logo"].replace(/[^0-9]+/g, ''))
|
|
||||||
.setColor(client.config.embedColor)
|
|
||||||
.setDescription(client.messages.messageCommandsDeprecatedDescription)
|
|
||||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
|
||||||
|
|
||||||
msg.channel.send({ embeds: [embed] });
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
msg.delete();
|
|
||||||
}, 30000);
|
|
||||||
} catch (error) {
|
|
||||||
msg.reply({
|
|
||||||
content: client.messages.runningCommandFailed,
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
name: 'messageDelete',
|
|
||||||
async execute(client, msg) {
|
|
||||||
if (!msg.author.bot || !msg.guild) return;
|
|
||||||
const radio = client.radio.get(msg.guild.id);
|
|
||||||
if (!radio) return;
|
|
||||||
if(msg.id != radio.message.id) return;
|
|
||||||
radio.message = null;
|
|
||||||
}
|
|
||||||
}
|
|
11
src/client/events/messageDelete.ts
Normal file
11
src/client/events/messageDelete.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { Message, PartialMessage } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
|
||||||
|
export default function messageDelete(client: RadioClient, msg: Message | PartialMessage){
|
||||||
|
if(!msg.author?.bot || !msg.guild) return;
|
||||||
|
const radio = client.radio?.get(msg.guild.id);
|
||||||
|
if(!radio) return;
|
||||||
|
if(!radio.message) return;
|
||||||
|
if(msg.id != radio.message.id) return;
|
||||||
|
radio.message = null;
|
||||||
|
}
|
@ -1,94 +0,0 @@
|
|||||||
import Datastore from "../datastore.js";
|
|
||||||
import fetch from "node-fetch";
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
name: 'ready',
|
|
||||||
async execute(client) {
|
|
||||||
|
|
||||||
client.funcs.logger("Bot", "Ready");
|
|
||||||
|
|
||||||
/*DATASTORE*/
|
|
||||||
client.funcs.logger('Datastore', 'Initialize');
|
|
||||||
client.datastore = new Datastore();
|
|
||||||
|
|
||||||
/*DEVELOPERS*/
|
|
||||||
client.funcs.logger('Developers');
|
|
||||||
|
|
||||||
client.developers = "";
|
|
||||||
let user = "";
|
|
||||||
for (let i = 0; i < client.config.devId.length; i++) {
|
|
||||||
user = await client.users.fetch(client.config.devId[i]);
|
|
||||||
console.log("- " + user.tag);
|
|
||||||
if (i == client.config.devId.length - 1) {
|
|
||||||
client.developers += user.tag;
|
|
||||||
} else {
|
|
||||||
client.developers += user.tag + " & ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log("\n");
|
|
||||||
|
|
||||||
/*STATIONS*/
|
|
||||||
try {
|
|
||||||
client.funcs.logger('Stations', 'Started fetching list – ' + client.config.stationslistUrl);
|
|
||||||
client.stations = await fetch(client.config.stationslistUrl)
|
|
||||||
.then(client.funcs.checkFetchStatus)
|
|
||||||
.then(response => response.json());
|
|
||||||
|
|
||||||
client.funcs.logger('Stations');
|
|
||||||
client.stations.forEach(station => {
|
|
||||||
console.log("- " + station.name);
|
|
||||||
});
|
|
||||||
console.log("\n");
|
|
||||||
|
|
||||||
client.funcs.logger('Stations', 'Successfully fetched list');
|
|
||||||
} catch (error) {
|
|
||||||
client.funcs.logger('Stations', 'Fetching list failed');
|
|
||||||
console.error(error + "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
setInterval(async () => {
|
|
||||||
try {
|
|
||||||
client.funcs.logger('Stations', 'Started fetching list – ' + client.config.stationslistUrl);
|
|
||||||
client.stations = await fetch(client.config.stationslistUrl)
|
|
||||||
.then(client.funcs.checkFetchStatus)
|
|
||||||
.then(response => response.json());
|
|
||||||
|
|
||||||
client.funcs.logger('Stations', 'Successfully fetched list');
|
|
||||||
} catch (error) {
|
|
||||||
client.funcs.logger('Stations', 'Fetching list failed');
|
|
||||||
//console.error(error);
|
|
||||||
}
|
|
||||||
}, 3600000);
|
|
||||||
|
|
||||||
if(!client.stations) {
|
|
||||||
client.user.setStatus('dnd');
|
|
||||||
}
|
|
||||||
|
|
||||||
/*GUILDS*/
|
|
||||||
client.funcs.logger('Guilds', 'Started fetching list');
|
|
||||||
|
|
||||||
client.funcs.logger('Guilds');
|
|
||||||
let guilds = await client.guilds.fetch();
|
|
||||||
guilds.forEach(guild => {
|
|
||||||
console.log("- " + guild.id + ": " + guild.name);
|
|
||||||
});
|
|
||||||
console.log("\n");
|
|
||||||
|
|
||||||
client.funcs.logger('Guilds', 'Successfully fetched list');
|
|
||||||
|
|
||||||
/*STATISTICS*/
|
|
||||||
client.datastore.calculateGlobal(client);
|
|
||||||
|
|
||||||
/*EMOJIS*/
|
|
||||||
require(`../emojis.js`).execute(client);
|
|
||||||
|
|
||||||
/*COMMANDS*/
|
|
||||||
require(`../commands.js`).execute(client);
|
|
||||||
|
|
||||||
setTimeout(function () {
|
|
||||||
/*RESTORE RADIO*/
|
|
||||||
require(`../restoreradio.js`).execute(client, guilds);
|
|
||||||
}, 5000);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
77
src/client/events/ready.ts
Normal file
77
src/client/events/ready.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import RadioClient from "../../Client";
|
||||||
|
import Datastore, { datastore } from "../classes/Datastore";
|
||||||
|
import Radio from "../classes/Radio";
|
||||||
|
import Stations from "../classes/Stations";
|
||||||
|
import Streamer from "../classes/Streamer";
|
||||||
|
import Statistics from "../classes/Statistics";
|
||||||
|
import commands from "../commands";
|
||||||
|
import { OAuth2Guild } from "discord.js";
|
||||||
|
|
||||||
|
export default async function ready(client: RadioClient) {
|
||||||
|
client.funcs.logger("Bot", "Ready");
|
||||||
|
|
||||||
|
/*DATASTORE*/
|
||||||
|
client.funcs.logger('Datastore', 'Initialize');
|
||||||
|
client.datastore = new Datastore();
|
||||||
|
|
||||||
|
client.datastore.map.forEach((datastore: datastore) => {
|
||||||
|
client.funcs.logger('Datastore', datastore.guild.id + " / " + datastore.guild.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.funcs.logger('Datastore', 'Ready');
|
||||||
|
|
||||||
|
/*DEVELOPERS*/
|
||||||
|
let developers : string[] = [];
|
||||||
|
for(let devID of client.config.devIDs){
|
||||||
|
developers.push((await client.users.fetch(devID)).tag);
|
||||||
|
}
|
||||||
|
client.funcs.logger('Developers', developers.join(" & "));
|
||||||
|
|
||||||
|
/*STATIONS*/
|
||||||
|
client.stations = new Stations();
|
||||||
|
|
||||||
|
await client.stations.fetch({
|
||||||
|
url: client.config.stationslistUrl,
|
||||||
|
show: true
|
||||||
|
});
|
||||||
|
|
||||||
|
client.streamer = new Streamer();
|
||||||
|
client.streamer.init(client);
|
||||||
|
|
||||||
|
if(!client.stations) {
|
||||||
|
client.user?.setStatus('dnd');
|
||||||
|
}
|
||||||
|
|
||||||
|
/*GUILDS*/
|
||||||
|
client.funcs.logger('Guilds', 'Started fetching list');
|
||||||
|
|
||||||
|
let guilds = await client.guilds.fetch();
|
||||||
|
guilds.forEach((guild: OAuth2Guild) => {
|
||||||
|
client.funcs.logger('Guilds', guild.id + " / " + guild.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.funcs.logger('Guilds', 'Successfully fetched list');
|
||||||
|
|
||||||
|
/*STATISTICS*/
|
||||||
|
client.statistics = new Statistics();
|
||||||
|
client.statistics.calculateGlobal(client);
|
||||||
|
|
||||||
|
/*COMMANDS*/
|
||||||
|
commands(client);
|
||||||
|
|
||||||
|
/*RADIO*/
|
||||||
|
client.radio = new Radio();
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
/*RESTORE RADIOS*/
|
||||||
|
client.radio?.restore(client, guilds);
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
if(client.stations) {
|
||||||
|
/*MAINTENANCE MODE*/
|
||||||
|
client.funcs.logger("Maintenance Mode", "Disabled");
|
||||||
|
client.config.maintenanceMode = false;
|
||||||
|
}
|
||||||
|
}, 10000);
|
||||||
|
}
|
10
src/client/events/uncaughtException.ts
Normal file
10
src/client/events/uncaughtException.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import RadioClient from "../../Client";
|
||||||
|
|
||||||
|
export default function uncaughtException(client: RadioClient, error: Error) {
|
||||||
|
client.funcs.logger("Error");
|
||||||
|
console.log(error.stack);
|
||||||
|
console.log('');
|
||||||
|
|
||||||
|
if(error.name == "DiscordAPIError" && error.message == "Unknown interaction") return;
|
||||||
|
process.emit('SIGINT');
|
||||||
|
}
|
@ -1,70 +0,0 @@
|
|||||||
const {
|
|
||||||
getVoiceConnection,
|
|
||||||
joinVoiceChannel
|
|
||||||
} = require("@discordjs/voice");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
name: "voiceStateUpdate",
|
|
||||||
async execute(client, oldState, newState) {
|
|
||||||
if (oldState.channel === null) return;
|
|
||||||
let change = false;
|
|
||||||
const radio = client.radio.get(newState.guild.id);
|
|
||||||
if (!radio) return;
|
|
||||||
|
|
||||||
if (newState.member.id === client.user.id && oldState.member.id === client.user.id) {
|
|
||||||
|
|
||||||
if (newState.channel === null) {
|
|
||||||
client.funcs.statisticsUpdate(client, newState.guild, radio);
|
|
||||||
radio.connection?.destroy();
|
|
||||||
radio.audioPlayer?.stop();
|
|
||||||
radio.message?.delete();
|
|
||||||
client.funcs.logger('Radio', 'Stream stopped' + " / " + newState.guild.id);
|
|
||||||
return client.radio.delete(newState.guild.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
const newPermissions = newState.channel.permissionsFor(newState.client.user);
|
|
||||||
if (!newPermissions.has("CONNECT") || !newPermissions.has("SPEAK") || !newPermissions.has("VIEW_CHANNEL")) {
|
|
||||||
try {
|
|
||||||
setTimeout(
|
|
||||||
async () => (
|
|
||||||
radio.connection = joinVoiceChannel({
|
|
||||||
channelId: oldState.channel.id,
|
|
||||||
guildId: oldState.channel.guild.id,
|
|
||||||
adapterCreator: oldState.channel.guild.voiceAdapterCreator
|
|
||||||
})
|
|
||||||
//radio.connection = await oldState.channel.join()
|
|
||||||
),
|
|
||||||
1000
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
client.funcs.statisticsUpdate(client, newState.guild, radio);
|
|
||||||
radio.connection?.destroy();
|
|
||||||
radio.audioPlayer?.stop();
|
|
||||||
radio.message?.delete();
|
|
||||||
client.funcs.logger('Radio', 'Stream stopped' + " / " + newState.guild.id);
|
|
||||||
client.radio.delete(oldState.guild.id);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (newState.channel !== radio.voiceChannel) {
|
|
||||||
change = true;
|
|
||||||
radio.voiceChannel = newState.channel;
|
|
||||||
radio.connection = getVoiceConnection(newState.channel.guild.id);
|
|
||||||
//radio.connection = await newState.channel.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((oldState.channel.members.size === 1 && oldState.channel === radio.voiceChannel) || change) {
|
|
||||||
setTimeout(() => {
|
|
||||||
if (!radio || !radio.connection || !radio.connection === null) return;
|
|
||||||
if (radio.voiceChannel.members.size === 1) {
|
|
||||||
client.funcs.statisticsUpdate(client, newState.guild, radio);
|
|
||||||
radio.connection?.destroy();
|
|
||||||
radio.audioPlayer?.stop();
|
|
||||||
radio.message?.delete();
|
|
||||||
client.funcs.logger('Radio', 'Stream stopped' + " / " + newState.guild.id);
|
|
||||||
client.radio.delete(newState.guild.id);
|
|
||||||
}
|
|
||||||
}, 60000);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
65
src/client/events/voiceStateUpdate.ts
Normal file
65
src/client/events/voiceStateUpdate.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { GuildMember, PermissionFlagsBits, VoiceState } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
const {
|
||||||
|
getVoiceConnection,
|
||||||
|
joinVoiceChannel
|
||||||
|
} = require("@discordjs/voice");
|
||||||
|
|
||||||
|
export default async function voiceStateUpdate(client: RadioClient, oldState: VoiceState, newState: VoiceState) {
|
||||||
|
if (oldState.channel === null) return;
|
||||||
|
let change = false;
|
||||||
|
const radio = client.radio?.get(newState.guild.id);
|
||||||
|
if (!radio) return;
|
||||||
|
|
||||||
|
if (newState.member?.id === client.user?.id && oldState.member?.id === client.user?.id) {
|
||||||
|
|
||||||
|
if (newState.channel === null) {
|
||||||
|
client.statistics?.update(client, newState.guild, radio);
|
||||||
|
radio.connection?.destroy();
|
||||||
|
radio.message?.delete();
|
||||||
|
client.funcs.logger('Radio', newState.guild.id + " / " + 'Stop');
|
||||||
|
return client.radio?.delete(newState.guild.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newPermissions = newState.channel.permissionsFor(newState.client.user);
|
||||||
|
if (!newPermissions?.has(PermissionFlagsBits.Connect) || !newPermissions?.has(PermissionFlagsBits.Speak) || !newPermissions?.has(PermissionFlagsBits.ViewChannel)) {
|
||||||
|
try {
|
||||||
|
setTimeout(
|
||||||
|
async () => (
|
||||||
|
radio.connection = joinVoiceChannel({
|
||||||
|
channelId: oldState.channel?.id,
|
||||||
|
guildId: oldState.channel?.guild.id,
|
||||||
|
adapterCreator: oldState.channel?.guild.voiceAdapterCreator
|
||||||
|
})
|
||||||
|
),
|
||||||
|
1000
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
client.statistics?.update(client, newState.guild, radio);
|
||||||
|
radio.connection?.destroy();
|
||||||
|
radio.message?.delete();
|
||||||
|
client.funcs.logger('Radio', newState.guild.id + " / " + 'Stop');
|
||||||
|
client.radio?.delete(oldState.guild.id);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newState.channel !== radio.voiceChannel) {
|
||||||
|
change = true;
|
||||||
|
radio.voiceChannel = newState.channel;
|
||||||
|
radio.connection = getVoiceConnection(newState.channel.guild.id);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((oldState.channel.members.filter(member => !member.user.bot).size === 0 && oldState.channel === radio.voiceChannel) || change) {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!radio || !radio.connection || !radio.connection === null) return;
|
||||||
|
if (radio.voiceChannel.members.filter((member: GuildMember) => !member.user.bot).size === 0) {
|
||||||
|
client.statistics?.update(client, newState.guild, radio);
|
||||||
|
radio.connection?.destroy();
|
||||||
|
radio.message?.delete();
|
||||||
|
client.funcs.logger('Radio', newState.guild.id + " / " + 'Stop');
|
||||||
|
client.radio?.delete(newState.guild.id);
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
};
|
11
src/client/events/warning.ts
Normal file
11
src/client/events/warning.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import RadioClient from "../../Client";
|
||||||
|
|
||||||
|
export default function warning(client: RadioClient, warning: Error) {
|
||||||
|
if(warning.name == "ExperimentalWarning" && warning.message.startsWith("stream/web")) return;
|
||||||
|
|
||||||
|
client.funcs.logger("Warning");
|
||||||
|
console.warn(warning.name);
|
||||||
|
console.warn(warning.message);
|
||||||
|
console.warn(warning.stack);
|
||||||
|
console.log('');
|
||||||
|
}
|
12
src/client/funcs.ts
Normal file
12
src/client/funcs.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import check from "./funcs/check";
|
||||||
|
import isDev from "./funcs/isDev";
|
||||||
|
import listStations from "./funcs/listStations";
|
||||||
|
import loadState from "./funcs/loadState";
|
||||||
|
import logger from "./funcs/logger";
|
||||||
|
import msToTime from "./funcs/msToTime";
|
||||||
|
import play from "./funcs/play";
|
||||||
|
import saveState from "./funcs/saveState";
|
||||||
|
|
||||||
|
export const funcs = {
|
||||||
|
check, isDev, listStations, loadState, logger, msToTime, play, saveState
|
||||||
|
}
|
@ -1,29 +0,0 @@
|
|||||||
module.exports = function (client, interaction, command) {
|
|
||||||
let message = {};
|
|
||||||
const radio = client.radio.get(interaction.guild.id);
|
|
||||||
const permissions = interaction.channel.permissionsFor(interaction.user);
|
|
||||||
if (!radio) {
|
|
||||||
interaction.reply({
|
|
||||||
content: client.messageEmojis["error"] + client.messages.notPlaying,
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (interaction.member.voice.channel !== radio.voiceChannel) {
|
|
||||||
interaction.reply({
|
|
||||||
content: client.messageEmojis["error"] + client.messages.wrongVoiceChannel,
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if(!command.permission == 'none'){
|
|
||||||
if (!permissions.has(command.permission)) {
|
|
||||||
message.noPerms = client.messages.noPerms.replace("%command.permission%", command.permission);
|
|
||||||
interaction.reply({
|
|
||||||
content: client.messageEmojis["error"] + message.noPerms,
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
} else return true;
|
|
||||||
} else return true;
|
|
||||||
};
|
|
34
src/client/funcs/check.ts
Normal file
34
src/client/funcs/check.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { ButtonInteraction, ChatInputCommandInteraction, GuildMember, StringSelectMenuInteraction } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
import { command } from "../commands";
|
||||||
|
|
||||||
|
export default function check(client: RadioClient, interaction: ButtonInteraction | ChatInputCommandInteraction | StringSelectMenuInteraction, command: command) {
|
||||||
|
|
||||||
|
const radio = client.radio?.get(interaction.guild?.id);
|
||||||
|
if(!client.stations) {
|
||||||
|
interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.replace(client.messages.errorToGetPlaylist, {
|
||||||
|
"%client.config.supportGuild%": client.config.supportGuild
|
||||||
|
}),
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!radio) {
|
||||||
|
interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.notPlaying,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interaction.member instanceof GuildMember && interaction.member?.voice.channel !== radio.voiceChannel) {
|
||||||
|
interaction.reply({
|
||||||
|
content: client.messages.emojis["error"] + client.messages.wrongVoiceChannel,
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
@ -1,7 +0,0 @@
|
|||||||
module.exports = function (response) {
|
|
||||||
if (response.ok) { // res.status >= 200 && res.status < 300
|
|
||||||
return response;
|
|
||||||
} else {
|
|
||||||
throw new Error(response.status + " " + response.statusText);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
module.exports = function (devList, authorID){
|
|
||||||
let response = false;
|
|
||||||
Object.keys(devList).forEach(function(oneDev) {
|
|
||||||
let devID = devList[oneDev];
|
|
||||||
if(authorID == devID){
|
|
||||||
response = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return response;
|
|
||||||
}
|
|
9
src/client/funcs/isDev.ts
Normal file
9
src/client/funcs/isDev.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Snowflake } from "discord.js";
|
||||||
|
|
||||||
|
export default function isDev(devIDs : string[], authorID : Snowflake){
|
||||||
|
for (const devID of devIDs){
|
||||||
|
if(authorID == devID){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
src/client/funcs/listStations.ts
Normal file
30
src/client/funcs/listStations.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { ActionRowBuilder, ButtonInteraction, ChatInputCommandInteraction, SelectMenuComponentOptionData, StringSelectMenuBuilder, StringSelectMenuInteraction } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
|
||||||
|
export default function listStations(client: RadioClient, interaction: ButtonInteraction | ChatInputCommandInteraction | StringSelectMenuInteraction){
|
||||||
|
if(!client.stations) return;
|
||||||
|
|
||||||
|
let options : SelectMenuComponentOptionData[] = new Array();
|
||||||
|
|
||||||
|
for (const station of client.stations){
|
||||||
|
options.push({
|
||||||
|
label: station.name,
|
||||||
|
description: station.owner,
|
||||||
|
value: station.name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const menu = new ActionRowBuilder<StringSelectMenuBuilder>()
|
||||||
|
.addComponents(
|
||||||
|
new StringSelectMenuBuilder()
|
||||||
|
.setCustomId('play')
|
||||||
|
.setPlaceholder('Nothing selected')
|
||||||
|
.addOptions(options)
|
||||||
|
);
|
||||||
|
|
||||||
|
return interaction.reply({
|
||||||
|
content: '**Select station:**',
|
||||||
|
components: [menu],
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
@ -1,10 +0,0 @@
|
|||||||
module.exports = function loadState(client, guild){
|
|
||||||
let data = client.datastore.getEntry(guild.id);
|
|
||||||
if(!data) return;
|
|
||||||
let state;
|
|
||||||
|
|
||||||
state = data.state;
|
|
||||||
data.state = {};
|
|
||||||
client.datastore.updateEntry(guild, data);
|
|
||||||
return state;
|
|
||||||
}
|
|
13
src/client/funcs/loadState.ts
Normal file
13
src/client/funcs/loadState.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { OAuth2Guild } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
|
||||||
|
export default function loadState(client: RadioClient, guild: OAuth2Guild) {
|
||||||
|
if(!client.datastore) return;
|
||||||
|
let data = client.datastore.getEntry(guild.id);
|
||||||
|
if(!data) return;
|
||||||
|
let state = data.state;
|
||||||
|
if(!state) return;
|
||||||
|
data.state = null;
|
||||||
|
client.datastore.updateEntry(guild, data);
|
||||||
|
return state;
|
||||||
|
}
|
@ -1,5 +0,0 @@
|
|||||||
module.exports = function (area, text){
|
|
||||||
let date = new Date();
|
|
||||||
console.log('[' + area + '] – ' + date.toISOString());
|
|
||||||
if(text) console.log(text + '\n');
|
|
||||||
}
|
|
5
src/client/funcs/logger.ts
Normal file
5
src/client/funcs/logger.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export default function logger(area: string, text?: string){
|
||||||
|
let date = new Date();
|
||||||
|
console.log('[' + area + '] - ' + date.toISOString());
|
||||||
|
if(text) console.log(text + '\n');
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
module.exports = function msToTime(duration) {
|
export default function msToTime(duration : number) {
|
||||||
let seconds = Math.floor((duration / 1000) % 60),
|
let seconds = Math.floor((duration / 1000) % 60),
|
||||||
minutes = Math.floor((duration / (1000 * 60)) % 60),
|
minutes = Math.floor((duration / (1000 * 60)) % 60),
|
||||||
hours = Math.floor((duration / (1000 * 60 * 60)) % 24),
|
hours = Math.floor((duration / (1000 * 60 * 60)) % 24),
|
||||||
@ -9,4 +9,4 @@ module.exports = function msToTime(duration) {
|
|||||||
: +hours > 0
|
: +hours > 0
|
||||||
? `${+hours < 10 ? `0${hours}` : hours}:${+minutes < 10 ? `0${minutes}` : minutes}:${+seconds < 10 ? `0${seconds}` : seconds}`
|
? `${+hours < 10 ? `0${hours}` : hours}:${+minutes < 10 ? `0${minutes}` : minutes}:${+seconds < 10 ? `0${seconds}` : seconds}`
|
||||||
: `${+minutes < 10 ? `0${minutes}` : minutes}:${+seconds < 10 ? `0${seconds}` : seconds}`;
|
: `${+minutes < 10 ? `0${minutes}` : minutes}:${+seconds < 10 ? `0${seconds}` : seconds}`;
|
||||||
}
|
}
|
83
src/client/funcs/play.ts
Normal file
83
src/client/funcs/play.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChatInputCommandInteraction, EmbedBuilder, Guild, OAuth2Guild, StringSelectMenuInteraction } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
import { station } from "../classes/Stations";
|
||||||
|
|
||||||
|
export default async function play(client: RadioClient, interaction: ChatInputCommandInteraction | StringSelectMenuInteraction | null, guild: OAuth2Guild | Guild | null, station: station) {
|
||||||
|
if(!guild) return;
|
||||||
|
|
||||||
|
const radio = client.radio?.get(guild.id);
|
||||||
|
const audioPlayer = client.streamer?.listen(station);
|
||||||
|
radio.connection.subscribe(audioPlayer);
|
||||||
|
client.funcs.logger('Radio', guild.id + " / " + "Play" + " / " + radio.station.name);
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setTitle(client.user?.username || "-")
|
||||||
|
.setThumbnail((radio.station.logo || "https://cdn.discordapp.com/emojis/" + client.messages.emojis["play"].replace(/[^0-9]+/g, '')))
|
||||||
|
.setColor(client.config.embedColor)
|
||||||
|
.addFields({
|
||||||
|
name: client.messages.nowplayingTitle,
|
||||||
|
value: client.messages.replace(client.messages.nowplayingDescription, {
|
||||||
|
"%radio.station.name%": radio.station.name,
|
||||||
|
"%radio.station.owner%\n": radio.station.name != radio.station.owner ? radio.station.owner + "\n" : "",
|
||||||
|
"%client.funcs.msToTime(completed)%": "",
|
||||||
|
"**": "",
|
||||||
|
"**:2": ""
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||||
|
.setFooter({
|
||||||
|
text: client.messages.footerText,
|
||||||
|
iconURL: "https://cdn.discordapp.com/emojis/" + client.messages.emojis["eximiabots"].replace(/[^0-9]+/g, '')
|
||||||
|
});
|
||||||
|
|
||||||
|
const buttons = new ActionRowBuilder<ButtonBuilder>()
|
||||||
|
.addComponents(
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setCustomId('list')
|
||||||
|
.setEmoji(client.messages.emojis["list"])
|
||||||
|
.setStyle(ButtonStyle.Secondary)
|
||||||
|
)
|
||||||
|
.addComponents(
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setCustomId('prev')
|
||||||
|
.setEmoji(client.messages.emojis["prev"])
|
||||||
|
.setStyle(ButtonStyle.Secondary)
|
||||||
|
)
|
||||||
|
.addComponents(
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setCustomId('stop')
|
||||||
|
.setEmoji(client.messages.emojis["stop"])
|
||||||
|
.setStyle(ButtonStyle.Secondary)
|
||||||
|
)
|
||||||
|
.addComponents(
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setCustomId('next')
|
||||||
|
.setEmoji(client.messages.emojis["next"])
|
||||||
|
.setStyle(ButtonStyle.Secondary)
|
||||||
|
)
|
||||||
|
.addComponents(
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setCustomId('statistics')
|
||||||
|
.setEmoji(client.messages.emojis["statistics"])
|
||||||
|
.setStyle(ButtonStyle.Secondary)
|
||||||
|
);
|
||||||
|
|
||||||
|
if(!radio.message){
|
||||||
|
radio.message = await radio.textChannel?.send({ embeds: [embed], components: [buttons] });
|
||||||
|
} else {
|
||||||
|
if(radio.textChannel.id == radio.message.channel.id){
|
||||||
|
radio.message.edit({ embeds: [embed], components: [buttons] });
|
||||||
|
} else {
|
||||||
|
radio.message?.delete();
|
||||||
|
radio.message = await radio.textChannel?.send({ embeds: [embed], components: [buttons] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interaction?.reply({
|
||||||
|
content: client.messages.emojis["play"] + client.messages.replace(client.messages.play, {
|
||||||
|
"%radio.station.name%": radio.station.name
|
||||||
|
}),
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
@ -1,18 +0,0 @@
|
|||||||
module.exports = function saveState(client, guild, radio){
|
|
||||||
client.datastore.checkEntry(guild.id);
|
|
||||||
|
|
||||||
let date = new Date();
|
|
||||||
|
|
||||||
let data = client.datastore.getEntry(guild.id);
|
|
||||||
|
|
||||||
data.state = {};
|
|
||||||
data.state.channels = {};
|
|
||||||
data.state.channels.text = radio.textChannel.id;
|
|
||||||
data.state.channels.voice = radio.voiceChannel.id;
|
|
||||||
data.state.date = date.toISOString();
|
|
||||||
data.state.station = {};
|
|
||||||
data.state.station.name = radio.station.name;
|
|
||||||
data.state.station.owner = radio.station.owner;
|
|
||||||
|
|
||||||
client.datastore.updateEntry(guild, data);
|
|
||||||
}
|
|
26
src/client/funcs/saveState.ts
Normal file
26
src/client/funcs/saveState.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { Guild } from "discord.js";
|
||||||
|
import RadioClient from "../../Client";
|
||||||
|
import { radio } from "../classes/Radio";
|
||||||
|
|
||||||
|
export default function saveState(client: RadioClient, guild: Guild, radio: radio){
|
||||||
|
if(!client.datastore) return;
|
||||||
|
client.datastore.checkEntry(guild.id);
|
||||||
|
|
||||||
|
let date = new Date();
|
||||||
|
|
||||||
|
let data = client.datastore.getEntry(guild.id);
|
||||||
|
if(!data) return;
|
||||||
|
data.state = {
|
||||||
|
channels: {
|
||||||
|
text: radio.textChannel?.id,
|
||||||
|
voice: radio.voiceChannel?.id
|
||||||
|
},
|
||||||
|
date: date.toISOString(),
|
||||||
|
station: {
|
||||||
|
name: radio.station.name,
|
||||||
|
owner: radio.station.owner
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
client.datastore.updateEntry(guild, data);
|
||||||
|
}
|
@ -1,22 +0,0 @@
|
|||||||
module.exports = function statisticsUpdate(client, guild, radio) {
|
|
||||||
|
|
||||||
client.datastore.checkEntry(guild.id);
|
|
||||||
|
|
||||||
radio.datastore = client.datastore.getEntry(guild.id);
|
|
||||||
|
|
||||||
if(!radio.datastore.statistics[radio.station.name]){
|
|
||||||
radio.datastore.statistics[radio.station.name] = {};
|
|
||||||
radio.datastore.statistics[radio.station.name].time = 0;
|
|
||||||
radio.datastore.statistics[radio.station.name].used = 0;
|
|
||||||
client.datastore.updateEntry(guild, radio.datastore);
|
|
||||||
}
|
|
||||||
|
|
||||||
let date = new Date();
|
|
||||||
radio.currentTime = date.getTime();
|
|
||||||
radio.playTime = parseInt(radio.currentTime)-parseInt(radio.startTime);
|
|
||||||
radio.datastore.statistics[radio.station.name].time = parseInt(radio.datastore.statistics[radio.station.name].time)+parseInt(radio.playTime);
|
|
||||||
|
|
||||||
radio.datastore.statistics[radio.station.name].used = parseInt(radio.datastore.statistics[radio.station.name].used)+1;
|
|
||||||
client.datastore.updateEntry(guild, radio.datastore);
|
|
||||||
client.datastore.calculateGlobal(client);
|
|
||||||
}
|
|
@ -1,17 +1,29 @@
|
|||||||
module.exports = {
|
export const messages = {
|
||||||
|
replace(message: string, variables: { [key: string]: string }){
|
||||||
|
for(let variable in variables){
|
||||||
|
if(variable.includes('%')){
|
||||||
|
message = message.replace(variable, variables[variable]);
|
||||||
|
} else if(variable.includes(':')){
|
||||||
|
message = message.replace(variable.split(':')[0], variables[variable]);
|
||||||
|
} else {
|
||||||
|
message = message.replace(variable, variables[variable]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
},
|
||||||
wrongVoiceChannel: "You need to be in the same voice channel as RadioX to use this command!",
|
wrongVoiceChannel: "You need to be in the same voice channel as RadioX to use this command!",
|
||||||
noPerms: "You need the %command.permission% permission to use this command!",
|
noPerms: "You need the %command.permission% permission to use this command!",
|
||||||
notPlaying: "There is nothing playing!",
|
notPlaying: "There is nothing playing!",
|
||||||
runningCommandFailed: "Running this command failed!",
|
runningCommandFailed: "Running this command failed!",
|
||||||
noPermsEmbed: "I cannot send embeds (Embed links).",
|
noPermsEmbed: "I cannot send embeds (Embed links).",
|
||||||
bugTitle: "Found a bug with %client.user.username%?",
|
bugTitle: "Found a bug with %client.user.username%?",
|
||||||
bugDescription: "Join the support server \n %client.config.supportGuild%",
|
bugDescription: "Join the support server" + "\n" + "%client.config.supportGuild%",
|
||||||
helpTitle: "%client.user.username% help:",
|
helpTitle: "%client.user.username% help:",
|
||||||
helpDescription: "%commands%",
|
helpDescription: "%commands%",
|
||||||
inviteTitle: "Invite %client.user.username% to your Discord server!",
|
inviteTitle: "Invite %client.user.username% to your Discord server!",
|
||||||
listTitle: "Radio Stations",
|
listTitle: "Radio Stations",
|
||||||
nowplayingTitle: "Now Playing",
|
nowplayingTitle: "Now Playing",
|
||||||
nowplayingDescription: "**%radio.station.name%** \n Owner: %radio.station.owner% \n %client.funcs.msToTime(completed)%",
|
nowplayingDescription: "**%radio.station.name%**" + "\n" + "%radio.station.owner%" + "\n" + "%client.funcs.msToTime(completed)%",
|
||||||
noVoiceChannel: "You need to be in a voice channel to play radio!",
|
noVoiceChannel: "You need to be in a voice channel to play radio!",
|
||||||
noQuery: "You need to use a number or search for a supported station!",
|
noQuery: "You need to use a number or search for a supported station!",
|
||||||
noPermsConnect: "I cannot connect to your voice channel.",
|
noPermsConnect: "I cannot connect to your voice channel.",
|
||||||
@ -29,12 +41,23 @@ module.exports = {
|
|||||||
sendedMaintenanceMessage: "This bot is going to be under maintenance!",
|
sendedMaintenanceMessage: "This bot is going to be under maintenance!",
|
||||||
footerText: "EximiaBots by Warén Group",
|
footerText: "EximiaBots by Warén Group",
|
||||||
statusTitle: "%client.user.username% Status",
|
statusTitle: "%client.user.username% Status",
|
||||||
statusField1: "Bot Latency",
|
statusField1: ":clock1: Bot Uptime",
|
||||||
statusField2: "API Latency",
|
statusField2: ":floppy_disk: Bot Version",
|
||||||
statusField3: "Uptime",
|
statusField3: ":heartbeat: WebSocket Ping",
|
||||||
statusField4: "Version",
|
statusField4: ":hourglass: Latency",
|
||||||
statusField5: "Hosted by",
|
statusField5: ":globe_with_meridians: Hosted by",
|
||||||
errorStationURL: "Station can't be URL",
|
errorStationURL: "Station can't be URL",
|
||||||
messageCommandsDeprecatedTitle: "%client.user.username%",
|
maintenance: "Shhhh... We are now sleeping and dreaming about new features to implement. Will be back soon.",
|
||||||
messageCommandsDeprecatedDescription: "We recommend you to reauthorize our bot by clicking the invite link down below, because Discord is planning to remove message content from verified bots [Read More](https://support-dev.discord.com/hc/en-us/articles/4404772028055) \n\n **Invite Bot** \n https://wgi.fi/radiox_invite \n\n This bot now supports slash commands, you should start using them instead. Type / into the message box and select the bot you wish to use. Remember to be careful as there are a few bugs here and there on Discord. \n\n We will remove this deprecation message in March of 2022 when RadioX 1.0.0 is released."
|
emojis: {
|
||||||
};
|
logo: "<:RadioX:688765708808487072>",
|
||||||
|
eximiabots: "<:EximiaBots:693277919929303132>",
|
||||||
|
list: "<:RadioXList:688541155519889482>",
|
||||||
|
play: "<:RadioXPlay:688541155712827458>",
|
||||||
|
stop: "<:RadioXStop:688541155377414168>",
|
||||||
|
statistics: "<:RadioXStatistics:694954485507686421>",
|
||||||
|
maintenance: "<:RadioXMaintenance:695043843057254493>",
|
||||||
|
error: "<:RadioXError:688541155792781320>",
|
||||||
|
prev: "<:RadioXPrev:882153637370023957>",
|
||||||
|
next: "<:RadioXNext:882153637474893834>"
|
||||||
|
}
|
||||||
|
};
|
@ -1,207 +0,0 @@
|
|||||||
import Discord from "discord.js";
|
|
||||||
const {
|
|
||||||
createAudioPlayer,
|
|
||||||
createAudioResource,
|
|
||||||
getVoiceConnection,
|
|
||||||
joinVoiceChannel
|
|
||||||
} = require("@discordjs/voice");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
async execute(client, guilds) {
|
|
||||||
guilds.forEach(async guild => {
|
|
||||||
let state = client.funcs.loadState(client, guild);
|
|
||||||
if(!state) return;
|
|
||||||
if(!state.station || !state.channels.voice || !state.channels.text) return;
|
|
||||||
|
|
||||||
const sstation = await searchStation(state.station.name, client);
|
|
||||||
let url = sstation.stream[sstation.stream.default];
|
|
||||||
let station = sstation;
|
|
||||||
|
|
||||||
const construct = {
|
|
||||||
textChannel: client.channels.cache.get(state.channels.text),
|
|
||||||
voiceChannel: client.channels.cache.get(state.channels.voice),
|
|
||||||
connection: null,
|
|
||||||
message: null,
|
|
||||||
audioPlayer: createAudioPlayer(),
|
|
||||||
station: station
|
|
||||||
};
|
|
||||||
client.radio.set(guild.id, construct);
|
|
||||||
|
|
||||||
try {
|
|
||||||
let voiceChannel = client.channels.cache.get(state.channels.voice);
|
|
||||||
const connection =
|
|
||||||
getVoiceConnection(guild.id) ??
|
|
||||||
joinVoiceChannel({
|
|
||||||
channelId: voiceChannel.id,
|
|
||||||
guildId: voiceChannel.guild.id,
|
|
||||||
adapterCreator: voiceChannel.guild.voiceAdapterCreator
|
|
||||||
});
|
|
||||||
|
|
||||||
construct.connection = connection;
|
|
||||||
let date = new Date();
|
|
||||||
construct.startTime = date.getTime();
|
|
||||||
|
|
||||||
play(null, guild, client, url, Discord);
|
|
||||||
|
|
||||||
client.datastore.checkEntry(guild.id);
|
|
||||||
construct.datastore = client.datastore.getEntry(guild.id);
|
|
||||||
|
|
||||||
if (!construct.datastore.statistics[construct.station.name]) {
|
|
||||||
construct.datastore.statistics[construct.station.name] = {};
|
|
||||||
construct.datastore.statistics[construct.station.name].time = 0;
|
|
||||||
construct.datastore.statistics[construct.station.name].used = 0;
|
|
||||||
client.datastore.updateEntry(guild, construct.datastore);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function play(interaction, guild, client, url, Discord) {
|
|
||||||
let message = {};
|
|
||||||
const radio = client.radio.get(guild.id);
|
|
||||||
const resource = createAudioResource(url);
|
|
||||||
radio.connection.subscribe(radio.audioPlayer);
|
|
||||||
radio.audioPlayer.play(resource);
|
|
||||||
resource.playStream
|
|
||||||
.on("readable", () => {
|
|
||||||
client.funcs.logger('Radio', 'Stream started' + " / " + guild.id + " / " + radio.station.name);
|
|
||||||
})
|
|
||||||
.on("finish", () => {
|
|
||||||
client.funcs.logger('Radio', 'Stream finished' + " / " + guild.id);
|
|
||||||
client.funcs.statisticsUpdate(client, guild, radio);
|
|
||||||
radio.connection?.destroy();
|
|
||||||
radio.audioPlayer?.stop();
|
|
||||||
client.radio.delete(guild.id);
|
|
||||||
return;
|
|
||||||
})
|
|
||||||
.on("error", error => {
|
|
||||||
client.funcs.logger('Radio', 'Stream errored');
|
|
||||||
console.error(error);
|
|
||||||
radio.connection?.destroy();
|
|
||||||
radio.audioPlayer?.stop();
|
|
||||||
client.radio.delete(guild.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
message.nowplayingDescription = client.messages.nowplayingDescription.replace("%radio.station.name%", radio.station.name);
|
|
||||||
message.nowplayingDescription = message.nowplayingDescription.replace("%radio.station.owner%", radio.station.owner);
|
|
||||||
message.nowplayingDescription = message.nowplayingDescription.replace("%client.funcs.msToTime(completed)%", "");
|
|
||||||
message.nowplayingDescription = message.nowplayingDescription.replace("Owner: ", "");
|
|
||||||
message.nowplayingDescription = message.nowplayingDescription.replace("**", "");
|
|
||||||
message.nowplayingDescription = message.nowplayingDescription.replace("**", "");
|
|
||||||
|
|
||||||
const embed = new Discord.MessageEmbed()
|
|
||||||
.setTitle(client.user.username)
|
|
||||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["play"].replace(/[^0-9]+/g, ''))
|
|
||||||
.setColor(client.config.embedColor)
|
|
||||||
.addField(client.messages.nowplayingTitle, message.nowplayingDescription, true)
|
|
||||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
|
||||||
|
|
||||||
const buttons = new Discord.MessageActionRow()
|
|
||||||
.addComponents(
|
|
||||||
new Discord.MessageButton()
|
|
||||||
.setCustomId('list')
|
|
||||||
.setEmoji(client.messageEmojis["list"])
|
|
||||||
.setStyle('SECONDARY')
|
|
||||||
)
|
|
||||||
.addComponents(
|
|
||||||
new Discord.MessageButton()
|
|
||||||
.setCustomId('prev')
|
|
||||||
.setEmoji(client.messageEmojis["prev"])
|
|
||||||
.setStyle('SECONDARY')
|
|
||||||
.setDisabled(true)
|
|
||||||
)
|
|
||||||
.addComponents(
|
|
||||||
new Discord.MessageButton()
|
|
||||||
.setCustomId('stop')
|
|
||||||
.setEmoji(client.messageEmojis["stop"])
|
|
||||||
.setStyle('SECONDARY')
|
|
||||||
)
|
|
||||||
.addComponents(
|
|
||||||
new Discord.MessageButton()
|
|
||||||
.setCustomId('next')
|
|
||||||
.setEmoji(client.messageEmojis["next"])
|
|
||||||
.setStyle('SECONDARY')
|
|
||||||
.setDisabled(true)
|
|
||||||
)
|
|
||||||
.addComponents(
|
|
||||||
new Discord.MessageButton()
|
|
||||||
.setCustomId('statistics')
|
|
||||||
.setEmoji(client.messageEmojis["statistics"])
|
|
||||||
.setStyle('SECONDARY')
|
|
||||||
);
|
|
||||||
|
|
||||||
if(!radio.message){
|
|
||||||
radio.message = await radio.textChannel.send({ embeds: [embed], components: [buttons] });
|
|
||||||
} else {
|
|
||||||
radio.message.edit({ embeds: [embed], components: [buttons] });
|
|
||||||
}
|
|
||||||
|
|
||||||
message.play = client.messages.play.replace("%radio.station.name%", radio.station.name);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function searchStation(key, client) {
|
|
||||||
if (client.stations === null) return false;
|
|
||||||
let foundStations = [];
|
|
||||||
if (!key) return false;
|
|
||||||
if (key == "radio") return false;
|
|
||||||
|
|
||||||
client.stations
|
|
||||||
.filter(
|
|
||||||
x => x.name.toUpperCase().includes(key.toUpperCase()) || x === key
|
|
||||||
)
|
|
||||||
.forEach(x =>
|
|
||||||
foundStations.push({ station: x, name: x.name, probability: 100 })
|
|
||||||
);
|
|
||||||
|
|
||||||
if (key.startsWith("radio ")) key = key.slice(6);
|
|
||||||
const probabilityIncrement = 100 / key.split(" ").length / 2;
|
|
||||||
for (let i = 0; i < key.split(" ").length; i++) {
|
|
||||||
client.stations
|
|
||||||
.filter(
|
|
||||||
x => x.name.toUpperCase().includes(key.split(" ")[i].toUpperCase()) || x === key
|
|
||||||
)
|
|
||||||
.forEach(x =>
|
|
||||||
foundStations.push({ station: x, name: x.name, probability: probabilityIncrement })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (foundStations.length === 0) return false;
|
|
||||||
for (let i = 0; i < foundStations.length; i++) {
|
|
||||||
for (let j = 0; j < foundStations.length; j++) {
|
|
||||||
if (foundStations[i] === foundStations[j] && i !== j) foundStations.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let i = 0; i < foundStations.length; i++) {
|
|
||||||
if (foundStations[i].name.length > key.length) {
|
|
||||||
foundStations[i].probability -=
|
|
||||||
(foundStations[i].name.split(" ").length - key.split(" ").length) *
|
|
||||||
(probabilityIncrement * 0.5);
|
|
||||||
} else if (foundStations[i].name.length === key.length) {
|
|
||||||
foundStations[i].probability += probabilityIncrement * 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let j = 0; j < key.split(" ").length; j++) {
|
|
||||||
if (!foundStations[i].name.toUpperCase().includes(key.toUpperCase().split(" ")[j])) {
|
|
||||||
foundStations[i].probability -= probabilityIncrement * 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let highestProbabilityStation;
|
|
||||||
for (let i = 0; i < foundStations.length; i++) {
|
|
||||||
if (
|
|
||||||
!highestProbabilityStation ||
|
|
||||||
highestProbabilityStation.probability < foundStations[i].probability
|
|
||||||
)
|
|
||||||
highestProbabilityStation = foundStations[i];
|
|
||||||
if (
|
|
||||||
highestProbabilityStation &&
|
|
||||||
highestProbabilityStation.probability === foundStations[i].probability
|
|
||||||
) {
|
|
||||||
highestProbabilityStation = foundStations[i].station;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return highestProbabilityStation;
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
export interface command { }
|
|
||||||
|
|
||||||
export interface radio {}
|
|
@ -1,26 +0,0 @@
|
|||||||
require('dotenv/config');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
|
|
||||||
//credentials
|
|
||||||
token: process.env.DISCORD_TOKEN,
|
|
||||||
|
|
||||||
//radio stations
|
|
||||||
stationslistUrl: process.env.RADIOX_STATIONSLISTURL || "https://gitea.cwinfo.org/cwchristerw/radio/raw/branch/master/playlist.json",
|
|
||||||
|
|
||||||
//support
|
|
||||||
supportGuild: "https://discord.gg/rRA65Mn",
|
|
||||||
devId: [
|
|
||||||
"493174343484833802",
|
|
||||||
"360363051792203779"
|
|
||||||
],
|
|
||||||
|
|
||||||
//misc
|
|
||||||
embedColor: "#88aa00",
|
|
||||||
hostedBy: "[Warén Group](https://waren.io)",
|
|
||||||
|
|
||||||
//Settings
|
|
||||||
version: process.env.RADIOX_VERSION || process.env.npm_package_version,
|
|
||||||
debug: process.env.DEBUG_MODE || false
|
|
||||||
|
|
||||||
}
|
|
28
src/config.ts
Normal file
28
src/config.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { ColorResolvable } from "discord.js";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
//credentials
|
||||||
|
token: process.env.DISCORD_TOKEN,
|
||||||
|
|
||||||
|
//radio stations
|
||||||
|
stationslistUrl: process.env.RADIOX_STATIONSLISTURL || "https://git.cwinfo.net/cwchristerw/radio/raw/branch/master/playlist.json",
|
||||||
|
|
||||||
|
//support
|
||||||
|
supportGuild: "https://discord.gg/rRA65Mn",
|
||||||
|
devIDs: [
|
||||||
|
"493174343484833802",
|
||||||
|
"360363051792203779"
|
||||||
|
],
|
||||||
|
|
||||||
|
//misc
|
||||||
|
embedColor: "#88aa00" as ColorResolvable,
|
||||||
|
hostedBy: "[Warén Group](https://waren.io)",
|
||||||
|
|
||||||
|
//Settings
|
||||||
|
version: process.env.DEV_MODE ? (process.env.npm_package_version ?? "0.0.0") + "-dev" : process.env.npm_package_version ?? "-",
|
||||||
|
debug: process.env.DEBUG_MODE || false,
|
||||||
|
devMode: process.env.DEV_MODE || false,
|
||||||
|
maintenanceMode: false,
|
||||||
|
streamerMode: process.env.STREAMER_MODE || "manual"
|
||||||
|
}
|
@ -1,3 +0,0 @@
|
|||||||
const { default: RadioClient } = require("./Client");
|
|
||||||
|
|
||||||
const client = new RadioClient();
|
|
3
src/index.ts
Normal file
3
src/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import RadioClient from "./Client";
|
||||||
|
|
||||||
|
new RadioClient();
|
Reference in New Issue
Block a user