chore: init

This commit is contained in:
MoonLeeeaf
2024-05-10 21:14:50 +08:00
commit a376ead195
29 changed files with 3024 additions and 0 deletions

121
server_src/api-msgs.js Normal file
View File

@@ -0,0 +1,121 @@
/*
* ©2024 满月叶
* Github: MoonLeeeaf
* 通讯辅助类
*/
const io = require("./iolib")
const hash = require("./hashlib")
const vals = require("./val")
const users = require("./api-users")
let getSameHashedValue = (a, b) => {
let _a = [hash.md5(a) + hash.sha256(a), hash.md5(b) + hash.sha256(b)].sort()
let [_1, _2] = _a
return hash.sha256hex(hash.sha256hex(_1) + hash.sha256hex(_2))
}
let getSingleChatDir = (a, b) => {
return vals.LINGCHAIR_SINGLE_MESSAGE_DIR + "/" + getSameHashedValue(a, b)
}
let apis = {
// 储存单聊消息: 操作者, 访问密钥, 发送至, 消息内容
// 消息存储方式为计次直接储存, 每一个消息都有对应的 ID
// 读取某一段落时使用遍历方式
// @API
sendSingleMsg: (name, accessToken, target, msg) => {
if (!users.checkAccessToken(name, accessToken))
return { code: -1, msg: "访问令牌错误" }
if (!users.isUserExists(target))
return { code: -1, msg: "目标用户不存在" }
if (msg.trim() == "")
return { code: -1, msg: "不是有内容的消息我不要" }
let fileDir = getSingleChatDir(name, target)
io.mkdirs(fileDir)
let countFile = io.open(fileDir + "/count.txt", "rw")
if (!io.exists(fileDir + "/count.txt"))
countFile.write("0")
let count = parseInt(countFile.read())
count += 1
let time = Date.now()
io.open(fileDir + "/msg_" + count + ".json", "w").writeJson({
name: name,
msg: msg,
msgid: count,
time: time,
}).close()
countFile.write(count + "")
return { code: 0, msg: "成功", msgid: count, time: time }
},
// 读取消息记录
// 从起始点到结束点读取,由最新到最老(计次越大越新)
// 不提供 startId 则默认从最新计次往前数
// 若超过 limit 计次范围, 直接终止遍历
// @API
getSingleMsgHistroy: (name, accessToken, target, sid, limit) => {
if (!users.checkAccessToken(name, accessToken))
return { code: -1, msg: "访问令牌错误" }
if (!users.isUserExists(target))
return { code: -1, msg: "目标用户不存在" }
let fileDir = getSingleChatDir(name, target)
io.mkdirs(fileDir)
let countFile = io.open(fileDir + "/count.txt", "rw")
if (!io.exists(fileDir + "/count.txt"))
countFile.write("0")
let startId = sid
if (startId == null)
startId = parseInt(countFile.read().toString())
let list = []
let i = startId
let i2 = 0
let cfn
while(true) {
cfn = fileDir + "/msg_" + i + ".json"
// 1. 超过界限
// 2. 超过计次
// 3. 超过最大限度
if ((!io.exists(cfn)) || i2 > limit || i2 > 100) break
try {
let data = io.open(cfn, "r").readJson()
list.unshift(data)
} catch (e) {
return { code: -2, msg: e }
}
i--
i2++
}
return { code: 0, msg: "成功", histroy: list }
},
// 上传图片: 操作者, 访问密钥, 发送至, 图片
// 未来需要一些操作来删除未使用的图片文件
// @API
uploadImage: (name, accessToken, target, msg) => {
if (!users.checkAccessToken(name, accessToken))
return { code: -1, msg: "访问令牌错误" }
if (!users.isUserExists(target))
return { code: -1, msg: "目标用户不存在" }
let fileDir = getSingleChatDir(name, target) + "/images/"
io.mkdirs(fileDir)
},
}
module.exports = apis

195
server_src/api-users.js Normal file
View File

