1<?php
2/**
3 * Live chat API for private messages.
4 * Actions: send, poll, delete, conversations
5 */
6require_once __DIR__ . '/../includes/bootstrap.php';
7must_login();
8if (!csrf_ok()) json_out(['error' => 'CSRF'], 403);
9
10$action = post('action');
11$uid = (int)$USER['id'];
12
13
14function conv_id(int $a, int $b): string {
15 return min($a,$b) . '-' . max($a,$b);
16}
17
18
19if ($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
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
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
54if ($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
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
82if ($action === 'load') {
83 $otherId = (int)post('other_id');
84 $before = (int)post('before_id');
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);
103
104
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
116if ($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
141if ($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
155json_out(['error' => 'Unknown action'], 400);