숲 채팅 이모티콘

최신, ogq 이모티콘 고정 및 개인설정 채팅 너비 및 기본 모드 일시 간격 조절

  1. // ==UserScript==
  2. // @name 숲 채팅 이모티콘
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.0.4
  5. // @description 최신, ogq 이모티콘 고정 및 개인설정 채팅 너비 및 기본 모드 일시 간격 조절
  6. // @author aowelwld2
  7. // @include https://play.sooplive.co.kr/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=tampermonkey.net
  9. // @license MIT
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. function init() {
  17. const chattingArea = document.querySelector("#chatting_area");
  18. const actionBox = chattingArea.querySelector("#actionbox");
  19.  
  20. const personSettingEl = chattingArea.querySelector(
  21. ".chat_layer.sub.person .contents > ul"
  22. );
  23. const rangeLI = document.createElement("li");
  24. const rangeLabel = document.createElement("label");
  25.  
  26. rangeLabel.classList.add("customLabel");
  27. rangeLabel.innerHTML = `<span>채팅너비(${chattingArea.offsetWidth}px)</span>`;
  28. rangeLI.append(rangeLabel);
  29.  
  30. const rangeInput = document.createElement("input");
  31. rangeInput.type = "range";
  32. rangeInput.min = 300;
  33. rangeInput.max = 400;
  34. rangeInput.value = localStorage.getItem("customChattingAreaWidth")
  35. ? localStorage.getItem("customChattingAreaWidth")
  36. : chattingArea.offsetWidth;
  37. rangeLabel.append(rangeInput);
  38. rangeInput.addEventListener("input", () => {
  39. changeChatAreaWidth(rangeInput.value);
  40. localStorage.setItem("customChattingAreaWidth", rangeInput.value);
  41. });
  42. personSettingEl.append(rangeLI);
  43.  
  44. const chatStyleEl = document.createElement("style");
  45. document.head.append(chatStyleEl);
  46. function changeChatAreaWidth(width) {
  47. rangeLabel.querySelector("span").textContent = `채팅너비 (${width}px)`;
  48. chatStyleEl.textContent = `
  49. #webplayer.chat_open {
  50. --chatting_W: ${width}px;
  51. }
  52. `;
  53. }
  54. const narrowDiv = document.createElement("div");
  55. narrowDiv.classList.add("checkbox_wrap");
  56. const narrowLI = document.createElement("li");
  57. const narrowLabel = document.createElement("label");
  58. const narrowInput = document.createElement("input");
  59. narrowInput.type = "checkbox";
  60. narrowInput.id = "narrowChatArea";
  61. narrowInput.checked =
  62. localStorage.getItem("customChattingAreaNarrow") === "1" ? true : false;
  63. narrowLabel.htmlFor = "narrowChatArea";
  64. narrowLabel.textContent = "전체간격줄이기";
  65.  
  66. narrowDiv.append(narrowInput, narrowLabel);
  67. narrowLI.append(narrowDiv);
  68. personSettingEl.append(narrowLI);
  69. narrowInput.addEventListener("change", () => {
  70. narrowChatArea(narrowInput.checked);
  71. localStorage.setItem(
  72. "customChattingAreaNarrow",
  73. narrowInput.checked ? 1 : 0
  74. );
  75. });
  76.  
  77. function narrowChatArea(on = true) {
  78. on
  79. ? document.querySelector("#webplayer_contents").classList.add("narrow")
  80. : document
  81. .querySelector("#webplayer_contents")
  82. .classList.remove("narrow");
  83. localStorage.setItem("customChattingAreaNarrow", on ? 1 : 0);
  84. }
  85.  
  86. const emoticonBox = document.querySelector("#emoticonBox");
  87. const recentEmoticonBtn = emoticonBox.querySelector(
  88. ".tab_area .item_list ul > li[data-type='RECENT'] .ic_clock"
  89. );
  90. const subTabArea = emoticonBox.querySelector(".subTab_area");
  91. const defaultSubTab = subTabArea.querySelector("li[data-type='DEFAULT']");
  92. const OGQSubTab = subTabArea.querySelector("li[data-type='OGQ']");
  93.  
  94. function defaultEmoticonClick() {
  95. recentEmoticonBtn.click();
  96. setTimeout(() => {
  97. defaultSubTab.click();
  98. }, 100);
  99. }
  100. function OGQEmoticonClick() {
  101. recentEmoticonBtn.click();
  102. setTimeout(() => {
  103. OGQSubTab.click();
  104. }, 100);
  105. }
  106.  
  107. const chattingItemWrap = chattingArea.querySelector(".chatting-item-wrap");
  108. const chatArea = chattingItemWrap.querySelector("#chat_area");
  109. const customEmojiBox = document.createElement("div");
  110. customEmojiBox.classList.add("customEmojiBox");
  111. let isLoading = false;
  112.  
  113. function renderEmoticon(type = "default") {
  114. type === "default" ? defaultEmoticonClick() : OGQEmoticonClick();
  115. if (isLoading) return;
  116. isLoading = true;
  117. setTimeout(() => {
  118. isLoading = false;
  119.  
  120. const diffType = type === "default" ? "OGQ" : "default";
  121. const isExist = chattingItemWrap.querySelector(".customEmojiBox")
  122. ? true
  123. : false;
  124. const isOn = customEmojiBox.classList.contains(type) ? true : false;
  125. const isDiffOn = customEmojiBox.classList.contains(diffType)
  126. ? true
  127. : false;
  128. if (isOn) {
  129. customEmojiBox.classList.remove(type);
  130. customEmojiBox.innerHTML = "";
  131. customEmojiBox.style.display = "none";
  132. chatArea.style.bottom = "0";
  133. return;
  134. }
  135.  
  136. const emoticonBox = document.querySelector("#emoticonBox");
  137. const emoticonItemBox = emoticonBox.querySelector(".emoticon_item");
  138. const itemList = [];
  139. emoticonItemBox.querySelectorAll("span > a")?.forEach((item, index) => {
  140. if (index < 21) {
  141. const itemClone = item.cloneNode(true);
  142. itemClone.addEventListener("click", item.click.bind(item));
  143. itemList.push(itemClone);
  144. }
  145. });
  146. if (isDiffOn) {
  147. customEmojiBox.classList.remove(diffType);
  148. customEmojiBox.innerHTML = "";
  149. }
  150. customEmojiBox.append(...itemList);
  151.  
  152. if (!isExist) {
  153. chattingItemWrap.append(customEmojiBox);
  154. }
  155. customEmojiBox.style.display = "flex";
  156. customEmojiBox.classList.add(type);
  157. chatArea.style.position = "relative";
  158. chatArea.style.bottom = customEmojiBox.offsetHeight + 8 + "px";
  159. }, 200);
  160. }
  161.  
  162. const recentEmoticonCustomBtnLI = document.createElement("li");
  163. const recentEmoticonCustomBtn = document.createElement("a");
  164. recentEmoticonCustomBtn.href = "javascript:;";
  165. recentEmoticonCustomBtn.classList.add("customEmojiBtn");
  166. recentEmoticonCustomBtn.textContent = "최근";
  167. recentEmoticonCustomBtnLI.append(recentEmoticonCustomBtn);
  168.  
  169. const OGQEmoticonCustomBtnLI = document.createElement("li");
  170. const OGQEmoticonCustomBtn = document.createElement("a");
  171. OGQEmoticonCustomBtn.href = "javascript:;";
  172. OGQEmoticonCustomBtn.classList.add("customEmojiBtn");
  173. OGQEmoticonCustomBtn.textContent = "OGQ";
  174. OGQEmoticonCustomBtnLI.append(OGQEmoticonCustomBtn);
  175.  
  176. recentEmoticonCustomBtnLI.addEventListener("click", () => {
  177. renderEmoticon("default");
  178. });
  179. OGQEmoticonCustomBtnLI.addEventListener("click", () => {
  180. renderEmoticon("OGQ");
  181. });
  182.  
  183. actionBox
  184. .querySelector(".item_box")
  185. .append(recentEmoticonCustomBtnLI, OGQEmoticonCustomBtnLI);
  186.  
  187. document.head.append(chatStyleEl);
  188.  
  189. // 커스텀 스타일 추가
  190.  
  191. const defaultStyleEl = document.createElement("style");
  192. const defaultStyle = `.customLabel {
  193. display: flex;
  194. justify-content: space-between;
  195. align-items: center;
  196. position: relative;
  197. min-height: 36px;
  198. margin: 0 auto;
  199. padding: 13px 16px;
  200. box-sizing: border-box;
  201. font-size: 14px;
  202. color: #525661;
  203. transition: all 0.2s cubic-bezier(0.56, 0.12, 0.12, 0.98);
  204. }
  205. [dark="true"] .chat_layer .contents label.customLabel,
  206. body.thema_dark .chat_layer .contents label.customLabel {
  207. color: #d5d7dc;
  208. }
  209. .chatbox #emoticonContainer {
  210. bottom: 10px;
  211. transform: translateX(0);
  212. }
  213. .chatbox #emoticonContainer.on {
  214. bottom: 10px;
  215. max-width: 360px;
  216. min-width: 320px;
  217. right: unset;
  218. left: 0;
  219. transform: translateX(-105%);
  220. }
  221. .screen_mode #webplayer #webplayer_contents .wrapping.side {
  222. z-index: 111;
  223. position: relative;
  224. overflow: visible;
  225. }
  226. .screen_mode #webplayer #webplayer_contents .wrapping.side .box.chatting_box {
  227. overflow: visible;
  228. }
  229. .chatbox .actionbox .chat_item_list .item_box li a.customEmojiBtn {
  230. line-height: 32px;
  231. font-size: 15px;
  232. font-weight: bold;
  233. color: #555;
  234. }
  235. .chatbox .actionbox .chat_item_list .item_box li a.customEmojiBtn:hover {
  236. color: darkorange;
  237. background-color: transparent;
  238. }
  239. .chatting-item-wrap .customEmojiBox {
  240. position: absolute;
  241. bottom: 0;
  242. left: 0;
  243. width: 100%;
  244. display: flex;
  245. flex-wrap: wrap;
  246. gap: 8px 4px;
  247. padding: 8px 8px;
  248. background-color: #fefefe;
  249. }
  250. [dark="true"] .chatting-item-wrap .customEmojiBox {
  251. background-color: #222;
  252. border-top: 1px solid #444;
  253. }
  254. .chatting-item-wrap .customEmojiBox a {
  255. width: 36px;
  256. height: 36px;
  257. display: inline-flex;
  258. align-items: center;
  259. justify-content: center;
  260. border-radius: 4px;
  261. }
  262. .chatting-item-wrap .customEmojiBox a:hover {
  263. background-color: rgba(117, 123, 138, 0.2);
  264. }
  265. body:not(.screen_mode) #webplayer #webplayer_contents.narrow {
  266. gap: 8px;
  267. padding-left: 8px;
  268. padding-right: 8px;
  269. }
  270. `;
  271. defaultStyleEl.textContent = defaultStyle;
  272.  
  273. document.head.append(defaultStyleEl);
  274. document.querySelector("#btn_emo").click();
  275. setTimeout(() => {
  276. document.querySelector("#btn_emo").click();
  277. }, 500);
  278. localStorage.getItem("customChattingAreaNarrow") === "1"
  279. ? narrowChatArea(true)
  280. : narrowChatArea(false);
  281. localStorage.getItem("customChattingAreaWidth")
  282. ? changeChatAreaWidth(localStorage.getItem("customChattingAreaWidth"))
  283. : changeChatAreaWidth(chattingArea.offsetWidth);
  284. }
  285. const checkLivePlay = setInterval(() => {
  286. const isPlay = livePlayer.mainMedia;
  287. if (isPlay) {
  288. clearInterval(checkLivePlay);
  289. setTimeout(() => {
  290. init();
  291. $("#write_area").off("cut copy paste");
  292. }, 500);
  293. }
  294. }, 500);
  295. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址