@@ -0,0 +1,195 @@
/*
* ©2024 满月叶
* Github: MoonLeeeaf
* 用户辅助类
*/
const io = require("./iolib")
const hash = require("./hashlib")
const vals = require("./val")
// 获取用户资料所在的路径
let getUserPath = (name) => {
return vals.LINGCHAIR_DATA_DIR + "/users/" + name
}
// 用户是否存在
let isUserExists = (name) => {
return io.exists(getUserPath(name))
}
let apis = {
isUserExists: isUserExists,
// ================================
// 无需令牌的 API
// ================================
// 创建账号: 账号, 密码 返回账号唯一 ID 和成功信息 失败返回 null 和原因
// 账号文件结构: {uid: 10000, name: "GenShin", nick: "Impact", passwd: "SHA-256 + MD5"}
// 注意: 密码在客户端也应该经过哈希处理(SHA256 + MD5)
// @APi
signUp: (name, passwd) => {
if (passwd == null || name == null)
return { msg: "必须输入 账号和密码", code: -1 }
let path = getUserPath(name)
if (isUserExists(name))
return { msg: "用户账号名重复", code: -1 }
io.mkdirs(path)
let idCount = io.open(vals.LINGCHAIR_USERS_COUNT_FILE)
let uid = parseInt(idCount.read("*a"))
idCount.write((uid + 1) + "").close()
io.open(path + "/user.json").writeJson({
uid: uid,
name: name,
nick: null,
passwd: hash.sha256(passwd) + hash.md5(passwd),
}).close()
return { uid: uid, msg: "成功", code: 0 }
},
// 登录账号: 账号, 密码 返回刷新令牌 失败返回 null 和原因
// 注意: 密码在客户端应该经过哈希处理(SHA256 + MD5)
// @API
signIn: (name, passwd) => {
if (passwd == null || name == null)
return { msg: "必须输入 账号和密码", code: -1 }
if (!isUserExists(name))
return { msg: "用户不存在", code: -1 }
if (apis.getPassWordHashedRaw(name) !== (hash.sha256(passwd) + hash.md5(passwd)))
return { msg: "账号所对应的密码错误", code: -1 }
return { msg: "成功", code: 0, refreshToken: apis.getRefreshToken(name, apis.getPassWordHashed(name)) }
},
// 获取刷新令牌: 账号,密码 返回刷新令牌
// 注意: 密码在客户端也应该经过哈希处理(SHA256 + MD5)
// 刷新令牌算法: 哈希(用户ID + 当前年 + 当前月 + 密码 + 盐)
// 有效期: 一个月
getRefreshToken: (name, passwd) => {
let d = new Date()
let raw = name + d.getFullYear() + d.getMonth() + passwd + "LINGCHAIR-TEST-DEMO"
return hash.sha256(raw) + hash.md5(raw)
},
// 获取访问令牌: 账号,刷新令牌 返回访问令牌
// 注意: 密码在客户端也应该经过哈希处理(SHA256 + MD5)
// 刷新令牌算法: 哈希(用户ID + 当前年 + 当前月 + 密码 + 盐)
// 有效期: 一天
getAccessTokenNonApi: (name, rt) => {
if (!apis.checkRefreshToken(name, rt))
return null
let date = new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'numeric', day: 'numeric' })
return hash.sha256(name + date) + hash.md5(rt + date + "LINGCHAIR-ACCESS-TEST-DEMO")
},
// 获取密码(已被哈希处理) 返回密码
// 在密码被设置前已经被哈希过,不需要重复
// 算法: (SHA256 + MD5)
// 警告: 这是经过二次哈希的
getPassWordHashed: (name) => {
return hash.sha256(apis.getPassWordHashedRaw(name)) + hash.md5(apis.getPassWordHashedRaw(name))
},
// 请勿与上面的混淆
// 上面的是经过第二次哈希的
getPassWordHashedRaw: (name) => {
return io.open(getUserPath(name) + "/user.json").readJson().passwd
},
// 检测刷新令牌是否正确: 账号, 刷新令牌 返回布尔值
// 密码在服务端经过哈希保存 不需要重复输入密码
checkRefreshToken: (name, rt) => {
return apis.getRefreshToken(name, apis.getPassWordHashed(name)) === rt
},
// 检测访问令牌是否正确: 账号, 访问令牌 返回布尔值
// 密码在服务端经过哈希保存 不需要重复输入密码
checkAccessToken: (name, at) => {
return apis.getAccessTokenNonApi(name, apis.getRefreshToken(name, apis.getPassWordHashed(name /* 就是你这个傻逼害得我找两年BUG */))) === at
},
// ================================
// 需要令牌的 API
// ================================
// 获取访问令牌: 账号, 刷新令牌 返回访问令牌 失败返回 -1 和原因
// 有效期: 一天
// 算法: SHA256(name) + MD5(rt + 盐)
// @Api
getAccessToken: (name, rt) => {
if (!apis.checkRefreshToken(name, rt))
return { msg: "刷新令牌不正确!", code: -1 }
return { msg: "成功", code: 0, accessToken: apis.getAccessTokenNonApi(name, rt) }
},
// 设置头像: 账号, 访问令牌, 头像数据 返回结果
// @API
setHeadImage: (name, at, head) => {
if (!apis.checkAccessToken(name, at))
return { msg: "访问令牌不正确!", code: -1 }
io.open(vals.LINGCHAIR_USERS_HEAD_DIR + "/" + name + ".png", "w").write(head).close()
return { msg: "成功", code: 0 }
},
// 修改昵称
// @APi
setNick: (name, at, nick) => {
if (!apis.checkAccessToken(name, at))
return { msg: "访问令牌不正确!", code: -1 }
let path = getUserPath(name)
let configIo = io.open(path + "/user.json", "rw")
let config = configIo.readJson()
config.nick = nick
configIo.writeJson(config)
configIo.close()
return { msg: "成功", code: 0 }
},
// 取联系人列表(好友): 账号, 访问令牌 返回好友列表
getFriendsNonApi: (name, at) => {
let file = getUserPath(name) + "/friends.json"
if (!io.exists(file))
io.open(file, "w").writeJson({list: [name]}).close()
return io.open(file, "r").readJson().list
},
// 取用户昵称: 账号 返回昵称
getNickNonApi: (name) => {
let file = getUserPath(name) + "/user.json"
return io.open(file, "r").readJson().nick
},
// 取昵称: 账号 返回昵称
// @API
getNick: (name, at) => {
return { msg: "成功", code: 0, nick: apis.getNickNonApi(name)}
},
// 取联系人列表(好友): 账号, 访问令牌 返回好友列表
// @API
getFriends: (name, at) => {
if (!apis.checkAccessToken(name, at))
return { msg: "访问令牌不正确!", code: -1 }
return { msg: "成功", code: 0, friends: apis.getFriendsNonApi(name, at)}
},
}
module.exports = apis

