From cd2542e0fd8578f6e41114ffebbda1f16f7d04ce Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 2 Apr 2018 04:15:27 +0900 Subject: Refactor --- src/common/drive/add-file.ts | 2 +- src/common/drive/upload-from-url.ts | 46 ++ src/common/drive/upload_from_url.ts | 46 -- src/common/get-notification-summary.ts | 27 - src/common/get-post-summary.ts | 45 - src/common/get-reaction-emoji.ts | 14 - src/common/othello/ai/back.ts | 376 --------- src/common/othello/ai/front.ts | 233 ------ src/common/othello/ai/index.ts | 1 - src/common/othello/core.ts | 340 -------- src/common/othello/maps.ts | 911 --------------------- src/common/remote/activitypub/act/create.ts | 9 - src/common/remote/activitypub/act/index.ts | 22 - src/common/remote/activitypub/create.ts | 87 -- src/common/remote/activitypub/renderer/context.ts | 5 - src/common/remote/activitypub/renderer/document.ts | 7 - src/common/remote/activitypub/renderer/follow.ts | 8 - src/common/remote/activitypub/renderer/hashtag.ts | 7 - src/common/remote/activitypub/renderer/image.ts | 6 - src/common/remote/activitypub/renderer/key.ts | 10 - src/common/remote/activitypub/renderer/note.ts | 44 - .../activitypub/renderer/ordered-collection.ts | 6 - src/common/remote/activitypub/renderer/person.ts | 20 - src/common/remote/activitypub/resolve-person.ts | 109 --- src/common/remote/activitypub/resolver.ts | 97 --- src/common/remote/activitypub/type.ts | 3 - src/common/remote/resolve-user.ts | 26 - src/common/remote/webfinger.ts | 25 - src/common/text/parse/elements/mention.ts | 2 +- src/common/user/get-acct.ts | 3 - src/common/user/get-summary.ts | 18 - src/common/user/parse-acct.ts | 4 - 32 files changed, 48 insertions(+), 2511 deletions(-) create mode 100644 src/common/drive/upload-from-url.ts delete mode 100644 src/common/drive/upload_from_url.ts delete mode 100644 src/common/get-notification-summary.ts delete mode 100644 src/common/get-post-summary.ts delete mode 100644 src/common/get-reaction-emoji.ts delete mode 100644 src/common/othello/ai/back.ts delete mode 100644 src/common/othello/ai/front.ts delete mode 100644 src/common/othello/ai/index.ts delete mode 100644 src/common/othello/core.ts delete mode 100644 src/common/othello/maps.ts delete mode 100644 src/common/remote/activitypub/act/create.ts delete mode 100644 src/common/remote/activitypub/act/index.ts delete mode 100644 src/common/remote/activitypub/create.ts delete mode 100644 src/common/remote/activitypub/renderer/context.ts delete mode 100644 src/common/remote/activitypub/renderer/document.ts delete mode 100644 src/common/remote/activitypub/renderer/follow.ts delete mode 100644 src/common/remote/activitypub/renderer/hashtag.ts delete mode 100644 src/common/remote/activitypub/renderer/image.ts delete mode 100644 src/common/remote/activitypub/renderer/key.ts delete mode 100644 src/common/remote/activitypub/renderer/note.ts delete mode 100644 src/common/remote/activitypub/renderer/ordered-collection.ts delete mode 100644 src/common/remote/activitypub/renderer/person.ts delete mode 100644 src/common/remote/activitypub/resolve-person.ts delete mode 100644 src/common/remote/activitypub/resolver.ts delete mode 100644 src/common/remote/activitypub/type.ts delete mode 100644 src/common/remote/resolve-user.ts delete mode 100644 src/common/remote/webfinger.ts delete mode 100644 src/common/user/get-acct.ts delete mode 100644 src/common/user/get-summary.ts delete mode 100644 src/common/user/parse-acct.ts (limited to 'src/common') diff --git a/src/common/drive/add-file.ts b/src/common/drive/add-file.ts index 52a7713dd9..af85a23d23 100644 --- a/src/common/drive/add-file.ts +++ b/src/common/drive/add-file.ts @@ -14,7 +14,7 @@ import DriveFile, { getGridFSBucket } from '../../models/drive-file'; import DriveFolder from '../../models/drive-folder'; import { pack } from '../../models/drive-file'; import event, { publishDriveStream } from '../event'; -import getAcct from '../user/get-acct'; +import getAcct from '../../misc/user/get-acct'; import config from '../../conf'; const gm = _gm.subClass({ diff --git a/src/common/drive/upload-from-url.ts b/src/common/drive/upload-from-url.ts new file mode 100644 index 0000000000..5dd9695936 --- /dev/null +++ b/src/common/drive/upload-from-url.ts @@ -0,0 +1,46 @@ +import * as URL from 'url'; +import { IDriveFile, validateFileName } from '../../models/drive-file'; +import create from './add-file'; +import * as debug from 'debug'; +import * as tmp from 'tmp'; +import * as fs from 'fs'; +import * as request from 'request'; + +const log = debug('misskey:common:drive:upload_from_url'); + +export default async (url, user, folderId = null): Promise => { + let name = URL.parse(url).pathname.split('/').pop(); + if (!validateFileName(name)) { + name = null; + } + + // Create temp file + const path = await new Promise((res: (string) => void, rej) => { + tmp.file((e, path) => { + if (e) return rej(e); + res(path); + }); + }); + + // write content at URL to temp file + await new Promise((res, rej) => { + const writable = fs.createWriteStream(path); + request(url) + .on('error', rej) + .on('end', () => { + writable.close(); + res(path); + }) + .pipe(writable) + .on('error', rej); + }); + + const driveFile = await create(user, path, name, null, folderId); + + // clean-up + fs.unlink(path, (e) => { + if (e) log(e.stack); + }); + + return driveFile; +}; diff --git a/src/common/drive/upload_from_url.ts b/src/common/drive/upload_from_url.ts deleted file mode 100644 index 5dd9695936..0000000000 --- a/src/common/drive/upload_from_url.ts +++ /dev/null @@ -1,46 +0,0 @@ -import * as URL from 'url'; -import { IDriveFile, validateFileName } from '../../models/drive-file'; -import create from './add-file'; -import * as debug from 'debug'; -import * as tmp from 'tmp'; -import * as fs from 'fs'; -import * as request from 'request'; - -const log = debug('misskey:common:drive:upload_from_url'); - -export default async (url, user, folderId = null): Promise => { - let name = URL.parse(url).pathname.split('/').pop(); - if (!validateFileName(name)) { - name = null; - } - - // Create temp file - const path = await new Promise((res: (string) => void, rej) => { - tmp.file((e, path) => { - if (e) return rej(e); - res(path); - }); - }); - - // write content at URL to temp file - await new Promise((res, rej) => { - const writable = fs.createWriteStream(path); - request(url) - .on('error', rej) - .on('end', () => { - writable.close(); - res(path); - }) - .pipe(writable) - .on('error', rej); - }); - - const driveFile = await create(user, path, name, null, folderId); - - // clean-up - fs.unlink(path, (e) => { - if (e) log(e.stack); - }); - - return driveFile; -}; diff --git a/src/common/get-notification-summary.ts b/src/common/get-notification-summary.ts deleted file mode 100644 index 03db722c84..0000000000 --- a/src/common/get-notification-summary.ts +++ /dev/null @@ -1,27 +0,0 @@ -import getPostSummary from './get-post-summary'; -import getReactionEmoji from './get-reaction-emoji'; - -/** - * 通知を表す文字列を取得します。 - * @param notification 通知 - */ -export default function(notification: any): string { - switch (notification.type) { - case 'follow': - return `${notification.user.name}にフォローされました`; - case 'mention': - return `言及されました:\n${notification.user.name}「${getPostSummary(notification.post)}」`; - case 'reply': - return `返信されました:\n${notification.user.name}「${getPostSummary(notification.post)}」`; - case 'repost': - return `Repostされました:\n${notification.user.name}「${getPostSummary(notification.post)}」`; - case 'quote': - return `引用されました:\n${notification.user.name}「${getPostSummary(notification.post)}」`; - case 'reaction': - return `リアクションされました:\n${notification.user.name} <${getReactionEmoji(notification.reaction)}>「${getPostSummary(notification.post)}」`; - case 'poll_vote': - return `投票されました:\n${notification.user.name}「${getPostSummary(notification.post)}」`; - default: - return `<不明な通知タイプ: ${notification.type}>`; - } -} diff --git a/src/common/get-post-summary.ts b/src/common/get-post-summary.ts deleted file mode 100644 index 8d0033064f..0000000000 --- a/src/common/get-post-summary.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * 投稿を表す文字列を取得します。 - * @param {*} post 投稿 - */ -const summarize = (post: any): string => { - let summary = ''; - - // チャンネル - summary += post.channel ? `${post.channel.title}:` : ''; - - // 本文 - summary += post.text ? post.text : ''; - - // メディアが添付されているとき - if (post.media) { - summary += ` (${post.media.length}つのメディア)`; - } - - // 投票が添付されているとき - if (post.poll) { - summary += ' (投票)'; - } - - // 返信のとき - if (post.replyId) { - if (post.reply) { - summary += ` RE: ${summarize(post.reply)}`; - } else { - summary += ' RE: ...'; - } - } - - // Repostのとき - if (post.repostId) { - if (post.repost) { - summary += ` RP: ${summarize(post.repost)}`; - } else { - summary += ' RP: ...'; - } - } - - return summary.trim(); -}; - -export default summarize; diff --git a/src/common/get-reaction-emoji.ts b/src/common/get-reaction-emoji.ts deleted file mode 100644 index c661205379..0000000000 --- a/src/common/get-reaction-emoji.ts +++ /dev/null @@ -1,14 +0,0 @@ -export default function(reaction: string): string { - switch (reaction) { - case 'like': return '👍'; - case 'love': return '❤️'; - case 'laugh': return '😆'; - case 'hmm': return '🤔'; - case 'surprise': return '😮'; - case 'congrats': return '🎉'; - case 'angry': return '💢'; - case 'confused': return '😥'; - case 'pudding': return '🍮'; - default: return ''; - } -} diff --git a/src/common/othello/ai/back.ts b/src/common/othello/ai/back.ts deleted file mode 100644 index 0950adaa9f..0000000000 --- a/src/common/othello/ai/back.ts +++ /dev/null @@ -1,376 +0,0 @@ -/** - * -AI- - * Botのバックエンド(思考を担当) - * - * 対話と思考を同じプロセスで行うと、思考時間が長引いたときにストリームから - * 切断されてしまうので、別々のプロセスで行うようにします - */ - -import * as request from 'request-promise-native'; -import Othello, { Color } from '../core'; -import conf from '../../../conf'; - -let game; -let form; - -/** - * BotアカウントのユーザーID - */ -const id = conf.othello_ai.id; - -/** - * BotアカウントのAPIキー - */ -const i = conf.othello_ai.i; - -let post; - -process.on('message', async msg => { - // 親プロセスからデータをもらう - if (msg.type == '_init_') { - game = msg.game; - form = msg.form; - } - - // フォームが更新されたとき - if (msg.type == 'update-form') { - form.find(i => i.id == msg.body.id).value = msg.body.value; - } - - // ゲームが始まったとき - if (msg.type == 'started') { - onGameStarted(msg.body); - - //#region TLに投稿する - const game = msg.body; - const url = `${conf.url}/othello/${game.id}`; - const user = game.user1Id == id ? game.user2 : game.user1; - const isSettai = form[0].value === 0; - const text = isSettai - ? `?[${user.name}](${conf.url}/@${user.username})さんの接待を始めました!` - : `対局を?[${user.name}](${conf.url}/@${user.username})さんと始めました! (強さ${form[0].value})`; - - const res = await request.post(`${conf.api_url}/posts/create`, { - json: { i, - text: `${text}\n→[観戦する](${url})` - } - }); - - post = res.createdPost; - //#endregion - } - - // ゲームが終了したとき - if (msg.type == 'ended') { - // ストリームから切断 - process.send({ - type: 'close' - }); - - //#region TLに投稿する - const user = game.user1Id == id ? game.user2 : game.user1; - const isSettai = form[0].value === 0; - const text = isSettai - ? msg.body.winnerId === null - ? `?[${user.name}](${conf.url}/@${user.username})さんに接待で引き分けました...` - : msg.body.winnerId == id - ? `?[${user.name}](${conf.url}/@${user.username})さんに接待で勝ってしまいました...` - : `?[${user.name}](${conf.url}/@${user.username})さんに接待で負けてあげました♪` - : msg.body.winnerId === null - ? `?[${user.name}](${conf.url}/@${user.username})さんと引き分けました~` - : msg.body.winnerId == id - ? `?[${user.name}](${conf.url}/@${user.username})さんに勝ちました♪` - : `?[${user.name}](${conf.url}/@${user.username})さんに負けました...`; - - await request.post(`${conf.api_url}/posts/create`, { - json: { i, - repostId: post.id, - text: text - } - }); - //#endregion - - process.exit(); - } - - // 打たれたとき - if (msg.type == 'set') { - onSet(msg.body); - } -}); - -let o: Othello; -let botColor: Color; - -// 各マスの強さ -let cellWeights; - -/** - * ゲーム開始時 - * @param g ゲーム情報 - */ -function onGameStarted(g) { - game = g; - - // オセロエンジン初期化 - o = new Othello(game.settings.map, { - isLlotheo: game.settings.isLlotheo, - canPutEverywhere: game.settings.canPutEverywhere, - loopedBoard: game.settings.loopedBoard - }); - - // 各マスの価値を計算しておく - cellWeights = o.map.map((pix, i) => { - if (pix == 'null') return 0; - const [x, y] = o.transformPosToXy(i); - let count = 0; - const get = (x, y) => { - if (x < 0 || y < 0 || x >= o.mapWidth || y >= o.mapHeight) return 'null'; - return o.mapDataGet(o.transformXyToPos(x, y)); - }; - - if (get(x , y - 1) == 'null') count++; - if (get(x + 1, y - 1) == 'null') count++; - if (get(x + 1, y ) == 'null') count++; - if (get(x + 1, y + 1) == 'null') count++; - if (get(x , y + 1) == 'null') count++; - if (get(x - 1, y + 1) == 'null') count++; - if (get(x - 1, y ) == 'null') count++; - if (get(x - 1, y - 1) == 'null') count++; - //return Math.pow(count, 3); - return count >= 4 ? 1 : 0; - }); - - botColor = game.user1Id == id && game.black == 1 || game.user2Id == id && game.black == 2; - - if (botColor) { - think(); - } -} - -function onSet(x) { - o.put(x.color, x.pos); - - if (x.next === botColor) { - think(); - } -} - -const db = {}; - -function think() { - console.log('Thinking...'); - console.time('think'); - - const isSettai = form[0].value === 0; - - // 接待モードのときは、全力(5手先読みくらい)で負けるようにする - const maxDepth = isSettai ? 5 : form[0].value; - - /** - * Botにとってある局面がどれだけ有利か取得する - */ - function staticEval() { - let score = o.canPutSomewhere(botColor).length; - - cellWeights.forEach((weight, i) => { - // 係数 - const coefficient = 30; - weight = weight * coefficient; - - const stone = o.board[i]; - if (stone === botColor) { - // TODO: 価値のあるマスに設置されている自分の石に縦か横に接するマスは価値があると判断する - score += weight; - } else if (stone !== null) { - score -= weight; - } - }); - - // ロセオならスコアを反転 - if (game.settings.isLlotheo) score = -score; - - // 接待ならスコアを反転 - if (isSettai) score = -score; - - return score; - } - - /** - * αβ法での探索 - */ - const dive = (pos: number, alpha = -Infinity, beta = Infinity, depth = 0): number => { - // 試し打ち - o.put(o.turn, pos); - - const key = o.board.toString(); - let cache = db[key]; - if (cache) { - if (alpha >= cache.upper) { - o.undo(); - return cache.upper; - } - if (beta <= cache.lower) { - o.undo(); - return cache.lower; - } - alpha = Math.max(alpha, cache.lower); - beta = Math.min(beta, cache.upper); - } else { - cache = { - upper: Infinity, - lower: -Infinity - }; - } - - const isBotTurn = o.turn === botColor; - - // 勝った - if (o.turn === null) { - const winner = o.winner; - - // 勝つことによる基本スコア - const base = 10000; - - let score; - - if (game.settings.isLlotheo) { - // 勝ちは勝ちでも、より自分の石を少なくした方が美しい勝ちだと判定する - score = o.winner ? base - (o.blackCount * 100) : base - (o.whiteCount * 100); - } else { - // 勝ちは勝ちでも、より相手の石を少なくした方が美しい勝ちだと判定する - score = o.winner ? base + (o.blackCount * 100) : base + (o.whiteCount * 100); - } - - // 巻き戻し - o.undo(); - - // 接待なら自分が負けた方が高スコア - return isSettai - ? winner !== botColor ? score : -score - : winner === botColor ? score : -score; - } - - if (depth === maxDepth) { - // 静的に評価 - const score = staticEval(); - - // 巻き戻し - o.undo(); - - return score; - } else { - const cans = o.canPutSomewhere(o.turn); - - let value = isBotTurn ? -Infinity : Infinity; - let a = alpha; - let b = beta; - - // 次のターンのプレイヤーにとって最も良い手を取得 - for (const p of cans) { - if (isBotTurn) { - const score = dive(p, a, beta, depth + 1); - value = Math.max(value, score); - a = Math.max(a, value); - if (value >= beta) break; - } else { - const score = dive(p, alpha, b, depth + 1); - value = Math.min(value, score); - b = Math.min(b, value); - if (value <= alpha) break; - } - } - - // 巻き戻し - o.undo(); - - if (value <= alpha) { - cache.upper = value; - } else if (value >= beta) { - cache.lower = value; - } else { - cache.upper = value; - cache.lower = value; - } - - db[key] = cache; - - return value; - } - }; - - /** - * αβ法での探索(キャッシュ無し)(デバッグ用) - */ - const dive2 = (pos: number, alpha = -Infinity, beta = Infinity, depth = 0): number => { - // 試し打ち - o.put(o.turn, pos); - - const isBotTurn = o.turn === botColor; - - // 勝った - if (o.turn === null) { - const winner = o.winner; - - // 勝つことによる基本スコア - const base = 10000; - - let score; - - if (game.settings.isLlotheo) { - // 勝ちは勝ちでも、より自分の石を少なくした方が美しい勝ちだと判定する - score = o.winner ? base - (o.blackCount * 100) : base - (o.whiteCount * 100); - } else { - // 勝ちは勝ちでも、より相手の石を少なくした方が美しい勝ちだと判定する - score = o.winner ? base + (o.blackCount * 100) : base + (o.whiteCount * 100); - } - - // 巻き戻し - o.undo(); - - // 接待なら自分が負けた方が高スコア - return isSettai - ? winner !== botColor ? score : -score - : winner === botColor ? score : -score; - } - - if (depth === maxDepth) { - // 静的に評価 - const score = staticEval(); - - // 巻き戻し - o.undo(); - - return score; - } else { - const cans = o.canPutSomewhere(o.turn); - - // 次のターンのプレイヤーにとって最も良い手を取得 - for (const p of cans) { - if (isBotTurn) { - alpha = Math.max(alpha, dive2(p, alpha, beta, depth + 1)); - } else { - beta = Math.min(beta, dive2(p, alpha, beta, depth + 1)); - } - if (alpha >= beta) break; - } - - // 巻き戻し - o.undo(); - - return isBotTurn ? alpha : beta; - } - }; - - const cans = o.canPutSomewhere(botColor); - const scores = cans.map(p => dive(p)); - const pos = cans[scores.indexOf(Math.max(...scores))]; - - console.log('Thinked:', pos); - console.timeEnd('think'); - - process.send({ - type: 'put', - pos - }); -} diff --git a/src/common/othello/ai/front.ts b/src/common/othello/ai/front.ts deleted file mode 100644 index e5496132f6..0000000000 --- a/src/common/othello/ai/front.ts +++ /dev/null @@ -1,233 +0,0 @@ -/** - * -AI- - * Botのフロントエンド(ストリームとの対話を担当) - * - * 対話と思考を同じプロセスで行うと、思考時間が長引いたときにストリームから - * 切断されてしまうので、別々のプロセスで行うようにします - */ - -import * as childProcess from 'child_process'; -const WebSocket = require('ws'); -import * as ReconnectingWebSocket from 'reconnecting-websocket'; -import * as request from 'request-promise-native'; -import conf from '../../../conf'; - -// 設定 //////////////////////////////////////////////////////// - -/** - * BotアカウントのAPIキー - */ -const i = conf.othello_ai.i; - -/** - * BotアカウントのユーザーID - */ -const id = conf.othello_ai.id; - -//////////////////////////////////////////////////////////////// - -/** - * ホームストリーム - */ -const homeStream = new ReconnectingWebSocket(`${conf.ws_url}/?i=${i}`, undefined, { - constructor: WebSocket -}); - -homeStream.on('open', () => { - console.log('home stream opened'); -}); - -homeStream.on('close', () => { - console.log('home stream closed'); -}); - -homeStream.on('message', message => { - const msg = JSON.parse(message.toString()); - - // タイムライン上でなんか言われたまたは返信されたとき - if (msg.type == 'mention' || msg.type == 'reply') { - const post = msg.body; - - if (post.userId == id) return; - - // リアクションする - request.post(`${conf.api_url}/posts/reactions/create`, { - json: { i, - postId: post.id, - reaction: 'love' - } - }); - - if (post.text) { - if (post.text.indexOf('オセロ') > -1) { - request.post(`${conf.api_url}/posts/create`, { - json: { i, - replyId: post.id, - text: '良いですよ~' - } - }); - - invite(post.userId); - } - } - } - - // メッセージでなんか言われたとき - if (msg.type == 'messaging_message') { - const message = msg.body; - if (message.text) { - if (message.text.indexOf('オセロ') > -1) { - request.post(`${conf.api_url}/messaging/messages/create`, { - json: { i, - userId: message.userId, - text: '良いですよ~' - } - }); - - invite(message.userId); - } - } - } -}); - -// ユーザーを対局に誘う -function invite(userId) { - request.post(`${conf.api_url}/othello/match`, { - json: { i, - userId: userId - } - }); -} - -/** - * オセロストリーム - */ -const othelloStream = new ReconnectingWebSocket(`${conf.ws_url}/othello?i=${i}`, undefined, { - constructor: WebSocket -}); - -othelloStream.on('open', () => { - console.log('othello stream opened'); -}); - -othelloStream.on('close', () => { - console.log('othello stream closed'); -}); - -othelloStream.on('message', message => { - const msg = JSON.parse(message.toString()); - - // 招待されたとき - if (msg.type == 'invited') { - onInviteMe(msg.body.parent); - } - - // マッチしたとき - if (msg.type == 'matched') { - gameStart(msg.body); - } -}); - -/** - * ゲーム開始 - * @param game ゲーム情報 - */ -function gameStart(game) { - // ゲームストリームに接続 - const gw = new ReconnectingWebSocket(`${conf.ws_url}/othello-game?i=${i}&game=${game.id}`, undefined, { - constructor: WebSocket - }); - - gw.on('open', () => { - console.log('othello game stream opened'); - - // フォーム - const form = [{ - id: 'strength', - type: 'radio', - label: '強さ', - value: 2, - items: [{ - label: '接待', - value: 0 - }, { - label: '弱', - value: 1 - }, { - label: '中', - value: 2 - }, { - label: '強', - value: 3 - }, { - label: '最強', - value: 5 - }] - }]; - - //#region バックエンドプロセス開始 - const ai = childProcess.fork(__dirname + '/back.js'); - - // バックエンドプロセスに情報を渡す - ai.send({ - type: '_init_', - game, - form - }); - - ai.on('message', msg => { - if (msg.type == 'put') { - gw.send(JSON.stringify({ - type: 'set', - pos: msg.pos - })); - } else if (msg.type == 'close') { - gw.close(); - } - }); - - // ゲームストリームから情報が流れてきたらそのままバックエンドプロセスに伝える - gw.on('message', message => { - const msg = JSON.parse(message.toString()); - ai.send(msg); - }); - //#endregion - - // フォーム初期化 - setTimeout(() => { - gw.send(JSON.stringify({ - type: 'init-form', - body: form - })); - }, 1000); - - // どんな設定内容の対局でも受け入れる - setTimeout(() => { - gw.send(JSON.stringify({ - type: 'accept' - })); - }, 2000); - }); - - gw.on('close', () => { - console.log('othello game stream closed'); - }); -} - -/** - * オセロの対局に招待されたとき - * @param inviter 誘ってきたユーザー - */ -async function onInviteMe(inviter) { - console.log(`Someone invited me: @${inviter.username}`); - - // 承認 - const game = await request.post(`${conf.api_url}/othello/match`, { - json: { - i, - userId: inviter.id - } - }); - - gameStart(game); -} diff --git a/src/common/othello/ai/index.ts b/src/common/othello/ai/index.ts deleted file mode 100644 index 5cd1db82da..0000000000 --- a/src/common/othello/ai/index.ts +++ /dev/null @@ -1 +0,0 @@ -require('./front'); diff --git a/src/common/othello/core.ts b/src/common/othello/core.ts deleted file mode 100644 index 217066d375..0000000000 --- a/src/common/othello/core.ts +++ /dev/null @@ -1,340 +0,0 @@ -/** - * true ... 黒 - * false ... 白 - */ -export type Color = boolean; -const BLACK = true; -const WHITE = false; - -export type MapPixel = 'null' | 'empty'; - -export type Options = { - isLlotheo: boolean; - canPutEverywhere: boolean; - loopedBoard: boolean; -}; - -export type Undo = { - /** - * 色 - */ - color: Color, - - /** - * どこに打ったか - */ - pos: number; - - /** - * 反転した石の位置の配列 - */ - effects: number[]; - - /** - * ターン - */ - turn: Color; -}; - -/** - * オセロエンジン - */ -export default class Othello { - public map: MapPixel[]; - public mapWidth: number; - public mapHeight: number; - public board: Color[]; - public turn: Color = BLACK; - public opts: Options; - - public prevPos = -1; - public prevColor: Color = null; - - private logs: Undo[] = []; - - /** - * ゲームを初期化します - */ - constructor(map: string[], opts: Options) { - //#region binds - this.put = this.put.bind(this); - //#endregion - - //#region Options - this.opts = opts; - if (this.opts.isLlotheo == null) this.opts.isLlotheo = false; - if (this.opts.canPutEverywhere == null) this.opts.canPutEverywhere = false; - if (this.opts.loopedBoard == null) this.opts.loopedBoard = false; - //#endregion - - //#region Parse map data - this.mapWidth = map[0].length; - this.mapHeight = map.length; - const mapData = map.join(''); - - this.board = mapData.split('').map(d => { - if (d == '-') return null; - if (d == 'b') return BLACK; - if (d == 'w') return WHITE; - return undefined; - }); - - this.map = mapData.split('').map(d => { - if (d == '-' || d == 'b' || d == 'w') return 'empty'; - return 'null'; - }); - //#endregion - - // ゲームが始まった時点で片方の色の石しかないか、始まった時点で勝敗が決定するようなマップの場合がある - if (this.canPutSomewhere(BLACK).length == 0) { - if (this.canPutSomewhere(WHITE).length == 0) { - this.turn = null; - } else { - this.turn = WHITE; - } - } - } - - /** - * 黒石の数 - */ - public get blackCount() { - return this.board.filter(x => x === BLACK).length; - } - - /** - * 白石の数 - */ - public get whiteCount() { - return this.board.filter(x => x === WHITE).length; - } - - /** - * 黒石の比率 - */ - public get blackP() { - if (this.blackCount == 0 && this.whiteCount == 0) return 0; - return this.blackCount / (this.blackCount + this.whiteCount); - } - - /** - * 白石の比率 - */ - public get whiteP() { - if (this.blackCount == 0 && this.whiteCount == 0) return 0; - return this.whiteCount / (this.blackCount + this.whiteCount); - } - - public transformPosToXy(pos: number): number[] { - const x = pos % this.mapWidth; - const y = Math.floor(pos / this.mapWidth); - return [x, y]; - } - - public transformXyToPos(x: number, y: number): number { - return x + (y * this.mapWidth); - } - - /** - * 指定のマスに石を打ちます - * @param color 石の色 - * @param pos 位置 - */ - public put(color: Color, pos: number) { - this.prevPos = pos; - this.prevColor = color; - - this.board[pos] = color; - - // 反転させられる石を取得 - const effects = this.effects(color, pos); - - // 反転させる - for (const pos of effects) { - this.board[pos] = color; - } - - const turn = this.turn; - - this.logs.push({ - color, - pos, - effects, - turn - }); - - this.calcTurn(); - } - - private calcTurn() { - // ターン計算 - if (this.canPutSomewhere(!this.prevColor).length > 0) { - this.turn = !this.prevColor; - } else if (this.canPutSomewhere(this.prevColor).length > 0) { - this.turn = this.prevColor; - } else { - this.turn = null; - } - } - - public undo() { - const undo = this.logs.pop(); - this.prevColor = undo.color; - this.prevPos = undo.pos; - this.board[undo.pos] = null; - for (const pos of undo.effects) { - const color = this.board[pos]; - this.board[pos] = !color; - } - this.turn = undo.turn; - } - - /** - * 指定した位置のマップデータのマスを取得します - * @param pos 位置 - */ - public mapDataGet(pos: number): MapPixel { - const [x, y] = this.transformPosToXy(pos); - if (x < 0 || y < 0 || x >= this.mapWidth || y >= this.mapHeight) return 'null'; - return this.map[pos]; - } - - /** - * 打つことができる場所を取得します - */ - public canPutSomewhere(color: Color): number[] { - const result = []; - - this.board.forEach((x, i) => { - if (this.canPut(color, i)) result.push(i); - }); - - return result; - } - - /** - * 指定のマスに石を打つことができるかどうかを取得します - * @param color 自分の色 - * @param pos 位置 - */ - public canPut(color: Color, pos: number): boolean { - // 既に石が置いてある場所には打てない - if (this.board[pos] !== null) return false; - - if (this.opts.canPutEverywhere) { - // 挟んでなくても置けるモード - return this.mapDataGet(pos) == 'empty'; - } else { - // 相手の石を1つでも反転させられるか - return this.effects(color, pos).length !== 0; - } - } - - /** - * 指定のマスに石を置いた時の、反転させられる石を取得します - * @param color 自分の色 - * @param pos 位置 - */ - public effects(color: Color, pos: number): number[] { - const enemyColor = !color; - - // ひっくり返せる石(の位置)リスト - let stones = []; - - const initPos = pos; - - // 走査 - const iterate = (fn: (i: number) => number[]) => { - let i = 1; - const found = []; - - while (true) { - let [x, y] = fn(i); - - // 座標が指し示す位置がボード外に出たとき - if (this.opts.loopedBoard) { - if (x < 0 ) x = this.mapWidth - ((-x) % this.mapWidth); - if (y < 0 ) y = this.mapHeight - ((-y) % this.mapHeight); - if (x >= this.mapWidth ) x = x % this.mapWidth; - if (y >= this.mapHeight) y = y % this.mapHeight; - - // for debug - //if (x < 0 || y < 0 || x >= this.mapWidth || y >= this.mapHeight) { - // console.log(x, y); - //} - - // 一周して自分に帰ってきたら - if (this.transformXyToPos(x, y) == initPos) { - // ↓のコメントアウトを外すと、「現時点で自分の石が隣接していないが、 - // そこに置いたとするとループして最終的に挟んだことになる」というケースを有効化します。(Test4のマップで違いが分かります) - // このケースを有効にした方が良いのか無効にした方が良いのか判断がつかなかったためとりあえず無効としておきます - // (あと無効な方がゲームとしておもしろそうだった) - stones = stones.concat(found); - break; - } - } else { - if (x == -1 || y == -1 || x == this.mapWidth || y == this.mapHeight) break; - } - - const pos = this.transformXyToPos(x, y); - - //#region 「配置不能」マスに当たった場合走査終了 - const pixel = this.mapDataGet(pos); - if (pixel == 'null') break; - //#endregion - - // 石取得 - const stone = this.board[pos]; - - // 石が置かれていないマスなら走査終了 - if (stone === null) break; - - // 相手の石なら「ひっくり返せるかもリスト」に入れておく - if (stone === enemyColor) found.push(pos); - - // 自分の石なら「ひっくり返せるかもリスト」を「ひっくり返せるリスト」に入れ、走査終了 - if (stone === color) { - stones = stones.concat(found); - break; - } - - i++; - } - }; - - const [x, y] = this.transformPosToXy(pos); - - iterate(i => [x , y - i]); // 上 - iterate(i => [x + i, y - i]); // 右上 - iterate(i => [x + i, y ]); // 右 - iterate(i => [x + i, y + i]); // 右下 - iterate(i => [x , y + i]); // 下 - iterate(i => [x - i, y + i]); // 左下 - iterate(i => [x - i, y ]); // 左 - iterate(i => [x - i, y - i]); // 左上 - - return stones; - } - - /** - * ゲームが終了したか否か - */ - public get isEnded(): boolean { - return this.turn === null; - } - - /** - * ゲームの勝者 (null = 引き分け) - */ - public get winner(): Color { - if (!this.isEnded) return undefined; - - if (this.blackCount == this.whiteCount) return null; - - if (this.opts.isLlotheo) { - return this.blackCount > this.whiteCount ? WHITE : BLACK; - } else { - return this.blackCount > this.whiteCount ? BLACK : WHITE; - } - } -} diff --git a/src/common/othello/maps.ts b/src/common/othello/maps.ts deleted file mode 100644 index 68e5a446f1..0000000000 --- a/src/common/othello/maps.ts +++ /dev/null @@ -1,911 +0,0 @@ -/** - * 組み込みマップ定義 - * - * データ値: - * (スペース) ... マス無し - * - ... マス - * b ... 初期配置される黒石 - * w ... 初期配置される白石 - */ - -export type Map = { - name?: string; - category?: string; - author?: string; - data: string[]; -}; - -export const fourfour: Map = { - name: '4x4', - category: '4x4', - data: [ - '----', - '-wb-', - '-bw-', - '----' - ] -}; - -export const sixsix: Map = { - name: '6x6', - category: '6x6', - data: [ - '------', - '------', - '--wb--', - '--bw--', - '------', - '------' - ] -}; - -export const roundedSixsix: Map = { - name: '6x6 rounded', - category: '6x6', - author: 'syuilo', - data: [ - ' ---- ', - '------', - '--wb--', - '--bw--', - '------', - ' ---- ' - ] -}; - -export const roundedSixsix2: Map = { - name: '6x6 rounded 2', - category: '6x6', - author: 'syuilo', - data: [ - ' -- ', - ' ---- ', - '--wb--', - '--bw--', - ' ---- ', - ' -- ' - ] -}; - -export const eighteight: Map = { - name: '8x8', - category: '8x8', - data: [ - '--------', - '--------', - '--------', - '---wb---', - '---bw---', - '--------', - '--------', - '--------' - ] -}; - -export const eighteightH1: Map = { - name: '8x8 handicap 1', - category: '8x8', - data: [ - 'b-------', - '--------', - '--------', - '---wb---', - '---bw---', - '--------', - '--------', - '--------' - ] -}; - -export const eighteightH2: Map = { - name: '8x8 handicap 2', - category: '8x8', - data: [ - 'b-------', - '--------', - '--------', - '---wb---', - '---bw---', - '--------', - '--------', - '-------b' - ] -}; - -export const eighteightH3: Map = { - name: '8x8 handicap 3', - category: '8x8', - data: [ - 'b------b', - '--------', - '--------', - '---wb---', - '---bw---', - '--------', - '--------', - '-------b' - ] -}; - -export const eighteightH4: Map = { - name: '8x8 handicap 4', - category: '8x8', - data: [ - 'b------b', - '--------', - '--------', - '---wb---', - '---bw---', - '--------', - '--------', - 'b------b' - ] -}; - -export const eighteightH12: Map = { - name: '8x8 handicap 12', - category: '8x8', - data: [ - 'bb----bb', - 'b------b', - '--------', - '---wb---', - '---bw---', - '--------', - 'b------b', - 'bb----bb' - ] -}; - -export const eighteightH16: Map = { - name: '8x8 handicap 16', - category: '8x8', - data: [ - 'bbb---bb', - 'b------b', - '-------b', - '---wb---', - '---bw---', - 'b-------', - 'b------b', - 'bb---bbb' - ] -}; - -export const eighteightH20: Map = { - name: '8x8 handicap 20', - category: '8x8', - data: [ - 'bbb--bbb', - 'b------b', - 'b------b', - '---wb---', - '---bw---', - 'b------b', - 'b------b', - 'bbb---bb' - ] -}; - -export const eighteightH28: Map = { - name: '8x8 handicap 28', - category: '8x8', - data: [ - 'bbbbbbbb', - 'b------b', - 'b------b', - 'b--wb--b', - 'b--bw--b', - 'b------b', - 'b------b', - 'bbbbbbbb' - ] -}; - -export const roundedEighteight: Map = { - name: '8x8 rounded', - category: '8x8', - author: 'syuilo', - data: [ - ' ------ ', - '--------', - '--------', - '---wb---', - '---bw---', - '--------', - '--------', - ' ------ ' - ] -}; - -export const roundedEighteight2: Map = { - name: '8x8 rounded 2', - category: '8x8', - author: 'syuilo', - data: [ - ' ---- ', - ' ------ ', - '--------', - '---wb---', - '---bw---', - '--------', - ' ------ ', - ' ---- ' - ] -}; - -export const roundedEighteight3: Map = { - name: '8x8 rounded 3', - category: '8x8', - author: 'syuilo', - data: [ - ' -- ', - ' ---- ', - ' ------ ', - '---wb---', - '---bw---', - ' ------ ', - ' ---- ', - ' -- ' - ] -}; - -export const eighteightWithNotch: Map = { - name: '8x8 with notch', - category: '8x8', - author: 'syuilo', - data: [ - '--- ---', - '--------', - '--------', - ' --wb-- ', - ' --bw-- ', - '--------', - '--------', - '--- ---' - ] -}; - -export const eighteightWithSomeHoles: Map = { - name: '8x8 with some holes', - category: '8x8', - author: 'syuilo', - data: [ - '--- ----', - '----- --', - '-- -----', - '---wb---', - '---bw- -', - ' -------', - '--- ----', - '--------' - ] -}; - -export const circle: Map = { - name: 'Circle', - category: '8x8', - author: 'syuilo', - data: [ - ' -- ', - ' ------ ', - ' ------ ', - '---wb---', - '---bw---', - ' ------ ', - ' ------ ', - ' -- ' - ] -}; - -export const smile: Map = { - name: 'Smile', - category: '8x8', - author: 'syuilo', - data: [ - ' ------ ', - '--------', - '-- -- --', - '---wb---', - '-- bw --', - '--- ---', - '--------', - ' ------ ' - ] -}; - -export const window: Map = { - name: 'Window', - category: '8x8', - author: 'syuilo', - data: [ - '--------', - '- -- -', - '- -- -', - '---wb---', - '---bw---', - '- -- -', - '- -- -', - '--------' - ] -}; - -export const reserved: Map = { - name: 'Reserved', - category: '8x8', - author: 'Aya', - data: [ - 'w------b', - '--------', - '--------', - '---wb---', - '---bw---', - '--------', - '--------', - 'b------w' - ] -}; - -export const x: Map = { - name: 'X', - category: '8x8', - author: 'Aya', - data: [ - 'w------b', - '-w----b-', - '--w--b--', - '---wb---', - '---bw---', - '--b--w--', - '-b----w-', - 'b------w' - ] -}; - -export const parallel: Map = { - name: 'Parallel', - category: '8x8', - author: 'Aya', - data: [ - '--------', - '--------', - '--------', - '---bb---', - '---ww---', - '--------', - '--------', - '--------' - ] -}; - -export const lackOfBlack: Map = { - name: 'Lack of Black', - category: '8x8', - data: [ - '--------', - '--------', - '--------', - '---w----', - '---bw---', - '--------', - '--------', - '--------' - ] -}; - -export const squareParty: Map = { - name: 'Square Party', - category: '8x8', - author: 'syuilo', - data: [ - '--------', - '-wwwbbb-', - '-w-wb-b-', - '-wwwbbb-', - '-bbbwww-', - '-b-bw-w-', - '-bbbwww-', - '--------' - ] -}; - -export const minesweeper: Map = { - name: 'Minesweeper', - category: '8x8', - author: 'syuilo', - data: [ - 'b-b--w-w', - '-w-wb-b-', - 'w-b--w-b', - '-b-wb-w-', - '-w-bw-b-', - 'b-w--b-w', - '-b-bw-w-', - 'w-w--b-b' - ] -}; - -export const tenthtenth: Map = { - name: '10x10', - category: '10x10', - data: [ - '----------', - '----------', - '----------', - '----------', - '----wb----', - '----bw----', - '----------', - '----------', - '----------', - '----------' - ] -}; - -export const hole: Map = { - name: 'The Hole', - category: '10x10', - author: 'syuilo', - data: [ - '----------', - '----------', - '--wb--wb--', - '--bw--bw--', - '---- ----', - '---- ----', - '--wb--wb--', - '--bw--bw--', - '----------', - '----------' - ] -}; - -export const grid: Map = { - name: 'Grid', - category: '10x10', - author: 'syuilo', - data: [ - '----------', - '- - -- - -', - '----------', - '- - -- - -', - '----wb----', - '----bw----', - '- - -- - -', - '----------', - '- - -- - -', - '----------' - ] -}; - -export const cross: Map = { - name: 'Cross', - category: '10x10', - author: 'Aya', - data: [ - ' ---- ', - ' ---- ', - ' ---- ', - '----------', - '----wb----', - '----bw----', - '----------', - ' ---- ', - ' ---- ', - ' ---- ' - ] -}; - -export const charX: Map = { - name: 'Char X', - category: '10x10', - author: 'syuilo', - data: [ - '--- ---', - '---- ----', - '----------', - ' -------- ', - ' --wb-- ', - ' --bw-- ', - ' -------- ', - '----------', - '---- ----', - '--- ---' - ] -}; - -export const charY: Map = { - name: 'Char Y', - category: '10x10', - author: 'syuilo', - data: [ - '--- ---', - '---- ----', - '----------', - ' -------- ', - ' --wb-- ', - ' --bw-- ', - ' ------ ', - ' ------ ', - ' ------ ', - ' ------ ' - ] -}; - -export const walls: Map = { - name: 'Walls', - category: '10x10', - author: 'Aya', - data: [ - ' bbbbbbbb ', - 'w--------w', - 'w--------w', - 'w--------w', - 'w---wb---w', - 'w---bw---w', - 'w--------w', - 'w--------w', - 'w--------w', - ' bbbbbbbb ' - ] -}; - -export const cpu: Map = { - name: 'CPU', - category: '10x10', - author: 'syuilo', - data: [ - ' b b b b ', - 'w--------w', - ' -------- ', - 'w--------w', - ' ---wb--- ', - ' ---bw--- ', - 'w--------w', - ' -------- ', - 'w--------w', - ' b b b b ' - ] -}; - -export const checker: Map = { - name: 'Checker', - category: '10x10', - author: 'Aya', - data: [ - '----------', - '----------', - '----------', - '---wbwb---', - '---bwbw---', - '---wbwb---', - '---bwbw---', - '----------', - '----------', - '----------' - ] -}; - -export const japaneseCurry: Map = { - name: 'Japanese curry', - category: '10x10', - author: 'syuilo', - data: [ - 'w-b-b-b-b-', - '-w-b-b-b-b', - 'w-w-b-b-b-', - '-w-w-b-b-b', - 'w-w-wwb-b-', - '-w-wbb-b-b', - 'w-w-w-b-b-', - '-w-w-w-b-b', - 'w-w-w-w-b-', - '-w-w-w-w-b' - ] -}; - -export const mosaic: Map = { - name: 'Mosaic', - category: '10x10', - author: 'syuilo', - data: [ - '- - - - - ', - ' - - - - -', - '- - - - - ', - ' - w w - -', - '- - b b - ', - ' - w w - -', - '- - b b - ', - ' - - - - -', - '- - - - - ', - ' - - - - -', - ] -}; - -export const arena: Map = { - name: 'Arena', - category: '10x10', - author: 'syuilo', - data: [ - '- - -- - -', - ' - - - - ', - '- ------ -', - ' -------- ', - '- --wb-- -', - '- --bw-- -', - ' -------- ', - '- ------ -', - ' - - - - ', - '- - -- - -' - ] -}; - -export const reactor: Map = { - name: 'Reactor', - category: '10x10', - author: 'syuilo', - data: [ - '-w------b-', - 'b- - - -w', - '- --wb-- -', - '---b w---', - '- b wb w -', - '- w bw b -', - '---w b---', - '- --bw-- -', - 'w- - - -b', - '-b------w-' - ] -}; - -export const sixeight: Map = { - name: '6x8', - category: 'Special', - data: [ - '------', - '------', - '------', - '--wb--', - '--bw--', - '------', - '------', - '------' - ] -}; - -export const spark: Map = { - name: 'Spark', - category: 'Special', - author: 'syuilo', - data: [ - ' - - ', - '----------', - ' -------- ', - ' -------- ', - ' ---wb--- ', - ' ---bw--- ', - ' -------- ', - ' -------- ', - '----------', - ' - - ' - ] -}; - -export const islands: Map = { - name: 'Islands', - category: 'Special', - author: 'syuilo', - data: [ - '-------- ', - '---wb--- ', - '---bw--- ', - '-------- ', - ' - - ', - ' - - ', - ' --------', - ' --------', - ' --------', - ' --------' - ] -}; - -export const galaxy: Map = { - name: 'Galaxy', - category: 'Special', - author: 'syuilo', - data: [ - ' ------ ', - ' --www--- ', - ' ------w--- ', - '---bbb--w---', - '--b---b-w-b-', - '-b--wwb-w-b-', - '-b-w-bww--b-', - '-b-w-b---b--', - '---w--bbb---', - ' ---w------ ', - ' ---www-- ', - ' ------ ' - ] -}; - -export const triangle: Map = { - name: 'Triangle', - category: 'Special', - author: 'syuilo', - data: [ - ' -- ', - ' -- ', - ' ---- ', - ' ---- ', - ' --wb-- ', - ' --bw-- ', - ' -------- ', - ' -------- ', - '----------', - '----------' - ] -}; - -export const iphonex: Map = { - name: 'iPhone X', - category: 'Special', - author: 'syuilo', - data: [ - ' -- -- ', - '--------', - '--------', - '--------', - '--------', - '---wb---', - '---bw---', - '--------', - '--------', - '--------', - '--------', - ' ------ ' - ] -}; - -export const dealWithIt: Map = { - name: 'Deal with it!', - category: 'Special', - author: 'syuilo', - data: [ - '------------', - '--w-b-------', - ' --b-w------', - ' --w-b---- ', - ' ------- ' - ] -}; - -export const experiment: Map = { - name: 'Let\'s experiment', - category: 'Special', - author: 'syuilo', - data: [ - ' ------------ ', - '------wb------', - '------bw------', - '--------------', - ' - - ', - '------ ------', - 'bbbbbb wwwwww', - 'bbbbbb wwwwww', - 'bbbbbb wwwwww', - 'bbbbbb wwwwww', - 'wwwwww bbbbbb' - ] -}; - -export const bigBoard: Map = { - name: 'Big board', - category: 'Special', - data: [ - '----------------', - '----------------', - '----------------', - '----------------', - '----------------', - '----------------', - '----------------', - '-------wb-------', - '-------bw-------', - '----------------', - '----------------', - '----------------', - '----------------', - '----------------', - '----------------', - '----------------' - ] -}; - -export const twoBoard: Map = { - name: 'Two board', - category: 'Special', - author: 'Aya', - data: [ - '-------- --------', - '-------- --------', - '-------- --------', - '---wb--- ---wb---', - '---bw--- ---bw---', - '-------- --------', - '-------- --------', - '-------- --------' - ] -}; - -export const test1: Map = { - name: 'Test1', - category: 'Test', - data: [ - '--------', - '---wb---', - '---bw---', - '--------' - ] -}; - -export const test2: Map = { - name: 'Test2', - category: 'Test', - data: [ - '------', - '------', - '-b--w-', - '-w--b-', - '-w--b-' - ] -}; - -export const test3: Map = { - name: 'Test3', - category: 'Test', - data: [ - '-w-', - '--w', - 'w--', - '-w-', - '--w', - 'w--', - '-w-', - '--w', - 'w--', - '-w-', - '---', - 'b--', - ] -}; - -export const test4: Map = { - name: 'Test4', - category: 'Test', - data: [ - '-w--b-', - '-w--b-', - '------', - '-w--b-', - '-w--b-' - ] -}; - -// https://misskey.xyz/othello/5aaabf7fe126e10b5216ea09 64 -export const test5: Map = { - name: 'Test5', - category: 'Test', - data: [ - '--wwwwww--', - '--wwwbwwww', - '-bwwbwbwww', - '-bwwwbwbww', - '-bwwbwbwbw', - '-bwbwbwb-w', - 'bwbwwbbb-w', - 'w-wbbbbb--', - '--w-b-w---', - '----------' - ] -}; diff --git a/src/common/remote/activitypub/act/create.ts b/src/common/remote/activitypub/act/create.ts deleted file mode 100644 index 9eb74800ea..0000000000 --- a/src/common/remote/activitypub/act/create.ts +++ /dev/null @@ -1,9 +0,0 @@ -import create from '../create'; - -export default (resolver, actor, activity) => { - if ('actor' in activity && actor.account.uri !== activity.actor) { - throw new Error(); - } - - return create(resolver, actor, activity.object); -}; diff --git a/src/common/remote/activitypub/act/index.ts b/src/common/remote/activitypub/act/index.ts deleted file mode 100644 index a76983638f..0000000000 --- a/src/common/remote/activitypub/act/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import create from './create'; -import createObject from '../create'; -import Resolver from '../resolver'; - -export default (actor, value) => { - return new Resolver().resolve(value).then(resolved => Promise.all(resolved.map(async promisedResult => { - const { resolver, object } = await promisedResult; - const created = await (await createObject(resolver, actor, [object]))[0]; - - if (created !== null) { - return created; - } - - switch (object.type) { - case 'Create': - return create(resolver, actor, object); - - default: - return null; - } - }))); -}; diff --git a/src/common/remote/activitypub/create.ts b/src/common/remote/activitypub/create.ts deleted file mode 100644 index ea780f01ea..0000000000 --- a/src/common/remote/activitypub/create.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { JSDOM } from 'jsdom'; -import config from '../../../conf'; -import Post from '../../../models/post'; -import RemoteUserObject, { IRemoteUserObject } from '../../../models/remote-user-object'; -import uploadFromUrl from '../../drive/upload_from_url'; -import Resolver from './resolver'; -const createDOMPurify = require('dompurify'); - -function createRemoteUserObject($ref, $id, { id }) { - const object = { $ref, $id }; - - if (!id) { - return { object }; - } - - return RemoteUserObject.insert({ uri: id, object }); -} - -async function createImage(actor, object) { - if ('attributedTo' in object && actor.account.uri !== object.attributedTo) { - throw new Error(); - } - - const { _id } = await uploadFromUrl(object.url, actor); - return createRemoteUserObject('driveFiles.files', _id, object); -} - -async function createNote(resolver, actor, object) { - if ('attributedTo' in object && actor.account.uri !== object.attributedTo) { - throw new Error(); - } - - const mediaIds = 'attachment' in object && - (await Promise.all(await create(resolver, actor, object.attachment))) - .filter(media => media !== null && media.object.$ref === 'driveFiles.files') - .map(({ object }) => object.$id); - - const { window } = new JSDOM(object.content); - - const { _id } = await Post.insert({ - channelId: undefined, - index: undefined, - createdAt: new Date(object.published), - mediaIds, - replyId: undefined, - repostId: undefined, - poll: undefined, - text: window.document.body.textContent, - textHtml: object.content && createDOMPurify(window).sanitize(object.content), - userId: actor._id, - appId: null, - viaMobile: false, - geo: undefined - }); - - // Register to search database - if (object.content && config.elasticsearch.enable) { - const es = require('../../db/elasticsearch'); - - es.index({ - index: 'misskey', - type: 'post', - id: _id.toString(), - body: { - text: window.document.body.textContent - } - }); - } - - return createRemoteUserObject('posts', _id, object); -} - -export default async function create(parentResolver: Resolver, actor, value): Promise>> { - const results = await parentResolver.resolveRemoteUserObjects(value); - - return results.map(promisedResult => promisedResult.then(({ resolver, object }) => { - switch (object.type) { - case 'Image': - return createImage(actor, object); - - case 'Note': - return createNote(resolver, actor, object); - } - - return null; - })); -} diff --git a/src/common/remote/activitypub/renderer/context.ts b/src/common/remote/activitypub/renderer/context.ts deleted file mode 100644 index b56f727ae7..0000000000 --- a/src/common/remote/activitypub/renderer/context.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default [ - 'https://www.w3.org/ns/activitystreams', - 'https://w3id.org/security/v1', - { Hashtag: 'as:Hashtag' } -]; diff --git a/src/common/remote/activitypub/renderer/document.ts b/src/common/remote/activitypub/renderer/document.ts deleted file mode 100644 index 4a456416a9..0000000000 --- a/src/common/remote/activitypub/renderer/document.ts +++ /dev/null @@ -1,7 +0,0 @@ -import config from '../../../../conf'; - -export default ({ _id, contentType }) => ({ - type: 'Document', - mediaType: contentType, - url: `${config.drive_url}/${_id}` -}); diff --git a/src/common/remote/activitypub/renderer/follow.ts b/src/common/remote/activitypub/renderer/follow.ts deleted file mode 100644 index 86a3f8cede..0000000000 --- a/src/common/remote/activitypub/renderer/follow.ts +++ /dev/null @@ -1,8 +0,0 @@ -import config from '../../../../conf'; -import { IRemoteUser } from '../../../../models/user'; - -export default ({ username }, followee: IRemoteUser) => ({ - type: 'Follow', - actor: `${config.url}/@${username}`, - object: followee.account.uri -}); diff --git a/src/common/remote/activitypub/renderer/hashtag.ts b/src/common/remote/activitypub/renderer/hashtag.ts deleted file mode 100644 index ad42700204..0000000000 --- a/src/common/remote/activitypub/renderer/hashtag.ts +++ /dev/null @@ -1,7 +0,0 @@ -import config from '../../../../conf'; - -export default tag => ({ - type: 'Hashtag', - href: `${config.url}/search?q=#${encodeURIComponent(tag)}`, - name: '#' + tag -}); diff --git a/src/common/remote/activitypub/renderer/image.ts b/src/common/remote/activitypub/renderer/image.ts deleted file mode 100644 index 345fbbec59..0000000000 --- a/src/common/remote/activitypub/renderer/image.ts +++ /dev/null @@ -1,6 +0,0 @@ -import config from '../../../../conf'; - -export default ({ _id }) => ({ - type: 'Image', - url: `${config.drive_url}/${_id}` -}); diff --git a/src/common/remote/activitypub/renderer/key.ts b/src/common/remote/activitypub/renderer/key.ts deleted file mode 100644 index 3cac86b769..0000000000 --- a/src/common/remote/activitypub/renderer/key.ts +++ /dev/null @@ -1,10 +0,0 @@ -import config from '../../../../conf'; -import { extractPublic } from '../../../../crypto_key'; -import { ILocalUser } from '../../../../models/user'; - -export default (user: ILocalUser) => ({ - id: `${config.url}/@${user.username}/publickey`, - type: 'Key', - owner: `${config.url}/@${user.username}`, - publicKeyPem: extractPublic(user.account.keypair) -}); diff --git a/src/common/remote/activitypub/renderer/note.ts b/src/common/remote/activitypub/renderer/note.ts deleted file mode 100644 index 2fe20b2136..0000000000 --- a/src/common/remote/activitypub/renderer/note.ts +++ /dev/null @@ -1,44 +0,0 @@ -import renderDocument from './document'; -import renderHashtag from './hashtag'; -import config from '../../../../conf'; -import DriveFile from '../../../../models/drive-file'; -import Post from '../../../../models/post'; -import User from '../../../../models/user'; - -export default async (user, post) => { - const promisedFiles = DriveFile.find({ _id: { $in: post.mediaIds } }); - let inReplyTo; - - if (post.replyId) { - const inReplyToPost = await Post.findOne({ - _id: post.replyId, - }); - - if (inReplyToPost !== null) { - const inReplyToUser = await User.findOne({ - _id: post.userId, - }); - - if (inReplyToUser !== null) { - inReplyTo = `${config.url}@${inReplyToUser.username}/${inReplyToPost._id}`; - } - } - } else { - inReplyTo = null; - } - - const attributedTo = `${config.url}/@${user.username}`; - - return { - id: `${attributedTo}/${post._id}`, - type: 'Note', - attributedTo, - content: post.textHtml, - published: post.createdAt.toISOString(), - to: 'https://www.w3.org/ns/activitystreams#Public', - cc: `${attributedTo}/followers`, - inReplyTo, - attachment: (await promisedFiles).map(renderDocument), - tag: post.tags.map(renderHashtag) - }; -}; diff --git a/src/common/remote/activitypub/renderer/ordered-collection.ts b/src/common/remote/activitypub/renderer/ordered-collection.ts deleted file mode 100644 index 2ca0f77354..0000000000 --- a/src/common/remote/activitypub/renderer/ordered-collection.ts +++ /dev/null @@ -1,6 +0,0 @@ -export default (id, totalItems, orderedItems) => ({ - id, - type: 'OrderedCollection', - totalItems, - orderedItems -}); diff --git a/src/common/remote/activitypub/renderer/person.ts b/src/common/remote/activitypub/renderer/person.ts deleted file mode 100644 index 7303b30385..0000000000 --- a/src/common/remote/activitypub/renderer/person.ts +++ /dev/null @@ -1,20 +0,0 @@ -import renderImage from './image'; -import renderKey from './key'; -import config from '../../../../conf'; - -export default user => { - const id = `${config.url}/@${user.username}`; - - return { - type: 'Person', - id, - inbox: `${id}/inbox`, - outbox: `${id}/outbox`, - preferredUsername: user.username, - name: user.name, - summary: user.description, - icon: user.avatarId && renderImage({ _id: user.avatarId }), - image: user.bannerId && renderImage({ _id: user.bannerId }), - publicKey: renderKey(user) - }; -}; diff --git a/src/common/remote/activitypub/resolve-person.ts b/src/common/remote/activitypub/resolve-person.ts deleted file mode 100644 index 73584946e5..0000000000 --- a/src/common/remote/activitypub/resolve-person.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { JSDOM } from 'jsdom'; -import { toUnicode } from 'punycode'; -import User, { validateUsername, isValidName, isValidDescription } from '../../../models/user'; -import queue from '../../../queue'; -import webFinger from '../webfinger'; -import create from './create'; -import Resolver from './resolver'; - -async function isCollection(collection) { - return ['Collection', 'OrderedCollection'].includes(collection.type); -} - -export default async (value, usernameLower, hostLower, acctLower) => { - if (!validateUsername(usernameLower)) { - throw new Error(); - } - - const { resolver, object } = await new Resolver().resolveOne(value); - - if ( - object === null || - object.type !== 'Person' || - typeof object.preferredUsername !== 'string' || - object.preferredUsername.toLowerCase() !== usernameLower || - !isValidName(object.name) || - !isValidDescription(object.summary) - ) { - throw new Error(); - } - - const [followers, following, outbox, finger] = await Promise.all([ - resolver.resolveOne(object.followers).then( - resolved => isCollection(resolved.object) ? resolved.object : null, - () => null - ), - resolver.resolveOne(object.following).then( - resolved => isCollection(resolved.object) ? resolved.object : null, - () => null - ), - resolver.resolveOne(object.outbox).then( - resolved => isCollection(resolved.object) ? resolved.object : null, - () => null - ), - webFinger(object.id, acctLower), - ]); - - const summaryDOM = JSDOM.fragment(object.summary); - - // Create user - const user = await User.insert({ - avatarId: null, - bannerId: null, - createdAt: Date.parse(object.published), - description: summaryDOM.textContent, - followersCount: followers.totalItem, - followingCount: following.totalItem, - name: object.name, - postsCount: outbox.totalItem, - driveCapacity: 1024 * 1024 * 8, // 8MiB - username: object.preferredUsername, - usernameLower, - host: toUnicode(finger.subject.replace(/^.*?@/, '')), - hostLower, - account: { - publicKey: { - id: object.publicKey.id, - publicKeyPem: object.publicKey.publicKeyPem - }, - inbox: object.inbox, - uri: object.id, - }, - }); - - queue.create('http', { - type: 'performActivityPub', - actor: user._id, - outbox - }).save(); - - const [avatarId, bannerId] = await Promise.all([ - object.icon, - object.image - ].map(async value => { - if (value === undefined) { - return null; - } - - try { - const created = await create(resolver, user, value); - - await Promise.all(created.map(asyncCreated => asyncCreated.then(created => { - if (created !== null && created.object.$ref === 'driveFiles.files') { - throw created.object.$id; - } - }, () => {}))); - - return null; - } catch (id) { - return id; - } - })); - - User.update({ _id: user._id }, { $set: { avatarId, bannerId } }); - - user.avatarId = avatarId; - user.bannerId = bannerId; - - return user; -}; diff --git a/src/common/remote/activitypub/resolver.ts b/src/common/remote/activitypub/resolver.ts deleted file mode 100644 index a167fa1339..0000000000 --- a/src/common/remote/activitypub/resolver.ts +++ /dev/null @@ -1,97 +0,0 @@ -import RemoteUserObject from '../../../models/remote-user-object'; -import { IObject } from './type'; -const request = require('request-promise-native'); - -type IResult = { - resolver: Resolver; - object: IObject; -}; - -export default class Resolver { - private requesting: Set; - - constructor(iterable?: Iterable) { - this.requesting = new Set(iterable); - } - - private async resolveUnrequestedOne(value) { - if (typeof value !== 'string') { - return { resolver: this, object: value }; - } - - const resolver = new Resolver(this.requesting); - - resolver.requesting.add(value); - - const object = await request({ - url: value, - headers: { - Accept: 'application/activity+json, application/ld+json' - }, - json: true - }); - - if (object === null || ( - Array.isArray(object['@context']) ? - !object['@context'].includes('https://www.w3.org/ns/activitystreams') : - object['@context'] !== 'https://www.w3.org/ns/activitystreams' - )) { - throw new Error(); - } - - return { resolver, object }; - } - - private async resolveCollection(value) { - if (Array.isArray(value)) { - return value; - } - - const resolved = typeof value === 'string' ? - await this.resolveUnrequestedOne(value) : - value; - - switch (resolved.type) { - case 'Collection': - return resolved.items; - - case 'OrderedCollection': - return resolved.orderedItems; - - default: - return [resolved]; - } - } - - public async resolve(value): Promise>> { - const collection = await this.resolveCollection(value); - - return collection - .filter(element => !this.requesting.has(element)) - .map(this.resolveUnrequestedOne.bind(this)); - } - - public resolveOne(value) { - if (this.requesting.has(value)) { - throw new Error(); - } - - return this.resolveUnrequestedOne(value); - } - - public async resolveRemoteUserObjects(value) { - const collection = await this.resolveCollection(value); - - return collection.filter(element => !this.requesting.has(element)).map(element => { - if (typeof element === 'string') { - const object = RemoteUserObject.findOne({ uri: element }); - - if (object !== null) { - return object; - } - } - - return this.resolveUnrequestedOne(element); - }); - } -} diff --git a/src/common/remote/activitypub/type.ts b/src/common/remote/activitypub/type.ts deleted file mode 100644 index 94e2c350a2..0000000000 --- a/src/common/remote/activitypub/type.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type IObject = { - type: string; -}; diff --git a/src/common/remote/resolve-user.ts b/src/common/remote/resolve-user.ts deleted file mode 100644 index 4959539da6..0000000000 --- a/src/common/remote/resolve-user.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { toUnicode, toASCII } from 'punycode'; -import User from '../../models/user'; -import resolvePerson from './activitypub/resolve-person'; -import webFinger from './webfinger'; - -export default async (username, host, option) => { - const usernameLower = username.toLowerCase(); - const hostLowerAscii = toASCII(host).toLowerCase(); - const hostLower = toUnicode(hostLowerAscii); - - let user = await User.findOne({ usernameLower, hostLower }, option); - - if (user === null) { - const acctLower = `${usernameLower}@${hostLowerAscii}`; - - const finger = await webFinger(acctLower, acctLower); - const self = finger.links.find(link => link.rel && link.rel.toLowerCase() === 'self'); - if (!self) { - throw new Error(); - } - - user = await resolvePerson(self.href, usernameLower, hostLower, acctLower); - } - - return user; -}; diff --git a/src/common/remote/webfinger.ts b/src/common/remote/webfinger.ts deleted file mode 100644 index fec5da689c..0000000000 --- a/src/common/remote/webfinger.ts +++ /dev/null @@ -1,25 +0,0 @@ -const WebFinger = require('webfinger.js'); - -const webFinger = new WebFinger({ }); - -type ILink = { - href: string; - rel: string; -}; - -type IWebFinger = { - links: ILink[]; - subject: string; -}; - -export default (query, verifier): Promise => new Promise((res, rej) => webFinger.lookup(query, (error, result) => { - if (error) { - return rej(error); - } - - if (result.object.subject.toLowerCase().replace(/^acct:/, '') !== verifier) { - return rej('WebFinger verfification failed'); - } - - res(result.object); -})); diff --git a/src/common/text/parse/elements/mention.ts b/src/common/text/parse/elements/mention.ts index 2025dfdaad..3c81979d0c 100644 --- a/src/common/text/parse/elements/mention.ts +++ b/src/common/text/parse/elements/mention.ts @@ -1,7 +1,7 @@ /** * Mention */ -import parseAcct from '../../../../common/user/parse-acct'; +import parseAcct from '../../../../misc/user/parse-acct'; module.exports = text => { const match = text.match(/^(?:@[a-zA-Z0-9\-]+){1,2}/); diff --git a/src/common/user/get-acct.ts b/src/common/user/get-acct.ts deleted file mode 100644 index 9afb03d88b..0000000000 --- a/src/common/user/get-acct.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default user => { - return user.host === null ? user.username : `${user.username}@${user.host}`; -}; diff --git a/src/common/user/get-summary.ts b/src/common/user/get-summary.ts deleted file mode 100644 index 2c71d3eae9..0000000000 --- a/src/common/user/get-summary.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { IUser, isLocalUser } from '../../models/user'; -import getAcct from './get-acct'; - -/** - * ユーザーを表す文字列を取得します。 - * @param user ユーザー - */ -export default function(user: IUser): string { - let string = `${user.name} (@${getAcct(user)})\n` + - `${user.postsCount}投稿、${user.followingCount}フォロー、${user.followersCount}フォロワー\n`; - - if (isLocalUser(user)) { - const account = user.account; - string += `場所: ${account.profile.location}、誕生日: ${account.profile.birthday}\n`; - } - - return string + `「${user.description}」`; -} diff --git a/src/common/user/parse-acct.ts b/src/common/user/parse-acct.ts deleted file mode 100644 index ef1f55405d..0000000000 --- a/src/common/user/parse-acct.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default acct => { - const splitted = acct.split('@', 2); - return { username: splitted[0], host: splitted[1] || null }; -}; -- cgit v1.2.3-freya