smmcat-timingpush:群内定时推送

启用插件后,会在 koishi 实例目录下的 /data/smm_scheduledTask 生成一个 init.json 文件。修改它,完成基本的配置操作

设置定时修改案例:{时间:群号:[消息1,消息2, //...]}

{
    "12:00": {
      "1011033": ["中午好啊,要吃饭咯"],
      "1101111": ["中午好啊,要吃饭咯"],
    },
    "22:00": {
      "1011033": ["10点了,要睡觉咯", "请睡觉吧,不早了"],
    }
}

指令提供

  • /查询推送 查询本群的所有推送信息
  • /添加推送 添加指定时间段的本群推送 例:/添加推送 12:00 吃饭啊!
  • /删除推送 删除本群指定时间段的推送 例:/删除推送 12:00

由于我没有 onebot 的开发环境,就没有演示截图了。遇到问题请务必反馈


特殊声明:

  • 插件开启了一个 1分钟 执行一次的定时器,并非精确到秒。因此不保证准点 0 秒触发事件。但是会在该分钟的区间内去执行对应的播报时间。
  • 消息数组是随机抽取一条作为发送的。可设置多个消息模板,用于随机抽取一条发送
2 个赞

效果图来了

2 个赞

未来有开源的计划吗。想借鉴一下实现方式

2 个赞

都可以开源的,只是懒,没有提交 github

import { Context, Schema, Session } from 'koishi'
import fs from 'fs'
import path from 'path'

export const name = 'smmcat-timingpush'

export const usage = `
定时文件格式如下:
\`\`\`
{
    "12:00": {
      "1011033": ["中午好啊,要吃饭咯"],
      "1101111": ["中午好啊,要吃饭咯"],
    },
    "22:00": {
      "1011033": ["10点了,要睡觉咯", "请睡觉吧,不早了"],
    }
}
\`\`\`
`

export interface Config {
  questPath: string
}

export const Config: Schema<Config> = Schema.object({
  questPath: Schema.string().default('./data/smm_scheduledTask').description('任务执行信息路径')
})

export function apply(ctx: Context, config: Config) {

  // 任务队列
  let scheduledTaskList = {}

  function init() {
    const initPath = path.join(ctx.baseDir, config.questPath);
    console.log("定时任务初始化完成");
    if (!fs.existsSync(initPath)) {
      fs.mkdirSync(initPath, { recursive: true })
      fs.writeFileSync(path.join(initPath, 'init.json'), '{}', 'utf-8')
      console.log('定时推送文件已创建');
    }
    let content = fs.readFileSync(path.join(initPath, 'init.json'), 'utf-8');
    scheduledTaskList = content ? JSON.parse(content) : {};
  }


  function setLocalStoreData() {
    const initPath = path.join(ctx.baseDir, config.questPath, 'init.json')
    // 创建文件并写入内容
    fs.writeFileSync(initPath, JSON.stringify(scheduledTaskList), 'utf-8')
    console.log('更新内容完成');
  }

  // 写入数据
  function setData(params: { time: string, guildId: string, msg: string[] }) {
    const { time, guildId, msg } = params
    if (![time, guildId, msg].every(item => item)) return { code: false, msg: "缺少必要参数" }

    if (!scheduledTaskList[time]) {
      scheduledTaskList[time] = {}
    }
    if (!scheduledTaskList[time][guildId]) {
      scheduledTaskList[time][guildId] = []
    }

    scheduledTaskList[time][guildId].push(...msg)
    setLocalStoreData()
    return { code: true, msg: "添加成功" }
  }

  // 删除数据
  function delData(params: { time: string, guildId: string }) {
    const { time, guildId } = params
    if (![time, guildId].every(item => item)) return { code: false, msg: "缺少必要参数" }
    if (!scheduledTaskList[time]) {
      return { code: false, msg: "没有该时间段的事件" }
    }
    if (!scheduledTaskList[time][guildId]) {
      return { code: false, msg: "该时间段没有本群的定时任务" }
    }
    delete scheduledTaskList[time][guildId]
    setLocalStoreData()
    return { code: true, msg: "删除成功" }
  }

  // 查询数据
  function checkData(params: { guildId: string }) {
    const { guildId } = params
    if (![guildId].every(item => item)) return { code: false, msg: "缺少必要参数" }
    const temp = {}
    Object.keys(scheduledTaskList).forEach((time) => {
      Object.keys(scheduledTaskList[time]).forEach((qun) => {
        if (qun == guildId) {
          if (!temp[time]) {
            temp[time] = {}
          }
          temp[time][qun] = scheduledTaskList[time][qun]
        }
      })
    })
    const msg = Object.keys(temp).map((time) => {
      return Object.keys(temp[time]).map((qun) => {
        return `时间:${time}\n\n分发内容:${temp[time][qun].join('\n')}`
      }).join('\n')
    }).join('\n------------------------\n')
    return { code: true, msg: msg.length ? msg : '当前还没有定时事项' }
  }

  const userOrder = {
    setData,
    delData,
    checkData
  }

  // 定时业务
  const scheduledTask = {
    timer: null,
    // 定时任务队列
    async createScheduledTask() {
      // 创建定时任务
      console.log("启动定时任务");
      this.timer = ctx.setInterval(() => {
        // 查看是否存在任务
        scheduledTask.checkScheduledTask()
      }, 60000) 
    },
    // 格式化时间 
    formatTime(time: Date): string {
      const hours = time.getHours().toString().padStart(2, '0')
      const minutes = time.getMinutes().toString().padStart(2, '0')
      return `${hours}:${minutes}`
    },
    // 判断当前是否存在任务
    checkScheduledTask() {
      const time = new Date()
      // 获取当前时间字符值
      const keys = this.formatTime(time)
      console.log(keys, scheduledTaskList[keys]);

      // 存在事件时
      if (scheduledTaskList[keys]) {
        // 遍历需要发送群与要发送的内容
        Object.keys(scheduledTaskList[keys]).map((item) => {
          // 获取消息内容
          const msgList = scheduledTaskList[keys][item]
          const msg = msgList[Math.floor(Math.random() * msgList.length)]

          // 随机发送预设中的内容
          console.log('推送消息群:' + item);
          console.log('推送的消息:' + msg);
          ctx.bots[0].sendMessage(item, msg);
        })
      }
    },
    // 清除定时任务
    clearScheduledTask() {
      if (this.timer) {
        this.timer && this.timer()
        this.timer = null
      }
    }
  }

  ctx
    .command('取消推送')
    .action(async ({ session }) => {
      if (!scheduledTask.timer) {
        await session.send('未开启定时推送,取消失败')
      }
      scheduledTask.clearScheduledTask()
    })

  ctx
    .command('开始推送')
    .action(async ({ session }) => {
      if (scheduledTask.timer) {
        await session.send('已开启定时推送,无需再次开启')
      }
      scheduledTask.createScheduledTask()
    })

  ctx
    .command('查询推送')
    .action(async ({ session }) => {
      if (!checkIsGuild(session)) {
        return '该命令只能在群组内使用'
      }
      const data = userOrder.checkData({ guildId: session.guildId })
      if (!data.code) {
        return `[×] ` + data.msg
      }
      return `[√] ` + data.msg
    })

  ctx
    .command('添加推送 <time> <msg>')
    .action(async ({ session }, time, msg) => {
      if (!checkIsGuild(session)) {
        return '该命令只能在群组内使用'
      }
      if (!time) return `请输入时间。例如格式 /添加推送 12:00 内容`
      if (!msg) return `请输入定时需要推送的消息,例如格式 /添加推送 12:00 内容`
      const data = userOrder.setData({ guildId: session.guildId, time, msg: [msg] })
      if (!data.code) {
        return `[×] ` + data.msg
      }
      return `[√] ` + data.msg
    })

  ctx
    .command('删除推送 <time>')
    .action(async ({ session }, time) => {
      if (!checkIsGuild(session)) {
        return '该命令只能在群组内使用'
      }
      if (!time) return `请输入时间。例如格式 /删除推送 12:00`
      const data = userOrder.delData({ guildId: session.guildId, time })
      if (!data.code) {
        return `[×] ` + data.msg
      }
      return `[√] ` + data.msg
    })

  // 是否为群内操作
  function checkIsGuild(session: Session) {
    if (session.guildId) return true
    return false
  }

  ctx.on('ready', async () => {
    try {
      await init()
      scheduledTask.createScheduledTask()
    } catch (err) {
      ctx.logger('smmcat-timingpush').error(err)
    }
  })
}
3 个赞

大佬我发现我无法使用该插件,无论是直接修改josn文件或者是指令添加推送,均无法实现,有解决方法吗?大佬!!!

image

1 个赞

你这个报错…是重复启用插件。至于为什么不能添加推送我还不清楚

1 个赞

有个不算问题的问题,就是开启插件后会一直在控制台打印下边这些东西

21:55 undefined
21:56 undefined
21:57 undefined
21:58 undefined
21:59 undefined
22:00 undefined
22:01 undefined

虽然什么也不影响,但是有一点点打扰看东西,顺带提一嘴,不修也行(谢谢大佬,插件很好用

1 个赞

噢那个啊…是之前开发测试打印的东西。判断这个时段是否有事件…

我会抽空在一个修补版本中去掉

2 个赞

只推送一半是怎么回事

1 个赞

1 个赞

噢,那是我可能设置字数上限来着(不对啊,代码里我没设置字数上线啊)。
你实际到了10点后的提示应该没问题吧?

1 个赞

我试过了,就是实际推送的也是有限制字数的

1 个赞


我试了一下,在json里面修改,然后也是能改的,但是效果还是我那个问题,查询查不出来,然后在q群里面用指令添加推送之后,就会自动改成那种情况,然后所有在里面json设置的都是

↓这是使用指令添加推送后

1 个赞

噢噢,我明白了,因为你的内容有空格,参数会只识别部分。这个我晚点更新

我会把
添加推送 time <msg>
改为
添加推送 <time> msg:text>

另外,你若是手动修改完json文件,需要重启插件。不然不会同步的

2 个赞

更新到 smmcat-timingpush@0.0.9 解决这个问题

1 个赞

发现了个新bug,上次的问题倒是解决了,就是这次删除之后json里面的时间没有删除掉,虽然不是很严重,但是强迫症看着膈应

还有就是我发现添加推送只能在当前群聊里添加,如果这样的话我在一个群里添加推送就会有点刷屏的感觉,如果可以的话希望能加个功能,就是能够指定推送的群聊,或者所有机器人在的群聊都推送

1 个赞

一下推送给所有群?不建议,小心分分钟出事。

1 个赞

请更新到 smmcat-timingpush@0.0.10

后续删除群组的操作,都会判断该时间组内有无任意群组。若没有就会直接把该整个时间组移除。

结构为 时间组 → 群组 → 消息组

"12:00": {
      "1011033": ["中午好啊,要吃饭咯"],
      "1101111": ["中午好啊,要吃饭咯"]
    }

已添加新指令 /指定群添加推送 格式演示:

/指定添加推送 100321192 10:00 这是推送的内容


tip: 感谢提供建议,这些不是不能做,是有需求才做,且保证不用在坏事上

2 个赞


发现一个bug,如图,在我不小心添加了两个同样的时间段的推送后,无法用指令进行删除推送,两个都不能删除了,使用删除推送 12:00 会提示没有该时间段的事件,希望能通过序号删除,而不是删除某个时间段,比如删除推送 1这样的
还有就是查询推送的格式有点问题,“时间:”后接了群号,有点反直觉,倒是不影响使用

草 我自己发现了问题 我把群号也添加进推送内容了,怪不得删不了也推送不了


好像它本身就有点问题,是我打的格式有问题吗?

1 个赞

指令应该是 /指定群添加推送

你把群号当做时间存进去了,当然匹配不到了。应该写 /删除推送 1017072701

1 个赞