13
server_src/color.js Normal file
View File

@@ -0,0 +1,13 @@
/*
* ©2024 满月叶
* Github: MoonLeeeaf
* 控制台颜色辅助类
*/
module.exports = {
none: "\033[0m",
red: "\033[1;31m",
green: "\033[1;32m",
yellow: "\033[1;33m",
blue: "\033[1;34m",
}

16
server_src/hashlib.js Normal file
View File

@@ -0,0 +1,16 @@
/*
* ©2024 满月叶
* Github: MoonLeeeaf
* 哈希辅助类
*/
const crypto = require("crypto")
let apis = {
sha256: (data) => crypto.createHash("sha256").update(data).digest("base64"),
md5: (data) => crypto.createHash("md5").update(data).digest("base64"),
sha256hex: (data) => crypto.createHash("sha256").update(data).digest("hex"),
md5hex: (data) => crypto.createHash("md5").update(data).digest("hex"),
}
module.exports = apis

17
server_src/httpApi.js Normal file
View File

@@ -0,0 +1,17 @@
/*
* ©2024 满月叶
* Github: MoonLeeeaf
* 铃之椅 Node 服务端
*/
// 不得不说 express 太强了
const vals = require("./val")
const express = require("express")
let api = express()
api.use("/", express.static("ling_chair_http"))
api.use("/users_head/", express.static(vals.LINGCHAIR_DATA_DIR + "/users_head"))
module.exports = api

48
server_src/iolib.js Normal file
View File

@@ -0,0 +1,48 @@
/*
* ©2024 满月叶
* Github: MoonLeeeaf
* 更简单地使用 FileSystem 库
*/
const fs = require("fs")
class IoImpl {
constructor(p, m) {
this.path = p
this.mode = m
}
write(byteOrString) {
fs.writeFileSync(this.path, byteOrString)
return this
}
read(type) {
// TODO: impentments this method
return fs.readFileSync(this.path)
}
close() {
delete this.path
delete this.mode
delete this.read
delete this.write
}
readJson() {
return JSON.parse(this.read("*a"))
}
writeJson(data) {
return this.write(JSON .stringify(data))
}
}
let apis = {
open: (path, mode) => {
return new IoImpl(path, mode)
},
mkdirs: (path) => {
try {fs.mkdirSync(path, { recursive: true })}catch(e){}
},
exists: (path) => {
return fs.existsSync(path)
},
}
module.exports = apis

