これは@[email protected]
による移植版です。
参照: https://gist.github.com/saki-lere/8d128649391d59fb0e4c942c5557328e
Copyright 2022 taiy https://github.com/taiyme
Apache License Version 2.0 https://www.apache.org/licenses/LICENSE-2.0
配布しているプラグインの質問や要望は @[email protected] へお願いします。
設定 > クライアント設定「プラグイン」> プラグインのインストール
免責: プラグインの導入は自己責任でお願いします。
Notes: プラグインの説明 - 配布ファイル。
パクるプラグインを適用すると、ノートメニューに「パクる」が、数字引用プラグインを適用すると、ノートメニューに「数字引用する」が出現します。
数字引用について → 数字引用 - Submarin Wiki
【設定】 以下のオプションを指定します。
auto
public
home
followers
有効
無効
auto
の場合、この指定は無視され、元のノートに追従します。有効
無効
auto
の場合、この指定は無視され、元のノートに追従します。有効
無効
有効
無効
有効
無効
有効
無効
Notes: 数字引用プラグインは、それぞれ「パクる」を「数字引用する」に読み替えます。
【権限】 このプラグインはそれぞれの理由のため権限の許可が必要です。
投稿時刻を表示するプラグインを適用すると、ノートメニューに「投稿時刻を表示」が出現します。
【設定】 以下のオプションを指定します。
YYYY/MM/DD HH:mm:ss.SSS
540
【権限】 このプラグインは許可の必要な権限はありません。
いまのなしって言ったら直前のノートが削除されるやつ
ここが詳しい → いまのなし | Misskey Plugins & CSS
【設定】 以下のオプションを指定します。
いまのなし
有効
無効
有効
無効
Notes: いまのなしv2.0.0以降、完全一致/部分一致のオプションが独立し、個別で指定できるようになりました。
【権限】 このプラグインはそれぞれの理由のため権限の許可が必要です。
/// @ 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 | |
}) |