ドキュメントをmisskey-hubに移行

This commit is contained in:
syuilo 2021-11-05 16:18:52 +09:00
parent c625a3fa5b
commit 8630542146
1355 changed files with 13 additions and 60421 deletions

View file

@ -49,10 +49,6 @@ If your language is not listed in Crowdin, please open an issue.
![Crowdin](https://d322cqt584bo4o.cloudfront.net/misskey/localized.svg)
## Documentation
* Documents for instance admins are located in [`/docs`](/docs).
* Documents for end users are located in [`/src/docs`](/src/docs).
## Testing
- Test codes are located in [`/test`](/test).

View file

@ -2,6 +2,3 @@ files:
- source: /locales/ja-JP.yml
translation: /locales/%locale%.yml
update_option: update_as_unapproved
- source: /src/docs/ja-JP/**/*.md
translation: /src/docs/%locale%/**/%original_file_name%
update_option: update_as_unapproved

View file

@ -816,14 +816,6 @@ _accountDelete:
started: "削除処理が開始されました。"
inProgress: "削除が進行中"
_docs:
continueReading: "続きを読む"
features: "機能"
generalTopics: "一般的なトピック"
advancedTopics: "高度なトピック"
admin: "管理"
translateWarn: "このドキュメントは翻訳されたものです。オリジナルとは内容が異なる場合があります。"
_ad:
back: "戻る"
reduceFrequencyOfThisAd: "この広告の表示頻度を下げる"

View file

@ -71,7 +71,6 @@
"@types/koa__cors": "3.0.3",
"@types/koa__multer": "2.0.4",
"@types/koa__router": "8.0.8",
"@types/markdown-it": "12.2.3",
"@types/matter-js": "0.17.6",
"@types/mocha": "8.2.3",
"@types/node": "16.11.4",
@ -169,8 +168,6 @@
"koa-slow": "2.1.0",
"koa-views": "7.0.1",
"langmap": "0.0.16",
"markdown-it": "12.2.0",
"markdown-it-anchor": "7.1.0",
"matter-js": "0.17.1",
"mfm-js": "0.20.0",
"misskey-js": "0.0.8",

View file

@ -16,10 +16,10 @@
</template>
</div>
<div class="sub">
<MkA to="/docs" @click.passive="close()" v-click-anime>
<a href="https://misskey-hub.net/help.html" target="_blank" @click.passive="close()" v-click-anime>
<i class="fas fa-question-circle icon"></i>
<div class="text">{{ $ts.help }}</div>
</MkA>
</a>
<MkA to="/about" @click.passive="close()" v-click-anime>
<i class="fas fa-info-circle icon"></i>
<div class="text">{{ $t('aboutX', { x: instanceName }) }}</div>

View file

@ -30,7 +30,7 @@ export default defineComponent({
methods: {
whatIsNew() {
this.$refs.modal.close();
this.$router.push('/docs/general/changelog');
window.open(`https://misskey-hub.net/docs/releases.html#_${version.replace(/\./g, '-')}`, '_blank');
}
}
});

View file

@ -11,7 +11,7 @@
</div>
</section>
<section class="_debobigegoItem" style="text-align: center; padding: 0 16px;">
{{ $ts._aboutMisskey.about }}<br><MkA class="_link" to="/docs/general/misskey">{{ $ts.learnMore }}</MkA>
{{ $ts._aboutMisskey.about }}<br><a href="https://misskey-hub.net/docs/misskey.html" target="_blank" class="_link">{{ $ts.learnMore }}</a>
</section>
<FormGroup>
<FormLink to="https://github.com/misskey-dev/misskey" external>

View file

@ -1,240 +0,0 @@
<template>
<div class="qyqbqfal" v-size="{ max: [500] }">
<div class="main">
<div class="title">{{ title }}</div>
<div class="body" v-html="body"></div>
<div class="footer">
<MkLink :url="`https://github.com/misskey-dev/misskey/blob/master/src/docs/${lang}/${doc}.md`" class="at">{{ $ts.docSource }}</MkLink>
<p v-if="lang !== 'ja-JP'">{{ $ts.translateWarn }}</p>
</div>
</div>
</div>
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue';
import MarkdownIt from 'markdown-it';
import MarkdownItAnchor from 'markdown-it-anchor';
import { url, lang } from '@client/config';
import MkLink from '@client/components/link.vue';
import * as symbols from '@client/symbols';
const markdown = MarkdownIt({
html: true
});
markdown.use(MarkdownItAnchor, {
slugify: (s) => encodeURIComponent(String(s).trim().replace(/\s+/g, '-'))
});
export default defineComponent({
components: {
MkLink
},
props: {
doc: {
type: String,
required: true
}
},
data() {
return {
[symbols.PAGE_INFO]: computed(() => this.title ? {
title: this.title,
icon: 'fas fa-question-circle',
} : null),
title: null,
body: null,
markdown: null,
lang,
}
},
watch: {
doc: {
handler() {
this.fetchDoc();
},
immediate: true,
}
},
methods: {
fetchDoc() {
fetch(`${url}/doc-assets/${lang}/${this.doc}.md`).then(res => res.text()).then(md => {
this.parse(md);
}).catch(() => {
fetch(`${url}/doc-assets/ja-JP/${this.doc}.md`).then(res => res.text()).then(md => {
this.parse(md);
});
});
},
parse(md: string) {
//
md = md.replace(/\{_URL_\}/g, url);
// markdown
const parsed = markdown.parse(md, {});
if (parsed.length === 0) return;
const buf = [...parsed];
const headingTokens = [];
let headingStart = 0;
//
while (buf[0].type !== 'heading_open') {
buf.shift();
headingStart++;
}
buf.shift();
while (buf[0].type as string !== 'heading_close') {
const token = buf.shift();
if (token) {
headingTokens.push(token);
}
}
// body
const bodyTokens = [...parsed];
bodyTokens.splice(headingStart, headingTokens.length + 2);
//
this.title = markdown.renderer.render(headingTokens, {}, {});
this.body = markdown.renderer.render(bodyTokens, {}, {});
}
}
});
</script>
<style lang="scss" scoped>
.qyqbqfal {
padding: 32px;
background: var(--panel);
line-height: 1.5;
&.max-width_500px {
padding: 24px;
}
> .main {
max-width: 800px;
margin: 0 auto;
> .title {
font-size: 1.5em;
font-weight: bold;
padding: 0 0 0.75em 0;
margin: 0 0 1em 0;
border-bottom: solid 2px var(--divider);
}
> .body {
> *:first-child {
margin-top: 0;
}
> *:last-child {
margin-bottom: 0;
}
::v-deep(a) {
color: var(--link);
}
::v-deep(blockquote) {
display: block;
margin: 8px;
padding: 6px 0 6px 12px;
color: var(--fg);
border-left: solid 3px var(--fg);
opacity: 0.7;
p {
margin: 0;
}
}
::v-deep(h2) {
font-size: 1.25em;
padding: 0 0 0.5em 0;
margin: 1.5em 0 1em 0;
border-bottom: solid 0.5px var(--divider);
}
::v-deep(h3) {
margin: 1.25em 0 0.5em 0;
}
::v-deep(table) {
width: 100%;
max-width: 100%;
overflow: auto;
}
::v-deep(kbd.group) {
display: inline-block;
padding: 2px;
border: 1px solid var(--divider);
border-radius: 4px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
}
::v-deep(kbd.key) {
display: inline-block;
padding: 6px 8px;
border: solid 0.5px var(--divider);
border-radius: 4px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
}
::v-deep(code) {
display: inline-block;
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
tab-size: 2;
background: #272822;
color: #f8f8f2;
border-radius: 6px;
padding: 4px 6px;
}
::v-deep(pre) {
background: #272822;
color: #f8f8f2;
border-radius: 6px;
padding: 12px 16px;
> code {
padding: 0;
}
}
::v-deep(.info) {
font-size: 90%;
background: var(--infoBg);
color: var(--infoFg);
padding: 1em;
margin: 0.75em 0;
border-radius: 6px;
}
::v-deep(.warn) {
font-size: 90%;
background: var(--infoWarnBg);
color: var(--infoWarnFg);
padding: 1em;
margin: 0.75em 0;
border-radius: 6px;
}
}
> .footer {
padding: 1.5em 0 0 0;
margin: 1.5em 0 0 0;
border-top: solid 2px var(--divider);
}
}
}
</style>

View file

@ -1,152 +0,0 @@
<template>
<div class="vtaihdtm">
<div class="body">
<div class="search">
<MkInput v-model="query" :debounce="true" type="search" class="" :placeholder="$ts.search">
<template #prefix><i class="fas fa-search"></i></template>
</MkInput>
</div>
<div class="list">
<MkFolder>
<template #header>{{ $ts._docs.generalTopics }}</template>
<div class="docs">
<MkA v-for="doc in docs.filter(doc => doc.path.startsWith('general/'))" :key="doc.path" :to="`/docs/${doc.path}`" class="doc">
<div class="title">{{ doc.title }}</div>
<div class="summary">{{ doc.summary }}</div>
<div class="read">{{ $ts._docs.continueReading }}</div>
</MkA>
</div>
</MkFolder>
<MkFolder>
<template #header>{{ $ts._docs.features }}</template>
<div class="docs">
<MkA v-for="doc in docs.filter(doc => doc.path.startsWith('features/'))" :key="doc.path" :to="`/docs/${doc.path}`" class="doc">
<div class="title">{{ doc.title }}</div>
<div class="summary">{{ doc.summary }}</div>
<div class="read">{{ $ts._docs.continueReading }}</div>
</MkA>
</div>
</MkFolder>
<MkFolder>
<template #header>{{ $ts._docs.advancedTopics }}</template>
<div class="docs">
<MkA v-for="doc in docs.filter(doc => doc.path.startsWith('advanced/'))" :key="doc.path" :to="`/docs/${doc.path}`" class="doc">
<div class="title">{{ doc.title }}</div>
<div class="summary">{{ doc.summary }}</div>
<div class="read">{{ $ts._docs.continueReading }}</div>
</MkA>
</div>
</MkFolder>
<MkFolder>
<template #header>{{ $ts._docs.admin }}</template>
<div class="docs">
<MkA v-for="doc in docs.filter(doc => doc.path.startsWith('admin/'))" :key="doc.path" :to="`/docs/${doc.path}`" class="doc">
<div class="title">{{ doc.title }}</div>
<div class="summary">{{ doc.summary }}</div>
<div class="read">{{ $ts._docs.continueReading }}</div>
</MkA>
</div>
</MkFolder>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { url, lang } from '@client/config';
import * as symbols from '@client/symbols';
import MkFolder from '@client/components/ui/folder.vue';
import MkInput from '@client/components/form/input.vue';
export default defineComponent({
components: {
MkFolder,
MkInput,
},
data() {
return {
[symbols.PAGE_INFO]: {
title: this.$ts.help,
icon: 'fas fa-question-circle'
},
docs: [],
query: null,
}
},
watch: {
query() {
fetch(`${url}/docs.json?lang=${lang}&q=${this.query}`).then(res => res.json()).then(docs => {
this.docs = docs;
});
}
},
created() {
fetch(`${url}/docs.json?lang=ja-JP`).then(res => res.json()).then(jaDocs => {
fetch(`${url}/docs.json?lang=${lang}`).then(res => res.json()).then(docs => {
this.docs = jaDocs.map(doc => {
const exist = docs.find(d => d.path === doc.path);
return exist || doc;
});
});
});
},
});
</script>
<style lang="scss" scoped>
.vtaihdtm {
background: var(--panel);
> .body {
max-width: 900px;
margin: 0 auto;
> .search {
padding: 16px;
}
> .list {
padding: 0 16px;
.docs {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
grid-gap: 12px;
margin: 0 0 16px 0;
> .doc {
display: inline-block;
padding: 16px;
border: solid 1px var(--divider);
border-radius: 6px;
&:hover {
border: solid 1px var(--accent);
text-decoration: none;
}
> .title {
font-weight: bold;
}
> .summary {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 0.9em;
}
> .read {
color: var(--link);
font-size: 0.9em;
}
}
}
}
}
}
</style>

View file

@ -23,7 +23,7 @@
</template>
<script lang="ts">
// SPECIFICATION: /src/docs/ja-JP/advanced/share-page.md
// SPECIFICATION: https://misskey-hub.net/docs/features/share-form.html
import { defineComponent } from 'vue';
import MkButton from '@client/components/ui/button.vue';

View file

@ -43,7 +43,7 @@
<div>{{ $ts._tutorial.step7_1 }}</div>
<I18n :src="$ts._tutorial.step7_2" tag="div">
<template #help>
<MkA class="_link" to="/docs">{{ $ts.help }}</MkA>
<a href="https://misskey-hub.net/help.html" target="_blank" class="_link">{{ $ts.help }}</a>
</template>
</I18n>
<div>{{ $ts._tutorial.step7_3 }}</div>

View file

@ -133,7 +133,7 @@ export default defineComponent({
text: this.$ts.help,
icon: 'fas fa-question-circle',
action: () => {
os.pageWindow('/docs');
window.open(`https://misskey-hub.net/help.md`, '_blank');
}
}], ev.currentTarget || ev.target);
},

