Skip to content

Commit

Permalink
feat: 封装主动消息接口 修复转换绝对路径参数错误
Browse files Browse the repository at this point in the history
  • Loading branch information
Lain. committed May 16, 2024
1 parent 1b4a1d8 commit 168e262
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 114 deletions.
37 changes: 34 additions & 3 deletions lib/Renderer/Renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,45 @@ class Renderer {
* @param {{
* id: string,
* name: string,
* data: any
* }}
* data: {
* tplFile: string,
* saveId?: string,
* imgType?: "jpeg" | "png",
* quality?: number,
* omitBackground?: boolean,
* path?: string,
* multiPage?: boolean,
* multiPageHeight?: number,
* setViewport?: { width: number, height: number, deviceScaleFactor: number },
* pageGotoParams?: any
* }
* }} options
* @param options.data.tplFile 模板路径或http地址,必传
* @param options.data.saveId 生成html名称,为空name代替
* @param options.data.imgType screenshot参数,生成图片类型:jpeg,png
* @param options.data.quality screenshot参数,图片质量 0-100,jpeg是可传,默认90
* @param options.data.omitBackground screenshot参数,隐藏默认的白色背景,背景透明。默认不透明
* @param options.data.path screenshot参数,截图保存路径。截图图片类型将从文件扩展名推断出来。如果是相对路径,则从当前路径解析。如果没有指定路径,图片将不会保存到硬盘。
* @param options.data.multiPage 是否分页截图,默认false
* @param options.data.multiPageHeight 分页状态下页面高度,默认4000
* @param options.data.setViewport 页面视窗大小和设备像素比 默认1920*1080、1
* @param options.data.pageGotoParams 页面goto时的参数
* @return {Promise<string|string[]>} - 分片返回数组,单页返回base64://
*/
render ({ id, name, data }) {
async render ({ id, name, data }) {
const app = this.App(id)
if (!app) throw new Error(`[调用渲染器失败] 未找到渲染器:${id}`)
return app.render(name, data)
}

/**
* 快速渲染
* @param {string} file html路径或者http地址
*/
async renderHtml (file) {
const app = this.App()
return app.render('renderHtml', { tplFile: file })
}
}

export default new Renderer()
74 changes: 72 additions & 2 deletions lib/bot/bot.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import '../adapter/kritor/index.js'
import { EventEmitter } from 'events'
import logger from '../config/log.js'
import message from '../event/message.js'
import notice from '../event/notice.js'
import request from '../event/request.js'
import common from '../common/common.js'
import { segment, common, logger } from '#Karin'

class Bot extends EventEmitter {
constructor () {
Expand Down Expand Up @@ -41,6 +40,77 @@ class Bot extends EventEmitter {
logger.mark(`[适配器][WebSocket][注册][${data.url}]`)
})
}

/**
* 获取bot
* @param {string} [id] - Bot的id 未传入则返回第一个Bot
* @returns {KarinAdapter}
*/
getBot (id = '') {
if (id === '') {
/** 如果没有任何Bot 产生错误 */
if (Object.keys(this.adapter).length === 0) {
throw new Error('找不到Bot,请先注册Bot')
}
return Object.values(this.adapter)[0]
}
return this.adapter[id]
}

