UNPKG

wechaty-puppet-padplus

Version:
1,053 lines 52.3 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const util_1 = __importDefault(require("util")); const path_1 = __importDefault(require("path")); const file_box_1 = __importDefault(require("file-box")); const array_flatten_1 = __importDefault(require("array-flatten")); const wechaty_puppet_1 = require("wechaty-puppet"); const config_1 = require("./config"); const request_queue_1 = require("./padplus-manager/api-request/request-queue"); const padplus_manager_1 = __importDefault(require("./padplus-manager/padplus-manager")); const schemas_1 = require("./schemas"); const room_convertor_1 = require("./convert-manager/room-convertor"); const room_event_join_message_parser_1 = require("./pure-function-helpers/room-event-join-message-parser"); const room_event_leave_message_parser_1 = require("./pure-function-helpers/room-event-leave-message-parser"); const room_event_topic_message_parser_1 = require("./pure-function-helpers/room-event-topic-message-parser"); const friendship_event_message_parser_1 = require("./pure-function-helpers/friendship-event-message-parser"); const pure_function_helpers_1 = require("./pure-function-helpers"); const contact_raw_payload_parser_1 = require("./pure-function-helpers/contact-raw-payload-parser"); const xml_to_json_1 = require("./pure-function-helpers/xml-to-json"); const PRE = 'PUPPET_PADPLUS'; class PuppetPadplus extends wechaty_puppet_1.Puppet { constructor(options = {}) { super(options); this.options = options; const token = this.options.token || config_1.padplusToken(); const name = this.options.name; if (token) { this.manager = new padplus_manager_1.default({ endpoint: this.options.endpoint || config_1.GRPC_ENDPOINT, name, token, }); } else { config_1.log.error(PRE, `can not get token info from options for start grpc gateway.`); throw new Error(`can not get token info.`); } } start() { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `start()`); this.state.on('pending'); yield this.startManager(this.manager); this.state.on(true); }); } startManager(manager) { const _super = Object.create(null, { login: { get: () => super.login } }); return __awaiter(this, void 0, void 0, function* () { manager.on('scan', (url, status) => __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `scan : ${url}, status: ${status}`); this.emit('scan', url, status); })); manager.on('login', (loginData) => __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `login success : ${util_1.default.inspect(loginData)}`); yield _super.login.call(this, loginData.userName); yield this.manager.syncContacts(); })); manager.setMemory(this.memory); manager.on('message', msg => this.onMessage(msg)); manager.on('ready', () => this.emit('ready')); manager.on('reset', (reason) => { this.emit('reset', reason); }); manager.on('heartbeat', (data) => { this.emit('watchdog', { data, }); }); manager.on('logout', () => this.logout()); manager.on('error', (err) => { this.emit('error', err); }); yield manager.start(); }); } stop() { return __awaiter(this, void 0, void 0, function* () { config_1.log.verbose(PRE, 'stop()'); if (!this.manager) { throw new Error('no padplus manager'); } if (this.state.off()) { config_1.log.verbose(PRE, 'stop() is called on a OFF puppet. await ready(off) and return.'); yield this.state.ready('off'); return; } this.state.off('pending'); yield this.manager.stop(); yield this.manager.removeAllListeners(); this.state.off(true); config_1.log.verbose(PRE, `stop() finished`); }); } logout() { return __awaiter(this, void 0, void 0, function* () { config_1.log.verbose(PRE, 'logout()'); yield this.manager.logout(this.selfId()); this.emit('logout', this.selfId()); this.id = undefined; this.emit('reset', 'padplus reset'); }); } onMessage(message) { return __awaiter(this, void 0, void 0, function* () { const messageType = message.msgType; switch (messageType) { case schemas_1.PadplusMessageType.Sys: yield Promise.all([ this.onRoomJoinEvent(message), this.onRoomLeaveEvent(message), this.onRoomTopicEvent(message), this.onFriendshipEvent(message), ]); break; case schemas_1.PadplusMessageType.VerifyMsg: yield this.onFriendshipEvent(message); break; case schemas_1.PadplusMessageType.Recalled: this.emit('message', message.msgId); yield this.onRoomJoinEvent(message); break; case schemas_1.PadplusMessageType.Text: yield this.onFriendshipEvent(message); this.emit('message', message.msgId); break; case schemas_1.PadplusMessageType.Contact: case schemas_1.PadplusMessageType.Image: case schemas_1.PadplusMessageType.Deleted: case schemas_1.PadplusMessageType.Voice: case schemas_1.PadplusMessageType.SelfAvatar: case schemas_1.PadplusMessageType.PossibleFriendMsg: case schemas_1.PadplusMessageType.ShareCard: case schemas_1.PadplusMessageType.Video: case schemas_1.PadplusMessageType.Emoticon: case schemas_1.PadplusMessageType.Location: case schemas_1.PadplusMessageType.App: case schemas_1.PadplusMessageType.VoipMsg: case schemas_1.PadplusMessageType.StatusNotify: case schemas_1.PadplusMessageType.VoipNotify: case schemas_1.PadplusMessageType.VoipInvite: case schemas_1.PadplusMessageType.MicroVideo: case schemas_1.PadplusMessageType.SelfInfo: case schemas_1.PadplusMessageType.SysNotice: case schemas_1.PadplusMessageType.N11_2048: case schemas_1.PadplusMessageType.N15_32768: default: this.emit('message', message.msgId); break; } }); } /** * ======================== * CONTACT SECTION * ======================== */ contactSelfQrcode() { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `contactSelfQrcode()`); return this.manager.contactSelfQrcode(); }); } contactSelfName(name) { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `contactSelfName(${name})`); yield this.manager.contactSelfName(name); }); } contactSelfSignature(signature) { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `contactSelfSignature(${signature})`); yield this.manager.contactSelfSignature(signature); }); } contactSelfInfo() { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `contactSelfInfo()`); return this.manager.contactSelfInfo(); }); } contactAlias(contactId, alias) { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `contactAlias(), contactId: ${contactId}, alias : ${alias}`); if (typeof alias === 'undefined') { const payload = yield this.contactPayload(contactId); return payload.alias || ''; } if (!this.manager) { throw new Error(`no padplus manage.`); } yield this.manager.setContactAlias(contactId, alias || ''); }); } contactAvatar(contactId, file) { return __awaiter(this, void 0, void 0, function* () { if (file) { if (contactId !== this.selfId()) { throw new Error(`can not set avatar for others.`); } if (!this.manager) { throw new Error(`no padplus manager.`); } return; } const payload = yield this.contactRawPayload(contactId); if (!payload || !payload.bigHeadUrl) { throw new Error(`can not find contact.`); } const fileBox = file_box_1.default.fromUrl(payload.bigHeadUrl, `wechaty-contact-avatar-${payload.userName}.jpg`); return fileBox; }); } contactList() { return __awaiter(this, void 0, void 0, function* () { config_1.log.verbose(PRE, `contactList()`); if (!this.manager) { throw new Error(`no padplus manager.`); } const selfId = this.selfId(); const contactIds = yield this.manager.getContactIdList(selfId); return contactIds; }); } contactRawPayload(contactId) { return __awaiter(this, void 0, void 0, function* () { if (!this.manager) { throw new Error(`no manager.`); } const payload = yield this.manager.getContactPayload(contactId); return payload; }); } contactRawPayloadParser(rawPayload) { return __awaiter(this, void 0, void 0, function* () { config_1.log.verbose(PRE, `contactRawPayloadParser()`); const payload = contact_raw_payload_parser_1.contactRawPayloadParser(rawPayload); return payload; }); } /** * ========================= * FRIENDSHIP SECTION * ========================= */ onFriendshipEvent(message) { return __awaiter(this, void 0, void 0, function* () { config_1.log.verbose(PRE, 'onPadplusMessageFriendshipEvent({id=%s})', message.msgId); /** * 1. Look for friendship confirm event */ const friendshipConfirmContactId = friendship_event_message_parser_1.friendshipConfirmEventMessageParser(message); /** * 2. Look for friendship receive event */ const friendshipReceiveContactId = yield friendship_event_message_parser_1.friendshipReceiveEventMessageParser(message); /** * 3. Look for friendship verify event */ const friendshipVerifyContactId = friendship_event_message_parser_1.friendshipVerifyEventMessageParser(message); if (friendshipConfirmContactId || friendshipReceiveContactId || friendshipVerifyContactId) { // Maybe load contact here since we know a new friend is added if (!this.manager) { throw new Error(`no manager.`); } const friendship = yield pure_function_helpers_1.friendshipRawPayloadParser(message); if (!friendship) { config_1.log.silly(PRE, `not friendship : ${message.msgId}`); return; } const { msgId } = message; yield this.manager.saveFriendship(msgId, friendship); this.emit('friendship', msgId); } }); } friendshipAdd(contactId, hello) { return __awaiter(this, void 0, void 0, function* () { config_1.log.verbose(PRE, `friendshipAdd(${contactId}, ${hello})`); if (!this.manager) { throw new Error('no padplus manager'); } const searchContact = yield this.manager.searchContact(contactId); /** * If the contact is not stranger, than using WXSearchContact can get userName */ if (searchContact.wxid !== '' && !pure_function_helpers_1.isStrangerV1(searchContact.v1) && !pure_function_helpers_1.isStrangerV2(searchContact.v2)) { config_1.log.verbose(PRE, `friendshipAdd ${contactId} has been friend with bot, no need to send friend request!`); return; } let strangerV1; let strangerV2; if (pure_function_helpers_1.isStrangerV1(searchContact.v1)) { strangerV1 = searchContact.v1; strangerV2 = searchContact.v2; } else if (pure_function_helpers_1.isStrangerV2(searchContact.v2)) { strangerV2 = searchContact.v2; strangerV1 = searchContact.v1; } else { throw new Error('stranger neither v1 nor v2!'); } const isPhoneNumber = contactId.match(/^[1]([3-9])[0-9]{9}$/); const res = yield this.manager.addFriend(contactId, hello, isPhoneNumber ? schemas_1.SearchContactTypeStatus.MOBILE : schemas_1.SearchContactTypeStatus.WXID, // default to wxid strangerV1 || '', strangerV2 || ''); if (res && res.status !== '0') { throw new Error(`add friend failed.`); } else if (res && res.status === '0') { config_1.log.silly(PRE, `add friend request success.`); } }); } friendshipAccept(friendshipId) { return __awaiter(this, void 0, void 0, function* () { config_1.log.verbose(PRE, `friendshipAccept(${friendshipId})`); if (!this.manager) { throw new Error(`no manager.`); } const payload = yield this.manager.getFriendship(friendshipId); if (!payload || payload.type !== wechaty_puppet_1.FriendshipType.Receive) { throw new Error(`friendship type error.`); } const { contactId, stranger, ticket } = payload; if (!stranger || !ticket) { throw new Error(`friendship data error, stranger or ticket is null.`); } yield this.manager.confirmFriendship(contactId, stranger, ticket); }); } friendshipRawPayload(friendshipId) { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `friendshipId : ${util_1.default.inspect(friendshipId)}`); if (!this.manager) { throw new Error(`no manager.`); } const payload = yield this.manager.getFriendship(friendshipId); if (payload) { return payload; } throw new Error(`can not find friendship.`); }); } friendshipRawPayloadParser(rawPayload) { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `friendship rawPayload : ${util_1.default.inspect(rawPayload)}`); return rawPayload; }); } /** * ======================== * MESSAGE SECTION * ======================== */ messageFile(messageId) { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `messageFile() messageId : ${util_1.default.inspect(messageId)}`); const rawPayload = yield this.messageRawPayload(messageId); const payload = yield this.messagePayload(messageId); let filename = payload.filename || payload.id; const type = payload.type === wechaty_puppet_1.MessageType.Image ? 'img' : payload.type === wechaty_puppet_1.MessageType.Video ? 'video' : 'file'; switch (payload.type) { case wechaty_puppet_1.MessageType.Image: case wechaty_puppet_1.MessageType.Attachment: case wechaty_puppet_1.MessageType.Video: let content = rawPayload.content; const mediaData = { appMsgType: type === 'file' ? 6 : 0, content, contentType: type, createTime: rawPayload.createTime, fileName: rawPayload.fileName || '', fromUserName: rawPayload.fromUserName, msgId: rawPayload.msgId, msgType: rawPayload.msgType, src: rawPayload.url, toUserName: rawPayload.toUserName, }; const data = yield request_queue_1.RequestQueue.exec(() => this.manager.loadRichMediaData(mediaData)); if (data && data.src) { const name = path_1.default.parse(data.src).base; return file_box_1.default.fromUrl(encodeURI(data.src), name); } else { throw new Error(`Can not get media data url by this message id: ${messageId}`); } case wechaty_puppet_1.MessageType.Emoticon: if (rawPayload && rawPayload.url) { return file_box_1.default.fromUrl(rawPayload.url); } else { throw new Error(`can not get image/audio url fot message id: ${messageId}`); } case wechaty_puppet_1.MessageType.Audio: if (rawPayload && rawPayload.url) { const fileBox = file_box_1.default.fromUrl(rawPayload.url); let contentXML; if (pure_function_helpers_1.isRoomId(rawPayload.fromUserName)) { contentXML = rawPayload.content.split(':\n')[1]; } else { contentXML = rawPayload.content; } const content = yield xml_to_json_1.xmlToJson(contentXML); fileBox.metadata = { voiceLength: content.msg.voicemsg.$.voicelength / 1000, }; return fileBox; } else { throw new Error(`can not get image/audio url fot message id: ${messageId}`); } default: const base64 = 'Tm90IFN1cHBvcnRlZCBBdHRhY2htZW50IEZpbGUgVHlwZSBpbiBNZXNzYWdlLgpTZWU6IGh0dHBzOi8vZ2l0aHViLmNvbS9DaGF0aWUvd2VjaGF0eS9pc3N1ZXMvMTI0OQo='; filename = 'wechaty-puppet-padplus-message-attachment-' + messageId + '.txt'; return file_box_1.default.fromBase64(base64, filename); } }); } messageUrl(messageId) { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `messageUrl() messageId : ${util_1.default.inspect(messageId)}`); const rawPayload = yield this.messageRawPayload(messageId); const payload = yield this.messagePayload(messageId); if (payload.type !== wechaty_puppet_1.MessageType.Url) { throw new Error('Can not get url from non url payload'); } else { const appPayload = yield pure_function_helpers_1.appMessageParser(rawPayload); if (appPayload) { return { description: appPayload.des, thumbnailUrl: appPayload.thumburl, title: appPayload.title, url: appPayload.url, }; } else { throw new Error('Can not parse url message payload'); } } }); } messageContact(messageId) { return __awaiter(this, void 0, void 0, function* () { throw new Error(`not implement`); }); } messageMiniProgram(messageId) { config_1.log.silly(PRE, `messageMiniProgram() messageId : ${util_1.default.inspect(messageId)}`); throw new Error('Method not implemented.'); } messageForward(receiver, messageId) { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `messageForward() receiver: ${receiver}, messageId : ${util_1.default.inspect(messageId)}`); const payload = yield this.messagePayload(messageId); if (payload.type === wechaty_puppet_1.MessageType.Text) { if (!payload.text) { throw new Error('no text'); } yield this.messageSendText(receiver, payload.text); } else if (payload.type === wechaty_puppet_1.MessageType.Audio) { throw new Error('Message type Audio not supported.'); } else if (payload.type === wechaty_puppet_1.MessageType.Url) { yield this.messageSendUrl(receiver, yield this.messageUrl(messageId)); } else if (payload.type === wechaty_puppet_1.MessageType.MiniProgram) { yield this.messageSendMiniProgram(receiver, yield this.messageMiniProgram(messageId)); } else if (payload.type === wechaty_puppet_1.MessageType.ChatHistory) { throw new Error('Message type ChatHistory not supported.'); } else { yield this.messageSendFile(receiver, yield this.messageFile(messageId)); } }); } generateBaseMsg(msgId, to) { const msg = { content: '', createTime: new Date().getTime(), fromUserName: this.selfId(), imgStatus: 0, l1MsgType: 0, msgId, msgSource: '', msgSourceCd: 0, msgType: schemas_1.PadplusMessageType.Text, newMsgId: Number(msgId), pushContent: '', status: schemas_1.PadplusMessageStatus.One, toUserName: to, uin: '', wechatUserName: this.selfId(), }; config_1.log.silly(PRE, 'generateBaseMsg(%s) %s', to, JSON.stringify(msg)); this.manager.cachePadplusMessagePayload.set(msgId, msg); return msg; } messageSendText(receiver, text, mentionIdList) { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, 'messageSend(%s, %s)', JSON.stringify(receiver), text); const contactIdOrRoomId = receiver.roomId || receiver.contactId; let msgData; if (mentionIdList && mentionIdList.length > 0) { msgData = yield this.manager.sendMessage(this.selfId(), contactIdOrRoomId, text, schemas_1.PadplusMessageType.Text, mentionIdList.toString()); if (config_1.PADPLUS_REPLAY_MESSAGE) { this.replayTextMsg(msgData.msgId, contactIdOrRoomId, text, mentionIdList); } } else { msgData = yield this.manager.sendMessage(this.selfId(), contactIdOrRoomId, text, schemas_1.PadplusMessageType.Text); if (config_1.PADPLUS_REPLAY_MESSAGE) { this.replayTextMsg(msgData.msgId, contactIdOrRoomId, text); } } if (msgData.success) { const msgPayload = { content: text + ((mentionIdList && mentionIdList.length > 0) ? mentionIdList.toString() : ''), createTime: msgData.timestamp, fromUserName: this.selfId(), imgStatus: 0, l1MsgType: 0, msgId: msgData.msgId, msgSource: 'self', msgSourceCd: 0, msgType: schemas_1.PadplusMessageType.Text, newMsgId: Number(msgData.msgId), pushContent: text, status: 1, toUserName: contactIdOrRoomId, uin: '', wechatUserName: '', }; this.manager.cachePadplusMessagePayload.set(msgData.msgId, msgPayload); } return msgData.msgId; }); } replayTextMsg(msgId, to, text, atUserList) { const payload = this.generateBaseMsg(msgId, to); payload.msgType = schemas_1.PadplusMessageType.Text; payload.content = text; if (atUserList) { payload.msgSource = `<msgsource>\n\t<atuserlist>${atUserList.join(',')}</atuserlist>\n</msgsource>\n`; } config_1.log.silly(PRE, 'replayTextMsg replaying message: %s', JSON.stringify(payload)); this.emit('message', payload.msgId); } messageSendContact(receiver, contactId) { return __awaiter(this, void 0, void 0, function* () { config_1.log.verbose(PRE, `messageSend('%s', %s)`, receiver, contactId); const contactIdOrRoomId = receiver.roomId || receiver.contactId; let contact = yield this.manager.getContact(contactId); if (contact) { const content = { headImgUrl: contact.smallHeadUrl, nickName: contact.nickName, userName: contact.userName, }; const contactData = yield this.manager.sendContact(this.selfId(), contactIdOrRoomId, JSON.stringify(content)); if (config_1.PADPLUS_REPLAY_MESSAGE) { this.replayContactMsg(contactData.msgId, contactIdOrRoomId, JSON.stringify(content)); } if (contactData.success) { const msgPayload = { content: JSON.stringify(content), createTime: contactData.timestamp, fromUserName: this.selfId(), imgStatus: 0, l1MsgType: 0, msgId: contactData.msgId, msgSource: 'self', msgSourceCd: 0, msgType: schemas_1.PadplusMessageType.Text, newMsgId: Number(contactData.msgId), pushContent: JSON.stringify(content), status: 1, toUserName: contactIdOrRoomId, uin: '', wechatUserName: '', }; this.manager.cachePadplusMessagePayload.set(contactData.msgId, msgPayload); } return contactData.msgId; } else { throw new Error('not able to send contact'); } }); } replayContactMsg(msgId, to, content) { const payload = this.generateBaseMsg(msgId, to); payload.msgType = schemas_1.PadplusMessageType.ShareCard; payload.content = content; config_1.log.silly(PRE, 'replayContactMsg replaying message: %s', JSON.stringify(payload)); this.emit('message', payload.msgId); } messageSendFile(receiver, file) { return __awaiter(this, void 0, void 0, function* () { config_1.log.verbose(PRE, 'messageSendFile(%s, %s)', receiver, file); const contactIdOrRoomId = receiver.roomId || receiver.contactId; const fileUrl = yield this.manager.generatorFileUrl(file); const fileSize = (yield file.toBuffer()).length; config_1.log.silly(PRE, `file url : ${util_1.default.inspect(fileUrl)}`); // this needs to run before mimeType is available yield file.ready(); const type = (file.mimeType && file.mimeType !== 'application/octet-stream') ? file.mimeType : path_1.default.extname(file.name); config_1.log.silly(PRE, `fileType ${type}`); switch (type) { case '.slk': throw new Error('not support'); case 'image/jpeg': case 'image/png': case '.jpg': case '.jpeg': case '.png': const picData = yield this.manager.sendFile(this.selfId(), contactIdOrRoomId, decodeURIComponent(fileUrl), file.name, 'pic'); if (config_1.PADPLUS_REPLAY_MESSAGE) { this.replayImageMsg(picData.msgId, contactIdOrRoomId, decodeURIComponent(fileUrl)); } if (picData.success) { const msgPayload = { content: `<msg>${fileUrl}</msg>`, createTime: picData.timestamp, fromUserName: this.selfId(), imgStatus: 0, l1MsgType: 0, msgId: picData.msgId, msgSource: 'self', msgSourceCd: 0, msgType: schemas_1.PadplusMessageType.Text, newMsgId: Number(picData.msgId), pushContent: `<msg>${fileUrl}</msg>`, status: 1, toUserName: contactIdOrRoomId, uin: '', wechatUserName: '', }; this.manager.cachePadplusMessagePayload.set(picData.msgId, msgPayload); } return picData.msgId; case 'video/mp4': case '.mp4': const videoData = yield this.manager.sendFile(this.selfId(), contactIdOrRoomId, fileUrl, file.name, 'video'); if (config_1.PADPLUS_REPLAY_MESSAGE) { this.replayAppMsg(videoData.msgId, contactIdOrRoomId, fileUrl); } if (videoData.success) { const msgPayload = { content: `<msg>${fileUrl}</msg>`, createTime: videoData.timestamp, fromUserName: this.selfId(), imgStatus: 0, l1MsgType: 0, msgId: videoData.msgId, msgSource: 'self', msgSourceCd: 0, msgType: schemas_1.PadplusMessageType.Text, newMsgId: Number(videoData.msgId), pushContent: `<msg>${fileUrl}</msg>`, status: 1, toUserName: contactIdOrRoomId, uin: '', wechatUserName: '', }; this.manager.cachePadplusMessagePayload.set(videoData.msgId, msgPayload); } return videoData.msgId; default: const docData = yield this.manager.sendFile(this.selfId(), contactIdOrRoomId, fileUrl, file.name, 'doc', fileSize); if (config_1.PADPLUS_REPLAY_MESSAGE) { this.replayAppMsg(docData.msgId, contactIdOrRoomId, fileUrl); } if (docData.success) { const msgPayload = { content: `<msg>${fileUrl}</msg>`, createTime: docData.timestamp, fromUserName: this.selfId(), imgStatus: 0, l1MsgType: 0, msgId: docData.msgId, msgSource: 'self', msgSourceCd: 0, msgType: schemas_1.PadplusMessageType.Text, newMsgId: Number(docData.msgId), pushContent: `<msg>${fileUrl}</msg>`, status: 1, toUserName: contactIdOrRoomId, uin: '', wechatUserName: '', }; this.manager.cachePadplusMessagePayload.set(docData.msgId, msgPayload); } return docData.msgId; } }); } replayImageMsg(msgId, to, url) { const payload = this.generateBaseMsg(msgId, to); payload.msgType = schemas_1.PadplusMessageType.Image; payload.content = `<msg>${url}</msg>`; payload.url = url; config_1.log.silly(PRE, 'replayImageMsg replaying message: %s', JSON.stringify(payload)); this.emit('message', payload.msgId); } replayAppMsg(msgId, to, content) { const payload = this.generateBaseMsg(msgId, to); payload.msgType = schemas_1.PadplusMessageType.App; payload.content = `<msg>${content}</msg>`; config_1.log.silly(PRE, 'replayAppMsg replaying message: %s', JSON.stringify(payload)); this.emit('message', payload.msgId); } messageSendUrl(receiver, urlLinkPayload) { return __awaiter(this, void 0, void 0, function* () { config_1.log.verbose(PRE, `messageSendUrl(${receiver})`, JSON.stringify(receiver), JSON.stringify(urlLinkPayload)); const contactIdOrRoomId = receiver.roomId || receiver.contactId; const { url, title, thumbnailUrl, description } = urlLinkPayload; const payload = { description, thumburl: thumbnailUrl, title, type: 5, url, }; const urlLinkData = yield this.manager.sendUrlLink(this.selfId(), contactIdOrRoomId, JSON.stringify(payload)); if (config_1.PADPLUS_REPLAY_MESSAGE) { this.replayUrlLinkMsg(urlLinkData.msgId, contactIdOrRoomId, JSON.stringify(payload)); } if (urlLinkData.success) { const msgPayload = { content: JSON.stringify(payload), createTime: urlLinkData.timestamp, fromUserName: this.selfId(), imgStatus: 0, l1MsgType: 0, msgId: urlLinkData.msgId, msgSource: 'self', msgSourceCd: 0, msgType: schemas_1.PadplusMessageType.Text, newMsgId: Number(urlLinkData.msgId), pushContent: JSON.stringify(payload), status: 1, toUserName: contactIdOrRoomId, uin: '', wechatUserName: '', }; this.manager.cachePadplusMessagePayload.set(urlLinkData.msgId, msgPayload); } config_1.log.silly(PRE, `urlLinkData : ${util_1.default.inspect(urlLinkData)}`); return urlLinkData.msgId; }); } replayUrlLinkMsg(msgId, to, content) { const payload = this.generateBaseMsg(msgId, to); payload.msgType = schemas_1.PadplusMessageType.App; payload.content = content; config_1.log.silly(PRE, 'replayUrlLinkMsg replaying message: %s', JSON.stringify(payload)); this.emit('message', payload.msgId); } messageSendMiniProgram(receiver, miniProgramPayload) { config_1.log.silly(PRE, `messageSendMiniProgram() receiver : ${util_1.default.inspect(receiver)}, miniProgramPayload: ${miniProgramPayload}`); throw new Error('Method not implemented.'); } messageRawPayload(messageId) { return __awaiter(this, void 0, void 0, function* () { config_1.log.verbose(PRE, 'messageRawPayload(%s)', messageId); const rawPayload = yield this.manager.cachePadplusMessagePayload.get(messageId); if (!rawPayload) { throw new Error('no message rawPayload'); } return rawPayload; }); } messageRawPayloadParser(rawPayload) { return __awaiter(this, void 0, void 0, function* () { config_1.log.verbose(PRE, 'messageRawPayloadParser()'); const payload = yield pure_function_helpers_1.messageRawPayloadParser(rawPayload); if (payload.mentionIdList && payload.mentionIdList.length === 1 && payload.mentionIdList[0] === 'announcement@all') { const memberIds = yield this.roomMemberList(payload.roomId); payload.mentionIdList = memberIds.filter(m => m !== payload.fromId); payload.text = `${payload.text || ''}`; } return payload; }); } /** * ======================== * ROOM SECTION * ======================== */ onRoomJoinEvent(message) { return __awaiter(this, void 0, void 0, function* () { const joinEvent = yield room_event_join_message_parser_1.roomJoinEventMessageParser(message); if (joinEvent) { config_1.log.silly(PRE, `receive join event : ${util_1.default.inspect(joinEvent)}`); const inviteeNameList = joinEvent.inviteeNameList; const inviterName = joinEvent.inviterName; const roomId = joinEvent.roomId; const timestamp = joinEvent.timestamp; const inviteeIdList = yield config_1.retry((retryException, attempt) => __awaiter(this, void 0, void 0, function* () { config_1.log.verbose(PRE, 'onPadplusMessageRoomEventJoin({id=%s}) roomJoin retry(attempt=%d)', attempt); const tryIdList = array_flatten_1.default(yield Promise.all(inviteeNameList.map(inviteeName => this.roomMemberSearch(roomId, inviteeName)))); if (tryIdList.length) { return tryIdList; } if (!this.manager) { throw new Error('no manager'); } /** * Set Cache Dirty */ yield this.roomMemberPayloadDirty(roomId); return retryException(new Error('roomMemberSearch() not found')); })).catch(e => { config_1.log.verbose(PRE, 'onPadplusMessageRoomEventJoin({id=%s}) roomJoin retry() fail: %s', e.message); return []; }); let inviterIdList = yield this.roomMemberSearch(roomId, inviterName); if (inviterIdList.length < 1) { yield this.roomMemberPayloadDirty(roomId); yield this.manager.getRoomMembers(roomId); inviterIdList = yield this.roomMemberSearch(roomId, inviterName); if (inviterIdList.length < 1) { throw new Error(`can not get room member`); } } else if (inviterIdList.length > 1) { config_1.log.verbose(PRE, 'onPadplusMessageRoomEventJoin() inviterId found more than 1, use the first one.'); } const inviterId = inviterIdList[0]; /** * Set Cache Dirty */ yield this.roomMemberPayloadDirty(roomId); yield this.roomPayloadDirty(roomId); this.emit('room-join', roomId, inviteeIdList, inviterId, timestamp); } }); } onRoomLeaveEvent(message) { return __awaiter(this, void 0, void 0, function* () { config_1.log.verbose(PRE, 'onRoomLeaveEvent({id=%s})', message.msgId); const leaveEvent = room_event_leave_message_parser_1.roomLeaveEventMessageParser(message); if (leaveEvent) { config_1.log.silly(PRE, `receive remove event : ${util_1.default.inspect(leaveEvent)}`); const leaverNameList = leaveEvent.leaverNameList; const removerName = leaveEvent.removerName; const roomId = leaveEvent.roomId; const timestamp = leaveEvent.timestamp; const leaverIdList = array_flatten_1.default(yield Promise.all(leaverNameList.map(leaverName => this.roomMemberSearch(roomId, leaverName)))); const removerIdList = yield this.roomMemberSearch(roomId, removerName); if (removerIdList.length < 1) { throw new Error('no removerId found'); } else if (removerIdList.length > 1) { config_1.log.verbose(PRE, 'onPadplusMessageRoomEventLeave(): removerId found more than 1, use the first one.'); } const removerId = removerIdList[0]; if (!this.manager) { throw new Error('no padplusManager'); } /** * Set Cache Dirty */ yield this.roomMemberPayloadDirty(roomId); yield this.roomPayloadDirty(roomId); this.emit('room-leave', roomId, leaverIdList, removerId, timestamp); } }); } onRoomTopicEvent(message) { return __awaiter(this, void 0, void 0, function* () { config_1.log.verbose(PRE, 'onPadplusMessageRoomEventTopic({id=%s})', message.msgId); const topicEvent = room_event_topic_message_parser_1.roomTopicEventMessageParser(message); if (topicEvent) { config_1.log.silly(PRE, `receive topic event : ${util_1.default.inspect(topicEvent)}`); const changerName = topicEvent.changerName; const newTopic = topicEvent.topic; const roomId = topicEvent.roomId; const timestamp = topicEvent.timestamp; const roomOldPayload = yield this.roomPayload(roomId); const oldTopic = roomOldPayload.topic; const changerIdList = yield this.roomMemberSearch(roomId, changerName); if (changerIdList.length < 1) { throw new Error('no changerId found'); } else if (changerIdList.length > 1) { config_1.log.verbose(PRE, 'onPadplusMessageRoomEventTopic() changerId found more than 1, use the first one.'); } const changerId = changerIdList[0]; if (!this.manager) { throw new Error('no padplusManager'); } /** * Set Cache Dirty */ yield this.roomPayloadDirty(roomId); if (this.manager && this.manager.cacheManager) { yield this.manager.cacheManager.deleteRoom(roomId); } this.emit('room-topic', roomId, newTopic, oldTopic, changerId, timestamp); } }); } roomInvitationAccept(roomInvitationId) { config_1.log.silly(PRE, `roomInvitationId : ${util_1.default.inspect(roomInvitationId)}`); throw new Error('Method not implemented.'); } roomInvitationRawPayload(roomInvitationId) { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `roomInvitationId : ${util_1.default.inspect(roomInvitationId)}`); if (!this.manager) { throw new Error(`no manager.`); } const payload = yield this.manager.roomInvitationRawPayload(roomInvitationId); return payload; }); } roomInvitationRawPayloadParser(rawPayload) { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `room invitation rawPayload : ${util_1.default.inspect(rawPayload)}`); const payload = { id: rawPayload.id, inviterId: rawPayload.fromUser, roomMemberCount: 0, roomMemberIdList: [], roomTopic: rawPayload.roomName, timestamp: rawPayload.timestamp, }; return payload; }); } roomAdd(roomId, contactId) { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `roomId : ${util_1.default.inspect(roomId)}, contactId: ${contactId}`); if (!this.manager) { throw new Error(`no manager.`); } yield this.manager.roomAddMember(roomId, contactId); }); } roomAvatar(roomId) { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `roomAvatar(roomId : ${util_1.default.inspect(roomId)})`); if (!this.manager) { throw new Error(`no manager.`); } const room = yield this.roomRawPayload(roomId); if (room) { const avatarUrl = room.bigHeadUrl || room.smallHeadUrl; return file_box_1.default.fromUrl(avatarUrl, `${roomId}_avatar_${Date.now()}.png`); } else { throw new Error(`Can not load room info by roomId : ${roomId}`); } }); } roomCreate(contactIdList, topic) { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `topic : ${topic}, contactIdList: ${contactIdList.join(',')}`); if (!this.manager) { throw new Error(`no manager.`); } const result = yield this.manager.createRoom(topic || '', contactIdList); return result; }); } roomDel(roomId, contactId) { return __awaiter(this, void 0, void 0, function* () { config_1.log.verbose(PRE, `roomDel(${roomId}, ${contactId})`); const memberIdList = yield this.roomMemberList(roomId); if (memberIdList.includes(contactId)) { yield this.manager.deleteRoomMember(roomId, contactId); } else { config_1.log.verbose(PRE, `roomDel() room(${roomId}) has no member contact(${contactId})`); } }); } roomQuit(roomId) { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `roomId : ${util_1.default.inspect(roomId)}`); if (!this.manager) { throw new Error(`no manager.`); } yield this.manager.quitRoom(roomId); }); } roomTopic(roomId, topic) { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `roomId : ${roomId}, topic: ${topic}`); if (typeof topic === 'undefined') { const room = yield this.roomPayload(roomId); return room && (room.topic || ''); } if (!this.manager) { throw new Error(`no manager.`); } yield this.manager.setRoomTopic(roomId, topic); yield this.roomPayloadDirty(roomId); yield new Promise(resolve => setTimeout(resolve, 500)); yield this.roomTopic(roomId); }); } roomQrcode(roomId) { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `roomId : ${util_1.default.inspect(roomId)}`); if (!this.manager) { throw new Error(`no manager.`); } const roomQrcode = yield this.manager.getRoomQrcode(roomId); return roomQrcode; }); } roomList() { return __awaiter(this, void 0, void 0, function* () { config_1.log.verbose(PRE, `roomList()`); if (!this.manager) { throw new Error(`no manager.`); } const roomIds = yield this.manager.getRoomIdList(); return roomIds; }); } roomMemberList(roomId) { return __awaiter(this, void 0, void 0, function* () { config_1.log.silly(PRE, `roomMemberList(), roomId : ${util_1.default.inspect(roomId)}`); if (!this.manager) { throw new Error(`no manager.`); } const roomIds = yield this.manager.getRoomMemberIdLis