const isSingBox = Plugins.APP_TITLE.includes('SingBox')
const onRun = async () => {
showUI()
}
const onReady = async () => {
const appStore = Plugins.useAppStore()
appStore.addCustomActions('subscriptions_header', {
id: Plugin.id,
component: 'Button',
componentProps: {
type: 'link',
onClick: showUI
},
componentSlots: {
default: '添加组合订阅'
}
})
}
const showUI = () => {
const { h, reactive, computed, defineComponent } = Vue
const subscribesStore = Plugins.useSubscribesStore()
const state = reactive({
name: '',
selectedSubs: [],
externalSubs: {},
operates: {
addSequence: false,
deduplicate: false
}
})
const existingSubs = computed(() => subscribesStore.subscribes.map((sub) => sub.name))
const component = defineComponent({
template: `
`,
setup() {
const toggleSelection = (name) => {
const idx = state.selectedSubs.indexOf(name)
if (idx === -1) state.selectedSubs.push(name)
else state.selectedSubs.splice(idx, 1)
}
return { state, existingSubs, toggleSelection }
}
})
const modal = Plugins.modal(
{
title: '聚合订阅',
width: '720px',
maskClosable: true,
cancel: true,
submitText: '添加',
afterClose: () => {
modal.destroy()
},
onOk: async () => {
if (!state.name.trim()) {
Plugins.message.error('请输入订阅的名称')
return false
}
if (!state.selectedSubs.length && !Object.keys(state.externalSubs).length) {
Plugins.message.error('请至少选择一个订阅')
return false
}
try {
await addCollectionSubs(state)
return true
} catch (err) {
Plugins.message.error(err.message ?? String(err))
return false
}
}
},
{
default: () => h(component)
}
)
modal.open()
}
const addCollectionSubs = async (state) => {
const { name, selectedSubs, externalSubs, operates } = state
const subscribesStore = Plugins.useSubscribesStore()
for (const [name, url] of Object.entries(externalSubs)) {
const subscription = subscribesStore.getSubscribeTemplate(name, { url })
await subscribesStore.addSubscribe(subscription)
selectedSubs.push(name)
}
const script = generateScript({ names: selectedSubs, operates })
const collectionSubscription = subscribesStore.getSubscribeTemplate(name, { url: '' })
collectionSubscription.type = 'Manual'
collectionSubscription.script = script
await subscribesStore.addSubscribe(collectionSubscription)
await Plugins.WriteFile(collectionSubscription.path, '[]')
await Plugins.alert('提示', `已添加组合订阅「${state.name}」\n\n之后如需修改订阅配置,请手动编辑订阅脚本`)
}
const generateScript = (params) => {
const { names, operates } = params
return `
const onSubscribe = async (proxies, subscription) => {
const members = ${JSON.stringify(names)}; //如需添加或移除订阅请修改此处
const operates = ${JSON.stringify(operates, null, 2)}; //如需启用或禁用操作请修改此处
const subscribesStore = Plugins.useSubscribesStore();
const promises = subscribesStore.subscribes.map(async (sub) => {
if (members.includes(sub.name)) {
try {
await subscribesStore.updateSubscribe(sub.id);
const proxies = (await Plugins.ReadFile(sub.path, { Mode: 'Text' })) || '[]';
return Plugins.YAML.parse(proxies);
} catch (err) {
Plugins.message.warn(\`\${subscription.name} 的成员 \${sub.name} 更新失败,已忽略,\${err}\`);
return [];
}
}
return [];
});
let collectionProxies = (await Promise.all(promises)).flat();
if (operates.deduplicate) {
const uniqueMap = new Map();
collectionProxies = collectionProxies.filter((p) => {
const key = \`\${p.server}:${isSingBox ? 'p.server_port' : 'p.port'}\`;
if (!uniqueMap.has(key)) {
uniqueMap.set(key, p);
return true;
}
return false;
});
}
if (operates.addSequence) {
const seqWidth = String(collectionProxies.length).length;
collectionProxies = collectionProxies.map((p, i) => {
const seq = String(i + 1).padStart(seqWidth, '0');
${
isSingBox
? `
return {
...p,
tag: \`\${p.tag} - \${seq}\`,
};
`
: `
return {
...p,
name: \`\${p.name} - \${seq}\`,
};
`
}
});
}
return { proxies: collectionProxies, subscription };
};
`.trim()
}