Skip to content

Commit 1412760

Browse files
MarioRaafatMoBahgat010shady-2004
authored
fix(tweet): view counter + delete + counters + type + clean service
* fix(tweet): handle mentions corner cases * fix(tweet): handle mentions corner cases * fix(tweet): arabic hashtags * fix(tweet): do not notify mentioned user if the original tweet is his * fix(tweet): mention in update tweet * fix(tweet): some fixes * fix(tweet): decrement counters * fix(tweets): view sync * fix(tweets): reply and get replies new versions (the old commented) * fix(tweets): clean unes blocks * fix(tweets): hard delete with clean up cron jobs + clean repy, replies + extend the response to include post_type (the actual one) * fix(test): unit tests * fix(test): unit tests * feat(tweet): views counter * fix(test): unit tests * fix(mig): to make sure all works fine --------- Co-authored-by: Mohamed Bahgat <mbahgat503@gmail.com> Co-authored-by: shady-2004 <shadyshiko2004@gmail.com>
1 parent 3cef382 commit 1412760

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2143
-1703
lines changed

simple-socket-test.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,7 @@ <h2>Event Logs</h2>
489489
const message = {
490490
content: content,
491491
message_type: messageType,
492+
image_url: "https://yapperdev.blob.core.windows.net/profile-images/test-team-1765575149782-standard.jpg",
492493
};
493494

