|
| 1 | +import { Context } from 'grammy'; |
| 2 | +import { ServiceProvider } from '../database/ServiceProvider'; |
| 3 | +import { BotReply } from '../../utils/chat/BotReply'; |
| 4 | +import { ChatInfo } from '../../utils/chat/ChatInfo'; |
| 5 | +export class MessagesService { |
| 6 | + private _reply: BotReply; |
| 7 | + private _chatInfo: ChatInfo; |
| 8 | + constructor(private _ctx: Context) { |
| 9 | + this._reply = new BotReply(_ctx); |
| 10 | + this._chatInfo = new ChatInfo(_ctx); |
| 11 | + } |
| 12 | + async isCode() { |
| 13 | + const entities = this._ctx.message!.entities; |
| 14 | + entities?.forEach((e) => { |
| 15 | + if (e.type === 'pre' && e.language) { |
| 16 | + this._reply.textReply(`Your code is garbage.\n\n- Linus Torvalds`); |
| 17 | + } |
| 18 | + }); |
| 19 | + } |
| 20 | + async isNewUser() { |
| 21 | + if (this._ctx.message?.new_chat_members?.length! > 0) { |
| 22 | + const users = this._ctx.message!.new_chat_members!; |
| 23 | + const chat = this._ctx.chat; |
| 24 | + const service = ServiceProvider.getInstance(); |
| 25 | + const groupService = await service.getGroupService(); |
| 26 | + let group = await groupService.getByGroupId(chat!.id); |
| 27 | + if (!group) { |
| 28 | + group = await groupService.save(this._ctx); |
| 29 | + } |
| 30 | + let welcome_message = group.welcome_message; |
| 31 | + if (!welcome_message) { |
| 32 | + welcome_message = `Welcome to ${this._ctx.chat!.title}\n\nWe are so glad to have you here!. If you have any questions, don't hesitate to ask! 💬\n\nEnjoy your time here!`; |
| 33 | + } |
| 34 | + for (const user of users) { |
| 35 | + if (user.id !== this._ctx.me?.id) { |
| 36 | + await groupService.updateMembers(this._ctx.chat!.id!, this._ctx.from?.id!); |
| 37 | + await this._reply.send(welcome_message); |
| 38 | + } |
| 39 | + } |
| 40 | + } |
| 41 | + } |
| 42 | + Spam() {} |
| 43 | + async userIsLeftGroup() { |
| 44 | + if (this._ctx.message?.left_chat_member) { |
| 45 | + const user = this._ctx.message.left_chat_member!; |
| 46 | + |
| 47 | + if (user.id !== this._ctx.me?.id) { |
| 48 | + // Notify the group about the member leaving |
| 49 | + const username = user.username ? `@${user.username}` : user.first_name; |
| 50 | + await this._ctx.reply(`${username} has left the chat.`); |
| 51 | + } |
| 52 | + } |
| 53 | + } |
| 54 | + |
| 55 | + async checkAndHandleBlacklistedWords() { |
| 56 | + const groupId = this._ctx.chat?.id; |
| 57 | + if (!groupId) { |
| 58 | + return; |
| 59 | + } |
| 60 | + const messageText = this._ctx.message?.text; |
| 61 | + const messageId = this._ctx.message?.message_id; |
| 62 | + const userId = this._ctx.message?.from?.id; |
| 63 | + |
| 64 | + if (!messageText || !messageId || !userId) { |
| 65 | + return; |
| 66 | + } |
| 67 | + const service = ServiceProvider.getInstance(); |
| 68 | + const [groupService] = await Promise.all([service.getGroupService()]); |
| 69 | + let group = await groupService.getByGroupId(groupId); |
| 70 | + if (!group) { |
| 71 | + group = await groupService.save(this._ctx); |
| 72 | + } |
| 73 | + const approvedUsers = group.approved_users.map(Number); |
| 74 | + const isAdmin = await this._chatInfo.isAdmin(this._ctx, userId); |
| 75 | + // Allow approved users and admins to bypass blacklist checks |
| 76 | + if (approvedUsers.includes(+userId) || isAdmin) { |
| 77 | + return; |
| 78 | + } |
| 79 | + const blacklist = group.black_list; |
| 80 | + const isForbidden = blacklist.some((word) => { |
| 81 | + const regex = new RegExp(`(^|\\s)${word}($|\\s|[.,!?;])`, 'i'); // \b is a word boundary, 'i' is for case-insensitive matching |
| 82 | + return regex.test(messageText); |
| 83 | + }); |
| 84 | + if (isForbidden) { |
| 85 | + await this._ctx.restrictChatMember( |
| 86 | + userId, |
| 87 | + { |
| 88 | + can_send_messages: false, |
| 89 | + can_send_polls: false, |
| 90 | + can_send_other_messages: false, |
| 91 | + can_add_web_page_previews: false, |
| 92 | + can_send_photos: false, |
| 93 | + can_send_audios: false, |
| 94 | + }, |
| 95 | + { until_date: Math.floor(Date.now() / 1000) + 3600 } |
| 96 | + ); |
| 97 | + await this._ctx.deleteMessage(); |
| 98 | + const warningMessage = await this.generateBlacklistWarningMessage(this._ctx, blacklist); |
| 99 | + await this._reply.send(warningMessage); |
| 100 | + } |
| 101 | + } |
| 102 | + |
| 103 | + private async generateBlacklistWarningMessage(ctx: Context, blacklist: string[]): Promise<string> { |
| 104 | + const info_user = ctx.message?.from?.username || ctx.message?.from.first_name; |
| 105 | + const messageText = this._ctx.message?.text; |
| 106 | + // Function to mask forbidden words and return the modified message |
| 107 | + const maskForbiddenWords = (message: string, blacklist: string[]): string => { |
| 108 | + let maskedMessage = message; |
| 109 | + blacklist.forEach((word) => { |
| 110 | + const regex = new RegExp(`(^|\\s)${word}($|\\s|[.,!?;])`, 'i'); |
| 111 | + maskedMessage = maskedMessage.replace(regex, (match) => { |
| 112 | + return match.replace(new RegExp(word, 'i'), '**'); |
| 113 | + }); |
| 114 | + }); |
| 115 | + return maskedMessage; |
| 116 | + }; |
| 117 | + |
| 118 | + // Function to check message for forbidden words and return line number if applicable |
| 119 | + const getLineNumber = (message: string, blacklist: string[]): number | null => { |
| 120 | + const lines = message.split('\n'); // Split message into lines |
| 121 | + for (let i = 0; i < lines.length; i++) { |
| 122 | + const line = lines[i]; |
| 123 | + const isForbidden = blacklist.some((word) => { |
| 124 | + const regex = new RegExp(`(^|\\s)${word}($|\\s|[.,!?;])`, 'i'); |
| 125 | + return regex.test(line); |
| 126 | + }); |
| 127 | + if (isForbidden) { |
| 128 | + return i + 1; // Return the 1-based index of the line |
| 129 | + } |
| 130 | + } |
| 131 | + return null; // Return null if no forbidden words are found |
| 132 | + }; |
| 133 | + // Sending the warning with the line number and masked message |
| 134 | + const lineNumber = getLineNumber(messageText!, blacklist); |
| 135 | + const maskedMessage = maskForbiddenWords(messageText!, blacklist); |
| 136 | + |
| 137 | + let warningMessage = ` |
| 138 | +⚠️ Warning, ${info_user}! |
| 139 | +
|
| 140 | +You have used a forbidden word from the blacklist in your message. As a result, you have been temporarily muted for 1 hour. |
| 141 | +
|
| 142 | +This action has been taken to maintain a respectful and safe environment for everyone in the group. |
| 143 | +
|
| 144 | +Please be mindful of your language and refrain from using inappropriate words. Repeated violations may result in further actions. |
| 145 | +
|
| 146 | +Here is the message you sent (with forbidden words masked): |
| 147 | +_"${maskedMessage}"_ |
| 148 | +
|
| 149 | +We appreciate your understanding and cooperation in keeping the community welcoming for all members. Thank you! |
| 150 | +`; |
| 151 | + |
| 152 | + if (lineNumber !== null) { |
| 153 | + warningMessage += `\n\n⚠️ Forbidden word detected on line ${lineNumber}.`; |
| 154 | + } |
| 155 | + return warningMessage; |
| 156 | + } |
| 157 | +} |
0 commit comments