Skip to content

Instantly share code, notes, and snippets.

@taiyme
Last active January 9, 2024 05:44
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save taiyme/94c1ebb9d867c626a87bbb6a08b3662d to your computer and use it in GitHub Desktop.
Save taiyme/94c1ebb9d867c626a87bbb6a08b3662d to your computer and use it in GitHub Desktop.

taiy製Misskeyプラグイン

Copyright 2022 taiy https://github.com/taiyme
Apache License Version 2.0 https://www.apache.org/licenses/LICENSE-2.0

taiy製MisskeyカスタムCSSはこちら

配布しているプラグインの質問や要望は @taiy@submarin.online へお願いします。

導入

設定 > クライアント設定「プラグイン」> プラグインのインストール

免責: プラグインの導入は自己責任でお願いします。

目次

Notes: プラグインの説明 - 配布ファイル。

パクる・数字引用プラグイン

パクるプラグインを適用すると、ノートメニューに「パクる」が、数字引用プラグインを適用すると、ノートメニューに「数字引用する」が出現します。

数字引用について → 数字引用 - Submarin Wiki

【設定】 以下のオプションを指定します。

  • 公開範囲
    • auto public home followers
    • 投稿するノートの公開範囲を指定します。
  • 常にローカルでパクる
    • 有効 無効
    • 公開範囲が auto の場合、この指定は無視され、元のノートに追従します。
  • 元のチャンネルでパクる
    • 有効 無効
    • 公開範囲が auto の場合、この指定は無視され、元のノートに追従します。
  • 返信元でパクる
    • 有効 無効
  • 引用もパクる
    • 有効 無効
  • 投票もパクる
    • 有効 無効
  • 添付ファイルもパクる
    • 有効 無効
    • ドライブに保存・添付するため、時間を要することがあります。

Notes: 数字引用プラグインは、それぞれ「パクる」を「数字引用する」に読み替えます。

【権限】 このプラグインはそれぞれの理由のため権限の許可が必要です。

  • write:notes (ノートを作成・削除する)
    • ノートを投稿するため
  • write:drive (ドライブを操作する)
    • 添付ファイルをドライブに保存するため
  • read:drive (ドライブを見る)
    • 保存したファイルを取得し投稿するため

投稿時刻を表示するプラグイン

投稿時刻を表示するプラグインを適用すると、ノートメニューに「投稿時刻を表示」が出現します。

【設定】 以下のオプションを指定します。

  • 書式
  • 時差
    • 数値 540
    • 時差を分単位で指定します。

【権限】 このプラグインは許可の必要な権限はありません。

いまのなし

いまのなしって言ったら直前のノートが削除されるやつ

ここが詳しい → いまのなし | Misskey Plugins & CSS

【設定】 以下のオプションを指定します。

  • 単語(完全一致)
    • 文字列 いまのなし
    • カンマ区切りで複数指定します。
    • 猫化/nyaize(日本語・英語)に対応しています。
  • 単語(部分一致)
    • 文字列
    • カンマ区切りで複数指定します。
    • 猫化/nyaize(日本語・英語)に対応しています。
  • 削除を確認する(完全一致)
    • 有効 無効
    • 誤って自動削除されるのを防ぎます。
  • 削除を確認する(部分一致)
    • 有効 無効
    • 誤って自動削除されるのを防ぎます。

Notes: いまのなしv2.0.0以降、完全一致/部分一致のオプションが独立し、個別で指定できるようになりました。

【権限】 このプラグインはそれぞれの理由のため権限の許可が必要です。

  • write:notes (ノートを作成・削除する)
    • ノートを削除するため
