2025-12-15 21:01:50 -05:00
|
|
|
|
/*
|
|
|
|
|
|
Made with ❤️ by neoarz
|
|
|
|
|
|
I am not responsible for any damage caused by this plugin; use at your own risk
|
|
|
|
|
|
Vencord does not endorse/support this plugin (Works with Equicord as well)
|
|
|
|
|
|
dm @neoarz if u need help or have any questions
|
|
|
|
|
|
https://github.com/neoarz/NitroSniper
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
import { Devs } from "@utils/constants";
|
|
|
|
|
|
import { Logger } from "@utils/Logger";
|
2026-04-22 22:02:36 -04:00
|
|
|
|
import definePlugin from "@utils/types";
|
|
|
|
|
|
import { Message } from "@vencord/discord-types";
|
2025-12-17 23:24:14 -05:00
|
|
|
|
import { findByPropsLazy } from "@webpack";
|
2026-04-22 13:03:22 -04:00
|
|
|
|
import { UserStore } from "@webpack/common";
|
2025-12-15 21:01:50 -05:00
|
|
|
|
|
2026-04-22 22:02:36 -04:00
|
|
|
|
import { settings } from "./settings";
|
2026-04-28 00:25:17 -04:00
|
|
|
|
import type { ClaimRequest, WebhookResult } from "./types";
|
2026-04-22 22:02:36 -04:00
|
|
|
|
import { sendClaimWebhook } from "./webhook";
|
|
|
|
|
|
|
|
|
|
|
|
const GIFT_LINK_REGEX = /(?:discord\.gift\/|discord\.com\/gifts?\/)([a-zA-Z0-9]{16,24})/;
|
|
|
|
|
|
|
2025-12-15 21:01:50 -05:00
|
|
|
|
const logger = new Logger("NitroSniper");
|
2025-12-17 23:24:14 -05:00
|
|
|
|
const GiftActions = findByPropsLazy("redeemGiftCode");
|
|
|
|
|
|
|
|
|
|
|
|
let startTime = 0;
|
|
|
|
|
|
let claiming = false;
|
2026-04-22 22:02:36 -04:00
|
|
|
|
const claimQueue: ClaimRequest[] = [];
|
2025-12-17 23:24:14 -05:00
|
|
|
|
|
2026-04-22 12:38:23 -04:00
|
|
|
|
function resetState() {
|
|
|
|
|
|
startTime = Date.now();
|
2026-04-22 22:02:36 -04:00
|
|
|
|
claimQueue.length = 0;
|
2026-04-22 12:38:23 -04:00
|
|
|
|
claiming = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-28 00:25:17 -04:00
|
|
|
|
function toError(error: unknown) {
|
|
|
|
|
|
return error instanceof Error ? error : new Error(String(error));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 22:02:36 -04:00
|
|
|
|
function isOwnMessage(message: Message) {
|
|
|
|
|
|
return message.author?.id === UserStore.getCurrentUser()?.id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function shouldSkipMessage(message: Message) {
|
|
|
|
|
|
return settings.store.ignoreOwnGiftLinks && isOwnMessage(message);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function isMessageOlderThanStart(message: Message) {
|
|
|
|
|
|
return new Date(message.timestamp).getTime() < startTime;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function extractGiftCode(content: string) {
|
|
|
|
|
|
return content.match(GIFT_LINK_REGEX)?.[1] ?? null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function createClaimRequest(message: Message): ClaimRequest | null {
|
|
|
|
|
|
const code = message.content ? extractGiftCode(message.content) : null;
|
|
|
|
|
|
if (!code) return null;
|
|
|
|
|
|
|
2026-04-28 00:25:17 -04:00
|
|
|
|
const authorId = message.author?.id;
|
|
|
|
|
|
const authorAvatar = message.author?.avatar;
|
|
|
|
|
|
|
2026-04-22 22:02:36 -04:00
|
|
|
|
return {
|
|
|
|
|
|
code,
|
2026-04-28 00:25:17 -04:00
|
|
|
|
authorId,
|
2026-04-22 22:02:36 -04:00
|
|
|
|
authorName: message.author?.globalName ?? message.author?.username,
|
|
|
|
|
|
authorUsername: message.author?.username,
|
2026-04-28 00:25:17 -04:00
|
|
|
|
authorAvatarUrl: authorId && authorAvatar
|
|
|
|
|
|
? `https://cdn.discordapp.com/avatars/${authorId}/${authorAvatar}.png?size=128`
|
|
|
|
|
|
: undefined,
|
2026-04-22 22:02:36 -04:00
|
|
|
|
channelId: message.channel_id,
|
|
|
|
|
|
guildId: message.guild_id,
|
|
|
|
|
|
messageId: message.id
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function notifyClaim(result: WebhookResult, request: ClaimRequest) {
|
|
|
|
|
|
void sendClaimWebhook(
|
|
|
|
|
|
settings.store.webhookUrl,
|
|
|
|
|
|
result,
|
|
|
|
|
|
request
|
|
|
|
|
|
).catch(webhookError => {
|
|
|
|
|
|
logger.error("Failed to send NitroSniper webhook notification", webhookError);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function continueQueue() {
|
|
|
|
|
|
claiming = false;
|
|
|
|
|
|
processQueue();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleClaimSuccess(request: ClaimRequest) {
|
|
|
|
|
|
logger.log(`Successfully redeemed code: ${request.code}`);
|
|
|
|
|
|
notifyClaim("claimed", request);
|
|
|
|
|
|
continueQueue();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleClaimFailure(request: ClaimRequest, error: Error) {
|
|
|
|
|
|
logger.error(`Failed to redeem code: ${request.code}`, error);
|
|
|
|
|
|
notifyClaim("failed", request);
|
|
|
|
|
|
continueQueue();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-17 23:24:14 -05:00
|
|
|
|
function processQueue() {
|
2026-04-22 22:02:36 -04:00
|
|
|
|
if (claiming) return;
|
2025-12-17 23:24:14 -05:00
|
|
|
|
|
2026-04-22 22:02:36 -04:00
|
|
|
|
const request = claimQueue.shift();
|
|
|
|
|
|
if (!request) return;
|
2025-12-17 23:24:14 -05:00
|
|
|
|
|
2026-04-22 22:02:36 -04:00
|
|
|
|
claiming = true;
|
2025-12-17 23:24:14 -05:00
|
|
|
|
GiftActions.redeemGiftCode({
|
2026-04-22 22:02:36 -04:00
|
|
|
|
code: request.code,
|
|
|
|
|
|
onRedeemed: () => handleClaimSuccess(request),
|
2026-04-28 00:25:17 -04:00
|
|
|
|
onError: (error: unknown) => handleClaimFailure(request, toError(error))
|
2025-12-17 23:24:14 -05:00
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-12-15 21:01:50 -05:00
|
|
|
|
|
|
|
|
|
|
export default definePlugin({
|
|
|
|
|
|
name: "NitroSniper",
|
|
|
|
|
|
description: "Automatically redeems Nitro gift links sent in chat",
|
|
|
|
|
|
authors: [Devs.neoarz],
|
2026-04-22 12:38:23 -04:00
|
|
|
|
tags: ["Chat", "Utility"],
|
|
|
|
|
|
searchTerms: ["nitro", "gift", "redeem", "snipe"],
|
2026-04-22 13:03:22 -04:00
|
|
|
|
settings,
|
2025-12-15 21:01:50 -05:00
|
|
|
|
|
|
|
|
|
|
start() {
|
2026-04-22 12:38:23 -04:00
|
|
|
|
resetState();
|
2025-12-15 21:01:50 -05:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
flux: {
|
2026-04-22 22:02:36 -04:00
|
|
|
|
MESSAGE_CREATE({ message }: { message: Message; }) {
|
|
|
|
|
|
if (!message.content || shouldSkipMessage(message) || isMessageOlderThanStart(message)) return;
|
2025-12-17 23:24:14 -05:00
|
|
|
|
|
2026-04-22 22:02:36 -04:00
|
|
|
|
const request = createClaimRequest(message);
|
|
|
|
|
|
if (!request) return;
|
2025-12-17 23:24:14 -05:00
|
|
|
|
|
2026-04-22 22:02:36 -04:00
|
|
|
|
claimQueue.push(request);
|
2025-12-17 23:24:14 -05:00
|
|
|
|
processQueue();
|
2025-12-15 21:01:50 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-17 23:24:14 -05:00
|
|
|
|
});
|