Skip to content

Commit

Permalink
feat(plugin-common): optimize admin command
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Jan 14, 2020
1 parent 22bbfbe commit 0cc58cf
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 21 deletions.
2 changes: 2 additions & 0 deletions packages/koishi-core/src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export enum GroupFlag {
noEmit = 4,
}

export const groupFlags: (keyof typeof GroupFlag)[] = ['noCommand', 'noResponse', 'noEmit']

export type Group<K extends GroupField = GroupField> = Observed<Pick<GroupData, K | 'id'>>
export type GroupField = keyof GroupData
export const groupFields: GroupField[] = []
Expand Down
63 changes: 42 additions & 21 deletions packages/plugin-common/src/admin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Context, User, userFlags, UserFlag, Meta, UserField, getTargetId, CommandConfig, GroupField, UserData, GroupData } from 'koishi-core'
import { Context, User, userFlags, UserFlag, Meta, UserField, getTargetId, CommandConfig, GroupField, UserData, GroupData, GroupFlag, groupFlags, Group, userFields, groupFields } from 'koishi-core'
import { isInteger, difference, Observed, paramCase } from 'koishi-utils'

type ActionCallback <T extends {}, K extends keyof T> =
Expand All @@ -17,11 +17,11 @@ export interface GroupAction {
const userActionMap: Record<string, UserAction> = {}
const groupActionMap: Record<string, GroupAction> = {}

export function registerUserAction <K extends UserField> (name: string, callback: ActionCallback<UserData, K>, fields: K[] = []) {
export function registerUserAction <K extends UserField> (name: string, callback: ActionCallback<UserData, K>, fields?: K[]) {
userActionMap[paramCase(name)] = { callback, fields }
}

export function registerGroupAction <K extends GroupField> (name: string, callback: ActionCallback<GroupData, K>, fields: K[] = []) {
export function registerGroupAction <K extends GroupField> (name: string, callback: ActionCallback<GroupData, K>, fields?: K[]) {
groupActionMap[paramCase(name)] = { callback, fields }
}

Expand All @@ -36,7 +36,7 @@ registerUserAction('setAuth', async (meta, user, value) => {
await user._update()
return meta.$send('用户权限已修改。')
}
})
}, ['authority'])

registerUserAction('setFlag', async (meta, user, ...flags) => {
if (!flags.length) return meta.$send(`可用的标记有 ${userFlags.join(', ')}。`)
Expand All @@ -47,18 +47,18 @@ registerUserAction('setFlag', async (meta, user, ...flags) => {
}
await user._update()
return meta.$send('用户信息已修改。')
})
}, ['flag'])

registerUserAction('unsetFlag', async (meta, user, ...flags) => {
if (!flags.length) return meta.$send(`可用的 flag 有:${userFlags.join(', ')}。`)
if (!flags.length) return meta.$send(`可用的标记有 ${userFlags.join(', ')}。`)
const notFound = difference(flags, userFlags)
if (notFound.length) return meta.$send(`未找到标记 ${notFound.join(', ')}。`)
for (const name of flags) {
user.flag &= ~UserFlag[name]
}
await user._update()
return meta.$send('用户信息已修改。')
})
}, ['flag'])

registerUserAction('clearUsage', async (meta, user, ...commands) => {
if (commands.length) {
Expand All @@ -70,7 +70,7 @@ registerUserAction('clearUsage', async (meta, user, ...commands) => {
}
await user._update()
return meta.$send('用户信息已修改。')
})
}, ['usage'])

registerUserAction('showUsage', async (meta, user, ...commands) => {
const { usage } = user
Expand All @@ -80,7 +80,29 @@ registerUserAction('showUsage', async (meta, user, ...commands) => {
'用户今日各指令的调用次数为:',
...commands.sort().map(name => `${name}${usage[name] ? usage[name].count : 0} 次`),
].join('\n'))
})
}, ['usage'])

registerGroupAction('setFlag', async (meta, group, ...flags) => {
if (!flags.length) return meta.$send(`可用的标记有 ${groupFlags.join(', ')}。`)
const notFound = difference(flags, groupFlags)
if (notFound.length) return meta.$send(`未找到标记 ${notFound.join(', ')}。`)
for (const name of flags) {
group.flag |= GroupFlag[name]
}
await group._update()
return meta.$send('群信息已修改。')
}, ['flag'])

