feat: 完善弹幕AI
This commit is contained in:
parent
d723fc6cda
commit
bfcc4a5ce3
@ -128,7 +128,7 @@ export default function Controls(): JSX.Element {
|
|||||||
className="btn join-item"
|
className="btn join-item"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
query({
|
query({
|
||||||
name: "红豆",
|
name: "音乐弹幕",
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
// eslint-disable-next-line no-console -- temp
|
// eslint-disable-next-line no-console -- temp
|
||||||
@ -157,7 +157,7 @@ export default function Controls(): JSX.Element {
|
|||||||
>
|
>
|
||||||
<div className="text-center">当前延迟 {offset / 1000} 秒</div>
|
<div className="text-center">当前延迟 {offset / 1000} 秒</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full max-h-40 overflow-auto">
|
<div className="w-full h-[20vh] overflow-auto">
|
||||||
<ul className="timeline timeline-vertical">
|
<ul className="timeline timeline-vertical">
|
||||||
{history.map((item, i) => (
|
{history.map((item, i) => (
|
||||||
<li key={i}>
|
<li key={i}>
|
||||||
|
@ -3,10 +3,11 @@ import { useState, useEffect, useCallback, useRef } from "react";
|
|||||||
import useWebSocket, { ReadyState } from "react-use-websocket";
|
import useWebSocket, { ReadyState } from "react-use-websocket";
|
||||||
import MessageDispatcher from "./message";
|
import MessageDispatcher from "./message";
|
||||||
import { Token } from "@/lib/types";
|
import { Token } from "@/lib/types";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
const MOMO_IM_URL = "wss://live-ws.immomo.com/ws/im";
|
const MOMO_IM_URL = "wss://live-ws.immomo.com/ws/im";
|
||||||
|
|
||||||
export const Danmu: React.FC<{ token: Token }> = ({ token }) => {
|
export default function Danmu({ token }: { token: Token }) {
|
||||||
const [messageHistory, setMessageHistory] = useState<MessageEvent<any>[]>([]);
|
const [messageHistory, setMessageHistory] = useState<MessageEvent<any>[]>([]);
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@ -52,15 +53,28 @@ export const Danmu: React.FC<{ token: Token }> = ({ token }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (lastMessage !== null) {
|
if (lastMessage !== null) {
|
||||||
if (lastMessage.data) {
|
if (lastMessage.data) {
|
||||||
|
const current = dayjs().format("HH:mm:ss");
|
||||||
const message = JSON.parse(lastMessage.data);
|
const message = JSON.parse(lastMessage.data);
|
||||||
if (message.type === "pong") {
|
if (message.type === "pong") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(message);
|
if (message.type === "group") {
|
||||||
setMessageHistory((prev) => prev.concat(message));
|
message.groups.forEach((group: any) => {
|
||||||
if (ref.current) {
|
group.current = current;
|
||||||
|
setMessageHistory((prev) => prev.concat(group));
|
||||||
|
setTimeout(() => {
|
||||||
|
if (ref.current) {
|
||||||
|
ref.current.scrollTop = ref.current.scrollHeight;
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.current = current;
|
||||||
|
setMessageHistory((prev) => prev.concat(message));
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
ref.current.scrollTop = ref.current.scrollHeight;
|
if (ref.current) {
|
||||||
|
ref.current.scrollTop = ref.current.scrollHeight;
|
||||||
|
}
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,10 +82,10 @@ export const Danmu: React.FC<{ token: Token }> = ({ token }) => {
|
|||||||
}, [lastMessage]);
|
}, [lastMessage]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-[50vh] overflow-auto" ref={ref}>
|
<div className="h-[60vh] overflow-auto" ref={ref}>
|
||||||
{messageHistory.map((message, idx) => (
|
{messageHistory.map((message, idx) => (
|
||||||
<MessageDispatcher key={idx} message={message} />
|
<MessageDispatcher key={idx} message={message} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
@ -1,21 +1,50 @@
|
|||||||
export default function MessageDispatcher({ message }: { message: any }) {
|
import dayjs from 'dayjs';
|
||||||
const messageType = message?.groups?.[0]?.units?.[0]?.type;
|
|
||||||
let nick = "wait";
|
|
||||||
let content = "wait";
|
|
||||||
|
|
||||||
if (message?.type === 'sauth_ret') {
|
export default function MessageDispatcher({ message }: { message: any }) {
|
||||||
return <div>{message?.data?.em}</div>
|
const messageType = message?.units?.[0]?.type;
|
||||||
|
let nick = "";
|
||||||
|
let content = "[暂不支持该类型消息]";
|
||||||
|
let fortuneLv = 0;
|
||||||
|
let text = '';
|
||||||
|
let productId = '';
|
||||||
|
let amount = 0;
|
||||||
|
|
||||||
|
if (message?.type === "sauth_ret") {
|
||||||
|
if (message?.data?.em !== "ok") {
|
||||||
|
return <div>{message?.data?.em}</div>;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messageType === "Message") {
|
switch (messageType) {
|
||||||
nick = message?.groups?.[0]?.nick;
|
case "Message":
|
||||||
content = message?.groups?.[0]?.units?.[0]?.["Msg.Message.data"]?.text;
|
nick = message?.nick;
|
||||||
|
content = message?.units?.[0]?.["Msg.Message.data"]?.text;
|
||||||
|
break;
|
||||||
|
case "EnterRoom":
|
||||||
|
text = message?.units?.[0]?.["Set.EnterRoom.data"]?.text;
|
||||||
|
fortuneLv = message?.units?.[0]?.["Set.EnterRoom.data"]?.fortune_lv;
|
||||||
|
content = `有新人:${text} lv: ${fortuneLv}`;
|
||||||
|
break;
|
||||||
|
case "Version":
|
||||||
|
// TODO 解析
|
||||||
|
return null
|
||||||
|
case "GiftV3":
|
||||||
|
nick = message?.nick;
|
||||||
|
text = message?.['units']?.[0]?.['GiftV3.giftV3']?.['text']
|
||||||
|
productId = message?.['units']?.[0]?.['GiftV3.giftV3']?.['productId']
|
||||||
|
amount = message?.['units']?.[0]?.['GiftV3.giftV3']?.['weight']
|
||||||
|
content = `${nick} 送出礼物,内容为: ${text},产品ID为: ${productId}, 价值:${amount}`
|
||||||
|
nick = ''
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log("debug....", messageType, message);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="chat chat-start">
|
<div className="p-1 text-xs">
|
||||||
<div className="chat-bubble">
|
[{message.current}]{nick}{nick ? ': ' : ''}{content}
|
||||||
{nick}: {content}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,56 @@
|
|||||||
import { getToken } from "@/lib/actions/token";
|
import { getToken } from "@/lib/actions/token";
|
||||||
import Controls from "./controls";
|
import Controls from "./controls";
|
||||||
import { Danmu } from "./danmu";
|
import Danmu from "./danmu";
|
||||||
|
import { listLives } from "@/lib/actions/live";
|
||||||
|
import { listSongs } from "../songs/actions";
|
||||||
|
|
||||||
export default async function Page() {
|
export default async function Page() {
|
||||||
const token = await getToken();
|
const token = await getToken();
|
||||||
|
const lives = await listLives();
|
||||||
|
const songs = await listSongs();
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col md:flex-row">
|
<div className="flex flex-col md:flex-row">
|
||||||
<section className="basis-1/3 card">
|
<section className="basis-1/3 card">
|
||||||
<div className="card-body max-h-[50vh] overflow-auto">直播间列表</div>
|
<div className="h-[50vh] md:h-[80vh] overflow-auto card bg-base-100 card-bordered m-8">
|
||||||
|
<div className="card-body">
|
||||||
|
<div className="card-title">直播间列表</div>
|
||||||
|
<ul>
|
||||||
|
{lives.map((live, i) => (
|
||||||
|
<li key={i}>
|
||||||
|
<label>
|
||||||
|
{live.name} - {live.room_id}
|
||||||
|
<input
|
||||||
|
className="raido radio-primary"
|
||||||
|
type="radio"
|
||||||
|
name="live"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section className="basis-1/3 card">
|
<section className="basis-1/3 card">
|
||||||
<div className="card-body max-h-[50vh] overflow-auto">歌曲清单</div>
|
<div className="h-[50vh] md:h-[80vh] overflow-auto card bg-base-100 card-bordered m-8">
|
||||||
|
<div className="card-body">
|
||||||
|
<div className="card-title">收藏的歌曲</div>
|
||||||
|
<ul>
|
||||||
|
{songs.map((song, i) => (
|
||||||
|
<li key={i}>
|
||||||
|
<label>
|
||||||
|
{song.name} - {song.artist}
|
||||||
|
<input
|
||||||
|
className="raido radio-primary"
|
||||||
|
type="radio"
|
||||||
|
name="song"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section className="basis-1/3 card">
|
<section className="basis-1/3 card">
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
|
26
lib/actions/live.ts
Normal file
26
lib/actions/live.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
"use server";
|
||||||
|
|
||||||
|
import { cookies } from "next/headers";
|
||||||
|
|
||||||
|
export async function listLives(): Promise<any[]> {
|
||||||
|
const token = cookies().get("token");
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.append("pageSize", "99999");
|
||||||
|
const lives = await fetch(
|
||||||
|
"https://tachy.daoyoucloud.com/api/lives:list?" + params.toString(),
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"X-App": "danmu-sim",
|
||||||
|
"X-Authenticator": "basic",
|
||||||
|
authorization: "Bearer " + token?.value,
|
||||||
|
"content-type": "application/json",
|
||||||
|
},
|
||||||
|
next: {
|
||||||
|
tags: ["lives"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const result = await lives.json();
|
||||||
|
return result?.data ?? [];
|
||||||
|
}
|
@ -10,6 +10,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.6.8",
|
"axios": "^1.6.8",
|
||||||
|
"dayjs": "^1.11.10",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
"next": "14.1.4",
|
"next": "14.1.4",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
@ -8,6 +8,9 @@ dependencies:
|
|||||||
axios:
|
axios:
|
||||||
specifier: ^1.6.8
|
specifier: ^1.6.8
|
||||||
version: 1.6.8
|
version: 1.6.8
|
||||||
|
dayjs:
|
||||||
|
specifier: ^1.11.10
|
||||||
|
version: 1.11.10
|
||||||
fuse.js:
|
fuse.js:
|
||||||
specifier: ^7.0.0
|
specifier: ^7.0.0
|
||||||
version: 7.0.0
|
version: 7.0.0
|
||||||
@ -826,6 +829,10 @@ packages:
|
|||||||
is-data-view: 1.0.1
|
is-data-view: 1.0.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/dayjs@1.11.10:
|
||||||
|
resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/debug@3.2.7:
|
/debug@3.2.7:
|
||||||
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
|
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
Loading…
Reference in New Issue
Block a user