#!/usr/bin/env node /** * Wechaty - https://github.com/chatie/wechaty * * @copyright 2016-2018 Huan LI * * 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. * */ /** * * Known ISSUES: * - BUG1: can't find member by this NickName: * ' leaver: 艾静JOY * - BUG2: leave event not right: sometimes can not found member (any more, because they left) * create a room need at least three people * when we create a room, the following one is the 3rd people. * * put name of one of your friend here, or room create function will not work. * * ::::::::: ___CHANGE ME___ ::::::::: * vvvvvvvvv * vvvvvvvvv * vvvvvvvvv */ const HELPER_CONTACT_NAME = '李卓桓' /** * ^^^^^^^^^ * ^^^^^^^^^ * ^^^^^^^^^ * ::::::::: ___CHANGE ME___ ::::::::: * */ /* tslint:disable:variable-name */ import qrTerm from 'qrcode-terminal' import { config, Contact, Room, Wechaty, log, } from 'wechaty' const welcome = ` =============== Powered by Wechaty =============== -------- https://github.com/Chatie/wechaty -------- Hello, I'm a Wechaty Botie with the following super powers: 1. Find a room 2. Add people to room 3. Del people from room 4. Change room topic 5. Monitor room events 6. etc... If you send a message of magic word 'ding', you will get a invitation to join my own room! __________________________________________________ Hope you like it, and you are very welcome to upgrade me for more super powers! Please wait... I'm trying to login in... ` console.log(welcome) const bot = Wechaty.instance({ profile: config.default.DEFAULT_PROFILE }) bot .on('scan', (qrcode, status) => { qrTerm.generate(qrcode, { small: true }) console.log(`${qrcode}\n[${status}] Scan QR Code in above url to login: `) }) .on('logout' , user => log.info('Bot', `"${user.name()}" logouted`)) .on('error' , e => log.info('Bot', 'error: %s', e)) /** * Global Event: login * * do initialization inside this event. * (better to set a timeout, for browser need time to download other data) */ .on('login', async function(user) { let msg = `${user.name()} logined` log.info('Bot', msg) await this.say(msg) msg = `setting to manageDingRoom() after 3 seconds ... ` log.info('Bot', msg) await this.say(msg) setTimeout(manageDingRoom.bind(this), 3000) }) /** * Global Event: room-join */ .on('room-join', async function(room, inviteeList, inviter) { log.info( 'Bot', 'EVENT: room-join - Room "%s" got new member "%s", invited by "%s"', await room.topic(), inviteeList.map(c => c.name()).join(','), inviter.name(), ) console.log('bot room-join room id:', room.id) const topic = await room.topic() await room.say(`welcome to "${topic}"!`, inviteeList[0]) }) /** * Global Event: room-leave */ .on('room-leave', async function(room, leaverList) { log.info('Bot', 'EVENT: room-leave - Room "%s" lost member "%s"', await room.topic(), leaverList.map(c => c.name()).join(','), ) const topic = await room.topic() const name = leaverList[0] ? leaverList[0].name() : 'no contact!' await room.say(`kick off "${name}" from "${topic}"!` ) }) /** * Global Event: room-topic */ .on('room-topic', async function(room, topic, oldTopic, changer) { try { log.info('Bot', 'EVENT: room-topic - Room "%s" change topic from "%s" to "%s" by member "%s"', room, oldTopic, topic, changer, ) await room.say(`room-topic - change topic from "${oldTopic}" to "${topic}" by member "${changer.name()}"` ) } catch (e) { log.error('Bot', 'room-topic event exception: %s', e.stack) } }) /** * Global Event: message */ .on('message', async function(msg) { if (msg.age() > 3 * 60) { log.info('Bot', 'on(message) skip age("%d") > 3 * 60 seconds: "%s"', msg.age(), msg) return } const room = msg.room() const from = msg.from() const text = msg.text() if (!from) { return } console.log((room ? '[' + await room.topic() + ']' : '') + '<' + from.name() + '>' + ':' + msg, ) if (msg.self()) { return // skip self } /** * `dong` will be the magic(toggle) word: * bot will quit current room by herself. */ if (/^dong$/i.test(text)) { if (room) { await room.say('You said dong in the room, I will quit by myself!', from) await room.quit() } else { await from.say('Nothing to do. If you say "dong" in a room, I will quit from the room.') } return } /** * `ding` will be the magic(toggle) word: * 1. say ding first time, will got a room invitation * 2. say ding in room, will be removed out */ if (/^ding$/i.test(text)) { /** * in-room message */ if (room) { if (/^ding/i.test(await room.topic())) { /** * move contact out of room */ await getOutRoom(from, room) } /** * peer to peer message */ } else { /** * find room name start with "ding" */ try { const dingRoom = await this.Room.find({ topic: /^ding/i }) if (dingRoom) { /** * room found */ log.info('Bot', 'onMessage: got dingRoom: "%s"', await dingRoom.topic()) if (await dingRoom.has(from)) { /** * speaker is already in room */ const topic = await dingRoom.topic() log.info('Bot', 'onMessage: sender has already in dingRoom') await dingRoom.say(`I found you have joined in room "${topic}"!`, from) await from.say(`no need to ding again, because you are already in room: "${topic}"`) // sendMessage({ // content: 'no need to ding again, because you are already in ding room' // , to: sender // }) } else { /** * put speaker into room */ log.info('Bot', 'onMessage: add sender("%s") to dingRoom("%s")', from.name(), dingRoom.topic()) await from.say('ok, I will put you in ding room!') await putInRoom(from, dingRoom) } } else { /** * room not found */ log.info('Bot', 'onMessage: dingRoom not found, try to create one') /** * create the ding room */ const newRoom = await createDingRoom(from) console.log('createDingRoom id:', newRoom.id) /** * listen events from ding room */ await manageDingRoom() } } catch (e) { log.error(e) } } } }) .start() .catch(e => console.error(e)) async function manageDingRoom() { log.info('Bot', 'manageDingRoom()') /** * Find Room */ try { const room = await bot.Room.find({ topic: /^ding/i }) if (!room) { log.warn('Bot', 'there is no room topic ding(yet)') return } log.info('Bot', 'start monitor "ding" room join/leave/topic event') /** * Event: Join */ room.on('join', function(inviteeList, inviter) { log.verbose('Bot', 'Room EVENT: join - "%s", "%s"', inviteeList.map(c => c.name()).join(', '), inviter.name(), ) console.log('room.on(join) id:', this.id) checkRoomJoin.call(this, room, inviteeList, inviter) }) /** * Event: Leave */ room.on('leave', (leaverList, remover) => { log.info('Bot', 'Room EVENT: leave - "%s" leave(remover "%s"), byebye', leaverList.join(','), remover || 'unknown') }) /** * Event: Topic Change */ room.on('topic', (topic, oldTopic, changer) => { log.info('Bot', 'Room EVENT: topic - changed from "%s" to "%s" by member "%s"', oldTopic, topic, changer.name(), ) }) } catch (e) { log.warn('Bot', 'Room.find rejected: "%s"', e.stack) } } async function checkRoomJoin(room, inviteeList, inviter) { log.info('Bot', 'checkRoomJoin("%s", "%s", "%s")', await room.topic(), inviteeList.map(c => c.name()).join(','), inviter.name(), ) try { // let to, content const userSelf = bot.userSelf() if (inviter.id !== userSelf.id) { await room.say('RULE1: Invitation is limited to me, the owner only. Please do not invit people without notify me.', inviter, ) await room.say('Please contact me: by send "ding" to me, I will re-send you a invitation. Now I will remove you out, sorry.', inviteeList, ) await room.topic('ding - warn ' + inviter.name()) setTimeout( _ => inviteeList.forEach(c => room.del(c)), 10 * 1000, ) } else { await room.say('Welcome to my room! :)') let welcomeTopic welcomeTopic = inviteeList.map(c => c.name()).join(', ') await room.topic('ding - welcome ' + welcomeTopic) } } catch (e) { log.error('Bot', 'checkRoomJoin() exception: %s', e.stack) } } async function putInRoom(contact, room) { log.info('Bot', 'putInRoom("%s", "%s")', contact.name(), await room.topic()) try { await room.add(contact) setTimeout( _ => room.say('Welcome ', contact), 10 * 1000, ) } catch (e) { log.error('Bot', 'putInRoom() exception: ' + e.stack) } } async function getOutRoom(contact, room) { log.info('Bot', 'getOutRoom("%s", "%s")', contact, room) try { await room.say('You said "ding" in my room, I will remove you out.') await room.del(contact) } catch (e) { log.error('Bot', 'getOutRoom() exception: ' + e.stack) } } function getHelperContact() { log.info('Bot', 'getHelperContact()') // create a new room at least need 3 contacts return bot.Contact.find({ name: HELPER_CONTACT_NAME }) } async function createDingRoom(contact) { log.info('Bot', 'createDingRoom("%s")', contact) try { const helperContact = await getHelperContact() if (!helperContact) { log.warn('Bot', 'getHelperContact() found nobody') await contact.say(`You don't have a friend called "${HELPER_CONTACT_NAME}", because create a new room at least need 3 contacts, please set [HELPER_CONTACT_NAME] in the code first!`) return } log.info('Bot', 'getHelperContact() ok. got: "%s"', helperContact.name()) const contactList = [contact, helperContact] log.verbose('Bot', 'contactList: "%s"', contactList.join(',')) await contact.say(`There isn't ding room. I'm trying to create a room with "${helperContact.name()}" and you`) const room = await bot.Room.create(contactList, 'ding') log.info('Bot', 'createDingRoom() new ding room created: "%s"', room) await room.topic('ding - created') await room.say('ding - created') return room } catch (e) { log.error('Bot', 'getHelperContact() exception:', e.stack) throw e } }