/**
* 发送主动消息
* @param {string} [id] - Bot的id
* @param {{
* scene: 'private' | 'group',
* peer: string,
* }} contact - 目标信息
* @param {string} contact.scene - 场景
* @param {string} contact.peer - 目标id
* @param {string} elements - 消息内容
* @returns {Promise<{message_id}>}
*/
async sendMsg (id, contact, elements, options = { recallMsg: 0, button: false, retry_count: 1 }) {
/** 将msg格式化为数组 */
if (!Array.isArray(elements)) {
elements = [elements]
}
elements = elements.map(element => {
if (typeof element == 'string') {
return segment.text(element)
}
return element
})
let { recallMsg, button, retry_count } = options
logger.debug(button) // 后续处理
const bot = this.getBot(id)

/** 先发 提升速度 */
let msgRes = bot.SendMessage(contact, elements, retry_count)

const reply_log = common.reply_log(elements)
const self_id = bot.account.uid || bot.account.uin
if (contact.scene === 'group') {
logger.info(common.logger(self_id, `${logger.green(`Send Proactive Group ${contact.peer}: `)}${reply_log}`))
} else {
logger.info(common.logger(self_id, `${logger.green(`Send Proactive private ${contact.peer}: `)}${reply_log}`))
}

try {
this.emit('karin:count:send', 1)
/** 取结果 */
msgRes = await msgRes
logger.debug(common.logger(self_id, `回复消息结果:${JSON.stringify(msgRes, null, 2)}`))
} catch (err) {
logger.error(common.logger(self_id, `回复消息失败:${reply_log}`))
logger.error(common.logger(self_id), err)
}

/** 快速撤回 */
if (bot.RecallMessage && recallMsg > 0 && msgRes?.message_id) {
setTimeout(() => bot.RecallMessage(null, msgRes.message_id), recallMsg * 1000)
}
return msgRes
}
}

export default new Bot()
114 changes: 105 additions & 9 deletions lib/common/common.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { logger } from '#Karin'
import axios from 'axios'
import fs from 'fs'
import path from 'path'
import { pipeline, Readable } from 'stream'
import axios from 'axios'
import lodash from 'lodash'
import { promisify } from 'util'
import segment from '../utils/segment.js'
import { logger, segment } from '#Karin'
import { pipeline, Readable } from 'stream'

class Common {
/**
Expand All @@ -25,10 +25,7 @@ class Common {
try {
this.mkdir(path.dirname(savePath))
logger.debug(`[下载文件] ${fileUrl}`)
const response = await axios.get(fileUrl, {
...param,
responseType: 'stream' // 设置响应类型为流
})
const response = await axios.get(fileUrl, { ...param, responseType: 'stream' })
const streamPipeline = promisify(pipeline)
await streamPipeline(response.data, fs.createWriteStream(savePath))
return true
Expand Down Expand Up @@ -73,7 +70,7 @@ class Common {
*/
absPath (_path) {
_path = _path.replace('file://', '')
return path.resolve(_path)
return path.resolve(_path).replace(/\\/g, '/')
}

/**
Expand Down Expand Up @@ -203,6 +200,105 @@ class Common {
return segment.node(fakeUin, fakeNick, element)
})
}

/** 构建消息体日志 */
reply_log (message) {
const logs = []

for (let val of message) {
switch (val.type) {
case 'text':
logs.push(val.text)
break
case 'face':
logs.push(`[face:${val.id}]`)
break
case 'video':
case 'image':
case 'voice':
case 'record':
case 'file': {
let file
if (Buffer.isBuffer(val.file)) {
file = 'Buffer://...'
} else if (/^http|^file/.test(val.file)) {
file = val.file
} else {
file = 'base64://...'
}
logs.push(`[${val.type}:${file}]`)
break
}
case 'at':
logs.push(`[at:${val.uid}]`)
break
case 'rps':
logs.push(`[rps:${val.id}]`)
break
case 'dice':
logs.push(`[dice:${val.id}]`)
break
case 'shake':
logs.push('[shake:窗口抖动]')
break
case 'poke':
logs.push(`[poke:${val.id}]`)
break
case 'anonymous':
logs.push(`[anonymous:${val.ignore}]`)
break
case 'share':
logs.push(`[share:${JSON.stringify(val)}]`)
break
case 'contact':
logs.push(`[contact:${JSON.stringify(val)}]`)
break
case 'location':
logs.push(`[location:${JSON.stringify(val)}]`)
break
case 'music':
logs.push(`[music:${JSON.stringify(val)}]`)
break
case 'reply':
logs.push(`[reply:${val.message_id}]`)
break
case 'forward':
logs.push(`[forward:${val.id}]`)
break
case 'node':
logs.push(`[node:${JSON.stringify(val)}]`)
break
case 'xml':
logs.push(`[xml:${val.data}]`)
break
case 'json':
logs.push(`[json:${val.data}]`)
break
case 'markdown': {
if (val.content) {
logs.push(`[markdown:${val.content}]`)
} else {
let content = { id: val.custom_template_id }
for (let v of val.params) content[v.key] = v.values[0]
logs.push(`[markdown:${JSON.stringify(content)}]`)
}
break
}
case 'rows': {
const rows = []
for (let v of val.rows) rows.push(v.log)
logs.push(`[rows:${JSON.stringify(rows)}]`)
break
}
case 'button':
logs.push(`[button:${val.log}]`)
break
default:
logs.push(`[未知:${JSON.stringify(val)}]`)
}
}
return lodash.truncate(logs.join(''), { length: 500 })
}
}

