Compare commits

...

11 Commits

Author SHA1 Message Date
f6db2d5d7a update gitignore
All checks were successful
Main / build-and-push-docker-image (20.x) (push) Successful in 5m41s
2026-01-02 20:34:21 -05:00
df433c1951 Added cache exchange urq, fixed eslint, added prettier, upgraded yarn, added statsSite command.
All checks were successful
Main / build-and-push-docker-image (20.x) (push) Successful in 4m58s
2026-01-02 10:10:20 -05:00
c088d7c4a5 Added new commands. getGroupStats and registerGroup.
All checks were successful
Main / build-and-push-docker-image (20.x) (push) Successful in 3m3s
2025-12-16 15:25:02 -05:00
66fb9cef85 Remove cache exchange 2025-12-16 15:24:34 -05:00
789803da59 Update imports and group info extraction from context. 2025-12-16 15:24:16 -05:00
8d8b7f4c3a Added new query 2025-12-16 15:23:24 -05:00
0177aae79a Update file name 2025-12-16 15:23:12 -05:00
8076b984f5 Update groupID type for mutations. 2025-12-16 15:23:04 -05:00
2050e61706 Added mutation key to features.
All checks were successful
Main / build-and-push-docker-image (20.x) (push) Successful in 3m32s
2025-12-10 19:39:15 -05:00
465fbf96c9 fixed env variable not working.
All checks were successful
Main / build-and-push-docker-image (20.x) (push) Successful in 5m58s
2025-12-06 23:45:46 -05:00
30546606d7 Merge pull request 'graphql' (#81) from graphql into main
All checks were successful
Main / build-and-push-docker-image (20.x) (push) Successful in 6m0s
Reviewed-on: #81
2025-12-07 03:32:59 +00:00
22 changed files with 610 additions and 130 deletions

View File

@@ -9,4 +9,5 @@ SERVER_PORT=3000
BOT_ADMINS=[1] BOT_ADMINS=[1]
GROUP_IDS=- GROUP_IDS=-
GRAPHQL_URL=http://localhost:3000/api/graphql GRAPHQL_URL=http://localhost:3000/api/graphql
GRAPHQL_API_TOKEN="token-here" GRAPHQL_API_TOKEN="token-here"
GRAPHQL_MUTATION_KEY="token-here"

4
.gitignore vendored
View File

@@ -135,4 +135,6 @@ data/
# Ignore SQLite database # Ignore SQLite database
*.db *.db
*.db-journal *.db-journal
build

View File

@@ -1,9 +1,11 @@
import globals from "globals"; import globals from "globals";
import pluginJs from "@eslint/js"; import pluginJs from "@eslint/js";
import { defineConfig, globalIgnores } from "eslint/config";
import tseslint from "typescript-eslint"; import tseslint from "typescript-eslint";
/** @type {import('eslint').Linter.Config[]} */ /** @type {import('eslint').Linter.Config[]} */
export default [ export default defineConfig([
globalIgnores(["build/"]),
{ files: ["**/*.{js,mjs,cjs,ts}"] }, { files: ["**/*.{js,mjs,cjs,ts}"] },
{ languageOptions: { globals: globals.browser } }, { languageOptions: { globals: globals.browser } },
pluginJs.configs.recommended, pluginJs.configs.recommended,
@@ -22,4 +24,4 @@ export default [
] ]
} }
} }
]; ]);

View File

@@ -3,7 +3,7 @@
"type": "module", "type": "module",
"version": "3.1.0", "version": "3.1.0",
"private": true, "private": true,
"packageManager": "yarn@4.9.2", "packageManager": "yarn@4.12.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/*"
@@ -46,12 +46,15 @@
}, },
"devDependencies": { "devDependencies": {
"@antfu/eslint-config": "5.4.1", "@antfu/eslint-config": "5.4.1",
"@eslint/js": "^9.39.2",
"@types/node": "^24.5.2", "@types/node": "^24.5.2",
"eslint": "^9.36.0", "eslint": "^9.39.2",
"husky": "^9.1.7", "husky": "^9.1.7",
"lint-staged": "^16.2.3", "lint-staged": "^16.2.3",
"prettier": "3.7.4",
"tsc-watch": "^7.2.0", "tsc-watch": "^7.2.0",
"typescript": "^5.9.2" "typescript": "^5.9.3",
"typescript-eslint": "^8.51.0"
}, },
"lint-staged": { "lint-staged": {
"*.ts": "eslint" "*.ts": "eslint"

View File

@@ -1,13 +1,15 @@
import { Composer } from "grammy"; 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 urql from "#root/lib/urql.js"; import { urql } from "#root/main.js";
import increment from "#root/lib/graphql/mutations/incrimentMutation.js"; import increment from "#root/lib/graphql/mutations/incrementMutation.js";
const composer = new Composer<Context>(); const composer = new Composer<Context>();
const feature = composer.chatType(["private", "group", "supergroup"]); const feature = composer.chatType(["private", "group", "supergroup"]);
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
/** /**
* What triggers this feature and adds to the log when it has been triggered. * What triggers this feature and adds to the log when it has been triggered.
* The trigger is the command "/botInfo" * The trigger is the command "/botInfo"
@@ -16,7 +18,7 @@ feature.hears(
/^\/botInfo/, /^\/botInfo/,
logHandle("bot-info-command"), logHandle("bot-info-command"),
async (ctx: Context) => { async (ctx: Context) => {
await urql.mutation(increment, { trigger: true }); await urql.mutation(increment, { trigger: true, mutationKey });
// Checks if the context includes a message property. // Checks if the context includes a message property.
if (ctx.msg && ctx.chat && ctx.msg.from) { if (ctx.msg && ctx.chat && ctx.msg.from) {
@@ -30,7 +32,7 @@ feature.hears(
} }
await urql await urql
.mutation(increment, { command: true }) .mutation(increment, { command: true, mutationKey })
.toPromise() .toPromise()
.then(async () => { .then(async () => {
if (ctx.msg) { if (ctx.msg) {

View File

@@ -3,8 +3,8 @@ 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 from "#root/lib/twitterLinkCheck.js";
import urql from "#root/lib/urql.js"; import { urql } from "#root/main.js";
import increment from "#root/lib/graphql/mutations/incrimentMutation.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";
@@ -12,17 +12,19 @@ const composer = new Composer<Context>();
const feature = composer.chatType(["group", "supergroup"]); const feature = composer.chatType(["group", "supergroup"]);
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
/** /**
* What triggers this feature and adds to the log when it has been triggered. * What triggers this feature and adds to the log when it has been triggered.
* The trigger is anytime an embedded url is detected. * The trigger is anytime an embedded url is detected.
*/ */
feature.on("message::url", logHandle("embed-check"), async (ctx: Context) => { feature.on("message::url", logHandle("embed-check"), async (ctx: Context) => {
await urql.mutation(increment, { trigger: true }); await urql.mutation(increment, { trigger: true, mutationKey });
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; const groupID = ctx.chat?.id.toString() || "";
const groupUsername = ctx.chat?.username; const groupUsername = ctx.chat?.username || "";
let deletedLinks = 0; let deletedLinks = 0;
const username = ctx.msg.from?.username; const username = ctx.msg.from?.username;
@@ -49,7 +51,7 @@ feature.on("message::url", logHandle("embed-check"), async (ctx: Context) => {
deletedLinks++; deletedLinks++;
await urql await urql
.mutation(increment, { link: true }) .mutation(increment, { link: true, mutationKey })
.toPromise() .toPromise()
.then(async () => { .then(async () => {
if (ctx.msg) { if (ctx.msg) {
@@ -69,7 +71,7 @@ feature.on("message::url", logHandle("embed-check"), async (ctx: Context) => {
deletedLinks++; deletedLinks++;
await urql await urql
.mutation(increment, { link: true }) .mutation(increment, { link: true, mutationKey })
.toPromise() .toPromise()
.then(async () => { .then(async () => {
if (ctx.msg) { if (ctx.msg) {
@@ -89,7 +91,7 @@ feature.on("message::url", logHandle("embed-check"), async (ctx: Context) => {
deletedLinks++; deletedLinks++;
await urql await urql
.mutation(increment, { link: true }) .mutation(increment, { link: true, mutationKey })
.toPromise() .toPromise()
.then(async () => { .then(async () => {
if (ctx.msg) { if (ctx.msg) {
@@ -114,7 +116,7 @@ feature.on("message::url", logHandle("embed-check"), async (ctx: Context) => {
deletedLinks++; deletedLinks++;
await urql await urql
.mutation(increment, { link: true }) .mutation(increment, { link: true, mutationKey })
.toPromise() .toPromise()
.then(async () => { .then(async () => {
if (ctx.msg) { if (ctx.msg) {
@@ -134,7 +136,7 @@ feature.on("message::url", logHandle("embed-check"), async (ctx: Context) => {
deletedLinks++; deletedLinks++;
await urql await urql
.mutation(increment, { link: true }) .mutation(increment, { link: true, mutationKey })
.toPromise() .toPromise()
.then(async () => { .then(async () => {
if (ctx.msg) { if (ctx.msg) {
@@ -154,7 +156,7 @@ feature.on("message::url", logHandle("embed-check"), async (ctx: Context) => {
deletedLinks++; deletedLinks++;
await urql await urql
.mutation(increment, { link: true }) .mutation(increment, { link: true, mutationKey })
.toPromise() .toPromise()
.then(async () => { .then(async () => {
if (ctx.msg) { if (ctx.msg) {
@@ -169,12 +171,18 @@ feature.on("message::url", logHandle("embed-check"), async (ctx: Context) => {
if (deletedLinks) { if (deletedLinks) {
return await urql return await urql
.mutation(addGroup, { groupID, groupName, groupUsername }) .mutation(addGroup, {
groupID,
groupName,
groupUsername,
mutationKey
})
.toPromise() .toPromise()
.then(() => .then(() =>
urql.mutation(incrementGroup, { urql.mutation(incrementGroup, {
groupID, groupID,
linksDeleted: deletedLinks linksDeleted: deletedLinks,
mutationKey
}) })
); );
} }

View File

@@ -0,0 +1,96 @@
import { Composer } from "grammy";
import type { Context } from "#root/bot/context.js";
import { logHandle } from "#root/bot/helpers/logging.js";
import { urql } from "#root/main.js";
import increment from "#root/lib/graphql/mutations/incrementMutation.js";
import getGroupStats from "#root/lib/graphql/queries/getGroupStatsQuery.js";
const composer = new Composer<Context>();
const feature = composer.chatType(["group", "supergroup"]);
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
/**
* What triggers this feature and adds to the log when it has been triggered.
* The trigger is the command "/groupStats"
*/
feature.hears(
/^\/groupStats/,
logHandle("groups-stats-command"),
async (ctx: Context) => {
await urql.mutation(increment, { trigger: true, mutationKey });
// Checks if the context includes a message property.
if (ctx.msg && ctx.chat && ctx.msg.from) {
// Doesn't respond to regular users in groups. This is to prevent users spamming the command.
const chatMember = await ctx.getChatMember(ctx.msg.from.id);
if (!["creator", "administrator"].includes(chatMember.status)) {
return;
}
// Stringify the groupID
const groupID = ctx.chat?.id.toString() || "";
// Query to get group stats.
await urql
.query(getGroupStats, {
groupID
})
.toPromise()
.then(async res => {
// Replies to the message.
if (ctx.msg) {
// Check if the group has a document in the database and respond accordingly.
if (res.data.getGroupStats !== null) {
const { name, username, linksDeleted } = res.data.getGroupStats;
await ctx.reply(
`Your group is registered in the database as "${name}" ${
username.length
? `with a username of ${name}`
: `without a public username`
}\\.\n\nThe bot has successfully deleted ${linksDeleted} links from your group\\.`,
{
parse_mode: "MarkdownV2",
reply_parameters: { message_id: ctx.msg.message_id }
}
);
return await ctx.reply(
`If you need to update this information you can use the \\/registerGroup command\\.`,
{
parse_mode: "MarkdownV2",
reply_parameters: { message_id: ctx.msg.message_id }
}
);
}
// Default response //
await ctx.reply(
`Your group was not found in the database\\. You can use the \\/registerGroup command to add your group to the database\\.`,
{
parse_mode: "MarkdownV2",
reply_parameters: { message_id: ctx.msg.message_id }
}
);
return await ctx.reply(
`This is optional\\. If the bot ever removes a link in your group then the group information will be added to the database and tracked from then on\\.`,
{
parse_mode: "MarkdownV2",
reply_parameters: { message_id: ctx.msg.message_id }
}
);
}
return await urql
.mutation(increment, { command: true, mutationKey })
.toPromise();
});
}
}
);
export { composer as getGroupStats };

View File

@@ -1,19 +1,21 @@
import { Composer } from "grammy"; 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 urql from "#root/lib/urql.js"; import { urql } from "#root/main.js";
import increment from "#root/lib/graphql/mutations/incrimentMutation.js"; import increment from "#root/lib/graphql/mutations/incrementMutation.js";
const composer = new Composer<Context>(); const composer = new Composer<Context>();
const feature = composer.chatType(["group", "supergroup", "private"]); const feature = composer.chatType(["group", "supergroup", "private"]);
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
/** /**
* What triggers this feature and adds to the log when it has been triggered. * What triggers this feature and adds to the log when it has been triggered.
* The trigger is the command "/help" * The trigger is the command "/help"
*/ */
feature.hears(/^\/help/, logHandle("help"), async (ctx: Context) => { feature.hears(/^\/help/, logHandle("help"), async (ctx: Context) => {
await urql.mutation(increment, { trigger: true }); await urql.mutation(increment, { trigger: true, mutationKey });
// const GROUP_IDS = process.env.GROUP_IDS // const GROUP_IDS = process.env.GROUP_IDS
// ? process.env.GROUP_IDS.split(",") // ? process.env.GROUP_IDS.split(",")
@@ -22,7 +24,7 @@ feature.hears(/^\/help/, logHandle("help"), async (ctx: Context) => {
// Checks there is a chat and msg property in the context. // Checks there is a chat and msg property in the context.
if (ctx.chat && ctx.msg && ctx.msg.from) { if (ctx.chat && ctx.msg && ctx.msg.from) {
await urql await urql
.mutation(increment, { command: true }) .mutation(increment, { command: true, mutationKey })
.toPromise() .toPromise()
.then(async () => { .then(async () => {
if (ctx.msg && ctx.chat && ctx.msg.from) { if (ctx.msg && ctx.chat && ctx.msg.from) {

View File

@@ -2,8 +2,8 @@ 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 { metaRegex } from "#root/lib/metaLinkCheck.js"; import { metaRegex } from "#root/lib/metaLinkCheck.js";
import urql from "#root/lib/urql.js"; import { urql } from "#root/main.js";
import increment from "#root/lib/graphql/mutations/incrimentMutation.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";
@@ -11,6 +11,8 @@ const composer = new Composer<Context>();
const feature = composer.chatType(["group", "supergroup"]); const feature = composer.chatType(["group", "supergroup"]);
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
/** /**
* What triggers this feature and adds to the log when it has been triggered. * What triggers this feature and adds to the log when it has been triggered.
* The trigger uses the global Twitter regex to detect Twitter and X links within messages. * The trigger uses the global Twitter regex to detect Twitter and X links within messages.
@@ -19,7 +21,7 @@ feature.hears(
metaRegex, metaRegex,
logHandle("blacklist-detection-meta"), logHandle("blacklist-detection-meta"),
async (ctx: Context) => { async (ctx: Context) => {
await urql.mutation(increment, { trigger: true }); await urql.mutation(increment, { trigger: true, mutationKey });
if (ctx.chat && ctx.msg) { if (ctx.chat && ctx.msg) {
const username = ctx.msg.from?.username; const username = ctx.msg.from?.username;
@@ -27,25 +29,34 @@ feature.hears(
ctx.msg.delete(); ctx.msg.delete();
return await urql return await urql
.mutation(increment, { link: true }) .mutation(increment, { link: true, mutationKey })
.toPromise() .toPromise()
.then(async () => { .then(async () => {
if (ctx.msg) { if (ctx.msg && ctx.chat) {
// 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} Facebook and meta links along with with links to meta\\-owned services 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} Facebook and meta links along with with links to meta\\-owned services 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\\.`,
{ parse_mode: "MarkdownV2" } { parse_mode: "MarkdownV2" }
); );
const groupName = ctx.chat?.title; const groupName = ctx.chat?.title || "";
const groupID = ctx.chat?.id; const groupID = ctx.chat?.id.toString() || "";
const groupUsername = ctx.chat?.username; const groupUsername = ctx.chat?.username || "";
return await urql return await urql
.mutation(addGroup, { groupID, groupName, groupUsername }) .mutation(addGroup, {
groupID,
groupName,
groupUsername,
mutationKey
})
.toPromise() .toPromise()
.then(() => .then(() =>
urql.mutation(incrementGroup, { groupID, linksDeleted: 1 }) urql.mutation(incrementGroup, {
groupID,
linksDeleted: 1,
mutationKey
})
); );
} }
}); });

View File

@@ -0,0 +1,65 @@
import { Composer } from "grammy";
import type { Context } from "#root/bot/context.js";
import { logHandle } from "#root/bot/helpers/logging.js";
import { urql } from "#root/main.js";
import increment from "#root/lib/graphql/mutations/incrementMutation.js";
import addGroup from "#root/lib/graphql/mutations/addGroupMutation.js";
const composer = new Composer<Context>();
const feature = composer.chatType(["group", "supergroup"]);
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
/**
* What triggers this feature and adds to the log when it has been triggered.
* The trigger is the command "/registerGroup"
*/
feature.hears(
/^\/registerGroup/,
logHandle("register-group-command"),
async (ctx: Context) => {
await urql.mutation(increment, { trigger: true, mutationKey });
// Checks if the context includes a message property.
if (ctx.msg && ctx.chat && ctx.msg.from) {
// Doesn't respond to regular users in groups. This is to prevent users spamming the command.
const chatMember = await ctx.getChatMember(ctx.msg.from.id);
if (!["creator", "administrator"].includes(chatMember.status)) {
return;
}
const groupName = ctx.chat?.title || "";
const groupID = ctx.chat?.id.toString() || ""; // Stringify the groupID
const groupUsername = ctx.chat?.username || "";
// Add or update the group
await urql
.mutation(addGroup, {
groupID,
groupName,
groupUsername,
mutationKey
})
.toPromise()
.then(async () => {
await urql
.mutation(increment, { command: true, mutationKey })
.toPromise();
if (ctx.msg) {
// Replies to the message.
return await ctx.reply(
`Your group has been successfully added to the database\\.`,
{
parse_mode: "MarkdownV2",
reply_parameters: { message_id: ctx.msg.message_id }
}
);
}
});
}
}
);
export { composer as registerGroup };

View File

@@ -0,0 +1,53 @@
import { Composer } from "grammy";
import type { Context } from "#root/bot/context.js";
import { logHandle } from "#root/bot/helpers/logging.js";
import { urql } from "#root/main.js";
import increment from "#root/lib/graphql/mutations/incrementMutation.js";
const composer = new Composer<Context>();
const feature = composer.chatType(["private", "group", "supergroup"]);
/**
* What triggers this feature and adds to the log when it has been triggered.
* The trigger is the command "/botInfo"
*/
feature.hears(
/^\/botStats/,
logHandle("site-stats-command"),
async (ctx: Context) => {
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
await urql.mutation(increment, { trigger: true, mutationKey });
// Checks if the context includes a message property.
if (ctx.msg && ctx.chat && ctx.msg.from) {
// Doesn't respond to regular users in groups. This is to prevent users spamming the command.
if (ctx.chat.type !== "private") {
const chatMember = await ctx.getChatMember(ctx.msg.from.id);
if (!["creator", "administrator"].includes(chatMember.status)) {
return;
}
}
await urql
.mutation(increment, { command: true, mutationKey })
.toPromise()
.then(async () => {
if (ctx.msg) {
// Replies to the message.
return await ctx.reply(
`The bot statistics website can be found at https://bot\\-stats\\.werewolfkid\\.monster/\\.`,
{
parse_mode: "MarkdownV2",
reply_parameters: { message_id: ctx.msg.message_id }
}
);
}
});
}
}
);
export { composer as statsSiteCommand };

View File

@@ -2,8 +2,8 @@ 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 { twitterRegex } from "#root/lib/twitterLinkCheck.js"; import { twitterRegex } from "#root/lib/twitterLinkCheck.js";
import urql from "#root/lib/urql.js"; import { urql } from "#root/main.js";
import increment from "#root/lib/graphql/mutations/incrimentMutation.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";
@@ -11,6 +11,8 @@ const composer = new Composer<Context>();
const feature = composer.chatType(["group", "supergroup"]); const feature = composer.chatType(["group", "supergroup"]);
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
/** /**
* What triggers this feature and adds to the log when it has been triggered. * What triggers this feature and adds to the log when it has been triggered.
* The trigger uses the global Twitter regex to detect Twitter and X links within messages. * The trigger uses the global Twitter regex to detect Twitter and X links within messages.
@@ -19,7 +21,7 @@ feature.hears(
twitterRegex, twitterRegex,
logHandle("blacklist-detection-twitter"), logHandle("blacklist-detection-twitter"),
async (ctx: Context) => { async (ctx: Context) => {
await urql.mutation(increment, { trigger: true }); await urql.mutation(increment, { trigger: true, mutationKey });
if (ctx.chat && ctx.msg) { if (ctx.chat && ctx.msg) {
const username = ctx.msg.from?.username; const username = ctx.msg.from?.username;
@@ -27,25 +29,34 @@ feature.hears(
ctx.msg.delete(); ctx.msg.delete();
return await urql return await urql
.mutation(increment, { link: true }) .mutation(increment, { link: true, mutationKey })
.toPromise() .toPromise()
.then(async () => { .then(async () => {
if (ctx.msg) { if (ctx.msg && ctx.chat) {
// 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\\.`,
{ parse_mode: "MarkdownV2" } { parse_mode: "MarkdownV2" }
); );
const groupName = ctx.chat?.title; const groupName = ctx.chat?.title || "";
const groupID = ctx.chat?.id; const groupID = ctx.chat?.id.toString() || "";
const groupUsername = ctx.chat?.username; const groupUsername = ctx.chat?.username || "";
return await urql return await urql
.mutation(addGroup, { groupID, groupName, groupUsername }) .mutation(addGroup, {
groupID,
groupName,
groupUsername,
mutationKey
})
.toPromise() .toPromise()
.then(() => .then(() =>
urql.mutation(incrementGroup, { groupID, linksDeleted: 1 }) urql.mutation(incrementGroup, {
groupID,
linksDeleted: 1,
mutationKey
})
); );
} }
}); });

View File

@@ -1,21 +1,24 @@
import { Composer } from "grammy"; 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 urql from "#root/lib/urql.js"; import { urql } from "#root/main.js";
import increment from "#root/lib/graphql/mutations/incrimentMutation.js"; import increment from "#root/lib/graphql/mutations/incrementMutation.js";
const composer = new Composer<Context>(); const composer = new Composer<Context>();
const feature = composer.chatType("private"); const feature = composer.chatType("private");
const mutationKey = process.env.GRAPHQL_MUTATION_KEY || "";
/** /**
* What triggers this feature and adds to the log when it has been triggered. * What triggers this feature and adds to the log when it has been triggered.
* The trigger is the command "/start" or "start" * The trigger is the command "/start" or "start"
*/ */
feature.command("start", logHandle("command-start"), async ctx => { feature.command("start", logHandle("command-start"), async ctx => {
await urql.mutation(increment, { trigger: true }); await urql.mutation(increment, { trigger: true, mutationKey });
await urql await urql
.mutation(increment, { command: true }) .mutation(increment, { command: true, mutationKey })
.toPromise() .toPromise()
.then(async () => { .then(async () => {
if (ctx.msg) { if (ctx.msg) {

View File

@@ -5,7 +5,7 @@ import type { BotConfig } from "grammy";
import { unhandledFeature } from "#root/bot/features/unhandled.js"; import { unhandledFeature } from "#root/bot/features/unhandled.js";
import { welcomeFeature } from "#root/bot/features/welcome.js"; import { welcomeFeature } from "#root/bot/features/welcome.js";
import { errorHandler } from "#root/bot/handlers/error.js"; import { errorHandler } from "#root/bot/handlers/error.js";
import { i18n, isMultipleLocales } from "#root/bot/i18n.js"; import { i18n } from "#root/bot/i18n.js";
import { session } from "#root/bot/middlewares/session.js"; import { session } from "#root/bot/middlewares/session.js";
import { updateLogger } from "#root/bot/middlewares/update-logger.js"; import { updateLogger } from "#root/bot/middlewares/update-logger.js";
import { autoChatAction } from "@grammyjs/auto-chat-action"; import { autoChatAction } from "@grammyjs/auto-chat-action";
@@ -18,6 +18,9 @@ import { metaBlacklist } from "./features/metaBlacklist.js";
import { botInfoCommand } from "./features/botInfoCommand.js"; import { botInfoCommand } from "./features/botInfoCommand.js";
import { helpCommand } from "./features/helpCommand.js"; import { helpCommand } from "./features/helpCommand.js";
import { embedCheck } from "./features/embedCheck.js"; import { embedCheck } from "./features/embedCheck.js";
import { registerGroup } from "./features/registerBotCommand.js";
import { getGroupStats } from "./features/getGroupStatsCommand.js";
import { statsSiteCommand } from "./features/statsSiteCommand.js";
interface Dependencies { interface Dependencies {
config: Config; config: Config;
@@ -70,6 +73,9 @@ export function createBot(
// Commands // Commands
protectedBot.use(botInfoCommand); protectedBot.use(botInfoCommand);
protectedBot.use(helpCommand); protectedBot.use(helpCommand);
protectedBot.use(registerGroup);
protectedBot.use(getGroupStats);
protectedBot.use(statsSiteCommand);
// Blacklist Feature // Blacklist Feature
protectedBot.use(twitterBlacklist); protectedBot.use(twitterBlacklist);

View File

@@ -2,21 +2,19 @@ import { gql } from "@urql/core";
const addGroup = gql` const addGroup = gql`
mutation addGroup( mutation addGroup(
$groupID: BigInt $groupID: String!
$groupName: String $groupName: String!
$groupUsername: String $groupUsername: String
$mutationKey: String
) { ) {
addGroup( addGroup(
groupID: $groupID groupID: $groupID
groupName: $groupName groupName: $groupName
groupUsername: $groupUsername groupUsername: $groupUsername
mutationKey: $mutationKey
) { ) {
telegramID
name name
username username
linksDeleted
createdAt
updatedAt
} }
} }
`; `;

View File

@@ -1,14 +1,19 @@
import { gql } from "@urql/core"; import { gql } from "@urql/core";
const incrementGroup = gql` const incrementGroup = gql`
mutation incrementGroup($groupID: BigInt, $linksDeleted: Int) { mutation incrementGroup(
incrementGroup(groupID: $groupID, linksDeleted: $linksDeleted) { $groupID: String!
telegramID $linksDeleted: Int!
$mutationKey: String
) {
incrementGroup(
groupID: $groupID
linksDeleted: $linksDeleted
mutationKey: $mutationKey
) {
name name
username username
linksDeleted linksDeleted
createdAt
updatedAt
} }
} }
`; `;

View File

@@ -0,0 +1,22 @@
import { gql } from "@urql/core";
const increment = gql`
mutation increment(
$command: Boolean
$link: Boolean
$trigger: Boolean
$mutationKey: String
) {
increment(
command: $command
link: $link
trigger: $trigger
mutationKey: $mutationKey
) {
createdAt
updatedAt
}
}
`;
export default increment;

View File

@@ -1,12 +0,0 @@
import { gql } from "@urql/core";
const increment = gql`
mutation increment($command: Boolean, $link: Boolean, $trigger: Boolean) {
increment(command: $command, link: $link, trigger: $trigger) {
createdAt
updatedAt
}
}
`;
export default increment;

View File

@@ -0,0 +1,13 @@
import { gql } from "@urql/core";
const getGroupStats = gql`
query getGroupStats($groupID: String!) {
getGroupStats(groupID: $groupID) {
name
username
linksDeleted
}
}
`;
export default getGroupStats;

View File

@@ -1,13 +0,0 @@
import { Client, cacheExchange, fetchExchange } from "@urql/core";
const urql = new Client({
url: process.env.GRAPHQL_URL || "",
exchanges: [cacheExchange, fetchExchange],
fetchOptions: {
headers: {
"x-api-key": process.env?.GRAPHQL_API_TOKEN || ""
}
}
});
export default urql;

View File

@@ -8,6 +8,7 @@ import { config } from "#root/config.js";
import { logger } from "#root/logger.js"; import { logger } from "#root/logger.js";
import { createServer, createServerManager } from "#root/server/index.js"; import { createServer, createServerManager } from "#root/server/index.js";
import { run } from "@grammyjs/runner"; import { run } from "@grammyjs/runner";
import { cacheExchange, Client, fetchExchange } from "@urql/core";
async function startPolling(config: PollingConfig) { async function startPolling(config: PollingConfig) {
const bot = createBot(config.botToken, { const bot = createBot(config.botToken, {
@@ -112,3 +113,13 @@ function onShutdown(cleanUp: () => Promise<void>) {
process.on("SIGINT", handleShutdown); process.on("SIGINT", handleShutdown);
process.on("SIGTERM", handleShutdown); process.on("SIGTERM", handleShutdown);
} }
export const urql = new Client({
url: process.env.GRAPHQL_URL || "",
exchanges: [fetchExchange, cacheExchange],
fetchOptions: {
headers: {
"x-api-key": process.env?.GRAPHQL_API_TOKEN || ""
}
}
});

271
yarn.lock
View File

@@ -434,21 +434,23 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@eslint/config-array@npm:^0.21.0": "@eslint/config-array@npm:^0.21.1":
version: 0.21.0 version: 0.21.1
resolution: "@eslint/config-array@npm:0.21.0" resolution: "@eslint/config-array@npm:0.21.1"
dependencies: dependencies:
"@eslint/object-schema": "npm:^2.1.6" "@eslint/object-schema": "npm:^2.1.7"
debug: "npm:^4.3.1" debug: "npm:^4.3.1"
minimatch: "npm:^3.1.2" minimatch: "npm:^3.1.2"
checksum: 10c0/0ea801139166c4aa56465b309af512ef9b2d3c68f9198751bbc3e21894fe70f25fbf26e1b0e9fffff41857bc21bfddeee58649ae6d79aadcd747db0c5dca771f checksum: 10c0/2f657d4edd6ddcb920579b72e7a5b127865d4c3fb4dda24f11d5c4f445a93ca481aebdbd6bf3291c536f5d034458dbcbb298ee3b698bc6c9dd02900fe87eec3c
languageName: node languageName: node
linkType: hard linkType: hard
"@eslint/config-helpers@npm:^0.3.1": "@eslint/config-helpers@npm:^0.4.2":
version: 0.3.1 version: 0.4.2
resolution: "@eslint/config-helpers@npm:0.3.1" resolution: "@eslint/config-helpers@npm:0.4.2"
checksum: 10c0/f6c5b3a0b76a0d7d84cc93e310c259e6c3e0792ddd0a62c5fc0027796ffae44183432cb74b2c2b1162801ee1b1b34a6beb5d90a151632b4df7349f994146a856 dependencies:
"@eslint/core": "npm:^0.17.0"
checksum: 10c0/92efd7a527b2d17eb1a148409d71d80f9ac160b565ac73ee092252e8bf08ecd08670699f46b306b94f13d22e88ac88a612120e7847570dd7cdc72f234d50dcb4
languageName: node languageName: node
linkType: hard linkType: hard
@@ -461,6 +463,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@eslint/core@npm:^0.17.0":
version: 0.17.0
resolution: "@eslint/core@npm:0.17.0"
dependencies:
"@types/json-schema": "npm:^7.0.15"
checksum: 10c0/9a580f2246633bc752298e7440dd942ec421860d1946d0801f0423830e67887e4aeba10ab9a23d281727a978eb93d053d1922a587d502942a713607f40ed704e
languageName: node
linkType: hard
"@eslint/eslintrc@npm:^3.3.1": "@eslint/eslintrc@npm:^3.3.1":
version: 3.3.1 version: 3.3.1
resolution: "@eslint/eslintrc@npm:3.3.1" resolution: "@eslint/eslintrc@npm:3.3.1"
@@ -478,10 +489,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@eslint/js@npm:9.36.0": "@eslint/js@npm:9.39.2, @eslint/js@npm:^9.39.2":
version: 9.36.0 version: 9.39.2
resolution: "@eslint/js@npm:9.36.0" resolution: "@eslint/js@npm:9.39.2"
checksum: 10c0/e3f6fb7d6f117d79615574f7bef4f238bcfed6ece0465d28226c3a75d2b6fac9cc189121e8673562796ca8ccea2bf9861715ee5cf4a3dbef87d17811c0dac22c checksum: 10c0/00f51c52b04ac79faebfaa65a9652b2093b9c924e945479f1f3945473f78aee83cbc76c8d70bbffbf06f7024626575b16d97b66eab16182e1d0d39daff2f26f5
languageName: node languageName: node
linkType: hard linkType: hard
@@ -502,10 +513,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@eslint/object-schema@npm:^2.1.6": "@eslint/object-schema@npm:^2.1.7":
version: 2.1.6 version: 2.1.7
resolution: "@eslint/object-schema@npm:2.1.6" resolution: "@eslint/object-schema@npm:2.1.7"
checksum: 10c0/b8cdb7edea5bc5f6a96173f8d768d3554a628327af536da2fc6967a93b040f2557114d98dbcdbf389d5a7b290985ad6a9ce5babc547f36fc1fde42e674d11a56 checksum: 10c0/936b6e499853d1335803f556d526c86f5fe2259ed241bc665000e1d6353828edd913feed43120d150adb75570cae162cf000b5b0dfc9596726761c36b82f4e87
languageName: node languageName: node
linkType: hard linkType: hard
@@ -519,6 +530,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@eslint/plugin-kit@npm:^0.4.1":
version: 0.4.1
resolution: "@eslint/plugin-kit@npm:0.4.1"
dependencies:
"@eslint/core": "npm:^0.17.0"
levn: "npm:^0.4.1"
checksum: 10c0/51600f78b798f172a9915dffb295e2ffb44840d583427bc732baf12ecb963eb841b253300e657da91d890f4b323d10a1bd12934bf293e3018d8bb66fdce5217b
languageName: node
linkType: hard
"@fluent/bundle@npm:^0.17.1": "@fluent/bundle@npm:^0.17.1":
version: 0.17.1 version: 0.17.1
resolution: "@fluent/bundle@npm:0.17.1" resolution: "@fluent/bundle@npm:0.17.1"
@@ -813,6 +834,26 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/eslint-plugin@npm:8.51.0":
version: 8.51.0
resolution: "@typescript-eslint/eslint-plugin@npm:8.51.0"
dependencies:
"@eslint-community/regexpp": "npm:^4.10.0"
"@typescript-eslint/scope-manager": "npm:8.51.0"
"@typescript-eslint/type-utils": "npm:8.51.0"
"@typescript-eslint/utils": "npm:8.51.0"
"@typescript-eslint/visitor-keys": "npm:8.51.0"
ignore: "npm:^7.0.0"
natural-compare: "npm:^1.4.0"
ts-api-utils: "npm:^2.2.0"
peerDependencies:
"@typescript-eslint/parser": ^8.51.0
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10c0/3140e66a0f722338d56bf3de2b7cbb9a74a812d8da90fc61975ea029f6a401252c0824063d4c4baab9827de6f0209b34f4bbdc46e3f5fefd8fa2ff4a3980406f
languageName: node
linkType: hard
"@typescript-eslint/eslint-plugin@npm:^8.44.0": "@typescript-eslint/eslint-plugin@npm:^8.44.0":
version: 8.44.1 version: 8.44.1
resolution: "@typescript-eslint/eslint-plugin@npm:8.44.1" resolution: "@typescript-eslint/eslint-plugin@npm:8.44.1"
@@ -834,6 +875,22 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/parser@npm:8.51.0":
version: 8.51.0
resolution: "@typescript-eslint/parser@npm:8.51.0"
dependencies:
"@typescript-eslint/scope-manager": "npm:8.51.0"
"@typescript-eslint/types": "npm:8.51.0"
"@typescript-eslint/typescript-estree": "npm:8.51.0"
"@typescript-eslint/visitor-keys": "npm:8.51.0"
debug: "npm:^4.3.4"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10c0/b6aab1d82cc98a77aaae7637bf2934980104799793b3fd5b893065d930fe9b23cd6c2059d6f73fb454ea08f9e956e84fa940310d8435092a14be645a42062d94
languageName: node
linkType: hard
"@typescript-eslint/parser@npm:^8.44.0": "@typescript-eslint/parser@npm:^8.44.0":
version: 8.44.1 version: 8.44.1
resolution: "@typescript-eslint/parser@npm:8.44.1" resolution: "@typescript-eslint/parser@npm:8.44.1"
@@ -876,6 +933,19 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/project-service@npm:8.51.0":
version: 8.51.0
resolution: "@typescript-eslint/project-service@npm:8.51.0"
dependencies:
"@typescript-eslint/tsconfig-utils": "npm:^8.51.0"
"@typescript-eslint/types": "npm:^8.51.0"
debug: "npm:^4.3.4"
peerDependencies:
typescript: ">=4.8.4 <6.0.0"
checksum: 10c0/c6e6efbf79e126261e1742990b0872a34bbbe9931d99f0aabd12cb70a65a361e02d626db4b632dabee2b2c26b7e5b48344fc5a796c56438ae0788535e2bbe092
languageName: node
linkType: hard
"@typescript-eslint/scope-manager@npm:8.35.1": "@typescript-eslint/scope-manager@npm:8.35.1":
version: 8.35.1 version: 8.35.1
resolution: "@typescript-eslint/scope-manager@npm:8.35.1" resolution: "@typescript-eslint/scope-manager@npm:8.35.1"
@@ -896,6 +966,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/scope-manager@npm:8.51.0":
version: 8.51.0
resolution: "@typescript-eslint/scope-manager@npm:8.51.0"
dependencies:
"@typescript-eslint/types": "npm:8.51.0"
"@typescript-eslint/visitor-keys": "npm:8.51.0"
checksum: 10c0/dd1e75fc13e6b1119954612d9e8ad3f2d91bc37dcde85fd00e959171aaf6c716c4c265c90c5accf24b5831bd3f48510b0775e5583085b8fa2ad5c37c8980ae1a
languageName: node
linkType: hard
"@typescript-eslint/scope-manager@npm:^8.41.0": "@typescript-eslint/scope-manager@npm:^8.41.0":
version: 8.42.0 version: 8.42.0
resolution: "@typescript-eslint/scope-manager@npm:8.42.0" resolution: "@typescript-eslint/scope-manager@npm:8.42.0"
@@ -924,6 +1004,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/tsconfig-utils@npm:8.51.0, @typescript-eslint/tsconfig-utils@npm:^8.51.0":
version: 8.51.0
resolution: "@typescript-eslint/tsconfig-utils@npm:8.51.0"
peerDependencies:
typescript: ">=4.8.4 <6.0.0"
checksum: 10c0/46cab9a5342b4a8f8a1d05aaee4236c5262a540ad0bca1f0e8dad5d63ed1e634b88ce0c82a612976dab09861e21086fc995a368df0435ac43fb960e0b9e5cde2
languageName: node
linkType: hard
"@typescript-eslint/type-utils@npm:8.44.1": "@typescript-eslint/type-utils@npm:8.44.1":
version: 8.44.1 version: 8.44.1
resolution: "@typescript-eslint/type-utils@npm:8.44.1" resolution: "@typescript-eslint/type-utils@npm:8.44.1"
@@ -940,6 +1029,22 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/type-utils@npm:8.51.0":
version: 8.51.0
resolution: "@typescript-eslint/type-utils@npm:8.51.0"
dependencies:
"@typescript-eslint/types": "npm:8.51.0"
"@typescript-eslint/typescript-estree": "npm:8.51.0"
"@typescript-eslint/utils": "npm:8.51.0"
debug: "npm:^4.3.4"
ts-api-utils: "npm:^2.2.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10c0/7c17214e54bc3a4fe4551d9251ffbac52e84ca46eeae840c0f981994b7cbcc837ef32a2b6d510b02d958a8f568df355e724d9c6938a206716271a1b0c00801b7
languageName: node
linkType: hard
"@typescript-eslint/types@npm:8.35.1, @typescript-eslint/types@npm:^8.35.1": "@typescript-eslint/types@npm:8.35.1, @typescript-eslint/types@npm:^8.35.1":
version: 8.35.1 version: 8.35.1
resolution: "@typescript-eslint/types@npm:8.35.1" resolution: "@typescript-eslint/types@npm:8.35.1"
@@ -961,6 +1066,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/types@npm:8.51.0, @typescript-eslint/types@npm:^8.51.0":
version: 8.51.0
resolution: "@typescript-eslint/types@npm:8.51.0"
checksum: 10c0/eb3473d0bb71eb886438f35887b620ffadae7853b281752a40c73158aee644d136adeb82549be7d7c30f346fe888b2e979dff7e30e67b35377e8281018034529
languageName: node
linkType: hard
"@typescript-eslint/types@npm:^8.11.0": "@typescript-eslint/types@npm:^8.11.0":
version: 8.24.1 version: 8.24.1
resolution: "@typescript-eslint/types@npm:8.24.1" resolution: "@typescript-eslint/types@npm:8.24.1"
@@ -1015,6 +1127,25 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/typescript-estree@npm:8.51.0":
version: 8.51.0
resolution: "@typescript-eslint/typescript-estree@npm:8.51.0"
dependencies:
"@typescript-eslint/project-service": "npm:8.51.0"
"@typescript-eslint/tsconfig-utils": "npm:8.51.0"
"@typescript-eslint/types": "npm:8.51.0"
"@typescript-eslint/visitor-keys": "npm:8.51.0"
debug: "npm:^4.3.4"
minimatch: "npm:^9.0.4"
semver: "npm:^7.6.0"
tinyglobby: "npm:^0.2.15"
ts-api-utils: "npm:^2.2.0"
peerDependencies:
typescript: ">=4.8.4 <6.0.0"
checksum: 10c0/5386acc67298a6757681b6264c29a6b9304be7a188f11498bbaa82bb0a3095fd79394ad80d6520bdff3fa3093199f9a438246604ee3281b76f7ed574b7516854
languageName: node
linkType: hard
"@typescript-eslint/utils@npm:8.44.1": "@typescript-eslint/utils@npm:8.44.1":
version: 8.44.1 version: 8.44.1
resolution: "@typescript-eslint/utils@npm:8.44.1" resolution: "@typescript-eslint/utils@npm:8.44.1"
@@ -1030,6 +1161,21 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/utils@npm:8.51.0":
version: 8.51.0
resolution: "@typescript-eslint/utils@npm:8.51.0"
dependencies:
"@eslint-community/eslint-utils": "npm:^4.7.0"
"@typescript-eslint/scope-manager": "npm:8.51.0"
"@typescript-eslint/types": "npm:8.51.0"
"@typescript-eslint/typescript-estree": "npm:8.51.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10c0/ffb8237cfb33a1998ae2812b136d42fb65e7497f185d46097d19e43112e41b3ef59f901ba679c2e5372ad3007026f6e5add3a3de0f2e75ce6896918713fa38a8
languageName: node
linkType: hard
"@typescript-eslint/utils@npm:^8.24.1, @typescript-eslint/utils@npm:^8.34.1": "@typescript-eslint/utils@npm:^8.24.1, @typescript-eslint/utils@npm:^8.34.1":
version: 8.35.1 version: 8.35.1
resolution: "@typescript-eslint/utils@npm:8.35.1" resolution: "@typescript-eslint/utils@npm:8.35.1"
@@ -1075,6 +1221,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/visitor-keys@npm:8.51.0":
version: 8.51.0
resolution: "@typescript-eslint/visitor-keys@npm:8.51.0"
dependencies:
"@typescript-eslint/types": "npm:8.51.0"
eslint-visitor-keys: "npm:^4.2.1"
checksum: 10c0/fce5603961cf336e71095f7599157de65e3182f61cbd6cab33a43551ee91485b4e9bf6cacc1b275cf6f3503b92f8568fe2267a45c82e60e386ee73db727a26ca
languageName: node
linkType: hard
"@urql/core@npm:^6.0.1": "@urql/core@npm:^6.0.1":
version: 6.0.1 version: 6.0.1
resolution: "@urql/core@npm:6.0.1" resolution: "@urql/core@npm:6.0.1"
@@ -2156,23 +2312,22 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"eslint@npm:^9.36.0": "eslint@npm:^9.39.2":
version: 9.36.0 version: 9.39.2
resolution: "eslint@npm:9.36.0" resolution: "eslint@npm:9.39.2"
dependencies: dependencies:
"@eslint-community/eslint-utils": "npm:^4.8.0" "@eslint-community/eslint-utils": "npm:^4.8.0"
"@eslint-community/regexpp": "npm:^4.12.1" "@eslint-community/regexpp": "npm:^4.12.1"
"@eslint/config-array": "npm:^0.21.0" "@eslint/config-array": "npm:^0.21.1"
"@eslint/config-helpers": "npm:^0.3.1" "@eslint/config-helpers": "npm:^0.4.2"
"@eslint/core": "npm:^0.15.2" "@eslint/core": "npm:^0.17.0"
"@eslint/eslintrc": "npm:^3.3.1" "@eslint/eslintrc": "npm:^3.3.1"
"@eslint/js": "npm:9.36.0" "@eslint/js": "npm:9.39.2"
"@eslint/plugin-kit": "npm:^0.3.5" "@eslint/plugin-kit": "npm:^0.4.1"
"@humanfs/node": "npm:^0.16.6" "@humanfs/node": "npm:^0.16.6"
"@humanwhocodes/module-importer": "npm:^1.0.1" "@humanwhocodes/module-importer": "npm:^1.0.1"
"@humanwhocodes/retry": "npm:^0.4.2" "@humanwhocodes/retry": "npm:^0.4.2"
"@types/estree": "npm:^1.0.6" "@types/estree": "npm:^1.0.6"
"@types/json-schema": "npm:^7.0.15"
ajv: "npm:^6.12.4" ajv: "npm:^6.12.4"
chalk: "npm:^4.0.0" chalk: "npm:^4.0.0"
cross-spawn: "npm:^7.0.6" cross-spawn: "npm:^7.0.6"
@@ -2202,7 +2357,7 @@ __metadata:
optional: true optional: true
bin: bin:
eslint: bin/eslint.js eslint: bin/eslint.js
checksum: 10c0/0e2705a94847813b03f2f3c1367c0708319cbb66458250a09b2d056a088c56e079a1c1d76c44feebf51971d9ce64d010373b2a4f007cd1026fc24f95c89836df checksum: 10c0/bb88ca8fd16bb7e1ac3e13804c54d41c583214460c0faa7b3e7c574e69c5600c7122295500fb4b0c06067831111db740931e98da1340329527658e1cf80073d3
languageName: node languageName: node
linkType: hard linkType: hard
@@ -3757,6 +3912,7 @@ __metadata:
resolution: "no-twitter-bot@workspace:." resolution: "no-twitter-bot@workspace:."
dependencies: dependencies:
"@antfu/eslint-config": "npm:5.4.1" "@antfu/eslint-config": "npm:5.4.1"
"@eslint/js": "npm:^9.39.2"
"@grammyjs/auto-chat-action": "npm:0.1.1" "@grammyjs/auto-chat-action": "npm:0.1.1"
"@grammyjs/commands": "npm:1.2.0" "@grammyjs/commands": "npm:1.2.0"
"@grammyjs/hydrate": "npm:1.6.0" "@grammyjs/hydrate": "npm:1.6.0"
@@ -3768,7 +3924,7 @@ __metadata:
"@types/node": "npm:^24.5.2" "@types/node": "npm:^24.5.2"
"@urql/core": "npm:^6.0.1" "@urql/core": "npm:^6.0.1"
callback-data: "npm:1.1.1" callback-data: "npm:1.1.1"
eslint: "npm:^9.36.0" eslint: "npm:^9.39.2"
grammy: "npm:1.38.2" grammy: "npm:1.38.2"
hono: "npm:4.9.9" hono: "npm:4.9.9"
husky: "npm:^9.1.7" husky: "npm:^9.1.7"
@@ -3776,9 +3932,11 @@ __metadata:
lint-staged: "npm:^16.2.3" lint-staged: "npm:^16.2.3"
pino: "npm:9.12.0" pino: "npm:9.12.0"
pino-pretty: "npm:13.1.1" pino-pretty: "npm:13.1.1"
prettier: "npm:3.7.4"
tsc-watch: "npm:^7.2.0" tsc-watch: "npm:^7.2.0"
tsx: "npm:4.20.6" tsx: "npm:4.20.6"
typescript: "npm:^5.9.2" typescript: "npm:^5.9.3"
typescript-eslint: "npm:^8.51.0"
valibot: "npm:1.1.0" valibot: "npm:1.1.0"
languageName: unknown languageName: unknown
linkType: soft linkType: soft
@@ -4162,6 +4320,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"prettier@npm:3.7.4":
version: 3.7.4
resolution: "prettier@npm:3.7.4"
bin:
prettier: bin/prettier.cjs
checksum: 10c0/9675d2cd08eacb1faf1d1a2dbfe24bfab6a912b059fc9defdb380a408893d88213e794a40a2700bd29b140eb3172e0b07c852853f6e22f16f3374659a1a13389
languageName: node
linkType: hard
"proc-log@npm:^5.0.0": "proc-log@npm:^5.0.0":
version: 5.0.0 version: 5.0.0
resolution: "proc-log@npm:5.0.0" resolution: "proc-log@npm:5.0.0"
@@ -4741,6 +4908,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"ts-api-utils@npm:^2.2.0":
version: 2.4.0
resolution: "ts-api-utils@npm:2.4.0"
peerDependencies:
typescript: ">=4.8.4"
checksum: 10c0/ed185861aef4e7124366a3f6561113557a57504267d4d452a51e0ba516a9b6e713b56b4aeaab9fa13de9db9ab755c65c8c13a777dba9133c214632cb7b65c083
languageName: node
linkType: hard
"ts-declaration-location@npm:^1.0.6": "ts-declaration-location@npm:^1.0.6":
version: 1.0.7 version: 1.0.7
resolution: "ts-declaration-location@npm:1.0.7" resolution: "ts-declaration-location@npm:1.0.7"
@@ -4800,23 +4976,38 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"typescript@npm:^5.9.2": "typescript-eslint@npm:^8.51.0":
version: 5.9.2 version: 8.51.0
resolution: "typescript@npm:5.9.2" resolution: "typescript-eslint@npm:8.51.0"
bin: dependencies:
tsc: bin/tsc "@typescript-eslint/eslint-plugin": "npm:8.51.0"
tsserver: bin/tsserver "@typescript-eslint/parser": "npm:8.51.0"
checksum: 10c0/cd635d50f02d6cf98ed42de2f76289701c1ec587a363369255f01ed15aaf22be0813226bff3c53e99d971f9b540e0b3cc7583dbe05faded49b1b0bed2f638a18 "@typescript-eslint/typescript-estree": "npm:8.51.0"
"@typescript-eslint/utils": "npm:8.51.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10c0/ae26e783e7c2e1e2b20c278e5d743fc9aa0ea0ce3c3c06c53f7b0913617ec75d40cb8732fec85e95071e87a5a90fa0867e2c3a11d7b8fec986c55460cc652b47
languageName: node languageName: node
linkType: hard linkType: hard
"typescript@patch:typescript@npm%3A^5.9.2#optional!builtin<compat/typescript>": "typescript@npm:^5.9.3":
version: 5.9.2 version: 5.9.3
resolution: "typescript@patch:typescript@npm%3A5.9.2#optional!builtin<compat/typescript>::version=5.9.2&hash=5786d5" resolution: "typescript@npm:5.9.3"
bin: bin:
tsc: bin/tsc tsc: bin/tsc
tsserver: bin/tsserver tsserver: bin/tsserver
checksum: 10c0/34d2a8e23eb8e0d1875072064d5e1d9c102e0bdce56a10a25c0b917b8aa9001a9cf5c225df12497e99da107dc379360bc138163c66b55b95f5b105b50578067e checksum: 10c0/6bd7552ce39f97e711db5aa048f6f9995b53f1c52f7d8667c1abdc1700c68a76a308f579cd309ce6b53646deb4e9a1be7c813a93baaf0a28ccd536a30270e1c5
languageName: node
linkType: hard
"typescript@patch:typescript@npm%3A^5.9.3#optional!builtin<compat/typescript>":
version: 5.9.3
resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin<compat/typescript>::version=5.9.3&hash=5786d5"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 10c0/ad09fdf7a756814dce65bc60c1657b40d44451346858eea230e10f2e95a289d9183b6e32e5c11e95acc0ccc214b4f36289dcad4bf1886b0adb84d711d336a430
languageName: node languageName: node
linkType: hard linkType: hard