您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
The script adds emote functions in the chat.
// ==UserScript== // @name Better Peka2.TV // @namespace http://tampermonkey.net/ // @version 50 // @description The script adds emote functions in the chat. // @author SeregPie // @require https://code.jquery.com/jquery-3.1.1.min.js // @match http://peka2.tv/* // ==/UserScript== (function() { 'use strict'; !function(a){var b=a.extend(!0,function(a){this._parentEdge=a,this._childNodes=new Map,this._handleObjects=[]},{prototype:{signal:function(b){b=null===b?[null]:a.makeArray(b);var c=[];this._collectCallables(c,b,0),c.forEach(function(a){try{a()}catch(a){}})},_collectCallables:function(b,c,d){if(this._handleObjects.forEach(function(d){var e=a.Event("signal",d);b.push(function(){e.handler.apply(e.target,[e].concat(c))})}),c.length>d){var e=this._childNodes.get(c[d]);if(e){var f=c.slice();f.splice(d,1),e._collectCallables(b,f,d)}if(e=this._childNodes.get(a)){var g=d+1;e._collectCallables(b,c,g)}}},addHandleObject:function(b){var c=b.data;return c=null===c?[null]:a.makeArray(c),this._addHandleObject(b,c)},_addHandleObject:function(c,d){if(d.length){var e=d[0],f=this._childNodes.get(e);return f||this._childNodes.set(e,f=new b([this,e])),f._addHandleObject(c,d.slice(1))}return this._handleObjects.push(c),{dispose:a.proxy(function(){var b=a.inArray(c,this._handleObjects);b>=0&&(this._handleObjects.splice(b,1),this._removeIfEmpty())},this)}},_removeChildNode:function(a){this._childNodes.delete(a),this._removeIfEmpty()},_removeIfEmpty:function(){this.isEmpty()&&this._parentEdge&&this._parentEdge[0]._removeChildNode(this._parentEdge[1])},isEmpty:function(){return!this._handleObjects.length&&!this._childNodes.size}}}),c=new b;a.signal=function(){return c.signal(arguments)},a.event.special.signal={noBubble:!0,add:function(a){a.data=c.addHandleObject({currentTarget:this,data:a.data,handler:a.handler,namespace:a.namespace,target:this})},remove:function(a){a.data.dispose()}}}(jQuery); const ns = {}; ns.peka2tv = $.extend(true, function(storage_key) { this.storage_event_listener = ns.peka2tv._storage_event_listener.bind(this); this._storage_key = storage_key; this._emotes = {}; this._source_emotes_hidden = {}; this._chat_message_gap_decrease = 1; this._stretch_emote_list = true; this._load(); }, { _storage_event_listener(event) { if (event.originalEvent.key === this._storage_key) { this._reload(); } }, _emote_sources: { 'twitch.tv': { purify_emote(emote) { return emote.toLowerCase().replace(/(^\s*(:(tw-)?)?)|((:)?\s*$)/g, ''); }, get_code(emote) { return ':tw-'+emote+':'; }, get_image_url(emote, value) { return 'https://static-cdn.jtvnw.net/emoticons/v1/'+value+'/1.0'; }, get_value(emote, callback) { this.get_values(values => { let value = values[emote]; if (value) { callback(value); } }); }, _values: null, get_values(callback) { if (this._values === null) { let deferred = $.Deferred(); Promise.all([ new Promise(resolve => { let attempt = () => { $.get('https://twitchemotes.com/api_cache/v2/global.json') .done(resolve) .fail(() => setTimeout(attempt, 5 * 1000)); }; attempt(); }), new Promise(resolve => { let attempt = () => { $.get('https://twitchemotes.com/api_cache/v2/subscriber.json') .done(resolve) .fail(() => setTimeout(attempt, 5 * 1000)); }; attempt(); }), ]) .then(([global_emotes, subscriber_emotes]) => { let values = {}; $.each(subscriber_emotes.unknown_emotes.emotes, (i, obj) => { let emote = obj.code.toLowerCase(); let value = obj.image_id; values[emote] = value; }); $.each(subscriber_emotes.channels, (channel, obj) => { $.each(obj.emotes, (i, obj) => { let emote = obj.code.toLowerCase(); let value = obj.image_id; values[emote] = value; }); }); $.each(global_emotes.emotes, (code, obj) => { let emote = code.toLowerCase(); let value = obj.image_id; values[emote] = value; }); deferred.resolve(values); }); this._values = deferred.promise(); } $.when(this._values).done(values => callback(JSON.parse(JSON.stringify(values)))); }, }, 'goodgame.ru': { purify_emote(emote) { return emote.toLowerCase().replace(/(^\s*(:(gg-)?)?)|((:)?\s*$)/g, ''); }, get_code(emote) { return ':gg-'+emote+':'; }, _image_base_url: 'https://goodgame.ru/images/', get_image_url(emote, value) { return this._image_base_url+value; }, get_value(emote, callback) { this.get_values(values => { let value = values[emote]; if (value) { callback(value); } }); }, _values: null, get_values(callback) { if (this._values === null) { let values = {}; let deferred = $.Deferred(); let collect_values = (page) => { return new Promise(resolve => { let attempt = () => { let proxy = 'https://cors-anywhere.herokuapp.com/'; $.get(proxy+'http://api2.goodgame.ru/v2/smiles?page='+page) .done(json => { console.log(json); $.each(json._embedded.smiles, (i, obj) => { let emote = obj.key.toLowerCase(); let value = obj.urls.big.substr(this._image_base_url.length).replace(/\?\d+$/, ''); values[emote] = value; }); resolve(json); }) .fail(() => setTimeout(attempt, 5 * 1000)); }; attempt(); }); }; collect_values(1).then(json => { let promises = []; for (let page = 2, pages = json.page_count; page <= pages; page++) { promises.push(collect_values(page)); } Promise.all(promises).then(() => deferred.resolve(values)); }); this._values = deferred.promise(); } $.when(this._values).done(values => callback(JSON.parse(JSON.stringify(values)))); }, }, }, get_emote_sources() { return Object.keys(this._emote_sources); }, prototype: { import(data) { try { this._put_data(data); this._save_data(data); } catch (err) {} }, export() { return this._get_data(); }, _load() { try { this._reload(); } catch (err) {} }, _reload() { try { let data = this._load_data(); this._put_data(data); } catch (err) {} }, _save() { try { let data = this._get_data(); this._save_data(data); } catch (err) {} }, _load_data() { return JSON.parse(localStorage.getItem(this._storage_key)); }, _save_data(data) { localStorage.setItem(this._storage_key, JSON.stringify(data)); }, _get_data() { let data = { emotes: this._emotes, source_emotes_hidden: this._source_emotes_hidden, chat_message_gap_decrease: this._chat_message_gap_decrease, stretch_emote_list: this._stretch_emote_list, }; return data; }, _put_data(data) { $.each(data.emotes, (source, emotes) => { this._set_emotes(source, emotes); }); $.each(data.source_emotes_hidden, (source, emotes_hidden) => { this._set_source_emotes_hidden(source, emotes_hidden); }); if (data.chat_message_gap_decrease !== undefined) { this._set_chat_message_gap_decrease(data.chat_message_gap_decrease); } if (data.stretch_emote_list !== undefined) { this._set_stretch_emote_list(data.stretch_emote_list); } }, get_emotes(source) { return Object.keys(this._emotes[source] || {}); }, get_emote_code(source, emote) { emote = ns.peka2tv._emote_sources[source].purify_emote(emote); let value = (this._emotes[source] || {})[emote]; if (value === undefined) return ''; return ns.peka2tv._emote_sources[source].get_code(emote, value); }, get_emote_image_url(source, emote) { emote = ns.peka2tv._emote_sources[source].purify_emote(emote); let value = (this._emotes[source] || {})[emote]; if (value === undefined) return ''; return ns.peka2tv._emote_sources[source].get_image_url(emote, value); }, add_emote(source, emote, callback) { emote = ns.peka2tv._emote_sources[source].purify_emote(emote); callback = callback || $.noop; ns.peka2tv._emote_sources[source].get_value(emote, value => { if (this._add_emote(source, emote, value)) { this._save(); callback(); } }); }, add_random_emotes(source, count, callback) { callback = callback || $.noop; ns.peka2tv._emote_sources[source].get_values(values => { let emotes = this.get_emotes(source); emotes.forEach(emote => delete values[emote]); emotes = Object.keys(values); count = Math.min(count, emotes.length); while (count-- > 0) { let emote = emotes.splice(Math.floor(Math.random() * emotes.length), 1)[0]; let value = values[emote]; this._add_emote(source, emote, value); } callback(); }); }, _add_emote(source, emote, value) { if (!(this._emotes[source] || {}).hasOwnProperty(emote)) { this._emotes[source] = this._emotes[source] || {}; this._emotes[source][emote] = value; $.signal(this, 'add_emote', source, emote); return true; } return false; }, pop_emote(source, emote) { emote = ns.peka2tv._emote_sources[source].purify_emote(emote); if (this._pop_emote(source, emote)) { this._save(); } }, _pop_emote(source, emote) { if ((this._emotes[source] || {}).hasOwnProperty(emote)) { delete this._emotes[source][emote]; if ($.isEmptyObject(this._emotes[source])) { delete this._emotes[source]; } $.signal(this, 'pop_emote', source, emote); return true; } return false; }, _set_emotes(source, emotes) { let emotes_2pop = {}; this.get_emotes(source).forEach(emote => emotes_2pop[emote] = true); $.each(emotes, (emote, value) => { this._add_emote(source, emote, value); delete emotes_2pop[emote]; }); $.each(emotes_2pop, emote => { this._pop_emote(source, emote); }); }, get_source_emotes_hidden(source) { return !!this._source_emotes_hidden[source]; }, set_source_emotes_hidden(source, emotes_hidden) { if (this._set_source_emotes_hidden(source, emotes_hidden)) { this._save(); } }, _set_source_emotes_hidden(source, emotes_hidden) { if (!!this._source_emotes_hidden[source] !== emotes_hidden) { if (emotes_hidden) { this._source_emotes_hidden[source] = true; } else { delete this._source_emotes_hidden[source]; } $.signal(this, 'set_source_emotes_hidden', source, emotes_hidden); return true; } return false; }, get_chat_message_gap_decrease() { return this._chat_message_gap_decrease; }, set_chat_message_gap_decrease(chat_message_gap_decrease) { if (this._set_chat_message_gap_decrease(chat_message_gap_decrease)) { this._save(); } }, _set_chat_message_gap_decrease(chat_message_gap_decrease) { if (this._chat_message_gap_decrease !== chat_message_gap_decrease) { this._chat_message_gap_decrease = chat_message_gap_decrease; $.signal(this, 'set_chat_message_gap_decrease', chat_message_gap_decrease); return true; } return false; }, get_stretch_emote_list() { return this._stretch_emote_list; }, set_stretch_emote_list(stretch_emote_list) { if (this._set_stretch_emote_list(stretch_emote_list)) { this._save(); } }, _set_stretch_emote_list(stretch_emote_list) { if (this._stretch_emote_list !== stretch_emote_list) { this._stretch_emote_list = stretch_emote_list; $.signal(this, 'set_stretch_emote_list', stretch_emote_list); return true; } return false; }, } }); const unique_key = 'x7tcckanh13l8FyiRt7bXB12NpBz9Djn'; const peka2tv = new ns.peka2tv(unique_key); $(window).on('storage', peka2tv.storage_event_listener); $(function() { setInterval(() => { $('.chat-emote-list').each(function() { if ($(this).data(unique_key)) return; $(this).data(unique_key, true); $(this) .closest('chat-instance') .find('chat-text textarea') .on('change keypress', function() { $(this).closest('chat-instance').find('chat-smile-list > .chat-emote-list-wrapper').addClass('ng-hide').prop('hidden', true); }) .end() .end(); ns.peka2tv.get_emote_sources().forEach(source => { $('<div>', { on: { contextmenu: function() { return false; }, }, }) .append( $('<div>', { class: 'chat-emote-group', }) .on('signal.add_emote', [peka2tv, 'add_emote', source], function(e, emote) { let image_url = peka2tv.get_emote_image_url(source, emote); let code = peka2tv.get_emote_code(source, emote); $('<div>', { class: 'chat-emote-list__item', attr: { 'title': code, }, on: { click: function() { $(this).closest('chat-instance').find('chat-text textarea') .val((i, val) => val + code) .focus(); return false; }, contextmenu: function() { peka2tv.pop_emote(source, emote); return false; }, }, }) .on('signal.pop_emote', [peka2tv, 'pop_emote', source, emote], function() { $(this).remove(); }) .append($('<img>', {src: image_url})) .appendTo($(this).find('.emotes')); return false; }) .on('signal.set_source_emotes_hidden', [peka2tv, 'set_source_emotes_hidden', source], function(e, source_emotes_hidden) { $(this) .find('.hiddenable') .css('display', source_emotes_hidden ? 'none' :'') .end() .find('.source_emotes_hidden') .text(source_emotes_hidden ? '➖' : '➕') .end(); return false; }) .append( $('<div>', { class: 'chat-emote-group__title', css: { 'display': 'flex', }, }) .append( $('<div>', { text: '➕', class: 'source_emotes_hidden', css: { 'display': 'flex', 'flex-direction': 'column', 'justify-content': 'center', 'border': '1px solid Gray', 'cursor': 'pointer', 'font-size': 6+'px', }, on: { click: function() { peka2tv.set_source_emotes_hidden(source, !peka2tv.get_source_emotes_hidden(source)); }, }, }), $('<div>', {css: {'width': '5px'}}), $('<div>', {text: source}) ), $('<div>', {class: 'hiddenable'}) .append( $('<div>', { css: { 'display': 'flex', }, }) .append( $('<input>', { type: 'text', placeholder: 'добавь смайлик', css: { 'min-width': 0, 'flex-grow': 1, }, on: { keypress: function(event) { if (event.which === 13) { let emote = $(this).val(); if (emote.length) { $(this).val(''); peka2tv.add_emote(source, emote); } return false; } }, } }), $('<button>', { type: 'button', text: '?', title: 'мне повезёт', on: { click: function(event) { peka2tv.add_random_emotes(source, 1); return false; }, } }) ), $('<div>', {class: 'emotes'}) ) ) .each(function() { $(this).triggerHandler('signal.set_source_emotes_hidden', [peka2tv.get_source_emotes_hidden(source)]); peka2tv.get_emotes(source).forEach(emote => { $(this).triggerHandler('signal.add_emote', [emote]); }); }) ) .prependTo(this); }); }); }, 100); }); $(function() { let interval_id = setInterval(() => { $('.header-item-more-list__item[href="http://forum.peka2.tv"]').each(function() { clearInterval(interval_id); $(this).attr({'href': 'http://forum.peka2.tv/search.php?do=getnew'}); }); }, 100); }); $(function() { $('<style>') .on('signal.set_chat_message_gap_decrease', [peka2tv, 'set_chat_message_gap_decrease'], function(e, chat_message_gap_decrease) { let value = {'1': '.46', '2': '.27'}[chat_message_gap_decrease]; if (value) { $(this) .html(` .chat-msg { padding: ${value}rem 0 !important; } `) .prependTo('body'); } else { $(this).detach(); } }) .each(function() { $(this).triggerHandler('signal.set_chat_message_gap_decrease', [peka2tv.get_chat_message_gap_decrease()]); }); }); $(function() { $( `<style> chat-common chat-instance .chat-emote-list-wrapper { left: 0 !important; right: 0 !important; top: 1.75rem !important; bottom: 3.375rem !important; width: auto !important; height: auto !important; max-width: none !important; max-height: none !important; } chat-common chat-instance .chat-emote-list-wrapper > chat-emote-list { width: 100% !important; height: 100% !important; } </style>` ) .on('signal.set_stretch_emote_list', [peka2tv, 'set_stretch_emote_list'], function(e, stretch_emote_list) { if (stretch_emote_list) { $(this).prependTo('body'); } else { $(this).detach(); } }) .each(function() { $(this).triggerHandler('signal.set_stretch_emote_list', [peka2tv.get_stretch_emote_list()]); }); }); $(function() { let el_menu = $(` <div class="_settings" style=" position: fixed; z-index: 9002; top: 80px; left: 0; right: 0; max-width: 600px; margin: 0 auto; padding: 20px; border: 2px solid black; border-radius: 10px; background-color: white; "> <label> <span>уменьшить расстояние между сообщениями в чате</span> <select class="_chat_message_gap_decrease"> <option value="0">нет</option> <option value="1">немного</option> <option value="2">сильно</option> </select> </label> <div style=" height: 10px; "></div> <label> <span>растянуть список смайлов на весь чат</span> <input type="checkbox" class="_stretch_emote_list"/> </label> <div style=" height: 10px; "></div> <textarea class="_data" rows="5" style=" resize: none; width: 100%; "></textarea> <button type="button" class="_export">экспортировать настройки</button> <button type="button" class="_import">импортировать настройки</button> <div style=" height: 50px; "></div> <button class="_close">закрыть</button> </div> `) .find('._close') .on('click', function() { $(this).closest('._settings').detach(); }) .end() .find('._chat_message_gap_decrease') .on('signal.set_chat_message_gap_decrease', [peka2tv, 'set_chat_message_gap_decrease'], function(e, chat_message_gap_decrease) { $(this).val(chat_message_gap_decrease); }) .each(function() { $(this).triggerHandler('signal.set_chat_message_gap_decrease', [peka2tv.get_chat_message_gap_decrease()]); }) .on('change', function() { let chat_message_gap_decrease = parseInt($(this).val()); peka2tv.set_chat_message_gap_decrease(chat_message_gap_decrease); }) .end() .find('._stretch_emote_list') .on('signal.set_stretch_emote_list', [peka2tv, 'set_stretch_emote_list'], function(e, stretch_emote_list) { $(this).prop('checked', stretch_emote_list); }) .each(function() { $(this).triggerHandler('signal.set_stretch_emote_list', [peka2tv.get_stretch_emote_list()]); }) .on('change', function() { let stretch_emote_list = $(this).is(':checked'); peka2tv.set_stretch_emote_list(stretch_emote_list); }) .end() .find('._export') .on('click', function() { try { let data = peka2tv.export(); data = JSON.stringify(data); $(this).closest('._settings').find('._data').val(data); } catch (err) {} }) .end() .find('._import') .on('click', function() { try { let data = $(this).closest('._settings').find('._data').val(); data = JSON.parse(data); peka2tv.import(data); } catch (err) {} }) .end() .get(0); setInterval(() => { $('.chat-settings-menu').each(function() { if ($(this).data(unique_key)) return; $(this).data(unique_key, true); $('<div>', { class: 'context-menu__item', text: 'Настройки (Better Peka2.TV)', click: function() { $(el_menu).appendTo('body'); }, appendTo: this, }); }); }, 100); }); })();