· 5 years ago · Jan 20, 2021, 12:12 AM
1registerPlugin({
2 name: 'Youtube Search',
3 version: '1.3.4',
4 engine: '>= 1.0.0',
5 backends: ["ts3", "discord"],
6 description: 'Youtube video search',
7 author: 'NT5',
8 requiredModules: ["http"],
9 vars: [
10 {
11 name: 'yt_apikey',
12 title: 'API KEY (https://console.developers.google.com/project)',
13 type: 'string'
14 },
15 {
16 name: 'ytdl_action',
17 title: 'Action with YoutubeDL',
18 type: 'select',
19 indent: 1,
20 options: [
21 'Nothing',
22 'Download',
23 'Stream'
24 ]
25 },
26 {
27 name: 'ytdl_playback',
28 title: 'Playback action',
29 type: 'select',
30 indent: 1,
31 options: [
32 'Queue',
33 'Force play'
34 ]
35 },
36 {
37 name: 'yt_catchurl',
38 title: 'Catch YouTube Links',
39 type: 'checkbox'
40 },
41 {
42 name: 'yt_maxresults',
43 title: 'Max youtube videos (1~>=50)',
44 type: 'number',
45 placeholder: '1'
46 },
47 {
48 name: 'yt_maxduration',
49 title: 'Max youtube video duration for playback (in seconds) default 900seg (15min)',
50 type: 'number',
51 placeholder: '900'
52 },
53 {
54 name: 'yt_randomplay',
55 title: 'Play random video from search (only work if maxresults>=2)',
56 type: 'checkbox'
57 },
58 {
59 name: 'command_trigger',
60 title: 'Command trigger',
61 type: 'string',
62 placeholder: 'youtube'
63 },
64 {
65 name: 'command_message',
66 title: 'Message Format (supports bbcode)',
67 type: 'multiline',
68 placeholder: '[B]You[/B][COLOR=#ff0000]Tube[/COLOR] - Title: {title} - Link: [url={yt_link}]{yt_link}[/url] - By: {upload_by}'
69 },
70 {
71 name: 'yt_titleblacklist',
72 title: 'Banned video titles <comma saparated>',
73 type: 'string',
74 placeholder: '10 hours, ...'
75 },
76 {
77 name: 'command_adminpermissions',
78 title: 'Admin users (ID or Name)',
79 type: 'array',
80 vars: [
81 {
82 name: 'user',
83 type: 'string',
84 indent: 2,
85 placeholder: 'Username or id'
86 }
87 ]
88 },
89 {
90 name: 'command_blacklistusers',
91 title: 'Banned users <comma saparated> ',
92 type: 'string',
93 placeholder: 'trollface, <id/username>...'
94 },
95 {
96 name: 'command_permissionsServerGroups',
97 title: 'List of server groups that the bot should accept command and links (ID or Name)',
98 type: 'array',
99 vars: [
100 {
101 name: 'group',
102 type: 'string',
103 indent: 2,
104 placeholder: 'Group name or id'
105 }
106 ]
107 }
108 /*
109 TODO
110 - [FEATURE] Add a playlist support
111 {
112 name: 'catch_url_playlist',
113 title: 'Catch YouTube playlist',
114 type: 'select',
115 options: [
116 'Yes',
117 'No'
118 ]
119 }
120 */
121 ]
122}, function (sinusbot, config, manifest) {
123
124 var backend = require('backend');
125 var engine = require('engine');
126 var event = require('event');
127 var http = require("http");
128
129 // Script utils
130
131 // String format util
132 if (!String.prototype.format) {
133 String.prototype.format = function () {
134 var str = this.toString();
135 if (!arguments.length) {
136 return str;
137 }
138 var args = typeof arguments[0];
139 args = (("string" === args || "number" === args) ? arguments : arguments[0]);
140 for (var arg in args) {
141 str = str.replace(RegExp("\\{" + arg + "\\}", "gi"), args[arg]);
142 }
143 return str;
144 }
145 }
146
147 // String truncate util http://stackoverflow.com/questions/1199352
148 String.prototype.trunc = function (n, useWordBoundary) {
149 if (this.length <= n) { return this; }
150 var subString = this.substr(0, n - 1);
151 return (useWordBoundary
152 ? subString.substr(0, subString.lastIndexOf(' '))
153 : subString) + "...";
154 };
155
156 // Polyfill util https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill
157 if (typeof Object.assign !== 'function') {
158 Object.assign = function (target, varArgs) { // .length of function is 2
159 'use strict';
160 if (target === null) { // TypeError if undefined or null
161 throw new TypeError('Cannot convert undefined or null to object');
162 }
163
164 var to = Object(target);
165
166 for (var index = 1; index < arguments.length; index++) {
167 var nextSource = arguments[index];
168
169 if (nextSource !== null) { // Skip over if undefined or null
170 for (var nextKey in nextSource) {
171 // Avoid bugs when hasOwnProperty is shadowed
172 if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
173 to[nextKey] = nextSource[nextKey];
174 }
175 }
176 }
177 }
178 return to;
179 };
180 }
181
182 // Dateformat youtube apiv3 to seconds http://stackoverflow.com/questions/22148885
183 function youtube_duration_seconds (duration) {
184 var a = duration.match(/\d+/g);
185
186 if (duration.indexOf('M') >= 0 && duration.indexOf('H') === -1 && duration.indexOf('S') === -1) {
187 a = [0, a[0], 0];
188 }
189
190 if (duration.indexOf('H') >= 0 && duration.indexOf('M') === -1) {
191 a = [a[0], 0, a[1]];
192 }
193 if (duration.indexOf('H') >= 0 && duration.indexOf('M') === -1 && duration.indexOf('S') === -1) {
194 a = [a[0], 0, 0];
195 }
196
197 duration = 0;
198
199 if (a.length === 3) {
200 duration = duration + parseInt(a[0]) * 3600;
201 duration = duration + parseInt(a[1]) * 60;
202 duration = duration + parseInt(a[2]);
203 }
204
205 if (a.length === 2) {
206 duration = duration + parseInt(a[0]) * 60;
207 duration = duration + parseInt(a[1]);
208 }
209
210 if (a.length === 1) {
211 duration = duration + parseInt(a[0]);
212 }
213 return duration
214 }
215
216 // D:H:M:S
217 function seconds_to_human (seconds) {
218 var years = Math.floor(seconds / 31536000);
219 seconds = Math.floor(seconds - 31536000 * years);
220 var months = Math.floor(seconds / 2628000);
221 seconds = Math.floor(seconds - 2628000 * months);
222 var weeks = Math.floor(seconds / 604800);
223 seconds = Math.floor(seconds - 604800 * weeks);
224 var days = Math.floor(seconds / 86400);
225 seconds = Math.floor(seconds - 86400 * days);
226 var hours = Math.floor(seconds / 3600);
227 seconds = Math.floor(seconds - 3600 * hours);
228 var minutes = Math.floor(seconds / 60);
229 seconds = Math.floor(seconds - 60 * minutes);
230
231 var messages = [];
232
233 if (years > 0)
234 messages.push("{0}years".format(years));
235 if (months > 0)
236 messages.push("{0}months".format(months));
237 if (weeks > 0)
238 messages.push("{0}weeks".format(weeks));
239 if (days > 0)
240 messages.push("{0}days".format(days));
241 if (hours > 0)
242 messages.push("{0}hrs".format(hours));
243 if (minutes > 0)
244 messages.push("{0}min".format(minutes));
245 if (seconds > 0)
246 messages.push("{0}secs".format(seconds))
247
248 return (messages.length > 0 ? messages.join(", ") : "{0}secs".format(seconds));
249 }
250
251 // {000,000,...}
252 function addCommas (nStr) {
253 nStr += '';
254 var x = nStr.split('.'),
255 x1 = x[0],
256 x2 = x.length > 1 ? '.' + x[1] : '',
257 rgx = /(\d+)(\d{3})/;
258 while (rgx.test(x1)) {
259 x1 = x1.replace(rgx, '$1' + ',' + '$2');
260 }
261 return x1 + x2;
262 }
263
264 // URL format util http://stackoverflow.com/questions/1714786/
265 function URLSerialize (obj, prefix) {
266 var str = [], p;
267 for (p in obj) {
268 if (obj.hasOwnProperty(p)) {
269 var k = prefix ? prefix + "[" + p + "]" : p, v = obj[p];
270 str.push((v !== null && typeof v === "object") ?
271 URLSerialize(v, k) :
272 encodeURIComponent(k) + "=" + encodeURIComponent(v));
273 }
274 }
275 return str.join("&");
276 }
277
278 // Plugin Methods
279 var youtube = {
280 config: {
281 api: {
282 url: "https://www.googleapis.com/youtube/v3/{path}?{fields}",
283 key: config.yt_apikey || 0,
284 maxresults: (function () {
285 var mr = parseInt(config.yt_maxresults);
286 return (mr >= 1 && mr <= 50 ? mr : 1);
287 }())
288 },
289 plugin: {
290 manifest: {
291 running_time: Math.floor(Date.now() / 1000),
292 version: manifest.version,
293 name: manifest.name,
294 description: manifest.description,
295 authors: [
296 {
297 name: 'NT5',
298 role: 'Main Dev'
299 }
300 ]
301 },
302 regex: {
303 // !{command}[-{area}] [{text}]
304 cmd: /^!(\w+)(?:-(\w+))?(?:\s(.+))?/,
305 // {videId}
306 youtube: /(?:http|https):\/\/www\.(?:youtube\.com|youtu\.be)\/watch\?v=([\w-]+)/
307 },
308 command_trigger: config.command_trigger || 'youtube',
309 catch_url: config.yt_catchurl,
310 randomplay: config.yt_randomplay,
311 yt_maxduration: config.yt_maxduration || 900,
312 yt_titleblacklist: (typeof config.yt_titleblacklist !== 'undefined' && config.yt_titleblacklist.length > 0 ? config.yt_titleblacklist.split(',') : []),
313 command_message: config.command_message || '[B]You[/B][COLOR=#ff0000]Tube[/COLOR] - Title: {title} - Link: [url={yt_link}]{yt_link}[/url] - By: {upload_by}',
314 ytdl_action: parseInt(config.ytdl_action) || 0,
315 ytdl_playback: parseInt(config.ytdl_playback) || 0,
316 server_groups: config.command_permissionsServerGroups || [],
317 adminpermissions: config.command_adminpermissions || [],
318 blacklistusers: (typeof config.command_blacklistusers !== 'undefined' && config.command_blacklistusers.length > 0 ? config.command_blacklistusers.split(',') : [])
319 }
320 },
321 getJSON: function (options) {
322 options = (typeof options !== "object") ? {} : options;
323
324 options.method = options.method || 'GET';
325 options.url = options.url || '';
326 options.headers = options.headers || { 'Content-Type': 'application/json; charset=UTF-8' };
327 options.callback = options.callback || function (err, res) { engine.log(res); };
328 options.error_callback = options.error_callback || function (err, res) { engine.log(err); };
329
330 http.simpleRequest({
331 method: options.method,
332 url: options.url,
333 timeout: 6000,
334 headers: options.headers
335 }, function (err, res) {
336 if (err || res.statusCode !== 200) {
337 engine.log('Request error [{error}] Code: [{code}] Data: [{data}]'.format({
338 error: err,
339 data: res.data,
340 code: res.statusCode
341 }));
342 options.error_callback(err);
343 } else {
344 var json = JSON.parse(res.data);
345 options.callback(json);
346 }
347 });
348 },
349 api: {
350 search: function (options) {
351 options = (typeof options !== "object") ? {} : options;
352
353 options.query = options.query || '';
354 options.maxresults = options.maxresults || youtube.config.api.maxresults;
355 options.type = options.type || 'video';
356 options.fields = options.fields || 'items(snippet/title,snippet/description,snippet/channelTitle,id)';
357 options.part = options.part || 'snippet';
358 options.api_key = options.api_key || youtube.config.api.key;
359 options.callback = options.callback || function (json) { engine.log(json); };
360 options.error_callback = options.error_callback || function (error) { engine.log(error); };
361
362 youtube.getJSON({
363 url: youtube.config.api.url.format({
364 path: 'search',
365 fields: URLSerialize({
366 q: options.query,
367 part: options.part,
368 type: options.type,
369 maxResults: options.maxresults,
370 fields: options.fields,
371 key: options.api_key
372 })
373 }),
374 callback: options.callback,
375 error_callback: options.error_callback
376 });
377 },
378 video: function (options) {
379 options = (typeof options !== "object") ? {} : options;
380
381 options.videoId = options.videoId || 0;
382 options.fields = options.fields || 'items(snippet/title,snippet/description,snippet/channelTitle,id,kind,statistics,contentDetails/duration)';
383 options.part = options.part || 'snippet,statistics,contentDetails';
384 options.api_key = options.api_key || youtube.config.api.key;
385 options.callback = options.callback || function (json) { engine.log(json); };
386 options.error_callback = options.error_callback || function (error) { engine.log(error); };
387
388 youtube.getJSON({
389 url: youtube.config.api.url.format({
390 path: 'videos',
391 fields: URLSerialize({
392 id: options.videoId,
393 part: options.part,
394 fields: options.fields,
395 key: options.api_key
396 })
397 }),
398 callback: options.callback,
399 error_callback: options.error_callback
400 });
401 },
402 playlist: function (options) {
403 options = (typeof options !== "object") ? {} : options;
404
405 options.playlistId = options.playlistId || 0;
406 options.part = options.part || 'snippet';
407 options.fields = options.fields || 'items(snippet/title,snippet/description,snippet/channelTitle,id,kind)';
408 options.maxResults = options.maxResults || 1;
409 options.api_key = options.api_key || youtube.config.api.key;
410 options.callback = options.callback || function (json) { engine.log(json); };
411 options.error_callback = options.error_callback || function (error) { engine.log(error); };
412
413 youtube.getJSON({
414 url: youtube.config.api.url.format({
415 path: 'playlists',
416 fields: URLSerialize({
417 id: options.playlistId,
418 maxResults: options.maxResults,
419 part: options.part,
420 key: options.api_key
421 })
422 }),
423 callback: options.callback,
424 error_callback: options.error_callback
425 });
426 },
427 playlistItems: function (options) {
428 options = (typeof options !== "object") ? {} : options;
429
430 options.playlistId = options.playlistId || 0;
431 options.pageToken = options.pageToken || false;
432 options.part = options.part || 'contentDetails';
433 options.maxResults = options.maxResults || 50;
434 options.api_key = options.api_key || youtube.config.api.key;
435 options.callback = options.callback || function (json) { engine.log(json); };
436 options.error_callback = options.error_callback || function (error) { engine.log(error); };
437
438 youtube.getJSON({
439 url: youtube.config.api.url.format({
440 path: 'playlistItems',
441 fields: URLSerialize({
442 playlistId: options.playlistId,
443 maxResults: options.maxResults,
444 part: options.part,
445 key: options.api_key
446 })
447 }),
448 callback: options.callback,
449 error_callback: options.error_callback
450 });
451 }
452 },
453 msg: function (options) {
454 options = (typeof options !== "object") ? {} : options;
455
456 options.text = options.text || 'ravioli ravioli';
457 options.mode = options.mode || 0;
458 options.backend = options.backend || backend;
459 options.client = options.client || false;
460 options.channel = options.channel || options.backend.getCurrentChannel();
461
462 var maxlength = 800;
463 var timeoutdelay = 125;
464
465 switch (engine.getBackend()) {
466 case "discord":
467 options.mode = 2;
468 break;
469 case "ts3":
470 default:
471 break;
472 }
473
474 /*
475 TODO
476 - [BUG] Make sure if works in all cases
477 */
478
479 var parse_msg = function (options) {
480 if (options.text.length >= maxlength) {
481 var truncated = options.text.trunc(maxlength, true);
482 var new_text = options.text.slice((truncated.length - 3), options.text.length);
483
484 options.chat(truncated);
485 options.text = new_text;
486 setTimeout(function () {
487 parse_msg(options)
488 }, timeoutdelay);
489 } else {
490 options.chat(options.text);
491 }
492 };
493
494 switch (options.mode) {
495 case 1: // Private client message
496 if (options.client) {
497 parse_msg({
498 text: options.text,
499 chat: options.client.chat.bind(options.client)
500 });
501 } else {
502 options.mode = 0;
503 youtube.msg(options);
504 }
505 break;
506 case 2: // Channel message
507 if (options.channel) {
508 parse_msg({
509 text: options.text,
510 chat: options.channel.chat.bind(options.channel)
511 });
512 } else {
513 options.mode = 0;
514 youtube.msg(options);
515 }
516 break;
517 default: // Server message
518 parse_msg({
519 text: options.text,
520 chat: options.backend.chat.bind(options.backend)
521 });
522 break;
523 }
524
525 },
526 commands: {
527 'youtube': {
528 syntax: 'Syntax: !{cmd}-[{valids}] [<text>]',
529 active: true,
530 hidden: true,
531 admin: false,
532 callback: function (data) {
533 data = (typeof data !== "object") ? {} : data;
534
535 var msg = function (text) {
536 youtube.msg(Object.assign(data, {
537 text: text
538 }));
539 };
540
541 var error_callback = function (error) {
542 msg("Search failed (Bad request)");
543 engine.log(error);
544 };
545
546 youtube.api.search({
547 query: data.text,
548 fields: 'items(id)',
549 callback: function (search) {
550 search = (typeof search !== "object") ? {} : search;
551 search.items = search.items || [];
552
553 if (search.items.length <= 0) {
554 msg("Search failed (Nothing found)");
555 } else {
556 var playback = false;
557 var items = search.items;
558
559 items.forEach(function (item) {
560 item = (typeof item !== "object") ? {} : item;
561 item.id = item.id || {};
562 item.id.videoId = item.id.videoId || 0;
563 item.id.kind = item.id.kind || 'default';
564
565 youtube.api.video({
566 videoId: item.id.videoId,
567 callback: function (video) {
568 var probability = youtube.config.plugin.randomplay ? (Math.random() >= (1.0 - (1 / items.length))) : true;
569
570 youtube.callbacks.video_message({
571 msg: data,
572 video: video
573 });
574
575 if (!playback && items[items.length - 1].id.videoId === video.items[0].id) {
576 if (youtube.callbacks.video_playback({ video: video })) {
577 playback = true;
578 } else {
579 youtube.msg(Object.assign(data, {
580 text: 'Could not play video with filters set',
581 mode: 1
582 }));
583 }
584 } else if (!playback && probability) {
585 if (youtube.callbacks.video_playback({ video: video })) {
586 playback = true;
587 }
588 }
589 },
590 error_callback: error_callback
591 });
592 });
593 }
594 },
595 error_callback: error_callback
596 });
597 }
598 },
599 'video': {
600 syntax: 'Syntax !{cmd}-{par} <video-id/link>',
601 active: true,
602 hidden: false,
603 admin: false,
604 callback: function (data) {
605 var msg = function (text) {
606 youtube.msg(Object.assign(data, {
607 text: text
608 }));
609 };
610
611 var format = require('format');
612 var message_format = [
613 [format.bold('You'), format.color('Tube', '#ff0000'), ' '].join(''),
614 [
615 [
616 format.bold('Title:'), '{title}',
617 format.bold('Link:'), '[url={yt_link}]{yt_link}[/url]',
618 format.bold('Duration:'), '{duration}',
619 format.bold('Views:'), '{viewCount}',
620 format.bold('Likes:'), '{likeCount}',
621 format.bold('Dislikes:'), '{dislikeCount}',
622 format.bold('Comments:'), '{commentCount}',
623 format.bold('By:'), '{upload_by}',
624 format.bold('Description:'), '{description_complete}'
625 ].join(' '),
626 ].join(' - ')
627 ];
628
629 var videoid = youtube.config.plugin.regex.youtube.exec(data.text) || data.text;
630
631 youtube.api.video({
632 videoId: (typeof videoid === 'object' ? videoid[1] : videoid),
633 callback: function (video) {
634 youtube.callbacks.video_message({
635 msg: data,
636 message_format: message_format.join(''),
637 video: video
638 });
639 },
640 error_callback: function (err) {
641 msg("Search failed (Bad request)");
642 }
643 });
644 }
645 },
646 'search': {
647 syntax: 'Syntax !{cmd}-{par} <text>',
648 active: true,
649 hidden: false,
650 admin: false,
651 callback: function (data) {
652 var msg = function (text) {
653 youtube.msg(Object.assign(data, {
654 text: text
655 }));
656 };
657
658 var format = require('format');
659 var message_format = [
660 [format.bold('You'), format.color('Tube', '#ff0000'), ' '].join(''),
661 [
662 [
663 format.bold('Title:'), '{title}',
664 format.bold('Link:'), '[url={yt_link}]{yt_link}[/url]',
665 format.bold('Description:'), '{description}',
666 format.bold('by:'), '{upload_by}'
667 ].join(' '),
668 ].join(' - ')
669 ];
670
671 youtube.api.search({
672 query: data.text,
673 maxresults: 5,
674 callback: function (search) {
675 search = (typeof search !== "object") ? {} : search;
676 search.items = search.items || [];
677 var items = search.items;
678
679 items.forEach(function (item) {
680 /*
681 Engine.log(item);
682 */
683 item = (typeof item !== "object") ? {} : item;
684 item.id = item.id || {};
685 item.id.videoId = item.id.videoId || 0;
686 item.id.kind = item.id.kind || 'default';
687
688 youtube.callbacks.video_message({
689 msg: data,
690 message_format: message_format.join(''),
691 video: {
692 items: [{
693 id: item.id.videoId,
694 kind: item.id.kind,
695 snippet: item.snippet
696 }]
697 }
698 });
699 });
700 },
701 error_callback: function (err) {
702 msg("Search failed (Bad request)");
703 }
704 });
705 }
706 },
707 'playlist': {
708 syntax: '!{cmd}-{par} <playlist-id/playlist-link>',
709 active: false,
710 hidden: false,
711 admin: false,
712 callback: function (data) {
713 data = (typeof data !== "object") ? {} : data;
714
715 var msg = function (text) {
716 youtube.msg(Object.assign(data, {
717 text: text
718 }));
719 };
720
721 var playListId = data.text; // PLer7LLaCGeKcmzK7V8rK2WqAj4kKligOW
722
723 youtube.api.playlist({
724 playlistId: playListId,
725 callback: function (playlist) {
726 var item = playlist.items[0];
727 msg('{0} by {1} id: {2}'.format(
728 item.snippet.title,
729 item.snippet.channelTitle,
730 item.id
731 ));
732
733 youtube.api.playlistItems({
734 playlistId: item.id,
735 maxResults: 50,
736 callback: function (pl) {
737 var media = require('media');
738 pl.items.forEach(function (item) {
739 msg(item.contentDetails.videoId);
740 // Media.enqueueYt(item.contentDetails.videoId);
741 });
742 }
743 });
744 }
745 });
746 }
747 },
748 'about': {
749 syntax: false,
750 active: true,
751 hidden: false,
752 admin: false,
753 callback: function (data) {
754 var bot = backend.getBotClient();
755
756 var authors = (function () {
757 var text = [];
758 youtube.config.plugin.manifest.authors.forEach(function (author) {
759 text.push('{name} [{role}]'.format({
760 name: author.name,
761 role: author.role
762 }));
763 });
764 return text.join(', ');
765 });
766
767 youtube.msg(Object.assign(data, {
768 text: '{script_name} ({script_description}) script v{version} by {authors} running on {bot_name} for {running_time}'.format({
769 version: youtube.config.plugin.manifest.version,
770 script_name: youtube.config.plugin.manifest.name,
771 script_description: youtube.config.plugin.manifest.description,
772 authors: authors,
773 bot_name: bot.name(),
774 running_time: seconds_to_human(Math.floor(Date.now() / 1000) - youtube.config.plugin.manifest.running_time)
775 })
776 }));
777 }
778 },
779 'setkey': {
780 syntax: 'Syntax !{cmd}-{par} <key>',
781 active: true,
782 hidden: false,
783 admin: true,
784 callback: function (data) {
785 data = (typeof data !== "object") ? {} : data;
786 data.mode = 1;
787
788 var old_key = youtube.config.api.key;
789
790 youtube.config.api.key = data.text;
791 config.yt_apikey = youtube.config.api.key;
792 engine.saveConfig(config);
793
794 youtube.msg(Object.assign(data, {
795 text: 'Api Key renewed from {old_key} to {key}'.format({
796 key: youtube.config.api.key,
797 old_key: old_key
798 })
799 }));
800 }
801 },
802 'reset': {
803 syntax: 'Syntax !{cmd}-{par} {botname}',
804 active: true,
805 hidden: false,
806 admin: true,
807 callback: function (data) {
808 var msg = function (text) {
809 youtube.msg(Object.assign(data, {
810 text: text
811 }));
812 };
813 var bot = backend.getBotClient();
814
815 if (data.text === bot.name()) {
816 config = {};
817 engine.saveConfig(config);
818 if (!engine.reloadScripts()) {
819 msg('Can\'t reload script because its disabled in config.ini');
820 }
821 msg('All configuration reset to default. If not take effect make sure do you have activate "AllowReload" on your config.ini or reload it manually');
822 } else {
823 msg('You should type the bot name to reset settings');
824 }
825 }
826 },
827 'test': {
828 syntax: false,
829 active: true,
830 hidden: true,
831 admin: true,
832 callback: function (data) {
833 data = (typeof data !== "object") ? {} : data;
834
835 youtube.msg(Object.assign(data, {
836 text: youtube.config.api.maxresults
837 }));
838
839 engine.notify('ravioli ravioli');
840
841 var media = require('media');
842 var queue = media.getQueue();
843 queue.forEach(function (track) {
844 youtube.msg(Object.assign(data, {
845 text: "{0} - {1}".format(track.title(), seconds_to_human(track.duration() > 0 ? (track.duration() / 1000) : 0))
846 }));
847 });
848 }
849 },
850 'exec': {
851 syntax: 'Syntax !{cmd}-{par} <text>',
852 active: true,
853 hidden: true,
854 admin: true,
855 callback: function (data) {
856 data = (typeof data !== "object") ? {} : data;
857
858 try {
859 eval((data.text));
860 } catch(e) {
861 youtube.msg(Object.assign(data, {
862 text: e
863 }));
864 }
865 }
866 },
867 getCommands: (function () {
868 var commands = [];
869 Object.keys(youtube.commands).forEach(function (key) {
870 var command = youtube.commands[key];
871 if (command.active && !command.hidden) {
872 commands.push(key);
873 }
874 });
875 return commands || false;
876 })
877 },
878 callbacks: {
879 video_message: function (data) {
880 data = (typeof data !== "object") ? {} : data;
881
882 data.video = data.video || {};
883 data.msg = data.msg || {};
884
885 data.message_format = data.message_format || youtube.config.plugin.command_message;
886
887 var msg = function (text) {
888 youtube.msg(Object.assign(data.msg, {
889 text: text
890 }));
891 };
892
893 data.video.items = data.video.items || [];
894
895 data.video.items.forEach(function (item) {
896 item = (typeof item !== "object") ? {} : item;
897 item.kind = item.kind || 'default';
898 item.id = item.id || 0;
899
900 item.snippet = item.snippet || {};
901 item.snippet.title = item.snippet.title || 'no video';
902 item.snippet.description = item.snippet.description || 'no video';
903 item.snippet.channelTitle = item.snippet.channelTitle || 'no video';
904
905 item.contentDetails = item.contentDetails || {};
906 item.contentDetails.duration = item.contentDetails.duration || '0S';
907
908 item.statistics = item.statistics || {};
909 item.statistics.commentCount = item.statistics.commentCount || 0;
910 item.statistics.viewCount = item.statistics.viewCount || 0;
911 item.statistics.likeCount = item.statistics.likeCount || 0;
912 item.statistics.dislikeCount = item.statistics.dislikeCount || 0;
913 item.statistics.favoriteCount = item.statistics.favoriteCount || 0;
914
915 });
916
917 if (data.video.items.length > 0) {
918 var item = data.video.items[0];
919
920 if (item.kind === 'youtube#video') {
921 var str_var = {
922 title: item.snippet.title,
923 description_complete: item.snippet.description,
924 description: item.snippet.description.trunc(160),
925 video_id: item.id,
926 yt_link: 'http://www.youtube.com/watch?v={0}'.format(item.id),
927 upload_by: item.snippet.channelTitle,
928 duration: seconds_to_human(youtube_duration_seconds(item.contentDetails.duration)),
929 commentCount: addCommas(item.statistics.commentCount),
930 viewCount: addCommas(item.statistics.viewCount),
931 likeCount: addCommas(item.statistics.likeCount),
932 dislikeCount: addCommas(item.statistics.dislikeCount),
933 favoriteCount: addCommas(item.statistics.favoriteCount)
934 };
935
936 // Send message
937 msg(data.message_format.format(str_var));
938 } else {
939 msg("Search failed (Invalid type)");
940 engine.log(data.video);
941 }
942 } else {
943 msg("Search failed (Nothing found)");
944 }
945 },
946 video_playback: function (data) {
947 data = (typeof data !== "object") ? {} : data;
948
949 data.video = data.video || {};
950 data.video.items = data.video.items || [];
951
952 data.video.items.forEach(function (item) {
953 item = (typeof item !== "object") ? {} : item;
954 item.kind = item.kind || 'default';
955 item.id = item.id || 0;
956 item.snippet = item.snippet || {};
957 item.snippet.title = item.snippet.title || 'no video';
958 item.contentDetails = item.contentDetails || {};
959 item.contentDetails.duration = item.contentDetails.duration || '0S';
960 });
961
962 // Check for valid json
963 if (data.video.items.length > 0) {
964 var media = require('media');
965
966 var video = data.video.items[0];
967
968 var playable = {
969 title: function () {
970 var video_title = video.snippet.title.toLowerCase();
971 var blacklist = false;
972 youtube.config.plugin.yt_titleblacklist.forEach(function (word) {
973 if (!blacklist && video_title.indexOf(word.toLowerCase()) !== -1) {
974 blacklist = true;
975 }
976 });
977 return (blacklist ? false : true);
978 },
979 duration: function () {
980 var video_duration = youtube_duration_seconds(video.contentDetails.duration);
981 if (video_duration <= youtube.config.plugin.yt_maxduration) return true;
982 return false;
983 }
984 };
985 if (!playable.title() || !playable.duration()) return false;
986
987 var videoId = video.id;
988 var queue = (youtube.config.plugin.ytdl_playback === 0 ? true : false);
989
990 /*
991 {youtube.config.plugin.ytdl_action}
992 1: Download
993 2: Stream
994 0: Nothing
995 */
996 switch (youtube.config.plugin.ytdl_action) {
997 case 1: // Download
998 if (queue) {
999 if (media.ytdl(videoId, false) && media.enqueueYt(videoId)) {
1000 engine.log("Download & append to queue: " + videoId);
1001 } else {
1002 engine.log("Cannot enqueue: " + videoId);
1003 }
1004 } else {
1005 if (media.ytdl(videoId, (queue ? false : true))) {
1006 engine.log("Donwload & play: " + videoId);
1007 } else {
1008 engine.log("Can't download: " + videoId);
1009 }
1010 }
1011 return true;
1012 case 2: // Stream
1013 if (queue) {
1014 if (media.enqueueYt(videoId)) {
1015 engine.log("Append to queue: " + videoId);
1016 } else {
1017 engine.log("Cannot enqueue: " + videoId);
1018 }
1019 } else {
1020 if (media.yt(videoId)) {
1021 engine.log("Streaming: " + videoId);
1022 } else {
1023 engine.log("Can't Streaming: " + videoId);
1024 }
1025 }
1026 return true;
1027 default: // Nothing
1028 return true;
1029 }
1030 }
1031 return false;
1032 }
1033 }
1034 };
1035
1036 // Check for script version
1037 (function () {
1038 var store = require('store');
1039 var version = store.getInstance('script_version')
1040
1041 if (version !== youtube.config.plugin.manifest.version) {
1042 engine.log('Your running a different version of the script, resetting configuration, please reconfigure it from web panel.');
1043 engine.notify('Configure youtube search script');
1044
1045 store.setInstance('script_version', youtube.config.plugin.manifest.version);
1046 engine.saveConfig({});
1047 }
1048
1049 }());
1050
1051 // Chat event
1052 event.on('chat', function (ev) {
1053
1054 var client = ev.client;
1055 var channel = ev.channel;
1056 var bot = backend.getBotClient();
1057
1058 if (client.isSelf()) return;
1059
1060 // Check for valid groups
1061 /*
1062 TODO
1063 - [ENH] Better way to split groups
1064 - [BUG] Make sure if works in all cases
1065 */
1066 var permission = {
1067 config: {
1068 group: youtube.config.plugin.server_groups.map(function (arr) {
1069 return arr.group;
1070 }),
1071 client: youtube.config.plugin.adminpermissions.map(function (arr) {
1072 return arr.user;
1073 }),
1074 banned: youtube.config.plugin.blacklistusers
1075 },
1076 group: {
1077 has_permission: function () {
1078 if (permission.config.group.length > 0) {
1079 var has_permission = false;
1080 client.getServerGroups().forEach(function (group) {
1081 if ((!has_permission) && ((permission.config.group.indexOf(group.name()) > -1) || (permission.config.group.indexOf(group.id()) > -1))) {
1082 has_permission = true;
1083 }
1084 });
1085 return has_permission;
1086 }
1087 return true;
1088 }
1089 },
1090 client: {
1091 is_banned: function () {
1092 if (permission.config.banned.length > 0) {
1093 if ((permission.config.banned.indexOf(client.name()) > -1) || (permission.config.banned.indexOf(client.uniqueID()) > -1))
1094 return true;
1095 }
1096 return false;
1097 },
1098 is_admin: function () {
1099 if (permission.config.client.length > 0) {
1100 if ((permission.config.client.indexOf(client.name()) > -1) || (permission.config.client.indexOf(client.uniqueID()) > -1))
1101 return true;
1102 }
1103 return false;
1104 }
1105 }
1106 };
1107 if (!permission.group.has_permission()) {
1108 engine.log('{client_name}, not have enough permission to execute script'.format({
1109 client_name: client.name()
1110 }));
1111 return;
1112 }
1113 if (permission.client.is_banned()) {
1114 engine.log('{client_name}, is banned from the bot and can\'t execute commands'.format({
1115 client_name: client.name()
1116 }));
1117 return;
1118 }
1119
1120 var main_cmd = youtube.commands['youtube'];
1121 var msg = {
1122 mode: ev.mode,
1123 client: client,
1124 channel: channel
1125 };
1126 var cmd, par, text;
1127 // Regex text: !{command}[-{area}] {text}
1128 if ((text = youtube.config.plugin.regex.cmd.exec(ev.text)) !== null) {
1129 cmd = text[1].toLowerCase(); // Command trigger
1130 par = text[2]; // Command area
1131 text = text[3]; // Args
1132
1133 // Chat command equals to command trigger
1134 if (cmd === youtube.config.plugin.command_trigger.toLowerCase() && main_cmd.active) {
1135 // If have a sub-command
1136 if (par) {
1137 par = par.toLocaleLowerCase();
1138 // Sub-command exists on script
1139 if (par in youtube.commands && par !== 'youtube') {
1140 // Command have a callback function
1141 if ('callback' in youtube.commands[par] && typeof youtube.commands[par].callback === 'function') {
1142 var command = youtube.commands[par];
1143
1144 // Command is turned off
1145 if (!command.active) {
1146 engine.log('{command} command is turned off and can\'t execute'.format({
1147 command: par
1148 }));
1149 return;
1150 }
1151
1152 if (command.admin && !permission.client.is_admin()) {
1153 youtube.msg(Object.assign(msg, {
1154 mode: 1,
1155 text: 'You not have enough permissions'
1156 }));
1157 return;
1158 }
1159
1160 // If the command have a syntax to work
1161 if (command.syntax) {
1162 // Check if chat have text
1163 if (text) {
1164 command.callback(Object.assign({
1165 text: text
1166 }, msg));
1167 // No text pass return command syntax
1168 } else {
1169 youtube.msg(Object.assign({
1170 text: command.syntax.format({
1171 cmd: cmd,
1172 par: par,
1173 botname: bot.name()
1174 })
1175 }, msg));
1176 }
1177 // No syntax needed trigger commmand
1178 } else {
1179 command.callback(msg);
1180 }
1181 // Send message if not valid callback found
1182 } else {
1183 youtube.msg(Object.assign(msg, {
1184 mode: 1,
1185 text: 'Invalid command !{cmd}-{par} not have a valid callback'.format({
1186 cmd: cmd,
1187 par: par
1188 })
1189 }));
1190 }
1191 // Sub-command not exists, send valid list
1192 } else {
1193 youtube.msg(Object.assign(msg, {
1194 text: 'Invalid command !{cmd}-{par}. Valid commands: !{cmd}-[{valids}]'.format({
1195 cmd: cmd,
1196 par: par,
1197 valids: youtube.commands.getCommands
1198 })
1199 }));
1200 }
1201 } else if (text) {
1202 // Trigger search {text}
1203 main_cmd.callback(Object.assign({
1204 text: text
1205 }, msg));
1206 } else {
1207 youtube.msg(Object.assign({
1208 text: main_cmd.syntax.format({
1209 cmd: cmd,
1210 valids: youtube.commands.getCommands
1211 })
1212 }, msg));
1213 }
1214 }
1215 // No command format found; check if a valid Youtube URL
1216 } else {
1217 var videoId;
1218 if (youtube.config.plugin.catch_url && (videoId = youtube.config.plugin.regex.youtube.exec(ev.text)) !== null) {
1219 videoId = videoId[1];
1220 // Trigger search {videoId}
1221 youtube.api.video({
1222 videoId: videoId,
1223 callback: function (data) {
1224 youtube.callbacks.video_message({
1225 video: data,
1226 msg: msg
1227 });
1228
1229 if (msg.mode === 2) {
1230 youtube.callbacks.video_playback({
1231 video: data
1232 });
1233 }
1234 },
1235 error_callback: function (data) {
1236 youtube.msg(Object.assign(msg, {
1237 text: "Invalid request (Bad request)"
1238 }));
1239 }
1240 });
1241 }
1242 }
1243 });
1244});
1245