93
server_src/main.js Normal file
View File

@@ -0,0 +1,93 @@
/*
* ©2024 满月叶
* Github: MoonLeeeaf
* 铃之椅 Node 服务端
*/
console.log("正在初始化...")
const log = (t) => {
console.log("[" + new Date().toLocaleTimeString('en-US', { hour12: false }) + "] " + t)
}
const sio = require("socket.io")
const http = require("http")
const https = require("https")
const fs = require("fs")
const process = require("process")
const vals = require("./val")
const color = require("./color")
//定义 Http 服务器回调
let httpServerCallback = require("./httpApi")
// 定义 Socket.io 服务器回调
let wsServerCallback = require("./wsApi")
let httpServer
if (vals.LINGCHAIR_SERVER_CONFIG.useHttps)
httpServer = https.createServer({
key: fs.readFileSync(vals.LINGCHAIR_SERVER_CONFIG.https.key),
cert: fs.readFileSync(vals.LINGCHAIR_SERVER_CONFIG.https.cert),
}, httpServerCallback)
else
httpServer = http.createServer(httpServerCallback)
let wsServer = new sio.Server(httpServer)
const cachedClients = {}
let checkEmpty = (i) => {
if (i instanceof Array) {
for (k in i) {
if (checkEmpty(i[k])) return true
}
}
return (i == null) || ("" === i) || (0 === i)
}
wsServer.on("connect", (client) => {
log("客户端 " + client.handshake.address + " 已连接, 用户名(未经验证): " + client.handshake.auth.name)
for (const cb in wsServerCallback) {
client.on(cb, (...args) => {
log("客户端 " + client.handshake.address + " 对接口 [" + cb + "] 发起请求,参数为 " + JSON.stringify(args[0]))
let callback = args[args.length - 1]
try {
wsServerCallback[cb](args[0], (reArgs) => {
callback(reArgs)
log("返回接口 [" + cb + "] 到 " + client.handshake.address + ",参数为 " + JSON.stringify(reArgs))
}, client, cachedClients)
} catch (e) {
log(color.yellow + "调用接口或返回数据时出错: " + e + color.none)
callback({ code: -1, msg: e })
}
})
}
client.on("disconnect", () => {
if (!client.handshake.auth.passCheck)
return log("未验证的客户端 " + client.handshake.address + " 已断开, 未验证的用户名: " + client.handshake.auth.name)
// 为了支持多客户端登录 我豁出去了
if (cachedClients[client.handshake.auth.name].length === 1)
cachedClients[client.handshake.auth.name] = null
else
cachedClients[client.handshake.auth.name].forEach((item, index, arr) => {
if (item == client) {
arr.splice(index, 1)
}
})
log("客户端 " + client.handshake.address + " 已断开, 用户名: " + client.handshake.auth.name)
})
})
httpServer.listen(vals.LINGCHAIR_SERVER_CONFIG.port)
console.log(color.red + "=== 铃之椅 - Server ===" + color.none + "\n\r")
console.log(color.yellow + "Github: MoonLeeeaf" + color.none)
log(color.green + "运行服务于端口 " + vals.LINGCHAIR_SERVER_CONFIG.port + " 上," + (vals.LINGCHAIR_SERVER_CONFIG.useHttps == true ? "已" : "未") + "使用 HTTPS" + color.none)
log(color.green + "服务已启动..." + color.none)

56
server_src/val.js Normal file
View File

