パクるプラグイン(aiscript1.1.0以降対応版)
ノートメニューに【パクる】を追加します。
基本情報
| バージョン | 3.0.0 |
|---|---|
| 作者 | @souhait@submarin.online (Original: @taiy@submarin.online, 0.12.4Fix: @elysion@submarin.online) |
| AiScript | 1.1.0 |
| ファイル | pakuru.ais |
| SHA-512 | b95baa05506610fae46e6740cc7ef3596eb8e6fbfb766bafcf7b97df4e5d1b9c5d5b4e8efa481f5cd10ba89899f3f2e16d296893b951007bc0714fc1ebc1a960 |
インストール
- インストール先のサーバー(Misskey v2023.11.0 以降)にログインした状態で開いてください。
- インストール時にハッシュ値が照合されるため、配布しているファイルが改ざんされていないことを確認できます。
- このプラグインが要求する権限: write:notes, write:drive, read:drive
設定項目
| キー | 型 | ラベル | 説明 | 既定値 |
|---|---|---|---|---|
visibility | string | 公開範囲 | (既定値: auto) 以下から、投稿するノートの公開範囲を指定します。 auto public home followers | auto |
localOnly | boolean | 常にローカルでパクる | (既定値: 無効) 公開範囲が【auto】の場合、この指定は無視され、元のノートに追従します。 | false |
channel | boolean | 元のチャンネルでパクる | (既定値: 有効) 公開範囲が【auto】の場合、この指定は無視され、元のノートに追従します。 | true |
reply | boolean | 返信元でパクる | (既定値: 有効) | true |
renote | boolean | 引用もパクる | (既定値: 有効) | true |
poll | boolean | 投票もパクる | (既定値: 有効) | true |
files | boolean | 添付ファイルもパクる | (既定値: 有効) ドライブに保存・添付するため、時間を要することがあります。 | true |
配布 API
配布 URL: https://misskey-plugins.souhait.me/api/plugins/pakuru.json
次の形式の JSON を返します。data は改行コードを LF に統一したプラグインのソースコードです。
{
"type": "plugin",
"data": "<プラグインのソースコード>"
}ソースコード
表示する
/// @ 1.1.0
### {
name: "パクるプラグイン(aiscript1.1.0以降対応版)"
version: "3.0.0"
author: "@souhait@submarin.online (Original: @taiy@submarin.online, 0.12.4Fix: @elysion@submarin.online)"
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 {
var id = file.id
arr.map(@(obj, index) {
if (file.comment == obj.uuid) {
var file_ex = Mk:api("drive/files/update", {
fileId: file.id
comment: obj.comment
})
complete[index] = file_ex
id = file_ex.id
}
})
latestId = id
}
}
if !(complete.incl(null)) {
stop()
callback(complete)
}
}, true)
return null
}
@parse(note, callback) {
@toBool(value) {
match value {
case true => return true
case false => return false
case "" => return false
case null => return false
case 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)
})
})