UNPKG

33.5 kBJavaScriptView Raw
1"use strict";
2var __importDefault = (this && this.__importDefault) || function (mod) {
3 return (mod && mod.__esModule) ? mod : { "default": mod };
4};
5Object.defineProperty(exports, "__esModule", { value: true });
6exports.Wechaty = void 0;
7/**
8 * Wechaty Chatbot SDK - https://github.com/wechaty/wechaty
9 *
10 * @copyright 2016 Huan LI (李卓桓) <https://github.com/huan>, and
11 * Wechaty Contributors <https://github.com/wechaty>.
12 *
13 * Licensed under the Apache License, Version 2.0 (the "License");
14 * you may not use this file except in compliance with the License.
15 * You may obtain a copy of the License at
16 *
17 * http://www.apache.org/licenses/LICENSE-2.0
18 *
19 * Unless required by applicable law or agreed to in writing, software
20 * distributed under the License is distributed on an "AS IS" BASIS,
21 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 * See the License for the specific language governing permissions and
23 * limitations under the License.
24 *
25 */
26const cuid_1 = __importDefault(require("cuid"));
27const os_1 = __importDefault(require("os"));
28const clone_class_1 = require("clone-class");
29const wechaty_puppet_1 = require("wechaty-puppet");
30const config_1 = require("./config");
31const version_1 = require("./version");
32const io_1 = require("./io");
33const puppet_manager_1 = require("./puppet-manager");
34const mod_1 = require("./user/mod");
35const timestamp_to_date_1 = require("./helper-functions/pure/timestamp-to-date");
36const wechaty_events_1 = require("./events/wechaty-events");
37const plugin_1 = require("./plugin");
38const PUPPET_MEMORY_NAME = 'puppet';
39/**
40 * Main bot class.
41 *
42 * A `Bot` is a WeChat client depends on which puppet you use.
43 * It may equals
44 * - web-WeChat, when you use: [puppet-puppeteer](https://github.com/wechaty/wechaty-puppet-puppeteer)/[puppet-wechat4u](https://github.com/wechaty/wechaty-puppet-wechat4u)
45 * - ipad-WeChat, when you use: [puppet-padchat](https://github.com/wechaty/wechaty-puppet-padchat)
46 * - ios-WeChat, when you use: puppet-ioscat
47 *
48 * See more:
49 * - [What is a Puppet in Wechaty](https://github.com/wechaty/wechaty-getting-started/wiki/FAQ-EN#31-what-is-a-puppet-in-wechaty)
50 *
51 * > If you want to know how to send message, see [Message](#Message) <br>
52 * > If you want to know how to get contact, see [Contact](#Contact)
53 *
54 * @example <caption>The World's Shortest ChatBot Code: 6 lines of JavaScript</caption>
55 * const { Wechaty } = require('wechaty')
56 * const bot = new Wechaty()
57 * bot.on('scan', (qrCode, status) => console.log('https://wechaty.js.org/qrcode/' + encodeURIComponent(qrcode)))
58 * bot.on('login', user => console.log(`User ${user} logged in`))
59 * bot.on('message', message => console.log(`Message: ${message}`))
60 * bot.start()
61 */
62class Wechaty extends wechaty_events_1.WechatyEventEmitter {
63 /**
64 * The term [Puppet](https://github.com/wechaty/wechaty/wiki/Puppet) in Wechaty is an Abstract Class for implementing protocol plugins.
65 * The plugins are the component that helps Wechaty to control the WeChat(that's the reason we call it puppet).
66 * The plugins are named XXXPuppet, for example:
67 * - [PuppetPuppeteer](https://github.com/wechaty/wechaty-puppet-puppeteer):
68 * - [PuppetPadchat](https://github.com/wechaty/wechaty-puppet-padchat)
69 *
70 * @typedef PuppetModuleName
71 * @property {string} PUPPET_DEFAULT
72 * The default puppet.
73 * @property {string} wechaty-puppet-wechat4u
74 * The default puppet, using the [wechat4u](https://github.com/nodeWechat/wechat4u) to control the [WeChat Web API](https://wx.qq.com/) via a chrome browser.
75 * @property {string} wechaty-puppet-padchat
76 * - Using the WebSocket protocol to connect with a Protocol Server for controlling the iPad WeChat program.
77 * @property {string} wechaty-puppet-puppeteer
78 * - Using the [google puppeteer](https://github.com/GoogleChrome/puppeteer) to control the [WeChat Web API](https://wx.qq.com/) via a chrome browser.
79 * @property {string} wechaty-puppet-mock
80 * - Using the mock data to mock wechat operation, just for test.
81 */
82 /**
83 * The option parameter to create a wechaty instance
84 *
85 * @typedef WechatyOptions
86 * @property {string} name -Wechaty Name. </br>
87 * When you set this: </br>
88 * `new Wechaty({name: 'wechaty-name'}) ` </br>
89 * it will generate a file called `wechaty-name.memory-card.json`. </br>
90 * This file stores the login information for bot. </br>
91 * If the file is valid, the bot can auto login so you don't need to scan the qrCode to login again. </br>
92 * Also, you can set the environment variable for `WECHATY_NAME` to set this value when you start. </br>
93 * eg: `WECHATY_NAME="your-cute-bot-name" node bot.js`
94 * @property {PuppetModuleName | Puppet} puppet -Puppet name or instance
95 * @property {Partial<PuppetOptions>} puppetOptions -Puppet TOKEN
96 * @property {string} ioToken -Io TOKEN
97 */
98 /**
99 * Creates an instance of Wechaty.
100 * @param {WechatyOptions} [options={}]
101 *
102 */
103 constructor(options = {}) {
104 super();
105 this.options = options;
106 this.log = config_1.log;
107 config_1.log.verbose('Wechaty', 'constructor()');
108 this.memory = this.options.memory;
109 this.id = cuid_1.default();
110 this.state = new wechaty_puppet_1.StateSwitch('Wechaty', { log: config_1.log });
111 this.readyState = new wechaty_puppet_1.StateSwitch('WechatyReady', { log: config_1.log });
112 this.wechaty = this;
113 /**
114 * Huan(202008):
115 *
116 * Set max listeners to 1K, so that we can add lots of listeners without the warning message.
117 * The listeners might be one of the following functionilities:
118 * 1. Plugins
119 * 2. Redux Observables
120 * 3. etc...
121 */
122 super.setMaxListeners(1024);
123 this.pluginUninstallerList = [];
124 this.installGlobalPlugin();
125 }
126 get Contact() { return guardWechatify(this.wechatifiedContact); }
127 get ContactSelf() { return guardWechatify(this.wechatifiedContactSelf); }
128 get Friendship() { return guardWechatify(this.wechatifiedFriendship); }
129 get Image() { return guardWechatify(this.wechatifiedImage); }
130 get Message() { return guardWechatify(this.wechatifiedMessage); }
131 get MiniProgram() { return guardWechatify(this.wechatifiedMiniProgram); }
132 get Room() { return guardWechatify(this.wechatifiedRoom); }
133 get RoomInvitation() { return guardWechatify(this.wechatifiedRoomInvitation); }
134 get Tag() { return guardWechatify(this.wechatifiedTag); }
135 get UrlLink() { return guardWechatify(this.wechatifiedUrlLink); }
136 /**
137 * Get the global instance of Wechaty
138 *
139 * @param {WechatyOptions} [options={}]
140 *
141 * @example <caption>The World's Shortest ChatBot Code: 6 lines of JavaScript</caption>
142 * const { Wechaty } = require('wechaty')
143 *
144 * Wechaty.instance() // Global instance
145 * .on('scan', (url, status) => console.log(`Scan QR Code to login: ${status}\n${url}`))
146 * .on('login', user => console.log(`User ${user} logged in`))
147 * .on('message', message => console.log(`Message: ${message}`))
148 * .start()
149 */
150 static instance(options) {
151 if (options && this.globalInstance) {
152 throw new Error('instance can be only initialized once by options!');
153 }
154 if (!this.globalInstance) {
155 this.globalInstance = new Wechaty(options);
156 }
157 return this.globalInstance;
158 }
159 /**
160 * @param {WechatyPlugin[]} plugins - The plugins you want to use
161 *
162 * @return {Wechaty} - this for chaining,
163 *
164 * @desc
165 * For wechaty ecosystem, allow user to define a 3rd party plugin for the all wechaty instances
166 *
167 * @example
168 * // Report all chat message to my server.
169 *
170 * function WechatyReportPlugin(options: { url: string }) {
171 * return function (this: Wechaty) {
172 * this.on('message', message => http.post(options.url, { data: message }))
173 * }
174 * }
175 *
176 * bot.use(WechatyReportPlugin({ url: 'http://somewhere.to.report.your.data.com' })
177 */
178 static use(...plugins) {
179 const pluginList = plugins.flat();
180 this.globalPluginList = this.globalPluginList.concat(pluginList);
181 }
182 /**
183 * @ignore
184 */
185 toString() {
186 if (!this.options) {
187 return this.constructor.name;
188 }
189 return [
190 'Wechaty#',
191 this.id,
192 `<${(this.options && this.options.puppet) || ''}>`,
193 `(${(this.memory && this.memory.name) || ''})`,
194 ].join('');
195 }
196 /**
197 * Wechaty bot name set by `options.name`
198 * default: `wechaty`
199 */
200 name() {
201 return this.options.name || 'wechaty';
202 }
203 on(event, listener) {
204 config_1.log.verbose('Wechaty', 'on(%s, listener) registering... listenerCount: %s', event, this.listenerCount(event));
205 return super.on(event, listener);
206 }
207 /**
208 * @param {WechatyPlugin[]} plugins - The plugins you want to use
209 *
210 * @return {Wechaty} - this for chaining,
211 *
212 * @desc
213 * For wechaty ecosystem, allow user to define a 3rd party plugin for the current wechaty instance.
214 *
215 * @example
216 * // The same usage with Wechaty.use().
217 *
218 */
219 use(...plugins) {
220 const pluginList = plugins.flat();
221 const uninstallerList = pluginList
222 .map(plugin => plugin(this))
223 .filter(plugin_1.isWechatyPluginUninstaller);
224 this.pluginUninstallerList.push(...uninstallerList);
225 return this;
226 }
227 installGlobalPlugin() {
228 const uninstallerList = clone_class_1.instanceToClass(this, Wechaty)
229 .globalPluginList
230 .map(plugin => plugin(this))
231 .filter(plugin_1.isWechatyPluginUninstaller);
232 this.pluginUninstallerList.push(...uninstallerList);
233 }
234 async initPuppet() {
235 config_1.log.verbose('Wechaty', 'initPuppet() %s', this.options.puppet || '');
236 const initialized = !!this.puppet;
237 if (initialized) {
238 config_1.log.verbose('Wechaty', 'initPuppet(%s) had already been initialized, no need to init twice', this.options.puppet);
239 return;
240 }
241 if (!this.memory) {
242 throw new Error('no memory');
243 }
244 const puppet = this.options.puppet || config_1.config.systemPuppetName();
245 const puppetMemory = this.memory.multiplex(PUPPET_MEMORY_NAME);
246 const puppetInstance = await puppet_manager_1.PuppetManager.resolve({
247 puppet,
248 puppetOptions: this.options.puppetOptions,
249 // wechaty : this,
250 });
251 /**
252 * Plug the Memory Card to Puppet
253 */
254 puppetInstance.setMemory(puppetMemory);
255 this.initPuppetEventBridge(puppetInstance);
256 this.wechatifyUserModules(puppetInstance);
257 this.emit('puppet', puppetInstance);
258 }
259 initPuppetEventBridge(puppet) {
260 config_1.log.verbose('Wechaty', 'initPuppetEventBridge(%s)', puppet);
261 const eventNameList = Object.keys(wechaty_puppet_1.PUPPET_EVENT_DICT);
262 for (const eventName of eventNameList) {
263 config_1.log.verbose('Wechaty', 'initPuppetEventBridge() puppet.on(%s) (listenerCount:%s) registering...', eventName, puppet.listenerCount(eventName));
264 switch (eventName) {
265 case 'dong':
266 puppet.on('dong', payload => {
267 this.emit('dong', payload.data);
268 });
269 break;
270 case 'error':
271 puppet.on('error', payload => {
272 this.emit('error', new Error(payload.data));
273 });
274 break;
275 case 'heartbeat':
276 puppet.on('heartbeat', payload => {
277 /**
278 * Use `watchdog` event from Puppet to `heartbeat` Wechaty.
279 */
280 // TODO: use a throttle queue to prevent beat too fast.
281 this.emit('heartbeat', payload.data);
282 });
283 break;
284 case 'friendship':
285 puppet.on('friendship', async (payload) => {
286 const friendship = this.Friendship.load(payload.friendshipId);
287 try {
288 await friendship.ready();
289 this.emit('friendship', friendship);
290 friendship.contact().emit('friendship', friendship);
291 }
292 catch (e) {
293 this.emit('error', e);
294 }
295 });
296 break;
297 case 'login':
298 puppet.on('login', async (payload) => {
299 const contact = this.ContactSelf.load(payload.contactId);
300 try {
301 await contact.ready();
302 this.emit('login', contact);
303 }
304 catch (e) {
305 this.emit('error', e);
306 }
307 });
308 break;
309 case 'logout':
310 puppet.on('logout', async (payload) => {
311 const contact = this.ContactSelf.load(payload.contactId);
312 try {
313 await contact.ready();
314 this.emit('logout', contact, payload.data);
315 }
316 catch (e) {
317 this.emit('error', e);
318 }
319 });
320 break;
321 case 'message':
322 puppet.on('message', async (payload) => {
323 const msg = this.Message.load(payload.messageId);
324 try {
325 await msg.ready();
326 this.emit('message', msg);
327 const room = msg.room();
328 if (room) {
329 room.emit('message', msg);
330 }
331 else {
332 msg.talker().emit('message', msg);
333 }
334 }
335 catch (e) {
336 this.emit('error', e);
337 }
338 });
339 break;
340 case 'ready':
341 puppet.on('ready', () => {
342 config_1.log.silly('Wechaty', 'initPuppetEventBridge() puppet.on(ready)');
343 this.emit('ready');
344 this.readyState.on(true);
345 });
346 break;
347 case 'room-invite':
348 puppet.on('room-invite', async (payload) => {
349 const roomInvitation = this.RoomInvitation.load(payload.roomInvitationId);
350 this.emit('room-invite', roomInvitation);
351 });
352 break;
353 case 'room-join':
354 puppet.on('room-join', async (payload) => {
355 const room = this.Room.load(payload.roomId);
356 try {
357 await room.sync();
358 const inviteeList = payload.inviteeIdList.map(id => this.Contact.load(id));
359 await Promise.all(inviteeList.map(c => c.ready()));
360 const inviter = this.Contact.load(payload.inviterId);
361 await inviter.ready();
362 const date = timestamp_to_date_1.timestampToDate(payload.timestamp);
363 this.emit('room-join', room, inviteeList, inviter, date);
364 room.emit('join', inviteeList, inviter, date);
365 }
366 catch (e) {
367 this.emit('error', e);
368 }
369 });
370 break;
371 case 'room-leave':
372 puppet.on('room-leave', async (payload) => {
373 try {
374 const room = this.Room.load(payload.roomId);
375 /**
376 * See: https://github.com/wechaty/wechaty/pull/1833
377 */
378 await room.sync();
379 const leaverList = payload.removeeIdList.map(id => this.Contact.load(id));
380 await Promise.all(leaverList.map(c => c.ready()));
381 const remover = this.Contact.load(payload.removerId);
382 await remover.ready();
383 const date = timestamp_to_date_1.timestampToDate(payload.timestamp);
384 this.emit('room-leave', room, leaverList, remover, date);
385 room.emit('leave', leaverList, remover, date);
386 // issue #254
387 const selfId = this.puppet.selfId();
388 if (selfId && payload.removeeIdList.includes(selfId)) {
389 await this.puppet.dirtyPayload(wechaty_puppet_1.PayloadType.Room, payload.roomId);
390 await this.puppet.dirtyPayload(wechaty_puppet_1.PayloadType.RoomMember, payload.roomId);
391 }
392 }
393 catch (e) {
394 this.emit('error', e);
395 }
396 });
397 break;
398 case 'room-topic':
399 puppet.on('room-topic', async (payload) => {
400 try {
401 const room = this.Room.load(payload.roomId);
402 await room.sync();
403 const changer = this.Contact.load(payload.changerId);
404 await changer.ready();
405 const date = timestamp_to_date_1.timestampToDate(payload.timestamp);
406 this.emit('room-topic', room, payload.newTopic, payload.oldTopic, changer, date);
407 room.emit('topic', payload.newTopic, payload.oldTopic, changer, date);
408 }
409 catch (e) {
410 this.emit('error', e);
411 }
412 });
413 break;
414 case 'scan':
415 puppet.on('scan', async (payload) => {
416 this.emit('scan', payload.qrcode || '', payload.status, payload.data);
417 });
418 break;
419 case 'reset':
420 // Do not propagation `reset` event from puppet
421 break;
422 case 'dirty':
423 /**
424 * https://github.com/wechaty/wechaty-puppet-service/issues/43
425 */
426 puppet.on('dirty', async ({ payloadType, payloadId }) => {
427 try {
428 switch (payloadType) {
429 case wechaty_puppet_1.PayloadType.RoomMember:
430 case wechaty_puppet_1.PayloadType.Contact:
431 await this.Contact.load(payloadId).sync();
432 break;
433 case wechaty_puppet_1.PayloadType.Room:
434 await this.Room.load(payloadId).sync();
435 break;
436 /**
437 * Huan(202008): noop for the following
438 */
439 case wechaty_puppet_1.PayloadType.Friendship:
440 // Friendship has no payload
441 break;
442 case wechaty_puppet_1.PayloadType.Message:
443 // Message does not need to dirty (?)
444 break;
445 case wechaty_puppet_1.PayloadType.Unknown:
446 default:
447 throw new Error('unknown payload type: ' + payloadType);
448 }
449 }
450 catch (e) {
451 this.emit('error', e);
452 }
453 });
454 break;
455 default:
456 /**
457 * Check: The eventName here should have the type `never`
458 */
459 throw new Error('eventName ' + eventName + ' unsupported!');
460 }
461 }
462 }
463 wechatifyUserModules(puppet) {
464 config_1.log.verbose('Wechaty', 'wechatifyUserModules(%s)', puppet);
465 if (this.wechatifiedContactSelf) {
466 throw new Error('can not be initialized twice!');
467 }
468 /**
469 * 1. Setup Wechaty User Classes
470 */
471 this.wechatifiedContact = mod_1.wechatifyContact(this);
472 this.wechatifiedContactSelf = mod_1.wechatifyContactSelf(this);
473 this.wechatifiedFriendship = mod_1.wechatifyFriendship(this);
474 this.wechatifiedImage = mod_1.wechatifyImage(this);
475 this.wechatifiedMessage = mod_1.wechatifyMessage(this);
476 this.wechatifiedMiniProgram = mod_1.wechatifyMiniProgram(this);
477 this.wechatifiedRoom = mod_1.wechatifyRoom(this);
478 this.wechatifiedRoomInvitation = mod_1.wechatifyRoomInvitation(this);
479 this.wechatifiedTag = mod_1.wechatifyTag(this);
480 this.wechatifiedUrlLink = mod_1.wechatifyUrlLink(this);
481 this.puppet = puppet;
482 }
483 /**
484 * Start the bot, return Promise.
485 *
486 * @returns {Promise<void>}
487 * @description
488 * When you start the bot, bot will begin to login, need you WeChat scan qrcode to login
489 * > Tips: All the bot operation needs to be triggered after start() is done
490 * @example
491 * await bot.start()
492 * // do other stuff with bot here
493 */
494 async start() {
495 config_1.log.verbose('Wechaty', '<%s>(%s) start() v%s is starting...', this.options.puppet || config_1.config.systemPuppetName(), this.options.name || '', this.version());
496 config_1.log.verbose('Wechaty', 'id: %s', this.id);
497 if (this.state.on()) {
498 config_1.log.silly('Wechaty', 'start() on a starting/started instance');
499 await this.state.ready('on');
500 config_1.log.silly('Wechaty', 'start() state.ready() resolved');
501 return;
502 }
503 this.readyState.off(true);
504 if (this.lifeTimer) {
505 throw new Error('start() lifeTimer exist');
506 }
507 this.state.on('pending');
508 try {
509 if (!this.memory) {
510 this.memory = new wechaty_puppet_1.MemoryCard(this.options.name);
511 }
512 try {
513 await this.memory.load();
514 }
515 catch (e) {
516 config_1.log.silly('Wechaty', 'start() memory.load() had already loaded');
517 }
518 await this.initPuppet();
519 await this.puppet.start();
520 if (this.options.ioToken) {
521 this.io = new io_1.Io({
522 token: this.options.ioToken,
523 wechaty: this,
524 });
525 await this.io.start();
526 }
527 }
528 catch (e) {
529 console.error(e);
530 config_1.log.error('Wechaty', 'start() exception: %s', e && e.message);
531 config_1.Raven.captureException(e);
532 this.emit('error', e);
533 try {
534 await this.stop();
535 }
536 catch (e) {
537 config_1.log.error('Wechaty', 'start() stop() exception: %s', e && e.message);
538 config_1.Raven.captureException(e);
539 this.emit('error', e);
540 }
541 return;
542 }
543 this.on('heartbeat', () => this.memoryCheck());
544 this.lifeTimer = setInterval(() => {
545 config_1.log.silly('Wechaty', 'start() setInterval() this timer is to keep Wechaty running...');
546 }, 1000 * 60 * 60);
547 this.state.on(true);
548 this.emit('start');
549 }
550 /**
551 * Stop the bot
552 *
553 * @returns {Promise<void>}
554 * @example
555 * await bot.stop()
556 */
557 async stop() {
558 config_1.log.verbose('Wechaty', '<%s> stop() v%s is stopping ...', this.options.puppet || config_1.config.systemPuppetName(), this.version());
559 /**
560 * Uninstall Plugins
561 * no matter the state is `ON` or `OFF`.
562 */
563 while (this.pluginUninstallerList.length > 0) {
564 const uninstaller = this.pluginUninstallerList.pop();
565 if (uninstaller)
566 uninstaller();
567 }
568 if (this.state.off()) {
569 config_1.log.silly('Wechaty', 'stop() on an stopping/stopped instance');
570 await this.state.ready('off');
571 config_1.log.silly('Wechaty', 'stop() state.ready(off) resolved');
572 return;
573 }
574 this.readyState.off(true);
575 this.state.off('pending');
576 if (this.lifeTimer) {
577 clearInterval(this.lifeTimer);
578 this.lifeTimer = undefined;
579 }
580 try {
581 await this.puppet.stop();
582 }
583 catch (e) {
584 config_1.log.warn('Wechaty', 'stop() puppet.stop() exception: %s', e.message);
585 }
586 try {
587 if (this.io) {
588 await this.io.stop();
589 this.io = undefined;
590 }
591 }
592 catch (e) {
593 config_1.log.error('Wechaty', 'stop() exception: %s', e.message);
594 config_1.Raven.captureException(e);
595 this.emit('error', e);
596 }
597 this.state.off(true);
598 this.emit('stop');
599 }
600 async ready() {
601 config_1.log.verbose('Wechaty', 'ready()');
602 return this.readyState.ready('on').then(() => {
603 return config_1.log.silly('Wechaty', 'ready() this.readyState.ready(on) resolved');
604 });
605 }
606 /**
607 * Logout the bot
608 *
609 * @returns {Promise<void>}
610 * @example
611 * await bot.logout()
612 */
613 async logout() {
614 config_1.log.verbose('Wechaty', 'logout()');
615 try {
616 await this.puppet.logout();
617 }
618 catch (e) {
619 config_1.log.error('Wechaty', 'logout() exception: %s', e.message);
620 config_1.Raven.captureException(e);
621 throw e;
622 }
623 }
624 /**
625 * Get the logon / logoff state
626 *
627 * @returns {boolean}
628 * @example
629 * if (bot.logonoff()) {
630 * console.log('Bot logged in')
631 * } else {
632 * console.log('Bot not logged in')
633 * }
634 */
635 logonoff() {
636 try {
637 return this.puppet.logonoff();
638 }
639 catch (e) {
640 // https://github.com/wechaty/wechaty/issues/1878
641 return false;
642 }
643 }
644 /**
645 * Get current user
646 *
647 * @returns {ContactSelf}
648 * @example
649 * const contact = bot.userSelf()
650 * console.log(`Bot is ${contact.name()}`)
651 */
652 userSelf() {
653 const userId = this.puppet.selfId();
654 const user = this.ContactSelf.load(userId);
655 return user;
656 }
657 /**
658 * Send message to userSelf, in other words, bot send message to itself.
659 * > Tips:
660 * This function is depending on the Puppet Implementation, see [puppet-compatible-table](https://github.com/wechaty/wechaty/wiki/Puppet#3-puppet-compatible-table)
661 *
662 * @param {(string | Contact | FileBox | UrlLink | MiniProgram)} something
663 * send text, Contact, or file to bot. </br>
664 * You can use {@link https://www.npmjs.com/package/file-box|FileBox} to send file
665 *
666 * @returns {Promise<void>}
667 *
668 * @example
669 * const bot = new Wechaty()
670 * await bot.start()
671 * // after logged in
672 *
673 * // 1. send text to bot itself
674 * await bot.say('hello!')
675 *
676 * // 2. send Contact to bot itself
677 * const contact = await bot.Contact.find()
678 * await bot.say(contact)
679 *
680 * // 3. send Image to bot itself from remote url
681 * import { FileBox } from 'wechaty'
682 * const fileBox = FileBox.fromUrl('https://wechaty.github.io/wechaty/images/bot-qr-code.png')
683 * await bot.say(fileBox)
684 *
685 * // 4. send Image to bot itself from local file
686 * import { FileBox } from 'wechaty'
687 * const fileBox = FileBox.fromFile('/tmp/text.jpg')
688 * await bot.say(fileBox)
689 *
690 * // 5. send Link to bot itself
691 * const linkPayload = new UrlLink ({
692 * description : 'WeChat Bot SDK for Individual Account, Powered by TypeScript, Docker, and Love',
693 * thumbnailUrl: 'https://avatars0.githubusercontent.com/u/25162437?s=200&v=4',
694 * title : 'Welcome to Wechaty',
695 * url : 'https://github.com/wechaty/wechaty',
696 * })
697 * await bot.say(linkPayload)
698 *
699 * // 6. send MiniProgram to bot itself
700 * const miniPayload = new MiniProgram ({
701 * username : 'gh_xxxxxxx', //get from mp.weixin.qq.com
702 * appid : '', //optional, get from mp.weixin.qq.com
703 * title : '', //optional
704 * pagepath : '', //optional
705 * description : '', //optional
706 * thumbnailurl : '', //optional
707 * })
708 * await bot.say(miniPayload)
709 */
710 async say(something) {
711 config_1.log.verbose('Wechaty', 'say(%s)', something);
712 // huan: to make TypeScript happy
713 await this.userSelf().say(something);
714 }
715 /**
716 * @ignore
717 */
718 static version(gitHash = false) {
719 if (gitHash && version_1.GIT_COMMIT_HASH) {
720 return `#git[${version_1.GIT_COMMIT_HASH}]`;
721 }
722 return version_1.VERSION;
723 }
724 /**
725 * @ignore
726 * Return version of Wechaty
727 *
728 * @param {boolean} [forceNpm=false] - If set to true, will only return the version in package.json. </br>
729 * Otherwise will return git commit hash if .git exists.
730 * @returns {string} - the version number
731 * @example
732 * console.log(Wechaty.instance().version()) // return '#git[af39df]'
733 * console.log(Wechaty.instance().version(true)) // return '0.7.9'
734 */
735 version(forceNpm = false) {
736 return Wechaty.version(forceNpm);
737 }
738 /**
739 * @ignore
740 */
741 static async sleep(millisecond) {
742 await new Promise(resolve => {
743 setTimeout(resolve, millisecond);
744 });
745 }
746 /**
747 * @ignore
748 */
749 async sleep(millisecond) {
750 return Wechaty.sleep(millisecond);
751 }
752 /**
753 * @private
754 */
755 ding(data) {
756 config_1.log.silly('Wechaty', 'ding(%s)', data || '');
757 try {
758 this.puppet.ding(data);
759 }
760 catch (e) {
761 config_1.log.error('Wechaty', 'ding() exception: %s', e.message);
762 config_1.Raven.captureException(e);
763 throw e;
764 }
765 }
766 /**
767 * @ignore
768 */
769 memoryCheck(minMegabyte = 4) {
770 const freeMegabyte = Math.floor(os_1.default.freemem() / 1024 / 1024);
771 config_1.log.silly('Wechaty', 'memoryCheck() free: %d MB, require: %d MB', freeMegabyte, minMegabyte);
772 if (freeMegabyte < minMegabyte) {
773 const e = new Error(`memory not enough: free ${freeMegabyte} < require ${minMegabyte} MB`);
774 config_1.log.warn('Wechaty', 'memoryCheck() %s', e.message);
775 this.emit('error', e);
776 }
777 }
778 /**
779 * @ignore
780 */
781 async reset(reason) {
782 config_1.log.verbose('Wechaty', 'reset() with reason: %s, call stack: %s', reason || 'no reason',
783 // https://stackoverflow.com/a/2060330/1123955
784 new Error().stack);
785 await this.puppet.stop();
786 await this.puppet.start();
787 }
788 unref() {
789 config_1.log.verbose('Wechaty', 'unref()');
790 if (this.lifeTimer) {
791 this.lifeTimer.unref();
792 }
793 this.puppet.unref();
794 }
795}
796exports.Wechaty = Wechaty;
797Wechaty.VERSION = version_1.VERSION;
798Wechaty.log = config_1.log;
799Wechaty.globalPluginList = [];
800/**
801 * Huan(202008): we will bind the wechaty puppet with user modules (Contact, Room, etc) together inside the start() method
802 */
803function guardWechatify(userModule) {
804 if (!userModule) {
805 throw new Error('Wechaty user module (for example, wechaty.Room) can not be used before wechaty.start()!');
806 }
807 return userModule;
808}
809//# sourceMappingURL=wechaty.js.map
\No newline at end of file