Код:
<script>
document.addEventListener('DOMContentLoaded', async () => {
const chatWrapper = document.querySelector('.interactive-chat');
if (!chatWrapper) return;
const hostPost = chatWrapper.closest('.post');
const hostPostId = hostPost ? hostPost.id : null;
const punMain = document.getElementById('pun-main') || document.body;
const style = document.createElement('style');
style.innerHTML = `
.chat-loading { text-align: center; font-size: 18px; padding: 50px; opacity: 1; animation: chatBlink 1.5s infinite; color: #888; }
@keyframes chatBlink { 0% { opacity: 0.4; } 50% { opacity: 1; } 100% { opacity: 0.4; } }
.chat-topic-title { text-align: center; font-weight: bold; font-size: 1.1em; padding: 10px 15px; border-bottom: 1px solid rgba(128,128,128,0.2); margin-bottom: 10px; }
.post[data-user-id] { display: none !important; }
body.show-original-posts .post[data-user-id] { display: block !important; }
.chat-toggle-btn {
font-size: 11px; padding: 4px 10px; cursor: pointer;
border: 1px solid rgba(128,128,128,0.4); background: transparent;
border-radius: 4px; color: inherit; opacity: 0.7; transition: 0.2s;
}
.chat-toggle-btn:hover { opacity: 1; background: rgba(128,128,128,0.1); }
.chat-controls-wrapper { text-align: right; margin-bottom: 10px; }
/* Стили для кнопки плюса (репутации) в чате */
.chat-like-btn {
display: inline-flex; align-items: center; gap: 4px;
font-size: 11px; padding: 2px 7px; margin-top: 6px;
border-radius: 10px; background: rgba(128, 128, 128, 0.12);
cursor: pointer; user-select: none; transition: 0.2s;
opacity: 0.8; float: right; clear: both;
}
.chat-like-btn:hover { opacity: 1; background: rgba(220, 50, 50, 0.15); color: #e03131; }
.chat-like-btn.liked { color: #e03131; background: rgba(220, 50, 50, 0.15); opacity: 1; }
/* Стили нижней панели ввода (как в Telegram) */
.write-msg {
display: flex; align-items: flex-end; gap: 8px;
padding: 10px 14px; border-top: 1px solid rgba(128,128,128,0.2);
background: rgba(0, 0, 0, 0.03);
}
.chat-tg-input {
flex: 1; resize: none; overflow-y: hidden;
min-height: 20px; max-height: 140px;
padding: 8px 14px; border-radius: 18px;
border: 1px solid rgba(128,128,128,0.3);
background: var(--bg-input, #fff); color: inherit;
font-family: inherit; font-size: 14px; line-height: 1.4;
outline: none; box-sizing: border-box; transition: border-color 0.2s;
}
.chat-tg-input:focus { border-color: rgba(128,128,128,0.6); }
.chat-tg-send {
width: 36px; height: 36px; border-radius: 50%;
border: none; background: #3390ec; color: #fff;
cursor: pointer; display: flex; align-items: center; justify-content: center;
flex-shrink: 0; transition: transform 0.15s, background 0.2s;
}
.chat-tg-send:hover { background: #287ad1; }
.chat-tg-send:active { transform: scale(0.92); }
.chat-tg-send svg { width: 18px; height: 18px; fill: currentColor; margin-left: 2px; }
`;
document.head.appendChild(style);
punMain.insertBefore(chatWrapper, punMain.firstChild);
function createToggleBtn(targetContainer) {
const wrapper = document.createElement('div');
wrapper.className = 'chat-controls-wrapper';
const btn = document.createElement('button');
btn.className = 'chat-toggle-btn';
btn.textContent = 'Показать сообщения (для ред.)';
btn.addEventListener('click', (e) => {
e.preventDefault();
const isVisible = document.body.classList.toggle('show-original-posts');
btn.textContent = isVisible ? 'Скрыть сообщения' : 'Показать сообщения (для ред.)';
});
wrapper.appendChild(btn);
targetContainer.parentNode.insertBefore(wrapper, targetContainer);
}
createToggleBtn(chatWrapper);
chatWrapper.innerHTML = '<div class="chat-loading">Загрузка чата... собираем сообщения</div>';
try {
const pageLinksElements = Array.from(document.querySelectorAll('.pagelink a'));
let allPageUrls = new Set();
const currentUrl = window.location.href.split('#')[0];
allPageUrls.add(currentUrl);
pageLinksElements.forEach(a => {
if (!a.classList.contains('next') && !a.classList.contains('prev')) {
allPageUrls.add(a.href.split('#')[0]);
}
});
const sortedUrls = Array.from(allPageUrls).sort((a, b) => {
const pA = parseInt(new URL(a).searchParams.get('p')) || 1;
const pB = parseInt(new URL(b).searchParams.get('p')) || 1;
return pA - pB;
});
const postsData = [];
const processedPostIds = new Set();
const uniqueUserIds = new Set();
let topicStarterId = null;
for (const url of sortedUrls) {
let doc;
if (url === currentUrl) {
doc = document;
} else {
const response = await fetch(url);
const buffer = await response.arrayBuffer();
const decoder = new TextDecoder('windows-1251');
const html = decoder.decode(buffer);
const parser = new DOMParser();
doc = parser.parseFromString(html, 'text/html');
}
const postsOnPage = Array.from(doc.querySelectorAll('.post[data-user-id]'));
for (const postEl of postsOnPage) {
const userId = postEl.getAttribute('data-user-id');
if (!userId) continue;
if (!topicStarterId) topicStarterId = userId;
const postId = postEl.id;
if (processedPostIds.has(postId) || (hostPostId && postId === hostPostId)) continue;
processedPostIds.add(postId);
uniqueUserIds.add(userId);
const time = postEl.querySelector('.permalink')?.textContent || '';
const avatarImg = postEl.querySelector('.pa-avatar img');
const postAvatar = avatarImg ? avatarImg.src : null;
const contentBox = postEl.querySelector('.post-content');
const ratingLink = postEl.querySelector('.post-rating a');
const ratingCount = ratingLink ? (ratingLink.textContent.trim() || '0') : null;
if (contentBox) {
const content = contentBox.innerHTML
.replace(/\[icon\][\s\S]*?\[\/icon\]/gi, '')
.replace(/<div class="post-sig".*?<\/div>/gi, '')
.trim();
if (content.replace(/<[^>]*>?/gm, '').trim() !== '' || content.includes('<img')) {
postsData.push({ postId, userId, time, content, postAvatar, ratingCount });
}
}
}
}
let userMap = {};
if (uniqueUserIds.size > 0) {
const userApiRes = await fetch(`/api.php?method=users.get&user_id=${Array.from(uniqueUserIds).join(',')}&fields=user_id,avatar,username,name`);
const userApiData = await userApiRes.json();
(userApiData.response?.users || []).forEach(u => {
userMap[u.user_id] = {
avatar: u.avatar || 'https://upforme.ru/uploads/001c/b7/5d/6/359207.png',
name: u.username || u.name || 'Unknown'
};
});
}
let topicTitle = "Чат";
const urlParams = new URLSearchParams(window.location.search);
const topicId = urlParams.get('id');
if (topicId) {
try {
const topicRes = await fetch(`/api.php?method=topic.get&topic_id=${topicId}&fields=id,subject`);
const topicData = await topicRes.json();
const topicsList = topicData.response?.topics || topicData.response || [];
const currentTopic = Array.isArray(topicsList) ? topicsList.find(t => t.id == topicId) : null;
if (currentTopic && currentTopic.subject) topicTitle = currentTopic.subject;
} catch (e) {
topicTitle = document.querySelector('h1')?.textContent || "Чат";
}
}
const topicHeaderHTML = `<div class="chat-topic-title">💬 ${topicTitle}</div>`;
let messagesHTML = '<div class="chwrap"><div class="soc-chat-wrapper">';
postsData.forEach(post => {
const isTopicStarter = post.userId === topicStarterId;
const msgClass = isTopicStarter ? 'right-msg' : 'left-msg';
const user = userMap[post.userId] || { avatar: '', name: 'Аноним' };
const avatarToUse = post.postAvatar || user.avatar;
const likeBtnHTML = post.ratingCount !== null
? `<div class="chat-like-btn" data-post-id="${post.postId}" title="Поставить плюс">
<span>❤</span> <span class="like-count">${post.ratingCount}</span>
</div>`
: '';
messagesHTML += `
<div class="${msgClass}" data-msgtime="${post.time}" data-post-id="${post.postId}">
<img src="${avatarToUse}" class="msg-av" title="${user.name}">
<div class="msg-body">
${post.content}
${likeBtnHTML}
</div>
</div>
`;
});
const originalTextarea = document.getElementById('main-reply');
const writeMsgHTML = originalTextarea ? `
<div class="write-msg">
<textarea class="chat-tg-input" rows="1" placeholder="Написать сообщение... (Enter — отправить, Shift+Enter — строка)"></textarea>
<button type="button" class="chat-tg-send" title="Отправить">
<svg viewBox="0 0 24 24"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
</button>
</div>
` : `<div class="write-msg" style="justify-content:center; opacity:0.6; font-size:12px;">Форма ответа недоступна</div>`;
messagesHTML += `</div>${writeMsgHTML}</div>`;
chatWrapper.innerHTML = topicHeaderHTML + messagesHTML;
chatWrapper.addEventListener('click', (e) => {
const likeBtn = e.target.closest('.chat-like-btn');
if (!likeBtn) return;
const postId = likeBtn.getAttribute('data-post-id');
const countSpan = likeBtn.querySelector('.like-count');
let realPost = document.getElementById(postId);
let realRatingAnchor = realPost ? realPost.querySelector('.post-rating a') : null;
if (realRatingAnchor) {
realRatingAnchor.click();
setTimeout(() => {
countSpan.textContent = realRatingAnchor.textContent.trim();
likeBtn.classList.add('liked');
}, 500);
} else if (typeof window.BR === 'function') {
let phantomPost = document.createElement('div');
phantomPost.id = postId;
phantomPost.className = 'post';
phantomPost.style.display = 'none';
phantomPost.innerHTML = `<div class="post-rating"><p class="container"><a>${countSpan.textContent}</a></p></div>`;
document.body.appendChild(phantomPost);
const phantomAnchor = phantomPost.querySelector('a');
window.BR(phantomAnchor);
setTimeout(() => {
countSpan.textContent = phantomAnchor.textContent.trim();
likeBtn.classList.add('liked');
phantomPost.remove();
}, 600);
}
});
const chatInput = chatWrapper.querySelector('.chat-tg-input');
const chatSendBtn = chatWrapper.querySelector('.chat-tg-send');
if (chatInput && originalTextarea) {
chatInput.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = Math.min(this.scrollHeight, 140) + 'px';
this.style.overflowY = this.scrollHeight > 140 ? 'auto' : 'hidden';
});
const submitChatMessage = () => {
const text = chatInput.value.trim();
if (!text) return;
originalTextarea.value = text;
const originalSubmitBtn = document.querySelector('.formsubmit input[name="submit"]')
|| document.querySelector('#post input[name="submit"]');
if (originalSubmitBtn) {
chatInput.disabled = true;
chatSendBtn.style.opacity = '0.5';
originalSubmitBtn.click();
} else {
const form = originalTextarea.closest('form');
if (form) form.submit();
}
};
chatSendBtn.addEventListener('click', submitChatMessage);
chatInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
submitChatMessage();
}
});
}
const socChatWrapper = chatWrapper.querySelector('.soc-chat-wrapper');
if (socChatWrapper) {
socChatWrapper.scrollTop = socChatWrapper.scrollHeight;
}
} catch (error) {
console.error('Ошибка при генерации чата:', error);
chatWrapper.innerHTML = '<div style="text-align:center; color:red; padding: 20px;">Произошла ошибка при загрузке чата.</div>';
}
});
</script>





