提交 a24ef323 编写于 作者: Huan (李卓桓)'s avatar Huan (李卓桓)

Merge branch 'master' of github.com:wechaty/wechaty

......@@ -327,12 +327,18 @@ Wechaty support the following 6 events:
8. room-leave
9. room-topic
### this.say(content: string)
`this` is `Sayable` for all listeners.
which means there will be a `this.say()` method inside listener call, you can use it sending message to `filehelper`, just for logging / reporting / any usage for your convienience
### 1. Event: `scan`
A `scan` event will be emitted when the bot need to show you a QrCode for scaning.
```typescript
wechaty.on('scan', (url: string, code: number) => {
wechaty.on('scan', (this: Sayable, url: string, code: number) => {
console.log(`[${code}] Scan ${url} to login.` )
})
```
......@@ -350,7 +356,7 @@ wechaty.on('scan', (url: string, code: number) => {
After the bot login full successful, the event `login` will be emitted, with a [Contact](#class-contact) of current logined user.
```javascript
wechaty.on('login', user => {
wechaty.on('login', (this: Sayable, user: Contact) => {
console.log(`user ${user} login`)
})
```
......@@ -360,7 +366,7 @@ wechaty.on('login', user => {
`logout` will be emitted when bot detected it is logout, with a [Contact](#class-contact) of current logined user.
```javascript
wechaty.on('logout', user => {
wechaty.on('logout', (this: Sayable, user: Contact) => {
console.log(`user ${user} logout`)
})
```
......@@ -370,27 +376,29 @@ wechaty.on('logout', user => {
Emit when there's a new message.
```javascript
wechaty.on('message', (message: Message) => {
wechaty.on('message', (this: Sayable, message: Message) => {
console.log('message ${message} received')
})
```
### 5. Event: `error`
Emit for error
Emit when there's a error occoured.
```ts
wechaty.on('error', (err: Error) => {
console.log(err.message)
```javascript
wechaty.on('error', (this: Sayable, err: Error) => {
console.log('error ${err.message} received')
})
```
The `message` here is a [Message](#class-message).
### 6. Event: `friend`
Fired when we got new friend request, or confirm a friend ship.
```typescript
wechaty.on('friend', (contact: Contact, request: FriendRequest) => {
wechaty.on('friend', (this: Sayable, contact: Contact, request: FriendRequest) => {
if (request) { // 1. request to be friend from new contact
request.accept()
console.log('auto accepted for ' + contact + ' with message: ' + request.hello)
......@@ -403,7 +411,7 @@ wechaty.on('friend', (contact: Contact, request: FriendRequest) => {
### 7. Event: `room-join`
```typescript
wechaty.on('room-join', (room: Room, invitee: Contact, inviter: Contact) => {
wechaty.on('room-join', (this: Sayable, room: Room, invitee: Contact, inviter: Contact) => {
console.log(`Room ${room} got new member ${invitee}, invited by ${inviter}`)
})
```
......@@ -411,7 +419,7 @@ wechaty.on('room-join', (room: Room, invitee: Contact, inviter: Contact) => {
### 8. Event: `room-leave`
```typescript
wechaty.on('room-leave', (room: Room, leaver: Contact) => {
wechaty.on('room-leave', (this: Sayable, room: Room, leaver: Contact) => {
console.log(`Room ${room} lost member ${leaver}`)
})
```
......@@ -419,7 +427,7 @@ wechaty.on('room-leave', (room: Room, leaver: Contact) => {
### 9. Event: `room-topic`
```typescript
wechaty.on('room-topic', (room: Room, topic: string, oldTopic: string, changer: Contact) => {
wechaty.on('room-topic', (this: Sayable, room: Room, topic: string, oldTopic: string, changer: Contact) => {
console.log(`Room ${room} topic changed from ${oldTopic} to ${topic} by {changer}`)
})
```
......@@ -482,6 +490,8 @@ wechaty.reply(message, 'roger')
All wechat messages will be encaped as a Message.
`Message` is `Sayable`
### Message.from(contact?: Contact|string): Contact
get the sender from message, or set it.
......@@ -540,6 +550,8 @@ message.set('content', 'Hello, World!')
### Contact.name(): string
`Contact` is `Sayable`
### Contact.ready(): Contact
A Contact may be not fully initialized yet. Call `ready()` to confirm we get all the data needed.
......@@ -552,6 +564,10 @@ contact.ready()
})
```
### Contact.say(content: string)
say `content` to Contact
### @deprecated Contact.get(prop): String|Number
Get prop from a contact.
......@@ -572,7 +588,8 @@ contact.get('name')
## Class Room
Doc is cheap, read code: [Example/Room-Bot](https://github.com/wechaty/wechaty/blob/master/example/room-bot.js)
Doc is cheap, show you code: [Example/Room-Bot](https://github.com/wechaty/wechaty/blob/master/example/room-bot.js)
`Room` is `Sayable`
### Room.say(content: string, replyTo: Contact|Contact[]): Promise<void>
......@@ -597,7 +614,13 @@ room.ready()
force reload data for Room
### Event: `join`
### Room Events
`this` is `Sayable` for all listeners.
which means there will be a `this.say()` method inside listener call, you can use it sending message to `filehelper`, just for logging / reporting / any usage for your convienience
#### Event: `join`
```javascript
Room.on('join', (invitee, inviter) => void)
......@@ -611,13 +634,13 @@ room.on('join', (invitee, inviter) => {
})
```
### Event: `leave`
#### Event: `leave`
```typescript
Room.on('leave', (leaver) => void)
```
### Event: `topic`
#### Event: `topic`
```typescript
Room.on('topic', (topic, oldTopic, changer) => void)
......@@ -729,18 +752,18 @@ npm test
# Version History
## v0.5.0 master (2016/10) The First Typescript Version
1. #40 Converted to Typescript (2016/10/11)
1. added `say()` method to Contact/Room instance, and to `this` inside wechaty event listeners
1. [#40](https://github.com/wechaty/wechaty/issues/40) Converted to Typescript (2016/10/11)
1. [#41](https://github.com/wechaty/wechaty/issues/41) added `say()` method to Contact/Room instance, and to `this` inside wechaty event listeners, to make them `Sayable`
1. BREAKING CHANGE: global event `scan` arguments changed from 1 to 2: now is (url: string, code: number) instead of {url, code} before.
## [v0.4.0](https://github.com/wechaty/wechaty/releases/tag/v0.4.0) (2016/10/9) The Latest Javascript Version
1. #32 Extend Room Class with:
1. [#32](https://github.com/wechaty/wechaty/issues/32) Extend Room Class with:
1. Global events: `room-join`, `room-leave`, `room-topic`
1. Room events: `join`, `leave`, `topic`
1. Create a new Room: `Room.create()`
1. Add/Del/Topic for Room
1. Other methods like nick/member/has/etc...
1. #33 New Class `FriendRequest` with:
1. [#33](https://github.com/wechaty/wechaty/issues/33) New Class `FriendRequest` with:
1. `Wechaty.on('friend', (contact, request) => {})` with Wechaty new Event `friend`
1. `accept()` to accept a friend request
1. `send()` to send new friend request
......
......@@ -151,4 +151,8 @@ export type RecommendInfo = {
VerifyFlag: number
}
export interface Sayable {
say(content: string, replyTo?: any): Promise<any>
}
export default Config
/**
*
* Wechaty: Wechat for ChatBots.
*
* Class WechatyEvent
*
* Licenst: ISC
* https://github.com/wechaty/wechaty
*
* Events Function Wrapper
*
*/
import {
Config
, WechatyEventName
} from './config'
import Contact from './contact'
import Message from './message'
import Room from './room'
import Wechaty from './wechaty'
import log from './brolog-env'
type WechatyEventScope = {
say: (content: string, replyTo?: Contact|Contact[]) => void
}
const EVENT_CONFIG = {
error: wrapFilehelper
, friend: wrapFilehelper
, heartbeat: wrapFilehelper
, login: wrapFilehelper
, logout: null // NULL
, message: wrapMessage
, 'room-join': wrapRoom
, 'room-leave': wrapRoom
, 'room-topic': wrapRoom
, scan: null // NULL
}
class EventScope {
public static list() {
return Object.keys(EVENT_CONFIG)
}
public static wrap(this: Wechaty|Room, event: WechatyEventName, listener: Function): Function {
log.verbose('WechatyEvent', 'wrap(%s, %s)', event, typeof listener)
if (typeof listener !== 'function') {
throw new Error('`listener` should be function')
}
if (!(event in EVENT_CONFIG)) {
throw new Error('event not support: ' + event)
}
const wrapper = EVENT_CONFIG[event]
/**
* We assign a empty object to each event listener,
* to carry the indenpendent scope
*/
if (wrapper) {
return wrapper(listener)
} else {
return listener
}
}
}
function isContact(contact) {
if (contact.map && contact[0] instanceof Contact) {
return true
} else if (contact instanceof Contact) {
return true
}
return false
}
function isRoom(room) {
/**
* here not use `instanceof Room` is because circular dependence problem...
*/
if (!room || !room.constructor
|| room.constructor.name !== 'Room') {
return false
}
return true
}
// function wrapContact(listener) {
// log.verbose('WechatyEvent', 'wrapContact()')
// return (...argList) => {
// log.silly('WechatyEvent', 'wrapContact() listener')
// if (!isContact(argList[0])) {
// throw new Error('contact not found in argList')
// }
// const contact = argList[0]
// const eventScope = <WechatyEventScope>{}
// eventScope.say = (content) => {
// const msg = new Message()
// msg.to(contact)
// msg.content(content)
// return Config.puppetInstance()
// .send(msg)
// }
// return listener.apply(eventScope, argList)
// }
// }
function wrapRoom(listener) {
log.verbose('WechatyEvent', 'wrapRoom()')
return (room: Room, ...argList) => {
log.silly('WechatyEvent', 'wrapRoom(%s, %s, %s, %s) listener', room.topic(), argList[0], argList[1], argList[2])
let contact
for (let arg of argList) {
if (!contact && isContact(arg)) {
contact = arg
}
}
if (!room || !isRoom(room) || !contact) {
throw new Error('room or contact not found')
}
const eventScope = <WechatyEventScope>{}
eventScope.say = (content: string, replyTo?: Contact) => {
if (!replyTo) {
replyTo = contact
} else if (!isContact(replyTo)) {
throw new Error('replyTo is not Contact instance(s)')
}
return room.say(content, replyTo)
}
return listener.apply(eventScope, [room, ...argList])
}
}
function wrapMessage(listener) {
log.verbose('WechatyEvent', 'wrapMessage()')
return (...argList) => {
log.silly('WechatyEvent', 'wrapMessage() listener')
// console.log('############### wrapped on message listener')
// console.log(typeof Message)
// console.log(argList)
if (!(argList[0] instanceof Message)) {
throw new Error('Message instance not found')
}
const msg = argList[0]
const sender = msg.from()
// const receiver = msg.to()
const room = msg.room()
const eventScope = <WechatyEventScope>{}
eventScope.say = (content, replyTo) => {
log.silly('WechatyEvent', 'wrapMessage() say("%s", "%s")', content, replyTo)
if (room) {
return room.say(content, replyTo)
}
const m = new Message()
m.to(sender)
m.content(content)
return Config.puppetInstance()
.send(m)
}
return listener.apply(eventScope, argList)
}
}
function wrapFilehelper(listener) {
log.verbose('WechatyEvent', 'wrapFilehelper()')
return (...argList) => {
log.silly('WechatyEvent', 'wrapFilehelper() listener')
const eventScope = <WechatyEventScope>{}
eventScope.say = (content) => {
log.silly('WechatyEvent', 'wrapFilehelper() say(%s)', content)
const msg = new Message()
msg.to('filehelper')
msg.content(content)
return Config.puppetInstance()
.send(msg)
}
return listener.apply(eventScope, argList)
}
}
export default EventScope
......@@ -37,13 +37,13 @@ import Event from './event'
import Server from './server'
import Watchdog from './watchdog'
type PuppetWebSetting = {
export type PuppetWebSetting = {
head?: string
profile?: string
}
const DEFAULT_PUPPET_PORT = 18788 // // W(87) X(88), ascii char code ;-]
class PuppetWeb extends Puppet {
export class PuppetWeb extends Puppet {
public browser: Browser
public bridge: Bridge
......@@ -318,9 +318,21 @@ class PuppetWeb extends Puppet {
})
}
/**
* Bot say...
* send to `filehelper` for notice / log
*/
public say(content: string) {
const m = new Message()
m.to('filehelper')
m.content(content)
return this.send(m)
}
// @deprecated
public reply(message: Message, replyContent: string) {
log.warn('PuppetWeb', 'reply() @deprecated, please use Message.say()')
if (this.self(message)) {
return Promise.reject(new Error('will not to reply message of myself'))
}
......
......@@ -71,6 +71,9 @@ abstract class Puppet extends EventEmitter {
// }
public abstract send(message: Message): Promise<any>
public abstract say(content: string)
// @deprecated
public abstract reply(message: Message, reply): Promise<any>
public abstract reset(reason?: string)
......
......@@ -14,7 +14,7 @@ import Config from './config'
import Contact from './contact'
import Message from './message'
import UtilLib from './util-lib'
import EventScope from './event-scope'
// import EventScope from './event-scope'
import log from './brolog-env'
......@@ -116,26 +116,16 @@ export class Room extends EventEmitter {
public on(event: string, listener: Function): this {
log.verbose('Room', 'on(%s, %s)', event, typeof listener)
/**
* wrap a room event listener to a global event listener
*/
const listenerWithRoomArg = (room: Room, ...argList) => {
return listener.apply(this, argList)
const thisWithSay = {
say: (content: string) => {
return Config.puppetInstance()
.say(content)
}
}
super.on(event, function() {
return listener.apply(thisWithSay, arguments)
})
/**
* every room event must can be mapped to a global event.
* such as: `join` to `room-join`
*/
const globalEventName = 'room-' + event
const listenerWithScope = EventScope.wrap.call(this, globalEventName, listenerWithRoomArg)
/**
* bind(listenerWithScope, this):
* the `this` is for simulate the global room-* event,
* provide the first argument `room`
*/
super.on(event, listenerWithScope.bind(listenerWithScope, this))
return this
}
......
......@@ -9,13 +9,14 @@
*
*/
import { EventEmitter } from 'events'
import * as path from 'path'
import * as fs from 'fs'
import * as path from 'path'
import {
Config
, HeadType
, PuppetType
, Sayable
, WechatyEventName
} from './config'
......@@ -26,7 +27,7 @@ import Puppet from './puppet'
import PuppetWeb from './puppet-web/'
import Room from './room'
import UtilLib from './util-lib'
import EventScope from './event-scope'
// import EventScope from './event-scope'
import log from './brolog-env'
......@@ -155,23 +156,30 @@ export class Wechaty extends EventEmitter {
return this
}
public on(event: 'error' , listener: (error: Error) => void): this
public on(event: 'friend' , listener: (friend: Contact, request: FriendRequest) => void): this
public on(event: 'heartbeat' , listener: (data: any) => void): this
public on(event: 'logout' , listener: (user: Contact) => void): this
public on(event: 'login' , listener: (user: Contact) => void): this
public on(event: 'message' , listener: (message: Message, n: number) => void): this
public on(event: 'room-join' , listener: (room: Room, invitee: Contact, inviter: Contact) => void): this
public on(event: 'room-join' , listener: (room: Room, inviteeList: Contact, inviter: Contact) => void): this
public on(event: 'room-leave' , listener: (room: Room, leaver: Contact) => void): this
public on(event: 'room-topic' , listener: (room: Room, topic: string, oldTopic: string, changer: Contact) => void): this
public on(event: 'scan' , listener: (url: string, code: number) => void): this
public on(event: 'error' , listener: (this: Sayable, error: Error) => void): this
public on(event: 'friend' , listener: (this: Sayable, friend: Contact, request: FriendRequest) => void): this
public on(event: 'heartbeat' , listener: (this: Sayable, data: any) => void): this
public on(event: 'logout' , listener: (this: Sayable, user: Contact) => void): this
public on(event: 'login' , listener: (this: Sayable, user: Contact) => void): this
public on(event: 'message' , listener: (this: Sayable, message: Message, n: number) => void): this
public on(event: 'room-join' , listener: (this: Sayable, room: Room, invitee: Contact, inviter: Contact) => void): this
public on(event: 'room-join' , listener: (this: Sayable, room: Room, inviteeList: Contact, inviter: Contact) => void): this
public on(event: 'room-leave' , listener: (this: Sayable, room: Room, leaver: Contact) => void): this
public on(event: 'room-topic' , listener: (this: Sayable, room: Room, topic: string, oldTopic: string, changer: Contact) => void): this
public on(event: 'scan' , listener: (this: Sayable, url: string, code: number) => void): this
public on(event: WechatyEventName, listener: Function): this {
log.verbose('Wechaty', 'on(%s, %s)', event, typeof listener)
const listenerWithScope = EventScope.wrap.call(this, event, listener)
super.on(event, listenerWithScope)
const thisWithSay: Sayable = {
say: (content: string) => {
return Config.puppetInstance()
.say(content)
}
}
super.on(event, function() {
return listener.apply(thisWithSay, arguments)
})
return this
}
......@@ -185,11 +193,23 @@ export class Wechaty extends EventEmitter {
, profile: this.setting.profile
})
break
default:
throw new Error('Puppet unsupport(yet): ' + this.setting.type)
}
EventScope.list().map(e => {
; // must have a semicolon here to seperate the last line with `[]`
[ 'error'
, 'friend'
, 'heartbeat'
, 'login'
, 'logout'
, 'message'
, 'room-join'
, 'room-leave'
, 'room-topic'
, 'scan'
].map(e => {
// https://strongloop.com/strongblog/an-introduction-to-javascript-es6-arrow-functions/
// We’ve lost () around the argument list when there’s just one argument (rest arguments are an exception, eg (...args) => ...)
puppet.on(e, (...args) => {
......@@ -206,7 +226,7 @@ export class Wechaty extends EventEmitter {
*/
// set puppet before init, because we need this.puppet if we quit() before init() finish
this.puppet = puppet
this.puppet = puppet
// set puppet instance to Wechaty Static variable, for using by Contact/Room/Message/FriendRequest etc.
Config.puppetInstance(puppet)
......@@ -254,6 +274,11 @@ export class Wechaty extends EventEmitter {
})
}
public say(content: string) {
return this.puppet.say(content)
}
/// @deprecated
public reply(message: Message, reply: string) {
log.warn('Wechaty', 'reply() @deprecated, please use Message.say()')
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册