mirror of
https://github.com/openmax-server/server.git
synced 2026-05-23 03:51:43 +03:00
352 lines
14 KiB
Python
352 lines
14 KiB
Python
import hashlib
|
||
import json
|
||
import random
|
||
import time
|
||
|
||
|
||
class Tools:
|
||
def __init__(self):
|
||
pass
|
||
|
||
def generate_profile(
|
||
self,
|
||
id=1,
|
||
phone=70000000000,
|
||
avatarUrl=None,
|
||
photoId=None,
|
||
updateTime=0,
|
||
firstName="Test",
|
||
lastName="Account",
|
||
options=[],
|
||
description=None,
|
||
accountStatus=0,
|
||
profileOptions=[],
|
||
includeProfileOptions=True,
|
||
username=None,
|
||
):
|
||
contact = {
|
||
"id": id,
|
||
"updateTime": updateTime,
|
||
"phone": phone,
|
||
"names": [
|
||
{
|
||
"name": firstName,
|
||
"firstName": firstName,
|
||
"lastName": lastName,
|
||
"type": "ONEME",
|
||
}
|
||
],
|
||
"options": options,
|
||
"accountStatus": accountStatus,
|
||
}
|
||
|
||
if avatarUrl:
|
||
contact["photoId"] = photoId
|
||
contact["baseUrl"] = avatarUrl
|
||
contact["baseRawUrl"] = avatarUrl
|
||
|
||
if description:
|
||
contact["description"] = description
|
||
|
||
if username:
|
||
contact["link"] = "https://max.ru/" + username
|
||
|
||
if includeProfileOptions:
|
||
return {"contact": contact, "profileOptions": profileOptions}
|
||
else:
|
||
return contact
|
||
|
||
def generate_profile_tt(
|
||
self,
|
||
id=1,
|
||
phone=70000000000,
|
||
avatarUrl=None,
|
||
photoId=None,
|
||
updateTime=0,
|
||
firstName="Test",
|
||
lastName="Account",
|
||
options=[],
|
||
description=None,
|
||
username=None,
|
||
):
|
||
contact = {
|
||
"id": id,
|
||
"updateTime": updateTime,
|
||
"phone": phone,
|
||
"names": [{"name": f"{firstName} {lastName}", "type": "TT"}],
|
||
"options": options,
|
||
}
|
||
|
||
if avatarUrl:
|
||
contact["photoId"] = photoId
|
||
contact["baseUrl"] = avatarUrl
|
||
contact["baseRawUrl"] = avatarUrl
|
||
|
||
if description:
|
||
contact["description"] = description
|
||
|
||
if username:
|
||
contact["link"] = "https://tamtam.chat/" + username
|
||
|
||
return contact
|
||
|
||
def generate_chat(
|
||
self, id, owner, type, participants, lastMessage, lastEventTime, prevMessageId=0
|
||
):
|
||
"""Генерация чата"""
|
||
# Генерируем список участников
|
||
if isinstance(participants, dict):
|
||
result_participants = {str(k): v for k, v in participants.items()}
|
||
else:
|
||
# assume list
|
||
result_participants = {str(participant): 0 for participant in participants}
|
||
|
||
result = None
|
||
|
||
# Генерируем нужный список в зависимости от типа чата
|
||
if type == "DIALOG":
|
||
result = {
|
||
"id": id,
|
||
"type": type,
|
||
"status": "ACTIVE",
|
||
"owner": owner,
|
||
"participants": result_participants,
|
||
"lastMessage": lastMessage,
|
||
"lastEventTime": lastEventTime,
|
||
"lastDelayedUpdateTime": 0,
|
||
"lastFireDelayedErrorTime": 0,
|
||
"created": 1,
|
||
"cid": id,
|
||
"prevMessageId": prevMessageId,
|
||
"joinTime": 1,
|
||
"modified": lastEventTime,
|
||
}
|
||
|
||
# Возвращаем
|
||
return result
|
||
|
||
async def generate_chats(self, chatIds, db_pool, senderId, include_favourites=True, protocol_type='mobile'):
|
||
"""Генерирует чаты для отдачи клиенту"""
|
||
# Готовый список с чатами
|
||
chats = []
|
||
|
||
# Формируем список чатов
|
||
for chatId in chatIds:
|
||
async with db_pool.acquire() as db_connection:
|
||
async with db_connection.cursor() as cursor:
|
||
# Получаем чат по id
|
||
await cursor.execute(
|
||
"SELECT * FROM `chats` WHERE id = %s", (chatId,)
|
||
)
|
||
row = await cursor.fetchone()
|
||
|
||
if row:
|
||
# Получаем последнее сообщение из чата
|
||
message, messageTime = await self.get_last_message(
|
||
chatId, db_pool, protocol_type=protocol_type
|
||
)
|
||
|
||
# Формируем список участников с временем последней активности
|
||
participant_ids = await self.get_chat_participants(
|
||
chatId, db_pool
|
||
)
|
||
|
||
participants = await self.get_participant_last_activity(
|
||
chatId, participant_ids, db_pool
|
||
)
|
||
|
||
# Получаем ID предыдущего сообщения
|
||
prevMessageId = await self.get_previous_message_id(
|
||
chatId, db_pool, protocol_type=protocol_type
|
||
)
|
||
|
||
# Выносим результат в лист
|
||
chats.append(
|
||
self.generate_chat(
|
||
row.get("id"),
|
||
row.get("owner"),
|
||
row.get("type"),
|
||
participants,
|
||
message,
|
||
messageTime,
|
||
prevMessageId,
|
||
)
|
||
)
|
||
|
||
if include_favourites:
|
||
# Получаем последнее сообщение из избранного
|
||
message, messageTime = await self.get_last_message(
|
||
senderId, db_pool, protocol_type=protocol_type
|
||
)
|
||
|
||
# ID избранного
|
||
chatId = senderId ^ senderId
|
||
|
||
# Получаем последнюю активность участника (отправителя) в избранном
|
||
participants = await self.get_participant_last_activity(
|
||
senderId, [senderId], db_pool
|
||
)
|
||
|
||
# Получаем ID предыдущего сообщения для избранного (чат ID = senderId)
|
||
prevMessageId = await self.get_previous_message_id(senderId, db_pool, protocol_type=protocol_type)
|
||
|
||
# Хардкодим в лист чатов избранное
|
||
chats.append(
|
||
self.generate_chat(
|
||
chatId if protocol_type == 'mobile' else str(chatId),
|
||
senderId,
|
||
"DIALOG",
|
||
participants,
|
||
message,
|
||
messageTime,
|
||
prevMessageId,
|
||
)
|
||
)
|
||
|
||
return chats
|
||
|
||
async def insert_message(
|
||
self, chatId, senderId, text, attaches, elements, cid, type, db_pool
|
||
):
|
||
"""Добавление сообщения в историю"""
|
||
async with db_pool.acquire() as db_connection:
|
||
async with db_connection.cursor() as cursor:
|
||
# Получаем id последнего сообщения в чате
|
||
await cursor.execute(
|
||
"SELECT id FROM `messages` WHERE chat_id = %s ORDER BY time DESC LIMIT 1",
|
||
(chatId,),
|
||
)
|
||
|
||
row = await cursor.fetchone() or {}
|
||
last_message_id = row.get("id") or 0 # последнее id сообщения в чате
|
||
message_id = self.generate_id()
|
||
message_time = int(time.time() * 1000) # время отправки сообщения
|
||
|
||
# Вносим новое сообщение в таблицу
|
||
await cursor.execute(
|
||
"INSERT INTO `messages` (id, chat_id, sender, time, text, attaches, cid, elements, type) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)",
|
||
(
|
||
message_id,
|
||
chatId,
|
||
senderId,
|
||
message_time,
|
||
text,
|
||
json.dumps(attaches),
|
||
cid,
|
||
json.dumps(elements),
|
||
type,
|
||
),
|
||
)
|
||
|
||
# Возвращаем айдишки
|
||
return int(message_id), int(last_message_id), message_time
|
||
|
||
async def get_last_message(self, chatId, db_pool, protocol_type='mobile'):
|
||
"""Получение последнего сообщения в чате"""
|
||
async with db_pool.acquire() as db_connection:
|
||
async with db_connection.cursor() as cursor:
|
||
# Получаем id последнего сообщения в чате
|
||
await cursor.execute(
|
||
"SELECT * FROM `messages` WHERE chat_id = %s ORDER BY time DESC LIMIT 1",
|
||
(chatId,),
|
||
)
|
||
|
||
row = await cursor.fetchone()
|
||
|
||
# Если нет результатов - возвращаем None
|
||
if not row:
|
||
return None, None
|
||
|
||
# Собираем сообщение
|
||
message = {
|
||
"id": row.get("id") if protocol_type == 'mobile' else str(row.get('id')),
|
||
"time": int(row.get("time")),
|
||
"type": row.get("type"),
|
||
"sender": row.get("sender"),
|
||
"cid": int(row.get("cid")),
|
||
"text": row.get("text"),
|
||
"attaches": json.loads(row.get("attaches")),
|
||
"elements": json.loads(row.get("elements")),
|
||
"reactionInfo": {},
|
||
}
|
||
|
||
# Возвращаем
|
||
return message, int(row.get("time"))
|
||
|
||
async def get_previous_message_id(self, chatId, db_pool, protocol_type='mobile'):
|
||
"""Получение ID предыдущего сообщения (второго с конца) в чате."""
|
||
async with db_pool.acquire() as db_connection:
|
||
async with db_connection.cursor() as cursor:
|
||
await cursor.execute(
|
||
"SELECT id FROM `messages` WHERE chat_id = %s ORDER BY time DESC LIMIT 1 OFFSET 1",
|
||
(chatId,),
|
||
)
|
||
row = await cursor.fetchone()
|
||
|
||
# Если результат есть, возвращаем его
|
||
if row:
|
||
return row.get("id") if protocol_type == 'mobile' else str(row.get('id'))
|
||
|
||
# В ином случае возвращаем 0
|
||
return 0 if protocol_type == 'mobile' else "0"
|
||
|
||
async def get_participant_last_activity(self, chatId, participant_ids, db_pool):
|
||
"""Возвращает словарь {participant_id: last_activity_time} для участников чата."""
|
||
if not participant_ids:
|
||
return {}
|
||
|
||
async with db_pool.acquire() as db_connection:
|
||
async with db_connection.cursor() as cursor:
|
||
# Собираем всех участников
|
||
placeholders = ",".join(["%s"] * len(participant_ids))
|
||
query = f"""
|
||
SELECT sender, MAX(time) as last_time
|
||
FROM messages
|
||
WHERE chat_id = %s AND sender IN ({placeholders})
|
||
GROUP BY sender
|
||
"""
|
||
params = (chatId,) + tuple(participant_ids)
|
||
await cursor.execute(query, params)
|
||
rows = await cursor.fetchall()
|
||
|
||
# Собираем список участников без времени последней активности в чате
|
||
result = {str(pid): 0 for pid in participant_ids}
|
||
|
||
# Обновляем для каждого участника время последней активности в чате
|
||
for row in rows:
|
||
sender = str(row["sender"])
|
||
last_time = row["last_time"]
|
||
if last_time is not None:
|
||
result[sender] = int(last_time)
|
||
|
||
return result
|
||
|
||
async def get_chat_participants(self, chatId, db_pool):
|
||
"""Возвращает список ID участников чата из таблицы chat_participants."""
|
||
async with db_pool.acquire() as db_connection:
|
||
async with db_connection.cursor() as cursor:
|
||
await cursor.execute(
|
||
"SELECT user_id FROM chat_participants WHERE chat_id = %s",
|
||
(chatId,),
|
||
)
|
||
rows = await cursor.fetchall()
|
||
return [row["user_id"] for row in rows]
|
||
|
||
async def auth_required(self, userPhone, coro, *args):
|
||
if userPhone:
|
||
await coro(*args)
|
||
|
||
def generate_id(self):
|
||
# Получаем время в юниксе
|
||
timestamp = int(time.time())
|
||
|
||
# Генерируем дополнительно рандомное число
|
||
random_number = random.randint(0, 9999)
|
||
|
||
# Собираем их вместе и вычисляем хеш
|
||
combined = f"{timestamp}{random_number}".encode()
|
||
unique_id = int(hashlib.md5(combined).hexdigest(), 16) % 1000000000
|
||
|
||
# Возвращаем
|
||
return unique_id
|