// ==UserScript== // @name SOOP(숲) 하이라이트 댓글 버튼 추가 스크립트 (by 도연) // @namespace http://tampermonkey.net/ // @version 1.0.3 // @description SOOP(숲) 게시물 댓글에서 '하이라이트 댓글'로 공유할 수 있는 버튼을 추가합니다. (Made by 도연) // @author https://github.com/dokdo2013 // @license MIT // @match https://ch.sooplive.co.kr/*/post/* // @grant none // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.4/toastr.min.js // @downloadURL https://update.greasyfork.cloud/scripts/501990/SOOP%28%EC%88%B2%29%20%ED%95%98%EC%9D%B4%EB%9D%BC%EC%9D%B4%ED%8A%B8%20%EB%8C%93%EA%B8%80%20%EB%B2%84%ED%8A%BC%20%EC%B6%94%EA%B0%80%20%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%20%28by%20%EB%8F%84%EC%97%B0%29.user.js // @updateURL https://update.greasyfork.cloud/scripts/501990/SOOP%28%EC%88%B2%29%20%ED%95%98%EC%9D%B4%EB%9D%BC%EC%9D%B4%ED%8A%B8%20%EB%8C%93%EA%B8%80%20%EB%B2%84%ED%8A%BC%20%EC%B6%94%EA%B0%80%20%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%20%28by%20%EB%8F%84%EC%97%B0%29.meta.js // ==/UserScript== (function() { 'use strict'; // Toastr CSS 추가 const toastrCSS = ` @import url('https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css'); `; const style = document.createElement('style'); style.textContent = toastrCSS; document.head.appendChild(style); console.log('Script started'); // URL에서 ch_id와 post_id를 추출 const urlPattern = /^https:\/\/ch\.sooplive\.co\.kr\/([^\/]+)\/post\/([^\/#?]+)(?:[#?].*)?$/; const match = window.location.href.match(urlPattern); if (!match) { console.error('URL does not match the expected pattern'); return; } const ch_id = match[1]; const post_id = match[2]; console.log(`Extracted ch_id: ${bj_id}, post_id: ${post_id}`); // API 호출 함수 async function fetchComments(bj_id, post_id) { let allComments = []; let page = 1; let lastPage = 1; try { while (page <= lastPage) { console.log(`Fetching comments for page ${page}`); const response = await fetch(`https://chapi.sooplive.co.kr/api/${bj_id}/title/${post_id}/comment?page=${page}&orderby=reg_date`); const data = await response.json(); if (data.data) { allComments = allComments.concat(data.data); lastPage = data.meta.last_page; console.log(`Fetched page ${page}, last_page: ${lastPage}`); page++; } else { console.error('Failed to fetch comments:', data); break; } } } catch (error) { console.error('Error fetching comments:', error); } console.log(`Total comments fetched: ${allComments.length}`); return allComments; } // 댓글에 버튼 추가하는 함수 function addButtonToComments(comments) { const commentElements = document.querySelectorAll('ul.cmmt-list > li'); if (!commentElements.length) { console.error('No comment elements found on the page'); return; } console.log(`Found ${commentElements.length} comment elements on the page`); commentElements.forEach((element, index) => { const autorWrap = element.querySelector('.cmmt-header .autor_wrap'); const util = element.querySelector('.cmmt-header .util'); const cmmtBtn = element.querySelector('.cmmt-btn'); if (autorWrap && util) { const nicknameElement = autorWrap.querySelector('div > button > p'); const timeElement = autorWrap.querySelector('div > span'); const nickname = nicknameElement ? nicknameElement.innerText : ''; const time = timeElement ? timeElement.innerText : ''; const matchedComment = comments.find(comment => comment.user_nick === nickname.split('(')[0] && comment.reg_date === time); if (matchedComment) { const button = document.createElement('button'); button.innerHTML = ` `; button.style.padding = '2px 4px'; button.style.border = 'none'; button.style.borderRadius = '4px'; button.style.backgroundColor = 'transparent'; button.style.cursor = 'pointer'; button.dataset.comment = matchedComment.p_comment_no; button.addEventListener('click', function() { const commentId = this.dataset.comment; let url = window.location.href; // #으로 시작하는 selector가 있는지 확인 if (url.includes('#')) { // # 이후의 부분을 제거 url = url.split('#')[0]; } // 새로운 URL 생성 const newUrl = `${url}#comment_noti${commentId}`; // 사용자 지정 메시지 생성 const message = `링크가 클립보드에 복사되었습니다.\n${newUrl}`; // 클립보드에 복사 navigator.clipboard.writeText(newUrl).then(() => { console.log('URL이 클립보드에 복사되었습니다.'); // toast 메시지 표시 toastr.success('URL이 클립보드에 복사되었습니다.', '복사 완료', { timeOut: 3000, positionClass: "toast-bottom-center", }); }).catch(err => { console.error('클립보드 복사 중 에러 발생:', err); // toast 에러 메시지 표시 toastr.error('클립보드 복사 중 에러 발생', '오류', { timeOut: 3000, positionClass: "toast-bottom-center", }); }); }); cmmtBtn.appendChild(button); console.log(`Button added to comment with ID: ${matchedComment.p_comment_no}`); } else { console.warn(`No matching comment found for nickname: ${nickname}, time: ${time}`); } } else { console.warn('Missing autor_wrap or util element in comment element', element); } }); } // 페이지 로드 후 실행 window.addEventListener('load', async () => { console.log('Page loaded'); setTimeout(async () => { try { const comments = await fetchComments(bj_id, post_id); addButtonToComments(comments); toastr.success('링크 공유 버튼 로딩을 완료했어요!', '성공', { timeOut: 2000, positionClass: "toast-bottom-center", }); } catch (e) { toastr.error('링크 공유 버튼 로딩 중 에러가 발생했습니다', '오류', { timeOut: 3000, positionClass: "toast-bottom-center", }); } }, 1000); // 1초 지연 }); })();