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 4 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
138 changes: 138 additions & 0 deletions examples/echo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/**
* Wechaty - https://github.com/chatie/wechaty
*
* @copyright 2016-2018 Huan LI <[email protected]>
*
* 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.
*
*/
import {
EventErrorPayload,
EventLoginPayload,
EventLogoutPayload,
EventMessagePayload,
EventScanPayload,
FileBox,
MessageType
} from 'wechaty-puppet'

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

/**
*
* 1. Declare your Bot!
*
*/
const puppet = new PuppetOA();

// 2. Register event handlers for Bot
puppet
.on('logout', onLogout)
.on('login', onLogin)
.on('scan', onScan)
.on('error', onError)
.on('message', onMessage)

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

/**
*
* 4. You are all set. ;-]
*
*/

/**
*
* 5. Define Event Handler Functions for:
* `scan`, `login`, `logout`, `error`, and `message`
*
*/
function onScan(payload: EventScanPayload) {
if (payload.qrcode) {
// Generate a QR Code online via
// http://goqr.me/api/doc/create-qr-code/
const qrcodeImageUrl = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/wechaty/wechaty-getting-started/blob/7c3ebd90f4b8cb7f22bf49ca419debbdde275437/examples/ding-dong-bot.ts#L21-L24

Please use our wechaty.js.org to generate the online qrcode. because the qrserver.com is too slow and always out of service.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the comment below. TL;DR I deleted it in the newest commit.

'https://api.qrserver.com/v1/create-qr-code/?data=',
encodeURIComponent(payload.qrcode),
].join('')
console.info(`[${payload.status}] ${qrcodeImageUrl}\nScan QR Code above to log in: `)
} else {
console.info(`[${payload.status}]`)
}
}

function onLogin(payload: EventLoginPayload) {
console.info(`${payload.contactId} login`)
puppet.messageSendText(payload.contactId, 'Wechaty login').catch(console.error)
zhangfand marked this conversation as resolved.
Show resolved Hide resolved
}

function onLogout(payload: EventLogoutPayload) {
console.info(`${payload.contactId} logouted`)
}

function onError(payload: EventErrorPayload) {
console.error('Bot error:', payload.data)
/*
if (bot.logonoff()) {
bot.say('Wechaty error: ' + e.message).catch(console.error)
}
*/
}

/**
*
* 6. The most important handler is for:
* dealing with Messages.
*
*/
async function onMessage(event: EventMessagePayload) {
const payload = await puppet.messagePayload(event.messageId)
console.info('onMessage:', JSON.stringify(payload))
switch (payload.type) {
case MessageType.Text:
await puppet.messageSendText(payload.fromId!, payload.text!);
break
case MessageType.Audio:
// Forward the received audio content back to the user.
// HACK(zhangfan): We use .filename to pass url to the the message audio content.
// Note that setting name in .fromUrl is necessary because .messageSend relies on it to
// infer the Content-Type to the send audio file back to users.
// TODO(zhangfan): This is fragile. Fix it.
let fileBox = FileBox.fromUrl(payload.filename!, "message.amr")
console.log(`file name: ${fileBox.name}; mimetype: ${fileBox.mimeType}`)
await puppet.messageSend(payload.fromId!, fileBox, "voice");
break
default:
console.info(`onMessage: unsupported message type: ${payload.type}`)
}
}


/**
*
* 7. Output the Welcome Message
*
*/
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.46",
"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
7 changes: 5 additions & 2 deletions src/puppet-oa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,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 All @@ -488,7 +491,7 @@ class PuppetOA extends Puppet {
return payload
}

private async messageSend (
public async messageSend (
conversationId: string,
something: string | FileBox, // | Attachment
mediatype: OAMediaType = 'image'
Expand Down Expand Up @@ -539,7 +542,7 @@ class PuppetOA extends Puppet {
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