@xud6/cq-websocket
Version:
A Node SDK for developing QQ chatbots based on WebSocket, which is depending on CoolQ and CQHTTP API plugin.
127 lines (117 loc) • 2.9 kB
JavaScript
const isSupportedTag = require('./isSupportedTag')
const CQTag = require('./CQTag')
const {
CQAt,
CQAnonymous,
CQBFace,
CQCustomMusic,
CQDice,
CQEmoji,
CQFace,
CQImage,
CQMusic,
CQRecord,
CQRPS,
CQSFace,
CQShake,
CQShare,
CQText
} = require('./models')
const CQTAGS_EXTRACTOR = /\[CQ[^\]]*\]/g
const CQTAG_ANALYSOR = /\[CQ:([a-z]+?)(?:,((,?[a-zA-Z0-9-_.]+=[^,[\]]*)*))?\]/
function parseData (dataStr = '') {
return dataStr
? dataStr.split(',')
.map(opt => opt.split('='))
.reduce((data, [ k, v ]) => {
data[k] = v
return data
}, {})
: null
}
function castCQTag (cqtag) {
let proto
switch (cqtag._type) {
case 'anonymous':
proto = CQAnonymous.prototype
break
case 'at':
proto = CQAt.prototype
break
case 'bface':
proto = CQBFace.prototype
break
case 'music':
proto = cqtag.data.type === 'custom'
? CQCustomMusic.prototype : CQMusic.prototype
break
case 'dice':
proto = CQDice.prototype
break
case 'emoji':
proto = CQEmoji.prototype
break
case 'face':
proto = CQFace.prototype
break
case 'image':
proto = CQImage.prototype
break
case 'record':
proto = CQRecord.prototype
break
case 'rps':
proto = CQRPS.prototype
break
case 'sface':
proto = CQSFace.prototype
break
case 'shake':
proto = CQShake.prototype
break
case 'share':
proto = CQShare.prototype
break
case 'text':
proto = CQText.prototype
}
return Object.setPrototypeOf(cqtag, proto).coerce()
}
/**
* @param {string|any[]} message
*/
module.exports = function parse (message) {
if (typeof message === 'string') {
let textTagScanner = 0
const nonTextTags = (message.match(CQTAGS_EXTRACTOR) || [])
.map(_tag => _tag.match(CQTAG_ANALYSOR))
.filter(_tag => _tag && isSupportedTag(_tag[1]))
.map(_tag => new CQTag(_tag[1], parseData(_tag[2])))
.map(castCQTag)
// insert text tags into appropriate position
const ret = nonTextTags.reduce((tags, cqtag, index) => {
const cqtagStr = cqtag.toString()
const cqtagIndex = message.indexOf(cqtagStr)
if (cqtagIndex !== textTagScanner) {
const text = message.substring(textTagScanner, cqtagIndex)
tags.push(new CQText(text))
}
tags.push(cqtag)
textTagScanner = cqtagIndex + cqtagStr.length
return tags
}, [])
if (textTagScanner < message.length) {
// there is still text
const text = message.substring(textTagScanner)
ret.push(new CQText(text))
}
return ret
}
if (Array.isArray(message)) {
return message
.filter(_tag => isSupportedTag(_tag.type))
.map(_tag => new CQTag(_tag.type, _tag.data))
.map(castCQTag)
}
return []
}