Skip to content

Commit 7bd0b5a

Browse files
committed
feat: implement phishtank for database of url of phishing links
1 parent d11ac67 commit 7bd0b5a

5 files changed

Lines changed: 103 additions & 24 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ dist
55
.temp
66
*.log
77
*.env
8+
*.phishtank

src/components/events/message.ts

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { errors } from "../utils/data";
1616
import emojiRegex from "emoji-regex";
1717
import { funD, happyEE, sadEE, loveEE } from "../../data/reaction";
1818
import { containsAny } from "../utils/string";
19+
import { checkMessage } from "../utils/phishtank";
1920

2021
const regex = emojiRegex();
2122
const commandPrefix = process.env.COMMAND_PREFIX || "!";
@@ -53,13 +54,35 @@ export default async function (msg: Message, type: string) {
5354
.replace(/[\u0300-\u036f\u00b4\u0060\u005e\u007e]/g, "")
5455
.trim();
5556

57+
/*
58+
*
59+
* Check for scam urls
60+
*/
61+
62+
Promise.resolve().then(async () => {
63+
const spamUrls = checkMessage(msg.body);
64+
65+
if (spamUrls.length == 0) return;
66+
67+
const text = `
68+
\`Phishing Alert\`
69+
70+
We've found that this url(s): \`${spamUrls.join(", ")}\`
71+
to be phishing site/page.
72+
Proceed with caution.
73+
`;
74+
await msg.reply(text);
75+
});
76+
5677
/*
5778
*
5879
* Quiz command validation
5980
*/
6081
if (msg.hasQuotedMsg) {
61-
const quoted = await msg.getQuotedMessage();
62-
if (await quiz(msg, quoted)) return;
82+
Promise.resolve().then(async () => {
83+
const quoted = await msg.getQuotedMessage();
84+
await quiz(msg, quoted);
85+
});
6386
}
6487

6588
const prefix = !msg.body.startsWith(commandPrefix);
@@ -99,22 +122,26 @@ export default async function (msg: Message, type: string) {
99122
*
100123
* Process msg reaction
101124
*/
102-
if (!msg.fromMe) {
103-
const emojis = [...new Set([...msg.body.matchAll(regex)].map((m) => m[0]))];
104-
if (emojis.length > 0) {
105-
const react = emojis[Math.floor(Math.random() * emojis.length)];
106-
107-
await msg.react(react);
108-
} else if (containsAny(msg.body, funD)) {
109-
await msg.react("🤣");
110-
} else if (containsAny(msg.body, happyEE)) {
111-
await msg.reply(funD[Math.floor(Math.random() * funD.length)]);
112-
} else if (containsAny(msg.body, sadEE)) {
113-
await msg.react("😭");
114-
} else if (containsAny(msg.body, loveEE)) {
115-
await msg.react("❤️");
125+
Promise.resolve().then(async () => {
126+
if (!msg.fromMe) {
127+
const emojis = [
128+
...new Set([...msg.body.matchAll(regex)].map((m) => m[0])),
129+
];
130+
if (emojis.length > 0) {
131+
const react = emojis[Math.floor(Math.random() * emojis.length)];
132+
133+
await msg.react(react);
134+
} else if (containsAny(msg.body, funD)) {
135+
await msg.react("🤣");
136+
} else if (containsAny(msg.body, happyEE)) {
137+
await msg.reply(funD[Math.floor(Math.random() * funD.length)]);
138+
} else if (containsAny(msg.body, sadEE)) {
139+
await msg.react("😭");
140+
} else if (containsAny(msg.body, loveEE)) {
141+
await msg.react("❤️");
142+
}
116143
}
117-
}
144+
});
118145

119146
/*
120147
* Check if the message starts with the command prefix.

src/components/utils/phishtank.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import * as path from "path";
2+
import log from "./log";
3+
4+
const phishTankFile = path.join(
5+
__dirname,
6+
"../../../.phishtank/verified_online.json",
7+
);
8+
let phishingSet: Set<string> = new Set();
9+
10+
function normalizeUrl(raw: string): string | null {
11+
try {
12+
const u = new URL(raw.trim());
13+
let normalized = `${u.protocol}//${u.hostname}${u.pathname}`;
14+
if (!normalized.endsWith("/")) {
15+
normalized += "/";
16+
}
17+
return normalized;
18+
} catch {
19+
return null;
20+
}
21+
}
22+
23+
async function loadPhishTank() {
24+
try {
25+
const data = await import(phishTankFile);
26+
27+
const urls = data.default
28+
.map((entry: any) => normalizeUrl(entry.url))
29+
.filter((u: string | null): u is string => Boolean(u));
30+
31+
phishingSet = new Set(urls);
32+
33+
log.info(
34+
"Phishtank",
35+
`Loaded ${phishingSet.size} phishing URLs from PhishTank.`,
36+
);
37+
} catch (err: any) {
38+
log.error("Phishtank", "Error loading PhishTank JSON:", err.message || err);
39+
}
40+
}
41+
42+
function extractUrls(text: string): string[] {
43+
const urlRegex = /(https?:\/\/[^\s]+)/g;
44+
return text.match(urlRegex) || [];
45+
}
46+
47+
export function checkMessage(message: string): string[] {
48+
const urls = extractUrls(message)
49+
.map((url) => normalizeUrl(url))
50+
.filter((u): u is string => Boolean(u));
51+
52+
return urls.filter((url) => phishingSet.has(url));
53+
}
54+
55+
loadPhishTank();

src/components/utils/requirements.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { execSync } from "child_process";
22
import * as process from "process";
33
import semver from "semver";
44
import log from "./log";
5-
import url from "url";
65

76
function checkNodeVersion() {
87
const current = process.versions.node;

src/index.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,18 @@ import dotenv from "dotenv";
22
dotenv.config();
33

44
import { checkRequirements } from "./components/utils/requirements";
5-
import { Message } from "whatsapp-web.js";
6-
import path from "path";
75
import log from "./components/utils/log";
86
import { mapCommands } from "./components/utils/cmd/loader";
97
import watcher from "./components/utils/cmd/watcher";
108
import "./components/process";
119
import "./components/server";
10+
import { checkMessage } from "./components/utils/phishtank";
1211
import MemoryMonitor from "./components/utils/memMonitor";
12+
import sleep from "./components/utils/sleep";
1313

1414
const monitor = new MemoryMonitor({
1515
interval: 60000,
16-
thresholdMB: parseInt(
17-
process.env.PROJECT_THRESHOLD_MEMORY || "1024",
18-
10
19-
)
16+
thresholdMB: parseInt(process.env.PROJECT_THRESHOLD_MEMORY || "1024", 10),
2017
});
2118

2219
monitor.start();

0 commit comments

Comments
 (0)