
github仓库请点击
视频教程请点击
本仓库旨在为学习 mixin messenger 的开发。
学习完,将会熟悉
(小助理机器人ID: 7000101422)主要是提供消息触发的服务机制。支持如下功能:
打开终端初始化项目
mkdir mixin-course-assistant cd mixin-course-assistant npm init -y npm install mixin-node-sdk
{
"pin": "",
"client_id": "",
"session_id": "",
"pin_token": "",
"private_key": ""
}
在根目录下创建 index.js
由于是消息服务,所以我们得连接 Mixin 的 websocket 用于接受消息
const { BlazeClient } = require('mixin-node-sdk')
const config = require('./config.json')
// 这里的 parse:true 是sdk来解析 base64 的消息
// 这里的 syncAck:true 是sdk来帮我们 ack 消息
const client = new BlazeClient(config, { parse: true, syncAck: true })
client.loopBlaze({
onMessage(msg) {
console.log(msg)
},
// 我们不处理 ack 的消息,所以放一个空函数,这样就不会走到 onMessage 的逻辑里
onAckReceipt() {
}
})
node index.js
2. 编写 user_id 和 identity_number 的用户查询打开手机跟自己的机器人发一条消息。看终端是否能打印出发送的消息。如果能,说明 websocket 连接成功。
const QRCode = require('qrcode')
async function handleUser({ data, user_id }) {
const user = await client.readUser(data) // 根据用户的输入来查询 user
if (user && user.user_id) { // 走到这里说明 user 已经查询到了。
const transferAction = `mixin://transfer/${user.user_id}` // 定义一个 transfer 的 schema 后边要用,
// 这里同时发送3-4条消息所以使用 promise.all
Promise.all([
client.sendContactMsg( // 给用户发送联系人卡片
user_id,
{ user_id: user.user_id }
),
client.sendAppButtonMsg( // 给用户发送转账的 button
user_id,
[
{
label: `Transfer to ${user.full_name}`,
action: transferAction,
color: '#000'
}
]
),
new Promise(resolve => { // 给用户发送转账的二维码
QRCode.toBuffer( // 将 transferAction -> jpeg 的 buf
transferAction,
async (err, buf) => {
const { attachment_id } = await client.uploadFile(buf) // 上传 buf
await client.sendImageMsg(user_id, { // 发送图片消息
attachment_id, // 资源id
mime_type: "image/jpeg",
width: 300,
height: 300,
size: buf.length,
thumbnail: Buffer.from(buf).toString('base64'), // 封面, buf 的base64
})
resolve()
})
}),
new Promise(async resolve => { // 如果用户查询的是 identity_number 的话,则给用户发送 user_id
if (String(data) === user.identity_number)
await client.sendTextMsg(user_id, user.user_id)
resolve()
})
])
return true
}
return false
}
//...
client.loopBlaze({
onMessage(msg) {
// 改造一下这里
handleUser(msg)
},
onAckReceipt() {
}
})
//...
3. 编写 asset_id 和 symbol 的资产查询
- 使用 mixin 移动端给机器人发送 30265 ,看机器人是否会返回 4 条消息。
- 再把返回的 user_id 发送给机器人,看机器人是否会返回跟第一步相同的 3 条消息。
- 如果是则说明测试通过。
const { validate: isUUID } = require('uuid') // mixin-node-sdk 包里有 uuid 了,所以可以直接引入
async function handleAsset({ data, user_id }) {
if (isUUID(data)) {
// 说明有可能是 asset_id
const asset = await readNetworkAsset(data)
if (asset && asset.asset_id) {
// 说明是 asset_id,且已经查询到了
await client.sendPostMsg(user_id, '```jsonn' +
JSON.stringify(asset, null, 2) +
'n```')// 发送 json 格式的 markdown
return true
}
} else {
// 说明有可能是 symbol
const assets = await searchNetworkAsset(data)
if (assets.length > 0) {
// 说明是 symbol,且已经查询到了
await Promise.all([
client.sendPostMsg(user_id, '```jsonn' +
JSON.stringify(assets, null, 2) +
'n```'), // 返回 json 格式的 markdown
client.sendTextMsg(user_id, assets[0].asset_id) // 返回 查询到的第一个 asset_id
])
return true
}
}
return false
}
//...
client.loopBlaze({
onMessage(msg) {
// 改造一下这里
handleAsset(msg)
},
onAckReceipt() {
}
})
//...
4. 编写 /claim 向机器人签到并领取 1 CNB
- 使用 mixin 移动端给机器人发送 btc ,看机器人是否会返回 2 条消息。
- 再把返回的 asset_id 发送给机器人,看机器人是否会返回 1 条 btc 的资产相关消息。
- 如果是则说明测试通过。
const cnb_asset_id = '965e5c6e-434c-3fa9-b780-c50f43cd955c' // 预先查询到了 cnb 的 asset_id 备用。
async function handleClaim({data, user_id}) {
const trace_id = client.uniqueConversationID(
user_id + client.keystore.client_id,
new Date().toDateString()
) // 用户这个用户今天唯一的 trace_id
const transfer = await client.readTransfer(trace_id) // 查询今天是否领取过
if (transfer && transfer.snapshot_id) {
// 已经领取
await client.sendMessageText(
user_id,
'您今日已领取,请明日再来。'
)
} else {
// 否则的话给用户转 1 cnb
await client.transfer({
trace_id,
asset_id: cnb_asset_id,
amount: '1',
opponent_id: user_id,
})
}
}
//...
client.loopBlaze({
onMessage(msg) {
// 改造一下这里
handleClaim(msg)
},
onAckReceipt() {
}
})
//...
5. 编写 /donate 向机器人获取机器人转账地址
- 使用 mixin 移动端给机器人发送 /claim ,看机器人是否转账 1 cnb
- 再发送一次 /claim ,看机器人是否回复 您今日已领取,请明日再来。
- 如果是则说明测试通过。
// 1. 直接回复 donate 的按钮
async function handleDonate({user_id}) {
client.sendAppButtonMsg( // 给用户发送 donate 的 button
user_id,
[
{
label: `点击向我捐赠`,
action: `mixin://transfer/${client.keystore.client_id}`,
color: '#000'
}
]
)
}
// 2. 在 loopBlaze 的地方,需要监听收到转账的消息。
client.loopBlaze({
...,
async onTransfer({ data, user_id }) {
const { amount, asset_id } = data
const { symbol } = await client.readAsset(asset_id)
client.sendMessageText(user_id, `打赏的 ${amount} ${symbol} 已收到,感谢您的支持。`)
}
})
6. 帮助信息 + 2 个交互按钮
const helpMsg = `
1. 支持用户查询,请发送 user_id | identity_number
2. 支持资产查询,请发送 asset_id | symbol
3. 支持每日领取 1cnb,请发送 /claim 或点击签到
4. 支持打赏,请发送 /donate 或点击打赏
`
async function sendHelpMsgWithInfo(user_id, info) { // 发送帮助消息
await Promise.all([
client.sendTextMsg(user_id, info + helpMsg),
client.sendAppButtonMsg(user_id, [
{ label: "签到", action: "input:/claim", color: "#000" },
{ label: "打赏", action: "input:/donate", color: "#000" }
])
])
return true
}
7. 组装逻辑
async function handleMsg(msg) {
const { data, category, user_id } = msg
if (category !== 'PLAIN_TEXT')
return sendHelpMsgWithInfo(user_id, "仅支持文本消息。")
if (data === '/claim') return handleClaim(msg) // 处理 /claim 消息
if (data === '/donate') return handleDonate(msg) // 处理 /donate 消息
if (isUUID(data)) { // 处理 uuid 消息
const res = await Promise.all([
handleUser(msg),
handleAsset(msg)
])
return res.some(v => v)
}
if (isNaN(Number(data))) return handleAsset(msg) // 处理 symbol -> assets 的消息
else return handleUser(msg) // 处理 identity_number -> user 的消息
}
//...
client.loopBlaze({
// 改造一下这里
async onMessage(msg) {
const isHandle = await handleMsg(msg)
if(!isHandle) return sendHelpMsgWithInfo(msg.user_id, "指令输入不正确。")
},
onAckReceipt() {
},
onTransfer() { // 这里可以再忽略一下转账消息
}
})
//...
可以把上述的测试全部再走一遍。
全部通过,说明测试成功。