registerGroupAction('unsetFlag', async (meta, group, ...flags) => {
if (!flags.length) return meta.$send(`可用的标记有 ${groupFlags.join(', ')}。`)
const notFound = difference(flags, groupFlags)
if (notFound.length) return meta.$send(`未找到标记 ${notFound.join(', ')}。`)
for (const name of flags) {
group.flag &= ~GroupFlag[name]
}
await group._update()
return meta.$send('群信息已修改。')
}, ['flag'])

export default function apply (ctx: Context, options: CommandConfig) {
const userActions = Object.keys(userActionMap).map(paramCase).join(', ')
Expand All @@ -92,35 +114,34 @@ export default function apply (ctx: Context, options: CommandConfig) {
.option('-G, --this-group', '指定目标群为本群')
.action(async ({ meta, options }, name: string, ...args: string[]) => {
const isGroup = 'g' in options || 'G' in options
if ('user' in options && isGroup) {
return meta.$send('不能同时目标为指定用户和群。')
}
if ('user' in options && isGroup) return meta.$send('不能同时目标为指定用户和群。')

const actionList = isGroup ? groupActions : userActions
const actionMap = isGroup ? groupActionMap : userActionMap
if (!name) {
return meta.$send(`当前的可用指令有:${actionList}。`)
}
if (!name) return meta.$send(`当前的可用指令有:${actionList}。`)

const action = actionMap[paramCase(name)]
if (!action) return meta.$send(`指令未找到。当前的可用指令有:${actionList}。`)

if (isGroup) {
let group: Observed<GroupData>
const fields = action.fields ? action.fields.slice() as GroupField[] : groupFields
let group: Group
if (options.thisGroup) {
group = await ctx.database.observeGroup(meta.$group)
} else if (typeof options.group === 'number') {
group = await ctx.database.observeGroup(options.group)
group = await ctx.database.observeGroup(meta.$group, fields)
} else if (isInteger(options.group) && options.group > 0) {
group = await ctx.database.observeGroup(options.group, fields)
}
if (!group) return meta.$send('未找到指定的群。')
return action.callback.call(ctx, meta, group, ...args)
} else {
const fields = action.fields.slice() as UserField[]
const fields = action.fields ? action.fields.slice() as UserField[] : userFields
if (!fields.includes('authority')) fields.push('authority')
let user: User
if (options.user) {
const qq = getTargetId(options.user)
if (!qq) return meta.$send('未指定目标。')
user = await ctx.database.observeUser(qq, -1, fields)
if (!user) return meta.$send('未找到用户。')
if (!user) return meta.$send('未找到指定的用户。')
if (qq !== meta.$user.id && meta.$user.authority <= user.authority) return meta.$send('权限不足。')
} else {
user = await ctx.database.observeUser(meta.$user, 0, fields)
Expand Down
9 changes: 9 additions & 0 deletions packages/plugin-common/tests/__snapshots__/admin.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`group operations list actions: admin -G 1`] = `"当前的可用指令有:set-flag, unset-flag。"`;

exports[`group operations list actions: admin -G foo 1`] = `"指令未找到。当前的可用指令有:set-flag, unset-flag。"`;

exports[`user operations list actions: admin 1`] = `"当前的可用指令有:set-auth, set-flag, unset-flag, clear-usage, show-usage。"`;

exports[`user operations list actions: admin foo 1`] = `"指令未找到。当前的可用指令有:set-auth, set-flag, unset-flag, clear-usage, show-usage。"`;
113 changes: 113 additions & 0 deletions packages/plugin-common/tests/admin.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { MockedApp, MemoryDatabase } from 'koishi-test-utils'
import { registerDatabase, userFlags, groupFlags, UserFlag, GroupFlag } from 'koishi-core'
import admin, { registerUserAction, registerGroupAction } from '../src/admin'

registerDatabase('memory', MemoryDatabase)

const app = new MockedApp({ database: { memory: {} } })
const session = app.createSession('group', 123, 321)

app.plugin(admin)
app.command('foo', { maxUsage: 10 }).action(({ meta }) => meta.$send('bar'))
app.command('bar', { maxUsage: 10 }).action(({ meta }) => meta.$send('foo'))

beforeAll(async () => {
await app.start()
await app.database.getUser(123, 4)
await app.database.getUser(456, 3)
await app.database.getUser(789, 4)
await app.database.getGroup(321, app.selfId)
await app.database.getGroup(654, app.selfId)
})

describe('basic features', () => {
test('check', async () => {
await session.shouldHaveReply('admin -u 456 -g 321', '不能同时目标为指定用户和群。')
})
})

describe('user operations', () => {
test('list actions', async () => {
await session.shouldMatchSnapshot('admin')
await session.shouldMatchSnapshot('admin foo')
})

test('check target', async () => {
await session.shouldHaveReply('admin -u bar set-flag', '未指定目标。')
await session.shouldHaveReply('admin -u 233 set-flag', '未找到指定的用户。')
await session.shouldHaveReply('admin -u 789 show-usage', '权限不足。')
})

test('setAuth', async () => {
await session.shouldHaveReply('admin -u 456 set-auth -1', '参数错误。')
await session.shouldHaveReply('admin -u 456 set-auth 3', '用户权限未改动。')
await session.shouldHaveReply('admin -u 456 set-auth 2', '用户权限已修改。')
await session.shouldHaveReply('admin -u 456 set-auth 4', '权限不足。')
})

test('setFlag', async () => {
await session.shouldHaveReply('admin -u 456 set-flag', `可用的标记有 ${userFlags.join(', ')}。`)
await session.shouldHaveReply('admin -u 456 set-flag foo', '未找到标记 foo。')
await session.shouldHaveReply('admin -u 456 set-flag ignore', '用户信息已修改。')
await expect(app.database.getUser(456)).resolves.toHaveProperty('flag', UserFlag.ignore)
})

test('unsetFlag', async () => {
await session.shouldHaveReply('admin -u 456 unset-flag', `可用的标记有 ${userFlags.join(', ')}。`)
await session.shouldHaveReply('admin -u 456 unset-flag foo', '未找到标记 foo。')
await session.shouldHaveReply('admin -u 456 unset-flag ignore', '用户信息已修改。')
await expect(app.database.getUser(456)).resolves.toHaveProperty('flag', 0)
})

test('showUsage', async () => {
await session.shouldHaveReply('admin show-usage', '用户今日没有调用过指令。')
await session.shouldHaveReply('foo', 'bar')
await session.shouldHaveReply('admin show-usage', '用户今日各指令的调用次数为:\nfoo:1 次')
await session.shouldHaveReply('admin show-usage foo bar', '用户今日各指令的调用次数为:\nbar:0 次\nfoo:1 次')
})

test('clearUsage', async () => {
await session.shouldHaveReply('bar', 'foo')
await session.shouldHaveReply('admin clear-usage foo', '用户信息已修改。')
await session.shouldHaveReply('admin show-usage', '用户今日各指令的调用次数为:\nbar:1 次')
await session.shouldHaveReply('admin clear-usage', '用户信息已修改。')
await session.shouldHaveReply('admin show-usage', '用户今日没有调用过指令。')
})
})

describe('group operations', () => {
test('list actions', async () => {
await session.shouldMatchSnapshot('admin -G')
await session.shouldMatchSnapshot('admin -G foo')
})

test('check target', async () => {
await session.shouldHaveReply('admin -g bar set-flag', '未找到指定的群。')
})

test('setFlag', async () => {
await session.shouldHaveReply('admin -G set-flag', `可用的标记有 ${groupFlags.join(', ')}。`)
await session.shouldHaveReply('admin -g 654 set-flag foo', '未找到标记 foo。')
await session.shouldHaveReply('admin -g 654 set-flag noCommand noEmit', '群信息已修改。')
await expect(app.database.getGroup(654)).resolves.toHaveProperty('flag', GroupFlag.noCommand | GroupFlag.noEmit)
})

test('unsetFlag', async () => {
await session.shouldHaveReply('admin -G unset-flag', `可用的标记有 ${groupFlags.join(', ')}。`)
await session.shouldHaveReply('admin -g 654 unset-flag foo', '未找到标记 foo。')
await session.shouldHaveReply('admin -g 654 unset-flag noEmit noResponse', '群信息已修改。')
await expect(app.database.getGroup(654)).resolves.toHaveProperty('flag', GroupFlag.noCommand)
})
})

describe('custom actions', () => {
test('user action', async () => {
registerUserAction('test', meta => meta.$send('foo'))
await session.shouldHaveReply('admin test', 'foo')
})

test('group action', async () => {
registerGroupAction('test', meta => meta.$send('bar'))
await session.shouldHaveReply('admin -G test', 'bar')
})
})

0 comments on commit 0cc58cf

Please sign in to comment.