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

Add support to save EMOTICNO/VOICE/VIDEO/MICROVIDEO for MediaMessage #4

......@@ -54,3 +54,6 @@ t.*
/dist/
yarn.lock
.yarn/
.yarn-cache/
.v8flags.*
test/fixture/docker/package.json
......@@ -113,16 +113,29 @@ function wechaty::runBot() {
echo "Install dependencies modules ..."
yarn < /dev/null # yarn will close stdin??? cause `read` command fail after yarn
}
npm --progress=false install @types/node > /dev/null
# echo -n "Linking Wechaty module to bot ... "
# npm link wechaty < /dev/null > /dev/null 2>&1
# echo "linked. "
echo "Executing ts-node $*"
# npm --progress=false install @types/node > /dev/null
local -i ret=0
ts-node "$@" &
case "$botFile" in
*.js)
echo "Executing node $*"
node "$@" &
;;
*.ts)
# yarn add @types/node
echo "Executing ts-node $*"
ts-node "$@" &
;;
*)
echo "ERROR: wechaty::runBot() neith .js nor .ts"
exit -1 &
esac
wait "$!" || ret=$? # fix `can only `return' from a function or sourced script` error
case "$ret" in
......@@ -139,13 +152,39 @@ function wechaty::runBot() {
return "$ret"
}
function wechaty::io-client() {
figlet " Wechaty.io "
figlet " Authing By:"
echo
echo "WECHATY_TOKEN=$WECHATY_TOKEN "
echo
npm run io-client
}
function wechaty::help() {
echo <<HELP
Usage: wechaty <mybot.js | mybot.ts | command>
figlet " Docker Usage: "
cat <<HELP
Usage: wechaty [ mybot.js | mybot.ts | COMMAND ]
Run a JavaScript/TypeScript bot, or a command.
Bot File:
mybot.js: a JavaScript program for your bot. will run by Node.js v7
mybot.ts: a TypeScript program for your bot. will run by ts-node/TypeScript v2
Commands:
demo Run Wechaty DEMO
doctor Print Diagnose Report
test Run Unit Test
Learn more at:
https://github.com/wechaty/wechaty/wiki/Docker
1. mybot.js: a JavaScript program for your bot. will run by node v6
2. mybot.ts: a TypeScript program for your bot. will run by ts-node
3. command: demo, test, doctor
HELP
}
......@@ -165,7 +204,12 @@ function main() {
local -i ret=0
case "$1" in
local defaultArg=help
if [ -n "$WECHATY_TOKEN" ]; then
defaultArg=io-client
fi
case "${1:-${defaultArg}}" in
#
# 1. Get a shell
#
......@@ -188,19 +232,20 @@ function main() {
npm "$@" || ret=$?
;;
'')
if [ -n "$WECHATY_TOKEN" ]; then
npm start
else
wechaty::help
fi
help)
wechaty::help
;;
io-client)
wechaty::io-client
;;
#
# 4. Default to execute npm run ...
#
*)
npm "$@" || ret=$?
[ "$1" = "run" ] && shift
npm run "$@" || ret=$?
;;
esac
......
#!/bin/sh
#!/bin/bash
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments. The hook should
......@@ -6,6 +6,7 @@
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit".
set -e
npm run lint
npm --no-git-tag-version version patch || true
[ -z "$CYGWIN" ] && npm version patch
# npm --no-git-tag-version version patch || true
......@@ -2,9 +2,11 @@ machine:
services:
- docker
# dependencies:
# pre:
# - sudo apt-get update; sudo apt-get install shellcheck
dependencies:
pre:
- sudo add-apt-repository ppa:duggan/bats --yes
- sudo apt-get update -qq
- sudo apt-get install -qq bats
test:
override:
- docker info
......
......@@ -37,7 +37,7 @@ import {
const APIAI_API_KEY = '7217d7bce18c4bcfbe04ba7bdfaf9c08'
const brainApiAi = ApiAi(APIAI_API_KEY)
const bot = Wechaty.instance({ profile: 'example-bot.wechaty.json' })
const bot = Wechaty.instance({ profile: Config.DEFAULT_PROFILE })
console.log(`
Welcome to api.AI Wechaty Bot.
......
......@@ -10,11 +10,15 @@
/* tslint:disable:variable-name */
const QrcodeTerminal = require('qrcode-terminal')
import * as util from 'util'
import {
Message
Config
// , Message
, MessageType
, Wechaty
} from '../'
const bot = Wechaty.instance({ profile: 'example-bot.wechaty.json' })
const bot = Wechaty.instance({ profile: Config.DEFAULT_PROFILE })
bot
.on('scan', (url, code) => {
......@@ -27,9 +31,16 @@ bot
.on('message', m => {
console.log(`RECV: ${m}`)
if (m.type() === Message.TYPE['IMAGE']) {
console.log(util.inspect(m))
if ( m.type() === MessageType.IMAGE
|| m.type() === MessageType.EMOTICON
|| m.type() === MessageType.VIDEO
|| m.type() === MessageType.VOICE
|| m.type() === MessageType.MICROVIDEO
) {
console.log('IMAGE url: ' + m.get('url'))
const filename = m.id + '.jpg'
const filename = m.id + m.ext()
console.log('IMAGE local filename: ' + filename)
const fileStream = require('fs').createWriteStream(filename)
......
......@@ -6,7 +6,10 @@ import {
import { Contact } from './src/contact'
import { FriendRequest } from './src/friend-request'
import { IoClient } from './src/io-client'
import { Message } from './src/message'
import {
Message
, MessageType
} from './src/message'
import { Puppet } from './src/puppet'
import { PuppetWeb } from './src/puppet-web/'
import { Room } from './src/room'
......@@ -21,6 +24,7 @@ export {
, FriendRequest
, IoClient
, Message
, MessageType
, Puppet
, PuppetWeb
, Room
......
{
"name": "wechaty",
"version": "0.5.10",
"description": "Wechat for Bot (Personal Account)",
"version": "0.5.16",
"description": "Wechat for Bot(Personal Account)",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"wechaty": {
......@@ -22,8 +22,8 @@
"doctor": "ts-node bin/doctor",
"clean": "shx rm -fr dist/*",
"lint": "npm run eslint && npm run tslint",
"eslint": "eslint \"{bin,example,src,test}/**/*.js\"",
"tslint": "tslint \"{bin,example,src,test}/**/*.ts\" && tsc --noEmit",
"eslint": "eslint \"{bin,example,src,test}/**/*.js\" --ignore-pattern=\"test/fixture/**\"",
"tslint": "tslint \"{bin,example,src,test}/**/*.ts\" --exclude=\"test/fixture/**\" && tsc --noEmit",
"shlint": "bash -n bin/*.sh",
"pretest": "npm run clean && npm run lint && npm run build",
"sloc": "sloc bin example src test index.ts --details --format cli-table --keys total,source,comment && sloc bin example src test index.ts",
......@@ -35,16 +35,17 @@
"testdev": "cross-env LC_ALL=C WECHATY_LOG=silly ava --ext ts --serial --verbose --fail-fast --timeout=3m",
"testdist": "cross-env WECHATY_LOG=SILLY WECHATY_HEAD=chrome ava --ext ts --verbose --fail-fast --timeout=2m",
"ava": "cross-env LC_ALL=C WECHATY_LOG=verbose node_modules/.bin/ava --ext js",
"start": "ts-node bin/client",
"io-client": "ts-node bin/io-client",
"dev": "ts-node dev.ts",
"demo": "ts-node example/ding-dong-bot.ts"
"demo": "ts-node example/ding-dong-bot.ts",
"start": "npm run demo"
},
"repository": {
"type": "git",
"url": "git+https://github.com/wechaty/wechaty.git"
},
"bin": {
"wechaty-client": "dist/bin/client.js",
"wechaty-io-client": "dist/bin/io-client.js",
"wechaty-doctor": "dist/bin/doctor.js",
"wechaty-version": "dist/bin/version.js"
},
......@@ -86,7 +87,7 @@
}
},
"engines": {
"node": ">= 7.0.0"
"node": ">= 6.9.0"
},
"dependencies": {
"arrify": "^1.0.1",
......
......@@ -20,21 +20,9 @@ case "$1" in
;;
test)
#
# 1. test JavaScript(nodejs): if could run `js-bot.js``
#
echo docker run -ti "$optRm" -v /dev/shm:/dev/shm -v "$(pwd)":/bot "$imageName" test/docker-bot/js-bot.js
docker run -ti "$optRm" -v /dev/shm:/dev/shm -v "$(pwd)":/bot "$imageName" test/docker-bot/js-bot.js
#
# 2. test TypeScript(ts-node): if could run `ts-bot.ts``
#
echo docker run -ti "$optRm" -v /dev/shm:/dev/shm -v "$(pwd)":/bot "$imageName" test/docker-bot/ts-bot.ts
docker run -ti "$optRm" -v /dev/shm:/dev/shm -v "$(pwd)":/bot "$imageName" test/docker-bot/ts-bot.ts
#
# 3. run npm test
#
echo "bats test/"
IMAGE_NAME="$imageName" bats test/
echo docker run -ti "$optRm" -v /dev/shm:/dev/shm "$imageName" test
exec docker run -ti "$optRm" -v /dev/shm:/dev/shm "$imageName" test
ret=$?
......
......@@ -175,12 +175,18 @@ export type ScanInfo = {
code: number
}
/**
* from Message
*/
export type RecommendInfo = {
UserName: string
NickName: string
Content: string // request message
Ticket: string // a pass token
NickName: string // display_name
Content: string // request message
HeadImgUrl: string // message.RecommendInfo.HeadImgUrl
Ticket: string // a pass token
VerifyFlag: number
}
export interface Sayable {
......
......@@ -79,7 +79,7 @@ export class Contact implements Sayable {
}
return !rawObj ? null : {
id: rawObj.UserName
id: rawObj.UserName // MMActualSender??? MMPeerUserName??? `getUserContact(message.MMActualSender,message.MMPeerUserName).HeadImgUrl`
, uin: rawObj.Uin // stable id: 4763975 || getCookie("wxuin")
, weixin: rawObj.Alias // Wechat ID
, name: rawObj.NickName
......
......@@ -23,41 +23,69 @@ export class MediaMessage extends Message {
this.bridge = (Config.puppetInstance() as PuppetWeb)
.bridge
}
public async ready(): Promise<this> {
public async ready(): Promise<void> {
log.silly('MediaMessage', 'ready()')
const parentReady = super.ready.bind(this)
// return co.call(this, function* () {
try {
await parentReady()
const url = await this.getMsgImg(this.id)
await super.ready()
let url: string
switch (this.type()) {
case Message.TYPE['EMOTICON']:
url = await this.bridge.getMsgEmoticon(this.id)
break
case Message.TYPE['IMAGE']:
url = await this.bridge.getMsgImg(this.id)
break
case Message.TYPE['VIDEO']:
case Message.TYPE['MICROVIDEO']:
url = await this.bridge.getMsgVideo(this.id)
break
case Message.TYPE['VOICE']:
url = await this.bridge.getMsgVoice(this.id)
break
default:
throw new Error('not support message type for MediaMessage')
}
this.obj.url = url
return this // IMPORTANT!
// return this // IMPORTANT!
} catch (e) {
log.warn('MediaMessage', 'ready() exception: %s', e.message)
throw e
}
// return co.call(this, function* () {
// yield parentReady()
// const url = yield this.getMsgImg(this.id)
// this.obj.url = url
// return this // IMPORTANT!
// })
// .catch(e => {
// log.warn('MediaMessage', 'ready() exception: %s', e.message)
// throw e
// })
}
private getMsgImg(id: string): Promise<string> {
return this.bridge.getMsgImg(id)
.catch(e => {
log.warn('MediaMessage', 'getMsgImg(%d) exception: %s', id, e.message)
throw e
})
public ext(): string {
switch (this.type()) {
case Message.TYPE['EMOTICON']:
return '.gif'
case Message.TYPE['IMAGE']:
return '.jpg'
case Message.TYPE['VIDEO']:
case Message.TYPE['MICROVIDEO']:
return '.mp4'
case Message.TYPE['VOICE']:
return '.mp3'
default:
throw new Error('not support type: ' + this.type())
}
}
// private getMsgImg(id: string): Promise<string> {
// return this.bridge.getMsgImg(id)
// .catch(e => {
// log.warn('MediaMessage', 'getMsgImg(%d) exception: %s', id, e.message)
// throw e
// })
// }
public readyStream(): Promise<NodeJS.ReadableStream> {
return this.ready()
.then(() => {
......
......@@ -19,21 +19,95 @@ import { UtilLib } from './util-lib'
export type MessageRawObj = {
MsgId: string
MsgType: number
MMActualSender: string
MMActualSender: string // getUserContact(message.MMActualSender,message.MMPeerUserName).isContact()
MMPeerUserName: string // message.MsgType == CONF.MSGTYPE_TEXT && message.MMPeerUserName == 'newsapp'
ToUserName: string
MMActualContent: string // Content has @id prefix added by wx
Status: string
MMDigest: string
MMDisplayTime: number // Javascript timestamp of milliseconds
/**
* MsgType == MSGTYPE_APP && message.AppMsgType == CONF.APPMSGTYPE_URL
* class="cover" mm-src="{{getMsgImg(message.MsgId,'slave')}}"
*/
Url: string
MMAppMsgDesc: string // class="desc" ng-bind="message.MMAppMsgDesc"
/**
* MsgType == MSGTYPE_APP && message.AppMsgType == CONF.APPMSGTYPE_ATTACH
*/
MMAppMsgFileExt: string // doc, docx ...
FileName: string
MMAppMsgFileSize: number
MMAppMsgDownloadUrl: string // <a download ng-if="message.MMFileStatus == CONF.MM_SEND_FILE_STATUS_SUCCESS && (massage.MMStatus == CONF.MSG_SEND_STATUS_SUCC || massage.MMStatus === undefined) " href="{{message.MMAppMsgDownloadUrl}}">下载</a>
MMUploadProgress: number // < 100
/**
* 模板消息
* MSGTYPE_APP && message.AppMsgType == CONF.APPMSGTYPE_READER_TYPE
* item.url
* item.title
* item.pub_time
* item.cover
* item.digest
*/
MMCategory: any[] // item in message.MMCategory
/**
* Type
*
* MsgType == CONF.MSGTYPE_VOICE : ng-style="{'width':40 + 7*message.VoiceLength/1000}
*/
MsgType: number
AppMsgType: number // message.MsgType == CONF.MSGTYPE_APP && message.AppMsgType == CONF.APPMSGTYPE_URL
// message.MsgType == CONF.MSGTYPE_TEXT && message.SubMsgType != CONF.MSGTYPE_LOCATION
SubMsgType: number // "msgType":"{{message.MsgType}}","subType":{{message.SubMsgType||0}},"msgId":"{{message.MsgId}}"
/**
* Status-es
*/
Status: string
MMStatus: number // img ng-show="message.MMStatus == 1" class="ico_loading"
// ng-click="resendMsg(message)" ng-show="message.MMStatus == 5" title="重新发送"
MMFileStatus: number // <p class="loading" ng-show="message.MMStatus == 1 || message.MMFileStatus == CONF.MM_SEND_FILE_STATUS_FAIL">
// CONF.MM_SEND_FILE_STATUS_QUEUED, MM_SEND_FILE_STATUS_SENDING
/**
* Location
*/
MMLocationUrl: string // ng-if="message.MsgType == CONF.MSGTYPE_TEXT && message.SubMsgType == CONF.MSGTYPE_LOCATION"
// <a href="{{message.MMLocationUrl}}" target="_blank">
/**
* MsgType == CONF.MSGTYPE_EMOTICON
*
* getMsgImg(message.MsgId,'big',message)
*/
/**
* Image
*
* getMsgImg(message.MsgId,'slave')
*/
MMImgStyle: string // ng-style="message.MMImgStyle"
MMPreviewSrc: string // message.MMPreviewSrc || message.MMThumbSrc || getMsgImg(message.MsgId,'slave')
MMThumbSrc: string
/**
* Friend Request & ShareCard ?
*
* MsgType == CONF.MSGTYPE_SHARECARD" ng-click="showProfile($event,message.RecommendInfo.UserName)
* MsgType == CONF.MSGTYPE_VERIFYMSG
*/
RecommendInfo?: RecommendInfo
}
export type MessageObj = {
id: string
type: string
type: number
from: string
to: string
room?: string
......@@ -45,15 +119,43 @@ export type MessageObj = {
url?: string // for MessageMedia class
}
export type MessageType = {
[index: string]: number|string
// export type MessageTypeName = 'TEXT' | 'IMAGE' | 'VOICE' | 'VERIFYMSG' | 'POSSIBLEFRIEND_MSG' | 'SHARECARD' | 'VIDEO' | 'EMOTICON' | 'LOCATION' | 'APP' | 'VOIPMSG' | 'STATUSNOTIFY' | 'VOIPNOTIFY' | 'VOIPINVITE' | 'MICROVIDEO' | 'SYSNOTICE' | 'SYS' | 'RECALLED'
// export type MessageTypeValue = 1 | 3 | 34 | 37 | 40 | 42 | 43 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 62 | 9999 | 10000 | 10002
export type MessageTypeMap = {
[index: string]: string|number
// MessageTypeName: MessageTypeValue
// , MessageTypeValue: MessageTypeName
}
export const enum MessageType {
TEXT = 1,
IMAGE = 3,
VOICE = 34,
VERIFYMSG = 37,
POSSIBLEFRIEND_MSG = 40,
SHARECARD = 42,
VIDEO = 43,
EMOTICON = 47,
LOCATION = 48,
APP = 49,
VOIPMSG = 50,
STATUSNOTIFY = 51,
VOIPNOTIFY = 52,
VOIPINVITE = 53,
MICROVIDEO = 62,
SYSNOTICE = 9999,
SYS = 10000,
RECALLED = 10002
}
export class Message implements Sayable {
public static counter = 0
private _counter: number
public static TYPE: MessageType = {
public static TYPE: MessageTypeMap = {
TEXT: 1,
IMAGE: 3,
VOICE: 34,
......@@ -82,6 +184,10 @@ export class Message implements Sayable {
throw Error('abstract method')
}
public ext(): string {
throw Error('abstract method')
}
constructor(public rawObj?: MessageRawObj) {
this._counter = Message.counter++
log.silly('Message', 'constructor() SN:%d', this._counter)
......@@ -100,7 +206,7 @@ export class Message implements Sayable {
const obj: MessageObj = {
id: rawObj.MsgId
, type: rawObj.MsgType
, from: rawObj.MMActualSender
, from: rawObj.MMActualSender // MMPeerUserName
, to: rawObj.ToUserName
, content: rawObj.MMActualContent // Content has @id prefix added by wx
, status: rawObj.Status
......@@ -225,7 +331,10 @@ export class Message implements Sayable {
return this.obj.content
}
public type() { return this.obj.type }
public type(): MessageType {
return this.obj.type as MessageType
}
public typeEx() { return Message.TYPE[this.obj.type] }
public count() { return this._counter }
......@@ -241,7 +350,7 @@ export class Message implements Sayable {
return fromId === userId
}
public async ready(): Promise<this> {
public async ready(): Promise<void> {
log.silly('Message', 'ready()')
try {
......@@ -256,7 +365,8 @@ export class Message implements Sayable {
await to.ready() // Contact to
if (room) { await room.ready() } // Room member list
return this // return this for chain
// return this // return this for chain
} catch (e) {
log.error('Message', 'ready() exception: %s', e.stack)
// console.log(e)
......@@ -270,7 +380,7 @@ export class Message implements Sayable {
* @deprecated
*/
public get(prop: string): string {
log.warn('Message', 'DEPRECATED get()')
log.warn('Message', 'DEPRECATED get() at %s', new Error('stack').stack)
if (!prop || !(prop in this.obj)) {
const s = '[' + Object.keys(this.obj).join(',') + ']'
......@@ -283,7 +393,7 @@ export class Message implements Sayable {
* @deprecated
*/
public set(prop: string, value: string): this {
log.warn('Message', 'DEPRECATED set()')
log.warn('Message', 'DEPRECATED set() at %s', new Error('stack').stack)
if (typeof value !== 'string') {
throw new Error('value must be string, we got: ' + typeof value)
......
......@@ -253,11 +253,45 @@ export class Bridge {
}
public getMsgImg(id): Promise<string> {
log.verbose('PuppetWebBridge', 'getMsgImg(%s)', id)
return this.proxyWechaty('getMsgImg', id)
.catch(e => {
log.silly('PuppetWebBridge', 'proxyWechaty(getMsgImg, %d) exception: %s', id, e.message)
.catch(e => {
log.silly('PuppetWebBridge', 'proxyWechaty(getMsgImg, %d) exception: %s', id, e.message)
throw e
})
}
public getMsgEmoticon(id): Promise<string> {
log.verbose('PuppetWebBridge', 'getMsgEmoticon(%s)', id)
return this.proxyWechaty('getMsgEmoticon', id)
.catch(e => {
log.silly('PuppetWebBridge', 'proxyWechaty(getMsgEmoticon, %d) exception: %s', id, e.message)
throw e
})
}
public async getMsgVideo(id): Promise<string> {
log.verbose('PuppetWebBridge', 'getMsgVideo(%s)', id)
try {
return await this.proxyWechaty('getMsgVideo', id)
} catch (e) {
log.silly('PuppetWebBridge', 'proxyWechaty(getMsgVideo, %d) exception: %s', id, e.message)
throw e
})
}
}
public async getMsgVoice(id): Promise<string> {
log.verbose('PuppetWebBridge', 'getMsgVoice(%s)', id)
try {
return await this.proxyWechaty('getMsgVoice', id)
} catch (e) {
log.silly('PuppetWebBridge', 'proxyWechaty(getMsgVoice, %d) exception: %s', id, e.message)
throw e
}
}
public getContact(id: string): Promise<string> {
......
......@@ -37,7 +37,7 @@ export class BrowserDriver {
await this.driver.quit()
// }
} catch (e) {
log.warn('PuppetWebBrowserDriver', 'init() this.driver.quit() soft exception: %s'
log.verbose('PuppetWebBrowserDriver', 'init() this.driver.quit() soft exception: %s'
, e.stack
)
}
......@@ -246,18 +246,21 @@ export class BrowserDriver {
try {
const session = await new Promise((resolve, reject) => {
// resolve
driver.getSession()
.then(session => resolve(session))
// reject
setTimeout(() => {
const timer = setTimeout(() => {
const e = new Error('valid() driver.getSession() timeout(halt?)')
log.warn('PuppetWebBrowserDriver' , e.message)
log.verbose('PuppetWebBrowserDriver', e.stack)
reject(e)
}, 10 * 1000)
// resolve
driver.getSession()
.then(session => {
clearTimeout(timer)
resolve(session)
})
})
log.verbose('PuppetWebBrowserDriver', 'valid() driver.getSession() done')
......
......@@ -26,8 +26,8 @@ import {
, MediaMessage
} from '../message'
import { Firer } from './firer'
import { PuppetWeb } from './puppet-web'
import { Firer } from './firer'
import { PuppetWeb } from './puppet-web'
/* tslint:disable:variable-name */
export const Event = {
......@@ -59,12 +59,16 @@ async function onBrowserDead(this: PuppetWeb, e): Promise<void> {
// guard by variable: isBrowserBirthing to prevent the 2nd time entrance.
// if (this.browser.targetState() !== 'open'
// || this.browser.currentState() === 'opening') {
if ((browser.state.current() === 'open' && browser.state.inprocess())
|| browser.state.target() !== 'open'
) {
// if ( (browser.state.current() === 'open' && browser.state.inprocess())
// || browser.state.target() !== 'open'
if (browser.state.target() === 'close' || browser.state.inprocess()) {
log.verbose('PuppetWebEvent', 'onBrowserDead() will do nothing because %s, or %s'
, 'browser.state.target() !== open'
, 'browser.state.current() === open & inprocess'
// , 'browser.state.target() !== open'
// , 'browser.state.current() === open & inprocess'
, 'browser.state.target() === close'
, 'browser.state.inprocess()'
)
return
}
......@@ -82,8 +86,12 @@ async function onBrowserDead(this: PuppetWeb, e): Promise<void> {
await this.browser.quit()
log.verbose('PuppetWebEvent', 'onBrowserDead() browser.quit() done')
if (browser.state.target() !== 'open') {
log.warn('PuppetWebEvent', 'onBrowserDead() will not init browser because browser.state.target(%s) !== open'
// if (browser.state.target() !== 'open') {
// log.warn('PuppetWebEvent', 'onBrowserDead() will not init browser because browser.state.target(%s) !== open'
// , browser.state.target()
// )
if (browser.state.target() === 'close') {
log.warn('PuppetWebEvent', 'onBrowserDead() will not init browser because browser.state.target(%s) is `close`'
, browser.state.target()
)
return
......@@ -97,12 +105,12 @@ async function onBrowserDead(this: PuppetWeb, e): Promise<void> {
const dong = await this.ding()
if (/dong/i.test(dong)) {
log.verbose('PuppetWebEvent', 'onBrowserDead() ding() works well after reset')
} else {
const err = new Error('ding() got "' + dong + '", should be "dong" ')
log.warn('PuppetWebEvent', 'onBrowserDead() %s', err.message)
throw err
}
log.verbose('PuppetWebEvent', 'onBrowserDead() ding() works well after reset')
} catch (e) {
log.error('PuppetWebEvent', 'onBrowserDead() exception: %s', e.message)
try {
......@@ -115,6 +123,7 @@ async function onBrowserDead(this: PuppetWeb, e): Promise<void> {
log.verbose('PuppetWebEvent', 'onBrowserDead() new browser borned')
// why POISON here... forgot, faint
this.emit('watchdog', {
data: `onBrowserDead() new browser borned`
, type: 'POISON'
......@@ -123,9 +132,8 @@ async function onBrowserDead(this: PuppetWeb, e): Promise<void> {
return
}
function onServerDing(this: PuppetWeb, data) {
function onServerDing(this: PuppetWeb, data): void {
log.silly('PuppetWebEvent', 'onServerDing(%s)', data)
// this.watchDog(data)
this.emit('watchdog', { data })
}
......@@ -147,7 +155,6 @@ async function onServerScan(this: PuppetWeb, data: ScanInfo) {
}
// feed watchDog a `scan` type of food
// this.watchDog(data, {type: 'scan'})
const food: WatchdogFood = {
data
, type: 'SCAN'
......@@ -170,7 +177,6 @@ async function onServerDisconnect(this: PuppetWeb, data): Promise<void> {
this.user = null
}
// if (this.currentState() === 'killing') {
if (this.state.current() === 'dead' && this.state.inprocess()) {
log.verbose('PuppetWebEvent', 'onServerDisconnect() be called when state.current() is `dead` and inprocess()')
return
......@@ -213,8 +219,8 @@ async function onServerDisconnect(this: PuppetWeb, data): Promise<void> {
throw e
}
this.bridge.init()
.then(ret => log.verbose('PuppetWebEvent', 'onServerDisconnect() setTimeout() bridge re-inited: %s', ret))
.catch(e => log.error('PuppetWebEvent', 'onServerDisconnect() setTimeout() exception: [%s]', e))
.then(() => log.verbose('PuppetWebEvent', 'onServerDisconnect() setTimeout() bridge.init() done.'))
.catch(e => log.error('PuppetWebEvent', 'onServerDisconnect() setTimeout() bridge.init() exception: [%s]', e))
}, 1000) // 1 second instead of 10 seconds? try. (should be enough to wait)
return
......@@ -279,8 +285,6 @@ async function onServerLogin(this: PuppetWeb, data, attempt = 0): Promise<void>
if (this.userId) {
log.verbose('PuppetWebEvent', 'onServerLogin() be called but with userId set?')
}
// co.call(this, function* () {
try {
// co.call to make `this` context work inside generator.
// See also: https://github.com/tj/co/issues/274
......@@ -311,7 +315,6 @@ async function onServerLogin(this: PuppetWeb, data, attempt = 0): Promise<void>
this.emit('login', this.user)
// }).catch(e => {
} catch (e) {
log.error('PuppetWebEvent', 'onServerLogin() exception: %s', e)
console.log(e.stack)
......@@ -340,7 +343,6 @@ function onServerLogout(this: PuppetWeb, data) {
async function onServerMessage(this: PuppetWeb, data): Promise<void> {
let m = new Message(data)
// co.call(this, function* () {
try {
await m.ready()
......@@ -368,9 +370,15 @@ async function onServerMessage(this: PuppetWeb, data): Promise<void> {
* Check Type for special Message
* reload if needed
*/
console.log(m.type())
switch (m.type()) {
case Message.TYPE['EMOTICON']:
case Message.TYPE['IMAGE']:
// log.verbose('PuppetWebEvent', 'onServerMessage() IMAGE message')
case Message.TYPE['VIDEO']:
case Message.TYPE['VOICE']:
case Message.TYPE['MICROVIDEO']:
log.verbose('PuppetWebEvent', 'onServerMessage() EMOTICON/IMAGE/VIDEO/VOICE/MICROVIDEO message')
m = new MediaMessage(data)
break
}
......
......@@ -113,14 +113,18 @@ export class PuppetWeb extends Puppet {
public async quit(): Promise<void> {
log.verbose('PuppetWeb', 'quit()')
// XXX should we set `target` to `dead` in `quit()`?
// this.state.target('dead')
if (this.state.current() === 'dead' && this.state.inprocess()) {
log.warn('PuppetWeb', 'quit() is called but state.current() is `dead` and inprocess() ?')
throw new Error('do not call quit again when quiting')
if (this.state.current() === 'dead') {
if (this.state.inprocess()) {
log.warn('PuppetWeb', 'quit() is called but state.current() is `dead` and inprocess() ?')
throw new Error('do not call quit again when quiting')
}
log.warn('PuppetWeb', 'quit() is called but state.current() is `dead`, return and will not run quit() again(do nothing).')
return
}
// XXX should we set `target` to `dead` in `quit()` ???
// this.state.target('dead')
/**
* must feed POISON to Watchdog
* before state set to `dead` & `inprocess`
......
......@@ -236,10 +236,6 @@
*/
var contentChatScope = rootScope.$new()
injector.get('$controller')('contentChatController', {$scope: contentChatScope })
/*
s =
*/
// get all we need from wx in browser(angularjs)
WechatyBro.glue = {
......@@ -320,7 +316,9 @@
log('logout(' + data + ')')
WechatyBro.vars.loginStatus = false
// WechatyBro.emit('logout', data)
WechatyBro.glue.loginFactory.loginout()
if (WechatyBro.glue.loginFactory) {
WechatyBro.glue.loginFactory.loginout()
}
checkScan()
}
function quit() {
......@@ -395,15 +393,45 @@
/**
*
* Help Functions which Proxy to WXAPP AngularJS Scope & Factory
*
* getMsgImg(message.MsgId,'slave')
* getMsgImg(message.MsgId,'big',message)
*/
function getMsgImg(id) {
function getMsgImg(id, type, message) {
var contentChatScope = WechatyBro.glue.contentChatScope
if (!contentChatScope) {
throw new Error('getMsgImg() contentChatScope not found')
}
var location = window.location.href.replace(/\/$/, '')
var path = contentChatScope.getMsgImg(id)
var path = contentChatScope.getMsgImg(id, type, message)
return location + path
}
function getMsgEmoticon(id) {
var chatFactory = WechatyBro.glue.chatFactory
var message = chatFactory.getMsg(id)
return message.MMPreviewSrc || getMsgImg(message.MsgId,'big',message) || message.MMThumbSrc
}
function getMsgVideo(id) {
var contentChatScope = WechatyBro.glue.contentChatScope
if (!contentChatScope) {
throw new Error('getMsgVideo() contentChatScope not found')
}
var location = window.location.href.replace(/\/$/, '')
var path = contentChatScope.getMsgVideo(id)
return location + path
}
/**
* from playVoice()
*/
function getMsgVoice(id) {
var confFactory = WechatyBro.glue.confFactory
var accountFactory = WechatyBro.glue.accountFactory
var location = window.location.href.replace(/\/$/, '')
var path = confFactory.API_webwxgetvoice + "?msgid=" + id + "&skey=" + accountFactory.getSkey()
return location + path
}
......@@ -732,9 +760,12 @@
, emit: emit // send event to server
, logout: logout // logout current logined user
, getContact: getContact
, getUserName: getUserName
, getMsgImg: getMsgImg
, getContact
, getUserName
, getMsgImg
, getMsgEmoticon
, getMsgVideo
, getMsgVoice
// for Wechaty Contact Class
, contactFindAsync
......
......@@ -172,6 +172,7 @@ export class Room extends EventEmitter implements Sayable {
private parse(rawObj: RoomRawObj): RoomObj | null {
if (!rawObj) {
log.warn('Room', 'parse() on a empty rawObj?')
return null
}
return {
......
......@@ -102,10 +102,14 @@ export class UtilLib {
const options = require('url').parse(url)
options.headers = {
Accept: 'image/webp,image/*,*/*;q=0.8'
, 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'
// Accept: 'image/webp,image/*,*/*;q=0.8'
Accept: '*/*'
, Referer: 'https://wx.qq.com/'
, 'Accept-Encoding': 'gzip, deflate, sdch'
, Range: 'bytes=0-'
, 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'
// , 'Accept-Encoding': 'gzip, deflate, sdch'
, 'Accept-Encoding': 'identity;q=1, *;q=0'
, 'Accept-Language': 'zh-CN,zh;q=0.8'
}
options.agent = http.globalAgent
......
#!/usr/bin/env bats
function dockerRun() {
docker run -v "$(pwd)":/bot ${IMAGE_NAME:-'wechaty:test'} $@
}
fixture=test/fixture/docker
@test "javascript bot" {
cd "$fixture"
run dockerRun js-bot.js
[ "$status" -eq 0 ]
}
@test "javascript syntax error" {
cd "$fixture"
run dockerRun syntax-error.js
[ "$status" -ne 0 ]
}
@test "typescript bot" {
cd "$fixture"
run dockerRun ts-bot.ts
[ "$status" -eq 0 ]
}
@test "typescript type error" {
cd "$fixture"
run dockerRun type-error.ts
[ "$status" -ne 0 ]
}
@test "javascript bot with require" {
cd "$fixture/with-package-json/"
run dockerRun with-require.js
[ "$status" -eq 0 ]
}
@test "javascript bot require error" {
cd "$fixture/with-package-json/"
run dockerRun with-require-error.js
[ "$status" -ne 0 ]
}
@test "typescript bot with import" {
cd "$fixture/with-package-json/"
run dockerRun with-import.ts
[ "$status" -eq 0 ]
}
@test "typescript bot with import error" {
cd "$fixture/with-package-json/"
run dockerRun with-import-error.ts
[ "$status" -ne 0 ]
}
@test "doctor(default by npm run)" {
run dockerRun doctor
[ "$status" -eq 0 ]
}
@test "run doctor" {
run dockerRun run doctor
[ "$status" -eq 0 ]
}
@test "run non-exist command" {
run dockerRun run fdasfadsfasdfasdfasdfasd
[ "$status" -ne 0 ]
}
@test "direct non-exist command" {
run dockerRun fdasfadsfasdfasdfasdfasd
[ "$status" -ne 0 ]
}
hello, I'm not a javascript file
let i = 3
i = "should set to string because it's type is number"
console.log(i)
{
"name": "with-package-json",
"version": "1.0.0",
"description": "",
"main": "with-require-error.js",
"dependencies": {
"brolog": "^0.3.11"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
import { NotExist } from 'not-exist-at-all-fdsafasdfafsad'
const ne = new NotExist()
console.log(ne) // should not run to here
import { Brolog } from 'brolog'
const brolog = new Brolog()
brolog.info('OK', 'with-import.ts')
const { NotExist } = require('not-exist-at-all-fasdfasdsfaf')
const ne = new NowExist()
console.log(ne) // should not run to here
const { Brolog } = require('brolog')
const brolog = new Brolog()
brolog.info('OK', 'with-require.js')
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册