xgit simple git

nexus

nexus

clone git clone https://kb.hax.al/nexus

api/chat.php

1 <?php
2 /**
3  * Live chat API for private messages.
4  * Actions: send, poll, delete, conversations
5  */
6 require_once __DIR__ . '/../includes/bootstrap.php';
7 must_login();
8 if (!csrf_ok()) json_out(['error' => 'CSRF'], 403);
9 
10 $action = post('action');
11 $uid    = (int)$USER['id'];
12 
13 /* ── Helper: build conversation_id ───────────────────── */
14 function conv_id(int $a, int $b): string {
15     return min($a,$b) . '-' . max($a,$b);
16 }
17 
18 /* ── Send a message ──────────────────────────────────── */
19 if ($action === 'send') {
20     $toId   = (int)post('to_id');
21     $body   = sanitise(post('body'));
22 
23     if (!$toId || $toId === $uid) json_out(['error' => 'Invalid recipient'], 400);
24     if (!$body)                   json_out(['error' => 'Message is empty'], 400);
25     if (mb_strlen($body) > 2000)  json_out(['error' => 'Message too long (max 2000)'], 400);
26 
27     $other = DB::row('SELECT id,username FROM users WHERE id=? AND suspended=0', [$toId]);
28     if (!$other) json_out(['error' => 'User not found'], 404);
29 
30     $convId = conv_id($uid, $toId);
31 
32     $msgId = DB::insert(
33         'INSERT INTO messages (sender_id,receiver_id,subject,body,conversation_id) VALUES (?,?,?,?,?)',
34         [$uid, $toId, '', $body, $convId]
35     );
36 
37     // Notify recipient
38     add_notification($toId, 'message', [
39         'from'    => $USER['username'],
40         'from_id' => $uid,
41         'subject' => mb_substr($body, 0, 60) . (mb_strlen($body) > 60 ? '…' : ''),
42     ]);
43 
44     // Return the new message row for live append
45     $msg = DB::row(
46         'SELECT m.*, u.username AS sender_name, u.avatar AS sender_av
47          FROM messages m JOIN users u ON u.id=m.sender_id
48          WHERE m.id=?', [$msgId]
49     );
50     json_out(['ok' => true, 'message' => $msg]);
51 }
52 
53 /* ── Poll: fetch new messages since a given id ───────── */
54 if ($action === 'poll') {
55     $otherId  = (int)post('other_id');
56     $sinceId  = (int)post('since_id');
57     if (!$otherId) json_out(['error' => 'Missing other_id'], 400);
58 
59     $convId = conv_id($uid, $otherId);
60     $msgs   = DB::rows(
61         'SELECT m.*, u.username AS sender_name, u.avatar AS sender_av
62          FROM messages m JOIN users u ON u.id=m.sender_id
63          WHERE m.conversation_id=? AND m.id>?
64          ORDER BY m.id ASC LIMIT 50',
65         [$convId, $sinceId]
66     );
67 
68     // Mark messages from other user as read
69     if ($msgs) {
70         $now = DB::now();
71         DB::run(
72             "UPDATE messages SET is_read=1
73              WHERE conversation_id=? AND receiver_id=? AND `is_read`=0",
74             [$convId, $uid]
75         );
76     }
77 
78     json_out(['ok' => true, 'messages' => $msgs]);
79 }
80 
81 /* ── Load full conversation ──────────────────────────── */
82 if ($action === 'load') {
83     $otherId = (int)post('other_id');
84     $before  = (int)post('before_id'); // for pagination
85     if (!$otherId) json_out(['error' => 'Missing other_id'], 400);
86 
87     $convId = conv_id($uid, $otherId);
88     $params = [$convId];
89     $where  = '';
90     if ($before) {
91         $where   = ' AND m.id < ?';
92         $params[] = $before;
93     }
94 
95     $msgs = DB::rows(
96         "SELECT m.*, u.username AS sender_name, u.avatar AS sender_av
97          FROM messages m JOIN users u ON u.id=m.sender_id
98          WHERE m.conversation_id=? $where
99          ORDER BY m.id DESC LIMIT 40",
100         $params
101     );
102     $msgs = array_reverse($msgs); // oldest first
103 
104     // Mark as read
105     DB::run(
106         "UPDATE messages SET `is_read`=1
107          WHERE conversation_id=? AND receiver_id=? AND `is_read`=0",
108         [$convId, $uid]
109     );
110 
111     $other = DB::row('SELECT id,username,avatar,role,karma,last_seen FROM users WHERE id=?', [$otherId]);
112     json_out(['ok' => true, 'messages' => $msgs, 'other' => $other]);
113 }
114 
115 /* ── List conversations ──────────────────────────────── */
116 if ($action === 'conversations') {
117     $convs = DB::rows(
118         "SELECT m.*,
119                 u.username AS other_name, u.avatar AS other_av,
120                 (SELECT COUNT(*) FROM messages m2
121                  WHERE m2.conversation_id=m.conversation_id
122                    AND m2.receiver_id=? AND m2.is_read=0) AS unread_count
123          FROM messages m
124          JOIN users u ON u.id = CASE WHEN m.sender_id=? THEN m.receiver_id ELSE m.sender_id END
125          WHERE m.conversation_id IN (
126              SELECT conversation_id FROM messages
127              WHERE sender_id=? OR receiver_id=?
128          )
129          AND m.id IN (
130              SELECT MAX(id) FROM messages
131              WHERE sender_id=? OR receiver_id=?
132              GROUP BY conversation_id
133          )
134          ORDER BY m.created_at DESC",
135         [$uid, $uid, $uid, $uid, $uid, $uid]
136     );
137     json_out(['ok' => true, 'conversations' => $convs]);
138 }
139 
140 /* ── Delete a message ────────────────────────────────── */
141 if ($action === 'delete') {
142     $msgId = (int)post('msg_id');
143     $msg   = DB::row('SELECT * FROM messages WHERE id=?', [$msgId]);
144     if (!$msg) json_out(['error' => 'Not found'], 404);
145     if ($msg['sender_id'] !== $uid && $msg['receiver_id'] !== $uid) json_out(['error' => 'Forbidden'], 403);
146 
147     if ($msg['sender_id'] === $uid)
148         DB::run('UPDATE messages SET deleted_by_sender=1 WHERE id=?', [$msgId]);
149     else
150         DB::run('UPDATE messages SET deleted_by_receiver=1 WHERE id=?', [$msgId]);
151 
152     json_out(['ok' => true]);
153 }
154 
155 json_out(['error' => 'Unknown action'], 400);