Compare commits

...
3 Commits
Author SHA1 Message Date
whyour 1cfcf8d194 更新版本 v2.10.8 2021-11-16 14:36:13 +08:00
whyour d1e9f741d0 修复脚本管理删除、新建增加选择父目录 2021-11-16 14:18:02 +08:00
kilo5hzandGitHub 0cde9d07ee 增加gotify推送 (#905)
* 增加gotify推送
2021-11-15 23:04:55 +08:00
9 changed files with 204 additions and 53 deletions
+7 -2
View File
@@ -120,6 +120,9 @@ export default (app: Router) => {
if (!path.endsWith('/')) { if (!path.endsWith('/')) {
path += '/'; path += '/';
} }
if (!path.startsWith('/')) {
path = `${config.scriptPath}${path}`;
}
if (config.writePathList.every((x) => !path.startsWith(x))) { if (config.writePathList.every((x) => !path.startsWith(x))) {
return res.send({ return res.send({
code: 430, code: 430,
@@ -184,15 +187,17 @@ export default (app: Router) => {
celebrate({ celebrate({
body: Joi.object({ body: Joi.object({
filename: Joi.string().required(), filename: Joi.string().required(),
path: Joi.string().allow(''),
}), }),
}), }),
async (req: Request, res: Response, next: NextFunction) => { async (req: Request, res: Response, next: NextFunction) => {
const logger: Logger = Container.get('logger'); const logger: Logger = Container.get('logger');
try { try {
let { filename } = req.body as { let { filename, path } = req.body as {
filename: string; filename: string;
path: string;
}; };
const filePath = `${config.scriptPath}${filename}`; const filePath = `${config.scriptPath}${path}/${filename}`;
fs.unlinkSync(filePath); fs.unlinkSync(filePath);
res.send({ code: 200 }); res.send({ code: 200 });
} catch (e) { } catch (e) {
+57 -4
View File
@@ -13,6 +13,13 @@
const querystring = require('querystring'); const querystring = require('querystring');
const $ = new Env(); const $ = new Env();
const timeout = 15000; //超时时间(单位毫秒) const timeout = 15000; //超时时间(单位毫秒)
// =======================================gotify通知设置区域==============================================
//gotify_url 填写gotify地址,如https://push.example.de:8080
//gotify_token 填写gotify的消息应用token
//gotify_priority 填写推送消息优先级,默认为0
let GOTIFY_URL = '';
let GOTIFY_TOKEN = '';
let GOTIFY_PRIORITY = 0;
// =======================================go-cqhttp通知设置区域=========================================== // =======================================go-cqhttp通知设置区域===========================================
//gobot_url 填写请求地址http://127.0.0.1/send_private_msg //gobot_url 填写请求地址http://127.0.0.1/send_private_msg
//gobot_token 填写在go-cqhttp文件设置的访问密钥 //gobot_token 填写在go-cqhttp文件设置的访问密钥
@@ -84,6 +91,16 @@ let PUSH_PLUS_TOKEN = '';
let PUSH_PLUS_USER = ''; let PUSH_PLUS_USER = '';
//==========================云端环境变量的判断与接收========================= //==========================云端环境变量的判断与接收=========================
if (process.env.GOTIFY_URL) {
GOTIFY_URL = process.env.GOTIFY_URL;
}
if (process.env.GOTIFY_TOKEN) {
GOTIFY_TOKEN = process.env.GOTIFY_TOKEN;
}
if (process.env.GOTIFY_PRIORITY) {
GOTIFY_PRIORITY = process.env.GOTIFY_PRIORITY;
}
if (process.env.GOBOT_URL) { if (process.env.GOBOT_URL) {
GOBOT_URL = process.env.GOBOT_URL; GOBOT_URL = process.env.GOBOT_URL;
} }
@@ -200,9 +217,45 @@ async function sendNotify(
qywxamNotify(text, desp), //企业微信应用消息推送 qywxamNotify(text, desp), //企业微信应用消息推送
iGotNotify(text, desp, params), //iGot iGotNotify(text, desp, params), //iGot
gobotNotify(text, desp),//go-cqhttp gobotNotify(text, desp),//go-cqhttp
gotifyNotify(text, desp),//gotify
]); ]);
} }
function gotifyNotify(text, desp) {
return new Promise((resolve) => {
if (GOTIFY_URL && GOTIFY_TOKEN) {
const options = {
url: `${GOTIFY_URL}/message?token=${GOTIFY_TOKEN}`,
body: `title=${encodeURIComponent(text)}&message=${encodeURIComponent(desp)}&priority=${GOTIFY_PRIORITY}`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
}
};
$.post(options, (err, resp, data) => {
try {
if (err) {
console.log('gotify发送通知调用API失败!!\n');
console.log(err);
} else {
data = JSON.parse(data);
if (data.id) {
console.log('gotify发送通知消息成功🎉\n');
} else {
console.log(`${data.message}\n`);
}
}
} catch (e) {
$.logErr(e, resp);
} finally {
resolve();
}
});
} else {
resolve();
}
});
}
function gobotNotify(text, desp, time = 2100) { function gobotNotify(text, desp, time = 2100) {
return new Promise((resolve) => { return new Promise((resolve) => {
if (GOBOT_URL) { if (GOBOT_URL) {
@@ -673,8 +726,8 @@ function qywxamNotify(text, desp) {
if (err) { if (err) {
console.log( console.log(
'成员ID:' + '成员ID:' +
ChangeUserId(desp) + ChangeUserId(desp) +
'企业微信应用消息发送通知消息失败!!\n', '企业微信应用消息发送通知消息失败!!\n',
); );
console.log(err); console.log(err);
} else { } else {
@@ -682,8 +735,8 @@ function qywxamNotify(text, desp) {
if (data.errcode === 0) { if (data.errcode === 0) {
console.log( console.log(
'成员ID:' + '成员ID:' +
ChangeUserId(desp) + ChangeUserId(desp) +
'企业微信应用消息发送通知消息成功🎉。\n', '企业微信应用消息发送通知消息成功🎉。\n',
); );
} else { } else {
console.log(`${data.errmsg}\n`); console.log(`${data.errmsg}\n`);
+25
View File
@@ -51,6 +51,10 @@ push_config = {
# /send_group_msg 时填入 group_id=QQ群 # /send_group_msg 时填入 group_id=QQ群
'GOBOT_TOKEN': '', # go-cqhttp 的 access_token 'GOBOT_TOKEN': '', # go-cqhttp 的 access_token
'GOTIFY_URL': '', # gotify地址,如https://push.example.de:8080
'GOTIFY_TOKEN': '', # gotify的消息应用token
'GOTIFY_PRIORITY': 0, # 推送消息优先级,默认为0
'IGOT_PUSH_KEY': '', # iGot 聚合推送的 IGOT_PUSH_KEY 'IGOT_PUSH_KEY': '', # iGot 聚合推送的 IGOT_PUSH_KEY
'PUSH_KEY': '', # server 酱的 PUSH_KEY,兼容旧版与 Turbo 版 'PUSH_KEY': '', # server 酱的 PUSH_KEY,兼容旧版与 Turbo 版
@@ -194,6 +198,25 @@ def go_cqhttp(title: str, content: str) -> None:
print("go-cqhttp 推送失败!") print("go-cqhttp 推送失败!")
def gotify(title:str,content:str) -> None:
"""
使用 gotify 推送消息。
"""
if not push_config.get("GOTIFY_URL") or not push_config.get("GOTIFY_TOKEN"):
print("gotify 服务的 GOTIFY_URL 或 GOTIFY_TOKEN 未设置!!\n取消推送")
return
print("gotify 服务启动")
url = f'{push_config.get("GOTIFY_URL")}/message?token={push_config.get("GOTIFY_TOKEN")}'
data = {"title": title,"message": content,"priority": push_config.get("GOTIFY_PRIORITY")}
response = requests.post(url,data=data).json()
if response.get("id"):
print("gotify 推送成功!")
else:
print("gotify 推送失败!")
def iGot(title: str, content: str) -> None: def iGot(title: str, content: str) -> None:
""" """
使用 iGot 推送消息。 使用 iGot 推送消息。
@@ -473,6 +496,8 @@ if push_config.get("FSKEY"):
notify_function.append(feishu_bot) notify_function.append(feishu_bot)
if push_config.get("GOBOT_URL") and push_config.get("GOBOT_QQ"): if push_config.get("GOBOT_URL") and push_config.get("GOBOT_QQ"):
notify_function.append(go_cqhttp) notify_function.append(go_cqhttp)
if push_config.get("GOTIFY_URL") and push_config.get("GOTIFY_TOKEN"):
notify_function.append(gotify)
if push_config.get("IGOT_PUSH_KEY"): if push_config.get("IGOT_PUSH_KEY"):
notify_function.append(iGot) notify_function.append(iGot)
if push_config.get("PUSH_KEY"): if push_config.get("PUSH_KEY"):
+4 -10
View File
@@ -39,17 +39,11 @@ copy_dep() {
reload_pm2() { reload_pm2() {
pm2 l &>/dev/null pm2 l &>/dev/null
if test -z "$(pm2 info panel 1>/dev/null)"; then pm2 delete panel --source-map-support --time &>/dev/null
pm2 reload panel --source-map-support --time &>/dev/null pm2 start $dir_root/build/app.js -n panel --source-map-support --time &>/dev/null
else
pm2 start $dir_root/build/app.js -n panel --source-map-support --time &>/dev/null
fi
if test -z "$(pm2 info schedule 1>/dev/null)"; then pm2 delete schedule --source-map-support --time &>/dev/null
pm2 reload schedule --source-map-support --time &>/dev/null pm2 start $dir_root/build/schedule.js -n schedule --source-map-support --time &>/dev/null
else
pm2 start $dir_root/build/schedule.js -n schedule --source-map-support --time &>/dev/null
fi
} }
pm2_log() { pm2_log() {
+4 -10
View File
@@ -318,17 +318,11 @@ patch_version() {
reload_pm2() { reload_pm2() {
pm2 l &>/dev/null pm2 l &>/dev/null
if [[ $(pm2 info panel 2>/dev/null) ]]; then pm2 delete panel --source-map-support --time &>/dev/null
pm2 reload panel --source-map-support --time &>/dev/null pm2 start $dir_root/build/app.js -n panel --source-map-support --time &>/dev/null
else
pm2 start $dir_root/build/app.js -n panel --source-map-support --time &>/dev/null
fi
if [[ $(pm2 info schedule 2>/dev/null) ]]; then pm2 delete schedule --source-map-support --time &>/dev/null
pm2 reload schedule --source-map-support --time &>/dev/null pm2 start $dir_root/build/schedule.js -n schedule --source-map-support --time &>/dev/null
else
pm2 start $dir_root/build/schedule.js -n schedule --source-map-support --time &>/dev/null
fi
} }
## 对比脚本 ## 对比脚本
+32 -4
View File
@@ -1,28 +1,42 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Modal, message, Input, Form } from 'antd'; import { Modal, message, Input, Form, Select } from 'antd';
import { request } from '@/utils/http'; import { request } from '@/utils/http';
import config from '@/utils/config'; import config from '@/utils/config';
const { Option } = Select;
const EditScriptNameModal = ({ const EditScriptNameModal = ({
handleCancel, handleCancel,
treeData,
visible, visible,
}: { }: {
visible: boolean; visible: boolean;
handleCancel: (file?: { filename: string }) => void; treeData: any[];
handleCancel: (file?: {
filename: string;
path: string;
key: string;
}) => void;
}) => { }) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [dirs, setDirs] = useState<any[]>([]);
const handleOk = async (values: any) => { const handleOk = async (values: any) => {
setLoading(true); setLoading(true);
values.path = values.path || '';
request request
.post(`${config.apiPrefix}scripts`, { .post(`${config.apiPrefix}scripts`, {
data: { filename: values.filename, content: '' }, data: { filename: values.filename, path: values.path, content: '' },
}) })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('保存文件成功'); message.success('保存文件成功');
handleCancel({ filename: values.filename }); handleCancel({
filename: values.filename,
path: values.path,
key: `${values.path}-${values.filename}`,
});
} else { } else {
message.error(data); message.error(data);
} }
@@ -33,6 +47,8 @@ const EditScriptNameModal = ({
useEffect(() => { useEffect(() => {
form.resetFields(); form.resetFields();
const originDirs = treeData.filter((x) => x.disabled);
setDirs([{ key: '' }, ...originDirs]);
}, [visible]); }, [visible]);
return ( return (
@@ -56,10 +72,22 @@ const EditScriptNameModal = ({
<Form form={form} layout="vertical" name="edit_name_modal"> <Form form={form} layout="vertical" name="edit_name_modal">
<Form.Item <Form.Item
name="filename" name="filename"
label="文件名"
rules={[{ required: true, message: '请输入文件名' }]} rules={[{ required: true, message: '请输入文件名' }]}
> >
<Input placeholder="请输入文件名" /> <Input placeholder="请输入文件名" />
</Form.Item> </Form.Item>
<Form.Item
label="父目录"
name="path"
initialValue={dirs && dirs.length > 0 ? dirs[0].key : ''}
>
<Select placeholder="请选择父目录">
{dirs.map((x) => (
<Option value={x.key}>{x.key || '根'}</Option>
))}
</Select>
</Form.Item>
</Form> </Form>
</Modal> </Modal>
); );
+57 -14
View File
@@ -113,7 +113,7 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
setValue('加载中...'); setValue('加载中...');
const newMode = value ? LangMap[value.slice(-3)] : ''; const newMode = value ? LangMap[value.slice(-3)] : '';
setMode(isPhone && newMode === 'typescript' ? 'javascript' : newMode); setMode(isPhone && newMode === 'typescript' ? 'javascript' : newMode);
setSelect(value); setSelect(node.key);
setTitle(node.parent || node.value); setTitle(node.parent || node.value);
setCurrentNode(node); setCurrentNode(node);
getDetail(node); getDetail(node);
@@ -167,7 +167,7 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
const cancelEdit = () => { const cancelEdit = () => {
setIsEditing(false); setIsEditing(false);
setValue('加载中...'); setValue('加载中...');
getDetail({ value: select }); getDetail(currentNode);
}; };
const saveFile = () => { const saveFile = () => {
@@ -177,7 +177,7 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
<> <>
<Text style={{ wordBreak: 'break-all' }} type="warning"> <Text style={{ wordBreak: 'break-all' }} type="warning">
{select} {currentNode.value}
</Text>{' '} </Text>{' '}
</> </>
@@ -190,7 +190,7 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
request request
.put(`${config.apiPrefix}scripts`, { .put(`${config.apiPrefix}scripts`, {
data: { data: {
filename: select, filename: currentNode.value,
path: currentNode.parent || '', path: currentNode.parent || '',
content, content,
}, },
@@ -230,15 +230,30 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
request request
.delete(`${config.apiPrefix}scripts`, { .delete(`${config.apiPrefix}scripts`, {
data: { data: {
filename: select, filename: currentNode.value,
path: currentNode.parent || '',
}, },
}) })
.then((_data: any) => { .then((_data: any) => {
if (_data.code === 200) { if (_data.code === 200) {
message.success(`删除成功`); message.success(`删除成功`);
let newData = [...data]; let newData = [...data];
const index = newData.findIndex((x) => x.value === select); if (currentNode.parent) {
newData.splice(index, 1); const parentNodeIndex = newData.findIndex(
(x) => x.key === currentNode.parent,
);
const parentNode = newData[parentNodeIndex];
const index = parentNode.children.findIndex(
(y) => y.key === currentNode.key,
);
parentNode.children.splice(index, 1);
newData.splice(parentNodeIndex, 1, { ...parentNode });
} else {
const index = newData.findIndex(
(x) => x.key === currentNode.key,
);
newData.splice(index, 1);
}
setData(newData); setData(newData);
} else { } else {
message.error(_data); message.error(_data);
@@ -256,12 +271,27 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
}; };
const addFileModalClose = ( const addFileModalClose = (
{ filename }: { filename: string } = { filename: '' }, { filename, path, key }: { filename: string; path: string; key: string } = {
filename: '',
path: '',
key: '',
},
) => { ) => {
if (filename) { if (filename) {
const newData = [...data]; const newData = [...data];
const _file = { title: filename, key: filename, value: filename }; const _file = { title: filename, key, value: filename, parent: path };
newData.unshift(_file); if (path) {
const parentNodeIndex = newData.findIndex((x) => x.key === path);
const parentNode = newData[parentNodeIndex];
if (parentNode.children && parentNode.children.length > 0) {
parentNode.children.unshift(_file);
} else {
parentNode.children = [_file];
}
newData.splice(parentNodeIndex, 1, { ...parentNode });
} else {
newData.unshift(_file);
}
setData(newData); setData(newData);
onSelect(_file.value, _file); onSelect(_file.value, _file);
setIsEditing(true); setIsEditing(true);
@@ -273,7 +303,7 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
request request
.post(`${config.apiPrefix}scripts/download`, { .post(`${config.apiPrefix}scripts/download`, {
data: { data: {
filename: select, filename: currentNode.value,
}, },
}) })
.then((_data: any) => { .then((_data: any) => {
@@ -281,7 +311,7 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
const a = document.createElement('a'); const a = document.createElement('a');
a.href = url; a.href = url;
a.download = select; a.download = currentNode.value;
document.documentElement.appendChild(a); document.documentElement.appendChild(a);
a.click(); a.click();
document.documentElement.removeChild(a); document.documentElement.removeChild(a);
@@ -319,10 +349,20 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
<Menu.Item key="add" icon={<PlusOutlined />} onClick={addFile}> <Menu.Item key="add" icon={<PlusOutlined />} onClick={addFile}>
</Menu.Item> </Menu.Item>
<Menu.Item key="edit" icon={<EditOutlined />} onClick={editFile}> <Menu.Item
key="edit"
icon={<EditOutlined />}
onClick={editFile}
disabled={!select}
>
</Menu.Item> </Menu.Item>
<Menu.Item key="delete" icon={<DeleteOutlined />} onClick={deleteFile}> <Menu.Item
key="delete"
icon={<DeleteOutlined />}
onClick={deleteFile}
disabled={!select}
>
</Menu.Item> </Menu.Item>
</Menu> </Menu>
@@ -368,6 +408,7 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
</Tooltip>, </Tooltip>,
<Tooltip title="编辑"> <Tooltip title="编辑">
<Button <Button
disabled={!select}
type="primary" type="primary"
onClick={editFile} onClick={editFile}
icon={<EditOutlined />} icon={<EditOutlined />}
@@ -376,6 +417,7 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
<Tooltip title="删除"> <Tooltip title="删除">
<Button <Button
type="primary" type="primary"
disabled={!select}
onClick={deleteFile} onClick={deleteFile}
icon={<DeleteOutlined />} icon={<DeleteOutlined />}
/> />
@@ -459,6 +501,7 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
/> />
<EditScriptNameModal <EditScriptNameModal
visible={isAddFileModalVisible} visible={isAddFileModalVisible}
treeData={data}
handleCancel={addFileModalClose} handleCancel={addFileModalClose}
/> />
</div> </div>
+10
View File
@@ -64,6 +64,7 @@ export default {
logs: '任务日志', logs: '任务日志',
}, },
notificationModes: [ notificationModes: [
{ value: 'gotify', label: 'Gotify' },
{ value: 'goCqHttpBot', label: 'GoCqHttpBot' }, { value: 'goCqHttpBot', label: 'GoCqHttpBot' },
{ value: 'serverChan', label: 'Server酱' }, { value: 'serverChan', label: 'Server酱' },
{ value: 'bark', label: 'Bark' }, { value: 'bark', label: 'Bark' },
@@ -77,6 +78,15 @@ export default {
{ value: 'closed', label: '已关闭' }, { value: 'closed', label: '已关闭' },
], ],
notificationModeMap: { notificationModeMap: {
gotify: [
{
label: 'gotifyUrl',
tip: 'gotify的url地址,例如 https://push.example.de:8080',
required: true,
},
{ label: 'gotifyToken', tip: 'gotify的消息应用token码', required: true },
{ label: 'gotifyPriority', tip: '推送消息的优先级' },
],
goCqHttpBot: [ goCqHttpBot: [
{ {
label: 'goCqHttpBotUrl', label: 'goCqHttpBotUrl',
+8 -9
View File
@@ -1,10 +1,9 @@
export const version = '2.10.7'; export const version = '2.10.8';
export const changeLogLink = 'https://t.me/jiao_long/226'; export const changeLogLink = 'https://t.me/jiao_long/227';
export const changeLog = `2.10.7 版本说明 export const changeLog = `2.10.8 版本说明
1. repo命令默认给仓库添加sendNotify依赖 1. 脚本管理新建文件增加选择父目录
2. 增加 /ql/deps 目录,此目录下的依赖文件会覆盖系统默认和仓库内的依赖文件 2. 修复脚本管理更新文件
3. 修复脚本管理列表及搜索 3. 增加gotify推送,感谢 https://github.com/kilo5hz PR
4. 修复环境变量手机端列表样式 4. 修复不能复制deps目录文件
5. 修复秒级定时任务服务 5. 修复可能的玩客云问题
6. 修复bot启动命令
`; `;