@@ -0,0 +1,56 @@
/*
* ©2024 满月叶
* Github: MoonLeeeaf
* 铃之椅 Node 服务端
*/
const io = require("./iolib")
let vals = {}
// 配置目录
vals.LINGCHAIR_CONFIG_DIR = "ling_chair_config"
// HTTP 服务器资源目录
vals.LINGCHAIR_HTTP_DIR = "ling_chair_http"
// 服务端配置
vals.LINGCHAIR_SERVER_CONFIG_FILE = vals.LINGCHAIR_CONFIG_DIR + "/server.json"
// 主要数据目录
vals.LINGCHAIR_DATA_DIR = "ling_chair_data"
// 用户数据
vals.LINGCHAIR_USERS_DATA_DIR = vals.LINGCHAIR_DATA_DIR + "/users"
// 用户头像
vals.LINGCHAIR_USERS_HEAD_DIR = vals.LINGCHAIR_DATA_DIR + "/users_head"
// 群聊消息
vals.LINGCHAIR_GROUP_MESSAGE_DIR = vals.LINGCHAIR_DATA_DIR + "/messages/group"
// 单聊消息
vals.LINGCHAIR_SINGLE_MESSAGE_DIR = vals.LINGCHAIR_DATA_DIR + "/messages/single"
// 用户 ID 计次
vals.LINGCHAIR_USERS_COUNT_FILE = vals.LINGCHAIR_USERS_DATA_DIR + "/count.txt"
// 创建必备目录
io.mkdirs(vals.LINGCHAIR_CONFIG_DIR)
io.mkdirs(vals.LINGCHAIR_USERS_DATA_DIR)
io.mkdirs(vals.LINGCHAIR_USERS_HEAD_DIR)
io.mkdirs(vals.LINGCHAIR_GROUP_MESSAGE_DIR)
io.mkdirs(vals.LINGCHAIR_SINGLE_MESSAGE_DIR)
// 生成服务端配置文件
if (!io.exists(vals.LINGCHAIR_SERVER_CONFIG_FILE)) io.open(vals.LINGCHAIR_SERVER_CONFIG_FILE, "w").write(JSON.stringify({
useHttps: false,
port: 3601,
bindAddress: "",
https: {
key: "",
cert: "",
},
})).close()
if (!io.exists(vals.LINGCHAIR_USERS_COUNT_FILE)) io.open(vals.LINGCHAIR_USERS_COUNT_FILE, "w").write("10000").close()
// 加载服务端配置文件
vals.LINGCHAIR_SERVER_CONFIG = JSON.parse(io.open(vals.LINGCHAIR_SERVER_CONFIG_FILE, "r").read("*a"))
module.exports = vals

214
server_src/wsApi.js Normal file
View File