export default new Common()
100 changes: 1 addition & 99 deletions lib/event/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export default class Event {

/** 先发 提升速度 */
let msgRes = e.replyCallback(elements, retry_count)
const reply_log = this.reply_log(elements)
const reply_log = common.reply_log(elements)

if (e.isGroup) {
logger.info(common.logger(e.self_id, `${logger.green(`Send Group ${e.group_id}: `)}${reply_log}`))
Expand All @@ -149,102 +149,4 @@ export default class Event {
}
Object.freeze(e.reply)
}

reply_log (message) {
const logs = []

for (let val of message) {
switch (val.type) {
case 'text':
logs.push(val.text)
break
case 'face':
logs.push(`[face:${val.id}]`)
break
case 'video':
case 'image':
case 'voice':
case 'record':
case 'file': {
let file
if (Buffer.isBuffer(val.file)) {
file = 'Buffer://...'
} else if (/^http|^file/.test(val.file)) {
file = val.file
} else {
file = 'base64://...'
}
logs.push(`[${val.type}:${file}]`)
break
}
case 'at':
logs.push(`[at:${val.uid}]`)
break
case 'rps':
logs.push(`[rps:${val.id}]`)
break
case 'dice':
logs.push(`[dice:${val.id}]`)
break
case 'shake':
logs.push('[shake:窗口抖动]')
break
case 'poke':
logs.push(`[poke:${val.id}]`)
break
case 'anonymous':
logs.push(`[anonymous:${val.ignore}]`)
break
case 'share':
logs.push(`[share:${JSON.stringify(val)}]`)
break
case 'contact':
logs.push(`[contact:${JSON.stringify(val)}]`)
break
case 'location':
logs.push(`[location:${JSON.stringify(val)}]`)
break
case 'music':
logs.push(`[music:${JSON.stringify(val)}]`)
break
case 'reply':
logs.push(`[reply:${val.message_id}]`)
break
case 'forward':
logs.push(`[forward:${val.id}]`)
break
case 'node':
logs.push(`[node:${JSON.stringify(val)}]`)
break
case 'xml':
logs.push(`[xml:${val.data}]`)
break
case 'json':
logs.push(`[json:${val.data}]`)
break
case 'markdown': {
if (val.content) {
logs.push(`[markdown:${val.content}]`)
} else {
let content = { id: val.custom_template_id }
for (let v of val.params) content[v.key] = v.values[0]
logs.push(`[markdown:${JSON.stringify(content)}]`)
}
break
}
case 'rows': {
const rows = []
for (let v of val.rows) rows.push(v.log)
logs.push(`[rows:${JSON.stringify(rows)}]`)
break
}
case 'button':
logs.push(`[button:${val.log}]`)
break
default:
logs.push(`[未知:${JSON.stringify(val)}]`)
}
}
return lodash.truncate(logs.join(''), { length: 500 })
}
}
Loading

0 comments on commit 168e262

Please sign in to comment.