BOT
创建BOT
import { Puppet } from "wechaty-puppet";
export function createBot(): Wechaty {
const token: string = "YOUR_TOKEN....";
const puppet = new Puppet({
token,
});
return new Wechaty({
name: "BotName",
puppet,
});
}
启动并登录BOT
import QRCode from "qrcode-terminal";
export async function prepareBot(): Promise<Wechaty> {
const bot = createBot();
// 在这里监听感兴趣的 bot 事件回调。
bot.on("scan", (qrcode: string, status: ScanStatus) => {
if (status === ScanStatus.Waiting && qrcode) {
log.info(
"TestBot",
`onScan: ${ScanStatus[status]}(${status})\n\n ▼▼▼ Please scan following qr code to login ▼▼▼\n`
);
QRCode.generate(qrcode, { small: true });
} else {
log.info("TestBot", `onScan: ${ScanStatus[status]}(${status})`);
}
})
.on("login", (user: Contact) => {
// login 事件并不代表登录完全完成,只是通知目前登录的账号是什么,后续仍有初始化任务需要完成。
log.info("TestBot", "%s login", user);
})
.on("logout", (user: Contact, reason: string) => {
log.info("TestBot", "%s logout, reason:%s", user, reason);
});
await bot.start();
await bot.ready();
// !!!只有当 bot ready 才能保证 bot 已经完全登录且初始化成功,这之后才能调用 bot 各种API
return bot;
}
- 完全事件列表参考:
WechatyEventName
。以及每个事件对应的回调数据类型:WechatyEventFunction
主动登出BOT
await bot.logout();
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;
发送联系人卡片
const contactCard = (await bot.Contact.find({ id: "wxid_" }))!;
await await sendMessage("TO", contactCard);
发送图片
// 图片大小建议不要超过 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);
发送语音
// 语音文件为 silk 格式。silk 是 skype 开源的一款语音编解码器,被微信的语音文件所采用。
// 注意:文件后缀必须是 `sil`!
const voiceFilePath = "/Users/.../voice.sil"
const voiceLength = 6000; // 需要提供语音长度,单位为毫秒
const fileBox = FileBox.fromFile(voiceFilePath);
fileBox.mimeType = "audio/silk";
fileBox.metadata = {
voiceLength,
};
const message = await sendMessage("TO", fileBox);
- silk 转码工具:
silk-v3-decoder
发送视频
const videoFilePath = "/Users/.../video.mp4";
const fileBox = FileBox.fromFile(videoFilePath);
// const fileBox = FileBox.fromUrl("https://.../video.mp4");
const message = await sendMessage("TO", fileBox);
发送链接
const urlLink = new UrlLink({
title: "Hello World! 你好世界!",
description: "This is description。描述可中文",
thumbnailUrl: "https://.../thumb.jpg",
url: "https://...",
});
const message = await sendMessage("TO", urlLink);
发送小程序卡片
// 封面图片为 cdn 图片
const miniProgramPayload: MiniProgramPayload = {
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"
}
// 封面图片为自定义外部图片(注意控制图片大小)
const miniProgramPayload: MiniProgramPayload = {
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: undefined,
thumbUrl: "https://.../thumb.jpeg", // 推荐在 200K 以内,比例 5:4,宽度不大于 1080px
username: "gh_8a51...@app",
};
const miniProgram = new MiniProgram(miniProgramPayload);
const message = await sendMessage("TO", miniProgram);
发送表情(动图)
这些参数就是收到的表情消息中的数据。
const emoticonBox = FileBox.fromUrl("http://emoji.qpic.cn/wx_emoji/.../", `message-emotion.jpg`);
emoticonBox.mimeType = "emoticon";
emoticonBox.metadata = {
md5: "45229f68c17167f57ba9393004fcef98",
len: 12345,
type: 2,
gameext: "",
};
const message = await sendMessage("TO", emoticonBox);
撤回消息
await message.recall()
接收消息
bot.on("message", async (message: Message) => {
switch (message.type()) {
// 文本消息
case MessageType.Text:
const text = message.text();
break;
// 图片消息
case MessageType.Image:
const messageImage = await message.toImage();
// 缩略图
const thumbImage = await messageImage.thumbnail();
const thumbImageData = await thumbImage.toBuffer();
// thumbImageData: 缩略图图片二进制数据
// 大图
const hdImage = await messageImage.hd();
const hdImageData = await hdImage.toBuffer();
// 大图图片二进制数据
// 原图
const artworkImage = await messageImage.artwork();
const artworkImageData = await artworkImage.toBuffer();
// artworkImageData: 原图图片二进制数据
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:
const audioFileBox = await message.toFileBox();
const audioData: Buffer = await audioFileBox.toBuffer();
// audioData: silk 格式的语音文件二进制数据
break;
// 视频消息
case MessageType.Video:
const videoFileBox = await message.toFileBox();
const videoData: Buffer = await videoFileBox.toBuffer();
// videoData: 视频文件二进制数
break;
// 动图表情消息
case MessageType.Emoticon:
const emotionFile = await message.toFileBox();
const emotionData: Buffer = await emotionFile.toBuffer();
// emotionData: 动图 Gif文件 二进制数据
break;
// 文件消息
case MessageType.Attachment:
const attachFileBox = await message.toFileBox();
const attachData = await attachFileBox.toBuffer();
// attachData: 文件二进制数据
break;
// 其他消息
default:
break;
}
});
- silk 转码工具:
silk-v3-decoder
Contact
获取所有联系人列表
// 所有联系人列表中,包含了聊天室中哪些不认识联系人
const allContactList = await bot.Contact.findAll();
// 获取你添加过的好友。和微信一样,不知道对方是否删除了你
const friendList = allContactList.filter(contact => contact.friend());
判断联系人是否为好友
const isFriend: boolean = contact.friend();
获取/设置BOT自己的昵称
const self = bot.currentUser;
// 获取
const oldName = self.name();
// 设置
const toName: string = "NEW NICK NAME"
await self.name(toName);
获取BOT自己的二维码
const self = bot.currentUser;
const qrStr = await self.qrcode();
// 再用二维码生成工具将 qrStr 生成为二维码即可
设置BOT自己的签名
const self = bot.currentUser;
await self.signature("NEW SIGNATURE");
获取/设置他人昵称
const contact = (await bot.Contact.find({ id: "wxid_xxx" }))!;
// 获取
const oldAlias = await contact.alias();
// 设置
await contact.alias("新的备注");
获取自己/其他人的头像
// 自己
const selfContact = bot.currentUser;
const selfAvatarFileBox: FileBox = await selfContact.avatar();
// 他人
const contact = (await bot.Contact.find({ id:"wxid_xxx" }))!;
const otherAvatarFileBox: FileBox = await contact.avatar();
删除联系人
const puppet = bot.puppet ;
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);
通过手机号搜索联系人,并添加好友
const contact = await bot.Friendship.search({ phone: "135xxx" });
await bot.Friendship.add(contact!, "朋友,你好");
通过微信号搜索联系人,并添加好友
const contact = await bot.Friendship.search({ weixin: "wx" });
await bot.Friendship.add(contact!, "朋友,你好");
Room
获取群聊列表
const allRooms = await bot.Room.findAll();
创建群聊
// 至少两个其他好友
const memberIdList = ["wxid_xxx", "wxid_yyy"];
const contactList = [];
for (const userId of memberIdList) {
const contact = await bot.Contact.find({ id: userId });
contactList.push(contact!);
}
const roomName = "room.....";
const newRoom = await bot.Room.create(contactList, roomName);
await newRoom.ready();
await newRoom.say("Hello World!");
获取群聊成员
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 room = (await bot.Room.find({ id: "xxx@chatroom" }))!;
const contact = await bot.Contact.find({ id: "wxid_" });
await room.del(contact!);
// 稍微等待一下
await new Promise((resolve) => setTimeout(resolve, 1000));
// newMemberList 就不包含被删除的联系人
const newMemberList = await room.memberAll();
获取群聊头像
const avatarFileBox = await room!.avatar();
获取群聊二维码
二维码需要 bot 保持在线至少24小时后才能获取,否则会出现“生成的群二维码已经失效”的错误
const qrString = await room.qrCode();
// 用二维码生成工具,将 qrString 生成为二维码即可
获取/设置群聊名称
// 获取
const oldTopic = await room.topic();
// 设置
await room.topic("新的群聊名称");
获取/设置群公告
// 获取
const announcement = await room.announce();
// 设置
await room.announce("新的群公告");
退出群聊
await room.quit();
通过群邀请(加入他人的群聊)
bot.on("room-invite", async (roomInvite: RoomInvitation) => {
await roomInvite.accept();
});
有人加入群聊通知
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}`);
});
Tag
为用户添加标签
const tag = await bot.Tag.get("标签名称");
const contact = await bot.Contact.find({ id: "wxid_xxx" });
await tag.add(contact!);
删除用户标签
const tag = await bot.Tag.get("标签名称");
const contact = await bot.Contact.find({ id: "wxid_xxx" });
await tag.remove(contact!);
删除标签
const tag = await bot.Tag.get("标签名称");
await bot.Tag.delete(tag);
获取用户标签
const contact = await bot.Contact.find({ id: "wxid_xxx" });
const tags = await contact!.tags();