UNPKG

wechaty-puppet-official-account

Version:
627 lines 23.7 kB
"use strict"; /** * Wechaty - https://github.com/chatie/wechaty * * @copyright 2016-2018 Huan LI <zixia@zixia.net> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PuppetOA = void 0; const PUPPET = __importStar(require("wechaty-puppet")); const file_box_1 = require("file-box"); const config_js_1 = require("./config.js"); const official_account_js_1 = require("./official-account/official-account.js"); class PuppetOA extends PUPPET.Puppet { messageLocation(_messageId) { throw new Error('Method not implemented.'); } messageSendLocation(_conversationId, _locationPayload) { throw new Error('Method not implemented.'); } contactPhone(contactId, phoneList) { config_js_1.log.info('contactPhone(%s, %s)', contactId, phoneList); throw new Error('Method not implemented.'); } contactCorporationRemark(contactId, corporationRemark) { config_js_1.log.info('contactCorporationRemark(%s, %s)', contactId, corporationRemark); throw new Error('Method not implemented.'); } contactDescription(contactId, description) { config_js_1.log.info('contactDescription(%s, %s)', contactId, description); throw new Error('Method not implemented.'); } static VERSION = config_js_1.VERSION; appId; appSecret; port; token; webhookProxyUrl; personalMode; accessTokenProxyUrl; oa; _heartBeatTimer; constructor(options = {}) { super(); config_js_1.log.verbose('PuppetOA', 'constructor()'); options = { ...(0, config_js_1.envOptions)(), ...options, }; if (options.appId) { this.appId = options.appId; } else { throw new Error(` PuppetOA: appId not found. Please either set the WECHATY_PUPPET_OA_APP_ID environment variable, or set 'appId' optoins for PuppetOA. `); } if (options.appSecret) { this.appSecret = options.appSecret; } else { throw new Error(` PuppetOA: appSecret not found. Please either set the WECHATY_PUPPET_OA_APP_SECRET environment variable, or set 'appSecret' options for PuppetOA. `); } if (options.token) { this.token = options.token; } else { throw new Error(` PuppetOA: token not found. Please either set WECHATY_PUPPET_OA_TOKEN environment variabnle, or set 'token' options for PuppetOA. `); } if (options.personalMode) { this.personalMode = options.personalMode; } else { this.personalMode = false; } this.port = options.port; this.webhookProxyUrl = options.webhookProxyUrl; /** * NOTE: if the ip address of server is dynamic, it can't fetch the accessToken from tencent server. * So, the accessTokenProxyUrl configuration is needed to fetch the accessToken from the specific endpoint. * * eg: accessTokenProxyUrl = 'http://your-endpoint/' * puppet-oa will fetch accessToken from: http://your-endpoint/token?grant_type=client_credential&appid=${appId}&secret=${appSecret} */ if (options.accessTokenProxyUrl) { if (options.accessTokenProxyUrl.endsWith('/')) { options.accessTokenProxyUrl = options.accessTokenProxyUrl.substring(0, options.accessTokenProxyUrl.length - 1); } this.accessTokenProxyUrl = options.accessTokenProxyUrl; } } version() { return config_js_1.VERSION; } async onStart() { config_js_1.log.verbose('PuppetOA', 'onStart()'); this.oa = new official_account_js_1.OfficialAccount({ appId: this.appId, appSecret: this.appSecret, personalMode: this.personalMode, port: this.port, token: this.token, webhookProxyUrl: this.webhookProxyUrl, }); this.bridgeEvents(this.oa); await this.oa.start(); await this._startPuppetHeart(true); // FIXME: Huan(202008) find a way to get the bot user information // Official Account Info can be customized by user, so It should be // configured by environment variable. // set gh_ prefix to identify the official-account const currentUserId = `gh_${this.appId}`; await this.oa.payloadStore.setContactPayload(currentUserId, { openid: currentUserId }); this.login(currentUserId); this.emit('ready', { data: 'ready' }); } async _startPuppetHeart(firstTime = true) { if (firstTime && this._heartBeatTimer) { return; } this.emit('heartbeat', { data: 'heartbeat@office: live' }); // eslint-disable-next-line @typescript-eslint/no-misused-promises this._heartBeatTimer = setTimeout(async () => { await this._startPuppetHeart(false); return undefined; }, 15 * 1000); // 15s } // 停止监听心跳 _stopPuppetHeart() { if (!this._heartBeatTimer) { return; } clearTimeout(this._heartBeatTimer); this._heartBeatTimer = undefined; } bridgeEvents(oa) { oa.on('message', msg => this.emit('message', { messageId: msg.MsgId })); oa.on('login', _ => this.login(this.currentUserId)); oa.on('ready', _ => this.emit('ready', { data: 'ready' })); oa.on('logout', _ => this.wrapAsync(this.logout('oa.on(logout)'))); } async onStop() { config_js_1.log.verbose('PuppetOA', 'onStop()'); if (this.oa) { this.oa.removeAllListeners(); await this.oa.stop(); this.oa = undefined; await this.logout('oa.on(logout)'); } this._stopPuppetHeart(); } ding(data) { config_js_1.log.silly('PuppetOA', 'ding(%s)', data || ''); // FIXME: do the real job setTimeout(() => this.emit('dong', { data: data || '' }), 1000); } /** * * ContactSelf * * */ async contactSelfQRCode() { config_js_1.log.verbose('PuppetOA', 'contactSelfQRCode()'); return 'qrcode in the future ;^)'; } async contactSelfName(name) { config_js_1.log.verbose('PuppetOA', 'contactSelfName(%s)', name); } async contactSelfSignature(signature) { config_js_1.log.verbose('PuppetOA', 'contactSelfSignature(%s)', signature); } async contactAlias(contactId, alias) { config_js_1.log.verbose('PuppetOA', 'contactAlias(%s, %s)', contactId, alias); /** * 1. set */ if (alias) { await this.oa?.updateContactRemark(contactId, alias); return alias; } /** * 2. get */ const contactPayload = await this.contactPayload(contactId); if (!contactPayload.alias) { config_js_1.log.warn('Contact<%s> has no alias', contactId); } return contactPayload.alias; } async contactList() { config_js_1.log.verbose('PuppetOA', 'contactList()'); const contactIdList = await this.oa?.getContactList(); if (!contactIdList) { throw new Error('contactIdList found from oa store'); } return contactIdList; } async contactAvatar(contactId, file) { config_js_1.log.verbose('PuppetOA', 'contactAvatar(%s)', contactId); /** * 1. set */ if (file) { return; } /** * 2. get */ const contactPayload = await this.contactPayload(contactId); const fileBox = contactPayload.avatar ? file_box_1.FileBox.fromUrl(contactPayload.avatar) : undefined; if (fileBox) { return fileBox; } } async contactRawPayloadParser(oaPayload) { const payload = { alias: oaPayload.remark, avatar: oaPayload.headimgurl, city: oaPayload.city, friend: true, gender: oaPayload.sex, id: oaPayload.openid, name: oaPayload.nickname, phone: [], province: oaPayload.province, signature: '', star: false, type: PUPPET.types.Contact.Individual, weixin: oaPayload.unionid, }; return payload; } async contactRawPayload(id) { config_js_1.log.verbose('PuppetOA', 'contactRawPayload(%s)', id); const contactInfoPayload = await this.oa?.getContactPayload(id); if (!contactInfoPayload) { throw new Error(`can not get PUPPET.payloads.Contact(${id})`); } return contactInfoPayload; } /** * * Message * */ async messageContact(messageId) { config_js_1.log.verbose('PuppetOA', 'messageContact(%s)', messageId); // const attachment = this.mocker.MockMessage.loadAttachment(messageId) // if (attachment instanceof ContactMock) { // return attachment.id // } return ''; } async messageImage(messageId, imageType) { config_js_1.log.verbose('PuppetOA', 'messageImage(%s, %s[%s])', messageId, imageType, PUPPET.types.Image[imageType]); // const attachment = this.mocker.MockMessage.loadAttachment(messageId) // if (attachment instanceof FileBox) { // return attachment // } const payload = await this.messagePayload(messageId); let fileBox; if (payload.type === PUPPET.types.Message.Image) { if (!payload.filename) { throw Error(`image message type must have filename file. <${payload}>`); } fileBox = file_box_1.FileBox.fromUrl(payload.filename); } else { throw Error('can"t get file from the message'); } return fileBox; } async messageRecall(messageId) { config_js_1.log.verbose('PuppetOA', 'messageRecall(%s)', messageId); return false; } async messageFile(id) { config_js_1.log.verbose('PuppetOA', 'messageFile(%s)', id); const payload = await this.messagePayload(id); switch (payload.type) { case PUPPET.types.Message.Image: if (!payload.filename) { throw Error(`Image message must have filename. <${payload}>`); } return file_box_1.FileBox.fromUrl(payload.filename); case PUPPET.types.Message.Audio: if (!payload.filename) { throw Error(`Audio message must have filename. <${payload}>`); } // payload.filename is an URL to the audio file. The name of the file is not in the URL. // Setting a filename with expected extension is necessary for inference of mime type in // FileBox. return file_box_1.FileBox.fromUrl(payload.filename, 'message.amr'); default: throw Error('can"t get file from the message'); } } async messageUrl(messageId) { config_js_1.log.verbose('PuppetOA', 'messageUrl(%s)', messageId); // const attachment = this.mocker.MockMessage.loadAttachment(messageId) // if (attachment instanceof UrlLink) { // return attachment.payload // } return { title: 'mock title for ' + messageId, url: 'https://mock.url', }; } async messageMiniProgram(messageId) { config_js_1.log.verbose('PuppetOA', 'messageMiniProgram(%s)', messageId); // const attachment = this.mocker.MockMessage.loadAttachment(messageId) // if (attachment instanceof MiniProgram) { // return attachment.payload // } return { title: 'mock title for ' + messageId, }; } async messageRawPayloadParser(rawPayload) { const payload = { id: rawPayload.MsgId, listenerId: rawPayload.ToUserName, talkerId: rawPayload.FromUserName, timestamp: parseInt(rawPayload.CreateTime), type: PUPPET.types.Message.Text, }; if (rawPayload.MsgType === 'image') { payload.type = PUPPET.types.Message.Image; if (!rawPayload.PicUrl) { throw Error(`Image Payload must has PicUrl field :<${JSON.stringify(rawPayload)}>`); } payload.filename = rawPayload.PicUrl; } else if (rawPayload.MsgType === 'video') { payload.type = PUPPET.types.Message.Video; } else if (rawPayload.MsgType === 'location') { payload.type = PUPPET.types.Message.Location; } else if (rawPayload.MsgType === 'text') { payload.text = rawPayload.Content; } else if (rawPayload.MsgType === 'event') { payload.text = rawPayload.EventKey; } else if (rawPayload.MsgType === 'voice') { payload.type = PUPPET.types.Message.Audio; payload.filename = await this.oa?.getAudioUrl(rawPayload.MediaId); } return payload; } async messageRawPayload(id) { config_js_1.log.verbose('PuppetOA', 'messageRawPayload(%s)', id); const payload = await this.oa?.payloadStore.getMessagePayload(id); if (!payload) { throw new Error('payload not found from oa store'); } return payload; } async _messageSend(conversationId, something, // | Attachment mediatype = 'image') { config_js_1.log.verbose('PuppetOA', 'messageSend(%s, %s)', conversationId, something); let msgId = null; if (typeof something === 'string') { const payload = { content: something, msgtype: 'text', touser: conversationId, }; if (this.personalMode) { msgId = await this.oa?.sendCustomMessagePersonal(payload); if (!msgId) { throw new Error('can"t send personal CustomeMessage'); } } else { msgId = await this.oa?.sendCustomMessage(payload); } } else if (file_box_1.FileBox.valid(something)) { await something.ready(); msgId = await this.oa?.sendFile({ file: something, msgtype: mediatype, touser: conversationId }); } if (!msgId) { throw new Error('PuppetOA messageSend() can"t get msgId response'); } return msgId; } async messageSendText(conversationId, text) { return this._messageSend(conversationId, text); } async messageSendFile(conversationId, file) { let msgtype; const mimeType = file.mediaType; switch (mimeType) { case 'image/jpeg': case 'image/jpg': case 'image/png': case 'image/gif': msgtype = 'image'; break; case 'audio/amr': case 'audio/mpeg': msgtype = 'voice'; break; case 'video/mp4': msgtype = 'video'; break; default: throw new Error(`unsupported media type: ${file.mimeType}`); } return this._messageSend(conversationId, file, msgtype); } async messageSendContact(conversationId, contactId) { config_js_1.log.verbose('PuppetOA', 'messageSendUrl(%s, %s)', conversationId, contactId); // const contact = this.mocker.MockContact.load(contactId) // return this.messageSend(conversationId, contact) } async messageSendUrl(conversationId, urlLinkPayload) { config_js_1.log.verbose('PuppetOA', 'messageSendUrl(%s, %s)', conversationId, urlLinkPayload); let msgId = null; msgId = await this.oa?.sendCustomLink({ touser: conversationId, urlLinkPayload }); if (!msgId) { throw new Error('PuppetOA messageSendUrl() can"t get msgId response'); } return msgId; } async messageSendMiniProgram(conversationId, miniProgramPayload) { config_js_1.log.verbose('PuppetOA', 'messageSendMiniProgram(%s, %s)', conversationId, JSON.stringify(miniProgramPayload)); let msgId = null; msgId = await this.oa?.sendCustomMiniProgram({ miniProgram: miniProgramPayload, touser: conversationId }); if (!msgId) { throw new Error('PuppetOA messageSendMiniProgram() can"t get msgId response'); } return msgId; } async messageForward(conversationId, messageId) { config_js_1.log.verbose('PuppetOA', 'messageForward(%s, %s)', conversationId, messageId); } async conversationReadMark(conversationId, hasRead) { config_js_1.log.verbose('PuppetOA', 'conversationReadMark(%s, %s)', conversationId, hasRead); } /** * * Room * */ async roomRawPayloadParser(payload) { return payload; } async roomRawPayload(id) { config_js_1.log.verbose('PuppetOA', 'roomRawPayload(%s)', id); return {}; } async roomList() { config_js_1.log.verbose('PuppetOA', 'roomList()'); return []; } async roomDel(roomId, contactId) { config_js_1.log.verbose('PuppetOA', 'roomDel(%s, %s)', roomId, contactId); } async roomAvatar(roomId) { config_js_1.log.verbose('PuppetOA', 'roomAvatar(%s)', roomId); const payload = await this.roomPayload(roomId); if (payload.avatar) { return file_box_1.FileBox.fromUrl(payload.avatar); } config_js_1.log.warn('PuppetOA', 'roomAvatar() avatar not found, use the chatie default.'); return (0, config_js_1.qrCodeForChatie)(); } async roomAdd(roomId, contactId) { config_js_1.log.verbose('PuppetOA', 'roomAdd(%s, %s)', roomId, contactId); } async roomTopic(roomId, topic) { config_js_1.log.verbose('PuppetOA', 'roomTopic(%s, %s)', roomId, topic); if (typeof topic === 'undefined') { return 'mock room topic'; } await this.dirtyPayload(PUPPET.types.Payload.Room, roomId); } async roomCreate(contactIdList, topic) { config_js_1.log.verbose('PuppetOA', 'roomCreate(%s, %s)', contactIdList, topic); return 'mock_room_id'; } async roomQuit(roomId) { config_js_1.log.verbose('PuppetOA', 'roomQuit(%s)', roomId); } async roomQRCode(roomId) { config_js_1.log.verbose('PuppetOA', 'roomQRCode(%s)', roomId); return roomId + ' mock qrcode'; } async roomMemberList(roomId) { config_js_1.log.verbose('PuppetOA', 'roomMemberList(%s)', roomId); return []; } async roomMemberRawPayload(roomId, contactId) { config_js_1.log.verbose('PuppetOA', 'roomMemberRawPayload(%s, %s)', roomId, contactId); return { avatar: 'mock-avatar-data', id: 'xx', name: 'mock-name', roomAlias: 'yy', }; } async roomMemberRawPayloadParser(rawPayload) { config_js_1.log.verbose('PuppetOA', 'roomMemberRawPayloadParser(%s)', rawPayload); return rawPayload; } async roomAnnounce(roomId, text) { if (text) { return; } return 'mock announcement for ' + roomId; } /** * * Room Invitation * */ async roomInvitationAccept(roomInvitationId) { config_js_1.log.verbose('PuppetOA', 'roomInvitationAccept(%s)', roomInvitationId); } async roomInvitationRawPayload(roomInvitationId) { config_js_1.log.verbose('PuppetOA', 'roomInvitationRawPayload(%s)', roomInvitationId); } async roomInvitationRawPayloadParser(rawPayload) { config_js_1.log.verbose('PuppetOA', 'roomInvitationRawPayloadParser(%s)', JSON.stringify(rawPayload)); return rawPayload; } /** * * Friendship * */ async friendshipRawPayload(id) { return { id }; } async friendshipRawPayloadParser(rawPayload) { return rawPayload; } async friendshipSearchPhone(phone) { config_js_1.log.verbose('PuppetOA', 'friendshipSearchPhone(%s)', phone); return null; } async friendshipSearchWeixin(weixin) { config_js_1.log.verbose('PuppetOA', 'friendshipSearchWeixin(%s)', weixin); return null; } async friendshipAdd(contactId, hello) { config_js_1.log.verbose('PuppetOA', 'friendshipAdd(%s, %s)', contactId, hello); } async friendshipAccept(friendshipId) { config_js_1.log.verbose('PuppetOA', 'friendshipAccept(%s)', friendshipId); } /** * * Tag * */ async tagContactAdd(tagId, contactId) { config_js_1.log.verbose('PuppetOA', 'tagContactAdd(%s)', tagId, contactId); await this.oa?.addTagToMembers(tagId, [contactId]); } async tagContactRemove(tagId, contactId) { config_js_1.log.verbose('PuppetOA', 'tagContactRemove(%s)', tagId, contactId); await this.oa?.removeTagFromMembers(tagId, [contactId]); } async tagContactDelete(tagId) { config_js_1.log.verbose('PuppetOA', 'tagContactDelete(%s)', tagId); await this.oa?.deleteTag(tagId); } async tagContactList(contactId) { config_js_1.log.verbose('PuppetOA', 'tagContactList(%s)', contactId); if (!this.oa) { throw new Error('can not find oa object'); } // 1. get all of the tags if (!contactId) { const tagList = await this.oa.getContactList(); return tagList; } // 2. get the member tags const tagList = await this.oa.getMemberTags(contactId); return tagList; } } exports.PuppetOA = PuppetOA; exports.default = PuppetOA; //# sourceMappingURL=puppet-oa.js.map