Skip to content

API 使用文档 (TypeScript JavaScript)

chengeng edited this page Aug 22, 2022 · 1 revision

BOT

创建BOT

import { WechatyBuilder } from 'wechaty'
import { PuppetEngine } from "wechaty-puppet-engine";

export function createBot(): Wechaty {
  const puppet = new PuppetEngine({
    port: 8089, // wechaty 服务的端口号,默认8089 运行多个微信 记得这里要更换
    runLocal: true, // 微信和服务是否运行在同一台机器 默认是 建议也是运行在同一台机器
    httpServer: 'http://127.0.0.1:8055', // hook的地址 dll注入的时候有配置微信hook的监听端口号
  });

  return WechatyBuilder.build({
    name: "BotName",
    puppet,
  });
}

启动并登录BOT

import QRCode from "qrcode-terminal";

export async function prepareBot(): Promise<Wechaty> {
  const bot = createBot();
  
  .on("login", (user: Contact) => {
    // login 事件并不代表登录完全完成,只是通知目前登录的账号是什么,后续仍有初始化任务需要完成。
    log.info("TestBot", "%s login", user);
  })
  

  await bot.start();
  await bot.ready();

  // !!!只有当 bot ready 才能保证 bot 已经完全登录且初始化成功,这之后才能调用 bot 各种API

  return bot;
}

Message

/**
 * toUserId: wxid_xxx | xxx@chatroom
 * payload: string | number | Message | Contact | FileBox | MiniProgram | UrlLink
 */)
const sendMessage = async (toUserId: string, payload: any): Promise<Message> => {
  const toContact = await bot.Contact.load(toUserId);
  const message = (await toContact.say(payload)) as Message;
  return message;
};

发送文字

const message = await sendMessage("TO", "Hello World");

发送文字,群聊 @ 某人

const toRoom = await bot.Room.load("xxx@chatroom");

const atUserIdList = ["wxid_xxx", "wxid_yyy"];
const atUserList: Contact[] = [];
for (const userId of atUserIdList) {
  const contact = await bot.Contact.load(userId);
  atUserList.push(contact!);
}

const message = (await toRoom.say(payload, ...atUserList)) as Message;

发送图片

// 图片大小建议不要超过 2 M
const imageFilePath = "/Users/.../image.jpeg";
const fileBox = FileBox.fromFile(imageFilePath);

// const fileBox = FileBox.fromUrl("https://.../image.jpeg");

const message = await sendMessage("TO", fileBox);

发送视频

暂时只支持以文件形式发送

const videoFilePath = "/Users/.../video.mp4";
const fileBox = FileBox.fromFile(videoFilePath);

// const fileBox = FileBox.fromUrl("https://.../video.mp4");

const message = await sendMessage("TO", fileBox);

发送文件

const fileFilePath = "/Users/.../文件.pdf";
const fileBox = FileBox.fromFile(fileFilePath);

// const fileBox = FileBox.fromUrl("https://.../文件.pdf");

const message = await sendMessage("TO", fileBox);

发送链接

const urlLink = new bot.UrlLink({
  title: 'Hello World! 你好世界!',
  description: 'This is description。描述可中文',
  thumbnailUrl: 'https://img.aibotk.com/aibotk/public/yq3wWdBL0BnJV4Z1_sh.jpeg',
  url: 'http://wechat.aibotk.com/material/file',
});
const message = await sendMessage("TO", urlLink);

发送小程序卡片

// 封面图片为 cdn 图片
const miniProgramPayload: MiniProgramPayload = {
  description: '美团打车',
  title: '美团打车',
  pagePath: 'pages/index/index2.html?showCarKnowledge=false&source=xcx_sy2021',
  thumbUrl: 'https://img.aibotk.com/aibotk/public/yq3wWdBL0BnJV4Z1_meiri.jpeg',
  username: 'gh_b86a530798ae',
}

const miniProgram = new bot.MiniProgram(miniProgramPayload);

