feat: add proactive messaging support in CronJobPage and enhance file download tool with user notification option

This commit is contained in:
Soulter
2026-02-01 18:12:11 +08:00
parent 9bf63354be
commit 2213fb1ebf
5 changed files with 66 additions and 24 deletions
+14 -8
View File
@@ -144,7 +144,11 @@ class FileDownloadTool(FunctionTool):
"remote_path": {
"type": "string",
"description": "The path of the file in the sandbox to download.",
}
},
"also_send_to_user": {
"type": "boolean",
"description": "Whether to also send the downloaded file to the user via message. Defaults to true.",
},
},
"required": ["remote_path"],
}
@@ -154,6 +158,7 @@ class FileDownloadTool(FunctionTool):
self,
context: ContextWrapper[AstrAgentContext],
remote_path: str,
also_send_to_user: bool = True,
) -> ToolExecResult:
sb = await get_booter(
context.context.context,
@@ -168,13 +173,14 @@ class FileDownloadTool(FunctionTool):
await sb.download_file(remote_path, local_path)
logger.info(f"File {remote_path} downloaded from sandbox to {local_path}")
try:
name = os.path.basename(local_path)
await context.context.event.send(
MessageChain(chain=[File(name=name, file=local_path)])
)
except Exception as e:
logger.error(f"Error sending file message: {e}")
if also_send_to_user:
try:
name = os.path.basename(local_path)
await context.context.event.send(
MessageChain(chain=[File(name=name, file=local_path)])
)
except Exception as e:
logger.error(f"Error sending file message: {e}")
# remove
try:
+9
View File
@@ -90,6 +90,14 @@ class Platform(abc.ABC):
def get_stats(self) -> dict:
"""获取平台统计信息"""
meta = self.meta()
meta_info = {
"id": meta.id,
"name": meta.name,
"display_name": meta.adapter_display_name or meta.name,
"description": meta.description,
"support_streaming_message": meta.support_streaming_message,
"support_proactive_message": meta.support_proactive_message,
}
return {
"id": meta.id or self.config.get("id"),
"type": meta.name,
@@ -105,6 +113,7 @@ class Platform(abc.ABC):
if self.last_error
else None,
"unified_webhook": self.unified_webhook(),
"meta": meta_info,
}
@abc.abstractmethod
@@ -86,6 +86,7 @@ class WebChatAdapter(Platform):
name="webchat",
description="webchat",
id="webchat",
support_proactive_message=False,
)
async def send_by_session(
@@ -43,25 +43,15 @@ const sidebarItem: menu[] = [
icon: 'mdi-book-open-variant',
to: '/knowledge-base',
},
{
title: 'core.navigation.persona',
icon: 'mdi-heart',
to: '/persona'
},
{
title: 'core.navigation.groups.more',
icon: 'mdi-dots-horizontal',
children: [
{
title: 'core.navigation.persona',
icon: 'mdi-heart',
to: '/persona'
},
{
title: 'core.navigation.subagent',
icon: 'mdi-vector-link',
to: '/subagent'
},
{
title: 'core.navigation.cron',
icon: 'mdi-clock-outline',
to: '/cron'
},
{
title: 'core.navigation.conversation',
icon: 'mdi-database',
@@ -72,6 +62,16 @@ const sidebarItem: menu[] = [
icon: 'mdi-pencil-ruler',
to: '/session-management'
},
{
title: 'core.navigation.cron',
icon: 'mdi-clock-outline',
to: '/cron'
},
{
title: 'core.navigation.subagent',
icon: 'mdi-vector-link',
to: '/subagent'
},
{
title: 'core.navigation.dashboard',
icon: 'mdi-view-dashboard',
+27 -1
View File
@@ -6,7 +6,14 @@
<h2 class="text-h5 font-weight-bold">未来任务管理</h2>
<v-chip size="x-small" color="orange-darken-2" variant="tonal" label>Beta</v-chip>
</div>
<div class="text-body-2 text-medium-emphasis">查看给 AstrBot 布置的未来任务AstrBot 将会被自动唤醒执行任务然后将结果告知任务布置方</div>
<div class="text-body-2 text-medium-emphasis">
查看给 AstrBot 布置的未来任务AstrBot 将会被自动唤醒执行任务然后将结果告知任务布置方
主动发送结果仅支持以下平台
<span v-if="proactivePlatforms.length">
{{ proactivePlatforms.map((p) => `${p.display_name || p.name}(${p.id})`).join('、') }}
</span>
<span v-else>暂无支持主动消息的平台请在平台设置中开启</span>
</div>
</div>
<div class="d-flex align-center" style="gap: 8px;">
<v-btn variant="tonal" color="primary" :loading="loading" @click="loadJobs">刷新</v-btn>
@@ -60,6 +67,7 @@ import axios from 'axios'
const loading = ref(false)
const jobs = ref<any[]>([])
const proactivePlatforms = ref<{ id: string; name: string; display_name?: string }[]>([])
const snackbar = ref({ show: false, message: '', color: 'success' })
@@ -102,6 +110,23 @@ async function loadJobs() {
}
}
async function loadPlatforms() {
try {
const res = await axios.get('/api/platform/stats')
if (res.data.status === 'ok' && Array.isArray(res.data.data?.platforms)) {
proactivePlatforms.value = res.data.data.platforms
.filter((p: any) => p?.meta?.support_proactive_message)
.map((p: any) => ({
id: p?.id || p?.meta?.id || 'unknown',
name: p?.meta?.name || p?.type || '',
display_name: p?.meta?.display_name || p?.display_name
}))
}
} catch (e) {
// ignore platform fetch errors in UI; subtitle will show fallback
}
}
async function toggleJob(job: any) {
try {
const res = await axios.patch(`/api/cron/jobs/${job.job_id}`, { enabled: job.enabled })
@@ -131,6 +156,7 @@ async function deleteJob(job: any) {
onMounted(() => {
loadJobs()
loadPlatforms()
})
</script>