Skip to content

Commit

Permalink
new group commands
Browse files Browse the repository at this point in the history
  • Loading branch information
pytour committed Jan 17, 2021
1 parent 9a801d0 commit 0f9edce
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 105 deletions.
3 changes: 2 additions & 1 deletion bot/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ MongoClient.connect(process.env.MONGO_URL, {
sessionName: "session",
});
bot.launch();
bot.telegram.getMe().then((info) => console.log("Bot started", info));
let info = await bot.telegram.getMe();
console.log("Bot started", info);
await updateHandler(bot);
notification(bot);
});
Expand Down
3 changes: 2 additions & 1 deletion bot/src/db/models/groupModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ const groupSchema = mongoose.Schema(
title: { type: String },
username: { type: String },
status: { type: String },
mode: { type: String, default: 'captcha_new_users' },
cleanMode: { type: Boolean, default: false },
admins: { type: Object },
},
{ timestamps: true }
);

module.exports = mongoose.model("Group", groupSchema);
230 changes: 151 additions & 79 deletions bot/src/handlers/group/textMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,13 @@ const Extra = require("telegraf/extra");
const { nanoid } = require("nanoid");
const { saveCaptcha } = require("../../db/controllers/captcha");
const restrictUser = require("../../utils/restrictUser");
const { getGroup, updateGroup } = require("../../db/controllers/group");
const {
getGroup,
updateGroup,
saveGroup,
} = require("../../db/controllers/group");
const { getSession } = require("../../db/controllers/session");

const updAdmins = async (ctx, group) => {
// Update admins for group in database
try {
const admins = await ctx.telegram.getChatAdministrators(ctx.chat.id);
await new Promise((r) => setTimeout(r, 2000));
let updateValues = { $set: { admins: admins } };
await updateGroup(group._id, updateValues);
await ctx.reply("Ok, i remember admins of this group 🧠 🤓");
} catch (error) {
console.log(error);
await ctx.reply("🥺 Error" + error);
}
};

/**
* Handle text messages in any group / supergroup
* @param {Object} ctx context
Expand All @@ -28,21 +18,48 @@ module.exports = async (ctx) => {

// If group admins, then leave
const groupId = ctx.chat.id;
const text = ctx.message.text;
let group = await getGroup(groupId);
if (group.admins) {
// If user in admins then return
console.log("Checking user in group admins...");
for (const admin of group.admins) {
if (admin.user && admin.user.id === userId) {
console.log("User is admin");

if (ctx.message.text === "/updateadmins") {
// todo: add admin commands
// /help - list of commands
// /captcha_each_message - restrict users with Captcha on each message in group
// /captcha_new_users - restrict only new paticipants with Captcha
// /clean_mode_on - delete all messages from users
// /clean_mode_off - do not delete all messages from users
if (text === "/updateadmins" || text === `/updateadmins@${ctx.me}`) {
// Update admins for group in database
await updAdmins(ctx, group);
} else if (ctx.message.text === "/captcha") {
} else if (text === "/captcha" || text === `/captcha@${ctx.me}`) {
// Captcha link
const link = `https://t.me/${ctx.me}?start=${groupId}`;
await ctx.reply("Captcha link for this group: " + link);
} else if (text === "/clean_mode" || text === `/clean_mode@${ctx.me}`) {
await cleanMode(ctx, group);
} else if (text === "/help" || text === `/help@${ctx.me}`) {
let msg = `⚙️ Group admin commands:\n
/updateadmins - 👥 update admins list\n
/captcha - 🔗 🖼 captcha link\n
/captcha_each_message - 🚧 💬 restrict users with Captcha on each message in group\n
/captcha_new_users - 🚧 👤 restrict only new paticipants with Captcha\n
/clean_mode - 🧹 💬 toggle command Bot delete all new messages from users
`;
await ctx.reply(msg);
} else if (
text === "/captcha_each_message" ||
text === `/captcha_each_message@${ctx.me}`
) {
await captchaMode(ctx, group, "captcha_each_message");
} else if (
text === "/captcha_new_users" ||
text === `/captcha_new_users@${ctx.me}`
) {
await captchaMode(ctx, group, "captcha_new_users");
}
return;
}
Expand All @@ -55,77 +72,98 @@ module.exports = async (ctx) => {
return await deleteLastMessage(groupId, message_id, ctx);
}

// Set restrictions for each message from regular user in group
try {
await restrictUser(ctx, userId);
} catch (error) {
if (
error.message ===
"400: Bad Request: not enough rights to restrict/unrestrict chat member"
) {
return await ctx.reply(
"Bot doesn't have enough rights to restrict/unrestrict chat member.\nPlease add me to the admins."
);
} else if (
error.message ===
"400: Bad Request: method is available only for supergroups"
) {
return await ctx.reply(
"Bot is available only for supergroups.\nPlease upgrade this group to supergroup."
);
if (group.mode === "captcha_each_message") {
// Set restrictions for each message from regular user in group
try {
await restrictUser(ctx, userId);
} catch (error) {
if (
error.message ===
"400: Bad Request: not enough rights to restrict/unrestrict chat member"
) {
return await ctx.reply(
"Bot doesn't have enough rights to restrict/unrestrict chat member.\nPlease add me to the admins."
);
} else if (
error.message ===
"400: Bad Request: method is available only for supergroups"
) {
return await ctx.reply(
"Bot is available only for supergroups.\nPlease upgrade this group to supergroup."
);
}
console.log("Resrict error:", error.message);
}
console.log("Resrict error:", error.message);
}

// Check if user already exists in Sessions collection
// If exists, then send captcha message in private with user conversation
// Note: session key = "userId:userId"
let session;
let key = `${userId}:${userId}`;
try {
session = await getSession(key);
console.log('Session key', session.key);
} catch (error) {
// User dont start conversation with bot yet
console.log("User dont start conversation with bot yet.");
}
if (session && session.data) {
// Create captcha and send in pm
const id = nanoid(6);
const captcha = {
id: id,
groupId: groupId,
from: ctx.message.from,
status: "ACTIVE",
};
// Check if user already exists in Sessions collection
// If exists, then send captcha message in private with user conversation
// Note: session key = "userId:userId"
let session;
let key = `${userId}:${userId}`;
try {
await saveCaptcha(captcha);
console.log("Captcha saved id", id);
session = await getSession(key);
console.log("Session key", session.key);
} catch (error) {
console.log(error);
// User dont start conversation with bot yet
console.log("User dont start conversation with bot yet.");
}
if (session && session.data) {
// Create captcha and send in pm
const id = nanoid(6);
const captcha = {
id: id,
groupId: groupId,
from: ctx.message.from,
status: "ACTIVE",
};
try {
await saveCaptcha(captcha);
console.log("Captcha saved id", id);
} catch (error) {
console.log(error);
}

const appLink =
process.env.NODE_ENV === "production"
? `${process.env.WEBAPP_URI}/captcha/${id}`
: `http://localhost:3000/captcha/${id}`; // DeepLink for dev mode tests
const appLink =
process.env.NODE_ENV === "production"
? `${process.env.WEBAPP_URI}/captcha/${id}`
: `http://127.0.0.1:3000/captcha/${id}`; // DeepLink for dev mode tests

try {
await ctx.telegram.sendMessage(
userId,
`Your captcha:\n\n ${appLink}`,
Extra.markup((m) =>
m.inlineKeyboard([[m.urlButton("Captcha", appLink)]])
)
);
} catch (error) {
console.log(error);
try {
await ctx.telegram.sendMessage(
userId,
`Your captcha:`,
Extra.markup((m) =>
m.inlineKeyboard([[m.urlButton("Captcha", appLink)]])
)
);
} catch (error) {
console.log(error);
}
}

// If group have clean mode then delete message
if (group.cleanMode) {
if (process.env.NODE_ENV === "development")
console.log("Clean mode active");
// Delete last user message after 10 sec
const message_id = ctx.message.message_id;
await deleteLastMessage(groupId, message_id, ctx);
}
}
};

// Delete last user message after 10 sec
const message_id = ctx.message.message_id;
await deleteLastMessage(groupId, message_id, ctx);
const updAdmins = async (ctx, group) => {
// Update admins for group in database
try {
const admins = await ctx.telegram.getChatAdministrators(ctx.chat.id);
await new Promise((r) => setTimeout(r, 2000));
let updateValues = { $set: { admins: admins } };
await updateGroup(group._id, updateValues);
await ctx.reply("Ok, i remember admins of this group 🧠 🤓");
} catch (error) {
console.log(error);
await ctx.reply("🥺 Error" + error);
}
};

/**
Expand Down Expand Up @@ -153,3 +191,37 @@ const deleteMsg = async (chatId, message_id, ctx) => {
throw error.message;
}
};

/**
* Toggle clean mode in groups
* @param {Object} ctx
* @param {String} groupId
*/
const cleanMode = async (ctx, group) => {
let msg = "";
console.log("Group:", group);
try {
group.cleanMode = !group.cleanMode;
console.log("Updated clean mode:", group.cleanMode, !group.cleanMode);
//await saveGroup(group);
await updateGroup(group._id, { $set: { cleanMode: group.cleanMode } });
msg = group.cleanMode ? "Clean mode active" : "Clean mode disabled";
} catch (error) {
console.log("Error:", error);
msg = error;
}
await ctx.reply(msg);
};

