File size: 2,623 Bytes
24727a3
 
 
a0a2b9e
24727a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a0a2b9e
 
 
24727a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a0a2b9e
 
 
 
 
 
 
 
24727a3
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import { prisma } from '../../config/database.js'
import { saveMessage } from './chat.service.js'
import { STAFF_ROLES } from '../../lib/constants.js'
import { handleClientMessage } from './bot.service.js'

const msgRateLimiter = new Map()

function checkMsgRateLimit(userId) {
  const now = Date.now()
  const history = (msgRateLimiter.get(userId) || []).filter(t => now - t < 60000)
  if (history.length >= 30) return false
  msgRateLimiter.set(userId, [...history, now])
  return true
}

setInterval(() => msgRateLimiter.clear(), 5 * 60 * 1000)

export function setupChatSocket(io) {
  io.on('connection', (socket) => {
    const userId = socket.userId
    if (!userId) return socket.disconnect()

    socket.join(`user:${userId}`)

    socket.on('chat:join', async (conversationId) => {
      try {
        const conversation = await prisma.conversation.findUnique({
          where: { id: conversationId },
          include: { order: { select: { userId: true } } }
        })
        if (!conversation) return

        const isAdmin = STAFF_ROLES.includes(socket.userRole)
        const isOwner = conversation.order?.userId === userId
        const isClient = conversation.clientId === userId
        if (isAdmin || isOwner || isClient) {
          socket.join(`conversation:${conversationId}`)
        }
      } catch { /* silently fail */ }
    })

    socket.on('chat:message', async (data) => {
      try {
        if (!checkMsgRateLimit(userId)) return

        const { conversationId, content } = data || {}
        if (!content?.trim() || typeof content !== 'string') return
        if (content.length > 2000) return

        const { message, ownerId } = await saveMessage(conversationId, userId, content.trim())

        io.to(`conversation:${conversationId}`).emit('chat:newMessage', message)
        if (ownerId !== userId) {
          io.to(`user:${ownerId}`).emit('chat:newMessage', message)
        }

        // لو المرسل عميل (مش أدمن) — شوف لو محتاج رد تلقائي
        if (!STAFF_ROLES.includes(socket.userRole)) {
          const botMsg = await handleClientMessage(conversationId, content.trim())
          if (botMsg) {
            io.to(`conversation:${conversationId}`).emit('chat:newMessage', botMsg)
          }
        }
      } catch { /* fail silently */ }
    })

    socket.on('typing', ({ conversationId, isTyping }) => {
      if (!conversationId) return
      const room = socket.rooms?.has(`conversation:${conversationId}`)
      if (!room) return
      socket.to(`conversation:${conversationId}`).emit('typing', { userId, isTyping })
    })
  })
}