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 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
- filt.reply_text,
106
  reply_markup=keyboard,
107
- disable_web_page_preview=True
 
108
  )
109
  else:
110
  await client.send_cached_media(
111
  chat_id,
112
  filt.file_id,
113
- caption=filt.reply_text,
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(InlineKeyboardButton(btn.name, url=btn.url))
106
  else:
107
- keyb.append([InlineKeyboardButton(btn.name, url=btn.url)])
108
  return keyb
109
 
110
  def revert_buttons(buttons):
111
  res = ""
112
  for btn in buttons:
113
- if btn.same_line:
114
- res += "\n[{}](buttonurl://{}:same)".format(btn.name, btn.url)
115
- else:
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
- text = msg.text.split(None, 2)[2]
126
- data_type = Types.TEXT
 
127
 
128
  elif msg.reply_to_message:
129
  reply = msg.reply_to_message
130
- if reply.text:
131
- text = reply.text
132
- data_type = Types.TEXT
133
- elif reply.sticker:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- def button_markdown_parser(txt: str, entities: List[MessageEntity] = None, offset: int = 0) -> (str, List):
 
 
 
 
 
 
 
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
- buttons.append((match.group(2), match.group(3), bool(match.group(4))))
 
 
 
 
 
 
 
 
 
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
- VALID_NOTE_FORMATTERS = [
46
- "first",
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).format(first=first_name)
131
-
132
- res = cust_welcome.format(
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).format(first=first_name)
144
 
145
  if welc_type == Types.TEXT or welc_type == Types.BUTTON_TEXT:
146
- sent = await send(message, res, keyboard, "Welcome!")
 
 
 
 
 
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>๐Ÿ‘ธ QueenNoxi ๐Ÿ‘ธ</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
  [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/AlexaInc/queen-noxi)
42
 
43
  ### Manual Setup
44
- 1. Clone the repo:
45
- ```bash
46
- git clone https://github.com/AlexaInc/queen-noxi.git
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
  [![Deploy](https://www.herokucdn.com/deploy/button.svg)](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)