Parcourir la source

Added Antispam & Antiflood

thehamkercat il y a 5 ans
Parent
commit
25a5b32daa
6 fichiers modifiés avec 341 ajouts et 32 suppressions
  1. 0 1
      .gitignore
  2. 53 30
      wbb/modules/admin.py
  3. 118 0
      wbb/modules/flood.py
  4. 1 1
      wbb/modules/nsfw.py
  5. 167 0
      wbb/modules/spam.py
  6. 2 0
      wbb/utils/filter_groups.py

+ 0 - 1
.gitignore

@@ -1,5 +1,4 @@
 *.m4a
-spam.py
 *.apk
 *.c
 exec

+ 53 - 30
wbb/modules/admin.py

@@ -24,8 +24,10 @@ SOFTWARE.
 import asyncio
 
 from pyrogram import filters
-from pyrogram.types import ChatPermissions, Message
-
+from pyrogram.types import (
+        ChatPermissions, Message, CallbackQuery,
+        InlineKeyboardMarkup, InlineKeyboardButton
+        )
 from wbb import BOT_ID, SUDOERS, app
 from wbb.core.decorators.errors import capture_err
 from wbb.utils.dbfunctions import (add_warn, get_warn, int_to_alpha,
@@ -309,17 +311,25 @@ async def pin(_, message: Message):
 @adminsOnly("can_restrict_members")
 async def mute(_, message: Message):
     if len(message.command) == 2:
-        user_id = (await app.get_users(message.text.split(None, 1)[1])).id
+        user = (await app.get_users(message.text.split(None, 1)[1]))
     elif len(message.command) == 1 and message.reply_to_message:
-        user_id = message.reply_to_message.from_user.id
+        user = message.reply_to_message.from_user
     else:
         return await message.reply_text(
             "Provide a username or reply to a user's message to mute."
         )
-    if user_id in SUDOERS:
+    if user.id in SUDOERS:
         return await message.reply_text("You Wanna Mute The Elevated One?")
-    await message.chat.restrict_member(user_id, permissions=ChatPermissions())
-    await message.reply_text("Muted!")
+    await message.chat.restrict_member(user.id, permissions=ChatPermissions())
+    keyboard = InlineKeyboardMarkup(
+                [[
+                    InlineKeyboardButton(
+                        text="🚨   Unmute   🚨",
+                        callback_data=f"unmute_{user.id}"
+                    )
+                ]]
+            )
+    await message.reply_text(f"Enough freedom of speech, Muted {user.mention} !", reply_markup=keyboard)
 
 
 # Unmute members
@@ -361,7 +371,7 @@ async def ban_deleted_accounts(_, message: Message):
             banned_users += 1
         await message.reply_text(f"Banned {banned_users} Deleted Accounts")
     else:
-        await message.reply_text("No Deleted Accounts In This Chat")
+        await message.reply_text("There are no deleted accounts in this chat")
 
 
 @app.on_message(filters.command("warn") & ~filters.edited)
@@ -381,13 +391,22 @@ async def warn_user(_, message: Message):
     else:
         if user_id in await list_members(chat_id):
             warns = await get_warn(chat_id, await int_to_alpha(user_id))
+            keyboard = InlineKeyboardMarkup(
+                        [[
+                            InlineKeyboardButton(
+                                text="🚨  Remove Warn  🚨",
+                                callback_data=f"unwarn_{user_id}",
+                            )
+                        ]]
+                    )
             if warns:
                 warns = warns["warns"]
             else:
                 warn = {"warns": 1}
                 await add_warn(chat_id, await int_to_alpha(user_id), warn)
                 return await message.reply_text(
-                    f"Warned {mention} !, 1/3 warnings now."
+                    f"Warned {mention} | 1/3 warnings now.",
+                    reply_markup=keyboard,
                 )
             if warns >= 2:
                 await message.chat.kick_member(user_id)
@@ -397,36 +416,40 @@ async def warn_user(_, message: Message):
                 await remove_warns(chat_id, await int_to_alpha(user_id))
             else:
                 warn = {"warns": warns + 1}
-                await add_warn(chat_id, await int_to_alpha(user_id), warn)
+
                 await message.reply_text(
-                    f"Warned {mention} !, {warns+1}/3 warnings now."
+                    f"Warned {mention} | {warns+1}/3 warnings now.",
+                    reply_markup=keyboard,
                 )
+                return await add_warn(chat_id, await int_to_alpha(user_id), warn)
         else:
             await message.reply_text("This user isn't here.")
 
 
-# Rmwarn
-
-
-@app.on_message(filters.command("rmwarn") & ~filters.edited)
-@adminsOnly("can_restrict_members")
-async def remove_warning(_, message: Message):
-    if not message.reply_to_message:
-        return await message.reply_text(
-            "Reply to a message to remove a user's warning."
-        )
-    user_id = message.reply_to_message.from_user.id
-    mention = message.reply_to_message.from_user.mention
-    chat_id = message.chat.id
+@app.on_callback_query(filters.regex("unwarn_"))
+async def remove_warning(_, cq: CallbackQuery):
+    from_user = cq.from_user
+    chat_id = cq.message.chat.id
+    permissions = await member_permissions(chat_id, from_user.id)
+    permission = "can_restrict_members"
+    if permission not in permissions:
+        return await cq.answer(
+                "You don't have enough permissions to perform this action.\n"
+                + f"Permission needed: {permission}",
+                show_alert=True,
+                )
+    user_id = cq.data.split("_")[1]
     warns = await get_warn(chat_id, await int_to_alpha(user_id))
     if warns:
         warns = warns["warns"]
-    if warns == 0 or not warns:
-        await message.reply_text(f"{mention} have no warnings.")
-    else:
-        warn = {"warns": warns - 1}
-        await add_warn(chat_id, await int_to_alpha(user_id), warn)
-        await message.reply_text(f"Removed warning of {mention}.")
+    if not warns or warns == 0:
+        return await cq.answer("User has no warnings.")
+    warn = {"warns": warns - 1}
+    await add_warn(chat_id, await int_to_alpha(user_id), warn)
+    text = cq.message.text.markdown
+    text = f"~~{text}~~\n\n"
+    text += f"__Warn removed by {from_user.mention}__"
+    await cq.message.edit(text)
 
 
 # Rmwarns

+ 118 - 0
wbb/modules/flood.py

@@ -0,0 +1,118 @@
+"""
+MIT License
+
+Copyright (c) 2021 TheHamkerCat
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+"""
+from pyrogram import filters
+from pyrogram.types import (
+        Message, ChatPermissions, CallbackQuery,
+        InlineKeyboardMarkup, InlineKeyboardButton
+        )
+from time import time
+
+from wbb import app, SUDOERS
+from wbb.core.decorators.errors import capture_err
+from wbb.modules.admin import list_admins, member_permissions
+from wbb.utils.filter_groups import flood_group
+
+
+__MODULE__ = "Flood"
+__HELP__ = """
+Anti-Flood system, the one who sends more than 7 messages in a row, gets muted for an hour.
+Except for admins ofc, and no, you can't change the number of messages or action type.
+"""
+
+
+DB = {} # NOTE Use mongodb instead of a fucking dict.
+
+@app.on_message(
+        ~filters.service
+        & ~filters.me
+        & ~filters.private
+        & ~filters.channel,
+        group=flood_group)
+@capture_err
+async def flood_control_func(_, message: Message):
+    if not message.from_user:
+        return
+    user_id = message.from_user.id
+    mention = message.from_user.mention
+    chat_id = message.chat.id
+    
+    # Ignore devs and admins
+    mods = (await list_admins(chat_id)) + SUDOERS
+    if user_id in mods:
+        return
+
+    # Initialize db if not already.
+    if chat_id not in DB:
+        DB[chat_id] = {}
+    if user_id not in DB[chat_id]:
+        DB[chat_id][user_id] = 0
+    
+    # Reset every other user's flood count if someone else sends a message.
+    for user in DB[chat_id].keys():
+        if user != user_id:
+            DB[chat_id][user] = 0
+
+    # Mute if user sends more than 7 messages in a row
+    if DB[chat_id][user_id] >= 7:
+        DB[chat_id][user_id] = 0
+        try:
+            await message.chat.restrict_member(
+                    user_id,
+                    permissions=ChatPermissions(),
+                    until_date=int(time() + 3600)
+                    )
+        except Exception:
+            return
+        keyboard = InlineKeyboardMarkup(
+                [[
+                    InlineKeyboardButton(
+                        text="🚨   Unmute   🚨",
+                        callback_data=f"unmute_{user_id}"
+                    )
+                ]]
+            )
+        return await message.reply_text(
+                f"Imagine flooding the chat in front of me, Muted {mention} for an hour!",
+                reply_markup=keyboard
+                )
+    DB[chat_id][user_id] += 1
+
+
+@app.on_callback_query(filters.regex("unmute_"))
+async def flood_callback_func(_, cq: CallbackQuery):
+    from_user = cq.from_user
+    permissions = await member_permissions(cq.message.chat.id, from_user.id)
+    permission = "can_restrict_members"
+    if permission not in permissions:
+        return await cq.answer(
+                "You don't have enough permissions to perform this action.\n"
+                + f"Permission needed: {permission}",
+                show_alert=True,
+                )
+    user_id = cq.data.split("_")[1]
+    await cq.message.chat.unban_member(user_id)
+    text = cq.message.text.markdown
+    text = f"~~{text}~~\n\n"
+    text += f"__User unmuted by {from_user.mention}__"
+    await cq.message.edit(text)

+ 1 - 1
wbb/modules/nsfw.py

@@ -95,7 +95,7 @@ async def detect_nsfw(_, message):
 **Hentai:** `{results.hentai} %`
 **Drawings:** `{results.drawings} %`
 **————————————————————————**
-__Use '/anti_nsfw disable' to disable this.__
+__Use `/anti_nsfw disable` to disable this.__
 """
     )
 

+ 167 - 0
wbb/modules/spam.py

@@ -0,0 +1,167 @@
+"""
+MIT License
+
+Copyright (c) 2021 TheHamkerCat
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+"""
+from pyrogram import filters
+from pyrogram.types import (
+        Message, CallbackQuery,
+        InlineKeyboardMarkup, InlineKeyboardButton
+        )
+
+from wbb import app, arq, SUDOERS, LOG_GROUP_ID
+from wbb.core.decorators.errors import capture_err
+from wbb.modules.admin import list_admins, member_permissions
+from wbb.utils.filter_groups import spam_protection_group
+
+
+__MODULE__ = "AntiSpam"
+__HELP__ = """
+**Antispam helps from protect your group from spam.**
+
+- It's still new so it might be a bit less accurate.
+
+- You can enable or disable it according to your needs.
+
+- Currently, it only alerts for spam and gives you a button to delete the message,
+  but in the future, it will delete the spam message automatically.
+
+**Commands:**
+    - /spam_protection [ENABLE|DISABLE]
+        To enable or disable this service.
+    - /spam
+        To mark a message as spam, this will help devs to 
+        improve spam protection algorithm.
+"""
+
+
+@app.on_message(
+        (filters.text | filters.caption)
+        & ~filters.private
+        & ~filters.me,
+        group=spam_protection_group
+)
+async def spam_protection_func(_, message: Message):
+    text = message.text if message.text else message.caption
+    user = message.from_user
+    chat_id = message.chat.id
+    if not text or not user:
+        return
+
+    # We'll handle admins only if it's spam, ignore only sudo users for now.
+    if user.id in SUDOERS:
+        return
+
+    data = await arq.nlp(text)
+    data = data.result[0]
+    if not data.is_spam:
+        return
+    if user.id in (await list_admins(chat_id)):
+        return
+    text = f"""
+🚨  **SPAM DETECTED** 🚨
+
+**User:** {message.from_user.mention}
+**Message:** [Link]({message.link})
+**Spam Probability:** {data.spam_probability} %
+**Action:** Alerted
+"""
+    keyboard = InlineKeyboardMarkup(
+            [
+                [
+                    InlineKeyboardButton(
+                        text="Yes it's spam",
+                        callback_data="s_p_spam"
+                    ),
+                    InlineKeyboardButton(
+                        text="No, it's not spam",
+                        callback_data="s_p_ham",
+                    )
+                ]
+            ]
+        )
+    await message.reply_text(
+            text=text,
+            reply_markup=keyboard,
+            disable_web_page_preview=True,
+            )
+
+dev_forward = "If you're not a developer of WBB, forward this message to devs, " \
+                  + "so that they can use it to improve spam protection algorithm."
+
+
+@app.on_callback_query(filters.regex("s_p_"))
+async def spam_p_callback(_, cq: CallbackQuery):
+    from_user = cq.from_user
+    chat_id = cq.message.chat.id
+
+    permissions = await member_permissions(chat_id, from_user.id)
+    permission = "can_delete_messages"
+    if permission not in permissions:
+        return await cq.answer(
+                "You don't have enough permissions to perform this action.\n"
+                + f"Permission needed: {permission}",
+                show_alert=True,
+                )
+
+    if cq.data.split("_")[-1] == "spam":
+        try:
+            await cq.message.reply_to_message.delete()
+        except Exception:
+            await cq.message.delete()
+            return
+        text = cq.message.text.markdown
+        text = text.replace("Alerted", f"Deleted Message With {from_user.mention}'s Approval.")
+        await cq.message.edit(text)
+    
+    await cq.message.delete()
+    if cq.message.reply_to_message:
+        text = f"**ADMINS OF {chat_id} FLAGGED THIS MESSAGE AS SPAM**\n\n"
+        text += f"`{cq.message.reply_to_message.text.markdown}`\n\n__{dev_forward}__"
+        return await app.send_message(LOG_GROUP_ID, text, disable_web_page_preview=True)
+
+
+@app.on_message(filters.command("spam") & ~filters.private)
+async def spam_flag_func(_, message: Message):
+    if not message.reply_to_message:
+        return await message.reply_text("Reply to a message to flag it as spam")
+    
+    r = message.reply_to_message
+    if not r.text and not r.caption:
+        return await message.reply_text("Reply to a text message to flag it as spam")
+
+    text = r.text or r.caption
+    if not text:
+        return await message.reply_text("Reply to a text message to flag it as spam")
+    
+    msg = f"""
+**ADMINS OF {message.chat.id} FLAGGED THIS MESSAGE AS SPAM**
+
+```
+{text.markdown}
+```
+
+__{dev_forward}__
+"""
+    await message.reply_text(
+            "Message was flagged as spam, Devs will use this to improve spam protection algorithm."
+            )
+    await app.send_message(LOG_GROUP_ID, msg, disable_web_page_preview=True)

+ 2 - 0
wbb/utils/filter_groups.py

@@ -34,3 +34,5 @@ pipes_group = 10
 taglog_group = 11
 chat_watcher_group = 12
 trust_group = 13
+flood_group = 14
+spam_protection_group = 15