mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-11-17 05:33:42 +08:00
✨ feat: add telegram bot (#71)
This commit is contained in:
@@ -9,7 +9,8 @@ import {
|
||||
IconGardenCart,
|
||||
IconUser,
|
||||
IconUserScan,
|
||||
IconActivity
|
||||
IconActivity,
|
||||
IconBrandTelegram
|
||||
} from '@tabler/icons-react';
|
||||
|
||||
// constant
|
||||
@@ -23,7 +24,8 @@ const icons = {
|
||||
IconGardenCart,
|
||||
IconUser,
|
||||
IconUserScan,
|
||||
IconActivity
|
||||
IconActivity,
|
||||
IconBrandTelegram
|
||||
};
|
||||
|
||||
// ==============================|| DASHBOARD MENU ITEMS ||============================== //
|
||||
@@ -118,6 +120,15 @@ const panel = {
|
||||
icon: icons.IconAdjustments,
|
||||
breadcrumbs: false,
|
||||
isAdmin: true
|
||||
},
|
||||
{
|
||||
id: 'telegram',
|
||||
title: 'Telegram Bot',
|
||||
type: 'item',
|
||||
url: '/panel/telegram',
|
||||
icon: icons.IconBrandTelegram,
|
||||
breadcrumbs: false,
|
||||
isAdmin: true
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@ const User = Loadable(lazy(() => import('views/User')));
|
||||
const Profile = Loadable(lazy(() => import('views/Profile')));
|
||||
const NotFoundView = Loadable(lazy(() => import('views/Error')));
|
||||
const Analytics = Loadable(lazy(() => import('views/Analytics')));
|
||||
const Telegram = Loadable(lazy(() => import('views/Telegram')));
|
||||
|
||||
// dashboard routing
|
||||
const Dashboard = Loadable(lazy(() => import('views/Dashboard')));
|
||||
@@ -71,6 +72,10 @@ const MainRoutes = {
|
||||
{
|
||||
path: '404',
|
||||
element: <NotFoundView />
|
||||
},
|
||||
{
|
||||
path: 'telegram',
|
||||
element: <Telegram />
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ const ResetPassword = Loadable(lazy(() => import('views/Authentication/Auth/Rese
|
||||
const Home = Loadable(lazy(() => import('views/Home')));
|
||||
const About = Loadable(lazy(() => import('views/About')));
|
||||
const NotFoundView = Loadable(lazy(() => import('views/Error')));
|
||||
const Jump = Loadable(lazy(() => import('views/Jump')));
|
||||
|
||||
// ==============================|| AUTHENTICATION ROUTING ||============================== //
|
||||
|
||||
@@ -51,6 +52,10 @@ const OtherRoutes = {
|
||||
{
|
||||
path: '/404',
|
||||
element: <NotFoundView />
|
||||
},
|
||||
{
|
||||
path: '/jump',
|
||||
element: <Jump />
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
16
web/src/views/Jump/index.js
Normal file
16
web/src/views/Jump/index.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
export default function Jump() {
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const jump = params.get('url');
|
||||
if (jump) {
|
||||
window.location.href = jump;
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
return <div>正在跳转中...</div>;
|
||||
}
|
||||
@@ -12,11 +12,13 @@ import {
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Divider
|
||||
Divider,
|
||||
Chip,
|
||||
Typography
|
||||
} from '@mui/material';
|
||||
import Grid from '@mui/material/Unstable_Grid2';
|
||||
import SubCard from 'ui-component/cards/SubCard';
|
||||
import { IconBrandWechat, IconBrandGithub, IconMail } from '@tabler/icons-react';
|
||||
import { IconBrandWechat, IconBrandGithub, IconMail, IconBrandTelegram } from '@tabler/icons-react';
|
||||
import Label from 'ui-component/Label';
|
||||
import { API } from 'utils/api';
|
||||
import { showError, showSuccess, onGitHubOAuthClicked, copy } from 'utils/common';
|
||||
@@ -141,6 +143,9 @@ export default function Profile() {
|
||||
<Label variant="ghost" color={inputs.email ? 'primary' : 'default'}>
|
||||
<IconMail /> {inputs.email || '未绑定'}
|
||||
</Label>
|
||||
<Label variant="ghost" color={inputs.telegram_id ? 'primary' : 'default'}>
|
||||
<IconBrandTelegram /> {inputs.telegram_id || '未绑定'}
|
||||
</Label>
|
||||
</Stack>
|
||||
<SubCard title="个人信息">
|
||||
<Grid container spacing={2}>
|
||||
@@ -209,6 +214,7 @@ export default function Profile() {
|
||||
</Button>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
<Grid xs={12} md={4}>
|
||||
<Button
|
||||
variant="contained"
|
||||
@@ -229,6 +235,35 @@ export default function Profile() {
|
||||
<></>
|
||||
)}
|
||||
</Grid>
|
||||
|
||||
{status.telegram_bot && ( //&& !inputs.telegram_id
|
||||
<Grid xs={12} md={12}>
|
||||
<Stack spacing={2}>
|
||||
<Divider />
|
||||
|
||||
<Alert severity="info">
|
||||
<Typography variant="h3">Telegram 机器人</Typography>
|
||||
<br />
|
||||
<Typography variant="body1">
|
||||
1. 点击下方按钮,将会在 Telegram 中打开 机器人,点击 /start 开始。
|
||||
<br />
|
||||
<Chip
|
||||
icon={<IconBrandTelegram />}
|
||||
label={'@' + status.telegram_bot}
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
size="small"
|
||||
onClick={() => window.open('https://t.me/' + status.telegram_bot, '_blank')}
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
2. 向机器人发送/bind命令后,输入下方的访问令牌即可绑定。(如果没有生成,请点击下方按钮生成)
|
||||
</Typography>
|
||||
</Alert>
|
||||
{/* <Typography variant=""> */}
|
||||
</Stack>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</SubCard>
|
||||
<SubCard title="其他">
|
||||
|
||||
225
web/src/views/Telegram/component/EditModal.js
Normal file
225
web/src/views/Telegram/component/EditModal.js
Normal file
@@ -0,0 +1,225 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import * as Yup from 'yup';
|
||||
import { Formik } from 'formik';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { useState, useEffect } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Button,
|
||||
Divider,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
OutlinedInput,
|
||||
FormHelperText,
|
||||
Select,
|
||||
MenuItem,
|
||||
TextField
|
||||
} from '@mui/material';
|
||||
|
||||
import { showSuccess, showError } from 'utils/common';
|
||||
import { API } from 'utils/api';
|
||||
|
||||
const validationSchema = Yup.object().shape({
|
||||
is_edit: Yup.boolean(),
|
||||
command: Yup.string().required('命令 不能为空'),
|
||||
description: Yup.string().required('说明 不能为空'),
|
||||
parse_mode: Yup.string().required('消息类型 不能为空'),
|
||||
reply_message: Yup.string().required('消息内容 不能为空')
|
||||
});
|
||||
|
||||
const originInputs = {
|
||||
command: '',
|
||||
description: '',
|
||||
parse_mode: 'MarkdownV2',
|
||||
reply_message: ''
|
||||
};
|
||||
|
||||
const EditModal = ({ open, actionId, onCancel, onOk }) => {
|
||||
const theme = useTheme();
|
||||
const [inputs, setInputs] = useState(originInputs);
|
||||
|
||||
const submit = async (values, { setErrors, setStatus, setSubmitting }) => {
|
||||
setSubmitting(true);
|
||||
|
||||
let res;
|
||||
try {
|
||||
if (values.is_edit) {
|
||||
res = await API.post(`/api/option/telegram/`, { ...values, id: parseInt(actionId) });
|
||||
} else {
|
||||
res = await API.post(`/api/option/telegram/`, values);
|
||||
}
|
||||
const { success, message } = res.data;
|
||||
if (success) {
|
||||
if (values.is_edit) {
|
||||
showSuccess('菜单更新成功!');
|
||||
} else {
|
||||
showSuccess('菜单创建成功!');
|
||||
}
|
||||
setSubmitting(false);
|
||||
setStatus({ success: true });
|
||||
onOk(true);
|
||||
} else {
|
||||
showError(message);
|
||||
setErrors({ submit: message });
|
||||
}
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const load = async () => {
|
||||
try {
|
||||
let res = await API.get(`/api/option/telegram/${actionId}`);
|
||||
const { success, message, data } = res.data;
|
||||
if (success) {
|
||||
data.is_edit = true;
|
||||
setInputs(data);
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (actionId) {
|
||||
load().then();
|
||||
} else {
|
||||
setInputs(originInputs);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [actionId]);
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={onCancel} fullWidth maxWidth={'md'}>
|
||||
<DialogTitle sx={{ margin: '0px', fontWeight: 700, lineHeight: '1.55556', padding: '24px', fontSize: '1.125rem' }}>
|
||||
{actionId ? '编辑菜单' : '新建菜单'}
|
||||
</DialogTitle>
|
||||
<Divider />
|
||||
<DialogContent>
|
||||
<Formik initialValues={inputs} enableReinitialize validationSchema={validationSchema} onSubmit={submit}>
|
||||
{({ errors, handleBlur, handleChange, handleSubmit, touched, values, isSubmitting }) => (
|
||||
<form noValidate onSubmit={handleSubmit}>
|
||||
<FormControl fullWidth error={Boolean(touched.command && errors.command)} sx={{ ...theme.typography.otherInput }}>
|
||||
<InputLabel htmlFor="channel-command-label">命令</InputLabel>
|
||||
<OutlinedInput
|
||||
id="channel-command-label"
|
||||
label="命令"
|
||||
type="text"
|
||||
value={values.command}
|
||||
name="command"
|
||||
onBlur={handleBlur}
|
||||
onChange={handleChange}
|
||||
inputProps={{ autoComplete: 'command' }}
|
||||
aria-describedby="helper-text-channel-command-label"
|
||||
/>
|
||||
{touched.command && errors.command && (
|
||||
<FormHelperText error id="helper-tex-channel-command-label">
|
||||
{errors.command}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</FormControl>
|
||||
|
||||
<FormControl fullWidth error={Boolean(touched.description && errors.description)} sx={{ ...theme.typography.otherInput }}>
|
||||
<InputLabel htmlFor="channel-description-label">说明</InputLabel>
|
||||
<OutlinedInput
|
||||
id="channel-description-label"
|
||||
label="说明"
|
||||
type="text"
|
||||
value={values.description}
|
||||
name="description"
|
||||
onBlur={handleBlur}
|
||||
onChange={handleChange}
|
||||
inputProps={{ autoComplete: 'description' }}
|
||||
aria-describedby="helper-text-channel-description-label"
|
||||
/>
|
||||
{touched.description && errors.description && (
|
||||
<FormHelperText error id="helper-tex-channel-description-label">
|
||||
{errors.description}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</FormControl>
|
||||
|
||||
<FormControl fullWidth error={Boolean(touched.parse_mode && errors.parse_mode)} sx={{ ...theme.typography.otherInput }}>
|
||||
<InputLabel htmlFor="channel-parse_mode-label">消息类型</InputLabel>
|
||||
<Select
|
||||
id="channel-parse_mode-label"
|
||||
label="消息类型"
|
||||
value={values.parse_mode}
|
||||
name="parse_mode"
|
||||
onBlur={handleBlur}
|
||||
onChange={handleChange}
|
||||
MenuProps={{
|
||||
PaperProps: {
|
||||
style: {
|
||||
maxHeight: 200
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<MenuItem key="MarkdownV2" value="MarkdownV2">
|
||||
{' '}
|
||||
MarkdownV2{' '}
|
||||
</MenuItem>
|
||||
<MenuItem key="Markdown" value="Markdown">
|
||||
{' '}
|
||||
Markdown{' '}
|
||||
</MenuItem>
|
||||
<MenuItem key="html" value="html">
|
||||
{' '}
|
||||
html{' '}
|
||||
</MenuItem>
|
||||
</Select>
|
||||
{touched.parse_mode && errors.parse_mode && (
|
||||
<FormHelperText error id="helper-tex-channel-parse_mode-label">
|
||||
{errors.parse_mode}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</FormControl>
|
||||
|
||||
<FormControl fullWidth error={Boolean(touched.reply_message && errors.reply_message)} sx={{ ...theme.typography.otherInput }}>
|
||||
<TextField
|
||||
multiline
|
||||
id="channel-reply_message-label"
|
||||
label="消息内容"
|
||||
value={values.reply_message}
|
||||
name="reply_message"
|
||||
onBlur={handleBlur}
|
||||
onChange={handleChange}
|
||||
aria-describedby="helper-text-channel-reply_message-label"
|
||||
minRows={5}
|
||||
placeholder="消息内容"
|
||||
/>
|
||||
{touched.reply_message && errors.reply_message && (
|
||||
<FormHelperText error id="helper-tex-channel-reply_message-label">
|
||||
{errors.reply_message}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</FormControl>
|
||||
|
||||
<DialogActions>
|
||||
<Button onClick={onCancel}>取消</Button>
|
||||
<Button disableElevation disabled={isSubmitting} type="submit" variant="contained" color="primary">
|
||||
提交
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</form>
|
||||
)}
|
||||
</Formik>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditModal;
|
||||
|
||||
EditModal.propTypes = {
|
||||
open: PropTypes.bool,
|
||||
actionId: PropTypes.number,
|
||||
onCancel: PropTypes.func,
|
||||
onOk: PropTypes.func
|
||||
};
|
||||
114
web/src/views/Telegram/component/TableRow.js
Normal file
114
web/src/views/Telegram/component/TableRow.js
Normal file
@@ -0,0 +1,114 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { useState } from 'react';
|
||||
|
||||
import {
|
||||
Popover,
|
||||
TableRow,
|
||||
MenuItem,
|
||||
TableCell,
|
||||
IconButton,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
Button,
|
||||
Stack
|
||||
} from '@mui/material';
|
||||
|
||||
import { IconDotsVertical, IconEdit, IconTrash } from '@tabler/icons-react';
|
||||
|
||||
export default function TelegramTableRow({ item, manageAction, handleOpenModal, setModalId }) {
|
||||
const [open, setOpen] = useState(null);
|
||||
const [openDelete, setOpenDelete] = useState(false);
|
||||
|
||||
const handleDeleteOpen = () => {
|
||||
handleCloseMenu();
|
||||
setOpenDelete(true);
|
||||
};
|
||||
|
||||
const handleDeleteClose = () => {
|
||||
setOpenDelete(false);
|
||||
};
|
||||
|
||||
const handleOpenMenu = (event) => {
|
||||
setOpen(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleCloseMenu = () => {
|
||||
setOpen(null);
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
handleCloseMenu();
|
||||
await manageAction(item.id, 'delete');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableRow tabIndex={item.id}>
|
||||
<TableCell>{item.id}</TableCell>
|
||||
|
||||
<TableCell>{item.command}</TableCell>
|
||||
|
||||
<TableCell>{item.description}</TableCell>
|
||||
|
||||
<TableCell>{item.parse_mode}</TableCell>
|
||||
<TableCell>{item.reply_message}</TableCell>
|
||||
<TableCell>
|
||||
<Stack direction="row" spacing={1}>
|
||||
<IconButton onClick={handleOpenMenu} sx={{ color: 'rgb(99, 115, 129)' }}>
|
||||
<IconDotsVertical />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<Popover
|
||||
open={!!open}
|
||||
anchorEl={open}
|
||||
onClose={handleCloseMenu}
|
||||
anchorOrigin={{ vertical: 'top', horizontal: 'left' }}
|
||||
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
|
||||
PaperProps={{
|
||||
sx: { width: 140 }
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleCloseMenu();
|
||||
handleOpenModal();
|
||||
setModalId(item.id);
|
||||
}}
|
||||
>
|
||||
<IconEdit style={{ marginRight: '16px' }} />
|
||||
编辑
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleDeleteOpen} sx={{ color: 'error.main' }}>
|
||||
<IconTrash style={{ marginRight: '16px' }} />
|
||||
删除
|
||||
</MenuItem>
|
||||
</Popover>
|
||||
|
||||
<Dialog open={openDelete} onClose={handleDeleteClose}>
|
||||
<DialogTitle>删除菜单</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>是否删除菜单 {item.name}?</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleDeleteClose}>关闭</Button>
|
||||
<Button onClick={handleDelete} sx={{ color: 'error.main' }} autoFocus>
|
||||
删除
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
TelegramTableRow.propTypes = {
|
||||
item: PropTypes.object,
|
||||
manageAction: PropTypes.func,
|
||||
handleOpenModal: PropTypes.func,
|
||||
setModalId: PropTypes.func
|
||||
};
|
||||
270
web/src/views/Telegram/index.js
Normal file
270
web/src/views/Telegram/index.js
Normal file
@@ -0,0 +1,270 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { showError, showSuccess } from 'utils/common';
|
||||
|
||||
import Table from '@mui/material/Table';
|
||||
import TableBody from '@mui/material/TableBody';
|
||||
import TableContainer from '@mui/material/TableContainer';
|
||||
import PerfectScrollbar from 'react-perfect-scrollbar';
|
||||
import TablePagination from '@mui/material/TablePagination';
|
||||
import LinearProgress from '@mui/material/LinearProgress';
|
||||
import ButtonGroup from '@mui/material/ButtonGroup';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
|
||||
import { Button, Card, Box, Stack, Container, Typography, Chip, Alert } from '@mui/material';
|
||||
import TelegramTableRow from './component/TableRow';
|
||||
import KeywordTableHead from 'ui-component/TableHead';
|
||||
import TableToolBar from 'ui-component/TableToolBar';
|
||||
import { API } from 'utils/api';
|
||||
import { ITEMS_PER_PAGE } from 'constants';
|
||||
import { IconRefresh, IconPlus } from '@tabler/icons-react';
|
||||
import EditeModal from './component/EditModal';
|
||||
import { IconBrandTelegram, IconReload } from '@tabler/icons-react';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
export default function Telegram() {
|
||||
const [page, setPage] = useState(0);
|
||||
const [order, setOrder] = useState('desc');
|
||||
const [orderBy, setOrderBy] = useState('id');
|
||||
const [rowsPerPage, setRowsPerPage] = useState(ITEMS_PER_PAGE);
|
||||
const [listCount, setListCount] = useState(0);
|
||||
const [searchKeyword, setSearchKeyword] = useState('');
|
||||
const [searching, setSearching] = useState(false);
|
||||
const [telegramMenus, setTelegramMenus] = useState([]);
|
||||
const [refreshFlag, setRefreshFlag] = useState(false);
|
||||
let [status, setStatus] = useState(false);
|
||||
let [isWebhook, setIsWebhook] = useState(false);
|
||||
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const [editTelegramMenusId, setEditTelegramMenusId] = useState(0);
|
||||
|
||||
const handleSort = (event, id) => {
|
||||
const isAsc = orderBy === id && order === 'asc';
|
||||
if (id !== '') {
|
||||
setOrder(isAsc ? 'desc' : 'asc');
|
||||
setOrderBy(id);
|
||||
}
|
||||
};
|
||||
|
||||
const handleChangePage = (event, newPage) => {
|
||||
setPage(newPage);
|
||||
};
|
||||
|
||||
const handleChangeRowsPerPage = (event) => {
|
||||
setPage(0);
|
||||
setRowsPerPage(parseInt(event.target.value, 10));
|
||||
};
|
||||
|
||||
const searchMenus = async (event) => {
|
||||
event.preventDefault();
|
||||
const formData = new FormData(event.target);
|
||||
setPage(0);
|
||||
setSearchKeyword(formData.get('keyword'));
|
||||
};
|
||||
|
||||
const fetchData = async (page, rowsPerPage, keyword, order, orderBy) => {
|
||||
setSearching(true);
|
||||
try {
|
||||
if (orderBy) {
|
||||
orderBy = order === 'desc' ? '-' + orderBy : orderBy;
|
||||
}
|
||||
const res = await API.get(`/api/option/telegram/`, {
|
||||
params: {
|
||||
page: page + 1,
|
||||
size: rowsPerPage,
|
||||
keyword: keyword,
|
||||
order: orderBy
|
||||
}
|
||||
});
|
||||
const { success, message, data } = res.data;
|
||||
if (success) {
|
||||
setListCount(data.total_count);
|
||||
setTelegramMenus(data.data);
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
setSearching(false);
|
||||
};
|
||||
|
||||
const reload = async () => {
|
||||
try {
|
||||
const res = await API.put('/api/option/telegram/reload');
|
||||
const { success, message } = res.data;
|
||||
if (success) {
|
||||
showSuccess('重载成功!');
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatus = async () => {
|
||||
try {
|
||||
const res = await API.get('/api/option/telegram/status');
|
||||
const { success, data } = res.data;
|
||||
if (success) {
|
||||
setStatus(data.status);
|
||||
setIsWebhook(data.is_webhook);
|
||||
}
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// 处理刷新
|
||||
const handleRefresh = async () => {
|
||||
setOrderBy('id');
|
||||
setOrder('desc');
|
||||
setRefreshFlag(!refreshFlag);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(page, rowsPerPage, searchKeyword, order, orderBy);
|
||||
}, [page, rowsPerPage, searchKeyword, order, orderBy, refreshFlag]);
|
||||
|
||||
useEffect(() => {
|
||||
getStatus().then();
|
||||
}, []);
|
||||
|
||||
const manageMenus = async (id, action) => {
|
||||
const url = '/api/option/telegram/';
|
||||
let res;
|
||||
|
||||
try {
|
||||
switch (action) {
|
||||
case 'delete':
|
||||
res = await API.delete(url + id);
|
||||
break;
|
||||
}
|
||||
const { success, message } = res.data;
|
||||
if (success) {
|
||||
showSuccess('操作成功完成!');
|
||||
if (action === 'delete') {
|
||||
await handleRefresh();
|
||||
}
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const handleOpenModal = (id) => {
|
||||
setEditTelegramMenusId(id);
|
||||
setOpenModal(true);
|
||||
};
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setOpenModal(false);
|
||||
setEditTelegramMenusId(0);
|
||||
};
|
||||
|
||||
const handleOkModal = (status) => {
|
||||
if (status === true) {
|
||||
handleCloseModal();
|
||||
handleRefresh();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack direction="row" alignItems="center" justifyContent="space-between" mb={5}>
|
||||
<Typography variant="h4">Telegram Bot菜单</Typography>
|
||||
<Button variant="contained" color="primary" startIcon={<IconPlus />} onClick={() => handleOpenModal(0)}>
|
||||
新建
|
||||
</Button>
|
||||
</Stack>
|
||||
<Stack mb={5}>
|
||||
<Alert severity="info">
|
||||
添加修改菜单命令/说明后(如果没有修改命令和说明可以不用重载),需要重新载入菜单才能生效。
|
||||
如果未查看到新菜单,请尝试杀后台后重新启动程序。
|
||||
</Alert>
|
||||
</Stack>
|
||||
<Stack direction="row" alignItems="center" justifyContent="flex-start" mb={2} spacing={2}>
|
||||
<Chip
|
||||
icon={<IconBrandTelegram />}
|
||||
label={(status ? '在线' : '离线') + (isWebhook ? '(Webhook)' : '(Polling)')}
|
||||
color={status ? 'primary' : 'error'}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
/>
|
||||
|
||||
<Button variant="contained" size="small" endIcon={<IconReload />} onClick={reload}>
|
||||
重新载入菜单
|
||||
</Button>
|
||||
</Stack>
|
||||
<Card>
|
||||
<Box component="form" onSubmit={searchMenus} noValidate>
|
||||
<TableToolBar placeholder={'搜索ID和命令...'} />
|
||||
</Box>
|
||||
<Toolbar
|
||||
sx={{
|
||||
textAlign: 'right',
|
||||
height: 50,
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
p: (theme) => theme.spacing(0, 1, 0, 3)
|
||||
}}
|
||||
>
|
||||
<Container>
|
||||
<ButtonGroup variant="outlined" aria-label="outlined small primary button group">
|
||||
<Button onClick={handleRefresh} startIcon={<IconRefresh width={'18px'} />}>
|
||||
刷新
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Container>
|
||||
</Toolbar>
|
||||
{searching && <LinearProgress />}
|
||||
<PerfectScrollbar component="div">
|
||||
<TableContainer sx={{ overflow: 'unset' }}>
|
||||
<Table sx={{ minWidth: 800 }}>
|
||||
<KeywordTableHead
|
||||
order={order}
|
||||
orderBy={orderBy}
|
||||
onRequestSort={handleSort}
|
||||
headLabel={[
|
||||
{ id: 'id', label: 'ID', disableSort: false },
|
||||
{ id: 'command', label: '命令', disableSort: false },
|
||||
{ id: 'description', label: '说明', disableSort: false },
|
||||
{ id: 'parse_mode', label: '回复类型', disableSort: false },
|
||||
{ id: 'reply_message', label: '回复内容', disableSort: false },
|
||||
{ id: 'action', label: '操作', disableSort: true }
|
||||
]}
|
||||
/>
|
||||
<TableBody>
|
||||
{telegramMenus.map((row) => (
|
||||
<TelegramTableRow
|
||||
item={row}
|
||||
manageAction={manageMenus}
|
||||
key={row.id}
|
||||
handleOpenModal={handleOpenModal}
|
||||
setModalId={setEditTelegramMenusId}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</PerfectScrollbar>
|
||||
<TablePagination
|
||||
page={page}
|
||||
component="div"
|
||||
count={listCount}
|
||||
rowsPerPage={rowsPerPage}
|
||||
onPageChange={handleChangePage}
|
||||
rowsPerPageOptions={[10, 25, 30]}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
showFirstButton
|
||||
showLastButton
|
||||
/>
|
||||
</Card>
|
||||
<EditeModal open={openModal} onCancel={handleCloseModal} onOk={handleOkModal} actionId={editTelegramMenusId} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user