所有插件都可以共同使用一个交易市场,这思路是来源于 G胖 的建立
稀有道具可以售卖、购买;卖出后为了买另一个游戏的道具。这些思路通过这个插件可以完成。
不同的插件,通过设置唯一的 by 值,交易市场可以做出每个业务事件触发进行隔离
通过插件作者自己控制商品的走向,交易市场只提供展示和实现交易业务的媒介
为此,随手做了一个引用该服务做出的 demo,可以运行或者研究一下:
import { Context, Schema, Session, h } from 'koishi'
import { } from 'koishi-plugin-smmcat-economy'
import { } from 'koishi-plugin-smmcat-localstorage'
import { } from 'koishi-plugin-monetary'
import { Economy_BuyEventData, Economy_EventData, Economy_RecycleData } from 'koishi-plugin-smmcat-economy/lib/type'
import fs from 'fs/promises'
import path from 'path'
export const name = 'smmcat-trydemo'
export interface Config { }
export const inject = ['economy', 'localstorage', 'database', "monetary"]
export const Config: Schema<Config> = Schema.object({})
export function apply(ctx: Context, config: Config) {
/** 商品清单 */
type FoodItem = {
name: string,
pic: string,
type: string,
price: number,
info: string
}
/** 用户仓库 */
type UserStash = {
[key: string]: FoodItem & {
total: number
}
}
let foodList: FoodItem[] = []
async function initStoreFoodData() {
foodList = JSON.parse(await fs.readFile(path.join(__dirname, './data.json'), 'utf-8'))
}
const shop = {
userDataList: {},
/** 数据初始化 */
async init() {
const upath = path.join(ctx.localstorage.basePath, 'smmcat-shop')
try {
await fs.access(upath)
} catch (error) {
fs.mkdir(upath, { recursive: true })
}
const dirList = await fs.readdir(upath)
const temp = {}
const eventList = dirList.map((item) => {
return new Promise(async (resolve) => {
try {
temp[item] = JSON.parse(await ctx.localstorage.getItem(`smmcat-shop/${item}`))
resolve(true)
} catch (error) {
resolve(true)
}
})
})
await Promise.all(eventList)
this.userDataList = temp
},
/** 上架商品 */
async uploadProduct(session: Session<'id'>, productName: string, price: number, quantity = 1) {
console.log(session.user.id);
const userId = session.userId
const userData: UserStash = this.initUserData(userId)
if (!userData[productName]) {
await session.send('你的仓库中没有该商品')
return
}
const productItem = userData[productName]
await session.send(`${productName}的市场价为:${productItem.price};\n你即将售卖的单价为:${price}`)
await session.send(`是否上架?\n(20秒回复 "是" 则上架)`)
await session.prompt(20000)
if ((productItem.total - quantity) <= 0) {
await session.send('数量不足,无法上架')
return
}
productItem.total -= quantity
const upValue = {
pic: productItem.pic,
name: productItem.name,
total: quantity,
price,
uid: session.user.id,
by: 'smmcat-shop',
type: productItem.type,
isSell: true
}
if (productItem.total == 0) {
delete userData[productName]
}
await ctx.economy.sellMarketSomeItem(session, upValue)
this.updataStore(userId)
await session.send('提交完成')
},
/** 用户信息初始化 */
initUserData(userId: string) {
if (!this.userDataList[userId]) {
this.userDataList[userId] = {}
const rowData = foodList[Math.floor(Math.random() * foodList.length)]
console.log(rowData);
this.userDataList[userId][rowData.name] = { ...rowData, total: 20 }
this.updataStore(userId)
}
return this.userDataList[userId]
},
/** 数据本地存储 */
async updataStore(userId: string) {
const dataList = this.userDataList[userId]
await ctx.localstorage.setItem(`smmcat-shop/${userId}`, JSON.stringify(dataList))
}
}
ctx.economy.on('buying', async (event: Economy_EventData<Economy_BuyEventData>) => {
const buy_userId = event.session.userId
const userStash: UserStash = shop.initUserData(buy_userId)
await event.session.send(
`${h.image(event.upValue.pic)}` +
`商品名:${event.upValue.name}\n` +
`下单数量:${event.upValue.quantity}\n` +
`总价:${event.upValue.total}\n` +
'请在20秒内决定购买.\n如若购买请发 "是"')
const res = await event.session.prompt(20000)
if (res === undefined) {
await event.session.send('超时结束...')
return
}
if (res.trim() === "是") {
const productItem = foodList.find((item) => item.name === event.upValue.name)
if (!productItem) {
await event.session.send('找不到该商品信息,操作失败')
return
}
try {
console.log(event.session.id);
console.log(event.upValue.uid);
// 结算账单
await ctx.monetary.cost(event.session.user.id, event.upValue.total)
await ctx.monetary.gain(event.upValue.uid, event.upValue.total)
} catch (error) {
await event.session.send('操作失败,余额不足')
return
}
const getData = { ...productItem, total: event.upValue.quantity }
if (!userStash[event.upValue.name]) {
userStash[event.upValue.name] = getData
} else {
userStash[event.upValue.name].total += event.upValue.quantity
}
event.upValue.isBuy = true
shop.updataStore(buy_userId)
await event.session.send('购买完成...')
}
}, 'smmcat-shop')
ctx.command("查看余额").userFields(["id"]).action(async ({ session }) => {
const uid = session.user.id;
const [data] = await ctx.database.get("monetary", { uid });
if (!data) {
const num = 1000;
ctx.monetary.gain(uid, num);
await session.send(`首次玩家,赠您1000积分`);
return;
}
await session.send(`您当前积分为:${data.value.toString()}`);
})
ctx
.command('商品上架 <productName> <quantity:number>').userFields(["id"])
.action(async ({ session }, productName, quantity = 1) => {
const userStash = shop.initUserData(session.userId)
console.log(userStash);
if (!productName) {
await session.send('请输入要上架市场的物品')
return
}
if (!userStash[productName]) {
await session.send('你的仓库不存在该物品')
return
}
await session.send('您准备对商品定价多少?(20秒内回复)')
const res = await session.prompt(20000)
if (res === undefined) return
if (isNaN(Number(res))) {
await session.send('输入有误,结束提交')
return
}
const price = Math.floor(Number(res))
await shop.uploadProduct(session, productName, price, quantity)
})
ctx
.command('查看库存')
.action(async ({ session }) => {
const userId = session.userId
const userStash: UserStash = shop.initUserData(userId)
const stashKeyList = Object.keys(userStash)
if (!stashKeyList.length) {
await session.send('你当前没有任何库存')
return
}
const msg = stashKeyList.map((item) => {
return `[${userStash[item].name}] 持有 ${userStash[item].total} (市场价:${userStash[item].price})`
}).join('\n')
await session.send(msg)
})
ctx.economy.on('recycle', async (event: Economy_EventData<Economy_RecycleData>) => {
const userId = event.session.userId
const userStash: UserStash = shop.initUserData(userId)
event.upValue.forEach((item) => {
// 操作每个数据退回到用户仓库
const productItem = foodList.find((i) => i.name === item.name)
if (!productItem) return
const getData = { ...productItem, total: item.total }
if (!userStash[item.name]) {
userStash[item.name] = getData
} else {
userStash[item.name].total += item.total
}
})
// 操作完成后,通知服务
shop.updataStore(userId)
event.isOver = true
}, 'smmcat-shop')
ctx.on('ready', () => {
initStoreFoodData()
shop.init()
})
}
对应的数据 data.json
来源
[
{
"name": "猪脚饭",
"pic": "https://smmcat.cn/run/fish/props/64/猪脚饭.png",
"type": "食物",
"price": 13,
"info": "打工人的一碗猪脚饭"
},
{
"name": "烧鸡",
"pic": "https://smmcat.cn/run/fish/props/64/水果鸡肉.png",
"type": "食物",
"price": 15,
"info": "真是一个不折不扣的烧鸡"
},
{
"name": "维他柠檬茶",
"pic": "https://smmcat.cn/run/fish/props/64/快乐水.png",
"type": "饮料",
"price": 6,
"info": "维他柠檬茶,爽过吸大码"
}
]
效果展示
上架操作
购买操作
收益统计
商店日志
服务自带指令