Skip to content

Commit

Permalink
feat: basic implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
MaikoTan committed May 27, 2023
1 parent d002cd0 commit 15dd1fa
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 22 deletions.
70 changes: 67 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,74 @@
# koishi-template
# koishi-plugin-bang

A template repository for creating koishi plugins
bang (!) command implementation in Koishi

## Usage

Click the [`Use this template`](https://github.com/AwesomeHamster/koishi-template/generate) button to create a new repository.
Just like the bang command in bash, you can use `!` to introduce a command that you have previously issued.


### `!!` - Repeat the last command

```bash
$ echo hello
hello
$ !!
hello
```

### `!n` - Repeat the nth command

```bash
$ echo hello
hello
$ echo world
world
$ !1
hello
```

### `!-n` - Repeat the command n steps back

```bash
$ echo hello
hello
$ echo world
world
$ !-1
world
```

### `!string` - Repeat the last command starting with `string`

```bash
$ echo hello
hello
$ echo world
world
$ !e
world
```

### `!?string` - Repeat the last command containing `string`

```bash
$ echo hello
hello
$ echo world
world
$ !?l
world
```

### `:p` suffix - Print the command instead of executing it

```bash
$ echo hello
hello
$ !!:p
echo hello
```


## License

Expand Down
22 changes: 12 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
{
"name": "koishi-template",
"name": "koishi-plugin-bang",
"version": "1.0.0",
"description": "A template repository for creating koishi plugins",
"description": "bang (!) command implementation in Koishi",
"keywords": [
"bot",
"qqbot",
"chatbot",
"plugin",
"koishi",
"koishijs"
"koishijs",
"linux-command",
"bang"
],
"author": {
"name": "Maiko Tan",
"email": "[email protected]"
},
"homepage": "https://github.com/AwesomeHamster/koishi-template",
"homepage": "https://github.com/AwesomeHamster/koishi-plugin-bang",
"license": "MIT",
"main": "dist/index.bundle.js",
"directories": {
Expand All @@ -29,7 +31,7 @@
"types": "dist/index.d.ts",
"repository": {
"type": "git",
"url": "git+https://github.com/AwesomeHamster/koishi-template.git"
"url": "git+https://github.com/AwesomeHamster/koishi-plugin-bang.git"
},
"scripts": {
"build": "constructeur build && tsc --emitDeclarationOnly",
Expand All @@ -41,15 +43,15 @@
"prettier": "prettier '**/*.{js,ts,json,yml,yaml,md}' '!dist/**/*'"
},
"bugs": {
"url": "https://github.com/AwesomeHamster/koishi-template/issues"
"url": "https://github.com/AwesomeHamster/koishi-plugin-bang/issues"
},
"koishi": {
"description": {
"en": "Koishi Template",
"zh": "Koishi Template"
"en": "bang (!) command implementation",
"zh": "bang (!) 指令实现"
},
"service": {
"required": [],
"required": ["database"],
"optional": [],
"implements": []
},
Expand All @@ -61,7 +63,7 @@
},
"prettier": "@hamster-bot/prettier-config",
"peerDependencies": {
"koishi": "^4.8.5"
"koishi": "^4.10.1"
},
"devDependencies": {
"@hamster-bot/constructeur": "*",
Expand Down
3 changes: 0 additions & 3 deletions src/i18n/zh/template.yml

This file was deleted.

99 changes: 93 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,98 @@
import { Context, Schema } from 'koishi'
import { Context, Schema, Session } from 'koishi'

export interface Config {}
declare module 'koishi' {
export interface Tables {
bang: {
session: string
commands: string[]
}
}
}

export interface Config {
history: number
bang: string
}

export const Config: Schema<Config> = Schema.object({
history: Schema.number().default(500).description('Number of commands to remember'),
bang: Schema.string().default('!').description('Change it if you want to use other trigger'),
})

export const name = 'bang'

export const using = ['database']

export async function apply(ctx: Context, config: Config): Promise<void> {
ctx.i18n.define('en-US', require('./locales/en-US.yml'))
ctx.i18n.define('en', require('./locales/en-US.yml'))

ctx.model.extend('bang', {
session: { type: 'string' },
commands: { type: 'list' },
}, { primary: 'session' })

ctx.before('command/execute', async (argv) => {
if (!argv.session || !argv.source) return

const ori = (await ctx.database.get('bang', argv.session.fid))[0]
if (ori) {
ori.commands.push(argv.source)
ori.commands = ori.commands.slice(0, config.history)
ctx.database.set('bang', argv.session.fid, { commands: ori.commands })
} else {
ctx.database.create('bang', { session: argv.session?.fid, commands: [argv.source] })
}
}, true)

async function executeCommand(session: Session, command: string, onlyPrint = false) {
if (onlyPrint) {
return command
}

return await session.execute(command)
}

ctx.middleware(async (session, next) => {
if (!session.content.trim().startsWith(config.bang)) {
return await next()
}

export const Config: Schema<Config> = Schema.object({})
let rest = session.content.trim().slice(config.bang.length)
const history = (await ctx.database.get('bang', session.fid))[0]
if (!history) session.text('bang.errors.no-history')

export const name = 'koishi-template'
// parse `:p` suffix, which should print the command instead of executing it
let onlyPrint = false
if (rest.endsWith(':p')) {
rest = rest.slice(0, -2)
onlyPrint = true
}
// TODO: support `:s` suffix, which can be used to substitute the command
// TODO: support `:n` suffix (while n is a number), which can be used to capture the nth argument
// Also `:$` for the last argument.

export async function apply(ctx: Context): Promise<void> {
// ...
if (/^!$/.test(rest)) {
// parse bangbang like !!
const last = history.commands[history.commands.length - 1]
return await executeCommand(session, last, onlyPrint)
} else if (/^(-?\d+)$/.test(rest)) {
// parse numbers like !1 or !-1
const index = parseInt(rest)
if (index === 0) return session.text('bang.errors.invalid-index')
const command = history.commands[index > 0 ? index - 1 : history.commands.length + index]
if (!command) return session.text('bang.errors.invalid-index')
return await executeCommand(session, command, onlyPrint)
} else if (/^(\w+)$/.test(rest)) {
// parse strings like !ls
const command = history.commands.find(c => c.startsWith(rest))
if (!command) return session.text('bang.errors.command-not-found')
return await executeCommand(session, command, onlyPrint)
} else if (/^\?\w+$/.test(rest)) {
// parse non-preceding strings like !?test.txt => !ls test.txt
const command = history.commands.find(c => c.includes(rest.slice(1)))
if (!command) return session.text('bang.errors.command-not-found')
return await executeCommand(session, command, onlyPrint)
}
})
}
7 changes: 7 additions & 0 deletions src/locales/en-US.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
bang:
errors:
no-history: There is no command history.

invalid-index: Invalid index for fetching command history.

command-not-found: Command not found.

0 comments on commit 15dd1fa

Please sign in to comment.