const message = await sendMessage("TO", miniProgram);

发送音乐卡片

// 封面图片为 cdn 图片
const musicPayload = {
  type: 'music', // 类型 固定 muisc
  name: '幸福了 然后呢', // 歌名
  author: 'A-Lin', // 作者
  appid: 'wx5aa333606550dfd5', // appid  酷狗 wx79f2c4418704b4f8,网易云 wx8dd6ecd81906fd84,QQ音乐 wx5aa333606550dfd5
  // 点击音乐卡片跳转地址
  jumpUrl: 'http://isure6.stream.qqmusic.qq.com/C400002QTAVi3WShQg.m4a?fromtag=30006&guid=2000000006&uin=0&vkey=5F8F57E2B07E3D952EB4FAAE0D2A965CBD055BA8D822EAB3D9F41871F5EAB464D68095C572917296B9934F5DE6722A52020E562793E5921A',
  // 网路歌曲直链
  musicUrl: 'http://isure6.stream.qqmusic.qq.com/C400002QTAVi3WShQg.m4a?fromtag=30006&guid=2000000006&uin=0&vkey=5F8F57E2B07E3D952EB4FAAE0D2A965CBD055BA8D822EAB3D9F41871F5EAB464D68095C572917296B9934F5DE6722A52020E562793E5921A',
  // 歌曲封面地址 cdn地址
  imageUrl: 'https://img.aibotk.com/aibotk/public/yq3wWdBL0BnJV4Z1_WechatIMG3550.jpeg'
}
// 注意添加顺序不可搞错,否则发不出去
const music = await bot.Post.builder()
        .add(musicPayload.type)
        .add(musicPayload.name)
        .add(musicPayload.author)
        .add(musicPayload.appid)
        .add(musicPayload.jumpUrl)
        .add(musicPayload.musicUrl)
        .add(musicPayload.imageUrl)
        .build()

const message = await sendMessage("TO", music);

接收消息

bot.on("message", async (message: Message) => {
    switch (message.type()) {
      // 文本消息
      case MessageType.Text:
        const text = message.text();
        break;

      // 图片消息
      case MessageType.Image:
        const res = await msg.toFileBox()
        log.info('res', res)
        await res.toFile('./' + res.name)
        break;

      // 链接卡片消息
      case MessageType.Url:
        const urlLink: UrlLink = await message.toUrlLink();
        // urlLink: 链接主要数据:包括 title,URL,description

        const urlThumbImage = await message.toFileBox();
        const urlThumbImageData = await urlThumbImage.toBuffer();
        // urlThumbImageData: 链接的缩略图二进制数据

        break;

      // 小程序卡片消息
      case MessageType.MiniProgram:
        const miniProgram: MiniProgram = await message.toMiniProgram();
        /*
        miniProgram: 小程序卡片数据
        {
          appid: "wx363a...",
          description: "贝壳找房 - 真房源",
          title: "美国白宫,10室8厅9卫,99999刀/月",
          iconUrl: "http://mmbiz.qpic.cn/mmbiz_png/.../640?wx_fmt=png&wxfrom=200",
          pagePath: "pages/home/home.html...",
          shareId: "0_wx363afd5a1384b770_..._1615104758_0",
          thumbKey: "84db921169862291...",
          thumbUrl: "3051020100044a304802010002046296f57502033d14...",
          username: "gh_8a51...@app"
        }
       */
        break;

      // 语音消息
      case MessageType.Audio:
        // 暂不支持下载
        break;

      // 视频消息
      case MessageType.Video:
        const video = await msg.toFileBox()
        log.info('res', video)
        await video.toFile('./' + video.name)
        // videoData: 视频文件二进制数
        break;

      // 动图表情消息
      case MessageType.Emoticon:
        // 暂不支持下载

        break;

      // 文件消息
      case MessageType.Attachment:
        const file = await msg.toFileBox()
        log.info('res',file)
        await file.toFile('./' + file.name)

        break;

      // 其他消息
      default:
        break;
    }
  });

