owned this note
owned this note
Published
Linked with GitHub
# GLaDOS Plugin Guide
このドキュメントは現在執筆中です。
実際に動作しているサンプルは [GLaDOS-bot-plugins](https://github.com/NephyProject/GLaDOS-bot-plugins) を参照してください。
```kotlin=
import jp.nephy.glados.core.plugins.Plugin
import net.dv8tion.jda.core.events.ReadyEvent
import java.util.*
import java.util.concurrent.TimeUnit
object ExamplePlugin: Plugin() {
override suspend fun onReady(event: ReadyEvent) {
logger.info { "Discord に接続しました。" }
}
@Command(description = "Ping Pong")
suspend fun ping(event: Command.Event) {
event.embedResult {
"🏓 Pong!"
}.await()
}
@Loop(10, TimeUnit.SECONDS)
fun everyTenSeconds() {
println(":)")
}
@Schedule(multipleHours = [1])
fun everyHours() {
val calendar = Calendar.getInstance()
logger.info { "Current hour is ${calendar.get(Calendar.HOUR)}." }
}
@Web.Page("/v1/test", "api.example.com")
suspend fun test(event: Web.AccessEvent) {
event.call.respondText {
"Request OK!"
}
}
}
```
以下の例では特記なき場合 `import` は省略して記述します。
## クラス宣言
### Plugin クラス
すべてのプラグインは `Plugin()` を継承する必要があります。継承していないクラスはスキップされます。
```kotlin=
// OK
object ValidPlugin: Plugin()
// NG
class InvalidPlugin: Map<String, String> {
// ...
}
```
プラグインは `jp.nephy.glados.core.plugins.EventModel`, `CoroutineScope`, `Closeable` を継承しています。
#### EventModel
GLaDOS で扱えるイベントハンドラが Empty Body でデフォルト実装されたインターフェイスです。すべてのイベントハンドラは中断関数(`suspend fun`)です。
利用可能なイベントハンドラは [EventModel.kt](https://github.com/NephyProject/GLaDOS-bot/blob/master/src/main/kotlin/jp/nephy/glados/core/plugins/EventModel.kt)にすべて宣言されています。
```kotlin=
override suspend fun onTweetstormStatus(event: StatusEvent) {
println(status.fullText())
}
```
#### CoroutineScope
Kotlin 1.3 で正式版に移行した Coroutine の `coroutineContext` が実装されています。
Plugin クラス内では `launch {}`, `async {}` などの Job ビルダーのスコープが既に定義されているので `GlobalScope` 等を使用する必要はありません。
Job は GLaDOS の管理する ThreadDispatcher で処理されます。
```kotlin=
override suspend fun onReady(event: ReadyEvent) {
launch {
delay(10000)
logger.info { "10 secs!" }
}
}
```
#### Closeable
`close()` が Empty Body でデフォルト実装されています。必要に応じて `override` することで Unload 時の挙動を実装できます。(Plugin Unload は未実装です)
```kotlin=
object CloseablePlugin: Plugin() {
private val httpClient = HttpClient()
override fun close() {
httpClient.close()
}
}
```
### visibility
プラグインは `public` の可視性で宣言する必要があります。`internal`, `protected`, `private` のクラスはロードされません。
```kotlin=
// OK
object PublicPlugin: Plugin()
// NG
private object PrivatePlugin: Plugin()
```
### object v.s. class
プラグインは `object` で宣言することを推奨します。なぜならプラグインは ロード時にのみインスタンス化され `static` のような振る舞いをするからです。
`object` で宣言することで別の プラグイン から容易に参照できるようになります。
```kotlin=
object PluginA: Plugin() {
const val specialString = "AAAAA"
}
object PluginB: Plugin() {
fun func() {
val a = PluginA.specialString
// ...
}
}
```
### constructor
`object` 宣言が推奨されることから分かるように プラグインは プライマリ コンストラクタに引数を持たせてはいけません。
GLaDOS はそのようなクラスをロードしません。
```kotlin=
// Good
object Plugin1: Plugin()
// OK
class Plugin2: Plugin()
// OK
class Plugin3: Plugin() {
constructor(param: Double): this()
}
// NG
class Plugin4(param: String): Plugin()
// NG
class Plugin5(param: Int): Plugin() {
constructor(): this(1)
}
```
## 関数宣言
### visibility
クラス宣言と同様に GLaDOS のプラグインでロードする関数は 可視性を `public` で宣言する必要があります。
`private` 等で宣言した場合, GLaDOS はそのような関数をロードしません。
```kotlin=
@Command
suspend fun publicFunction(event: Command.Event) {
// OK
}
@Command
private suspend fun privateFunction(event: Command.Event) {
// NG
}
```
### override
GLaDOS のプラグインは引数で対応するイベントハンドラを検索します。そのため override は必須ではありませんが, 楽であるため override することを推奨します。
```kotlin=
override suspend fun onGuildMessageReceived(event: GuildMessageReceiveEvent) {
logger.info { "Overriding: ${event.message.contentDisplay}" }
}
suspend fun guildMessageReceived(event: GuildMessageReceiveEvent) {
logger.info { "Not overriding: ${event.message.contentDisplay}" }
}
```
同じ引数を持つ関数を同一 Plugin 内に複数宣言することも可能です。この場合は もう一方を override せずに宣言する必要があります。
### suspend
GLaDOS のプラグインの関数は すべて suspend に対応しています。非中断関数でも宣言することはできますが, Kotlin Coroutines による柔軟なコードを書くことができるため suspend 宣言を推奨します。
```kotlin=
@Command
suspend fun hello(event: Command.Event) {
}
@Command
fun hello2(event: Command.Event) {
}
```
### inline
GLaDOS のプラグイン関数は 必要に応じて `inline` 関数化することは可能ですが, 推奨されません。`inline` は引数のラムダのオーバーヘッドを減らすために使用すべきです。
```kotlin=
@Schedule(multipleHours = [1])
inline fun everyHours() {
}
```
## アノテーション
### @Event
このアノテーションは必須ではありませんが, 付与することで実行の優先度を指定できます。
| 必須? | 付与対象 |
| ---- | ---- |
| No | 関数 (GLaDOS のイベントハンドラ) |
| 引数 | 型 | デフォルト値 | 説明 |
| ---- | ---- | ---- | ---- |
| priority | Plugin.Priority | Priority.Normal | 実行の優先度 |
```kotlin=
@Event(priority = Priority.Highest)
override suspend fun onGuildMemberAdded(event: GuildMemberAddEvent) {
// Executes with top-priority
}
```
### @Command
`Command.Event` を引数にとる関数に付与することで, コマンドとして登録できます。
| 必須? | 付与対象 |
| ---- | ---- |
| Yes | 関数 (`Command.Event` のみを引数に持つ) |
| 引数 | 型 | デフォルト値 | 説明 |
| ---- | ---- | ---- | ---- |
| priority | Plugin.Priority | Priority.Normal | 実行の優先度を指定 |
| command | String | 関数名 | コマンド名; <prefix><コマンド名>でコマンドを実行できるようになります |
| aliases | Array<String> | [] | コマンド名のエイリアス |
| priority | Plugin.Priority | Priority.Normal | 実行の優先度 |
| permission | Plugin.Command.PermissionPolicy | PermissionPolicy.Anyone | コマンドを実行できるユーザ |
| channelType | Plugin.Command.TargetChannelType | TargetChannelType.Any | コマンドを実行できるチャンネルタイプ |
| case | Plugin.Command.CasePolicy | CasePolicy.Ignore | コマンド名でのCaseの区別 |
| condition | Plugin.Command.ConditionPolicy | ConditionPolicy.Anytime | コマンドを実行できる状況
| description | String | "" | コマンドの説明 (helpで使用されます) |
| args | Array<String> | [] | コマンド引数の説明 |
| checkArgsCount | Boolean | true | 引数の個数が `args` で指定した個数と等しいかをチェック
| prefix | String | "!" | コマンドのプレフィックス |
| category | String | "" | コマンドのカテゴリ (help で使用されます)
```kotlin=
@Command(description = "Responds with hello world.")
suspend fun hello(event: Command.Event) {
event.reply {
text {
append("Hello, World!")
}
}.await()
}
```
### @Loop
引数なしの関数に付与することで, 一定間隔ごとに実行する関数を宣言できます。
| 必須? | 付与対象 |
| ---- | ---- |
| Yes | 関数 (引数なし) |
| 引数 | 型 | デフォルト値 | 説明 |
| ---- | ---- | ---- | ---- |
| **interval** | Long | *必須* | 実行の間隔 |
| **unit** | TimeUnit | *必須* | 実行の間隔の単位 |
| priority | Plugin.Priority | Priority.Normal | 実行の優先度 |
```kotlin=
@Loop(10, TimeUnit.SECONDS)
fun everyTenSecs() {
// Executes every 10 secs
}
```
### @Schedule
引数なしの関数に付与することで, 定期実行する関数を宣言できます。
| 必須? | 付与対象 |
| ---- | ---- |
| Yes | 関数 (引数なし) |
| 引数 | 型 | デフォルト値 | 説明 |
| ---- | ---- | ---- | ---- |
| hours | Array<Int> | [] | 実行する時 (hour) |
| minutes | Array<Int> | [] | 実行する分 (minute) |
| multipleHours | Array<Int> | [] | 実行する時 (hour) の公倍数 |
| multipleMinutes | Array<Int> | [] | 実行する分 (minute) の公倍数 |
| priority | Plugin.Priority | Priority.Normal | 実行の優先度 |
```kotlin=
@Schedule(multipleMinutes = [3])
fun everyThreeMinutes() {
// Executes every 3 minutes (0, 3, 6, 9, ..., 57)
}
```
### @Tweetstorm
@Tweetstorm は Tweetstorm のイベントを受け取る関数を宣言するアノテーションです。関数にのみ付与できます。
### @Web.Page
@Web.Page は サーブする Web ページを宣言するアノテーションです。関数にのみ付与できます。
### @Web.ErrorPage
@Web.ErrorPage は Web サーバのエラーページを宣言するアノテーションです。関数にのみ付与できます。
### @Web.Session
@Web.Session は Web サーバで使用するセッションクラスを宣言するアノテーションです。関数にのみ付与できます。
### @Experimental
@Experimental は実験的なプラグインを定義するアノテーションです。クラスにのみ付与できます。
このアノテーションを含むプラグインのコマンドを実行すると ユーザに試験的機能であることについて同意を求めます。
### @Testable
`--debug` オプションを付けて起動した場合にも実行可能なプラグインを定義するアノテーションです。クラスにのみ付与できます。
### @TestOnly
`--debug` オプションを付けて起動した場合にのみ実行可能なプラグインを定義するアノテーションです。クラスにのみ付与できます。