Пожалуйста, обратите внимание, что пользователь заблокирован
Ы, снова уставший все делал, поэтому просто вот исходы без лишнего текста
Легко интегрировать на любой сайт))
node.js для бекенда, фронт простой html+js
На бекенде вам необходимо указать токен от бота и ваш чат айди
На фронте найти следующую строку и указать в ней свой сервер или домен, а также заменить ws на wss если вы будете использовать SSL
Небольшая демонстрация
index.html:
main.js:
server.js:
Легко интегрировать на любой сайт))
node.js для бекенда, фронт простой html+js
На бекенде вам необходимо указать токен от бота и ваш чат айди
На фронте найти следующую строку и указать в ней свой сервер или домен, а также заменить ws на wss если вы будете использовать SSL
const socket = new WebSocket("ws://localhost:3000");Небольшая демонстрация
index.html:
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>xss.pro</title>
</head>
<body>
<div id="SupportChat"></div>
<script src="./main.js"></script>
</body>
</html>
main.js:
JavaScript:
function initSupportChat(containerId) {
const container = document.getElementById(containerId);
if (!container) {
console.error("Контейнер с id=" + containerId + " не найден!");
return;
}
const styleEl = document.createElement("style");
styleEl.textContent = `
#chatToggleButton {
position: fixed;
bottom: 20px;
right: 20px;
width: 50px;
height: 50px;
border-radius: 50%;
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
cursor: pointer;
font-size: 24px;
display: flex;
align-items: center;
justify-content: center;
background-color: #007bff;
color: #fff;
border: none;
outline: none;
z-index: 9999;
}
#chatToggleButton:hover {
background-color: #0056b3;
}
#chatContainer {
position: fixed;
bottom: 80px;
right: 20px;
width: 320px;
max-height: 450px;
min-height: 450px;
display: none;
flex-direction: column;
z-index: 9999;
background-color: #f1f1f1;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
}
#chatHeader {
background-color: #007bff;
color: #fff;
padding: 10px;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
font-weight: bold;
display: flex;
align-items: center;
justify-content: space-between;
}
.close-btn {
cursor: pointer;
background: none;
border: none;
font-size: 18px;
color: #fff;
}
.close-btn:hover {
color: #ddd;
}
#chatMessages {
flex: 1;
padding: 10px;
overflow-y: auto;
background-color: #fff;
border-bottom: 1px solid #ccc;
}
#chatInputArea {
display: flex;
flex-direction: column;
background-color: #f1f1f1;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
padding: 10px;
}
#userMessage {
resize: none;
font-family: sans-serif;
padding: 8px;
margin-bottom: 8px;
border-radius: 6px;
border: 1px solid #ccc;
}
#sendButton {
align-self: flex-end;
border: none;
background-color: #007bff;
color: #fff;
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
}
#sendButton:hover {
background-color: #0056b3;
}
.message-line {
display: flex;
flex-direction: column;
margin-bottom: 10px;
max-width: 100%;
}
.message-author {
font-size: 0.8em;
margin-bottom: 2px;
color: #666;
}
.user-message-bubble {
align-self: flex-start;
background-color: #e5e5ea;
color: #333;
border-radius: 10px;
padding: 8px 12px;
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
word-wrap: break-word;
}
.support-message-bubble {
align-self: flex-end;
background-color: #007bff;
color: #fff;
border-radius: 10px;
padding: 8px 12px;
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
word-wrap: break-word;
}
`;
document.head.appendChild(styleEl);
container.innerHTML = `
<button id="chatToggleButton">💬</button>
<div id="chatContainer">
<div id="chatHeader">
<span>Support</span>
<button class="close-btn" id="chatCloseBtn">×</button>
</div>
<div id="chatMessages"></div>
<div id="chatInputArea">
<textarea id="userMessage" rows="2" placeholder="Your message..."></textarea>
<button id="sendButton">Send message</button>
</div>
</div>
`;
let storedUserId = localStorage.getItem("chatUserId");
if (!storedUserId) {
storedUserId = "user_" + Math.random().toString(36).substr(2, 9);
localStorage.setItem("chatUserId", storedUserId);
}
let userIP = null;
fetch('https://api.ipify.org?format=json')
.then(res => res.json())
.then(data => {
userIP = data.ip;
})
.catch(err => {
console.warn(err);
});
const socket = new WebSocket("ws://localhost:3000");
socket.onopen = () => {
console.log("Ws done");
socket.send(JSON.stringify({
type: "init",
userId: storedUserId
}));
};
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'chatHistory') {
const messages = data.data;
document.getElementById("chatMessages").innerHTML = "";
messages.forEach(msg => {
addMessageToChat(msg.from, msg.text);
});
}
if (data.type === 'supportReply') {
addMessageToChat('support', data.data);
}
};
socket.onclose = () => console.log("Ws close");
function sendMessage() {
const input = document.getElementById("userMessage");
const message = input.value.trim();
if (!message) return;
socket.send(JSON.stringify({
type: 'message',
data: message,
ip: userIP || "???"
}));
addMessageToChat(storedUserId, message);
input.value = "";
input.focus();
}
const userMessageInput = document.getElementById("userMessage");
userMessageInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
document.getElementById("sendButton").onclick = sendMessage;
function addMessageToChat(author, text) {
const chatBox = document.getElementById("chatMessages");
const messageLine = document.createElement("div");
messageLine.classList.add("message-line");
const authorLabel = document.createElement("div");
authorLabel.classList.add("message-author");
const bubble = document.createElement("div");
if (author === storedUserId) {
authorLabel.textContent = "You";
bubble.classList.add("user-message-bubble");
} else {
authorLabel.textContent = "Support";
bubble.classList.add("support-message-bubble");
authorLabel.style.textAlign = "right";
}
bubble.textContent = text;
messageLine.appendChild(authorLabel);
messageLine.appendChild(bubble);
chatBox.appendChild(messageLine);
chatBox.scrollTop = chatBox.scrollHeight;
}
function toggleChat() {
const chat = document.getElementById("chatContainer");
if (chat.style.display === "none" || chat.style.display === "") {
chat.style.display = "flex";
} else {
chat.style.display = "none";
}
}
document.getElementById("chatToggleButton").onclick = toggleChat;
document.getElementById("chatCloseBtn").onclick = toggleChat;
}
initSupportChat("SupportChat");
server.js:
JavaScript:
const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const TelegramBot = require('node-telegram-bot-api');
const TELEGRAM_BOT_TOKEN = '7695799359:AAEMY39Fvh3213123123nPOwvW3uG6k';
const TELEGRAM_SUPPORT_CHAT_ID = -1111111;
const bot = new TelegramBot(TELEGRAM_BOT_TOKEN, { polling: true });
const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });
let connections = {};
let messagesByUser = {};
let pendingReplies = {};
wss.on('connection', (ws) => {
ws.on('close', () => {
if (ws.userId && connections[ws.userId] === ws) {
delete connections[ws.userId];
}
});
ws.on('message', (message) => {
try {
const msgData = JSON.parse(message);
if (msgData.type === 'init') {
if (connections[msgData.userId]) {
connections[msgData.userId].close();
}
ws.userId = msgData.userId;
connections[ws.userId] = ws;
const existingMessages = messagesByUser[ws.userId] || [];
ws.send(JSON.stringify({
type: 'chatHistory',
data: existingMessages
}));
} else if (msgData.type === 'message') {
const userId = ws.userId || 'unknown_user';
const userMessage = msgData.data;
const userIp = msgData.ip || '???';
if (!messagesByUser[userId]) {
messagesByUser[userId] = [];
}
messagesByUser[userId].push({
from: userId,
text: userMessage,
timestamp: Date.now()
});
bot.sendMessage(
TELEGRAM_SUPPORT_CHAT_ID,
`Пользователь ${userId} \nIP: ${userIp}\n\n${userMessage}`,
{
reply_markup: {
inline_keyboard: [
[
{
text: 'Ответить',
callback_data: `reply_${userId}`
}
]
]
}
}
);
}
} catch (err) {
console.error(err);
}
});
});
bot.on('callback_query', async (query) => {
const data = query.data;
if (data && data.startsWith('reply_')) {
const parts = data.split('_');
const userId = parts.slice(1).join('_');
const askMsg = await bot.sendMessage(query.message.chat.id, `Введите ответ для ${userId}:`, {
reply_markup: {
force_reply: true
}
});
pendingReplies[askMsg.message_id] = { userId };
}
});
bot.on('message', async (msg) => {
if (
msg.reply_to_message &&
pendingReplies[msg.reply_to_message.message_id]
) {
const { userId } = pendingReplies[msg.reply_to_message.message_id];
const supportReply = msg.text;
delete pendingReplies[msg.reply_to_message.message_id];
const ws = connections[userId];
if (ws && ws.readyState === WebSocket.OPEN) {
if (!messagesByUser[userId]) {
messagesByUser[userId] = [];
}
messagesByUser[userId].push({
from: 'support',
text: supportReply,
timestamp: Date.now()
});
ws.send(JSON.stringify({
type: 'supportReply',
data: supportReply
}));
} else {
bot.sendMessage(msg.chat.id, `Клиент ${userId} уже офлайн или не найден`);
}
}
});
app.get('/', (req, res) => {
res.send('All good');
});
const PORT = 3000;
server.listen(PORT, () => {
console.log(`Started server`);
});
