mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-06-21 07:33:49 +08:00
feat: add wechat clawbot notification setup UI
This commit is contained in:
BIN
src/assets/images/logos/clawbot.png
Normal file
BIN
src/assets/images/logos/clawbot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import api from '@/api'
|
||||
import { NotificationConf } from '@/api/types'
|
||||
import { getLogoUrl } from '@/utils/imageUtils'
|
||||
import { useToast } from 'vue-toastification'
|
||||
@@ -45,6 +46,7 @@ const notificationInfo = ref<NotificationConf>({
|
||||
// 各通知类型的名称字典
|
||||
const notificationTypeNames: { [key: string]: string } = {
|
||||
wechat: t('notification.wechat.name'),
|
||||
wechatclawbot: t('notification.wechatclawbot.name'),
|
||||
telegram: t('notification.telegram.name'),
|
||||
qqbot: t('notification.qqbot.name'),
|
||||
vocechat: t('notification.vocechat.name'),
|
||||
@@ -68,6 +70,18 @@ const notificationTypes = [
|
||||
{ value: '其它', title: t('notificationSwitch.other') },
|
||||
]
|
||||
|
||||
interface WechatClawBotStatus {
|
||||
connected?: boolean
|
||||
account_id?: string | null
|
||||
qrcode?: string | null
|
||||
qrcode_url?: string | null
|
||||
qrcode_status?: string | null
|
||||
qrcode_updated_at?: number | null
|
||||
known_targets?: Array<{ userid: string; username: string; last_active?: number | null }>
|
||||
default_target?: string | null
|
||||
base_url?: string | null
|
||||
}
|
||||
|
||||
function ensureWechatConfigDefaults(notification: NotificationConf) {
|
||||
if (notification.type !== 'wechat') {
|
||||
return
|
||||
@@ -83,6 +97,39 @@ function ensureWechatConfigDefaults(notification: NotificationConf) {
|
||||
}
|
||||
}
|
||||
|
||||
function ensureWechatClawBotConfigDefaults(notification: NotificationConf) {
|
||||
if (notification.type !== 'wechatclawbot') {
|
||||
return
|
||||
}
|
||||
if (!notification.config) {
|
||||
notification.config = {}
|
||||
}
|
||||
if (!notification.config.WECHATCLAWBOT_BASE_URL) {
|
||||
notification.config.WECHATCLAWBOT_BASE_URL = 'https://ilinkai.weixin.qq.com'
|
||||
}
|
||||
if (!notification.config.WECHATCLAWBOT_POLL_TIMEOUT) {
|
||||
notification.config.WECHATCLAWBOT_POLL_TIMEOUT = 25
|
||||
}
|
||||
}
|
||||
|
||||
const wechatClawBotLoading = ref(false)
|
||||
const wechatClawBotActionLoading = ref(false)
|
||||
const wechatClawBotStatus = ref<WechatClawBotStatus | null>(null)
|
||||
let wechatClawBotTimer: number | null = null
|
||||
|
||||
function getWechatClawBotRequestParams(extraParams: Record<string, any> = {}) {
|
||||
const config = notificationInfo.value.config || {}
|
||||
return {
|
||||
source: notificationInfo.value.name,
|
||||
fallback_source: props.notification.name,
|
||||
WECHATCLAWBOT_BASE_URL: config.WECHATCLAWBOT_BASE_URL,
|
||||
WECHATCLAWBOT_DEFAULT_TARGET: config.WECHATCLAWBOT_DEFAULT_TARGET,
|
||||
WECHATCLAWBOT_ADMINS: config.WECHATCLAWBOT_ADMINS,
|
||||
WECHATCLAWBOT_POLL_TIMEOUT: config.WECHATCLAWBOT_POLL_TIMEOUT,
|
||||
...extraParams,
|
||||
}
|
||||
}
|
||||
|
||||
const isWechatBotMode = computed({
|
||||
get: () => notificationInfo.value.config?.WECHAT_MODE === 'bot',
|
||||
set: value => {
|
||||
@@ -101,7 +148,11 @@ function openNotificationInfoDialog() {
|
||||
// 替换成深复制,避免修改时影响原数据
|
||||
notificationInfo.value = cloneDeep(props.notification)
|
||||
ensureWechatConfigDefaults(notificationInfo.value)
|
||||
ensureWechatClawBotConfigDefaults(notificationInfo.value)
|
||||
notificationInfoDialog.value = true
|
||||
if (notificationInfo.value.type === 'wechatclawbot') {
|
||||
fetchWechatClawBotStatus(true)
|
||||
}
|
||||
}
|
||||
|
||||
// 保存详情数据
|
||||
@@ -117,16 +168,137 @@ function saveNotificationInfo() {
|
||||
return
|
||||
}
|
||||
ensureWechatConfigDefaults(notificationInfo.value)
|
||||
ensureWechatClawBotConfigDefaults(notificationInfo.value)
|
||||
notificationInfoDialog.value = false
|
||||
emit('change', notificationInfo.value, props.notification.name)
|
||||
emit('done')
|
||||
}
|
||||
|
||||
function clearWechatClawBotTimer() {
|
||||
if (wechatClawBotTimer) {
|
||||
window.clearTimeout(wechatClawBotTimer)
|
||||
wechatClawBotTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleWechatClawBotRefresh() {
|
||||
clearWechatClawBotTimer()
|
||||
if (!notificationInfoDialog.value || notificationInfo.value.type !== 'wechatclawbot') {
|
||||
return
|
||||
}
|
||||
const connected = wechatClawBotStatus.value?.connected
|
||||
const pendingStatus = ['waiting', 'scanned'].includes((wechatClawBotStatus.value?.qrcode_status || '').toLowerCase())
|
||||
if (connected || pendingStatus) {
|
||||
wechatClawBotTimer = window.setTimeout(() => {
|
||||
fetchWechatClawBotStatus(false)
|
||||
}, connected ? 10000 : 3000)
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchWechatClawBotStatus(autoGenerateQrcode = false) {
|
||||
if (notificationInfo.value.type !== 'wechatclawbot' || !notificationInfo.value.name) {
|
||||
return
|
||||
}
|
||||
wechatClawBotLoading.value = true
|
||||
try {
|
||||
const result: { [key: string]: any } = await api.get('notification/wechatclawbot/status', {
|
||||
params: getWechatClawBotRequestParams({ auto_generate_qrcode: autoGenerateQrcode }),
|
||||
})
|
||||
if (result.success) {
|
||||
wechatClawBotStatus.value = result.data
|
||||
scheduleWechatClawBotRefresh()
|
||||
} else {
|
||||
wechatClawBotStatus.value = null
|
||||
clearWechatClawBotTimer()
|
||||
$toast.error(result.message || t('notification.wechatclawbot.statusLoadFailed'))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
clearWechatClawBotTimer()
|
||||
$toast.error(t('notification.wechatclawbot.statusLoadFailed'))
|
||||
} finally {
|
||||
wechatClawBotLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshWechatClawBotQrcode() {
|
||||
if (!notificationInfo.value.name) {
|
||||
return
|
||||
}
|
||||
wechatClawBotActionLoading.value = true
|
||||
try {
|
||||
const result: { [key: string]: any } = await api.post('notification/wechatclawbot/refresh', null, {
|
||||
params: getWechatClawBotRequestParams(),
|
||||
})
|
||||
if (result.success) {
|
||||
wechatClawBotStatus.value = result.data
|
||||
scheduleWechatClawBotRefresh()
|
||||
$toast.success(t('notification.wechatclawbot.qrcodeRefreshSuccess'))
|
||||
} else {
|
||||
$toast.error(result.message || t('notification.wechatclawbot.qrcodeRefreshFailed'))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
$toast.error(t('notification.wechatclawbot.qrcodeRefreshFailed'))
|
||||
} finally {
|
||||
wechatClawBotActionLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function logoutWechatClawBot() {
|
||||
if (!notificationInfo.value.name) {
|
||||
return
|
||||
}
|
||||
wechatClawBotActionLoading.value = true
|
||||
try {
|
||||
const result: { [key: string]: any } = await api.post('notification/wechatclawbot/logout', null, {
|
||||
params: getWechatClawBotRequestParams(),
|
||||
})
|
||||
if (result.success) {
|
||||
$toast.success(result.message || t('notification.wechatclawbot.logoutSuccess'))
|
||||
await fetchWechatClawBotStatus(true)
|
||||
} else {
|
||||
$toast.error(result.message || t('notification.wechatclawbot.logoutFailed'))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
$toast.error(t('notification.wechatclawbot.logoutFailed'))
|
||||
} finally {
|
||||
wechatClawBotActionLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function formatWechatClawBotTime(timestamp?: number | null) {
|
||||
if (!timestamp) {
|
||||
return ''
|
||||
}
|
||||
return new Date(timestamp * 1000).toLocaleString()
|
||||
}
|
||||
|
||||
const wechatClawBotStatusText = computed(() => {
|
||||
const status = (wechatClawBotStatus.value?.qrcode_status || '').toLowerCase()
|
||||
if (wechatClawBotStatus.value?.connected) {
|
||||
return t('notification.wechatclawbot.connected')
|
||||
}
|
||||
if (status === 'scanned') {
|
||||
return t('notification.wechatclawbot.scanned')
|
||||
}
|
||||
if (status === 'expired') {
|
||||
return t('notification.wechatclawbot.expired')
|
||||
}
|
||||
if (status === 'confirmed') {
|
||||
return t('notification.wechatclawbot.confirmed')
|
||||
}
|
||||
return t('notification.wechatclawbot.waiting')
|
||||
})
|
||||
|
||||
// 根据存储类型选择图标
|
||||
const getIcon = computed(() => {
|
||||
switch (props.notification.type) {
|
||||
case 'wechat':
|
||||
return getLogoUrl('wechat')
|
||||
case 'wechatclawbot':
|
||||
return getLogoUrl('wechatclawbot')
|
||||
case 'telegram':
|
||||
return getLogoUrl('telegram')
|
||||
case 'qqbot':
|
||||
@@ -148,8 +320,15 @@ const getIcon = computed(() => {
|
||||
|
||||
// 按钮点击
|
||||
function onClose() {
|
||||
clearWechatClawBotTimer()
|
||||
emit('close')
|
||||
}
|
||||
|
||||
watch(notificationInfoDialog, value => {
|
||||
if (!value) {
|
||||
clearWechatClawBotTimer()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
@@ -347,6 +526,137 @@ function onClose() {
|
||||
</VCol>
|
||||
</template>
|
||||
</VRow>
|
||||
<VRow v-else-if="notificationInfo.type == 'wechatclawbot'">
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="notificationInfo.name"
|
||||
:label="t('notification.name')"
|
||||
:placeholder="t('notification.name')"
|
||||
:hint="t('notification.nameHint')"
|
||||
persistent-hint
|
||||
prepend-inner-icon="mdi-label"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="notificationInfo.config.WECHATCLAWBOT_BASE_URL"
|
||||
:label="t('notification.wechatclawbot.baseUrl')"
|
||||
:hint="t('notification.wechatclawbot.baseUrlHint')"
|
||||
persistent-hint
|
||||
prepend-inner-icon="mdi-web"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="notificationInfo.config.WECHATCLAWBOT_DEFAULT_TARGET"
|
||||
:label="t('notification.wechatclawbot.defaultTarget')"
|
||||
:placeholder="t('notification.wechatclawbot.defaultTargetPlaceholder')"
|
||||
:hint="t('notification.wechatclawbot.defaultTargetHint')"
|
||||
persistent-hint
|
||||
prepend-inner-icon="mdi-account-arrow-right"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="notificationInfo.config.WECHATCLAWBOT_ADMINS"
|
||||
:label="t('notification.wechatclawbot.admins')"
|
||||
:placeholder="t('notification.wechatclawbot.adminsPlaceholder')"
|
||||
:hint="t('notification.wechatclawbot.adminsHint')"
|
||||
persistent-hint
|
||||
prepend-inner-icon="mdi-account-supervisor"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="notificationInfo.config.WECHATCLAWBOT_POLL_TIMEOUT"
|
||||
:label="t('notification.wechatclawbot.pollTimeout')"
|
||||
:hint="t('notification.wechatclawbot.pollTimeoutHint')"
|
||||
persistent-hint
|
||||
type="number"
|
||||
prepend-inner-icon="mdi-timer-outline"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12">
|
||||
<VCard variant="tonal" class="pa-4">
|
||||
<div class="d-flex flex-wrap align-center justify-space-between gap-3 mb-3">
|
||||
<div>
|
||||
<div class="text-subtitle-1 font-weight-medium">{{ t('notification.wechatclawbot.loginStatus') }}</div>
|
||||
<div class="text-body-2 text-medium-emphasis">{{ wechatClawBotStatusText }}</div>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<VBtn
|
||||
size="small"
|
||||
variant="tonal"
|
||||
:loading="wechatClawBotLoading"
|
||||
@click.stop="fetchWechatClawBotStatus(true)"
|
||||
>
|
||||
{{ t('common.refresh') }}
|
||||
</VBtn>
|
||||
<VBtn
|
||||
size="small"
|
||||
color="primary"
|
||||
variant="tonal"
|
||||
:loading="wechatClawBotActionLoading"
|
||||
@click.stop="refreshWechatClawBotQrcode"
|
||||
>
|
||||
{{ t('notification.wechatclawbot.refreshQrcode') }}
|
||||
</VBtn>
|
||||
<VBtn
|
||||
size="small"
|
||||
color="error"
|
||||
variant="tonal"
|
||||
:loading="wechatClawBotActionLoading"
|
||||
:disabled="!wechatClawBotStatus?.connected"
|
||||
@click.stop="logoutWechatClawBot"
|
||||
>
|
||||
{{ t('notification.wechatclawbot.logout') }}
|
||||
</VBtn>
|
||||
</div>
|
||||
</div>
|
||||
<VRow>
|
||||
<VCol cols="12" md="5">
|
||||
<div class="rounded text-center p-3 border h-100 d-flex align-center justify-center min-h-[16rem]">
|
||||
<VImg
|
||||
v-if="wechatClawBotStatus?.qrcode_url"
|
||||
:src="wechatClawBotStatus.qrcode_url"
|
||||
width="220"
|
||||
height="220"
|
||||
class="mx-auto"
|
||||
/>
|
||||
<VProgressCircular v-else-if="wechatClawBotLoading" indeterminate color="primary" />
|
||||
<div v-else class="text-body-2 text-medium-emphasis">
|
||||
{{ t('notification.wechatclawbot.noQrcode') }}
|
||||
</div>
|
||||
</div>
|
||||
</VCol>
|
||||
<VCol cols="12" md="7">
|
||||
<VAlert variant="tonal" :type="wechatClawBotStatus?.connected ? 'success' : 'info'" class="mb-3">
|
||||
<div class="text-body-2">{{ t('notification.wechatclawbot.scanHint') }}</div>
|
||||
<div v-if="wechatClawBotStatus?.account_id" class="mt-2">
|
||||
{{ t('notification.wechatclawbot.accountId') }}: {{ wechatClawBotStatus.account_id }}
|
||||
</div>
|
||||
<div v-if="wechatClawBotStatus?.qrcode_updated_at" class="mt-2">
|
||||
{{ t('notification.wechatclawbot.qrcodeUpdatedAt') }}:
|
||||
{{ formatWechatClawBotTime(wechatClawBotStatus.qrcode_updated_at) }}
|
||||
</div>
|
||||
</VAlert>
|
||||
<div class="text-subtitle-2 mb-2">{{ t('notification.wechatclawbot.knownTargets') }}</div>
|
||||
<VList v-if="wechatClawBotStatus?.known_targets?.length" density="compact" class="border rounded">
|
||||
<VListItem
|
||||
v-for="item in wechatClawBotStatus.known_targets"
|
||||
:key="item.userid"
|
||||
:title="item.username || item.userid"
|
||||
:subtitle="`${item.userid}${item.last_active ? ` · ${formatWechatClawBotTime(item.last_active)}` : ''}`"
|
||||
/>
|
||||
</VList>
|
||||
<div v-else class="text-body-2 text-medium-emphasis">
|
||||
{{ t('notification.wechatclawbot.noKnownTargets') }}
|
||||
</div>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</VCard>
|
||||
</VCol>
|
||||
</VRow>
|
||||
<VRow v-else-if="notificationInfo.type == 'telegram'">
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
|
||||
@@ -91,6 +91,7 @@ const userForm = ref<ExtendedUser>({
|
||||
},
|
||||
settings: {
|
||||
wechat_userid: null,
|
||||
wechatclawbot_userid: null,
|
||||
telegram_userid: null,
|
||||
slack_userid: null,
|
||||
discord_userid: null,
|
||||
@@ -503,6 +504,15 @@ onMounted(() => {
|
||||
prepend-inner-icon="mdi-wechat"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="userForm.settings.wechatclawbot_userid"
|
||||
density="comfortable"
|
||||
clearable
|
||||
:label="t('dialog.userAddEdit.wechatClawBot')"
|
||||
prepend-inner-icon="mdi-robot-happy-outline"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="userForm.settings.telegram_userid"
|
||||
|
||||
@@ -329,6 +329,7 @@ export function useSetupWizard() {
|
||||
notification: {
|
||||
'telegram': 'TelegramModule',
|
||||
'wechat': 'WechatModule',
|
||||
'wechatclawbot': 'WechatClawBotModule',
|
||||
'slack': 'SlackModule',
|
||||
'synologychat': 'SynologyChatModule',
|
||||
'qqbot': 'QQBotModule',
|
||||
@@ -423,7 +424,17 @@ export function useSetupWizard() {
|
||||
wizardData.value.notification.type = type
|
||||
// 如果名称为空或为默认名称,则设置默认名称
|
||||
if (!wizardData.value.notification.name || wizardData.value.notification.name.includes('通知')) {
|
||||
wizardData.value.notification.name = `${type} 通知`
|
||||
const displayNameMap: Record<string, string> = {
|
||||
wechat: '企业微信',
|
||||
wechatclawbot: '微信 ClawBot',
|
||||
telegram: 'Telegram',
|
||||
slack: 'Slack',
|
||||
synologychat: 'SynologyChat',
|
||||
qqbot: 'QQ',
|
||||
vocechat: 'VoceChat',
|
||||
webpush: 'WebPush',
|
||||
}
|
||||
wizardData.value.notification.name = `${displayNameMap[type] || type} 通知`
|
||||
}
|
||||
wizardData.value.notification.enabled = true
|
||||
// 不清空config和switchs,保留用户已输入的值
|
||||
@@ -656,6 +667,8 @@ export function useSetupWizard() {
|
||||
validationErrors.value.notification.WECHAT_APP_SECRET = true
|
||||
}
|
||||
break
|
||||
case 'wechatclawbot':
|
||||
break
|
||||
case 'telegram':
|
||||
if (!config.TELEGRAM_TOKEN?.trim()) {
|
||||
errors.push(t('notification.telegram.tokenRequired'))
|
||||
@@ -854,7 +867,7 @@ export function useSetupWizard() {
|
||||
case 5: // 媒体服务器测试 - 只有选择了媒体服务器才测试
|
||||
return !!wizardData.value.mediaServer.type
|
||||
case 6: // 消息通知测试 - 只有选择了通知才测试
|
||||
return !!wizardData.value.notification.type
|
||||
return !!wizardData.value.notification.type && wizardData.value.notification.type !== 'wechatclawbot'
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -353,7 +353,8 @@ export default {
|
||||
},
|
||||
notification: {
|
||||
title: 'Notifications',
|
||||
description: 'Notification channels (WeChat, Telegram, Slack, SynologyChat, VoceChat, WebPush), message scope',
|
||||
description:
|
||||
'Notification channels (WeChat Work, WeChat ClawBot, Telegram, Slack, SynologyChat, VoceChat, WebPush), message scope',
|
||||
},
|
||||
about: {
|
||||
title: 'About',
|
||||
@@ -472,6 +473,38 @@ export default {
|
||||
adminsHint: 'User IDs that can use admin menu and commands, separated by commas',
|
||||
adminsPlaceholder: 'User IDs list, separated by commas',
|
||||
},
|
||||
wechatclawbot: {
|
||||
name: 'WeChat ClawBot',
|
||||
baseUrl: 'iLink Base URL',
|
||||
baseUrlHint: 'iLink service URL for WeChat ClawBot, keep default in most cases',
|
||||
defaultTarget: 'Default Target',
|
||||
defaultTargetHint: 'Optional target userid; leave empty to notify interacted users',
|
||||
defaultTargetPlaceholder: 'userid (optional)',
|
||||
admins: 'Admin Whitelist',
|
||||
adminsHint: 'User IDs allowed to run slash commands, separated by commas',
|
||||
adminsPlaceholder: 'User IDs list, separated by commas',
|
||||
pollTimeout: 'Poll Timeout (seconds)',
|
||||
pollTimeoutHint: 'Long polling timeout, recommended 20-30 seconds',
|
||||
loginStatus: 'Login Status',
|
||||
connected: 'Connected',
|
||||
waiting: 'Waiting for QR scan',
|
||||
scanned: 'Scanned, waiting for confirmation',
|
||||
confirmed: 'Confirmed, establishing connection',
|
||||
expired: 'QR code expired',
|
||||
refreshQrcode: 'Refresh QR Code',
|
||||
logout: 'Logout',
|
||||
noQrcode: 'No QR code yet. Refresh or save config first.',
|
||||
scanHint: 'Scan with WeChat to bind. Save and enable this channel before first use.',
|
||||
accountId: 'Account ID',
|
||||
qrcodeUpdatedAt: 'QR Updated At',
|
||||
knownTargets: 'Recent Interacted Users',
|
||||
noKnownTargets: 'No interaction records yet',
|
||||
statusLoadFailed: 'Failed to load WeChat ClawBot status',
|
||||
qrcodeRefreshSuccess: 'WeChat ClawBot QR code refreshed',
|
||||
qrcodeRefreshFailed: 'Failed to refresh WeChat ClawBot QR code',
|
||||
logoutSuccess: 'WeChat ClawBot logged out',
|
||||
logoutFailed: 'Failed to logout WeChat ClawBot',
|
||||
},
|
||||
telegram: {
|
||||
name: 'Telegram',
|
||||
token: 'Bot Token',
|
||||
@@ -1732,7 +1765,8 @@ export default {
|
||||
timeSaveSuccess: 'Notification send time saved successfully',
|
||||
timeSaveFailed: 'Failed to save notification send time!',
|
||||
channel: 'Notification',
|
||||
wechat: 'WeChat',
|
||||
wechat: 'WeChat Work',
|
||||
wechatClawBot: 'WeChat ClawBot',
|
||||
resourceDownload: 'Resource Download',
|
||||
mediaImport: 'Media Import',
|
||||
subscription: 'Subscription',
|
||||
@@ -1816,7 +1850,7 @@ export default {
|
||||
animeCategory: 'Anime',
|
||||
downloadUser: 'Remote Search Auto Download User List',
|
||||
downloadUserHint:
|
||||
'Whether to automatically download when searching with Telegram, WeChat, etc., comma separated, set to all to represent all users auto-download',
|
||||
'Whether to auto-download when searching with Telegram, WeChat Work, etc., comma separated, set to all for all users',
|
||||
multipleNameSearch: 'Multiple Name Resource Search',
|
||||
multipleNameSearchHint:
|
||||
'Search site resources using multiple names (Chinese, English, etc.) and merge search results, will increase site access frequency',
|
||||
@@ -2061,7 +2095,8 @@ export default {
|
||||
resetDefaultAvatar: 'Reset Default Avatar',
|
||||
restoreCurrentAvatar: 'Restore Current Avatar',
|
||||
notifications: 'Notifications',
|
||||
wechat: 'WeChat UserID',
|
||||
wechat: 'WeChat Work UserID',
|
||||
wechatClawBot: 'WeChat ClawBot UserID',
|
||||
telegram: 'Telegram UserID',
|
||||
slack: 'Slack UserID',
|
||||
discord: 'Discord UserID',
|
||||
@@ -2787,7 +2822,8 @@ export default {
|
||||
nickname: 'Nickname',
|
||||
nicknamePlaceholder: 'Display nickname, takes precedence over username',
|
||||
accountBinding: 'Account Binding',
|
||||
wechatUser: 'WeChat User',
|
||||
wechatUser: 'WeChat Work User',
|
||||
wechatClawBotUser: 'WeChat ClawBot User',
|
||||
telegramUser: 'Telegram User',
|
||||
slackUser: 'Slack User',
|
||||
discordUser: 'Discord User',
|
||||
|
||||
@@ -351,7 +351,7 @@ export default {
|
||||
},
|
||||
notification: {
|
||||
title: '通知',
|
||||
description: '通知渠道(微信、Telegram、Slack、SynologyChat、VoceChat、WebPush)、消息发送范围',
|
||||
description: '通知渠道(企业微信、微信 ClawBot、Telegram、Slack、SynologyChat、VoceChat、WebPush)、消息发送范围',
|
||||
},
|
||||
about: {
|
||||
title: '关于',
|
||||
@@ -468,6 +468,38 @@ export default {
|
||||
adminsHint: '可使用管理菜单及命令的用户ID列表,多个ID使用,分隔',
|
||||
adminsPlaceholder: '用户ID列表,多个ID使用,分隔',
|
||||
},
|
||||
wechatclawbot: {
|
||||
name: '微信 ClawBot',
|
||||
baseUrl: 'iLink 地址',
|
||||
baseUrlHint: '微信 ClawBot iLink 服务地址,通常使用默认值',
|
||||
defaultTarget: '默认通知目标',
|
||||
defaultTargetHint: '可填写用户 userid;不填则默认发给已互动用户',
|
||||
defaultTargetPlaceholder: '用户 userid(可选)',
|
||||
admins: '管理员白名单',
|
||||
adminsHint: '允许执行斜杠命令的用户ID列表,多个ID使用,分隔',
|
||||
adminsPlaceholder: '用户ID列表,多个ID使用,分隔',
|
||||
pollTimeout: '轮询超时(秒)',
|
||||
pollTimeoutHint: '长轮询请求超时时间,建议 20-30 秒',
|
||||
loginStatus: '登录状态',
|
||||
connected: '已连接',
|
||||
waiting: '等待扫码',
|
||||
scanned: '已扫码,待确认',
|
||||
confirmed: '已确认,正在建立连接',
|
||||
expired: '二维码已过期',
|
||||
refreshQrcode: '刷新二维码',
|
||||
logout: '退出登录',
|
||||
noQrcode: '暂无二维码,请先刷新或保存配置后重试',
|
||||
scanHint: '使用微信扫码绑定后,状态会自动刷新。首次使用请先保存并启用该通知渠道。',
|
||||
accountId: '账号ID',
|
||||
qrcodeUpdatedAt: '二维码更新时间',
|
||||
knownTargets: '最近互动用户',
|
||||
noKnownTargets: '暂无互动用户记录',
|
||||
statusLoadFailed: '获取微信 ClawBot 状态失败',
|
||||
qrcodeRefreshSuccess: '微信 ClawBot 二维码已刷新',
|
||||
qrcodeRefreshFailed: '刷新微信 ClawBot 二维码失败',
|
||||
logoutSuccess: '微信 ClawBot 已退出登录',
|
||||
logoutFailed: '微信 ClawBot 退出登录失败',
|
||||
},
|
||||
telegram: {
|
||||
name: 'Telegram',
|
||||
token: 'Bot Token',
|
||||
@@ -1703,7 +1735,8 @@ export default {
|
||||
timeSaveSuccess: '通知发送时间保存成功',
|
||||
timeSaveFailed: '通知发送时间保存失败!',
|
||||
channel: '通知',
|
||||
wechat: '微信',
|
||||
wechat: '企业微信',
|
||||
wechatClawBot: '微信 ClawBot',
|
||||
resourceDownload: '资源下载',
|
||||
mediaImport: '整理入库',
|
||||
subscription: '订阅',
|
||||
@@ -1781,7 +1814,7 @@ export default {
|
||||
tvCategory: '电视剧',
|
||||
animeCategory: '动漫',
|
||||
downloadUser: '远程搜索自动下载用户',
|
||||
downloadUserHint: '使用Telegram、微信等搜索时是否自动下载,使用逗号分割,设置为 all 代表所有用户自动择优下载',
|
||||
downloadUserHint: '使用Telegram、企业微信等搜索时是否自动下载,使用逗号分割,设置为 all 代表所有用户自动择优下载',
|
||||
multipleNameSearch: '多名称资源搜索',
|
||||
multipleNameSearchHint: '使用多个名称(中文、英文等)搜索站点资源并合并搜索结果,会增加站点访问频率',
|
||||
downloadSubtitle: '下载站点字幕',
|
||||
@@ -2021,7 +2054,8 @@ export default {
|
||||
resetDefaultAvatar: '重置默认头像',
|
||||
restoreCurrentAvatar: '还原当前头像',
|
||||
notifications: '通知',
|
||||
wechat: '微信ID',
|
||||
wechat: '企业微信ID',
|
||||
wechatClawBot: '微信 ClawBot ID',
|
||||
telegram: 'Telegram ID',
|
||||
slack: 'Slack ID',
|
||||
discord: 'Discord ID',
|
||||
@@ -2740,7 +2774,8 @@ export default {
|
||||
nickname: '昵称',
|
||||
nicknamePlaceholder: '显示昵称,优先于用户名显示',
|
||||
accountBinding: '账号绑定',
|
||||
wechatUser: '微信用户',
|
||||
wechatUser: '企业微信用户',
|
||||
wechatClawBotUser: '微信 ClawBot 用户',
|
||||
telegramUser: 'Telegram用户',
|
||||
slackUser: 'Slack用户',
|
||||
discordUser: 'Discord用户',
|
||||
|
||||
@@ -352,7 +352,7 @@ export default {
|
||||
},
|
||||
notification: {
|
||||
title: '通知',
|
||||
description: '通知渠道(微信、Telegram、Slack、SynologyChat、VoceChat、WebPush)、消息發送範圍',
|
||||
description: '通知渠道(企業微信、微信 ClawBot、Telegram、Slack、SynologyChat、VoceChat、WebPush)、消息發送範圍',
|
||||
},
|
||||
about: {
|
||||
title: '關於',
|
||||
@@ -469,6 +469,38 @@ export default {
|
||||
adminsHint: '可使用管理菜單及命令的用戶ID列表,多個ID使用,分隔',
|
||||
adminsPlaceholder: '用戶ID列表,多個ID使用,分隔',
|
||||
},
|
||||
wechatclawbot: {
|
||||
name: '微信 ClawBot',
|
||||
baseUrl: 'iLink 地址',
|
||||
baseUrlHint: '微信 ClawBot iLink 服務地址,通常使用預設值',
|
||||
defaultTarget: '預設通知目標',
|
||||
defaultTargetHint: '可填寫使用者 userid;不填則預設發給已互動使用者',
|
||||
defaultTargetPlaceholder: '使用者 userid(可選)',
|
||||
admins: '管理員白名單',
|
||||
adminsHint: '允許執行斜線命令的用戶ID列表,多個ID使用,分隔',
|
||||
adminsPlaceholder: '用戶ID列表,多個ID使用,分隔',
|
||||
pollTimeout: '輪詢超時(秒)',
|
||||
pollTimeoutHint: '長輪詢請求超時時間,建議 20-30 秒',
|
||||
loginStatus: '登入狀態',
|
||||
connected: '已連線',
|
||||
waiting: '等待掃碼',
|
||||
scanned: '已掃碼,待確認',
|
||||
confirmed: '已確認,正在建立連線',
|
||||
expired: '二維碼已過期',
|
||||
refreshQrcode: '刷新二維碼',
|
||||
logout: '退出登入',
|
||||
noQrcode: '暫無二維碼,請先刷新或保存配置後再試',
|
||||
scanHint: '使用微信掃碼綁定後,狀態會自動刷新。首次使用請先保存並啟用該通知渠道。',
|
||||
accountId: '帳號ID',
|
||||
qrcodeUpdatedAt: '二維碼更新時間',
|
||||
knownTargets: '最近互動用戶',
|
||||
noKnownTargets: '暫無互動用戶記錄',
|
||||
statusLoadFailed: '獲取微信 ClawBot 狀態失敗',
|
||||
qrcodeRefreshSuccess: '微信 ClawBot 二維碼已刷新',
|
||||
qrcodeRefreshFailed: '刷新微信 ClawBot 二維碼失敗',
|
||||
logoutSuccess: '微信 ClawBot 已退出登入',
|
||||
logoutFailed: '微信 ClawBot 退出登入失敗',
|
||||
},
|
||||
telegram: {
|
||||
name: 'Telegram',
|
||||
token: 'Bot Token',
|
||||
@@ -1705,7 +1737,8 @@ export default {
|
||||
timeSaveSuccess: '通知發送時間保存成功',
|
||||
timeSaveFailed: '通知發送時間保存失敗!',
|
||||
channel: '通知',
|
||||
wechat: '微信',
|
||||
wechat: '企業微信',
|
||||
wechatClawBot: '微信 ClawBot',
|
||||
resourceDownload: '資源下載',
|
||||
mediaImport: '整理入庫',
|
||||
subscription: '訂閱',
|
||||
@@ -1791,7 +1824,7 @@ export default {
|
||||
mediaSourceHint: '搜索媒體信息時使用的數據源以及排序',
|
||||
filterRuleGroupHint: '搜索媒體信息時按選定的過濾規則組對結果進行過濾',
|
||||
downloadUserPlaceholder: '用戶ID1,用戶ID2',
|
||||
downloadUserHint: '使用Telegram、微信等搜索時是否自動下載,使用逗號分割,設置為 all 代表所有用戶自動擇優下載',
|
||||
downloadUserHint: '使用Telegram、企業微信等搜索時是否自動下載,使用逗號分割,設置為 all 代表所有用戶自動擇優下載',
|
||||
downloadLabelPlaceholder: 'MOVIEPILOT',
|
||||
},
|
||||
directory: {
|
||||
@@ -2023,7 +2056,8 @@ export default {
|
||||
resetDefaultAvatar: '重置默認頭像',
|
||||
restoreCurrentAvatar: '還原當前頭像',
|
||||
notifications: '通知',
|
||||
wechat: '微信UserID',
|
||||
wechat: '企業微信 UserID',
|
||||
wechatClawBot: '微信 ClawBot ID',
|
||||
telegram: 'Telegram UserID',
|
||||
slack: 'Slack UserID',
|
||||
discord: 'Discord UserID',
|
||||
@@ -2742,7 +2776,8 @@ export default {
|
||||
nickname: '暱稱',
|
||||
nicknamePlaceholder: '顯示暱稱,優先於用戶名顯示',
|
||||
accountBinding: '賬號綁定',
|
||||
wechatUser: '微信用戶',
|
||||
wechatUser: '企業微信用戶',
|
||||
wechatClawBotUser: '微信 ClawBot 用戶',
|
||||
telegramUser: 'Telegram用戶',
|
||||
slackUser: 'Slack用戶',
|
||||
discordUser: 'Discord用戶',
|
||||
|
||||
@@ -13,6 +13,7 @@ import plexLogo from '@/assets/images/logos/plex.png'
|
||||
import trimemediaLogo from '@/assets/images/logos/trimemedia.png'
|
||||
import ugreenLogo from '@/assets/images/logos/ugreen.png'
|
||||
import wechatLogo from '@/assets/images/logos/wechat.png'
|
||||
import clawbotLogo from '@/assets/images/logos/clawbot.png'
|
||||
import telegramLogo from '@/assets/images/logos/telegram.webp'
|
||||
import slackLogo from '@/assets/images/logos/slack.webp'
|
||||
import discordLogo from '@/assets/images/logos/discord.png'
|
||||
@@ -44,6 +45,7 @@ const logoMap: Record<string, string> = {
|
||||
trimemedia: trimemediaLogo,
|
||||
ugreen: ugreenLogo,
|
||||
wechat: wechatLogo,
|
||||
wechatclawbot: clawbotLogo,
|
||||
telegram: telegramLogo,
|
||||
slack: slackLogo,
|
||||
discord: discordLogo,
|
||||
|
||||
@@ -299,12 +299,15 @@ onMounted(() => {
|
||||
<VIcon icon="mdi-plus" />
|
||||
<VMenu :activator="'parent'" :close-on-content-click="true">
|
||||
<VList>
|
||||
<VListItem @click="addNotification('wechat')">
|
||||
<VListItemTitle>{{ t('setting.notification.wechat') }}</VListItemTitle>
|
||||
</VListItem>
|
||||
<VListItem @click="addNotification('telegram')">
|
||||
<VListItemTitle>{{ t('setting.notification.telegram') }}</VListItemTitle>
|
||||
</VListItem>
|
||||
<VListItem @click="addNotification('wechat')">
|
||||
<VListItemTitle>{{ t('setting.notification.wechat') }}</VListItemTitle>
|
||||
</VListItem>
|
||||
<VListItem @click="addNotification('wechatclawbot')">
|
||||
<VListItemTitle>{{ t('setting.notification.wechatClawBot') }}</VListItemTitle>
|
||||
</VListItem>
|
||||
<VListItem @click="addNotification('telegram')">
|
||||
<VListItemTitle>{{ t('setting.notification.telegram') }}</VListItemTitle>
|
||||
</VListItem>
|
||||
<VListItem @click="addNotification('slack')">
|
||||
<VListItemTitle>{{ t('setting.notification.slack') }}</VListItemTitle>
|
||||
</VListItem>
|
||||
|
||||
@@ -49,7 +49,20 @@ const notificationTypes = [
|
||||
>
|
||||
<VCardText class="text-center">
|
||||
<VImg :src="getLogoUrl('wechat')" height="48" width="48" class="mx-auto mb-2" />
|
||||
<div class="text-h6">微信</div>
|
||||
<div class="text-h6">企业微信</div>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VCol>
|
||||
<VCol cols="12" md="3">
|
||||
<VCard
|
||||
:color="wizardData.notification.type === 'wechatclawbot' ? 'primary' : 'default'"
|
||||
:variant="wizardData.notification.type === 'wechatclawbot' ? 'tonal' : 'outlined'"
|
||||
class="cursor-pointer"
|
||||
@click="selectNotification('wechatclawbot')"
|
||||
>
|
||||
<VCardText class="text-center">
|
||||
<VImg :src="getLogoUrl('wechatclawbot')" height="48" width="48" class="mx-auto mb-2" />
|
||||
<div class="text-h6">WeChat ClawBot</div>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VCol>
|
||||
@@ -251,6 +264,50 @@ const notificationTypes = [
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
<VRow v-else-if="wizardData.notification.type === 'wechatclawbot'">
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="wizardData.notification.name"
|
||||
:label="t('notification.name')"
|
||||
:placeholder="t('notification.name')"
|
||||
:hint="t('notification.nameHint')"
|
||||
:error="validationErrors.notification.name"
|
||||
:error-messages="validationErrors.notification.name ? [t('notification.nameRequired')] : []"
|
||||
persistent-hint
|
||||
prepend-inner-icon="mdi-label"
|
||||
required
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="wizardData.notification.config.WECHATCLAWBOT_BASE_URL"
|
||||
:label="t('notification.wechatclawbot.baseUrl')"
|
||||
:hint="t('notification.wechatclawbot.baseUrlHint')"
|
||||
persistent-hint
|
||||
prepend-inner-icon="mdi-web"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="wizardData.notification.config.WECHATCLAWBOT_DEFAULT_TARGET"
|
||||
:label="t('notification.wechatclawbot.defaultTarget')"
|
||||
:placeholder="t('notification.wechatclawbot.defaultTargetPlaceholder')"
|
||||
:hint="t('notification.wechatclawbot.defaultTargetHint')"
|
||||
persistent-hint
|
||||
prepend-inner-icon="mdi-account-arrow-right"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="wizardData.notification.config.WECHATCLAWBOT_ADMINS"
|
||||
:label="t('notification.wechatclawbot.admins')"
|
||||
:placeholder="t('notification.wechatclawbot.adminsPlaceholder')"
|
||||
:hint="t('notification.wechatclawbot.adminsHint')"
|
||||
persistent-hint
|
||||
prepend-inner-icon="mdi-account-supervisor"
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
<VRow v-else-if="wizardData.notification.type === 'telegram'">
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
|
||||
@@ -436,6 +436,15 @@ watch(
|
||||
prepend-inner-icon="mdi-wechat"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="accountInfo.settings.wechatclawbot_userid"
|
||||
density="comfortable"
|
||||
clearable
|
||||
:label="t('profile.wechatClawBotUser')"
|
||||
prepend-inner-icon="mdi-robot-happy-outline"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="accountInfo.settings.telegram_userid"
|
||||
|
||||
Reference in New Issue
Block a user