···11import { HANDLE_CHECKS } from "./constants.js";
22import logger from "./logger.js";
33-import { Handle } from "./types.js";
43import {
54 createAccountReport,
65 createAccountComment,
76 createAccountLabel,
88- checkAccountLabels,
97} from "./moderation.js";
1010-import { limit } from "./limits.js";
118129export const checkHandle = async (
1310 did: string,
1411 handle: string,
1512 time: number,
1613) => {
1717- const ActLabelChk = await limit(() => checkAccountLabels(did));
1814 // Get a list of labels
1915 const labels: string[] = Array.from(
2016 HANDLE_CHECKS,
···4440 }
45414642 if (checkList?.toLabel === true) {
4343+ logger.info(`[CHECKHANDLE]: Labeling ${did} for ${checkList!.label}`);
4744 {
4845 createAccountLabel(
4946 did,
···5451 }
55525653 if (checkList?.reportAcct === true) {
5757- logger.info(`Report only: ${handle}`);
5454+ logger.info(`[CHECKHANDLE]: Reporting ${did} for ${checkList!.label}`);
5855 createAccountReport(did, `${time}: ${checkList!.comment} - ${handle}`);
5956 }
60576158 if (checkList?.commentAcct === true) {
6262- logger.info(`Comment only: ${handle}`);
5959+ logger.info(
6060+ `[CHECKHANDLE]: Commenting on ${did} for ${checkList!.label}`,
6161+ );
6362 createAccountComment(did, `${time}: ${checkList!.comment} - ${handle}`);
6463 }
6564 }
+33-14
src/checkPosts.ts
···11-import { POST_CHECKS } from "./constants.js";
11+import { LINK_SHORTENER, POST_CHECKS, langs } from "./constants.js";
22import { Post } from "./types.js";
33import logger from "./logger.js";
44import {
···77 createAccountComment,
88 createPostReport,
99} from "./moderation.js";
1010-import { LINK_SHORTENER } from "./constants.js";
1111-import { getFinalUrl } from "./utils.js";
1010+import { getFinalUrl, getLanguage } from "./utils.js";
12111312export const checkPosts = async (post: Post[]) => {
1413 // Get a list of labels
···2322 if (LINK_SHORTENER.test(post[0].text)) {
2423 try {
2524 const url = post[0].text.match(urlRegex);
2626- if (url) {
2525+ if (url && LINK_SHORTENER.test(url[0])) {
2626+ logger.info(`[CHECKPOSTS]: Checking shortened URL: ${url[0]}`);
2727 const finalUrl = await getFinalUrl(url[0]);
2828 if (finalUrl) {
2929 const originalUrl = post[0].text;
3030- post[0].text = finalUrl;
3131- logger.info(`Shortened URL resolved: ${originalUrl} -> ${finalUrl}`);
3030+ post[0].text = post[0].text.replace(url[0], finalUrl);
3131+ logger.info(
3232+ `[CHECKPOSTS]: Shortened URL resolved: ${originalUrl} -> ${finalUrl}`,
3333+ );
3234 }
3335 }
3436 } catch (error) {
3535- logger.error(`Failed to resolve shortened URL: ${post[0].text}`, error);
3737+ logger.error(
3838+ `[CHECKPOSTS]: Failed to resolve shortened URL: ${post[0].text}`,
3939+ error,
4040+ );
3641 // Keep the original URL if resolution fails
3742 }
3843 }
39444545+ // Get the post's language
4646+ const lang = await getLanguage(post[0].text);
4747+4048 // iterate through the labels
4149 labels.forEach((label) => {
4250 const checkPost = POST_CHECKS.find(
4351 (postCheck) => postCheck.label === label,
4452 );
45535454+ if (label === "contains-slur" || label === "monitor-slur") {
5555+ if (!langs.includes(lang)) {
5656+ return;
5757+ }
5858+ }
5959+4660 if (checkPost?.ignoredDIDs) {
4761 if (checkPost?.ignoredDIDs.includes(post[0].did)) {
4848- logger.info(`Whitelisted DID: ${post[0].did}`);
6262+ logger.info(`[CHECKPOSTS]: Whitelisted DID: ${post[0].did}`);
4963 return;
5064 }
5165 }
···5468 // Check if post is whitelisted
5569 if (checkPost?.whitelist) {
5670 if (checkPost?.whitelist.test(post[0].text)) {
5757- logger.info(`Whitelisted phrase found"`);
7171+ logger.info(`[CHECKPOSTS]: Whitelisted phrase found"`);
5872 return;
5973 }
6074 }
61756276 if (checkPost!.toLabel === true) {
6363- logger.info(`Labeling post: ${post[0].atURI} for ${checkPost!.label}`);
7777+ logger.info(
7878+ `[CHECKPOSTS]: Labeling ${post[0].atURI} for ${checkPost!.label}`,
7979+ );
6480 createPostLabel(
6581 post[0].atURI,
6682 post[0].cid,
···71877288 if (checkPost!.reportPost === true) {
7389 logger.info(
7474- `Suspected ${checkPost!.label} in post at ${post[0].atURI}`,
9090+ `[CHECKPOSTS]: Reporting ${post[0].atURI} for ${checkPost!.label}`,
7591 );
7692 logger.info(`Reporting: ${post[0].atURI}`);
7793 createPostReport(
···8298 }
839984100 if (checkPost!.reportAcct === true) {
8585- logger.info(`${checkPost!.label} in post at ${post[0].atURI}`);
8686- logger.info(`Report only: ${post[0].did}`);
101101+ logger.info(
102102+ `[CHECKPOSTS]: Reporting on ${post[0].did} for ${checkPost!.label} in ${post[0].atURI}`,
103103+ );
87104 createAccountReport(
88105 post[0].did,
89106 `${post[0].time}: ${checkPost?.comment} at ${post[0].atURI} with text "${post[0].text}"`,
···91108 }
9210993110 if (checkPost!.commentAcct === true) {
9494- logger.info(`Comment on account: ${post[0].did}`);
111111+ logger.info(
112112+ `[CHECKPOSTS]: Commenting on ${post[0].did} for ${checkPost!.label} in ${post[0].atURI}`,
113113+ );
95114 createAccountComment(
96115 post[0].did,
97116 `${post[0].time}: ${checkPost?.comment} at ${post[0].atURI} with text "${post[0].text}"`,
+37-10
src/checkProfiles.ts
···11-import { describe } from "node:test";
22-import { PROFILE_CHECKS } from "./constants.js";
11+import { login } from "./agent.js";
22+import { langs, PROFILE_CHECKS } from "./constants.js";
33import logger from "./logger.js";
44import {
55 createAccountReport,
66 createAccountLabel,
77- checkAccountLabels,
87 createAccountComment,
98} from "./moderation.js";
1010-import { limit } from "./limits.js";
99+import { getLanguage } from "./utils.js";
11101211export const checkDescription = async (
1312 did: string,
···1514 displayName: string,
1615 description: string,
1716) => {
1717+ const lang = await getLanguage(description);
1818+1919+ if (!langs.includes(lang)) {
2020+ return;
2121+ }
2222+1823 const labels: string[] = Array.from(
1924 PROFILE_CHECKS,
2025 (profileCheck) => profileCheck.label,
···2934 // Check if DID is whitelisted
3035 if (checkProfiles?.ignoredDIDs) {
3136 if (checkProfiles.ignoredDIDs.includes(did)) {
3232- logger.info(`Whitelisted DID: ${did}`);
3737+ logger.info(`[CHECKDESCRIPTION]: Whitelisted DID: ${did}`);
3338 return;
3439 }
3540 }
···4045 // Check if description is whitelisted
4146 if (checkProfiles!.whitelist) {
4247 if (checkProfiles!.whitelist.test(description)) {
4343- logger.info(`Whitelisted phrase found.`);
4848+ logger.info(`[CHECKDESCRIPTION]: Whitelisted phrase found.`);
4449 return;
4550 }
4651 }
47524853 if (checkProfiles!.toLabel === true) {
4949- logger.info(`Creating label for ${did}`);
5054 createAccountLabel(
5155 did,
5256 `${checkProfiles!.label}`,
5357 `${time}: ${checkProfiles!.comment} - ${displayName} - ${description}`,
5458 );
5959+ logger.info(
6060+ `[CHECKDESCRIPTION]: Labeling ${did} for ${checkProfiles!.label}`,
6161+ );
5562 }
56635764 if (checkProfiles!.reportAcct === true) {
5865 createAccountReport(
5966 did,
6067 `${time}: ${checkProfiles!.comment} - ${displayName} - ${description}`,
6868+ );
6969+ logger.info(
7070+ `[CHECKDESCRIPTION]: Reporting ${did} for ${checkProfiles!.label}`,
6171 );
6272 }
63736474 if (checkProfiles!.commentAcct === true) {
6565- logger.info(`Commenting on account for ${did}`);
6675 createAccountComment(
6776 did,
6877 `${time}: ${checkProfiles!.comment} - ${displayName} - ${description}`,
7878+ );
7979+ logger.info(
8080+ `[CHECKDESCRIPTION]: Commenting on ${did} for ${checkProfiles!.label}`,
6981 );
7082 }
7183 }
···8092 displayName: string,
8193 description: string,
8294) => {
9595+ const lang = await getLanguage(description);
9696+9797+ if (!langs.includes(lang)) {
9898+ return;
9999+ }
100100+83101 // Get a list of labels
84102 const labels: string[] = Array.from(
85103 PROFILE_CHECKS,
···95113 // Check if DID is whitelisted
96114 if (checkProfiles?.ignoredDIDs) {
97115 if (checkProfiles.ignoredDIDs.includes(did)) {
9898- logger.info(`Whitelisted DID: ${did}`);
116116+ logger.info(`[CHECKDISPLAYNAME]: Whitelisted DID: ${did}`);
99117 return;
100118 }
101119 }
···106124 // Check if displayName is whitelisted
107125 if (checkProfiles!.whitelist) {
108126 if (checkProfiles!.whitelist.test(displayName)) {
109109- logger.info(`Whitelisted phrase found.`);
127127+ logger.info(`[CHECKDISPLAYNAME]: Whitelisted phrase found.`);
110128 return;
111129 }
112130 }
···117135 `${checkProfiles!.label}`,
118136 `${time}: ${checkProfiles!.comment} - ${displayName} - ${description}`,
119137 );
138138+ logger.info(
139139+ `[CHECKDISPLAYNAME]: Labeling ${did} for ${checkProfiles!.label}`,
140140+ );
120141 }
121142122143 if (checkProfiles!.reportAcct === true) {
···124145 did,
125146 `${time}: ${checkProfiles!.comment} - ${displayName} - ${description}`,
126147 );
148148+ logger.info(
149149+ `[CHECKDISPLAYNAME]: Reporting ${did} for ${checkProfiles!.label}`,
150150+ );
127151 }
128152129153 if (checkProfiles!.commentAcct === true) {
130154 createAccountComment(
131155 did,
132156 `${time}: ${checkProfiles!.comment} - ${displayName} - ${description}`,
157157+ );
158158+ logger.info(
159159+ `[CHECKDISPLAYNAME]: Commenting on ${did} for ${checkProfiles!.label}`,
133160 );
134161 }
135162 }
+116
src/monitor.ts
···11+import { describe } from "node:test";
22+import { PROFILE_CHECKS } from "./constants.js";
33+import logger from "./logger.js";
44+import { createAccountReport, createAccountLabel } from "./moderation.js";
55+66+export const monitorDescription = async (
77+ did: string,
88+ time: number,
99+ displayName: string,
1010+ description: string,
1111+) => {
1212+ // Get a list of labels
1313+ const labels: string[] = Array.from(
1414+ PROFILE_CHECKS,
1515+ (profileCheck) => profileCheck.label,
1616+ );
1717+1818+ // iterate through the labels
1919+ labels.forEach((label) => {
2020+ const checkProfiles = PROFILE_CHECKS.find(
2121+ (profileCheck) => profileCheck.label === label,
2222+ );
2323+2424+ // Check if DID is whitelisted
2525+ if (checkProfiles?.ignoredDIDs) {
2626+ if (checkProfiles.ignoredDIDs.includes(did)) {
2727+ return logger.info(`Whitelisted DID: ${did}`);
2828+ }
2929+ }
3030+3131+ if (description) {
3232+ if (checkProfiles?.description === true) {
3333+ if (checkProfiles!.check.test(description)) {
3434+ if (checkProfiles!.whitelist) {
3535+ if (checkProfiles!.whitelist.test(description)) {
3636+ logger.info(`Whitelisted phrase found.`);
3737+ return;
3838+ }
3939+ } else {
4040+ logger.info(`${checkProfiles!.label} in description for ${did}`);
4141+ }
4242+4343+ if (checkProfiles!.reportOnly === true) {
4444+ createAccountReport(
4545+ did,
4646+ `${time}: ${checkProfiles!.comment} - ${displayName} - ${description}`,
4747+ );
4848+ return;
4949+ } else {
5050+ createAccountLabel(
5151+ did,
5252+ `${checkProfiles!.label}`,
5353+ `${time}: ${checkProfiles!.comment}`,
5454+ );
5555+ }
5656+ }
5757+ }
5858+ }
5959+ });
6060+};
6161+6262+export const monitorDisplayName = async (
6363+ did: string,
6464+ time: number,
6565+ displayName: string,
6666+ description: string,
6767+) => {
6868+ // Get a list of labels
6969+ const labels: string[] = Array.from(
7070+ PROFILE_CHECKS,
7171+ (profileCheck) => profileCheck.label,
7272+ );
7373+7474+ // iterate through the labels
7575+ labels.forEach((label) => {
7676+ const checkProfiles = PROFILE_CHECKS.find(
7777+ (profileCheck) => profileCheck.label === label,
7878+ );
7979+8080+ // Check if DID is whitelisted
8181+ if (checkProfiles?.ignoredDIDs) {
8282+ if (checkProfiles.ignoredDIDs.includes(did)) {
8383+ return logger.info(`Whitelisted DID: ${did}`);
8484+ }
8585+ }
8686+8787+ if (displayName) {
8888+ if (checkProfiles?.displayName === true) {
8989+ if (checkProfiles!.check.test(displayName)) {
9090+ if (checkProfiles!.whitelist) {
9191+ if (checkProfiles!.whitelist.test(displayName)) {
9292+ logger.info(`Whitelisted phrase found.`);
9393+ return;
9494+ }
9595+ } else {
9696+ logger.info(`${checkProfiles!.label} in displayName for ${did}`);
9797+ }
9898+9999+ if (checkProfiles!.reportOnly === true) {
100100+ createAccountReport(
101101+ did,
102102+ `${time}: ${checkProfiles!.comment} - ${displayName} - ${description}`,
103103+ );
104104+ return;
105105+ } else {
106106+ createAccountLabel(
107107+ did,
108108+ `${checkProfiles!.label}`,
109109+ `${time}: ${checkProfiles!.comment}`,
110110+ );
111111+ }
112112+ }
113113+ }
114114+ }
115115+ });
116116+};
+42-3
src/utils.ts
···11+import logger from "./logger.js";
22+13/* Normalize the Unicode characters: this doesn't consistently work yet, there is something about certain bluesky strings that causes it to fail. */
24export function normalizeUnicode(text: string): string {
35 // First decompose the characters (NFD)
···3335}
34363537export async function getFinalUrl(url: string): Promise<string> {
3838+ const controller = new AbortController();
3939+ const timeoutId = setTimeout(() => controller.abort(), 10000); // 10-second timeout
4040+3641 try {
3742 const response = await fetch(url, {
3843 method: "HEAD",
3944 redirect: "follow", // This will follow redirects automatically
4545+ signal: controller.signal, // Pass the abort signal to fetch
4046 });
4141-4747+ clearTimeout(timeoutId); // Clear the timeout if fetch completes
4248 return response.url; // This will be the final URL after redirects
4349 } catch (error) {
4444- console.error("Error fetching URL:", error);
4545- throw error;
5050+ clearTimeout(timeoutId); // Clear the timeout if fetch fails
5151+ // Log the error with more specific information if it's a timeout
5252+ if (error instanceof Error && error.name === "AbortError") {
5353+ logger.warn(`Timeout fetching URL: ${url}`, error);
5454+ } else {
5555+ logger.warn(`Error fetching URL: ${url}`, error);
5656+ }
5757+ throw error; // Re-throw the error to be caught by the caller
5858+ }
5959+}
6060+6161+export async function getLanguage(profile: string): Promise<string> {
6262+ if (typeof profile !== "string" || profile === null) {
6363+ logger.warn(
6464+ "[GETLANGUAGE] getLanguage called with invalid profile data, defaulting to 'eng'.",
6565+ profile,
6666+ );
6767+ return "eng"; // Default or throw an error
6868+ }
6969+7070+ const profileText = profile.trim();
7171+7272+ if (profileText.length === 0) {
7373+ return "eng";
4674 }
7575+7676+ const lande = (await import("lande")).default;
7777+ let langsProbabilityMap = lande(profileText);
7878+7979+ // Sort by probability in descending order
8080+ langsProbabilityMap.sort(
8181+ (a: [string, number], b: [string, number]) => b[1] - a[1],
8282+ );
8383+8484+ // Return the language code with the highest probability
8585+ return langsProbabilityMap[0][0];
4786}