AiScriptで書いた自作関数類

# 前書き
実際の動作に関して不具合があっても責任は取りません。
動作の確認を行わず関数の編集を行う場合があります。
動作が適当でない場合などは教えていただけると助かります。
# 自作関数
## `str`
### `@findURLs(text: str): arr`
`text`からURLを検出し、URLの配列を返す。
```aiscript
@findURLs(text){
  let result = []
  let urlChars = "!#$%&()+,-./0123456789:=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"
  var strBegin = 0
  let strEnd = text.len
  loop {
    let subText = text.slice(strBegin, strEnd)
    let httpIndex = subText.index_of("http://")
    let httpsIndex = subText.index_of("https://")
    let d = if (httpIndex < 0) httpsIndex else if (httpsIndex < 0) httpIndex else Math:min(httpIndex, httpsIndex)
    if (d < 0) break
    let urlBegin = strBegin + d
    var urlEnd = urlBegin
    var urlEndTemp = urlEnd
    var parenthesesCount = 0
    loop {
      let pickChar = text.pick(urlEndTemp)
      if (pickChar == null || !urlChars.incl(pickChar)) break
      if (pickChar == "(") parenthesesCount += 1
      elif (pickChar == ")") parenthesesCount -= 1
      if (parenthesesCount < 0) break
      urlEndTemp += 1
      if (parenthesesCount == 0) urlEnd = urlEndTemp
    }
    result.push(text.slice(urlBegin,urlEnd))
    strBegin = urlEnd
  }
  return result
}
```
## `arr`
### `@randomSelect(list :arr, rand: @(min: num, max: num){ num }): value`
`list`から`rand`関数を用いて無作為に要素を選択し、選択された要素を返す。
```aiscript
@randomSelect(list, rand){
  return list[rand(0, list.len - 1)]
}
```
### `@shuffle(list: arr, rand: @(min: num, max: num){ num }): null`
`list`を`rand`関数を用いて無作為にシャッフルする。
```aiscript
@shuffle(list, rand){
  let swap = @(list, i, j){
    let a = list[i]
    list[i] = list[j]
    list[j] = a
  }
  let right = list.len - 1 
  for let left right {swap(list, left, rand(left, right))}
}
```
非推奨(短くかける代わりに偏りが激しく、出現頻度に2倍程度差が出ることもある。)
```aiscript
@shuffle(list, rand){ list.sort(@(){ rand(0, 1) - 0.5 }) }
```
### `@drop(list: arr, index: num): value`
`list`から`index`番目の要素を削除し、削除された要素を返す。
```aiscript
@drop(list, index){
  let leng = list.len
  let item = list[index]
  for let i (leng - 1) if (index <= i) list[i] = list[i + 1]
  list.pop()
  return index
}
```
## `obj`
### `@obj2yaml(obj: obj): str`
`obj`を受け取り、`yaml`形式の文字列として返す。
```aiscript
@obj2yaml(obj){
  let func = @(obj, nest){
    var yaml = ""
    let nextNest = `{nest}  `
    if (Core:type(obj) == "obj") {
      let keys = Obj:keys(obj)
      each let key keys yaml = `{yaml}\n{nest}{key}: {func(Obj:get(obj key), nextNest)}`
      return yaml
    }
    if (Core:type(obj) == "arr") {
      each let item obj yaml = `{yaml}\n{nest}- {func(item, nextNest)}`
      return yaml
    }
    return `{obj}`
  }
  return func(obj, "")
}
```
## `Note`
### `@getNotes(userId: str, limit: num): arr`
`userId`に応じて、新しいものから最大`limit`個までのノートの配列を返す。
```aiscript
@getNotes(userId, limit){
  let getNoteFunc = @(limit, params){
    if (params.limit <= 0) return []
    let notes = Mk:api("users/notes" params)
    if (notes.len < params.limit) return notes
    limit -= params.limit
    return notes.concat(getNoteFunc(limit, {userId: userId, limit: Math:min(limit, 100), untilId: notes[notes.len - 1].id}))
  }
  return getNoteFunc(limit, {userId: userId, limit: Math:min(limit, 100)})
}
```
### `@countReactions(note: Note): num`
`note`についたリアクションの数を返す。
```aiscript
@countReactions(note){
  let reactions = note.reactions
  var count = 0
  each let reaction Obj:keys(reactions) count += Obj:get(reactions, reaction)
  return count
}
```
## `User`
### `@getUsers(limit: num): arr`
古いものからユーザーを最大`limit`個までのユーザーの配列を返す。
```aiscript
@getUsers(limit){
  let getUserFunc = @(limit, params){
    if (params.limit <= 0) return []
    let users = Mk:api("users" params)
    if (users.len < params.limit) return users
    limit -= params.limit
    return users.concat(getUserFunc(limit, {limit: Math:min(limit, 100), offset: (params.offset+100)}))
  }
  return getUserFunc(limit, {limit: Math:min(limit, 100), offset: 0})
}
```
## `Pages`
### `@getPages(limit: num): arr`
古いものからユーザーを最大`limit`個までのユーザーが作ったページのリストを返す。
ただし、1ユーザーが100ページ以上作った場合などに対応していない。
```aiscript
@getPages(limit){
  let users = getUsers(limit)
  var pageList = []
  each let user users pageList = pageList.concat(Mk:api("users/pages" {userId: user.id, limit: 100}))
  return pageList
}
```
### `@getPageContentData(pageId: str, contentName: str): str`
`pageId`で指し示されるページが、ある構造を保つ時、`contentName`を用いて、その内容を返す。
```aiscript
@getPageContentData(pageId, contentName){
  let contents = Mk:api("pages/show" {pageId: pageId}).content.filter(@(content){content.title==contentName})
  if ((contents.len < 1) || (contents[0].type != "section")) return null
  let childrens = contents[0].children
  if ((childrens.len < 1) || (childrens[0].type != "text")) return null
  return childrens[0].text
}
```
### `@md2page(title: str, name: str, markdown: str): obj`
`title`、`name`、`markdown`を用いて、ページを作成できるオブジェクトを返す。
```aiscript
@md2page(title,name,markdown){
  let s2f = @(str){
    let markdown = `{Str:lf}{str}{Str:lf}`
    let mdEnd = markdown.len
    let markdownList = []
    let codeBlockList = {}
    var mdStart = 0
    loop {
      let startPoint = markdown.slice(mdStart,mdEnd).index_of(`{Str:lf}\`\`\``) + mdStart
      let endPoint = markdown.slice(startPoint + 1,mdEnd).index_of(`{Str:lf}\`\`\`{Str:lf}`) + startPoint + 1
      if (endPoint <= startPoint) {
        markdownList.push(markdown.slice(mdStart,mdEnd))
        break
      }
      if (mdStart + 1 < startPoint) markdownList.push(markdown.slice(mdStart + 1,startPoint))
      let uuid = Util:uuid()
      markdownList.push(`{Str:lf}{uuid}`)
      codeBlockList[uuid] = markdown.slice(startPoint + 1,endPoint + 4)
      mdStart = endPoint + 4
    }
    let result = `{Str:lf}{markdownList.join("")}`
    return {
      markdown: result.slice(0, result.len - 1)
      codeBlockList: codeBlockList
    }
  }
  let hfn = @(format, splitKey){
    let contentArray = []
    let splittedMarkdown = format.markdown.split(`{Str:lf}{splitKey} `)
    let initContent = splittedMarkdown.shift()
    if (initContent != "") {
      let splittedInitContent = initContent.split(Str:lf)
      splittedInitContent.shift()
      var lines = []
      let lfn = @(lines, contentArray){
        var textBlock = lines.join(Str:lf)
        let uuidKeys = Obj:keys(format.codeBlockList)
        each let key uuidKeys textBlock = textBlock.replace(key format.codeBlockList[key])
        if (lines.len > 0) contentArray.push({type: "text", text: textBlock})        
        return []
      }
      each let line splittedInitContent {
        if (`{Str:lf}{line}`.incl(`{Str:lf}?note `)) {
          lfn(lines,contentArray)
          contentArray.push({type: "note", note: line.replace("?note ", "")})
        }
        else {
          lines.push(line)
        }
      }
      lfn(lines,contentArray)
    }
    let subContents = splittedMarkdown.map(@(pieceOfMarkdown){
      let splittedPieceOfMarkdown = pieceOfMarkdown.split(Str:lf)
      return {
        type: "section"
        title: splittedPieceOfMarkdown.shift()
        children: hfn({markdown: `{Str:lf}{splittedPieceOfMarkdown.join(Str:lf)}`, codeBlockList: format.codeBlockList},`{splitKey}#`)
      }
    })
    return contentArray.concat(subContents)
  }
  return {
    title: title
    name: name
    content: hfn(s2f(markdown),"#")
    variables: []
    script: ""
  }
}
```