tencent-connect / bot-node-sdk

QQ频道机器人 NODESDK
MIT License
111 stars 38 forks source link

无法使用本地图片发送消息 #76

Open feilongproject opened 2 years ago

feilongproject commented 2 years ago

官方API文档里有说明,可以通过multipart/form-data参数里的file_image字段,直接通过文件上传的方式发送图片,请求适配

Giftia commented 2 years ago

附议,帖子的接口nodeSDK也该跟进一下

1011382654 commented 2 years ago

+1

feilongproject commented 2 years ago

目前自己写了个example

import FormData from 'form-data';////需要自己安装
import fetchfrom 'node-fetch';//需要自己安装
import fs from 'fs';
import { IMessage } from 'qq-guild-bot';

export async function sendImage(msg: IMessage, picName: string,) {

    picName = picName?.startsWith("/") ? picName : `${config.picPath.out}/${picName}`;
    log.debug(`uploading ${picName}`);

    var picData = fs.createReadStream(picName);

    var formdata = new FormData();
    formdata.append("msg_id", msg.id);
    //formdata.append("content", "123456");
    formdata.append("file_image", picData);

    await fetch(`https://api.sgroup.qq.com/channels/${msg.channel_id}/messages`, {
        method: "POST",
        headers: {
            "Content-Type": formdata.getHeaders()["content-type"],
            "Authorization": `Bot ${config.appID}.${config.token}`
        },
        body: formdata

    }).then(async res => {
        const body = await res.json();
        if (body.code)
            throw new Error(body);

    }).catch(error => {
        log.error(error);
    })

}
NWYLZW commented 2 years ago

https://github.com/satorijs/qq-guild-sdk/blob/master/packages/core/test/index.spec.ts#L48-L53

欢迎试试我的,或者尝试一下 koishi ,koishi 的 adapter 也是基于该 sdk 封装的

2-3-5-7 commented 1 week ago

如果有人想自己实现发送图片,楼上的代码是对的,但是有一点需要说明,就是主动发送图片会触发异常,异常消息为正在审核,也就是 fetch 后始终跳转到 catch 中,这是正常的。我用了一个 promise 来等待审核通过

let passPromise = null;
    try {
        const response = await axios.post(apiUrl, formData, {
                headers: {
                    ...formData.getHeaders(),
                    Authorization: `Bot ${qqGuildConfig.appID}.${qqGuildConfig.token}`
                }, timeout: 10 * 1000
            }
        );
        // 正常会有异常,触发审核,不会走到下面
        console.log(`[${new Date().toLocaleString()} Guild] 图片发送成功: photo ${photoPath}`, response.data);
        return response;
    } catch (error) {
        // 等待审核中
        if (error.response.status === 500 && error.response.data.code === 304023) {
            passPromise = new Promise((resolve, reject) => {
                handlePassPromise = data => {
                    if (data.msg.audit_id === error.response.data.data.message_audit.audit_id) {
                        if (data.eventType === 'MESSAGE_AUDIT_PASS')
                            resolve(data);
                        else
                            reject(data);
                    }
                };
                setTimeout(() => reject(new Error('审核超时')), 10 * 1000);
            });
        } else {
            console.error(`[${new Date().toLocaleString()} Guild] 图片发送失败: photo ${photoPath}, error: ${error}`);
        }
    }
    if (passPromise) {
        try {
            const audit = await passPromise;
            console.log(`[${new Date().toLocaleString()} Guild] 图片发送成功: photo ${photoPath}`, audit);
            return audit;
        } catch ( error ) {
            console.error(`[${new Date().toLocaleString()} Guild] 审核未通过或超时: ${error}`);
        }
    }

    return null;

下面的 websocket 中调用处理函数,handlePassPromise 为全局变量

ws.on('MESSAGE_AUDIT', (data) => {
    console.log('[MESSAGE_AUDIT] 事件接收 :', data);
    if (handlePassPromise)
        handlePassPromise(data);
});