Skip to content

使用 ReScript 开发

ReScript 是一门健壮的类型化语言,可以编译成高效易读的 JavaScript。相比于 TypeScript,ReScript 是 JavaScript 的子集,有着远比 TypeScript 更为严格和安全的类型系统。也是 OCaml 的方言之一,结合了大量函数式编程与现代化编程特性,同时保留了 C 系语言的花括号语法风格,这使你不会像面对其它函数式编程一样对其陌生语法感到茫然,变得极易上手和入门。如果你是一名 Rust 开发者将会对 ReScript 很多地方感到亲切(就像是没有所有权和生命周期的 Rust)。

NOTE

详细信息与入门指南请参考 The ReScript Programming Language

基本使用

Kotori 从 v1.7 开始支持用 ReScript 编写插件,尽管这并非强制性,但如若你对函数式编程感兴趣或者对安全性有要求,那么使用 ReScript 编写 Kotori 插件将是不二之举。

ReScript 模块的 package.json 会有所不同:

json
{
  "name": "kotori-plugin-my-project-res",
  "version": "1.0.0",
  "description": "a kotori project kotori project by rescript",
  "main": "src/Main.res.js",
  "scripts": {
    "res:build": "rescript",
    "res:clean": "rescript clean",
    "res:dev": "rescript -w"
  },
  "keywords": [
    "kotori",
    "chatbot",
    "kotori-plugin",
    "rescript"
  ],
  "license": "BAN-ZHINESE-USING",
  "files": [
    "src",
    "LICENSE",
    "README.md"
  ],
  "author": "Himeno",
  "peerDependencies": {
    "kotori-bot": "^1.7.1"
  },
  "dependencies": {
    "rescript-kotori": "^1.0.0",
    "rescript": "11.1.4",
    "@rescript/core": "^1.6.1"
  }
}

此外,模块根目录(并非工作区)的 rescript.json 也是必要的:

json
{
  "$schema": "./node_modules/rescript/docs/docson/build-schema.json",
  "name": "kotori-plugin-res-test",
  "sources": {
    "dir": "src",
    "subdirs": true
  },
  "package-specs": {
    "module": "commonjs",
    "in-source": true
  },
  "suffix": ".res.js",
  "bs-dependencies": [
    "@rescript/core",
    "rescript-kotori"
  ],
  "bsc-flags": [
    "-open RescriptCore"
  ],
  "jsx": {
    "module": "KotoriMsg"
  }
}

基本示例

res
open Kotori
open Msg
open Session
open Ctx

let inject = ["browser"]

@scope("logger") @send external logger: (context, 'a) => unit = "debug"
@send external task: (context, string, unit => unit) => unit = "task"

type config = {
  times: int,
  duration: int,
  steps: int,
  minNum: int,
  maxNum: int,
}

let config =
  [
    ("times", Tsu.int()->Tsu.default(7)),
    ("duration", Tsu.int()->Tsu.default(180)),
    ("steps", Tsu.int()->Tsu.default(3)),
    ("minNum", Tsu.int()->Tsu.default(1)),
    ("maxNum", Tsu.int()->Tsu.default(10)),
  ]
  ->Dict.fromArray
  ->Tsu.object

let main = (ctx: context, config: config) => {
  ctx->on(
    #ready(
      () => {
        ctx->logger(config)
      },
    ),
  )

  ctx
  ->Cmd.make("greet - get a greeting")
  ->Cmd.action_async(async (_, session) => {
    let res =
      await ctx->Http.get("https://api.hotaru.icu/ial/hitokoto/v2/?format=text", Js.Undefined)
    <Text>
      {switch res->Type.typeof {
      | #string => session->format("Greet: \n{0}", [res->Kotori.Utils.toAny])
      | _ => "Sorry, I cannot get a greeting right now."
      }}
    </Text>
  })
  ->Cmd.help("Get a greeting from hotaru.icu")
  ->Cmd.scope(#all)
  ->Cmd.alias(["hi", "hey", "hello"])
  ->ignore

  ctx
  ->Cmd.make("res [saying=functional]")
  ->Cmd.action_async(async ({args}, session) => {
    let userId = switch session.userId {
    | Some(userId) => userId
    | None => "Unknown"
    }
    <Seg>
      <Text> {"Hello "} </Text>
      <Mention userId />
      <Br />
      <Text>
        {switch args {
        | [String(saying)] => session->format("Greet: \n{0}", [saying])
        | _ => "Sorry, I cannot get a greeting right now."
        }}
      </Text>
      <Seg>
        <Text> {"he is a example image"} </Text>
        <Image src="https://i.imgur.com/y5y5y5.png" />
      </Seg>
    </Seg>
  })
  ->ignore

  ctx
  ->on(
    #on_group_increase(
      async session => {
        switch session.userId {
        | Some(userId) if userId !== session.api.adapter.selfId =>
          session
          ->quick(
            <Seg>
              <Text> {"welcome to here!"} </Text>
              <Mention userId />
            </Seg>,
          )
          ->ignore
        | _ => ()
        }
      },
    ),
  )
  ->ignore

  ctx
  ->task("0/10 * * * * *", () => {
    ctx->logger("hi! this message is from rescript plugin!")
  })
  ->ignore
}

TIP

ReScript 与 React 均由 Facebook 开发,因此它天然支持 JSX 语法。


NOTE

Kotori 提供的 ReScript 相关 API 请参考 接口文档