Spaces:
Paused
Paused
Alexainc commited on
Commit ยท
2567473
1
Parent(s): bc14d67
feat: enhanced filters and welcome message formatting with advanced buttons and placeholders
Browse files- QueenNoxi/modules/cust_filters.py +15 -8
- QueenNoxi/modules/helper_funcs/formatters.py +71 -0
- QueenNoxi/modules/helper_funcs/misc.py +26 -6
- QueenNoxi/modules/helper_funcs/msg_types.py +23 -12
- QueenNoxi/modules/helper_funcs/string_handling.py +20 -4
- QueenNoxi/modules/notes.py +29 -35
- QueenNoxi/modules/welcome.py +14 -23
- README.md +6 -17
QueenNoxi/modules/cust_filters.py
CHANGED
|
@@ -16,6 +16,7 @@ from QueenNoxi.modules.helper_funcs.chat_status import user_admin, connection_st
|
|
| 16 |
from QueenNoxi.modules.helper_funcs.misc import build_keyboard, revert_buttons
|
| 17 |
from QueenNoxi.modules.helper_funcs.msg_types import get_filter_type, Types
|
| 18 |
from QueenNoxi.modules.helper_funcs.string_handling import button_markdown_parser, split_quotes
|
|
|
|
| 19 |
from QueenNoxi.modules.sql import cust_filters_sql as sql
|
| 20 |
|
| 21 |
# Handler group for filters
|
|
@@ -44,11 +45,12 @@ async def add_filter(client: Client, message: Message):
|
|
| 44 |
return
|
| 45 |
keyword = extracted[0].lower()
|
| 46 |
|
| 47 |
-
text, file_type, file_id = await get_filter_type(message)
|
| 48 |
-
|
| 49 |
-
# Extract buttons from text if any
|
| 50 |
-
_, buttons = button_markdown_parser(text) if text else (None, [])
|
| 51 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
sql.new_add_filter(chat_id, keyword, text, file_type, file_id, buttons)
|
| 53 |
await message.reply_text(f"Saved filter '{keyword}'!")
|
| 54 |
|
|
@@ -100,18 +102,23 @@ async def reply_filter(client: Client, message: Message):
|
|
| 100 |
buttons = sql.get_buttons(chat_id, keyword)
|
| 101 |
keyboard = InlineKeyboardMarkup(build_keyboard(buttons)) if buttons else None
|
| 102 |
|
|
|
|
|
|
|
| 103 |
if filt.file_type in (Types.TEXT, Types.BUTTON_TEXT):
|
| 104 |
await message.reply_text(
|
| 105 |
-
|
| 106 |
reply_markup=keyboard,
|
| 107 |
-
|
|
|
|
| 108 |
)
|
| 109 |
else:
|
| 110 |
await client.send_cached_media(
|
| 111 |
chat_id,
|
| 112 |
filt.file_id,
|
| 113 |
-
caption=
|
| 114 |
-
reply_markup=keyboard
|
|
|
|
|
|
|
| 115 |
)
|
| 116 |
break
|
| 117 |
|
|
|
|
| 16 |
from QueenNoxi.modules.helper_funcs.misc import build_keyboard, revert_buttons
|
| 17 |
from QueenNoxi.modules.helper_funcs.msg_types import get_filter_type, Types
|
| 18 |
from QueenNoxi.modules.helper_funcs.string_handling import button_markdown_parser, split_quotes
|
| 19 |
+
from QueenNoxi.modules.helper_funcs.formatters import format_message
|
| 20 |
from QueenNoxi.modules.sql import cust_filters_sql as sql
|
| 21 |
|
| 22 |
# Handler group for filters
|
|
|
|
| 45 |
return
|
| 46 |
keyword = extracted[0].lower()
|
| 47 |
|
| 48 |
+
text, file_type, file_id, buttons = await get_filter_type(message)
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
+
if not file_type:
|
| 51 |
+
await message.reply_text("You didn't specify what to reply with!")
|
| 52 |
+
return
|
| 53 |
+
|
| 54 |
sql.new_add_filter(chat_id, keyword, text, file_type, file_id, buttons)
|
| 55 |
await message.reply_text(f"Saved filter '{keyword}'!")
|
| 56 |
|
|
|
|
| 102 |
buttons = sql.get_buttons(chat_id, keyword)
|
| 103 |
keyboard = InlineKeyboardMarkup(build_keyboard(buttons)) if buttons else None
|
| 104 |
|
| 105 |
+
res, flags = await format_message(filt.reply_text, message.from_user, message.chat)
|
| 106 |
+
|
| 107 |
if filt.file_type in (Types.TEXT, Types.BUTTON_TEXT):
|
| 108 |
await message.reply_text(
|
| 109 |
+
res,
|
| 110 |
reply_markup=keyboard,
|
| 111 |
+
reply_to_message_id=message.id,
|
| 112 |
+
**flags
|
| 113 |
)
|
| 114 |
else:
|
| 115 |
await client.send_cached_media(
|
| 116 |
chat_id,
|
| 117 |
filt.file_id,
|
| 118 |
+
caption=res,
|
| 119 |
+
reply_markup=keyboard,
|
| 120 |
+
reply_to_message_id=message.id,
|
| 121 |
+
**flags
|
| 122 |
)
|
| 123 |
break
|
| 124 |
|
QueenNoxi/modules/helper_funcs/formatters.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import re
|
| 2 |
+
from typing import Optional, Dict, Tuple
|
| 3 |
+
from pyrogram import enums
|
| 4 |
+
from pyrogram.types import Message, User, Chat
|
| 5 |
+
from QueenNoxi import BOT_USERNAME
|
| 6 |
+
from QueenNoxi.modules.helper_funcs.string_handling import escape_markdown, escape_invalid_curly_brackets
|
| 7 |
+
|
| 8 |
+
async def format_message(text: str, user: User, chat: Chat) -> Tuple[str, Dict]:
|
| 9 |
+
if not text:
|
| 10 |
+
return "", {}
|
| 11 |
+
|
| 12 |
+
first = escape_markdown(user.first_name)
|
| 13 |
+
last = escape_markdown(user.last_name or user.first_name)
|
| 14 |
+
fullname = escape_markdown(f"{user.first_name} {user.last_name}" if user.last_name else user.first_name)
|
| 15 |
+
username = f"@{user.username}" if user.username else user.mention
|
| 16 |
+
mention = user.mention
|
| 17 |
+
id = user.id
|
| 18 |
+
chatname = escape_markdown(chat.title if chat and chat.type != enums.ChatType.PRIVATE else user.first_name)
|
| 19 |
+
|
| 20 |
+
rules_link = f"t.me/{BOT_USERNAME}?start={chat.id}" if chat and chat.type != enums.ChatType.PRIVATE else f"t.me/{BOT_USERNAME}"
|
| 21 |
+
rules = f"[Rules]({rules_link})"
|
| 22 |
+
|
| 23 |
+
VALID_PLACEHOLDERS = [
|
| 24 |
+
"first", "last", "fullname", "username", "mention", "id", "chatname", "rules"
|
| 25 |
+
]
|
| 26 |
+
|
| 27 |
+
text = escape_invalid_curly_brackets(text, VALID_PLACEHOLDERS)
|
| 28 |
+
|
| 29 |
+
try:
|
| 30 |
+
text = text.format(
|
| 31 |
+
first=first,
|
| 32 |
+
last=last,
|
| 33 |
+
fullname=fullname,
|
| 34 |
+
username=username,
|
| 35 |
+
mention=mention,
|
| 36 |
+
id=id,
|
| 37 |
+
chatname=chatname,
|
| 38 |
+
rules=rules
|
| 39 |
+
)
|
| 40 |
+
except Exception as e:
|
| 41 |
+
import logging
|
| 42 |
+
logging.error(f"Error formatting message: {e}")
|
| 43 |
+
|
| 44 |
+
# Flags extraction
|
| 45 |
+
flags = {
|
| 46 |
+
"disable_web_page_preview": False,
|
| 47 |
+
"disable_notification": False,
|
| 48 |
+
"protect_content": False,
|
| 49 |
+
"has_spoiler": False
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
if "{preview}" in text:
|
| 53 |
+
text = text.replace("{preview}", "")
|
| 54 |
+
flags["disable_web_page_preview"] = False # Logic might be inverted depending on default
|
| 55 |
+
else:
|
| 56 |
+
flags["disable_web_page_preview"] = True # Default to disable if not specified?
|
| 57 |
+
# Actually user said "{preview} enables link previews", so default should be disabled.
|
| 58 |
+
|
| 59 |
+
if "{nonotif}" in text:
|
| 60 |
+
text = text.replace("{nonotif}", "")
|
| 61 |
+
flags["disable_notification"] = True
|
| 62 |
+
|
| 63 |
+
if "{protect}" in text:
|
| 64 |
+
text = text.replace("{protect}", "")
|
| 65 |
+
flags["protect_content"] = True
|
| 66 |
+
|
| 67 |
+
if "{mediaspoiler}" in text:
|
| 68 |
+
text = text.replace("{mediaspoiler}", "")
|
| 69 |
+
flags["has_spoiler"] = True
|
| 70 |
+
|
| 71 |
+
return text.strip(), flags
|
QueenNoxi/modules/helper_funcs/misc.py
CHANGED
|
@@ -101,19 +101,39 @@ async def send_to_list(client, send_to: list, message: str, parse_mode=None) ->
|
|
| 101 |
def build_keyboard(buttons):
|
| 102 |
keyb = []
|
| 103 |
for btn in buttons:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
if btn.same_line and keyb:
|
| 105 |
-
keyb[-1].append(
|
| 106 |
else:
|
| 107 |
-
keyb.append([
|
| 108 |
return keyb
|
| 109 |
|
| 110 |
def revert_buttons(buttons):
|
| 111 |
res = ""
|
| 112 |
for btn in buttons:
|
| 113 |
-
if btn.
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
res += "\n[{}](buttonurl://{})".format(btn.name, btn.url)
|
| 117 |
return res
|
| 118 |
|
| 119 |
def is_module_loaded(name):
|
|
|
|
| 101 |
def build_keyboard(buttons):
|
| 102 |
keyb = []
|
| 103 |
for btn in buttons:
|
| 104 |
+
color_prefix = ""
|
| 105 |
+
if btn.color:
|
| 106 |
+
if btn.color == "success":
|
| 107 |
+
color_prefix = "๐ข "
|
| 108 |
+
elif btn.color == "danger":
|
| 109 |
+
color_prefix = "๐ด "
|
| 110 |
+
elif btn.color == "primary":
|
| 111 |
+
color_prefix = "๐ต "
|
| 112 |
+
elif btn.color == "warning":
|
| 113 |
+
color_prefix = "๐ก "
|
| 114 |
+
|
| 115 |
+
btn_text = f"{color_prefix}{btn.name}"
|
| 116 |
+
|
| 117 |
+
if btn.url.startswith("#"):
|
| 118 |
+
note_name = btn.url[1:]
|
| 119 |
+
button = InlineKeyboardButton(btn_text, callback_data=f"note_{note_name}")
|
| 120 |
+
elif btn.url in ("btn_next", "btn_back", "btn_home"):
|
| 121 |
+
button = InlineKeyboardButton(btn_text, callback_data=f"paginate_{btn.url}")
|
| 122 |
+
else:
|
| 123 |
+
button = InlineKeyboardButton(btn_text, url=btn.url)
|
| 124 |
+
|
| 125 |
if btn.same_line and keyb:
|
| 126 |
+
keyb[-1].append(button)
|
| 127 |
else:
|
| 128 |
+
keyb.append([button])
|
| 129 |
return keyb
|
| 130 |
|
| 131 |
def revert_buttons(buttons):
|
| 132 |
res = ""
|
| 133 |
for btn in buttons:
|
| 134 |
+
color_part = f"#{btn.color}" if btn.color else ""
|
| 135 |
+
same_line = ":same" if btn.same_line else ""
|
| 136 |
+
res += f"\n[{btn.name}](buttonurl{color_part}://{btn.url}{same_line})"
|
|
|
|
| 137 |
return res
|
| 138 |
|
| 139 |
def is_module_loaded(name):
|
QueenNoxi/modules/helper_funcs/msg_types.py
CHANGED
|
@@ -120,44 +120,55 @@ async def get_filter_type(msg: Message):
|
|
| 120 |
text = None
|
| 121 |
data_type = None
|
| 122 |
content = None
|
|
|
|
| 123 |
|
| 124 |
if not msg.reply_to_message and msg.text and len(msg.text.split()) >= 3:
|
| 125 |
-
|
| 126 |
-
|
|
|
|
| 127 |
|
| 128 |
elif msg.reply_to_message:
|
| 129 |
reply = msg.reply_to_message
|
| 130 |
-
if reply.text:
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
content = reply.sticker.file_id
|
| 135 |
data_type = Types.STICKER
|
| 136 |
elif reply.document:
|
| 137 |
content = reply.document.file_id
|
| 138 |
-
text = reply.caption
|
| 139 |
data_type = Types.DOCUMENT
|
| 140 |
elif reply.photo:
|
| 141 |
content = reply.photo.file_id
|
| 142 |
-
text = reply.caption
|
| 143 |
data_type = Types.PHOTO
|
| 144 |
elif reply.audio:
|
| 145 |
content = reply.audio.file_id
|
| 146 |
-
text = reply.caption
|
| 147 |
data_type = Types.AUDIO
|
| 148 |
elif reply.voice:
|
| 149 |
content = reply.voice.file_id
|
| 150 |
-
text = reply.caption
|
| 151 |
data_type = Types.VOICE
|
| 152 |
elif reply.video:
|
| 153 |
content = reply.video.file_id
|
| 154 |
-
text = reply.caption
|
| 155 |
data_type = Types.VIDEO
|
| 156 |
elif reply.video_note:
|
| 157 |
content = reply.video_note.file_id
|
| 158 |
data_type = Types.VIDEO_NOTE
|
| 159 |
|
| 160 |
-
return text, data_type, content
|
| 161 |
|
| 162 |
|
| 163 |
##
|
|
|
|
| 120 |
text = None
|
| 121 |
data_type = None
|
| 122 |
content = None
|
| 123 |
+
buttons = []
|
| 124 |
|
| 125 |
if not msg.reply_to_message and msg.text and len(msg.text.split()) >= 3:
|
| 126 |
+
raw_text = msg.text.split(None, 2)[2]
|
| 127 |
+
text, buttons = button_markdown_parser(raw_text, entities=msg.entities)
|
| 128 |
+
data_type = Types.BUTTON_TEXT if buttons else Types.TEXT
|
| 129 |
|
| 130 |
elif msg.reply_to_message:
|
| 131 |
reply = msg.reply_to_message
|
| 132 |
+
if reply.text or reply.caption:
|
| 133 |
+
msgtext = reply.text or reply.caption
|
| 134 |
+
entities = reply.entities or reply.caption_entities
|
| 135 |
+
text, buttons = button_markdown_parser(msgtext, entities=entities)
|
| 136 |
+
|
| 137 |
+
# Auto-detect buttons from InlineKeyboardMarkup if no markdown buttons found
|
| 138 |
+
if not buttons and reply.reply_markup and reply.reply_markup.inline_keyboard:
|
| 139 |
+
from QueenNoxi.modules.helper_funcs.string_handling import Button
|
| 140 |
+
for row in reply.reply_markup.inline_keyboard:
|
| 141 |
+
for btn in row:
|
| 142 |
+
url = btn.url or (f"#{btn.callback_data}" if btn.callback_data else "")
|
| 143 |
+
if url:
|
| 144 |
+
same = True if row.index(btn) > 0 else False
|
| 145 |
+
buttons.append(Button(btn.text, url, same_line=same))
|
| 146 |
+
|
| 147 |
+
data_type = Types.BUTTON_TEXT if buttons else Types.TEXT
|
| 148 |
+
|
| 149 |
+
if reply.sticker:
|
| 150 |
content = reply.sticker.file_id
|
| 151 |
data_type = Types.STICKER
|
| 152 |
elif reply.document:
|
| 153 |
content = reply.document.file_id
|
|
|
|
| 154 |
data_type = Types.DOCUMENT
|
| 155 |
elif reply.photo:
|
| 156 |
content = reply.photo.file_id
|
|
|
|
| 157 |
data_type = Types.PHOTO
|
| 158 |
elif reply.audio:
|
| 159 |
content = reply.audio.file_id
|
|
|
|
| 160 |
data_type = Types.AUDIO
|
| 161 |
elif reply.voice:
|
| 162 |
content = reply.voice.file_id
|
|
|
|
| 163 |
data_type = Types.VOICE
|
| 164 |
elif reply.video:
|
| 165 |
content = reply.video.file_id
|
|
|
|
| 166 |
data_type = Types.VIDEO
|
| 167 |
elif reply.video_note:
|
| 168 |
content = reply.video_note.file_id
|
| 169 |
data_type = Types.VIDEO_NOTE
|
| 170 |
|
| 171 |
+
return text, data_type, content, buttons
|
| 172 |
|
| 173 |
|
| 174 |
##
|
QueenNoxi/modules/helper_funcs/string_handling.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
import re
|
| 2 |
import time
|
| 3 |
-
from typing import List
|
| 4 |
import bleach
|
| 5 |
import markdown2
|
| 6 |
from pyrogram import enums
|
|
@@ -15,7 +15,7 @@ MATCH_MD = re.compile(
|
|
| 15 |
)
|
| 16 |
|
| 17 |
LINK_REGEX = re.compile(r"(?<!\\)\[.+?\]\((.*?)\)")
|
| 18 |
-
BTN_URL_REGEX = re.compile(r"(\[([^\[]+?)\]\(buttonurl:(?:/{0,2})(.+?)(:same)?\))")
|
| 19 |
|
| 20 |
def _selective_escape(to_parse: str) -> str:
|
| 21 |
offset = 0
|
|
@@ -55,7 +55,14 @@ def markdown_parser(txt: str, entities: List[MessageEntity] = None, offset: int
|
|
| 55 |
res += _selective_escape(txt[prev:])
|
| 56 |
return res
|
| 57 |
|
| 58 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
markdown_note = markdown_parser(txt, entities, offset)
|
| 60 |
prev = 0
|
| 61 |
note_data = ""
|
|
@@ -68,7 +75,16 @@ def button_markdown_parser(txt: str, entities: List[MessageEntity] = None, offse
|
|
| 68 |
to_check -= 1
|
| 69 |
|
| 70 |
if n_escapes % 2 == 0:
|
| 71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
note_data += markdown_note[prev : match.start(1)]
|
| 73 |
prev = match.end(1)
|
| 74 |
else:
|
|
|
|
| 1 |
import re
|
| 2 |
import time
|
| 3 |
+
from typing import List, Tuple
|
| 4 |
import bleach
|
| 5 |
import markdown2
|
| 6 |
from pyrogram import enums
|
|
|
|
| 15 |
)
|
| 16 |
|
| 17 |
LINK_REGEX = re.compile(r"(?<!\\)\[.+?\]\((.*?)\)")
|
| 18 |
+
BTN_URL_REGEX = re.compile(r"(\[([^\[]+?)\]\(buttonurl(?:#([^:]+))?://(/{0,2})(.+?)(:same)?\))")
|
| 19 |
|
| 20 |
def _selective_escape(to_parse: str) -> str:
|
| 21 |
offset = 0
|
|
|
|
| 55 |
res += _selective_escape(txt[prev:])
|
| 56 |
return res
|
| 57 |
|
| 58 |
+
class Button:
|
| 59 |
+
def __init__(self, name, url, same_line=False, color=None):
|
| 60 |
+
self.name = name
|
| 61 |
+
self.url = url
|
| 62 |
+
self.same_line = same_line
|
| 63 |
+
self.color = color
|
| 64 |
+
|
| 65 |
+
def button_markdown_parser(txt: str, entities: List[MessageEntity] = None, offset: int = 0) -> Tuple[str, List[Button]]:
|
| 66 |
markdown_note = markdown_parser(txt, entities, offset)
|
| 67 |
prev = 0
|
| 68 |
note_data = ""
|
|
|
|
| 75 |
to_check -= 1
|
| 76 |
|
| 77 |
if n_escapes % 2 == 0:
|
| 78 |
+
# match.group(2) -> name
|
| 79 |
+
# match.group(3) -> color
|
| 80 |
+
# match.group(5) -> url/back/next/home
|
| 81 |
+
# match.group(6) -> :same
|
| 82 |
+
buttons.append(Button(
|
| 83 |
+
match.group(2),
|
| 84 |
+
match.group(5),
|
| 85 |
+
bool(match.group(6)),
|
| 86 |
+
match.group(3)
|
| 87 |
+
))
|
| 88 |
note_data += markdown_note[prev : match.start(1)]
|
| 89 |
prev = match.end(1)
|
| 90 |
else:
|
QueenNoxi/modules/notes.py
CHANGED
|
@@ -12,6 +12,7 @@ from pyrogram.errors import RPCError
|
|
| 12 |
|
| 13 |
import QueenNoxi.modules.sql.notes_sql as sql
|
| 14 |
from QueenNoxi import DRAGONS, pbot, BOT_ID, SUPPORT_CHAT
|
|
|
|
| 15 |
from QueenNoxi.modules.disable import DisableAbleCommandHandler
|
| 16 |
from QueenNoxi.modules.helper_funcs.chat_status import connection_status, user_admin
|
| 17 |
from QueenNoxi.modules.helper_funcs.misc import build_keyboard, revert_buttons
|
|
@@ -20,6 +21,7 @@ from QueenNoxi.modules.helper_funcs.string_handling import (
|
|
| 20 |
escape_invalid_curly_brackets,
|
| 21 |
escape_markdown
|
| 22 |
)
|
|
|
|
| 23 |
|
| 24 |
# Do not async
|
| 25 |
@connection_status
|
|
@@ -42,34 +44,8 @@ async def get(client: Client, message: Message, notename: str, show_none=True, n
|
|
| 42 |
sql.rm_note(chat_id, notename)
|
| 43 |
return
|
| 44 |
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
"last",
|
| 48 |
-
"fullname",
|
| 49 |
-
"username",
|
| 50 |
-
"id",
|
| 51 |
-
"chatname",
|
| 52 |
-
"mention",
|
| 53 |
-
]
|
| 54 |
-
valid_format = escape_invalid_curly_brackets(note.value, VALID_NOTE_FORMATTERS)
|
| 55 |
-
if valid_format:
|
| 56 |
-
text = valid_format.format(
|
| 57 |
-
first=escape_markdown(message.from_user.first_name),
|
| 58 |
-
last=escape_markdown(message.from_user.last_name or message.from_user.first_name),
|
| 59 |
-
fullname=escape_markdown(
|
| 60 |
-
" ".join(
|
| 61 |
-
[message.from_user.first_name, message.from_user.last_name]
|
| 62 |
-
if message.from_user.last_name
|
| 63 |
-
else [message.from_user.first_name]
|
| 64 |
-
)
|
| 65 |
-
),
|
| 66 |
-
username="@" + message.from_user.username if message.from_user.username else message.from_user.mention,
|
| 67 |
-
mention=message.from_user.mention,
|
| 68 |
-
chatname=escape_markdown(message.chat.title if message.chat.type != enums.ChatType.PRIVATE else message.from_user.first_name),
|
| 69 |
-
id=message.from_user.id,
|
| 70 |
-
)
|
| 71 |
-
else:
|
| 72 |
-
text = ""
|
| 73 |
|
| 74 |
buttons = sql.get_buttons(chat_id, notename)
|
| 75 |
keyb = []
|
|
@@ -90,21 +66,22 @@ async def get(client: Client, message: Message, notename: str, show_none=True, n
|
|
| 90 |
reply_to_message_id=reply_id,
|
| 91 |
parse_mode=parse_mode,
|
| 92 |
reply_markup=keyboard,
|
|
|
|
| 93 |
)
|
| 94 |
elif note.msgtype == Types.STICKER:
|
| 95 |
-
await client.send_sticker(chat_id, note.file, reply_to_message_id=reply_id, reply_markup=keyboard)
|
| 96 |
elif note.msgtype == Types.DOCUMENT:
|
| 97 |
-
await client.send_document(chat_id, note.file, caption=text, reply_to_message_id=reply_id, parse_mode=parse_mode, reply_markup=keyboard)
|
| 98 |
elif note.msgtype == Types.PHOTO:
|
| 99 |
-
await client.send_photo(chat_id, note.file, caption=text, reply_to_message_id=reply_id, parse_mode=parse_mode, reply_markup=keyboard)
|
| 100 |
elif note.msgtype == Types.AUDIO:
|
| 101 |
-
await client.send_audio(chat_id, note.file, caption=text, reply_to_message_id=reply_id, parse_mode=parse_mode, reply_markup=keyboard)
|
| 102 |
elif note.msgtype == Types.VOICE:
|
| 103 |
-
await client.send_voice(chat_id, note.file, caption=text, reply_to_message_id=reply_id, parse_mode=parse_mode, reply_markup=keyboard)
|
| 104 |
elif note.msgtype == Types.VIDEO:
|
| 105 |
-
await client.send_video(chat_id, note.file, caption=text, reply_to_message_id=reply_id, parse_mode=parse_mode, reply_markup=keyboard)
|
| 106 |
elif note.msgtype == Types.VIDEO_NOTE:
|
| 107 |
-
await client.send_video_note(chat_id, note.file, reply_to_message_id=reply_id, reply_markup=keyboard)
|
| 108 |
|
| 109 |
except RPCError as e:
|
| 110 |
await message.reply_text(f"This note could not be sent. Error: {e.MESSAGE}")
|
|
@@ -207,6 +184,23 @@ async def clearall_btn(client: Client, query):
|
|
| 207 |
else:
|
| 208 |
await query.answer("Only the owner of the chat can do this.", show_alert=True)
|
| 209 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
|
| 211 |
@DisableAbleCommandHandler(["notes", "saved"])
|
| 212 |
@connection_status
|
|
|
|
| 12 |
|
| 13 |
import QueenNoxi.modules.sql.notes_sql as sql
|
| 14 |
from QueenNoxi import DRAGONS, pbot, BOT_ID, SUPPORT_CHAT
|
| 15 |
+
from QueenNoxi import DRAGONS, pbot, BOT_ID, SUPPORT_CHAT, LOGGER
|
| 16 |
from QueenNoxi.modules.disable import DisableAbleCommandHandler
|
| 17 |
from QueenNoxi.modules.helper_funcs.chat_status import connection_status, user_admin
|
| 18 |
from QueenNoxi.modules.helper_funcs.misc import build_keyboard, revert_buttons
|
|
|
|
| 21 |
escape_invalid_curly_brackets,
|
| 22 |
escape_markdown
|
| 23 |
)
|
| 24 |
+
from QueenNoxi.modules.helper_funcs.formatters import format_message
|
| 25 |
|
| 26 |
# Do not async
|
| 27 |
@connection_status
|
|
|
|
| 44 |
sql.rm_note(chat_id, notename)
|
| 45 |
return
|
| 46 |
|
| 47 |
+
res, flags = await format_message(note.value, message.from_user, message.chat)
|
| 48 |
+
text = res
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
buttons = sql.get_buttons(chat_id, notename)
|
| 51 |
keyb = []
|
|
|
|
| 66 |
reply_to_message_id=reply_id,
|
| 67 |
parse_mode=parse_mode,
|
| 68 |
reply_markup=keyboard,
|
| 69 |
+
**flags
|
| 70 |
)
|
| 71 |
elif note.msgtype == Types.STICKER:
|
| 72 |
+
await client.send_sticker(chat_id, note.file, reply_to_message_id=reply_id, reply_markup=keyboard, **flags)
|
| 73 |
elif note.msgtype == Types.DOCUMENT:
|
| 74 |
+
await client.send_document(chat_id, note.file, caption=text, reply_to_message_id=reply_id, parse_mode=parse_mode, reply_markup=keyboard, **flags)
|
| 75 |
elif note.msgtype == Types.PHOTO:
|
| 76 |
+
await client.send_photo(chat_id, note.file, caption=text, reply_to_message_id=reply_id, parse_mode=parse_mode, reply_markup=keyboard, **flags)
|
| 77 |
elif note.msgtype == Types.AUDIO:
|
| 78 |
+
await client.send_audio(chat_id, note.file, caption=text, reply_to_message_id=reply_id, parse_mode=parse_mode, reply_markup=keyboard, **flags)
|
| 79 |
elif note.msgtype == Types.VOICE:
|
| 80 |
+
await client.send_voice(chat_id, note.file, caption=text, reply_to_message_id=reply_id, parse_mode=parse_mode, reply_markup=keyboard, **flags)
|
| 81 |
elif note.msgtype == Types.VIDEO:
|
| 82 |
+
await client.send_video(chat_id, note.file, caption=text, reply_to_message_id=reply_id, parse_mode=parse_mode, reply_markup=keyboard, **flags)
|
| 83 |
elif note.msgtype == Types.VIDEO_NOTE:
|
| 84 |
+
await client.send_video_note(chat_id, note.file, reply_to_message_id=reply_id, reply_markup=keyboard, **flags)
|
| 85 |
|
| 86 |
except RPCError as e:
|
| 87 |
await message.reply_text(f"This note could not be sent. Error: {e.MESSAGE}")
|
|
|
|
| 184 |
else:
|
| 185 |
await query.answer("Only the owner of the chat can do this.", show_alert=True)
|
| 186 |
|
| 187 |
+
@pbot.on_callback_query(filters.regex(r"^note_.*"))
|
| 188 |
+
async def note_callback(client: Client, query):
|
| 189 |
+
notename = query.data.split("_", 1)[1]
|
| 190 |
+
await get(client, query.message, notename, show_none=False)
|
| 191 |
+
await query.answer()
|
| 192 |
+
|
| 193 |
+
@pbot.on_callback_query(filters.regex(r"^paginate_.*"))
|
| 194 |
+
async def paginate_callback(client: Client, query):
|
| 195 |
+
action = query.data.split("_", 1)[1]
|
| 196 |
+
# Simple pagination placeholder logic
|
| 197 |
+
if action == "btn_next":
|
| 198 |
+
await query.answer("Next Page (Sample logic)", show_alert=True)
|
| 199 |
+
elif action == "btn_back":
|
| 200 |
+
await query.answer("Back Page (Sample logic)", show_alert=True)
|
| 201 |
+
elif action == "btn_home":
|
| 202 |
+
await query.answer("Home Page (Sample logic)", show_alert=True)
|
| 203 |
+
|
| 204 |
|
| 205 |
@DisableAbleCommandHandler(["notes", "saved"])
|
| 206 |
@connection_status
|
QueenNoxi/modules/welcome.py
CHANGED
|
@@ -40,6 +40,7 @@ from QueenNoxi.modules.helper_funcs.string_handling import (
|
|
| 40 |
escape_invalid_curly_brackets,
|
| 41 |
markdown_parser,
|
| 42 |
)
|
|
|
|
| 43 |
from QueenNoxi.modules.log_channel import loggable
|
| 44 |
from QueenNoxi.modules.sql.global_bans_sql import is_user_gbanned
|
| 45 |
|
|
@@ -77,7 +78,7 @@ async def send(message: Message, text: str, keyboard: InlineKeyboardMarkup, back
|
|
| 77 |
)
|
| 78 |
except RPCError as excp:
|
| 79 |
LOGGER.error(f"Error sending welcome: {excp}")
|
| 80 |
-
return await message.reply_text(backup_message)
|
| 81 |
|
| 82 |
@pbot.on_message(filters.new_chat_members & filters.group)
|
| 83 |
@loggable
|
|
@@ -118,39 +119,29 @@ async def new_member(client: Client, message: Message):
|
|
| 118 |
keyb = build_keyboard(buttons)
|
| 119 |
keyboard = InlineKeyboardMarkup(keyb)
|
| 120 |
|
| 121 |
-
first_name = new_mem.first_name or "User"
|
| 122 |
-
last_name = new_mem.last_name or ""
|
| 123 |
-
fullname = f"{first_name} {last_name}".strip()
|
| 124 |
-
count = await chat.get_member_count()
|
| 125 |
-
mention = new_mem.mention
|
| 126 |
-
username = f"@{new_mem.username}" if new_mem.username else mention
|
| 127 |
-
|
| 128 |
if cust_welcome:
|
| 129 |
if cust_welcome == sql.DEFAULT_WELCOME:
|
| 130 |
-
cust_welcome = random.choice(sql.DEFAULT_WELCOME_MESSAGES)
|
| 131 |
-
|
| 132 |
-
res =
|
| 133 |
-
first=first_name,
|
| 134 |
-
last=last_name or first_name,
|
| 135 |
-
fullname=fullname,
|
| 136 |
-
username=username,
|
| 137 |
-
mention=mention,
|
| 138 |
-
count=count,
|
| 139 |
-
chatname=chat.title,
|
| 140 |
-
id=new_mem.id
|
| 141 |
-
)
|
| 142 |
else:
|
| 143 |
-
res = random.choice(sql.DEFAULT_WELCOME_MESSAGES)
|
| 144 |
|
| 145 |
if welc_type == Types.TEXT or welc_type == Types.BUTTON_TEXT:
|
| 146 |
-
sent = await
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
else:
|
| 148 |
# Handle media welcomes
|
| 149 |
sent = await client.send_cached_media(
|
| 150 |
chat.id,
|
| 151 |
cust_content,
|
| 152 |
caption=res,
|
| 153 |
-
reply_markup=keyboard
|
|
|
|
| 154 |
)
|
| 155 |
|
| 156 |
# Clean previous welcome
|
|
|
|
| 40 |
escape_invalid_curly_brackets,
|
| 41 |
markdown_parser,
|
| 42 |
)
|
| 43 |
+
from QueenNoxi.modules.helper_funcs.formatters import format_message
|
| 44 |
from QueenNoxi.modules.log_channel import loggable
|
| 45 |
from QueenNoxi.modules.sql.global_bans_sql import is_user_gbanned
|
| 46 |
|
|
|
|
| 78 |
)
|
| 79 |
except RPCError as excp:
|
| 80 |
LOGGER.error(f"Error sending welcome: {excp}")
|
| 81 |
+
return await message.reply_text(backup_message, reply_to_message_id=reply_to)
|
| 82 |
|
| 83 |
@pbot.on_message(filters.new_chat_members & filters.group)
|
| 84 |
@loggable
|
|
|
|
| 119 |
keyb = build_keyboard(buttons)
|
| 120 |
keyboard = InlineKeyboardMarkup(keyb)
|
| 121 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
if cust_welcome:
|
| 123 |
if cust_welcome == sql.DEFAULT_WELCOME:
|
| 124 |
+
cust_welcome = random.choice(sql.DEFAULT_WELCOME_MESSAGES)
|
| 125 |
+
|
| 126 |
+
res, flags = await format_message(cust_welcome, new_mem, chat)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
else:
|
| 128 |
+
res, flags = await format_message(random.choice(sql.DEFAULT_WELCOME_MESSAGES), new_mem, chat)
|
| 129 |
|
| 130 |
if welc_type == Types.TEXT or welc_type == Types.BUTTON_TEXT:
|
| 131 |
+
sent = await message.reply_text(
|
| 132 |
+
res,
|
| 133 |
+
reply_markup=keyboard,
|
| 134 |
+
reply_to_message_id=message.id if not sql.clean_service(chat.id) else None,
|
| 135 |
+
**flags
|
| 136 |
+
)
|
| 137 |
else:
|
| 138 |
# Handle media welcomes
|
| 139 |
sent = await client.send_cached_media(
|
| 140 |
chat.id,
|
| 141 |
cust_content,
|
| 142 |
caption=res,
|
| 143 |
+
reply_markup=keyboard,
|
| 144 |
+
**flags
|
| 145 |
)
|
| 146 |
|
| 147 |
# Clean previous welcome
|
README.md
CHANGED
|
@@ -8,7 +8,7 @@ pinned: false
|
|
| 8 |
---
|
| 9 |
|
| 10 |
<h1 align="center">
|
| 11 |
-
<b>๐ธ
|
| 12 |
</h1>
|
| 13 |
|
| 14 |
<p align="center">
|
|
@@ -29,31 +29,20 @@ pinned: false
|
|
| 29 |
|
| 30 |
- ๐ก๏ธ **Advanced Protection**: Anti-Flood, Anti-Spam, and GBans.
|
| 31 |
- ๐ฌ **Unique Animations**: Robust Tenor-only animation system with local caching (reliable even when API limits hit).
|
|
|
|
| 32 |
- ๐ง **AI Integration**: Built-in Chatbot and ChatGPT features.
|
| 33 |
-
- ๐ต **Music Integration**: Manage your group's music with ease.
|
| 34 |
- โ๏ธ **Highly Customizable**: Easy to configure via environment variables.
|
| 35 |
|
| 36 |
## ๐ Deployment
|
| 37 |
|
| 38 |
### Heroku / HuggingFace
|
| 39 |
-
Click the button below to deploy your own instance:
|
| 40 |
-
|
| 41 |
[](https://heroku.com/deploy?template=https://github.com/AlexaInc/queen-noxi)
|
| 42 |
|
| 43 |
### Manual Setup
|
| 44 |
-
1. Clone
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
2. Install requirements:
|
| 49 |
-
```bash
|
| 50 |
-
pip install -r requirements.txt
|
| 51 |
-
```
|
| 52 |
-
3. Configure your `.env` following `SETUP_GUIDE.md`.
|
| 53 |
-
4. Run the bot:
|
| 54 |
-
```bash
|
| 55 |
-
python3 -m QueenNoxi
|
| 56 |
-
```
|
| 57 |
|
| 58 |
## ๐ Credits
|
| 59 |
- **Author**: [Alexainc](https://github.com/AlexaInc)
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
<h1 align="center">
|
| 11 |
+
<b>๐ธ Queen Noxi ๐ธ</b>
|
| 12 |
</h1>
|
| 13 |
|
| 14 |
<p align="center">
|
|
|
|
| 29 |
|
| 30 |
- ๐ก๏ธ **Advanced Protection**: Anti-Flood, Anti-Spam, and GBans.
|
| 31 |
- ๐ฌ **Unique Animations**: Robust Tenor-only animation system with local caching (reliable even when API limits hit).
|
| 32 |
+
- ๐จ **Animated Admin Commands**: Styled `/aban`, `/amute`, and `/aunmute` with premium visual effects.
|
| 33 |
- ๐ง **AI Integration**: Built-in Chatbot and ChatGPT features.
|
|
|
|
| 34 |
- โ๏ธ **Highly Customizable**: Easy to configure via environment variables.
|
| 35 |
|
| 36 |
## ๐ Deployment
|
| 37 |
|
| 38 |
### Heroku / HuggingFace
|
|
|
|
|
|
|
| 39 |
[](https://heroku.com/deploy?template=https://github.com/AlexaInc/queen-noxi)
|
| 40 |
|
| 41 |
### Manual Setup
|
| 42 |
+
1. Clone: `git clone https://github.com/AlexaInc/queen-noxi.git`
|
| 43 |
+
2. Install: `pip install -r requirements.txt`
|
| 44 |
+
3. Configure `.env` from `SETUP_GUIDE.md`.
|
| 45 |
+
4. Run: `python3 -m QueenNoxi`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
|
| 47 |
## ๐ Credits
|
| 48 |
- **Author**: [Alexainc](https://github.com/AlexaInc)
|