View file

@ -117,7 +117,7 @@ export default defineComponent({
text: this.$ts.help,
icon: 'fas fa-question-circle',
action: () => {
os.pageWindow('/docs');
window.open(`https://misskey-hub.net/help.md`, '_blank');
}
}], ev.currentTarget || ev.target);
},

View file

@ -137,7 +137,7 @@ export default defineComponent({
text: this.$ts.help,
icon: 'fas fa-question-circle',
action: () => {
os.pageWindow('/docs');
window.open(`https://misskey-hub.net/help.md`, '_blank');
}
}], ev.currentTarget || ev.target);
},

View file

@ -28,10 +28,8 @@ const defaultRoutes = [
{ path: '/about', component: page('about') },
{ path: '/about-misskey', component: page('about-misskey') },
{ path: '/featured', component: page('featured') },
{ path: '/docs', component: page('docs') },
{ path: '/theme-editor', component: page('theme-editor') },
{ path: '/advanced-theme-editor', component: page('advanced-theme-editor') },
{ path: '/docs/:doc(.*)', component: page('doc'), props: route => ({ doc: route.params.doc }) },
{ path: '/explore', component: page('explore') },
{ path: '/explore/tags/:tag', props: true, component: page('explore') },
{ path: '/federation', component: page('federation') },

View file

@ -53,7 +53,7 @@ export default defineComponent({
methods: {
help() {
this.$router.push('/docs/keyboard-shortcut');
window.open(`https://misskey-hub.net/docs/keyboard-shortcut.md`, '_blank');
},
}
});

View file

@ -120,7 +120,7 @@ export default defineComponent({
},
help() {
this.$router.push('/docs/keyboard-shortcut');
window.open(`https://misskey-hub.net/docs/keyboard-shortcut.md`, '_blank');
},
onTransition() {

View file

@ -134,7 +134,7 @@ export default defineComponent({
},
help() {
this.$router.push('/docs/keyboard-shortcut');
window.open(`https://misskey-hub.net/docs/keyboard-shortcut.md`, '_blank');
},
onTransition() {

View file

@ -56,7 +56,7 @@ export default defineComponent({
},
help() {
this.$router.push('/docs/keyboard-shortcut');
window.open(`https://misskey-hub.net/docs/keyboard-shortcut.md`, '_blank');
},
onTransition() {

View file

@ -1,8 +0,0 @@
# LTL/STL/GTLの無効化
Misskeyでは、LTL/STL/GTLをそれぞれ無効化することができます。有効/無効を切り替えるには、インスタンスコントロールパネルで設定します。
LTLやSTLは、そのインスタンス全員の投稿が見れるため、新規のユーザーにとってはユーザーを探す必要がなくなり、興味のあるユーザーを見つけやすいという利点があります。 しかし同時に、フォロー機能が活用されなくなったり、不適切な投稿が目につきやすくなったり、チャットのようになることで内輪感が生じて逆に新規ユーザーが参加しにくくなるといったデメリットも持ち合わせています。 サーバーによってメリット/デメリットどちらが優勢かは異なるので、オプションとして無効にできるようになっています。 もしデメリットの方が上回っていると感じたら、それらのタイムラインを無効化することも検討してください。
<div class="warn">⚠️ 無効化を行うと、ユーザーが困惑し、短期的に見て利用者が減る可能性があります。そのため、無効化の際は影響を慎重に検討し、事前に説明してフォローを整える期間を一定程度設けることを推奨します。</div>
なお、管理者/モデレーターは、これらのタイムラインの無効化状態は適用されず、引き続き利用することが可能です。

View file

@ -1,5 +0,0 @@
# よくある質問
ここでは、サーバー管理者向けのよくある質問を掲載しています。
## デフォルトテーマを設定したい
現在、デフォルトテーマ設定機能は実装されていません。

View file

@ -1,7 +0,0 @@
# AiScript
AiScriptは、Misskeyで使用できるスクリプト言語です。
<div class="info"> AiScript実装はMisskeyとは別リポジトリで、<a href="https://github.com/syuilo/aiscript" target="_blank">オープンソースで公開されています。</a></div>
## 使い方
AiScriptの構文や組み込み関数などのドキュメントは、[こちら](https://github.com/syuilo/aiscript/tree/master/docs)で公開されています。

View file

@ -1,58 +0,0 @@
# Misskey API
MisskeyAPIを使ってMisskeyクライアント、Misskey連携Webサービス、Bot等(以下「アプリケーション」と呼びます)を開発できます。 ストリーミングAPIもあるので、リアルタイム性のあるアプリケーションを作ることも可能です。
APIを使い始めるには、まずアクセストークンを取得する必要があります。 このドキュメントでは、アクセストークンを取得する手順を説明した後、基本的なAPIの使い方を説明します。
## アクセストークンの取得
基本的に、APIはリクエストにはアクセストークンが必要となります。 APIにリクエストするのが自分自身なのか、不特定の利用者に使ってもらうアプリケーションなのかによって取得手順は異なります。
* 前者の場合: [「自分自身のアクセストークンを手動発行する」](#自分自身のアクセストークンを手動発行する)に進む
* 後者の場合: [「アプリケーション利用者にアクセストークンの発行をリクエストする」](#アプリケーション利用者にアクセストークンの発行をリクエストする)に進む
### 自分自身のアクセストークンを手動発行する
「設定 > API」で、自分のアクセストークンを発行できます。
[「APIの使い方」へ進む](#APIの使い方)
### アプリケーション利用者にアクセストークンの発行をリクエストする
アプリケーション利用者のアクセストークンを取得するには、以下の手順で発行をリクエストします。
#### Step 1
UUIDを生成する。以後これをセッションIDと呼びます。
> このセッションIDは毎回生成し、使いまわさないようにしてください。
#### Step 2
`{_URL_}/miauth/{session}`をユーザーのブラウザで表示させる。`{session}`の部分は、セッションIDに置き換えてください。
> 例: `{_URL_}/miauth/c1f6d42b-468b-4fd2-8274-e58abdedef6f`
表示する際、URLにクエリパラメータとしていくつかのオプションを設定できます:
* `name` ... アプリケーション名
* > 例: `MissDeck`
* `icon` ... アプリケーションのアイコン画像URL
* > 例: `https://missdeck.example.com/icon.png`
* `callback` ... 認証が終わった後にリダイレクトするURL
* > 例: `https://missdeck.example.com/callback`
* リダイレクト時には、`session`というクエリパラメータでセッションIDが付きます
* `permission` ... アプリケーションが要求する権限
* > 例: `write:notes,write:following,read:drive`
* 要求する権限を`,`で区切って列挙します
* どのような権限があるかは[APIリファレンス](/api-doc)で確認できます
#### Step 3
ユーザーが発行を許可した後、`{_URL_}/api/miauth/{session}/check`にPOSTリクエストすると、レスポンスとしてアクセストークンを含むJSONが返ります。
レスポンスに含まれるプロパティ:
* `token` ... ユーザーのアクセストークン
* `user` ... ユーザーの情報
[「APIの使い方」へ進む](#APIの使い方)
## APIの使い方
**APIはすべてPOSTで、リクエスト/レスポンスともにJSON形式です。RESTではありません。** アクセストークンは、`i`というパラメータ名でリクエストに含めます。
* [APIリファレンス](/api-doc)
* [ストリーミングAPI](./stream)

View file

@ -1,74 +0,0 @@
# プラグインの作成
Misskey Webクライアントのプラグイン機能を使うと、クライアントを拡張し、様々な機能を追加できます。 ここではプラグインの作成にあたってのメタデータ定義や、AiScript APIリファレンスを掲載します。
## البيانات الوصفية
プラグインは、AiScriptのメタデータ埋め込み機能を使って、デフォルトとしてプラグインのメタデータを定義する必要があります。 メタデータは次のプロパティを含むオブジェクトです。
### name
プラグイン名
### author
プラグイン作者
### version
プラグインバージョン。数値を指定してください。
### description
プラグインの説明
### permissions
プラグインが要求する権限。MisskeyAPIにリクエストする際に用いられます。
### config
プラグインの設定情報を表すオブジェクト。 キーに設定名、値に以下のプロパティを含めます。
#### type
設定値の種類を表す文字列。以下から選択します。 string number boolean
#### label
ユーザーに表示する設定名
#### description
設定の説明
#### default
設定のデフォルト値
## APIリファレンス
AiScript標準で組み込まれているAPIは掲載しません。
### Mk:dialog(title text type)
ダイアログを表示します。typeには以下の値が設定できます。 info success warn error question 省略すると info になります。
### Mk:confirm(title text type)
確認ダイアログを表示します。typeには以下の値が設定できます。 info success warn error question 省略すると question になります。 ユーザーが"OK"を選択した場合は true を、"キャンセル"を選択した場合は false が返ります。
### Mk:api(endpoint params)
Misskey APIにリクエストします。第一引数にエンドポイント名、第二引数にパラメータオブジェクトを渡します。
### Mk:save(key value)
任意の値に任意の名前を付けて永続化します。永続化した値は、AiScriptコンテキストが終了しても残り、Mk:loadで読み取ることができます。
### Mk:load(key)
Mk:saveで永続化した指定の名前の値を読み取ります。
### Plugin:register_post_form_action(title fn)
投稿フォームにアクションを追加します。第一引数にアクション名、第二引数にアクションが選択された際のコールバック関数を渡します。 コールバック関数には、第一引数に投稿フォームオブジェクトが渡されます。
### Plugin:register_note_action(title fn)
ノートメニューに項目を追加します。第一引数に項目名、第二引数に項目が選択された際のコールバック関数を渡します。 コールバック関数には、第一引数に対象のノートオブジェクトが渡されます。
### Plugin:register_user_action(title fn)
ユーザーメニューに項目を追加します。第一引数に項目名、第二引数に項目が選択された際のコールバック関数を渡します。 コールバック関数には、第一引数に対象のユーザーオブジェクトが渡されます。
### Plugin:register_note_view_interruptor(fn)
UIに表示されるート情報を書き換えます。 コールバック関数には、第一引数に対象のノートオブジェクトが渡されます。 コールバック関数の返り値でノートが書き換えられます。
### Plugin:register_note_post_interruptor(fn)
ノート投稿時にノート情報を書き換えます。 コールバック関数には、第一引数に対象のノートオブジェクトが渡されます。 コールバック関数の返り値でノートが書き換えられます。
### Plugin:open_url(url)
第一引数に渡されたURLをブラウザの新しいタブで開きます。
### Plugin:config
プラグインの設定が格納されるオブジェクト。プラグイン定義のconfigで設定したキーで値が入ります。

View file

@ -1,6 +0,0 @@
# Botの作成
[Misskey API](./api)を利用してBotの開発が可能です。 また、いくつかのBot実装が公開されているため、ぜひ参考にしてください。
- [syuilo/ai](https://github.com/syuilo/ai) ... Node.js上で動く、TypeScript製Bot実装
Botを作成したときは、プロフィール設定からBotフラグをオンにしておくことを強くおすすめします。

View file

@ -1,160 +0,0 @@
# MisskeyリバーシBotの開発
Misskeyのリバーシ機能に対応したBotの開発方法をここに記します。
1. `games/reversi`ストリームに以下のパラメータを付けて接続する:
* `i`: botアカウントのAPIキー
2. 対局への招待が来たら、ストリームから`invited`イベントが流れてくる
* イベントの中身に、`parent`という名前で対局へ誘ってきたユーザーの情報が含まれている
3. `games/reversi/match`へ、`user_id`として`parent`の`id`が含まれたリクエストを送信する
4. 上手くいくとゲーム情報が返ってくるので、`games/reversi-game`ストリームへ、以下のパラメータを付けて接続する:
* `i`: botアカウントのAPIキー
* `game`: `game`の`id`
5. この間、相手がゲームの設定を変更するとその都度`update-settings`イベントが流れてくるので、必要であれば何かしらの処理を行う
6. 設定に満足したら、`{ type: 'accept' }`メッセージをストリームに送信する
7. ゲームが開始すると、`started`イベントが流れてくる
* イベントの中身にはゲーム情報が含まれている
8. 石を打つには、ストリームに`{ type: 'set', pos: <位置> }`を送信する(位置の計算方法は後述)
9. 相手または自分が石を打つと、ストリームから`set`イベントが流れてくる
* `color`として石の色が含まれている
* `pos`として位置情報が含まれている
## 位置の計算法
8x8のマップを考える場合、各マスの位置(インデックスと呼びます)は次のようになっています:
```
+--+--+--+--+--+--+--+--+
| 0| 1| 2| 3| 4| 5| 6| 7|
+--+--+--+--+--+--+--+--+
| 8| 9|10|11|12|13|14|15|
+--+--+--+--+--+--+--+--+
|16|17|18|19|20|21|22|23|
...
```
### X,Y座標 から インデックス に変換する
```
pos = x + (y * mapWidth)
```
`mapWidth`は、ゲーム情報の`map`から、次のようにして計算できます:
```
mapWidth = map[0].length
```
### インデックス から X,Y座標 に変換する
```
x = pos % mapWidth
y = Math.floor(pos / mapWidth)
```
## マップ情報
マップ情報は、ゲーム情報の`map`に入っています。 文字列の配列になっており、ひとつひとつの文字がマス情報を表しています。 それをもとにマップのデザインを知る事が出来ます:
* `(スペース)` ... マス無し
* `-` ... マス
* `b` ... 初期配置される黒石
* `w` ... 初期配置される白石
例えば、4*4の次のような単純なマップがあるとします:
```text
+---+---+---+---+
| | | | |
+---+---+---+---+
| | ○ | ● | |
+---+---+---+---+
| | ● | ○ | |
+---+---+---+---+
| | | | |
+---+---+---+---+
```
この場合、マップデータはこのようになります:
```javascript
['----', '-wb-', '-bw-', '----']
```
## ユーザーにフォームを提示して対話可能Botを作成する
ユーザーとのコミュニケーションを行うため、ゲームの設定画面でユーザーにフォームを提示することができます。 例えば、Botの強さをユーザーが設定できるようにする、といったシナリオが考えられます。
フォームを提示するには、`reversi-game`ストリームに次のメッセージを送信します:
```javascript
{
type: 'init-form',
body: [フォームコントロールの配列]
}
```
フォームコントロールの配列については今から説明します。 フォームコントロールは、次のようなオブジェクトです:
```javascript
{
id: 'switch1',
type: 'switch',
label: 'Enable hoge',
value: false
}
```
`id` ... コントロールのID。 `type` ... コントロールの種類。後述します。 `label` ... コントロールと一緒に表記するテキスト。 `value` ... コントロールのデフォルト値。
### フォームの操作を受け取る
ユーザーがフォームを操作すると、ストリームから`update-form`イベントが流れてきます。 イベントの中身には、コントロールのIDと、ユーザーが設定した値が含まれています。 例えば、上で示したスイッチをユーザーがオンにしたとすると、次のイベントが流れてきます:
```javascript
{
id: 'switch1',
value: true
}
```
### フォームコントロールの種類
#### スイッチ
type: `switch` スイッチを表示します。何かの機能をオン/オフさせたい場合に有用です。
##### プロパティ
`label` ... スイッチに表記するテキスト。
#### ラジオボタン
type: `radio` ラジオボタンを表示します。選択肢を提示するのに有用です。例えば、Botの強さを設定させるなどです。
##### プロパティ
`items` ... ラジオボタンの選択肢。例:
```javascript
items: [{
label: '弱',
value: 1
}, {
label: '中',
value: 2
}, {
label: '強',
value: 3
}]
```
#### スライダー
type: `slider` スライダーを表示します。
##### プロパティ
`min` ... スライダーの下限。 `max` ... スライダーの上限。 `step` ... 入力欄で刻むステップ値。
#### テキストボックス
type: `textbox` テキストボックスを表示します。ユーザーになにか入力させる一般的な用途に利用できます。
## ユーザーにメッセージを表示する
設定画面でユーザーと対話する、フォーム以外のもうひとつの方法がこれです。ユーザーになにかメッセージを表示することができます。 例えば、ユーザーがBotの対応していないモードやマップを選択したとき、警告を表示するなどです。 メッセージを表示するには、次のメッセージをストリームに送信します:
```javascript
{
type: 'message',
body: {
text: 'メッセージ内容',
type: 'メッセージの種類'
}
}
```
メッセージの種類: `success`, `info`, `warning`, `error`
## 投了する
投了をするには、<a href="./api/endpoints/games/reversi/games/surrender">このエンドポイント</a>にリクエストします。

View file

@ -1,54 +0,0 @@
# シェアページ
`/share`を開くと、共有用の投稿フォームを開くことができます。 ここではシェアページで利用できるクエリ文字列の一覧を示します。
## クエリ文字列一覧
### 文字
<dl>
<dt>title</dt>
<dd>タイトルです。本文の先頭に[ … ]と挿入されます。</dd>
<dt>text</dt>
<dd>本文です。</dd>
<dt>url</dt>
<dd>URLです。末尾に挿入されます。</dd>
</dl>
### リプライ情報
以下のいずれか
<dl>
<dt>replyId</dt>
<dd>リプライ先のートid</dd>
<dt>replyUri</dt>
<dd>リプライ先のUrlリモートのートオブジェクトを指定</dd>
</dl>
### Renote情報
以下のいずれか
<dl>
<dt>renoteId</dt>
<dd>Renote先のートid</dd>
<dt>renoteUri</dt>
<dd>Renote先のUrlリモートのートオブジェクトを指定</dd>
</dl>
### الظهور
※specifiedに相当する値はvisibility=specifiedとvisibleAccts/visibleUserIdsで指定する
<dl>
<dt>visibility</dt>
<dd>公開範囲 ['public' | 'home' | 'followers' | 'specified']</dd>
<dt>localOnly</dt>
<dd>0(false) or 1(true)</dd>
<dt>visibleUserIds</dt>
<dd>specified時のダイレクト先のユーザーid カンマ区切りで</dd>
<dt>visibleAccts</dt>
<dd>specified時のダイレクト先のacct?username[host] カンマ区切りで</dd>
</dl>
### الملفات
<dl>
<dt>fileIds</dt>
<dd>添付したいファイルのidカンマ区切りで</dd>
</dl>

View file

@ -1,350 +0,0 @@
# ストリーミングAPI
ストリーミングAPIを使うと、リアルタイムで様々な情報(例えばタイムラインに新しい投稿が流れてきた、メッセージが届いた、フォローされた、など)を受け取ったり、様々な操作を行ったりすることができます。
## ストリームに接続する
ストリーミングAPIを利用するには、まずMisskeyサーバーに**websocket**接続する必要があります。
以下のURLに、`i`というパラメータ名で認証情報を含めて、websocket接続してください。例:
```
%WS_URL%/streaming?i=xxxxxxxxxxxxxxx
```
認証情報は、自分のAPIキーや、アプリケーションからストリームに接続する際はユーザーのアクセストークンのことを指します。
<div class="info"> 認証情報の取得については、<a href="./api">こちらのドキュメント</a>をご確認ください。</div>
---
認証情報は省略することもできますが、その場合非ログインでの利用ということになり、受信できる情報や可能な操作は限られます。例:
```
%WS_URL%/streaming
```
---
ストリームに接続すると、後述するAPI操作や、投稿の購読を行ったりすることができます。 しかしまだこの段階では、例えばタイムラインへの新しい投稿を受信したりすることはできません。 それを行うには、ストリーム上で、後述する**チャンネル**に接続する必要があります。
**ストリームでのやり取りはすべてJSONです。**
## القنوات
MisskeyのストリーミングAPIにはチャンネルという概念があります。これは、送受信する情報を分離するための仕組みです。 Misskeyのストリームに接続しただけでは、まだリアルタイムでタイムラインの投稿を受信したりはできません。 ストリーム上でチャンネルに接続することで、様々な情報を受け取ったり情報を送信したりすることができるようになります。
ひとつのストリーム上で、同時に複数のチャンネルに接続することができます。
### チャンネルに接続する
チャンネルに接続するには、次のようなデータをJSONでストリームに送信します:
```json
{
type: 'connect',
body: {
channel: 'xxxxxxxx',
id: 'foobar',
params: {
...
}
}
}
```
ここで、
* `channel`には接続したいチャンネル名を設定します。チャンネルの種類については後述します。
* `id`にはそのチャンネルとやり取りするための任意のIDを設定します。ストリームでは様々なメッセージが流れるので、そのメッセージがどのチャンネルからのものなのか識別する必要があるからです。このIDは、UUIDや、乱数のようなもので構いません。
* `params`はチャンネルに接続する際のパラメータです。チャンネルによって接続時に必要とされるパラメータは異なります。パラメータ不要のチャンネルに接続する際は、このプロパティは省略可能です。
<div class="info"> IDはチャンネルごとではなく「チャンネルの接続ごと」です。なぜなら、同じチャンネルに異なるパラメータで複数接続するケースもあるからです。</div>
### チャンネルからのメッセージを受け取る
例えばタイムラインのチャンネルなら、新しい投稿があった時にメッセージを発します。そのメッセージを受け取ることで、タイムラインに新しい投稿がされたことをリアルタイムで知ることができます。
チャンネルがメッセージを発すると、次のようなデータがJSONでストリームに流れてきます:
```json
{
type: 'channel',
body: {
id: 'foobar',
type: 'something',
body: {
some: 'thing'
}
}
}
```
ここで、
* `id`には前述したそのチャンネルに接続する際に設定したIDが設定されています。これで、このメッセージがどのチャンネルからのものなのか知ることができます。
* `type`にはメッセージの種類が設定されます。チャンネルによって、どのような種類のメッセージが流れてくるかは異なります。
* `body`にはメッセージの内容が設定されます。チャンネルによって、どのような内容のメッセージが流れてくるかは異なります。
### チャンネルに向けてメッセージを送信する
チャンネルによっては、メッセージを受け取るだけでなく、こちらから何かメッセージを送信し、何らかの操作を行える場合があります。
チャンネルにメッセージを送信するには、次のようなデータをJSONでストリームに送信します:
```json
{
type: 'channel',
body: {
id: 'foobar',
type: 'something',
body: {
some: 'thing'
}
}
}
```
ここで、
* `id`には前述したそのチャンネルに接続する際に設定したIDを設定します。これで、このメッセージがどのチャンネルに向けたものなのか識別させることができます。
* `type`にはメッセージの種類を設定します。チャンネルによって、どのような種類のメッセージを受け付けるかは異なります。
* `body`にはメッセージの内容を設定します。チャンネルによって、どのような内容のメッセージを受け付けるかは異なります。
### チャンネルから切断する
チャンネルから切断するには、次のようなデータをJSONでストリームに送信します:
```json
{
type: 'disconnect',
body: {
id: 'foobar'
}
}
```
ここで、
* `id`には前述したそのチャンネルに接続する際に設定したIDを設定します。
## ストリームを経由してAPIリクエストする
ストリームを経由してAPIリクエストすると、HTTPリクエストを発生させずにAPIを利用できます。そのため、コードを簡潔にできたり、パフォーマンスの向上を見込めるかもしれません。
ストリームを経由してAPIリクエストするには、次のようなデータをJSONでストリームに送信します:
```json
{
type: 'api',
body: {
id: 'xxxxxxxxxxxxxxxx',
endpoint: 'notes/create',
data: {
text: 'yee haw!'
}
}
}
```
ここで、
* `id`には、APIのレスポンスを識別するための、APIリクエストごとの一意なIDを設定する必要があります。UUIDや、簡単な乱数のようなもので構いません。
* `endpoint`には、あなたがリクエストしたいAPIのエンドポイントを指定します。
* `data`には、エンドポイントのパラメータを含めます。
<div class="info"> APIのエンドポイントやパラメータについてはAPIリファレンスをご確認ください。</div>
### レスポンスの受信
APIへリクエストすると、レスポンスがストリームから次のような形式で流れてきます。
```json
{
type: 'api:xxxxxxxxxxxxxxxx',
body: {
...
}
}
```
ここで、
* `xxxxxxxxxxxxxxxx`の部分には、リクエストの際に設定された`id`が含まれています。これにより、どのリクエストに対するレスポンスなのか判別することができます。
* `body`には、レスポンスが含まれています。
## 投稿のキャプチャ
Misskeyは投稿のキャプチャと呼ばれる仕組みを提供しています。これは、指定した投稿のイベントをストリームで受け取る機能です。
例えばタイムラインを取得してユーザーに表示したとします。ここで誰かがそのタイムラインに含まれるどれかの投稿に対してリアクションしたとします。
しかし、クライアントからするとある投稿にリアクションが付いたことなどは知る由がないため、リアルタイムでリアクションをタイムライン上の投稿に反映して表示するといったことができません。
この問題を解決するために、Misskeyは投稿のキャプチャ機構を用意しています。投稿をキャプチャすると、その投稿に関するイベントを受け取ることができるため、リアルタイムでリアクションを反映させたりすることが可能になります。
### 投稿をキャプチャする
投稿をキャプチャするには、ストリームに次のようなメッセージを送信します:
```json
{
type: 'subNote',
body: {
id: 'xxxxxxxxxxxxxxxx'
}
}
```
ここで、
* `id`にキャプチャしたい投稿の`id`を設定します。
このメッセージを送信すると、Misskeyにキャプチャを要請したことになり、以後、その投稿に関するイベントが流れてくるようになります。
例えば投稿にリアクションが付いたとすると、次のようなメッセージが流れてきます:
```json
{
type: 'noteUpdated',
body: {
id: 'xxxxxxxxxxxxxxxx',
type: 'reacted',
body: {
reaction: 'like',
userId: 'yyyyyyyyyyyyyyyy'
}
}
}
```
ここで、
* `body`内の`id`に、イベントを発生させた投稿のIDが設定されます。
* `body`内の`type`に、イベントの種類が設定されます。
* `body`内の`body`に、イベントの詳細が設定されます。
#### イベントの種類
##### `reacted`
その投稿にリアクションがされた時に発生します。
* `reaction`に、リアクションの種類が設定されます。
* `userId`に、リアクションを行ったユーザーのIDが設定されます。
例:
```json
{
type: 'noteUpdated',
body: {
id: 'xxxxxxxxxxxxxxxx',
type: 'reacted',
body: {
reaction: 'like',
userId: 'yyyyyyyyyyyyyyyy'
}
}
}
```
##### `deleted`
その投稿が削除された時に発生します。
* `deletedAt`に、削除日時が設定されます。
例:
```json
{
type: 'noteUpdated',
body: {
id: 'xxxxxxxxxxxxxxxx',
type: 'deleted',
body: {
deletedAt: '2018-10-22T02:17:09.703Z'
}
}
}
```
##### `pollVoted`
その投稿に添付されたアンケートに投票された時に発生します。
* `choice`に、選択肢IDが設定されます。
* `userId`に、投票を行ったユーザーのIDが設定されます。
例:
```json
{
type: 'noteUpdated',
body: {
id: 'xxxxxxxxxxxxxxxx',
type: 'pollVoted',
body: {
choice: 2,
userId: 'yyyyyyyyyyyyyyyy'
}
}
}
```
### 投稿のキャプチャを解除する
その投稿がもう画面に表示されなくなったりして、その投稿に関するイベントをもう受け取る必要がなくなったときは、キャプチャの解除を申請してください。
次のメッセージを送信します:
```json
{
type: 'unsubNote',
body: {
id: 'xxxxxxxxxxxxxxxx'
}
}
```
ここで、
* `id`にキャプチャを解除したい投稿の`id`を設定します。
このメッセージを送信すると、以後、その投稿に関するイベントは流れてこないようになります。
# チャンネル一覧
## `main`
アカウントに関する基本的な情報が流れてきます。このチャンネルにパラメータはありません。
### 流れてくるイベント一覧
#### `renote`
自分の投稿がRenoteされた時に発生するイベントです。自分自身の投稿をRenoteしたときは発生しません。
#### `mention`
誰かからメンションされたときに発生するイベントです。
#### `readAllNotifications`
自分宛ての通知がすべて既読になったことを表すイベントです。このイベントを利用して、「通知があることを示すアイコン」のようなものをオフにしたりする等のケースが想定されます。
#### `meUpdated`
自分の情報が更新されたことを表すイベントです。
#### `follow`
自分が誰かをフォローしたときに発生するイベントです。
#### `unfollow`
自分が誰かのフォローを解除したときに発生するイベントです。
#### `followed`
自分が誰かにフォローされたときに発生するイベントです。
## `homeTimeline`
ホームタイムラインの投稿情報が流れてきます。このチャンネルにパラメータはありません。
### 流れてくるイベント一覧
#### `note`
タイムラインに新しい投稿が流れてきたときに発生するイベントです。
## `localTimeline`
ローカルタイムラインの投稿情報が流れてきます。このチャンネルにパラメータはありません。
### 流れてくるイベント一覧
#### `note`
ローカルタイムラインに新しい投稿が流れてきたときに発生するイベントです。
## `hybridTimeline`
ソーシャルタイムラインの投稿情報が流れてきます。このチャンネルにパラメータはありません。
### 流れてくるイベント一覧
#### `note`
ソーシャルタイムラインに新しい投稿が流れてきたときに発生するイベントです。
## `globalTimeline`
グローバルタイムラインの投稿情報が流れてきます。このチャンネルにパラメータはありません。
### 流れてくるイベント一覧
#### `note`
グローバルタイムラインに新しい投稿が流れてきたときに発生するイベントです。

View file

@ -1,4 +0,0 @@
# AiScript
## وظائف
デフォルトで値渡しです。

View file

@ -1,58 +0,0 @@
# Misskey API
MisskeyAPIを使ってMisskeyクライアント、Misskey連携Webサービス、Bot等(以下「アプリケーション」と呼びます)を開発できます。 ストリーミングAPIもあるので、リアルタイム性のあるアプリケーションを作ることも可能です。
APIを使い始めるには、まずアクセストークンを取得する必要があります。 このドキュメントでは、アクセストークンを取得する手順を説明した後、基本的なAPIの使い方を説明します。
## アクセストークンの取得
基本的に、APIはリクエストにはアクセストークンが必要となります。 APIにリクエストするのが自分自身なのか、不特定の利用者に使ってもらうアプリケーションなのかによって取得手順は異なります。
* 前者の場合: [「自分自身のアクセストークンを手動発行する」](#自分自身のアクセストークンを手動発行する)に進む
* 後者の場合: [「アプリケーション利用者にアクセストークンの発行をリクエストする」](#アプリケーション利用者にアクセストークンの発行をリクエストする)に進む
### 自分自身のアクセストークンを手動発行する
「設定 > API」で、自分のアクセストークンを発行できます。
[「APIの使い方」へ進む](#APIの使い方)
### アプリケーション利用者にアクセストークンの発行をリクエストする
アプリケーション利用者のアクセストークンを取得するには、以下の手順で発行をリクエストします。
#### Step 1
UUIDを生成する。以後これをセッションIDと呼びます。
> このセッションIDは毎回生成し、使いまわさないようにしてください。
#### Step 2
`{_URL_}/miauth/{session}`をユーザーのブラウザで表示させる。`{session}`の部分は、セッションIDに置き換えてください。
> 例: `{_URL_}/miauth/c1f6d42b-468b-4fd2-8274-e58abdedef6f`
表示する際、URLにクエリパラメータとしていくつかのオプションを設定できます:
* `name` ... アプリケーション名
* > 例: `MissDeck`
* `icon` ... アプリケーションのアイコン画像URL
* > 例: `https://missdeck.example.com/icon.png`
* `callback` ... 認証が終わった後にリダイレクトするURL
* > 例: `https://missdeck.example.com/callback`
* リダイレクト時には、`session`というクエリパラメータでセッションIDが付きます
* `permission` ... アプリケーションが要求する権限
* > 例: `write:notes,write:following,read:drive`
* 要求する権限を`,`で区切って列挙します
* どのような権限があるかは[APIリファレンス](/api-doc)で確認できます
#### Step 3
ユーザーが発行を許可した後、`{_URL_}/api/miauth/{session}/check`にPOSTリクエストすると、レスポンスとしてアクセストークンを含むJSONが返ります。
レスポンスに含まれるプロパティ:
* `token` ... ユーザーのアクセストークン
* `user` ... ユーザーの情報
[「APIの使い方」へ進む](#APIの使い方)
## APIの使い方
**APIはすべてPOSTで、リクエスト/レスポンスともにJSON形式です。RESTではありません。** アクセストークンは、`i`というパラメータ名でリクエストに含めます。
* [APIリファレンス](/api-doc)
* [ストリーミングAPI](./stream)

View file

@ -1,74 +0,0 @@
# プラグインの作成
Misskey Webクライアントのプラグイン機能を使うと、クライアントを拡張し、様々な機能を追加できます。 ここではプラグインの作成にあたってのメタデータ定義や、AiScript APIリファレンスを掲載します。
## البيانات الوصفية
プラグインは、AiScriptのメタデータ埋め込み機能を使って、デフォルトとしてプラグインのメタデータを定義する必要があります。 メタデータは次のプロパティを含むオブジェクトです。
### name