diff --git a/src/common/config.ts b/src/common/config.ts
index 6ddf484..fc44c13 100644
--- a/src/common/config.ts
+++ b/src/common/config.ts
@@ -1,9 +1,26 @@
 import fs from 'node:fs'
 import { Config, OB11Config } from './types'
-import { mergeNewProperties } from './utils/helper'
 import path from 'node:path'
 import { selfInfo, DATA_DIR } from './globalVars'
 
+// 在保证老对象已有的属性不变化的情况下将新对象的属性复制到老对象
+function mergeNewProperties(newObj: any, oldObj: any) {
+  Object.keys(newObj).forEach((key) => {
+    // 如果老对象不存在当前属性,则直接复制
+    if (!oldObj.hasOwnProperty(key)) {
+      oldObj[key] = newObj[key]
+    } else {
+      // 如果老对象和新对象的当前属性都是对象,则递归合并
+      if (typeof oldObj[key] === 'object' && typeof newObj[key] === 'object') {
+        mergeNewProperties(newObj[key], oldObj[key])
+      } else if (typeof oldObj[key] === 'object' || typeof newObj[key] === 'object') {
+        // 属性冲突,有一方不是对象,直接覆盖
+        oldObj[key] = newObj[key]
+      }
+    }
+  })
+}
+
 export class ConfigUtil {
   private readonly configPath: string
   private config: Config | null = null
diff --git a/src/common/utils/LegacyLog.ts b/src/common/utils/LegacyLog.ts
index 1c335d2..f644911 100644
--- a/src/common/utils/LegacyLog.ts
+++ b/src/common/utils/LegacyLog.ts
@@ -1,9 +1,25 @@
 import fs from 'fs'
 import path from 'node:path'
-import { truncateString } from './index'
 import { getConfigUtil } from '../config'
 import { LOG_DIR } from '../globalVars'
 
+function truncateString(obj: any, maxLength = 500) {
+  if (obj !== null && typeof obj === 'object') {
+    Object.keys(obj).forEach((key) => {
+      if (typeof obj[key] === 'string') {
+        // 如果是字符串且超过指定长度,则截断
+        if (obj[key].length > maxLength) {
+          obj[key] = obj[key].substring(0, maxLength) + '...'
+        }
+      } else if (typeof obj[key] === 'object') {
+        // 如果是对象或数组,则递归调用
+        truncateString(obj[key], maxLength)
+      }
+    })
+  }
+  return obj
+}
+
 export const logFileName = `llonebot-${new Date().toLocaleString('zh-CN')}.log`.replace(/\//g, '-').replace(/:/g, '-')
 
 export function log(...msg: any[]) {
diff --git a/src/common/utils/QQBasicInfo.ts b/src/common/utils/QQBasicInfo.ts
deleted file mode 100644
index d4368a5..0000000
--- a/src/common/utils/QQBasicInfo.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import path from 'node:path'
-import os from 'node:os'
-
-export const exePath = process.execPath
-
-function getPKGPath() {
-  let p = path.join(path.dirname(exePath), 'resources', 'app', 'package.json')
-  if (os.platform() === 'darwin') {
-    p = path.join(path.dirname(path.dirname(exePath)), 'Resources', 'app', 'package.json')
-  }
-  return p
-}
-
-export const pkgInfoPath = getPKGPath()
-let configVersionInfoPath: string
-
-
-if (os.platform() !== 'linux') {
-  configVersionInfoPath = path.join(path.dirname(exePath), 'resources', 'app', 'versions', 'config.json')
-}
-else {
-  const userPath = os.homedir()
-  const appDataPath = path.resolve(userPath, './.config/QQ')
-  configVersionInfoPath = path.resolve(appDataPath, './versions/config.json')
-}
-
-if (typeof configVersionInfoPath !== 'string') {
-  throw new Error('Something went wrong when load QQ info path')
-}
-
-export { configVersionInfoPath }
-
-type QQPkgInfo = {
-  version: string
-  buildVersion: string
-  platform: string
-  eleArch: string
-}
-
-export const qqPkgInfo: QQPkgInfo = require(pkgInfoPath)
-// platform_type: 3,
-// app_type: 4,
-// app_version: '9.9.9-23159',
-// qua: 'V1_WIN_NQ_9.9.9_23159_GW_B',
-// appid: '537213764',
-// platVer: '10.0.26100',
-// clientVer: '9.9.9-23159',
-
-export function getBuildVersion(): number {
-  return +qqPkgInfo.buildVersion
-}
\ No newline at end of file
diff --git a/src/common/utils/file.ts b/src/common/utils/file.ts
index fc08594..95ab6c9 100644
--- a/src/common/utils/file.ts
+++ b/src/common/utils/file.ts
@@ -56,34 +56,6 @@ export function calculateFileMD5(filePath: string): Promise<string> {
   })
 }
 
-export interface HttpDownloadOptions {
-  url: string
-  headers?: Record<string, string> | string
-}
-export async function httpDownload(options: string | HttpDownloadOptions): Promise<Buffer> {
-  let url: string
-  let headers: Record<string, string> = {
-    'User-Agent':
-      'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36',
-  }
-  if (typeof options === 'string') {
-    url = options
-  } else {
-    url = options.url
-    if (options.headers) {
-      if (typeof options.headers === 'string') {
-        headers = JSON.parse(options.headers)
-      } else {
-        headers = options.headers
-      }
-    }
-  }
-  const fetchRes = await fetch(url, { headers })
-  if (!fetchRes.ok) throw new Error(`下载文件失败: ${fetchRes.statusText}`)
-
-  return Buffer.from(await fetchRes.arrayBuffer())
-}
-
 export enum FileUriType {
   Unknown = 0,
   FileURL = 1,
@@ -117,10 +89,11 @@ interface FetchFileRes {
   url: string
 }
 
-async function fetchFile(url: string): Promise<FetchFileRes> {
+export async function fetchFile(url: string, headersInit?: Record<string, string>): Promise<FetchFileRes> {
   const headers: Record<string, string> = {
     'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36',
-    'Host': new URL(url).hostname
+    'Host': new URL(url).hostname,
+    ...headersInit
   }
   const raw = await fetch(url, { headers }).catch((err) => {
     if (err.cause) {
diff --git a/src/common/utils/helper.ts b/src/common/utils/helper.ts
deleted file mode 100644
index d35d309..0000000
--- a/src/common/utils/helper.ts
+++ /dev/null
@@ -1,169 +0,0 @@
-export function truncateString(obj: any, maxLength = 500) {
-  if (obj !== null && typeof obj === 'object') {
-    Object.keys(obj).forEach((key) => {
-      if (typeof obj[key] === 'string') {
-        // 如果是字符串且超过指定长度,则截断
-        if (obj[key].length > maxLength) {
-          obj[key] = obj[key].substring(0, maxLength) + '...'
-        }
-      } else if (typeof obj[key] === 'object') {
-        // 如果是对象或数组,则递归调用
-        truncateString(obj[key], maxLength)
-      }
-    })
-  }
-  return obj
-}
-
-export function isNumeric(str: string) {
-  return /^\d+$/.test(str)
-}
-
-export function sleep(ms: number): Promise<void> {
-  return new Promise((resolve) => setTimeout(resolve, ms))
-}
-
-// 在保证老对象已有的属性不变化的情况下将新对象的属性复制到老对象
-export function mergeNewProperties(newObj: any, oldObj: any) {
-  Object.keys(newObj).forEach((key) => {
-    // 如果老对象不存在当前属性,则直接复制
-    if (!oldObj.hasOwnProperty(key)) {
-      oldObj[key] = newObj[key]
-    } else {
-      // 如果老对象和新对象的当前属性都是对象,则递归合并
-      if (typeof oldObj[key] === 'object' && typeof newObj[key] === 'object') {
-        mergeNewProperties(newObj[key], oldObj[key])
-      } else if (typeof oldObj[key] === 'object' || typeof newObj[key] === 'object') {
-        // 属性冲突,有一方不是对象,直接覆盖
-        oldObj[key] = newObj[key]
-      }
-    }
-  })
-}
-
-export function isNull(value: unknown) {
-  return value === undefined || value === null
-}
-
-/**
- * 将字符串按最大长度分割并添加换行符
- * @param str 原始字符串
- * @param maxLength 每行的最大字符数
- * @returns 处理后的字符串,超过长度的地方将会换行
- */
-export function wrapText(str: string, maxLength: number): string {
-  // 初始化一个空字符串用于存放结果
-  let result: string = ''
-
-  // 循环遍历字符串,每次步进maxLength个字符
-  for (let i = 0; i < str.length; i += maxLength) {
-    // 从i开始,截取长度为maxLength的字符串段,并添加到结果字符串
-    // 如果不是第一段,先添加一个换行符
-    if (i > 0) result += '\n'
-    result += str.substring(i, i + maxLength)
-  }
-
-  return result
-}
-
-
-/**
- * 函数缓存装饰器,根据方法名、参数、自定义key生成缓存键,在一定时间内返回缓存结果
- * @param ttl 超时时间,单位毫秒
- * @param customKey 自定义缓存键前缀,可为空,防止方法名参数名一致时导致缓存键冲突
- * @returns 处理后缓存或调用原方法的结果
- */
-export function cacheFunc(ttl: number, customKey: string = '') {
-  const cache = new Map<string, { expiry: number; value: any }>()
-
-  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor): PropertyDescriptor {
-    const originalMethod = descriptor.value
-    const className = target.constructor.name  // 获取类名
-    const methodName = propertyKey             // 获取方法名
-    descriptor.value = async function (...args: any[]) {
-      const cacheKey = `${customKey}${className}.${methodName}:${JSON.stringify(args)}`
-      const cached = cache.get(cacheKey)
-      if (cached && cached.expiry > Date.now()) {
-        return cached.value
-      } else {
-        const result = await originalMethod.apply(this, args)
-        cache.set(cacheKey, { value: result, expiry: Date.now() + ttl })
-        return result
-      }
-    }
-
-    return descriptor
-  }
-}
-
-export function CacheClassFuncAsync(ttl = 3600 * 1000, customKey = '') {
-  function logExecutionTime(target: any, methodName: string, descriptor: PropertyDescriptor) {
-    const cache = new Map<string, { expiry: number; value: any }>()
-    const originalMethod = descriptor.value
-    descriptor.value = async function (...args: any[]) {
-      const key = `${customKey}${String(methodName)}.(${args.map(arg => JSON.stringify(arg)).join(', ')})`
-      cache.forEach((value, key) => {
-        if (value.expiry < Date.now()) {
-          cache.delete(key)
-        }
-      })
-      const cachedValue = cache.get(key)
-      if (cachedValue && cachedValue.expiry > Date.now()) {
-        return cachedValue.value
-      }
-      const result = await originalMethod.apply(this, args)
-      cache.set(key, { expiry: Date.now() + ttl, value: result })
-      return result
-    }
-  }
-  return logExecutionTime
-}
-
-export function CacheClassFuncAsyncExtend(ttl: number = 3600 * 1000, customKey: string = '', checker: any = (...data: any[]) => { return true }) {
-  function logExecutionTime(target: any, methodName: string, descriptor: PropertyDescriptor) {
-    const cache = new Map<string, { expiry: number; value: any }>()
-    const originalMethod = descriptor.value
-    descriptor.value = async function (...args: any[]) {
-      const key = `${customKey}${String(methodName)}.(${args.map(arg => JSON.stringify(arg)).join(', ')})`
-      cache.forEach((value, key) => {
-        if (value.expiry < Date.now()) {
-          cache.delete(key)
-        }
-      })
-      const cachedValue = cache.get(key)
-      if (cachedValue && cachedValue.expiry > Date.now()) {
-        return cachedValue.value
-      }
-      const result = await originalMethod.apply(this, args)
-      if (!checker(...args, result)) {
-        return result //丢弃缓存
-      }
-      cache.set(key, { expiry: Date.now() + ttl, value: result })
-      return result
-    }
-  }
-  return logExecutionTime
-}
-
-// forked from https://github.com/NapNeko/NapCatQQ/blob/6f6b258f22d7563f15d84e7172c4d4cbb547f47e/src/common/utils/helper.ts#L14
-export class UUIDConverter {
-  static encode(highStr: string, lowStr: string): string {
-    const high = BigInt(highStr)
-    const low = BigInt(lowStr)
-    const highHex = high.toString(16).padStart(16, '0')
-    const lowHex = low.toString(16).padStart(16, '0')
-    const combinedHex = highHex + lowHex
-    const uuid = `${combinedHex.substring(0, 8)}-${combinedHex.substring(8, 12)}-${combinedHex.substring(
-      12,
-      16,
-    )}-${combinedHex.substring(16, 20)}-${combinedHex.substring(20)}`
-    return uuid
-  }
-
-  static decode(uuid: string): { high: string; low: string } {
-    const hex = uuid.replace(/-/g, '')
-    const high = BigInt('0x' + hex.substring(0, 16))
-    const low = BigInt('0x' + hex.substring(16))
-    return { high: high.toString(), low: low.toString() }
-  }
-}
\ No newline at end of file
diff --git a/src/common/utils/index.ts b/src/common/utils/index.ts
index 8dc8ccd..320014e 100644
--- a/src/common/utils/index.ts
+++ b/src/common/utils/index.ts
@@ -1,9 +1,7 @@
 export * from './file'
-export * from './helper'
+export * from './misc'
 export * from './LegacyLog'
-export * from './qqlevel'
-export * from './QQBasicInfo'
+export * from './misc'
 export * from './upgrade'
-export { getVideoInfo } from './video'
-export { checkFfmpeg } from './video'
+export { getVideoInfo, checkFfmpeg } from './video'
 export { encodeSilk } from './audio'
\ No newline at end of file
diff --git a/src/common/utils/misc.ts b/src/common/utils/misc.ts
new file mode 100644
index 0000000..44218d1
--- /dev/null
+++ b/src/common/utils/misc.ts
@@ -0,0 +1,15 @@
+import { QQLevel } from '@/ntqqapi/types'
+
+export function isNumeric(str: string) {
+  return /^\d+$/.test(str)
+}
+
+export function calcQQLevel(level: QQLevel) {
+  const { crownNum, sunNum, moonNum, starNum } = level
+  return crownNum * 64 + sunNum * 16 + moonNum * 4 + starNum
+}
+
+export function getBuildVersion(): number {
+  const version: string = globalThis.LiteLoader.versions.qqnt
+  return +version.split('-')[1]
+}
\ No newline at end of file
diff --git a/src/common/utils/qqlevel.ts b/src/common/utils/qqlevel.ts
deleted file mode 100644
index ac89932..0000000
--- a/src/common/utils/qqlevel.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-// QQ等级换算
-import { QQLevel } from '../../ntqqapi/types'
-
-export function calcQQLevel(level: QQLevel) {
-  const { crownNum, sunNum, moonNum, starNum } = level
-  return crownNum * 64 + sunNum * 16 + moonNum * 4 + starNum
-}
diff --git a/src/common/utils/upgrade.ts b/src/common/utils/upgrade.ts
index 844f07c..79c4323 100644
--- a/src/common/utils/upgrade.ts
+++ b/src/common/utils/upgrade.ts
@@ -1,8 +1,8 @@
-import { version } from '../../version'
-import * as path from 'node:path'
-import * as fs from 'node:fs'
-import { copyFolder, httpDownload, log } from '.'
+import path from 'node:path'
 import compressing from 'compressing'
+import { writeFile } from 'node:fs/promises'
+import { version } from '../../version'
+import { copyFolder, log, fetchFile } from '.'
 import { PLUGIN_DIR, TEMP_DIR } from '../globalVars'
 
 const downloadMirrorHosts = ['https://mirror.ghproxy.com/']
@@ -34,8 +34,8 @@ export async function upgradeLLOneBot() {
     // 多镜像下载
     for (const mirrorGithub of downloadMirrorHosts) {
       try {
-        const buffer = await httpDownload(mirrorGithub + downloadUrl)
-        fs.writeFileSync(filePath, buffer)
+        const res = await fetchFile(mirrorGithub + downloadUrl)
+        await writeFile(filePath, res.data)
         downloadSuccess = true
         break
       } catch (e) {
@@ -89,7 +89,7 @@ export async function getRemoteVersionByMirror(mirrorGithub: string) {
   let releasePage = 'error'
 
   try {
-    releasePage = (await httpDownload(mirrorGithub + '/LLOneBot/LLOneBot/releases')).toString()
+    releasePage = (await fetchFile(mirrorGithub + '/LLOneBot/LLOneBot/releases')).data.toString()
     // log("releasePage", releasePage);
     if (releasePage === 'error') return ''
     return releasePage.match(new RegExp('(?<=(tag/v)).*?(?=("))'))?.[0]
diff --git a/src/global.d.ts b/src/global.d.ts
index 1baf170..d378e52 100644
--- a/src/global.d.ts
+++ b/src/global.d.ts
@@ -1,8 +1,10 @@
-import { type LLOneBot } from './preload'
+import type { LLOneBot } from './preload'
+import { Dict } from 'cosmokit'
 
 declare global {
   interface Window {
     llonebot: LLOneBot
-    LiteLoader: Record<string, any>
+    LiteLoader: Dict
   }
-}
+  var LiteLoader: Dict
+}
\ No newline at end of file
diff --git a/src/main/log.ts b/src/main/log.ts
index a69a757..dca8b32 100644
--- a/src/main/log.ts
+++ b/src/main/log.ts
@@ -3,9 +3,9 @@ import { Context, Logger } from 'cordis'
 import { appendFile } from 'node:fs'
 import { LOG_DIR, selfInfo } from '@/common/globalVars'
 import { noop } from 'cosmokit'
-import { getConfigUtil } from '../common/config'
 
 interface Config {
+  enable: boolean
   filename: string
 }
 
@@ -13,9 +13,8 @@ export default class Log {
   static name = 'logger'
 
   constructor(ctx: Context, cfg: Config) {
-    // fetch data from the database
     Logger.targets.splice(0, Logger.targets.length)
-    if (!getConfigUtil().getConfig().log) {
+    if (!cfg.enable) {
       return
     }
     const file = path.join(LOG_DIR, cfg.filename)
diff --git a/src/main/main.ts b/src/main/main.ts
index b57f3c6..76bfa93 100644
--- a/src/main/main.ts
+++ b/src/main/main.ts
@@ -50,7 +50,7 @@ function onLoad() {
   }
 
   if (!fs.existsSync(LOG_DIR)) {
-    fs.mkdirSync(LOG_DIR, { recursive: true })
+    fs.mkdirSync(LOG_DIR)
   }
 
   ipcMain.handle(CHANNEL_CHECK_VERSION, async (event, arg) => {
@@ -152,10 +152,11 @@ function onLoad() {
       return
     }
     if (!fs.existsSync(TEMP_DIR)) {
-      fs.mkdirSync(TEMP_DIR, { recursive: true })
+      fs.mkdirSync(TEMP_DIR)
     }
     const ctx = new Context()
     ctx.plugin(Log, {
+      enable: config.log!,
       filename: logFileName
     })
     ctx.plugin(NTQQFileApi)
diff --git a/src/ntqqapi/api/group.ts b/src/ntqqapi/api/group.ts
index 676c383..6553fc7 100644
--- a/src/ntqqapi/api/group.ts
+++ b/src/ntqqapi/api/group.ts
@@ -8,7 +8,7 @@ import { NTEventDispatch } from '@/common/utils/EventTask'
 import { NodeIKernelGroupListener } from '../listeners'
 import { NodeIKernelGroupService } from '../services'
 import { Service, Context } from 'cordis'
-import { isNumeric } from '@/common/utils/helper'
+import { isNumeric } from '@/common/utils/misc'
 
 declare module 'cordis' {
   interface Context {
diff --git a/src/ntqqapi/api/webapi.ts b/src/ntqqapi/api/webapi.ts
index e24fbe4..6cfc468 100644
--- a/src/ntqqapi/api/webapi.ts
+++ b/src/ntqqapi/api/webapi.ts
@@ -129,22 +129,6 @@ export class NTQQWebApi extends Service {
     super(ctx, 'ntWebApi', true)
   }
 
-  async getGroupEssenceMsg(GroupCode: string, page_start: string): Promise<GroupEssenceMsgRet | undefined> {
-    const { cookies: CookieValue, bkn: Bkn } = await this.ctx.ntUserApi.getCookies('qun.qq.com')
-    const url = 'https://qun.qq.com/cgi-bin/group_digest/digest_list?bkn=' + Bkn + '&group_code=' + GroupCode + '&page_start=' + page_start + '&page_limit=20'
-    let ret: GroupEssenceMsgRet
-    try {
-      ret = await RequestUtil.HttpGetJson<GroupEssenceMsgRet>(url, 'GET', '', { 'Cookie': CookieValue })
-    } catch {
-      return undefined
-    }
-    //console.log(url, CookieValue)
-    if (ret.retcode !== 0) {
-      return undefined
-    }
-    return ret
-  }
-
   async getGroupMembers(GroupCode: string, cached: boolean = true): Promise<WebApiGroupMember[]> {
     const memberData: Array<WebApiGroupMember> = new Array<WebApiGroupMember>()
     const cookieObject = await this.ctx.ntUserApi.getCookies('qun.qq.com')
diff --git a/src/ntqqapi/constructor.ts b/src/ntqqapi/constructor.ts
index a4bd057..bdc3084 100644
--- a/src/ntqqapi/constructor.ts
+++ b/src/ntqqapi/constructor.ts
@@ -18,9 +18,9 @@ import ffmpeg from 'fluent-ffmpeg'
 import { calculateFileMD5, isGIF } from '../common/utils/file'
 import { defaultVideoThumb, getVideoInfo } from '../common/utils/video'
 import { encodeSilk } from '../common/utils/audio'
-import { isNull } from '../common/utils'
 import faceConfig from './helper/face_config.json'
 import { Context } from 'cordis'
+import { isNullable } from 'cosmokit'
 
 export const mFaceCache = new Map<string, string>() // emojiId -> faceName
 
@@ -315,7 +315,7 @@ export namespace SendMsgElementConstructor {
     // 实际测试并不能控制结果
 
     // 随机1到6
-    if (isNull(resultId)) resultId = Math.floor(Math.random() * 6) + 1
+    if (isNullable(resultId)) resultId = Math.floor(Math.random() * 6) + 1
     return {
       elementType: ElementType.FACE,
       elementId: '',
@@ -337,7 +337,7 @@ export namespace SendMsgElementConstructor {
   // 猜拳(石头剪刀布)表情
   export function rps(resultId: number | null): SendFaceElement {
     // 实际测试并不能控制结果
-    if (isNull(resultId)) resultId = Math.floor(Math.random() * 3) + 1
+    if (isNullable(resultId)) resultId = Math.floor(Math.random() * 3) + 1
     return {
       elementType: ElementType.FACE,
       elementId: '',
diff --git a/src/ntqqapi/core.ts b/src/ntqqapi/core.ts
index bc0aec5..5a91189 100644
--- a/src/ntqqapi/core.ts
+++ b/src/ntqqapi/core.ts
@@ -5,8 +5,8 @@ import { MessageUnique } from '../common/utils/MessageUnique'
 import { NTEventDispatch } from '../common/utils/EventTask'
 import { wrapperConstructor, getSession } from './wrapper'
 import { Config as LLOBConfig } from '../common/types'
-import { llonebotError, TEMP_DIR } from '../common/globalVars'
-import { isNumeric } from '../common/utils/helper'
+import { llonebotError } from '../common/globalVars'
+import { isNumeric } from '../common/utils/misc'
 import { NTMethod } from './ntcall'
 import {
   RawMessage,
diff --git a/src/onebot11/action/go-cqhttp/DownloadFile.ts b/src/onebot11/action/go-cqhttp/DownloadFile.ts
index be9e430..808b841 100644
--- a/src/onebot11/action/go-cqhttp/DownloadFile.ts
+++ b/src/onebot11/action/go-cqhttp/DownloadFile.ts
@@ -1,9 +1,9 @@
 import BaseAction from '../BaseAction'
-import { ActionName } from '../types'
 import fs from 'fs'
 import fsPromise from 'fs/promises'
 import path from 'node:path'
-import { calculateFileMD5, httpDownload } from '@/common/utils'
+import { ActionName } from '../types'
+import { calculateFileMD5, fetchFile } from '@/common/utils'
 import { TEMP_DIR } from '@/common/globalVars'
 import { randomUUID } from 'node:crypto'
 
@@ -31,8 +31,8 @@ export default class GoCQHTTPDownloadFile extends BaseAction<Payload, FileRespon
       await fsPromise.writeFile(filePath, payload.base64, 'base64')
     } else if (payload.url) {
       const headers = this.getHeaders(payload.headers)
-      const buffer = await httpDownload({ url: payload.url, headers: headers })
-      await fsPromise.writeFile(filePath, buffer)
+      const res = await fetchFile(payload.url, headers)
+      await fsPromise.writeFile(filePath, res.data)
     } else {
       throw new Error('不存在任何文件, 无法下载')
     }
diff --git a/src/onebot11/action/go-cqhttp/GetStrangerInfo.ts b/src/onebot11/action/go-cqhttp/GetStrangerInfo.ts
index a3db4ba..2ba6534 100644
--- a/src/onebot11/action/go-cqhttp/GetStrangerInfo.ts
+++ b/src/onebot11/action/go-cqhttp/GetStrangerInfo.ts
@@ -2,9 +2,9 @@ import BaseAction from '../BaseAction'
 import { OB11User } from '../../types'
 import { OB11Constructor } from '../../constructor'
 import { ActionName } from '../types'
-import { getBuildVersion } from '@/common/utils/QQBasicInfo'
+import { getBuildVersion } from '@/common/utils'
 import { OB11UserSex } from '../../types'
-import { calcQQLevel } from '@/common/utils/qqlevel'
+import { calcQQLevel } from '@/common/utils/misc'
 
 interface Payload {
   user_id: number | string
diff --git a/src/onebot11/action/go-cqhttp/QuickOperation.ts b/src/onebot11/action/go-cqhttp/QuickOperation.ts
index 439058d..6e1d9ac 100644
--- a/src/onebot11/action/go-cqhttp/QuickOperation.ts
+++ b/src/onebot11/action/go-cqhttp/QuickOperation.ts
@@ -1,5 +1,5 @@
 import BaseAction from '../BaseAction'
-import { handleQuickOperation, QuickOperation, QuickOperationEvent } from '../../helper/quick-operation'
+import { handleQuickOperation, QuickOperation, QuickOperationEvent } from '../../helper/quickOperation'
 import { ActionName } from '../types'
 
 interface Payload {
diff --git a/src/onebot11/action/go-cqhttp/SendForwardMsg.ts b/src/onebot11/action/go-cqhttp/SendForwardMsg.ts
index f484e87..aa75905 100644
--- a/src/onebot11/action/go-cqhttp/SendForwardMsg.ts
+++ b/src/onebot11/action/go-cqhttp/SendForwardMsg.ts
@@ -1,6 +1,7 @@
-import SendMsg, { convertMessage2List } from '../msg/SendMsg'
+import SendMsg from '../msg/SendMsg'
 import { OB11PostSendMsg } from '../../types'
 import { ActionName } from '../types'
+import { convertMessage2List } from '../../helper/createMessage'
 
 export class GoCQHTTPSendForwardMsg extends SendMsg {
   actionName = ActionName.GoCQHTTP_SendForwardMsg
diff --git a/src/onebot11/action/go-cqhttp/UploadFile.ts b/src/onebot11/action/go-cqhttp/UploadFile.ts
index cb726cd..8065a96 100644
--- a/src/onebot11/action/go-cqhttp/UploadFile.ts
+++ b/src/onebot11/action/go-cqhttp/UploadFile.ts
@@ -5,7 +5,7 @@ import { SendMsgElementConstructor } from '@/ntqqapi/constructor'
 import { ChatType, SendFileElement } from '@/ntqqapi/types'
 import { uri2local } from '@/common/utils'
 import { Peer } from '@/ntqqapi/types'
-import { sendMsg } from '../msg/SendMsg'
+import { sendMsg } from '../../helper/createMessage'
 
 interface Payload {
   user_id: number | string
diff --git a/src/onebot11/action/group/GetGroupMemberInfo.ts b/src/onebot11/action/group/GetGroupMemberInfo.ts
index 4264368..4a90cf0 100644
--- a/src/onebot11/action/group/GetGroupMemberInfo.ts
+++ b/src/onebot11/action/group/GetGroupMemberInfo.ts
@@ -1,9 +1,9 @@
+import BaseAction from '../BaseAction'
 import { OB11GroupMember } from '../../types'
 import { OB11Constructor } from '../../constructor'
-import BaseAction from '../BaseAction'
 import { ActionName } from '../types'
-import { isNull } from '@/common/utils/helper'
 import { selfInfo } from '@/common/globalVars'
+import { isNullable } from 'cosmokit'
 
 interface Payload {
   group_id: number | string
@@ -16,7 +16,7 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
   protected async _handle(payload: Payload) {
     const member = await this.ctx.ntGroupApi.getGroupMember(payload.group_id.toString(), payload.user_id.toString())
     if (member) {
-      if (isNull(member.sex)) {
+      if (isNullable(member.sex)) {
         //log('获取群成员详细信息')
         const info = await this.ctx.ntUserApi.getUserDetailInfo(member.uid, true)
         //log('群成员详细信息结果', info)
diff --git a/src/onebot11/action/llonebot/GetEvent.ts b/src/onebot11/action/llonebot/GetEvent.ts
index 0d7c63c..64f8a60 100644
--- a/src/onebot11/action/llonebot/GetEvent.ts
+++ b/src/onebot11/action/llonebot/GetEvent.ts
@@ -1,6 +1,6 @@
 import BaseAction from '../BaseAction'
 import { ActionName } from '../types'
-import { getHttpEvent } from '../../helper/event-for-http'
+import { getHttpEvent } from '../../helper/eventForHttp'
 import { OB11Message } from '../../types'
 import { OB11BaseEvent } from '../../event/OB11BaseEvent'
 
diff --git a/src/onebot11/action/msg/GetMsg.ts b/src/onebot11/action/msg/GetMsg.ts
index e5f175b..745ebb4 100644
--- a/src/onebot11/action/msg/GetMsg.ts
+++ b/src/onebot11/action/msg/GetMsg.ts
@@ -1,6 +1,6 @@
+import BaseAction from '../BaseAction'
 import { OB11Message } from '../../types'
 import { OB11Constructor } from '../../constructor'
-import BaseAction from '../BaseAction'
 import { ActionName } from '../types'
 import { MessageUnique } from '@/common/utils/MessageUnique'
 
@@ -14,7 +14,6 @@ class GetMsg extends BaseAction<PayloadType, OB11Message> {
   actionName = ActionName.GetMsg
 
   protected async _handle(payload: PayloadType) {
-    // log("history msg ids", Object.keys(msgHistory));
     if (!payload.message_id) {
       throw '参数message_id不能为空'
     }
diff --git a/src/onebot11/action/msg/SendMsg.ts b/src/onebot11/action/msg/SendMsg.ts
index 220dc50..8034d7d 100644
--- a/src/onebot11/action/msg/SendMsg.ts
+++ b/src/onebot11/action/msg/SendMsg.ts
@@ -1,8 +1,6 @@
 import {
-  AtType,
   ChatType,
   ElementType,
-  GroupMemberRole,
   RawMessage,
   SendMessageElement,
 } from '@/ntqqapi/types'
@@ -11,26 +9,19 @@ import {
   OB11MessageData,
   OB11MessageDataType,
   OB11MessageJson,
-  OB11MessageMixType,
   OB11MessageMusic,
   OB11MessageNode,
   OB11PostSendMsg,
 } from '../../types'
-import { SendMsgElementConstructor } from '@/ntqqapi/constructor'
 import BaseAction from '../BaseAction'
 import { ActionName, BaseCheckResult } from '../types'
 import fs from 'node:fs'
-import fsPromise from 'node:fs/promises'
-import { decodeCQCode } from '../../cqcode'
 import { getConfigUtil } from '@/common/config'
-import { sleep } from '@/common/utils/helper'
-import { uri2local } from '@/common/utils'
 import { CustomMusicSignPostData, IdMusicSignPostData, MusicSign, MusicSignPostData } from '@/common/utils/sign'
 import { Peer } from '@/ntqqapi/types/msg'
 import { MessageUnique } from '@/common/utils/MessageUnique'
-import { OB11MessageFileBase } from '../../types'
-import { Context } from 'cordis'
 import { selfInfo } from '@/common/globalVars'
+import { convertMessage2List, createSendElements, sendMsg } from '../../helper/createMessage'
 
 export interface ReturnDataType {
   message_id: number
@@ -42,266 +33,6 @@ export enum ContextMode {
   Group = 2
 }
 
-interface MessageContext {
-  deleteAfterSentFiles: string[]
-  peer: Peer
-}
-
-export function convertMessage2List(message: OB11MessageMixType, autoEscape = false) {
-  if (typeof message === 'string') {
-    if (autoEscape === true) {
-      message = [
-        {
-          type: OB11MessageDataType.text,
-          data: {
-            text: message,
-          },
-        },
-      ]
-    }
-    else {
-      message = decodeCQCode(message.toString())
-    }
-  }
-  else if (!Array.isArray(message)) {
-    message = [message]
-  }
-  return message
-}
-
-// forked from https://github.com/NapNeko/NapCatQQ/blob/6f6b258f22d7563f15d84e7172c4d4cbb547f47e/src/onebot11/action/msg/SendMsg/create-send-elements.ts#L26
-async function handleOb11FileLikeMessage(
-  ctx: Context,
-  { data: inputdata }: OB11MessageFileBase,
-  { deleteAfterSentFiles }: Pick<MessageContext, 'deleteAfterSentFiles'>,
-) {
-  //有的奇怪的框架将url作为参数 而不是file 此时优先url 同时注意可能传入的是非file://开头的目录 By Mlikiowa
-  const {
-    path,
-    isLocal,
-    fileName,
-    errMsg,
-    success,
-  } = (await uri2local(inputdata?.url || inputdata.file))
-
-  if (!success) {
-    ctx.logger.error('文件下载失败', errMsg)
-    throw Error('文件下载失败' + errMsg)
-  }
-
-  if (!isLocal) { // 只删除http和base64转过来的文件
-    deleteAfterSentFiles.push(path)
-  }
-
-  return { path, fileName: inputdata.name || fileName }
-}
-
-export async function createSendElements(
-  ctx: Context,
-  messageData: OB11MessageData[],
-  peer: Peer,
-  ignoreTypes: OB11MessageDataType[] = [],
-) {
-  let sendElements: SendMessageElement[] = []
-  let deleteAfterSentFiles: string[] = []
-  for (let sendMsg of messageData) {
-    if (ignoreTypes.includes(sendMsg.type)) {
-      continue
-    }
-    switch (sendMsg.type) {
-      case OB11MessageDataType.text: {
-        const text = sendMsg.data?.text
-        if (text) {
-          sendElements.push(SendMsgElementConstructor.text(sendMsg.data!.text))
-        }
-      }
-        break
-      case OB11MessageDataType.at: {
-        if (!peer) {
-          continue
-        }
-        if (sendMsg.data?.qq) {
-          const atQQ = String(sendMsg.data.qq)
-          if (atQQ === 'all') {
-            // todo:查询剩余的at全体次数
-            const groupCode = peer.peerUid
-            let remainAtAllCount = 1
-            let isAdmin: boolean = true
-            if (groupCode) {
-              try {
-                remainAtAllCount = (await ctx.ntGroupApi.getGroupAtAllRemainCount(groupCode)).atInfo
-                  .RemainAtAllCountForUin
-                ctx.logger.info(`群${groupCode}剩余at全体次数`, remainAtAllCount)
-                const self = await ctx.ntGroupApi.getGroupMember(groupCode, selfInfo.uin)
-                isAdmin = self?.role === GroupMemberRole.admin || self?.role === GroupMemberRole.owner
-              } catch (e) {
-              }
-            }
-            if (isAdmin && remainAtAllCount > 0) {
-              sendElements.push(SendMsgElementConstructor.at(atQQ, atQQ, AtType.atAll, '@全体成员'))
-            }
-          }
-          else if (peer.chatType === ChatType.group) {
-            const atMember = await ctx.ntGroupApi.getGroupMember(peer.peerUid, atQQ)
-            if (atMember) {
-              const display = `@${atMember.cardName || atMember.nick}`
-              sendElements.push(
-                SendMsgElementConstructor.at(atQQ, atMember.uid, AtType.atUser, display),
-              )
-            } else {
-              const atNmae = sendMsg.data?.name
-              const uid = await ctx.ntUserApi.getUidByUin(atQQ) || ''
-              const display = atNmae ? `@${atNmae}` : ''
-              sendElements.push(
-                SendMsgElementConstructor.at(atQQ, uid, AtType.atUser, display),
-              )
-            }
-          }
-        }
-      }
-        break
-      case OB11MessageDataType.reply: {
-        if (sendMsg.data?.id) {
-          const replyMsgId = await MessageUnique.getMsgIdAndPeerByShortId(+sendMsg.data.id)
-          if (!replyMsgId) {
-            ctx.logger.warn('回复消息不存在', replyMsgId)
-            continue
-          }
-          const replyMsg = (await ctx.ntMsgApi.getMsgsByMsgId(
-            replyMsgId.Peer,
-            [replyMsgId.MsgId!]
-          )).msgList[0]
-          if (replyMsg) {
-            sendElements.push(
-              SendMsgElementConstructor.reply(
-                replyMsg.msgSeq,
-                replyMsg.msgId,
-                replyMsg.senderUin!,
-                replyMsg.senderUin!,
-              ),
-            )
-          }
-        }
-      }
-        break
-      case OB11MessageDataType.face: {
-        const faceId = sendMsg.data?.id
-        if (faceId) {
-          sendElements.push(SendMsgElementConstructor.face(parseInt(faceId)))
-        }
-      }
-        break
-      case OB11MessageDataType.mface: {
-        sendElements.push(
-          SendMsgElementConstructor.mface(
-            sendMsg.data.emoji_package_id,
-            sendMsg.data.emoji_id,
-            sendMsg.data.key,
-            sendMsg.data.summary,
-          ),
-        )
-      }
-        break
-      case OB11MessageDataType.image: {
-        const res = await SendMsgElementConstructor.pic(
-          ctx,
-          (await handleOb11FileLikeMessage(ctx, sendMsg, { deleteAfterSentFiles })).path,
-          sendMsg.data.summary || '',
-          sendMsg.data.subType || 0
-        )
-        deleteAfterSentFiles.push(res.picElement.sourcePath)
-        sendElements.push(res)
-      }
-        break
-      case OB11MessageDataType.file: {
-        const { path, fileName } = await handleOb11FileLikeMessage(ctx, sendMsg, { deleteAfterSentFiles })
-        sendElements.push(await SendMsgElementConstructor.file(ctx, path, fileName))
-      }
-        break
-      case OB11MessageDataType.video: {
-        const { path, fileName } = await handleOb11FileLikeMessage(ctx, sendMsg, { deleteAfterSentFiles })
-        let thumb = sendMsg.data.thumb
-        if (thumb) {
-          const uri2LocalRes = await uri2local(thumb)
-          if (uri2LocalRes.success) thumb = uri2LocalRes.path
-        }
-        const res = await SendMsgElementConstructor.video(ctx, path, fileName, thumb)
-        deleteAfterSentFiles.push(res.videoElement.filePath)
-        sendElements.push(res)
-      }
-        break
-      case OB11MessageDataType.voice: {
-        const { path } = await handleOb11FileLikeMessage(ctx, sendMsg, { deleteAfterSentFiles })
-        sendElements.push(await SendMsgElementConstructor.ptt(ctx, path))
-      }
-        break
-      case OB11MessageDataType.json: {
-        sendElements.push(SendMsgElementConstructor.ark(sendMsg.data.data))
-      }
-        break
-      case OB11MessageDataType.poke: {
-        let qq = sendMsg.data?.qq || sendMsg.data?.id
-      }
-        break
-      case OB11MessageDataType.dice: {
-        const resultId = sendMsg.data?.result
-        sendElements.push(SendMsgElementConstructor.dice(resultId))
-      }
-        break
-      case OB11MessageDataType.RPS: {
-        const resultId = sendMsg.data?.result
-        sendElements.push(SendMsgElementConstructor.rps(resultId))
-      }
-        break
-    }
-  }
-
-  return {
-    sendElements,
-    deleteAfterSentFiles,
-  }
-}
-
-export async function sendMsg(
-  ctx: Context,
-  peer: Peer,
-  sendElements: SendMessageElement[],
-  deleteAfterSentFiles: string[],
-  waitComplete = true,
-) {
-  if (!sendElements.length) {
-    throw '消息体无法解析,请检查是否发送了不支持的消息类型'
-  }
-  // 计算发送的文件大小
-  let totalSize = 0
-  for (const fileElement of sendElements) {
-    try {
-      if (fileElement.elementType === ElementType.PTT) {
-        totalSize += fs.statSync(fileElement.pttElement.filePath).size
-      }
-      if (fileElement.elementType === ElementType.FILE) {
-        totalSize += fs.statSync(fileElement.fileElement.filePath).size
-      }
-      if (fileElement.elementType === ElementType.VIDEO) {
-        totalSize += fs.statSync(fileElement.videoElement.filePath).size
-      }
-      if (fileElement.elementType === ElementType.PIC) {
-        totalSize += fs.statSync(fileElement.picElement.sourcePath).size
-      }
-    } catch (e) {
-      ctx.logger.warn('文件大小计算失败', e, fileElement)
-    }
-  }
-  //log('发送消息总大小', totalSize, 'bytes')
-  const timeout = 10000 + (totalSize / 1024 / 256 * 1000)  // 10s Basic Timeout + PredictTime( For File 512kb/s )
-  //log('设置消息超时时间', timeout)
-  const returnMsg = await ctx.ntMsgApi.sendMsg(peer, sendElements, waitComplete, timeout)
-  returnMsg.msgShortId = MessageUnique.createMsg(peer, returnMsg.msgId)
-  ctx.logger.info('消息发送', returnMsg.msgShortId)
-  deleteAfterSentFiles.map(path => fsPromise.unlink(path))
-  return returnMsg
-}
-
 export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
   actionName = ActionName.SendMsg
 
@@ -422,7 +153,6 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
         } as OB11MessageJson
       }
     }
-    // log("send msg:", peer, sendElements)
     const { sendElements, deleteAfterSentFiles } = await createSendElements(this.ctx, messages, peer)
     if (sendElements.length === 1) {
       if (sendElements[0] === null) {
@@ -462,7 +192,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
         sendElements,
         true,
       )
-      await sleep(400)
+      await this.ctx.sleep(400)
       return nodeMsg
     } catch (e) {
       this.ctx.logger.warn(e, '克隆转发消息失败,将忽略本条消息', msg)
@@ -522,7 +252,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
           for (const eles of sendElementsSplit) {
             const nodeMsg = await sendMsg(this.ctx, selfPeer, eles, [], true)
             nodeMsgIds.push(nodeMsg.msgId)
-            await sleep(400)
+            await this.ctx.sleep(400)
             this.ctx.logger.info('转发节点生成成功', nodeMsg.msgId)
           }
           deleteAfterSentFiles.map((f) => fs.unlink(f, () => {
diff --git a/src/onebot11/action/user/GetFriendList.ts b/src/onebot11/action/user/GetFriendList.ts
index ee91b92..103dde6 100644
--- a/src/onebot11/action/user/GetFriendList.ts
+++ b/src/onebot11/action/user/GetFriendList.ts
@@ -2,7 +2,7 @@ import BaseAction from '../BaseAction'
 import { OB11User } from '../../types'
 import { OB11Constructor } from '../../constructor'
 import { ActionName } from '../types'
-import { getBuildVersion } from '@/common/utils/QQBasicInfo'
+import { getBuildVersion } from '@/common/utils'
 
 interface Payload {
   no_cache: boolean | string
diff --git a/src/onebot11/adapter.ts b/src/onebot11/adapter.ts
index 331e9b2..d09dd36 100644
--- a/src/onebot11/adapter.ts
+++ b/src/onebot11/adapter.ts
@@ -22,7 +22,7 @@ import { OB11Http, OB11HttpPost } from './connect/http'
 import { OB11BaseEvent } from './event/OB11BaseEvent'
 import { OB11Message } from './types'
 import { OB11BaseMetaEvent } from './event/meta/OB11BaseMetaEvent'
-import { postHttpEvent } from './helper/event-for-http'
+import { postHttpEvent } from './helper/eventForHttp'
 import { initActionMap } from './action'
 import { llonebotError } from '../common/globalVars'
 import { OB11GroupCardEvent } from './event/notice/OB11GroupCardEvent'
diff --git a/src/onebot11/connect/http.ts b/src/onebot11/connect/http.ts
index 55bdb20..408045a 100644
--- a/src/onebot11/connect/http.ts
+++ b/src/onebot11/connect/http.ts
@@ -8,7 +8,7 @@ import { llonebotError, selfInfo } from '@/common/globalVars'
 import { OB11Response } from '../action/OB11Response'
 import { OB11Message } from '../types'
 import { OB11BaseEvent } from '../event/OB11BaseEvent'
-import { handleQuickOperation, QuickOperationEvent } from '../helper/quick-operation'
+import { handleQuickOperation, QuickOperationEvent } from '../helper/quickOperation'
 import { OB11HeartbeatEvent } from '../event/meta/OB11HeartbeatEvent'
 
 type RegisterHandler = (res: Response, payload: any) => Promise<any>
diff --git a/src/onebot11/constructor.ts b/src/onebot11/constructor.ts
index c3e4d82..d169b4e 100644
--- a/src/onebot11/constructor.ts
+++ b/src/onebot11/constructor.ts
@@ -18,7 +18,6 @@ import {
   Peer,
   GroupMember,
   RawMessage,
-  SelfInfo,
   Sex,
   TipGroupElementType,
   User,
@@ -32,8 +31,7 @@ import { OB11GroupIncreaseEvent } from './event/notice/OB11GroupIncreaseEvent'
 import { OB11GroupBanEvent } from './event/notice/OB11GroupBanEvent'
 import { OB11GroupUploadNoticeEvent } from './event/notice/OB11GroupUploadNoticeEvent'
 import { OB11GroupNoticeEvent } from './event/notice/OB11GroupNoticeEvent'
-import { calcQQLevel } from '../common/utils/qqlevel'
-import { isNull, sleep } from '../common/utils/helper'
+import { calcQQLevel } from '../common/utils/misc'
 import { getConfigUtil } from '../common/config'
 import { OB11GroupTitleEvent } from './event/notice/OB11GroupTitleEvent'
 import { OB11GroupCardEvent } from './event/notice/OB11GroupCardEvent'
@@ -46,7 +44,7 @@ import { OB11GroupRecallNoticeEvent } from './event/notice/OB11GroupRecallNotice
 import { OB11FriendPokeEvent, OB11GroupPokeEvent } from './event/notice/OB11PokeEvent'
 import { OB11BaseNoticeEvent } from './event/notice/OB11BaseNoticeEvent'
 import { OB11GroupEssenceEvent } from './event/notice/OB11GroupEssenceEvent'
-import { omit } from 'cosmokit'
+import { omit, isNullable } from 'cosmokit'
 import { Context } from 'cordis'
 import { selfInfo } from '@/common/globalVars'
 
@@ -379,7 +377,7 @@ export namespace OB11Constructor {
         // log("收到群提示消息", groupElement)
         if (groupElement.type === TipGroupElementType.memberIncrease) {
           ctx.logger.info('收到群成员增加消息', groupElement)
-          await sleep(1000)
+          await ctx.sleep(1000)
           const member = await ctx.ntGroupApi.getGroupMember(msg.peerUid, groupElement.memberUid)
           let memberUin = member?.uin
           if (!memberUin) {
@@ -598,7 +596,7 @@ export namespace OB11Constructor {
             const title = json.items[3].txt
             ctx.logger.info('收到群成员新头衔消息', json)
             ctx.ntGroupApi.getGroupMember(msg.peerUid, memberUin).then(member => {
-              if (!isNull(member)) {
+              if (!isNullable(member)) {
                 member.memberSpecialTitle = title
               }
             })
diff --git a/src/onebot11/helper/createMessage.ts b/src/onebot11/helper/createMessage.ts
new file mode 100644
index 0000000..6b111f3
--- /dev/null
+++ b/src/onebot11/helper/createMessage.ts
@@ -0,0 +1,277 @@
+import fs from 'node:fs'
+import fsPromise from 'node:fs/promises'
+import {
+  AtType,
+  ChatType,
+  GroupMemberRole,
+  SendMessageElement,
+  ElementType
+} from '@/ntqqapi/types'
+import {
+  OB11MessageData,
+  OB11MessageDataType,
+  OB11MessageFileBase,
+  OB11MessageMixType
+} from '../types'
+import { decodeCQCode } from '../cqcode'
+import { Peer } from '@/ntqqapi/types/msg'
+import { SendMsgElementConstructor } from '@/ntqqapi/constructor'
+import { MessageUnique } from '@/common/utils/MessageUnique'
+import { selfInfo } from '@/common/globalVars'
+import { uri2local } from '@/common/utils'
+import { Context } from 'cordis'
+
+export async function createSendElements(
+  ctx: Context,
+  messageData: OB11MessageData[],
+  peer: Peer,
+  ignoreTypes: OB11MessageDataType[] = [],
+) {
+  let sendElements: SendMessageElement[] = []
+  let deleteAfterSentFiles: string[] = []
+  for (let sendMsg of messageData) {
+    if (ignoreTypes.includes(sendMsg.type)) {
+      continue
+    }
+    switch (sendMsg.type) {
+      case OB11MessageDataType.text: {
+        const text = sendMsg.data?.text
+        if (text) {
+          sendElements.push(SendMsgElementConstructor.text(sendMsg.data!.text))
+        }
+      }
+        break
+      case OB11MessageDataType.at: {
+        if (!peer) {
+          continue
+        }
+        if (sendMsg.data?.qq) {
+          const atQQ = String(sendMsg.data.qq)
+          if (atQQ === 'all') {
+            // todo:查询剩余的at全体次数
+            const groupCode = peer.peerUid
+            let remainAtAllCount = 1
+            let isAdmin: boolean = true
+            if (groupCode) {
+              try {
+                remainAtAllCount = (await ctx.ntGroupApi.getGroupAtAllRemainCount(groupCode)).atInfo
+                  .RemainAtAllCountForUin
+                ctx.logger.info(`群${groupCode}剩余at全体次数`, remainAtAllCount)
+                const self = await ctx.ntGroupApi.getGroupMember(groupCode, selfInfo.uin)
+                isAdmin = self?.role === GroupMemberRole.admin || self?.role === GroupMemberRole.owner
+              } catch (e) {
+              }
+            }
+            if (isAdmin && remainAtAllCount > 0) {
+              sendElements.push(SendMsgElementConstructor.at(atQQ, atQQ, AtType.atAll, '@全体成员'))
+            }
+          }
+          else if (peer.chatType === ChatType.group) {
+            const atMember = await ctx.ntGroupApi.getGroupMember(peer.peerUid, atQQ)
+            if (atMember) {
+              const display = `@${atMember.cardName || atMember.nick}`
+              sendElements.push(
+                SendMsgElementConstructor.at(atQQ, atMember.uid, AtType.atUser, display),
+              )
+            } else {
+              const atNmae = sendMsg.data?.name
+              const uid = await ctx.ntUserApi.getUidByUin(atQQ) || ''
+              const display = atNmae ? `@${atNmae}` : ''
+              sendElements.push(
+                SendMsgElementConstructor.at(atQQ, uid, AtType.atUser, display),
+              )
+            }
+          }
+        }
+      }
+        break
+      case OB11MessageDataType.reply: {
+        if (sendMsg.data?.id) {
+          const replyMsgId = await MessageUnique.getMsgIdAndPeerByShortId(+sendMsg.data.id)
+          if (!replyMsgId) {
+            ctx.logger.warn('回复消息不存在', replyMsgId)
+            continue
+          }
+          const replyMsg = (await ctx.ntMsgApi.getMsgsByMsgId(
+            replyMsgId.Peer,
+            [replyMsgId.MsgId!]
+          )).msgList[0]
+          if (replyMsg) {
+            sendElements.push(
+              SendMsgElementConstructor.reply(
+                replyMsg.msgSeq,
+                replyMsg.msgId,
+                replyMsg.senderUin!,
+                replyMsg.senderUin!,
+              ),
+            )
+          }
+        }
+      }
+        break
+      case OB11MessageDataType.face: {
+        const faceId = sendMsg.data?.id
+        if (faceId) {
+          sendElements.push(SendMsgElementConstructor.face(parseInt(faceId)))
+        }
+      }
+        break
+      case OB11MessageDataType.mface: {
+        sendElements.push(
+          SendMsgElementConstructor.mface(
+            sendMsg.data.emoji_package_id,
+            sendMsg.data.emoji_id,
+            sendMsg.data.key,
+            sendMsg.data.summary,
+          ),
+        )
+      }
+        break
+      case OB11MessageDataType.image: {
+        const res = await SendMsgElementConstructor.pic(
+          ctx,
+          (await handleOb11FileLikeMessage(ctx, sendMsg, { deleteAfterSentFiles })).path,
+          sendMsg.data.summary || '',
+          sendMsg.data.subType || 0
+        )
+        deleteAfterSentFiles.push(res.picElement.sourcePath)
+        sendElements.push(res)
+      }
+        break
+      case OB11MessageDataType.file: {
+        const { path, fileName } = await handleOb11FileLikeMessage(ctx, sendMsg, { deleteAfterSentFiles })
+        sendElements.push(await SendMsgElementConstructor.file(ctx, path, fileName))
+      }
+        break
+      case OB11MessageDataType.video: {
+        const { path, fileName } = await handleOb11FileLikeMessage(ctx, sendMsg, { deleteAfterSentFiles })
+        let thumb = sendMsg.data.thumb
+        if (thumb) {
+          const uri2LocalRes = await uri2local(thumb)
+          if (uri2LocalRes.success) thumb = uri2LocalRes.path
+        }
+        const res = await SendMsgElementConstructor.video(ctx, path, fileName, thumb)
+        deleteAfterSentFiles.push(res.videoElement.filePath)
+        sendElements.push(res)
+      }
+        break
+      case OB11MessageDataType.voice: {
+        const { path } = await handleOb11FileLikeMessage(ctx, sendMsg, { deleteAfterSentFiles })
+        sendElements.push(await SendMsgElementConstructor.ptt(ctx, path))
+      }
+        break
+      case OB11MessageDataType.json: {
+        sendElements.push(SendMsgElementConstructor.ark(sendMsg.data.data))
+      }
+        break
+      case OB11MessageDataType.poke: {
+        let qq = sendMsg.data?.qq || sendMsg.data?.id
+      }
+        break
+      case OB11MessageDataType.dice: {
+        const resultId = sendMsg.data?.result
+        sendElements.push(SendMsgElementConstructor.dice(resultId))
+      }
+        break
+      case OB11MessageDataType.RPS: {
+        const resultId = sendMsg.data?.result
+        sendElements.push(SendMsgElementConstructor.rps(resultId))
+      }
+        break
+    }
+  }
+
+  return {
+    sendElements,
+    deleteAfterSentFiles,
+  }
+}
+
+// forked from https://github.com/NapNeko/NapCatQQ/blob/6f6b258f22d7563f15d84e7172c4d4cbb547f47e/src/onebot11/action/msg/SendMsg/create-send-elements.ts#L26
+async function handleOb11FileLikeMessage(
+  ctx: Context,
+  { data: inputdata }: OB11MessageFileBase,
+  { deleteAfterSentFiles }: { deleteAfterSentFiles: string[] },
+) {
+  //有的奇怪的框架将url作为参数 而不是file 此时优先url 同时注意可能传入的是非file://开头的目录 By Mlikiowa
+  const {
+    path,
+    isLocal,
+    fileName,
+    errMsg,
+    success,
+  } = (await uri2local(inputdata?.url || inputdata.file))
+
+  if (!success) {
+    ctx.logger.error('文件下载失败', errMsg)
+    throw Error('文件下载失败' + errMsg)
+  }
+
+  if (!isLocal) { // 只删除http和base64转过来的文件
+    deleteAfterSentFiles.push(path)
+  }
+
+  return { path, fileName: inputdata.name || fileName }
+}
+
+export function convertMessage2List(message: OB11MessageMixType, autoEscape = false) {
+  if (typeof message === 'string') {
+    if (autoEscape === true) {
+      message = [
+        {
+          type: OB11MessageDataType.text,
+          data: {
+            text: message,
+          },
+        },
+      ]
+    }
+    else {
+      message = decodeCQCode(message.toString())
+    }
+  }
+  else if (!Array.isArray(message)) {
+    message = [message]
+  }
+  return message
+}
+
+export async function sendMsg(
+  ctx: Context,
+  peer: Peer,
+  sendElements: SendMessageElement[],
+  deleteAfterSentFiles: string[],
+  waitComplete = true,
+) {
+  if (!sendElements.length) {
+    throw '消息体无法解析,请检查是否发送了不支持的消息类型'
+  }
+  // 计算发送的文件大小
+  let totalSize = 0
+  for (const fileElement of sendElements) {
+    try {
+      if (fileElement.elementType === ElementType.PTT) {
+        totalSize += fs.statSync(fileElement.pttElement.filePath).size
+      }
+      if (fileElement.elementType === ElementType.FILE) {
+        totalSize += fs.statSync(fileElement.fileElement.filePath).size
+      }
+      if (fileElement.elementType === ElementType.VIDEO) {
+        totalSize += fs.statSync(fileElement.videoElement.filePath).size
+      }
+      if (fileElement.elementType === ElementType.PIC) {
+        totalSize += fs.statSync(fileElement.picElement.sourcePath).size
+      }
+    } catch (e) {
+      ctx.logger.warn('文件大小计算失败', e, fileElement)
+    }
+  }
+  //log('发送消息总大小', totalSize, 'bytes')
+  const timeout = 10000 + (totalSize / 1024 / 256 * 1000)  // 10s Basic Timeout + PredictTime( For File 512kb/s )
+  //log('设置消息超时时间', timeout)
+  const returnMsg = await ctx.ntMsgApi.sendMsg(peer, sendElements, waitComplete, timeout)
+  returnMsg.msgShortId = MessageUnique.createMsg(peer, returnMsg.msgId)
+  ctx.logger.info('消息发送', returnMsg.msgShortId)
+  deleteAfterSentFiles.map(path => fsPromise.unlink(path))
+  return returnMsg
+}
\ No newline at end of file
diff --git a/src/onebot11/helper/event-for-http.ts b/src/onebot11/helper/eventForHttp.ts
similarity index 100%
rename from src/onebot11/helper/event-for-http.ts
rename to src/onebot11/helper/eventForHttp.ts
diff --git a/src/onebot11/helper/quick-operation.ts b/src/onebot11/helper/quickOperation.ts
similarity index 99%
rename from src/onebot11/helper/quick-operation.ts
rename to src/onebot11/helper/quickOperation.ts
index 23dda5c..5aada42 100644
--- a/src/onebot11/helper/quick-operation.ts
+++ b/src/onebot11/helper/quickOperation.ts
@@ -2,13 +2,12 @@ import { OB11Message, OB11MessageAt, OB11MessageData, OB11MessageDataType } from
 import { OB11FriendRequestEvent } from '../event/request/OB11FriendRequest'
 import { OB11GroupRequestEvent } from '../event/request/OB11GroupRequest'
 import { ChatType, GroupRequestOperateTypes, Peer } from '@/ntqqapi/types'
-import { convertMessage2List, createSendElements, sendMsg } from '../action/msg/SendMsg'
+import { convertMessage2List, createSendElements, sendMsg } from '../helper/createMessage'
 import { getConfigUtil } from '@/common/config'
 import { MessageUnique } from '@/common/utils/MessageUnique'
 import { isNullable } from 'cosmokit'
 import { Context } from 'cordis'
 
-
 interface QuickOperationPrivateMessage {
   reply?: string
   auto_escape?: boolean
@@ -21,7 +20,6 @@ interface QuickOperationGroupMessage extends QuickOperationPrivateMessage {
   kick?: boolean
   ban?: boolean
   ban_duration?: number
-  //
 }
 
 interface QuickOperationFriendRequest {
diff --git a/tsconfig.json b/tsconfig.json
index f3257f4..f59aa42 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,7 +6,6 @@
     "strict": true,
     "noImplicitAny": false,
     "esModuleInterop": true,
-    "allowJs": true,
     "allowSyntheticDefaultImports": true,
     "experimentalDecorators": true,
     "resolveJsonModule": true,