feat: 完善弹幕AI

This commit is contained in:
sealday 2024-04-07 03:23:34 +08:00
parent d723fc6cda
commit bfcc4a5ce3
7 changed files with 142 additions and 25 deletions

View File

@ -128,7 +128,7 @@ export default function Controls(): JSX.Element {
className="btn join-item"
onClick={() => {
query({
name: "红豆",
name: "音乐弹幕",
})
.then((result) => {
// 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>
<div className="w-full max-h-40 overflow-auto">
<div className="w-full h-[20vh] overflow-auto">
<ul className="timeline timeline-vertical">
{history.map((item, i) => (
<li key={i}>

View File

@ -3,10 +3,11 @@ import { useState, useEffect, useCallback, useRef } from "react";
import useWebSocket, { ReadyState } from "react-use-websocket";
import MessageDispatcher from "./message";
import { Token } from "@/lib/types";
import dayjs from "dayjs";
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 ref = useRef<HTMLDivElement>(null);
@ -52,15 +53,28 @@ export const Danmu: React.FC<{ token: Token }> = ({ token }) => {
useEffect(() => {
if (lastMessage !== null) {
if (lastMessage.data) {
const current = dayjs().format("HH:mm:ss");
const message = JSON.parse(lastMessage.data);
if (message.type === "pong") {
return;
}
console.log(message);
setMessageHistory((prev) => prev.concat(message));
if (ref.current) {
if (message.type === "group") {
message.groups.forEach((group: any) => {
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(() => {
ref.current.scrollTop = ref.current.scrollHeight;
if (ref.current) {
ref.current.scrollTop = ref.current.scrollHeight;
}
}, 0);
}
}
@ -68,10 +82,10 @@ export const Danmu: React.FC<{ token: Token }> = ({ token }) => {
}, [lastMessage]);
return (
<div className="h-[50vh] overflow-auto" ref={ref}>
<div className="h-[60vh] overflow-auto" ref={ref}>
{messageHistory.map((message, idx) => (
<MessageDispatcher key={idx} message={message} />
))}
</div>
);
};
}

View File

@ -1,21 +1,50 @@
export default function MessageDispatcher({ message }: { message: any }) {
const messageType = message?.groups?.[0]?.units?.[0]?.type;
let nick = "wait";
let content = "wait";
import dayjs from 'dayjs';
if (message?.type === 'sauth_ret') {
return <div>{message?.data?.em}</div>
export default function MessageDispatcher({ message }: { message: any }) {
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") {
nick = message?.groups?.[0]?.nick;
content = message?.groups?.[0]?.units?.[0]?.["Msg.Message.data"]?.text;
switch (messageType) {
case "Message":
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 (
<div className="chat chat-start">
<div className="chat-bubble">
{nick}: {content}
<div className="p-1 text-xs">
[{message.current}]{nick}{nick ? ': ' : ''}{content}
</div>
</div>
);
}

View File

@ -1,16 +1,56 @@
import { getToken } from "@/lib/actions/token";
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() {
const token = await getToken();
const lives = await listLives();
const songs = await listSongs();
return (
<div className="flex flex-col md:flex-row">
<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 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 className="basis-1/3 card">
<div className="card-body">

26
lib/actions/live.ts Normal file
View 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 ?? [];
}

View File

@ -10,6 +10,7 @@
},
"dependencies": {
"axios": "^1.6.8",
"dayjs": "^1.11.10",
"fuse.js": "^7.0.0",
"next": "14.1.4",
"react": "^18.2.0",

View File

@ -8,6 +8,9 @@ dependencies:
axios:
specifier: ^1.6.8
version: 1.6.8
dayjs:
specifier: ^1.11.10
version: 1.11.10
fuse.js:
specifier: ^7.0.0
version: 7.0.0
@ -826,6 +829,10 @@ packages:
is-data-view: 1.0.1
dev: true
/dayjs@1.11.10:
resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
dev: false
/debug@3.2.7:
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
peerDependencies: