Drincann / Mirai-js

运行在 Node.js、浏览器下,基于 mirai-api-http 的 QQ 机器人开发框架。
https://mirai-js-drincann.vercel.app
GNU Affero General Public License v3.0
252 stars 31 forks source link

关于合并转发Forward类型 #201

Closed semilt closed 2 years ago

semilt commented 2 years ago

看了一下文档和代码里Message类里似乎没有合并转发的相关内容? 我自己添加了相关的代码: Message.js

class Forward extends MessageType {
  constructor({
    nodeList
  }) {
    super({
      type: 'Forward'
    });
    this.nodeList = nodeList;
  }

}
...
  addForward(senderId, time, senderName, messageChain) {
    let nodeList = {
      senderId: senderId,
      time: time.getTime(),
      senderName: senderName,
      messageChain: messageChain.getMessageChain()
    };
    this.messageChain.push(new Forward({
      nodeList
    }));
    return this;
  }

Message.d.ts

addForward(senderId: Number, time: Date, senderName: String, messageChain: Message): Message;

但是在使用时会发生报错:

mirai-js: error core.sendGroupMessage (node:27016) UnhandledPromiseRejectionWarning: Error: 无效参数

似乎是在与mcl的post发送出现了问题,可能是什么原因呢?

semilt commented 2 years ago

懂了,似乎是messageChain位置的参数有问题,我考虑一下怎么处理吧

Drincann commented 2 years ago

@semilt 我感觉我们要做的应该是,以某种语义化的方式管理一个或多个 nodeList,如果每次 addForward 总是合并转发一条消息,似乎有些奇怪。

Thank you for contribution.

semilt commented 2 years ago

@Drincann 有时会有无中生有一个nodeList的需求存在(就比如我现在在做的),因此我认为addForward方法还是有必要的,只是刚刚因为我没有完全理解MessageType里nodeList的部分,误以为它是一个对象才引发了当时的错误,现在我已经弄明白怎么回事了。 关于语义化,其实我不是很明白语义化具体指什么?

Drincann commented 2 years ago

@semilt 我有一个思路,我们实现 beginForward 和 endForward,开发者的代码就像这样:

(new Message)
.beginForward()
.addMessageNode(/*msg or msg chain*/)
.addMessageNode(...)
.endForward()
.addText(...)
.add...

我们可以包装一个 ForwardManager class,让 Message.beginForward 返回一个 ForwardManager class 的实例,然后在 endForward 的时候返回之前的 Message 的实例,同时处理一个 nodeList 到 MessageChain 中。(不过我疑惑的是,可以再一个 messagechain 中同时出现合并转发消息和其他消息吗?我好像没见过这种消息,它的行为会分开发送吗?)

我们当然可以在 Message 中通过维护状态来实现 Message 下的 beginForward 和 endForward,但这可能破坏了 Message 的语义,毕竟开发者可以在添加同一个 nodeList 的中途添加其他 MessageType,就像这样:

(new Message)
.beginForward()
.addText('this msg is not in the Forward nodeList but is really looks like is in it')
. addMessageNode(/*...*/)
.endForward()
.add...
Drincann commented 2 years ago

@semilt 语义化的 api 大概指的就是,非常易读、符合人类直觉且调用友好的 api。

semilt commented 2 years ago

@Drincann 合并转发应该不可以和其他消息一同发送,不如我们将合并转发直接单独包装在一个方法里?比如

bot.sendForward({
group: ...,
nodes: new FowardNode(),addNode()
})
Drincann commented 2 years ago

@semilt

我认为这个方案非常好。:+1:

Drincann commented 2 years ago

@semilt

我还没有调过这个 MessageType。看了文档之后有些不太理解,关于 senderId、time 以及 senderName 的作用。

为什么我们合并转发消息需要自己手动传入这些参数,难道这些参数不是历史消息所自己携带的吗。

或者说,nodeList 是可以手动创建的,不要求历史上有这条消息。

semilt commented 2 years ago

@Drincann nodeList可以无中生有,就是说我可以自己决定转发的消息中的内容、发送者、发送者的昵称还有时间,当然也可以只用一个messageId来引用一个之前存在的消息

semilt commented 2 years ago

@Drincann 那我尝试实现一下

Drincann commented 2 years ago

@Drincann nodeList可以无中生有,就是说我可以自己决定转发的消息中的内容、发送者、发送者的昵称还有时间,当然也可以只用一个messageId来引用一个之前存在的消息

@semilt 居然还能这样玩,这岂不是可以伪造聊天记录。:sweat:

semilt commented 2 years ago

@Drincann 所以这个虽然给了用户很大的自由(虽然我觉得就是单纯的腾讯想省事,毕竟每个聊天上下文里messageId都是独立的,跨聊天转发这样做最省事),但是如果有人居心叵测也会很麻烦

semilt commented 2 years ago

@Drincann 我写了一个版本,已经提交PR了