Skip to content

Instantly share code, notes, and snippets.

@salano-ym
Forked from taiyme/!MisskeyPlugins.md
Last active February 17, 2023 13:02
Show Gist options
  • Save salano-ym/a771ee15806dad19f1849955ac52ec4e to your computer and use it in GitHub Desktop.
Save salano-ym/a771ee15806dad19f1849955ac52ec4e to your computer and use it in GitHub Desktop.
taiy製Misskeyプラグイン AiScript 0.12.4移植版

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はこちら

配布しているプラグインの質問や要望は @[email protected] へお願いします。

導入

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

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

目次

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