keyword 添加图片/文件缓存功能 (#122)

-replyset cache 1 开启
-replyset cache 0 关闭
-replyset cache clear 清除cache设置
-replyset cache remove 刷新cache,可以使用global和plain/regex 规则id,具体参照 [具体参照 Keyword 进阶版教程](https://telegra.ph/Keyword-插件使用教程---进阶篇-02-07)
This commit is contained in:
c3b2a 2021-02-08 20:25:46 +08:00 committed by GitHub
parent ae7b3c3aab
commit 628bd476e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 227 additions and 132 deletions

View File

@ -1,7 +1,7 @@
import re, time, asyncio, requests, os, json import re, time, asyncio, requests, os, json
from io import BytesIO from io import BytesIO
from os import path, mkdir, remove from os import path, mkdir, remove, makedirs
from shutil import copyfile, move from shutil import copyfile, move, rmtree
from uuid import uuid4 from uuid import uuid4
from base64 import b64encode, b64decode from base64 import b64encode, b64decode
from importlib import import_module from importlib import import_module
@ -128,6 +128,31 @@ def valid_time(chat_id):
return True 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): async def del_msg(context, t_lim):
await asyncio.sleep(t_lim) await asyncio.sleep(t_lim)
try: try:
@ -136,7 +161,7 @@ async def del_msg(context, t_lim):
pass pass
async def send_reply(chat_id, reply_msg, context): async def send_reply(chat_id, trigger, mode, reply_msg, context):
try: try:
real_chat_id = chat_id real_chat_id = chat_id
chat = context.chat chat = context.chat
@ -144,12 +169,13 @@ async def send_reply(chat_id, reply_msg, context):
replace_data = {} replace_data = {}
if chat_id < 0: if chat_id < 0:
replace_data = { replace_data = {
"user_id": sender.id,
"first_name": sender.first_name,
"last_name": sender.last_name if sender.last_name else "",
"chat_id": chat.id, "chat_id": chat.id,
"chat_name": chat.title "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: else:
replace_data["user_id"] = chat_id replace_data["user_id"] = chat_id
if sender: if sender:
@ -185,33 +211,38 @@ async def send_reply(chat_id, reply_msg, context):
chat_id = int(s[4:]) chat_id = int(s[4:])
type_parse.remove(s) type_parse.remove(s)
break break
if "plain" == re_type: if ("file" in type_parse or "photo" in type_parse) and len(re_msg.split()) >= 2:
if could_send_msg: if could_send_msg:
update_last_time = True update_last_time = True
await bot.send_message(chat_id, re_msg, reply_to=None) re_data = re_msg.split(" ")
elif "reply" == re_type and chat_id == real_chat_id: cache_exists, cache_path = has_cache(chat_id, mode, trigger, re_data[0])
if could_send_msg: is_opened = cache_opened(chat_id, mode, trigger)
update_last_time = True filename = "/tmp/" + re_data[0]
await bot.send_message(chat_id, re_msg, reply_to=context.id) if is_opened:
elif ("file" in type_parse or "photo" in type_parse) and len(re_msg.split()) >= 2: filename = cache_path
if could_send_msg: if not cache_exists:
update_last_time = True if re_data[1][0:7] == "file://":
if not path.exists("/tmp"): re_data[1] = re_data[1][7:]
mkdir("/tmp") copyfile(" ".join(re_data[1:]), filename)
re_data = re_msg.split() else:
file_name = "/tmp/" + re_data[0] fileget = requests.get(" ".join(re_data[1:]))
if re_data[1][0:7] == "file://": with open(filename, "wb") as f:
copyfile(re_data[1][7:], file_name) f.write(fileget.content)
else: else:
file_get = requests.get(" ".join(re_data[1:])) if re_data[1][0:7] == "file://":
with open(file_name, "wb") as f: re_data[1] = re_data[1][7:]
f.write(file_get.content) 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 reply_to = None
if "reply" in type_parse: if "reply" in type_parse:
reply_to = context.id reply_to = context.id
await bot.send_file(chat_id, file_name, await bot.send_file(chat_id, filename,
reply_to=reply_to, force_document=("file" in type_parse)) reply_to=reply_to, force_document=("file" in type_parse))
remove(file_name) if not is_opened:
remove(filename)
elif ("tgfile" in type_parse or "tgphoto" in type_parse) and len(re_msg.split()) >= 2: elif ("tgfile" in type_parse or "tgphoto" in type_parse) and len(re_msg.split()) >= 2:
if could_send_msg: if could_send_msg:
update_last_time = True update_last_time = True
@ -221,8 +252,13 @@ async def send_reply(chat_id, reply_msg, context):
file_name = "/tmp/" + re_data[0] file_name = "/tmp/" + re_data[0]
_data = BytesIO() _data = BytesIO()
re_data[1] = re_data[1].split("/")[-2:] re_data[1] = re_data[1].split("/")[-2:]
msg_chat_id = int(re_data[1][0]) 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]) 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] media_msg = (await bot.get_messages(msg_chat_id, msg_id_inchat))[0]
_data = BytesIO() _data = BytesIO()
if media_msg and media_msg.media: if media_msg and media_msg.media:
@ -238,15 +274,23 @@ async def send_reply(chat_id, reply_msg, context):
await bot.send_file(chat_id, file_name, reply_to=reply_to, await bot.send_file(chat_id, file_name, reply_to=reply_to,
force_document=("tgfile" in type_parse)) force_document=("tgfile" in type_parse))
remove(file_name) remove(file_name)
elif "plain" in type_parse:
if could_send_msg:
update_last_time = True
await bot.send_message(chat_id, re_msg, reply_to=None)
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)
elif "op" in type_parse: elif "op" in type_parse:
if re_msg == "delete": if re_msg == "delete":
await context.delete() await context.delete()
elif re_msg.split()[0] == "sleep" and len(re_msg.split()) == 2: elif re_msg.split()[0] == "sleep" and len(re_msg.split()) == 2:
sleep_time = re_msg.split()[1] sleep_time = re_msg.split()[1]
await asyncio.sleep(float(sleep_time)) await asyncio.sleep(float(sleep_time))
chat_id = real_chat_id
except: except:
chat_id = real_chat_id pass
chat_id = real_chat_id
if update_last_time: if update_last_time:
global group_last_time global group_last_time
group_last_time[int(chat_id)] = time.time() group_last_time[int(chat_id)] = time.time()
@ -386,8 +430,16 @@ async def reply_set(context):
redis_data = f"keyword.{chat_id}.single.{params[0]}.{rule_data}" redis_data = f"keyword.{chat_id}.single.{params[0]}.{rule_data}"
del params[0:2] del params[0:2]
settings_dict = get_redis(redis_data) settings_dict = get_redis(redis_data)
cmd_list = ["help", "mode", "list", "freq", "show", "clear"] cmd_list = ["help", "mode", "list", "freq", "show", "cache", "clear"]
cmd_dict = {"help": (1,), "mode": (2,), "list": (2, 3), "freq": (2,), "show": (1,), "clear": (1,)} cmd_dict = {
"help": (1,),
"mode": (2,),
"list": (2, 3),
"freq": (2,),
"show": (1,),
"cache": (2,),
"clear": (1,)
}
if len(params) < 1: if len(params) < 1:
await context.edit("参数错误") await context.edit("参数错误")
await del_msg(context, 5) await del_msg(context, 5)
@ -405,7 +457,12 @@ async def reply_set(context):
await del_msg(context, 15) await del_msg(context, 15)
return return
elif params[0] == "show": elif params[0] == "show":
defaults = {"mode": "未设置", "list": "未设置", "freq": "未设置"} defaults = {
"mode": "未设置 (默认黑名单)",
"list": "未设置 (默认为空)",
"freq": "未设置 (默认为 1)",
"cache": "未设置 (默认关闭)"
}
msg = "Settings: \n" msg = "Settings: \n"
for k, v in defaults.items(): for k, v in defaults.items():
msg += f"`{k}` -> `{settings_dict[k] if k in settings_dict else v}`\n" msg += f"`{k}` -> `{settings_dict[k] if k in settings_dict else v}`\n"
@ -520,6 +577,41 @@ async def reply_set(context):
await context.edit("频率需为正数") await context.edit("频率需为正数")
await del_msg(context, 5) await del_msg(context, 5)
return return
elif params[0] == "cache":
if params[1] == "0":
settings_dict["cache"] = "0"
redis.set(redis_data, save_rules(settings_dict, None))
await context.edit("已关闭缓存功能")
await del_msg(context, 5)
return
elif params[1] == "1":
settings_dict["cache"] = "1"
redis.set(redis_data, save_rules(settings_dict, None))
await context.edit("已开启缓存功能")
await del_msg(context, 5)
return
elif params[1] == "remove":
if redis_data == "keyword.settings":
rmtree("data/keyword_cache")
elif redis_data.split(".")[2] == "single":
rmtree(f"data/keyword_cache/{chat_id}/"
f"{redis_data.split('.')[3]}:{redis_data.split('.')[4]}")
else:
rmtree(f"data/keyword_cache/{chat_id}")
await context.edit("已删除缓存")
await del_msg(context, 5)
return
elif params[1] == "clear":
if "cache" in settings_dict:
del settings_dict["cache"]
redis.set(redis_data, save_rules(settings_dict, None))
await context.edit("清除成功")
await del_msg(context, 5)
return
else:
await context.edit(f"参数错误")
await del_msg(context, 5)
return
elif params[0] == "clear": elif params[0] == "clear":
redis.delete(redis_data) redis.delete(redis_data)
await context.edit("清除成功") await context.edit("清除成功")
@ -537,106 +629,109 @@ async def reply_set(context):
async def funcset(context): async def funcset(context):
if not path.exists("plugins/keyword_func"): if not path.exists("plugins/keyword_func"):
mkdir("plugins/keyword_func") mkdir("plugins/keyword_func")
params = context.parameter try:
params = " ".join(params).split("\n") params = context.parameter
cmd = [] params = " ".join(params).split("\n")
if len(params) >= 1: cmd = []
cmd = params[0].split() if len(params) >= 1:
if len(cmd) > 0: cmd = params[0].split()
if len(cmd) == 1 and cmd[0] == "ls": if len(cmd) > 0:
send_msg = "Functions:\n" if len(cmd) == 1 and cmd[0] == "ls":
count = 1 send_msg = "Functions:\n"
for p in os.listdir("plugins/keyword_func"): count = 1
if path.isfile(f"plugins/keyword_func/{p}"): for p in os.listdir("plugins/keyword_func"):
try: if path.isfile(f"plugins/keyword_func/{p}"):
send_msg += f"{count}: `{p[:-3]}`\n" try:
count += 1 send_msg += f"{count}: `{p[:-3]}`\n"
except: count += 1
pass except:
await context.edit(send_msg) pass
return await context.edit(send_msg)
elif len(cmd) == 2 and cmd[0] == "show": return
file_path = f"plugins/keyword_func/{cmd[1]}.py" elif len(cmd) == 2 and cmd[0] == "show":
if path.exists(file_path) and path.isfile(file_path): file_path = f"plugins/keyword_func/{cmd[1]}.py"
await bot.send_file(context.chat_id, file_path) if path.exists(file_path) and path.isfile(file_path):
await context.edit("发送成功") await bot.send_file(context.chat_id, file_path)
await del_msg(context, 5) await context.edit("发送成功")
else:
await context.edit("函数不存在")
await del_msg(context, 5)
return
elif len(cmd) == 2 and cmd[0] == "del":
file_path = f"plugins/keyword_func/{cmd[1]}.py"
if path.exists(file_path) and path.isfile(file_path):
remove(file_path)
await context.edit("删除成功PagerMaid-Modify 正在重新启动。")
await bot.disconnect()
else:
await context.edit("函数不存在")
await del_msg(context, 5)
return
elif len(cmd) == 2 and cmd[0] == "new":
message = await context.get_reply_message()
if context.media:
message = context
cmd[1] = cmd[1].replace(".py", "")
if message and message.media:
try:
data = BytesIO()
await bot.download_file(message.media.document, data)
with open(f"plugins/keyword_func/{cmd[1]}.py", "wb") as f:
f.write(data.getvalue())
await context.edit(f"函数 {cmd[1]} 已添加PagerMaid-Modify 正在重新启动。")
await bot.disconnect()
except:
await context.edit("函数添加失败")
await del_msg(context, 5) await del_msg(context, 5)
else:
await context.edit("未回复消息或回复的消息中不包含文件")
await del_msg(context, 5)
return
elif len(cmd) == 2 and cmd[0] == "install":
func_name = cmd[1]
func_online = \
json.loads(
requests.get("https://raw.githubusercontent.com/xtaodada/PagerMaid_Plugins/master"
"/keyword_func/list.json").content)['list']
if func_name in func_online:
func_directory = f"{working_dir}/plugins/keyword_func/"
file_path = func_name + ".py"
func_content = requests.get(
f"https://raw.githubusercontent.com/xtaodada/PagerMaid_Plugins/master"
f"/keyword_func/{func_name}.py").content
with open(file_path, 'wb') as f:
f.write(func_content)
if path.exists(f"{func_directory}{file_path}"):
remove(f"{func_directory}{file_path}")
move(file_path, func_directory)
else: else:
move(file_path, func_directory) await context.edit("函数不存在")
await context.edit(f"函数 {path.basename(file_path)[:-3]} 已添加PagerMaid-Modify 正在重新启动。") await del_msg(context, 5)
await log(f"成功安装函数 {path.basename(file_path)[:-3]}.") return
await bot.disconnect() elif len(cmd) == 2 and cmd[0] == "del":
file_path = f"plugins/keyword_func/{cmd[1]}.py"
if path.exists(file_path) and path.isfile(file_path):
remove(file_path)
await context.edit("删除成功PagerMaid-Modify 正在重新启动。")
await bot.disconnect()
else:
await context.edit("函数不存在")
await del_msg(context, 5)
return
elif len(cmd) == 2 and cmd[0] == "new":
message = await context.get_reply_message()
if context.media:
message = context
cmd[1] = cmd[1].replace(".py", "")
if message and message.media:
try:
data = BytesIO()
await bot.download_file(message.media.document, data)
with open(f"plugins/keyword_func/{cmd[1]}.py", "wb") as f:
f.write(data.getvalue())
await context.edit(f"函数 {cmd[1]} 已添加PagerMaid-Modify 正在重新启动。")
await bot.disconnect()
except:
await context.edit("函数添加失败")
await del_msg(context, 5)
else:
await context.edit("未回复消息或回复的消息中不包含文件")
await del_msg(context, 5)
return
elif len(cmd) == 2 and cmd[0] == "install":
func_name = cmd[1]
func_online = \
json.loads(
requests.get("https://raw.githubusercontent.com/xtaodada/PagerMaid_Plugins/master"
"/keyword_func/list.json").content)['list']
if func_name in func_online:
func_directory = f"{working_dir}/plugins/keyword_func/"
file_path = func_name + ".py"
func_content = requests.get(
f"https://raw.githubusercontent.com/xtaodada/PagerMaid_Plugins/master"
f"/keyword_func/{func_name}.py").content
with open(file_path, 'wb') as f:
f.write(func_content)
if path.exists(f"{func_directory}{file_path}"):
remove(f"{func_directory}{file_path}")
move(file_path, func_directory)
else:
move(file_path, func_directory)
await context.edit(f"函数 {path.basename(file_path)[:-3]} 已添加PagerMaid-Modify 正在重新启动。")
await log(f"成功安装函数 {path.basename(file_path)[:-3]}.")
await bot.disconnect()
else:
await context.edit(f"{func_name} 函数不存在")
await del_msg(context, 5)
return
elif len(cmd) == 1 and cmd[0] == "help":
await context.edit("""
`-funcset new <func_name>` (要回复带有文件的信息或自己附带文件)
`-funcset install <func_name>` 云端获取函数文件
`-funcset del <func_name>`
`-funcset show <func_name>` (发送文件)
`-funcset ls` (列出所有函数)""")
else: else:
await context.edit(f"{func_name} 函数不存在") await context.edit("参数错误")
await del_msg(context, 5) await del_msg(context, 5)
return return
elif len(cmd) == 1 and cmd[0] == "help":
await context.edit("""
`-funcset new <func_name>` (要回复带有文件的信息或自己附带文件)
`-funcset install <func_name>` 云端获取函数文件
`-funcset del <func_name>`
`-funcset show <func_name>` (发送文件)
`-funcset ls` (列出所有函数)""")
else: else:
await context.edit("参数错误") await context.edit("参数错误")
await del_msg(context, 5) await del_msg(context, 5)
return return
else: except:
await context.edit("参数错误") pass
await del_msg(context, 5)
return
@listener(incoming=True, ignore_edited=True) @listener(incoming=True, ignore_edited=True)
@ -646,7 +741,7 @@ async def auto_reply(context):
try: try:
chat_id = context.chat_id chat_id = context.chat_id
sender_id = context.sender_id sender_id = context.sender_id
if context.id not in read_context: if f"{chat_id}:{context.id}" not in read_context:
plain_dict = get_redis(f"keyword.{chat_id}.plain") plain_dict = get_redis(f"keyword.{chat_id}.plain")
regex_dict = get_redis(f"keyword.{chat_id}.regex") regex_dict = get_redis(f"keyword.{chat_id}.regex")
g_settings = get_redis("keyword.settings") g_settings = get_redis("keyword.settings")
@ -675,8 +770,8 @@ async def auto_reply(context):
if tmp: if tmp:
could_reply = validate(str(sender_id), int(tmp.get("mode", "0")), tmp.get("list", [])) could_reply = validate(str(sender_id), int(tmp.get("mode", "0")), tmp.get("list", []))
if could_reply: if could_reply:
read_context[context.id] = None read_context[f"{chat_id}:{context.id}"] = None
await send_reply(chat_id, parse_multi(v), context) await send_reply(chat_id, k, "plain", parse_multi(v), context)
for k, v in regex_dict.items(): for k, v in regex_dict.items():
pattern = re.compile(k) pattern = re.compile(k)
if pattern.search(send_text): if pattern.search(send_text):
@ -685,7 +780,7 @@ async def auto_reply(context):
if tmp: if tmp:
could_reply = validate(str(sender_id), int(tmp.get("mode", "0")), tmp.get("list", [])) could_reply = validate(str(sender_id), int(tmp.get("mode", "0")), tmp.get("list", []))
if could_reply: if could_reply:
read_context[context.id] = None read_context[f"{chat_id}:{context.id}"] = None
catch_pattern = r"\$\{regex_(?P<str>((?!\}).)+)\}" catch_pattern = r"\$\{regex_(?P<str>((?!\}).)+)\}"
count = 0 count = 0
while re.search(catch_pattern, v) and count < 20: while re.search(catch_pattern, v) and count < 20:
@ -698,8 +793,8 @@ async def auto_reply(context):
capture_data = "" capture_data = ""
v = v.replace("${regex_%s}" % group_name, capture_data) v = v.replace("${regex_%s}" % group_name, capture_data)
count += 1 count += 1
await send_reply(chat_id, parse_multi(v), context) await send_reply(chat_id, k, "regex", parse_multi(v), context)
else: else:
del read_context[context.id] del read_context[f"{chat_id}:{context.id}"]
except: except:
pass pass

View File

@ -362,10 +362,10 @@
}, },
{ {
"name": "keyword", "name": "keyword",
"version": "2.4", "version": "2.5",
"section": "chat", "section": "chat",
"maintainer": "c3b2a, xtaodada", "maintainer": "c3b2a, xtaodada",
"size": "28.6 kb", "size": "32.9 kb",
"supported": true, "supported": true,
"des-short": "群组关键词自动回复插件", "des-short": "群组关键词自动回复插件",
"des": "命令: keyword, replyset, funcset。" "des": "命令: keyword, replyset, funcset。"