494495
if (messageType === "reply" && replyToId) {

src/auth/auth.service.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,11 @@ export class AuthService {
137137
const { name, birth_date, email, captcha_token } = dto;
138138

139139
// Verify CAPTCHA first
140-
// try {
141-
// await this.captcha_service.validateCaptcha(captcha_token);
142-
// } catch (error) {
143-
// throw new BadRequestException(ERROR_MESSAGES.CAPTCHA_VERIFICATION_FAILED);
144-
// }
140+
try {
141+
await this.captcha_service.validateCaptcha(captcha_token);
142+
} catch (error) {
143+
throw new BadRequestException(ERROR_MESSAGES.CAPTCHA_VERIFICATION_FAILED);
144+
}
145145

146146
const existing_user = await this.user_repository.findByEmail(email);
147147
if (existing_user) {

src/background-jobs/ai-summary/ai-summary.service.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { BackgroundJobsService } from '../background-jobs';
33
import { QUEUE_NAMES } from '../constants/queue.constants';
44
import { Injectable, Logger } from '@nestjs/common';
55
import type { Queue } from 'bull';
6-
import { JOB_NAMES, JOB_PRIORITIES, JOB_DELAYS } from '../constants/queue.constants';
6+
import { JOB_DELAYS, JOB_NAMES, JOB_PRIORITIES } from '../constants/queue.constants';
77
import { GenerateTweetSummaryDto } from './ai-summary.dto';
88

99
@Injectable()
@@ -25,13 +25,13 @@ export class AiSummaryJobService extends BackgroundJobsService<GenerateTweetSumm
2525
return `tweet-summary:${dto.tweet_id}`;
2626
}
2727

28-
// Override queueJob to customize cleanup for fixed jobId
28+
// Override queueJob to customize cleanup for fixed job_id
2929
async queueGenerateSummary(dto: GenerateTweetSummaryDto) {
3030
try {
31-
const jobId = this.getJobId(dto);
31+
const job_id = this.getJobId(dto);
3232

3333
const job = await this.queue.add(this.job_name, dto, {
34-
jobId,
34+
jobId: job_id,
3535
priority: JOB_PRIORITIES.MEDIUM,
3636
delay: JOB_DELAYS.IMMEDIATE,
3737
attempts: 3,

src/background-jobs/notifications/mention/mention.dto.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export class MentionBackGroundNotificationJobDTO {
88
parent_tweet?: TweetResponseDTO;
99

1010
mentioned_by: string;
11-
mentioned_usernames?: string[];
11+
mentioned_user_ids?: string[];
1212

1313
tweet_type: 'tweet' | 'quote' | 'reply';
1414

src/background-jobs/notifications/mention/mention.processor.spec.ts

Lines changed: 23 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,6 @@ describe('MentionProcessor', () => {
8686
user_id: 'user-author',
8787
};
8888

89-
const mock_users = [
90-
{ id: 'user-1', username: 'user1' },
91-
{ id: 'user-2', username: 'user2' },
92-
];
93-
9489
const mock_mentioner = {
9590
id: 'user-author',
9691
username: 'author',
@@ -99,11 +94,10 @@ describe('MentionProcessor', () => {
9994
avatar_url: 'avatar.jpg',
10095
};
10196

102-
user_repository.find.mockResolvedValue(mock_users as User[]);
10397
user_repository.findOne.mockResolvedValue(mock_mentioner as User);
10498

10599
const job = mock_job({
106-
mentioned_usernames: ['user1', 'user2'],
100+
mentioned_user_ids: ['user-1', 'user-2'],
107101
mentioned_by: 'user-author',
108102
tweet_id: 'tweet-123',
109103
tweet: mock_tweet as unknown as Tweet,
@@ -113,10 +107,6 @@ describe('MentionProcessor', () => {
113107

114108
await processor.handleSendMentionNotification(job);
115109

116-
expect(user_repository.find).toHaveBeenCalledWith({
117-
where: [{ username: 'user1' }, { username: 'user2' }],
118-
select: ['id'],
119-
});
120110
expect(user_repository.findOne).toHaveBeenCalledTimes(2);
121111
expect(notifications_service.saveNotificationAndSend).toHaveBeenCalledTimes(2);
122112
});
@@ -128,12 +118,8 @@ describe('MentionProcessor', () => {
128118
user_id: 'user-author',
129119
};
130120

131-
const mock_users = [{ id: 'user-author', username: 'author' }];
132-
133-
user_repository.find.mockResolvedValue(mock_users as User[]);
134-
135121
const job = mock_job({
136-
mentioned_usernames: ['author'],
122+
mentioned_user_ids: ['user-author'],
137123
mentioned_by: 'user-author',
138124
tweet_id: 'tweet-123',
139125
tweet: mock_tweet as unknown as Tweet,
@@ -158,29 +144,26 @@ describe('MentionProcessor', () => {
158144
text: 'Original tweet',
159145
};
160146

161-
const mock_users = [{ id: 'user-1', username: 'user1' }];
162-
163147
const mock_mentioner = {
164148
id: 'user-author',
165-
username: 'author',
166-
email: 'author@test.com',
167-
name: 'Author',
168-
avatar_url: 'avatar.jpg',
149+
username: 'author-user',
150+
email: 'author@example.com',
151+
name: 'Author User',
152+
avatar_url: 'http://example.com/avatar.jpg',
169153
};
170154

171-
user_repository.find.mockResolvedValue(mock_users as User[]);
172-
user_repository.findOne.mockResolvedValue(mock_mentioner as User);
173-
174155
const job = mock_job({
175-
mentioned_usernames: ['user1'],
156+
mentioned_user_ids: ['user-1'],
176157
mentioned_by: 'user-author',
177-
tweet_id: 'quote-123',
178158
tweet: mock_tweet as unknown as Tweet,
179159
parent_tweet: mock_parent_tweet as any,
180160
tweet_type: 'quote',
181161
action: 'add',
182162
});
183163

164+
user_repository.findOne.mockResolvedValue(mock_mentioner as any);
165+
notifications_service.saveNotificationAndSend.mockResolvedValue(undefined);
166+
184167
await processor.handleSendMentionNotification(job);
185168

186169
expect(notifications_service.saveNotificationAndSend).toHaveBeenCalledWith(
@@ -192,10 +175,7 @@ describe('MentionProcessor', () => {
192175
mentioned_by: 'user-author',
193176
tweet_type: 'quote',
194177
}),
195-
expect.objectContaining({
196-
type: NotificationType.MENTION,
197-
tweet_type: 'quote',
198-
})
178+
expect.anything()
199179
);
200180
});
201181

@@ -211,8 +191,6 @@ describe('MentionProcessor', () => {
211191
text: 'Original tweet',
212192
};
213193

214-
const mock_users = [{ id: 'user-1', username: 'user1' }];
215-
216194
const mock_mentioner = {
217195
id: 'user-author',
218196
username: 'author',
@@ -221,11 +199,10 @@ describe('MentionProcessor', () => {
221199
avatar_url: 'avatar.jpg',
222200
};
223201

224-
user_repository.find.mockResolvedValue(mock_users as User[]);
225202
user_repository.findOne.mockResolvedValue(mock_mentioner as User);
226203

227204
const job = mock_job({
228-
mentioned_usernames: ['user1'],
205+
mentioned_user_ids: ['user-1'],
229206
mentioned_by: 'user-author',
230207
tweet_id: 'reply-123',
231208
tweet: mock_tweet as unknown as Tweet,
@@ -253,7 +230,7 @@ describe('MentionProcessor', () => {
253230
const logger_spy = jest.spyOn(processor['logger'], 'warn');
254231

255232
const job = mock_job({
256-
mentioned_usernames: ['user1'],
233+
mentioned_user_ids: ['user-1'],
257234
mentioned_by: 'user-author',
258235
tweet_id: 'tweet-123',
259236
tweet_type: 'tweet',
@@ -275,15 +252,12 @@ describe('MentionProcessor', () => {
275252
user_id: 'user-author',
276253
};
277254

278-
const mock_users = [{ id: 'user-1', username: 'user1' }];
279-
280255
const logger_spy = jest.spyOn(processor['logger'], 'warn');
281256

282-
user_repository.find.mockResolvedValue(mock_users as User[]);
283257
user_repository.findOne.mockResolvedValue(null);
284258

285259
const job = mock_job({
286-
mentioned_usernames: ['user1'],
260+
mentioned_user_ids: ['user-1'],
287261
mentioned_by: 'user-author',
288262
tweet_id: 'tweet-123',
289263
tweet: mock_tweet as unknown as Tweet,
@@ -300,18 +274,12 @@ describe('MentionProcessor', () => {
300274

301275
describe('handleSendMentionNotification - remove action', () => {
302276
it('should remove mention notifications for multiple users', async () => {
303-
const mock_users = [
304-
{ id: 'user-1', username: 'user1' },
305-
{ id: 'user-2', username: 'user2' },
306-
];
307-
308-
user_repository.find.mockResolvedValue(mock_users as User[]);
309277
notifications_service.removeMentionNotification.mockResolvedValue(
310278
'notification-id-123'
311279
);
312280

313281
const job = mock_job({
314-
mentioned_usernames: ['user1', 'user2'],
282+
mentioned_user_ids: ['user-1', 'user-2'],
315283
mentioned_by: 'user-author',
316284
tweet_id: 'tweet-123',
317285
action: 'remove',
@@ -338,13 +306,10 @@ describe('MentionProcessor', () => {
338306
});
339307

340308
it('should skip sending notification if removal failed', async () => {
341-
const mock_users = [{ id: 'user-1', username: 'user1' }];
342-
343-
user_repository.find.mockResolvedValue(mock_users as User[]);
344309
notifications_service.removeMentionNotification.mockResolvedValue(null);
345310

346311
const job = mock_job({
347-
mentioned_usernames: ['user1'],
312+
mentioned_user_ids: ['user-1'],
348313
mentioned_by: 'user-author',
349314
tweet_id: 'tweet-123',
350315
action: 'remove',
@@ -357,18 +322,12 @@ describe('MentionProcessor', () => {
357322
});
358323

359324
it('should not remove mention for the author themselves', async () => {
360-
const mock_users = [
361-
{ id: 'user-author', username: 'author' },
362-
{ id: 'user-1', username: 'user1' },
363-
];
364-
365-
user_repository.find.mockResolvedValue(mock_users as User[]);
366325
notifications_service.removeMentionNotification.mockResolvedValue(
367326
'notification-id-123'
368327
);
369328

370329
const job = mock_job({
371-
mentioned_usernames: ['author', 'user1'],
330+
mentioned_user_ids: ['user-author', 'user-1'],
372331
mentioned_by: 'user-author',
373332
tweet_id: 'tweet-123',
374333
action: 'remove',
@@ -386,7 +345,7 @@ describe('MentionProcessor', () => {
386345

387346
it('should handle empty mentioned_usernames array', async () => {
388347
const job = mock_job({
389-
mentioned_usernames: [],
348+
mentioned_user_ids: [],
390349
mentioned_by: 'user-author',
391350
tweet_id: 'tweet-123',
392351
action: 'remove',
@@ -400,14 +359,13 @@ describe('MentionProcessor', () => {
400359

401360
it('should handle missing tweet_id', async () => {
402361
const job = mock_job({
403-
mentioned_usernames: ['user1'],
362+
mentioned_user_ids: ['user-1'],
404363
mentioned_by: 'user-author',
405364
action: 'remove',
406365
});
407366

408367
await processor.handleSendMentionNotification(job);
409368

410-
expect(user_repository.find).not.toHaveBeenCalled();
411369
expect(notifications_service.removeMentionNotification).not.toHaveBeenCalled();
412370
});
413371
});
@@ -422,10 +380,11 @@ describe('MentionProcessor', () => {
422380

423381
const logger_spy = jest.spyOn(processor['logger'], 'error');
424382
const error = new Error('Database connection failed');
425-
user_repository.find.mockRejectedValue(error);
383+
384+
user_repository.findOne.mockRejectedValue(error);
426385

427386
const job = mock_job({
428-
mentioned_usernames: ['user1'],
387+
mentioned_user_ids: ['user-1'],
429388
mentioned_by: 'user-author',
430389
tweet_id: 'tweet-123',
431390
tweet: mock_tweet as unknown as Tweet,
@@ -447,8 +406,6 @@ describe('MentionProcessor', () => {
447406
user_id: 'user-author',
448407
};
449408

450-
const mock_users = [{ id: 'user-1', username: 'user1' }];
451-
452409
const mock_mentioner = {
453410
id: 'user-author',
454411
username: 'author',
@@ -457,14 +414,13 @@ describe('MentionProcessor', () => {
457414
avatar_url: 'avatar.jpg',
458415
};
459416

460-
user_repository.find.mockResolvedValue(mock_users as User[]);
461417
user_repository.findOne.mockResolvedValue(mock_mentioner as User);
462418

463419
const error = new Error('Save failed');
464420
notifications_service.saveNotificationAndSend.mockRejectedValue(error);
465421

466422
const job = mock_job({
467-
mentioned_usernames: ['user1'],
423+
mentioned_user_ids: ['user-1'],
468424
mentioned_by: 'user-author',
469425
tweet_id: 'tweet-123',
470426
tweet: mock_tweet as unknown as Tweet,

src/background-jobs/notifications/mention/mention.processor.ts

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export class MentionProcessor {
3131
async handleSendMentionNotification(job: Job<MentionBackGroundNotificationJobDTO>) {
3232
try {
3333
const {
34-
mentioned_usernames,
34+
mentioned_user_ids,
3535
mentioned_by,
3636
tweet_id,
3737
tweet,
@@ -42,29 +42,23 @@ export class MentionProcessor {
4242

4343
if (action === 'remove') {
4444
// For remove action, we need usernames to find user IDs
45-
if (!mentioned_usernames || mentioned_usernames.length === 0 || !tweet_id) return;
46-
47-
// Fetch user IDs from usernames
48-
const users = await this.user_repository.find({
49-
where: mentioned_usernames.map((username) => ({ username })),
50-
select: ['id'],
51-
});
45+
if (!mentioned_user_ids || mentioned_user_ids.length === 0 || !tweet_id) return;
5246

5347
// Queue removal for each mentioned user
54-
for (const user of users) {
55-
if (user.id === mentioned_by) continue;
48+
for (const user_id of mentioned_user_ids) {
49+
if (user_id === mentioned_by) continue;
5650

5751
const notification_id =
5852
await this.notifications_service.removeMentionNotification(
59-
user.id,
53+
user_id,
6054
tweet_id,
6155
mentioned_by
6256
);
6357

6458
if (notification_id) {
6559
this.notifications_service.sendNotificationOnly(
6660
NotificationType.MENTION,
67-
user.id,
61+
user_id,
6862
{
6963
id: notification_id,
7064
...job.data,
@@ -80,19 +74,13 @@ export class MentionProcessor {
8074
}
8175

8276
// For add action with usernames (batch processing)
83-
else if (mentioned_usernames && mentioned_usernames.length > 0) {
84-
// Fetch user IDs from usernames
85-
const users = await this.user_repository.find({
86-
where: mentioned_usernames.map((username) => ({ username })),
87-
select: ['id'],
88-
});
89-
77+
else if (mentioned_user_ids && mentioned_user_ids.length > 0) {
9078
// Process mention for each user
91-
for (const user of users) {
92-
if (user.id === mentioned_by) continue;
79+
for (const user_id of mentioned_user_ids) {
80+
if (user_id === mentioned_by) continue;
9381

9482
await this.processMentionForUser(
95-
user.id,
83+
user_id,
9684
mentioned_by,
9785
tweet,
9886
parent_tweet,

0 commit comments

Comments
 (0)