mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b08a29897f | ||
![]() |
b59c1d9122 | ||
![]() |
adb9cea701 | ||
![]() |
5e148d2e82 | ||
![]() |
a0d780558e | ||
![]() |
ad56065a4e | ||
![]() |
f5dee80b6e | ||
![]() |
9cc75881b8 | ||
![]() |
593fb13b61 | ||
![]() |
fca90592d6 | ||
![]() |
d6848e2855 | ||
![]() |
7539a4129f | ||
![]() |
5402574266 | ||
![]() |
853175aa1a |
@@ -4,7 +4,7 @@
|
|||||||
"name": "NapCatQQ",
|
"name": "NapCatQQ",
|
||||||
"slug": "NapCat.Framework",
|
"slug": "NapCat.Framework",
|
||||||
"description": "高性能的 OneBot 11 协议实现",
|
"description": "高性能的 OneBot 11 协议实现",
|
||||||
"version": "4.5.5",
|
"version": "4.5.9",
|
||||||
"icon": "./logo.png",
|
"icon": "./logo.png",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
@@ -32,6 +32,7 @@
|
|||||||
"@heroui/pagination": "^2.2.9",
|
"@heroui/pagination": "^2.2.9",
|
||||||
"@heroui/popover": "2.3.10",
|
"@heroui/popover": "2.3.10",
|
||||||
"@heroui/select": "2.4.10",
|
"@heroui/select": "2.4.10",
|
||||||
|
"@heroui/skeleton": "^2.2.6",
|
||||||
"@heroui/slider": "2.4.8",
|
"@heroui/slider": "2.4.8",
|
||||||
"@heroui/snippet": "2.2.11",
|
"@heroui/snippet": "2.2.11",
|
||||||
"@heroui/spinner": "2.2.7",
|
"@heroui/spinner": "2.2.7",
|
||||||
|
@@ -231,7 +231,7 @@ export default function AudioPlayer(props: AudioPlayerProps) {
|
|||||||
: 'top-3 -left-8 rounded-l-full bg-opacity-50 backdrop-blur-md'
|
: 'top-3 -left-8 rounded-l-full bg-opacity-50 backdrop-blur-md'
|
||||||
)}
|
)}
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="danger"
|
color="primary"
|
||||||
size="sm"
|
size="sm"
|
||||||
onPress={() => setIsCollapsed(!isCollapsed)}
|
onPress={() => setIsCollapsed(!isCollapsed)}
|
||||||
>
|
>
|
||||||
|
@@ -33,7 +33,7 @@ const AddButton: React.FC<AddButtonProps> = (props) => {
|
|||||||
>
|
>
|
||||||
<DropdownTrigger>
|
<DropdownTrigger>
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
startContent={<IoAddCircleOutline className="text-2xl" />}
|
startContent={<IoAddCircleOutline className="text-2xl" />}
|
||||||
>
|
>
|
||||||
新建
|
新建
|
||||||
|
@@ -27,7 +27,7 @@ const SaveButtons: React.FC<SaveButtonsProps> = ({
|
|||||||
取消更改
|
取消更改
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
isLoading={isSubmitting}
|
isLoading={isSubmitting}
|
||||||
onPress={() => onSubmit()}
|
onPress={() => onSubmit()}
|
||||||
>
|
>
|
||||||
|
@@ -110,7 +110,7 @@ const AudioInsert = () => {
|
|||||||
<Tooltip content="发送音频">
|
<Tooltip content="发送音频">
|
||||||
<div className="max-w-fit">
|
<div className="max-w-fit">
|
||||||
<PopoverTrigger>
|
<PopoverTrigger>
|
||||||
<Button color="danger" variant="flat" isIconOnly radius="full">
|
<Button color="primary" variant="flat" isIconOnly radius="full">
|
||||||
<IoMic className="text-xl" />
|
<IoMic className="text-xl" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
@@ -120,7 +120,7 @@ const AudioInsert = () => {
|
|||||||
<Tooltip content="上传音频">
|
<Tooltip content="上传音频">
|
||||||
<Button
|
<Button
|
||||||
className="text-lg"
|
className="text-lg"
|
||||||
color="danger"
|
color="primary"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
variant="flat"
|
variant="flat"
|
||||||
radius="full"
|
radius="full"
|
||||||
@@ -137,7 +137,7 @@ const AudioInsert = () => {
|
|||||||
<PopoverTrigger tooltip="输入音频地址">
|
<PopoverTrigger tooltip="输入音频地址">
|
||||||
<Button
|
<Button
|
||||||
className="text-lg"
|
className="text-lg"
|
||||||
color="danger"
|
color="primary"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
variant="flat"
|
variant="flat"
|
||||||
radius="full"
|
radius="full"
|
||||||
@@ -154,7 +154,7 @@ const AudioInsert = () => {
|
|||||||
placeholder="请输入音频地址"
|
placeholder="请输入音频地址"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
radius="full"
|
radius="full"
|
||||||
@@ -177,7 +177,7 @@ const AudioInsert = () => {
|
|||||||
<PopoverTrigger>
|
<PopoverTrigger>
|
||||||
<Button
|
<Button
|
||||||
className="text-lg"
|
className="text-lg"
|
||||||
color="danger"
|
color="primary"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
variant="flat"
|
variant="flat"
|
||||||
radius="full"
|
radius="full"
|
||||||
@@ -190,7 +190,7 @@ const AudioInsert = () => {
|
|||||||
<PopoverContent className="flex-col gap-2 p-4">
|
<PopoverContent className="flex-col gap-2 p-4">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button
|
<Button
|
||||||
color={isRecording ? 'danger' : 'danger'}
|
color={isRecording ? 'primary' : 'primary'}
|
||||||
variant="flat"
|
variant="flat"
|
||||||
onPress={isRecording ? stopRecording : startRecording}
|
onPress={isRecording ? stopRecording : startRecording}
|
||||||
>
|
>
|
||||||
@@ -198,7 +198,7 @@ const AudioInsert = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
{showPreview && audioPreview && (
|
{showPreview && audioPreview && (
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
onPress={handleShowPreview}
|
onPress={handleShowPreview}
|
||||||
>
|
>
|
||||||
@@ -212,7 +212,7 @@ const AudioInsert = () => {
|
|||||||
className={clsx(
|
className={clsx(
|
||||||
'w-4 h-4 rounded-full',
|
'w-4 h-4 rounded-full',
|
||||||
isRecording
|
isRecording
|
||||||
? 'animate-pulse bg-danger-400'
|
? 'animate-pulse bg-primary-400'
|
||||||
: 'bg-success-400'
|
: 'bg-success-400'
|
||||||
)}
|
)}
|
||||||
></span>
|
></span>
|
||||||
|
@@ -10,7 +10,7 @@ const DiceInsert = () => {
|
|||||||
return (
|
return (
|
||||||
<Tooltip content="发送骰子">
|
<Tooltip content="发送骰子">
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
radius="full"
|
radius="full"
|
||||||
|
@@ -55,7 +55,7 @@ const EmojiPicker = ({ onInsertEmoji, onOpenChange }: EmojiPickerProps) => {
|
|||||||
<Tooltip content="插入表情">
|
<Tooltip content="插入表情">
|
||||||
<div className="max-w-fit">
|
<div className="max-w-fit">
|
||||||
<PopoverTrigger>
|
<PopoverTrigger>
|
||||||
<Button color="danger" variant="flat" isIconOnly radius="full">
|
<Button color="primary" variant="flat" isIconOnly radius="full">
|
||||||
<MdEmojiEmotions className="text-xl" />
|
<MdEmojiEmotions className="text-xl" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
@@ -65,7 +65,7 @@ const EmojiPicker = ({ onInsertEmoji, onOpenChange }: EmojiPickerProps) => {
|
|||||||
{visibleEmojis.map((emoji) => (
|
{visibleEmojis.map((emoji) => (
|
||||||
<Button
|
<Button
|
||||||
key={emoji.id}
|
key={emoji.id}
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
radius="full"
|
radius="full"
|
||||||
|
@@ -35,7 +35,7 @@ const FileInsert = () => {
|
|||||||
<Tooltip content="发送文件">
|
<Tooltip content="发送文件">
|
||||||
<div className="max-w-fit">
|
<div className="max-w-fit">
|
||||||
<PopoverTrigger>
|
<PopoverTrigger>
|
||||||
<Button color="danger" variant="flat" isIconOnly radius="full">
|
<Button color="primary" variant="flat" isIconOnly radius="full">
|
||||||
<FaFolder className="text-lg" />
|
<FaFolder className="text-lg" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
@@ -45,7 +45,7 @@ const FileInsert = () => {
|
|||||||
<Tooltip content="上传文件">
|
<Tooltip content="上传文件">
|
||||||
<Button
|
<Button
|
||||||
className="text-lg"
|
className="text-lg"
|
||||||
color="danger"
|
color="primary"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
variant="flat"
|
variant="flat"
|
||||||
radius="full"
|
radius="full"
|
||||||
@@ -62,7 +62,7 @@ const FileInsert = () => {
|
|||||||
<PopoverTrigger tooltip="输入文件地址">
|
<PopoverTrigger tooltip="输入文件地址">
|
||||||
<Button
|
<Button
|
||||||
className="text-lg"
|
className="text-lg"
|
||||||
color="danger"
|
color="primary"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
variant="flat"
|
variant="flat"
|
||||||
radius="full"
|
radius="full"
|
||||||
@@ -79,7 +79,7 @@ const FileInsert = () => {
|
|||||||
placeholder="请输入文件地址"
|
placeholder="请输入文件地址"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
radius="full"
|
radius="full"
|
||||||
|
@@ -23,7 +23,7 @@ const ImageInsert = ({ insertImage, onOpenChange }: ImageInsertProps) => {
|
|||||||
<Tooltip content="插入图片">
|
<Tooltip content="插入图片">
|
||||||
<div className="max-w-fit">
|
<div className="max-w-fit">
|
||||||
<PopoverTrigger>
|
<PopoverTrigger>
|
||||||
<Button color="danger" variant="flat" isIconOnly radius="full">
|
<Button color="primary" variant="flat" isIconOnly radius="full">
|
||||||
<MdImage className="text-xl" />
|
<MdImage className="text-xl" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
@@ -33,7 +33,7 @@ const ImageInsert = ({ insertImage, onOpenChange }: ImageInsertProps) => {
|
|||||||
<Tooltip content="上传图片">
|
<Tooltip content="上传图片">
|
||||||
<Button
|
<Button
|
||||||
className="text-lg"
|
className="text-lg"
|
||||||
color="danger"
|
color="primary"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
variant="flat"
|
variant="flat"
|
||||||
radius="full"
|
radius="full"
|
||||||
@@ -50,7 +50,7 @@ const ImageInsert = ({ insertImage, onOpenChange }: ImageInsertProps) => {
|
|||||||
<PopoverTrigger tooltip="输入图片地址">
|
<PopoverTrigger tooltip="输入图片地址">
|
||||||
<Button
|
<Button
|
||||||
className="text-lg"
|
className="text-lg"
|
||||||
color="danger"
|
color="primary"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
variant="flat"
|
variant="flat"
|
||||||
radius="full"
|
radius="full"
|
||||||
@@ -67,7 +67,7 @@ const ImageInsert = ({ insertImage, onOpenChange }: ImageInsertProps) => {
|
|||||||
placeholder="请输入图片地址"
|
placeholder="请输入图片地址"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
radius="full"
|
radius="full"
|
||||||
|
@@ -80,7 +80,7 @@ const MusicInsert = () => {
|
|||||||
<Tooltip content="发送音乐">
|
<Tooltip content="发送音乐">
|
||||||
<div className="max-w-fit">
|
<div className="max-w-fit">
|
||||||
<PopoverTrigger>
|
<PopoverTrigger>
|
||||||
<Button color="danger" variant="flat" isIconOnly radius="full">
|
<Button color="primary" variant="flat" isIconOnly radius="full">
|
||||||
<IoMusicalNotes className="text-xl" />
|
<IoMusicalNotes className="text-xl" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
@@ -132,7 +132,7 @@ const MusicInsert = () => {
|
|||||||
<Button
|
<Button
|
||||||
fullWidth
|
fullWidth
|
||||||
size="lg"
|
size="lg"
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
radius="full"
|
radius="full"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
@@ -236,7 +236,7 @@ const MusicInsert = () => {
|
|||||||
<Button
|
<Button
|
||||||
fullWidth
|
fullWidth
|
||||||
size="lg"
|
size="lg"
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
radius="full"
|
radius="full"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
@@ -19,7 +19,7 @@ const ReplyInsert = ({ insertReply }: ReplyInsertProps) => {
|
|||||||
<Tooltip content="回复消息">
|
<Tooltip content="回复消息">
|
||||||
<div className="max-w-fit">
|
<div className="max-w-fit">
|
||||||
<PopoverTrigger>
|
<PopoverTrigger>
|
||||||
<Button color="danger" variant="flat" isIconOnly radius="full">
|
<Button color="primary" variant="flat" isIconOnly radius="full">
|
||||||
<BsChatQuoteFill className="text-lg" />
|
<BsChatQuoteFill className="text-lg" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
@@ -38,7 +38,7 @@ const ReplyInsert = ({ insertReply }: ReplyInsertProps) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
radius="full"
|
radius="full"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
|
@@ -10,7 +10,7 @@ const RPSInsert = () => {
|
|||||||
return (
|
return (
|
||||||
<Tooltip content="发送猜拳">
|
<Tooltip content="发送猜拳">
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
radius="full"
|
radius="full"
|
||||||
|
@@ -35,7 +35,7 @@ const VideoInsert = () => {
|
|||||||
<Tooltip content="发送视频">
|
<Tooltip content="发送视频">
|
||||||
<div className="max-w-fit">
|
<div className="max-w-fit">
|
||||||
<PopoverTrigger>
|
<PopoverTrigger>
|
||||||
<Button color="danger" variant="flat" isIconOnly radius="full">
|
<Button color="primary" variant="flat" isIconOnly radius="full">
|
||||||
<IoVideocam className="text-xl" />
|
<IoVideocam className="text-xl" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
@@ -45,7 +45,7 @@ const VideoInsert = () => {
|
|||||||
<Tooltip content="上传视频">
|
<Tooltip content="上传视频">
|
||||||
<Button
|
<Button
|
||||||
className="text-lg"
|
className="text-lg"
|
||||||
color="danger"
|
color="primary"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
variant="flat"
|
variant="flat"
|
||||||
radius="full"
|
radius="full"
|
||||||
@@ -62,7 +62,7 @@ const VideoInsert = () => {
|
|||||||
<PopoverTrigger tooltip="输入视频地址">
|
<PopoverTrigger tooltip="输入视频地址">
|
||||||
<Button
|
<Button
|
||||||
className="text-lg"
|
className="text-lg"
|
||||||
color="danger"
|
color="primary"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
variant="flat"
|
variant="flat"
|
||||||
radius="full"
|
radius="full"
|
||||||
@@ -79,7 +79,7 @@ const VideoInsert = () => {
|
|||||||
placeholder="请输入视频地址"
|
placeholder="请输入视频地址"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
radius="full"
|
radius="full"
|
||||||
|
@@ -190,7 +190,7 @@ const ChatInput = () => {
|
|||||||
<DiceInsert />
|
<DiceInsert />
|
||||||
<RPSInsert />
|
<RPSInsert />
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
const messages = getChatMessage()
|
const messages = getChatMessage()
|
||||||
showStructuredMessage(messages)
|
showStructuredMessage(messages)
|
||||||
|
@@ -15,7 +15,7 @@ export default function ChatInputModal() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button onPress={onOpen} color="danger" radius="full" variant="flat">
|
<Button onPress={onOpen} color="primary" radius="full" variant="flat">
|
||||||
构造聊天消息
|
构造聊天消息
|
||||||
</Button>
|
</Button>
|
||||||
<Modal
|
<Modal
|
||||||
@@ -36,7 +36,7 @@ export default function ChatInputModal() {
|
|||||||
</div>
|
</div>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button color="danger" onPress={onClose} variant="flat">
|
<Button color="primary" onPress={onClose} variant="flat">
|
||||||
关闭
|
关闭
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
|
@@ -78,7 +78,7 @@ const NetworkDisplayCard = <T extends keyof NetworkType>({
|
|||||||
{debug ? '关闭调试' : '开启调试'}
|
{debug ? '关闭调试' : '开启调试'}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
startContent={<MdDeleteForever />}
|
startContent={<MdDeleteForever />}
|
||||||
onPress={handleDelete}
|
onPress={handleDelete}
|
||||||
>
|
>
|
||||||
|
@@ -19,7 +19,7 @@ const NetworkItemDisplay: React.FC<NetworkItemDisplayProps> = ({
|
|||||||
className={clsx(
|
className={clsx(
|
||||||
'bg-opacity-60 shadow-sm md:rounded-3xl',
|
'bg-opacity-60 shadow-sm md:rounded-3xl',
|
||||||
size === 'md'
|
size === 'md'
|
||||||
? 'col-span-8 md:col-span-2 bg-danger-50 shadow-danger-100'
|
? 'col-span-8 md:col-span-2 bg-primary-50 shadow-primary-100'
|
||||||
: 'col-span-2 md:col-span-1 bg-warning-100 shadow-warning-200'
|
: 'col-span-2 md:col-span-1 bg-warning-100 shadow-warning-200'
|
||||||
)}
|
)}
|
||||||
shadow="sm"
|
shadow="sm"
|
||||||
|
@@ -33,7 +33,7 @@ export default function CreateFileModal({
|
|||||||
<ModalHeader>新建</ModalHeader>
|
<ModalHeader>新建</ModalHeader>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<ButtonGroup color="danger">
|
<ButtonGroup color="primary">
|
||||||
<Button
|
<Button
|
||||||
variant={fileType === 'file' ? 'solid' : 'flat'}
|
variant={fileType === 'file' ? 'solid' : 'flat'}
|
||||||
onPress={() => onTypeChange('file')}
|
onPress={() => onTypeChange('file')}
|
||||||
@@ -51,10 +51,10 @@ export default function CreateFileModal({
|
|||||||
</div>
|
</div>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button color="danger" variant="flat" onPress={onClose}>
|
<Button color="primary" variant="flat" onPress={onClose}>
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
<Button color="danger" onPress={onCreate}>
|
<Button color="primary" onPress={onCreate}>
|
||||||
创建
|
创建
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
|
@@ -81,10 +81,10 @@ export default function FileEditModal({
|
|||||||
</div>
|
</div>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button color="danger" variant="flat" onPress={onClose}>
|
<Button color="primary" variant="flat" onPress={onClose}>
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
<Button color="danger" onPress={onSave}>
|
<Button color="primary" onPress={onSave}>
|
||||||
保存
|
保存
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
|
@@ -82,7 +82,7 @@ export default function FilePreviewModal({
|
|||||||
{contentElement}
|
{contentElement}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button color="danger" variant="flat" onPress={onClose}>
|
<Button color="primary" variant="flat" onPress={onClose}>
|
||||||
关闭
|
关闭
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
|
@@ -82,7 +82,7 @@ export default function FileTable({
|
|||||||
setPreviewImages([])
|
setPreviewImages([])
|
||||||
setPreviewIndex(0)
|
setPreviewIndex(0)
|
||||||
setShowImage(false)
|
setShowImage(false)
|
||||||
}, [files])
|
}, [currentPath])
|
||||||
|
|
||||||
const onPreviewImage = (name: string, images: PreviewImage[]) => {
|
const onPreviewImage = (name: string, images: PreviewImage[]) => {
|
||||||
const index = images.findIndex((image) => image.key === name)
|
const index = images.findIndex((image) => image.key === name)
|
||||||
@@ -116,7 +116,7 @@ export default function FileTable({
|
|||||||
isCompact
|
isCompact
|
||||||
showControls
|
showControls
|
||||||
showShadow
|
showShadow
|
||||||
color="danger"
|
color="primary"
|
||||||
page={page}
|
page={page}
|
||||||
total={pages}
|
total={pages}
|
||||||
onChange={(page) => setPage(page)}
|
onChange={(page) => setPage(page)}
|
||||||
@@ -195,7 +195,7 @@ export default function FileTable({
|
|||||||
<ButtonGroup size="sm">
|
<ButtonGroup size="sm">
|
||||||
<Button
|
<Button
|
||||||
isIconOnly
|
isIconOnly
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
onPress={() => onRenameRequest(file.name)}
|
onPress={() => onRenameRequest(file.name)}
|
||||||
>
|
>
|
||||||
@@ -203,7 +203,7 @@ export default function FileTable({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
isIconOnly
|
isIconOnly
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
onPress={() => onMoveRequest(file.name)}
|
onPress={() => onMoveRequest(file.name)}
|
||||||
>
|
>
|
||||||
@@ -211,7 +211,7 @@ export default function FileTable({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
isIconOnly
|
isIconOnly
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
onPress={() => onCopyPath(file.name)}
|
onPress={() => onCopyPath(file.name)}
|
||||||
>
|
>
|
||||||
@@ -219,7 +219,7 @@ export default function FileTable({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
isIconOnly
|
isIconOnly
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
onPress={() => onDownload(filePath)}
|
onPress={() => onDownload(filePath)}
|
||||||
>
|
>
|
||||||
@@ -227,7 +227,7 @@ export default function FileTable({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
isIconOnly
|
isIconOnly
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
onPress={() => onDelete(filePath)}
|
onPress={() => onDelete(filePath)}
|
||||||
>
|
>
|
||||||
|
@@ -74,6 +74,9 @@ export default function ImageNameButton({
|
|||||||
src={data}
|
src={data}
|
||||||
alt={name}
|
alt={name}
|
||||||
className="w-8 h-8 flex-shrink-0"
|
className="w-8 h-8 flex-shrink-0"
|
||||||
|
classNames={{
|
||||||
|
wrapper: 'w-8 h-8 flex-shrink-0'
|
||||||
|
}}
|
||||||
radius="sm"
|
radius="sm"
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@@ -86,13 +86,13 @@ function DirectoryTree({
|
|||||||
onPress={handleClick}
|
onPress={handleClick}
|
||||||
className="py-1 px-2 text-left justify-start min-w-0 min-h-0 h-auto text-sm rounded-md"
|
className="py-1 px-2 text-left justify-start min-w-0 min-h-0 h-auto text-sm rounded-md"
|
||||||
size="sm"
|
size="sm"
|
||||||
color="danger"
|
color="primary"
|
||||||
variant={variant}
|
variant={variant}
|
||||||
startContent={
|
startContent={
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'rounded-md',
|
'rounded-md',
|
||||||
isSeleted ? 'bg-danger-600' : 'bg-danger-50'
|
isSeleted ? 'bg-primary-600' : 'bg-primary-50'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{expanded ? <IoRemove /> : <IoAdd />}
|
{expanded ? <IoRemove /> : <IoAdd />}
|
||||||
@@ -105,7 +105,7 @@ function DirectoryTree({
|
|||||||
<div>
|
<div>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex py-1 px-8">
|
<div className="flex py-1 px-8">
|
||||||
<Spinner size="sm" color="danger" />
|
<Spinner size="sm" color="primary" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
dirs.map((dirName) => {
|
dirs.map((dirName) => {
|
||||||
@@ -155,10 +155,10 @@ export default function MoveModal({
|
|||||||
<p className="text-sm text-default-500">移动项:{selectionInfo}</p>
|
<p className="text-sm text-default-500">移动项:{selectionInfo}</p>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button color="danger" variant="flat" onPress={onClose}>
|
<Button color="primary" variant="flat" onPress={onClose}>
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
<Button color="danger" onPress={onMove}>
|
<Button color="primary" onPress={onMove}>
|
||||||
确定
|
确定
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
|
@@ -31,10 +31,10 @@ export default function RenameModal({
|
|||||||
<Input label="新名称" value={newFileName} onChange={onNameChange} />
|
<Input label="新名称" value={newFileName} onChange={onNameChange} />
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button color="danger" variant="flat" onPress={onClose}>
|
<Button color="primary" variant="flat" onPress={onClose}>
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
<Button color="danger" onPress={onRename}>
|
<Button color="primary" onPress={onRename}>
|
||||||
确定
|
确定
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
|
@@ -33,7 +33,7 @@ export default function Hitokoto() {
|
|||||||
<div className="relative">
|
<div className="relative">
|
||||||
{loading && <PageLoading />}
|
{loading && <PageLoading />}
|
||||||
{error ? (
|
{error ? (
|
||||||
<div className="text-danger-400">一言加载失败:{error.message}</div>
|
<div className="text-primary-400">一言加载失败:{error.message}</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div>{data?.hitokoto}</div>
|
<div>{data?.hitokoto}</div>
|
||||||
@@ -52,7 +52,7 @@ export default function Hitokoto() {
|
|||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
isIconOnly
|
isIconOnly
|
||||||
radius="full"
|
radius="full"
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
>
|
>
|
||||||
<IoRefresh />
|
<IoRefresh />
|
||||||
|
@@ -34,7 +34,7 @@ export default function HoverTiltedCard({
|
|||||||
rotateAmplitude = 14,
|
rotateAmplitude = 14,
|
||||||
showTooltip = false,
|
showTooltip = false,
|
||||||
overlayContent = (
|
overlayContent = (
|
||||||
<div className="text-center mt-6 px-4 py-0.5 shadow-lg rounded-full bg-danger-600 text-default-100 bg-opacity-80">
|
<div className="text-center mt-6 px-4 py-0.5 shadow-lg rounded-full bg-primary-600 text-default-100 bg-opacity-80">
|
||||||
NapCat
|
NapCat
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
|
@@ -43,7 +43,7 @@ const ImageInput: React.FC<ImageInputProps> = ({ onChange, value, label }) => {
|
|||||||
onChange('')
|
onChange('')
|
||||||
if (inputRef.current) inputRef.current.value = ''
|
if (inputRef.current) inputRef.current.value = ''
|
||||||
}}
|
}}
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
size="sm"
|
size="sm"
|
||||||
>
|
>
|
||||||
|
@@ -16,13 +16,13 @@ const logLevelColor: {
|
|||||||
| 'secondary'
|
| 'secondary'
|
||||||
| 'success'
|
| 'success'
|
||||||
| 'warning'
|
| 'warning'
|
||||||
| 'danger'
|
| 'primary'
|
||||||
} = {
|
} = {
|
||||||
[LogLevel.DEBUG]: 'default',
|
[LogLevel.DEBUG]: 'default',
|
||||||
[LogLevel.INFO]: 'primary',
|
[LogLevel.INFO]: 'primary',
|
||||||
[LogLevel.WARN]: 'warning',
|
[LogLevel.WARN]: 'warning',
|
||||||
[LogLevel.ERROR]: 'danger',
|
[LogLevel.ERROR]: 'primary',
|
||||||
[LogLevel.FATAL]: 'danger'
|
[LogLevel.FATAL]: 'primary'
|
||||||
}
|
}
|
||||||
const LogLevelSelect = (props: LogLevelSelectProps) => {
|
const LogLevelSelect = (props: LogLevelSelectProps) => {
|
||||||
const { selectedKeys, onSelectionChange } = props
|
const { selectedKeys, onSelectionChange } = props
|
||||||
|
@@ -65,7 +65,7 @@ const Modal: React.FC<ModalProps> = React.memo((props) => {
|
|||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
{showCancel && (
|
{showCancel && (
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="light"
|
variant="light"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
onCancel?.()
|
onCancel?.()
|
||||||
@@ -76,7 +76,7 @@ const Modal: React.FC<ModalProps> = React.memo((props) => {
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
onConfirm?.()
|
onConfirm?.()
|
||||||
nativeClose()
|
nativeClose()
|
||||||
|
@@ -28,7 +28,7 @@ import type {
|
|||||||
|
|
||||||
function displayData(data: number, loading: boolean, error?: Error) {
|
function displayData(data: number, loading: boolean, error?: Error) {
|
||||||
if (error) {
|
if (error) {
|
||||||
return <MdError className="text-danger-400" />
|
return <MdError className="text-primary-400" />
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
@@ -175,7 +175,7 @@ export default function NapCatRepoInfo() {
|
|||||||
className="group h-auto py-3"
|
className="group h-auto py-3"
|
||||||
endContent={
|
endContent={
|
||||||
releaseError ? (
|
releaseError ? (
|
||||||
<MdError className="text-danger-400" />
|
<MdError className="text-primary-400" />
|
||||||
) : releaseLoading ? (
|
) : releaseLoading ? (
|
||||||
<Spinner size="sm" />
|
<Spinner size="sm" />
|
||||||
) : (
|
) : (
|
||||||
@@ -229,7 +229,7 @@ export default function NapCatRepoInfo() {
|
|||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
startContent={
|
startContent={
|
||||||
<IconWrapper className="bg-danger/10 text-danger dark:text-danger-500">
|
<IconWrapper className="bg-primary/10 text-primary dark:text-primary-500">
|
||||||
<BookIcon />
|
<BookIcon />
|
||||||
</IconWrapper>
|
</IconWrapper>
|
||||||
}
|
}
|
||||||
|
@@ -150,7 +150,7 @@ const GenericForm = <T extends keyof NetworkConfigType>({
|
|||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
isDisabled={formState.isSubmitting}
|
isDisabled={formState.isSubmitting}
|
||||||
variant="light"
|
variant="light"
|
||||||
onPress={onClose}
|
onPress={onClose}
|
||||||
|
@@ -91,7 +91,7 @@ const OneBotApiDebug: React.FC<OneBotApiDebugProps> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="p-4 pt-14 rounded-lg shadow-md">
|
<section className="p-4 pt-14 rounded-lg shadow-md">
|
||||||
<h1 className="text-2xl font-bold mb-4 flex items-center gap-1 text-danger-400">
|
<h1 className="text-2xl font-bold mb-4 flex items-center gap-1 text-primary-400">
|
||||||
<PiCatDuotone />
|
<PiCatDuotone />
|
||||||
{data.description}
|
{data.description}
|
||||||
</h1>
|
</h1>
|
||||||
@@ -125,7 +125,7 @@ const OneBotApiDebug: React.FC<OneBotApiDebugProps> = (props) => {
|
|||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
onPress={sendRequest}
|
onPress={sendRequest}
|
||||||
color="danger"
|
color="primary"
|
||||||
size="lg"
|
size="lg"
|
||||||
radius="full"
|
radius="full"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
|
@@ -27,7 +27,7 @@ const SchemaType = ({
|
|||||||
name = '固定值'
|
name = '固定值'
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
let chipColor: 'primary' | 'success' | 'danger' | 'warning' | 'secondary' =
|
let chipColor: 'primary' | 'success' | 'primary' | 'warning' | 'secondary' =
|
||||||
'primary'
|
'primary'
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'enum':
|
case 'enum':
|
||||||
@@ -37,7 +37,7 @@ const SchemaType = ({
|
|||||||
chipColor = 'secondary'
|
chipColor = 'secondary'
|
||||||
break
|
break
|
||||||
case 'array':
|
case 'array':
|
||||||
chipColor = 'danger'
|
chipColor = 'primary'
|
||||||
break
|
break
|
||||||
case 'object':
|
case 'object':
|
||||||
chipColor = 'success'
|
chipColor = 'success'
|
||||||
|
@@ -33,11 +33,11 @@ const OneBotApiNavList: React.FC<OneBotApiNavListProps> = (props) => {
|
|||||||
>
|
>
|
||||||
<div className="w-64 h-full overflow-y-auto px-2 pt-2 pb-10 md:pb-0">
|
<div className="w-64 h-full overflow-y-auto px-2 pt-2 pb-10 md:pb-0">
|
||||||
<Input
|
<Input
|
||||||
className="sticky top-0 z-10 text-danger-600"
|
className="sticky top-0 z-10 text-primary-600"
|
||||||
classNames={{
|
classNames={{
|
||||||
inputWrapper:
|
inputWrapper:
|
||||||
'bg-opacity-30 bg-danger-50 backdrop-blur-sm border border-danger-300 mb-2',
|
'bg-opacity-30 bg-primary-50 backdrop-blur-sm border border-primary-300 mb-2',
|
||||||
input: 'bg-transparent !text-danger-400 !placeholder-danger-400'
|
input: 'bg-transparent !text-primary-400 !placeholder-primary-400'
|
||||||
}}
|
}}
|
||||||
radius="full"
|
radius="full"
|
||||||
placeholder="搜索 API"
|
placeholder="搜索 API"
|
||||||
@@ -51,7 +51,7 @@ const OneBotApiNavList: React.FC<OneBotApiNavListProps> = (props) => {
|
|||||||
key={apiName}
|
key={apiName}
|
||||||
shadow="none"
|
shadow="none"
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'w-full border border-danger-100 rounded-lg mb-1 bg-opacity-30 backdrop-blur-sm text-danger-400',
|
'w-full border border-primary-100 rounded-lg mb-1 bg-opacity-30 backdrop-blur-sm text-primary-400',
|
||||||
{
|
{
|
||||||
hidden: !(
|
hidden: !(
|
||||||
apiName.includes(searchValue) ||
|
apiName.includes(searchValue) ||
|
||||||
@@ -59,7 +59,7 @@ const OneBotApiNavList: React.FC<OneBotApiNavListProps> = (props) => {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'!bg-opacity-40 border border-danger-400 bg-danger-50 text-danger-600':
|
'!bg-opacity-40 border border-primary-400 bg-primary-50 text-primary-600':
|
||||||
apiName === selectedApi
|
apiName === selectedApi
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
@@ -69,8 +69,8 @@ const OneBotApiNavList: React.FC<OneBotApiNavListProps> = (props) => {
|
|||||||
<CardBody>
|
<CardBody>
|
||||||
<h2 className="font-bold">{api.description}</h2>
|
<h2 className="font-bold">{api.description}</h2>
|
||||||
<div
|
<div
|
||||||
className={clsx('text-sm text-danger-200', {
|
className={clsx('text-sm text-primary-200', {
|
||||||
'!text-danger-400': apiName === selectedApi
|
'!text-primary-400': apiName === selectedApi
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{apiName}
|
{apiName}
|
||||||
|
@@ -109,7 +109,7 @@ const OneBotItemRender = ({ data, index, style }: OneBotItemRenderProps) => {
|
|||||||
<PopoverTrigger>
|
<PopoverTrigger>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
radius="full"
|
radius="full"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
|
@@ -30,7 +30,7 @@ const OneBotDisplayResponse: React.FC<OneBotDisplayResponseProps> = ({
|
|||||||
<PopoverTrigger>
|
<PopoverTrigger>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
radius="full"
|
radius="full"
|
||||||
className="text-medium"
|
className="text-medium"
|
||||||
|
@@ -43,7 +43,7 @@ const OneBotSendModal: React.FC<OneBotSendModalProps> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button onPress={onOpen} color="danger" radius="full" variant="flat">
|
<Button onPress={onOpen} color="primary" radius="full" variant="flat">
|
||||||
构造请求
|
构造请求
|
||||||
</Button>
|
</Button>
|
||||||
<Modal
|
<Modal
|
||||||
@@ -75,11 +75,11 @@ const OneBotSendModal: React.FC<OneBotSendModalProps> = (props) => {
|
|||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<ChatInputModal />
|
<ChatInputModal />
|
||||||
|
|
||||||
<Button color="danger" variant="flat" onPress={onClose}>
|
<Button color="primary" variant="flat" onPress={onClose}>
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
onPress={() => handleSendMessage(onClose)}
|
onPress={() => handleSendMessage(onClose)}
|
||||||
>
|
>
|
||||||
发送
|
发送
|
||||||
|
@@ -10,7 +10,7 @@ function StatusTag({
|
|||||||
color
|
color
|
||||||
}: {
|
}: {
|
||||||
title: string
|
title: string
|
||||||
color: 'success' | 'danger' | 'warning'
|
color: 'success' | 'primary' | 'warning'
|
||||||
}) {
|
}) {
|
||||||
const textClassName = `text-${color} text-sm`
|
const textClassName = `text-${color} text-sm`
|
||||||
const bgClassName = `bg-${color}`
|
const bgClassName = `bg-${color}`
|
||||||
@@ -27,7 +27,7 @@ export default function WSStatus({ state }: WSStatusProps) {
|
|||||||
return <StatusTag title="已连接" color="success" />
|
return <StatusTag title="已连接" color="success" />
|
||||||
}
|
}
|
||||||
if (state === ReadyState.CLOSED) {
|
if (state === ReadyState.CLOSED) {
|
||||||
return <StatusTag title="已关闭" color="danger" />
|
return <StatusTag title="已关闭" color="primary" />
|
||||||
}
|
}
|
||||||
if (state === ReadyState.CONNECTING) {
|
if (state === ReadyState.CONNECTING) {
|
||||||
return <StatusTag title="连接中" color="warning" />
|
return <StatusTag title="连接中" color="warning" />
|
||||||
|
@@ -16,7 +16,7 @@ export interface QQInfoCardProps {
|
|||||||
const QQInfoCard: React.FC<QQInfoCardProps> = ({ data, error, loading }) => {
|
const QQInfoCard: React.FC<QQInfoCardProps> = ({ data, error, loading }) => {
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
className="relative bg-danger-100 bg-opacity-60 overflow-hidden flex-shrink-0 shadow-md shadow-danger-300 dark:shadow-danger-50"
|
className="relative bg-primary-100 bg-opacity-60 overflow-hidden flex-shrink-0 shadow-md shadow-primary-300 dark:shadow-primary-50"
|
||||||
shadow="none"
|
shadow="none"
|
||||||
radius="lg"
|
radius="lg"
|
||||||
>
|
>
|
||||||
@@ -30,7 +30,7 @@ const QQInfoCard: React.FC<QQInfoCardProps> = ({ data, error, loading }) => {
|
|||||||
</CardBody>
|
</CardBody>
|
||||||
) : (
|
) : (
|
||||||
<CardBody className="flex-row items-center gap-2 overflow-hidden relative">
|
<CardBody className="flex-row items-center gap-2 overflow-hidden relative">
|
||||||
<div className="absolute right-0 bottom-0 text-5xl text-danger-400">
|
<div className="absolute right-0 bottom-0 text-5xl text-primary-400">
|
||||||
<BsTencentQq />
|
<BsTencentQq />
|
||||||
</div>
|
</div>
|
||||||
<div className="relative flex-shrink-0 z-10">
|
<div className="relative flex-shrink-0 z-10">
|
||||||
@@ -43,14 +43,14 @@ const QQInfoCard: React.FC<QQInfoCardProps> = ({ data, error, loading }) => {
|
|||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'w-4 h-4 rounded-full absolute right-0.5 bottom-0 border-2 border-danger-100 z-10',
|
'w-4 h-4 rounded-full absolute right-0.5 bottom-0 border-2 border-primary-100 z-10',
|
||||||
data?.online ? 'bg-green-500' : 'bg-gray-500'
|
data?.online ? 'bg-green-500' : 'bg-gray-500'
|
||||||
)}
|
)}
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-col justify-center">
|
<div className="flex-col justify-center">
|
||||||
<div className="text-lg truncate">{data?.nick}</div>
|
<div className="text-lg truncate">{data?.nick}</div>
|
||||||
<div className="text-danger-500 text-sm">{data?.uin}</div>
|
<div className="text-primary-500 text-sm">{data?.uin}</div>
|
||||||
</div>
|
</div>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
)}
|
)}
|
||||||
|
@@ -11,7 +11,7 @@ const QrCodeLogin: React.FC<QrCodeLoginProps> = ({ qrcode }) => {
|
|||||||
<div className="bg-white p-2 rounded-md w-fit mx-auto relative overflow-hidden">
|
<div className="bg-white p-2 rounded-md w-fit mx-auto relative overflow-hidden">
|
||||||
{!qrcode && (
|
{!qrcode && (
|
||||||
<div className="absolute left-2 top-2 right-2 bottom-2 bg-white bg-opacity-50 backdrop-blur flex items-center justify-center">
|
<div className="absolute left-2 top-2 right-2 bottom-2 bg-white bg-opacity-50 backdrop-blur flex items-center justify-center">
|
||||||
<Spinner color="danger" />
|
<Spinner color="primary" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<QRCodeSVG size={180} value={qrcode} />
|
<QRCodeSVG size={180} value={qrcode} />
|
||||||
|
@@ -63,7 +63,7 @@ const SideBar: React.FC<SideBarProps> = (props) => {
|
|||||||
<div className="mt-auto mb-10 md:mb-0">
|
<div className="mt-auto mb-10 md:mb-0">
|
||||||
<Button
|
<Button
|
||||||
className="w-full"
|
className="w-full"
|
||||||
color="danger"
|
color="primary"
|
||||||
radius="full"
|
radius="full"
|
||||||
variant="light"
|
variant="light"
|
||||||
onPress={toggleTheme}
|
onPress={toggleTheme}
|
||||||
@@ -75,7 +75,7 @@ const SideBar: React.FC<SideBarProps> = (props) => {
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className="w-full mb-2"
|
className="w-full mb-2"
|
||||||
color="danger"
|
color="primary"
|
||||||
radius="full"
|
radius="full"
|
||||||
variant="light"
|
variant="light"
|
||||||
onPress={onRevokeAuth}
|
onPress={onRevokeAuth}
|
||||||
|
@@ -55,7 +55,7 @@ const renderItems = (items: MenuItem[], children = false) => {
|
|||||||
isActive && 'bg-opacity-60',
|
isActive && 'bg-opacity-60',
|
||||||
b64img && 'backdrop-blur-md text-white'
|
b64img && 'backdrop-blur-md text-white'
|
||||||
)}
|
)}
|
||||||
color="danger"
|
color="primary"
|
||||||
endContent={
|
endContent={
|
||||||
canOpen ? (
|
canOpen ? (
|
||||||
// div实现箭头V效果
|
// div实现箭头V效果
|
||||||
@@ -63,7 +63,9 @@ const renderItems = (items: MenuItem[], children = false) => {
|
|||||||
className={clsx(
|
className={clsx(
|
||||||
'ml-auto relative w-3 h-3 transition-transform',
|
'ml-auto relative w-3 h-3 transition-transform',
|
||||||
open && 'transform rotate-180',
|
open && 'transform rotate-180',
|
||||||
isActive ? 'text-danger-500' : 'text-red-300 dark:text-white',
|
isActive
|
||||||
|
? 'text-primary-500'
|
||||||
|
: 'text-red-300 dark:text-white',
|
||||||
'before:rounded-full',
|
'before:rounded-full',
|
||||||
'before:content-[""]',
|
'before:content-[""]',
|
||||||
'before:block',
|
'before:block',
|
||||||
@@ -95,7 +97,7 @@ const renderItems = (items: MenuItem[], children = false) => {
|
|||||||
className={clsx(
|
className={clsx(
|
||||||
'w-3 h-1.5 rounded-full ml-auto shadow-lg',
|
'w-3 h-1.5 rounded-full ml-auto shadow-lg',
|
||||||
isActive
|
isActive
|
||||||
? 'bg-danger-500 animate-spinner-ease-spin'
|
? 'bg-primary-500 animate-spinner-ease-spin'
|
||||||
: 'bg-red-300 dark:bg-white'
|
: 'bg-red-300 dark:bg-white'
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
@@ -4,6 +4,8 @@ import { Chip } from '@heroui/chip'
|
|||||||
import { Spinner } from '@heroui/spinner'
|
import { Spinner } from '@heroui/spinner'
|
||||||
import { Tooltip } from '@heroui/tooltip'
|
import { Tooltip } from '@heroui/tooltip'
|
||||||
import { useRequest } from 'ahooks'
|
import { useRequest } from 'ahooks'
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
import { BsStars } from 'react-icons/bs'
|
||||||
import { FaCircleInfo, FaInfo, FaQq } from 'react-icons/fa6'
|
import { FaCircleInfo, FaInfo, FaQq } from 'react-icons/fa6'
|
||||||
import { IoLogoChrome, IoLogoOctocat } from 'react-icons/io'
|
import { IoLogoChrome, IoLogoOctocat } from 'react-icons/io'
|
||||||
import { RiMacFill } from 'react-icons/ri'
|
import { RiMacFill } from 'react-icons/ri'
|
||||||
@@ -32,10 +34,10 @@ const SystemInfoItem: React.FC<SystemInfoItemProps> = ({
|
|||||||
endContent
|
endContent
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex text-sm gap-1 p-2 items-center shadow-sm shadow-danger-50 dark:shadow-danger-100 rounded text-danger-400">
|
<div className="flex text-sm gap-1 p-2 items-center shadow-sm shadow-primary-50 dark:shadow-primary-100 rounded text-primary-400">
|
||||||
{icon}
|
{icon}
|
||||||
<div className="w-24">{title}</div>
|
<div className="w-24">{title}</div>
|
||||||
<div className="text-danger-200">{value}</div>
|
<div className="text-primary-200">{value}</div>
|
||||||
<div className="ml-auto">{endContent}</div>
|
<div className="ml-auto">{endContent}</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -60,7 +62,7 @@ const NewVersionTip = (props: NewVersionTipProps) => {
|
|||||||
<Button
|
<Button
|
||||||
isIconOnly
|
isIconOnly
|
||||||
radius="full"
|
radius="full"
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="shadow"
|
variant="shadow"
|
||||||
className="!w-5 !h-5 !min-w-0 text-small shadow-md"
|
className="!w-5 !h-5 !min-w-0 text-small shadow-md"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
@@ -97,12 +99,48 @@ const NewVersionTip = (props: NewVersionTipProps) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AISummaryComponent = () => {
|
||||||
|
const {
|
||||||
|
data: aiSummaryData,
|
||||||
|
loading: aiSummaryLoading,
|
||||||
|
error: aiSummaryError,
|
||||||
|
run: runAiSummary
|
||||||
|
} = useRequest(
|
||||||
|
(version) =>
|
||||||
|
request.get<ServerResponse<string | null>>(
|
||||||
|
`https://release.nc.152710.xyz/?version=${version}`,
|
||||||
|
{
|
||||||
|
timeout: 30000
|
||||||
|
}
|
||||||
|
),
|
||||||
|
{
|
||||||
|
manual: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
runAiSummary(currentVersion)
|
||||||
|
}, [currentVersion, runAiSummary])
|
||||||
|
|
||||||
|
if (aiSummaryLoading) {
|
||||||
|
return (
|
||||||
|
<div className="flex justify-center py-1">
|
||||||
|
<Spinner size="sm" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (aiSummaryError) {
|
||||||
|
return <div className="text-center text-primary-500">AI 摘要获取失败</div>
|
||||||
|
}
|
||||||
|
return <span className="text-default-700">{aiSummaryData?.data.data}</span>
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip content="有新版本可用">
|
<Tooltip content="有新版本可用">
|
||||||
<Button
|
<Button
|
||||||
isIconOnly
|
isIconOnly
|
||||||
radius="full"
|
radius="full"
|
||||||
color="danger"
|
color="primary"
|
||||||
variant="shadow"
|
variant="shadow"
|
||||||
className="!w-5 !h-5 !min-w-0 text-small shadow-md"
|
className="!w-5 !h-5 !min-w-0 text-small shadow-md"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
@@ -120,6 +158,13 @@ const NewVersionTip = (props: NewVersionTipProps) => {
|
|||||||
<span>最新版本</span>
|
<span>最新版本</span>
|
||||||
<Chip color="primary">{latestVersion}</Chip>
|
<Chip color="primary">{latestVersion}</Chip>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="p-2 rounded-md bg-content2 text-sm">
|
||||||
|
<div className="text-primary-400 font-bold flex items-center gap-1 mb-1">
|
||||||
|
<BsStars />
|
||||||
|
<span>AI总结</span>
|
||||||
|
</div>
|
||||||
|
{<AISummaryComponent />}
|
||||||
|
</div>
|
||||||
<div className="text-sm space-y-2 !mt-4">
|
<div className="text-sm space-y-2 !mt-4">
|
||||||
{middleVersions.map((versionInfo) => (
|
{middleVersions.map((versionInfo) => (
|
||||||
<div
|
<div
|
||||||
@@ -189,8 +234,8 @@ const SystemInfo: React.FC<SystemInfoProps> = (props) => {
|
|||||||
error: qqVersionError
|
error: qqVersionError
|
||||||
} = useRequest(WebUIManager.getQQVersion)
|
} = useRequest(WebUIManager.getQQVersion)
|
||||||
return (
|
return (
|
||||||
<Card className="bg-opacity-60 shadow-sm shadow-danger-50 dark:shadow-danger-100 overflow-visible flex-1">
|
<Card className="bg-opacity-60 shadow-sm shadow-primary-50 dark:shadow-primary-100 overflow-visible flex-1">
|
||||||
<CardHeader className="pb-0 items-center gap-1 text-danger-500 font-extrabold">
|
<CardHeader className="pb-0 items-center gap-1 text-primary-500 font-extrabold">
|
||||||
<FaCircleInfo className="text-lg" />
|
<FaCircleInfo className="text-lg" />
|
||||||
<span>系统信息</span>
|
<span>系统信息</span>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
@@ -55,7 +55,7 @@ const SystemStatusDisplay: React.FC<SystemStatusDisplayProps> = ({ data }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="bg-opacity-60 shadow-sm shadow-danger-50 dark:shadow-danger-100 col-span-1 lg:col-span-2 relative overflow-hidden">
|
<Card className="bg-opacity-60 shadow-sm shadow-primary-50 dark:shadow-primary-100 col-span-1 lg:col-span-2 relative overflow-hidden">
|
||||||
<div className="absolute h-full right-0 top-0">
|
<div className="absolute h-full right-0 top-0">
|
||||||
<Image
|
<Image
|
||||||
src={bkg}
|
src={bkg}
|
||||||
@@ -69,7 +69,7 @@ const SystemStatusDisplay: React.FC<SystemStatusDisplayProps> = ({ data }) => {
|
|||||||
</div>
|
</div>
|
||||||
<CardBody className="overflow-visible md:flex-row gap-4 items-center justify-stretch z-10">
|
<CardBody className="overflow-visible md:flex-row gap-4 items-center justify-stretch z-10">
|
||||||
<div className="flex-1 w-full md:max-w-96">
|
<div className="flex-1 w-full md:max-w-96">
|
||||||
<h2 className="text-lg font-semibold flex items-center gap-1 text-danger-400">
|
<h2 className="text-lg font-semibold flex items-center gap-1 text-primary-400">
|
||||||
<GiCpu className="text-xl" />
|
<GiCpu className="text-xl" />
|
||||||
<span>CPU</span>
|
<span>CPU</span>
|
||||||
</h2>
|
</h2>
|
||||||
@@ -88,7 +88,7 @@ const SystemStatusDisplay: React.FC<SystemStatusDisplayProps> = ({ data }) => {
|
|||||||
unit="%"
|
unit="%"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-lg font-semibold flex items-center gap-1 text-danger-400 mt-2">
|
<h2 className="text-lg font-semibold flex items-center gap-1 text-primary-400 mt-2">
|
||||||
<BiSolidMemoryCard className="text-xl" />
|
<BiSolidMemoryCard className="text-xl" />
|
||||||
<span>内存</span>
|
<span>内存</span>
|
||||||
</h2>
|
</h2>
|
||||||
|
@@ -62,7 +62,7 @@ export const Tab = forwardRef<HTMLDivElement, TabProps>(
|
|||||||
className={clsx(
|
className={clsx(
|
||||||
'px-2 py-1 flex items-center gap-1 text-sm font-medium border-b-2 transition-colors',
|
'px-2 py-1 flex items-center gap-1 text-sm font-medium border-b-2 transition-colors',
|
||||||
isSelected
|
isSelected
|
||||||
? 'border-danger text-danger'
|
? 'border-primary text-primary'
|
||||||
: 'border-transparent hover:border-default',
|
: 'border-transparent hover:border-default',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
|
@@ -35,6 +35,7 @@ const AudioProvider: React.FC<MusicProviderProps> = ({ children }) => {
|
|||||||
const [musicId, setMusicId] = useState<number>(0)
|
const [musicId, setMusicId] = useState<number>(0)
|
||||||
const [playMode, setPlayMode] = useState<PlayMode>(PlayMode.Loop)
|
const [playMode, setPlayMode] = useState<PlayMode>(PlayMode.Loop)
|
||||||
const music = musicList.find((music) => music.id === musicId)
|
const music = musicList.find((music) => music.id === musicId)
|
||||||
|
const [token] = useLocalStorage(key.token, '')
|
||||||
const onNext = () => {
|
const onNext = () => {
|
||||||
const nextID = getNextMusic(musicList, musicId, playMode)
|
const nextID = getNextMusic(musicList, musicId, playMode)
|
||||||
setMusicId(nextID)
|
setMusicId(nextID)
|
||||||
@@ -60,8 +61,8 @@ const AudioProvider: React.FC<MusicProviderProps> = ({ children }) => {
|
|||||||
setMusicId(res[0].id)
|
setMusicId(res[0].id)
|
||||||
}
|
}
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchMusicList(listId)
|
if (listId && token) fetchMusicList(listId)
|
||||||
}, [listId])
|
}, [listId, token])
|
||||||
return (
|
return (
|
||||||
<AudioContext.Provider
|
<AudioContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
@@ -79,7 +79,7 @@ const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|||||||
}, [location.pathname])
|
}, [location.pathname])
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="h-screen relative flex bg-danger-50 dark:bg-black items-stretch"
|
className="h-screen relative flex bg-primary-50 dark:bg-black items-stretch"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage: `url(${b64img})`,
|
backgroundImage: `url(${b64img})`,
|
||||||
backgroundSize: 'cover'
|
backgroundSize: 'cover'
|
||||||
@@ -99,9 +99,9 @@ const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'h-10 flex items-center font-bold text-xl backdrop-blur-lg rounded-full',
|
'h-10 flex items-center font-bold text-xl backdrop-blur-lg rounded-full',
|
||||||
'dark:bg-background dark:shadow-danger-100',
|
'dark:bg-background dark:shadow-primary-100',
|
||||||
'bg-background !bg-opacity-50',
|
'bg-background !bg-opacity-50',
|
||||||
'shadow-sm shadow-danger-50',
|
'shadow-sm shadow-primary-50',
|
||||||
'z-30 m-2 mb-0 sticky top-2 left-0'
|
'z-30 m-2 mb-0 sticky top-2 left-0'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { Card, CardBody } from '@heroui/card'
|
import { Card, CardBody } from '@heroui/card'
|
||||||
import { Image } from '@heroui/image'
|
import { Image } from '@heroui/image'
|
||||||
import { Link } from '@heroui/link'
|
import { Link } from '@heroui/link'
|
||||||
|
import { Skeleton } from '@heroui/skeleton'
|
||||||
import { Spinner } from '@heroui/spinner'
|
import { Spinner } from '@heroui/spinner'
|
||||||
import { useRequest } from 'ahooks'
|
import { useRequest } from 'ahooks'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
@@ -22,7 +23,7 @@ function VersionInfo() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2 text-2xl font-bold">
|
<div className="flex items-center gap-2 text-2xl font-bold">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="text-danger-500 drop-shadow-md">NapCat</div>
|
<div className="text-primary-500 drop-shadow-md">NapCat</div>
|
||||||
{error ? (
|
{error ? (
|
||||||
error.message
|
error.message
|
||||||
) : loading ? (
|
) : loading ? (
|
||||||
@@ -30,7 +31,7 @@ function VersionInfo() {
|
|||||||
) : (
|
) : (
|
||||||
<RotatingText
|
<RotatingText
|
||||||
texts={['WebUI', data?.version ?? '']}
|
texts={['WebUI', data?.version ?? '']}
|
||||||
mainClassName="overflow-hidden flex items-center bg-danger-500 px-2 rounded-lg text-default-50 shadow-md"
|
mainClassName="overflow-hidden flex items-center bg-primary-500 px-2 rounded-lg text-default-50 shadow-md"
|
||||||
staggerFrom={'last'}
|
staggerFrom={'last'}
|
||||||
initial={{ y: '100%' }}
|
initial={{ y: '100%' }}
|
||||||
animate={{ y: 0 }}
|
animate={{ y: 0 }}
|
||||||
@@ -78,16 +79,12 @@ export default function AboutPage() {
|
|||||||
const imageUrl = getImageUrl(baseUrl)
|
const imageUrl = getImageUrl(baseUrl)
|
||||||
|
|
||||||
if (!imageUrl) {
|
if (!imageUrl) {
|
||||||
return (
|
return <Skeleton className="h-16 rounded-lg" />
|
||||||
<div className="flex-1 h-32 flex items-center justify-center bg-default-100 rounded-lg">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Image
|
<Image
|
||||||
className="flex-1 pointer-events-none select-none"
|
className="flex-1 pointer-events-none select-none rounded-none"
|
||||||
src={imageUrl}
|
src={imageUrl}
|
||||||
alt={alt}
|
alt={alt}
|
||||||
/>
|
/>
|
||||||
@@ -107,12 +104,12 @@ export default function AboutPage() {
|
|||||||
<div className="flex-1 flex flex-col gap-2 py-2">
|
<div className="flex-1 flex flex-col gap-2 py-2">
|
||||||
<VersionInfo />
|
<VersionInfo />
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<p className="font-bold text-danger-400">NapCat 是什么?</p>
|
<p className="font-bold text-primary-400">NapCat 是什么?</p>
|
||||||
<p className="text-default-800">
|
<p className="text-default-800">
|
||||||
基于TypeScript构建的Bot框架,通过相应的启动器或者框架,主动调用QQ
|
基于TypeScript构建的Bot框架,通过相应的启动器或者框架,主动调用QQ
|
||||||
Node模块提供给客户端的接口,实现Bot的功能.
|
Node模块提供给客户端的接口,实现Bot的功能.
|
||||||
</p>
|
</p>
|
||||||
<p className="font-bold text-danger-400">魔法版介绍</p>
|
<p className="font-bold text-primary-400">魔法版介绍</p>
|
||||||
<p className="text-default-800">
|
<p className="text-default-800">
|
||||||
猫猫框架通过魔法的手段获得了 QQ 的发送消息、接收消息等接口。
|
猫猫框架通过魔法的手段获得了 QQ 的发送消息、接收消息等接口。
|
||||||
为了方便使用,猫猫框架将通过一种名为 OneBot 的约定将你的 HTTP /
|
为了方便使用,猫猫框架将通过一种名为 OneBot 的约定将你的 HTTP /
|
||||||
@@ -131,7 +128,7 @@ export default function AboutPage() {
|
|||||||
href="https://qm.qq.com/q/F9cgs1N3Mc"
|
href="https://qm.qq.com/q/F9cgs1N3Mc"
|
||||||
>
|
>
|
||||||
<CardBody className="flex-row items-center gap-2">
|
<CardBody className="flex-row items-center gap-2">
|
||||||
<span className="p-2 rounded-small bg-primary-50">
|
<span className="p-2 rounded-small bg-primary-50 text-primary-500">
|
||||||
<BsTencentQq size={16} />
|
<BsTencentQq size={16} />
|
||||||
</span>
|
</span>
|
||||||
<span>官方社群1</span>
|
<span>官方社群1</span>
|
||||||
@@ -146,7 +143,7 @@ export default function AboutPage() {
|
|||||||
href="https://qm.qq.com/q/hSt0u9PVn"
|
href="https://qm.qq.com/q/hSt0u9PVn"
|
||||||
>
|
>
|
||||||
<CardBody className="flex-row items-center gap-2">
|
<CardBody className="flex-row items-center gap-2">
|
||||||
<span className="p-2 rounded-small bg-primary-50">
|
<span className="p-2 rounded-small bg-primary-50 text-primary-500">
|
||||||
<BsTencentQq size={16} />
|
<BsTencentQq size={16} />
|
||||||
</span>
|
</span>
|
||||||
<span>官方社群2</span>
|
<span>官方社群2</span>
|
||||||
@@ -161,7 +158,7 @@ export default function AboutPage() {
|
|||||||
href="https://t.me/MelodicMoonlight"
|
href="https://t.me/MelodicMoonlight"
|
||||||
>
|
>
|
||||||
<CardBody className="flex-row items-center gap-2">
|
<CardBody className="flex-row items-center gap-2">
|
||||||
<span className="p-2 rounded-small bg-primary-50">
|
<span className="p-2 rounded-small bg-primary-50 text-primary-500">
|
||||||
<BsTelegram size={16} />
|
<BsTelegram size={16} />
|
||||||
</span>
|
</span>
|
||||||
<span>Telegram</span>
|
<span>Telegram</span>
|
||||||
@@ -176,7 +173,7 @@ export default function AboutPage() {
|
|||||||
href="https://napcat.napneko.icu/"
|
href="https://napcat.napneko.icu/"
|
||||||
>
|
>
|
||||||
<CardBody className="flex-row items-center gap-2">
|
<CardBody className="flex-row items-center gap-2">
|
||||||
<span className="p-2 rounded-small bg-primary-50">
|
<span className="p-2 rounded-small bg-primary-50 text-primary-500">
|
||||||
<IoDocument size={16} />
|
<IoDocument size={16} />
|
||||||
</span>
|
</span>
|
||||||
<span>使用文档</span>
|
<span>使用文档</span>
|
||||||
|
@@ -41,7 +41,7 @@ export default function HttpDebug() {
|
|||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
isIconOnly
|
isIconOnly
|
||||||
color="danger"
|
color="primary"
|
||||||
radius="md"
|
radius="md"
|
||||||
variant="shadow"
|
variant="shadow"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
@@ -64,7 +64,7 @@ export default function WSDebug() {
|
|||||||
/>
|
/>
|
||||||
<div className="flex-shrink-0 flex gap-2 col-span-2 md:col-span-1">
|
<div className="flex-shrink-0 flex gap-2 col-span-2 md:col-span-1">
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
onPress={handleConnect}
|
onPress={handleConnect}
|
||||||
size="lg"
|
size="lg"
|
||||||
radius="full"
|
radius="full"
|
||||||
|
@@ -332,7 +332,7 @@ export default function FileManagerPage() {
|
|||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div className="mb-4 flex items-center gap-4 sticky top-14 z-10 bg-content1 py-1">
|
<div className="mb-4 flex items-center gap-4 sticky top-14 z-10 bg-content1 py-1">
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
size="sm"
|
size="sm"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
variant="flat"
|
variant="flat"
|
||||||
@@ -343,7 +343,7 @@ export default function FileManagerPage() {
|
|||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
size="sm"
|
size="sm"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
variant="flat"
|
variant="flat"
|
||||||
@@ -354,7 +354,7 @@ export default function FileManagerPage() {
|
|||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
size="sm"
|
size="sm"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
@@ -365,7 +365,7 @@ export default function FileManagerPage() {
|
|||||||
<MdRefresh />
|
<MdRefresh />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
size="sm"
|
size="sm"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
variant="flat"
|
variant="flat"
|
||||||
@@ -379,7 +379,7 @@ export default function FileManagerPage() {
|
|||||||
selectedFiles === 'all') && (
|
selectedFiles === 'all') && (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
onPress={handleBatchDelete}
|
onPress={handleBatchDelete}
|
||||||
@@ -391,7 +391,7 @@ export default function FileManagerPage() {
|
|||||||
)
|
)
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
@@ -406,7 +406,7 @@ export default function FileManagerPage() {
|
|||||||
)
|
)
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="primary"
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
onPress={handleBatchDownload}
|
onPress={handleBatchDownload}
|
||||||
|
@@ -105,7 +105,7 @@ const DashboardIndexPage: React.FC = () => {
|
|||||||
<SystemStatusCard setArchInfo={setArchInfo} />
|
<SystemStatusCard setArchInfo={setArchInfo} />
|
||||||
</div>
|
</div>
|
||||||
<Networks />
|
<Networks />
|
||||||
<Card className="bg-opacity-60 shadow-sm shadow-danger-50">
|
<Card className="bg-opacity-60 shadow-sm shadow-primary-50">
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<Hitokoto />
|
<Hitokoto />
|
||||||
</CardBody>
|
</CardBody>
|
||||||
|
@@ -133,7 +133,7 @@ export default function TerminalPage() {
|
|||||||
size="sm"
|
size="sm"
|
||||||
className="min-w-0 w-4 h-4 flex-shrink-0"
|
className="min-w-0 w-4 h-4 flex-shrink-0"
|
||||||
onPress={() => closeTerminal(tab.id)}
|
onPress={() => closeTerminal(tab.id)}
|
||||||
color={selectedTab === tab.id ? 'danger' : 'default'}
|
color={selectedTab === tab.id ? 'primary' : 'default'}
|
||||||
>
|
>
|
||||||
<IoClose />
|
<IoClose />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -143,7 +143,7 @@ export default function TerminalPage() {
|
|||||||
</TabList>
|
</TabList>
|
||||||
<Button
|
<Button
|
||||||
isIconOnly
|
isIconOnly
|
||||||
color="danger"
|
color="primary"
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
onPress={createNewTerminal}
|
onPress={createNewTerminal}
|
||||||
|
@@ -13,5 +13,74 @@ export default {
|
|||||||
extend: {}
|
extend: {}
|
||||||
},
|
},
|
||||||
darkMode: 'class',
|
darkMode: 'class',
|
||||||
plugins: [heroui()]
|
plugins: [
|
||||||
|
heroui({
|
||||||
|
themes: {
|
||||||
|
light: {
|
||||||
|
colors: {
|
||||||
|
primary: {
|
||||||
|
DEFAULT: '#f31260',
|
||||||
|
foreground: '#fff',
|
||||||
|
50: '#fee7ef',
|
||||||
|
100: '#fdd0df',
|
||||||
|
200: '#faa0bf',
|
||||||
|
300: '#f871a0',
|
||||||
|
400: '#f54180',
|
||||||
|
500: '#f31260',
|
||||||
|
600: '#c20e4d',
|
||||||
|
700: '#920b3a',
|
||||||
|
800: '#610726',
|
||||||
|
900: '#310413'
|
||||||
|
},
|
||||||
|
danger: {
|
||||||
|
DEFAULT: '#DB3694',
|
||||||
|
foreground: '#fff',
|
||||||
|
50: '#FEEAF6',
|
||||||
|
100: '#FDD7DD',
|
||||||
|
200: '#FBAFC4',
|
||||||
|
300: '#F485AE',
|
||||||
|
400: '#E965A3',
|
||||||
|
500: '#DB3694',
|
||||||
|
600: '#BC278B',
|
||||||
|
700: '#9D1B7F',
|
||||||
|
800: '#7F1170',
|
||||||
|
900: '#690A66'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
colors: {
|
||||||
|
primary: {
|
||||||
|
DEFAULT: '#f31260',
|
||||||
|
foreground: '#fff',
|
||||||
|
50: '#310413',
|
||||||
|
100: '#610726',
|
||||||
|
200: '#920b3a',
|
||||||
|
300: '#c20e4d',
|
||||||
|
400: '#f31260',
|
||||||
|
500: '#f54180',
|
||||||
|
600: '#f871a0',
|
||||||
|
700: '#faa0bf',
|
||||||
|
800: '#fdd0df',
|
||||||
|
900: '#fee7ef'
|
||||||
|
},
|
||||||
|
danger: {
|
||||||
|
DEFAULT: '#DB3694',
|
||||||
|
foreground: '#fff',
|
||||||
|
50: '#690A66',
|
||||||
|
100: '#7F1170',
|
||||||
|
200: '#9D1B7F',
|
||||||
|
300: '#BC278B',
|
||||||
|
400: '#DB3694',
|
||||||
|
500: '#E965A3',
|
||||||
|
600: '#F485AE',
|
||||||
|
700: '#FBAFC4',
|
||||||
|
800: '#FDD7DD',
|
||||||
|
900: '#FEEAF6'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "4.5.5",
|
"version": "4.5.9",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:universal": "npm run build:webui && vite build --mode universal || exit 1",
|
"build:universal": "npm run build:webui && vite build --mode universal || exit 1",
|
||||||
"build:framework": "npm run build:webui && vite build --mode framework || exit 1",
|
"build:framework": "npm run build:webui && vite build --mode framework || exit 1",
|
||||||
|
@@ -66,7 +66,7 @@ export abstract class ConfigBase<T> {
|
|||||||
|
|
||||||
private handleError(e: unknown, message: string): void {
|
private handleError(e: unknown, message: string): void {
|
||||||
if (e instanceof SyntaxError) {
|
if (e instanceof SyntaxError) {
|
||||||
this.core.context.logger.logError(`[Core] [Config] 操作配置文件格式错误,请检查配置文件:`, e.message);
|
this.core.context.logger.logError('[Core] [Config] 操作配置文件格式错误,请检查配置文件:', e.message);
|
||||||
} else {
|
} else {
|
||||||
this.core.context.logger.logError(`[Core] [Config] ${message}:`, (e as Error).message);
|
this.core.context.logger.logError(`[Core] [Config] ${message}:`, (e as Error).message);
|
||||||
}
|
}
|
||||||
|
@@ -1 +1 @@
|
|||||||
export const napCatVersion = '4.5.5';
|
export const napCatVersion = '4.5.9';
|
||||||
|
@@ -43,7 +43,7 @@ export class NTQQFileApi {
|
|||||||
this.rkeyManager = new RkeyManager([
|
this.rkeyManager = new RkeyManager([
|
||||||
'https://rkey.napneko.icu/rkeys'
|
'https://rkey.napneko.icu/rkeys'
|
||||||
],
|
],
|
||||||
this.context.logger
|
this.context.logger
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,18 +300,18 @@ export class NTQQFileApi {
|
|||||||
element.elementType === ElementType.FILE
|
element.elementType === ElementType.FILE
|
||||||
) {
|
) {
|
||||||
switch (element.elementType) {
|
switch (element.elementType) {
|
||||||
case ElementType.PIC:
|
case ElementType.PIC:
|
||||||
element.picElement!.sourcePath = elementResults?.[elementIndex] ?? '';
|
element.picElement!.sourcePath = elementResults?.[elementIndex] ?? '';
|
||||||
break;
|
break;
|
||||||
case ElementType.VIDEO:
|
case ElementType.VIDEO:
|
||||||
element.videoElement!.filePath = elementResults?.[elementIndex] ?? '';
|
element.videoElement!.filePath = elementResults?.[elementIndex] ?? '';
|
||||||
break;
|
break;
|
||||||
case ElementType.PTT:
|
case ElementType.PTT:
|
||||||
element.pttElement!.filePath = elementResults?.[elementIndex] ?? '';
|
element.pttElement!.filePath = elementResults?.[elementIndex] ?? '';
|
||||||
break;
|
break;
|
||||||
case ElementType.FILE:
|
case ElementType.FILE:
|
||||||
element.fileElement!.filePath = elementResults?.[elementIndex] ?? '';
|
element.fileElement!.filePath = elementResults?.[elementIndex] ?? '';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
elementIndex++;
|
elementIndex++;
|
||||||
}
|
}
|
||||||
|
@@ -1,20 +1,22 @@
|
|||||||
import * as crypto from 'crypto';
|
import * as crypto from 'crypto';
|
||||||
import { PacketContext } from '@/core/packet/context/packetContext';
|
import {PacketContext} from '@/core/packet/context/packetContext';
|
||||||
import * as trans from '@/core/packet/transformer';
|
import * as trans from '@/core/packet/transformer';
|
||||||
import { PacketMsg } from '@/core/packet/message/message';
|
import {PacketMsg} from '@/core/packet/message/message';
|
||||||
import {
|
import {
|
||||||
PacketMsgFileElement,
|
PacketMsgFileElement,
|
||||||
PacketMsgPicElement,
|
PacketMsgPicElement,
|
||||||
PacketMsgPttElement,
|
PacketMsgPttElement,
|
||||||
PacketMsgVideoElement
|
PacketMsgVideoElement
|
||||||
} from '@/core/packet/message/element';
|
} from '@/core/packet/message/element';
|
||||||
import { ChatType } from '@/core';
|
import {ChatType, MsgSourceType, NTMsgType, RawMessage} from '@/core';
|
||||||
import { MiniAppRawData, MiniAppReqParams } from '@/core/packet/entities/miniApp';
|
import {MiniAppRawData, MiniAppReqParams} from '@/core/packet/entities/miniApp';
|
||||||
import { AIVoiceChatType } from '@/core/packet/entities/aiChat';
|
import {AIVoiceChatType} from '@/core/packet/entities/aiChat';
|
||||||
import { NapProtoDecodeStructType, NapProtoEncodeStructType } from '@napneko/nap-proto-core';
|
import {NapProtoDecodeStructType, NapProtoEncodeStructType, NapProtoMsg} from '@napneko/nap-proto-core';
|
||||||
import { IndexNode, MsgInfo } from '@/core/packet/transformer/proto';
|
import {IndexNode, LongMsgResult, MsgInfo} from '@/core/packet/transformer/proto';
|
||||||
import { OidbPacket } from '@/core/packet/transformer/base';
|
import {OidbPacket} from '@/core/packet/transformer/base';
|
||||||
import { ImageOcrResult } from '@/core/packet/entities/ocrResult';
|
import {ImageOcrResult} from '@/core/packet/entities/ocrResult';
|
||||||
|
import {gunzipSync} from 'zlib';
|
||||||
|
import {PacketMsgConverter} from '@/core/packet/message/converter';
|
||||||
|
|
||||||
export class PacketOperationContext {
|
export class PacketOperationContext {
|
||||||
private readonly context: PacketContext;
|
private readonly context: PacketContext;
|
||||||
@@ -57,10 +59,10 @@ export class PacketOperationContext {
|
|||||||
const res = trans.GetStrangerInfo.parse(resp);
|
const res = trans.GetStrangerInfo.parse(resp);
|
||||||
const extBigInt = BigInt(res.data.status.value);
|
const extBigInt = BigInt(res.data.status.value);
|
||||||
if (extBigInt <= 10n) {
|
if (extBigInt <= 10n) {
|
||||||
return { status: Number(extBigInt) * 10, ext_status: 0 };
|
return {status: Number(extBigInt) * 10, ext_status: 0};
|
||||||
}
|
}
|
||||||
status = Number((extBigInt & 0xff00n) + ((extBigInt >> 16n) & 0xffn));
|
status = Number((extBigInt & 0xff00n) + ((extBigInt >> 16n) & 0xffn));
|
||||||
return { status: 10, ext_status: status };
|
return {status: 10, ext_status: status};
|
||||||
} catch {
|
} catch {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -77,13 +79,13 @@ export class PacketOperationContext {
|
|||||||
const reqList = msg.flatMap(m =>
|
const reqList = msg.flatMap(m =>
|
||||||
m.msg.map(e => {
|
m.msg.map(e => {
|
||||||
if (e instanceof PacketMsgPicElement) {
|
if (e instanceof PacketMsgPicElement) {
|
||||||
return this.context.highway.uploadImage({ chatType, peerUid }, e);
|
return this.context.highway.uploadImage({chatType, peerUid}, e);
|
||||||
} else if (e instanceof PacketMsgVideoElement) {
|
} else if (e instanceof PacketMsgVideoElement) {
|
||||||
return this.context.highway.uploadVideo({ chatType, peerUid }, e);
|
return this.context.highway.uploadVideo({chatType, peerUid}, e);
|
||||||
} else if (e instanceof PacketMsgPttElement) {
|
} else if (e instanceof PacketMsgPttElement) {
|
||||||
return this.context.highway.uploadPtt({ chatType, peerUid }, e);
|
return this.context.highway.uploadPtt({chatType, peerUid}, e);
|
||||||
} else if (e instanceof PacketMsgFileElement) {
|
} else if (e instanceof PacketMsgFileElement) {
|
||||||
return this.context.highway.uploadFile({ chatType, peerUid }, e);
|
return this.context.highway.uploadFile({chatType, peerUid}, e);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}).filter(Boolean)
|
}).filter(Boolean)
|
||||||
@@ -116,6 +118,13 @@ export class PacketOperationContext {
|
|||||||
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async GetGroupImageUrl(groupUin: number, node: NapProtoEncodeStructType<typeof IndexNode>) {
|
||||||
|
const req = trans.DownloadGroupImage.build(groupUin, node);
|
||||||
|
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||||
|
const res = trans.DownloadImage.parse(resp);
|
||||||
|
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
||||||
|
}
|
||||||
|
|
||||||
async ImageOCR(imgUrl: string) {
|
async ImageOCR(imgUrl: string) {
|
||||||
const req = trans.ImageOCR.build(imgUrl);
|
const req = trans.ImageOCR.build(imgUrl);
|
||||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||||
@@ -195,4 +204,74 @@ export class PacketOperationContext {
|
|||||||
return res.msgInfo;
|
return res.msgInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async FetchForwardMsg(res_id: string): Promise<RawMessage[]> {
|
||||||
|
const req = trans.DownloadForwardMsg.build(this.context.napcore.basicInfo.uid, res_id);
|
||||||
|
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||||
|
const res = trans.DownloadForwardMsg.parse(resp);
|
||||||
|
const inflate = gunzipSync(res.result.payload);
|
||||||
|
const result = new NapProtoMsg(LongMsgResult).decode(inflate);
|
||||||
|
|
||||||
|
const main = result.action.find((r) => r.actionCommand === 'MultiMsg');
|
||||||
|
if (!main?.actionData.msgBody) {
|
||||||
|
throw new Error('msgBody is empty');
|
||||||
|
}
|
||||||
|
|
||||||
|
const messagesPromises = main.actionData.msgBody.map(async (msg) => {
|
||||||
|
if (!msg?.body?.richText?.elems) {
|
||||||
|
throw new Error('msg.body.richText.elems is empty');
|
||||||
|
}
|
||||||
|
const rawChains = new PacketMsgConverter().packetMsgToRaw(msg?.body?.richText?.elems);
|
||||||
|
const elements = await Promise.all(
|
||||||
|
rawChains.map(async ([element, rawElem]) => {
|
||||||
|
if (element.picElement && rawElem?.commonElem?.pbElem) {
|
||||||
|
const extra = new NapProtoMsg(MsgInfo).decode(rawElem.commonElem.pbElem);
|
||||||
|
const index = extra?.msgInfoBody[0]?.index;
|
||||||
|
if (msg?.responseHead.grp !== undefined) {
|
||||||
|
const groupUin = msg?.responseHead.grp?.groupUin ?? 0;
|
||||||
|
element.picElement = {
|
||||||
|
...element.picElement,
|
||||||
|
originImageUrl: await this.GetGroupImageUrl(groupUin, index!)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
element.picElement = {
|
||||||
|
...element.picElement,
|
||||||
|
originImageUrl: await this.GetImageUrl(this.context.napcore.basicInfo.uid, index!)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
chatType: ChatType.KCHATTYPEGROUP,
|
||||||
|
elements: elements,
|
||||||
|
guildId: '',
|
||||||
|
isOnlineMsg: false,
|
||||||
|
msgId: '7467703692092974645', // TODO: no necessary
|
||||||
|
msgRandom: '0',
|
||||||
|
msgSeq: String(msg.contentHead.sequence ?? 0),
|
||||||
|
msgTime: String(msg.contentHead.timeStamp ?? 0),
|
||||||
|
msgType: NTMsgType.KMSGTYPEMIX,
|
||||||
|
parentMsgIdList: [],
|
||||||
|
parentMsgPeer: {
|
||||||
|
chatType: ChatType.KCHATTYPEGROUP,
|
||||||
|
peerUid: String(msg?.responseHead.grp?.groupUin ?? 0),
|
||||||
|
},
|
||||||
|
peerName: '',
|
||||||
|
peerUid: '1094950020',
|
||||||
|
peerUin: '1094950020',
|
||||||
|
recallTime: '0',
|
||||||
|
records: [],
|
||||||
|
sendNickName: msg?.responseHead.grp?.memberName ?? '',
|
||||||
|
sendRemarkName: msg?.responseHead.grp?.memberName ?? '',
|
||||||
|
senderUid: '',
|
||||||
|
senderUin: '1094950020',
|
||||||
|
sourceType: MsgSourceType.K_DOWN_SOURCETYPE_UNKNOWN,
|
||||||
|
subMsgType: 1,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return await Promise.all(messagesPromises);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
Peer,
|
|
||||||
ChatType,
|
ChatType,
|
||||||
ElementType,
|
ElementType,
|
||||||
MessageElement,
|
MessageElement,
|
||||||
|
Peer,
|
||||||
RawMessage,
|
RawMessage,
|
||||||
SendArkElement,
|
SendArkElement,
|
||||||
SendFaceElement,
|
SendFaceElement,
|
||||||
@@ -31,7 +31,9 @@ import {
|
|||||||
PacketMsgVideoElement,
|
PacketMsgVideoElement,
|
||||||
PacketMultiMsgElement
|
PacketMultiMsgElement
|
||||||
} from '@/core/packet/message/element';
|
} from '@/core/packet/message/element';
|
||||||
import { PacketMsg, PacketSendMsgElement } from '@/core/packet/message/message';
|
import {PacketMsg, PacketSendMsgElement} from '@/core/packet/message/message';
|
||||||
|
import {NapProtoDecodeStructType} from '@napneko/nap-proto-core';
|
||||||
|
import {Elem} from '@/core/packet/transformer/proto';
|
||||||
|
|
||||||
const SupportedElementTypes = [
|
const SupportedElementTypes = [
|
||||||
ElementType.TEXT,
|
ElementType.TEXT,
|
||||||
@@ -154,4 +156,16 @@ export class PacketMsgConverter {
|
|||||||
}).filter((e) => e !== null)
|
}).filter((e) => e !== null)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
packetMsgToRaw(msg: NapProtoDecodeStructType<typeof Elem>[]): [MessageElement, NapProtoDecodeStructType<typeof Elem> | null][] {
|
||||||
|
const converters = [PacketMsgTextElement.parseElement,
|
||||||
|
PacketMsgAtElement.parseElement, PacketMsgReplyElement.parseElement, PacketMsgPicElement.parseElement];
|
||||||
|
return msg.map((element) => {
|
||||||
|
for (const converter of converters) {
|
||||||
|
const result = converter(element);
|
||||||
|
if (result) return result;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}).filter((e) => e !== null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,20 +1,22 @@
|
|||||||
import * as zlib from 'node:zlib';
|
import * as zlib from 'node:zlib';
|
||||||
import { NapProtoEncodeStructType, NapProtoMsg } from '@napneko/nap-proto-core';
|
import {NapProtoDecodeStructType, NapProtoEncodeStructType, NapProtoMsg} from '@napneko/nap-proto-core';
|
||||||
import {
|
import {
|
||||||
CustomFace,
|
CustomFace,
|
||||||
Elem,
|
Elem,
|
||||||
|
FileExtra,
|
||||||
|
GroupFileExtra,
|
||||||
MarkdownData,
|
MarkdownData,
|
||||||
MentionExtra,
|
MentionExtra,
|
||||||
|
MsgInfo,
|
||||||
NotOnlineImage,
|
NotOnlineImage,
|
||||||
|
OidbSvcTrpcTcp0XE37_800Response,
|
||||||
QBigFaceExtra,
|
QBigFaceExtra,
|
||||||
QSmallFaceExtra,
|
QSmallFaceExtra,
|
||||||
MsgInfo,
|
|
||||||
OidbSvcTrpcTcp0XE37_800Response,
|
|
||||||
FileExtra,
|
|
||||||
GroupFileExtra
|
|
||||||
} from '@/core/packet/transformer/proto';
|
} from '@/core/packet/transformer/proto';
|
||||||
import {
|
import {
|
||||||
|
ElementType,
|
||||||
FaceType,
|
FaceType,
|
||||||
|
MessageElement,
|
||||||
NTMsgAtType,
|
NTMsgAtType,
|
||||||
PicType,
|
PicType,
|
||||||
SendArkElement,
|
SendArkElement,
|
||||||
@@ -29,8 +31,11 @@ import {
|
|||||||
SendTextElement,
|
SendTextElement,
|
||||||
SendVideoElement
|
SendVideoElement
|
||||||
} from '@/core';
|
} from '@/core';
|
||||||
import { ForwardMsgBuilder } from '@/common/forward-msg-builder';
|
import {ForwardMsgBuilder} from '@/common/forward-msg-builder';
|
||||||
import { PacketMsg, PacketSendMsgElement } from '@/core/packet/message/message';
|
import {PacketMsg, PacketSendMsgElement} from '@/core/packet/message/message';
|
||||||
|
|
||||||
|
export type ParseElementFnR = [MessageElement, NapProtoDecodeStructType<typeof Elem> | null] | undefined;
|
||||||
|
type ParseElementFn = (elem: NapProtoDecodeStructType<typeof Elem>) => ParseElementFnR;
|
||||||
|
|
||||||
// raw <-> packet
|
// raw <-> packet
|
||||||
// TODO: SendStructLongMsgElement
|
// TODO: SendStructLongMsgElement
|
||||||
@@ -51,6 +56,8 @@ export abstract class IPacketMsgElement<T extends PacketSendMsgElement> {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static parseElement: ParseElementFn;
|
||||||
|
|
||||||
toPreview(): string {
|
toPreview(): string {
|
||||||
return '[暂不支持该消息类型喵~]';
|
return '[暂不支持该消息类型喵~]';
|
||||||
}
|
}
|
||||||
@@ -72,11 +79,30 @@ export class PacketMsgTextElement extends IPacketMsgElement<SendTextElement> {
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static override parseElement = (elem: NapProtoDecodeStructType<typeof Elem>): ParseElementFnR => {
|
||||||
|
if (elem.text?.str && (elem.text?.attr6Buf === undefined || elem.text?.attr6Buf?.length === 0)) {
|
||||||
|
return [{
|
||||||
|
textElement: {
|
||||||
|
content: elem.text?.str,
|
||||||
|
atType: NTMsgAtType.ATTYPEUNKNOWN,
|
||||||
|
atUid: '',
|
||||||
|
atTinyId: '',
|
||||||
|
atNtUid: '',
|
||||||
|
},
|
||||||
|
elementType: ElementType.UNKNOWN,
|
||||||
|
elementId: '',
|
||||||
|
}, null];
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
override toPreview(): string {
|
override toPreview(): string {
|
||||||
return this.text;
|
return this.text;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export class PacketMsgAtElement extends PacketMsgTextElement {
|
export class PacketMsgAtElement extends PacketMsgTextElement {
|
||||||
targetUid: string;
|
targetUid: string;
|
||||||
atAll: boolean;
|
atAll: boolean;
|
||||||
@@ -101,6 +127,22 @@ export class PacketMsgAtElement extends PacketMsgTextElement {
|
|||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
static override parseElement = (elem: NapProtoDecodeStructType<typeof Elem>): ParseElementFnR => {
|
||||||
|
if (elem.text?.str && (elem.text?.attr6Buf?.length ?? 100) >= 11) {
|
||||||
|
return [{
|
||||||
|
textElement: {
|
||||||
|
content: elem.text?.str,
|
||||||
|
atType: NTMsgAtType.ATTYPEONE,
|
||||||
|
atUid: String(Buffer.from(elem.text!.attr6Buf!).readUInt32BE(7)), // FIXME: hack
|
||||||
|
atTinyId: '',
|
||||||
|
atNtUid: '',
|
||||||
|
},
|
||||||
|
elementType: ElementType.UNKNOWN,
|
||||||
|
elementId: '',
|
||||||
|
}, null];
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PacketMsgReplyElement extends IPacketMsgElement<SendReplyElement> {
|
export class PacketMsgReplyElement extends IPacketMsgElement<SendReplyElement> {
|
||||||
@@ -137,21 +179,28 @@ export class PacketMsgReplyElement extends IPacketMsgElement<SendReplyElement> {
|
|||||||
pbReserve: {
|
pbReserve: {
|
||||||
messageId: this.messageId,
|
messageId: this.messageId,
|
||||||
},
|
},
|
||||||
toUin: BigInt(0),
|
toUin: BigInt(this.targetUin),
|
||||||
|
type: 1,
|
||||||
}
|
}
|
||||||
}, {
|
|
||||||
text: this.isGroupReply ? {
|
|
||||||
str: 'nya~',
|
|
||||||
pbReserve: new NapProtoMsg(MentionExtra).encode({
|
|
||||||
type: this.targetUin === 0 ? 1 : 2,
|
|
||||||
uin: 0,
|
|
||||||
field5: 0,
|
|
||||||
uid: String(this.targetUid),
|
|
||||||
}),
|
|
||||||
} : undefined,
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static override parseElement = (elem: NapProtoDecodeStructType<typeof Elem>): ParseElementFnR => {
|
||||||
|
if (elem.srcMsg && elem.srcMsg.pbReserve) {
|
||||||
|
const reserve = elem.srcMsg.pbReserve;
|
||||||
|
return [{
|
||||||
|
replyElement: {
|
||||||
|
replayMsgSeq: String(reserve.friendSeq ?? elem.srcMsg?.origSeqs?.[0] ?? 0),
|
||||||
|
replayMsgId: String(reserve.messageId ?? 0),
|
||||||
|
senderUin: String(elem?.srcMsg ?? 0)
|
||||||
|
},
|
||||||
|
elementType: ElementType.UNKNOWN,
|
||||||
|
elementId: '',
|
||||||
|
}, null];
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
override toPreview(): string {
|
override toPreview(): string {
|
||||||
return '[回复消息]';
|
return '[回复消息]';
|
||||||
}
|
}
|
||||||
@@ -207,6 +256,46 @@ export class PacketMsgFaceElement extends IPacketMsgElement<SendFaceElement> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static override parseElement = (elem: NapProtoDecodeStructType<typeof Elem>): ParseElementFnR => {
|
||||||
|
if (elem.face?.index) {
|
||||||
|
return [{
|
||||||
|
faceElement: {
|
||||||
|
faceIndex: elem.face.index,
|
||||||
|
faceType: FaceType.Normal
|
||||||
|
},
|
||||||
|
elementType: ElementType.UNKNOWN,
|
||||||
|
elementId: '',
|
||||||
|
}, null];
|
||||||
|
}
|
||||||
|
if (elem?.commonElem?.serviceType === 37 && elem?.commonElem?.pbElem) {
|
||||||
|
const qface = new NapProtoMsg(QBigFaceExtra).decode(elem?.commonElem?.pbElem);
|
||||||
|
if (qface?.faceId) {
|
||||||
|
return [{
|
||||||
|
faceElement: {
|
||||||
|
faceIndex: qface.faceId,
|
||||||
|
faceType: FaceType.Normal
|
||||||
|
},
|
||||||
|
elementType: ElementType.UNKNOWN,
|
||||||
|
elementId: '',
|
||||||
|
}, null];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (elem?.commonElem?.serviceType === 33 && elem?.commonElem?.pbElem) {
|
||||||
|
const qface = new NapProtoMsg(QSmallFaceExtra).decode(elem?.commonElem?.pbElem);
|
||||||
|
if (qface?.faceId) {
|
||||||
|
return [{
|
||||||
|
faceElement: {
|
||||||
|
faceIndex: qface.faceId,
|
||||||
|
faceType: FaceType.Normal
|
||||||
|
},
|
||||||
|
elementType: ElementType.UNKNOWN,
|
||||||
|
elementId: '',
|
||||||
|
}, null];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
override toPreview(): string {
|
override toPreview(): string {
|
||||||
return '[表情]';
|
return '[表情]';
|
||||||
}
|
}
|
||||||
@@ -295,6 +384,60 @@ export class PacketMsgPicElement extends IPacketMsgElement<SendPicElement> {
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static override parseElement = (elem: NapProtoDecodeStructType<typeof Elem>): ParseElementFnR => {
|
||||||
|
if (elem?.commonElem?.serviceType === 48 || [10, 20].includes(elem?.commonElem?.businessType ?? 0)) {
|
||||||
|
const extra = new NapProtoMsg(MsgInfo).decode(elem.commonElem!.pbElem!);
|
||||||
|
const msgInfoBody = extra.msgInfoBody[0];
|
||||||
|
const index = msgInfoBody?.index;
|
||||||
|
return [{
|
||||||
|
picElement: {
|
||||||
|
fileSize: index?.info.fileSize ?? 0,
|
||||||
|
picWidth: index?.info?.width ?? 0,
|
||||||
|
picHeight: index?.info?.height ?? 0,
|
||||||
|
fileName: index?.info?.fileHash ?? '',
|
||||||
|
sourcePath: '',
|
||||||
|
original: false,
|
||||||
|
picType: PicType.NEWPIC_APNG,
|
||||||
|
fileUuid: '',
|
||||||
|
fileSubId: '',
|
||||||
|
thumbFileSize: 0,
|
||||||
|
summary: '[图片]',
|
||||||
|
thumbPath: new Map(),
|
||||||
|
},
|
||||||
|
elementType: ElementType.UNKNOWN,
|
||||||
|
elementId: '',
|
||||||
|
}, elem];
|
||||||
|
}
|
||||||
|
if (elem?.notOnlineImage) {
|
||||||
|
const img = elem?.notOnlineImage; // url in originImageUrl
|
||||||
|
const preImg: MessageElement = {
|
||||||
|
picElement: {
|
||||||
|
fileSize: img.fileLen ?? 0,
|
||||||
|
picWidth: img.picWidth ?? 0,
|
||||||
|
picHeight: img.picHeight ?? 0,
|
||||||
|
fileName: Buffer.from(img.picMd5!).toString('hex') ?? '',
|
||||||
|
sourcePath: '',
|
||||||
|
original: false,
|
||||||
|
picType: PicType.NEWPIC_APNG,
|
||||||
|
fileUuid: '',
|
||||||
|
fileSubId: '',
|
||||||
|
thumbFileSize: 0,
|
||||||
|
summary: '[图片]',
|
||||||
|
thumbPath: new Map(),
|
||||||
|
},
|
||||||
|
elementType: ElementType.UNKNOWN,
|
||||||
|
elementId: '',
|
||||||
|
};
|
||||||
|
if (img.origUrl?.includes('&fileid=')) {
|
||||||
|
preImg.picElement!.originImageUrl = `https://multimedia.nt.qq.com.cn${img.origUrl}`;
|
||||||
|
} else {
|
||||||
|
preImg.picElement!.originImageUrl = `https://gchat.qpic.cn${img.origUrl}`;
|
||||||
|
}
|
||||||
|
return [preImg, elem];
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
override toPreview(): string {
|
override toPreview(): string {
|
||||||
return this.summary;
|
return this.summary;
|
||||||
}
|
}
|
||||||
|
50
src/core/packet/transformer/highway/DownloadGroupImage.ts
Normal file
50
src/core/packet/transformer/highway/DownloadGroupImage.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import * as proto from '@/core/packet/transformer/proto';
|
||||||
|
import { NapProtoEncodeStructType, NapProtoMsg } from '@napneko/nap-proto-core';
|
||||||
|
import { OidbPacket, PacketTransformer } from '@/core/packet/transformer/base';
|
||||||
|
import OidbBase from '@/core/packet/transformer/oidb/oidbBase';
|
||||||
|
import { IndexNode } from '@/core/packet/transformer/proto';
|
||||||
|
|
||||||
|
class DownloadGroupImage extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
build(group_uin: number, node: NapProtoEncodeStructType<typeof IndexNode>): OidbPacket {
|
||||||
|
const body = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
|
||||||
|
reqHead: {
|
||||||
|
common: {
|
||||||
|
requestId: 1,
|
||||||
|
command: 200
|
||||||
|
},
|
||||||
|
scene: {
|
||||||
|
requestType: 2,
|
||||||
|
businessType: 1,
|
||||||
|
sceneType: 2,
|
||||||
|
group: {
|
||||||
|
groupUin: group_uin
|
||||||
|
}
|
||||||
|
},
|
||||||
|
client: {
|
||||||
|
agentType: 2,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
download: {
|
||||||
|
node: node,
|
||||||
|
download: {
|
||||||
|
video: {
|
||||||
|
busiType: 0,
|
||||||
|
sceneType: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return OidbBase.build(0x11C4, 200, body, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
parse(data: Buffer) {
|
||||||
|
const oidbBody = OidbBase.parse(data).body;
|
||||||
|
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new DownloadGroupImage();
|
@@ -14,7 +14,7 @@ class DownloadImage extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
|
|||||||
reqHead: {
|
reqHead: {
|
||||||
common: {
|
common: {
|
||||||
requestId: 1,
|
requestId: 1,
|
||||||
command: 100
|
command: 200
|
||||||
},
|
},
|
||||||
scene: {
|
scene: {
|
||||||
requestType: 2,
|
requestType: 2,
|
||||||
|
@@ -12,3 +12,4 @@ export { default as UploadPrivateImage } from './UploadPrivateImage';
|
|||||||
export { default as UploadPrivatePtt } from './UploadPrivatePtt';
|
export { default as UploadPrivatePtt } from './UploadPrivatePtt';
|
||||||
export { default as UploadPrivateVideo } from './UploadPrivateVideo';
|
export { default as UploadPrivateVideo } from './UploadPrivateVideo';
|
||||||
export { default as DownloadImage } from './DownloadImage';
|
export { default as DownloadImage } from './DownloadImage';
|
||||||
|
export { default as DownloadGroupImage } from './DownloadGroupImage';
|
||||||
|
37
src/core/packet/transformer/message/DownloadForwardMsg.ts
Normal file
37
src/core/packet/transformer/message/DownloadForwardMsg.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import * as proto from '@/core/packet/transformer/proto';
|
||||||
|
import { NapProtoMsg } from '@napneko/nap-proto-core';
|
||||||
|
import { OidbPacket, PacketHexStrBuilder, PacketTransformer } from '@/core/packet/transformer/base';
|
||||||
|
|
||||||
|
class DownloadForwardMsg extends PacketTransformer<typeof proto.RecvLongMsgResp> {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
build(uid: string, resId: string): OidbPacket {
|
||||||
|
const req = new NapProtoMsg(proto.RecvLongMsgReq).encode({
|
||||||
|
info: {
|
||||||
|
uid: {
|
||||||
|
uid: uid
|
||||||
|
},
|
||||||
|
resId: resId,
|
||||||
|
acquire: true
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
field1: 2,
|
||||||
|
field2: 0,
|
||||||
|
field3: 0,
|
||||||
|
field4: 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
cmd: 'trpc.group.long_msg_interface.MsgService.SsoRecvLongMsg',
|
||||||
|
data: PacketHexStrBuilder(req)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
parse(data: Buffer) {
|
||||||
|
return new NapProtoMsg(proto.RecvLongMsgResp).decode(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new DownloadForwardMsg();
|
@@ -1 +1,2 @@
|
|||||||
export { default as UploadForwardMsg } from './UploadForwardMsg';
|
export { default as UploadForwardMsg } from './UploadForwardMsg';
|
||||||
|
export { default as DownloadForwardMsg } from './DownloadForwardMsg';
|
@@ -2,7 +2,7 @@ import { ProtoField, ScalarType } from '@napneko/nap-proto-core';
|
|||||||
import { PushMsgBody } from '@/core/packet/transformer/proto';
|
import { PushMsgBody } from '@/core/packet/transformer/proto';
|
||||||
|
|
||||||
export const LongMsgResult = {
|
export const LongMsgResult = {
|
||||||
action: ProtoField(2, () => LongMsgAction)
|
action: ProtoField(2, () => LongMsgAction, false, true)
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LongMsgAction = {
|
export const LongMsgAction = {
|
||||||
|
@@ -15,7 +15,6 @@ let napCatInitialized = false; // 添加一个标志
|
|||||||
function createServiceProxy(ServiceName) {
|
function createServiceProxy(ServiceName) {
|
||||||
return new Proxy(() => { }, {
|
return new Proxy(() => { }, {
|
||||||
get: (target, FunctionName) => {
|
get: (target, FunctionName) => {
|
||||||
console.log(ServiceName, FunctionName);
|
|
||||||
if (ServiceName === 'NodeIQQNTWrapperSession' && FunctionName === 'create') {
|
if (ServiceName === 'NodeIQQNTWrapperSession' && FunctionName === 'create') {
|
||||||
return () => new Proxy({}, {
|
return () => new Proxy({}, {
|
||||||
get: function (target, ClassFunName, receiver) {
|
get: function (target, ClassFunName, receiver) {
|
||||||
|
@@ -3,6 +3,7 @@ import { OB11Message, OB11MessageData, OB11MessageDataType, OB11MessageForward,
|
|||||||
import { ActionName } from '@/onebot/action/router';
|
import { ActionName } from '@/onebot/action/router';
|
||||||
import { MessageUnique } from '@/common/message-unique';
|
import { MessageUnique } from '@/common/message-unique';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
import { ChatType, ElementType, MsgSourceType, NTMsgType, RawMessage } from '@/core';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const SchemaData = Type.Object({
|
||||||
message_id: Type.Optional(Type.Union([Type.Number(), Type.String()])),
|
message_id: Type.Optional(Type.Union([Type.Number(), Type.String()])),
|
||||||
@@ -57,24 +58,72 @@ export class GoCQHTTPGetForwardMsgAction extends OneBotAction<Payload, {
|
|||||||
throw new Error('message_id is required');
|
throw new Error('message_id is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fakeForwardMsg = (res_id: string) => {
|
||||||
|
return {
|
||||||
|
chatType: ChatType.KCHATTYPEGROUP,
|
||||||
|
elements: [{
|
||||||
|
elementType: ElementType.MULTIFORWARD,
|
||||||
|
elementId: '',
|
||||||
|
multiForwardMsgElement: {
|
||||||
|
resId: res_id,
|
||||||
|
fileName: '',
|
||||||
|
xmlContent: '',
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
guildId: '',
|
||||||
|
isOnlineMsg: false,
|
||||||
|
msgId: '', // TODO: no necessary
|
||||||
|
msgRandom: '0',
|
||||||
|
msgSeq: '',
|
||||||
|
msgTime: '',
|
||||||
|
msgType: NTMsgType.KMSGTYPEMIX,
|
||||||
|
parentMsgIdList: [],
|
||||||
|
parentMsgPeer: {
|
||||||
|
chatType: ChatType.KCHATTYPEGROUP,
|
||||||
|
peerUid: '',
|
||||||
|
},
|
||||||
|
peerName: '',
|
||||||
|
peerUid: '284840486',
|
||||||
|
peerUin: '284840486',
|
||||||
|
recallTime: '0',
|
||||||
|
records: [],
|
||||||
|
sendNickName: '',
|
||||||
|
sendRemarkName: '',
|
||||||
|
senderUid: '',
|
||||||
|
senderUin: '1094950020',
|
||||||
|
sourceType: MsgSourceType.K_DOWN_SOURCETYPE_UNKNOWN,
|
||||||
|
subMsgType: 1,
|
||||||
|
} as RawMessage;
|
||||||
|
};
|
||||||
|
|
||||||
|
const protocolFallbackLogic = async (res_id: string) => {
|
||||||
|
const ob = (await this.obContext.apis.MsgApi.parseMessageV2(fakeForwardMsg(res_id)))?.arrayMsg;
|
||||||
|
if (ob) {
|
||||||
|
return {
|
||||||
|
messages: (ob?.message?.[0] as OB11MessageForward)?.data?.content
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw new Error('protocolFallbackLogic: 找不到相关的聊天记录');
|
||||||
|
};
|
||||||
|
|
||||||
const rootMsgId = MessageUnique.getShortIdByMsgId(msgId.toString());
|
const rootMsgId = MessageUnique.getShortIdByMsgId(msgId.toString());
|
||||||
const rootMsg = MessageUnique.getMsgIdAndPeerByShortId(rootMsgId ?? +msgId);
|
const rootMsg = MessageUnique.getMsgIdAndPeerByShortId(rootMsgId ?? +msgId);
|
||||||
if (!rootMsg) {
|
if (!rootMsg) {
|
||||||
throw new Error('msg not found');
|
return await protocolFallbackLogic(msgId.toString());
|
||||||
}
|
}
|
||||||
const data = await this.core.apis.MsgApi.getMsgsByMsgId(rootMsg.Peer, [rootMsg.MsgId]);
|
const data = await this.core.apis.MsgApi.getMsgsByMsgId(rootMsg.Peer, [rootMsg.MsgId]);
|
||||||
|
|
||||||
if (!data || data.result !== 0) {
|
if (!data || data.result !== 0) {
|
||||||
throw new Error('找不到相关的聊天记录' + data?.errMsg);
|
return await protocolFallbackLogic(msgId.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
const singleMsg = data.msgList[0];
|
const singleMsg = data.msgList[0];
|
||||||
if (!singleMsg) {
|
if (!singleMsg) {
|
||||||
throw new Error('找不到相关的聊天记录');
|
return await protocolFallbackLogic(msgId.toString());
|
||||||
}
|
}
|
||||||
const resMsg = (await this.obContext.apis.MsgApi.parseMessageV2(singleMsg))?.arrayMsg;//强制array 以便处理
|
const resMsg = (await this.obContext.apis.MsgApi.parseMessageV2(singleMsg))?.arrayMsg;//强制array 以便处理
|
||||||
if (!(resMsg?.message?.[0] as OB11MessageForward)?.data?.content) {
|
if (!(resMsg?.message?.[0] as OB11MessageForward)?.data?.content) {
|
||||||
throw new Error('找不到相关的聊天记录');
|
return await protocolFallbackLogic(msgId.toString());
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
messages: (resMsg?.message?.[0] as OB11MessageForward)?.data?.content
|
messages: (resMsg?.message?.[0] as OB11MessageForward)?.data?.content
|
||||||
|
@@ -1,41 +1,50 @@
|
|||||||
import { FileNapCatOneBotUUID } from '@/common/file-uuid';
|
import {FileNapCatOneBotUUID} from '@/common/file-uuid';
|
||||||
import { MessageUnique } from '@/common/message-unique';
|
import {MessageUnique} from '@/common/message-unique';
|
||||||
import {
|
import {
|
||||||
NTMsgAtType,
|
|
||||||
ChatType,
|
ChatType,
|
||||||
CustomMusicSignPostData,
|
CustomMusicSignPostData,
|
||||||
ElementType,
|
ElementType,
|
||||||
FaceIndex,
|
FaceIndex,
|
||||||
|
FaceType,
|
||||||
|
GrayTipElement,
|
||||||
|
GroupNotify,
|
||||||
IdMusicSignPostData,
|
IdMusicSignPostData,
|
||||||
MessageElement,
|
MessageElement,
|
||||||
NapCatCore,
|
NapCatCore,
|
||||||
NTGrayTipElementSubTypeV2,
|
NTGrayTipElementSubTypeV2,
|
||||||
|
NTMsgAtType,
|
||||||
Peer,
|
Peer,
|
||||||
RawMessage,
|
RawMessage,
|
||||||
SendMessageElement,
|
SendMessageElement,
|
||||||
SendTextElement,
|
SendTextElement,
|
||||||
FaceType,
|
|
||||||
GrayTipElement,
|
|
||||||
GroupNotify,
|
|
||||||
} from '@/core';
|
} from '@/core';
|
||||||
import faceConfig from '@/core/external/face_config.json';
|
import faceConfig from '@/core/external/face_config.json';
|
||||||
import { NapCatOneBot11Adapter, OB11Message, OB11MessageData, OB11MessageDataType, OB11MessageFileBase, OB11MessageForward, OB11MessageImage, OB11MessageVideo, } from '@/onebot';
|
import {
|
||||||
import { OB11Construct } from '@/onebot/helper/data';
|
NapCatOneBot11Adapter,
|
||||||
import { EventType } from '@/onebot/event/OneBotEvent';
|
OB11Message,
|
||||||
import { encodeCQCode } from '@/onebot/helper/cqcode';
|
OB11MessageData,
|
||||||
import { uriToLocalFile } from '@/common/file';
|
OB11MessageDataType,
|
||||||
import { RequestUtil } from '@/common/request';
|
OB11MessageFileBase,
|
||||||
import fsPromise, { constants } from 'node:fs/promises';
|
OB11MessageForward,
|
||||||
import { OB11FriendAddNoticeEvent } from '@/onebot/event/notice/OB11FriendAddNoticeEvent';
|
OB11MessageImage,
|
||||||
import { ForwardMsgBuilder } from '@/common/forward-msg-builder';
|
OB11MessageVideo,
|
||||||
import { NapProtoMsg } from '@napneko/nap-proto-core';
|
} from '@/onebot';
|
||||||
import { OB11GroupIncreaseEvent } from '../event/notice/OB11GroupIncreaseEvent';
|
import {OB11Construct} from '@/onebot/helper/data';
|
||||||
import { OB11GroupDecreaseEvent, GroupDecreaseSubType } from '../event/notice/OB11GroupDecreaseEvent';
|
import {EventType} from '@/onebot/event/OneBotEvent';
|
||||||
import { GroupAdmin } from '@/core/packet/transformer/proto/message/groupAdmin';
|
import {encodeCQCode} from '@/onebot/helper/cqcode';
|
||||||
import { OB11GroupAdminNoticeEvent } from '../event/notice/OB11GroupAdminNoticeEvent';
|
import {uriToLocalFile} from '@/common/file';
|
||||||
import { GroupChange, GroupChangeInfo, GroupInvite, PushMsgBody } from '@/core/packet/transformer/proto';
|
import {RequestUtil} from '@/common/request';
|
||||||
import { OB11GroupRequestEvent } from '../event/request/OB11GroupRequest';
|
import fsPromise, {constants} from 'node:fs/promises';
|
||||||
import { LRUCache } from '@/common/lru-cache';
|
import {OB11FriendAddNoticeEvent} from '@/onebot/event/notice/OB11FriendAddNoticeEvent';
|
||||||
|
import {ForwardMsgBuilder} from '@/common/forward-msg-builder';
|
||||||
|
import {NapProtoMsg} from '@napneko/nap-proto-core';
|
||||||
|
import {OB11GroupIncreaseEvent} from '../event/notice/OB11GroupIncreaseEvent';
|
||||||
|
import {GroupDecreaseSubType, OB11GroupDecreaseEvent} from '../event/notice/OB11GroupDecreaseEvent';
|
||||||
|
import {GroupAdmin} from '@/core/packet/transformer/proto/message/groupAdmin';
|
||||||
|
import {OB11GroupAdminNoticeEvent} from '../event/notice/OB11GroupAdminNoticeEvent';
|
||||||
|
import {GroupChange, GroupChangeInfo, GroupInvite, PushMsgBody} from '@/core/packet/transformer/proto';
|
||||||
|
import {OB11GroupRequestEvent} from '../event/request/OB11GroupRequest';
|
||||||
|
import {LRUCache} from '@/common/lru-cache';
|
||||||
|
|
||||||
type RawToOb11Converters = {
|
type RawToOb11Converters = {
|
||||||
[Key in keyof MessageElement as Key extends `${string}Element` ? Key : never]: (
|
[Key in keyof MessageElement as Key extends `${string}Element` ? Key : never]: (
|
||||||
@@ -84,12 +93,12 @@ export class OneBotMsgApi {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: OB11MessageDataType.text,
|
type: OB11MessageDataType.text,
|
||||||
data: { text },
|
data: {text},
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
let qq: string = 'all';
|
let qq: string = 'all';
|
||||||
if (element.atType !== NTMsgAtType.ATTYPEALL) {
|
if (element.atType !== NTMsgAtType.ATTYPEALL) {
|
||||||
const { atNtUid, atUid } = element;
|
const {atNtUid, atUid} = element;
|
||||||
qq = !atUid || atUid === '0' ? await this.core.apis.UserApi.getUinByUidV2(atNtUid) : atUid;
|
qq = !atUid || atUid === '0' ? await this.core.apis.UserApi.getUinByUidV2(atNtUid) : atUid;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@@ -197,7 +206,7 @@ export class OneBotMsgApi {
|
|||||||
peerUid: msg.peerUid,
|
peerUid: msg.peerUid,
|
||||||
guildId: '',
|
guildId: '',
|
||||||
};
|
};
|
||||||
const { emojiId } = _;
|
const {emojiId} = _;
|
||||||
const dir = emojiId.substring(0, 2);
|
const dir = emojiId.substring(0, 2);
|
||||||
const url = `https://gxh.vip.qq.com/club/item/parcel/item/${dir}/${emojiId}/raw300.gif`;
|
const url = `https://gxh.vip.qq.com/club/item/parcel/item/${dir}/${emojiId}/raw300.gif`;
|
||||||
const filename = `${dir}-${emojiId}.gif`;
|
const filename = `${dir}-${emojiId}.gif`;
|
||||||
@@ -264,7 +273,6 @@ export class OneBotMsgApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 丢弃该消息段
|
// 丢弃该消息段
|
||||||
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
|
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
|
||||||
this.core.context.logger.logError(
|
this.core.context.logger.logError(
|
||||||
@@ -355,18 +363,25 @@ export class OneBotMsgApi {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
multiForwardMsgElement: async (_, msg, _wrapper, context) => {
|
multiForwardMsgElement: async (element, msg, _wrapper, context) => {
|
||||||
const parentMsgPeer = msg.parentMsgPeer ?? {
|
const parentMsgPeer = msg.parentMsgPeer ?? {
|
||||||
chatType: msg.chatType,
|
chatType: msg.chatType,
|
||||||
guildId: '',
|
guildId: '',
|
||||||
peerUid: msg.peerUid,
|
peerUid: msg.peerUid,
|
||||||
};
|
};
|
||||||
const multiMsgs = await this.getMultiMessages(msg, parentMsgPeer);
|
let multiMsgs = await this.getMultiMessages(msg, parentMsgPeer);
|
||||||
// 拉取失败则跳过
|
// 拉取失败则跳过
|
||||||
if (!multiMsgs) return null;
|
if (!multiMsgs || multiMsgs.length === 0) {
|
||||||
|
try {
|
||||||
|
multiMsgs = await this.core.apis.PacketApi.pkt.operation.FetchForwardMsg(element.resId);
|
||||||
|
} catch (e) {
|
||||||
|
this.core.context.logger.logError('Protocol FetchForwardMsg fallback failed!', e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
const forward: OB11MessageForward = {
|
const forward: OB11MessageForward = {
|
||||||
type: OB11MessageDataType.forward,
|
type: OB11MessageDataType.forward,
|
||||||
data: { id: msg.msgId }
|
data: {id: msg.msgId}
|
||||||
};
|
};
|
||||||
if (!context.parseMultMsg) return forward;
|
if (!context.parseMultMsg) return forward;
|
||||||
forward.data.content = await this.parseMultiMessageContent(
|
forward.data.content = await this.parseMultiMessageContent(
|
||||||
@@ -397,7 +412,7 @@ export class OneBotMsgApi {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ob11ToRawConverters: Ob11ToRawConverters = {
|
ob11ToRawConverters: Ob11ToRawConverters = {
|
||||||
[OB11MessageDataType.text]: async ({ data: { text } }) => ({
|
[OB11MessageDataType.text]: async ({data: {text}}) => ({
|
||||||
elementType: ElementType.TEXT,
|
elementType: ElementType.TEXT,
|
||||||
elementId: '',
|
elementId: '',
|
||||||
textElement: {
|
textElement: {
|
||||||
@@ -409,7 +424,7 @@ export class OneBotMsgApi {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[OB11MessageDataType.at]: async ({ data: { qq: atQQ } }, context) => {
|
[OB11MessageDataType.at]: async ({data: {qq: atQQ}}, context) => {
|
||||||
function at(atUid: string, atNtUid: string, atType: NTMsgAtType, atName: string): SendTextElement {
|
function at(atUid: string, atNtUid: string, atType: NTMsgAtType, atName: string): SendTextElement {
|
||||||
return {
|
return {
|
||||||
elementType: ElementType.TEXT,
|
elementType: ElementType.TEXT,
|
||||||
@@ -436,7 +451,7 @@ export class OneBotMsgApi {
|
|||||||
return at(atQQ, uid, NTMsgAtType.ATTYPEONE, info.nick || '');
|
return at(atQQ, uid, NTMsgAtType.ATTYPEONE, info.nick || '');
|
||||||
},
|
},
|
||||||
|
|
||||||
[OB11MessageDataType.reply]: async ({ data: { id } }) => {
|
[OB11MessageDataType.reply]: async ({data: {id}}) => {
|
||||||
const replyMsgM = MessageUnique.getMsgIdAndPeerByShortId(parseInt(id));
|
const replyMsgM = MessageUnique.getMsgIdAndPeerByShortId(parseInt(id));
|
||||||
if (!replyMsgM) {
|
if (!replyMsgM) {
|
||||||
this.core.context.logger.logWarn('回复消息不存在', id);
|
this.core.context.logger.logWarn('回复消息不存在', id);
|
||||||
@@ -458,7 +473,7 @@ export class OneBotMsgApi {
|
|||||||
undefined;
|
undefined;
|
||||||
},
|
},
|
||||||
|
|
||||||
[OB11MessageDataType.face]: async ({ data: { id, resultId, chainCount } }) => {
|
[OB11MessageDataType.face]: async ({data: {id, resultId, chainCount}}) => {
|
||||||
const parsedFaceId = +id;
|
const parsedFaceId = +id;
|
||||||
// 从face_config.json中获取表情名称
|
// 从face_config.json中获取表情名称
|
||||||
const sysFaces = faceConfig.sysface;
|
const sysFaces = faceConfig.sysface;
|
||||||
@@ -522,12 +537,12 @@ export class OneBotMsgApi {
|
|||||||
},
|
},
|
||||||
|
|
||||||
[OB11MessageDataType.file]: async (sendMsg, context) => {
|
[OB11MessageDataType.file]: async (sendMsg, context) => {
|
||||||
const { path, fileName } = await this.handleOb11FileLikeMessage(sendMsg, context);
|
const {path, fileName} = await this.handleOb11FileLikeMessage(sendMsg, context);
|
||||||
return await this.core.apis.FileApi.createValidSendFileElement(context, path, fileName);
|
return await this.core.apis.FileApi.createValidSendFileElement(context, path, fileName);
|
||||||
},
|
},
|
||||||
|
|
||||||
[OB11MessageDataType.video]: async (sendMsg, context) => {
|
[OB11MessageDataType.video]: async (sendMsg, context) => {
|
||||||
const { path, fileName } = await this.handleOb11FileLikeMessage(sendMsg, context);
|
const {path, fileName} = await this.handleOb11FileLikeMessage(sendMsg, context);
|
||||||
|
|
||||||
let thumb = sendMsg.data.thumb;
|
let thumb = sendMsg.data.thumb;
|
||||||
if (thumb) {
|
if (thumb) {
|
||||||
@@ -545,7 +560,7 @@ export class OneBotMsgApi {
|
|||||||
this.core.apis.FileApi.createValidSendPttElement(
|
this.core.apis.FileApi.createValidSendPttElement(
|
||||||
(await this.handleOb11FileLikeMessage(sendMsg, context)).path),
|
(await this.handleOb11FileLikeMessage(sendMsg, context)).path),
|
||||||
|
|
||||||
[OB11MessageDataType.json]: async ({ data: { data } }) => ({
|
[OB11MessageDataType.json]: async ({data: {data}}) => ({
|
||||||
elementType: ElementType.ARK,
|
elementType: ElementType.ARK,
|
||||||
elementId: '',
|
elementId: '',
|
||||||
arkElement: {
|
arkElement: {
|
||||||
@@ -588,13 +603,13 @@ export class OneBotMsgApi {
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
// Need signing
|
// Need signing
|
||||||
[OB11MessageDataType.markdown]: async ({ data: { content } }) => ({
|
[OB11MessageDataType.markdown]: async ({data: {content}}) => ({
|
||||||
elementType: ElementType.MARKDOWN,
|
elementType: ElementType.MARKDOWN,
|
||||||
elementId: '',
|
elementId: '',
|
||||||
markdownElement: { content },
|
markdownElement: {content},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[OB11MessageDataType.music]: async ({ data }, context) => {
|
[OB11MessageDataType.music]: async ({data}, context) => {
|
||||||
// 保留, 直到...找到更好的解决方案
|
// 保留, 直到...找到更好的解决方案
|
||||||
if (data.id !== undefined) {
|
if (data.id !== undefined) {
|
||||||
if (!['qq', '163', 'kugou', 'kuwo', 'migu'].includes(data.type)) {
|
if (!['qq', '163', 'kugou', 'kuwo', 'migu'].includes(data.type)) {
|
||||||
@@ -618,8 +633,8 @@ export class OneBotMsgApi {
|
|||||||
|
|
||||||
let postData: IdMusicSignPostData | CustomMusicSignPostData;
|
let postData: IdMusicSignPostData | CustomMusicSignPostData;
|
||||||
if (data.id === undefined && data.content) {
|
if (data.id === undefined && data.content) {
|
||||||
const { content, ...others } = data;
|
const {content, ...others} = data;
|
||||||
postData = { singer: content, ...others };
|
postData = {singer: content, ...others};
|
||||||
} else {
|
} else {
|
||||||
postData = data;
|
postData = data;
|
||||||
}
|
}
|
||||||
@@ -631,7 +646,7 @@ export class OneBotMsgApi {
|
|||||||
try {
|
try {
|
||||||
const musicJson = await RequestUtil.HttpGetJson<string>(signUrl, 'POST', postData);
|
const musicJson = await RequestUtil.HttpGetJson<string>(signUrl, 'POST', postData);
|
||||||
return this.ob11ToRawConverters.json({
|
return this.ob11ToRawConverters.json({
|
||||||
data: { data: musicJson },
|
data: {data: musicJson},
|
||||||
type: OB11MessageDataType.json
|
type: OB11MessageDataType.json
|
||||||
}, context);
|
}, context);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -642,10 +657,10 @@ export class OneBotMsgApi {
|
|||||||
|
|
||||||
[OB11MessageDataType.node]: async () => undefined,
|
[OB11MessageDataType.node]: async () => undefined,
|
||||||
|
|
||||||
[OB11MessageDataType.forward]: async ({ data }, context) => {
|
[OB11MessageDataType.forward]: async ({data}, context) => {
|
||||||
const jsonData = ForwardMsgBuilder.fromResId(data.id);
|
const jsonData = ForwardMsgBuilder.fromResId(data.id);
|
||||||
return this.ob11ToRawConverters.json({
|
return this.ob11ToRawConverters.json({
|
||||||
data: { data: JSON.stringify(jsonData) },
|
data: {data: JSON.stringify(jsonData)},
|
||||||
type: OB11MessageDataType.json
|
type: OB11MessageDataType.json
|
||||||
}, context);
|
}, context);
|
||||||
},
|
},
|
||||||
@@ -665,17 +680,17 @@ export class OneBotMsgApi {
|
|||||||
|
|
||||||
[OB11MessageDataType.miniapp]: async () => undefined,
|
[OB11MessageDataType.miniapp]: async () => undefined,
|
||||||
|
|
||||||
[OB11MessageDataType.contact]: async ({ data: { type = 'qq', id } }, context) => {
|
[OB11MessageDataType.contact]: async ({data: {type = 'qq', id}}, context) => {
|
||||||
if (type === 'qq') {
|
if (type === 'qq') {
|
||||||
const arkJson = await this.core.apis.UserApi.getBuddyRecommendContactArkJson(id.toString(), '');
|
const arkJson = await this.core.apis.UserApi.getBuddyRecommendContactArkJson(id.toString(), '');
|
||||||
return this.ob11ToRawConverters.json({
|
return this.ob11ToRawConverters.json({
|
||||||
data: { data: arkJson.arkMsg },
|
data: {data: arkJson.arkMsg},
|
||||||
type: OB11MessageDataType.json
|
type: OB11MessageDataType.json
|
||||||
}, context);
|
}, context);
|
||||||
} else if (type === 'group') {
|
} else if (type === 'group') {
|
||||||
const arkJson = await this.core.apis.GroupApi.getGroupRecommendContactArkJson(id.toString());
|
const arkJson = await this.core.apis.GroupApi.getGroupRecommendContactArkJson(id.toString());
|
||||||
return this.ob11ToRawConverters.json({
|
return this.ob11ToRawConverters.json({
|
||||||
data: { data: arkJson.arkJson },
|
data: {data: arkJson.arkJson},
|
||||||
type: OB11MessageDataType.json
|
type: OB11MessageDataType.json
|
||||||
}, context);
|
}, context);
|
||||||
}
|
}
|
||||||
@@ -692,7 +707,10 @@ export class OneBotMsgApi {
|
|||||||
if (grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
|
if (grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
|
||||||
if (grayTipElement.jsonGrayTipElement.busiId == 1061) {
|
if (grayTipElement.jsonGrayTipElement.busiId == 1061) {
|
||||||
const PokeEvent = await this.obContext.apis.FriendApi.parsePrivatePokeEvent(grayTipElement, Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid)));
|
const PokeEvent = await this.obContext.apis.FriendApi.parsePrivatePokeEvent(grayTipElement, Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid)));
|
||||||
if (PokeEvent) { return PokeEvent; };
|
if (PokeEvent) {
|
||||||
|
return PokeEvent;
|
||||||
|
}
|
||||||
|
;
|
||||||
} else if (grayTipElement.jsonGrayTipElement.busiId == 19324 && msg.peerUid !== '') {
|
} else if (grayTipElement.jsonGrayTipElement.busiId == 19324 && msg.peerUid !== '') {
|
||||||
return new OB11FriendAddNoticeEvent(this.core, Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid)));
|
return new OB11FriendAddNoticeEvent(this.core, Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid)));
|
||||||
}
|
}
|
||||||
@@ -849,7 +867,7 @@ export class OneBotMsgApi {
|
|||||||
element[key],
|
element[key],
|
||||||
msg,
|
msg,
|
||||||
element,
|
element,
|
||||||
{ parseMultMsg }
|
{parseMultMsg}
|
||||||
);
|
);
|
||||||
if (key === 'faceElement' && !parsedElement) {
|
if (key === 'faceElement' && !parsedElement) {
|
||||||
return null;
|
return null;
|
||||||
@@ -902,13 +920,13 @@ export class OneBotMsgApi {
|
|||||||
) => Promise<SendMessageElement | undefined>;
|
) => Promise<SendMessageElement | undefined>;
|
||||||
const callResult = converter(
|
const callResult = converter(
|
||||||
sendMsg,
|
sendMsg,
|
||||||
{ peer, deleteAfterSentFiles },
|
{peer, deleteAfterSentFiles},
|
||||||
)?.catch(undefined);
|
)?.catch(undefined);
|
||||||
callResultList.push(callResult);
|
callResultList.push(callResult);
|
||||||
}
|
}
|
||||||
const ret = await Promise.all(callResultList);
|
const ret = await Promise.all(callResultList);
|
||||||
const sendElements: SendMessageElement[] = ret.filter(ele => !!ele);
|
const sendElements: SendMessageElement[] = ret.filter(ele => !!ele);
|
||||||
return { sendElements, deleteAfterSentFiles };
|
return {sendElements, deleteAfterSentFiles};
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendMsgWithOb11UniqueId(peer: Peer, sendElements: SendMessageElement[], deleteAfterSentFiles: string[]) {
|
async sendMsgWithOb11UniqueId(peer: Peer, sendElements: SendMessageElement[], deleteAfterSentFiles: string[]) {
|
||||||
@@ -970,8 +988,8 @@ export class OneBotMsgApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async handleOb11FileLikeMessage(
|
private async handleOb11FileLikeMessage(
|
||||||
{ data: inputdata }: OB11MessageFileBase,
|
{data: inputdata}: OB11MessageFileBase,
|
||||||
{ deleteAfterSentFiles }: SendMessageContext
|
{deleteAfterSentFiles}: SendMessageContext
|
||||||
) {
|
) {
|
||||||
let realUri = [inputdata.url, inputdata.file, inputdata.path].find(uri => uri && uri.trim()) ?? '';
|
let realUri = [inputdata.url, inputdata.file, inputdata.path].find(uri => uri && uri.trim()) ?? '';
|
||||||
if (!realUri) {
|
if (!realUri) {
|
||||||
@@ -980,28 +998,29 @@ export class OneBotMsgApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const downloadFile = async (uri: string) => {
|
const downloadFile = async (uri: string) => {
|
||||||
const { path, fileName, errMsg, success } = await uriToLocalFile(this.core.NapCatTempPath, uri);
|
const {path, fileName, errMsg, success} = await uriToLocalFile(this.core.NapCatTempPath, uri);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
this.core.context.logger.logError('文件下载失败', errMsg);
|
this.core.context.logger.logError('文件下载失败', errMsg);
|
||||||
throw new Error('文件下载失败: ' + errMsg);
|
throw new Error('文件下载失败: ' + errMsg);
|
||||||
}
|
}
|
||||||
return { path, fileName };
|
return {path, fileName};
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const { path, fileName } = await downloadFile(realUri);
|
const {path, fileName} = await downloadFile(realUri);
|
||||||
deleteAfterSentFiles.push(path);
|
deleteAfterSentFiles.push(path);
|
||||||
return { path, fileName: inputdata.name ?? fileName };
|
return {path, fileName: inputdata.name ?? fileName};
|
||||||
} catch {
|
} catch {
|
||||||
realUri = await this.handleObfuckName(realUri);
|
realUri = await this.handleObfuckName(realUri);
|
||||||
const { path, fileName } = await downloadFile(realUri);
|
const {path, fileName} = await downloadFile(realUri);
|
||||||
deleteAfterSentFiles.push(path);
|
deleteAfterSentFiles.push(path);
|
||||||
return { path, fileName: inputdata.name ?? fileName };
|
return {path, fileName: inputdata.name ?? fileName};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleObfuckName(name: string) {
|
async handleObfuckName(name: string) {
|
||||||
const contextMsgFile = FileNapCatOneBotUUID.decode(name);
|
const contextMsgFile = FileNapCatOneBotUUID.decode(name);
|
||||||
if (contextMsgFile && contextMsgFile.msgId && contextMsgFile.elementId) {
|
if (contextMsgFile && contextMsgFile.msgId && contextMsgFile.elementId) {
|
||||||
const { peer, msgId, elementId } = contextMsgFile;
|
const {peer, msgId, elementId} = contextMsgFile;
|
||||||
const rawMessage = (await this.core.apis.MsgApi.getMsgsByMsgId(peer, [msgId]))?.msgList.find(msg => msg.msgId === msgId);
|
const rawMessage = (await this.core.apis.MsgApi.getMsgsByMsgId(peer, [msgId]))?.msgList.find(msg => msg.msgId === msgId);
|
||||||
const mixElement = rawMessage?.elements.find(e => e.elementId === elementId);
|
const mixElement = rawMessage?.elements.find(e => e.elementId === elementId);
|
||||||
const mixElementInner = mixElement?.videoElement ?? mixElement?.fileElement ?? mixElement?.pttElement ?? mixElement?.picElement;
|
const mixElementInner = mixElement?.videoElement ?? mixElement?.fileElement ?? mixElement?.pttElement ?? mixElement?.picElement;
|
||||||
@@ -1009,18 +1028,19 @@ export class OneBotMsgApi {
|
|||||||
let url = '';
|
let url = '';
|
||||||
if (mixElement?.picElement && rawMessage) {
|
if (mixElement?.picElement && rawMessage) {
|
||||||
const tempData =
|
const tempData =
|
||||||
await this.obContext.apis.MsgApi.rawToOb11Converters.picElement?.(mixElement?.picElement, rawMessage, mixElement, { parseMultMsg: false }) as OB11MessageImage | undefined;
|
await this.obContext.apis.MsgApi.rawToOb11Converters.picElement?.(mixElement?.picElement, rawMessage, mixElement, {parseMultMsg: false}) as OB11MessageImage | undefined;
|
||||||
url = tempData?.data.url ?? '';
|
url = tempData?.data.url ?? '';
|
||||||
}
|
}
|
||||||
if (mixElement?.videoElement && rawMessage) {
|
if (mixElement?.videoElement && rawMessage) {
|
||||||
const tempData =
|
const tempData =
|
||||||
await this.obContext.apis.MsgApi.rawToOb11Converters.videoElement?.(mixElement?.videoElement, rawMessage, mixElement, { parseMultMsg: false }) as OB11MessageVideo | undefined;
|
await this.obContext.apis.MsgApi.rawToOb11Converters.videoElement?.(mixElement?.videoElement, rawMessage, mixElement, {parseMultMsg: false}) as OB11MessageVideo | undefined;
|
||||||
url = tempData?.data.url ?? '';
|
url = tempData?.data.url ?? '';
|
||||||
}
|
}
|
||||||
return url !== '' ? url : await this.core.apis.FileApi.downloadMedia(msgId, peer.chatType, peer.peerUid, elementId, '', '');
|
return url !== '' ? url : await this.core.apis.FileApi.downloadMedia(msgId, peer.chatType, peer.peerUid, elementId, '', '');
|
||||||
}
|
}
|
||||||
throw new Error('文件名解析失败');
|
throw new Error('文件名解析失败');
|
||||||
}
|
}
|
||||||
|
|
||||||
groupChangDecreseType2String(type: number): GroupDecreaseSubType {
|
groupChangDecreseType2String(type: number): GroupDecreaseSubType {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 130:
|
case 130:
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ConfigBase } from '@/common/config-base';
|
import { ConfigBase } from '@/common/config-base';
|
||||||
import { NapCatCore } from '@/core';
|
import type { NapCatCore } from '@/core';
|
||||||
import { OneBotConfig } from './config';
|
import { OneBotConfig } from './config';
|
||||||
import { AnySchema } from 'ajv';
|
import { AnySchema } from 'ajv';
|
||||||
|
|
||||||
|
@@ -1,13 +1,12 @@
|
|||||||
import { RequestHandler } from 'express';
|
import { RequestHandler } from 'express';
|
||||||
import { existsSync, readFileSync } from 'node:fs';
|
import { existsSync, readFileSync } from 'node:fs';
|
||||||
import { resolve } from 'node:path';
|
import { resolve } from 'node:path';
|
||||||
|
import { loadConfig, OneBotConfig } from '@/onebot/config/config';
|
||||||
import { OneBotConfig } from '@/onebot/config/config';
|
|
||||||
|
|
||||||
import { webUiPathWrapper } from '@/webui';
|
import { webUiPathWrapper } from '@/webui';
|
||||||
import { WebUiDataRuntime } from '@webapi/helper/Data';
|
import { WebUiDataRuntime } from '@webapi/helper/Data';
|
||||||
import { sendError, sendSuccess } from '@webapi/utils/response';
|
import { sendError, sendSuccess } from '@webapi/utils/response';
|
||||||
import { isEmpty } from '@webapi/utils/check';
|
import { isEmpty } from '@webapi/utils/check';
|
||||||
|
import json5 from 'json5';
|
||||||
|
|
||||||
// 获取OneBot11配置
|
// 获取OneBot11配置
|
||||||
export const OB11GetConfigHandler: RequestHandler = (_, res) => {
|
export const OB11GetConfigHandler: RequestHandler = (_, res) => {
|
||||||
@@ -19,16 +18,16 @@ export const OB11GetConfigHandler: RequestHandler = (_, res) => {
|
|||||||
}
|
}
|
||||||
// 获取登录的QQ号
|
// 获取登录的QQ号
|
||||||
const uin = WebUiDataRuntime.getQQLoginUin();
|
const uin = WebUiDataRuntime.getQQLoginUin();
|
||||||
// 读取配置文件
|
// 读取配置文件路径
|
||||||
const configFilePath = resolve(webUiPathWrapper.configPath, `./onebot11_${uin}.json`);
|
const configFilePath = resolve(webUiPathWrapper.configPath, `./onebot11_${uin}.json`);
|
||||||
// 尝试解析配置文件
|
// 尝试解析配置文件
|
||||||
try {
|
try {
|
||||||
// 读取配置文件
|
// 读取配置文件内容
|
||||||
const data = JSON.parse(
|
const configFileContent = existsSync(configFilePath)
|
||||||
existsSync(configFilePath)
|
? readFileSync(configFilePath).toString()
|
||||||
? readFileSync(configFilePath).toString()
|
: readFileSync(resolve(webUiPathWrapper.configPath, './onebot11.json')).toString();
|
||||||
: readFileSync(resolve(webUiPathWrapper.configPath, './onebot11.json')).toString()
|
// 解析配置文件并加载配置
|
||||||
) as OneBotConfig;
|
const data = loadConfig(json5.parse(configFileContent)) as OneBotConfig;
|
||||||
// 返回配置文件
|
// 返回配置文件
|
||||||
return sendSuccess(res, data);
|
return sendSuccess(res, data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -50,9 +49,12 @@ export const OB11SetConfigHandler: RequestHandler = async (req, res) => {
|
|||||||
}
|
}
|
||||||
// 写入配置
|
// 写入配置
|
||||||
try {
|
try {
|
||||||
await WebUiDataRuntime.setOB11Config(JSON.parse(req.body.config));
|
// 解析并加载配置
|
||||||
|
const config = loadConfig(json5.parse(req.body.config)) as OneBotConfig;
|
||||||
|
// 写入配置
|
||||||
|
await WebUiDataRuntime.setOB11Config(config);
|
||||||
return sendSuccess(res, null);
|
return sendSuccess(res, null);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return sendError(res, 'Error: ' + e);
|
return sendError(res, 'Error: ' + e);
|
||||||
}
|
}
|
||||||
};
|
};
|
Reference in New Issue
Block a user