Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Receive and send audio messages #21

Merged
merged 9 commits into from
Feb 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions examples/echo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {EventErrorPayload, EventMessagePayload, MessageType} from 'wechaty-puppet'

import {PuppetOA} from '../src/mod'

/**
*
* 1. Declare your Bot!
*
*/
const puppet = new PuppetOA({
port: 80
});

// 2. Register event handlers for Bot
puppet
.on('error', onError)
.on('message', onMessage)


function onError (payload: EventErrorPayload) {
console.error('Bot error:', payload.data)
}

async function onMessage (event: EventMessagePayload) {
const payload = await puppet.messagePayload(event.messageId);
switch (payload.type) {
case MessageType.Text:
return puppet.messageSendText(payload.fromId!, payload.text!)
case MessageType.Image:
case MessageType.Audio:
const fileBox = await puppet.messageFile(event.messageId)
return puppet.messageSendFile(payload.fromId!, fileBox)
default:
return puppet.messageSendText(payload.fromId!, `unsupported type: ${MessageType[payload.type]}`)
}
}

// 3. Start the bot!
puppet.start()
.catch(async e => {
console.error('Bot start() fail:', e)
await puppet.stop()
process.exit(-1)
})

const welcome = `
Puppet Version: ${puppet.version()}

Please wait... I'm trying to login in...

`
console.info(welcome)
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "wechaty-puppet-official-account",
"version": "0.5.43",
"version": "0.5.44",
"description": "Wechaty Puppet for WeChat Official Accounts",
"directories": {
"test": "tests"
Expand All @@ -16,6 +16,7 @@
"dist": "npm run clean && tsc",
"pack": "npm pack",
"start": "ts-node examples/ding-dong-bot.ts",
"start:echo": "ts-node examples/echo.ts",
"lint": "npm run lint:es && npm run lint:ts && npm run lint:md",
"lint:md": "markdownlint README.md",
"lint:ts": "tsc --noEmit",
Expand Down
16 changes: 16 additions & 0 deletions src/official-account/official-account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,22 @@ class OfficialAccount extends EventEmitter {
return tagNames
}

async getAudioUrl (mediaId: string): Promise<string> {
// NOTE(zhangfan): As per Wechat API documentation (https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Get_temporary_materials.html),
// /media/get behavior is not documented if the retrieved media content is an audio.
// From real world testing, we learned it returns the audio content directly.
// This is subject to changes.
//
// Here is an excerpt of the response header seen in tests:
// "connection": "close",
// "cache-control": "no-cache, must-revalidate",
// "date": "Thu, 04 Feb 2021 08:51:34 GMT",
// "content-disposition": "attachment; filename=\"Nz30tHrSoMhGf7FcOmddXuCIud-TP7Z71Yci6nOgYtGnLTkoD9V4yisRlj75Ghs7.amr\"",
// "content-type": "audio/amr",
// "content-length": "8630"
return `https://api.weixin.qq.com/cgi-bin/media/get?access_token=${this.accessToken}&media_id=${mediaId}`
}

async setMemberRemark (openid: string, remark: string): Promise<void> {
log.verbose('OfficialAccount', 'setMemberRemark(%s)', openid)

Expand Down
1 change: 1 addition & 0 deletions src/official-account/webhook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ class Webhook extends WebhookEventEmitter {
const knownTypeList = [
'text',
'image',
'voice',
]

this.userOpen[payload.FromUserName] = true
Expand Down
39 changes: 26 additions & 13 deletions src/puppet-oa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,18 +416,25 @@ class PuppetOA extends Puppet {
public async messageFile (id: string): Promise<FileBox> {
log.verbose('PuppetOA', 'messageFile(%s)', id)

const payload : MessagePayload = await this.messagePayload(id)
let fileBox : FileBox
const payload: MessagePayload = await this.messagePayload(id)

if (payload.type === MessageType.Image) {
if (!payload.filename) {
throw Error(`image message type must have filename file. <${payload}>`)
}
fileBox = FileBox.fromUrl(payload.filename)
} else {
throw Error('can"t get file from the message')
switch (payload.type) {
case MessageType.Image:
if (!payload.filename) {
throw Error(`Image message must have filename. <${payload}>`)
}
return FileBox.fromUrl(payload.filename)
case MessageType.Audio:
if (!payload.filename) {
throw Error(`Audio message must have filename. <${payload}>`)
}
// payload.filename is an URL to the audio file. The name of the file is not in the URL.
// Setting a filename with expected extension is necessary for inference of mime type in
// FileBox.
return FileBox.fromUrl(payload.filename, 'message.amr')
default:
throw Error('can"t get file from the message')
}
return fileBox
}

public async messageUrl (messageId: string) : Promise<UrlLinkPayload> {
Expand Down Expand Up @@ -473,6 +480,9 @@ class PuppetOA extends Puppet {
payload.type = MessageType.Location
} else if (rawPayload.MsgType === 'text') {
payload.text = rawPayload.Content
} else if (rawPayload.MsgType === 'voice') {
payload.type = MessageType.Audio
payload.filename = await this.oa?.getAudioUrl(rawPayload.MediaId!)
}
return payload
}
Expand Down Expand Up @@ -534,12 +544,15 @@ class PuppetOA extends Puppet {
): Promise<string> {
let msgtype: OAMediaType
switch (file.mimeType) {
case 'image/jpeg': msgtype = 'image'
case 'image/jpeg':
msgtype = 'image'
break
case 'audio/mpeg': msgtype = 'voice'
case 'audio/amr':
case 'audio/mpeg':
msgtype = 'voice'
break
default:
throw new Error('Media type not supported')
throw new Error(`unsupported media type: ${file.mimeType}`)
}
return this.messageSend(conversationId, file, msgtype)
}
Expand Down