Contact

获取所有联系人列表

// 所有联系人列表中,包含了聊天室中哪些不认识联系人
const allContactList = await bot.Contact.findAll();

// 获取你添加过的好友。和微信一样,不知道对方是否删除了你
const friendList = allContactList.filter(contact => contact.friend());

获取/设置BOT自己的昵称

const self = bot.userSelf();

// 获取
const oldName = self.name();

// 设置
const toName: string = "NEW NICK NAME"
await self.name(toName);

获取/设置他人备注

const contact = (await bot.Contact.find({ id: "wxid_xxx" }))!;

// 获取
const oldAlias = await contact.alias();

// 设置
await contact.alias("新的备注");

获取自己/其他人的头像

有可能获取不到,目前此dll 获取头像存在为空现象

// 自己
const selfContact = bot.userSelf();
const selfAvatarFileBox: FileBox = await selfContact.avatar();

// 他人
const contact = (await bot.Contact.find({ id:"wxid_xxx" }))!;
const otherAvatarFileBox: FileBox = await contact.avatar();

删除联系人

const puppet: PuppetEngine = bot.puppet as PuppetEngine;
await puppet.contactDelete(deleteUserName);

// contact 对象仍然可以得到,但是 friend 变为了 false
const contact = await bot.Contact.find({ id: deleteUserName });
expect(contact!.friend()).toBeFalsy();

通过好友申请

bot.on("friendship", async (friendship: Friendship) => {
  if (friendship.type() === FriendshipType.Receive) {
    await friendship.accept();
  }
});

添加联系人

// 这种方式的前提是:必须已经知道了对方的 id
const contact = await bot.Contact.find({ id: "wxid_" });
await bot.Friendship.add(contact!, hello);

通过手机号或QQ号搜索联系人,并添加好友

const contact = await bot.Friendship.search({ phone: "135xxx" });
await bot.Friendship.add(contact!, "朋友,你好");

Room

获取群聊列表

const allRooms = await bot.Room.findAll();

获取群聊成员

const memberList = await room.memberAll();

添加群聊成员

const room = (await bot.Room.find({ id: "xxx@chatroom" }))!;
const contact = await bot.Contact.find({ id: "wxid_" });
await room!.add(contact!);

// 稍微等待一下
await new Promise((resolve) => setTimeout(resolve, 1000));

// newMemberList 就包含新添加的成员了
const newMemberList = await room.memberAll();

获取群聊头像

const avatarFileBox = await room!.avatar();

获取/设置群聊名称

// 获取
const oldTopic = await room.topic();

// 设置
await room.topic("新的群聊名称");

有人加入群聊通知

bot.on("room-join", async (room: Room, inviteeList: Contact[], inviter: Contact, date) => {
  log.info(
    `on room join: ${room.toString()}, inviteeList: ${inviteeList.map((i) => i.id)}, inviter: ${
      inviter.id
    }, ${date}`
  );
});

有人退出群聊通知

只有自己是群主或管理员的群才能收到退出群聊通知。 只有自己移除或者他人移除的才有通知,自动退群的暂时不行

bot.on("room-leave", async (room: Room, leaverList: Contact[], remover?: Contact, date?: Date) => {
  log.info(
    `on room leave: ${room.toString()}, leaverList: ${leaverList.map((l) => l.id)}, remover: ${
      remover?.id
    } ${date}`
  );
});

群聊名称发生变化通知

bot.on("room-topic", async (room: Room, newTopic: string, oldTopic: string, changer: Contact, date?: Date) => {
  log.info(`on room topic: ${room.toString()}, ${newTopic}, ${oldTopic}, ${changer.toString()}, ${date}`);
});

本文档主要针对 Engine 给出使用示例,更丰富的文档请查阅:Wechaty 官方文档

Clone this wiki locally