Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.smalltech.hashtaglocal_backend.job;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.smalltech.hashtaglocal_backend.model.NotificationType;
import org.smalltech.hashtaglocal_backend.service.BroadcastService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
* Daily reminder that today's quiz is live, pushed to every active device (type=CHAT, lands on the
* chat page — same route as any other CHAT notification). Runs independently of {@link
* BulletinWeatherJob}: content doesn't reference a specific locality/bulletin, so it doesn't wait
* on that day's generation to finish.
*/
@Component
@RequiredArgsConstructor
@Slf4j
@ConditionalOnProperty(
name = "quiz.notification.enabled",
havingValue = "true",
matchIfMissing = true)
public class QuizNotificationJob {

private final BroadcastService broadcastService;

@Value("${quiz.notification.cron:0 30 8 * * *}")
private String scheduleExpression;

@Value("${quiz.notification.title}")
private String title;

@Value("${quiz.notification.body}")
private String body;

@Scheduled(
cron = "${quiz.notification.cron:0 30 8 * * *}",
zone = "${quiz.notification.zone:Asia/Kolkata}")
public void run() {
log.info("Quiz notification job started (cron={})", scheduleExpression);
broadcastService.sendSystemNotification(NotificationType.CHAT, "BULLETIN", title, body);
}
Comment on lines +42 to +44

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be "QUIZ" instead "BULLETIN"?

}
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,35 @@ public NotificationResult sendNotification(NotificationBody notification) {
payload.put("issueId", issueId);
}

return sendToAllTokens(NotificationSource.ADMIN, type, null, null, title, body, payload);
}

/** Automated (cron-triggered) push to every active device, logged with source=SYSTEM. */
@Transactional
public NotificationResult sendSystemNotification(
NotificationType type, String sourceRefType, String title, String body) {
Map<String, String> payload = new HashMap<>();
payload.put("type", type.name());
return sendToAllTokens(
NotificationSource.SYSTEM, type, sourceRefType, null, title, body, payload);
}

private NotificationResult sendToAllTokens(
NotificationSource source,
NotificationType type,
String sourceRefType,
Long sourceRefId,
String title,
String body,
Map<String, String> payload) {
List<String> tokens = userAuthSessionRepository.findAllActiveNotificationTokens();

NotificationLogEntity logEntry =
notificationLogRepository.save(
NotificationLogEntity.builder()
.source(NotificationSource.ADMIN)
.source(source)
.sourceRefType(sourceRefType)
.sourceRefId(sourceRefId)
.type(type)
.title(title)
.body(body)
Expand All @@ -76,7 +99,7 @@ public NotificationResult sendNotification(NotificationBody notification) {
Map<String, String> fcmData = new HashMap<>(payload);
fcmData.put("notificationLogId", logEntry.getId().toString());

String fcmTag = type.name().toLowerCase() + "_admin";
String fcmTag = type.name().toLowerCase() + "_" + source.name().toLowerCase();
int totalSuccess = 0;
for (int i = 0; i < tokens.size(); i += FCM_BATCH_SIZE) {
List<String> batch = tokens.subList(i, Math.min(i + FCM_BATCH_SIZE, tokens.size()));
Expand All @@ -85,7 +108,8 @@ public NotificationResult sendNotification(NotificationBody notification) {
totalSuccess += result.successCount();
}

log.info("{} sent: {} recipients, {} FCM accepted", type, tokens.size(), totalSuccess);
log.info(
"{} ({}) sent: {} recipients, {} FCM accepted", type, source, tokens.size(), totalSuccess);

logEntry.setRecipientCount(tokens.size());
logEntry.setSuccessCount(totalSuccess);
Expand Down
13 changes: 13 additions & 0 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,19 @@ groq:
url: ${GROQ_API_URL:https://api.groq.com/openai/v1/chat/completions}
model: ${GROQ_MODEL:llama-3.3-70b-versatile}

# Daily quiz notification: automated reminder that today's quiz is live (type=CHAT, lands
# on the chat page). Independent of the bulletin/weather job above — global send, no per-locality
# targeting, no dependency on that day's bulletin generation succeeding.
quiz:
notification:
enabled: ${QUIZ_NOTIFICATION_ENABLED:true}
# Daily at 8:30 AM IST — 30 min after the bulletin/weather job. Use '-' to disable.
cron: ${QUIZ_NOTIFICATION_CRON:0 30 8 * * *}
# Cron above is always interpreted in this zone, regardless of where the server is deployed.
zone: ${QUIZ_NOTIFICATION_ZONE:Asia/Kolkata}
title: ${QUIZ_NOTIFICATION_TITLE:☕ Rise & Quiz!}
body: ${QUIZ_NOTIFICATION_BODY:Your daily local quiz just dropped. Tap in and see how well you really know your city.}

portalissue:
enabled: ${PORTALISSUE_ENABLED:true}
cron: ${PORTALISSUE_CRON:0 0 1 * * *}
Expand Down