diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b577ac8..8871bd4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,10 +5,11 @@ ci: autoupdate_schedule: weekly autoupdate_commit_msg: "auto update by pre-commit hooks" repos: - - repo: https://github.com/pycqa/isort - rev: 5.10.1 - hooks: - - id: isort + # - repo: https://github.com/pycqa/isort + # rev: 5.10.1 + # hooks: + # - id: isort + # args: ["--profile", "black", "--filter-files"] - repo: https://github.com/psf/black rev: 22.1.0 @@ -19,4 +20,4 @@ repos: rev: v2.5.1 hooks: - id: prettier - types_or: [markdown] + types_or: [markdown, ts, tsx] diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fa00c1..c858311 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,3 +51,9 @@ - 增加过滤 nonebot 日志功能 - 前端可以刷新了(之前居然不可以) - 在镜像里塞进了浏览器(导致镜像体积起飞) + +## [0.4.4] + +- 又双叒叕重构了一下 +- 修复了 Docker 中 Playwright 下载的浏览器版本不正确问题 +- 加入了猴子补丁,使 Windows 里能运行 Playwright diff --git a/README.md b/README.md index 4c310f0..d3c6348 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ 支持的平台: - 微博 -- B 站 +- Bilibili - RSS - 明日方舟 - 塞壬唱片新闻 diff --git a/admin-frontend/src/App.test.tsx b/admin-frontend/src/App.test.tsx index 2a68616..d76787e 100644 --- a/admin-frontend/src/App.test.tsx +++ b/admin-frontend/src/App.test.tsx @@ -1,8 +1,8 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import App from './App'; +import React from "react"; +import { render, screen } from "@testing-library/react"; +import App from "./App"; -test('renders learn react link', () => { +test("renders learn react link", () => { render(); const linkElement = screen.getByText(/learn react/i); expect(linkElement).toBeInTheDocument(); diff --git a/admin-frontend/src/App.tsx b/admin-frontend/src/App.tsx index aacb1e7..6ab4c01 100644 --- a/admin-frontend/src/App.tsx +++ b/admin-frontend/src/App.tsx @@ -1,49 +1,46 @@ -import 'antd/dist/antd.css'; -import React, {useEffect} from 'react'; -import {useDispatch, useSelector} from 'react-redux'; -import {BrowserRouter as Router, Route, Switch} from 'react-router-dom'; -import './App.css'; -import {Admin} from './pages/admin'; -import {Auth} from './pages/auth'; -import {getGlobalConf} from './store/globalConfSlice'; -import {useAppSelector} from './store/hooks'; -import {loadLoginState, loginSelector} from './store/loginSlice'; - +import "antd/dist/antd.css"; +import React, { useEffect } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; +import "./App.css"; +import { Admin } from "./pages/admin"; +import { Auth } from "./pages/auth"; +import { getGlobalConf } from "./store/globalConfSlice"; +import { useAppSelector } from "./store/hooks"; +import { loadLoginState, loginSelector } from "./store/loginSlice"; function LoginSwitch() { - const login = useSelector(loginSelector) + const login = useSelector(loginSelector); if (login.login) { return ; } else { - return ( -
- not login -
- ) + return
not login
; } } function App() { - const dispatch = useDispatch() - const globalConf = useAppSelector(state => state.globalConf) + const dispatch = useDispatch(); + const globalConf = useAppSelector((state) => state.globalConf); useEffect(() => { dispatch(getGlobalConf()); - dispatch(loadLoginState()) + dispatch(loadLoginState()); }, [dispatch]); - return <> - { globalConf.loaded && + return ( + <> + {globalConf.loaded && ( - + - + - } - ; + )} + + ); } export default App; diff --git a/admin-frontend/src/api/config.ts b/admin-frontend/src/api/config.ts index 3286f74..d66f325 100644 --- a/admin-frontend/src/api/config.ts +++ b/admin-frontend/src/api/config.ts @@ -1,6 +1,12 @@ import axios from "axios"; -import { GlobalConf, TokenResp, SubscribeResp, TargetNameResp, SubscribeConfig } from "../utils/type"; -import { baseUrl } from './utils'; +import { + GlobalConf, + TokenResp, + SubscribeResp, + TargetNameResp, + SubscribeConfig, +} from "../utils/type"; +import { baseUrl } from "./utils"; export async function getGlobalConf(): Promise { const res = await axios.get(`${baseUrl}global_conf`); @@ -8,7 +14,9 @@ export async function getGlobalConf(): Promise { } export async function auth(token: string): Promise { - const res = await axios.get(`${baseUrl}auth`, {params: {token}}); + const res = await axios.get(`${baseUrl}auth`, { + params: { token }, + }); return res.data; } @@ -17,22 +25,39 @@ export async function getSubscribe(): Promise { return res.data; } -export async function getTargetName(platformName: string, target: string): Promise { - const res = await axios.get(`${baseUrl}target_name`, {params: {platformName, target}}); +export async function getTargetName( + platformName: string, + target: string +): Promise { + const res = await axios.get(`${baseUrl}target_name`, { + params: { platformName, target }, + }); return res.data; } export async function addSubscribe(groupNumber: string, req: SubscribeConfig) { - const res = await axios.post(`${baseUrl}subs`, req, {params: {groupNumber}}) + const res = await axios.post(`${baseUrl}subs`, req, { + params: { groupNumber }, + }); return res.data; } -export async function delSubscribe(groupNumber: string, platformName: string, target: string) { - const res = await axios.delete(`${baseUrl}subs`, {params: {groupNumber, platformName, target}}); +export async function delSubscribe( + groupNumber: string, + platformName: string, + target: string +) { + const res = await axios.delete(`${baseUrl}subs`, { + params: { groupNumber, platformName, target }, + }); return res.data; } -export async function updateSubscribe(groupNumber: string, req: SubscribeConfig) { - return axios.patch(`${baseUrl}subs`, req, {params: {groupNumber}}) - .then(res => res.data); +export async function updateSubscribe( + groupNumber: string, + req: SubscribeConfig +) { + return axios + .patch(`${baseUrl}subs`, req, { params: { groupNumber } }) + .then((res) => res.data); } diff --git a/admin-frontend/src/api/utils.ts b/admin-frontend/src/api/utils.ts index ba8a7c9..13ee025 100644 --- a/admin-frontend/src/api/utils.ts +++ b/admin-frontend/src/api/utils.ts @@ -1,51 +1,61 @@ -import axios, {AxiosError} from "axios"; -import {Store} from "src/store"; -import { clearLoginStatus } from 'src/store/loginSlice'; +import axios, { AxiosError } from "axios"; +import { Store } from "src/store"; +import { clearLoginStatus } from "src/store/loginSlice"; // import { useContext } from 'react'; // import { LoginContext } from "../utils/context"; -export const baseUrl = '/bison/api/' -let store: Store +export const baseUrl = "/bison/api/"; +let store: Store; export const injectStore = (_store: Store) => { - store = _store -} + store = _store; +}; // const loginStatus = useContext(LoginContext); -axios.interceptors.request.use(function (config) { - if (config.url && config.url.startsWith(baseUrl) && config.url !== `${baseUrl}auth` - && config.url !== `${baseUrl}global_conf`) { - const token = localStorage.getItem('token'); - if (token) { - config.headers['Authorization'] = `Bearer ${token}`; - } else { - throw new axios.Cancel('User not login'); +axios.interceptors.request.use( + function (config) { + if ( + config.url && + config.url.startsWith(baseUrl) && + config.url !== `${baseUrl}auth` && + config.url !== `${baseUrl}global_conf` + ) { + const token = localStorage.getItem("token"); + if (token) { + config.headers["Authorization"] = `Bearer ${token}`; + } else { + throw new axios.Cancel("User not login"); + } } + return config; + }, + function (error) { + return Promise.reject(error); } - return config; -}, function (error) { - return Promise.reject(error); -}); +); -axios.interceptors.response.use(function (response) { - // const data = response.data; - // const parseToMap = (item: any): any => { - // if (item instanceof Array) { - // return item.map(parseToMap); - // } else if (item instanceof Object) { - // let res = new Map(); - // for (const key of Object.keys(item)) { - // res.set(key, parseToMap(item[key])); - // } - // return res; - // } else { - // return item; - // } - // } - // response.data = parseToMap(data); - return response; -}, function(error: AxiosError) { - if(error.response && error.response.status === 401) { - store.dispatch(clearLoginStatus()); +axios.interceptors.response.use( + function (response) { + // const data = response.data; + // const parseToMap = (item: any): any => { + // if (item instanceof Array) { + // return item.map(parseToMap); + // } else if (item instanceof Object) { + // let res = new Map(); + // for (const key of Object.keys(item)) { + // res.set(key, parseToMap(item[key])); + // } + // return res; + // } else { + // return item; + // } + // } + // response.data = parseToMap(data); + return response; + }, + function (error: AxiosError) { + if (error.response && error.response.status === 401) { + store.dispatch(clearLoginStatus()); + } + return Promise.reject(error); } - return Promise.reject(error); -}); +); diff --git a/admin-frontend/src/component/addSubsModal.tsx b/admin-frontend/src/component/addSubsModal.tsx index b51113e..9b68a8f 100644 --- a/admin-frontend/src/component/addSubsModal.tsx +++ b/admin-frontend/src/component/addSubsModal.tsx @@ -1,15 +1,15 @@ -import {Form, Input, Modal, Select, Tag} from 'antd'; -import React, {useEffect, useState} from "react"; -import {useSelector} from 'react-redux'; -import {addSubscribe, getTargetName, updateSubscribe} from 'src/api/config'; -import {InputTag} from 'src/component/inputTag'; -import {platformConfSelector} from 'src/store/globalConfSlice'; -import {CategoryConfig, SubscribeConfig} from 'src/utils/type'; +import { Form, Input, Modal, Select, Tag } from "antd"; +import React, { useEffect, useState } from "react"; +import { useSelector } from "react-redux"; +import { addSubscribe, getTargetName, updateSubscribe } from "src/api/config"; +import { InputTag } from "src/component/inputTag"; +import { platformConfSelector } from "src/store/globalConfSlice"; +import { CategoryConfig, SubscribeConfig } from "src/utils/type"; interface InputTagCustomProp { - value?: Array, - onChange?: (value: Array) => void, - disabled?: boolean + value?: Array; + onChange?: (value: Array) => void; + disabled?: boolean; } function InputTagCustom(prop: InputTagCustomProp) { const [value, setValue] = useState(prop.value || []); @@ -18,165 +18,202 @@ function InputTagCustom(prop: InputTagCustomProp) { if (prop.onChange) { prop.onChange(newVal); } - } + }; useEffect(() => { if (prop.value) { setValue(prop.value); } - }, [prop.value]) + }, [prop.value]); return ( <> - { - prop.disabled ? 不支持标签: + {prop.disabled ? ( + 不支持标签 + ) : ( <> - {value.length === 0 && - 全部标签 - } - - - } + {value.length === 0 && 全部标签} + + + )} - ) + ); } interface AddModalProp { - showModal: boolean, - groupNumber: string, - setShowModal: (s: boolean) => void, - refresh: () => void - initVal?: SubscribeConfig + showModal: boolean; + groupNumber: string; + setShowModal: (s: boolean) => void; + refresh: () => void; + initVal?: SubscribeConfig; } export function AddModal({ - showModal, groupNumber, setShowModal, refresh, initVal + showModal, + groupNumber, + setShowModal, + refresh, + initVal, }: AddModalProp) { - const [ confirmLoading, setConfirmLoading ] = useState(false); - const platformConf = useSelector(platformConfSelector) - const [ hasTarget, setHasTarget ] = useState(false); - const [ categories, setCategories ] = useState({} as CategoryConfig); - const [ enabledTag, setEnableTag ] = useState(false); - const [ form ] = Form.useForm(); - const [ inited, setInited ] = useState(false); + const [confirmLoading, setConfirmLoading] = useState(false); + const platformConf = useSelector(platformConfSelector); + const [hasTarget, setHasTarget] = useState(false); + const [categories, setCategories] = useState({} as CategoryConfig); + const [enabledTag, setEnableTag] = useState(false); + const [form] = Form.useForm(); + const [inited, setInited] = useState(false); const changePlatformSelect = (platform: string) => { - setHasTarget(_ => platformConf[platform].hasTarget); - setCategories(_ => platformConf[platform].categories); - setEnableTag(platformConf[platform].enabledTag) - if (! platformConf[platform].hasTarget) { - getTargetName(platform, 'default') - .then(res => { - console.log(res) - form.setFieldsValue({ - targetName: res.targetName, - target: '' - }) - }) + setHasTarget((_) => platformConf[platform].hasTarget); + setCategories((_) => platformConf[platform].categories); + setEnableTag(platformConf[platform].enabledTag); + if (!platformConf[platform].hasTarget) { + getTargetName(platform, "default").then((res) => { + console.log(res); + form.setFieldsValue({ + targetName: res.targetName, + target: "", + }); + }); } else { form.setFieldsValue({ - targetName: '', - target: '' - }) + targetName: "", + target: "", + }); } - } + }; const handleSubmit = (value: any) => { - let newVal = Object.assign({}, value) - if (typeof newVal.tags !== 'object') { - newVal.tags = [] + let newVal = Object.assign({}, value); + if (typeof newVal.tags !== "object") { + newVal.tags = []; } - if (typeof newVal.cats !== 'object') { - newVal.cats = [] + if (typeof newVal.cats !== "object") { + newVal.cats = []; } - if (newVal.target === '') { - newVal.target = 'default' + if (newVal.target === "") { + newVal.target = "default"; } - if (initVal) { // patch - updateSubscribe(groupNumber, newVal) - .then(() => { - setConfirmLoading(false); - setShowModal(false); - form.resetFields(); - refresh(); - }); - } else { - addSubscribe(groupNumber, newVal) - .then(() => { + if (initVal) { + // patch + updateSubscribe(groupNumber, newVal).then(() => { setConfirmLoading(false); setShowModal(false); form.resetFields(); refresh(); - }); + }); + } else { + addSubscribe(groupNumber, newVal).then(() => { + setConfirmLoading(false); + setShowModal(false); + form.resetFields(); + refresh(); + }); } - } + }; const handleModleFinish = () => { form.submit(); setConfirmLoading(() => true); - } + }; useEffect(() => { if (initVal && !inited) { const platformName = initVal.platformName; setHasTarget(platformConf[platformName].hasTarget); setCategories(platformConf[platformName].categories); setEnableTag(platformConf[platformName].enabledTag); - setInited(true) - form.setFieldsValue(initVal) + setInited(true); + form.setFieldsValue(initVal); } - }, [initVal, form, platformConf, inited]) - return setShowModal(false)} - onOk={handleModleFinish}> -
- - - - { - try { - const res = await getTargetName(form.getFieldValue('platformName'), value); - if (res.targetName) { - form.setFieldsValue({ - targetName: res.targetName - }) - return Promise.resolve() - } else { - form.setFieldsValue({ - targetName: '' - }) - return Promise.reject("账号不正确,请重新检查账号") - } - } catch { - return Promise.reject('服务器错误,请稍后再试') - } - } - } - ]}> - - - - - - 0, message: "请至少选择一个分类进行订阅"} - ]}> - + {Object.keys(platformConf).map((platformName) => ( + + {platformConf[platformName].name} - ) - } - - - - - -
+ ))} + + + { + try { + const res = await getTargetName( + form.getFieldValue("platformName"), + value + ); + if (res.targetName) { + form.setFieldsValue({ + targetName: res.targetName, + }); + return Promise.resolve(); + } else { + form.setFieldsValue({ + targetName: "", + }); + return Promise.reject("账号不正确,请重新检查账号"); + } + } catch { + return Promise.reject("服务器错误,请稍后再试"); + } + }, + }, + ]} + > + + + + + + 0, + message: "请至少选择一个分类进行订阅", + }, + ]} + > + + + + + +
+ ); } diff --git a/admin-frontend/src/component/inputTag.tsx b/admin-frontend/src/component/inputTag.tsx index 9408a61..e633e91 100644 --- a/admin-frontend/src/component/inputTag.tsx +++ b/admin-frontend/src/component/inputTag.tsx @@ -1,122 +1,151 @@ -import {Input, Tag, Tooltip} from "antd"; -import {PresetColorType, PresetStatusColorType} from 'antd/lib/_util/colors' -import {LiteralUnion} from 'antd/lib/_util/type' -import React, {useRef, useState, useEffect} from "react"; -import { PlusOutlined } from '@ant-design/icons'; +import { Input, Tag, Tooltip } from "antd"; +import { PresetColorType, PresetStatusColorType } from "antd/lib/_util/colors"; +import { LiteralUnion } from "antd/lib/_util/type"; +import React, { useRef, useState, useEffect } from "react"; +import { PlusOutlined } from "@ant-design/icons"; interface InputTagProp { - value?: Array, - onChange?: (value: Array) => void + value?: Array; + onChange?: (value: Array) => void; color?: LiteralUnion; - addText?: string + addText?: string; } export function InputTag(prop: InputTagProp) { - const [ value, setValue ] = useState>(prop.value || []); - const [ inputVisible, setInputVisible ] = useState(false); - const [ inputValue, setInputValue ] = useState(''); - const [ editInputIndex, setEditInputIndex ] = useState(-1); - const [ editInputValue, setEditInputValue ] = useState(''); + const [value, setValue] = useState>(prop.value || []); + const [inputVisible, setInputVisible] = useState(false); + const [inputValue, setInputValue] = useState(""); + const [editInputIndex, setEditInputIndex] = useState(-1); + const [editInputValue, setEditInputValue] = useState(""); const inputRef = useRef(null as any); const editInputRef = useRef(null as any); useEffect(() => { if (prop.value) { setValue(prop.value); } - }, [prop.value]) + }, [prop.value]); useEffect(() => { if (inputVisible) { - inputRef.current.focus() + inputRef.current.focus(); } }, [inputVisible]); useEffect(() => { if (editInputIndex !== -1) { - editInputRef.current.focus(); + editInputRef.current.focus(); } }, [editInputIndex]); const handleClose = (removedTag: string) => { - const tags = value.filter(tag => tag !== removedTag); - setValue(_ => tags); + const tags = value.filter((tag) => tag !== removedTag); + setValue((_) => tags); if (prop.onChange) { prop.onChange(tags); } - } - + }; + const showInput = () => { - setInputVisible(_ => true); - } + setInputVisible((_) => true); + }; const handleInputConfirm = () => { if (inputValue && value.indexOf(inputValue) === -1) { const newVal = [...value, inputValue]; - setValue(_ => newVal); + setValue((_) => newVal); if (prop.onChange) { prop.onChange(newVal); } } - setInputVisible(_ => false); - setInputValue(_ => ''); - } + setInputVisible((_) => false); + setInputValue((_) => ""); + }; const handleEditInputChange = (e: any) => { - setEditInputValue(_ => e.target.value); - } + setEditInputValue((_) => e.target.value); + }; const handleEditInputConfirm = () => { const newTags = value.slice(); newTags[editInputIndex] = editInputValue; - setValue(_ => newTags); - if (prop.onChange) { - prop.onChange(newTags); - } - setEditInputIndex(_ => -1); - setEditInputValue(_ => ''); - } + setValue((_) => newTags); + if (prop.onChange) { + prop.onChange(newTags); + } + setEditInputIndex((_) => -1); + setEditInputValue((_) => ""); + }; const handleInputChange = (e: any) => { setInputValue(e.target.value); - } + }; return ( <> - { value.map((tag, index) => { + {value.map((tag, index) => { if (editInputIndex === index) { return ( - + + ); + } + const isLongTag = tag.length > 20; + const tagElem = ( + handleClose(tag)} + > + { + setEditInputIndex((_) => index); + setEditInputValue((_) => tag); + e.preventDefault(); + }} + > + {isLongTag ? `${tag.slice(0, 20)}...` : tag} + + ); - } - const isLongTag = tag.length > 20; - const tagElem = ( - handleClose(tag)}> - { - setEditInputIndex(_ => index); - setEditInputValue(_ => tag); - e.preventDefault(); - }}> - {isLongTag ? `${tag.slice(0, 20)}...` : tag} - - + return isLongTag ? ( + + {tagElem} + + ) : ( + tagElem ); - return isLongTag ? ( - - {tagElem} - - ) : ( tagElem ); })} {inputVisible && ( - + )} {!inputVisible && ( - - {prop.addText || "Add Tag"} + + {prop.addText || "Add Tag"} )} ); - } diff --git a/admin-frontend/src/component/subscribeCard.tsx b/admin-frontend/src/component/subscribeCard.tsx index 66d38dc..37c3ef0 100644 --- a/admin-frontend/src/component/subscribeCard.tsx +++ b/admin-frontend/src/component/subscribeCard.tsx @@ -1,108 +1,190 @@ -import {CopyOutlined, DeleteOutlined, EditOutlined} from '@ant-design/icons'; -import {Card, Col, Form, message, Popconfirm, Select, Tag, Tooltip} from 'antd'; -import Modal from 'antd/lib/modal/Modal'; -import React, {useState} from "react"; -import {useDispatch, useSelector} from 'react-redux'; -import {addSubscribe, delSubscribe} from 'src/api/config'; -import {platformConfSelector} from 'src/store/globalConfSlice'; -import {groupConfigSelector, updateGroupSubs} from 'src/store/groupConfigSlice'; -import {PlatformConfig, SubscribeConfig, SubscribeResp} from 'src/utils/type'; -import {AddModal} from './addSubsModal'; +import { CopyOutlined, DeleteOutlined, EditOutlined } from "@ant-design/icons"; +import { + Card, + Col, + Form, + message, + Popconfirm, + Select, + Tag, + Tooltip, +} from "antd"; +import Modal from "antd/lib/modal/Modal"; +import React, { useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { addSubscribe, delSubscribe } from "src/api/config"; +import { platformConfSelector } from "src/store/globalConfSlice"; +import { + groupConfigSelector, + updateGroupSubs, +} from "src/store/groupConfigSlice"; +import { PlatformConfig, SubscribeConfig, SubscribeResp } from "src/utils/type"; +import { AddModal } from "./addSubsModal"; interface CopyModalProp { - setShowModal: (modalShow: boolean) => void - showModal: boolean - config: SubscribeConfig, - groups: SubscribeResp - currentGroupNumber: string - reload: () => void + setShowModal: (modalShow: boolean) => void; + showModal: boolean; + config: SubscribeConfig; + groups: SubscribeResp; + currentGroupNumber: string; + reload: () => void; } -function CopyModal({setShowModal,config, - currentGroupNumber,groups,showModal,reload}: CopyModalProp) { - const [confirmLoading, setConfirmLoading] = useState(false) - const [ selectedGroups, setSelectGroups ] = useState>([]); - const postReqs = async (selectedGroups: Array, config: SubscribeConfig) => { - for(let selectedGroup of selectedGroups) { +function CopyModal({ + setShowModal, + config, + currentGroupNumber, + groups, + showModal, + reload, +}: CopyModalProp) { + const [confirmLoading, setConfirmLoading] = useState(false); + const [selectedGroups, setSelectGroups] = useState>([]); + const postReqs = async ( + selectedGroups: Array, + config: SubscribeConfig + ) => { + for (let selectedGroup of selectedGroups) { await addSubscribe(selectedGroup, config); } - } + }; const handleOk = () => { if (selectedGroups.length === 0) { message.error("请至少选择一个目标群"); - } else{ - setConfirmLoading(true) + } else { + setConfirmLoading(true); postReqs(selectedGroups, config).then(() => { - setConfirmLoading(false) - setShowModal(false) - return reload() - }) + setConfirmLoading(false); + setShowModal(false); + return reload(); + }); } - } - return setShowModal(false)} onOk={handleOk}> - + }; + return ( + setShowModal(false)} + onOk={handleOk} + > + + ); } interface SubscribeCardProp { - groupNumber: string - config: SubscribeConfig + groupNumber: string; + config: SubscribeConfig; } -export function SubscribeCard({groupNumber, config}: SubscribeCardProp) { - const platformConfs = useSelector(platformConfSelector) - const [showModal, setShowModal] = useState(false) - const [showEditModal, setShowEditModal] = useState(false) +export function SubscribeCard({ groupNumber, config }: SubscribeCardProp) { + const platformConfs = useSelector(platformConfSelector); + const [showModal, setShowModal] = useState(false); + const [showEditModal, setShowEditModal] = useState(false); const platformConf = platformConfs[config.platformName] as PlatformConfig; const dispatcher = useDispatch(); const groupSubscribes = useSelector(groupConfigSelector); - const reload = () => dispatcher(updateGroupSubs()) - const handleDelete = (groupNumber: string, platformName: string, target: string) => () => { - delSubscribe(groupNumber, platformName, target).then(() => { - reload() - }) - } + const reload = () => dispatcher(updateGroupSubs()); + const handleDelete = + (groupNumber: string, platformName: string, target: string) => () => { + delSubscribe(groupNumber, platformName, target).then(() => { + reload(); + }); + }; return ( - - - {setShowEditModal(state => !state)}}/> - , - - {setShowModal(state => !state)}}/> - , - - - , - ]}> -
- - { platformConf.hasTarget ? config.target : 无帐号 } - - - {Object.keys(platformConf.categories).length > 0 ? - config.cats.map((catKey: number) => ({platformConf.categories[catKey]})) : - 不支持类型} - - - {platformConf.enabledTag ? config.tags.length > 0 ? config.tags.map(tag => ({tag})) : (全部标签) : - 不支持Tag} - -
-
- - - - ) + + + { + setShowEditModal((state) => !state); + }} + /> + , + + { + setShowModal((state) => !state); + }} + /> + , + + + + + , + ]} + > +
+ + {platformConf.hasTarget ? ( + config.target + ) : ( + 无帐号 + )} + + + {Object.keys(platformConf.categories).length > 0 ? ( + config.cats.map((catKey: number) => ( + + {platformConf.categories[catKey]} + + )) + ) : ( + 不支持类型 + )} + + + {platformConf.enabledTag ? ( + config.tags.length > 0 ? ( + config.tags.map((tag) => ( + + {tag} + + )) + ) : ( + 全部标签 + ) + ) : ( + 不支持Tag + )} + +
+
+ + + + ); } diff --git a/admin-frontend/src/index.tsx b/admin-frontend/src/index.tsx index 5621bf9..874357b 100644 --- a/admin-frontend/src/index.tsx +++ b/admin-frontend/src/index.tsx @@ -1,11 +1,11 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import {Provider} from 'react-redux'; -import App from './App'; -import './index.css'; -import reportWebVitals from './reportWebVitals'; -import store from './store'; -import {injectStore} from 'src/api/utils'; +import React from "react"; +import ReactDOM from "react-dom"; +import { Provider } from "react-redux"; +import App from "./App"; +import "./index.css"; +import reportWebVitals from "./reportWebVitals"; +import store from "./store"; +import { injectStore } from "src/api/utils"; injectStore(store); ReactDOM.render( @@ -14,7 +14,7 @@ ReactDOM.render( , - document.getElementById('root') + document.getElementById("root") ); // If you want to start measuring performance in your app, pass a function diff --git a/admin-frontend/src/pages/admin/configPage/index.tsx b/admin-frontend/src/pages/admin/configPage/index.tsx index 06d676a..9552fe1 100644 --- a/admin-frontend/src/pages/admin/configPage/index.tsx +++ b/admin-frontend/src/pages/admin/configPage/index.tsx @@ -1,56 +1,73 @@ -import {Button, Collapse, Empty, Row} from 'antd'; -import React, {ReactElement, useEffect, useState} from "react"; -import {useDispatch, useSelector} from 'react-redux'; -import {AddModal} from 'src/component/addSubsModal'; -import {SubscribeCard} from 'src/component/subscribeCard'; -import {groupConfigSelector, updateGroupSubs} from 'src/store/groupConfigSlice'; +import { Button, Collapse, Empty, Row } from "antd"; +import React, { ReactElement, useEffect, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { AddModal } from "src/component/addSubsModal"; +import { SubscribeCard } from "src/component/subscribeCard"; +import { + groupConfigSelector, + updateGroupSubs, +} from "src/store/groupConfigSlice"; interface ConfigPageProp { - tab: string + tab: string; } export function ConfigPage(prop: ConfigPageProp) { - const [ showModal, setShowModal ] = useState(false); - const [ currentAddingGroupNumber, setCurrentAddingGroupNumber ] = useState(''); + const [showModal, setShowModal] = useState(false); + const [currentAddingGroupNumber, setCurrentAddingGroupNumber] = useState(""); const configData = useSelector(groupConfigSelector); const dispatcher = useDispatch(); useEffect(() => { - dispatcher(updateGroupSubs()) + dispatcher(updateGroupSubs()); }, [prop.tab, dispatcher]); - const clickNew = (groupNumber: string) => (e: React.MouseEvent) => { - setShowModal(_ => true); - setCurrentAddingGroupNumber(groupNumber); - e.stopPropagation(); - } - + const clickNew = + (groupNumber: string) => (e: React.MouseEvent) => { + setShowModal((_) => true); + setCurrentAddingGroupNumber(groupNumber); + e.stopPropagation(); + }; + if (Object.keys(configData).length === 0) { - return + return ; } else { let groups: Array = []; for (let key of Object.keys(configData)) { let value = configData[key]; groups.push( - {`${key} - ${value.name}`} - }> - - {value.subscribes.map((subs, idx) => )} + + {`${key} - ${value.name}`} + + + } + > + + {value.subscribes.map((subs, idx) => ( + + ))} - ) + ); } return ( -
- - {groups} - - dispatcher(updateGroupSubs())} - setShowModal={(s: boolean) => setShowModal(_ => s)} /> -
- ) +
+ {groups} + dispatcher(updateGroupSubs())} + setShowModal={(s: boolean) => setShowModal((_) => s)} + /> +
+ ); } } - - diff --git a/admin-frontend/src/pages/admin/index.tsx b/admin-frontend/src/pages/admin/index.tsx index 4d860be..8fe24aa 100644 --- a/admin-frontend/src/pages/admin/index.tsx +++ b/admin-frontend/src/pages/admin/index.tsx @@ -1,37 +1,39 @@ -import {BugOutlined, SettingOutlined} from '@ant-design/icons'; -import {Layout, Menu} from 'antd'; -import React, {useState} from "react"; -import {useSelector} from 'react-redux'; -import {loginSelector} from 'src/store/loginSlice'; -import './admin.css'; -import {ConfigPage} from './configPage'; +import { BugOutlined, SettingOutlined } from "@ant-design/icons"; +import { Layout, Menu } from "antd"; +import React, { useState } from "react"; +import { useSelector } from "react-redux"; +import { loginSelector } from "src/store/loginSlice"; +import "./admin.css"; +import { ConfigPage } from "./configPage"; export function Admin() { - const login = useSelector(loginSelector) - const [ tab, changeTab ] = useState("manage"); + const login = useSelector(loginSelector); + const [tab, changeTab] = useState("manage"); return ( - - -
-
- changeTab(key)}> - }>订阅管理 - { login.type === 'admin' && - }>查看日志 - } - -
- -
- { - tab === 'manage' ? - - : null - } -
-
-
- ) + + +
+ changeTab(key)} + > + }> + 订阅管理 + + {login.type === "admin" && ( + }> + 查看日志 + + )} + +
+ +
+ {tab === "manage" ? : null} +
+
+
+ ); } - diff --git a/admin-frontend/src/pages/auth.tsx b/admin-frontend/src/pages/auth.tsx index 299c58d..c27046d 100644 --- a/admin-frontend/src/pages/auth.tsx +++ b/admin-frontend/src/pages/auth.tsx @@ -1,28 +1,31 @@ -import React, {useEffect} from "react"; -import {useDispatch, useSelector} from "react-redux"; -import {useParams} from "react-router"; -import {Redirect} from 'react-router-dom'; -import {login, loginSelector} from 'src/store/loginSlice'; +import React, { useEffect } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { useParams } from "react-router"; +import { Redirect } from "react-router-dom"; +import { login, loginSelector } from "src/store/loginSlice"; -interface AuthParam { - code: string +interface AuthParam { + code: string; } export function Auth() { - const { code } = useParams(); + const { code } = useParams(); const dispatch = useDispatch(); - const loginState = useSelector(loginSelector) + const loginState = useSelector(loginSelector); useEffect(() => { const loginFun = async () => { dispatch(login(code)); - } + }; loginFun(); - }, [code, dispatch]) - return <> - { loginState.login ? - : - loginState.failed ? -
登录失败,请重新获取连接
: -
Logining...
- } -; + }, [code, dispatch]); + return ( + <> + {loginState.login ? ( + + ) : loginState.failed ? ( +
登录失败,请重新获取连接
+ ) : ( +
Logining...
+ )} + + ); } diff --git a/admin-frontend/src/reportWebVitals.ts b/admin-frontend/src/reportWebVitals.ts index 49a2a16..5fa3583 100644 --- a/admin-frontend/src/reportWebVitals.ts +++ b/admin-frontend/src/reportWebVitals.ts @@ -1,8 +1,8 @@ -import { ReportHandler } from 'web-vitals'; +import { ReportHandler } from "web-vitals"; const reportWebVitals = (onPerfEntry?: ReportHandler) => { if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { getCLS(onPerfEntry); getFID(onPerfEntry); getFCP(onPerfEntry); diff --git a/admin-frontend/src/setupTests.ts b/admin-frontend/src/setupTests.ts index 8f2609b..1dd407a 100644 --- a/admin-frontend/src/setupTests.ts +++ b/admin-frontend/src/setupTests.ts @@ -2,4 +2,4 @@ // allows you to do things like: // expect(element).toHaveTextContent(/react/i) // learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; +import "@testing-library/jest-dom"; diff --git a/admin-frontend/src/store/globalConfSlice.ts b/admin-frontend/src/store/globalConfSlice.ts index 914066a..d11d52b 100644 --- a/admin-frontend/src/store/globalConfSlice.ts +++ b/admin-frontend/src/store/globalConfSlice.ts @@ -1,23 +1,31 @@ -import {CaseReducer, createAsyncThunk, createSlice, PayloadAction} from "@reduxjs/toolkit"; -import {getGlobalConf as getGlobalConfApi} from "src/api/config"; -import {GlobalConf} from "src/utils/type"; -import {RootState} from "."; - +import { + CaseReducer, + createAsyncThunk, + createSlice, + PayloadAction, +} from "@reduxjs/toolkit"; +import { getGlobalConf as getGlobalConfApi } from "src/api/config"; +import { GlobalConf } from "src/utils/type"; +import { RootState } from "."; const initialState: GlobalConf = { platformConf: {}, - loaded: false -} + loaded: false, +}; -const setGlobalConf: CaseReducer> = (_, action) => { - return {...action.payload, loaded: true} -} +const setGlobalConf: CaseReducer> = ( + _, + action +) => { + return { ...action.payload, loaded: true }; +}; export const getGlobalConf = createAsyncThunk( "globalConf/set", getGlobalConfApi, { - condition: (_, { getState }) => !(getState() as RootState).globalConf.loaded + condition: (_, { getState }) => + !(getState() as RootState).globalConf.loaded, } ); @@ -26,10 +34,11 @@ export const globalConfSlice = createSlice({ initialState, reducers: {}, extraReducers: (builder) => { - builder.addCase(getGlobalConf.fulfilled, setGlobalConf) - } -}) + builder.addCase(getGlobalConf.fulfilled, setGlobalConf); + }, +}); -export const platformConfSelector = (state: RootState) => state.globalConf.platformConf +export const platformConfSelector = (state: RootState) => + state.globalConf.platformConf; -export default globalConfSlice.reducer +export default globalConfSlice.reducer; diff --git a/admin-frontend/src/store/groupConfigSlice.ts b/admin-frontend/src/store/groupConfigSlice.ts index 3f42130..98089c8 100644 --- a/admin-frontend/src/store/groupConfigSlice.ts +++ b/admin-frontend/src/store/groupConfigSlice.ts @@ -1,27 +1,36 @@ -import {CaseReducer, createAsyncThunk, createSlice, PayloadAction} from '@reduxjs/toolkit'; -import {SubscribeResp} from 'src/utils/type'; -import {getSubscribe} from 'src/api/config'; -import {RootState} from '.'; -const initialState: SubscribeResp = {} +import { + CaseReducer, + createAsyncThunk, + createSlice, + PayloadAction, +} from "@reduxjs/toolkit"; +import { SubscribeResp } from "src/utils/type"; +import { getSubscribe } from "src/api/config"; +import { RootState } from "."; +const initialState: SubscribeResp = {}; -const setSubs: CaseReducer> = (_, action) => { - return action.payload -} +const setSubs: CaseReducer> = ( + _, + action +) => { + return action.payload; +}; export const updateGroupSubs = createAsyncThunk( - "groupConfig/update", getSubscribe -) + "groupConfig/update", + getSubscribe +); export const groupConfigSlice = createSlice({ name: "groupConfig", initialState, reducers: { - setSubs + setSubs, }, extraReducers: (reducer) => { - reducer.addCase(updateGroupSubs.fulfilled, setSubs) - } -}) + reducer.addCase(updateGroupSubs.fulfilled, setSubs); + }, +}); export const groupConfigSelector = (state: RootState) => state.groupConfig; export default groupConfigSlice.reducer; diff --git a/admin-frontend/src/store/hooks.ts b/admin-frontend/src/store/hooks.ts index 99221df..5e6629e 100644 --- a/admin-frontend/src/store/hooks.ts +++ b/admin-frontend/src/store/hooks.ts @@ -1,5 +1,5 @@ -import {TypedUseSelectorHook, useDispatch, useSelector} from "react-redux"; -import {AppDispatch, RootState} from "."; +import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; +import { AppDispatch, RootState } from "."; -export const useAppDispacher = () => useDispatch() -export const useAppSelector: TypedUseSelectorHook = useSelector +export const useAppDispacher = () => useDispatch(); +export const useAppSelector: TypedUseSelectorHook = useSelector; diff --git a/admin-frontend/src/store/index.ts b/admin-frontend/src/store/index.ts index 36f6821..18530e7 100644 --- a/admin-frontend/src/store/index.ts +++ b/admin-frontend/src/store/index.ts @@ -1,15 +1,15 @@ -import {configureStore} from "@reduxjs/toolkit"; +import { configureStore } from "@reduxjs/toolkit"; import loginSlice from "./loginSlice"; import globalConfSlice from "./globalConfSlice"; -import groupConfigSlice from './groupConfigSlice'; +import groupConfigSlice from "./groupConfigSlice"; const store = configureStore({ reducer: { login: loginSlice, globalConf: globalConfSlice, groupConfig: groupConfigSlice, - } -}) + }, +}); export default store; diff --git a/admin-frontend/src/store/loginSlice.ts b/admin-frontend/src/store/loginSlice.ts index 6db2da1..1b6ab40 100644 --- a/admin-frontend/src/store/loginSlice.ts +++ b/admin-frontend/src/store/loginSlice.ts @@ -1,108 +1,121 @@ -import { AnyAction, CaseReducer, createAsyncThunk, createSlice, PayloadAction, ThunkAction } from "@reduxjs/toolkit"; -import jwt_decode from 'jwt-decode'; +import { + AnyAction, + CaseReducer, + createAsyncThunk, + createSlice, + PayloadAction, + ThunkAction, +} from "@reduxjs/toolkit"; +import jwt_decode from "jwt-decode"; import { LoginStatus, TokenResp } from "src/utils/type"; import { auth } from "src/api/config"; -import {RootState} from "."; +import { RootState } from "."; const initialState: LoginStatus = { login: false, - type: '', - name: '', - id: '123', + type: "", + name: "", + id: "123", // groups: [], - token: '', - failed: false -} + token: "", + failed: false, +}; interface storedInfo { - type: string - name: string - id: string + type: string; + name: string; + id: string; } -const loginAction: CaseReducer> = (_, action) => { +const loginAction: CaseReducer> = ( + _, + action +) => { return { login: true, failed: false, type: action.payload.type, name: action.payload.name, id: action.payload.id, - token: action.payload.token - } -} + token: action.payload.token, + }; +}; export const login = createAsyncThunk( "auth/login", async (code: string) => { let res = await auth(code); if (res.status !== 200) { - throw Error("Login Error") + throw Error("Login Error"); } else { - localStorage.setItem('loginInfo', JSON.stringify({ - 'type': res.type, - 'name': res.name, - id: res.id, - })) - localStorage.setItem('token', res.token) + localStorage.setItem( + "loginInfo", + JSON.stringify({ + type: res.type, + name: res.name, + id: res.id, + }) + ); + localStorage.setItem("token", res.token); } - return res + return res; }, { condition: (_: string, { getState }) => { - const { login } = getState() as { login: LoginStatus } + const { login } = getState() as { login: LoginStatus }; return !login.login; - } + }, } -) - +); export const loginSlice = createSlice({ - name: 'auth', + name: "auth", initialState, reducers: { doLogin: loginAction, doClearLogin: (state) => { - state.login = false - } + state.login = false; + }, }, extraReducers: (builder) => { builder.addCase(login.fulfilled, loginAction); builder.addCase(login.rejected, (stat) => { - stat.failed = true - }) - } -}) + stat.failed = true; + }); + }, +}); -export const { doLogin, doClearLogin } = loginSlice.actions +export const { doLogin, doClearLogin } = loginSlice.actions; -export const loadLoginState = (): ThunkAction => +export const loadLoginState = + (): ThunkAction => (dispatch, getState) => { if (getState().login.login) { - return + return; } - const infoJson = localStorage.getItem('loginInfo') - const jwtToken = localStorage.getItem('token'); + const infoJson = localStorage.getItem("loginInfo"); + const jwtToken = localStorage.getItem("token"); if (infoJson && jwtToken) { const decodedJwt = jwt_decode(jwtToken) as { exp: number }; if (decodedJwt.exp < Date.now() / 1000) { - return + return; } - const info = JSON.parse(infoJson) as storedInfo + const info = JSON.parse(infoJson) as storedInfo; const payload: TokenResp = { ...info, status: 200, token: jwtToken, - } - dispatch(doLogin(payload)) + }; + dispatch(doLogin(payload)); } - } + }; -export const clearLoginStatus = (): ThunkAction => - (dispatch) => { - localStorage.removeItem('loginInfo') - localStorage.removeItem('token') - dispatch(doClearLogin()) - } -export const loginSelector = (state: RootState) => state.login +export const clearLoginStatus = + (): ThunkAction => (dispatch) => { + localStorage.removeItem("loginInfo"); + localStorage.removeItem("token"); + dispatch(doClearLogin()); + }; +export const loginSelector = (state: RootState) => state.login; -export default loginSlice.reducer +export default loginSlice.reducer; diff --git a/admin-frontend/src/utils/type.ts b/admin-frontend/src/utils/type.ts index e514ed0..6f99657 100644 --- a/admin-frontend/src/utils/type.ts +++ b/admin-frontend/src/utils/type.ts @@ -1,34 +1,34 @@ interface QQGroup { - id: string, - name: string, + id: string; + name: string; } export interface LoginStatus { - login: boolean - type: string - name: string - id: string + login: boolean; + type: string; + name: string; + id: string; // groups: Array - token: string, - failed: boolean, + token: string; + failed: boolean; } export type LoginContextType = { - login: LoginStatus - save: (status: LoginStatus) => void -} + login: LoginStatus; + save: (status: LoginStatus) => void; +}; export interface SubscribeConfig { - platformName: string - target: string - targetName: string - cats: Array - tags: Array + platformName: string; + target: string; + targetName: string; + cats: Array; + tags: Array; } export interface GlobalConf { - platformConf: AllPlatformConf, - loaded: boolean + platformConf: AllPlatformConf; + loaded: boolean; } export interface AllPlatformConf { @@ -36,34 +36,34 @@ export interface AllPlatformConf { } export interface CategoryConfig { - [idx: number]: string + [idx: number]: string; } export interface PlatformConfig { - name: string - categories: CategoryConfig - enabledTag: boolean, - platformName: string, - hasTarget: boolean + name: string; + categories: CategoryConfig; + enabledTag: boolean; + platformName: string; + hasTarget: boolean; } export interface TokenResp { - status: number, - token: string, - type: string, - id: string - name: string + status: number; + token: string; + type: string; + id: string; + name: string; } export interface SubscribeGroupDetail { - name: string, - subscribes: Array + name: string; + subscribes: Array; } export interface SubscribeResp { - [idx: string]: SubscribeGroupDetail + [idx: string]: SubscribeGroupDetail; } export interface TargetNameResp { - targetName: string + targetName: string; } diff --git a/docker/Dockerfile_with_frontend b/docker/Dockerfile_with_frontend index 9a98ffc..69ad203 100644 --- a/docker/Dockerfile_with_frontend +++ b/docker/Dockerfile_with_frontend @@ -9,9 +9,9 @@ RUN apt-get update && apt-get install -y xvfb fonts-noto-color-emoji ttf-unifont libnspr4 libnss3 libpango-1.0-0 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 \ libxdamage1 libxext6 libxfixes3 libxrandr2 libxshmfence1 \ && rm -rf /var/lib/apt/lists/* -RUN pip install playwright && playwright install chromium COPY ./pyproject.toml ./poetry.lock* /app/ RUN poetry install --no-root --no-dev +RUN playwright install chromium ADD src /app/src ADD bot.py /app/ ENV HOST=0.0.0.0 diff --git a/docker/Dockerfile_with_frontend_sentry b/docker/Dockerfile_with_frontend_sentry index 146007e..beef9b3 100644 --- a/docker/Dockerfile_with_frontend_sentry +++ b/docker/Dockerfile_with_frontend_sentry @@ -9,11 +9,11 @@ RUN apt-get update && apt-get install -y xvfb fonts-noto-color-emoji ttf-unifont libnspr4 libnss3 libpango-1.0-0 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 \ libxdamage1 libxext6 libxfixes3 libxrandr2 libxshmfence1 \ && rm -rf /var/lib/apt/lists/* -RUN pip install playwright && playwright install chromium COPY ./pyproject.toml ./poetry.lock* ./bot.py /app/ RUN poetry add nonebot-plugin-sentry && \ sed '/nonebot.load_builtin_plugins()/a nonebot.load_plugin("nonebot_plugin_sentry")' -i bot.py RUN poetry install --no-root --no-dev +RUN playwright install chromium ADD src /app/src ENV HOST=0.0.0.0 CMD ["python", "bot.py"] diff --git a/pyproject.toml b/pyproject.toml index 05b4388..2619687 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "nonebot-bison" -version = "0.4.3" +version = "0.4.4" description = "Subscribe message from social medias" authors = ["felinae98 "] license = "MIT" @@ -61,16 +61,3 @@ markers = [ "render: render img by chrome" ] -[tool.black] -line-length = 88 -target-version = ["py39", "py310"] -include = '\.pyi?$' -extend-exclude = ''' -''' - -[tool.isort] -profile = "black" -line_length = 88 -length_sort = true -skip_gitignore = true -force_sort_within_sections = true diff --git a/src/plugins/auto_agree.py b/src/plugins/auto_agree.py index e27366f..55ef793 100644 --- a/src/plugins/auto_agree.py +++ b/src/plugins/auto_agree.py @@ -1,9 +1,9 @@ from typing import Union from nonebot import on_request -from nonebot.log import logger from nonebot.adapters.onebot.v11 import Bot -from nonebot.adapters.onebot.v11.event import GroupRequestEvent, FriendRequestEvent +from nonebot.adapters.onebot.v11.event import FriendRequestEvent, GroupRequestEvent +from nonebot.log import logger friend_req = on_request(priority=5) diff --git a/src/plugins/nonebot_bison/__init__.py b/src/plugins/nonebot_bison/__init__.py index 2fb5951..53aa56c 100644 --- a/src/plugins/nonebot_bison/__init__.py +++ b/src/plugins/nonebot_bison/__init__.py @@ -1,18 +1,15 @@ -import nonebot - from . import ( + admin_page, + config, + config_manager, + platform, post, + scheduler, send, types, utils, - config, - platform, - scheduler, - admin_page, - config_manager, ) __help__version__ = "0.4.3" __help__plugin__name__ = "nonebot_bison" __usage__ = "本bot可以提供b站、微博等社交媒体的消息订阅,详情" "请查看本bot文档,或者at本bot发送“添加订阅”订阅第一个帐号" -__module_name__ = "nonebot-bison" diff --git a/src/plugins/nonebot_bison/admin_page/api.py b/src/plugins/nonebot_bison/admin_page/api.py index a69f23f..bb9edf4 100644 --- a/src/plugins/nonebot_bison/admin_page/api.py +++ b/src/plugins/nonebot_bison/admin_page/api.py @@ -1,10 +1,10 @@ import nonebot from nonebot.adapters.onebot.v11.bot import Bot +from ..config import Config, NoSuchSubscribeException, NoSuchUserException +from ..platform import check_sub_target, platform_manager from .jwt import pack_jwt from .token_manager import token_manager -from ..platform import check_sub_target, platform_manager -from ..config import Config, NoSuchUserException, NoSuchSubscribeException async def test(): diff --git a/src/plugins/nonebot_bison/admin_page/jwt.py b/src/plugins/nonebot_bison/admin_page/jwt.py index 4344521..661621a 100644 --- a/src/plugins/nonebot_bison/admin_page/jwt.py +++ b/src/plugins/nonebot_bison/admin_page/jwt.py @@ -1,6 +1,6 @@ +import datetime import random import string -import datetime from typing import Optional import jwt diff --git a/src/plugins/nonebot_bison/config.py b/src/plugins/nonebot_bison/config.py index 2d67e2c..3a98006 100644 --- a/src/plugins/nonebot_bison/config.py +++ b/src/plugins/nonebot_bison/config.py @@ -1,16 +1,16 @@ +from collections import defaultdict import os from os import path -from collections import defaultdict -from typing import Literal, Mapping, TypedDict, DefaultDict +from typing import DefaultDict, Literal, Mapping, TypedDict import nonebot from nonebot import logger from tinydb import Query, TinyDB -from .utils import Singleton -from .types import User, Target from .platform import platform_manager from .plugin_config import plugin_config +from .types import Target, User +from .utils import Singleton supported_target_type = platform_manager.keys() diff --git a/src/plugins/nonebot_bison/config_manager.py b/src/plugins/nonebot_bison/config_manager.py index 81eec50..a171b81 100644 --- a/src/plugins/nonebot_bison/config_manager.py +++ b/src/plugins/nonebot_bison/config_manager.py @@ -1,20 +1,20 @@ from typing import Type from nonebot import on_command -from nonebot.rule import to_me -from nonebot.typing import T_State -from nonebot.matcher import Matcher -from nonebot.permission import SUPERUSER -from nonebot.params import State, Depends +from nonebot.adapters._event import Event as AbstractEvent from nonebot.adapters.onebot.v11 import Bot, Event from nonebot.adapters.onebot.v11.message import Message -from nonebot.adapters._event import Event as AbstractEvent from nonebot.adapters.onebot.v11.permission import GROUP_ADMIN, GROUP_OWNER +from nonebot.matcher import Matcher +from nonebot.params import Depends, State +from nonebot.permission import SUPERUSER +from nonebot.rule import to_me +from nonebot.typing import T_State from .config import Config -from .utils import parse_text -from .types import Target, Category from .platform import check_sub_target, platform_manager +from .types import Category, Target +from .utils import parse_text def _gen_prompt_template(prompt: str): diff --git a/src/plugins/nonebot_bison/platform/__init__.py b/src/plugins/nonebot_bison/platform/__init__.py index bdd0591..60b5e32 100644 --- a/src/plugins/nonebot_bison/platform/__init__.py +++ b/src/plugins/nonebot_bison/platform/__init__.py @@ -1,9 +1,9 @@ -from pathlib import Path -from pkgutil import iter_modules from collections import defaultdict from importlib import import_module +from pathlib import Path +from pkgutil import iter_modules -from .platform import Platform, NoTargetGroup +from .platform import NoTargetGroup, Platform _package_dir = str(Path(__file__).resolve().parent) for (_, module_name, _) in iter_modules([_package_dir]): diff --git a/src/plugins/nonebot_bison/platform/bilibili.py b/src/plugins/nonebot_bison/platform/bilibili.py index 2f62ee4..5ff3cc1 100644 --- a/src/plugins/nonebot_bison/platform/bilibili.py +++ b/src/plugins/nonebot_bison/platform/bilibili.py @@ -4,8 +4,8 @@ from typing import Any, Optional import httpx from ..post import Post -from ..types import Tag, Target, RawPost, Category -from .platform import NewMessage, CategoryNotSupport +from ..types import Category, RawPost, Tag, Target +from .platform import CategoryNotSupport, NewMessage class Bilibili(NewMessage): diff --git a/src/plugins/nonebot_bison/platform/ncm_artist.py b/src/plugins/nonebot_bison/platform/ncm_artist.py index b3cd106..a30072f 100644 --- a/src/plugins/nonebot_bison/platform/ncm_artist.py +++ b/src/plugins/nonebot_bison/platform/ncm_artist.py @@ -3,8 +3,8 @@ from typing import Any, Optional import httpx from ..post import Post +from ..types import RawPost, Target from .platform import NewMessage -from ..types import Target, RawPost class NcmArtist(NewMessage): diff --git a/src/plugins/nonebot_bison/platform/ncm_radio.py b/src/plugins/nonebot_bison/platform/ncm_radio.py index ea7a0f0..20abb52 100644 --- a/src/plugins/nonebot_bison/platform/ncm_radio.py +++ b/src/plugins/nonebot_bison/platform/ncm_radio.py @@ -3,8 +3,8 @@ from typing import Any, Optional import httpx from ..post import Post +from ..types import RawPost, Target from .platform import NewMessage -from ..types import Target, RawPost class NcmRadio(NewMessage): diff --git a/src/plugins/nonebot_bison/platform/platform.py b/src/plugins/nonebot_bison/platform/platform.py index fc813f5..34cdb76 100644 --- a/src/plugins/nonebot_bison/platform/platform.py +++ b/src/plugins/nonebot_bison/platform/platform.py @@ -1,15 +1,15 @@ -import time -from dataclasses import dataclass from abc import ABC, abstractmethod from collections import defaultdict -from typing import Any, Literal, Optional, Collection +from dataclasses import dataclass +import time +from typing import Any, Collection, Literal, Optional import httpx from nonebot import logger -from ..post import Post from ..plugin_config import plugin_config -from ..types import Tag, User, Target, RawPost, Category, UserSubInfo +from ..post import Post +from ..types import Category, RawPost, Tag, Target, User, UserSubInfo class CategoryNotSupport(Exception): diff --git a/src/plugins/nonebot_bison/platform/rss.py b/src/plugins/nonebot_bison/platform/rss.py index 4e5aefd..2adde67 100644 --- a/src/plugins/nonebot_bison/platform/rss.py +++ b/src/plugins/nonebot_bison/platform/rss.py @@ -1,13 +1,13 @@ import calendar from typing import Any, Optional -import httpx -import feedparser from bs4 import BeautifulSoup as bs +import feedparser +import httpx from ..post import Post +from ..types import RawPost, Target from .platform import NewMessage -from ..types import Target, RawPost class Rss(NewMessage): diff --git a/src/plugins/nonebot_bison/platform/wechat.py b/src/plugins/nonebot_bison/platform/wechat.py index 7486488..42850fd 100644 --- a/src/plugins/nonebot_bison/platform/wechat.py +++ b/src/plugins/nonebot_bison/platform/wechat.py @@ -1,14 +1,13 @@ -import re -import json -import hashlib from datetime import datetime +import hashlib +import json +import re from typing import Any, Optional -import httpx from bs4 import BeautifulSoup as bs +import httpx from ..types import * -from ..post import Post # from .platform import Platform diff --git a/src/plugins/nonebot_bison/post.py b/src/plugins/nonebot_bison/post.py index 5f7420b..96c1514 100644 --- a/src/plugins/nonebot_bison/post.py +++ b/src/plugins/nonebot_bison/post.py @@ -1,16 +1,16 @@ import base64 -from io import BytesIO +from dataclasses import dataclass, field from functools import reduce -from typing import Union, Optional -from dataclasses import field, dataclass +from io import BytesIO +from typing import Optional, Union -import httpx from PIL import Image +import httpx from nonebot import logger from nonebot.adapters.onebot.v11.message import Message, MessageSegment -from .utils import parse_text from .plugin_config import plugin_config +from .utils import parse_text @dataclass diff --git a/src/plugins/nonebot_bison/scheduler.py b/src/plugins/nonebot_bison/scheduler.py index 03ddd48..9ff4b2c 100644 --- a/src/plugins/nonebot_bison/scheduler.py +++ b/src/plugins/nonebot_bison/scheduler.py @@ -1,16 +1,15 @@ -import asyncio import logging -import nonebot -from nonebot.log import LoguruHandler -from nonebot import logger, get_driver from apscheduler.schedulers.asyncio import AsyncIOScheduler +import nonebot +from nonebot import get_driver, logger +from nonebot.log import LoguruHandler from .config import Config -from .types import UserSubInfo from .platform import platform_manager from .plugin_config import plugin_config -from .send import send_msgs, do_send_msgs +from .send import do_send_msgs, send_msgs +from .types import UserSubInfo scheduler = AsyncIOScheduler(timezone="Asia/Shanghai") diff --git a/src/plugins/nonebot_bison/types.py b/src/plugins/nonebot_bison/types.py index 56a0fdf..8073fa8 100644 --- a/src/plugins/nonebot_bison/types.py +++ b/src/plugins/nonebot_bison/types.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Any, NewType, Callable, NamedTuple +from typing import Any, Callable, NamedTuple, NewType RawPost = NewType("RawPost", Any) Target = NewType("Target", str) diff --git a/src/plugins/nonebot_bison/utils.py b/src/plugins/nonebot_bison/utils.py index 53a00c0..ac3f85f 100644 --- a/src/plugins/nonebot_bison/utils.py +++ b/src/plugins/nonebot_bison/utils.py @@ -1,19 +1,23 @@ -import os -import re -import sys -import base64 import asyncio -import subprocess +import base64 from html import escape +import os +from pathlib import Path +import platform +import re +import subprocess +import sys from time import asctime -from typing import Union, Callable, Optional, Awaitable +from typing import Awaitable, Callable, Optional, Union -import nonebot from bs4 import BeautifulSoup as bs -from nonebot.log import logger, default_format +import nonebot from nonebot.adapters.onebot.v11.message import MessageSegment +from nonebot.log import default_format, logger from playwright._impl._driver import compute_driver_executable -from playwright.async_api import Page, Browser, Playwright, async_playwright +from playwright.async_api import Browser, Page, Playwright, async_playwright +from uvicorn import config +from uvicorn.loops import asyncio as _asyncio from .plugin_config import plugin_config @@ -30,6 +34,16 @@ class Singleton(type): @nonebot.get_driver().on_startup def download_browser(): if not plugin_config.bison_browser and not plugin_config.bison_use_local: + system = platform.system() + if system == "Linux": + browser_path = Path.home() / ".cache" / "ms-playwright" + elif system == "Windows": + browser_path = Path.home() / "AppData" / "Local" / "ms-playwright" + else: + raise RuntimeError("platform not supported") + if browser_path.exists() and os.listdir(str(browser_path)): + logger.warning("Browser Exists, skip") + return env = os.environ.copy() driver_executable = compute_driver_executable() env["PW_CLI_TARGET_LANG"] = "python" @@ -219,4 +233,19 @@ if plugin_config.bison_filter_log: if config.log_level is None else config.log_level ) - logger.warning("test") + +# monkey patch +def asyncio_setup(): + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + +@property +def should_reload(self): + return False + + +if platform.system() == "Windows": + _asyncio.asyncio_setup = asyncio_setup + config.Config.should_reload = should_reload # type:ignore + logger.warning("检测到当前为 Windows 系统,已自动注入猴子补丁") diff --git a/tests/conftest.py b/tests/conftest.py index dc8e4ac..31dc490 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,9 +1,9 @@ -import typing from pathlib import Path +import typing -import pytest import nonebot from nonebug.app import App +import pytest @pytest.fixture diff --git a/tests/platforms/test_arknights.py b/tests/platforms/test_arknights.py index adb7587..1a3890c 100644 --- a/tests/platforms/test_arknights.py +++ b/tests/platforms/test_arknights.py @@ -1,7 +1,7 @@ -import respx -import pytest from httpx import Response from nonebug.app import App +import pytest +import respx from .utils import get_file, get_json diff --git a/tests/platforms/test_bilibili.py b/tests/platforms/test_bilibili.py index 5fc2b28..4bccb3d 100644 --- a/tests/platforms/test_bilibili.py +++ b/tests/platforms/test_bilibili.py @@ -1,6 +1,6 @@ -import pytest from httpx import Response from nonebug.app import App +import pytest from .utils import get_json diff --git a/tests/platforms/test_ncm_artist.py b/tests/platforms/test_ncm_artist.py index a6e5f0d..2ebcda8 100644 --- a/tests/platforms/test_ncm_artist.py +++ b/tests/platforms/test_ncm_artist.py @@ -1,9 +1,9 @@ import time -import respx -import pytest from httpx import Response from nonebug.app import App +import pytest +import respx from .utils import get_json diff --git a/tests/platforms/test_ncm_radio.py b/tests/platforms/test_ncm_radio.py index cc88a85..4386e44 100644 --- a/tests/platforms/test_ncm_radio.py +++ b/tests/platforms/test_ncm_radio.py @@ -1,9 +1,9 @@ import time -import respx -import pytest from httpx import Response from nonebug.app import App +import pytest +import respx from .utils import get_json diff --git a/tests/platforms/test_platform.py b/tests/platforms/test_platform.py index ff4930e..e78348c 100644 --- a/tests/platforms/test_platform.py +++ b/tests/platforms/test_platform.py @@ -1,8 +1,8 @@ from time import time from typing import Any, Optional -import pytest from nonebug.app import App +import pytest now = time() passed = now - 3 * 60 * 60 @@ -38,9 +38,9 @@ def user_info_factory(app: App, dummy_user): @pytest.fixture def mock_platform_without_cats_tags(app: App): - from nonebot_bison.post import Post - from nonebot_bison.types import Target, RawPost from nonebot_bison.platform.platform import NewMessage + from nonebot_bison.post import Post + from nonebot_bison.types import RawPost, Target class MockPlatform(NewMessage): @@ -87,9 +87,9 @@ def mock_platform_without_cats_tags(app: App): @pytest.fixture def mock_platform(app: App): - from nonebot_bison.post import Post from nonebot_bison.platform.platform import NewMessage - from nonebot_bison.types import Tag, Target, RawPost, Category + from nonebot_bison.post import Post + from nonebot_bison.types import Category, RawPost, Tag, Target class MockPlatform(NewMessage): @@ -145,9 +145,9 @@ def mock_platform(app: App): @pytest.fixture def mock_platform_no_target(app: App): + from nonebot_bison.platform.platform import CategoryNotSupport, NewMessage from nonebot_bison.post import Post - from nonebot_bison.types import Tag, Target, RawPost, Category - from nonebot_bison.platform.platform import NewMessage, CategoryNotSupport + from nonebot_bison.types import Category, RawPost, Tag, Target class MockPlatform(NewMessage): @@ -203,9 +203,9 @@ def mock_platform_no_target(app: App): @pytest.fixture def mock_platform_no_target_2(app: App): - from nonebot_bison.post import Post from nonebot_bison.platform.platform import NewMessage - from nonebot_bison.types import Tag, Target, RawPost, Category + from nonebot_bison.post import Post + from nonebot_bison.types import Category, RawPost, Tag, Target class MockPlatform(NewMessage): @@ -270,9 +270,9 @@ def mock_platform_no_target_2(app: App): @pytest.fixture def mock_status_change(app: App): - from nonebot_bison.post import Post from nonebot_bison.platform.platform import StatusChange - from nonebot_bison.types import Tag, Target, RawPost, Category + from nonebot_bison.post import Post + from nonebot_bison.types import Category, RawPost, Tag, Target class MockPlatform(StatusChange): @@ -440,9 +440,9 @@ async def test_group( user_info_factory, ): - from nonebot_bison.post import Post from nonebot_bison.platform.platform import NoTargetGroup - from nonebot_bison.types import Tag, Target, RawPost, Category + from nonebot_bison.post import Post + from nonebot_bison.types import Category, RawPost, Tag, Target group_platform = NoTargetGroup([mock_platform_no_target, mock_platform_no_target_2]) res1 = await group_platform.fetch_new_post( diff --git a/tests/platforms/test_weibo.py b/tests/platforms/test_weibo.py index 1edb4d6..9280ca6 100644 --- a/tests/platforms/test_weibo.py +++ b/tests/platforms/test_weibo.py @@ -1,11 +1,11 @@ from datetime import datetime -import respx -import pytest import feedparser -from pytz import timezone from httpx import Response from nonebug.app import App +import pytest +from pytz import timezone +import respx from .utils import get_file, get_json diff --git a/tests/test_config_manager.py b/tests/test_config_manager.py index 1673309..982d4b8 100644 --- a/tests/test_config_manager.py +++ b/tests/test_config_manager.py @@ -1,7 +1,7 @@ import typing -import pytest from nonebug.app import App +import pytest if typing.TYPE_CHECKING: import sys diff --git a/tests/test_merge_pic.py b/tests/test_merge_pic.py index 9e1a34b..1d8a8d7 100644 --- a/tests/test_merge_pic.py +++ b/tests/test_merge_pic.py @@ -1,7 +1,7 @@ import typing -import pytest from nonebug.app import App +import pytest if typing.TYPE_CHECKING: import sys diff --git a/tests/test_render.py b/tests/test_render.py index 646e3b7..ed08b6c 100644 --- a/tests/test_render.py +++ b/tests/test_render.py @@ -1,7 +1,7 @@ import typing -import pytest from nonebug.app import App +import pytest if typing.TYPE_CHECKING: import sys