Karma
commited on
Commit
·
f10725a
1
Parent(s):
9de9c9d
Create notes.py
Browse files- Mikobot/plugins/notes.py +629 -0
Mikobot/plugins/notes.py
ADDED
@@ -0,0 +1,629 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# <============================================== IMPORTS =========================================================>
|
2 |
+
import ast
|
3 |
+
import random
|
4 |
+
import re
|
5 |
+
from io import BytesIO
|
6 |
+
|
7 |
+
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Message, Update
|
8 |
+
from telegram.constants import MessageLimit, ParseMode
|
9 |
+
from telegram.error import BadRequest
|
10 |
+
from telegram.ext import (
|
11 |
+
CallbackQueryHandler,
|
12 |
+
CommandHandler,
|
13 |
+
ContextTypes,
|
14 |
+
MessageHandler,
|
15 |
+
filters,
|
16 |
+
)
|
17 |
+
from telegram.helpers import escape_markdown, mention_markdown
|
18 |
+
|
19 |
+
import Database.sql.notes_sql as sql
|
20 |
+
from Mikobot import DRAGONS, LOGGER, MESSAGE_DUMP, SUPPORT_CHAT, dispatcher, function
|
21 |
+
from Mikobot.plugins.disable import DisableAbleCommandHandler
|
22 |
+
from Mikobot.plugins.helper_funcs.chat_status import check_admin, connection_status
|
23 |
+
from Mikobot.plugins.helper_funcs.misc import build_keyboard, revert_buttons
|
24 |
+
from Mikobot.plugins.helper_funcs.msg_types import get_note_type
|
25 |
+
from Mikobot.plugins.helper_funcs.string_handling import (
|
26 |
+
escape_invalid_curly_brackets,
|
27 |
+
markdown_to_html,
|
28 |
+
)
|
29 |
+
|
30 |
+
from .cust_filters import MessageHandlerChecker
|
31 |
+
|
32 |
+
# <=======================================================================================================>
|
33 |
+
|
34 |
+
FILE_MATCHER = re.compile(r"^###file_id(!photo)?###:(.*?)(?:\s|$)")
|
35 |
+
STICKER_MATCHER = re.compile(r"^###sticker(!photo)?###:")
|
36 |
+
BUTTON_MATCHER = re.compile(r"^###button(!photo)?###:(.*?)(?:\s|$)")
|
37 |
+
MYFILE_MATCHER = re.compile(r"^###file(!photo)?###:")
|
38 |
+
MYPHOTO_MATCHER = re.compile(r"^###photo(!photo)?###:")
|
39 |
+
MYAUDIO_MATCHER = re.compile(r"^###audio(!photo)?###:")
|
40 |
+
MYVOICE_MATCHER = re.compile(r"^###voice(!photo)?###:")
|
41 |
+
MYVIDEO_MATCHER = re.compile(r"^###video(!photo)?###:")
|
42 |
+
MYVIDEONOTE_MATCHER = re.compile(r"^###video_note(!photo)?###:")
|
43 |
+
|
44 |
+
ENUM_FUNC_MAP = {
|
45 |
+
sql.Types.TEXT.value: dispatcher.bot.send_message,
|
46 |
+
sql.Types.BUTTON_TEXT.value: dispatcher.bot.send_message,
|
47 |
+
sql.Types.STICKER.value: dispatcher.bot.send_sticker,
|
48 |
+
sql.Types.DOCUMENT.value: dispatcher.bot.send_document,
|
49 |
+
sql.Types.PHOTO.value: dispatcher.bot.send_photo,
|
50 |
+
sql.Types.AUDIO.value: dispatcher.bot.send_audio,
|
51 |
+
sql.Types.VOICE.value: dispatcher.bot.send_voice,
|
52 |
+
sql.Types.VIDEO.value: dispatcher.bot.send_video,
|
53 |
+
}
|
54 |
+
|
55 |
+
|
56 |
+
# <================================================ FUNCTION =======================================================>
|
57 |
+
async def get(
|
58 |
+
update: Update,
|
59 |
+
context: ContextTypes.DEFAULT_TYPE,
|
60 |
+
notename,
|
61 |
+
show_none=True,
|
62 |
+
no_format=False,
|
63 |
+
):
|
64 |
+
bot = context.bot
|
65 |
+
chat_id = update.effective_message.chat.id
|
66 |
+
chat = update.effective_chat
|
67 |
+
note_chat_id = update.effective_chat.id
|
68 |
+
note = sql.get_note(note_chat_id, notename)
|
69 |
+
message = update.effective_message # type: Optional[Message]
|
70 |
+
|
71 |
+
if note:
|
72 |
+
if MessageHandlerChecker.check_user(update.effective_user.id):
|
73 |
+
return
|
74 |
+
# If we're replying to a message, reply to that message (unless it's an error)
|
75 |
+
if (
|
76 |
+
message.reply_to_message
|
77 |
+
and not message.reply_to_message.forum_topic_created
|
78 |
+
):
|
79 |
+
reply_id = message.reply_to_message.message_id
|
80 |
+
else:
|
81 |
+
reply_id = message.message_id
|
82 |
+
if note.is_reply:
|
83 |
+
if MESSAGE_DUMP:
|
84 |
+
try:
|
85 |
+
await bot.forward_message(
|
86 |
+
chat_id=chat_id,
|
87 |
+
from_chat_id=MESSAGE_DUMP,
|
88 |
+
message_id=note.value,
|
89 |
+
)
|
90 |
+
except BadRequest as excp:
|
91 |
+
if excp.message == "Message to forward not found":
|
92 |
+
await message.reply_text(
|
93 |
+
"This message seems to have been lost - I'll remove it "
|
94 |
+
"from your notes list.",
|
95 |
+
)
|
96 |
+
sql.rm_note(note_chat_id, notename)
|
97 |
+
else:
|
98 |
+
raise
|
99 |
+
else:
|
100 |
+
try:
|
101 |
+
await bot.forward_message(
|
102 |
+
chat_id=chat_id,
|
103 |
+
from_chat_id=chat_id,
|
104 |
+
message_id=markdown_to_html(note.value),
|
105 |
+
)
|
106 |
+
except BadRequest as excp:
|
107 |
+
if excp.message == "Message to forward not found":
|
108 |
+
await message.reply_text(
|
109 |
+
"Looks like the original sender of this note has deleted "
|
110 |
+
"their message - sorry! Get your bot admin to start using a "
|
111 |
+
"message dump to avoid this. I'll remove this note from "
|
112 |
+
"your saved notes.",
|
113 |
+
)
|
114 |
+
sql.rm_note(note_chat_id, notename)
|
115 |
+
else:
|
116 |
+
raise
|
117 |
+
else:
|
118 |
+
VALID_NOTE_FORMATTERS = [
|
119 |
+
"first",
|
120 |
+
"last",
|
121 |
+
"fullname",
|
122 |
+
"username",
|
123 |
+
"id",
|
124 |
+
"chatname",
|
125 |
+
"mention",
|
126 |
+
]
|
127 |
+
valid_format = escape_invalid_curly_brackets(
|
128 |
+
note.value,
|
129 |
+
VALID_NOTE_FORMATTERS,
|
130 |
+
)
|
131 |
+
if valid_format:
|
132 |
+
if not no_format:
|
133 |
+
if "%%%" in valid_format:
|
134 |
+
split = valid_format.split("%%%")
|
135 |
+
if all(split):
|
136 |
+
text = random.choice(split)
|
137 |
+
else:
|
138 |
+
text = valid_format
|
139 |
+
else:
|
140 |
+
text = valid_format
|
141 |
+
else:
|
142 |
+
text = valid_format
|
143 |
+
text = text.format(
|
144 |
+
first=escape_markdown(message.from_user.first_name),
|
145 |
+
last=escape_markdown(
|
146 |
+
message.from_user.last_name or message.from_user.first_name,
|
147 |
+
),
|
148 |
+
fullname=escape_markdown(
|
149 |
+
" ".join(
|
150 |
+
[message.from_user.first_name, message.from_user.last_name]
|
151 |
+
if message.from_user.last_name
|
152 |
+
else [message.from_user.first_name],
|
153 |
+
),
|
154 |
+
),
|
155 |
+
username="@" + message.from_user.username
|
156 |
+
if message.from_user.username
|
157 |
+
else mention_markdown(
|
158 |
+
message.from_user.id,
|
159 |
+
message.from_user.first_name,
|
160 |
+
),
|
161 |
+
mention=mention_markdown(
|
162 |
+
message.from_user.id,
|
163 |
+
message.from_user.first_name,
|
164 |
+
),
|
165 |
+
chatname=escape_markdown(
|
166 |
+
message.chat.title
|
167 |
+
if message.chat.type != "private"
|
168 |
+
else message.from_user.first_name,
|
169 |
+
),
|
170 |
+
id=message.from_user.id,
|
171 |
+
)
|
172 |
+
else:
|
173 |
+
text = ""
|
174 |
+
|
175 |
+
keyb = []
|
176 |
+
parseMode = ParseMode.HTML
|
177 |
+
buttons = sql.get_buttons(note_chat_id, notename)
|
178 |
+
if no_format:
|
179 |
+
parseMode = None
|
180 |
+
text += revert_buttons(buttons)
|
181 |
+
else:
|
182 |
+
keyb = build_keyboard(buttons)
|
183 |
+
|
184 |
+
keyboard = InlineKeyboardMarkup(keyb)
|
185 |
+
|
186 |
+
try:
|
187 |
+
if note.msgtype in (sql.Types.BUTTON_TEXT, sql.Types.TEXT):
|
188 |
+
await bot.send_message(
|
189 |
+
chat_id,
|
190 |
+
markdown_to_html(text),
|
191 |
+
reply_to_message_id=reply_id,
|
192 |
+
parse_mode=parseMode,
|
193 |
+
disable_web_page_preview=True,
|
194 |
+
reply_markup=keyboard,
|
195 |
+
message_thread_id=message.message_thread_id
|
196 |
+
if chat.is_forum
|
197 |
+
else None,
|
198 |
+
)
|
199 |
+
else:
|
200 |
+
await ENUM_FUNC_MAP[note.msgtype](
|
201 |
+
chat_id,
|
202 |
+
note.file,
|
203 |
+
caption=markdown_to_html(text),
|
204 |
+
reply_to_message_id=reply_id,
|
205 |
+
parse_mode=parseMode,
|
206 |
+
disable_web_page_preview=True,
|
207 |
+
reply_markup=keyboard,
|
208 |
+
message_thread_id=message.message_thread_id
|
209 |
+
if chat.is_forum
|
210 |
+
else None,
|
211 |
+
)
|
212 |
+
|
213 |
+
except BadRequest as excp:
|
214 |
+
if excp.message == "Entity_mention_user_invalid":
|
215 |
+
await message.reply_text(
|
216 |
+
"Looks like you tried to mention someone I've never seen before. If you really "
|
217 |
+
"want to mention them, forward one of their messages to me, and I'll be able "
|
218 |
+
"to tag them!",
|
219 |
+
)
|
220 |
+
elif FILE_MATCHER.match(note.value):
|
221 |
+
await message.reply_text(
|
222 |
+
"This note was an incorrectly imported file from another bot - I can't use "
|
223 |
+
"it. If you really need it, you'll have to save it again. In "
|
224 |
+
"the meantime, I'll remove it from your notes list.",
|
225 |
+
)
|
226 |
+
sql.rm_note(note_chat_id, notename)
|
227 |
+
else:
|
228 |
+
await message.reply_text(
|
229 |
+
"This note could not be sent, as it is incorrectly formatted. Ask in "
|
230 |
+
f"@{SUPPORT_CHAT} if you can't figure out why!",
|
231 |
+
)
|
232 |
+
LOGGER.exception(
|
233 |
+
"Could not parse message #%s in chat %s",
|
234 |
+
notename,
|
235 |
+
str(note_chat_id),
|
236 |
+
)
|
237 |
+
LOGGER.warning("Message was: %s", str(note.value))
|
238 |
+
return
|
239 |
+
elif show_none:
|
240 |
+
await message.reply_text("This note doesn't exist")
|
241 |
+
|
242 |
+
|
243 |
+
@connection_status
|
244 |
+
async def cmd_get(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
245 |
+
bot, args = context.bot, context.args
|
246 |
+
if len(args) >= 2 and args[1].lower() == "noformat":
|
247 |
+
await get(update, context, args[0].lower(), show_none=True, no_format=True)
|
248 |
+
elif len(args) >= 1:
|
249 |
+
await get(update, context, args[0].lower(), show_none=True)
|
250 |
+
else:
|
251 |
+
await update.effective_message.reply_text("Get rekt")
|
252 |
+
|
253 |
+
|
254 |
+
@connection_status
|
255 |
+
async def hash_get(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
256 |
+
message = update.effective_message.text
|
257 |
+
fst_word = message.split()[0]
|
258 |
+
no_hash = fst_word[1:].lower()
|
259 |
+
await get(update, context, no_hash, show_none=False)
|
260 |
+
|
261 |
+
|
262 |
+
@connection_status
|
263 |
+
async def slash_get(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
264 |
+
message, chat_id = update.effective_message.text, update.effective_chat.id
|
265 |
+
no_slash = message[1:]
|
266 |
+
note_list = sql.get_all_chat_notes(chat_id)
|
267 |
+
|
268 |
+
try:
|
269 |
+
noteid = note_list[int(no_slash) - 1]
|
270 |
+
note_name = str(noteid).strip(">").split()[1]
|
271 |
+
await get(update, context, note_name, show_none=False)
|
272 |
+
except IndexError:
|
273 |
+
await update.effective_message.reply_text("Wrong Note ID 😾")
|
274 |
+
|
275 |
+
|
276 |
+
@connection_status
|
277 |
+
@check_admin(is_user=True)
|
278 |
+
async def save(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
279 |
+
chat_id = update.effective_chat.id
|
280 |
+
msg = update.effective_message # type: Optional[Message]
|
281 |
+
if len(context.args) < 1:
|
282 |
+
await msg.reply_text("You should give the note a name.")
|
283 |
+
return
|
284 |
+
|
285 |
+
note_name, text, data_type, content, buttons = get_note_type(msg)
|
286 |
+
note_name = note_name.lower()
|
287 |
+
if data_type is None:
|
288 |
+
await msg.reply_text("Dude, there's no note content")
|
289 |
+
return
|
290 |
+
|
291 |
+
sql.add_note_to_db(
|
292 |
+
chat_id,
|
293 |
+
note_name,
|
294 |
+
text,
|
295 |
+
data_type,
|
296 |
+
buttons=buttons,
|
297 |
+
file=content,
|
298 |
+
)
|
299 |
+
|
300 |
+
await msg.reply_text(
|
301 |
+
f"Yas! Added `{note_name}`.\nGet it with /get `{note_name}`, or `#{note_name}`",
|
302 |
+
parse_mode=ParseMode.MARKDOWN,
|
303 |
+
)
|
304 |
+
|
305 |
+
if (
|
306 |
+
msg.reply_to_message
|
307 |
+
and msg.reply_to_message.from_user.is_bot
|
308 |
+
and not msg.reply_to_message.forum_topic_created
|
309 |
+
):
|
310 |
+
if text:
|
311 |
+
await msg.reply_text(
|
312 |
+
"Seems like you're trying to save a message from a bot. Unfortunately, "
|
313 |
+
"bots can't forward bot messages, so I can't save the exact message. "
|
314 |
+
"\nI'll save all the text I can, but if you want more, you'll have to "
|
315 |
+
"forward the message yourself, and then save it.",
|
316 |
+
)
|
317 |
+
else:
|
318 |
+
await msg.reply_text(
|
319 |
+
"Bots are kinda handicapped by telegram, making it hard for bots to "
|
320 |
+
"interact with other bots, so I can't save this message "
|
321 |
+
"like I usually would - do you mind forwarding it and "
|
322 |
+
"then saving that new message? Thanks!",
|
323 |
+
)
|
324 |
+
return
|
325 |
+
|
326 |
+
|
327 |
+
@connection_status
|
328 |
+
@check_admin(is_user=True)
|
329 |
+
async def clear(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
330 |
+
args = context.args
|
331 |
+
chat_id = update.effective_chat.id
|
332 |
+
if len(args) >= 1:
|
333 |
+
notename = args[0].lower()
|
334 |
+
|
335 |
+
if sql.rm_note(chat_id, notename):
|
336 |
+
await update.effective_message.reply_text("Successfully removed note.")
|
337 |
+
else:
|
338 |
+
await update.effective_message.reply_text(
|
339 |
+
"That's not a note in my database!"
|
340 |
+
)
|
341 |
+
|
342 |
+
|
343 |
+
async def clearall(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
344 |
+
chat = update.effective_chat
|
345 |
+
user = update.effective_user
|
346 |
+
member = await chat.get_member(user.id)
|
347 |
+
if member.status != "creator" and user.id not in DRAGONS:
|
348 |
+
await update.effective_message.reply_text(
|
349 |
+
"Only the chat owner can clear all notes at once.",
|
350 |
+
)
|
351 |
+
else:
|
352 |
+
buttons = InlineKeyboardMarkup(
|
353 |
+
[
|
354 |
+
[
|
355 |
+
InlineKeyboardButton(
|
356 |
+
text="Delete all notes",
|
357 |
+
callback_data="notes_rmall",
|
358 |
+
),
|
359 |
+
],
|
360 |
+
[InlineKeyboardButton(text="Cancel", callback_data="notes_cancel")],
|
361 |
+
],
|
362 |
+
)
|
363 |
+
await update.effective_message.reply_text(
|
364 |
+
f"Are you sure you would like to clear ALL notes in {chat.title}? This action cannot be undone.",
|
365 |
+
reply_markup=buttons,
|
366 |
+
parse_mode=ParseMode.MARKDOWN,
|
367 |
+
)
|
368 |
+
|
369 |
+
|
370 |
+
async def clearall_btn(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
371 |
+
query = update.callback_query
|
372 |
+
chat = update.effective_chat
|
373 |
+
message = update.effective_message
|
374 |
+
member = await chat.get_member(query.from_user.id)
|
375 |
+
if query.data == "notes_rmall":
|
376 |
+
if member.status == "creator" or query.from_user.id in DRAGONS:
|
377 |
+
note_list = sql.get_all_chat_notes(chat.id)
|
378 |
+
try:
|
379 |
+
for notename in note_list:
|
380 |
+
note = notename.name.lower()
|
381 |
+
sql.rm_note(chat.id, note)
|
382 |
+
await message.edit_text("Deleted all notes.")
|
383 |
+
except BadRequest:
|
384 |
+
return
|
385 |
+
|
386 |
+
if member.status == "administrator":
|
387 |
+
await query.answer("Only owner of the chat can do this.")
|
388 |
+
|
389 |
+
if member.status == "member":
|
390 |
+
await query.answer("You need to be admin to do this.")
|
391 |
+
elif query.data == "notes_cancel":
|
392 |
+
if member.status == "creator" or query.from_user.id in DRAGONS:
|
393 |
+
await message.edit_text("Clearing of all notes has been cancelled.")
|
394 |
+
return
|
395 |
+
if member.status == "administrator":
|
396 |
+
await query.answer("Only owner of the chat can do this.")
|
397 |
+
if member.status == "member":
|
398 |
+
await query.answer("You need to be admin to do this.")
|
399 |
+
|
400 |
+
|
401 |
+
@connection_status
|
402 |
+
async def list_notes(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
403 |
+
chat_id = update.effective_chat.id
|
404 |
+
note_list = sql.get_all_chat_notes(chat_id)
|
405 |
+
notes = len(note_list) + 1
|
406 |
+
msg = "Get note by `/notenumber` or `#notename` \n\n *ID* *Note* \n"
|
407 |
+
for note_id, note in zip(range(1, notes), note_list):
|
408 |
+
if note_id < 10:
|
409 |
+
note_name = f"`{note_id:2}.` `#{(note.name.lower())}`\n"
|
410 |
+
else:
|
411 |
+
note_name = f"`{note_id}.` `#{(note.name.lower())}`\n"
|
412 |
+
if len(msg) + len(note_name) > MessageLimit.MAX_TEXT_LENGTH:
|
413 |
+
await update.effective_message.reply_text(
|
414 |
+
msg, parse_mode=ParseMode.MARKDOWN
|
415 |
+
)
|
416 |
+
msg = ""
|
417 |
+
msg += note_name
|
418 |
+
|
419 |
+
if not note_list:
|
420 |
+
try:
|
421 |
+
await update.effective_message.reply_text("No notes in this chat!")
|
422 |
+
except BadRequest:
|
423 |
+
await update.effective_message.reply_text(
|
424 |
+
"No notes in this chat!", quote=False
|
425 |
+
)
|
426 |
+
|
427 |
+
elif len(msg) != 0:
|
428 |
+
await update.effective_message.reply_text(msg, parse_mode=ParseMode.MARKDOWN)
|
429 |
+
|
430 |
+
|
431 |
+
async def __import_data__(chat_id, data, message: Message):
|
432 |
+
failures = []
|
433 |
+
for notename, notedata in data.get("extra", {}).items():
|
434 |
+
match = FILE_MATCHER.match(notedata)
|
435 |
+
matchsticker = STICKER_MATCHER.match(notedata)
|
436 |
+
matchbtn = BUTTON_MATCHER.match(notedata)
|
437 |
+
matchfile = MYFILE_MATCHER.match(notedata)
|
438 |
+
matchphoto = MYPHOTO_MATCHER.match(notedata)
|
439 |
+
matchaudio = MYAUDIO_MATCHER.match(notedata)
|
440 |
+
matchvoice = MYVOICE_MATCHER.match(notedata)
|
441 |
+
matchvideo = MYVIDEO_MATCHER.match(notedata)
|
442 |
+
matchvn = MYVIDEONOTE_MATCHER.match(notedata)
|
443 |
+
|
444 |
+
if match:
|
445 |
+
failures.append(notename)
|
446 |
+
notedata = notedata[match.end() :].strip()
|
447 |
+
if notedata:
|
448 |
+
sql.add_note_to_db(chat_id, notename[1:], notedata, sql.Types.TEXT)
|
449 |
+
elif matchsticker:
|
450 |
+
content = notedata[matchsticker.end() :].strip()
|
451 |
+
if content:
|
452 |
+
sql.add_note_to_db(
|
453 |
+
chat_id,
|
454 |
+
notename[1:],
|
455 |
+
notedata,
|
456 |
+
sql.Types.STICKER,
|
457 |
+
file=content,
|
458 |
+
)
|
459 |
+
elif matchbtn:
|
460 |
+
parse = notedata[matchbtn.end() :].strip()
|
461 |
+
notedata = parse.split("<###button###>")[0]
|
462 |
+
buttons = parse.split("<###button###>")[1]
|
463 |
+
buttons = ast.literal_eval(buttons)
|
464 |
+
if buttons:
|
465 |
+
sql.add_note_to_db(
|
466 |
+
chat_id,
|
467 |
+
notename[1:],
|
468 |
+
notedata,
|
469 |
+
sql.Types.BUTTON_TEXT,
|
470 |
+
buttons=buttons,
|
471 |
+
)
|
472 |
+
elif matchfile:
|
473 |
+
file = notedata[matchfile.end() :].strip()
|
474 |
+
file = file.split("<###TYPESPLIT###>")
|
475 |
+
notedata = file[1]
|
476 |
+
content = file[0]
|
477 |
+
if content:
|
478 |
+
sql.add_note_to_db(
|
479 |
+
chat_id,
|
480 |
+
notename[1:],
|
481 |
+
notedata,
|
482 |
+
sql.Types.DOCUMENT,
|
483 |
+
file=content,
|
484 |
+
)
|
485 |
+
elif matchphoto:
|
486 |
+
photo = notedata[matchphoto.end() :].strip()
|
487 |
+
photo = photo.split("<###TYPESPLIT###>")
|
488 |
+
notedata = photo[1]
|
489 |
+
content = photo[0]
|
490 |
+
if content:
|
491 |
+
sql.add_note_to_db(
|
492 |
+
chat_id,
|
493 |
+
notename[1:],
|
494 |
+
notedata,
|
495 |
+
sql.Types.PHOTO,
|
496 |
+
file=content,
|
497 |
+
)
|
498 |
+
elif matchaudio:
|
499 |
+
audio = notedata[matchaudio.end() :].strip()
|
500 |
+
audio = audio.split("<###TYPESPLIT###>")
|
501 |
+
notedata = audio[1]
|
502 |
+
content = audio[0]
|
503 |
+
if content:
|
504 |
+
sql.add_note_to_db(
|
505 |
+
chat_id,
|
506 |
+
notename[1:],
|
507 |
+
notedata,
|
508 |
+
sql.Types.AUDIO,
|
509 |
+
file=content,
|
510 |
+
)
|
511 |
+
elif matchvoice:
|
512 |
+
voice = notedata[matchvoice.end() :].strip()
|
513 |
+
voice = voice.split("<###TYPESPLIT###>")
|
514 |
+
notedata = voice[1]
|
515 |
+
content = voice[0]
|
516 |
+
if content:
|
517 |
+
sql.add_note_to_db(
|
518 |
+
chat_id,
|
519 |
+
notename[1:],
|
520 |
+
notedata,
|
521 |
+
sql.Types.VOICE,
|
522 |
+
file=content,
|
523 |
+
)
|
524 |
+
elif matchvideo:
|
525 |
+
video = notedata[matchvideo.end() :].strip()
|
526 |
+
video = video.split("<###TYPESPLIT###>")
|
527 |
+
notedata = video[1]
|
528 |
+
content = video[0]
|
529 |
+
if content:
|
530 |
+
sql.add_note_to_db(
|
531 |
+
chat_id,
|
532 |
+
notename[1:],
|
533 |
+
notedata,
|
534 |
+
sql.Types.VIDEO,
|
535 |
+
file=content,
|
536 |
+
)
|
537 |
+
elif matchvn:
|
538 |
+
video_note = notedata[matchvn.end() :].strip()
|
539 |
+
video_note = video_note.split("<###TYPESPLIT###>")
|
540 |
+
notedata = video_note[1]
|
541 |
+
content = video_note[0]
|
542 |
+
if content:
|
543 |
+
sql.add_note_to_db(
|
544 |
+
chat_id,
|
545 |
+
notename[1:],
|
546 |
+
notedata,
|
547 |
+
sql.Types.VIDEO_NOTE,
|
548 |
+
file=content,
|
549 |
+
)
|
550 |
+
else:
|
551 |
+
sql.add_note_to_db(chat_id, notename[1:], notedata, sql.Types.TEXT)
|
552 |
+
|
553 |
+
if failures:
|
554 |
+
with BytesIO(str.encode("\n".join(failures))) as output:
|
555 |
+
output.name = "failed_imports.txt"
|
556 |
+
await dispatcher.bot.send_document(
|
557 |
+
chat_id,
|
558 |
+
document=output,
|
559 |
+
filename="failed_imports.txt",
|
560 |
+
caption="These files/photos failed to import due to originating "
|
561 |
+
"from another bot. This is a telegram API restriction, and can't "
|
562 |
+
"be avoided. Sorry for the inconvenience!",
|
563 |
+
message_thread_id=message.message_thread_id
|
564 |
+
if message.chat.is_forum
|
565 |
+
else None,
|
566 |
+
)
|
567 |
+
|
568 |
+
|
569 |
+
def __stats__():
|
570 |
+
return f"• {sql.num_notes()} notes, across {sql.num_chats()} chats."
|
571 |
+
|
572 |
+
|
573 |
+
def __migrate__(old_chat_id, new_chat_id):
|
574 |
+
sql.migrate_chat(old_chat_id, new_chat_id)
|
575 |
+
|
576 |
+
|
577 |
+
def __chat_settings__(chat_id, user_id):
|
578 |
+
notes = sql.get_all_chat_notes(chat_id)
|
579 |
+
return f"There are `{len(notes)}` notes in this chat."
|
580 |
+
|
581 |
+
|
582 |
+
# <=================================================== HELP ====================================================>
|
583 |
+
|
584 |
+
|
585 |
+
__help__ = """
|
586 |
+
» /get <notename> : get the note with this notename
|
587 |
+
» #<notename> : same as /get
|
588 |
+
» /notes or /saved : list all saved notes in this chat
|
589 |
+
» /number : Will pull the note of that number in the list
|
590 |
+
➠ If you would like to retrieve the contents of a note without any formatting, use `/get <notename> noformat`. This can \
|
591 |
+
be useful when updating a current note
|
592 |
+
|
593 |
+
*Admins only:*
|
594 |
+
» /save <notename> <notedata> : saves notedata as a note with name notename
|
595 |
+
➠ A button can be added to a note by using standard markdown link syntax - the link should just be prepended with a \
|
596 |
+
`buttonurl:` section, as such: `[somelink](buttonurl:example.com)`. Check `/markdownhelp` for more info
|
597 |
+
» /save <notename> : save the replied message as a note with name notename
|
598 |
+
Separate diff replies by `%%%` to get random notes
|
599 |
+
➠ *Example:*
|
600 |
+
`/save notename
|
601 |
+
Reply 1
|
602 |
+
%%%
|
603 |
+
Reply 2
|
604 |
+
%%%
|
605 |
+
Reply 3`
|
606 |
+
» /clear <notename>: clear note with this name
|
607 |
+
» /removeallnotes: removes all notes from the group
|
608 |
+
➠ *Note:* Note names are case-insensitive, and they are automatically converted to lowercase before getting saved.
|
609 |
+
|
610 |
+
"""
|
611 |
+
|
612 |
+
__mod_name__ = "NOTES"
|
613 |
+
|
614 |
+
# <================================================ HANDLER =======================================================>
|
615 |
+
function(CommandHandler("get", cmd_get))
|
616 |
+
function(MessageHandler(filters.Regex(r"^#[^\s]+"), hash_get, block=False))
|
617 |
+
function(MessageHandler(filters.Regex(r"^/\d+$"), slash_get, block=False))
|
618 |
+
function(CommandHandler("save", save, block=False))
|
619 |
+
function(CommandHandler("clear", clear, block=False))
|
620 |
+
|
621 |
+
function(
|
622 |
+
DisableAbleCommandHandler(
|
623 |
+
["notes", "saved"], list_notes, admin_ok=True, block=False
|
624 |
+
)
|
625 |
+
)
|
626 |
+
|
627 |
+
function(DisableAbleCommandHandler("removeallnotes", clearall, block=False))
|
628 |
+
function(CallbackQueryHandler(clearall_btn, pattern=r"notes_.*", block=False))
|
629 |
+
# <================================================ END =======================================================>
|