消息框架 消息前端完善,消息黑名单 #40

This commit is contained in:
userA
2023-10-20 16:51:00 +08:00
parent 77cd479edf
commit baa844c5f0
20 changed files with 2881 additions and 4234 deletions
@@ -46,7 +46,7 @@ public class BarrageEventCenter extends SpringBootPlugin {
public boolean event(BarrageEvent event){
this.info(String.format("accept a event:%s", event.getLiver()));
this.info(String.format("accept %s barrage event", event.getLiver()));
List<Barrage> barrages = event.getBarrages();
if(barrages==null)return false;
String filePath = event.getBarrageFilePath();
@@ -71,6 +71,7 @@ public class BarrageScoreCurvePlugin extends SpringBootPlugin {
AbstractSplitStrategy splitStrategy = SplitStrategyFactory.build(splitType, scoreStrategy, barrages, duration,liverKeywordMap);
if(splitStrategy!=null){
List<BarragePoint> split = splitStrategy.split();
this.info(String.format("%s主播弹幕曲线生成成功,文件名:%s", event.getLiver(),event.getFileName()),true);
barragePointMap.put(path,split==null?new ArrayList<>():split);
return split;
}
@@ -56,6 +56,7 @@ public class ScheduleTimeHandler implements InstantSlicingHandler {
private void monitorTask(){
long now = System.currentTimeMillis();
InstantSlicingPlugin plugin = InitPluginRegister.getPlugin(PluginName.INSTANT_SLICING_PLUGIN, InstantSlicingPlugin.class);
for (Map.Entry<String, ReptileTask> entry : taskTimeMap.entrySet()) {
ReptileTask task = entry.getValue();
try {
@@ -64,6 +65,7 @@ public class ScheduleTimeHandler implements InstantSlicingHandler {
Long time = TimeUtil.getTimeNaos(entry.getValue().getStartTime());
Integer times = taskSplitTimes.get(task.getTaskId()).get();
if(now - time >= splitTime*(times+1) || status == TaskStatus.Finish){
plugin.info("即时切片",String.format("爬虫任务 %s 触发即时切片,正在切片中...",task.getTaskId()),true);
taskSplitTimes.get(task.getTaskId()).incrementAndGet();
Object live = task.getRequest().getParam();
if (live instanceof Live) {
@@ -101,6 +103,7 @@ public class ScheduleTimeHandler implements InstantSlicingHandler {
}
}
}catch (Exception e){
plugin.error("即时切片失败",String.format("爬虫任务 %s 即时切片失败,原因:%s",task.getTaskId(),ExceptionUtil.getCause(e)),true);
ChopperLogFactory.getLogger(LoggerType.Barrage).error("Error:{}", ExceptionUtil.getCause(e));
}
}
@@ -8,4 +8,9 @@ public interface NoticeHorn {
void warn(String msg,boolean isNotice);
void info(String title,String msg,boolean isNotice);
void error(String title,String msg,boolean isNotice);
void warn(String title,String msg,boolean isNotice);
}
@@ -10,6 +10,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -24,6 +26,8 @@ public class NoticePlugin extends SpringBootPlugin {
@Resource
private NoticeHandler handler;
private Set<String> blackList;
private ExecutorService pool;
@Override
@@ -31,13 +35,16 @@ public class NoticePlugin extends SpringBootPlugin {
if(handler==null){
throw new RuntimeException("Notice Handler is null");
}
blackList = new TreeSet<>();
pool = Executors.newSingleThreadExecutor();
return super.init();
}
public void notice(Notice notice) {
pool.submit(()->{
handler.doHandler(notice);
if(!isBlack(notice.getFrom())){
handler.doHandler(notice);
}
});
}
@@ -49,4 +56,11 @@ public class NoticePlugin extends SpringBootPlugin {
notice(new Notice(type,form,content));
}
public void addBlack(String from){
blackList.add(from);
}
public boolean isBlack(String from){
return blackList.contains(from);
}
}
@@ -96,33 +96,48 @@ public abstract class CommonPlugin implements ChopperBotPlugin, NoticeHorn {
@Override
public void info(String msg, boolean isNotice) {
this.info(msg);
if(isNotice){
Optional.ofNullable(InitPluginRegister.getPlugin(PluginName.NOTICE_PLUGIN, NoticePlugin.class))
.ifPresent(plugin->{
plugin.notice(new Notice().info().title(pluginName).from(pluginName).content(msg));
});
}
info(pluginName,msg,isNotice);
}
@Override
public void error(String msg, boolean isNotice) {
this.error(msg);
error(pluginName,msg,isNotice);
}
@Override
public void warn(String msg, boolean isNotice) {
warn(pluginName,msg,isNotice);
}
@Override
public void info(String title, String msg, boolean isNotice) {
this.info(msg);
if(isNotice){
Optional.ofNullable(InitPluginRegister.getPlugin(PluginName.NOTICE_PLUGIN, NoticePlugin.class))
.ifPresent(plugin->{
plugin.notice( new Notice().error().title(pluginName).from(pluginName).content(msg));
plugin.notice(new Notice().info().title(title).from(pluginName).content(msg));
});
}
}
@Override
public void warn(String msg, boolean isNotice) {
public void error(String title, String msg, boolean isNotice) {
this.error(msg);
if(isNotice){
Optional.ofNullable(InitPluginRegister.getPlugin(PluginName.NOTICE_PLUGIN, NoticePlugin.class))
.ifPresent(plugin->{
plugin.notice(new Notice().error().title(title).from(pluginName).content(msg));
});
}
}
@Override
public void warn(String title, String msg, boolean isNotice) {
this.warn(msg);
if(isNotice){
Optional.ofNullable(InitPluginRegister.getPlugin(PluginName.NOTICE_PLUGIN, NoticePlugin.class))
.ifPresent(plugin->{
plugin.notice(new Notice().warn().title(pluginName).from(pluginName).content(msg));
plugin.notice(new Notice().warn().title(title).from(pluginName).content(msg));
});
}
}
@@ -0,0 +1,43 @@
package org.example.core.taskcenter.observer;
import org.example.constpool.ConstGroup;
import org.example.core.taskcenter.task.ReptileTask;
import org.springframework.stereotype.Component;
import java.time.temporal.ValueRange;
import java.util.List;
/**
* @author Genius
* @date 2023/10/20 14:35
**/
@Component
public class TaskNoticeObserver extends AbstractTaskCenterObserver{
private List<String> noticeList = List.of(ConstGroup.LIVE_ONLINE,ConstGroup.BARRAGE_ONLINE);
@Override
public void onAlready(ReptileTask task) {
}
@Override
public void onRunning(ReptileTask task) {
taskCenter.info(String.format("%s 任务爬取开始", task.getTaskId()),true);
}
@Override
public void onFinish(ReptileTask task) {
taskCenter.info(String.format("%s 任务爬取结束", task.getTaskId()),true);
}
@Override
public void send() {
}
@Override
public boolean isMe(String taskId) {
return noticeList.stream().anyMatch(taskId::startsWith);
}
}
@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@@ -18,6 +19,7 @@ import java.util.List;
//
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("follow_dog")
public class FollowDog {
public final static String ALL_LIVES = "all";
@@ -93,7 +93,7 @@ public class HeatRecommendation extends SpringBootPlugin {
try {
if(platform!=null){
List<FollowDog> followDogList;
this.info(String.format("%s Hotspot event detected.", platform),true);
this.info(String.format("%s Hotspot event detected.", platform));
if(platformFollowDogMap.containsKey(platform)
&&(followDogList=platformFollowDogMap.get(platform)).size()>0){
//发送给爬虫队列
@@ -115,7 +115,7 @@ public class HeatRecommendation extends SpringBootPlugin {
}
for (Live live : needRecommend(tempLives, getBanList(followDog.getBanLiver()), followDog.getTop())) {
String tempPlatform = live.getPlatform();
this.info(String.format("推荐请求:平台 %s,分区 %s,直播间 %s,主播 %s",
this.info("主播推荐",String.format("平台 %s,分区 %s,直播间 %s,主播 %s",
tempPlatform,live.getModuleName(),live.getLiveId(),live.getLiver()),true);
String checkGroup = CreeperGroupCenter.getGroupName(platform, ConstGroup.LIVER_CHECKER);
String liveGroup = CreeperGroupCenter.getGroupName(platform,ConstGroup.LIVE_ONLINE);
@@ -166,7 +166,7 @@ public class HeatRecommendation extends SpringBootPlugin {
public boolean updateFollowDog(FollowDog dog){
List<FollowDog> followDogs = platformFollowDogMap.get(dog.getPlatform());
if(followDogs!=null){
if(followDogs!=null&&!followDogs.isEmpty()){
followDogs.removeIf(dog1 -> {return dog1.getDogId().equals(dog.getDogId());});
followDogs.add(dog);
}
@@ -56,6 +56,7 @@ public class VideoSectionWorkShop extends SpringGuardPlugin {
String newVideoName = FileNameBuilder.buildVideoFileNameNoSuffix(liver, date)+"_section("+ FileUtil.convertTimeToFile(startTime) +"-"+FileUtil.convertTimeToFile(endTime)+")."+split[1];
String newPath = Paths.get(root,newVideoName).toString();
if (VideoUtil.cutVideoByFFMpeg(oldPath,newPath,startTime,endTime)) {
this.info("切片生成", String.format("产生切片文件%s 主播:%s", newVideoName,liver),true);
VideoSection videoSection = new VideoSection(newVideoName,request.getTag(),request.getLiver(),request.getPlatform());
}
}
+2 -2
View File
@@ -4,11 +4,11 @@
"GuardNum":10
},
"LiverFollower":{
"checkTime":120000,
"checkTime":60000,
"focusRecord":1,
"focusLive":0,
"focusBarrage":1
}
},
"updateTime":"2023-10-15 23:32:10"
"updateTime":"2023-10-20 15:34:44"
}
+3 -2
View File
@@ -32,8 +32,9 @@
"VideoPush":true,
"HotConfig":true,
"TaskCenter":true,
"AccountManager":true,
"CreeperConfig":true
}
},
"updateTime":"2023-10-19 21:31:08"
}
"updateTime":"2023-10-20 15:15:23"
}
+2639 -4154
View File
File diff suppressed because it is too large Load Diff
@@ -4,58 +4,30 @@
* @Description:
-->
<script setup lang="ts">
import {Notice} from "@/views/app/email/NoticeTypes"
import WebSocketClient from "@/utils/ws/webSocket";
import {useNoticeStore} from "@/views/app/email/noticeStore";
const noticeStore = useNoticeStore()
const unCheckNotice = ref<Notice[]>(noticeStore.getUnConfirmNoticeBox)
const webSocket = new WebSocketClient(()=>{
webSocket.sendMsg(webSocket.encodeMsg("notice","666"))
},(event)=>{
let dataMap = webSocket.decodeMsg(event.data)
console.log(dataMap)
let data = JSON.parse(dataMap.get("data"))
const notice: Notice = {...data,ago:'',color:'',icon:'',confirm:false };
noticeStore.sendNotice(notice)
unCheckNotice.value = noticeStore.getUnConfirmNoticeBox
});
const messages = [
{
title: "Brunch this weekend?",
color: "primary",
icon: "mdi-account-circle",
subtitle:
"Lorem ipsum dolor sit amet consectetur, adipisicing elit. Sint, repudiandae?",
time: "3 min",
},
{
title: "Summer BBQ",
color: "success",
icon: "mdi-email-outline",
subtitle:
"Lorem ipsum dolor sit amet consectetur, adipisicing elit. Sint, repudiandae?",
time: "3 min",
},
{
title: "Oui oui",
color: "teal lighten-1",
icon: "mdi-airplane-landing",
subtitle:
"Lorem ipsum dolor sit amet consectetur, adipisicing elit. Sint, repudiandae?",
time: "4 min",
},
{
title: "Disk capacity is at maximum",
color: "teal accent-3",
icon: "mdi-server",
subtitle:
"Lorem ipsum dolor sit amet consectetur, adipisicing elit. Sint, repudiandae?",
time: "3 hr",
},
{
title: "Recipe to try",
color: "blue-grey lighten-2",
icon: "mdi-noodles",
subtitle:
"Lorem ipsum dolor sit amet consectetur, adipisicing elit. Sint, repudiandae?",
time: "8 hr",
},
];
const confirmNotice = (index:number) =>{
unCheckNotice.value.splice(index, 1);
noticeStore.confirmNotice(index);
}
</script>
<template>
@@ -65,14 +37,15 @@ const messages = [
<!-- ---------------------------------------------- -->
<template v-slot:activator="{ props }">
<v-btn icon v-bind="props" class="text-none">
<v-badge content="2" color="error">
<v-badge v-if="unCheckNotice.length!==0" :content="unCheckNotice.length" color="error">
<v-icon>mdi-bell-outline</v-icon>
</v-badge>
<v-icon v-else>mdi-bell-outline</v-icon>
</v-btn>
</template>
<v-list elevation="1" lines="three" density="compact" max-width="400">
<v-list elevation="1" lines="three" density="compact" width="400">
<v-list-subheader>Notifications</v-list-subheader>
<v-list-item v-for="(message, i) in messages" :key="i" @click="">
<v-list-item v-for="(message, i) in unCheckNotice" :key="i" @click="confirmNotice(i)">
<!-- ---------------------------------------------- -->
<!-- Prepend-->
<!-- ---------------------------------------------- -->
@@ -86,7 +59,7 @@ const messages = [
<!-- ---------------------------------------------- -->
<template v-slot:append>
<div class="full-h d-flex align-center">
<span class="text-body-2 text-grey"> {{ message.time }}</span>
<span class="text-body-2 text-grey"> {{ message.ago }}</span>
</div>
</template>
<!-- ---------------------------------------------- -->
@@ -96,7 +69,8 @@ const messages = [
<v-list-item-title class="font-weight-bold text-primary">{{
message.title
}}</v-list-item-title>
<v-list-item-subtitle>{{ message.subtitle }}</v-list-item-subtitle>
<v-list-item-subtitle><strong>来源</strong>{{ message.from }}</v-list-item-subtitle>
<v-list-item-subtitle><strong>内容</strong>{{ message.content }}</v-list-item-subtitle>
</div>
</v-list-item>
<!-- ---------------------------------------------- -->
+3
View File
@@ -8,6 +8,8 @@ import App from "./App.vue";
// Composables
import { createApp } from "vue";
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css'
import vuetify from "./plugins/vuetify";
import { VueMasonryPlugin } from "vue-masonry";
import MasonryWall from "@yeger/vue-masonry-wall";
@@ -26,6 +28,7 @@ const pinia = createPinia();
pinia.use(piniaPersist);
const app = createApp(App);
app.use(ElementPlus);
app.use(router);
app.use(PerfectScrollbar);
app.use(VueMasonryPlugin);
+23
View File
@@ -37,3 +37,26 @@ export function isoStrToNormal(isoString: string): string {
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
}
export function getTimeAgo(input: string): string {
const currentTime = new Date();
const inputTime = new Date(input);
const timeDiff = currentTime.getTime() - inputTime.getTime();
const seconds = Math.floor(timeDiff / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
if (days > 0) {
return days === 1 ? '1 day ago' : `${days} days ago`;
} else if (hours > 0) {
return hours === 1 ? '1 hr ago' : `${hours} hrs ago`;
} else if (minutes > 0) {
return minutes === 1 ? '1 min ago' : `${minutes} min ago`;
} else if (seconds > 0) {
return seconds === 1 ? '1 sec ago' : `${seconds} sec ago`;
} else {
return 'now';
}
}
@@ -0,0 +1,11 @@
export interface Notice{
title:string,
from:string,
content:string,
type:string,
time:string,
ago?:string,
icon?:string,
color?:string,
confirm?:boolean
}
@@ -0,0 +1,66 @@
import { defineStore } from "pinia";
import {Notice} from "@/views/app/email/NoticeTypes";
import {ElNotification} from "element-plus";
import {getTimeAgo} from "@/utils/timeUtils";
export const showNotice = (notice:Notice) => {
ElNotification({
title: notice.title,
message: '<p><strong>时间:</strong>'+notice.time+'</p>'+
'<p><strong>来源:</strong>'+notice.from+'</p>'+
'<p><strong>内容:</strong>'+notice.content+'</p>',
dangerouslyUseHTMLString: true,
type: notice.type
});
};
export const useNoticeStore = defineStore({
id: "notice",
state: () => ({
noticeBox: ref<Notice[]>([]),
NoticeType:{
"info":{type:"success",icon:'mdi mdi-email-alert-outline',color:'green'},
"warn":{type:"warning",icon:'mdi mdi-alert',color:'orange'},
"error":{type:"error",icon:'mdi mdi-cancel',color:'red'}
}
}),
persist: {
enabled: true,
strategies: [
{
storage: localStorage,
paths: ["notice"],
},
],
},
getters: {
getNoticeBox(){
return this.noticeBox.map(item=>{
return { ...item, ago:getTimeAgo(item.time) };
})
},
getUnConfirmNoticeBox(){
return this.noticeBox.map(item=>{
if(!item.confirm){
return { ...item, ago:getTimeAgo(item.time) };
}
})
},
},
actions: {
sendNotice(notice: Notice){
notice.ago = getTimeAgo(notice.time)
notice.color = this.NoticeType[notice.type].color
notice.icon = this.NoticeType[notice.type].icon
notice.type = this.NoticeType[notice.type].type
this.noticeBox.push(notice)
showNotice(notice)
},
confirmNotice(index:number){
this.noticeBox[index].confirm = true;
}
},
});
@@ -15,7 +15,6 @@ const props = defineProps<{
}>();
const searchKey = ref("");
const avatar = "/src/assets/images/img/Creeper.png"
const snackbar = ref(false)
@@ -111,47 +110,47 @@ const headers = [
<template v-slot:item="{ item }">
<tr>
<td class="font-weight-bold">
<CopyLabel :text="item.columns.taskId" />
<CopyLabel :text="item.taskId" />
</td>
<td>
<v-avatar size="30">
<img :src="avatar" alt="alt" />
</v-avatar>
</td>
<td>{{ item.columns.startTime=="nil"?"未开始":item.columns.startTime }}</td>
<td>{{ item.startTime==="nil"?"未开始":item.startTime }}</td>
<td>{{ item.columns.endTime=="nil"?"未结束":item.columns.endTime }}</td>
<td>{{ item.endTime==="nil"?"未结束":item.endTime }}</td>
<td class="text-center">
<v-chip
size="small"
:color="getLabelColor(item.columns.status)"
:color="getLabelColor(item.status)"
class="font-weight-bold"
>
<v-icon
start
:icon="getLabelIcon(item.columns.status)"
:icon="getLabelIcon(item.status)"
></v-icon>
{{
item.columns.status
item.status
}}</v-chip
>
</td>
<td>
<v-btn @click="startMonitor(item.columns)">
<v-icon v-if="item.columns.hasMonitor===1" color="success">mdi mdi-monitor</v-icon>
<v-btn @click="startMonitor(item)">
<v-icon v-if="item.hasMonitor===1" color="success">mdi mdi-monitor</v-icon>
<v-icon v-else color="warning">mdi mdi-monitor</v-icon>
</v-btn>
</td>
<td>
<v-btn size="x-small" rounded="xl" elevation="8" prepend-icon="mdi mdi-pause" @click="stopTask(item.columns)">
<v-btn size="x-small" rounded="xl" elevation="8" prepend-icon="mdi mdi-pause" @click="stopTask(item)">
Pause
</v-btn>
<v-snackbar
v-model="snackbar"
location="center"
>
无法暂停因为爬虫并没有运行!
无法暂停因为爬虫并没有运行!
<template v-slot:actions>
<v-btn @click="closeSnackbar">Close</v-btn>
@@ -9,6 +9,7 @@ const taskStore = useTaskStore();
onMounted(async()=>{
await allTask().then(res=>{
taskStore.taskList = res.data["list"]
console.log(taskStore.taskList)
taskStore.loading = false
})
})