const captchaMode = async (ctx, group, mode) => {
let msg = "";
console.log("Upd captcha mode for group:", group, mode);
try {
await updateGroup(group._id, { $set: { mode: mode } });
msg = "🚧 captcha mode " + mode;
} catch (error) {
console.log("Error:", error);
msg = error;
}
await ctx.reply(msg);
};
9 changes: 6 additions & 3 deletions bot/src/handlers/groupChat.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@ module.exports = async (ctx) => {
// Only messages
if (!ctx.message) return;

// Save meesage date
// Save mesage date
ctx.session.lastMsgDate = new Date().toJSON();

// If chat admin skip restrictions

if (ctx.message.new_chat_participant) {
await newChatParticipant(ctx);
} else if (ctx.message.left_chat_participant) {
Expand All @@ -25,3 +23,8 @@ module.exports = async (ctx) => {
await textMessage(ctx);
}
};

// todo: add admin commands
// /help - list of commands
// /captcha_each_message - restrict users with Captcha on each message in group
// /captcha_new_users - restrict only new paticipants with Captcha
3 changes: 1 addition & 2 deletions bot/src/handlers/private/commands/about.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
module.exports = async (ctx) => {
let msg = `The bot will block users from messaging in chat until they fill out a captcha.
How it works: Watch this short video - https://youtu.be/GDSLRQaHfic`;
let msg = `The bot will block users from messaging in chat until they fill out a captcha.`;
await ctx.reply(msg);
};
2 changes: 1 addition & 1 deletion bot/src/handlers/private/commands/faq.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = async (ctx) => {
let msg = `FAQ`;
let msg = '';
msg += `\n\nHow it works? Watch this short video - https://youtu.be/GDSLRQaHfic`;
await ctx.reply(msg);
};
2 changes: 1 addition & 1 deletion bot/src/handlers/private/commands/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ module.exports = async (ctx) => {
const appLink =
process.env.NODE_ENV === "production"
? `${process.env.WEBAPP_URI}/captcha/${id}`
: `http://localhost:3000/captcha/${id}`; // DeepLink for dev mode tests
: `http://127.0.0.1:3000/captcha/${id}`; // DeepLink for dev mode tests
await ctx.reply(
`Your captcha:\n\n ${appLink}`,
Extra.markup((m) => m.inlineKeyboard([[m.urlButton("Captcha", appLink)]]))
Expand Down
8 changes: 0 additions & 8 deletions docker-compose-ssl.yml.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,6 @@ services:
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: password
mongo-express:
image: mongo-express
restart: always
ports:
- 8081:8081
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: root
ME_CONFIG_MONGODB_ADMINPASSWORD: password
rabbit1:
image: "rabbitmq:3-management"
hostname: "rabbit1"
Expand Down
8 changes: 0 additions & 8 deletions docker-compose.yml.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,6 @@ services:
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: password
mongo-express:
image: mongo-express
restart: always
ports:
- 8081:8081
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: root
ME_CONFIG_MONGODB_ADMINPASSWORD: password
rabbit1:
image: "rabbitmq:3-management"
hostname: "rabbit1"
Expand Down
11 changes: 10 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
# hCaptcha Honk Bot
<h1 align="center">Node.js hCaptcha Honk Bot</h1>

<div align="center">

Stop Telegram user-bots with hCaptcha

[![Bot](https://img.shields.io/badge/Bot-HonkhCaptchaBot-00aced.svg?style=flat-square&logo=telegram)](https://telegram.me/HonkhCaptchaBot)
[![Faucet](https://img.shields.io/badge/💬%20Faucet-Telegram%20Group-blue.svg?style=flat-square)](https://telegram.me/Honkfaucet)

</div>

The bot we [forked](https://github.com/KeithPatrick5/telegram-bot) will block users from messaging in chat until they fill out a captcha. Each claim is met with a new restriction requiring users to fill out captcha, thus eliminating bots from claiming tokens.

Expand Down

0 comments on commit 0f9edce

Please sign in to comment.