UNPKG

el-bot

Version:

A quick qq bot framework for mirai.

229 lines (202 loc) 6.19 kB
import type { CustomFields } from 'rss-parser' import consola from 'consola' import dayjs from 'dayjs' import { type Bot, defineBotPlugin } from 'el-bot' import fs from 'fs-extra' import { htmlToText } from 'html-to-text' import schedule from 'node-schedule' import Parser from 'rss-parser' interface RssConfig { name: string url: string cron: string customFields: CustomFields<string[], string[]> content: string[] target: object } class Rss { config: RssConfig parser: Parser constructor(public ctx: Bot, rssConfig: RssConfig) { const defaultConfig = { cron: '*/15 * * * *', customFields: { item: ['updated'], }, content: [ // eslint-disable-next-line no-template-curly-in-string '标题:${item.title}', // eslint-disable-next-line no-template-curly-in-string '链接:${item.link}', // eslint-disable-next-line no-template-curly-in-string '时间:${item.updated}', ], } rssConfig = Object.assign(defaultConfig, rssConfig) this.config = rssConfig this.parser = new Parser({ customFields: rssConfig.customFields, }) } init() { schedule.scheduleJob(this.config.cron, () => { this.parse() }) } async parse() { let feed: Parser.Output<any> try { feed = await this.parser.parseURL(this.config.url) } catch { this.ctx.logger.error( `[rss] ${this.config.name} 超时,${this.config.url} 解析失败`, ) return } if (feed.items && this.save(feed)) { // only semd first const content = feed.title + format(feed.items[0], this.config.content) this.ctx.sender.sendMessageByConfig(content, this.config.target) } } save(feed: any) { if (feed.items && feed.items.length <= 0) return false const path = `${this.ctx.tmpDir}rss.json` let rssJson: any = {} if (fs.existsSync(path)) { const data = fs.readFileSync(path) rssJson = JSON.parse(data.toString()) } else { fs.mkdirSync(this.ctx.tmpDir, { recursive: true }) } // 缓存文件不存在 或 对应对象不存在则更新 if ( Object.keys(rssJson).length === 0 || !rssJson[this.config.name] || (feed.items && rssJson[this.config.name].items[0].pubDate !== feed.items[0].pubDate) ) { this.ctx.logger.info(`[rss] ${feed.title} 已更新`) rssJson[this.config.name] = { title: feed.title, lastBuildDate: feed.lastBuildDate, items: [ { title: (feed.items as any)[0].title, pubDate: (feed.items as any)[0].pubDate, }, ], } fs.writeFileSync(path, JSON.stringify(rssJson)) this.ctx.logger.success(`[rss] 已在本地记录 ${feed.title} 新的 RSS 信息`) return true } else { this.ctx.logger.info(`[rss] ${feed.title} 未更新`) return false } } } function format(item: any, content: string[]) { item.updated = dayjs(item.updated).format('YYYY-MM-DD HH:mm:ss') item.pubDate = dayjs(item.pubDate).format('YYYY-MM-DD HH:mm:ss') if (item.summary) item.summary = htmlToText(item.summary) if (item.content) item.content = htmlToText(item.content) if (item.description) item.description = htmlToText(item.description) let template = '' // default template if (!content) { content = [ // eslint-disable-next-line no-template-curly-in-string '标题:${item.title}', // eslint-disable-next-line no-template-curly-in-string '链接:${item.link}', // eslint-disable-next-line no-template-curly-in-string '时间:${item.updated}', ] } content.forEach((line) => { template += `\n${line}` }) // not use eval // eslint-disable-next-line no-new-func return new Function('item', `return \`${template}\``)(item) } /** * 立即触发 RSS 抓取 * @param ctx * @param options */ export function triggerRss(ctx: Bot, options: RssConfig[]) { ctx.logger.success('[rss] 立即触发 RSS 抓取') let content = '您当前订阅的所有 RSS 源:' options.forEach((rssConfig: RssConfig) => { content += `\n${rssConfig.name}: ${rssConfig.url}` const rss = new Rss(ctx, rssConfig) rss.parse() }) return content } export type RssOptions = RssConfig[] export default defineBotPlugin<RssOptions>((options) => { return { extendCli(cli) { cli .command('rss', 'RSS 订阅', (args) => { return args .option('trigger', { alias: 't', type: 'boolean', describe: '立即触发 RSS 抓取', }) .option('list', { alias: 'l', type: 'string', describe: '订阅列表', }) }, ({ trigger, list }) => { consola.info(trigger, list) // const content = triggerRss(ctx, rssOptions) // const msg = mirai.curMsg as MessageType.GroupMessage // if (cmdOptions.list === 'current' && msg.sender.group) { // // 列出当前群 rss 订阅 // let rssList = '' // let count = 0 // options.forEach((rssConfig: RssConfig) => { // if (ctx.status.isListening(msg.sender, rssConfig.target)) { // count += 1 // rssList += `\n${rssConfig.name}: ${rssConfig.url}` // } // }) // if (count !== 0) // msg.reply(`本群共订阅了 ${count} 个 RSS 源:${rssList}`) // else // msg.reply('本群尚未订阅 RSS') // } // else if (cmdOptions.list === 'all') { // // 列出所有 rss 订阅 // if (ctx.user.isAllowed(msg.sender.id, true)) // msg.reply(content) // } }) }, setup(ctx) { // const { cli, napcat } = ctx const rssOptions = options consola.log(rssOptions) // 初始化定时 if (options) { options.forEach((rssConfig: RssConfig) => { const rss = new Rss(ctx, rssConfig) rss.init() }) } }, } })