forked from MN-BOTS/Mn-auto-delete-telegram-bot
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutils.py
More file actions
290 lines (247 loc) · 9.75 KB
/
Copy pathutils.py
File metadata and controls
290 lines (247 loc) · 9.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
import asyncio
from pyrogram.errors import (
FloodWait,
UserIsBlocked,
InputUserDeactivated,
PeerIdInvalid,
ChatWriteForbidden,
ChannelPrivate,
ChatAdminRequired,
MessageDeleteForbidden,
MessageIdInvalid,
MessageNotModified,
QueryIdInvalid,
)
from pyrogram.enums import ChatMemberStatus
from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
import database
OWNER_STATUSES = {ChatMemberStatus.OWNER, ChatMemberStatus.ADMINISTRATOR}
BLOCK_ERRORS = (UserIsBlocked, InputUserDeactivated, PeerIdInvalid)
CHAT_ERRORS = (ChatWriteForbidden, ChannelPrivate, ChatAdminRequired)
DELETE_IGNORED_ERRORS = (MessageDeleteForbidden, MessageIdInvalid)
EDIT_IGNORED_ERRORS = (MessageNotModified, MessageIdInvalid)
MAX_DELETE_SECONDS = 3600
REPO_URL = "https://github.com/MN-BOTS/Mn-auto-delete-telegram-bot"
DEV_URL = "https://github.com/mntgxo"
def human_time(seconds):
seconds = int(seconds)
if seconds <= 0:
return "Off"
units = ((3600, "h"), (60, "m"), (1, "s"))
parts = []
for size, suffix in units:
value, seconds = divmod(seconds, size)
if value:
parts.append(f"{value}{suffix}")
return " ".join(parts) or "0s"
def parse_time(value):
if not value:
raise ValueError("missing time")
raw = value.strip().lower()
if raw in {"off", "disable", "disabled", "0"}:
return 0
suffix = raw[-1]
number = raw[:-1] if suffix.isalpha() else raw
if not number.isdigit():
raise ValueError("invalid time")
amount = int(number)
multipliers = {"s": 1, "m": 60, "h": 3600}
if suffix.isalpha() and suffix not in multipliers:
raise ValueError("supported units are s, m, and h")
seconds = amount * multipliers.get(suffix, 1)
if seconds < 0 or seconds > MAX_DELETE_SECONDS:
raise ValueError("auto-delete time cannot be more than 1 hour")
return seconds
async def sleep_flood(seconds):
await asyncio.sleep(int(seconds) + 1)
async def safe_delete(client, chat_id, message_id):
while True:
try:
await client.delete_messages(chat_id, message_id)
database.finish_scheduled_message(chat_id, message_id, "deleted")
return True
except FloodWait as fw:
await sleep_flood(fw.value)
except DELETE_IGNORED_ERRORS as err:
database.finish_scheduled_message(chat_id, message_id, "skipped", str(err))
return False
except Exception as err:
database.finish_scheduled_message(chat_id, message_id, "failed", str(err))
return False
async def safe_delete_plain(client, chat_id, message_id):
if not message_id:
return False
while True:
try:
await client.delete_messages(chat_id, message_id)
return True
except FloodWait as fw:
await sleep_flood(fw.value)
except Exception:
return False
async def forward_with_flood(message, chat_id):
while True:
try:
await message.forward(chat_id)
return "sent"
except FloodWait as fw:
await sleep_flood(fw.value)
except BLOCK_ERRORS:
database.remove_user(chat_id)
return "blocked_removed"
except CHAT_ERRORS:
return "forbidden"
except Exception:
return "failed"
async def send_with_flood(client, chat_id, text, **kwargs):
while True:
try:
return await client.send_message(chat_id, text, **kwargs)
except FloodWait as fw:
await sleep_flood(fw.value)
except Exception:
return None
async def safe_reply(message, text, **kwargs):
while True:
try:
return await message.reply_text(text, **kwargs)
except FloodWait as fw:
await sleep_flood(fw.value)
except CHAT_ERRORS:
return None
except Exception:
return None
async def safe_edit_message(message, text, **kwargs):
while True:
try:
return await message.edit_text(text, **kwargs)
except FloodWait as fw:
await sleep_flood(fw.value)
except MessageNotModified:
return message
except EDIT_IGNORED_ERRORS:
return None
except CHAT_ERRORS:
return None
except Exception:
return None
async def safe_callback_answer(query, text=None, **kwargs):
while True:
try:
if text is None:
return await query.answer(**kwargs)
return await query.answer(text, **kwargs)
except FloodWait as fw:
await sleep_flood(fw.value)
except QueryIdInvalid:
return None
except Exception:
return None
async def is_chat_admin(client, chat_id, user_id):
if not user_id:
return False
while True:
try:
member = await client.get_chat_member(chat_id, user_id)
return member.status in OWNER_STATUSES
except FloodWait as fw:
await sleep_flood(fw.value)
except Exception:
return False
def settings_keyboard(chat_id, enabled):
toggle = "Disable 📴" if enabled else "Enable ✅"
action = "off" if enabled else "on"
return InlineKeyboardMarkup(
[
[InlineKeyboardButton(toggle, callback_data=f"set:{chat_id}:{action}")],
[
InlineKeyboardButton("15s", callback_data=f"delay:{chat_id}:15"),
InlineKeyboardButton("30s", callback_data=f"delay:{chat_id}:30"),
InlineKeyboardButton("1m", callback_data=f"delay:{chat_id}:60"),
],
[
InlineKeyboardButton("5m", callback_data=f"delay:{chat_id}:300"),
InlineKeyboardButton("30m", callback_data=f"delay:{chat_id}:1800"),
InlineKeyboardButton("1h Max", callback_data=f"delay:{chat_id}:3600"),
],
[InlineKeyboardButton("Help 📘", callback_data=f"chathelp:{chat_id}"), InlineKeyboardButton("Refresh 🔄", callback_data=f"settings:{chat_id}")],
]
)
def start_keyboard(fsubs=None):
rows = []
for sub in fsubs or []:
label = sub.get("title") or sub.get("username") or str(sub["_id"])
link = sub.get("invite_link") or (f"https://t.me/{sub['username']}" if sub.get("username") else None)
if link:
rows.append([InlineKeyboardButton(f"Join {label}", url=link)])
rows.append([InlineKeyboardButton("✅ I Joined", callback_data="check_fsub")])
rows.append([InlineKeyboardButton("Repo ⭐", url=REPO_URL)])
return InlineKeyboardMarkup(rows)
def main_menu_keyboard(is_owner=False):
rows = [
[InlineKeyboardButton("Commands 📚", callback_data="start:commands"), InlineKeyboardButton("Features ✨", callback_data="start:features")],
[InlineKeyboardButton("Credits 👨💻", callback_data="start:credits"), InlineKeyboardButton("Help 🛠", callback_data="start:help")],
[InlineKeyboardButton("Repo ⭐", url=REPO_URL), InlineKeyboardButton("Developer", url=DEV_URL)],
]
if is_owner:
rows.insert(0, [InlineKeyboardButton("Owner Panel 👑", callback_data="owner:panel")])
return InlineKeyboardMarkup(rows)
def back_home_keyboard(is_owner=False):
rows = [[InlineKeyboardButton("🏠 Home", callback_data="start:home")]]
if is_owner:
rows.append([InlineKeyboardButton("Owner Panel 👑", callback_data="owner:panel")])
rows.append([InlineKeyboardButton("Repo ⭐", url=REPO_URL)])
return InlineKeyboardMarkup(rows)
def home_text():
return (
"**👋 Welcome to MN Auto Delete Bot**\n\n"
"Manage auto-delete timers with MongoDB persistence, rich buttons, force-sub, broadcasts, and restart-safe schedules.\n\n"
"Default mode is safe: no chat deletes anything until its admin enables auto-delete."
)
def commands_text():
return (
"**📚 Commands**\n\n"
"**Group/Channel Admin**\n"
"• `/settings` - open settings UI\n"
"• `/setdelete 30s` - set delete time\n"
"• `/setdelete off` - disable auto-delete\n"
"• `/deleteon` / `/deleteoff` - quick toggle\n\n"
"**Owner PM**\n"
"• `/admin` - owner panel\n"
"• `/broadcast` - reply and forward everywhere\n"
"• `/addfsub`, `/delfsub`, `/fsubs` - force-sub manager"
)
def features_text():
return (
"**✨ Features**\n\n"
"• Per-group/channel custom timers\n"
"• Max delete time: 1 hour\n"
"• MongoDB restart recovery\n"
"• Proper FloodWait handling\n"
"• Multiple force-sub chats\n"
"• Forward-method broadcasts\n"
"• Blocked PM users removed from DB\n"
"• Hourly group notice replaces the previous notice"
)
def credits_text():
return f"**👨💻 Credits**\n\nDeveloper: GitHub.com/mntgxo\nRepo: {REPO_URL}"
def settings_text(chat, settings):
state = "Enabled ✅" if settings.get("enabled") else "Disabled 📴"
delay = human_time(settings.get("delete_delay", 0))
return (
f"**🛠 Auto Delete Settings**\n\n"
f"**Chat:** {chat.title or chat.id}\n"
f"**ID:** `{chat.id}`\n"
f"**Status:** {state}\n"
f"**Delete Time:** `{delay}`\n"
f"**Limit:** `1 hour max`\n\n"
"Use buttons below or `/setdelete 30s`, `/setdelete 5m`, `/setdelete 1h`, `/setdelete off`."
)
def chat_help_text():
return (
"**📘 Auto Delete Help**\n\n"
"Only group/channel owners and admins can change settings.\n"
"Allowed units: seconds (`s`), minutes (`m`), hours (`h`).\n"
"Maximum allowed auto-delete time is **1 hour**.\n\n"
"Examples: `/setdelete 15s`, `/setdelete 5m`, `/setdelete 1h`, `/setdelete off`."
)