├── .gitignore
├── FodyWeavers.xml
├── FodyWeavers.xsd
├── NeteaseCloudMusicApi.cs
├── README.md
├── YunPlugin-UNM.cs
├── YunPlugin-UNM.csproj
├── YunSettings.ini
├── docker
└── Dockerfile
└── lib
├── Nini.dll
├── TS3AudioBot.dll
└── TSLib.dll
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the
--------------------------------------------------------------------------------
/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/FodyWeavers.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks
13 |
14 |
15 |
16 |
17 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.
18 |
19 |
20 |
21 |
22 | A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks
23 |
24 |
25 |
26 |
27 | A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.
28 |
29 |
30 |
31 |
32 | A list of unmanaged 32 bit assembly names to include, delimited with line breaks.
33 |
34 |
35 |
36 |
37 | A list of unmanaged 64 bit assembly names to include, delimited with line breaks.
38 |
39 |
40 |
41 |
42 | The order of preloaded assemblies, delimited with line breaks.
43 |
44 |
45 |
46 |
47 |
48 | This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.
49 |
50 |
51 |
52 |
53 | Controls if .pdbs for reference assemblies are also embedded.
54 |
55 |
56 |
57 |
58 | Controls if runtime assemblies are also embedded.
59 |
60 |
61 |
62 |
63 | Controls whether the runtime assemblies are embedded with their full path or only with their assembly name.
64 |
65 |
66 |
67 |
68 | Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.
69 |
70 |
71 |
72 |
73 | As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.
74 |
75 |
76 |
77 |
78 | Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.
79 |
80 |
81 |
82 |
83 | Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.
84 |
85 |
86 |
87 |
88 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |
89 |
90 |
91 |
92 |
93 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.
94 |
95 |
96 |
97 |
98 | A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with |
99 |
100 |
101 |
102 |
103 | A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |.
104 |
105 |
106 |
107 |
108 | A list of unmanaged 32 bit assembly names to include, delimited with |.
109 |
110 |
111 |
112 |
113 | A list of unmanaged 64 bit assembly names to include, delimited with |.
114 |
115 |
116 |
117 |
118 | The order of preloaded assemblies, delimited with |.
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
127 |
128 |
129 |
130 |
131 | A comma-separated list of error codes that can be safely ignored in assembly verification.
132 |
133 |
134 |
135 |
136 | 'false' to turn off automatic generation of the XML Schema file.
137 |
138 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace NeteaseCloudMusicApi
4 | {
5 | public class ArtistsItem
6 | {
7 | public long id { get; set; }
8 |
9 | public string name { get; set; }
10 |
11 | public string picUrl;
12 |
13 | public List @alias;
14 |
15 | public long albumSize;
16 |
17 | public long picId;
18 |
19 | public string fansGroup;
20 |
21 | public string img1v1Url;
22 |
23 | public long img1v1;
24 |
25 | public string trans;
26 | }
27 |
28 | public class Artist
29 | {
30 |
31 | public long id;
32 |
33 | public string name;
34 |
35 | public string picUrl;
36 |
37 | public List @alias;
38 |
39 | public long albumSize;
40 |
41 | public long picId;
42 |
43 | public string fansGroup;
44 |
45 | public string img1v1Url;
46 |
47 | public long img1v1;
48 |
49 | public string trans;
50 | }
51 |
52 | public class Album
53 | {
54 |
55 | public long id;
56 |
57 | public string name;
58 |
59 | public Artist artist;
60 |
61 | public long publishTime;
62 |
63 | public long size;
64 |
65 | public long copyrightId;
66 |
67 | public long status;
68 |
69 | public long picId;
70 |
71 | public long mark;
72 | }
73 |
74 | public class SongsItem
75 | {
76 |
77 | public long id { get; set; }
78 |
79 | public string name { get; set; }
80 |
81 | public List artists { get; set; }
82 |
83 | public Album album;
84 |
85 | public long duration;
86 |
87 | public long copyrightId;
88 |
89 | public long status;
90 |
91 | public List @alias;
92 |
93 | public long rtype;
94 |
95 | public long ftype;
96 |
97 | public long mvid;
98 |
99 | public long fee;
100 |
101 | public string rUrl;
102 |
103 | public long mark;
104 | }
105 |
106 | public class Result
107 | {
108 |
109 | public List songs { get; set; }
110 |
111 | public bool hasMore;
112 |
113 | public long songCount;
114 | }
115 |
116 | public class yunSearchSong
117 | {
118 |
119 | public Result result { get; set; }
120 |
121 | public long code;
122 | }
123 |
124 | public class FreeTrialInfo
125 | {
126 |
127 | public long start;
128 |
129 | public long end;
130 | }
131 |
132 | public class FreeTrialPrivilege
133 | {
134 |
135 | public bool resConsumable;
136 |
137 | public bool userConsumable;
138 |
139 | public string listenType;
140 |
141 | public string cannotListenReason;
142 | }
143 |
144 | public class FreeTimeTrialPrivilege
145 | {
146 |
147 | public bool resConsumable;
148 |
149 | public bool userConsumable;
150 |
151 | public long type;
152 |
153 | public long remalongime;
154 | }
155 |
156 | public class DataItem
157 | {
158 |
159 | public long id;
160 |
161 | public string url { get; set; }
162 |
163 | public long br;
164 |
165 | public long size;
166 |
167 | public string md5;
168 |
169 | public long code;
170 |
171 | public long expi;
172 |
173 | public string type;
174 |
175 | public double gain;
176 |
177 | public double peak;
178 |
179 | public long fee;
180 |
181 | public string uf;
182 |
183 | public long payed;
184 |
185 | public long flag;
186 |
187 | public bool canExtend;
188 |
189 | public FreeTrialInfo freeTrialInfo;
190 |
191 | public string level;
192 |
193 | public string encodeType;
194 |
195 | public FreeTrialPrivilege freeTrialPrivilege;
196 |
197 | public FreeTimeTrialPrivilege freeTimeTrialPrivilege;
198 |
199 | public long urlSource;
200 |
201 | public long rightSource;
202 |
203 | public string podcastCtrp;
204 |
205 | public string effectTypes;
206 |
207 | public long time;
208 | }
209 |
210 | public class musicURL
211 | {
212 |
213 | public List data { get; set; }
214 |
215 | public long code { get; set; }
216 | }
217 |
218 | public class ArItem
219 | {
220 |
221 | public long id;
222 |
223 | public string name;
224 |
225 | public List tns;
226 |
227 | public List @alias;
228 | }
229 |
230 | public class Al
231 | {
232 |
233 | public long id;
234 |
235 | public string name;
236 |
237 | public string picUrl;
238 |
239 | public List tns;
240 |
241 | public long pic;
242 | }
243 |
244 | public class H
245 | {
246 |
247 | public long br;
248 |
249 | public long fid;
250 |
251 | public long size;
252 |
253 | public long vd;
254 |
255 | public long sr;
256 | }
257 |
258 | public class M
259 | {
260 |
261 | public long br;
262 |
263 | public long fid;
264 |
265 | public long size;
266 |
267 | public long vd;
268 |
269 | public long sr;
270 | }
271 |
272 | public class L
273 | {
274 |
275 | public long br;
276 |
277 | public long fid;
278 |
279 | public long size;
280 |
281 | public long vd;
282 |
283 | public long sr;
284 | }
285 |
286 | public class SongsItems
287 | {
288 |
289 | public string name { get; set; }
290 |
291 | public long id { get; set; }
292 |
293 | public long pst;
294 |
295 | public long t;
296 |
297 | public List ar;
298 |
299 | public List alia;
300 |
301 | public long pop;
302 |
303 | public long st;
304 |
305 | public string rt;
306 |
307 | public long fee;
308 |
309 | public long v;
310 |
311 | public string crbt;
312 |
313 | public string cf;
314 |
315 | public Al al;
316 |
317 | public long dt;
318 |
319 | public H h;
320 |
321 | public M m;
322 |
323 | public L l;
324 |
325 | public string sq;
326 |
327 | public string hr;
328 |
329 | public string a;
330 |
331 | public string cd;
332 |
333 | public long no;
334 |
335 | public string rtUrl;
336 |
337 | public long ftype;
338 |
339 | public List rtUrls;
340 |
341 | public long djId;
342 |
343 | public long copyright;
344 |
345 | public long s_id;
346 |
347 | public long mark;
348 |
349 | public long originCoverType;
350 |
351 | public string originSongSimpleData;
352 |
353 | public string tagPicList;
354 |
355 | public bool resourceState;
356 |
357 | public long version;
358 |
359 | public string songJumpInfo;
360 |
361 | public string entertainmentTags;
362 |
363 | public string awardTags;
364 |
365 | public long single;
366 |
367 | public bool noCopyrightRcmd;
368 |
369 | public long mst;
370 |
371 | public long cp;
372 |
373 | public long rtype;
374 |
375 | public string rurl;
376 |
377 | public long mv;
378 |
379 | public long publishTime;
380 | }
381 |
382 | public class FreeTrial
383 | {
384 |
385 | public bool resConsumable;
386 |
387 | public bool userConsumable;
388 |
389 | public string listenType;
390 | }
391 |
392 | public class ChargeInfoListItem
393 | {
394 |
395 | public long rate;
396 |
397 | public string chargeUrl;
398 |
399 | public string chargeMessage;
400 |
401 | public long chargeType;
402 | }
403 |
404 | public class PrivilegesItem
405 | {
406 |
407 | public long id;
408 |
409 | public long fee;
410 |
411 | public long payed;
412 |
413 | public long st;
414 |
415 | public long pl;
416 |
417 | public long dl;
418 |
419 | public long sp;
420 |
421 | public long cp;
422 |
423 | public long subp;
424 |
425 | public bool cs;
426 |
427 | public long maxbr;
428 |
429 | public long fl;
430 |
431 | public bool toast;
432 |
433 | public long flag;
434 |
435 | public bool preSell;
436 |
437 | public long playMaxbr;
438 |
439 | public long downloadMaxbr;
440 |
441 | public string maxBrLevel;
442 |
443 | public string playMaxBrLevel;
444 |
445 | public string downloadMaxBrLevel;
446 |
447 | public string plLevel;
448 |
449 | public string dlLevel;
450 |
451 | public string flLevel;
452 |
453 | public string rscl;
454 |
455 | public FreeTrial freeTrialPrivilege;
456 |
457 | public List chargeInfoList;
458 | }
459 |
460 | public class GeDan
461 | {
462 |
463 | public List songs { get; set; }
464 |
465 | public List privileges;
466 |
467 | public long code;
468 | }
469 |
470 | public class Creator
471 | {
472 | ///
473 | /// 淋雨丶伞
474 | ///
475 | public string nickname;
476 |
477 | public long userId;
478 |
479 | public long userType;
480 |
481 | public string avatarUrl;
482 |
483 | public long authStatus;
484 |
485 | public string expertTags;
486 |
487 | public string experts;
488 | }
489 |
490 | public class ArtistsItems
491 | {
492 |
493 | public string name;
494 |
495 | public long id;
496 |
497 | public long picId;
498 |
499 | public long img1v1Id;
500 |
501 | public string briefDesc;
502 |
503 | public string picUrl;
504 |
505 | public string img1v1Url;
506 |
507 | public long albumSize;
508 |
509 | public List @alias;
510 |
511 | public string trans;
512 |
513 | public long musicSize;
514 | }
515 |
516 | public class Artists
517 | {
518 |
519 | public string name;
520 |
521 | public long id;
522 |
523 | public long picId;
524 |
525 | public long img1v1Id;
526 |
527 | public string briefDesc;
528 |
529 | public string picUrl;
530 |
531 | public string img1v1Url;
532 |
533 | public long albumSize;
534 |
535 | public List @alias;
536 |
537 | public string trans;
538 |
539 | public long musicSize;
540 | }
541 |
542 | public class ArtistsItemss
543 | {
544 |
545 | public string name;
546 |
547 | public long id;
548 |
549 | public long picId;
550 |
551 | public long img1v1Id;
552 |
553 | public string briefDesc;
554 |
555 | public string picUrl;
556 |
557 | public string img1v1Url;
558 |
559 | public long albumSize;
560 |
561 | public List @alias;
562 |
563 | public string trans;
564 |
565 | public long musicSize;
566 | }
567 |
568 | public class Albums
569 | {
570 |
571 | public string name;
572 |
573 | public long id;
574 |
575 | public string idStr;
576 | ///
577 | /// 专辑
578 | ///
579 | public string type;
580 |
581 | public long size;
582 |
583 | public long picId;
584 |
585 | public string blurPicUrl;
586 |
587 | public long companyId;
588 |
589 | public long pic;
590 |
591 | public string picUrl;
592 |
593 | public long publishTime;
594 |
595 | public string description;
596 |
597 | public string tags;
598 |
599 | public string company;
600 |
601 | public string briefDesc;
602 |
603 | public Artist artist;
604 |
605 | public List songs;
606 |
607 | public List @alias;
608 |
609 | public long status;
610 |
611 | public long copyrightId;
612 |
613 | public string commentThreadId;
614 |
615 | public List artists;
616 | }
617 |
618 | public class BMusic
619 | {
620 |
621 | public string name;
622 |
623 | public long id;
624 |
625 | public long size;
626 |
627 | public string extension;
628 |
629 | public long sr;
630 |
631 | public long dfsId;
632 |
633 | public long bitrate;
634 |
635 | public long playTime;
636 |
637 | public long volumeDelta;
638 | }
639 |
640 | public class HMusic
641 | {
642 |
643 | public string name;
644 |
645 | public long id;
646 |
647 | public long size;
648 |
649 | public string extension;
650 |
651 | public long sr;
652 |
653 | public long dfsId;
654 |
655 | public long bitrate;
656 |
657 | public long playTime;
658 |
659 | public long volumeDelta;
660 | }
661 |
662 | public class MMusic
663 | {
664 |
665 | public string name;
666 |
667 | public long id;
668 |
669 | public long size;
670 |
671 | public string extension;
672 |
673 | public long sr;
674 |
675 | public long dfsId;
676 |
677 | public long bitrate;
678 |
679 | public long playTime;
680 |
681 | public long volumeDelta;
682 | }
683 |
684 | public class LMusic
685 | {
686 |
687 | public string name;
688 |
689 | public long id;
690 |
691 | public long size;
692 |
693 | public string extension;
694 |
695 | public long sr;
696 |
697 | public long dfsId;
698 |
699 | public long bitrate;
700 |
701 | public long playTime;
702 |
703 | public long volumeDelta;
704 | }
705 |
706 | public class Track
707 | {
708 |
709 | public string name;
710 |
711 | public long id;
712 |
713 | public long position;
714 |
715 | public List @alias;
716 |
717 | public long status;
718 |
719 | public long fee;
720 |
721 | public long copyrightId;
722 |
723 | public string disc;
724 |
725 | public long no;
726 |
727 | public List artists;
728 |
729 | public Albums album;
730 |
731 | public bool starred;
732 |
733 | public long popularity;
734 |
735 | public long score;
736 |
737 | public long starredNum;
738 |
739 | public long duration;
740 |
741 | public long playedNum;
742 |
743 | public long dayPlays;
744 |
745 | public long hearTime;
746 |
747 | public string ringtone;
748 |
749 | public string crbt;
750 |
751 | public string audition;
752 |
753 | public string copyFrom;
754 |
755 | public string commentThreadId;
756 |
757 | public string rtUrl;
758 |
759 | public long ftype;
760 |
761 | public List rtUrls;
762 |
763 | public long copyright;
764 |
765 | public long rtype;
766 |
767 | public string rurl;
768 |
769 | public BMusic bMusic;
770 |
771 | public string mp3Url;
772 |
773 | public long mvid;
774 |
775 | public HMusic hMusic;
776 |
777 | public MMusic mMusic;
778 |
779 | public LMusic lMusic;
780 | }
781 |
782 | public class PlaylistsItem
783 | {
784 |
785 | public long id { get; set; }
786 |
787 | public string name;
788 |
789 | public string coverImgUrl;
790 |
791 | public Creator creator;
792 |
793 | public bool subscribed;
794 |
795 | public long trackCount;
796 |
797 | public long userId;
798 |
799 | public long playCount;
800 |
801 | public long bookCount;
802 |
803 | public long specialType;
804 |
805 | public List officialTags;
806 |
807 | public string action;
808 |
809 | public string actionType;
810 |
811 | public string recommendText;
812 |
813 | public string score;
814 |
815 | public string description;
816 |
817 | public bool highQuality;
818 |
819 | public Track track;
820 |
821 | public string alg;
822 | }
823 |
824 | public class Results
825 | {
826 |
827 | public List playlists { get; set; }
828 |
829 | public bool hasMore;
830 |
831 | public List hlWords;
832 |
833 | public long playlistCount;
834 |
835 | public string searchQcReminder;
836 | }
837 |
838 | public class SearchGedan
839 | {
840 |
841 | public Results result { get; set; }
842 |
843 | public long code;
844 | }
845 |
846 | public class Data
847 | {
848 |
849 | public int code;
850 |
851 | public string unikey { get; set; }
852 | }
853 |
854 | public class LoginKey
855 | {
856 |
857 | public Data data { get; set; }
858 |
859 | public int code;
860 | }
861 |
862 | public class Datas
863 | {
864 |
865 | public string qrurl;
866 |
867 | public string qrimg { get; set; }
868 | }
869 |
870 | public class LoginImg
871 | {
872 |
873 | public int code;
874 |
875 | public Datas data { get; set; }
876 | }
877 |
878 | public class Status1
879 | {
880 |
881 | public long code { get; set; }
882 |
883 | public string message { get; set; }
884 |
885 | public string cookie { get; set; }
886 | }
887 |
888 | public class SubscribersItem
889 | {
890 |
891 | public string defaultAvatar;
892 |
893 | public int province;
894 |
895 | public int authStatus;
896 |
897 | public string followed;
898 |
899 | public string avatarUrl;
900 |
901 | public int accountStatus;
902 |
903 | public int gender;
904 |
905 | public int city;
906 |
907 | public int birthday;
908 |
909 | public long userId;
910 |
911 | public int userType;
912 |
913 | public string nickname;
914 |
915 | public string signature;
916 |
917 | public string description;
918 |
919 | public string detailDescription;
920 |
921 | public int avatarImgId;
922 |
923 | public int backgroundImgId;
924 |
925 | public string backgroundUrl;
926 |
927 | public int authority;
928 |
929 | public string mutual;
930 |
931 | public string expertTags;
932 |
933 | public string experts;
934 |
935 | public int djStatus;
936 |
937 | public int vipType;
938 |
939 | public string remarkName;
940 |
941 | public int authenticationTypes;
942 |
943 | public string avatarDetail;
944 |
945 | public string avatarImgIdStr;
946 |
947 | public string backgroundImgIdStr;
948 |
949 | public string anchor;
950 |
951 | public string avatarImgId_str;
952 | }
953 |
954 | public class AvatarDetail
955 | {
956 |
957 | public int userType;
958 |
959 | public int identityLevel;
960 |
961 | public string identityIconUrl;
962 | }
963 |
964 | public class Creators
965 | {
966 |
967 | public string defaultAvatar;
968 |
969 | public int province;
970 |
971 | public int authStatus;
972 |
973 | public string followed;
974 |
975 | public string avatarUrl;
976 |
977 | public int accountStatus;
978 |
979 | public int gender;
980 |
981 | public int city;
982 |
983 | public int birthday;
984 |
985 | public long userId;
986 |
987 | public int userType;
988 |
989 | public string nickname;
990 |
991 | public string signature;
992 |
993 | public string description;
994 |
995 | public string detailDescription;
996 |
997 | public int avatarImgId;
998 |
999 | public int backgroundImgId;
1000 |
1001 | public string backgroundUrl;
1002 |
1003 | public int authority;
1004 |
1005 | public string mutual;
1006 |
1007 | public string expertTags;
1008 |
1009 | public string experts;
1010 |
1011 | public int djStatus;
1012 |
1013 | public int vipType;
1014 |
1015 | public string remarkName;
1016 |
1017 | public int authenticationTypes;
1018 |
1019 | public AvatarDetail avatarDetail;
1020 |
1021 | public string avatarImgIdStr;
1022 |
1023 | public string backgroundImgIdStr;
1024 |
1025 | public string anchor;
1026 |
1027 | public string avatarImgId_str;
1028 | }
1029 |
1030 | public class Sq
1031 | {
1032 |
1033 | public int br;
1034 |
1035 | public int fid;
1036 |
1037 | public int size;
1038 |
1039 | public int vd;
1040 |
1041 | public int sr;
1042 | }
1043 |
1044 | public class TracksItem
1045 | {
1046 |
1047 | public string name;
1048 |
1049 | public int id;
1050 |
1051 | public int pst;
1052 |
1053 | public int t;
1054 |
1055 | public List ar;
1056 |
1057 | public List alia;
1058 |
1059 | public int pop;
1060 |
1061 | public int st;
1062 |
1063 | public string rt;
1064 |
1065 | public int fee;
1066 |
1067 | public int v;
1068 |
1069 | public string crbt;
1070 |
1071 | public string cf;
1072 |
1073 | public Al al;
1074 |
1075 | public int dt;
1076 |
1077 | public H h;
1078 |
1079 | public M m;
1080 |
1081 | public L l;
1082 |
1083 | public Sq sq;
1084 |
1085 | public string hr;
1086 |
1087 | public string a;
1088 |
1089 | public string cd;
1090 |
1091 | public int no;
1092 |
1093 | public string rtUrl;
1094 |
1095 | public int ftype;
1096 |
1097 | public List rtUrls;
1098 |
1099 | public int djId;
1100 |
1101 | public int copyright;
1102 |
1103 | public int s_id;
1104 |
1105 | public int mark;
1106 |
1107 | public int originCoverType;
1108 |
1109 |
1110 | public string originSongSimpleData;
1111 |
1112 | public string tagPicList;
1113 |
1114 | public bool resourceState;
1115 |
1116 | public int version;
1117 |
1118 | public string songJumpInfo;
1119 |
1120 | public string entertainmentTags;
1121 |
1122 | public int single;
1123 |
1124 | public string noCopyrightRcmd;
1125 |
1126 | public string rurl;
1127 |
1128 | public int mst;
1129 |
1130 | public int cp;
1131 |
1132 | public int mv;
1133 |
1134 | public int rtype;
1135 |
1136 | public long publishTime;
1137 | }
1138 |
1139 | public class TrackIdsItem
1140 | {
1141 |
1142 | public int id;
1143 |
1144 | public int v;
1145 |
1146 | public int t;
1147 |
1148 | public int at;
1149 |
1150 | public string alg;
1151 |
1152 | public int uid;
1153 |
1154 | public string rcmdReason;
1155 |
1156 | public string sc;
1157 |
1158 | public string f;
1159 |
1160 | public string sr;
1161 | }
1162 |
1163 | public class Playlist
1164 | {
1165 |
1166 | public long id;
1167 |
1168 | public string name { get; set; }
1169 |
1170 | public long coverImgId;
1171 |
1172 | public string coverImgUrl { get; set; }
1173 |
1174 | public string coverImgId_str;
1175 |
1176 | public int adType;
1177 |
1178 | public long userId;
1179 |
1180 | public int createTime;
1181 |
1182 | public int status;
1183 |
1184 | public bool opRecommend;
1185 |
1186 | public bool highQuality;
1187 |
1188 | public bool newImported;
1189 |
1190 | public int updateTime;
1191 |
1192 | public int trackCount { get; set; }
1193 |
1194 | public int specialType;
1195 |
1196 | public int privacy;
1197 |
1198 | public int trackUpdateTime;
1199 |
1200 | public string commentThreadId;
1201 |
1202 | public int playCount;
1203 |
1204 | public long trackNumberUpdateTime;
1205 |
1206 | public int subscribedCount;
1207 |
1208 | public int cloudTrackCount;
1209 |
1210 | public bool ordered;
1211 |
1212 | public string description;
1213 |
1214 | public List tags;
1215 |
1216 | public string updateFrequency;
1217 |
1218 | public int backgroundCoverId;
1219 |
1220 | public string backgroundCoverUrl;
1221 |
1222 | public int titleImage;
1223 |
1224 | public string titleImageUrl;
1225 |
1226 | public string englishTitle;
1227 |
1228 | public string officialPlaylistType;
1229 |
1230 | public bool copied;
1231 |
1232 | public string relateResType;
1233 |
1234 | public List subscribers;
1235 |
1236 | public bool subscribed;
1237 |
1238 | public Creators creator;
1239 |
1240 | public List tracks;
1241 |
1242 | public string videoIds;
1243 |
1244 | public string videos;
1245 |
1246 | public List trackIds;
1247 |
1248 | public string bannedTrackIds;
1249 |
1250 | public string mvResourceInfos;
1251 |
1252 | public int shareCount;
1253 |
1254 | public int commentCount;
1255 |
1256 | public string remixVideo;
1257 |
1258 | public string sharedUsers;
1259 |
1260 | public string historySharedUsers;
1261 |
1262 | public string gradeStatus;
1263 |
1264 | public string score;
1265 |
1266 | public string algTags;
1267 | }
1268 |
1269 | public class FreeTrialPrivileges
1270 | {
1271 |
1272 | public string resConsumable;
1273 |
1274 | public string userConsumable;
1275 |
1276 | public string listenType;
1277 | }
1278 |
1279 | public class ChargeInfoListItems
1280 | {
1281 |
1282 | public int rate;
1283 |
1284 | public string chargeUrl;
1285 |
1286 | public string chargeMessage;
1287 |
1288 | public int chargeType;
1289 | }
1290 |
1291 | public class PrivilegesItems
1292 | {
1293 |
1294 | public int id;
1295 |
1296 | public int fee;
1297 |
1298 | public int payed;
1299 |
1300 | public int realPayed;
1301 |
1302 | public int st;
1303 |
1304 | public int pl;
1305 |
1306 | public int dl;
1307 |
1308 | public int sp;
1309 |
1310 | public int cp;
1311 |
1312 | public int subp;
1313 |
1314 | public string cs;
1315 |
1316 | public int maxbr;
1317 |
1318 | public int fl;
1319 |
1320 | public string pc;
1321 |
1322 | public string toast;
1323 |
1324 | public int flag;
1325 |
1326 | public string paidBigBang;
1327 |
1328 | public string preSell;
1329 |
1330 | public int playMaxbr;
1331 |
1332 | public int downloadMaxbr;
1333 |
1334 | public string maxBrLevel;
1335 |
1336 | public string playMaxBrLevel;
1337 |
1338 | public string downloadMaxBrLevel;
1339 |
1340 | public string plLevel;
1341 |
1342 | public string dlLevel;
1343 |
1344 | public string flLevel;
1345 |
1346 | public string rscl;
1347 |
1348 | public FreeTrialPrivileges freeTrialPrivilege;
1349 |
1350 | public List chargeInfoList;
1351 | }
1352 |
1353 | public class GedanDetail
1354 | {
1355 |
1356 | public long code;
1357 |
1358 | public string relatedVideos;
1359 |
1360 | public Playlist playlist { get; set; }
1361 |
1362 | public string urls;
1363 |
1364 | public List privileges;
1365 |
1366 | public string sharedPrivilege;
1367 |
1368 | public string resEntrance;
1369 |
1370 | public string fromUsers;
1371 |
1372 | public int fromUserCount;
1373 |
1374 | public string songFromUsers;
1375 | }
1376 |
1377 | public class MusicCheck
1378 | {
1379 |
1380 | public bool success{ get; set; }
1381 |
1382 | public string message{ get; set; }
1383 | }
1384 |
1385 | public class ArItem1
1386 | {
1387 |
1388 | public int id;
1389 |
1390 | public string name;
1391 |
1392 | public List tns;
1393 |
1394 | public List @alias;
1395 | }
1396 |
1397 | public class Al1
1398 | {
1399 |
1400 | public int id;
1401 |
1402 | public string name;
1403 |
1404 | public string picUrl { get; set; }
1405 |
1406 | public List tns;
1407 |
1408 | public string pic_str;
1409 |
1410 | public long pic;
1411 | }
1412 |
1413 | public class H1
1414 | {
1415 |
1416 | public int br;
1417 |
1418 | public int fid;
1419 |
1420 | public int size;
1421 |
1422 | public int vd;
1423 |
1424 | public int sr;
1425 | }
1426 |
1427 | public class M1
1428 | {
1429 |
1430 | public int br;
1431 |
1432 | public int fid;
1433 |
1434 | public int size;
1435 |
1436 | public int vd;
1437 |
1438 | public int sr;
1439 | }
1440 |
1441 | public class L1
1442 | {
1443 |
1444 | public int br;
1445 |
1446 | public int fid;
1447 |
1448 | public int size;
1449 |
1450 | public int vd;
1451 |
1452 | public int sr;
1453 | }
1454 |
1455 | public class Sq1
1456 | {
1457 |
1458 | public int br;
1459 |
1460 | public int fid;
1461 |
1462 | public int size;
1463 |
1464 | public int vd;
1465 |
1466 | public int sr;
1467 | }
1468 |
1469 | public class SongsItem1
1470 | {
1471 |
1472 | public string name { get; set; }
1473 |
1474 | public long id;
1475 |
1476 | public int pst;
1477 |
1478 | public int t;
1479 |
1480 | public List ar;
1481 |
1482 | public List alia;
1483 |
1484 | public int pop;
1485 |
1486 | public int st;
1487 |
1488 | public string rt;
1489 |
1490 | public int fee;
1491 |
1492 | public int v;
1493 |
1494 | public string crbt;
1495 |
1496 | public string cf;
1497 |
1498 | public Al1 al { get; set; }
1499 |
1500 | public int dt;
1501 |
1502 | public H1 h;
1503 |
1504 | public M1 m;
1505 |
1506 | public L1 l;
1507 |
1508 | public Sq1 sq;
1509 |
1510 |
1511 | public string hr;
1512 |
1513 | public string a;
1514 |
1515 | public string cd;
1516 |
1517 | public int no;
1518 |
1519 | public string rtUrl;
1520 |
1521 | public int ftype;
1522 |
1523 | public List rtUrls;
1524 |
1525 | public long djId;
1526 |
1527 | public long copyright;
1528 |
1529 | public long s_id;
1530 |
1531 | public long mark;
1532 |
1533 | public int originCoverType;
1534 |
1535 | public string originSongSimpleData;
1536 |
1537 | public string tagPicList;
1538 |
1539 | public string resourceState;
1540 |
1541 | public int version;
1542 |
1543 | public string songJumpInfo;
1544 |
1545 | public string entertainmentTags;
1546 |
1547 | public string awardTags;
1548 |
1549 | public int single;
1550 |
1551 | public string noCopyrightRcmd;
1552 |
1553 | public int mv;
1554 |
1555 | public int mst;
1556 |
1557 | public int cp;
1558 |
1559 | public int rtype;
1560 |
1561 | public string rurl;
1562 |
1563 | public long publishTime;
1564 | }
1565 |
1566 | public class FreeTrialPrivilege1
1567 | {
1568 |
1569 | public string resConsumable;
1570 |
1571 | public string userConsumable;
1572 |
1573 | public string listenType;
1574 | }
1575 |
1576 | public class ChargeInfoListItem1
1577 | {
1578 |
1579 | public long rate;
1580 |
1581 | public string chargeUrl;
1582 |
1583 | public string chargeMessage;
1584 |
1585 | public int chargeType;
1586 | }
1587 |
1588 | public class PrivilegesItem1
1589 | {
1590 |
1591 | public long id;
1592 |
1593 | public int fee;
1594 |
1595 | public int payed;
1596 |
1597 | public int st;
1598 |
1599 | public int pl;
1600 |
1601 | public int dl;
1602 |
1603 | public int sp;
1604 |
1605 | public int cp;
1606 |
1607 | public int subp;
1608 |
1609 | public string cs;
1610 |
1611 | public int maxbr;
1612 |
1613 | public int fl;
1614 |
1615 | public string toast;
1616 |
1617 | public int flag;
1618 |
1619 | public string preSell;
1620 |
1621 | public int playMaxbr;
1622 |
1623 | public int downloadMaxbr;
1624 |
1625 | public string maxBrLevel;
1626 |
1627 | public string playMaxBrLevel;
1628 |
1629 | public string downloadMaxBrLevel;
1630 |
1631 | public string plLevel;
1632 |
1633 | public string dlLevel;
1634 |
1635 | public string flLevel;
1636 |
1637 | public string rscl;
1638 |
1639 | public FreeTrialPrivilege1 freeTrialPrivilege;
1640 |
1641 | public List chargeInfoList;
1642 | }
1643 |
1644 | public class MusicDetail
1645 | {
1646 |
1647 | public List songs { get; set; }
1648 |
1649 | public List privileges;
1650 |
1651 | public int code;
1652 | }
1653 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TS3AudioBot-NetEaseCloudmusic-UNM
2 | TS3AudioBot-NetEaseCloudmusic-UnblockNeteaseMusic-plugin
3 |
4 | 支持Windows、Linux、Docker环境。
5 |
6 | ## 关于解锁版权歌曲
7 | 需要在自建的 [NeteaseCloudMusicApi](https://github.com/Binaryify/NeteaseCloudMusicApi)(推荐Docker版)里面的 app.js 中添加 `process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0`, 如果是docker版的话就在环境里面添加`NODE_TLS_REJECT_UNAUTHORIZED = 0`。
8 | 需要自建 [UnblockNeteaseMusic](https://github.com/UnblockNeteaseMusic/server) 服务(推荐Docker版)。
9 |
10 | ## 关于设置文件YunSettings.ini
11 | `playMode=`是播放模式
12 | `WangYiYunAPI_Address=`是网易云API地址
13 | `cookies1=`是保存在你本地的身份验证,通过二维码登录获取。(不需要修改)
14 | `UNM_Address=`是 UnblockNeteaseMusic 服务的API地址。
15 |
16 | ## 替换插件文件后需要重启TS3AudioBot服务!!!
17 |
18 | ## 目前的指令:
19 | 正在播放的歌曲的图片和名称可以点机器人看它的头像和描述
20 | vip音乐想要先登陆才能播放完整版本:(输入指令后扫描机器人头像二维码登陆)
21 | `!yun login`
22 |
23 | 双击机器人,目前有以下指令
24 | 1.立即播放网易云音乐
25 | `!yun play 音乐名称` 或 `!yun play 音乐名称 歌手` (无版权歌曲点播)
26 |
27 | 2.播放网易云音乐歌单
28 | `!yun gedan 歌单id`
29 |
30 | 3.播放列表中的下一首
31 | `!yun next`
32 |
33 | 3.停止播放
34 | `!yun stop`
35 |
36 | 5.修改播放模式
37 | `!yun mode 数字0-3`
38 | `0 = 顺序播放`
39 | `1 = 顺序循环`
40 | `2 = 随机播放`
41 | `3 = 随机循环`
42 |
43 |
44 | ## 如果你需要基于主线[TS3AudioBot](https://github.com/Splamy/TS3AudioBot)构建Docker版的TS3AudioBot:
45 | Dockerfile支持x86、arm64、arm32三种架构,默认为x86。 如需其他架构请拉取Dockerfile修改注释
46 | 构建命令:`docker build -f Dockerfile -t local.docker.image/ts3audiobot:latest .`
47 | 运行方法参考[TS3AudioBot_docker](https://github.com/getdrunkonmovies-com/TS3AudioBot_docker)文档
48 |
49 | 相比主线解决了部分设备中关于ts3audiobot.db的权限无法运行的问题,Dockerfile中程序运行用户改为了root。
50 | 添加支持yt-dlp:
51 | 需要更改ts3audiobot.toml文件中 `youtube-dl = { path = "yt-dlp" }`
52 |
53 |
54 |
55 | ## 感谢
56 |
57 | - [Splamy](https://github.com/Splamy) 的 [TS3AudioBot](https://github.com/Splamy/TS3AudioBot) 项目
58 | - [bmatzelle](https://github.com/bmatzelle) 的 [Nini](https://github.com/bmatzelle/nini) 项目
59 | - [Fody](https://github.com/Fody) 的 [Costura.Fody](https://github.com/Fody/Costura/) 项目
60 | - [ZHANGTIANYAO1](https://github.com/ZHANGTIANYAO1) 的 [TS3AudioBot-NetEaseCloudmusic-plugin](https://github.com/ZHANGTIANYAO1/TS3AudioBot-NetEaseCloudmusic-plugin) 项目
61 | - [lauren12133](https://github.com/lauren12133) 关于TS3AudioBot编译Docker的教程和代码。
62 |
--------------------------------------------------------------------------------
/YunPlugin-UNM.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Net;
5 | using System.Reflection;
6 | using System.Runtime.InteropServices;
7 | using System.Text;
8 | using System.Text.Json;
9 | using System.Threading;
10 | using System.Threading.Tasks;
11 | using Nini.Config;
12 | using TS3AudioBot;
13 | using TS3AudioBot.Audio;
14 | using TS3AudioBot.CommandSystem;
15 | using TS3AudioBot.Plugins;
16 | using TSLib.Full;
17 | using NeteaseCloudMusicApi;
18 |
19 | public class YunPlugin : IBotPlugin
20 | {
21 | //===========================================初始化===========================================
22 | static IConfigSource MyIni;
23 | PlayManager tempplayManager;
24 | InvokerData tempinvoker;
25 | Ts3Client tempts3Client;
26 | public static string cookies;
27 | public static int playMode;
28 | public static string WangYiYunAPI_Address;
29 | public static string UNM_Address;
30 | List playlist = new List();
31 | public static int Playlocation = 0;
32 | private readonly SemaphoreSlim playlock = new SemaphoreSlim(1, 1);
33 | private readonly SemaphoreSlim Listeninglock = new SemaphoreSlim(1, 1);
34 | public void Initialize()
35 | {
36 | string iniFilePath;
37 |
38 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
39 | {
40 | Console.WriteLine("运行在Windows环境.");
41 | iniFilePath = "plugins/YunSettings.ini"; // Windows 文件目录
42 | }
43 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
44 | {
45 | string dockerEnvFilePath = "/.dockerenv";
46 |
47 | if (File.Exists(dockerEnvFilePath))
48 | {
49 | Console.WriteLine("运行在Docker环境.");
50 | }
51 | else
52 | {
53 | Console.WriteLine("运行在Linux环境.");
54 | }
55 |
56 | string location = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
57 | iniFilePath = File.Exists(dockerEnvFilePath) ? location + "/data/plugins/YunSettings.ini" : location + "/plugins/YunSettings.ini";
58 | }
59 | else
60 | {
61 | throw new NotSupportedException("不支持的操作系统");
62 | }
63 |
64 | Console.WriteLine(iniFilePath);
65 | MyIni = new IniConfigSource(iniFilePath);
66 |
67 | playMode = int.TryParse(MyIni.Configs["YunBot"].Get("playMode"), out int playModeValue) ? playModeValue : 0;
68 |
69 | string cookiesValue = MyIni.Configs["YunBot"].Get("cookies1");
70 | cookies = string.IsNullOrEmpty(cookiesValue) ? "" : cookiesValue;
71 |
72 | string wangYiYunAPI_AddressValue = MyIni.Configs["YunBot"].Get("WangYiYunAPI_Address");
73 | WangYiYunAPI_Address = string.IsNullOrEmpty(wangYiYunAPI_AddressValue) ? "http://127.0.0.1:3000" : wangYiYunAPI_AddressValue;
74 |
75 | string unmAddressValue = MyIni.Configs["YunBot"].Get("UNM_Address");
76 | UNM_Address = string.IsNullOrEmpty(unmAddressValue) ? "" : unmAddressValue;
77 |
78 | Console.WriteLine(playMode);
79 | Console.WriteLine(cookies);
80 | Console.WriteLine(WangYiYunAPI_Address);
81 | Console.WriteLine(UNM_Address);
82 |
83 | }
84 |
85 |
86 |
87 | public void SetPlplayManager(PlayManager playManager)
88 | {
89 | tempplayManager = playManager;
90 | }
91 | public PlayManager GetplayManager()
92 | {
93 | return tempplayManager;
94 | }
95 |
96 | public InvokerData Getinvoker()
97 | {
98 | return tempinvoker;
99 | }
100 |
101 | public void SetInvoker(InvokerData invoker)
102 | {
103 | tempinvoker = invoker;
104 | }
105 |
106 | public void SetTs3Client(Ts3Client ts3Client)
107 | {
108 | tempts3Client = ts3Client;
109 | }
110 |
111 | public Ts3Client GetTs3Client()
112 | {
113 | return tempts3Client;
114 | }
115 |
116 | //===========================================初始化===========================================
117 |
118 |
119 | //===========================================播放模式===========================================
120 | [Command("yun mode")]
121 | public string Playmode(int mode)
122 | {
123 | if (mode >= 0 && mode <= 3)
124 | {
125 | playMode = mode;
126 | MyIni.Configs["YunBot"].Set("playMode", mode.ToString());
127 | MyIni.Save();
128 | return mode switch
129 | {
130 | 0 => "顺序播放",
131 | 1 => "顺序循环",
132 | 2 => "随机播放",
133 | 3 => "随机循环",
134 | _ => "未知播放模式",
135 | };
136 | }
137 | else
138 | {
139 | return "请输入正确的播放模式(0 到 3 之间的整数)";
140 | }
141 | }
142 | //===========================================播放模式===========================================
143 |
144 |
145 | //===========================================单曲播放===========================================
146 | [Command("yun play")]
147 | public async Task CommandYunPlay(string arguments, PlayManager playManager, InvokerData invoker, Ts3Client ts3Client)
148 | {
149 | //playlist.Clear();
150 | SetInvoker(invoker);
151 | SetPlplayManager(playManager);
152 | SetTs3Client(ts3Client);
153 | bool songFound = false;
154 | string urlSearch = $"{WangYiYunAPI_Address}/search?keywords={arguments}&limit=30";
155 | string searchJson = await HttpGetAsync(urlSearch);
156 | yunSearchSong yunSearchSong = JsonSerializer.Deserialize(searchJson);
157 | string[] splitArguments = arguments.Split(" ");
158 | Console.WriteLine(splitArguments.Length);
159 | if (splitArguments.Length == 1)
160 | {
161 | _ = ProcessSong(yunSearchSong.result.songs[0].id, ts3Client, playManager, invoker);
162 | songFound = true;
163 | }
164 | else if (splitArguments.Length == 2)
165 | {
166 | // 歌曲名称和歌手
167 | string songName = splitArguments[0];
168 | string artist = splitArguments[1];
169 |
170 | for (int s = 0; s < yunSearchSong.result.songs.Count; s++)
171 | {
172 | if (yunSearchSong.result.songs[s].name == songName && yunSearchSong.result.songs[s].artists[0].name == artist)
173 | {
174 | _ = ProcessSong(yunSearchSong.result.songs[s].id, ts3Client, playManager, invoker);
175 | songFound = true;
176 | break;
177 | }
178 | }
179 | }
180 | else
181 | {
182 | // 输入为空或格式不符合预期
183 | Console.WriteLine("请输入有效的歌曲信息");
184 | _ = ts3Client.SendChannelMessage("请输入有效的歌曲信息");
185 | }
186 | Playlocation = songFound && Playlocation > 0 ? Playlocation - 1 : Playlocation;
187 | if (!songFound)
188 | {
189 | _ = ts3Client.SendChannelMessage("未找到歌曲");
190 | }
191 | }
192 |
193 | //===========================================单曲播放===========================================
194 |
195 |
196 | //===========================================歌单播放===========================================
197 | [Command("yun gedan")]
198 | public async Task CommandYunGedan(string arguments, PlayManager playManager, InvokerData invoker, Ts3Client ts3Client, Player player)
199 | {
200 | playlist.Clear();
201 | SetInvoker(invoker);
202 | SetPlplayManager(playManager);
203 | SetTs3Client(ts3Client);
204 | string urlSearch = $"{WangYiYunAPI_Address}/playlist/detail?id={arguments}";
205 | string searchJson = await HttpGetAsync(urlSearch);
206 | GedanDetail gedanDetail = JsonSerializer.Deserialize(searchJson);
207 | string gedanshuliang = gedanDetail.playlist.trackCount.ToString();
208 | _ = ts3Client.SendChannelMessage($"歌单共{gedanshuliang}首歌曲,正在添加到播放列表,请稍后。");
209 | int loopCount = -1;
210 | for (int i = 0; i < gedanDetail.playlist.trackCount; i += 50)
211 | {
212 | Console.WriteLine($"查询循环次数{loopCount+1}");
213 | loopCount += 1;
214 | if (i + 50 > gedanDetail.playlist.trackCount)
215 | {
216 | // 如果歌单的歌曲数量小于50,那么查询的数量就是歌曲的数量,否则查询的数量就是歌曲的数量减去50乘以查询的次数
217 | i = gedanDetail.playlist.trackCount < 50 ? gedanDetail.playlist.trackCount : gedanDetail.playlist.trackCount - 50 * loopCount;
218 | // 构建查询URL,如果歌单的歌曲数量小于50,那么偏移量就是0,否则偏移量就是查询的数量
219 | int offset = gedanDetail.playlist.trackCount < 50 ? 0 : i;
220 | urlSearch = $"{WangYiYunAPI_Address}/playlist/track/all?id={arguments}&limit=50&offset={offset}";
221 | searchJson = await HttpGetAsync(urlSearch);
222 | GeDan geDan1 = JsonSerializer.Deserialize(searchJson);
223 | for (int j = 0; j < i; j++){
224 | playlist.Add(geDan1.songs[j].id);
225 | Console.WriteLine(geDan1.songs[j].id);
226 | }
227 | break;
228 | }
229 | urlSearch = $"{WangYiYunAPI_Address}/playlist/track/all?id={arguments}&limit=50&offset={i}";
230 | searchJson = await HttpGetAsync(urlSearch);
231 | GeDan geDan = JsonSerializer.Deserialize(searchJson);
232 | for (int j = 0; j < 50; j++){
233 | playlist.Add(geDan.songs[j].id);
234 | Console.WriteLine(geDan.songs[j].id);
235 | }
236 | }
237 | Playlocation = 0;
238 | _ = ProcessSong(playlist[0], ts3Client, playManager, invoker);
239 | Console.WriteLine($"歌单共{playlist.Count}首歌");
240 | await Listeninglock.WaitAsync();
241 | playManager.ResourceStopped += async (sender, e) => await SongPlayMode(playManager, invoker, ts3Client);
242 | return $"播放列表加载完成,已加载{playlist.Count}首歌";
243 | }
244 | //===========================================歌单播放===========================================
245 |
246 | //===========================================下一曲===========================================
247 | [Command("yun next")]
248 | public async Task CommandYunNext(PlayManager playManager, InvokerData invoker, Ts3Client ts3Client)
249 | {
250 | await SongPlayMode(playManager, invoker, ts3Client);
251 | }
252 | //===========================================下一曲=============================================
253 | [Command("yun stop")]
254 | public async Task CommandYunStop(PlayManager playManager, Ts3Client ts3Client)
255 | {
256 | playlist.Clear();
257 | await playManager.Stop();
258 | }
259 |
260 | //===========================================播放逻辑===========================================
261 | private async Task SongPlayMode(PlayManager playManager, InvokerData invoker, Ts3Client ts3Client)
262 | {
263 | try
264 | {
265 | switch (playMode)
266 | {
267 | case 0: //顺序播放
268 | Playlocation += 1;
269 | await ProcessSong(playlist[Playlocation], ts3Client, playManager, invoker);
270 | break;
271 | case 1: //顺序循环
272 | if (Playlocation == playlist.Count - 1)
273 | {
274 | Playlocation = 0;
275 | await ProcessSong(playlist[Playlocation], ts3Client, playManager, invoker);
276 | }
277 | else
278 | {
279 | Playlocation += 1;
280 | await ProcessSong(playlist[Playlocation], ts3Client, playManager, invoker);
281 | }
282 | break;
283 | case 2: //随机播放
284 | Random random = new Random();
285 | Playlocation = random.Next(0, playlist.Count);
286 | await ProcessSong(playlist[Playlocation], ts3Client, playManager, invoker);
287 | break;
288 | case 3: //随机循环
289 | Random random1 = new Random();
290 | Playlocation = random1.Next(0, playlist.Count);
291 | await ProcessSong(playlist[Playlocation], ts3Client, playManager, invoker);
292 | break;
293 | default:
294 | break;
295 | }
296 | }
297 | catch (Exception)
298 | {
299 | Console.WriteLine("播放列表已空");
300 | _ = ts3Client.SendChannelMessage("已停止播放");
301 | }
302 | }
303 | private async Task ProcessSong(long id, Ts3Client ts3Client, PlayManager playManager, InvokerData invoker)
304 | {
305 | await playlock.WaitAsync();
306 | try {
307 | long musicId = id;
308 | string musicCheckUrl = $"{WangYiYunAPI_Address}/check/music?id={musicId}";
309 | string searchMusicCheckJson = await HttpGetAsync(musicCheckUrl);
310 | MusicCheck musicCheckJson = JsonSerializer.Deserialize(searchMusicCheckJson);
311 |
312 | // 根据音乐检查结果获取音乐播放URL
313 | string musicUrl = musicCheckJson.success.ToString() == "False" ? await GetcheckMusicUrl(musicId, true) : await GetMusicUrl(musicId, true);
314 |
315 | // 构造获取音乐详情的URL
316 | string musicDetailUrl = $"{WangYiYunAPI_Address}/song/detail?ids={musicId}";
317 | string musicDetailJson = await HttpGetAsync(musicDetailUrl);
318 | MusicDetail musicDetail = JsonSerializer.Deserialize(musicDetailJson);
319 |
320 | // 从音乐详情中获取音乐图片URL和音乐名称
321 | string musicImgUrl = musicDetail.songs[0].al.picUrl;
322 | string musicName = musicDetail.songs[0].name;
323 | Console.WriteLine($"歌曲id:{musicId},歌曲名称:{musicName},版权:{musicCheckJson.success}");
324 |
325 | // 设置Bot的头像为音乐图片
326 | _ = MainCommands.CommandBotAvatarSet(ts3Client, musicImgUrl);
327 |
328 | // 设置Bot的描述为音乐名称
329 | _ = MainCommands.CommandBotDescriptionSet(ts3Client, musicName);
330 |
331 | // 在控制台输出音乐播放URL
332 | Console.WriteLine(musicUrl);
333 |
334 | // 如果音乐播放URL不是错误,则添加到播放列表并通知频道
335 | if (musicUrl != "error")
336 | {
337 | _ = MainCommands.CommandPlay(playManager, invoker, musicUrl);
338 |
339 | // 更新Bot的描述为当前播放的音乐名称
340 | _ = MainCommands.CommandBotDescriptionSet(ts3Client, musicName);
341 |
342 | // 发送消息到频道,通知正在播放的音乐
343 | if (playlist.Count == 0)
344 | {
345 | _ = ts3Client.SendChannelMessage($"正在播放:{musicName}");
346 | }
347 | else
348 | {
349 | _ = ts3Client.SendChannelMessage($"正在播放第{Playlocation+1}首:{musicName}");
350 | }
351 | }
352 | }
353 | finally
354 | {
355 | playlock.Release();
356 | }
357 | }
358 | //===========================================播放逻辑===========================================
359 |
360 | //===========================================登录部分===========================================
361 | [Command("yun login")]
362 | public static async Task CommandLoginAsync(Ts3Client ts3Client, TsFullClient tsClient)
363 | {
364 | // 获取登录二维码的 key
365 | string key = await GetLoginKey();
366 |
367 | // 生成登录二维码并获取二维码图片的 base64 字符串
368 | string base64String = await GetLoginQRImage(key);
369 |
370 | // 发送二维码图片到 TeamSpeak 服务器频道
371 | await ts3Client.SendChannelMessage("正在生成二维码");
372 | await ts3Client.SendChannelMessage(base64String);
373 |
374 | // 将 base64 字符串转换为二进制图片数据,上传到 TeamSpeak 服务器作为头像
375 | await UploadQRImage(tsClient, base64String);
376 |
377 | // 设置 TeamSpeak 服务器的描述信息
378 | await ts3Client.ChangeDescription("请用网易云APP扫描二维码登陆");
379 |
380 | int i = 0;
381 | long code;
382 | string result;
383 |
384 | while (true)
385 | {
386 | // 检查登录状态
387 | Status1 status = await CheckLoginStatus(key);
388 |
389 | code = status.code;
390 | cookies = status.cookie;
391 | i = i + 1;
392 | Thread.Sleep(1000);
393 |
394 | if (i == 120)
395 | {
396 | result = "登陆失败或者超时";
397 | await ts3Client.SendChannelMessage("登陆失败或者超时");
398 | break;
399 | }
400 |
401 | if (code == 803)
402 | {
403 | result = "登陆成功";
404 | await ts3Client.SendChannelMessage("登陆成功");
405 | break;
406 | }
407 | }
408 |
409 | // 登录完成后删除上传的头像
410 | _ = await tsClient.DeleteAvatar();
411 |
412 | // 更新 cookies 到配置文件
413 | MyIni.Configs["YunBot"].Set("cookies1", $"\"{cookies}\"");
414 | MyIni.Save();
415 |
416 | return result;
417 | }
418 |
419 | // 获取登录二维码的 key
420 | private static async Task GetLoginKey()
421 | {
422 | string url = WangYiYunAPI_Address + "/login/qr/key" + "?timestamp=" + GetTimeStamp();
423 | string json = await HttpGetAsync(url);
424 | LoginKey loginKey = JsonSerializer.Deserialize(json);
425 | return loginKey.data.unikey;
426 | }
427 |
428 | // 生成登录二维码并获取二维码图片的 base64 字符串
429 | private static async Task GetLoginQRImage(string key)
430 | {
431 | string url = WangYiYunAPI_Address + $"/login/qr/create?key={key}&qrimg=true×tamp={GetTimeStamp()}";
432 | string json = await HttpGetAsync(url);
433 | LoginImg loginImg = JsonSerializer.Deserialize(json);
434 | return loginImg.data.qrimg;
435 | }
436 |
437 | // 上传二维码图片到 TeamSpeak 服务器
438 | private static async Task UploadQRImage(TsFullClient tsClient, string base64String)
439 | {
440 | string[] img = base64String.Split(",");
441 | byte[] bytes = Convert.FromBase64String(img[1]);
442 | Stream stream = new MemoryStream(bytes);
443 | _ = await tsClient.UploadAvatar(stream);
444 | }
445 |
446 | // 检查登录状态
447 | private static async Task CheckLoginStatus(string key)
448 | {
449 | string url = WangYiYunAPI_Address + $"/login/qr/check?key={key}×tamp={GetTimeStamp()}";
450 | string json = await HttpGetAsync(url);
451 | Status1 status = JsonSerializer.Deserialize(json);
452 | Console.WriteLine(json);
453 | return status;
454 | }
455 | //===============================================登录部分===============================================
456 |
457 |
458 | //===============================================获取歌曲信息===============================================
459 | //以下全是功能性函数
460 | public static async Task GetMusicUrl(long id, bool usingCookie = false)
461 | {
462 | return await GetMusicUrl(id.ToString(), usingCookie);
463 | }
464 |
465 | public static async Task GetMusicUrl(string id, bool usingCookie = false)
466 | {
467 | string url = $"{WangYiYunAPI_Address}/song/url?id={id}";
468 | if (usingCookie && !string.IsNullOrEmpty(cookies))
469 | {
470 | url += $"&cookie={cookies}";
471 | }
472 |
473 | string musicUrlJson = await HttpGetAsync(url);
474 | musicURL musicUrl = JsonSerializer.Deserialize(musicUrlJson);
475 |
476 | if (musicUrl.code != 200)
477 | {
478 | // 处理错误情况,这里你可以根据实际情况进行适当的处理
479 | return string.Empty;
480 | }
481 |
482 | string mp3 = musicUrl.data[0].url;
483 | return mp3;
484 | }
485 |
486 | public static async Task GetcheckMusicUrl(long id, bool usingcookie = false) //获得无版权歌曲URL
487 | {
488 | string url;
489 | url = WangYiYunAPI_Address + "/song/url?id=" + id.ToString() + "&proxy=" + UNM_Address;
490 | string musicurljson = await HttpGetAsync(url);
491 | musicURL musicurl = JsonSerializer.Deserialize(musicurljson);
492 | string mp3 = musicurl.data[0].url.ToString();
493 | string checkmp3 = mp3.Replace("http://music.163.com", UNM_Address);
494 | return checkmp3;
495 | }
496 |
497 | public static async Task GetMusicName(string arguments)//获得歌曲名称
498 | {
499 | string musicdetailurl = WangYiYunAPI_Address + "/song/detail?ids=" + arguments;
500 | string musicdetailjson = await HttpGetAsync(musicdetailurl);
501 | MusicDetail musicDetail = JsonSerializer.Deserialize(musicdetailjson);
502 | string musicname = musicDetail.songs[0].name;
503 | return musicname;
504 | }
505 | //===============================================获取歌曲信息===============================================
506 |
507 |
508 |
509 | //===============================================HTTP相关===============================================
510 | public static async Task HttpGetAsync(string url)
511 | {
512 | HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
513 | request.Method = "GET";
514 | request.Accept = "text/html, application/xhtml+xml, */*";
515 | request.ContentType = "application/json";
516 |
517 | // 异步获取响应
518 | using HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync();
519 | // 异步读取响应流
520 | using StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
521 | return await reader.ReadToEndAsync();
522 | }
523 |
524 | public static string GetTimeStamp() //获得时间戳
525 | {
526 | TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0);
527 | return Convert.ToInt64(ts.TotalSeconds).ToString();
528 | }
529 | //===============================================HTTP相关===============================================
530 | public void Dispose()
531 | {
532 |
533 | }
534 | }
535 |
--------------------------------------------------------------------------------
/YunPlugin-UNM.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp3.1
4 |
5 |
6 |
7 | .\lib\TS3AudioBot.dll
8 |
9 |
10 | .\lib\TSLib.dll
11 |
12 |
13 | .\lib\Nini.dll
14 |
15 |
16 |
17 |
18 | all
19 |
20 |
21 |
--------------------------------------------------------------------------------
/YunSettings.ini:
--------------------------------------------------------------------------------
1 | [YunBot]
2 | playMode=0
3 | WangYiYunAPI_Address=
4 | cookies1=
5 | UNM_Address=
6 |
--------------------------------------------------------------------------------
/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-alpine
2 | #FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-alpine-arm64v8
3 | #FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-alpine-arm32v7
4 |
5 | # which version and flavour of the audiobot to use
6 | ARG TS3_AUDIOBOT_RELEASE="0.12.0"
7 | ARG TS3_AUDIOBOT_FLAVOUR="TS3AudioBot_dotnetcore3.1.zip"
8 |
9 |
10 | # install all pre-requisites, these will be needed always
11 | RUN apk add \
12 | opus-dev \
13 | yt-dlp \
14 | youtube-dl \
15 | ffmpeg
16 |
17 | # download and install the TS3AudioBot in the specified version and flavour
18 | RUN mkdir -p /app \
19 | && cd /app \
20 | && echo "downloading https://github.com/Splamy/TS3AudioBot/releases/download/${TS3_AUDIOBOT_RELEASE}/${TS3_AUDIOBOT_FLAVOUR}" \
21 | && wget https://github.com/Splamy/TS3AudioBot/releases/download/${TS3_AUDIOBOT_RELEASE}/${TS3_AUDIOBOT_FLAVOUR} -O TS3AudioBot.zip \
22 | && unzip TS3AudioBot.zip \
23 | && rm TS3AudioBot.zip
24 |
25 | # make data directory and chown it to the ts3bot user
26 | RUN mkdir -p /app/data
27 |
28 | # set the work dir to data, so users can properly mount their config files to this dir with -v /host/path/to/data:/data
29 | WORKDIR /app/data
30 |
31 | # expose the webserver port
32 | EXPOSE 58913
33 |
34 | CMD ["dotnet", "/app/TS3AudioBot.dll", "--non-interactive"]
35 |
--------------------------------------------------------------------------------
/lib/Nini.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FiveHair/TS3AudioBot-NetEaseCloudmusic-plugin-UNM/1f4c013656a2254db79dd67b31c5fefb614c2e8a/lib/Nini.dll
--------------------------------------------------------------------------------
/lib/TS3AudioBot.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FiveHair/TS3AudioBot-NetEaseCloudmusic-plugin-UNM/1f4c013656a2254db79dd67b31c5fefb614c2e8a/lib/TS3AudioBot.dll
--------------------------------------------------------------------------------
/lib/TSLib.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FiveHair/TS3AudioBot-NetEaseCloudmusic-plugin-UNM/1f4c013656a2254db79dd67b31c5fefb614c2e8a/lib/TSLib.dll
--------------------------------------------------------------------------------