diff --git a/config.ini b/config.ini
index fc67302..9a4b766 100644
--- a/config.ini
+++ b/config.ini
@@ -3,8 +3,8 @@ bot_secret = 6454033742:AAFj2rUoVb2jJ_Lew4eiIecdr7s7xbrqeNU
enabled = 1
[YouTube]
-video_id = 3xLyYdp0UFg
-enabled = 1
+video_id = y2fwatM1oOg
+enabled = 0
[Twitch]
channel = coulthardf1
diff --git a/huySeek.py b/huySeek.py
index 201801a..e02cd58 100644
--- a/huySeek.py
+++ b/huySeek.py
@@ -14,6 +14,7 @@ import tzlocal
from zoneinfo import ZoneInfo
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib import parse
+from datetime import timezone
# Чтение конфигурации
config = configparser.ConfigParser()
@@ -23,14 +24,14 @@ config.read("config.ini")
chat_connectors = {}
chat_comments = {}
-# Telegram (опционально)
+# Telegram
if config.getboolean('Telegram', 'enabled', fallback=False):
import telegram
bot = telegram.Bot(config['Telegram']['bot_secret'])
chat_connectors['telegram'] = bot
chat_comments['tg'] = []
-# YouTube (опционально)
+# YouTube
if config.getboolean('YouTube', 'enabled', fallback=False):
print('Youtube enabled')
import pytchat
@@ -39,16 +40,18 @@ if config.getboolean('YouTube', 'enabled', fallback=False):
chat_connectors['youtube'] = chat
chat_comments['yt'] = []
-# Twitch (опционально)
+# Twitch
+TWITCH_CHANNEL = None
if config.getboolean('Twitch', 'enabled', fallback=False):
sys.path.append('twitchchatirc')
import twitch
twitch_channel = config['Twitch']['channel']
twitch_socket = twitch.TwitchChatIRC(twitch_channel)
chat_connectors['twitch'] = twitch_socket
+ TWITCH_CHANNEL = config['Twitch']['channel']
chat_comments['tw'] = []
-# Функции для работы с датами (оставлены как есть)
+# Функции для работы с датами
class DateTimeEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, datetime):
@@ -64,10 +67,12 @@ all_comments = []
all_old_comments = []
is_changed = False
overallcount = 0
+last_tg_update_id = 0
# Получение комментариев из Telegram
async def get_telegram_comments():
global chat_comments
+ global last_tg_update_id
chat_comments['tg'] = []
if 'telegram' not in chat_connectors:
@@ -76,9 +81,10 @@ async def get_telegram_comments():
async with bot:
try:
updates = await bot.get_updates(
+ offset=last_tg_update_id + 1,
allowed_updates=['message', 'edited_message'],
timeout=None
- )
+ )
except telegram.error.TimedOut:
print('TG connection timeout')
time.sleep(5)
@@ -86,6 +92,11 @@ async def get_telegram_comments():
except Exception as e:
print(f'TG connection error: {e}')
return
+
+ if not updates:
+ return
+
+ last_tg_update_id = updates[-1].update_id
for upd in updates:
msg = upd.message
@@ -102,15 +113,21 @@ async def get_telegram_comments():
if sendr == "Group":
sendr = 'Админ'
- netdatetime = msg.date.replace(tzinfo=tz)
- comm = {
- 'id': msg.message_id,
- 'type': 'tg',
- 'date': netdatetime,
- 'sendr': sendr,
- 'msg': msg.text
- }
- chat_comments['tg'].append(comm)
+ try:
+ netdatetime = msg.date.replace(tzinfo=timezone.utc).astimezone(tz)
+
+ comm = {
+ 'id': msg.message_id,
+ 'type': 'tg',
+ 'date': netdatetime,
+ 'sendr': sendr,
+ 'msg': msg.text
+ }
+ chat_comments['tg'].append(comm)
+ except Exception as e:
+ print(f"Ошибка преобразования времени: {e}")
+
+
# Получение комментариев из YouTube
def get_youtube_comments():
@@ -156,16 +173,35 @@ def update_all_comments():
global all_comments
global chat_comments
- # Добавляем новые комментарии из всех источников
- for source in ['tg', 'yt', 'tw']:
- if source in chat_comments:
- for comment in chat_comments[source]:
- if comment not in all_comments:
- all_comments.append(comment)
+ # Временный список для новых комментариев
+ new_comments = []
- # Сортируем по дате и ограничиваем количество
+ # Собираем новые комментарии из всех источников
+ for source in ['tg', 'yt', 'tw']:
+ if source in chat_comments and chat_comments[source]:
+ # Добавляем все новые комментарии из этого источника
+ new_comments.extend(chat_comments[source])
+ # Очищаем буфер этого источника
+ chat_comments[source] = []
+
+ # Добавляем новые комментарии к существующим
+ all_comments.extend(new_comments)
+
+ # Сортируем по дате
all_comments.sort(key=sort_by_date)
- all_comments = all_comments[-150:]
+
+ # Удаляем дубликаты по ID (если они есть)
+ seen_ids = set()
+ unique_comments = []
+
+ for comment in all_comments:
+ comment_id = (comment['id'], comment['type']) # ID уникален в рамках типа
+ if comment_id not in seen_ids:
+ seen_ids.add(comment_id)
+ unique_comments.append(comment)
+
+ # Ограничиваем количество и сохраняем
+ all_comments = unique_comments[-150:]
def make_json_object():
global all_comments
@@ -189,19 +225,31 @@ def make_json_object():
# Ограничиваем буферы для каждого источника
for source in ['yt', 'tg', 'tw']:
if source in chat_comments and len(chat_comments[source]) > 15:
+ # Сортируем ВСЕ источники по дате (старые -> новые)
+ chat_comments[source].sort(key=lambda x: x['date'])
+ # Берём последние 15 (самые новые)
chat_comments[source] = chat_comments[source][-15:]
# Добавляем приветственное сообщение каждые 10 минут
- if int(time.time()) % 600 == 0:
- dt = datetime.now(ZoneInfo('UTC')).astimezone(tz)
- hello_msg = {
- 'id': random.randint(100000, 999999),
- 'type': 'hello',
- 'date': dt,
- 'sendr': 'Eikichi-bot',
- 'msg': '🔥 Спасибо всем на стриме за интерес к переводу и поддержку! 🔥'
- }
- all_comments.append(hello_msg)
+ current_second = int(time.time())
+ if current_second % 600 <= 5:
+ # Проверяем, есть ли приветственное сообщение от Eikichi-bot в последних 20 сообщениях
+ has_recent_hello = any(
+ comment.get('sendr') == 'Eikichi-bot' and comment.get('type') == 'hello'
+ for comment in all_comments[-20:] # Последние 20 сообщений
+ )
+
+ if not has_recent_hello:
+ dt = datetime.now(ZoneInfo('UTC')).astimezone(tz)
+ hello_msg = {
+ 'id': random.randint(100000, 999999),
+ 'type': 'hello',
+ 'date': dt,
+ 'sendr': 'Eikichi-bot',
+ 'msg': '🔥 Спасибо всем на стриме за интерес к переводу и поддержку! 🔥'
+ }
+ all_comments.append(hello_msg)
+ print(f"{datetime.now().strftime('%H:%M:%S')} Добавлено приветственное сообщение")
# Обновляем общий список комментариев
update_all_comments()
@@ -211,9 +259,11 @@ def make_json_object():
def print_all_comments():
global all_comments
+ global chat_comments
print('-' * 40)
print(all_comments)
print('-' * 40)
+ print(chat_comments)
# HTTP сервер
class ChatServer(BaseHTTPRequestHandler):
@@ -267,6 +317,9 @@ server_thread.start()
print('Запуск подсистем чатов...')
print('...Удерживайте Ctrl+Alt+Shift+C для выхода и C для очистки консоли...')
+# print('Загрузка 7TV эмодзи...')
+# emotes_cache = get_7tv_emotes() if TWITCH_CHANNEL else {}
+
poll_time = 2 # Интервал проверки в секундах
diff --git a/index.html b/index.html
index 2906ecf..d4a2af1 100644
--- a/index.html
+++ b/index.html
@@ -30,7 +30,7 @@ body {
/* color: white; */
height: 100%;
min-width: 150px;
- max-width: 400px;
+ max-width: 450px;
display: flex;
flex-direction: column;
@@ -43,6 +43,8 @@ body {
flex-direction: row;
margin-bottom: 1px;
animation: fadeInUp 0.3s ease-out;
+
+ min-height: 0;
}
@keyframes fadeInUp {
@@ -67,6 +69,13 @@ body {
font-weight: bold;
flex-direction: column;
vertical-align: auto;
+
+ flex: 0 0 25%; /* Фиксируем ширину - не растягивается и не сжимается */
+ min-width: 0; /* Важно для работы переноса текста в flex-контейнере */
+ max-width: 25%; /* Ограничиваем максимальную ширину */
+ word-wrap: break-word; /* Перенос длинных слов */
+ overflow-wrap: break-word; /* Современная версия */
+ word-break: break-word; /* Разрыв слов если нужно */
}
.highlight-message {
@@ -75,9 +84,19 @@ body {
}
+.emote-7tv {
+ height: 1.4em;
+ width: auto;
+ vertical-align: middle;
+ display: inline-block;
+ margin: 0 2px;
+ image-rendering: pixelated;
+ image-rendering: -moz-crisp-edges;
+ image-rendering: crisp-edges;
+}
.yt {
- background-image: url('data:image/svg+xml,');
+ background-image: url('data:image/svg+xml,');
background-color: #E53935A0;
}
@@ -106,6 +125,11 @@ body {
padding: 5px;
width: 75%;
color: #fff;
+
+ flex: 1; /* Занимает всё оставшееся пространство */
+ min-width: 0; /* КРИТИЧЕСКИ ВАЖНО для работы текста в flex */
+ word-wrap: break-word;
+ overflow-wrap: break-word;
}
diff --git a/script.js b/script.js
index 87d2294..85cd4d8 100644
--- a/script.js
+++ b/script.js
@@ -1,28 +1,17 @@
const chatwin = document.getElementById("chatwin");
const anchor = document.getElementById("anchor");
-
-// Массив ключевых слов для выделения (без учета регистра)
-const specialKeywords = ['sergshemet', 'sergeyshemet', 'sshemet', 'сергей', 'серёга', 'админ'];
-
-// Функция для проверки содержит ли сообщение ключевые слова
+const specialKeywords = ['sergshemet', 'sergeyshemet', 'sshemet', 'серг', 'серег', 'серёг', 'админ'];
function containsSpecialKeywords(text) {
const lowerText = text.toLowerCase();
return specialKeywords.some(keyword => lowerText.includes(keyword.toLowerCase()));
}
-
-// Создание новой строки чата из JSON данных
function createNewLine(json) {
- // Сортируем сообщения по дате (самые старые первыми)
json.sort((a, b) => new Date(a.date) - new Date(b.date));
json.forEach(element => {
- // Проверяем, существует ли уже элемент с таким ID
let existingMsg = document.getElementById(element["id"]);
if (existingMsg) {
- // Обновляем текст существующего сообщения
existingMsg.innerHTML = element["msg"];
-
- // Проверяем на ключевые слова для подсветки
if (containsSpecialKeywords(element["msg"])) {
existingMsg.classList.add("highlight-message");
} else {
@@ -30,62 +19,39 @@ function createNewLine(json) {
}
return;
}
-
- // Создаем блок имени
const nameBlock = document.createElement("div");
nameBlock.className = "nameline " + element["type"];
-
- // Для донатов добавляем сумму
if (element["type"] == "donate") {
nameBlock.innerHTML = element["sendr"] + '
' + element['amount'] + "
"; } else { nameBlock.innerHTML = element["sendr"]; } - - // Создаем блок сообщения const msgBlock = document.createElement("div"); msgBlock.className = "msgline"; msgBlock.innerHTML = element["msg"]; msgBlock.id = element["id"]; - - // Добавляем подсветку если есть ключевые слова if (containsSpecialKeywords(element["msg"])) { msgBlock.classList.add("highlight-message"); } - - // Стиль для донатов if (element["type"] == "donate") { msgBlock.style.backgroundColor = "#0000FF20"; } - - // Создаем строку чата const row = document.createElement("div"); row.className = "chatRow"; row.setAttribute("name", element["id"]); row.appendChild(nameBlock); row.appendChild(msgBlock); - - // ВСЕГДА вставляем новое сообщение перед якорем (внизу) chatwin.insertBefore(row, anchor); - - // Прокручиваем сразу к новому сообщению scrollToBottom(); }); - - // Ограничиваем количество сообщений (200) removeOldMessages(200); } - -// Функция прокрутки в самый низ function scrollToBottom() { - // Используем setTimeout чтобы прокрутка происходила после добавления DOM элемента setTimeout(() => { window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); }, 10); } - -// Удаление старых сообщений function removeOldMessages(maxMessages = 200) { const messageRows = document.getElementsByClassName("chatRow"); const rowsToRemove = messageRows.length - maxMessages; @@ -98,8 +64,6 @@ function removeOldMessages(maxMessages = 200) { } } } - -// Запрос новых сообщений с сервера function requestNewLines() { fetch("http://localhost:8008/") .then(response => { @@ -113,33 +77,22 @@ function requestNewLines() { }) .catch(error => { console.error('Error fetching chat messages:', error); - - // Показываем сообщение об ошибке только если его нет const errorRow = document.getElementById("error-message"); if (!errorRow) { const errorDiv = document.createElement("div"); errorDiv.id = "error-message"; errorDiv.className = "chatRow error"; - errorDiv.innerHTML = '