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

PuppetPadchat refactoring... (#1249)

上级 34a0f53d
......@@ -39,6 +39,8 @@ const BOT_QR_CODE_IMAGE_FILE = path.resolve(
'../docs/images/bot-qr-code.png',
)
const bot = Wechaty.instance()
const welcome = `
| __ __ _ _
| \\ \\ / /__ ___| |__ __ _| |_ _ _
......@@ -49,6 +51,7 @@ const welcome = `
=============== Powered by Wechaty ===============
-------- https://github.com/chatie/wechaty --------
Version: ${bot.version(true)}
I'm a bot, my superpower is talk in Wechat.
......@@ -63,7 +66,6 @@ Please wait... I'm trying to login in...
`
console.log(welcome)
const bot = Wechaty.instance()
bot
.on('logout' , user => log.info('Bot', `${user.name()} logouted`))
......@@ -71,14 +73,14 @@ bot
log.info('Bot', `${user.name()} login`)
bot.say('Wechaty login').catch(console.error)
})
.on('scan', (qrCode, statusCode) => {
if (!/201|200/.test(String(statusCode))) {
QrcodeTerminal.generate(qrCode, { small: true }, (qrcode: string) => {
console.log(qrcode)
console.log(qrCode)
console.log(`[${statusCode}] Scan QR Code above url to log in: `)
})
}
.on('scan', (qrData, status, data) => {
QrcodeTerminal.generate(qrData, { small: true }, (asciiArt: string) => {
console.log(asciiArt)
console.log(`[${status}] Scan QR Code above url to log in: `)
if (data) {
console.log(data)
}
})
})
.on('message', async msg => {
try {
......
......@@ -41,6 +41,8 @@ const BOT_QR_CODE_IMAGE_FILE = path.resolve(
'../docs/images/bot-qr-code.png',
)
const bot = Wechaty.instance()
const welcome = `
| __ __ _ _
| \\ \\ / /__ ___| |__ __ _| |_ _ _
......@@ -51,6 +53,7 @@ const welcome = `
=============== Powered by Wechaty ===============
-------- https://github.com/chatie/wechaty --------
Version: ${bot.version(true)}
I'm a bot, my superpower is talk in Wechat.
......@@ -65,7 +68,6 @@ Please wait... I'm trying to login in...
`
console.log(welcome)
const bot = Wechaty.instance()
bot
.on('logout' , user => log.info('Bot', `${user.name()} logouted`))
......@@ -83,7 +85,6 @@ bot
})
})
.on('message', async msg => {
console.log('on message:')
try {
console.log(msg.toString()) // on(message) exception: Error: no file
const text = msg.text()
......
......@@ -22,7 +22,12 @@ import * as path from 'path'
import * as readPkgUp from 'read-pkg-up'
import * as Raven from 'raven'
import { log } from 'brolog'
import * as qrImage from 'qr-image'
import { log } from 'brolog'
import {
FileBox,
} from 'file-box'
import {
PuppetName,
......@@ -189,6 +194,17 @@ export class Config {
}
}
export const CHATIE_OFFICIAL_ACCOUNT_ID = 'gh_051c89260e5d'
export function qrCodeForChatie(): FileBox {
const CHATIE_OFFICIAL_ACCOUNT_QRCODE = 'http://weixin.qq.com/r/qymXj7DEO_1ErfTs93y5'
const name = 'qrcode-for-chatie.png'
const type = 'png'
const qrStream = qrImage.image(CHATIE_OFFICIAL_ACCOUNT_QRCODE, { type })
return FileBox.fromStream(qrStream, name)
}
export interface Sayable {
say(text: string, replyTo?: any|any[]): Promise<void>
}
......
......@@ -159,7 +159,7 @@ export class Message extends Accessory implements Sayable {
')',
]
if (this.type() === Message.Type.Text) {
msgStrList.push(`<${this.text()}>`)
msgStrList.push(`<${this.text().substr(0, 70)}>`)
} else {
log.silly('Message', 'toString() for message type: %s(%s)', Message.Type[this.type()], this.type())
......
......@@ -44,6 +44,7 @@ import {
import {
log,
qrCodeForChatie,
} from '../config'
export type PuppetFoodType = 'scan' | 'ding'
......@@ -284,6 +285,18 @@ export class PuppetMock extends Puppet {
log.verbose('PuppetMock', 'roomDel(%s, %s)', roomId, contactId)
}
public async roomAvatar(roomId: string): Promise<FileBox> {
log.verbose('PuppetMock', 'roomAvatar(%s)', roomId)
const payload = await this.roomPayload(roomId)
if (payload.avatar) {
return FileBox.fromUrl(payload.avatar)
}
log.warn('PuppetMock', 'roomAvatar() avatar not found, use the chatie default.')
return qrCodeForChatie()
}
public async roomAdd(
roomId : string,
contactId : string,
......
此差异已折叠。
export interface FunctionType {
userId: string,
msgId: string,
apiName: string,
param: string[],
}
export interface AutoDataType {
wxData?: string,
token?: string,
user_name?: string,
nick_name?: string,
}
export interface InitType {
message : string,
status : number,
}
export interface WXInitializeType {
message : string, // WxUserInit成功
status : number,
}
export interface WXAddChatRoomMemberType {
message : string, // "\n\u0010Everything+is+OK" (succeed) || "\n\u0014MemberList+are+wrong" ('user has in the room')
status : number, // 0
}
export interface WXGetQRCodeType {
qr_code : string,
}
export enum WXCheckQRCodeStatus {
WaitScan = 0,
WaitConfirm = 1,
Confirmed = 2,
Timeout = 3,
Cancel = 4,
}
export interface WXCheckQRCodePayload {
device_type ?: string, // android,
expired_time : number, // 238,
head_url ?: string, // http://wx.qlogo.cn/mmhead/ver_1/NkOvv1rTx3Dsqpicnhe0j7cVOR3psEAVfuhFLbmoAcwaob4eENNZlp3KIEsMgibfH4kRjDicFFXN3qdP6SGRXbo7GBs8YpN52icxSeBUX8xkZBA/0,
nick_name ?: string, // 苏轼,
password ?: string,
status : number, // 2 = success
user_name ?: string, // wxid_zj2cahpwzgie12
}
export interface WXHeartBeatType {
status : number, // 0
message : string, // ok
}
export interface WXGenerateWxDatType {
data : string, // 62data,
message : string, // '',
status : number, // 0
}
export interface WXLoadWxDatType {
status : number, // 0
message : string, // ok
}
export interface StandardType {
status : number, // 0
message : string, // ''
}
export interface WXGetLoginTokenType {
message : string,
status : number, // 0,
token : string, // XXXXXXXX,
uin : number, // 324216852
}
export interface WXAutoLoginType {
email : string,
external? : number, // 0,
long_link_server? : string, // szlong.weixin.qq.com,
message : string, // �Everything+is+ok,
nick_name : string,
phone_number : string,
qq : number, // 0,
short_link_server? : string // szshort.weixin.qq.com:80,
status : number // 0,
uin? : number // 324216852,
user_name : string // wxid_zj2cahpwzgie12
}
export interface WXLoginRequestType {
status: number // 0
}
export interface WXSendMsgType {
message : string,
msg_id : string, // '5612827783578738216',
status : number, // 0
}
export interface WXQRCodeLoginType {
email : string, // sushishigeshiren@163.com,
external : number, // 1,
long_link_server : string, // szlong.weixin.qq.com,
message : string, // �Everything+is+ok,
nick_name : string, // 苏轼,
phone_number : string, // 17326998117,
qq : number, // 0,
short_link_server : string, // szshort.weixin.qq.com:80,
status : number, // 0,
uin : number, // 324216852,
user_name : string, // wxid_zj2cahpwzgie12
}
......@@ -6,11 +6,11 @@ import * as test from 'blue-tape'
import {
isRoomId,
isContactId,
isOfficialContactId,
isContactOfficialId,
} from './misc'
test('isRoomId()', async t => {
const ROOM_ID = 'xxx@chatroom'
const ROOM_ID = 'xxx@chatroom'
const NOT_ROOM_ID = 'xxxxxxx'
t.ok(isRoomId(ROOM_ID), 'should return true for ROOM_ID')
......@@ -18,7 +18,7 @@ test('isRoomId()', async t => {
})
test('isContactId()', async t => {
const CONTACT_ID = 'sxxfdsa'
const CONTACT_ID = 'sxxfdsa'
const NOT_CONTACT_ID = 'fdsafasd@chatroom'
t.ok(isContactId(CONTACT_ID), 'should return true for CONTACT_ID')
......@@ -26,9 +26,9 @@ test('isContactId()', async t => {
})
test('isOfficialContactId()', async t => {
const OFFICIAL_CONTACT_ID = 'gh_sxxfdsa'
const OFFICIAL_CONTACT_ID = 'gh_sxxfdsa'
const NOT_OFFICIAL_CONTACT_ID = 'fdsafasd@chatroom'
t.ok(isOfficialContactId(OFFICIAL_CONTACT_ID), 'should return true for OFFICIAL_CONTACT_ID')
t.notOk(isOfficialContactId(NOT_OFFICIAL_CONTACT_ID), 'should return false for NOT_OFFICIAL_CONTACT_ID')
t.ok(isContactOfficialId(OFFICIAL_CONTACT_ID), 'should return true for OFFICIAL_CONTACT_ID')
t.notOk(isContactOfficialId(NOT_OFFICIAL_CONTACT_ID), 'should return false for NOT_OFFICIAL_CONTACT_ID')
})
......@@ -6,6 +6,6 @@ export function isContactId(id: string) {
return !isRoomId(id)
}
export function isOfficialContactId(id: string) {
export function isContactOfficialId(id: string) {
return /^gh_/i.test(id)
}
......@@ -16,6 +16,10 @@
* limitations under the License.
*
*/
import {
ContactGender,
} from '../puppet/'
// 1 when use WXSyncContact, 0 when use WXGetContact
export enum PadchatContactRoomStatus {
Get = 0,
......@@ -48,16 +52,17 @@ export enum PadchatMsgType {
}
export enum PadchatContinue {
Stop = 0, // Load Ready
Go = 1, // NOT Load Ready
Done = 0, // Load Ready
Go = 1, // NOT Load Ready
}
// 2 Female, 1 Male, 0 Not Known
export enum PadchatContactGender {
Unknown = 0,
Male = 1,
Female ,
}
// The same as ContactGender.
// export enum PadchatContactGender {
// Unknown = 0,
// Male = 1,
// Female ,
// }
export enum PadchatPayloadType {
Logout = -1, // -1 when logout
......@@ -82,7 +87,7 @@ export interface PadchatPayload {
* - WXGetContact
* @interface PadchatContactPayload
*/
export interface PadchatContactPayload {
export interface PadchatContactRawPayload {
/**
* Sometimes, WXSyncContact can only get the following result:
* {
......@@ -119,7 +124,7 @@ export interface PadchatContactPayload {
remark : string, // "女儿",
remark_py_initial : string, // 'lijiaruibeizhu',
remark_quan_pin : string, // 'LJRBZ',
sex : PadchatContactGender,
sex : ContactGender,
signature : string, // "且行且珍惜",
small_head : string, // "http://wx.qlogo.cn/mmhead/ver_1/xfCMmibHH74xGLoyeDFJadrZXX3eOEznPefiaCa3iczxZGMwPtDuSbRQKx3Xdm18un303mf0NFia3USY2nO2VEYILw/132",
status : PadchatContactRoomStatus, // 1 when use WXSyncContact, 0 when use WXGetContact
......@@ -331,7 +336,7 @@ export interface PadchatRoomMember {
* - WXGetContact
* @interface PadchatRoomPayload
*/
export interface PadchatRoomPayload {
export interface PadchatRoomRawPayload {
/**
* Sometimes, WXSyncContact can only get the following result:
* {
......@@ -360,7 +365,7 @@ export interface PadchatRoomPayload {
chatroom_owner: string, // "qq512436430",
continue?: PadchatContinue, // 1,
max_member_count: number, // 500,
member: string[], // JSON.parse(decodeURIComponent(member)) | "[\"qq512436430\",\"mengjunjun001\",\"wxid_zj2cahpwzgie12\",\"wxid_7708837087612\"]\n",
member?: string[], // JSON.parse(decodeURIComponent(member)) | "[\"qq512436430\",\"mengjunjun001\",\"wxid_zj2cahpwzgie12\",\"wxid_7708837087612\"]\n",
member_count: number, // 4,
nick_name: string, // room-topic:"facenet",
py_initial: string, // 'FACENET',
......@@ -375,7 +380,7 @@ export interface PadchatRoomMemberPayload {
chatroom_id : number, // not know: 700000156,
count : number, // 4,
// tslint:disable-next-line:max-line-length
member : PadchatRoomMember[], // JSON.parse(decodeURIComponent(member)): PadchatRoomRawMember[] | '[{"big_head":"http://wx.qlogo.cn/mmhead/ver_1/DpS0ZssJ5s8tEpSr9JuPTRxEUrCK0USrZcR3PjOMfUKDwpnZLxWXlD4Q38bJpcXBtwXWwevsul1lJqwsQzwItQ/0","chatroom_nick_name":"","invited_by":"wxid_7708837087612","nick_name":"李佳芮","small_head":"http://wx.qlogo.cn/mmhead/ver_1/DpS0ZssJ5s8tEpSr9JuPTRxEUrCK0USrZcR3PjOMfUKDwpnZLxWXlD4Q38bJpcXBtwXWwevsul1lJqwsQzwItQ/132","user_name":"qq512436430"},{"big_head":"http://wx.qlogo.cn/mmhead/ver_1/kcBj3gSibfFd2I9vQ8PBFyQ77cpPIfqkFlpTdkFZzBicMT6P567yj9IO6xG68WsibhqdPuG82tjXsveFATSDiaXRjw/0","chatroom_nick_name":"","invited_by":"wxid_7708837087612","nick_name":"梦君君","small_head":"http://wx.qlogo.cn/mmhead/ver_1/kcBj3gSibfFd2I9vQ8PBFyQ77cpPIfqkFlpTdkFZzBicMT6P567yj9IO6xG68WsibhqdPuG82tjXsveFATSDiaXRjw/132","user_name":"mengjunjun001"},{"big_head":"http://wx.qlogo.cn/mmhead/ver_1/3CsKibSktDV05eReoAicV0P8yfmuHSowfXAMvRuU7HEy8wMcQ2eibcaO1ccS95PskZchEWqZibeiap6Gpb9zqJB1WmNc6EdD6nzQiblSx7dC1eGtA/0","chatroom_nick_name":"","invited_by":"wxid_7708837087612","nick_name":"苏轼","small_head":"http://wx.qlogo.cn/mmhead/ver_1/3CsKibSktDV05eReoAicV0P8yfmuHSowfXAMvRuU7HEy8wMcQ2eibcaO1ccS95PskZchEWqZibeiap6Gpb9zqJB1WmNc6EdD6nzQiblSx7dC1eGtA/132","user_name":"wxid_zj2cahpwzgie12"},{"big_head":"http://wx.qlogo.cn/mmhead/ver_1/piaHuicak41b6ibmcEVxoWKnnhgGDG5EbaD0hibwkrRvKeDs3gs7XQrkym3Q5MlUeSKY8vw2FRVVstialggUxf2zic2O8CvaEsicSJcghf41nibA940/0","chatroom_nick_name":"","invited_by":"wxid_zj2cahpwzgie12","nick_name":"王宁","small_head":"http://wx.qlogo.cn/mmhead/ver_1/piaHuicak41b6ibmcEVxoWKnnhgGDG5EbaD0hibwkrRvKeDs3gs7XQrkym3Q5MlUeSKY8vw2FRVVstialggUxf2zic2O8CvaEsicSJcghf41nibA940/132","user_name":"wxid_7708837087612"}]\n',
member? : PadchatRoomMember[], // JSON.parse(decodeURIComponent(member)): PadchatRoomRawMember[] | '[{"big_head":"http://wx.qlogo.cn/mmhead/ver_1/DpS0ZssJ5s8tEpSr9JuPTRxEUrCK0USrZcR3PjOMfUKDwpnZLxWXlD4Q38bJpcXBtwXWwevsul1lJqwsQzwItQ/0","chatroom_nick_name":"","invited_by":"wxid_7708837087612","nick_name":"李佳芮","small_head":"http://wx.qlogo.cn/mmhead/ver_1/DpS0ZssJ5s8tEpSr9JuPTRxEUrCK0USrZcR3PjOMfUKDwpnZLxWXlD4Q38bJpcXBtwXWwevsul1lJqwsQzwItQ/132","user_name":"qq512436430"},{"big_head":"http://wx.qlogo.cn/mmhead/ver_1/kcBj3gSibfFd2I9vQ8PBFyQ77cpPIfqkFlpTdkFZzBicMT6P567yj9IO6xG68WsibhqdPuG82tjXsveFATSDiaXRjw/0","chatroom_nick_name":"","invited_by":"wxid_7708837087612","nick_name":"梦君君","small_head":"http://wx.qlogo.cn/mmhead/ver_1/kcBj3gSibfFd2I9vQ8PBFyQ77cpPIfqkFlpTdkFZzBicMT6P567yj9IO6xG68WsibhqdPuG82tjXsveFATSDiaXRjw/132","user_name":"mengjunjun001"},{"big_head":"http://wx.qlogo.cn/mmhead/ver_1/3CsKibSktDV05eReoAicV0P8yfmuHSowfXAMvRuU7HEy8wMcQ2eibcaO1ccS95PskZchEWqZibeiap6Gpb9zqJB1WmNc6EdD6nzQiblSx7dC1eGtA/0","chatroom_nick_name":"","invited_by":"wxid_7708837087612","nick_name":"苏轼","small_head":"http://wx.qlogo.cn/mmhead/ver_1/3CsKibSktDV05eReoAicV0P8yfmuHSowfXAMvRuU7HEy8wMcQ2eibcaO1ccS95PskZchEWqZibeiap6Gpb9zqJB1WmNc6EdD6nzQiblSx7dC1eGtA/132","user_name":"wxid_zj2cahpwzgie12"},{"big_head":"http://wx.qlogo.cn/mmhead/ver_1/piaHuicak41b6ibmcEVxoWKnnhgGDG5EbaD0hibwkrRvKeDs3gs7XQrkym3Q5MlUeSKY8vw2FRVVstialggUxf2zic2O8CvaEsicSJcghf41nibA940/0","chatroom_nick_name":"","invited_by":"wxid_zj2cahpwzgie12","nick_name":"王宁","small_head":"http://wx.qlogo.cn/mmhead/ver_1/piaHuicak41b6ibmcEVxoWKnnhgGDG5EbaD0hibwkrRvKeDs3gs7XQrkym3Q5MlUeSKY8vw2FRVVstialggUxf2zic2O8CvaEsicSJcghf41nibA940/132","user_name":"wxid_7708837087612"}]\n',
message : string, // '',
status : PadchatRoomMemberStatus, // number, // 0,
user_name : string, // '6350854677@chatroom'
......
......@@ -32,7 +32,7 @@ import {
MessageType,
// ContactQueryFilter,
ContactGender,
// ContactGender,
ContactType,
ContactPayload,
......@@ -46,11 +46,17 @@ import {
FriendRequestPayload,
} from '../puppet/'
import Misc from '../misc'
import {
isContactOfficialId,
isRoomId,
} from './misc'
// import Misc from '../misc'
import {
log,
} from '../config'
qrCodeForChatie,
} from '../config'
import {
WECHATY_PUPPET_PADCHAT_TOKEN,
......@@ -59,20 +65,21 @@ import {
import {
Bridge,
resolverDict,
// resolverDict,
// AutoDataType,
} from './bridge'
import {
PadchatPayload,
PadchatContactPayload,
// PadchatPayload,
PadchatContactRawPayload,
PadchatMessagePayload,
PadchatRoomPayload,
PadchatRoomRawPayload,
PadchatMessageType,
PadchatContinue,
PadchatMsgType,
PadchatStatus,
// PadchatContinue,
// PadchatMsgType,
// PadchatStatus,
// PadchatPayloadType,
// PadchatRoomRawMember,
} from './padchat-schemas'
......@@ -81,10 +88,10 @@ export type ScanFoodType = 'scan' | 'login' | 'logout'
export class PuppetPadchat extends Puppet {
public readonly cachePadchatContactPayload : LRU.Cache<string, PadchatContactPayload>
public readonly cachePadchatContactPayload : LRU.Cache<string, PadchatContactRawPayload>
// public readonly cachePadchatFriendRequestRawPayload : LRU.Cache<string, FriendRequestRawPayload>
public readonly cachePadchatMessagePayload : LRU.Cache<string, PadchatMessagePayload>
public readonly cachePadchatRoomPayload : LRU.Cache<string, PadchatRoomPayload>
public readonly cachePadchatRoomPayload : LRU.Cache<string, PadchatRoomRawPayload>
public bridge: Bridge
// public botWs: WebSocket
......@@ -97,16 +104,16 @@ export class PuppetPadchat extends Puppet {
const lruOptions: LRU.Options = {
max: 1000,
// length: function (n) { return n * 2},
dispose: function (key: string, val: Object) {
dispose: function (key: string, val: any) {
log.silly('Puppet', 'constructor() lruOptions.dispose(%s, %s)', key, JSON.stringify(val))
},
maxAge: 1000 * 60 * 60,
}
this.cachePadchatContactPayload = new LRU<string, PadchatContactPayload>(lruOptions)
this.cachePadchatContactPayload = new LRU<string, PadchatContactRawPayload>(lruOptions)
// this.cacheFriendRequestPayload = new LRU<string, FriendRequestPayload>(lruOptions)
this.cachePadchatMessagePayload = new LRU<string, PadchatMessagePayload>(lruOptions)
this.cachePadchatRoomPayload = new LRU<string, PadchatRoomPayload>(lruOptions)
this.cachePadchatRoomPayload = new LRU<string, PadchatRoomRawPayload>(lruOptions)
this.bridge = new Bridge({
memory : this.options.memory,
......@@ -155,16 +162,16 @@ export class PuppetPadchat extends Puppet {
})
this.watchdog.on('reset', async (food, timeout) => {
log.warn('PuppetPadchat', 'initWatchdogForPuppet() dog.on(reset) last food:%s, timeout:%s',
food.data, timeout)
try {
await this.stop()
await this.start()
} catch (e) {
puppet.emit('error', e)
}
})
// this.watchdog.on('reset', async (food, timeout) => {
// log.warn('PuppetPadchat', 'initWatchdogForPuppet() dog.on(reset) last food:%s, timeout:%s',
// food.data, timeout)
// try {
// await this.stop()
// await this.start()
// } catch (e) {
// puppet.emit('error', e)
// }
// })
this.emit('watchdog', {
data: 'inited',
......@@ -194,7 +201,7 @@ export class PuppetPadchat extends Puppet {
log.verbose('PuppetPadchat', 'startBridge()')
if (this.state.off()) {
const e = new Error('initBridge() found targetState != live, no init anymore')
const e = new Error('startBridge() state is off')
log.warn('PuppetPadchat', e.message)
throw e
}
......@@ -203,115 +210,27 @@ export class PuppetPadchat extends Puppet {
// this.bridge.on('ding' , Event.onDing.bind(this))
// this.bridge.on('error' , e => this.emit('error', e))
// this.bridge.on('log' , Event.onLog.bind(this))
this.bridge.on('login' , (userId: string) => {
this.login(userId)
this.bridge.on('login', (userId: string) => {
this.bridge.syncContactsAndRooms()
this.login(userId)
})
this.bridge.on('ws', (data: string) => {
const payload: PadchatPayload = JSON.parse(data)
this.wsOnMessage(payload)
this.bridge.on('logout', () => {
this.logout()
})
// this.bridge.on('logout' , Event.onLogout.bind(this))
// this.bridge.on('message' , Event.onMessage.bind(this))
this.bridge.on('scan', (qrCode, statusCode, data) => {
this.bridge.on('message', (messagePayload: PadchatMessagePayload) => {
this.cachePadchatMessagePayload.set(
messagePayload.msg_id,
messagePayload,
)
this.emit('message', messagePayload.msg_id)
})
this.bridge.on('scan', (qrCode: string, statusCode: number, data?: string) => {
this.emit('scan', qrCode, statusCode, data)
})
// this.bridge.on('unload' , Event.onUnload.bind(this))
await this.bridge.start()
}
private async wsOnMessage(payload: PadchatPayload) {
// if (typeof data !== 'string') {
// const e = new Error('Ipad Websocket return wrong data!')
// log.warn('PuppetPadchat', e.message)
// throw e
// }
// const payload: PadchatPayload = JSON.parse(data)
if ( payload.continue === PadchatContinue.Stop
&& payload.msg_type === PadchatMsgType.N15_32768
&& payload.status === PadchatStatus.One
) {
// "continue":0,"msg_type":32768,"status":1,"
}
log.silly('PuppetPadchat', 'WebSocket Server result: %s', JSON.stringify(payload))
// Data From Tencent
if (!payload.msgId) {
// rawWebSocketData:
// {
// "apiName": "",
// "data": "XXXX",
// "msgId": "",
// "userId": "test"
// }
if (!payload.data && !payload.apiName) {
log.silly('PuppetPadchat', 'WebSocket Server get empty message data form Tencent server')
return
}
const msgRawPayloadList: PadchatMessagePayload[] = JSON.parse(decodeURIComponent(payload.data))
msgRawPayloadList.forEach(async (msgRawPayload) => {
log.silly('PuppetPadchat', 'WebSocket Server rawData result: %s', JSON.stringify(msgRawPayload))
if (!msgRawPayload.msg_id) {
log.silly('PuppetPadchat', 'WebSocket Server: get empty message msg_id form Tencent server for payoad: %s',
JSON.stringify(msgRawPayload),
)
return
}
// const msg = this.Message.create(
// msgRawPayload['msg_id'],
// await this.messagePayload(msgRawPayload.msg_id),
// )
// await msg.ready()
this.cachePadchatMessagePayload.set(msgRawPayload.msg_id, msgRawPayload)
this.emit('message', msgRawPayload.msg_id)
})
// Data Return From WebSocket Client
} else {
// check logout:
if (payload.type === -1) {
// this.emit('logout', this.selfId())
this.logout() // no need to await
}
log.silly('PuppetPadchat', 'return apiName: %s, msgId: %s', payload.apiName, payload.msgId)
const msgId = payload.msgId
let rawData: Object
if (!payload.data) {
log.silly('PuppetPadchat', 'WebSocket Server get empty message data form API call: %s', payload.apiName)
rawData = {}
} else {
rawData = JSON.parse(decodeURIComponent(payload.data))
}
// rawWebSocketData:
// {
// "apiName": "WXHeartBeat",
// "data": "%7B%22status%22%3A0%2C%22message%22%3A%22ok%22%7D",
// "msgId": "abc231923912983",
// "userId": "test"
// }
if (resolverDict[msgId]) {
const resolve = resolverDict[msgId]
delete resolverDict[msgId]
// resolve({rawData: rawData, msgId: rawWebSocketData.msgId})
resolve(rawData)
} else {
log.warn('PuppetPadchat', 'wsOnMessage() msgId %s not in resolverDict', msgId)
}
}
}
public async stop(): Promise<void> {
log.verbose('PuppetPadchat', 'quit()')
......@@ -453,53 +372,26 @@ export class PuppetPadchat extends Puppet {
return file
}
public async contactRawPayload(id: string): Promise<PadchatContactPayload> {
public async contactRawPayload(id: string): Promise<PadchatContactRawPayload> {
log.verbose('PuppetPadchat', 'contactRawPayload(%s)', id)
const rawPayload = await Misc.retry(async (retry, attempt) => {
log.verbose('PuppetPadchat', 'contactRawPayload(%s) retry() attempt=%d', id, attempt)
let tryRawPayload = this.bridge.cachePadchatContactPayload[id]
if (!tryRawPayload) {
tryRawPayload = await this.bridge.WXGetContactPayload(id)
if (tryRawPayload) {
this.bridge.cachePadchatContactPayload[id] = tryRawPayload
}
}
if (tryRawPayload) {
return tryRawPayload
} else {
return retry(new Error('tryRawPayload empty'))
}
})
if (!rawPayload) {
throw new Error('no rawPayload')
}
const rawPayload = await this.bridge.contactRawPayload(id)
return rawPayload
}
public async contactRawPayloadParser(rawPayload: PadchatContactPayload): Promise<ContactPayload> {
log.verbose('PuppetPadchat', 'contactRawPayloadParser(%s)', rawPayload)
public async contactRawPayloadParser(rawPayload: PadchatContactRawPayload): Promise<ContactPayload> {
log.verbose('PuppetPadchat', 'contactRawPayloadParser(rawPayload.user_name="%s")', rawPayload.user_name)
if (!rawPayload.user_name) {
throw Error('cannot get user_name(wxid)!')
}
const gender = {
0: ContactGender.Unknown,
1: ContactGender.Male,
2: ContactGender.Female,
}
if (/@chatroom$/.test(rawPayload.user_name)) {
if (isRoomId(rawPayload.user_name)) {
throw Error('Room Object instead of Contact!')
}
let contactType = ContactType.Unknown
if (/^gh_/.test(rawPayload.user_name)) {
if (isContactOfficialId(rawPayload.user_name)) {
contactType = ContactType.Official
} else {
contactType = ContactType.Personal
......@@ -507,7 +399,7 @@ export class PuppetPadchat extends Puppet {
const payload: ContactPayload = {
id : rawPayload.user_name,
gender : gender[rawPayload.sex],
gender : rawPayload.sex,
type : contactType,
alias : rawPayload.remark,
avatar : rawPayload.big_head,
......@@ -616,33 +508,60 @@ export class PuppetPadchat extends Puppet {
type = MessageType.Text
}
const payload: MessagePayload = {
const payloadBase = {
id : rawPayload.msg_id,
timestamp : Date.now(),
fromId : rawPayload.from_user,
text : rawPayload.content,
toId : rawPayload.to_user,
// toId : rawPayload.to_user,
type : type,
}
if (/@chatroom$/.test(rawPayload.from_user)) {
payload.roomId = rawPayload.from_user
payload.fromId = rawPayload.content.split(':\n')[0]
payload.text = rawPayload.content.split(':\n')[1]
let roomId: undefined | string = undefined
let toId: undefined | string = undefined
// Msg from room
if (isRoomId(rawPayload.from_user)) {
// update fromId to actual sender instead of the room
payloadBase.fromId = rawPayload.content.split(':\n')[0]
// update the text to actual text of the message
payloadBase.text = rawPayload.content.split(':\n')[1]
if (!payload.roomId || !payload.fromId) {
roomId = rawPayload.from_user
if (!roomId || !payloadBase.fromId) {
throw Error('empty roomId or empty contactId!')
}
// const room = this.Room.load(payload.roomId)
// const contact = this.Contact.load(payload.fromId)
// await room.ready()
// await contact.ready()
}
// Msg to room
if (isRoomId(rawPayload.to_user)) {
roomId = rawPayload.to_user
// TODO: if the message @someone, the toId should set to the mentioned contact id(?)
toId = undefined
} else {
if (!payload.fromId) {
throw Error('empty contactId!')
toId = rawPayload.to_user
}
let payload: MessagePayload
// Two branch is the same code.
// Only for making TypeScript happy
if (toId) {
payload = {
...payloadBase,
toId,
roomId,
}
} else if (roomId) {
payload = {
...payloadBase,
toId,
roomId,
}
// const contact = this.Contact.load(payload.fromId)
// await contact.ready()
} else {
throw new Error('neither toId nor roomId')
}
log.verbose('PuppetPadchat', 'messagePayload(%s)', JSON.stringify(payload))
......@@ -665,7 +584,7 @@ export class PuppetPadchat extends Puppet {
receiver : Receiver,
file : FileBox,
): Promise<void> {
log.verbose('PuppetPadchat', 'messageSend(%s, %s)', receiver, file)
log.verbose('PuppetPadchat', 'messageSend("%s", %s)', JSON.stringify(receiver), file)
const id = receiver.contactId || receiver.roomId
if (!id) {
......@@ -683,7 +602,7 @@ export class PuppetPadchat extends Puppet {
messageId : string,
): Promise<void> {
log.verbose('PuppetPadchat', 'messageForward(%s, %s)',
receiver,
JSON.stringify(receiver),
messageId,
)
const payload = await this.messagePayload(messageId)
......@@ -709,47 +628,27 @@ export class PuppetPadchat extends Puppet {
* Room
*
*/
public async roomRawPayload(id: string): Promise<PadchatRoomPayload> {
public async roomRawPayload(id: string): Promise<PadchatRoomRawPayload> {
log.verbose('PuppetPadchat', 'roomRawPayload(%s)', id)
const rawPayload = await Misc.retry(async (retry, attempt) => {
log.silly('PuppetPadchat', 'roomRawPayload(%s) retry() attempt=%d', id, attempt)
let tryRawPayload = this.bridge.cachePadchatRoomPayload[id]
if (!tryRawPayload) {
tryRawPayload = await this.bridge.WXGetRoomPayload(id)
if (tryRawPayload) {
this.bridge.cachePadchatRoomPayload[id] = tryRawPayload
}
}
if (tryRawPayload) {
return tryRawPayload
} else {
return retry(new Error('tryRawPayload empty'))
}
})
if (!rawPayload) {
throw new Error('no rawPayload')
}
const rawPayload = await this.bridge.roomRawPayload(id)
return rawPayload
}
public async roomRawPayloadParser(rawPayload: PadchatRoomPayload): Promise<RoomPayload> {
log.verbose('PuppetPadchat', 'roomRawPayloadParser(%s)', rawPayload.user_name)
public async roomRawPayloadParser(rawPayload: PadchatRoomRawPayload): Promise<RoomPayload> {
log.verbose('PuppetPadchat', 'roomRawPayloadParser(rawPayload.user_name="%s")', rawPayload.user_name)
// const memberList = (rawPayload.member || [])
// .map(id => this.Contact.load(id))
// await Promise.all(memberList.map(c => c.ready()))
const padchatRoomRawMemberList = (await this.bridge.WXGetChatRoomMember(rawPayload.user_name)).member
const padchatRoomMemberList = (await this.bridge.WXGetChatRoomMember(rawPayload.user_name)).member
const aliasDict = {} as { [id: string]: string | undefined }
if (Array.isArray(padchatRoomRawMemberList)) {
padchatRoomRawMemberList.forEach(
if (Array.isArray(padchatRoomMemberList)) {
padchatRoomMemberList.forEach(
rawMember => {
aliasDict[rawMember.user_name] = rawMember.chatroom_nick_name
},
......@@ -759,7 +658,7 @@ export class PuppetPadchat extends Puppet {
const payload: RoomPayload = {
id : rawPayload.user_name,
topic : rawPayload.nick_name,
memberIdList : rawPayload.member,
memberIdList : rawPayload.member || [],
aliasDict,
}
......@@ -785,6 +684,19 @@ export class PuppetPadchat extends Puppet {
await this.bridge.WXDeleteChatRoomMember(roomId, contactId)
}
public async roomAvatar(roomId: string): Promise<FileBox> {
log.verbose('PuppetPadchat', 'roomAvatar(%s)', roomId)
const payload = await this.roomPayload(roomId)
if (payload.avatar) {
return FileBox.fromUrl(payload.avatar)
}
log.warn('PuppetPadchat', 'roomAvatar() avatar not found, use the chatie default.')
return qrCodeForChatie()
}
public async roomAdd(
roomId : string,
contactId : string,
......
......@@ -45,10 +45,15 @@ import {
config,
log,
Raven,
qrCodeForChatie,
} from '../config'
// import Profile from '../profile'
import Misc from '../misc'
import {
isRoomId,
} from './misc'
import {
Bridge,
Cookie,
......@@ -354,9 +359,9 @@ export class PuppetPuppeteer extends Puppet {
// FIXME: has there any better method to know the room ID?
if (rawPayload.MMIsChatRoom) {
if (/^@@/.test(rawPayload.FromUserName)) {
if (isRoomId(rawPayload.FromUserName)) {
roomId = rawPayload.FromUserName // MMPeerUserName always eq FromUserName ?
} else if (/^@@/.test(rawPayload.ToUserName)) {
} else if (isRoomId(rawPayload.ToUserName)) {
roomId = rawPayload.ToUserName
} else {
throw new Error('parse found a room message, but neither FromUserName nor ToUserName is a room(/^@@/)')
......@@ -368,7 +373,7 @@ export class PuppetPuppeteer extends Puppet {
}
if (rawPayload.ToUserName) {
if (!/^@@/.test(rawPayload.ToUserName)) { // if a message in room without any specific receiver, then it will set to be `undefined`
if (!isRoomId(rawPayload.ToUserName)) { // if a message in room without any specific receiver, then it will set to be `undefined`
toId = rawPayload.ToUserName
}
}
......@@ -1063,6 +1068,18 @@ export class PuppetPuppeteer extends Puppet {
}
}
public async roomAvatar(roomId: string): Promise<FileBox> {
log.verbose('PuppetPuppeteer', 'roomAvatar(%s)', roomId)
const payload = await this.roomPayload(roomId)
if (payload.avatar) {
return FileBox.fromUrl(payload.avatar)
}
log.warn('PuppetPuppeteer', 'roomAvatar() avatar not found, use the chatie default.')
return qrCodeForChatie()
}
public async roomAdd(
roomId : string,
contactId : string,
......
......@@ -52,6 +52,7 @@ import {
import {
log,
qrCodeForChatie,
} from '../config'
import {
......@@ -66,6 +67,10 @@ import {
WebRoomRawPayload,
} from '../puppet-puppeteer/web-schemas'
import {
isRoomId,
} from './misc'
export type PuppetFoodType = 'scan' | 'ding'
export type ScanFoodType = 'scan' | 'login' | 'logout'
......@@ -343,14 +348,14 @@ export class PuppetWechat4u extends Puppet {
public async contactRawPayloadParser(
rawPayload: WebContactRawPayload,
): Promise<ContactPayload> {
log.silly('PuppetPuppeteer', 'contactParseRawPayload(Object.keys(payload).length=%d)',
log.silly('PuppetWechat4u', 'contactParseRawPayload(Object.keys(payload).length=%d)',
Object.keys(rawPayload).length,
)
if (!Object.keys(rawPayload).length) {
log.error('PuppetPuppeteer', 'contactParseRawPayload(Object.keys(payload).length=%d)',
log.error('PuppetWechat4u', 'contactParseRawPayload(Object.keys(payload).length=%d)',
Object.keys(rawPayload).length,
)
log.error('PuppetPuppeteer', 'contactParseRawPayload() got empty rawPayload!')
log.error('PuppetWechat4u', 'contactParseRawPayload() got empty rawPayload!')
throw new Error('empty raw payload')
// return {
// gender: Gender.Unknown,
......@@ -483,17 +488,17 @@ export class PuppetWechat4u extends Puppet {
public async messageRawPayloadParser(
rawPayload: WebMessageRawPayload,
): Promise<MessagePayload> {
log.verbose('PuppetPuppeteer', 'messageRawPayloadParser(%s) @ %s', rawPayload, this)
log.verbose('PuppetWechat4u', 'messageRawPayloadParser(%s) @ %s', rawPayload, this)
console.log(rawPayload)
// console.log(rawPayload)
const id = rawPayload.MsgId
const text: string = rawPayload.Content.replace(/^\n/, '')
const timestamp: number = rawPayload.CreateTime
const filename: undefined | string = this.filename(rawPayload) || undefined
const toId = rawPayload.ToUserName
let roomId : undefined | string = undefined
let fromId = rawPayload.FromUserName
let fromId : string
let roomId : undefined | string
let toId : undefined | string
/**
* Check for the ChatRoom
......@@ -517,7 +522,8 @@ export class PuppetWechat4u extends Puppet {
* MsgType: 1,
* Content: '高阳:\n我是说错误上报的库',,
*/
if (/^@@/.test(fromId)) {
if (isRoomId(rawPayload.FromUserName)) {
// set room id
roomId = rawPayload.FromUserName
const header = rawPayload.Content.split('\n')[0]
......@@ -528,6 +534,7 @@ export class PuppetWechat4u extends Puppet {
const idOrName = matches[1]
if (this.wechat4u.contacts[idOrName]) {
// set from id from contact id in the message content
fromId = matches[1]
} else {
const memberContactList = await this.roomMemberSearch(roomId, idOrName)
......@@ -537,8 +544,18 @@ export class PuppetWechat4u extends Puppet {
if (memberContactList.length > 1) {
log.warn('PuppetWechat4u', 'messageRawPayloadParser() found more than one possible fromId, use the first one.')
}
// set from id from contact name in the message content
fromId = memberContactList[0]
}
} else {
fromId = rawPayload.FromUserName
}
if (isRoomId(rawPayload.ToUserName)) {
// The message is to room only, no specific receiver.
// TODO: if message is mention someone, toId should set to the mentioned contact(?)
} else {
toId = rawPayload.ToUserName
}
const type: MessageType = this.messageTypeFromWeb(rawPayload.MsgType)
......@@ -682,7 +699,7 @@ export class PuppetWechat4u extends Puppet {
public async roomRawPayloadParser(
rawPayload: WebRoomRawPayload,
): Promise<RoomPayload> {
log.verbose('PuppetPuppeteer', 'roomRawPayloadParser(%s)', rawPayload)
log.verbose('PuppetWechat4u', 'roomRawPayloadParser(%s)', rawPayload)
const id = rawPayload.UserName
const rawMemberList = rawPayload.MemberList || []
......@@ -727,6 +744,19 @@ export class PuppetWechat4u extends Puppet {
}
public async roomAvatar(roomId: string): Promise<FileBox> {
log.verbose('PuppetWechat4u', 'roomAvatar(%s)', roomId)
const payload = await this.roomPayload(roomId)
if (payload.avatar) {
// FIXME: set http headers with cookies
return FileBox.fromUrl(payload.avatar)
}
log.warn('PuppetWechat4u', 'roomAvatar() avatar not found, use the chatie default.')
return qrCodeForChatie()
}
public async roomAdd(
roomId : string,
contactId : string,
......
......@@ -87,6 +87,7 @@ class PuppetTest extends Puppet {
*
*/
public async roomAdd(roomId: string, contactId: string) : Promise<void> { return {roomId, contactId} as any }
public async roomAvatar(roomId: string) : Promise<FileBox> { return {roomId} as any }
public async roomCreate(contactIdList: string[], topic?: string) : Promise<string> { return {contactIdList, topic} as any }
public async roomDel(roomId: string, contactId: string) : Promise<void> { return {roomId, contactId} as any }
public async roomQuit(roomId: string) : Promise<void> { return {roomId} as any }
......
......@@ -584,6 +584,7 @@ export abstract class Puppet extends EventEmitter implements Sayable {
*
*/
public abstract async roomAdd(roomId: string, contactId: string) : Promise<void>
public abstract async roomAvatar(roomId: string) : Promise<FileBox>
public abstract async roomCreate(contactIdList: string[], topic?: string) : Promise<string>
public abstract async roomDel(roomId: string, contactId: string) : Promise<void>
public abstract async roomQuit(roomId: string) : Promise<void>
......
......@@ -20,13 +20,13 @@ export interface MessagePayloadBase {
}
export interface MessagePayloadRoom {
roomId : null | string,
toId? : null | string, // if to is not set, then room must be set
roomId : string,
toId? : string, // if to is not set, then room must be set
}
export interface MessagePayloadTo {
roomId? : null | string,
toId : null | string, // if to is not set, then room must be set
roomId? : string,
toId : string, // if to is not set, then room must be set
}
export type MessagePayload = MessagePayloadBase & (MessagePayloadRoom | MessagePayloadTo)
......@@ -12,6 +12,7 @@ export interface RoomPayload {
id : string,
topic : string,
avatar? : string,
memberIdList : string[],
ownerId? : string,
aliasDict : { [contactId: string]: string | undefined } // room alias
......@@ -22,4 +23,4 @@ export interface RoomPayload {
}
export type RoomPayloadFilterFunction = (payload: RoomPayload) => boolean
export type RoomPayloadFilterFactory = (query: RoomQueryFilter) => RoomPayloadFilterFunction
export type RoomPayloadFilterFactory = (query: RoomQueryFilter) => RoomPayloadFilterFunction
......@@ -797,6 +797,12 @@ export class Room extends Accessory implements Sayable {
return owner
}
public async avatar(): Promise<FileBox> {
log.verbose('Room', 'avatar()')
return this.puppet.roomAvatar(this.id)
}
}
export default Room
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册