@@ -0,0 +1,214 @@
/*
* ©2024 满月叶
* Github: MoonLeeeaf
* 铃之椅 Node 服务端
*/
const log = (t) => {
console.log("[" + new Date().toLocaleTimeString('en-US', { hour12: false }) + "] " + t)
}
const msgs = require("./api-msgs")
const users = require("./api-users")
const color = require("./color")
let checkEmpty = (i) => {
if (i instanceof Array) {
for (k in i) {
if (checkEmpty(i[k])) return true
}
}
return (i == null) || ("" === i) || (0 === i)
}
/*
* Api 规范:
* 1. 禁止中文 拼音
* 2. 一个 Api 做一件事 同一组 Api 用注释行分隔
* 3. 尽可能简单易懂 或者打注释
* 4. 保证客户端可用
*/
// Api 调用:
// 一般规定, code=0 正常, code=-1 异常, code=-2 运行时错误 另外还需要 msg="any"
// 可以随便 return 进行函数中断 因为这里的调用不会取返回值
let api = {
// ---------- 用户 API ----------
// 验证
// 调用方法自己看
"user.auth": (a, cb, client, cachedClients) => {
if (checkEmpty([a.name, a.refreshToken]))
return cb({ msg: "参数缺失", code: -1 })
if (!users.checkRefreshToken(a.name, a.refreshToken))
return cb({ code: -1, msg: "刷新令牌错误" })
log(color.yellow + "客户端 " + client.handshake.address + " 完成了用户 " + a.name + " 的验证" + color.none)
// 更新映射
client.handshake.auth.passCheck = true
if (cachedClients[a.name] == null)
cachedClients[a.name] = []
cachedClients[a.name].push(client)
cb({ code: 0, msg: "成功" })
},
// 注册
// {name: 账号, nick: 昵称, passwd: 密码} 返回 {data: {uid: 账号ID}}
// 密码在客户端应该经过哈希处理 算法为 SHA256+MD5
// 客户端在注册成功之后应该引导用户登录
"user.signUp": (a, cb) => {
if (checkEmpty([a.name, a.passwd]))
return cb({ msg: "参数缺失", code: -1 })
let { uid, msg, code } = users.signUp(a.name, a.passwd)
if (code !== 0)
return cb({ msg: msg, code: code })
cb({ msg: msg, code: 0, data: { uid: uid } })
},
// 登录
// {name: 账号, passwd: 密码} 返回 {data: {refreshToken: 刷新令牌}}
// 密码在客户端应该经过哈希处理 算法为 SHA256+MD5
"user.signIn": (a, cb) => {
if (checkEmpty([a.name, a.passwd]))
return cb({ msg: "参数缺失", code: -1 })
let { refreshToken, msg, code } = users.signIn(a.name, a.passwd)
if (code !== 0)
return cb({ msg: msg, code: code })
cb({ msg: msg, code: 0, data: { refreshToken: refreshToken } })
},
// 获取访问令牌
// {name: 账号, refreshToken: 刷新令牌} 返回 {data: {accessToken: 访问令牌}}
"user.getAccessToken": (a, cb) => {
if (checkEmpty([a.name, a.refreshToken]))
return cb({ msg: "参数缺失", code: -1 })
let { accessToken, msg, code } = users.getAccessToken(a.name, a.refreshToken)
if (code !== 0)
return cb({ msg: msg, code: code })
cb({ msg: msg, code: 0, data: { accessToken: accessToken } })
},
// 上传头像
// {name: 账号, accessToken: 访问令牌, headImage: 头像数据} 返回 {}
"user.setHeadImage": (a, cb) => {
if (checkEmpty([a.name, a.accessToken, a.headImage]))
return cb({ msg: "参数缺失", code: -1 })
let { msg, code } = users.setHeadImage(a.name, a.accessToken, a.headImage)
if (code !== 0)
return cb({ msg: msg, code: code })
cb({ msg: msg, code: 0 })
},
// 修改昵称
"user.setNick": (a, cb) => {
if (checkEmpty([a.name, a.accessToken, a.nick]))
return cb({ msg: "参数缺失", code: -1 })
let { msg, code } = users.setNick(a.name, a.accessToken, a.nick)
if (code !== 0)
return cb({ msg: msg, code: code })
cb({ msg: msg, code: 0 })
},
// ---------- 联系人 API --------
// 获取好友列表
// {name: 账号, accessToken: 访问令牌} 返回 {friends: []}
"user.getFriends": (a, cb) => {
if (checkEmpty([a.name, a.accessToken]))
return cb({ msg: "参数缺失", code: -1 })
let { msg, code, friends } = users.getFriends(a.name, a.accessToken)
if (code !== 0)
return cb({ msg: msg, code: code })
cb({ msg: msg, code: 0, data: { friends: friends } })
},
"user.getNick": (a, cb) => {
if (checkEmpty([a.name]))
return cb({ msg: "参数缺失", code: -1 })
let { msg, code, nick } = users.getNick(a.name)
if (code !== 0)
return cb({ msg: msg, code: code })
cb({ msg: msg, code: 0, data: { nick: nick } })
},
// ---------- 通讯 API ----------
// 单聊发送消息
// {name: 当前用户, target: 发送到, accessToken: 访问密钥, msg: 消息内容}
// 2024.3.30: 支持对方收到消息
"user.sendSingleMsg": (a, cb, c, cache) => {
if (checkEmpty([a.name, a.target, a.accessToken, a.msg]))
return cb({ msg: "参数缺失", code: -1 })
let { msg, code, msgid, time } = msgs.sendSingleMsg(a.name, a.accessToken, a.target, a.msg)
if (code !== 0)
return cb({ msg: msg, code: code })
// 微机课闲的没事干玩玩 发现私聊会多发一个(一个是本地的, 另一个是发送成功的) 选择一个关掉就好了
// 这里我选择客户端, 否则没法多设备同步
let args = {
target: a.name,
msg: {
msgid: msgid,
time: time,
msg: a.msg,
name: a.name,
},
type: "single",
}
if (cache[a.target] != null)
cache[a.target].forEach((v) => {
v.emit("msg.receive", args, () => { })
log("尝试向客户端 " + v.handshake.address + " 发送事件 [msg.receive], 参数为 " + JSON.stringify(args))
})
cb({ msg: msg, code: 0, data: { time: time } })
},
// 单聊获取历史记录
// {name: 当前用户, target: 聊天目标, accessToken: 访问密钥, startId: 计次开始的msgid, limit: 最大返回数(最大100)}
"user.getSingleChatHistroy": (a, cb) => {
if (checkEmpty([a.name, a.target, a.accessToken, a.limit]))
return cb({ msg: "参数缺失", code: -1 })
let { msg, code, histroy } = msgs.getSingleMsgHistroy(a.name, a.accessToken, a.target, a.startId, a.limit)
if (code !== 0)
return cb({ msg: msg, code: code })
cb({ msg: msg, code: 0, data: { histroy: histroy } })
},
}
module.exports = api