Skip to content
Merged
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Some features required secrets, such as to connect to CastleDKP.com or the Castl

#### 📣 EQNotify

`/eqnotify` lets raiders subscribe to phone notifications for the raid targets they care about. It watches the batphone channel and, for each subscriber whose keyword tags match the batphone (buff / last-hour RTE calls are filtered out), pushes an alert to their phone.
`/eqnotify` lets raiders subscribe to phone notifications for the raid targets they care about. It watches the batphone channel and, for each subscriber whose keyword tags match the batphone (buff / last-hour RTE calls are filtered out), pushes an alert to their phone. Alerts are only delivered to subscribers who **currently hold the Raider role**, so anyone who goes inactive or leaves stops receiving them automatically.

- **Delivery channels**: **WirePusher** (free, Android-only) or **Telegram** (iOS/Android/desktop). Telegram delivery requires `TELEGRAM_BOT_TOKEN`; if unset, only WirePusher is offered.
- **Getting your ID**: WirePusher users use their device ID from the app. Telegram users start a chat with the guild's EQNotify bot and get their numeric chat ID (e.g. from [@userinfobot](https://t.me/userinfobot)).
Expand Down
2 changes: 1 addition & 1 deletion src/features/eqnotify/commands/register-subcommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class RegisterSubcommand extends Subcommand {
? `Updated your EQNotify delivery to **${channel}**. Your notification tags are unchanged.`
: `You're enrolled in EQNotify via **${channel}**! You'll be notified for: ${DEFAULT_TAGS.join(
", "
)}.\nUse \`/eqnotify add-tag\` / \`/eqnotify remove-tag\` to customize, and \`/eqnotify test\` to verify delivery.`
)}.\nUse \`/eqnotify add-tag\` / \`/eqnotify remove-tag\` to customize, and \`/eqnotify test\` to verify delivery.\n_Note: alerts are only sent while you actively hold the Raider role._`
);
}

Expand Down
31 changes: 28 additions & 3 deletions src/features/eqnotify/eqnotify.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { eqnotify_subscriber, eqnotify_type } from "@prisma/client";
import { prismaClient } from "../..";
import { getMember, prismaClient } from "../..";
import { raiderRoleId } from "../../config";
import { log } from "../../shared/logger";
import { notify } from "./notifiers";
import {
Expand All @@ -18,6 +19,24 @@ interface EnrollInput {
type: eqnotify_type;
}

/**
* Whether a member currently holds the Raider role. Batphone alerts are only
* delivered to active Raiders, so a subscriber who loses the role (goes
* inactive, leaves the guild, etc.) stops receiving alerts without having to
* unregister. Failures to resolve the member are treated as "not a Raider".
*/
const hasRaiderRole = async (discordId: string): Promise<boolean> => {
try {
const member = await getMember(discordId);
return member.roles.cache.has(raiderRoleId);
} catch (error) {
log(
`EQNotify could not resolve member ${discordId} for role check: ${error}`
);
return false;
}
};

const requireSubscriber = async (discordId: string) => {
const subscriber = await prismaClient.eqnotify_subscriber.findUnique({
where: { discordId },
Expand Down Expand Up @@ -95,7 +114,8 @@ export const eqnotifyService = {

/**
* Matches a batphone against every subscriber's tags and delivers alerts.
* Delivery failures are logged but never interrupt other subscribers.
* Only subscribers who currently hold the Raider role are notified. Delivery
* failures are logged but never interrupt other subscribers.
*/
dispatch: async (content: string) => {
if (!content.trim() || isFiltered(content)) {
Expand All @@ -105,7 +125,12 @@ export const eqnotifyService = {
await Promise.all(
subscribers
.filter((sub) => matchesTags(content, sub.tags))
.map((sub) => eqnotifyService.notifySubscriber(sub, content))
.map(async (sub) => {
if (!(await hasRaiderRole(sub.discordId))) {
return;
}
await eqnotifyService.notifySubscriber(sub, content);
})
);
},

Expand Down
Loading