UNPKG

wechaty-puppet-service

Version:
1,093 lines (1,092 loc) 61.6 kB
"use strict"; 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; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PuppetService = void 0; /** * Wechaty Open Source Software - https://github.com/wechaty * * @copyright 2016 Huan LI (李卓桓) <https://github.com/huan>, and * Wechaty Contributors <https://github.com/wechaty>. * * 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. * */ const util_1 = __importDefault(require("util")); const PUPPET = __importStar(require("wechaty-puppet")); const wechaty_grpc_1 = require("wechaty-grpc"); const wechaty_redux_1 = require("wechaty-redux"); const ducks_1 = require("ducks"); // import type { Subscription } from 'rxjs' /** * Deprecated. Will be removed after Dec 31, 2022 */ const mod_js_1 = require("../deprecated/mod.js"); const serialize_file_box_js_1 = require("../deprecated/serialize-file-box.js"); const timestamp_js_1 = require("../pure-functions/timestamp.js"); const mod_js_2 = require("../file-box-helper/mod.js"); const config_js_1 = require("../config.js"); const event_type_rev_js_1 = require("../event-type-rev.js"); const package_json_js_1 = require("../package-json.js"); const grpc_manager_js_1 = require("./grpc-manager.js"); const payload_store_js_1 = require("./payload-store.js"); class PuppetService extends PUPPET.Puppet { options; static VERSION = config_js_1.VERSION; _cleanupCallbackList; _payloadStore; /** * Wechaty Redux */ _ducks; _store; _grpcManager; get grpcManager() { if (!this._grpcManager) { throw new Error('no grpc manager'); } return this._grpcManager; } /** * UUIDify: * We need to clone a FileBox * to set uuid loader/saver with this grpc client */ FileBoxUuid; constructor(options = {}) { super(options); this.options = options; this._payloadStore = new payload_store_js_1.PayloadStore({ token: config_js_1.envVars.WECHATY_PUPPET_SERVICE_TOKEN(this.options.token), }); this.hookPayloadStore(); this._ducks = new ducks_1.Ducks({ puppet: wechaty_redux_1.Duck }); this._store = this._ducks.configureStore(); this._cleanupCallbackList = []; this.FileBoxUuid = (0, mod_js_2.uuidifyFileBoxGrpc)(() => this.grpcManager.client); } async serializeFileBox(fileBox) { /** * 1. if the fileBox is one of type `Url`, `QRCode`, `Uuid`, etc, * then it can be serialized by `fileBox.toString()` * 2. if the fileBox is one of type `Stream`, `Buffer`, `File`, etc, * then it need to be convert to type `Uuid` * before serialized by `fileBox.toString()` */ const normalizedFileBox = await (0, mod_js_2.normalizeFileBoxUuid)(this.FileBoxUuid)(fileBox); return JSON.stringify(normalizedFileBox); } name() { return package_json_js_1.packageJson.name || 'wechaty-puppet-service'; } version() { return package_json_js_1.packageJson.version || '0.0.0'; } async onStart() { config_js_1.log.verbose('PuppetService', 'onStart()'); if (this._grpcManager) { config_js_1.log.warn('PuppetService', 'onStart() found this.grpc is already existed. dropped.'); this._grpcManager = undefined; } config_js_1.log.verbose('PuppetService', 'start() instanciating GrpcManager ...'); const grpcManager = new grpc_manager_js_1.GrpcManager(this.options); config_js_1.log.verbose('PuppetService', 'start() instanciating GrpcManager ... done'); /** * Huan(202108): when we started the event stream, * the `this.grpc` need to be available for all listeners. */ this._grpcManager = grpcManager; config_js_1.log.verbose('PuppetService', 'start() setting up bridge grpc event stream ...'); this.bridgeGrpcEventStream(grpcManager); config_js_1.log.verbose('PuppetService', 'start() setting up bridge grpc event stream ... done'); config_js_1.log.verbose('PuppetService', 'start() starting grpc manager...'); await grpcManager.start(); config_js_1.log.verbose('PuppetService', 'start() starting grpc manager... done'); /** * Ducks management */ const subscription = (0, wechaty_redux_1.puppet$)(this) .subscribe(this._store.dispatch); this._cleanupCallbackList.push(() => subscription.unsubscribe()); config_js_1.log.verbose('PuppetService', 'onStart() ... done'); } async onStop() { config_js_1.log.verbose('PuppetService', 'onStop()'); this._cleanupCallbackList.map(setImmediate); this._cleanupCallbackList.length = 0; if (this._grpcManager) { config_js_1.log.verbose('PuppetService', 'onStop() stopping grpc manager ...'); const grpcManager = this._grpcManager; this._grpcManager = undefined; await grpcManager.stop(); config_js_1.log.verbose('PuppetService', 'onStop() stopping grpc manager ... done'); } config_js_1.log.verbose('PuppetService', 'onStop() ... done'); } hookPayloadStore() { config_js_1.log.verbose('PuppetService', 'hookPayloadStore()'); this.on('login', async ({ contactId }) => { try { config_js_1.log.verbose('PuppetService', 'hookPayloadStore() this.on(login) contactId: "%s"', contactId); await this._payloadStore.start(contactId); } catch (e) { config_js_1.log.verbose('PuppetService', 'hookPayloadStore() this.on(login) rejection "%s"', e.message); } }); this.on('logout', async ({ contactId }) => { config_js_1.log.verbose('PuppetService', 'hookPayloadStore() this.on(logout) contactId: "%s"', contactId); try { await this._payloadStore.stop(); } catch (e) { config_js_1.log.verbose('PuppetService', 'hookPayloadStore() this.on(logout) rejection "%s"', e.message); } }); } bridgeGrpcEventStream(client) { config_js_1.log.verbose('PuppetService', 'bridgeGrpcEventStream(client)'); client .on('data', this.onGrpcStreamEvent.bind(this)) .on('end', () => { config_js_1.log.verbose('PuppetService', 'bridgeGrpcEventStream() eventStream.on(end)'); }) .on('error', (e) => { this.emit('error', e); // https://github.com/wechaty/wechaty-puppet-service/issues/16 // log.verbose('PuppetService', 'bridgeGrpcEventStream() eventStream.on(error) %s', e) // const reason = 'bridgeGrpcEventStream() eventStream.on(error) ' + e /** * Huan(202110): simple reset puppet when grpc client has error? (or not?) */ // this.wrapAsync(this.reset()) // /** // * The `Puppet` class have a throttleQueue for receiving the `reset` events // * and it's the `Puppet` class's duty for call the `puppet.reset()` to reset the puppet. // */ // if (this.state.on()) { // this.emit('reset', { data: reason }) // } }) .on('cancel', (...args) => { config_js_1.log.verbose('PuppetService', 'bridgeGrpcEventStream() eventStream.on(cancel), %s', JSON.stringify(args)); }); } onGrpcStreamEvent(event) { const type = event.getType(); const payload = event.getPayload(); config_js_1.log.verbose('PuppetService', 'onGrpcStreamEvent({type:%s(%s), payload(len:%s)})', event_type_rev_js_1.EventTypeRev[type], type, payload.length); config_js_1.log.silly('PuppetService', 'onGrpcStreamEvent({type:%s(%s), payload:"%s"})', event_type_rev_js_1.EventTypeRev[type], type, payload); if (type !== wechaty_grpc_1.puppet.EventType.EVENT_TYPE_HEARTBEAT) { this.emit('heartbeat', { data: `onGrpcStreamEvent(${event_type_rev_js_1.EventTypeRev[type]})`, }); } switch (type) { case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_DONG: this.emit('dong', JSON.parse(payload)); break; case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_ERROR: this.emit('error', JSON.parse(payload)); break; case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_HEARTBEAT: this.emit('heartbeat', JSON.parse(payload)); break; case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_FRIENDSHIP: this.emit('friendship', JSON.parse(payload)); break; case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_LOGIN: { const loginPayload = JSON.parse(payload); (async () => this.login(loginPayload.contactId))().catch(e => config_js_1.log.error('PuppetService', 'onGrpcStreamEvent() this.login() rejection %s', e.message)); } break; case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_LOGOUT: { const logoutPayload = JSON.parse(payload); (async () => this.logout(logoutPayload.data))().catch(e => config_js_1.log.error('PuppetService', 'onGrpcStreamEvent() this.logout() rejection %s', e.message)); } break; case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_DIRTY: this.emit('dirty', JSON.parse(payload)); break; case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_MESSAGE: this.emit('message', JSON.parse(payload)); break; case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_POST: this.emit('post', JSON.parse(payload)); break; case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_READY: this.emit('ready', JSON.parse(payload)); break; case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_ROOM_INVITE: this.emit('room-invite', JSON.parse(payload)); break; case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_ROOM_JOIN: this.emit('room-join', JSON.parse(payload)); break; case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_ROOM_LEAVE: this.emit('room-leave', JSON.parse(payload)); break; case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_ROOM_TOPIC: this.emit('room-topic', JSON.parse(payload)); break; case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_SCAN: this.emit('scan', JSON.parse(payload)); break; case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_RESET: config_js_1.log.warn('PuppetService', 'onGrpcStreamEvent() got an EventType.EVENT_TYPE_RESET ?'); // the `reset` event should be dealed not send out break; case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_UNSPECIFIED: config_js_1.log.error('PuppetService', 'onGrpcStreamEvent() got an EventType.EVENT_TYPE_UNSPECIFIED ?'); break; default: // Huan(202003): in default, the `type` type should be `never`, please check. throw new Error('eventType ' + type + ' unsupported! (code should not reach here)'); } } async logout(reason) { config_js_1.log.verbose('PuppetService', 'logout(%s)', reason ? `"${reason}"` : ''); await super.logout(reason); try { await util_1.default.promisify(this.grpcManager.client.logout .bind(this.grpcManager.client))(new wechaty_grpc_1.puppet.LogoutRequest()); } catch (e) { config_js_1.log.silly('PuppetService', 'logout() no grpc client'); } } ding(data) { config_js_1.log.silly('PuppetService', 'ding(%s)', data); const request = new wechaty_grpc_1.puppet.DingRequest(); request.setData(data || ''); this.grpcManager.client.ding(request, (error, _response) => { if (error) { config_js_1.log.error('PuppetService', 'ding() rejection: %s', error); } }); } /** * * Huan(202111) Issue #158 - Refactoring the 'dirty' event, dirtyPayload(), * and XXXPayloadDirty() methods logic & spec * * @see https://github.com/wechaty/puppet/issues/158 * */ async dirtyPayload(type, id) { config_js_1.log.verbose('PuppetService', 'dirtyPayload(%s, %s)', type, id); const request = new wechaty_grpc_1.puppet.DirtyPayloadRequest(); request.setId(id); request.setType(type); try { await util_1.default.promisify(this.grpcManager.client.dirtyPayload .bind(this.grpcManager.client))(request); } catch (e) { config_js_1.log.error('PuppetService', 'dirtyPayload() rejection: %s', e && e.message); throw e; } } /** * `onDirty()` is called when the puppet emit `dirty` event. * the event listener will be registered in `start()` from the `PuppetAbstract` class */ onDirty({ payloadType, payloadId, }) { config_js_1.log.verbose('PuppetService', 'onDirty(%s<%s>, %s)', PUPPET.types.Dirty[payloadType], payloadType, payloadId); const dirtyMap = { [PUPPET.types.Dirty.Contact]: async (id) => this._payloadStore.contact?.delete(id), [PUPPET.types.Dirty.Friendship]: async (_) => { }, [PUPPET.types.Dirty.Message]: async (_) => { }, [PUPPET.types.Dirty.Post]: async (_) => { }, [PUPPET.types.Dirty.Room]: async (id) => this._payloadStore.room?.delete(id), [PUPPET.types.Dirty.RoomMember]: async (id) => this._payloadStore.roomMember?.delete(id), [PUPPET.types.Dirty.Unspecified]: async (id) => { throw new Error('Unspecified type with id: ' + id); }, }; const dirtyFuncSync = this.wrapAsync(dirtyMap[payloadType]); dirtyFuncSync(payloadId); /** * We need to call `super.onDirty()` to clean the `PuppetAbstract` LRUCache */ super.onDirty({ payloadId, payloadType }); } async contactAlias(contactId, alias) { config_js_1.log.verbose('PuppetService', 'contactAlias(%s, %s)', contactId, alias); /** * Get alias */ if (typeof alias === 'undefined') { const request = new wechaty_grpc_1.puppet.ContactAliasRequest(); request.setId(contactId); const response = await util_1.default.promisify(this.grpcManager.client.contactAlias .bind(this.grpcManager.client))(request); const result = response.getAlias(); if (result) { return result; } { // DEPRECATED, will be removed after Dec 31, 2022 const aliasWrapper = response.getAliasStringValueDeprecated(); if (!aliasWrapper) { throw new Error('can not get aliasWrapper'); } return aliasWrapper.getValue(); } } /** * Set alias */ const request = new wechaty_grpc_1.puppet.ContactAliasRequest(); request.setId(contactId); request.setAlias(alias || ''); // null -> '', in server, we treat '' as null { // DEPRECATED, will be removed after Dec 31, 2022 const aliasWrapper = new wechaty_grpc_1.StringValue(); aliasWrapper.setValue(alias || ''); // null -> '', in server, we treat '' as null request.setAliasStringValueDeprecated(aliasWrapper); } await util_1.default.promisify(this.grpcManager.client.contactAlias .bind(this.grpcManager.client))(request); } async contactPhone(contactId, phoneList) { config_js_1.log.verbose('PuppetService', 'contactPhone(%s, %s)', contactId, phoneList); const request = new wechaty_grpc_1.puppet.ContactPhoneRequest(); request.setContactId(contactId); request.setPhonesList(phoneList); await util_1.default.promisify(this.grpcManager.client.contactPhone .bind(this.grpcManager.client))(request); } async contactCorporationRemark(contactId, corporationRemark) { config_js_1.log.verbose('PuppetService', 'contactCorporationRemark(%s, %s)', contactId, corporationRemark); const request = new wechaty_grpc_1.puppet.ContactCorporationRemarkRequest(); request.setContactId(contactId); if (corporationRemark) { request.setCorporationRemark(corporationRemark); } { // DEPRECATED, will be removed after Dec 31, 2022 const corporationRemarkWrapper = new wechaty_grpc_1.StringValue(); if (corporationRemark) { corporationRemarkWrapper.setValue(corporationRemark); request.setCorporationRemarkStringValueDeprecated(corporationRemarkWrapper); } } await util_1.default.promisify(this.grpcManager.client.contactCorporationRemark .bind(this.grpcManager.client))(request); } async contactDescription(contactId, description) { config_js_1.log.verbose('PuppetService', 'contactDescription(%s, %s)', contactId, description); const request = new wechaty_grpc_1.puppet.ContactDescriptionRequest(); request.setContactId(contactId); if (description) { request.setDescription(description); } { // DEPRECATED, will be removed after Dec 31, 2022 const descriptionWrapper = new wechaty_grpc_1.StringValue(); if (description) { descriptionWrapper.setValue(description); request.setDescriptionStringValueDeprecated(descriptionWrapper); } } await util_1.default.promisify(this.grpcManager.client.contactDescription .bind(this.grpcManager.client))(request); } async contactList() { config_js_1.log.verbose('PuppetService', 'contactList()'); const response = await util_1.default.promisify(this.grpcManager.client.contactList .bind(this.grpcManager.client))(new wechaty_grpc_1.puppet.ContactListRequest()); return response.getIdsList(); } async contactAvatar(contactId, fileBox) { config_js_1.log.verbose('PuppetService', 'contactAvatar(%s)', contactId); /** * 1. set */ if (fileBox) { const request = new wechaty_grpc_1.puppet.ContactAvatarRequest(); request.setId(contactId); const serializedFileBox = await this.serializeFileBox(fileBox); request.setFileBox(serializedFileBox); { // DEPRECATED, will be removed after Dec 31, 2022 const fileboxWrapper = new wechaty_grpc_1.StringValue(); fileboxWrapper.setValue(await (0, serialize_file_box_js_1.serializeFileBox)(fileBox)); request.setFileboxStringValueDeprecated(fileboxWrapper); } await util_1.default.promisify(this.grpcManager.client.contactAvatar .bind(this.grpcManager.client))(request); return; } /** * 2. get */ const request = new wechaty_grpc_1.puppet.ContactAvatarRequest(); request.setId(contactId); const response = await util_1.default.promisify(this.grpcManager.client.contactAvatar .bind(this.grpcManager.client))(request); let jsonText; jsonText = response.getFileBox(); { // DEPRECATED, will be removed after Dec 31, 2022 const deprecated = true; void deprecated; if (!jsonText) { const textWrapper = response.getFileboxStringValueDeprecated(); if (!textWrapper) { throw new Error('can not get textWrapper'); } jsonText = textWrapper.getValue(); } } return this.FileBoxUuid.fromJSON(jsonText); } async contactRawPayload(id) { config_js_1.log.verbose('PuppetService', 'contactRawPayload(%s)', id); const cachedPayload = await this._payloadStore.contact?.get(id); if (cachedPayload) { config_js_1.log.silly('PuppetService', 'contactRawPayload(%s) cache HIT', id); return cachedPayload; } const request = new wechaty_grpc_1.puppet.ContactPayloadRequest(); request.setId(id); const response = await util_1.default.promisify(this.grpcManager.client.contactPayload .bind(this.grpcManager.client))(request); const payload = { address: response.getAddress(), alias: response.getAlias(), avatar: response.getAvatar(), city: response.getCity(), corporation: response.getCorporation(), coworker: response.getCoworker(), description: response.getDescription(), friend: response.getFriend(), gender: response.getGender(), id: response.getId(), name: response.getName(), phone: response.getPhonesList(), province: response.getProvince(), signature: response.getSignature(), star: response.getStar(), title: response.getTitle(), type: response.getType(), weixin: response.getWeixin(), }; await this._payloadStore.contact?.set(id, payload); config_js_1.log.silly('PuppetService', 'contactRawPayload(%s) cache SET', id); return payload; } async contactRawPayloadParser(payload) { // log.silly('PuppetService', 'contactRawPayloadParser({id:%s})', payload.id) // passthrough return payload; } async contactSelfName(name) { config_js_1.log.verbose('PuppetService', 'contactSelfName(%s)', name); const request = new wechaty_grpc_1.puppet.ContactSelfNameRequest(); request.setName(name); await util_1.default.promisify(this.grpcManager.client.contactSelfName .bind(this.grpcManager.client))(request); } async contactSelfQRCode() { config_js_1.log.verbose('PuppetService', 'contactSelfQRCode()'); const response = await util_1.default.promisify(this.grpcManager.client.contactSelfQRCode .bind(this.grpcManager.client))(new wechaty_grpc_1.puppet.ContactSelfQRCodeRequest()); return response.getQrcode(); } async contactSelfSignature(signature) { config_js_1.log.verbose('PuppetService', 'contactSelfSignature(%s)', signature); const request = new wechaty_grpc_1.puppet.ContactSelfSignatureRequest(); request.setSignature(signature); await util_1.default.promisify(this.grpcManager.client.contactSelfSignature .bind(this.grpcManager.client))(request); } /** * * Conversation * */ conversationReadMark(conversationId, hasRead = true) { config_js_1.log.verbose('PuppetService', 'conversationMarkRead(%s, %s)', conversationId, hasRead); return PUPPET.throwUnsupportedError('not implemented. See https://github.com/wechaty/wechaty-puppet/pull/132'); } /** * * Message * */ async messageMiniProgram(messageId) { config_js_1.log.verbose('PuppetService', 'messageMiniProgram(%s)', messageId); const request = new wechaty_grpc_1.puppet.MessageMiniProgramRequest(); request.setId(messageId); const response = await util_1.default.promisify(this.grpcManager.client.messageMiniProgram .bind(this.grpcManager.client))(request); let miniProgramPayload = response.getMiniProgram()?.toObject(); if (!miniProgramPayload) { /** * Deprecated: will be removed after Dec 22, 2022 */ const jsonText = response.getMiniProgramDeprecated(); miniProgramPayload = JSON.parse(jsonText); } const payload = { ...miniProgramPayload, }; return payload; } async messageLocation(messageId) { config_js_1.log.verbose('PuppetService', 'messageLocation(%s)', messageId); const request = new wechaty_grpc_1.puppet.MessageLocationRequest(); request.setId(messageId); const response = await util_1.default.promisify(this.grpcManager.client.messageLocation .bind(this.grpcManager.client))(request); const locationPayload = response.getLocation(); const payload = { accuracy: 0, address: 'NOADDRESS', latitude: 0, longitude: 0, name: 'NONAME', ...locationPayload, }; return payload; } async messageImage(messageId, imageType) { config_js_1.log.verbose('PuppetService', 'messageImage(%s, %s[%s])', messageId, imageType, PUPPET.types.Image[imageType]); try { const request = new wechaty_grpc_1.puppet.MessageImageRequest(); request.setId(messageId); request.setType(imageType); const response = await util_1.default.promisify(this.grpcManager.client.messageImage .bind(this.grpcManager.client))(request); const jsonText = response.getFileBox(); if (jsonText) { return this.FileBoxUuid.fromJSON(jsonText); } } catch (e) { config_js_1.log.verbose('PuppetService', 'messageImage() rejection %s', e.message); } { // Deprecated. Will be removed after Dec 31, 2022 const request = new wechaty_grpc_1.puppet.MessageImageStreamRequest(); request.setId(messageId); request.setType(imageType); const pbStream = this.grpcManager.client.messageImageStream(request); const fileBox = await (0, mod_js_1.unpackFileBoxFromPb)(pbStream); // const fileBoxChunkStream = unpackFileBoxChunk(stream) // return unpackFileBox(fileBoxChunkStream) return fileBox; } } async messageContact(messageId) { config_js_1.log.verbose('PuppetService', 'messageContact(%s)', messageId); const request = new wechaty_grpc_1.puppet.MessageContactRequest(); request.setId(messageId); const response = await util_1.default.promisify(this.grpcManager.client.messageContact .bind(this.grpcManager.client))(request); const contactId = response.getId(); return contactId; } async messageSendMiniProgram(conversationId, miniProgramPayload) { config_js_1.log.verbose('PuppetService', 'messageSendMiniProgram(%s, "%s")', conversationId, JSON.stringify(miniProgramPayload)); const request = new wechaty_grpc_1.puppet.MessageSendMiniProgramRequest(); request.setConversationId(conversationId); const pbMiniProgramPayload = new wechaty_grpc_1.puppet.MiniProgramPayload(); if (miniProgramPayload.appid) { pbMiniProgramPayload.setAppid(miniProgramPayload.appid); } if (miniProgramPayload.description) { pbMiniProgramPayload.setDescription(miniProgramPayload.description); } if (miniProgramPayload.iconUrl) { pbMiniProgramPayload.setIconUrl(miniProgramPayload.iconUrl); } if (miniProgramPayload.pagePath) { pbMiniProgramPayload.setPagePath(miniProgramPayload.pagePath); } if (miniProgramPayload.shareId) { pbMiniProgramPayload.setShareId(miniProgramPayload.shareId); } if (miniProgramPayload.thumbKey) { pbMiniProgramPayload.setThumbKey(miniProgramPayload.thumbKey); } if (miniProgramPayload.thumbUrl) { pbMiniProgramPayload.setThumbUrl(miniProgramPayload.thumbUrl); } if (miniProgramPayload.title) { pbMiniProgramPayload.setTitle(miniProgramPayload.title); } if (miniProgramPayload.username) { pbMiniProgramPayload.setUsername(miniProgramPayload.username); } request.setMiniProgram(pbMiniProgramPayload); /** * Deprecated: will be removed after Dec 31, 2022 */ request.setMiniProgramDeprecated(JSON.stringify(miniProgramPayload)); const response = await util_1.default.promisify(this.grpcManager.client.messageSendMiniProgram .bind(this.grpcManager.client))(request); const messageId = response.getId(); if (messageId) { return messageId; } { /** * Huan(202110): Deprecated: will be removed after Dec 31, 2022 */ const messageIdWrapper = response.getIdStringValueDeprecated(); if (messageIdWrapper) { return messageIdWrapper.getValue(); } } } async messageSendLocation(conversationId, locationPayload) { config_js_1.log.verbose('PuppetService', 'messageSendLocation(%s)', conversationId, JSON.stringify(locationPayload)); const request = new wechaty_grpc_1.puppet.MessageSendLocationRequest(); request.setConversationId(conversationId); const pbLocationPayload = new wechaty_grpc_1.puppet.LocationPayload(); pbLocationPayload.setAccuracy(locationPayload.accuracy); pbLocationPayload.setAddress(locationPayload.address); pbLocationPayload.setLatitude(locationPayload.latitude); pbLocationPayload.setLongitude(locationPayload.longitude); pbLocationPayload.setName(locationPayload.name); request.setLocation(pbLocationPayload); const response = await util_1.default.promisify(this.grpcManager.client.messageSendLocation .bind(this.grpcManager.client))(request); const id = response.getId(); if (id) { return id; } } async messageRecall(messageId) { config_js_1.log.verbose('PuppetService', 'messageRecall(%s)', messageId); const request = new wechaty_grpc_1.puppet.MessageRecallRequest(); request.setId(messageId); const response = await util_1.default.promisify(this.grpcManager.client.messageRecall .bind(this.grpcManager.client))(request); return response.getSuccess(); } async messageFile(id) { config_js_1.log.verbose('PuppetService', 'messageFile(%s)', id); try { const request = new wechaty_grpc_1.puppet.MessageFileRequest(); request.setId(id); const response = await util_1.default.promisify(this.grpcManager.client.messageFile .bind(this.grpcManager.client))(request); const jsonText = response.getFileBox(); if (jsonText) { return this.FileBoxUuid.fromJSON(jsonText); } } catch (e) { config_js_1.log.warn('PuppetService', 'messageFile() rejection: %s', e.message); config_js_1.log.warn('PuppetService', [ 'This might because you are using Wechaty v1.x with a Puppet Service v0.x', 'Contact your Wechaty Puppet Service provided to report this problem', 'Related issues:', ' - https://github.com/wechaty/puppet-service/issues/179', ' - https://github.com/wechaty/puppet-service/pull/170', ].join('\n')); } { // Deprecated. `MessageFileStream` Will be removed after Dec 31, 2022 const request = new wechaty_grpc_1.puppet.MessageFileStreamRequest(); request.setId(id); const pbStream = this.grpcManager.client.messageFileStream(request); // const fileBoxChunkStream = unpackFileBoxChunk(pbStream) // return unpackFileBox(fileBoxChunkStream) const fileBox = await (0, mod_js_1.unpackFileBoxFromPb)(pbStream); return fileBox; } } async messageForward(conversationId, messageId) { config_js_1.log.verbose('PuppetService', 'messageForward(%s, %s)', conversationId, messageId); const request = new wechaty_grpc_1.puppet.MessageForwardRequest(); request.setConversationId(conversationId); request.setMessageId(messageId); const response = await util_1.default.promisify(this.grpcManager.client.messageForward .bind(this.grpcManager.client))(request); const forwardedMessageId = response.getId(); if (forwardedMessageId) { return forwardedMessageId; } { /** * Huan(202110): Deprecated: will be removed after Dec 31, 2022 */ const messageIdWrapper = response.getIdStringValueDeprecated(); if (messageIdWrapper) { return messageIdWrapper.getValue(); } } } async messageRawPayload(id) { config_js_1.log.verbose('PuppetService', 'messageRawPayload(%s)', id); // const cachedPayload = await this.payloadStore.message?.get(id) // if (cachedPayload) { // log.silly('PuppetService', 'messageRawPayload(%s) cache HIT', id) // return cachedPayload // } const request = new wechaty_grpc_1.puppet.MessagePayloadRequest(); request.setId(id); const response = await util_1.default.promisify(this.grpcManager.client.messagePayload .bind(this.grpcManager.client))(request); let timestamp; const receiveTime = response.getReceiveTime(); if (receiveTime) { timestamp = (0, timestamp_js_1.millisecondsFromTimestamp)(receiveTime); } else { // Deprecated: will be removed after Dec 31, 2022 timestamp = response.getTimestampDeprecated(); } const payload = { filename: response.getFilename(), id: response.getId(), listenerId: response.getListenerId(), mentionIdList: response.getMentionIdsList(), roomId: response.getRoomId(), talkerId: response.getTalkerId(), text: response.getText(), timestamp, type: response.getType(), }; // log.silly('PuppetService', 'messageRawPayload(%s) cache SET', id) // await this.payloadStore.message?.set(id, payload) return payload; } async messageRawPayloadParser(payload) { // log.silly('PuppetService', 'messagePayload({id:%s})', payload.id) // passthrough return payload; } async messageSendText(conversationId, text, mentionIdList) { config_js_1.log.verbose('PuppetService', 'messageSend(%s, %s)', conversationId, text); const request = new wechaty_grpc_1.puppet.MessageSendTextRequest(); request.setConversationId(conversationId); request.setText(text); if (typeof mentionIdList !== 'undefined') { request.setMentionalIdsList(mentionIdList); } const response = await util_1.default.promisify(this.grpcManager.client.messageSendText .bind(this.grpcManager.client))(request); const messageId = response.getId(); if (messageId) { return messageId; } { /** * Huan(202110): Deprecated: will be removed after Dec 31, 2022 */ const messageIdWrapper = response.getIdStringValueDeprecated(); if (messageIdWrapper) { return messageIdWrapper.getValue(); } } } async messageSendFile(conversationId, fileBox) { config_js_1.log.verbose('PuppetService', 'messageSendFile(%s, %s)', conversationId, fileBox); try { const request = new wechaty_grpc_1.puppet.MessageSendFileRequest(); request.setConversationId(conversationId); const serializedFileBox = await this.serializeFileBox(fileBox); request.setFileBox(serializedFileBox); const response = await util_1.default.promisify(this.grpcManager.client.messageSendFile .bind(this.grpcManager.client))(request); const messageId = response.getId(); if (messageId) { return messageId; } else { /** * Huan(202110): Deprecated: will be removed after Dec 31, 2022 */ const messageIdWrapper = response.getIdStringValueDeprecated(); if (messageIdWrapper) { return messageIdWrapper.getValue(); } } return; // void } catch (e) { config_js_1.log.verbose('PuppetService', 'messageSendFile() rejection: %s', e.message); } /** * Huan(202110): Deprecated: will be removed after Dec 31, 2022 * The old server will not support `Upload` gRPC method, * which I'm expecting the above code will throw a exception, * then the below code will be executed. */ return this.messageSendFileStream(conversationId, fileBox); } async messageSendContact(conversationId, contactId) { config_js_1.log.verbose('PuppetService', 'messageSend("%s", %s)', conversationId, contactId); const request = new wechaty_grpc_1.puppet.MessageSendContactRequest(); request.setConversationId(conversationId); request.setContactId(contactId); const response = await util_1.default.promisify(this.grpcManager.client.messageSendContact .bind(this.grpcManager.client))(request); const messageId = response.getId(); if (messageId) { return messageId; } { /** * Huan(202110): Deprecated: will be removed after Dec 31, 2022 */ const messageIdWrapper = response.getIdStringValueDeprecated(); if (messageIdWrapper) { return messageIdWrapper.getValue(); } } } async messageSendUrl(conversationId, urlLinkPayload) { config_js_1.log.verbose('PuppetService', 'messageSendUrl("%s", %s)', conversationId, JSON.stringify(urlLinkPayload)); const request = new wechaty_grpc_1.puppet.MessageSendUrlRequest(); request.setConversationId(conversationId); const pbUrlLinkPayload = new wechaty_grpc_1.puppet.UrlLinkPayload(); pbUrlLinkPayload.setUrl(urlLinkPayload.url); pbUrlLinkPayload.setTitle(urlLinkPayload.title); if (urlLinkPayload.description) { pbUrlLinkPayload.setDescription(urlLinkPayload.description); } if (urlLinkPayload.thumbnailUrl) { pbUrlLinkPayload.setThumbnailUrl(urlLinkPayload.thumbnailUrl); } request.setUrlLink(pbUrlLinkPayload); // Deprecated: will be removed after Dec 31, 2022 request.setUrlLinkDeprecated(JSON.stringify(urlLinkPayload)); const response = await util_1.default.promisify(this.grpcManager.client.messageSendUrl .bind(this.grpcManager.client))(request); const messageId = response.getId(); if (messageId) { return messageId; } { /** * Huan(202110): Deprecated: will be removed after Dec 31, 2022 */ const messageIdWrapper = response.getIdStringValueDeprecated(); if (messageIdWrapper) { return messageIdWrapper.getValue(); } } } async messageUrl(messageId) { config_js_1.log.verbose('PuppetService', 'messageUrl(%s)', messageId); const request = new wechaty_grpc_1.puppet.MessageUrlRequest(); request.setId(messageId); const response = await util_1.default.promisify(this.grpcManager.client.messageUrl .bind(this.grpcManager.client))(request); let pbUrlLinkPayload = response.getUrlLink()?.toObject(); if (!pbUrlLinkPayload) { // Deprecated: will be removed after Dec 31, 2022 const jsonText = response.getUrlLinkDeprecated(); pbUrlLinkPayload = JSON.parse(jsonText); } const payload = { title: 'NOTITLE', url: 'NOURL', ...pbUrlLinkPayload, }; return payload; } /** * * Room * */ async roomRawPayload(id) { config_js_1.log.verbose('PuppetService', 'roomRawPayload(%s)', id); const cachedPayload = await this._payloadStore.room?.get(id); if (cachedPayload) { config_js_1.log.silly('PuppetService', 'roomRawPayload(%s) cache HIT', id); return cachedPayload; } const request = new wechaty_grpc_1.puppet.RoomPayloadRequest(); request.setId(id); const response = await util_1.default.promisify(this.grpcManager.client.roomPayload .bind(this.grpcManager.client))(request); const payload = { adminIdList: response.getAdminIdsList(), avatar: response.getAvatar(), id: response.getId(), memberIdList: response.getMemberIdsList(), ownerId: response.getOwnerId(), topic: response.getTopic(), }; await this._payloadStore.room?.set(id, payload); config_js_1.log.silly('PuppetService', 'roomRawPayload(%s) cache SET', id); return payload; } async roomRawPayloadParser(payload) { // log.silly('PuppetService', 'roomRawPayloadParser({id:%s})', payload.id) // passthrough return payload; } async roomList() { config_js_1.log.verbose('PuppetService', 'roomList()'); const response = await util_1.default.promisify(this.grpcManager.client.roomList .bind(this.grpcManager.client))(new wechaty_grpc_1.puppet.RoomListRequest()); return response.getIdsList(); } async roomDel(roomId, contactId) { config_js_1.log.verbose('PuppetService', 'roomDel(%s, %s)', roomId, contactId); const request = new wechaty_grpc_1.puppet.RoomDelRequest(); request.setId(roomId); request.setContactId(contactId); await util_1.default.promisify(this.grpcManager.client.roomDel .bind(this.grpcManager.client))(request); } async roomAvatar(roomId) { config_js_1.log.verbose('PuppetService', 'roomAvatar(%s)', roomId); const request = new wechaty_grpc_1.puppet.RoomAvatarRequest(); request.setId(roomId); const response = await util_1.default.promisify(this.grpcManager.client.roomAvatar .bind(this.grpcManager.client))(request); const jsonText = response.getFileBox(); return this.FileBoxUuid.fromJSON(jsonText); } async roomAdd(roomId, contactId, inviteOnly) { config_js_1.log.verbose('PuppetService', 'roomAdd(%s, %s)', roomId, contactId); const request = new wechaty_grpc_1.puppet.RoomAddRequest(); request.setId(roomId); request.setContactId(contactId); request.setInviteOnly(inviteOnly); await util_1.default.promisify(this.grpcManager.client.roomAdd .bind(this.grpcManager.client))(request); } async roomTopic(roomId, topic) { config_js_1.log.verbose('PuppetService', 'roomTopic(%s, %s)', roomId, topic); /** * Get */ if (typeof topic === 'undefined') { const request = new wechaty_grpc_1.puppet.RoomTopicRequest(); request.setId(roomId); const response = await util_1.default.promisify(this.grpcManager.client.roomTopic .bind(this.grpcManager.client))(request); const result = response.getTopic(); if (result) { return result; } { // DEPRECATED, will be removed after Dec 31, 2022 const topicWrapper = response.getTopicStringValueDeprecated(); if (topicWrapper) { return topicWrapper.getValue(); } } return ''; } /** * Set */ const request = new wechaty_grpc_1.puppet.RoomTopicRequest(); request.setId(roomId); request.setTopic(topic); { // DEPRECATED, will be removed after Dec 31, 2022 const topicWrapper = new wechaty_grpc_1.StringValue(); topicWrapper.setValue(topic); request.setTopicStringValueDeprecated(topicWrapper); } await util_1.default.promisify(this.grpcManager.client.roomTopic .bind(this.grpcManager.client))(request); } async roomCreate(contactIdList, topic) { config_js_1.log.verbose('PuppetService', 'roomCreate(%s, %s)', contactIdList, topic); const request = new wechaty_grpc_1.puppet.RoomCreateRequest(); request.setContactIdsList(contactIdList); request.setTopic(topic); const response = await util_1.default.promisify(this.grpcManager.client.roomCreate .bind(this.grpcManager.client))(request); return response.getId(); } async roomQuit(roomId) { config_js_1.log.verbose('PuppetService', 'roomQuit(%s)', roomId); const request = new wechaty_grpc_1.puppet.RoomQuitRequest(); request.setId(roomId); await util_1.default.promisify(this.grpcManager.client.roomQuit .bind(this.grpcManager.client))(request); } async roomQRCode(roomId) { config_js_1.log.verbose('PuppetService', 'roomQRCode(%s)', roomId); const request = new wechaty_grpc_1.puppet.RoomQRCodeRequest(); request.setId(roomId); const response = await util_1.default.promisify(this.grpcManager.client.roomQRCode .bind(this.grpcManager.client))(request); return response.getQrcode(); } async roomMemberList(roomId) { config_js_1.log.verbose('PuppetService', 'roomMemberList(%s)', roomId); const request = new wechaty_grpc_1.puppet.RoomMemberListRequest(); request.setId(roomId); const response = await util_1.default.promisify(this.grpcManager.client.roomMemberList .bind(this.grpcManager.client))(request); return response.getMemberIdsList(); } async roomMemberRawPayload(roomId, contactId) { config_js_1.log.verbose('PuppetService', 'roomMemberRawPayload(%s, %s)', roomId, contactId); const cachedPayload = await this._payloadStore.roomMember?.get(roomId); const cachedRoomMemberPayload = cachedPayload && cachedPayload[contactId]; if (cachedRoomMemberPayload) { config_js_1.log.silly('PuppetService', 'roomMemberRawPayload(%s, %s) cache HIT', roomId, contactId); return cachedRoomMemberPayload; } const request = new wechaty_grpc_1.puppet.RoomMemberPayloadRequest(); request.setId(roomId); request.setMemberId(contactId); const response = await util_1.default.promisify(this.grpcManager.client.roomMemberPayload .bind(this.grpcManager.client))(request); const payload = { avatar: response.getAvatar(), id: response.getId(), inviterId: response.getInviterId(), name: response.getName(), roomAlias: response.getRoomAlias(), }; await this._payloadStore.roomMember?.set(roomId, { ...cachedPayload, contactId: payload, }); config_js_1.log.silly('PuppetService', 'roomMemberRawPayload(%s, %s) cache SET', roomId, contactId); return payload; } async roomMemberRawPayloadParser(payload) {