diff --git a/keyword_func/advanced.py b/keyword_func/advanced.py new file mode 100644 index 0000000..9a7effb --- /dev/null +++ b/keyword_func/advanced.py @@ -0,0 +1,356 @@ +import re, time, asyncio, requests, os, json +from io import BytesIO +from os import path, mkdir, remove, makedirs, chdir +from shutil import copyfile, move, rmtree +from uuid import uuid4 +from base64 import b64encode, b64decode +from importlib import import_module +from pagermaid import bot, redis, log, redis_status, working_dir +from pagermaid.listener import listener + +msg_freq = 1 +group_last_time = {} +read_context = {} + + +def is_num(x: str): + try: + x = int(x) + return isinstance(x, int) + except ValueError: + return False + + +def encode(s: str): + return str(b64encode(s.encode('utf-8')), 'utf-8') + + +def decode(s: str): + return str(b64decode(s.encode('utf-8')), 'utf-8') + + +def random_str(): + return str(uuid4()).replace('-', '') + + +def parse_rules(rules: str): + n_rules = {} + rules_parse = rules.split(";") + for p in rules_parse: + d = p.split(":") + if len(d) == 2: + key = decode(d[0]) + value = decode(d[1]) + n_rules[key] = value + return n_rules + + +def save_rules(rules: dict, placeholder: str): + n_rules = "" + for k, v in rules.items(): + if placeholder: + k = k.replace(placeholder, "'") + v = v.replace(placeholder, "'") + n_rules += encode(k) + ":" + encode(v) + ";" + return n_rules + + +def validate(user_id: str, mode: int, user_list: list): + if mode == 0: + return user_id not in user_list + elif mode == 1: + return user_id in user_list + else: + return False + + +def get_redis(db_key: str): + byte_data = redis.get(db_key) + byte_data = byte_data if byte_data else b"" + byte_data = str(byte_data, "ascii") + return parse_rules(byte_data) + + +def parse_multi(rule: str): + sep_ph = random_str() + col_ph = random_str() + rule = rule.replace(r"\||", sep_ph) + rule = rule.replace(r"\::", col_ph) + rule = rule.split("||") + n_rule = [] + for r in rule: + p = r.split("::") + p = [i.replace(sep_ph, "||") for i in p] + p = [i.replace(col_ph, "::") for i in p] + data = ['plain', ''] + if len(p) == 2: + data = p + else: + data[1] = p[0] + n_rule.append(data) + return n_rule + + +def get_capture(search_data, group_name: str): + try: + capture_data = search_data.group(group_name) + return capture_data + except: + return None + + +def get_rule(chat_id, rule_type, rule_index): + rule_index = int(rule_index) + rule_data = get_redis(f"keyword.{chat_id}.{rule_type}") + index = 0 + for k in rule_data.keys(): + if index == rule_index: + return encode(k) + index += 1 + return None + + +def valid_time(chat_id): + global msg_freq, group_last_time + cus_freq = get_redis(f"keyword.{chat_id}.settings").get("freq", msg_freq) + try: + cus_freq = float(cus_freq) + except: + cus_freq = msg_freq + n_time = time.time() + chat_id = int(chat_id) + if chat_id in group_last_time: + if n_time - group_last_time[chat_id] >= cus_freq: + return True + else: + return False + else: + return True + + +def has_cache(chat_id, mode, trigger, filename): + basepath = f"data/keyword_cache/{chat_id}/{mode}:{encode(trigger)}" + filepath = f"{basepath}/{filename}" + if not path.exists(basepath): + makedirs(basepath) + return (False, filepath) + if not path.exists(filepath): + return (False, filepath) + return (True, filepath) + + +def cache_opened(chat_id, mode, trigger): + rule_data = get_redis(f"keyword.{chat_id}.single" + f".{mode}.{encode(trigger)}").get("cache", None) + chat_data = get_redis(f"keyword.{chat_id}.settings").get("cache", None) + global_data = get_redis("keyword.settings").get("cache", None) + if rule_data: + return True if rule_data == "1" else False + elif chat_data: + return True if chat_data == "1" else False + elif global_data: + return True if global_data == "1" else False + return False + + +async def del_msg(context, t_lim): + await asyncio.sleep(t_lim) + try: + await context.delete() + except: + pass + + +async def send_reply(chat_id, trigger, mode, reply_msg, context): + try: + real_chat_id = chat_id + chat = context.chat + sender = context.sender + replace_data = {} + if chat_id < 0: + replace_data = { + "chat_id": chat.id, + "chat_name": chat.title + } + if sender: + replace_data["user_id"] = sender.id + replace_data["first_name"] = sender.first_name + replace_data["last_name"] = sender.last_name if sender.last_name else "" + else: + replace_data["user_id"] = chat_id + if sender: + replace_data["first_name"] = sender.first_name + replace_data["last_name"] = sender.last_name if sender.last_name else "" + if chat: + replace_data["chat_id"] = chat.id + last_name = chat.last_name + if not last_name: + last_name = "" + replace_data["chat_name"] = f"{chat.first_name} {last_name}" + update_last_time = False + could_send_msg = valid_time(chat_id) + for re_type, re_msg in reply_msg: + try: + for k, v in replace_data.items(): + re_type = re_type.replace(f"${k}", str(v)) + re_msg = re_msg.replace(f"${k}", str(v)) + type_parse = re_type.split(",") + type_parse = [(p[4:] if p[0:3] == "adv" else "") for p in type_parse] + for s in type_parse: + if len(s) >= 5 and "ext_" == s[0:4] and is_num(s[4:]): + chat_id = int(s[4:]) + type_parse.remove(s) + break + if ("file" in type_parse or "photo" in type_parse) and len(re_msg.split()) >= 2: + if could_send_msg: + update_last_time = True + re_data = re_msg.split(" ") + cache_exists, cache_path = has_cache(chat_id, mode, trigger, re_data[0]) + is_opened = cache_opened(chat_id, mode, trigger) + filename = "/tmp/" + re_data[0] + if is_opened: + filename = cache_path + if not cache_exists: + if re_data[1][0:7] == "file://": + re_data[1] = re_data[1][7:] + copyfile(" ".join(re_data[1:]), filename) + else: + fileget = requests.get(" ".join(re_data[1:])) + with open(filename, "wb") as f: + f.write(fileget.content) + else: + if re_data[1][0:7] == "file://": + re_data[1] = re_data[1][7:] + copyfile(" ".join(re_data[1:]), filename) + else: + fileget = requests.get(" ".join(re_data[1:])) + with open(filename, "wb") as f: + f.write(fileget.content) + reply_to = None + if "reply" in type_parse: + reply_to = context.id + await bot.send_file(chat_id, filename, + reply_to=reply_to, force_document=("file" in type_parse)) + if not is_opened: + remove(filename) + elif ("tgfile" in type_parse or "tgphoto" in type_parse) and len(re_msg.split()) >= 2: + if could_send_msg: + update_last_time = True + if not path.exists("/tmp"): + mkdir("/tmp") + re_data = re_msg.split() + file_name = "/tmp/" + re_data[0] + _data = BytesIO() + re_data[1] = re_data[1].split("/")[-2:] + try: + msg_chat_id = int(re_data[1][0]) + except: + async with bot.conversation(re_data[1][0]) as conversation: + msg_chat_id = conversation.chat_id + msg_id_inchat = int(re_data[1][1]) + await bot.send_message(chat_id, f"{msg_chat_id, msg_id_inchat}") + media_msg = (await bot.get_messages(msg_chat_id, msg_id_inchat))[0] + _data = BytesIO() + if media_msg and media_msg.media: + if "tgfile" in type_parse: + await bot.download_file(media_msg.media.document, _data) + else: + await bot.download_file(media_msg.photo, _data) + with open(file_name, "wb") as f: + f.write(_data.getvalue()) + reply_to = None + if "reply" in type_parse: + reply_to = context.id + await bot.send_file(chat_id, file_name, reply_to=reply_to, + force_document=("tgfile" in type_parse)) + remove(file_name) + elif "plain" in type_parse: + if could_send_msg: + update_last_time = True + await bot.send_message(chat_id, re_msg, + link_preview=("nopreview" not in type_parse)) + elif "reply" in type_parse and chat_id == real_chat_id: + if could_send_msg: + update_last_time = True + await bot.send_message(chat_id, re_msg, reply_to=context.id, + link_preview=("nopreview" not in type_parse)) + elif "op" in type_parse: + if re_msg == "delete": + await context.delete() + elif re_msg.split()[0] == "sleep" and len(re_msg.split()) == 2: + sleep_time = re_msg.split()[1] + await asyncio.sleep(float(sleep_time)) + except: + pass + chat_id = real_chat_id + if update_last_time: + global group_last_time + group_last_time[int(chat_id)] = time.time() + except: + pass + + +async def main(context): + if not redis_status(): + return + try: + chat_id = context.chat_id + sender_id = context.sender_id + if f"{chat_id}:{context.id}" not in read_context: + plain_dict = get_redis(f"keyword.{chat_id}.plain") + regex_dict = get_redis(f"keyword.{chat_id}.regex") + g_settings = get_redis("keyword.settings") + n_settings = get_redis(f"keyword.{chat_id}.settings") + g_mode = g_settings.get("mode", None) + n_mode = n_settings.get("mode", None) + mode = "0" + g_list = g_settings.get("list", None) + n_list = n_settings.get("list", None) + user_list = [] + if g_mode and n_mode: + mode = n_mode + elif g_mode or n_mode: + mode = g_mode if g_mode else n_mode + if g_list and n_list: + user_list = n_list + elif g_list or n_list: + user_list = g_list if g_list else n_list + send_text = context.text + if not send_text: + send_text = "" + for k, v in plain_dict.items(): + if k in send_text: + tmp = get_redis(f"keyword.{chat_id}.single.plain.{encode(k)}") + could_reply = validate(str(sender_id), int(mode), user_list) + if tmp: + could_reply = validate(str(sender_id), int(tmp.get("mode", "0")), tmp.get("list", [])) + if could_reply: + read_context[f"{chat_id}:{context.id}"] = None + await send_reply(chat_id, k, "plain", parse_multi(v), context) + for k, v in regex_dict.items(): + pattern = re.compile(k) + if pattern.search(send_text): + tmp = get_redis(f"keyword.{chat_id}.single.regex.{encode(k)}") + could_reply = validate(str(sender_id), int(mode), user_list) + if tmp: + could_reply = validate(str(sender_id), int(tmp.get("mode", "0")), tmp.get("list", [])) + if could_reply: + read_context[f"{chat_id}:{context.id}"] = None + catch_pattern = r"\$\{regex_(?P((?!\}).)+)\}" + count = 0 + while re.search(catch_pattern, v) and count < 20: + search_data = re.search(k, send_text) + group_name = re.search(catch_pattern, v).group("str") + capture_data = get_capture(search_data, group_name) + if not capture_data: + capture_data = "" + if re.search(catch_pattern, capture_data): + capture_data = "" + v = v.replace("${regex_%s}" % group_name, capture_data) + count += 1 + await send_reply(chat_id, k, "regex", parse_multi(v), context) + else: + del read_context[f"{chat_id}:{context.id}"] + except: + pass + return ""