Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a3cc0f0711 | |||
| aaac8c3aa7 |
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "yarn@4.12.0",
|
"packageManager": "yarn@4.13.0",
|
||||||
"description": "This grammY powered Telegram bot is designed to delete Twitter/X links and reformat services from whitelisted groups. This one is the main bot for the LCM Telegram groups/communities.",
|
"description": "This grammY powered Telegram bot is designed to delete Twitter/X links and reformat services from whitelisted groups. This one is the main bot for the LCM Telegram groups/communities.",
|
||||||
"imports": {
|
"imports": {
|
||||||
"#root/*": "./build/src/*"
|
"#root/*": "./build/src/*"
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ feature.hears(
|
|||||||
}
|
}
|
||||||
|
|
||||||
await urql
|
await urql
|
||||||
.mutation(increment, { command: true, mutationKey })
|
.mutation(increment, { command: true, mutationKey }) // Increments the trigger count
|
||||||
.toPromise()
|
.toPromise()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
if (ctx.msg) {
|
if (ctx.msg) {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ feature.hears(
|
|||||||
}
|
}
|
||||||
|
|
||||||
await urql
|
await urql
|
||||||
.mutation(increment, { command: true, mutationKey })
|
.mutation(increment, { command: true, mutationKey }) // Increments the trigger count
|
||||||
.toPromise()
|
.toPromise()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
if (ctx.msg) {
|
if (ctx.msg) {
|
||||||
|
|||||||
@@ -2,12 +2,13 @@ import { Composer } from "grammy";
|
|||||||
import type { Context } from "#root/bot/context.js";
|
import type { Context } from "#root/bot/context.js";
|
||||||
import { logHandle } from "#root/bot/helpers/logging.js";
|
import { logHandle } from "#root/bot/helpers/logging.js";
|
||||||
import metaLinkCheck from "#root/lib/metaLinkCheck.js";
|
import metaLinkCheck from "#root/lib/metaLinkCheck.js";
|
||||||
import twitterLinkCheck from "#root/lib/twitterLinkCheck.js";
|
import twitterLinkCheck, { twitterRegex } from "#root/lib/twitterLinkCheck.js";
|
||||||
import { urql } from "#root/main.js";
|
import { urql } from "#root/main.js";
|
||||||
import increment from "#root/lib/graphql/mutations/incrementMutation.js";
|
import increment from "#root/lib/graphql/mutations/incrementMutation.js";
|
||||||
import addGroup from "#root/lib/graphql/mutations/addGroupMutation.js";
|
import addGroup from "#root/lib/graphql/mutations/addGroupMutation.js";
|
||||||
import incrementGroup from "#root/lib/graphql/mutations/incrementGroupMutation.js";
|
import incrementGroup from "#root/lib/graphql/mutations/incrementGroupMutation.js";
|
||||||
import tiktokLinkCheck from "#root/lib/tiktokLinkCheck.js";
|
import tiktokLinkCheck from "#root/lib/tiktokLinkCheck.js";
|
||||||
|
import fetchLink from "#root/lib/fetchLink.js";
|
||||||
|
|
||||||
const composer = new Composer<Context>();
|
const composer = new Composer<Context>();
|
||||||
|
|
||||||
@@ -20,8 +21,10 @@ const feature = composer.chatType(["group", "supergroup"]);
|
|||||||
feature.on("message::url", logHandle("embed-check"), async (ctx: Context) => {
|
feature.on("message::url", logHandle("embed-check"), async (ctx: Context) => {
|
||||||
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
|
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
|
||||||
|
|
||||||
|
// Increments the trigger count
|
||||||
await urql.mutation(increment, { trigger: true, mutationKey });
|
await urql.mutation(increment, { trigger: true, mutationKey });
|
||||||
|
|
||||||
|
// Checks there is a chat and msg property in the context.
|
||||||
if (ctx.chat && ctx.msg) {
|
if (ctx.chat && ctx.msg) {
|
||||||
const groupName = ctx.chat?.title || "";
|
const groupName = ctx.chat?.title || "";
|
||||||
const groupID = ctx.chat?.id.toString() || "";
|
const groupID = ctx.chat?.id.toString() || "";
|
||||||
@@ -41,24 +44,25 @@ feature.on("message::url", logHandle("embed-check"), async (ctx: Context) => {
|
|||||||
|
|
||||||
// If the all embeds array isn't empty filter through them to check for every type of disallowed link.
|
// If the all embeds array isn't empty filter through them to check for every type of disallowed link.
|
||||||
if (allEmbeds.length) {
|
if (allEmbeds.length) {
|
||||||
const metaLinks = allEmbeds.filter(({ url }) => metaLinkCheck(url));
|
detectedLinks +=
|
||||||
const twitterLinks = allEmbeds.filter(({ url }) => twitterLinkCheck(url));
|
allEmbeds.filter(({ url }) => metaLinkCheck(url)).length || 0;
|
||||||
const tiktokLinks = allEmbeds.filter(({ url }) => tiktokLinkCheck(url));
|
|
||||||
|
|
||||||
// Handles Meta & Facebook Links
|
detectedLinks += (
|
||||||
if (metaLinks.length) {
|
await Promise.all(
|
||||||
detectedLinks += metaLinks.length;
|
allEmbeds.map(async ({ url }) => {
|
||||||
|
// Checks for a false positive.
|
||||||
|
if (twitterLinkCheck(url)) {
|
||||||
|
return await fetchLink(url, twitterRegex);
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).reduce((acc, curr) => {
|
||||||
|
return (acc += curr ? 1 : 0);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
// Handles Twitter/X links.
|
detectedLinks +=
|
||||||
if (twitterLinks.length) {
|
allEmbeds.filter(({ url }) => tiktokLinkCheck(url)).length || 0;
|
||||||
detectedLinks += twitterLinks.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handles TikTok links.
|
|
||||||
if (tiktokLinks.length) {
|
|
||||||
detectedLinks += tiktokLinks.length;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (detectedLinks) {
|
if (detectedLinks) {
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ feature.hears(/^\/help/, logHandle("help"), async (ctx: Context) => {
|
|||||||
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
|
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
|
||||||
const statsSite = process.env.STATS_SITE || "";
|
const statsSite = process.env.STATS_SITE || "";
|
||||||
|
|
||||||
|
// Increments the trigger count
|
||||||
await urql.mutation(increment, { trigger: true, mutationKey });
|
await urql.mutation(increment, { trigger: true, mutationKey });
|
||||||
|
// Retrieve the command list
|
||||||
const commandsListArr = await getCommandsList(`${statsSite}api/bot`, apiKey);
|
const commandsListArr = await getCommandsList(`${statsSite}api/bot`, apiKey);
|
||||||
|
|
||||||
// const GROUP_IDS = process.env.GROUP_IDS
|
// const GROUP_IDS = process.env.GROUP_IDS
|
||||||
@@ -67,6 +69,7 @@ feature.hears(/^\/help/, logHandle("help"), async (ctx: Context) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a readable command list string.
|
||||||
const commandsListStr = commandsListArr.reduce((prev, curr) => {
|
const commandsListStr = commandsListArr.reduce((prev, curr) => {
|
||||||
const { command, description, groups } = curr;
|
const { command, description, groups } = curr;
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,10 @@ feature.hears(
|
|||||||
async (ctx: Context) => {
|
async (ctx: Context) => {
|
||||||
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
|
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
|
||||||
|
|
||||||
|
// Increments the trigger count
|
||||||
await urql.mutation(increment, { trigger: true, mutationKey });
|
await urql.mutation(increment, { trigger: true, mutationKey });
|
||||||
|
|
||||||
|
// Checks there is a chat and msg property in the context.
|
||||||
if (ctx.chat && ctx.msg) {
|
if (ctx.chat && ctx.msg) {
|
||||||
const username = ctx.msg.from?.username;
|
const username = ctx.msg.from?.username;
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ feature.hears(
|
|||||||
async (ctx: Context) => {
|
async (ctx: Context) => {
|
||||||
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
|
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
|
||||||
|
|
||||||
|
// Increments the trigger count
|
||||||
await urql.mutation(increment, { trigger: true, mutationKey });
|
await urql.mutation(increment, { trigger: true, mutationKey });
|
||||||
|
|
||||||
// Checks if the context includes a message property.
|
// Checks if the context includes a message property.
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ feature.hears(
|
|||||||
async (ctx: Context) => {
|
async (ctx: Context) => {
|
||||||
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
|
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
|
||||||
|
|
||||||
|
// Increments the trigger count
|
||||||
await urql.mutation(increment, { trigger: true, mutationKey });
|
await urql.mutation(increment, { trigger: true, mutationKey });
|
||||||
|
|
||||||
if (ctx.chat && ctx.msg) {
|
if (ctx.chat && ctx.msg) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { urql } from "#root/main.js";
|
|||||||
import increment from "#root/lib/graphql/mutations/incrementMutation.js";
|
import increment from "#root/lib/graphql/mutations/incrementMutation.js";
|
||||||
import addGroup from "#root/lib/graphql/mutations/addGroupMutation.js";
|
import addGroup from "#root/lib/graphql/mutations/addGroupMutation.js";
|
||||||
import incrementGroup from "#root/lib/graphql/mutations/incrementGroupMutation.js";
|
import incrementGroup from "#root/lib/graphql/mutations/incrementGroupMutation.js";
|
||||||
|
import fetchLink from "#root/lib/fetchLink.js";
|
||||||
|
|
||||||
const composer = new Composer<Context>();
|
const composer = new Composer<Context>();
|
||||||
|
|
||||||
@@ -21,8 +22,10 @@ feature.hears(
|
|||||||
async (ctx: Context) => {
|
async (ctx: Context) => {
|
||||||
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
|
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
|
||||||
|
|
||||||
|
// Increments the trigger count
|
||||||
await urql.mutation(increment, { trigger: true, mutationKey });
|
await urql.mutation(increment, { trigger: true, mutationKey });
|
||||||
|
|
||||||
|
// Checks there is a chat and msg property in the context.
|
||||||
if (ctx.chat && ctx.msg) {
|
if (ctx.chat && ctx.msg) {
|
||||||
const username = ctx.msg.from?.username;
|
const username = ctx.msg.from?.username;
|
||||||
|
|
||||||
@@ -30,7 +33,11 @@ feature.hears(
|
|||||||
.mutation(increment, { link: 1, mutationKey })
|
.mutation(increment, { link: 1, mutationKey })
|
||||||
.toPromise()
|
.toPromise()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
if (ctx.msg && ctx.chat) {
|
if (ctx.msg && ctx.chat && ctx.msg.text) {
|
||||||
|
// Checks for a false positive
|
||||||
|
const truePositive = await fetchLink(ctx.msg.text, twitterRegex);
|
||||||
|
if (!truePositive) return;
|
||||||
|
|
||||||
// Replies to the user informing them of the action.
|
// Replies to the user informing them of the action.
|
||||||
await ctx.reply(
|
await ctx.reply(
|
||||||
`@${username} Twitter and X links along with reformatting services for Twitter posts are not allowed here\\. Please consider sharing the media directly or from other social media sources or websites\\. No administration action was taken against you other than the message being deleted\\.`,
|
`@${username} Twitter and X links along with reformatting services for Twitter posts are not allowed here\\. Please consider sharing the media directly or from other social media sources or websites\\. No administration action was taken against you other than the message being deleted\\.`,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const feature = composer.chatType("private");
|
|||||||
feature.command("start", logHandle("command-start"), async ctx => {
|
feature.command("start", logHandle("command-start"), async ctx => {
|
||||||
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
|
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
|
||||||
|
|
||||||
|
// Increments the trigger count
|
||||||
await urql.mutation(increment, { trigger: true, mutationKey });
|
await urql.mutation(increment, { trigger: true, mutationKey });
|
||||||
|
|
||||||
await urql
|
await urql
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* This function will fetch a url to follow all redirects and compare the base url
|
||||||
|
* with the RegEx to validate false and true positives.
|
||||||
|
*
|
||||||
|
* @param message The message that originally triggered the provided RegEx
|
||||||
|
* @param banRegEx RegEx to check against
|
||||||
|
* @returns @type boolean
|
||||||
|
*/
|
||||||
|
|
||||||
|
const linkChecker = async (message: string, banRegEx: RegExp) => {
|
||||||
|
const detectedLinks = message.split(" ").map(subStr => {
|
||||||
|
if (banRegEx.test(subStr)) {
|
||||||
|
return subStr;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!detectedLinks || detectedLinks.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolvedURLs = detectedLinks.map(async string => {
|
||||||
|
if (string === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url =
|
||||||
|
string.startsWith("http://") || string.startsWith("https://")
|
||||||
|
? string
|
||||||
|
: `https://${string}`;
|
||||||
|
|
||||||
|
const res = await fetch(url, {
|
||||||
|
headers: {
|
||||||
|
"user-agent":
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const resolvedURL = new URL(res.url);
|
||||||
|
const baseURL = resolvedURL.hostname.split(".").slice(-2).join(".");
|
||||||
|
|
||||||
|
if (/^x\.com/i.test(baseURL)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (await Promise.all(resolvedURLs)).includes(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default linkChecker;
|
||||||
@@ -6,6 +6,13 @@ export interface Commands {
|
|||||||
groups: boolean;
|
groups: boolean;
|
||||||
private: boolean;
|
private: boolean;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Fetched a list of commands with a provided url and API key.
|
||||||
|
* @param url The url to query.
|
||||||
|
* @interface Commands
|
||||||
|
* @param apiKey The api for the url.
|
||||||
|
* @returns Array of Commands object.
|
||||||
|
*/
|
||||||
|
|
||||||
const fetchCommandsList = async (
|
const fetchCommandsList = async (
|
||||||
url: string,
|
url: string,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const metaRegex =
|
|||||||
* This function will check if a url matches Meta services links using regex.
|
* This function will check if a url matches Meta services links using regex.
|
||||||
*
|
*
|
||||||
* @param linkUrl representing a suspected url on the banlist
|
* @param linkUrl representing a suspected url on the banlist
|
||||||
* @returns flag
|
* @returns @type boolean
|
||||||
*/
|
*/
|
||||||
const metaLinkCheck = (linkUrl: string): boolean => {
|
const metaLinkCheck = (linkUrl: string): boolean => {
|
||||||
let flag = false;
|
let flag = false;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const tiktokRegex = /(tiktok\.com)/gi;
|
|||||||
* This function will check if a url matches TikTok services links using regex.
|
* This function will check if a url matches TikTok services links using regex.
|
||||||
*
|
*
|
||||||
* @param linkUrl representing a suspected url on the banlist
|
* @param linkUrl representing a suspected url on the banlist
|
||||||
* @return flag
|
* @returns @type boolean
|
||||||
*/
|
*/
|
||||||
const tiktokLinkCheck = (linkUrl: string): boolean => {
|
const tiktokLinkCheck = (linkUrl: string): boolean => {
|
||||||
let flag = false;
|
let flag = false;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const twitterRegex = /(x\.com|twitter\.com)/gi;
|
|||||||
* This function will check if a url matches Twitter/X services links using regex.
|
* This function will check if a url matches Twitter/X services links using regex.
|
||||||
*
|
*
|
||||||
* @param linkUrl representing a suspected url on the banlist
|
* @param linkUrl representing a suspected url on the banlist
|
||||||
* @return flag
|
* @returns @type boolean
|
||||||
*/
|
*/
|
||||||
const twitterLinkCheck = (linkUrl: string): boolean => {
|
const twitterLinkCheck = (linkUrl: string): boolean => {
|
||||||
let flag = false;
|
let flag = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user