Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

qqnt新版图片无法反撤回的问题分析与解决方案 #84

Closed
luckksdfe opened this issue Sep 11, 2024 · 2 comments
Closed

qqnt新版图片无法反撤回的问题分析与解决方案 #84

luckksdfe opened this issue Sep 11, 2024 · 2 comments

Comments

@luckksdfe
Copy link

luckksdfe commented Sep 11, 2024

QQNT版本:9.9.15-27597 (32位)

在开启LiteLoaderQQNT-Anti-Recall插件后,我发现如果被撤回的消息未在QQ窗口中显示,则无法查看被撤回的图片,且图片显示为图裂的标志。
image

最初,我的想法是将QQ窗口保持在前台,并打开需要监控的群聊窗口以防止消息被撤回。然而,这种方法只能监控一个聊天窗口。

其背后的机制是,QQ仅会自动下载用户在打开窗口时可见的图片消息,而不是随时都下载并保存(这一设计实际上很合理,有助于减轻QQ系统的压力和用户的流量消耗)。

这个机制导致了问题的出现:如果窗口不可见,撤回的图片无法被自动下载和保存。(这是表象,实际上是图片下载的url有变所造成)

问题分析

为了解决这个问题,首先我们需要找到防撤回功能的源码部分,看看其实现方式:

// 下载被撤回的图片(抄自Lite-Tools)
async function processPic(msgItem) {
  msgItem?.elements?.forEach(async (el) => {
    if (el?.picElement) {
      output(el.originImageUrl);
      output(el);
      const pic = el.picElement;
      const picName = pic.md5HexStr.toUpperCase();
      const picUrl = `https://gchat.qpic.cn/gchatpic_new/0/0-0-${picName}/`;
      output("Download lost pic(s)... url=", picUrl, "msgId=", msgItem.msgId);
      if (!fs.existsSync(pic.sourcePath)) {
        output("Download pic:", `${picUrl}0`);
        const body = await request(`${picUrl}0`);
        fs.mkdirSync(path.dirname(pic.sourcePath), { recursive: true });
        fs.writeFileSync(pic.sourcePath, body);
      } else {
        output("Pic already existed, skip.", pic.sourcePath);
      }

从代码中可以看出,程序通过拼接图片的MD5值,向gchat.qpic.cn域名发起请求来下载图片。

不过,当我手动使用curl访问这个地址时,收到的返回结果是HTTP ERROR 400。通过Bing查询相关资料,得知该域名广泛用于图片下载,只需知道图片的MD5即可访问。可能由于滥用问题,腾讯已经弃用了这一接口。

我还找到一篇相关问题的issue:
[BUG] bot自身发出的图片url是无法访问的QQ旧图床链接 #343
LLOneBot/LLOneBot#343

接下来,看看LLOneBot的代码是如何实现的:

export const IMAGE_HTTP_HOST = 'https://gchat.qpic.cn'
export const IMAGE_HTTP_HOST_NT = 'https://multimedia.nt.qq.com.cn'

  async getImageUrl(element: PicElement) {
    if (!element) {
      return ''
    }
    const url: string = element.originImageUrl!  // 没有域名
    const md5HexStr = element.md5HexStr
    const fileMd5 = element.md5HexStr

    if (url) {
      const parsedUrl = new URL(IMAGE_HTTP_HOST + url) //临时解析拼接
      const imageAppid = parsedUrl.searchParams.get('appid')
      const isNewPic = imageAppid && ['1406', '1407'].includes(imageAppid)
      if (isNewPic) {
        let rkey = parsedUrl.searchParams.get('rkey')
        if (rkey) {
          return IMAGE_HTTP_HOST_NT + url
        }
        const rkeyData = await this.rkeyManager.getRkey()
        rkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey
        return IMAGE_HTTP_HOST_NT + url + rkey
      } else {
        // 老的图片url,不需要rkey
        return IMAGE_HTTP_HOST + url
      }
    } else if (fileMd5 || md5HexStr) {
      // 没有url,需要自己拼接
      return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 || md5HexStr)!.toUpperCase()}/0`
    }
    this.ctx.logger.error('图片url获取失败', element)
    return ''
  }
}

可以看到,这里的isNewPic部分负责判断是否使用新的图片下载逻辑。

调试过程

开启调试模式以查看日志:QQ.exe --enable-logging

打印el对象,修改代码:

// 下载被撤回的图片(源自Lite-Tools)
async function processPic(msgItem) {
  msgItem?.elements?.forEach(async (el) => {
    if (el?.picElement) {
      output(el.originImageUrl);
      output(el);

日志显示:

originImageMd5: '40B2CA803B2436E588C19DFCFF5C6C84',
originImageUrl: '/download?appid=1406&fileid=Cgk3OTkyNzcyOTESFPoVq_-vLoRr98fH6xLbp2hP6I3cGNbVCCD-Cij9jdSJ-7mIAw&spec=0',

其中,appid=1406与前文的isNewPic判断相符,意味着需要使用新的图片下载逻辑。

我们现在已经知道新的图片下载URL格式是:IMAGE_HTTP_HOST_NT + originImageUrl + rkey

获取rkey

rkey可以通过以下地址获取:https://llob.linyuchen.net/rkey,使用private_rkey

示例返回结果:

{
  "private_rkey": "&rkey=CAQSKAB6JWENi5LMM1551AAp6a_Ykh9RvPEkj7Vs91WuPERKpLK8jUYK-qE",
  "group_rkey": "&rkey=CAQSKAB6JWENi5LMM1551AAp6a_Ykh9RvPEkj7Vs91WuPERKNCyRF4OUoU8",
  "expired_time": 1726030493,
  "updated_time": "2024-09-11 12:04:53"
}

通过新的url下载图片

访问该链接后,可以成功下载图片:
https://multimedia.nt.qq.com.cn/download?appid=1406&fileid=Cgk3OTkyNzcyOTESFPoVq_-vLoRr98fH6xLbp2hP6I3cGNbVCCD-Cij9jdSJ-7mIAw&spec=0&rkey=CAQSKAB6JWENi5LMM1551AAp6a_Ykh9RvPEkj7Vs91WuPERKpLK8jUYK-qE
image


现在只剩下代码实现的工作了,交给作者!我先去吃饭了~~嘿嘿

@xh321
Copy link
Owner

xh321 commented Sep 11, 2024

非常感谢,我有空就写写~

@xh321
Copy link
Owner

xh321 commented Sep 12, 2024

基本修复了,等下个版本就ok啦

@xh321 xh321 closed this as completed Sep 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants