koishijs / novelai-bot

Generate images by NovelAI | 基于 NovelAI 的画图机器人
https://bot.novelai.dev
MIT License
2.48k stars 204 forks source link

Bug: 使用-r参数自定义分辨率报错 #159

Closed yi03 closed 1 year ago

yi03 commented 1 year ago

Describe the bug

在qq或沙盒里发送nai -r 512x512 cat,bot回复选项 resolution 输入无效,Cannot read properties of null (reading 'authority')。 不加-r参数bot回复正常。发送nai -r landscape catbot回复正常。

Steps to reproduce

在qq或沙盒里发送nai -r 512x512 cat

Expected behavior

bot发送一张512x512大小的图

Screenshots

QQ截图20230124101411

Relevant log output

[W] i18n missing "Cannot read properties of null (reading 'authority')"

Launcher

Koishi CLI (koishi start)

Backend

Stable Diffusion WebUI (AUTOMATIC1111)

Versions

Additional context

已开启sqlite数据库插件

MaikoTan commented 1 year ago

Why does nai -r 512x512 girl not work?

https://github.com/koishijs/koishi/blob/605c59bece1bde69facc8b2ff11f0a3b5df091c0/packages/core/src/middleware.ts#L254-L258

    if (this.ctx.database) {
      if (session.subtype === 'group') {
        // attach group data
        const channelFields = new Set<Channel.Field>(['flag', 'assignee', 'guildId', 'locale'])
        this.ctx.emit('before-attach-channel', session, channelFields)

When Koishi received a command and there is a database usable, Koishi would emit a before-channel-attached and before-user-attached BEFORE calling the observeChannel and observeUser function that would write properties into session.user the Proxy object.

https://github.com/koishijs/koishi/blob/605c59bece1bde69facc8b2ff11f0a3b5df091c0/packages/core/src/middleware.ts#L95-L97

    ctx.before('attach-channel', (session, fields) => {
      session.collect('channel', session.argv, fields)
    })

In middleware.ts, an event listener was registered with the before-channel-attached event, it would call the session.collect function.

https://github.com/koishijs/koishi/blob/605c59bece1bde69facc8b2ff11f0a3b5df091c0/packages/core/src/session.ts#L299

      if (!this.resolveCommand(argv)) return

https://github.com/koishijs/koishi/blob/605c59bece1bde69facc8b2ff11f0a3b5df091c0/packages/core/src/session.ts#L331

      const { options, args, error } = argv.command.parse(argv)

In the session.collect function, Koishi would try to resolve a command's details with session.resolveCommand function, which calling the argv.command.parse function.

https://github.com/koishijs/koishi/blob/605c59bece1bde69facc8b2ff11f0a3b5df091c0/packages/core/src/command/parser.ts#L346-L351

    // apply domain callback
    const transform = resolveType(type)
    if (transform) {
      try {
        return transform(source, argv.session)
      } catch (err) {

So then in the parse function, following the calling chain parse -> parseValue -> transform, the transform function is exactly the faulted resolution function in this case.

As I mentioned at the first, the argv.session here has only a null user property. Considering the code history, this code should not work at first.

But why does 约稿 -r 512x512 girl work?

Just as the workflow mentioned above, the shortcut command also following it, getting into session.resolveCommand function. But differently, the first line if (this.inferCommand(argv) was failing here (because the content was not be parsed), which making it an early return.

https://github.com/koishijs/koishi/blob/605c59bece1bde69facc8b2ff11f0a3b5df091c0/packages/core/src/session.ts#L328-L329

  resolveCommand(argv) {
    if (!this.inferCommand(argv)) return

So then the calling chain return-ed with nothing, nothing, nothing...until going back to the _process function mentioned at the top. Originally this message would be ignored cause it is not getting into the command invoke, but thanks to the session.response function assigned above in the _executeMatcher function, which makes it getting into the session once again.

https://github.com/koishijs/koishi/blob/605c59bece1bde69facc8b2ff11f0a3b5df091c0/packages/core/src/middleware.ts#L199-L202

    session.response = async () => {
      const output = await session.resolve(response, params)
      return segment.normalize(output, params.map(source => source ? segment.parse(source) : ''))
    }

https://github.com/koishijs/koishi/blob/605c59bece1bde69facc8b2ff11f0a3b5df091c0/packages/core/src/middleware.ts#L285-L286

    if (session.response) return session.response()
    return next()

Note here, the session here was already assigned with the session.user property with the observerUser function. So when the session.response was called that cycling the command again, following the steps above makes the resolution function functioning accidently.

MaikoTan commented 1 year ago

So the investigation is here. I would fix this bug on our side (I mean novelai-bot), and then let's see yasasii @shigma -chan would take the bug-fixing work with Koishi itself or not.