### {
name: "パクるプラグイン"
version: "2.0.0"
author: "taiy"
description: "ノートメニューに\"パクる\"を追加します。"
permissions: ["write:notes" "write:drive" "read:drive"]
config: {
visibility: {
type: "string"
label: "公開範囲"
description: "(既定値: auto) 以下から、投稿するノートの公開範囲を指定します。 auto public home followers"
default: "auto"
}
localOnly: {
type: "boolean"
label: "常にローカルでパクる"
description: "(既定値: 無効) 公開範囲が\"auto\"の場合、この指定は無視され、元のノートに追従します。"
default: no
}
channel: {
type: "boolean"
label: "元のチャンネルでパクる"
description: "(既定値: 有効) 公開範囲が\"auto\"の場合、この指定は無視され、元のノートに追従します。"
default: yes
}
reply: {
type: "boolean"
label: "返信元でパクる"
description: "(既定値: 有効)"
default: yes
}
renote: {
type: "boolean"
label: "引用もパクる"
description: "(既定値: 有効)"
default: yes
}
poll: {
type: "boolean"
label: "投票もパクる"
description: "(既定値: 有効)"
default: yes
}
files: {
type: "boolean"
label: "添付ファイルもパクる"
description: "(既定値: 有効) ドライブに保存・添付するため、時間を要することがあります。"
default: yes
}
}
}
#c = {}
c.visibility <- Plugin:config.visibility
? Core:not(Arr:incl(["auto" "public" "home" "followers"] c.visibility)) {
c.visibility <- "auto"
Mk:dialog("パクるプラグイン" `公開範囲の指定に誤りがあります。{Str:lf}自動で **{c.visibility}** が適用されます。` "warning")
}
c.localOnly <- Plugin:config.localOnly
c.channel <- Plugin:config.channel
c.reply <- Plugin:config.reply
c.renote <- Plugin:config.renote
c.poll <- Plugin:config.poll
c.files <- Plugin:config.files
c.auto <- (c.visibility = "auto")
@upload(files callback) {
@at(arr index) {
? (index < 1) << _
? (Arr:len(arr) < index) << _
<< arr[index]
}
@lastAt(arr) {
<< at(arr Arr:len(arr))
}
@fn_upload(file) {
#uuid = Util:uuid()
#uploadObj = {
url: file.url
force: yes
isSensitive: file.isSensitive
comment: uuid
}
? (file.marker != _) {
uploadObj.marker <- file.marker
}
Mk:api("drive/files/upload-from-url" uploadObj)
<< {
uuid: uuid
comment: file.comment
}
}
#latest = lastAt(Mk:api("drive/stream" {limit: 1}))
$latestId <- ? (latest != _) Obj:get(latest "id") . _
#arr = ~~ #file files { fn_upload(file) }
#complete = ~ Arr:len(arr) { _ }
#streamObj = {limit: Arr:len(arr)}
? (latestId != _) {
streamObj.sinceId <- latestId
}
#stop = Async:interval(2000 @() {
#result = Mk:api("drive/stream" streamObj)
? (result != _) {
~~ #file result {
Arr:map(arr @(obj index) {
? (file.comment = obj.uuid) {
file <- Mk:api("drive/files/update" {
fileId: file.id
comment: obj.comment
})
complete[index] <- file
}
})
latestId <- file.id
}
}
? Core:not(Arr:incl(complete _)) {
stop()
callback(complete)
}
} yes)
<< _
}
@parse(note callback) {
@toBool(value) {
? value {
yes => << yes
no => << no
"" => << no
_ => << no
0 => << no
}
<< yes
}
@or(arr) {
$bool <- no
Arr:find(arr @(v) {
<< ? toBool(v) {
bool <- yes
yes
} . no
})
<< bool
}
@and(arr) {
$bool <- yes
Arr:find(arr @(v) {
<< ? toBool(v) no . {
bool <- no
yes
}
})
<< bool
}
#obj = {
text: note.text
cw: note.cw
}
? c.auto {
obj.visibility <- note.visibility
? (note.localOnly != _) {
obj.localOnly <- note.localOnly
}
? (note.channelId != _) {
obj.channelId <- note.channelId
}
} . {
obj.visibility <- c.visibility
obj.localOnly <- c.localOnly
? and([c.channel note.channelId]) {
obj.channelId <- note.channelId
}
}
? and([c.reply note.replyId]) {
obj.replyId <- note.replyId
}
? and([c.renote note.renoteId]) {
obj.renoteId <- note.renoteId
}
? and([c.poll note.poll]) {
#poll = {
choices: Arr:map(note.poll.choices @(choice) {
<< ? (Core:type(choice) = "obj") Obj:get(choice "text") . choice
})
}
? (note.poll.multiple != _) {
poll.multiple <- note.poll.multiple
}
? (note.poll.expiresAt != _) {
#diff = (Date:parse(note.poll.expiresAt) - Date:parse(note.createdAt))
poll.expiredAfter <- diff
}
obj.poll <- poll
}
? and([c.files Arr:len(note.files)]) {
upload(note.files @(files) {
obj.fileIds <- Arr:map(files @(file) { file.id })
callback(obj)
})
} . {
callback(obj)
}
<< _
}
Plugin:register_note_action("パクる" @(note) {
parse(note @(parsed) {
Mk:api("notes/create" parsed)
})
})
### {
name: "数字引用プラグイン"
version: "2.0.0"
author: "taiy"
description: "ノートメニューに\"数字引用する\"を追加します。"
permissions: ["write:notes" "write:drive" "read:drive"]
config: {
visibility: {
type: "string"
label: "公開範囲"
description: "(既定値: auto) 以下から、投稿するノートの公開範囲を指定します。 auto public home followers"
default: "auto"
}
localOnly: {
type: "boolean"
label: "常にローカルで数字引用する"
description: "(既定値: 無効) 公開範囲が\"auto\"の場合、この指定は無視され、元のノートに追従します。"
default: no
}
channel: {
type: "boolean"
label: "元のチャンネルで数字引用する"
description: "(既定値: 有効) 公開範囲が\"auto\"の場合、この指定は無視され、元のノートに追従します。"
default: yes
}
reply: {
type: "boolean"
label: "返信元で数字引用する"
description: "(既定値: 有効)"
default: yes
}
renote: {
type: "boolean"
label: "引用も数字引用する"
description: "(既定値: 有効)"
default: yes
}
poll: {
type: "boolean"
label: "投票も数字引用する"
description: "(既定値: 有効)"
default: yes
}
files: {
type: "boolean"
label: "添付ファイルも数字引用する"
description: "(既定値: 有効) ドライブに保存・添付するため、時間を要することがあります。"
default: yes
}
}
}
#c = {}
c.visibility <- Plugin:config.visibility
? Core:not(Arr:incl(["auto" "public" "home" "followers"] c.visibility)) {
c.visibility <- "auto"
Mk:dialog("数字引用プラグイン" `公開範囲の指定に誤りがあります。{Str:lf}自動で **{c.visibility}** が適用されます。` "warning")
}
c.localOnly <- Plugin:config.localOnly
c.channel <- Plugin:config.channel
c.reply <- Plugin:config.reply
c.renote <- Plugin:config.renote
c.poll <- Plugin:config.poll
c.files <- Plugin:config.files
c.auto <- (c.visibility = "auto")
@numberquote(text) {
? (text = _) {
text <- ""
}
@genArr(size) {
<< ~ size {_}
}
@lastStr(str len) {
#end = (Str:len(str) + 1)
Str:slice(str (end - len) end)
}
? (lastStr(text 9) = "</center>") {
text <- `{text}{Str:lf}`
}
#text_len = Str:len(text)
#result = []
Arr:find(genArr(text_len) @(v i) {
i <- (text_len - (i - 1))
#s = Str:pick(text i)
#n = Str:to_num(s)
? (n != _) {
Arr:unshift(result s)
<< no
}
? (s = "-") {
? (text_len != i) {
Arr:unshift(result s)
}
<< yes
}
<< yes
})
$str <- Arr:join(result "")
#num = ? (str != "") Str:to_num(str) . 1
? (str != "") {
text <- Str:slice(text 1 ((text_len + 1) - Arr:len(result)))
}
<< `{text}{Core:to_str((num + 1))}`
}
@upload(files callback) {
@at(arr index) {
? (index < 1) << _
? (Arr:len(arr) < index) << _
<< arr[index]
}
@lastAt(arr) {
<< at(arr Arr:len(arr))
}
@fn_upload(file) {
#uuid = Util:uuid()
#uploadObj = {
url: file.url
force: yes
isSensitive: file.isSensitive
comment: uuid
}
? (file.marker != _) {
uploadObj.marker <- file.marker
}
Mk:api("drive/files/upload-from-url" uploadObj)
<< {
uuid: uuid
comment: file.comment
}
}
#latest = lastAt(Mk:api("drive/stream" {limit: 1}))
$latestId <- ? (latest != _) Obj:get(latest "id") . _
#arr = ~~ #file files { fn_upload(file) }
#complete = ~ Arr:len(arr) { _ }
#streamObj = {limit: Arr:len(arr)}
? (latestId != _) {
streamObj.sinceId <- latestId
}
#stop = Async:interval(2000 @() {
#result = Mk:api("drive/stream" streamObj)
? (result != _) {
~~ #file result {
Arr:map(arr @(obj index) {
? (file.comment = obj.uuid) {
file <- Mk:api("drive/files/update" {
fileId: file.id
comment: obj.comment
})
complete[index] <- file
}
})
latestId <- file.id
}
}
? Core:not(Arr:incl(complete _)) {
stop()
callback(complete)
}
} yes)
<< _
}
@parse(note callback) {
@toBool(value) {
? value {
yes => << yes
no => << no
"" => << no
_ => << no
0 => << no
}
<< yes
}
@or(arr) {
$bool <- no
Arr:find(arr @(v) {
<< ? toBool(v) {
bool <- yes
yes
} . no
})
<< bool
}
@and(arr) {
$bool <- yes
Arr:find(arr @(v) {
<< ? toBool(v) no . {
bool <- no
yes
}
})
<< bool
}
#obj = {
text: note.text
cw: note.cw
}
? c.auto {
obj.visibility <- note.visibility
? (note.localOnly != _) {
obj.localOnly <- note.localOnly
}
? (note.channelId != _) {
obj.channelId <- note.channelId
}
} . {
obj.visibility <- c.visibility
obj.localOnly <- c.localOnly
? and([c.channel note.channelId]) {
obj.channelId <- note.channelId
}
}
? and([c.reply note.replyId]) {
obj.replyId <- note.replyId
}
? and([c.renote note.renoteId]) {
obj.renoteId <- note.renoteId
}
? and([c.poll note.poll]) {
#poll = {
choices: Arr:map(note.poll.choices @(choice) {
<< ? (Core:type(choice) = "obj") Obj:get(choice "text") . choice
})
}
? (note.poll.multiple != _) {
poll.multiple <- note.poll.multiple
}
? (note.poll.expiresAt != _) {
#diff = (Date:parse(note.poll.expiresAt) - Date:parse(note.createdAt))
poll.expiredAfter <- diff
}
obj.poll <- poll
}
? and([c.files Arr:len(note.files)]) {
upload(note.files @(files) {
obj.fileIds <- Arr:map(files @(file) { file.id })
callback(obj)
})
} . {
callback(obj)
}
<< _
}
Plugin:register_note_action("数字引用する" @(note) {
parse(note @(parsed) {
parsed.text <- numberquote(parsed.text)
Mk:api("notes/create" parsed)
})
})
### {
name: "投稿時刻を表示するプラグイン"
version: "1.0.1"
author: "taiy"
description: "ノートメニューに\"投稿時刻を表示\"を追加します。"
permissions: []
config: {
format: {
type: "string"
label: "書式"
description: "(既定値: YYYY/MM/DD HH:mm:ss.SSS) フォーマットを指定します。Day.jsと同一のトークンが利用できます。(一部非対応)"
default: "YYYY/MM/DD HH:mm:ss.SSS"
}
offset: {
type: "number"
label: "時差"
description: "(既定値: 540) 時差を分単位で指定します。日本標準時(JST)は540です。夏時間には非対応です。"
default: 540
}
}
}
#has_format = Core:or((Plugin:config.format = "") (Plugin:config.format = _))
#c_format = ? has_format "YYYY/MM/DD HH:mm:ss.SSS" . Plugin:config.format
#has_offset = Core:or((Plugin:config.offset = "") (Plugin:config.offset = _))
#c_offset = ? has_offset 540 . Plugin:config.offset
? has_format {
Mk:dialog("投稿時刻を表示するプラグインより" `書式の指定がありません。{Str:lf}自動で **YYYY/MM/DD HH:mm:ss.SSS** が適用されます。` "warn")
}
? has_offset {
Mk:dialog("投稿時刻を表示するプラグインより" `時差の指定がありません。{Str:lf}自動で **540** が適用されます。` "warn")
}
@format(str tokens) {
@sort(arr mapfn) {
@bubbleSort(arr) {
#length = Arr:len(arr)
~ #i, length {
~ #j, (length - i) {
? (Obj:get(arr[j] "index") > Obj:get(arr[(j + 1)] "index")) {
#tmp = arr[j]
arr[j] <- arr[(j + 1)]
arr[(j + 1)] <- tmp
}
}
}
<< arr
}
<< Arr:map(bubbleSort(Arr:map(arr mapfn)) @(obj) {
<< Obj:get(obj "value")
})
}
@flat(arr) {
@fn_isArr(val) {
<< (Core:type(val) = "arr")
}
@fn_flat(arr) {
arr <- Arr:reduce(arr @(prev val) {
? Core:not(fn_isArr(val)) {
val <- [val]
}
<< Arr:concat(prev val)
} [])
? (Arr:len(Arr:filter(arr fn_isArr)) != 0) {
arr <- fn_flat(arr)
}
<< arr
}
<< fn_flat(Arr:copy(arr))
}
@at(arr index) {
? (index < 1) << _
? (Arr:len(arr) < index) << _
<< arr[index]
}
@startAt(arr) {
<< at(arr 1)
}
@lastAt(arr) {
<< at(arr Arr:len(arr))
}
@toStr(strArr) {
<< Arr:join(Arr:map(flat(strArr) @(strObj) {
<< Obj:get(strObj "string")
}) "")
}
tokens <- sort(Obj:kvs(tokens) @(arr) {
#token = arr[1]
#value = arr[2]
<< {
index: Str:len(token)
value: {
token: token
value: value
}
}
})
Arr:reverse(tokens)
str <- Arr:reduce(Str:split(str "") @(prev cur) {
? (cur = "[") {
Arr:push(prev {
string: ""
replaced: yes
})
} .? (cur = "]") {
Arr:push(prev {
string: ""
replaced: no
})
} . {
#last = lastAt(prev)
#s = Obj:get(last "string")
Obj:set(last "string" `{s}{cur}`)
}
<< prev
} [{
string: ""
replaced: no
}])
#result = Arr:reduce(tokens @(strArr tokenObj) {
strArr <- flat(strArr)
#t = Obj:get(tokenObj "token")
#v = Obj:get(tokenObj "value")
<< Arr:map(strArr @(strObj) {
#s = Obj:get(strObj "string")
#r = Obj:get(strObj "replaced")
? r {
<< strObj
}
#a = Str:split(s t)
<< Arr:reduce(a @(prev cur index) {
? (cur != "") {
Arr:push(prev {
string: cur
replaced: no
})
}
? (Arr:len(a) != index) {
Arr:push(prev {
string: v
replaced: yes
})
}
<< prev
} [])
})
} str)
<< toStr(result)
}
@DateObj(unixtime minuteOffset) {
#YEAR_ONE = 365
#YEAR_FOUR = 1461
#YEAR_100 = 36524
#YEAR_400 = 146097
#EPOCH_DAY = 719468
#monthday = [0,31,61,92,122,153,184,214,245,275,306,337]
#result = {
unixtime: unixtime
minuteOffset: minuteOffset
}
unixtime <- (unixtime + (minuteOffset * 60000))
$unixday <- Math:floor((unixtime / 86400000))
$leap <- 0
$year <- 0
$month <- _
$day <- _
$n <- _
$hour <- (Math:floor((unixtime / 3600000)) % 24)
$minute <- (Math:floor((unixtime / 60000)) % 60)
$second <- (Math:floor((unixtime / 1000)) % 60)
$millisecond <- (unixtime % 1000)
? (unixtime < 0) {
hour <- (hour + 24)
minute <- (minute + 60)
second <- (second + 60)
millisecond <- (millisecond + 1000)
}
#weekday = ((Math:floor((unixday + 4)) % 7) + 1)
? (weekday < 0) {
weekday <- (weekday + 7)
}
unixday <- (unixday + EPOCH_DAY)
year <- (year + (400 * Math:floor((unixday / YEAR_400))))
unixday <- (unixday % YEAR_400)
n <- Math:floor((unixday / YEAR_100))
year <- (year + (n * 100))
unixday <- (unixday % YEAR_100)
? (n = 4) {
leap <- 1
} . {
year <- (year + (4 * Math:floor((unixday / YEAR_FOUR))))
unixday <- (unixday % YEAR_FOUR)
n <- Math:floor((unixday / YEAR_ONE))
year <- (year + n)
unixday <- (unixday % YEAR_ONE)
? (n = 4) {
leap <- 1
}
}
? (leap != 0) {
month <- 2
day <- 29
} . {
month <- Math:floor((((unixday * 5) + 2) / 153))
day <- ((unixday - monthday[(month + 1)]) + 1)
month <- (month + 3)
? (month > 12) {
year <- (year + 1)
month <- (month - 12)
}
}
result.year <- year
result.month <- month
result.day <- day
result.hour <- hour
result.minute <- minute
result.second <- second
result.millisecond <- millisecond
result.weekday <- weekday
<< result
}
@DateTokenObj(unixtime minuteOffset) {
#d = DateObj(unixtime minuteOffset)
#result = {}
@zeroPadding(n length) {
#num = Str:to_num(n)
#abs = Math:abs(num)
#str = Core:to_str(abs)
#sign = ? (num < 0) "-" . ""
#len = Math:max(0 (length - Str:len(str)))
<< `{sign}{Arr:join(~ len {"0"} "")}{str}`
}
#z = zeroPadding
result.YY <- z((d.year % 100) 2)
result.YYYY <- z(d.year 4)
result.M <- z(d.month 1)
result.MM <- z(d.month 2)
result.D <- z(d.day 1)
result.DD <- z(d.day 2)
result.d <- z(d.weekday 1)
$hour12 <- (d.hour % 12)
? (hour12 = 0) {
hour12 <- 12
}
result.H <- z(d.hour 1)
result.HH <- z(d.hour 2)
result.h <- z(hour12 1)
result.hh <- z(hour12 2)
result.m <- z(d.minute 1)
result.mm <- z(d.minute 2)
result.s <- z(d.second 1)
result.ss <- z(d.second 2)
result.SSS <- z(d.millisecond 3)
#tzoffset = Math:abs(d.minuteOffset)
#tzsign = ? (d.minuteOffset < 0) "-" . "+"
#tzhour = z((Math:floor((tzoffset / 60)) % 60) 2)
#tzminute = z((tzoffset % 60) 2)
result.Z <- `{tzsign}{tzhour}:{tzminute}`
result.ZZ <- `{tzsign}{tzhour}{tzminute}`
result.A <- ? (d.hour < 12) "AM" . "PM"
result.a <- ? (d.hour < 12) "am" . "pm"
@indicator(n) {
#i = Math:abs(n)
#cent = (i % 100)
? Core:and((10 <= cent) (cent <= 20)) << `{n}th`
#dec = (i % 10)
? dec {
1 => << `{n}st`
2 => << `{n}nd`
3 => << `{n}rd`
}
<< `{n}th`
}
result.Do <- indicator(d.day)
$hour24 <- (d.hour % 24)
? (hour24 = 0) {
hour24 <- 24
}
result.k <- z(hour24 1)
result.kk <- z(hour24 2)
result.X <- `{Math:floor((d.unixtime / 1000))}`
result.x <- `{d.unixtime}`
<< result
}
@dateformat(fmt str offset) {
<< format(fmt DateTokenObj(Date:parse(str) offset))
}
Plugin:register_note_action("投稿時刻を表示" @(note) {
#formated = dateformat(c_format note.createdAt c_offset)
Mk:dialog("投稿時刻" formated "info")
})
Plugin:register_user_action("作成日時を表示" @(user) {
#formated = dateformat(c_format user.createdAt c_offset)
Mk:dialog("作成日時" formated "info")
})
### {
name: "いまのなし"
version: "2.0.1"
author: "taiy"
description: "消せ消せ消せ消せ消せ https://u.taiy.me/aiscript"
permissions: ["write:notes"]
config: {
exactWords: {
type: "string"
label: "単語(完全一致)"
description: "(既定値: いまのなし) カンマ区切りで複数指定します。nyaizeは以下の言語に対応しています。 ja-JP, en-US"
default: "いまのなし"
}
partialWords: {
type: "string"
label: "単語(部分一致)"
description: "(既定値なし) \"単語(完全一致)\" の説明を参照します。"
default: ""
}
exactAlert: {
type: "boolean"
label: "削除を確認する(完全一致)"
description: "(既定値: 有効)"
default: yes
}
partialAlert: {
type: "boolean"
label: "削除を確認する(部分一致)"
description: "(既定値: 有効)"
default: yes
}
}
}
#limitObj = {
userId: Obj:get(Mk:api("i" {}) "id")
limit: 1
}
:: Arr {
@at(arr index) {
<< ? ((index < 1) | (Arr:len(arr) < index)) _
. arr[index]
}
@lastAt(arr) {
<< Arr:at(arr Arr:len(arr))
}
}
:: Str {
@replaceFn(text old fn) {
#textU = Str:upper(text)
#oldU = Str:upper(old)
#oldLen = Str:len(old)
#textUArr = Str:split(textU oldU)
#obj = Arr:reduce(textUArr @(prev cur i) {
? (i != 1) {
#begin = prev.len
#end = (begin + oldLen)
prev.len <- end
Arr:push(prev.arr {
isMatched: yes
string: Str:slice(text begin end)
})
}
#begin = prev.len
#end = (begin + Str:len(cur))
prev.len <- end
Arr:push(prev.arr {
isMatched: no
string: Str:slice(text begin end)
})
<< prev
} {len: 1, arr: []})
<< Arr:join(Arr:map(obj.arr @(o) {
<< ? (o.string = "") ""
.? o.isMatched fn(o.string)
. o.string
}) "")
}
@nyaize(text) {
text <- Str:replace(Str:replace(Str:replace(text "な" "にゃ") "ナ" "ニャ") "ナ" "ニャ")
text <- Str:replaceFn(text "na" @(old) {
#x = ? (Str:slice(old 2 3) = "A") "YA" . "ya"
<< `{Str:slice(old 1 2)}{x}`
})
text <- Str:replaceFn(text "morning" @(old) {
#x = ? (Str:slice(old 5 8) = "ING") "YAN" . "yan"
<< `{Str:slice(old 1 5)}{x}`
})
text <- Str:replaceFn(text "everyone" @(old) {
#x = ? (Str:slice(old 6 9) = "ONE") "NYAN" . "nyan"
<< `{Str:slice(old 1 6)}{x}`
})
<< text
}
@quote(text) {
<< `> {Arr:join(Str:split(text Str:lf) `{Str:lf}> `)}`
}
}
#c = {}
c.exactAlert <- Plugin:config.exactAlert
c.exactWords <- Plugin:config.exactWords
? (c.exactWords = "") {
c.exactWords <- "いまのなし"
}
#exactMatches = Arr:filter(
Arr:map(
Str:split(c.exactWords ",")
@(m) { Str:trim(m) }
)
@(m) {(m != "")}
)
Arr:map(exactMatches @(m) {
Arr:unshift(exactMatches Str:nyaize(m))
})
c.partialAlert <- Plugin:config.partialAlert
c.partialWords <- Plugin:config.partialWords
? (c.partialWords = "") {
c.partialWords <- ""
}
#partialMatches = Arr:filter(
Arr:map(
Str:split(c.partialWords ",")
@(m) { Str:trim(m) }
)
@(m) {(m != "")}
)
Arr:map(partialMatches @(m) {
Arr:unshift(partialMatches Str:nyaize(m))
})
#queue = []
Async:interval(1000 @() {
#obj = Arr:shift(queue)
? (obj = _) << _
#alert = ? obj.type {
1 => c.exactAlert
2 => c.partialAlert
3 => (c.exactAlert | c.partialAlert)
}
#note = obj.note
#flag = ? alert Mk:confirm("" `このノートを削除しますか?{Str:lf}{Str:quote(note.text)}` "warning") . yes
? flag {
Mk:api("notes/delete" {
noteId: note.id
})
}
})
@judge(text) {
$exact <- no
$partial <- no
Arr:find(exactMatches @(m) {
exact <- (text = m)
<< exact
})
Arr:find(partialMatches @(m) {
partial <- Str:incl(text m)
<< partial
})
<< {exact: exact, partial: partial}
}
Plugin:register_note_post_interruptor(@(note) {
#text = Obj:get(note "text")
#judge = judge(text)
#type = ? (judge.exact & judge.partial) 3
.? judge.exact 1
.? judge.partial 2
. 0
? (type != 0) {
Arr:push(queue {
type: type,
note: Arr:lastAt(Mk:api("users/notes" limitObj))
})
}
<< note
})
### {
name: "NSFWプラグイン"
version: "0.0.0 (tester)"
author: "taiy"
description: "https://u.taiy.me/aiscript"
permissions: []
config: {
nsfw: {
type: "boolean"
label: "強制NSFW"
description: "(既定値: 有効)"
default: yes
}
cw: {
type: "boolean"
label: "強制CW"
description: "(既定値: 無効)"
default: no
}
}
}
@modNSFW(note) {
? (note.files != _) {
Arr:map(note.files @(file) {
file.isSensitive <- yes
})
}
<< note
}
@modCW(note) {
? (note.cw = _) {
note.cw <- ""
}
<< note
}
Plugin:register_note_view_interruptor(@(note) {
? Plugin:config.nsfw {
modNSFW(note)
}
? Plugin:config.cw {
modCW(note)
}
<< note
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment