From 6eff8fde74e01fa3527585ec1fbd3629d51bc97a Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 9 Jun 2018 01:45:25 +0900 Subject: サーバーの統計情報をメモリに記憶するようにするなど MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/api/stream/server.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'src/server/api/stream') diff --git a/src/server/api/stream/server.ts b/src/server/api/stream/server.ts index 4ca2ad1b10..342170a21e 100644 --- a/src/server/api/stream/server.ts +++ b/src/server/api/stream/server.ts @@ -11,9 +11,25 @@ export default function(request: websocket.request, connection: websocket.connec })); }; - ev.addListener('stats', onStats); + connection.on('message', async data => { + const msg = JSON.parse(data.utf8Data); + + switch (msg.type) { + case 'requestLog': + ev.once('serverStatsLog:' + msg.id, statsLog => { + connection.send(JSON.stringify({ + type: 'statsLog', + body: statsLog + })); + }); + ev.emit('requestServerStatsLog', msg.id); + break; + } + }); + + ev.addListener('serverStats', onStats); connection.on('close', () => { - ev.removeListener('stats', onStats); + ev.removeListener('serverStats', onStats); }); } -- cgit v1.2.3-freya From c78945436e39121f46e083eac7a6572ca2efe2d2 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 9 Jun 2018 04:14:26 +0900 Subject: #1686 --- locales/ja.yml | 5 + package.json | 2 +- .../app/common/scripts/streaming/notes-stats.ts | 30 ++++ .../app/common/scripts/streaming/server-stats.ts | 30 ++++ src/client/app/common/scripts/streaming/server.ts | 30 ---- src/client/app/common/views/widgets/index.ts | 2 + .../app/common/views/widgets/posts-monitor.vue | 182 +++++++++++++++++++++ src/client/app/common/views/widgets/server.vue | 6 +- src/client/app/desktop/views/components/home.vue | 1 + .../views/pages/deck/deck.widgets-column.vue | 1 + src/client/app/mios.ts | 12 +- src/client/app/mobile/views/pages/widgets.vue | 1 + src/db/mongodb.ts | 5 +- src/index.ts | 2 + src/models/note.ts | 3 + src/notes-stats-child.ts | 20 +++ src/notes-stats.ts | 20 +++ src/server-stats.ts | 2 +- src/server/api/stream/notes-stats.ts | 35 ++++ src/server/api/stream/server-stats.ts | 35 ++++ src/server/api/stream/server.ts | 35 ---- src/server/api/streaming.ts | 12 +- 22 files changed, 393 insertions(+), 78 deletions(-) create mode 100644 src/client/app/common/scripts/streaming/notes-stats.ts create mode 100644 src/client/app/common/scripts/streaming/server-stats.ts delete mode 100644 src/client/app/common/scripts/streaming/server.ts create mode 100644 src/client/app/common/views/widgets/posts-monitor.vue create mode 100644 src/notes-stats-child.ts create mode 100644 src/notes-stats.ts create mode 100644 src/server/api/stream/notes-stats.ts create mode 100644 src/server/api/stream/server-stats.ts delete mode 100644 src/server/api/stream/server.ts (limited to 'src/server/api/stream') diff --git a/locales/ja.yml b/locales/ja.yml index 120ccc7998..212ba90b69 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -63,6 +63,7 @@ common: memo: "メモ" trends: "トレンド" photo-stream: "フォトストリーム" + posts-monitor: "投稿チャート" slideshow: "スライドショー" version: "バージョン" broadcast: "ブロードキャスト" @@ -249,6 +250,10 @@ common/views/widgets/photo-stream.vue: title: "フォトストリーム" no-photos: "写真はありません" +common/views/widgets/posts-monitor.vue: + title: "投稿チャート" + toggle: "表示を切り替え" + common/views/widgets/server.vue: title: "サーバー情報" toggle: "表示を切り替え" diff --git a/package.json b/package.json index b0ad03aa6e..a18f37e0a2 100644 --- a/package.json +++ b/package.json @@ -152,7 +152,7 @@ "mkdirp": "0.5.1", "mocha": "5.2.0", "moji": "0.5.1", - "mongodb": "3.0.8", + "mongodb": "3.0.10", "monk": "6.0.6", "ms": "2.1.1", "nan": "2.10.0", diff --git a/src/client/app/common/scripts/streaming/notes-stats.ts b/src/client/app/common/scripts/streaming/notes-stats.ts new file mode 100644 index 0000000000..9e3e78a709 --- /dev/null +++ b/src/client/app/common/scripts/streaming/notes-stats.ts @@ -0,0 +1,30 @@ +import Stream from './stream'; +import StreamManager from './stream-manager'; +import MiOS from '../../../mios'; + +/** + * Notes stats stream connection + */ +export class NotesStatsStream extends Stream { + constructor(os: MiOS) { + super(os, 'notes-stats'); + } +} + +export class NotesStatsStreamManager extends StreamManager { + private os: MiOS; + + constructor(os: MiOS) { + super(); + + this.os = os; + } + + public getConnection() { + if (this.connection == null) { + this.connection = new NotesStatsStream(this.os); + } + + return this.connection; + } +} diff --git a/src/client/app/common/scripts/streaming/server-stats.ts b/src/client/app/common/scripts/streaming/server-stats.ts new file mode 100644 index 0000000000..9983dfcaf0 --- /dev/null +++ b/src/client/app/common/scripts/streaming/server-stats.ts @@ -0,0 +1,30 @@ +import Stream from './stream'; +import StreamManager from './stream-manager'; +import MiOS from '../../../mios'; + +/** + * Server stats stream connection + */ +export class ServerStatsStream extends Stream { + constructor(os: MiOS) { + super(os, 'server-stats'); + } +} + +export class ServerStatsStreamManager extends StreamManager { + private os: MiOS; + + constructor(os: MiOS) { + super(); + + this.os = os; + } + + public getConnection() { + if (this.connection == null) { + this.connection = new ServerStatsStream(this.os); + } + + return this.connection; + } +} diff --git a/src/client/app/common/scripts/streaming/server.ts b/src/client/app/common/scripts/streaming/server.ts deleted file mode 100644 index 2ea4239288..0000000000 --- a/src/client/app/common/scripts/streaming/server.ts +++ /dev/null @@ -1,30 +0,0 @@ -import Stream from './stream'; -import StreamManager from './stream-manager'; -import MiOS from '../../../mios'; - -/** - * Server stream connection - */ -export class ServerStream extends Stream { - constructor(os: MiOS) { - super(os, 'server'); - } -} - -export class ServerStreamManager extends StreamManager { - private os: MiOS; - - constructor(os: MiOS) { - super(); - - this.os = os; - } - - public getConnection() { - if (this.connection == null) { - this.connection = new ServerStream(this.os); - } - - return this.connection; - } -} diff --git a/src/client/app/common/views/widgets/index.ts b/src/client/app/common/views/widgets/index.ts index a4cabc43ba..0190393ba7 100644 --- a/src/client/app/common/views/widgets/index.ts +++ b/src/client/app/common/views/widgets/index.ts @@ -4,6 +4,7 @@ import wAnalogClock from './analog-clock.vue'; import wVersion from './version.vue'; import wRss from './rss.vue'; import wServer from './server.vue'; +import wPostsMonitor from './posts-monitor.vue'; import wMemo from './memo.vue'; import wBroadcast from './broadcast.vue'; import wCalendar from './calendar.vue'; @@ -22,6 +23,7 @@ Vue.component('mkw-tips', wTips); Vue.component('mkw-donation', wDonation); Vue.component('mkw-broadcast', wBroadcast); Vue.component('mkw-server', wServer); +Vue.component('mkw-posts-monitor', wPostsMonitor); Vue.component('mkw-memo', wMemo); Vue.component('mkw-rss', wRss); Vue.component('mkw-version', wVersion); diff --git a/src/client/app/common/views/widgets/posts-monitor.vue b/src/client/app/common/views/widgets/posts-monitor.vue new file mode 100644 index 0000000000..bdf811bad0 --- /dev/null +++ b/src/client/app/common/views/widgets/posts-monitor.vue @@ -0,0 +1,182 @@ + + + + + diff --git a/src/client/app/common/views/widgets/server.vue b/src/client/app/common/views/widgets/server.vue index 2fdd60499b..d796a3ae05 100644 --- a/src/client/app/common/views/widgets/server.vue +++ b/src/client/app/common/views/widgets/server.vue @@ -55,11 +55,11 @@ export default define({ this.fetching = false; }); - this.connection = (this as any).os.streams.serverStream.getConnection(); - this.connectionId = (this as any).os.streams.serverStream.use(); + this.connection = (this as any).os.streams.serverStatsStream.getConnection(); + this.connectionId = (this as any).os.streams.serverStatsStream.use(); }, beforeDestroy() { - (this as any).os.streams.serverStream.dispose(this.connectionId); + (this as any).os.streams.serverStatsStream.dispose(this.connectionId); }, methods: { toggle() { diff --git a/src/client/app/desktop/views/components/home.vue b/src/client/app/desktop/views/components/home.vue index 826753c169..cac1fd935b 100644 --- a/src/client/app/desktop/views/components/home.vue +++ b/src/client/app/desktop/views/components/home.vue @@ -23,6 +23,7 @@ + diff --git a/src/client/app/desktop/views/pages/deck/deck.widgets-column.vue b/src/client/app/desktop/views/pages/deck/deck.widgets-column.vue index 098a580405..2a3a2472dc 100644 --- a/src/client/app/desktop/views/pages/deck/deck.widgets-column.vue +++ b/src/client/app/desktop/views/pages/deck/deck.widgets-column.vue @@ -23,6 +23,7 @@ + diff --git a/src/client/app/mios.ts b/src/client/app/mios.ts index c644e22087..ba3f967a23 100644 --- a/src/client/app/mios.ts +++ b/src/client/app/mios.ts @@ -8,7 +8,8 @@ import Progress from './common/scripts/loading'; import Connection from './common/scripts/streaming/stream'; import { HomeStreamManager } from './common/scripts/streaming/home'; import { DriveStreamManager } from './common/scripts/streaming/drive'; -import { ServerStreamManager } from './common/scripts/streaming/server'; +import { ServerStatsStreamManager } from './common/scripts/streaming/server-stats'; +import { NotesStatsStreamManager } from './common/scripts/streaming/notes-stats'; import { MessagingIndexStreamManager } from './common/scripts/streaming/messaging-index'; import { OthelloStreamManager } from './common/scripts/streaming/othello'; @@ -104,14 +105,16 @@ export default class MiOS extends EventEmitter { localTimelineStream: LocalTimelineStreamManager; globalTimelineStream: GlobalTimelineStreamManager; driveStream: DriveStreamManager; - serverStream: ServerStreamManager; + serverStatsStream: ServerStatsStreamManager; + notesStatsStream: NotesStatsStreamManager; messagingIndexStream: MessagingIndexStreamManager; othelloStream: OthelloStreamManager; } = { localTimelineStream: null, globalTimelineStream: null, driveStream: null, - serverStream: null, + serverStatsStream: null, + notesStatsStream: null, messagingIndexStream: null, othelloStream: null }; @@ -218,7 +221,8 @@ export default class MiOS extends EventEmitter { this.store = initStore(this); //#region Init stream managers - this.streams.serverStream = new ServerStreamManager(this); + this.streams.serverStatsStream = new ServerStatsStreamManager(this); + this.streams.notesStatsStream = new NotesStatsStreamManager(this); this.once('signedin', () => { // Init home stream manager diff --git a/src/client/app/mobile/views/pages/widgets.vue b/src/client/app/mobile/views/pages/widgets.vue index eab0ca6a38..ea8580b4d0 100644 --- a/src/client/app/mobile/views/pages/widgets.vue +++ b/src/client/app/mobile/views/pages/widgets.vue @@ -15,6 +15,7 @@ + diff --git a/src/db/mongodb.ts b/src/db/mongodb.ts index 05bb72bfde..0f6d27ca15 100644 --- a/src/db/mongodb.ts +++ b/src/db/mongodb.ts @@ -12,7 +12,10 @@ const uri = u && p */ import mongo from 'monk'; -const db = mongo(uri); +const db = mongo(uri, { + poolSize: 16, + keepAlive: 1 +}); export default db; diff --git a/src/index.ts b/src/index.ts index 42a4f484e6..4a98b7564c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,6 +18,7 @@ import EnvironmentInfo from './utils/environmentInfo'; import MachineInfo from './utils/machineInfo'; import DependencyInfo from './utils/dependencyInfo'; import serverStats from './server-stats'; +import notesStats from './notes-stats'; import loadConfig from './config/load'; import { Config } from './config/types'; @@ -50,6 +51,7 @@ function main() { ev.mount(); serverStats(); + notesStats(); } else { workerMain(opt); } diff --git a/src/models/note.ts b/src/models/note.ts index ad8c1565f0..d4681b7b70 100644 --- a/src/models/note.ts +++ b/src/models/note.ts @@ -16,6 +16,9 @@ import Following from './following'; const Note = db.get('notes'); Note.createIndex('uri', { sparse: true, unique: true }); Note.createIndex('userId'); +Note.createIndex({ + createdAt: -1 +}); export default Note; export function isValidText(text: string): boolean { diff --git a/src/notes-stats-child.ts b/src/notes-stats-child.ts new file mode 100644 index 0000000000..0e0f14eaf1 --- /dev/null +++ b/src/notes-stats-child.ts @@ -0,0 +1,20 @@ +import Note from './models/note'; + +setInterval(async () => { + const [all, local] = await Promise.all([Note.count({ + createdAt: { + $gte: new Date(Date.now() - 3000) + } + }), Note.count({ + createdAt: { + $gte: new Date(Date.now() - 3000) + }, + '_user.host': null + })]); + + const stats = { + all, local + }; + + process.send(stats); +}, 3000); diff --git a/src/notes-stats.ts b/src/notes-stats.ts new file mode 100644 index 0000000000..3094c34af0 --- /dev/null +++ b/src/notes-stats.ts @@ -0,0 +1,20 @@ +import * as childProcess from 'child_process'; +import Xev from 'xev'; + +const ev = new Xev(); + +export default function() { + const log = []; + + const p = childProcess.fork(__dirname + '/notes-stats-child.js'); + + p.on('message', stats => { + ev.emit('notesStats', stats); + log.push(stats); + if (log.length > 100) log.shift(); + }); + + ev.on('requestNotesStatsLog', id => { + ev.emit('notesStatsLog:' + id, log); + }); +} diff --git a/src/server-stats.ts b/src/server-stats.ts index 85aa85b682..7b0d4a8576 100644 --- a/src/server-stats.ts +++ b/src/server-stats.ts @@ -6,7 +6,7 @@ import Xev from 'xev'; const ev = new Xev(); /** - * Report stats regularly + * Report server stats regularly */ export default function() { const log = []; diff --git a/src/server/api/stream/notes-stats.ts b/src/server/api/stream/notes-stats.ts new file mode 100644 index 0000000000..739b325848 --- /dev/null +++ b/src/server/api/stream/notes-stats.ts @@ -0,0 +1,35 @@ +import * as websocket from 'websocket'; +import Xev from 'xev'; + +const ev = new Xev(); + +export default function(request: websocket.request, connection: websocket.connection): void { + const onStats = stats => { + connection.send(JSON.stringify({ + type: 'stats', + body: stats + })); + }; + + connection.on('message', async data => { + const msg = JSON.parse(data.utf8Data); + + switch (msg.type) { + case 'requestLog': + ev.once('notesStatsLog:' + msg.id, statsLog => { + connection.send(JSON.stringify({ + type: 'statsLog', + body: statsLog + })); + }); + ev.emit('requestNotesStatsLog', msg.id); + break; + } + }); + + ev.addListener('notesStats', onStats); + + connection.on('close', () => { + ev.removeListener('notesStats', onStats); + }); +} diff --git a/src/server/api/stream/server-stats.ts b/src/server/api/stream/server-stats.ts new file mode 100644 index 0000000000..342170a21e --- /dev/null +++ b/src/server/api/stream/server-stats.ts @@ -0,0 +1,35 @@ +import * as websocket from 'websocket'; +import Xev from 'xev'; + +const ev = new Xev(); + +export default function(request: websocket.request, connection: websocket.connection): void { + const onStats = stats => { + connection.send(JSON.stringify({ + type: 'stats', + body: stats + })); + }; + + connection.on('message', async data => { + const msg = JSON.parse(data.utf8Data); + + switch (msg.type) { + case 'requestLog': + ev.once('serverStatsLog:' + msg.id, statsLog => { + connection.send(JSON.stringify({ + type: 'statsLog', + body: statsLog + })); + }); + ev.emit('requestServerStatsLog', msg.id); + break; + } + }); + + ev.addListener('serverStats', onStats); + + connection.on('close', () => { + ev.removeListener('serverStats', onStats); + }); +} diff --git a/src/server/api/stream/server.ts b/src/server/api/stream/server.ts deleted file mode 100644 index 342170a21e..0000000000 --- a/src/server/api/stream/server.ts +++ /dev/null @@ -1,35 +0,0 @@ -import * as websocket from 'websocket'; -import Xev from 'xev'; - -const ev = new Xev(); - -export default function(request: websocket.request, connection: websocket.connection): void { - const onStats = stats => { - connection.send(JSON.stringify({ - type: 'stats', - body: stats - })); - }; - - connection.on('message', async data => { - const msg = JSON.parse(data.utf8Data); - - switch (msg.type) { - case 'requestLog': - ev.once('serverStatsLog:' + msg.id, statsLog => { - connection.send(JSON.stringify({ - type: 'statsLog', - body: statsLog - })); - }); - ev.emit('requestServerStatsLog', msg.id); - break; - } - }); - - ev.addListener('serverStats', onStats); - - connection.on('close', () => { - ev.removeListener('serverStats', onStats); - }); -} diff --git a/src/server/api/streaming.ts b/src/server/api/streaming.ts index 6825b6336a..2d4cfc108f 100644 --- a/src/server/api/streaming.ts +++ b/src/server/api/streaming.ts @@ -12,7 +12,8 @@ import messagingStream from './stream/messaging'; import messagingIndexStream from './stream/messaging-index'; import othelloGameStream from './stream/othello-game'; import othelloStream from './stream/othello'; -import serverStream from './stream/server'; +import serverStatsStream from './stream/server-stats'; +import notesStatsStream from './stream/notes-stats'; import requestsStream from './stream/requests'; import { ParsedUrlQuery } from 'querystring'; import authenticate from './authenticate'; @@ -28,8 +29,13 @@ module.exports = (server: http.Server) => { ws.on('request', async (request) => { const connection = request.accept(); - if (request.resourceURL.pathname === '/server') { - serverStream(request, connection); + if (request.resourceURL.pathname === '/server-stats') { + serverStatsStream(request, connection); + return; + } + + if (request.resourceURL.pathname === '/notes-stats') { + notesStatsStream(request, connection); return; } -- cgit v1.2.3-freya From 8bc47baf4f47d29afb1c25e2ec3807384d06f812 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 12 Jun 2018 18:54:36 +0900 Subject: #1710 --- src/models/note.ts | 2 +- src/server/api/stream/home.ts | 10 +++++++++- src/server/api/stream/local-timeline.ts | 8 ++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) (limited to 'src/server/api/stream') diff --git a/src/models/note.ts b/src/models/note.ts index 359d953735..3bbfec0103 100644 --- a/src/models/note.ts +++ b/src/models/note.ts @@ -289,7 +289,7 @@ export const pack = async ( // Poll if (meId && _note.poll && !hide) { - _note.poll = (async (poll) => { + _note.poll = (async poll => { const vote = await PollVote .findOne({ userId: meId, diff --git a/src/server/api/stream/home.ts b/src/server/api/stream/home.ts index 54fde2d776..d9b8f7fb96 100644 --- a/src/server/api/stream/home.ts +++ b/src/server/api/stream/home.ts @@ -4,7 +4,7 @@ import * as debug from 'debug'; import User, { IUser } from '../../../models/user'; import Mute from '../../../models/mute'; -import { pack as packNote } from '../../../models/note'; +import { pack as packNote, pack } from '../../../models/note'; import readNotification from '../common/read-notification'; import call from '../call'; import { IApp } from '../../../models/app'; @@ -48,6 +48,14 @@ export default async function( } //#endregion + // Renoteなら再pack + if (x.type == 'note' && x.body.renoteId != null) { + x.body.renote = await pack(x.body.renoteId, user, { + detail: true + }); + data = JSON.stringify(x); + } + connection.send(data); } catch (e) { connection.send(data); diff --git a/src/server/api/stream/local-timeline.ts b/src/server/api/stream/local-timeline.ts index a790ba878b..8f6a445be0 100644 --- a/src/server/api/stream/local-timeline.ts +++ b/src/server/api/stream/local-timeline.ts @@ -3,6 +3,7 @@ import * as redis from 'redis'; import { IUser } from '../../../models/user'; import Mute from '../../../models/mute'; +import { pack } from '../../../models/note'; export default async function( request: websocket.request, @@ -31,6 +32,13 @@ export default async function( } //#endregion + // Renoteなら再pack + if (note.renoteId != null) { + note.renote = await pack(note.renoteId, user, { + detail: true + }); + } + connection.send(JSON.stringify({ type: 'note', body: note -- cgit v1.2.3-freya From 1ef66c962a1cea81dee4f5db32cd011feac7de44 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 17 Jun 2018 08:10:54 +0900 Subject: reversi :white_flower: :100: --- locales/de.yml | 2 +- locales/en.yml | 4 +- locales/es.yml | 2 +- locales/fr.yml | 2 +- locales/it.yml | 2 +- locales/ja.yml | 2 +- locales/ko.yml | 2 +- locales/pl.yml | 2 +- locales/pt.yml | 2 +- locales/ru.yml | 2 +- locales/zh.yml | 2 +- .../app/common/scripts/compose-notification.ts | 2 +- .../app/common/scripts/streaming/othello-game.ts | 11 - src/client/app/common/scripts/streaming/othello.ts | 31 - .../app/common/scripts/streaming/reversi-game.ts | 11 + src/client/app/common/scripts/streaming/reversi.ts | 31 + src/client/app/common/views/components/index.ts | 4 +- .../app/common/views/components/othello.game.vue | 324 -------- .../common/views/components/othello.gameroom.vue | 42 - .../app/common/views/components/othello.room.vue | 297 ------- src/client/app/common/views/components/othello.vue | 313 ------- .../app/common/views/components/reversi.game.vue | 324 ++++++++ .../common/views/components/reversi.gameroom.vue | 42 + .../app/common/views/components/reversi.room.vue | 297 +++++++ src/client/app/common/views/components/reversi.vue | 313 +++++++ src/client/app/desktop/script.ts | 10 +- .../app/desktop/views/components/game-window.vue | 6 +- .../app/desktop/views/components/ui.header.nav.vue | 12 +- src/client/app/desktop/views/pages/othello.vue | 50 -- src/client/app/desktop/views/pages/reversi.vue | 50 ++ src/client/app/mios.ts | 8 +- src/client/app/mobile/script.ts | 6 +- .../app/mobile/views/components/ui.header.vue | 12 +- src/client/app/mobile/views/components/ui.nav.vue | 14 +- src/client/app/mobile/views/pages/othello.vue | 50 -- src/client/app/mobile/views/pages/reversi.vue | 50 ++ src/client/assets/othello-put-me.mp3 | Bin 15672 -> 0 bytes src/client/assets/othello-put-you.mp3 | Bin 26121 -> 0 bytes src/client/assets/reversi-put-me.mp3 | Bin 0 -> 15672 bytes src/client/assets/reversi-put-you.mp3 | Bin 0 -> 26121 bytes src/config/types.ts | 2 +- src/models/othello-game.ts | 109 --- src/models/othello-matching.ts | 44 - src/models/reversi-game.ts | 109 +++ src/models/reversi-matching.ts | 44 + src/othello/ai/back.ts | 377 --------- src/othello/ai/front.ts | 233 ------ src/othello/ai/index.ts | 1 - src/othello/core.ts | 340 -------- src/othello/maps.ts | 911 --------------------- src/publishers/stream.ts | 12 +- src/reversi/ai/back.ts | 377 +++++++++ src/reversi/ai/front.ts | 233 ++++++ src/reversi/ai/index.ts | 1 + src/reversi/core.ts | 340 ++++++++ src/reversi/maps.ts | 911 +++++++++++++++++++++ src/server/api/endpoints.ts | 10 +- src/server/api/endpoints/othello/games.ts | 62 -- src/server/api/endpoints/othello/games/show.ts | 32 - src/server/api/endpoints/othello/invitations.ts | 15 - src/server/api/endpoints/othello/match.ts | 95 --- src/server/api/endpoints/othello/match/cancel.ts | 9 - src/server/api/endpoints/reversi/games.ts | 62 ++ src/server/api/endpoints/reversi/games/show.ts | 32 + src/server/api/endpoints/reversi/invitations.ts | 15 + src/server/api/endpoints/reversi/match.ts | 95 +++ src/server/api/endpoints/reversi/match/cancel.ts | 9 + src/server/api/stream/othello-game.ts | 333 -------- src/server/api/stream/othello.ts | 29 - src/server/api/stream/reversi-game.ts | 333 ++++++++ src/server/api/stream/reversi.ts | 29 + src/server/api/streaming.ts | 10 +- 72 files changed, 3774 insertions(+), 3774 deletions(-) delete mode 100644 src/client/app/common/scripts/streaming/othello-game.ts delete mode 100644 src/client/app/common/scripts/streaming/othello.ts create mode 100644 src/client/app/common/scripts/streaming/reversi-game.ts create mode 100644 src/client/app/common/scripts/streaming/reversi.ts delete mode 100644 src/client/app/common/views/components/othello.game.vue delete mode 100644 src/client/app/common/views/components/othello.gameroom.vue delete mode 100644 src/client/app/common/views/components/othello.room.vue delete mode 100644 src/client/app/common/views/components/othello.vue create mode 100644 src/client/app/common/views/components/reversi.game.vue create mode 100644 src/client/app/common/views/components/reversi.gameroom.vue create mode 100644 src/client/app/common/views/components/reversi.room.vue create mode 100644 src/client/app/common/views/components/reversi.vue delete mode 100644 src/client/app/desktop/views/pages/othello.vue create mode 100644 src/client/app/desktop/views/pages/reversi.vue delete mode 100644 src/client/app/mobile/views/pages/othello.vue create mode 100644 src/client/app/mobile/views/pages/reversi.vue delete mode 100644 src/client/assets/othello-put-me.mp3 delete mode 100644 src/client/assets/othello-put-you.mp3 create mode 100644 src/client/assets/reversi-put-me.mp3 create mode 100644 src/client/assets/reversi-put-you.mp3 delete mode 100644 src/models/othello-game.ts delete mode 100644 src/models/othello-matching.ts create mode 100644 src/models/reversi-game.ts create mode 100644 src/models/reversi-matching.ts delete mode 100644 src/othello/ai/back.ts delete mode 100644 src/othello/ai/front.ts delete mode 100644 src/othello/ai/index.ts delete mode 100644 src/othello/core.ts delete mode 100644 src/othello/maps.ts create mode 100644 src/reversi/ai/back.ts create mode 100644 src/reversi/ai/front.ts create mode 100644 src/reversi/ai/index.ts create mode 100644 src/reversi/core.ts create mode 100644 src/reversi/maps.ts delete mode 100644 src/server/api/endpoints/othello/games.ts delete mode 100644 src/server/api/endpoints/othello/games/show.ts delete mode 100644 src/server/api/endpoints/othello/invitations.ts delete mode 100644 src/server/api/endpoints/othello/match.ts delete mode 100644 src/server/api/endpoints/othello/match/cancel.ts create mode 100644 src/server/api/endpoints/reversi/games.ts create mode 100644 src/server/api/endpoints/reversi/games/show.ts create mode 100644 src/server/api/endpoints/reversi/invitations.ts create mode 100644 src/server/api/endpoints/reversi/match.ts create mode 100644 src/server/api/endpoints/reversi/match/cancel.ts delete mode 100644 src/server/api/stream/othello-game.ts delete mode 100644 src/server/api/stream/othello.ts create mode 100644 src/server/api/stream/reversi-game.ts create mode 100644 src/server/api/stream/reversi.ts (limited to 'src/server/api/stream') diff --git a/locales/de.yml b/locales/de.yml index 0a53d74651..dd6deed0f2 100644 --- a/locales/de.yml +++ b/locales/de.yml @@ -333,7 +333,7 @@ desktop/views/components/friends-maker.vue: refresh: "Mehr" close: "Schließen" desktop/views/components/game-window.vue: - game: "Othello" + game: "Reversi" desktop/views/components/home.vue: done: "Verbunden" add-widget: "Widget hinzufügen:" diff --git a/locales/en.yml b/locales/en.yml index 9d2e2e76be..4a452dc129 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -333,7 +333,7 @@ desktop/views/components/friends-maker.vue: refresh: "More" close: "Close" desktop/views/components/game-window.vue: - game: "Othello" + game: "Reversi" desktop/views/components/home.vue: done: "Submit" add-widget: "Add widget:" @@ -549,7 +549,7 @@ desktop/views/components/ui.header.nav.vue: home: "Home" deck: "Deck" messaging: "Messages" - game: "Play Othello" + game: "Play Reversi" desktop/views/components/ui.header.notifications.vue: title: "Notifications" desktop/views/components/ui.header.post.vue: diff --git a/locales/es.yml b/locales/es.yml index 4d6b348658..add3258292 100644 --- a/locales/es.yml +++ b/locales/es.yml @@ -333,7 +333,7 @@ desktop/views/components/friends-maker.vue: refresh: "もっと見る" close: "閉じる" desktop/views/components/game-window.vue: - game: "オセロ" + game: "リバーシ" desktop/views/components/home.vue: done: "完了" add-widget: "ウィジェットを追加:" diff --git a/locales/fr.yml b/locales/fr.yml index e618a876c5..0af534bc61 100644 --- a/locales/fr.yml +++ b/locales/fr.yml @@ -333,7 +333,7 @@ desktop/views/components/friends-maker.vue: refresh: "Plus" close: "Fermer" desktop/views/components/game-window.vue: - game: "Othello" + game: "Reversi" desktop/views/components/home.vue: done: "Envoyer" add-widget: "Ajouter un widget" diff --git a/locales/it.yml b/locales/it.yml index 4d6b348658..add3258292 100644 --- a/locales/it.yml +++ b/locales/it.yml @@ -333,7 +333,7 @@ desktop/views/components/friends-maker.vue: refresh: "もっと見る" close: "閉じる" desktop/views/components/game-window.vue: - game: "オセロ" + game: "リバーシ" desktop/views/components/home.vue: done: "完了" add-widget: "ウィジェットを追加:" diff --git a/locales/ja.yml b/locales/ja.yml index 86b16adaad..29decefe68 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -384,7 +384,7 @@ desktop/views/components/friends-maker.vue: close: "閉じる" desktop/views/components/game-window.vue: - game: "オセロ" + game: "リバーシ" desktop/views/components/home.vue: done: "完了" diff --git a/locales/ko.yml b/locales/ko.yml index 4d6b348658..add3258292 100644 --- a/locales/ko.yml +++ b/locales/ko.yml @@ -333,7 +333,7 @@ desktop/views/components/friends-maker.vue: refresh: "もっと見る" close: "閉じる" desktop/views/components/game-window.vue: - game: "オセロ" + game: "リバーシ" desktop/views/components/home.vue: done: "完了" add-widget: "ウィジェットを追加:" diff --git a/locales/pl.yml b/locales/pl.yml index 7c0d41e693..8febac051c 100644 --- a/locales/pl.yml +++ b/locales/pl.yml @@ -333,7 +333,7 @@ desktop/views/components/friends-maker.vue: refresh: "Więcej" close: "Zamknij" desktop/views/components/game-window.vue: - game: "Othello" + game: "Reversi" desktop/views/components/home.vue: done: "Wyślij" add-widget: "Dodaj widżet:" diff --git a/locales/pt.yml b/locales/pt.yml index 141209396f..a195c4d9b5 100644 --- a/locales/pt.yml +++ b/locales/pt.yml @@ -333,7 +333,7 @@ desktop/views/components/friends-maker.vue: refresh: "もっと見る" close: "閉じる" desktop/views/components/game-window.vue: - game: "オセロ" + game: "リバーシ" desktop/views/components/home.vue: done: "完了" add-widget: "ウィジェットを追加:" diff --git a/locales/ru.yml b/locales/ru.yml index 57d9f905d0..05992e9310 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -333,7 +333,7 @@ desktop/views/components/friends-maker.vue: refresh: "もっと見る" close: "閉じる" desktop/views/components/game-window.vue: - game: "オセロ" + game: "リバーシ" desktop/views/components/home.vue: done: "完了" add-widget: "ウィジェットを追加:" diff --git a/locales/zh.yml b/locales/zh.yml index 655d7e9fe6..cec6ff64a7 100644 --- a/locales/zh.yml +++ b/locales/zh.yml @@ -333,7 +333,7 @@ desktop/views/components/friends-maker.vue: refresh: "もっと見る" close: "閉じる" desktop/views/components/game-window.vue: - game: "オセロ" + game: "リバーシ" desktop/views/components/home.vue: done: "完了" add-widget: "ウィジェットを追加:" diff --git a/src/client/app/common/scripts/compose-notification.ts b/src/client/app/common/scripts/compose-notification.ts index c19b1c5ad0..cc28f75998 100644 --- a/src/client/app/common/scripts/compose-notification.ts +++ b/src/client/app/common/scripts/compose-notification.ts @@ -55,7 +55,7 @@ export default function(type, data): Notification { icon: data.user.avatarUrl + '?thumbnail&size=64' }; - case 'othello_invited': + case 'reversi_invited': return { title: '対局への招待があります', body: `${getUserName(data.parent)}さんから`, diff --git a/src/client/app/common/scripts/streaming/othello-game.ts b/src/client/app/common/scripts/streaming/othello-game.ts deleted file mode 100644 index 9e36f647bb..0000000000 --- a/src/client/app/common/scripts/streaming/othello-game.ts +++ /dev/null @@ -1,11 +0,0 @@ -import Stream from './stream'; -import MiOS from '../../../mios'; - -export class OthelloGameStream extends Stream { - constructor(os: MiOS, me, game) { - super(os, 'othello-game', { - i: me ? me.token : null, - game: game.id - }); - } -} diff --git a/src/client/app/common/scripts/streaming/othello.ts b/src/client/app/common/scripts/streaming/othello.ts deleted file mode 100644 index 8f4f217e39..0000000000 --- a/src/client/app/common/scripts/streaming/othello.ts +++ /dev/null @@ -1,31 +0,0 @@ -import StreamManager from './stream-manager'; -import Stream from './stream'; -import MiOS from '../../../mios'; - -export class OthelloStream extends Stream { - constructor(os: MiOS, me) { - super(os, 'othello', { - i: me.token - }); - } -} - -export class OthelloStreamManager extends StreamManager { - private me; - private os: MiOS; - - constructor(os: MiOS, me) { - super(); - - this.me = me; - this.os = os; - } - - public getConnection() { - if (this.connection == null) { - this.connection = new OthelloStream(this.os, this.me); - } - - return this.connection; - } -} diff --git a/src/client/app/common/scripts/streaming/reversi-game.ts b/src/client/app/common/scripts/streaming/reversi-game.ts new file mode 100644 index 0000000000..5638b3013f --- /dev/null +++ b/src/client/app/common/scripts/streaming/reversi-game.ts @@ -0,0 +1,11 @@ +import Stream from './stream'; +import MiOS from '../../../mios'; + +export class ReversiGameStream extends Stream { + constructor(os: MiOS, me, game) { + super(os, 'reversi-game', { + i: me ? me.token : null, + game: game.id + }); + } +} diff --git a/src/client/app/common/scripts/streaming/reversi.ts b/src/client/app/common/scripts/streaming/reversi.ts new file mode 100644 index 0000000000..2e4395f0f1 --- /dev/null +++ b/src/client/app/common/scripts/streaming/reversi.ts @@ -0,0 +1,31 @@ +import StreamManager from './stream-manager'; +import Stream from './stream'; +import MiOS from '../../../mios'; + +export class ReversiStream extends Stream { + constructor(os: MiOS, me) { + super(os, 'reversi', { + i: me.token + }); + } +} + +export class ReversiStreamManager extends StreamManager { + private me; + private os: MiOS; + + constructor(os: MiOS, me) { + super(); + + this.me = me; + this.os = os; + } + + public getConnection() { + if (this.connection == null) { + this.connection = new ReversiStream(this.os, this.me); + } + + return this.connection; + } +} diff --git a/src/client/app/common/views/components/index.ts b/src/client/app/common/views/components/index.ts index b91008f718..5b2fa084fb 100644 --- a/src/client/app/common/views/components/index.ts +++ b/src/client/app/common/views/components/index.ts @@ -27,7 +27,7 @@ import urlPreview from './url-preview.vue'; import twitterSetting from './twitter-setting.vue'; import fileTypeIcon from './file-type-icon.vue'; import Switch from './switch.vue'; -import Othello from './othello.vue'; +import Reversi from './reversi.vue'; import welcomeTimeline from './welcome-timeline.vue'; import uiInput from './ui/input.vue'; import uiButton from './ui/button.vue'; @@ -65,7 +65,7 @@ Vue.component('mk-url-preview', urlPreview); Vue.component('mk-twitter-setting', twitterSetting); Vue.component('mk-file-type-icon', fileTypeIcon); Vue.component('mk-switch', Switch); -Vue.component('mk-othello', Othello); +Vue.component('mk-reversi', Reversi); Vue.component('mk-welcome-timeline', welcomeTimeline); Vue.component('ui-input', uiInput); Vue.component('ui-button', uiButton); diff --git a/src/client/app/common/views/components/othello.game.vue b/src/client/app/common/views/components/othello.game.vue deleted file mode 100644 index 4c63bd18b8..0000000000 --- a/src/client/app/common/views/components/othello.game.vue +++ /dev/null @@ -1,324 +0,0 @@ - - - - - diff --git a/src/client/app/common/views/components/othello.gameroom.vue b/src/client/app/common/views/components/othello.gameroom.vue deleted file mode 100644 index 2dbd04ce49..0000000000 --- a/src/client/app/common/views/components/othello.gameroom.vue +++ /dev/null @@ -1,42 +0,0 @@ - - - diff --git a/src/client/app/common/views/components/othello.room.vue b/src/client/app/common/views/components/othello.room.vue deleted file mode 100644 index dcf41f2e04..0000000000 --- a/src/client/app/common/views/components/othello.room.vue +++ /dev/null @@ -1,297 +0,0 @@ - - - - - - - - - diff --git a/src/client/app/common/views/components/othello.vue b/src/client/app/common/views/components/othello.vue deleted file mode 100644 index a0971c45b4..0000000000 --- a/src/client/app/common/views/components/othello.vue +++ /dev/null @@ -1,313 +0,0 @@ - - - - - diff --git a/src/client/app/common/views/components/reversi.game.vue b/src/client/app/common/views/components/reversi.game.vue new file mode 100644 index 0000000000..dc79c95bb8 --- /dev/null +++ b/src/client/app/common/views/components/reversi.game.vue @@ -0,0 +1,324 @@ + + + + + diff --git a/src/client/app/common/views/components/reversi.gameroom.vue b/src/client/app/common/views/components/reversi.gameroom.vue new file mode 100644 index 0000000000..7ce0112451 --- /dev/null +++ b/src/client/app/common/views/components/reversi.gameroom.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/client/app/common/views/components/reversi.room.vue b/src/client/app/common/views/components/reversi.room.vue new file mode 100644 index 0000000000..5074845758 --- /dev/null +++ b/src/client/app/common/views/components/reversi.room.vue @@ -0,0 +1,297 @@ + + + + + + + + + diff --git a/src/client/app/common/views/components/reversi.vue b/src/client/app/common/views/components/reversi.vue new file mode 100644 index 0000000000..e4d7740bde --- /dev/null +++ b/src/client/app/common/views/components/reversi.vue @@ -0,0 +1,313 @@ + + + + + diff --git a/src/client/app/desktop/script.ts b/src/client/app/desktop/script.ts index 076d532d6d..201ab0a83d 100644 --- a/src/client/app/desktop/script.ts +++ b/src/client/app/desktop/script.ts @@ -34,7 +34,7 @@ import MkMessagingRoom from './views/pages/messaging-room.vue'; import MkNote from './views/pages/note.vue'; import MkSearch from './views/pages/search.vue'; import MkTag from './views/pages/tag.vue'; -import MkOthello from './views/pages/othello.vue'; +import MkReversi from './views/pages/reversi.vue'; import MkShare from './views/pages/share.vue'; /** @@ -64,8 +64,8 @@ init(async (launch) => { { path: '/search', component: MkSearch }, { path: '/tags/:tag', component: MkTag }, { path: '/share', component: MkShare }, - { path: '/othello', component: MkOthello }, - { path: '/othello/:game', component: MkOthello }, + { path: '/reversi', component: MkReversi }, + { path: '/reversi/:game', component: MkReversi }, { path: '/@:user', component: MkUser }, { path: '/notes/:note', component: MkNote } ] @@ -166,8 +166,8 @@ function registerNotifications(stream: HomeStreamManager) { setTimeout(n.close.bind(n), 7000); }); - connection.on('othello_invited', matching => { - const _n = composeNotification('othello_invited', matching); + connection.on('reversi_invited', matching => { + const _n = composeNotification('reversi_invited', matching); const n = new Notification(_n.title, { body: _n.body, icon: _n.icon diff --git a/src/client/app/desktop/views/components/game-window.vue b/src/client/app/desktop/views/components/game-window.vue index c3c0f90075..7c6cb9cd40 100644 --- a/src/client/app/desktop/views/components/game-window.vue +++ b/src/client/app/desktop/views/components/game-window.vue @@ -1,7 +1,7 @@ @@ -18,8 +18,8 @@ export default Vue.extend({ computed: { popout(): string { return this.game - ? `${url}/othello/${this.game.id}` - : `${url}/othello`; + ? `${url}/reversi/${this.game.id}` + : `${url}/reversi`; } } }); diff --git a/src/client/app/desktop/views/components/ui.header.nav.vue b/src/client/app/desktop/views/components/ui.header.nav.vue index fe2637cec3..42211b57fe 100644 --- a/src/client/app/desktop/views/components/ui.header.nav.vue +++ b/src/client/app/desktop/views/components/ui.header.nav.vue @@ -56,23 +56,23 @@ export default Vue.extend({ this.connection = (this as any).os.stream.getConnection(); this.connectionId = (this as any).os.stream.use(); - this.connection.on('othello_invited', this.onOthelloInvited); - this.connection.on('othello_no_invites', this.onOthelloNoInvites); + this.connection.on('reversi_invited', this.onReversiInvited); + this.connection.on('reversi_no_invites', this.onReversiNoInvites); } }, beforeDestroy() { if (this.$store.getters.isSignedIn) { - this.connection.off('othello_invited', this.onOthelloInvited); - this.connection.off('othello_no_invites', this.onOthelloNoInvites); + this.connection.off('reversi_invited', this.onReversiInvited); + this.connection.off('reversi_no_invites', this.onReversiNoInvites); (this as any).os.stream.dispose(this.connectionId); } }, methods: { - onOthelloInvited() { + onReversiInvited() { this.hasGameInvitations = true; }, - onOthelloNoInvites() { + onReversiNoInvites() { this.hasGameInvitations = false; }, diff --git a/src/client/app/desktop/views/pages/othello.vue b/src/client/app/desktop/views/pages/othello.vue deleted file mode 100644 index 0d8e987dd9..0000000000 --- a/src/client/app/desktop/views/pages/othello.vue +++ /dev/null @@ -1,50 +0,0 @@ - - - diff --git a/src/client/app/desktop/views/pages/reversi.vue b/src/client/app/desktop/views/pages/reversi.vue new file mode 100644 index 0000000000..098fc41f1c --- /dev/null +++ b/src/client/app/desktop/views/pages/reversi.vue @@ -0,0 +1,50 @@ + + + diff --git a/src/client/app/mios.ts b/src/client/app/mios.ts index ba3f967a23..9a8d19adbd 100644 --- a/src/client/app/mios.ts +++ b/src/client/app/mios.ts @@ -11,7 +11,7 @@ import { DriveStreamManager } from './common/scripts/streaming/drive'; import { ServerStatsStreamManager } from './common/scripts/streaming/server-stats'; import { NotesStatsStreamManager } from './common/scripts/streaming/notes-stats'; import { MessagingIndexStreamManager } from './common/scripts/streaming/messaging-index'; -import { OthelloStreamManager } from './common/scripts/streaming/othello'; +import { ReversiStreamManager } from './common/scripts/streaming/reversi'; import Err from './common/views/components/connect-failed.vue'; import { LocalTimelineStreamManager } from './common/scripts/streaming/local-timeline'; @@ -108,7 +108,7 @@ export default class MiOS extends EventEmitter { serverStatsStream: ServerStatsStreamManager; notesStatsStream: NotesStatsStreamManager; messagingIndexStream: MessagingIndexStreamManager; - othelloStream: OthelloStreamManager; + reversiStream: ReversiStreamManager; } = { localTimelineStream: null, globalTimelineStream: null, @@ -116,7 +116,7 @@ export default class MiOS extends EventEmitter { serverStatsStream: null, notesStatsStream: null, messagingIndexStream: null, - othelloStream: null + reversiStream: null }; /** @@ -233,7 +233,7 @@ export default class MiOS extends EventEmitter { this.streams.globalTimelineStream = new GlobalTimelineStreamManager(this, this.store.state.i); this.streams.driveStream = new DriveStreamManager(this, this.store.state.i); this.streams.messagingIndexStream = new MessagingIndexStreamManager(this, this.store.state.i); - this.streams.othelloStream = new OthelloStreamManager(this, this.store.state.i); + this.streams.reversiStream = new ReversiStreamManager(this, this.store.state.i); }); //#endregion diff --git a/src/client/app/mobile/script.ts b/src/client/app/mobile/script.ts index 1572fd73ed..cc0a8331ba 100644 --- a/src/client/app/mobile/script.ts +++ b/src/client/app/mobile/script.ts @@ -35,7 +35,7 @@ import MkFavorites from './views/pages/favorites.vue'; import MkUserLists from './views/pages/user-lists.vue'; import MkUserList from './views/pages/user-list.vue'; import MkSettings from './views/pages/settings.vue'; -import MkOthello from './views/pages/othello.vue'; +import MkReversi from './views/pages/reversi.vue'; import MkTag from './views/pages/tag.vue'; import MkShare from './views/pages/share.vue'; @@ -75,8 +75,8 @@ init((launch) => { { path: '/search', component: MkSearch }, { path: '/tags/:tag', component: MkTag }, { path: '/share', component: MkShare }, - { path: '/othello', name: 'othello', component: MkOthello }, - { path: '/othello/:game', component: MkOthello }, + { path: '/reversi', name: 'reversi', component: MkReversi }, + { path: '/reversi/:game', component: MkReversi }, { path: '/@:user', component: MkUser }, { path: '/@:user/followers', component: MkFollowers }, { path: '/@:user/following', component: MkFollowing }, diff --git a/src/client/app/mobile/views/components/ui.header.vue b/src/client/app/mobile/views/components/ui.header.vue index c72b15fb8b..c1ee70d105 100644 --- a/src/client/app/mobile/views/components/ui.header.vue +++ b/src/client/app/mobile/views/components/ui.header.vue @@ -45,8 +45,8 @@ export default Vue.extend({ this.connection = (this as any).os.stream.getConnection(); this.connectionId = (this as any).os.stream.use(); - this.connection.on('othello_invited', this.onOthelloInvited); - this.connection.on('othello_no_invites', this.onOthelloNoInvites); + this.connection.on('reversi_invited', this.onReversiInvited); + this.connection.on('reversi_no_invites', this.onReversiNoInvites); const ago = (new Date().getTime() - new Date(this.$store.state.i.lastUsedAt).getTime()) / 1000; const isHisasiburi = ago >= 3600; @@ -98,16 +98,16 @@ export default Vue.extend({ }, beforeDestroy() { if (this.$store.getters.isSignedIn) { - this.connection.off('othello_invited', this.onOthelloInvited); - this.connection.off('othello_no_invites', this.onOthelloNoInvites); + this.connection.off('reversi_invited', this.onReversiInvited); + this.connection.off('reversi_no_invites', this.onReversiNoInvites); (this as any).os.stream.dispose(this.connectionId); } }, methods: { - onOthelloInvited() { + onReversiInvited() { this.hasGameInvitation = true; }, - onOthelloNoInvites() { + onReversiNoInvites() { this.hasGameInvitation = false; } } diff --git a/src/client/app/mobile/views/components/ui.nav.vue b/src/client/app/mobile/views/components/ui.nav.vue index 80f60e4232..bb7a2f558c 100644 --- a/src/client/app/mobile/views/components/ui.nav.vue +++ b/src/client/app/mobile/views/components/ui.nav.vue @@ -19,7 +19,7 @@
  • %fa:R bell%%i18n:@notifications%%fa:angle-right%
  • %fa:R comments%%i18n:@messaging%%fa:angle-right%
  • %fa:R envelope%%i18n:@follow-requests%%fa:angle-right%
  • -
  • %fa:gamepad%%i18n:@game%%fa:angle-right%
  • +
  • %fa:gamepad%%i18n:@game%%fa:angle-right%
    • %fa:R calendar-alt%%i18n:@widgets%%fa:angle-right%
    • @@ -66,14 +66,14 @@ export default Vue.extend({ this.connection = (this as any).os.stream.getConnection(); this.connectionId = (this as any).os.stream.use(); - this.connection.on('othello_invited', this.onOthelloInvited); - this.connection.on('othello_no_invites', this.onOthelloNoInvites); + this.connection.on('reversi_invited', this.onReversiInvited); + this.connection.on('reversi_no_invites', this.onReversiNoInvites); } }, beforeDestroy() { if (this.$store.getters.isSignedIn) { - this.connection.off('othello_invited', this.onOthelloInvited); - this.connection.off('othello_no_invites', this.onOthelloNoInvites); + this.connection.off('reversi_invited', this.onReversiInvited); + this.connection.off('reversi_no_invites', this.onReversiNoInvites); (this as any).os.stream.dispose(this.connectionId); } }, @@ -83,10 +83,10 @@ export default Vue.extend({ if (query == null || query == '') return; this.$router.push('/search?q=' + encodeURIComponent(query)); }, - onOthelloInvited() { + onReversiInvited() { this.hasGameInvitation = true; }, - onOthelloNoInvites() { + onReversiNoInvites() { this.hasGameInvitation = false; }, dark() { diff --git a/src/client/app/mobile/views/pages/othello.vue b/src/client/app/mobile/views/pages/othello.vue deleted file mode 100644 index e04e583c20..0000000000 --- a/src/client/app/mobile/views/pages/othello.vue +++ /dev/null @@ -1,50 +0,0 @@ - - - diff --git a/src/client/app/mobile/views/pages/reversi.vue b/src/client/app/mobile/views/pages/reversi.vue new file mode 100644 index 0000000000..e2f0db6d87 --- /dev/null +++ b/src/client/app/mobile/views/pages/reversi.vue @@ -0,0 +1,50 @@ + + + diff --git a/src/client/assets/othello-put-me.mp3 b/src/client/assets/othello-put-me.mp3 deleted file mode 100644 index 4e0e72091c..0000000000 Binary files a/src/client/assets/othello-put-me.mp3 and /dev/null differ diff --git a/src/client/assets/othello-put-you.mp3 b/src/client/assets/othello-put-you.mp3 deleted file mode 100644 index 9244189c2d..0000000000 Binary files a/src/client/assets/othello-put-you.mp3 and /dev/null differ diff --git a/src/client/assets/reversi-put-me.mp3 b/src/client/assets/reversi-put-me.mp3 new file mode 100644 index 0000000000..4e0e72091c Binary files /dev/null and b/src/client/assets/reversi-put-me.mp3 differ diff --git a/src/client/assets/reversi-put-you.mp3 b/src/client/assets/reversi-put-you.mp3 new file mode 100644 index 0000000000..9244189c2d Binary files /dev/null and b/src/client/assets/reversi-put-you.mp3 differ diff --git a/src/config/types.ts b/src/config/types.ts index 62f63d4a3c..49eeac508b 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -60,7 +60,7 @@ export type Source = { hook_secret: string; username: string; }; - othello_ai?: { + reversi_ai?: { id: string; i: string; }; diff --git a/src/models/othello-game.ts b/src/models/othello-game.ts deleted file mode 100644 index 1dd375d2e5..0000000000 --- a/src/models/othello-game.ts +++ /dev/null @@ -1,109 +0,0 @@ -import * as mongo from 'mongodb'; -import * as deepcopy from 'deepcopy'; -import db from '../db/mongodb'; -import { IUser, pack as packUser } from './user'; - -const OthelloGame = db.get('othelloGames'); -export default OthelloGame; - -export interface IOthelloGame { - _id: mongo.ObjectID; - createdAt: Date; - startedAt: Date; - user1Id: mongo.ObjectID; - user2Id: mongo.ObjectID; - user1Accepted: boolean; - user2Accepted: boolean; - - /** - * どちらのプレイヤーが先行(黒)か - * 1 ... user1 - * 2 ... user2 - */ - black: number; - - isStarted: boolean; - isEnded: boolean; - winnerId: mongo.ObjectID; - logs: Array<{ - at: Date; - color: boolean; - pos: number; - }>; - settings: { - map: string[]; - bw: string | number; - isLlotheo: boolean; - canPutEverywhere: boolean; - loopedBoard: boolean; - }; - form1: any; - form2: any; - - // ログのposを文字列としてすべて連結したもののCRC32値 - crc32: string; -} - -/** - * Pack an othello game for API response - */ -export const pack = ( - game: any, - me?: string | mongo.ObjectID | IUser, - options?: { - detail?: boolean - } -) => new Promise(async (resolve, reject) => { - const opts = Object.assign({ - detail: true - }, options); - - let _game: any; - - // Populate the game if 'game' is ID - if (mongo.ObjectID.prototype.isPrototypeOf(game)) { - _game = await OthelloGame.findOne({ - _id: game - }); - } else if (typeof game === 'string') { - _game = await OthelloGame.findOne({ - _id: new mongo.ObjectID(game) - }); - } else { - _game = deepcopy(game); - } - - // Me - const meId: mongo.ObjectID = me - ? mongo.ObjectID.prototype.isPrototypeOf(me) - ? me as mongo.ObjectID - : typeof me === 'string' - ? new mongo.ObjectID(me) - : (me as IUser)._id - : null; - - // Rename _id to id - _game.id = _game._id; - delete _game._id; - - if (opts.detail === false) { - delete _game.logs; - delete _game.settings.map; - } else { - // 互換性のため - if (_game.settings.map.hasOwnProperty('size')) { - _game.settings.map = _game.settings.map.data.match(new RegExp(`.{1,${_game.settings.map.size}}`, 'g')); - } - } - - // Populate user - _game.user1 = await packUser(_game.user1Id, meId); - _game.user2 = await packUser(_game.user2Id, meId); - if (_game.winnerId) { - _game.winner = await packUser(_game.winnerId, meId); - } else { - _game.winner = null; - } - - resolve(_game); -}); diff --git a/src/models/othello-matching.ts b/src/models/othello-matching.ts deleted file mode 100644 index 0efba3ae5d..0000000000 --- a/src/models/othello-matching.ts +++ /dev/null @@ -1,44 +0,0 @@ -import * as mongo from 'mongodb'; -import * as deepcopy from 'deepcopy'; -import db from '../db/mongodb'; -import { IUser, pack as packUser } from './user'; - -const Matching = db.get('othelloMatchings'); -export default Matching; - -export interface IMatching { - _id: mongo.ObjectID; - createdAt: Date; - parentId: mongo.ObjectID; - childId: mongo.ObjectID; -} - -/** - * Pack an othello matching for API response - */ -export const pack = ( - matching: any, - me?: string | mongo.ObjectID | IUser -) => new Promise(async (resolve, reject) => { - - // Me - const meId: mongo.ObjectID = me - ? mongo.ObjectID.prototype.isPrototypeOf(me) - ? me as mongo.ObjectID - : typeof me === 'string' - ? new mongo.ObjectID(me) - : (me as IUser)._id - : null; - - const _matching = deepcopy(matching); - - // Rename _id to id - _matching.id = _matching._id; - delete _matching._id; - - // Populate user - _matching.parent = await packUser(_matching.parentId, meId); - _matching.child = await packUser(_matching.childId, meId); - - resolve(_matching); -}); diff --git a/src/models/reversi-game.ts b/src/models/reversi-game.ts new file mode 100644 index 0000000000..16b9ab0d4e --- /dev/null +++ b/src/models/reversi-game.ts @@ -0,0 +1,109 @@ +import * as mongo from 'mongodb'; +import * as deepcopy from 'deepcopy'; +import db from '../db/mongodb'; +import { IUser, pack as packUser } from './user'; + +const ReversiGame = db.get('reversiGames'); +export default ReversiGame; + +export interface IReversiGame { + _id: mongo.ObjectID; + createdAt: Date; + startedAt: Date; + user1Id: mongo.ObjectID; + user2Id: mongo.ObjectID; + user1Accepted: boolean; + user2Accepted: boolean; + + /** + * どちらのプレイヤーが先行(黒)か + * 1 ... user1 + * 2 ... user2 + */ + black: number; + + isStarted: boolean; + isEnded: boolean; + winnerId: mongo.ObjectID; + logs: Array<{ + at: Date; + color: boolean; + pos: number; + }>; + settings: { + map: string[]; + bw: string | number; + isLlotheo: boolean; + canPutEverywhere: boolean; + loopedBoard: boolean; + }; + form1: any; + form2: any; + + // ログのposを文字列としてすべて連結したもののCRC32値 + crc32: string; +} + +/** + * Pack an reversi game for API response + */ +export const pack = ( + game: any, + me?: string | mongo.ObjectID | IUser, + options?: { + detail?: boolean + } +) => new Promise(async (resolve, reject) => { + const opts = Object.assign({ + detail: true + }, options); + + let _game: any; + + // Populate the game if 'game' is ID + if (mongo.ObjectID.prototype.isPrototypeOf(game)) { + _game = await ReversiGame.findOne({ + _id: game + }); + } else if (typeof game === 'string') { + _game = await ReversiGame.findOne({ + _id: new mongo.ObjectID(game) + }); + } else { + _game = deepcopy(game); + } + + // Me + const meId: mongo.ObjectID = me + ? mongo.ObjectID.prototype.isPrototypeOf(me) + ? me as mongo.ObjectID + : typeof me === 'string' + ? new mongo.ObjectID(me) + : (me as IUser)._id + : null; + + // Rename _id to id + _game.id = _game._id; + delete _game._id; + + if (opts.detail === false) { + delete _game.logs; + delete _game.settings.map; + } else { + // 互換性のため + if (_game.settings.map.hasOwnProperty('size')) { + _game.settings.map = _game.settings.map.data.match(new RegExp(`.{1,${_game.settings.map.size}}`, 'g')); + } + } + + // Populate user + _game.user1 = await packUser(_game.user1Id, meId); + _game.user2 = await packUser(_game.user2Id, meId); + if (_game.winnerId) { + _game.winner = await packUser(_game.winnerId, meId); + } else { + _game.winner = null; + } + + resolve(_game); +}); diff --git a/src/models/reversi-matching.ts b/src/models/reversi-matching.ts new file mode 100644 index 0000000000..5efa5d76ad --- /dev/null +++ b/src/models/reversi-matching.ts @@ -0,0 +1,44 @@ +import * as mongo from 'mongodb'; +import * as deepcopy from 'deepcopy'; +import db from '../db/mongodb'; +import { IUser, pack as packUser } from './user'; + +const Matching = db.get('reversiMatchings'); +export default Matching; + +export interface IMatching { + _id: mongo.ObjectID; + createdAt: Date; + parentId: mongo.ObjectID; + childId: mongo.ObjectID; +} + +/** + * Pack an reversi matching for API response + */ +export const pack = ( + matching: any, + me?: string | mongo.ObjectID | IUser +) => new Promise(async (resolve, reject) => { + + // Me + const meId: mongo.ObjectID = me + ? mongo.ObjectID.prototype.isPrototypeOf(me) + ? me as mongo.ObjectID + : typeof me === 'string' + ? new mongo.ObjectID(me) + : (me as IUser)._id + : null; + + const _matching = deepcopy(matching); + + // Rename _id to id + _matching.id = _matching._id; + delete _matching._id; + + // Populate user + _matching.parent = await packUser(_matching.parentId, meId); + _matching.child = await packUser(_matching.childId, meId); + + resolve(_matching); +}); diff --git a/src/othello/ai/back.ts b/src/othello/ai/back.ts deleted file mode 100644 index e4d0cfdd33..0000000000 --- a/src/othello/ai/back.ts +++ /dev/null @@ -1,377 +0,0 @@ -/** - * -AI- - * Botのバックエンド(思考を担当) - * - * 対話と思考を同じプロセスで行うと、思考時間が長引いたときにストリームから - * 切断されてしまうので、別々のプロセスで行うようにします - */ - -import * as request from 'request-promise-native'; -import Othello, { Color } from '../core'; -import conf from '../../config'; -import getUserName from '../../renderers/get-user-name'; - -let game; -let form; - -/** - * BotアカウントのユーザーID - */ -const id = conf.othello_ai.id; - -/** - * BotアカウントのAPIキー - */ -const i = conf.othello_ai.i; - -let note; - -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 - ? `?[${getUserName(user)}](${conf.url}/@${user.username})さんの接待を始めました!` - : `対局を?[${getUserName(user)}](${conf.url}/@${user.username})さんと始めました! (強さ${form[0].value})`; - - const res = await request.post(`${conf.api_url}/notes/create`, { - json: { i, - text: `${text}\n→[観戦する](${url})` - } - }); - - note = res.createdNote; - //#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 - ? `?[${getUserName(user)}](${conf.url}/@${user.username})さんに接待で引き分けました...` - : msg.body.winnerId == id - ? `?[${getUserName(user)}](${conf.url}/@${user.username})さんに接待で勝ってしまいました...` - : `?[${getUserName(user)}](${conf.url}/@${user.username})さんに接待で負けてあげました♪` - : msg.body.winnerId === null - ? `?[${getUserName(user)}](${conf.url}/@${user.username})さんと引き分けました~` - : msg.body.winnerId == id - ? `?[${getUserName(user)}](${conf.url}/@${user.username})さんに勝ちました♪` - : `?[${getUserName(user)}](${conf.url}/@${user.username})さんに負けました...`; - - await request.post(`${conf.api_url}/notes/create`, { - json: { i, - renoteId: note.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/othello/ai/front.ts b/src/othello/ai/front.ts deleted file mode 100644 index ff74b7216e..0000000000 --- a/src/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 '../../config'; - -// 設定 //////////////////////////////////////////////////////// - -/** - * 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 note = msg.body; - - if (note.userId == id) return; - - // リアクションする - request.post(`${conf.api_url}/notes/reactions/create`, { - json: { i, - noteId: note.id, - reaction: 'love' - } - }); - - if (note.text) { - if (note.text.indexOf('オセロ') > -1) { - request.post(`${conf.api_url}/notes/create`, { - json: { i, - replyId: note.id, - text: '良いですよ~' - } - }); - - invite(note.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/othello/ai/index.ts b/src/othello/ai/index.ts deleted file mode 100644 index 5cd1db82da..0000000000 --- a/src/othello/ai/index.ts +++ /dev/null @@ -1 +0,0 @@ -require('./front'); diff --git a/src/othello/core.ts b/src/othello/core.ts deleted file mode 100644 index 217066d375..0000000000 --- a/src/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/othello/maps.ts b/src/othello/maps.ts deleted file mode 100644 index 68e5a446f1..0000000000 --- a/src/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/publishers/stream.ts b/src/publishers/stream.ts index 58a6ef49aa..b573b65a65 100644 --- a/src/publishers/stream.ts +++ b/src/publishers/stream.ts @@ -37,12 +37,12 @@ class MisskeyEvent { this.publish(`messaging-index-stream:${userId}`, type, typeof value === 'undefined' ? null : value); } - public publishOthelloStream(userId: ID, type: string, value?: any): void { - this.publish(`othello-stream:${userId}`, type, typeof value === 'undefined' ? null : value); + public publishReversiStream(userId: ID, type: string, value?: any): void { + this.publish(`reversi-stream:${userId}`, type, typeof value === 'undefined' ? null : value); } - public publishOthelloGameStream(gameId: ID, type: string, value?: any): void { - this.publish(`othello-game-stream:${gameId}`, type, typeof value === 'undefined' ? null : value); + public publishReversiGameStream(gameId: ID, type: string, value?: any): void { + this.publish(`reversi-game-stream:${gameId}`, type, typeof value === 'undefined' ? null : value); } public publishLocalTimelineStream(note: any): void { @@ -73,5 +73,5 @@ export const publishUserListStream = ev.publishUserListStream.bind(ev); export const publishNoteStream = ev.publishNoteStream.bind(ev); export const publishMessagingStream = ev.publishMessagingStream.bind(ev); export const publishMessagingIndexStream = ev.publishMessagingIndexStream.bind(ev); -export const publishOthelloStream = ev.publishOthelloStream.bind(ev); -export const publishOthelloGameStream = ev.publishOthelloGameStream.bind(ev); +export const publishReversiStream = ev.publishReversiStream.bind(ev); +export const publishReversiGameStream = ev.publishReversiGameStream.bind(ev); diff --git a/src/reversi/ai/back.ts b/src/reversi/ai/back.ts new file mode 100644 index 0000000000..42f1a0f4b8 --- /dev/null +++ b/src/reversi/ai/back.ts @@ -0,0 +1,377 @@ +/** + * -AI- + * Botのバックエンド(思考を担当) + * + * 対話と思考を同じプロセスで行うと、思考時間が長引いたときにストリームから + * 切断されてしまうので、別々のプロセスで行うようにします + */ + +import * as request from 'request-promise-native'; +import Reversi, { Color } from '../core'; +import conf from '../../config'; +import getUserName from '../../renderers/get-user-name'; + +let game; +let form; + +/** + * BotアカウントのユーザーID + */ +const id = conf.reversi_ai.id; + +/** + * BotアカウントのAPIキー + */ +const i = conf.reversi_ai.i; + +let note; + +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}/reversi/${game.id}`; + const user = game.user1Id == id ? game.user2 : game.user1; + const isSettai = form[0].value === 0; + const text = isSettai + ? `?[${getUserName(user)}](${conf.url}/@${user.username})さんの接待を始めました!` + : `対局を?[${getUserName(user)}](${conf.url}/@${user.username})さんと始めました! (強さ${form[0].value})`; + + const res = await request.post(`${conf.api_url}/notes/create`, { + json: { i, + text: `${text}\n→[観戦する](${url})` + } + }); + + note = res.createdNote; + //#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 + ? `?[${getUserName(user)}](${conf.url}/@${user.username})さんに接待で引き分けました...` + : msg.body.winnerId == id + ? `?[${getUserName(user)}](${conf.url}/@${user.username})さんに接待で勝ってしまいました...` + : `?[${getUserName(user)}](${conf.url}/@${user.username})さんに接待で負けてあげました♪` + : msg.body.winnerId === null + ? `?[${getUserName(user)}](${conf.url}/@${user.username})さんと引き分けました~` + : msg.body.winnerId == id + ? `?[${getUserName(user)}](${conf.url}/@${user.username})さんに勝ちました♪` + : `?[${getUserName(user)}](${conf.url}/@${user.username})さんに負けました...`; + + await request.post(`${conf.api_url}/notes/create`, { + json: { i, + renoteId: note.id, + text: text + } + }); + //#endregion + + process.exit(); + } + + // 打たれたとき + if (msg.type == 'set') { + onSet(msg.body); + } +}); + +let o: Reversi; +let botColor: Color; + +// 各マスの強さ +let cellWeights; + +/** + * ゲーム開始時 + * @param g ゲーム情報 + */ +function onGameStarted(g) { + game = g; + + // リバーシエンジン初期化 + o = new Reversi(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/reversi/ai/front.ts b/src/reversi/ai/front.ts new file mode 100644 index 0000000000..25ee43225a --- /dev/null +++ b/src/reversi/ai/front.ts @@ -0,0 +1,233 @@ +/** + * -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 '../../config'; + +// 設定 //////////////////////////////////////////////////////// + +/** + * BotアカウントのAPIキー + */ +const i = conf.reversi_ai.i; + +/** + * BotアカウントのユーザーID + */ +const id = conf.reversi_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 note = msg.body; + + if (note.userId == id) return; + + // リアクションする + request.post(`${conf.api_url}/notes/reactions/create`, { + json: { i, + noteId: note.id, + reaction: 'love' + } + }); + + if (note.text) { + if (note.text.indexOf('リバーシ') > -1) { + request.post(`${conf.api_url}/notes/create`, { + json: { i, + replyId: note.id, + text: '良いですよ~' + } + }); + + invite(note.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}/reversi/match`, { + json: { i, + userId: userId + } + }); +} + +/** + * リバーシストリーム + */ +const reversiStream = new ReconnectingWebSocket(`${conf.ws_url}/reversi?i=${i}`, undefined, { + constructor: WebSocket +}); + +reversiStream.on('open', () => { + console.log('reversi stream opened'); +}); + +reversiStream.on('close', () => { + console.log('reversi stream closed'); +}); + +reversiStream.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}/reversi-game?i=${i}&game=${game.id}`, undefined, { + constructor: WebSocket + }); + + gw.on('open', () => { + console.log('reversi 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('reversi game stream closed'); + }); +} + +/** + * リバーシの対局に招待されたとき + * @param inviter 誘ってきたユーザー + */ +async function onInviteMe(inviter) { + console.log(`Someone invited me: @${inviter.username}`); + + // 承認 + const game = await request.post(`${conf.api_url}/reversi/match`, { + json: { + i, + userId: inviter.id + } + }); + + gameStart(game); +} diff --git a/src/reversi/ai/index.ts b/src/reversi/ai/index.ts new file mode 100644 index 0000000000..5cd1db82da --- /dev/null +++ b/src/reversi/ai/index.ts @@ -0,0 +1 @@ +require('./front'); diff --git a/src/reversi/core.ts b/src/reversi/core.ts new file mode 100644 index 0000000000..f1f9de1486 --- /dev/null +++ b/src/reversi/core.ts @@ -0,0 +1,340 @@ +/** + * 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 Reversi { + 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/reversi/maps.ts b/src/reversi/maps.ts new file mode 100644 index 0000000000..f55cb1d516 --- /dev/null +++ b/src/reversi/maps.ts @@ -0,0 +1,911 @@ +/** + * 組み込みマップ定義 + * + * データ値: + * (スペース) ... マス無し + * - ... マス + * 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/reversi/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/server/api/endpoints.ts b/src/server/api/endpoints.ts index 5f0a020d6f..a5d13b0237 100644 --- a/src/server/api/endpoints.ts +++ b/src/server/api/endpoints.ts @@ -245,27 +245,27 @@ const endpoints: Endpoint[] = [ }, { - name: 'othello/match', + name: 'reversi/match', withCredential: true }, { - name: 'othello/match/cancel', + name: 'reversi/match/cancel', withCredential: true }, { - name: 'othello/invitations', + name: 'reversi/invitations', withCredential: true }, { - name: 'othello/games', + name: 'reversi/games', withCredential: true }, { - name: 'othello/games/show' + name: 'reversi/games/show' }, { diff --git a/src/server/api/endpoints/othello/games.ts b/src/server/api/endpoints/othello/games.ts deleted file mode 100644 index 2320a34b04..0000000000 --- a/src/server/api/endpoints/othello/games.ts +++ /dev/null @@ -1,62 +0,0 @@ -import $ from 'cafy'; import ID from '../../../../cafy-id'; -import OthelloGame, { pack } from '../../../../models/othello-game'; - -module.exports = (params, user) => new Promise(async (res, rej) => { - // Get 'my' parameter - const [my = false, myErr] = $.bool.optional().get(params.my); - if (myErr) return rej('invalid my param'); - - // Get 'limit' parameter - const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); - if (limitErr) return rej('invalid limit param'); - - // Get 'sinceId' parameter - const [sinceId, sinceIdErr] = $.type(ID).optional().get(params.sinceId); - if (sinceIdErr) return rej('invalid sinceId param'); - - // Get 'untilId' parameter - const [untilId, untilIdErr] = $.type(ID).optional().get(params.untilId); - if (untilIdErr) return rej('invalid untilId param'); - - // Check if both of sinceId and untilId is specified - if (sinceId && untilId) { - return rej('cannot set sinceId and untilId'); - } - - const q: any = my ? { - isStarted: true, - $or: [{ - user1Id: user._id - }, { - user2Id: user._id - }] - } : { - isStarted: true - }; - - const sort = { - _id: -1 - }; - - if (sinceId) { - sort._id = 1; - q._id = { - $gt: sinceId - }; - } else if (untilId) { - q._id = { - $lt: untilId - }; - } - - // Fetch games - const games = await OthelloGame.find(q, { - sort, - limit - }); - - // Reponse - res(Promise.all(games.map(async (g) => await pack(g, user, { - detail: false - })))); -}); diff --git a/src/server/api/endpoints/othello/games/show.ts b/src/server/api/endpoints/othello/games/show.ts deleted file mode 100644 index 6b2f5ce137..0000000000 --- a/src/server/api/endpoints/othello/games/show.ts +++ /dev/null @@ -1,32 +0,0 @@ -import $ from 'cafy'; import ID from '../../../../../cafy-id'; -import OthelloGame, { pack } from '../../../../../models/othello-game'; -import Othello from '../../../../../othello/core'; - -module.exports = (params, user) => new Promise(async (res, rej) => { - // Get 'gameId' parameter - const [gameId, gameIdErr] = $.type(ID).get(params.gameId); - if (gameIdErr) return rej('invalid gameId param'); - - const game = await OthelloGame.findOne({ _id: gameId }); - - if (game == null) { - return rej('game not found'); - } - - const o = new Othello(game.settings.map, { - isLlotheo: game.settings.isLlotheo, - canPutEverywhere: game.settings.canPutEverywhere, - loopedBoard: game.settings.loopedBoard - }); - - game.logs.forEach(log => { - o.put(log.color, log.pos); - }); - - const packed = await pack(game, user); - - res(Object.assign({ - board: o.board, - turn: o.turn - }, packed)); -}); diff --git a/src/server/api/endpoints/othello/invitations.ts b/src/server/api/endpoints/othello/invitations.ts deleted file mode 100644 index 4761537614..0000000000 --- a/src/server/api/endpoints/othello/invitations.ts +++ /dev/null @@ -1,15 +0,0 @@ -import Matching, { pack as packMatching } from '../../../../models/othello-matching'; - -module.exports = (params, user) => new Promise(async (res, rej) => { - // Find session - const invitations = await Matching.find({ - childId: user._id - }, { - sort: { - _id: -1 - } - }); - - // Reponse - res(Promise.all(invitations.map(async (i) => await packMatching(i, user)))); -}); diff --git a/src/server/api/endpoints/othello/match.ts b/src/server/api/endpoints/othello/match.ts deleted file mode 100644 index e70e579755..0000000000 --- a/src/server/api/endpoints/othello/match.ts +++ /dev/null @@ -1,95 +0,0 @@ -import $ from 'cafy'; import ID from '../../../../cafy-id'; -import Matching, { pack as packMatching } from '../../../../models/othello-matching'; -import OthelloGame, { pack as packGame } from '../../../../models/othello-game'; -import User from '../../../../models/user'; -import publishUserStream, { publishOthelloStream } from '../../../../publishers/stream'; -import { eighteight } from '../../../../othello/maps'; - -module.exports = (params, user) => new Promise(async (res, rej) => { - // Get 'userId' parameter - const [childId, childIdErr] = $.type(ID).get(params.userId); - if (childIdErr) return rej('invalid userId param'); - - // Myself - if (childId.equals(user._id)) { - return rej('invalid userId param'); - } - - // Find session - const exist = await Matching.findOne({ - parentId: childId, - childId: user._id - }); - - if (exist) { - // Destroy session - Matching.remove({ - _id: exist._id - }); - - // Create game - const game = await OthelloGame.insert({ - createdAt: new Date(), - user1Id: exist.parentId, - user2Id: user._id, - user1Accepted: false, - user2Accepted: false, - isStarted: false, - isEnded: false, - logs: [], - settings: { - map: eighteight.data, - bw: 'random', - isLlotheo: false - } - }); - - // Reponse - res(await packGame(game, user)); - - publishOthelloStream(exist.parentId, 'matched', await packGame(game, exist.parentId)); - - const other = await Matching.count({ - childId: user._id - }); - - if (other == 0) { - publishUserStream(user._id, 'othello_no_invites'); - } - } else { - // Fetch child - const child = await User.findOne({ - _id: childId - }, { - fields: { - _id: true - } - }); - - if (child === null) { - return rej('user not found'); - } - - // 以前のセッションはすべて削除しておく - await Matching.remove({ - parentId: user._id - }); - - // セッションを作成 - const matching = await Matching.insert({ - createdAt: new Date(), - parentId: user._id, - childId: child._id - }); - - // Reponse - res(); - - const packed = await packMatching(matching, child); - - // 招待 - publishOthelloStream(child._id, 'invited', packed); - - publishUserStream(child._id, 'othello_invited', packed); - } -}); diff --git a/src/server/api/endpoints/othello/match/cancel.ts b/src/server/api/endpoints/othello/match/cancel.ts deleted file mode 100644 index 562e691061..0000000000 --- a/src/server/api/endpoints/othello/match/cancel.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Matching from '../../../../../models/othello-matching'; - -module.exports = (params, user) => new Promise(async (res, rej) => { - await Matching.remove({ - parentId: user._id - }); - - res(); -}); diff --git a/src/server/api/endpoints/reversi/games.ts b/src/server/api/endpoints/reversi/games.ts new file mode 100644 index 0000000000..9d879ecf23 --- /dev/null +++ b/src/server/api/endpoints/reversi/games.ts @@ -0,0 +1,62 @@ +import $ from 'cafy'; import ID from '../../../../cafy-id'; +import ReversiGame, { pack } from '../../../../models/reversi-game'; + +module.exports = (params, user) => new Promise(async (res, rej) => { + // Get 'my' parameter + const [my = false, myErr] = $.bool.optional().get(params.my); + if (myErr) return rej('invalid my param'); + + // Get 'limit' parameter + const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); + if (limitErr) return rej('invalid limit param'); + + // Get 'sinceId' parameter + const [sinceId, sinceIdErr] = $.type(ID).optional().get(params.sinceId); + if (sinceIdErr) return rej('invalid sinceId param'); + + // Get 'untilId' parameter + const [untilId, untilIdErr] = $.type(ID).optional().get(params.untilId); + if (untilIdErr) return rej('invalid untilId param'); + + // Check if both of sinceId and untilId is specified + if (sinceId && untilId) { + return rej('cannot set sinceId and untilId'); + } + + const q: any = my ? { + isStarted: true, + $or: [{ + user1Id: user._id + }, { + user2Id: user._id + }] + } : { + isStarted: true + }; + + const sort = { + _id: -1 + }; + + if (sinceId) { + sort._id = 1; + q._id = { + $gt: sinceId + }; + } else if (untilId) { + q._id = { + $lt: untilId + }; + } + + // Fetch games + const games = await ReversiGame.find(q, { + sort, + limit + }); + + // Reponse + res(Promise.all(games.map(async (g) => await pack(g, user, { + detail: false + })))); +}); diff --git a/src/server/api/endpoints/reversi/games/show.ts b/src/server/api/endpoints/reversi/games/show.ts new file mode 100644 index 0000000000..f32eb23513 --- /dev/null +++ b/src/server/api/endpoints/reversi/games/show.ts @@ -0,0 +1,32 @@ +import $ from 'cafy'; import ID from '../../../../../cafy-id'; +import ReversiGame, { pack } from '../../../../../models/reversi-game'; +import Reversi from '../../../../../reversi/core'; + +module.exports = (params, user) => new Promise(async (res, rej) => { + // Get 'gameId' parameter + const [gameId, gameIdErr] = $.type(ID).get(params.gameId); + if (gameIdErr) return rej('invalid gameId param'); + + const game = await ReversiGame.findOne({ _id: gameId }); + + if (game == null) { + return rej('game not found'); + } + + const o = new Reversi(game.settings.map, { + isLlotheo: game.settings.isLlotheo, + canPutEverywhere: game.settings.canPutEverywhere, + loopedBoard: game.settings.loopedBoard + }); + + game.logs.forEach(log => { + o.put(log.color, log.pos); + }); + + const packed = await pack(game, user); + + res(Object.assign({ + board: o.board, + turn: o.turn + }, packed)); +}); diff --git a/src/server/api/endpoints/reversi/invitations.ts b/src/server/api/endpoints/reversi/invitations.ts new file mode 100644 index 0000000000..fc487205a9 --- /dev/null +++ b/src/server/api/endpoints/reversi/invitations.ts @@ -0,0 +1,15 @@ +import Matching, { pack as packMatching } from '../../../../models/reversi-matching'; + +module.exports = (params, user) => new Promise(async (res, rej) => { + // Find session + const invitations = await Matching.find({ + childId: user._id + }, { + sort: { + _id: -1 + } + }); + + // Reponse + res(Promise.all(invitations.map(async (i) => await packMatching(i, user)))); +}); diff --git a/src/server/api/endpoints/reversi/match.ts b/src/server/api/endpoints/reversi/match.ts new file mode 100644 index 0000000000..5a699ddbae --- /dev/null +++ b/src/server/api/endpoints/reversi/match.ts @@ -0,0 +1,95 @@ +import $ from 'cafy'; import ID from '../../../../cafy-id'; +import Matching, { pack as packMatching } from '../../../../models/reversi-matching'; +import ReversiGame, { pack as packGame } from '../../../../models/reversi-game'; +import User from '../../../../models/user'; +import publishUserStream, { publishReversiStream } from '../../../../publishers/stream'; +import { eighteight } from '../../../../reversi/maps'; + +module.exports = (params, user) => new Promise(async (res, rej) => { + // Get 'userId' parameter + const [childId, childIdErr] = $.type(ID).get(params.userId); + if (childIdErr) return rej('invalid userId param'); + + // Myself + if (childId.equals(user._id)) { + return rej('invalid userId param'); + } + + // Find session + const exist = await Matching.findOne({ + parentId: childId, + childId: user._id + }); + + if (exist) { + // Destroy session + Matching.remove({ + _id: exist._id + }); + + // Create game + const game = await ReversiGame.insert({ + createdAt: new Date(), + user1Id: exist.parentId, + user2Id: user._id, + user1Accepted: false, + user2Accepted: false, + isStarted: false, + isEnded: false, + logs: [], + settings: { + map: eighteight.data, + bw: 'random', + isLlotheo: false + } + }); + + // Reponse + res(await packGame(game, user)); + + publishReversiStream(exist.parentId, 'matched', await packGame(game, exist.parentId)); + + const other = await Matching.count({ + childId: user._id + }); + + if (other == 0) { + publishUserStream(user._id, 'reversi_no_invites'); + } + } else { + // Fetch child + const child = await User.findOne({ + _id: childId + }, { + fields: { + _id: true + } + }); + + if (child === null) { + return rej('user not found'); + } + + // 以前のセッションはすべて削除しておく + await Matching.remove({ + parentId: user._id + }); + + // セッションを作成 + const matching = await Matching.insert({ + createdAt: new Date(), + parentId: user._id, + childId: child._id + }); + + // Reponse + res(); + + const packed = await packMatching(matching, child); + + // 招待 + publishReversiStream(child._id, 'invited', packed); + + publishUserStream(child._id, 'reversi_invited', packed); + } +}); diff --git a/src/server/api/endpoints/reversi/match/cancel.ts b/src/server/api/endpoints/reversi/match/cancel.ts new file mode 100644 index 0000000000..bc8a4cd640 --- /dev/null +++ b/src/server/api/endpoints/reversi/match/cancel.ts @@ -0,0 +1,9 @@ +import Matching from '../../../../../models/reversi-matching'; + +module.exports = (params, user) => new Promise(async (res, rej) => { + await Matching.remove({ + parentId: user._id + }); + + res(); +}); diff --git a/src/server/api/stream/othello-game.ts b/src/server/api/stream/othello-game.ts deleted file mode 100644 index 841e542610..0000000000 --- a/src/server/api/stream/othello-game.ts +++ /dev/null @@ -1,333 +0,0 @@ -import * as websocket from 'websocket'; -import * as redis from 'redis'; -import * as CRC32 from 'crc-32'; -import OthelloGame, { pack } from '../../../models/othello-game'; -import { publishOthelloGameStream } from '../../../publishers/stream'; -import Othello from '../../../othello/core'; -import * as maps from '../../../othello/maps'; -import { ParsedUrlQuery } from 'querystring'; - -export default function(request: websocket.request, connection: websocket.connection, subscriber: redis.RedisClient, user?: any): void { - const q = request.resourceURL.query as ParsedUrlQuery; - const gameId = q.game; - - // Subscribe game stream - subscriber.subscribe(`misskey:othello-game-stream:${gameId}`); - subscriber.on('message', (_, data) => { - connection.send(data); - }); - - connection.on('message', async (data) => { - const msg = JSON.parse(data.utf8Data); - - switch (msg.type) { - case 'accept': - accept(true); - break; - - case 'cancel-accept': - accept(false); - break; - - case 'update-settings': - if (msg.settings == null) return; - updateSettings(msg.settings); - break; - - case 'init-form': - if (msg.body == null) return; - initForm(msg.body); - break; - - case 'update-form': - if (msg.id == null || msg.value === undefined) return; - updateForm(msg.id, msg.value); - break; - - case 'message': - if (msg.body == null) return; - message(msg.body); - break; - - case 'set': - if (msg.pos == null) return; - set(msg.pos); - break; - - case 'check': - if (msg.crc32 == null) return; - check(msg.crc32); - break; - } - }); - - async function updateSettings(settings) { - const game = await OthelloGame.findOne({ _id: gameId }); - - if (game.isStarted) return; - if (!game.user1Id.equals(user._id) && !game.user2Id.equals(user._id)) return; - if (game.user1Id.equals(user._id) && game.user1Accepted) return; - if (game.user2Id.equals(user._id) && game.user2Accepted) return; - - await OthelloGame.update({ _id: gameId }, { - $set: { - settings - } - }); - - publishOthelloGameStream(gameId, 'update-settings', settings); - } - - async function initForm(form) { - const game = await OthelloGame.findOne({ _id: gameId }); - - if (game.isStarted) return; - if (!game.user1Id.equals(user._id) && !game.user2Id.equals(user._id)) return; - - const set = game.user1Id.equals(user._id) ? { - form1: form - } : { - form2: form - }; - - await OthelloGame.update({ _id: gameId }, { - $set: set - }); - - publishOthelloGameStream(gameId, 'init-form', { - userId: user._id, - form - }); - } - - async function updateForm(id, value) { - const game = await OthelloGame.findOne({ _id: gameId }); - - if (game.isStarted) return; - if (!game.user1Id.equals(user._id) && !game.user2Id.equals(user._id)) return; - - const form = game.user1Id.equals(user._id) ? game.form2 : game.form1; - - const item = form.find(i => i.id == id); - - if (item == null) return; - - item.value = value; - - const set = game.user1Id.equals(user._id) ? { - form2: form - } : { - form1: form - }; - - await OthelloGame.update({ _id: gameId }, { - $set: set - }); - - publishOthelloGameStream(gameId, 'update-form', { - userId: user._id, - id, - value - }); - } - - async function message(message) { - message.id = Math.random(); - publishOthelloGameStream(gameId, 'message', { - userId: user._id, - message - }); - } - - async function accept(accept: boolean) { - const game = await OthelloGame.findOne({ _id: gameId }); - - if (game.isStarted) return; - - let bothAccepted = false; - - if (game.user1Id.equals(user._id)) { - await OthelloGame.update({ _id: gameId }, { - $set: { - user1Accepted: accept - } - }); - - publishOthelloGameStream(gameId, 'change-accepts', { - user1: accept, - user2: game.user2Accepted - }); - - if (accept && game.user2Accepted) bothAccepted = true; - } else if (game.user2Id.equals(user._id)) { - await OthelloGame.update({ _id: gameId }, { - $set: { - user2Accepted: accept - } - }); - - publishOthelloGameStream(gameId, 'change-accepts', { - user1: game.user1Accepted, - user2: accept - }); - - if (accept && game.user1Accepted) bothAccepted = true; - } else { - return; - } - - if (bothAccepted) { - // 3秒後、まだacceptされていたらゲーム開始 - setTimeout(async () => { - const freshGame = await OthelloGame.findOne({ _id: gameId }); - if (freshGame == null || freshGame.isStarted || freshGame.isEnded) return; - if (!freshGame.user1Accepted || !freshGame.user2Accepted) return; - - let bw: number; - if (freshGame.settings.bw == 'random') { - bw = Math.random() > 0.5 ? 1 : 2; - } else { - bw = freshGame.settings.bw as number; - } - - function getRandomMap() { - const mapCount = Object.entries(maps).length; - const rnd = Math.floor(Math.random() * mapCount); - return Object.entries(maps).find((x, i) => i == rnd)[1].data; - } - - const map = freshGame.settings.map != null ? freshGame.settings.map : getRandomMap(); - - await OthelloGame.update({ _id: gameId }, { - $set: { - startedAt: new Date(), - isStarted: true, - black: bw, - 'settings.map': map - } - }); - - //#region 盤面に最初から石がないなどして始まった瞬間に勝敗が決定する場合があるのでその処理 - const o = new Othello(map, { - isLlotheo: freshGame.settings.isLlotheo, - canPutEverywhere: freshGame.settings.canPutEverywhere, - loopedBoard: freshGame.settings.loopedBoard - }); - - if (o.isEnded) { - let winner; - if (o.winner === true) { - winner = freshGame.black == 1 ? freshGame.user1Id : freshGame.user2Id; - } else if (o.winner === false) { - winner = freshGame.black == 1 ? freshGame.user2Id : freshGame.user1Id; - } else { - winner = null; - } - - await OthelloGame.update({ - _id: gameId - }, { - $set: { - isEnded: true, - winnerId: winner - } - }); - - publishOthelloGameStream(gameId, 'ended', { - winnerId: winner, - game: await pack(gameId, user) - }); - } - //#endregion - - publishOthelloGameStream(gameId, 'started', await pack(gameId, user)); - }, 3000); - } - } - - // 石を打つ - async function set(pos) { - const game = await OthelloGame.findOne({ _id: gameId }); - - if (!game.isStarted) return; - if (game.isEnded) return; - if (!game.user1Id.equals(user._id) && !game.user2Id.equals(user._id)) return; - - const o = new Othello(game.settings.map, { - isLlotheo: game.settings.isLlotheo, - canPutEverywhere: game.settings.canPutEverywhere, - loopedBoard: game.settings.loopedBoard - }); - - game.logs.forEach(log => { - o.put(log.color, log.pos); - }); - - const myColor = - (game.user1Id.equals(user._id) && game.black == 1) || (game.user2Id.equals(user._id) && game.black == 2) - ? true - : false; - - if (!o.canPut(myColor, pos)) return; - o.put(myColor, pos); - - let winner; - if (o.isEnded) { - if (o.winner === true) { - winner = game.black == 1 ? game.user1Id : game.user2Id; - } else if (o.winner === false) { - winner = game.black == 1 ? game.user2Id : game.user1Id; - } else { - winner = null; - } - } - - const log = { - at: new Date(), - color: myColor, - pos - }; - - const crc32 = CRC32.str(game.logs.map(x => x.pos.toString()).join('') + pos.toString()); - - await OthelloGame.update({ - _id: gameId - }, { - $set: { - crc32, - isEnded: o.isEnded, - winnerId: winner - }, - $push: { - logs: log - } - }); - - publishOthelloGameStream(gameId, 'set', Object.assign(log, { - next: o.turn - })); - - if (o.isEnded) { - publishOthelloGameStream(gameId, 'ended', { - winnerId: winner, - game: await pack(gameId, user) - }); - } - } - - async function check(crc32) { - const game = await OthelloGame.findOne({ _id: gameId }); - - if (!game.isStarted) return; - - // 互換性のため - if (game.crc32 == null) return; - - if (crc32 !== game.crc32) { - connection.send(JSON.stringify({ - type: 'rescue', - body: await pack(game, user) - })); - } - } -} diff --git a/src/server/api/stream/othello.ts b/src/server/api/stream/othello.ts deleted file mode 100644 index fa62b05836..0000000000 --- a/src/server/api/stream/othello.ts +++ /dev/null @@ -1,29 +0,0 @@ -import * as mongo from 'mongodb'; -import * as websocket from 'websocket'; -import * as redis from 'redis'; -import Matching, { pack } from '../../../models/othello-matching'; -import publishUserStream from '../../../publishers/stream'; - -export default function(request: websocket.request, connection: websocket.connection, subscriber: redis.RedisClient, user: any): void { - // Subscribe othello stream - subscriber.subscribe(`misskey:othello-stream:${user._id}`); - subscriber.on('message', (_, data) => { - connection.send(data); - }); - - connection.on('message', async (data) => { - const msg = JSON.parse(data.utf8Data); - - switch (msg.type) { - case 'ping': - if (msg.id == null) return; - const matching = await Matching.findOne({ - parentId: user._id, - childId: new mongo.ObjectID(msg.id) - }); - if (matching == null) return; - publishUserStream(matching.childId, 'othello_invited', await pack(matching, matching.childId)); - break; - } - }); -} diff --git a/src/server/api/stream/reversi-game.ts b/src/server/api/stream/reversi-game.ts new file mode 100644 index 0000000000..63d9b220b0 --- /dev/null +++ b/src/server/api/stream/reversi-game.ts @@ -0,0 +1,333 @@ +import * as websocket from 'websocket'; +import * as redis from 'redis'; +import * as CRC32 from 'crc-32'; +import ReversiGame, { pack } from '../../../models/reversi-game'; +import { publishReversiGameStream } from '../../../publishers/stream'; +import Reversi from '../../../reversi/core'; +import * as maps from '../../../reversi/maps'; +import { ParsedUrlQuery } from 'querystring'; + +export default function(request: websocket.request, connection: websocket.connection, subscriber: redis.RedisClient, user?: any): void { + const q = request.resourceURL.query as ParsedUrlQuery; + const gameId = q.game; + + // Subscribe game stream + subscriber.subscribe(`misskey:reversi-game-stream:${gameId}`); + subscriber.on('message', (_, data) => { + connection.send(data); + }); + + connection.on('message', async (data) => { + const msg = JSON.parse(data.utf8Data); + + switch (msg.type) { + case 'accept': + accept(true); + break; + + case 'cancel-accept': + accept(false); + break; + + case 'update-settings': + if (msg.settings == null) return; + updateSettings(msg.settings); + break; + + case 'init-form': + if (msg.body == null) return; + initForm(msg.body); + break; + + case 'update-form': + if (msg.id == null || msg.value === undefined) return; + updateForm(msg.id, msg.value); + break; + + case 'message': + if (msg.body == null) return; + message(msg.body); + break; + + case 'set': + if (msg.pos == null) return; + set(msg.pos); + break; + + case 'check': + if (msg.crc32 == null) return; + check(msg.crc32); + break; + } + }); + + async function updateSettings(settings) { + const game = await ReversiGame.findOne({ _id: gameId }); + + if (game.isStarted) return; + if (!game.user1Id.equals(user._id) && !game.user2Id.equals(user._id)) return; + if (game.user1Id.equals(user._id) && game.user1Accepted) return; + if (game.user2Id.equals(user._id) && game.user2Accepted) return; + + await ReversiGame.update({ _id: gameId }, { + $set: { + settings + } + }); + + publishReversiGameStream(gameId, 'update-settings', settings); + } + + async function initForm(form) { + const game = await ReversiGame.findOne({ _id: gameId }); + + if (game.isStarted) return; + if (!game.user1Id.equals(user._id) && !game.user2Id.equals(user._id)) return; + + const set = game.user1Id.equals(user._id) ? { + form1: form + } : { + form2: form + }; + + await ReversiGame.update({ _id: gameId }, { + $set: set + }); + + publishReversiGameStream(gameId, 'init-form', { + userId: user._id, + form + }); + } + + async function updateForm(id, value) { + const game = await ReversiGame.findOne({ _id: gameId }); + + if (game.isStarted) return; + if (!game.user1Id.equals(user._id) && !game.user2Id.equals(user._id)) return; + + const form = game.user1Id.equals(user._id) ? game.form2 : game.form1; + + const item = form.find(i => i.id == id); + + if (item == null) return; + + item.value = value; + + const set = game.user1Id.equals(user._id) ? { + form2: form + } : { + form1: form + }; + + await ReversiGame.update({ _id: gameId }, { + $set: set + }); + + publishReversiGameStream(gameId, 'update-form', { + userId: user._id, + id, + value + }); + } + + async function message(message) { + message.id = Math.random(); + publishReversiGameStream(gameId, 'message', { + userId: user._id, + message + }); + } + + async function accept(accept: boolean) { + const game = await ReversiGame.findOne({ _id: gameId }); + + if (game.isStarted) return; + + let bothAccepted = false; + + if (game.user1Id.equals(user._id)) { + await ReversiGame.update({ _id: gameId }, { + $set: { + user1Accepted: accept + } + }); + + publishReversiGameStream(gameId, 'change-accepts', { + user1: accept, + user2: game.user2Accepted + }); + + if (accept && game.user2Accepted) bothAccepted = true; + } else if (game.user2Id.equals(user._id)) { + await ReversiGame.update({ _id: gameId }, { + $set: { + user2Accepted: accept + } + }); + + publishReversiGameStream(gameId, 'change-accepts', { + user1: game.user1Accepted, + user2: accept + }); + + if (accept && game.user1Accepted) bothAccepted = true; + } else { + return; + } + + if (bothAccepted) { + // 3秒後、まだacceptされていたらゲーム開始 + setTimeout(async () => { + const freshGame = await ReversiGame.findOne({ _id: gameId }); + if (freshGame == null || freshGame.isStarted || freshGame.isEnded) return; + if (!freshGame.user1Accepted || !freshGame.user2Accepted) return; + + let bw: number; + if (freshGame.settings.bw == 'random') { + bw = Math.random() > 0.5 ? 1 : 2; + } else { + bw = freshGame.settings.bw as number; + } + + function getRandomMap() { + const mapCount = Object.entries(maps).length; + const rnd = Math.floor(Math.random() * mapCount); + return Object.entries(maps).find((x, i) => i == rnd)[1].data; + } + + const map = freshGame.settings.map != null ? freshGame.settings.map : getRandomMap(); + + await ReversiGame.update({ _id: gameId }, { + $set: { + startedAt: new Date(), + isStarted: true, + black: bw, + 'settings.map': map + } + }); + + //#region 盤面に最初から石がないなどして始まった瞬間に勝敗が決定する場合があるのでその処理 + const o = new Reversi(map, { + isLlotheo: freshGame.settings.isLlotheo, + canPutEverywhere: freshGame.settings.canPutEverywhere, + loopedBoard: freshGame.settings.loopedBoard + }); + + if (o.isEnded) { + let winner; + if (o.winner === true) { + winner = freshGame.black == 1 ? freshGame.user1Id : freshGame.user2Id; + } else if (o.winner === false) { + winner = freshGame.black == 1 ? freshGame.user2Id : freshGame.user1Id; + } else { + winner = null; + } + + await ReversiGame.update({ + _id: gameId + }, { + $set: { + isEnded: true, + winnerId: winner + } + }); + + publishReversiGameStream(gameId, 'ended', { + winnerId: winner, + game: await pack(gameId, user) + }); + } + //#endregion + + publishReversiGameStream(gameId, 'started', await pack(gameId, user)); + }, 3000); + } + } + + // 石を打つ + async function set(pos) { + const game = await ReversiGame.findOne({ _id: gameId }); + + if (!game.isStarted) return; + if (game.isEnded) return; + if (!game.user1Id.equals(user._id) && !game.user2Id.equals(user._id)) return; + + const o = new Reversi(game.settings.map, { + isLlotheo: game.settings.isLlotheo, + canPutEverywhere: game.settings.canPutEverywhere, + loopedBoard: game.settings.loopedBoard + }); + + game.logs.forEach(log => { + o.put(log.color, log.pos); + }); + + const myColor = + (game.user1Id.equals(user._id) && game.black == 1) || (game.user2Id.equals(user._id) && game.black == 2) + ? true + : false; + + if (!o.canPut(myColor, pos)) return; + o.put(myColor, pos); + + let winner; + if (o.isEnded) { + if (o.winner === true) { + winner = game.black == 1 ? game.user1Id : game.user2Id; + } else if (o.winner === false) { + winner = game.black == 1 ? game.user2Id : game.user1Id; + } else { + winner = null; + } + } + + const log = { + at: new Date(), + color: myColor, + pos + }; + + const crc32 = CRC32.str(game.logs.map(x => x.pos.toString()).join('') + pos.toString()); + + await ReversiGame.update({ + _id: gameId + }, { + $set: { + crc32, + isEnded: o.isEnded, + winnerId: winner + }, + $push: { + logs: log + } + }); + + publishReversiGameStream(gameId, 'set', Object.assign(log, { + next: o.turn + })); + + if (o.isEnded) { + publishReversiGameStream(gameId, 'ended', { + winnerId: winner, + game: await pack(gameId, user) + }); + } + } + + async function check(crc32) { + const game = await ReversiGame.findOne({ _id: gameId }); + + if (!game.isStarted) return; + + // 互換性のため + if (game.crc32 == null) return; + + if (crc32 !== game.crc32) { + connection.send(JSON.stringify({ + type: 'rescue', + body: await pack(game, user) + })); + } + } +} diff --git a/src/server/api/stream/reversi.ts b/src/server/api/stream/reversi.ts new file mode 100644 index 0000000000..35c6167364 --- /dev/null +++ b/src/server/api/stream/reversi.ts @@ -0,0 +1,29 @@ +import * as mongo from 'mongodb'; +import * as websocket from 'websocket'; +import * as redis from 'redis'; +import Matching, { pack } from '../../../models/reversi-matching'; +import publishUserStream from '../../../publishers/stream'; + +export default function(request: websocket.request, connection: websocket.connection, subscriber: redis.RedisClient, user: any): void { + // Subscribe reversi stream + subscriber.subscribe(`misskey:reversi-stream:${user._id}`); + subscriber.on('message', (_, data) => { + connection.send(data); + }); + + connection.on('message', async (data) => { + const msg = JSON.parse(data.utf8Data); + + switch (msg.type) { + case 'ping': + if (msg.id == null) return; + const matching = await Matching.findOne({ + parentId: user._id, + childId: new mongo.ObjectID(msg.id) + }); + if (matching == null) return; + publishUserStream(matching.childId, 'reversi_invited', await pack(matching, matching.childId)); + break; + } + }); +} diff --git a/src/server/api/streaming.ts b/src/server/api/streaming.ts index 2d4cfc108f..a3e4ed6f85 100644 --- a/src/server/api/streaming.ts +++ b/src/server/api/streaming.ts @@ -10,8 +10,8 @@ import userListStream from './stream/user-list'; import driveStream from './stream/drive'; import messagingStream from './stream/messaging'; import messagingIndexStream from './stream/messaging-index'; -import othelloGameStream from './stream/othello-game'; -import othelloStream from './stream/othello'; +import reversiGameStream from './stream/reversi-game'; +import reversiStream from './stream/reversi'; import serverStatsStream from './stream/server-stats'; import notesStatsStream from './stream/notes-stats'; import requestsStream from './stream/requests'; @@ -56,8 +56,8 @@ module.exports = (server: http.Server) => { const q = request.resourceURL.query as ParsedUrlQuery; const [user, app] = await authenticate(q.i as string); - if (request.resourceURL.pathname === '/othello-game') { - othelloGameStream(request, connection, subscriber, user); + if (request.resourceURL.pathname === '/reversi-game') { + reversiGameStream(request, connection, subscriber, user); return; } @@ -75,7 +75,7 @@ module.exports = (server: http.Server) => { request.resourceURL.pathname === '/drive' ? driveStream : request.resourceURL.pathname === '/messaging' ? messagingStream : request.resourceURL.pathname === '/messaging-index' ? messagingIndexStream : - request.resourceURL.pathname === '/othello' ? othelloStream : + request.resourceURL.pathname === '/reversi' ? reversiStream : null; if (channel !== null) { -- cgit v1.2.3-freya From 80e5645a84cc60d76f79c48a3d565fb66ad88643 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 18 Jun 2018 09:54:53 +0900 Subject: wip --- locales/index.ts | 5 +- src/acct/parse.ts | 2 +- src/acct/render.ts | 4 +- src/build/i18n.ts | 4 +- src/cafy-id.ts | 4 +- src/daemons/hashtags-stats-child.ts | 60 --- src/daemons/hashtags-stats.ts | 20 - src/daemons/notes-stats.ts | 2 +- src/daemons/server-stats.ts | 4 +- src/db/mongodb.ts | 2 +- src/index.ts | 4 +- src/models/app.ts | 2 +- src/models/auth-session.ts | 2 +- src/models/drive-file.ts | 2 +- src/models/drive-folder.ts | 2 +- src/models/favorite.ts | 2 +- src/models/follow-request.ts | 2 +- src/models/messaging-message.ts | 2 +- src/models/note-reaction.ts | 2 +- src/models/note.ts | 10 +- src/models/notification.ts | 2 +- src/models/reversi-game.ts | 2 +- src/models/reversi-matching.ts | 2 +- src/models/signin.ts | 2 +- src/models/user-list.ts | 2 +- src/models/user.ts | 10 +- .../activitypub/renderer/ordered-collection.ts | 2 +- src/remote/activitypub/renderer/reject.ts | 2 +- src/remote/activitypub/request.ts | 4 +- src/remote/webfinger.ts | 4 +- src/reversi/ai/back.ts | 377 ------------------ src/reversi/ai/front.ts | 233 ----------- src/reversi/ai/index.ts | 1 - src/reversi/core.ts | 4 +- src/server/api/bot/core.ts | 439 --------------------- src/server/api/bot/interfaces/line.ts | 238 ----------- src/server/api/common/get-host-lower.ts | 2 +- src/server/api/endpoints/aggregation/posts.ts | 5 +- src/server/api/endpoints/aggregation/users.ts | 5 +- .../api/endpoints/aggregation/users/activity.ts | 15 +- .../api/endpoints/aggregation/users/followers.ts | 2 +- .../api/endpoints/aggregation/users/following.ts | 2 +- src/server/api/endpoints/aggregation/users/post.ts | 15 +- .../api/endpoints/aggregation/users/reaction.ts | 12 +- src/server/api/endpoints/app/create.ts | 10 +- src/server/api/endpoints/app/name_id/available.ts | 2 +- src/server/api/endpoints/app/show.ts | 8 +- src/server/api/endpoints/auth/accept.ts | 10 +- src/server/api/endpoints/auth/session/generate.ts | 2 +- src/server/api/endpoints/auth/session/show.ts | 10 +- src/server/api/endpoints/auth/session/userkey.ts | 2 +- src/server/api/endpoints/drive.ts | 3 +- src/server/api/endpoints/drive/files.ts | 6 +- src/server/api/endpoints/drive/files/create.ts | 6 +- src/server/api/endpoints/drive/files/delete.ts | 3 +- src/server/api/endpoints/drive/files/find.ts | 6 +- src/server/api/endpoints/drive/files/show.ts | 6 +- src/server/api/endpoints/drive/files/update.ts | 6 +- src/server/api/endpoints/drive/folders.ts | 2 +- src/server/api/endpoints/drive/folders/create.ts | 6 +- src/server/api/endpoints/drive/folders/find.ts | 6 +- src/server/api/endpoints/drive/folders/show.ts | 6 +- src/server/api/endpoints/drive/folders/update.ts | 8 +- src/server/api/endpoints/drive/stream.ts | 6 +- src/server/api/endpoints/following/create.ts | 7 +- src/server/api/endpoints/following/delete.ts | 7 +- .../api/endpoints/following/requests/accept.ts | 4 +- .../api/endpoints/following/requests/cancel.ts | 4 +- .../api/endpoints/following/requests/list.ts | 3 +- .../api/endpoints/following/requests/reject.ts | 4 +- src/server/api/endpoints/following/stalk.ts | 3 +- src/server/api/endpoints/following/unstalk.ts | 3 +- src/server/api/endpoints/hashtags/trend.ts | 5 +- src/server/api/endpoints/i.ts | 8 +- src/server/api/endpoints/i/2fa/done.ts | 7 +- src/server/api/endpoints/i/2fa/register.ts | 7 +- src/server/api/endpoints/i/2fa/unregister.ts | 7 +- src/server/api/endpoints/i/authorized_apps.ts | 6 +- src/server/api/endpoints/i/change_password.ts | 7 +- src/server/api/endpoints/i/favorites.ts | 6 +- src/server/api/endpoints/i/notifications.ts | 6 +- src/server/api/endpoints/i/pin.ts | 7 +- src/server/api/endpoints/i/regenerate_token.ts | 7 +- src/server/api/endpoints/i/signin_history.ts | 6 +- src/server/api/endpoints/i/update.ts | 8 +- .../api/endpoints/i/update_client_setting.ts | 9 +- src/server/api/endpoints/i/update_home.ts | 4 +- src/server/api/endpoints/i/update_mobile_home.ts | 4 +- src/server/api/endpoints/i/update_widget.ts | 12 +- src/server/api/endpoints/messaging/history.ts | 6 +- src/server/api/endpoints/messaging/messages.ts | 4 +- .../api/endpoints/messaging/messages/create.ts | 7 +- src/server/api/endpoints/meta.ts | 2 +- src/server/api/endpoints/mute/create.ts | 7 +- src/server/api/endpoints/mute/delete.ts | 7 +- src/server/api/endpoints/mute/list.ts | 7 +- src/server/api/endpoints/my/apps.ts | 6 +- src/server/api/endpoints/notes.ts | 2 +- src/server/api/endpoints/notes/conversation.ts | 12 +- src/server/api/endpoints/notes/create.ts | 11 +- src/server/api/endpoints/notes/delete.ts | 3 +- src/server/api/endpoints/notes/favorites/create.ts | 6 +- src/server/api/endpoints/notes/favorites/delete.ts | 6 +- src/server/api/endpoints/notes/global-timeline.ts | 6 +- src/server/api/endpoints/notes/local-timeline.ts | 6 +- src/server/api/endpoints/notes/mentions.ts | 10 +- .../api/endpoints/notes/polls/recommendation.ts | 6 +- src/server/api/endpoints/notes/polls/vote.ts | 19 +- src/server/api/endpoints/notes/reactions.ts | 10 +- src/server/api/endpoints/notes/reactions/create.ts | 6 +- src/server/api/endpoints/notes/reactions/delete.ts | 8 +- src/server/api/endpoints/notes/replies.ts | 3 +- src/server/api/endpoints/notes/reposts.ts | 10 +- src/server/api/endpoints/notes/search.ts | 364 ----------------- src/server/api/endpoints/notes/search_by_tag.ts | 26 +- src/server/api/endpoints/notes/show.ts | 10 +- src/server/api/endpoints/notes/timeline.ts | 6 +- src/server/api/endpoints/notes/trend.ts | 10 +- .../api/endpoints/notes/user-list-timeline.ts | 6 +- .../endpoints/notifications/mark_as_read_all.ts | 4 +- src/server/api/endpoints/reversi/games.ts | 3 +- src/server/api/endpoints/reversi/games/show.ts | 3 +- src/server/api/endpoints/reversi/invitations.ts | 3 +- src/server/api/endpoints/reversi/match.ts | 4 +- src/server/api/endpoints/reversi/match/cancel.ts | 3 +- src/server/api/endpoints/sw/register.ts | 6 +- src/server/api/endpoints/username/available.ts | 8 +- src/server/api/endpoints/users.ts | 7 +- src/server/api/endpoints/users/followers.ts | 7 +- src/server/api/endpoints/users/following.ts | 11 +- .../users/get_frequently_replied_users.ts | 9 +- src/server/api/endpoints/users/lists/create.ts | 6 +- src/server/api/endpoints/users/lists/list.ts | 3 +- src/server/api/endpoints/users/lists/push.ts | 4 +- src/server/api/endpoints/users/lists/show.ts | 3 +- src/server/api/endpoints/users/notes.ts | 7 +- src/server/api/endpoints/users/recommendation.ts | 11 +- src/server/api/endpoints/users/search.ts | 73 +--- .../api/endpoints/users/search_by_username.ts | 7 +- src/server/api/endpoints/users/show.ts | 9 +- src/server/api/private/signup.ts | 2 +- src/server/api/service/github.ts | 4 +- src/server/api/stream/notes-stats.ts | 2 +- src/server/api/stream/requests.ts | 19 - src/server/api/stream/reversi-game.ts | 14 +- src/server/api/stream/server-stats.ts | 2 +- src/server/api/streaming.ts | 6 - src/server/index.ts | 2 +- webpack.config.ts | 19 +- webpack/i18n.ts | 2 +- 150 files changed, 305 insertions(+), 2334 deletions(-) delete mode 100644 src/daemons/hashtags-stats-child.ts delete mode 100644 src/daemons/hashtags-stats.ts delete mode 100644 src/reversi/ai/back.ts delete mode 100644 src/reversi/ai/front.ts delete mode 100644 src/reversi/ai/index.ts delete mode 100644 src/server/api/bot/core.ts delete mode 100644 src/server/api/bot/interfaces/line.ts delete mode 100644 src/server/api/endpoints/notes/search.ts delete mode 100644 src/server/api/stream/requests.ts (limited to 'src/server/api/stream') diff --git a/locales/index.ts b/locales/index.ts index 3b4f76b06a..2ae84f30ae 100644 --- a/locales/index.ts +++ b/locales/index.ts @@ -6,15 +6,14 @@ import * as fs from 'fs'; import * as yaml from 'js-yaml'; export type LangKey = 'de' | 'en' | 'fr' | 'ja' | 'pl'; -export type LocaleObjectChildren = LocaleObject | string | undefined; -export type LocaleObject = {[key: string]: LocaleObjectChildren }; +export type LocaleObject = { [key: string]: any }; const loadLang = (lang: LangKey) => yaml.safeLoad( fs.readFileSync(`./locales/${lang}.yml`, 'utf-8')) as LocaleObject; const native = loadLang('ja'); -const langs: {[key in LangKey]: LocaleObject} = { +const langs: { [key: string]: LocaleObject } = { 'de': loadLang('de'), 'en': loadLang('en'), 'fr': loadLang('fr'), diff --git a/src/acct/parse.ts b/src/acct/parse.ts index ef1f55405d..0c00fccef6 100644 --- a/src/acct/parse.ts +++ b/src/acct/parse.ts @@ -1,4 +1,4 @@ -export default acct => { +export default (acct: string) => { const splitted = acct.split('@', 2); return { username: splitted[0], host: splitted[1] || null }; }; diff --git a/src/acct/render.ts b/src/acct/render.ts index 9afb03d88b..191034361d 100644 --- a/src/acct/render.ts +++ b/src/acct/render.ts @@ -1,3 +1,5 @@ -export default user => { +import { IUser } from "../models/user"; + +export default (user: IUser) => { return user.host === null ? user.username : `${user.username}@${user.host}`; }; diff --git a/src/build/i18n.ts b/src/build/i18n.ts index 308ff0da00..f7602dfd17 100644 --- a/src/build/i18n.ts +++ b/src/build/i18n.ts @@ -2,7 +2,7 @@ * Replace i18n texts */ -import locale, { isAvailableLanguage, LocaleObject, LocaleObjectChildren } from '../../locales'; +import locale, { isAvailableLanguage, LocaleObject } from '../../locales'; export default class Replacer { private lang: string; @@ -24,7 +24,7 @@ export default class Replacer { const texts = locale[this.lang]; - let text: LocaleObjectChildren = texts; + let text = texts; if (path) { if (text.hasOwnProperty(path)) { diff --git a/src/cafy-id.ts b/src/cafy-id.ts index 310b1eb20b..dac0f97bd2 100644 --- a/src/cafy-id.ts +++ b/src/cafy-id.ts @@ -1,8 +1,8 @@ import * as mongo from 'mongodb'; import { Query } from 'cafy'; -export const isAnId = x => mongo.ObjectID.isValid(x); -export const isNotAnId = x => !isAnId(x); +export const isAnId = (x: any) => mongo.ObjectID.isValid(x); +export const isNotAnId = (x: any) => !isAnId(x); /** * ID diff --git a/src/daemons/hashtags-stats-child.ts b/src/daemons/hashtags-stats-child.ts deleted file mode 100644 index 3f7f4d6e9e..0000000000 --- a/src/daemons/hashtags-stats-child.ts +++ /dev/null @@ -1,60 +0,0 @@ -import Note from '../models/note'; - -// 10分 -const interval = 1000 * 60 * 10; - -async function tick() { - const res = await Note.aggregate([{ - $match: { - createdAt: { - $gt: new Date(Date.now() - interval) - }, - tags: { - $exists: true, - $ne: [] - } - } - }, { - $unwind: '$tags' - }, { - $group: { - _id: '$tags', - count: { - $sum: 1 - } - } - }, { - $group: { - _id: null, - tags: { - $push: { - tag: '$_id', - count: '$count' - } - } - } - }, { - $project: { - _id: false, - tags: true - } - }]) as { - tags: Array<{ - tag: string; - count: number; - }> - }; - - const stats = res.tags - .sort((a, b) => a.count - b.count) - .map(tag => [tag.tag, tag.count]) - .slice(0, 10); - - console.log(stats); - - process.send(stats); -} - -tick(); - -setInterval(tick, interval); diff --git a/src/daemons/hashtags-stats.ts b/src/daemons/hashtags-stats.ts deleted file mode 100644 index 5ed028ac33..0000000000 --- a/src/daemons/hashtags-stats.ts +++ /dev/null @@ -1,20 +0,0 @@ -import * as childProcess from 'child_process'; -import Xev from 'xev'; - -const ev = new Xev(); - -export default function() { - const log = []; - - const p = childProcess.fork(__dirname + '/hashtags-stats-child.js'); - - p.on('message', stats => { - ev.emit('hashtagsStats', stats); - log.push(stats); - if (log.length > 30) log.shift(); - }); - - ev.on('requestHashTagsStatsLog', id => { - ev.emit('hashtagsStatsLog:' + id, log); - }); -} diff --git a/src/daemons/notes-stats.ts b/src/daemons/notes-stats.ts index 3094c34af0..021e6d64a7 100644 --- a/src/daemons/notes-stats.ts +++ b/src/daemons/notes-stats.ts @@ -4,7 +4,7 @@ import Xev from 'xev'; const ev = new Xev(); export default function() { - const log = []; + const log: any[] = []; const p = childProcess.fork(__dirname + '/notes-stats-child.js'); diff --git a/src/daemons/server-stats.ts b/src/daemons/server-stats.ts index 1403402508..5c793c6624 100644 --- a/src/daemons/server-stats.ts +++ b/src/daemons/server-stats.ts @@ -11,14 +11,14 @@ const interval = 1000; * Report server stats regularly */ export default function() { - const log = []; + const log: any[] = []; ev.on('requestServerStatsLog', id => { ev.emit('serverStatsLog:' + id, log); }); async function tick() { - osUtils.cpuUsage(cpuUsage => { + osUtils.cpuUsage((cpuUsage: number) => { const disk = diskusage.checkSync(os.platform() == 'win32' ? 'c:' : '/'); const stats = { cpu_usage: cpuUsage, diff --git a/src/db/mongodb.ts b/src/db/mongodb.ts index 05bb72bfde..0df87ea3a4 100644 --- a/src/db/mongodb.ts +++ b/src/db/mongodb.ts @@ -27,7 +27,7 @@ const nativeDbConn = async (): Promise => { if (mdb) return mdb; const db = await ((): Promise => new Promise((resolve, reject) => { - (mongodb as any).MongoClient.connect(uri, (e, client) => { + (mongodb as any).MongoClient.connect(uri, (e: Error, client: any) => { if (e) return reject(e); resolve(client.db(config.mongodb.db)); }); diff --git a/src/index.ts b/src/index.ts index 35cf5a243b..c89252dfc2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -60,7 +60,7 @@ function main() { /** * Init master process */ -async function masterMain(opt) { +async function masterMain(opt: any) { let config: Config; try { @@ -91,7 +91,7 @@ async function masterMain(opt) { /** * Init worker process */ -async function workerMain(opt) { +async function workerMain(opt: any) { if (!opt['only-processor']) { // start server await require('./server').default(); diff --git a/src/models/app.ts b/src/models/app.ts index 7926f2fac2..8dc7fe01d9 100644 --- a/src/models/app.ts +++ b/src/models/app.ts @@ -1,5 +1,5 @@ import * as mongo from 'mongodb'; -import * as deepcopy from 'deepcopy'; +const deepcopy = require('deepcopy'); import AccessToken from './access-token'; import db from '../db/mongodb'; import config from '../config'; diff --git a/src/models/auth-session.ts b/src/models/auth-session.ts index 219b1a4c8b..3d2c9ee3c1 100644 --- a/src/models/auth-session.ts +++ b/src/models/auth-session.ts @@ -1,5 +1,5 @@ import * as mongo from 'mongodb'; -import * as deepcopy from 'deepcopy'; +const deepcopy = require('deepcopy'); import db from '../db/mongodb'; import { pack as packApp } from './app'; diff --git a/src/models/drive-file.ts b/src/models/drive-file.ts index bb98c11a93..2bdf38f484 100644 --- a/src/models/drive-file.ts +++ b/src/models/drive-file.ts @@ -1,5 +1,5 @@ import * as mongo from 'mongodb'; -import * as deepcopy from 'deepcopy'; +const deepcopy = require('deepcopy'); import { pack as packFolder } from './drive-folder'; import config from '../config'; import monkDb, { nativeDbConn } from '../db/mongodb'; diff --git a/src/models/drive-folder.ts b/src/models/drive-folder.ts index cafb9cdf13..def519fade 100644 --- a/src/models/drive-folder.ts +++ b/src/models/drive-folder.ts @@ -1,5 +1,5 @@ import * as mongo from 'mongodb'; -import * as deepcopy from 'deepcopy'; +const deepcopy = require('deepcopy'); import db from '../db/mongodb'; import DriveFile from './drive-file'; diff --git a/src/models/favorite.ts b/src/models/favorite.ts index 45417212e3..b2d2fc93e8 100644 --- a/src/models/favorite.ts +++ b/src/models/favorite.ts @@ -1,5 +1,5 @@ import * as mongo from 'mongodb'; -import * as deepcopy from 'deepcopy'; +const deepcopy = require('deepcopy'); import db from '../db/mongodb'; import { pack as packNote } from './note'; diff --git a/src/models/follow-request.ts b/src/models/follow-request.ts index eea5d1c535..fdb20011f4 100644 --- a/src/models/follow-request.ts +++ b/src/models/follow-request.ts @@ -1,5 +1,5 @@ import * as mongo from 'mongodb'; -import * as deepcopy from 'deepcopy'; +const deepcopy = require('deepcopy'); import db from '../db/mongodb'; import { pack as packUser } from './user'; diff --git a/src/models/messaging-message.ts b/src/models/messaging-message.ts index ad2d1657e6..f46abd506d 100644 --- a/src/models/messaging-message.ts +++ b/src/models/messaging-message.ts @@ -1,5 +1,5 @@ import * as mongo from 'mongodb'; -import * as deepcopy from 'deepcopy'; +const deepcopy = require('deepcopy'); import { pack as packUser } from './user'; import { pack as packFile } from './drive-file'; import db from '../db/mongodb'; diff --git a/src/models/note-reaction.ts b/src/models/note-reaction.ts index e5e108c017..915dc0cf91 100644 --- a/src/models/note-reaction.ts +++ b/src/models/note-reaction.ts @@ -1,6 +1,6 @@ import * as mongo from 'mongodb'; import $ from 'cafy'; -import * as deepcopy from 'deepcopy'; +const deepcopy = require('deepcopy'); import db from '../db/mongodb'; import Reaction from './note-reaction'; import { pack as packUser } from './user'; diff --git a/src/models/note.ts b/src/models/note.ts index 83e85f8498..83cd93353b 100644 --- a/src/models/note.ts +++ b/src/models/note.ts @@ -1,5 +1,5 @@ import * as mongo from 'mongodb'; -import * as deepcopy from 'deepcopy'; +const deepcopy = require('deepcopy'); import rap from '@prezzemolo/rap'; import db from '../db/mongodb'; import { IUser, pack as packUser } from './user'; @@ -37,7 +37,11 @@ export type INote = { mediaIds: mongo.ObjectID[]; replyId: mongo.ObjectID; renoteId: mongo.ObjectID; - poll: any; // todo + poll: { + choices: Array<{ + id: number; + }> + }; text: string; tags: string[]; tagsLower: string[]; @@ -304,7 +308,7 @@ export const pack = async ( if (vote != null) { const myChoice = poll.choices - .filter(c => c.id == vote.choice)[0]; + .filter((c: any) => c.id == vote.choice)[0]; myChoice.isVoted = true; } diff --git a/src/models/notification.ts b/src/models/notification.ts index 875c6952b5..097e3e691f 100644 --- a/src/models/notification.ts +++ b/src/models/notification.ts @@ -1,5 +1,5 @@ import * as mongo from 'mongodb'; -import * as deepcopy from 'deepcopy'; +const deepcopy = require('deepcopy'); import db from '../db/mongodb'; import { IUser, pack as packUser } from './user'; import { pack as packNote } from './note'; diff --git a/src/models/reversi-game.ts b/src/models/reversi-game.ts index 16b9ab0d4e..cf497f0568 100644 --- a/src/models/reversi-game.ts +++ b/src/models/reversi-game.ts @@ -1,5 +1,5 @@ import * as mongo from 'mongodb'; -import * as deepcopy from 'deepcopy'; +const deepcopy = require('deepcopy'); import db from '../db/mongodb'; import { IUser, pack as packUser } from './user'; diff --git a/src/models/reversi-matching.ts b/src/models/reversi-matching.ts index 5efa5d76ad..90227bd121 100644 --- a/src/models/reversi-matching.ts +++ b/src/models/reversi-matching.ts @@ -1,5 +1,5 @@ import * as mongo from 'mongodb'; -import * as deepcopy from 'deepcopy'; +const deepcopy = require('deepcopy'); import db from '../db/mongodb'; import { IUser, pack as packUser } from './user'; diff --git a/src/models/signin.ts b/src/models/signin.ts index d8b05c0e30..55485d8667 100644 --- a/src/models/signin.ts +++ b/src/models/signin.ts @@ -1,5 +1,5 @@ import * as mongo from 'mongodb'; -import * as deepcopy from 'deepcopy'; +const deepcopy = require('deepcopy'); import db from '../db/mongodb'; const Signin = db.get('signin'); diff --git a/src/models/user-list.ts b/src/models/user-list.ts index 4cf1de61a4..5cfa7e4dfc 100644 --- a/src/models/user-list.ts +++ b/src/models/user-list.ts @@ -1,5 +1,5 @@ import * as mongo from 'mongodb'; -import * as deepcopy from 'deepcopy'; +const deepcopy = require('deepcopy'); import db from '../db/mongodb'; const UserList = db.get('userList'); diff --git a/src/models/user.ts b/src/models/user.ts index f0620a6cfd..942faeeff4 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -1,5 +1,5 @@ import * as mongo from 'mongodb'; -import * as deepcopy from 'deepcopy'; +const deepcopy = require('deepcopy'); import sequential = require('promise-sequential'); import rap from '@prezzemolo/rap'; import db from '../db/mongodb'; @@ -153,14 +153,6 @@ export function isValidBirthday(birthday: string): boolean { } //#endregion -export function init(user): IUser { - user._id = new mongo.ObjectID(user._id); - user.avatarId = new mongo.ObjectID(user.avatarId); - user.bannerId = new mongo.ObjectID(user.bannerId); - user.pinnedNoteId = new mongo.ObjectID(user.pinnedNoteId); - return user; -} - /** * Userを物理削除します */ diff --git a/src/remote/activitypub/renderer/ordered-collection.ts b/src/remote/activitypub/renderer/ordered-collection.ts index 2ca0f77354..9d543b1e1b 100644 --- a/src/remote/activitypub/renderer/ordered-collection.ts +++ b/src/remote/activitypub/renderer/ordered-collection.ts @@ -1,4 +1,4 @@ -export default (id, totalItems, orderedItems) => ({ +export default (id: string, totalItems: any, orderedItems: any) => ({ id, type: 'OrderedCollection', totalItems, diff --git a/src/remote/activitypub/renderer/reject.ts b/src/remote/activitypub/renderer/reject.ts index 29c998a6b4..2464486c2f 100644 --- a/src/remote/activitypub/renderer/reject.ts +++ b/src/remote/activitypub/renderer/reject.ts @@ -1,4 +1,4 @@ -export default object => ({ +export default (object: any) => ({ type: 'Reject', object }); diff --git a/src/remote/activitypub/request.ts b/src/remote/activitypub/request.ts index e6861fdb3e..e2b600ae2e 100644 --- a/src/remote/activitypub/request.ts +++ b/src/remote/activitypub/request.ts @@ -1,5 +1,5 @@ import { request } from 'https'; -import { sign } from 'http-signature'; +const { sign } = require('http-signature'); import { URL } from 'url'; import * as debug from 'debug'; @@ -8,7 +8,7 @@ import { ILocalUser } from '../../models/user'; const log = debug('misskey:activitypub:deliver'); -export default (user: ILocalUser, url: string, object) => new Promise((resolve, reject) => { +export default (user: ILocalUser, url: string, object: any) => new Promise((resolve, reject) => { log(`--> ${url}`); const { protocol, hostname, port, pathname, search } = new URL(url); diff --git a/src/remote/webfinger.ts b/src/remote/webfinger.ts index 4f1ff231c0..cba09379a1 100644 --- a/src/remote/webfinger.ts +++ b/src/remote/webfinger.ts @@ -12,8 +12,8 @@ type IWebFinger = { subject: string; }; -export default async function resolve(query): Promise { - return await new Promise((res, rej) => webFinger.lookup(query, (error, result) => { +export default async function resolve(query: any): Promise { + return await new Promise((res, rej) => webFinger.lookup(query, (error: Error, result: any) => { if (error) { return rej(error); } diff --git a/src/reversi/ai/back.ts b/src/reversi/ai/back.ts deleted file mode 100644 index 42f1a0f4b8..0000000000 --- a/src/reversi/ai/back.ts +++ /dev/null @@ -1,377 +0,0 @@ -/** - * -AI- - * Botのバックエンド(思考を担当) - * - * 対話と思考を同じプロセスで行うと、思考時間が長引いたときにストリームから - * 切断されてしまうので、別々のプロセスで行うようにします - */ - -import * as request from 'request-promise-native'; -import Reversi, { Color } from '../core'; -import conf from '../../config'; -import getUserName from '../../renderers/get-user-name'; - -let game; -let form; - -/** - * BotアカウントのユーザーID - */ -const id = conf.reversi_ai.id; - -/** - * BotアカウントのAPIキー - */ -const i = conf.reversi_ai.i; - -let note; - -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}/reversi/${game.id}`; - const user = game.user1Id == id ? game.user2 : game.user1; - const isSettai = form[0].value === 0; - const text = isSettai - ? `?[${getUserName(user)}](${conf.url}/@${user.username})さんの接待を始めました!` - : `対局を?[${getUserName(user)}](${conf.url}/@${user.username})さんと始めました! (強さ${form[0].value})`; - - const res = await request.post(`${conf.api_url}/notes/create`, { - json: { i, - text: `${text}\n→[観戦する](${url})` - } - }); - - note = res.createdNote; - //#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 - ? `?[${getUserName(user)}](${conf.url}/@${user.username})さんに接待で引き分けました...` - : msg.body.winnerId == id - ? `?[${getUserName(user)}](${conf.url}/@${user.username})さんに接待で勝ってしまいました...` - : `?[${getUserName(user)}](${conf.url}/@${user.username})さんに接待で負けてあげました♪` - : msg.body.winnerId === null - ? `?[${getUserName(user)}](${conf.url}/@${user.username})さんと引き分けました~` - : msg.body.winnerId == id - ? `?[${getUserName(user)}](${conf.url}/@${user.username})さんに勝ちました♪` - : `?[${getUserName(user)}](${conf.url}/@${user.username})さんに負けました...`; - - await request.post(`${conf.api_url}/notes/create`, { - json: { i, - renoteId: note.id, - text: text - } - }); - //#endregion - - process.exit(); - } - - // 打たれたとき - if (msg.type == 'set') { - onSet(msg.body); - } -}); - -let o: Reversi; -let botColor: Color; - -// 各マスの強さ -let cellWeights; - -/** - * ゲーム開始時 - * @param g ゲーム情報 - */ -function onGameStarted(g) { - game = g; - - // リバーシエンジン初期化 - o = new Reversi(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/reversi/ai/front.ts b/src/reversi/ai/front.ts deleted file mode 100644 index 25ee43225a..0000000000 --- a/src/reversi/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 '../../config'; - -// 設定 //////////////////////////////////////////////////////// - -/** - * BotアカウントのAPIキー - */ -const i = conf.reversi_ai.i; - -/** - * BotアカウントのユーザーID - */ -const id = conf.reversi_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 note = msg.body; - - if (note.userId == id) return; - - // リアクションする - request.post(`${conf.api_url}/notes/reactions/create`, { - json: { i, - noteId: note.id, - reaction: 'love' - } - }); - - if (note.text) { - if (note.text.indexOf('リバーシ') > -1) { - request.post(`${conf.api_url}/notes/create`, { - json: { i, - replyId: note.id, - text: '良いですよ~' - } - }); - - invite(note.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}/reversi/match`, { - json: { i, - userId: userId - } - }); -} - -/** - * リバーシストリーム - */ -const reversiStream = new ReconnectingWebSocket(`${conf.ws_url}/reversi?i=${i}`, undefined, { - constructor: WebSocket -}); - -reversiStream.on('open', () => { - console.log('reversi stream opened'); -}); - -reversiStream.on('close', () => { - console.log('reversi stream closed'); -}); - -reversiStream.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}/reversi-game?i=${i}&game=${game.id}`, undefined, { - constructor: WebSocket - }); - - gw.on('open', () => { - console.log('reversi 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('reversi game stream closed'); - }); -} - -/** - * リバーシの対局に招待されたとき - * @param inviter 誘ってきたユーザー - */ -async function onInviteMe(inviter) { - console.log(`Someone invited me: @${inviter.username}`); - - // 承認 - const game = await request.post(`${conf.api_url}/reversi/match`, { - json: { - i, - userId: inviter.id - } - }); - - gameStart(game); -} diff --git a/src/reversi/ai/index.ts b/src/reversi/ai/index.ts deleted file mode 100644 index 5cd1db82da..0000000000 --- a/src/reversi/ai/index.ts +++ /dev/null @@ -1 +0,0 @@ -require('./front'); diff --git a/src/reversi/core.ts b/src/reversi/core.ts index f1f9de1486..37f62a43d5 100644 --- a/src/reversi/core.ts +++ b/src/reversi/core.ts @@ -203,7 +203,7 @@ export default class Reversi { * 打つことができる場所を取得します */ public canPutSomewhere(color: Color): number[] { - const result = []; + const result: number[] = []; this.board.forEach((x, i) => { if (this.canPut(color, i)) result.push(i); @@ -239,7 +239,7 @@ export default class Reversi { const enemyColor = !color; // ひっくり返せる石(の位置)リスト - let stones = []; + let stones: number[] = []; const initPos = pos; diff --git a/src/server/api/bot/core.ts b/src/server/api/bot/core.ts deleted file mode 100644 index d41af48057..0000000000 --- a/src/server/api/bot/core.ts +++ /dev/null @@ -1,439 +0,0 @@ -import * as EventEmitter from 'events'; -import * as bcrypt from 'bcryptjs'; - -import User, { IUser, init as initUser, ILocalUser } from '../../../models/user'; - -import getNoteSummary from '../../../renderers/get-note-summary'; -import getUserName from '../../../renderers/get-user-name'; -import getUserSummary from '../../../renderers/get-user-summary'; -import parseAcct from '../../../acct/parse'; -import getNotificationSummary from '../../../renderers/get-notification-summary'; - -const hmm = [ - '?', - 'ふぅ~む...?', - 'ちょっと何言ってるかわからないです', - '「ヘルプ」と言うと利用可能な操作が確認できますよ' -]; - -/** - * Botの頭脳 - */ -export default class BotCore extends EventEmitter { - public user: IUser = null; - - private context: Context = null; - - constructor(user?: IUser) { - super(); - - this.user = user; - } - - public clearContext() { - this.setContext(null); - } - - public setContext(context: Context) { - this.context = context; - this.emit('updated'); - - if (context) { - context.on('updated', () => { - this.emit('updated'); - }); - } - } - - public export() { - return { - user: this.user, - context: this.context ? this.context.export() : null - }; - } - - protected _import(data) { - this.user = data.user ? initUser(data.user) : null; - this.setContext(data.context ? Context.import(this, data.context) : null); - } - - public static import(data) { - const bot = new BotCore(); - bot._import(data); - return bot; - } - - public async q(query: string): Promise { - if (this.context != null) { - return await this.context.q(query); - } - - if (/^@[a-zA-Z0-9_]+$/.test(query)) { - return await this.showUserCommand(query); - } - - switch (query) { - case 'ping': - return 'PONG'; - - case 'help': - case 'ヘルプ': - return '利用可能なコマンド一覧です:\n' + - 'help: これです\n' + - 'me: アカウント情報を見ます\n' + - 'login, signin: サインインします\n' + - 'logout, signout: サインアウトします\n' + - 'note: 投稿します\n' + - 'tl: タイムラインを見ます\n' + - 'no: 通知を見ます\n' + - '@<ユーザー名>: ユーザーを表示します\n' + - '\n' + - 'タイムラインや通知を見た後、「次」というとさらに遡ることができます。'; - - case 'me': - return this.user ? `${getUserName(this.user)}としてサインインしています。\n\n${getUserSummary(this.user)}` : 'サインインしていません'; - - case 'login': - case 'signin': - case 'ログイン': - case 'サインイン': - if (this.user != null) return '既にサインインしていますよ!'; - this.setContext(new SigninContext(this)); - return await this.context.greet(); - - case 'logout': - case 'signout': - case 'ログアウト': - case 'サインアウト': - if (this.user == null) return '今はサインインしてないですよ!'; - this.signout(); - return 'ご利用ありがとうございました <3'; - - case 'note': - case '投稿': - if (this.user == null) return 'まずサインインしてください。'; - this.setContext(new NoteContext(this)); - return await this.context.greet(); - - case 'tl': - case 'タイムライン': - if (this.user == null) return 'まずサインインしてください。'; - this.setContext(new TlContext(this)); - return await this.context.greet(); - - case 'no': - case 'notifications': - case '通知': - if (this.user == null) return 'まずサインインしてください。'; - this.setContext(new NotificationsContext(this)); - return await this.context.greet(); - - case 'guessing-game': - case '数当てゲーム': - this.setContext(new GuessingGameContext(this)); - return await this.context.greet(); - - default: - return hmm[Math.floor(Math.random() * hmm.length)]; - } - } - - public signin(user: IUser) { - this.user = user; - this.emit('signin', user); - this.emit('updated'); - } - - public signout() { - const user = this.user; - this.user = null; - this.emit('signout', user); - this.emit('updated'); - } - - public async refreshUser() { - this.user = await User.findOne({ - _id: this.user._id - }, { - fields: { - data: false - } - }); - - this.emit('updated'); - } - - public async showUserCommand(q: string): Promise { - try { - const user = await require('../endpoints/users/show')(parseAcct(q.substr(1)), this.user); - - const text = getUserSummary(user); - - return text; - } catch (e) { - return `問題が発生したようです...: ${e}`; - } - } -} - -abstract class Context extends EventEmitter { - protected bot: BotCore; - - public abstract async greet(): Promise; - public abstract async q(query: string): Promise; - public abstract export(): any; - - constructor(bot: BotCore) { - super(); - this.bot = bot; - } - - public static import(bot: BotCore, data: any) { - if (data.type == 'guessing-game') return GuessingGameContext.import(bot, data.content); - if (data.type == 'note') return NoteContext.import(bot, data.content); - if (data.type == 'tl') return TlContext.import(bot, data.content); - if (data.type == 'notifications') return NotificationsContext.import(bot, data.content); - if (data.type == 'signin') return SigninContext.import(bot, data.content); - return null; - } -} - -class SigninContext extends Context { - private temporaryUser: ILocalUser = null; - - public async greet(): Promise { - return 'まずユーザー名を教えてください:'; - } - - public async q(query: string): Promise { - if (this.temporaryUser == null) { - // Fetch user - const user = await User.findOne({ - usernameLower: query.toLowerCase(), - host: null - }, { - fields: { - data: false - } - }) as ILocalUser; - - if (user === null) { - return `${query}というユーザーは存在しませんでした... もう一度教えてください:`; - } else { - this.temporaryUser = user; - this.emit('updated'); - return `パスワードを教えてください:`; - } - } else { - // Compare password - const same = await bcrypt.compare(query, this.temporaryUser.password); - - if (same) { - this.bot.signin(this.temporaryUser); - this.bot.clearContext(); - return `${getUserName(this.temporaryUser)}さん、おかえりなさい!`; - } else { - return `パスワードが違います... もう一度教えてください:`; - } - } - } - - public export() { - return { - type: 'signin', - content: { - temporaryUser: this.temporaryUser - } - }; - } - - public static import(bot: BotCore, data: any) { - const context = new SigninContext(bot); - context.temporaryUser = data.temporaryUser; - return context; - } -} - -class NoteContext extends Context { - public async greet(): Promise { - return '内容:'; - } - - public async q(query: string): Promise { - await require('../endpoints/notes/create')({ - text: query - }, this.bot.user); - this.bot.clearContext(); - return '投稿しましたよ!'; - } - - public export() { - return { - type: 'note' - }; - } - - public static import(bot: BotCore, data: any) { - const context = new NoteContext(bot); - return context; - } -} - -class TlContext extends Context { - private next: string = null; - - public async greet(): Promise { - return await this.getTl(); - } - - public async q(query: string): Promise { - if (query == '次') { - return await this.getTl(); - } else { - this.bot.clearContext(); - return await this.bot.q(query); - } - } - - private async getTl() { - const tl = await require('../endpoints/notes/timeline')({ - limit: 5, - untilId: this.next ? this.next : undefined - }, this.bot.user); - - if (tl.length > 0) { - this.next = tl[tl.length - 1].id; - this.emit('updated'); - - const text = tl - .map(note => `${getUserName(note.user)}\n「${getNoteSummary(note)}」`) - .join('\n-----\n'); - - return text; - } else { - return 'タイムラインに表示するものがありません...'; - } - } - - public export() { - return { - type: 'tl', - content: { - next: this.next, - } - }; - } - - public static import(bot: BotCore, data: any) { - const context = new TlContext(bot); - context.next = data.next; - return context; - } -} - -class NotificationsContext extends Context { - private next: string = null; - - public async greet(): Promise { - return await this.getNotifications(); - } - - public async q(query: string): Promise { - if (query == '次') { - return await this.getNotifications(); - } else { - this.bot.clearContext(); - return await this.bot.q(query); - } - } - - private async getNotifications() { - const notifications = await require('../endpoints/i/notifications')({ - limit: 5, - untilId: this.next ? this.next : undefined - }, this.bot.user); - - if (notifications.length > 0) { - this.next = notifications[notifications.length - 1].id; - this.emit('updated'); - - const text = notifications - .map(notification => getNotificationSummary(notification)) - .join('\n-----\n'); - - return text; - } else { - return '通知はありません'; - } - } - - public export() { - return { - type: 'notifications', - content: { - next: this.next, - } - }; - } - - public static import(bot: BotCore, data: any) { - const context = new NotificationsContext(bot); - context.next = data.next; - return context; - } -} - -class GuessingGameContext extends Context { - private secret: number; - private history: number[] = []; - - public async greet(): Promise { - this.secret = Math.floor(Math.random() * 100); - this.emit('updated'); - return '0~100の秘密の数を当ててみてください:'; - } - - public async q(query: string): Promise { - if (query == 'やめる') { - this.bot.clearContext(); - return 'やめました。'; - } - - const guess = parseInt(query, 10); - - if (isNaN(guess)) { - return '整数で推測してください。「やめる」と言うとゲームをやめます。'; - } - - const firsttime = this.history.indexOf(guess) === -1; - - this.history.push(guess); - this.emit('updated'); - - if (this.secret < guess) { - return firsttime ? `${guess}よりも小さいですね` : `もう一度言いますが${guess}より小さいですよ`; - } else if (this.secret > guess) { - return firsttime ? `${guess}よりも大きいですね` : `もう一度言いますが${guess}より大きいですよ`; - } else { - this.bot.clearContext(); - return `正解です🎉 (${this.history.length}回目で当てました)`; - } - } - - public export() { - return { - type: 'guessing-game', - content: { - secret: this.secret, - history: this.history - } - }; - } - - public static import(bot: BotCore, data: any) { - const context = new GuessingGameContext(bot); - context.secret = data.secret; - context.history = data.history; - return context; - } -} diff --git a/src/server/api/bot/interfaces/line.ts b/src/server/api/bot/interfaces/line.ts deleted file mode 100644 index 733315391d..0000000000 --- a/src/server/api/bot/interfaces/line.ts +++ /dev/null @@ -1,238 +0,0 @@ -import * as EventEmitter from 'events'; -import * as Router from 'koa-router'; -import * as request from 'request'; -import * as crypto from 'crypto'; -import User from '../../../../models/user'; -import config from '../../../../config'; -import BotCore from '../core'; -import _redis from '../../../../db/redis'; -import prominence = require('prominence'); -import getAcct from '../../../../acct/render'; -import parseAcct from '../../../../acct/parse'; -import getNoteSummary from '../../../../renderers/get-note-summary'; -import getUserName from '../../../../renderers/get-user-name'; - -const redis = prominence(_redis); - -// SEE: https://developers.line.me/media/messaging-api/messages/sticker_list.pdf -const stickers = [ - '297', - '298', - '299', - '300', - '301', - '302', - '303', - '304', - '305', - '306', - '307' -]; - -class LineBot extends BotCore { - private replyToken: string; - - private reply(messages: any[]) { - request.post({ - url: 'https://api.line.me/v2/bot/message/reply', - headers: { - 'Authorization': `Bearer ${config.line_bot.channel_access_token}` - }, - json: { - replyToken: this.replyToken, - messages: messages - } - }, (err, res, body) => { - if (err) { - console.error(err); - return; - } - }); - } - - public async react(ev: any): Promise { - this.replyToken = ev.replyToken; - - switch (ev.type) { - // メッセージ - case 'message': - switch (ev.message.type) { - // テキスト - case 'text': - const res = await this.q(ev.message.text); - if (res == null) return; - // 返信 - this.reply([{ - type: 'text', - text: res - }]); - break; - - // スタンプ - case 'sticker': - // スタンプで返信 - this.reply([{ - type: 'sticker', - packageId: '4', - stickerId: stickers[Math.floor(Math.random() * stickers.length)] - }]); - break; - } - break; - - // noteback - case 'noteback': - const data = ev.noteback.data; - const cmd = data.split('|')[0]; - const arg = data.split('|')[1]; - switch (cmd) { - case 'showtl': - this.showUserTimelineNoteback(arg); - break; - } - break; - } - } - - public static import(data) { - const bot = new LineBot(); - bot._import(data); - return bot; - } - - public async showUserCommand(q: string) { - const user = await require('../../endpoints/users/show')(parseAcct(q.substr(1)), this.user); - - const acct = getAcct(user); - const actions = []; - - actions.push({ - type: 'noteback', - label: 'タイムラインを見る', - data: `showtl|${user.id}` - }); - - if (user.twitter) { - actions.push({ - type: 'uri', - label: 'Twitterアカウントを見る', - uri: `https://twitter.com/${user.twitter.screenName}` - }); - } - - actions.push({ - type: 'uri', - label: 'Webで見る', - uri: `${config.url}/@${acct}` - }); - - this.reply([{ - type: 'template', - altText: await super.showUserCommand(q), - template: { - type: 'buttons', - thumbnailImageUrl: `${user.avatarUrl}?thumbnail&size=1024`, - title: `${getUserName(user)} (@${acct})`, - text: user.description || '(no description)', - actions: actions - } - }]); - - return null; - } - - public async showUserTimelineNoteback(userId: string) { - const tl = await require('../../endpoints/users/notes')({ - userId: userId, - limit: 5 - }, this.user); - - const text = `${getUserName(tl[0].user)}さんのタイムラインはこちらです:\n\n` + tl - .map(note => getNoteSummary(note)) - .join('\n-----\n'); - - this.reply([{ - type: 'text', - text: text - }]); - } -} - -const handler = new EventEmitter(); - -handler.on('event', async (ev) => { - - const sourceId = ev.source.userId; - const sessionId = `line-bot-sessions:${sourceId}`; - - const session = await redis.get(sessionId); - let bot: LineBot; - - if (session == null) { - const user = await User.findOne({ - host: null, - 'line': { - userId: sourceId - } - }); - - bot = new LineBot(user); - - bot.on('signin', user => { - User.update(user._id, { - $set: { - 'line': { - userId: sourceId - } - } - }); - }); - - bot.on('signout', user => { - User.update(user._id, { - $set: { - 'line': { - userId: null - } - } - }); - }); - - redis.set(sessionId, JSON.stringify(bot.export())); - } else { - bot = LineBot.import(JSON.parse(session)); - } - - bot.on('updated', () => { - redis.set(sessionId, JSON.stringify(bot.export())); - }); - - if (session != null) bot.refreshUser(); - - bot.react(ev); -}); - -// Init router -const router = new Router(); - -if (config.line_bot) { - router.post('/hooks/line', ctx => { - const sig1 = ctx.headers['x-line-signature']; - - const hash = crypto.createHmac('SHA256', config.line_bot.channel_secret) - .update(ctx.request.rawBody); - - const sig2 = hash.digest('base64'); - - // シグネチャ比較 - if (sig1 === sig2) { - ctx.request.body.events.forEach(ev => { - handler.emit('event', ev); - }); - } else { - ctx.status = 400; - } - }); -} - -module.exports = router; diff --git a/src/server/api/common/get-host-lower.ts b/src/server/api/common/get-host-lower.ts index 550c233001..92704b4aa9 100644 --- a/src/server/api/common/get-host-lower.ts +++ b/src/server/api/common/get-host-lower.ts @@ -1,5 +1,5 @@ import { toUnicode } from 'punycode'; -export default host => { +export default (host: string) => { return toUnicode(host).toLowerCase(); }; diff --git a/src/server/api/endpoints/aggregation/posts.ts b/src/server/api/endpoints/aggregation/posts.ts index d348cadae9..9ada9cd3f5 100644 --- a/src/server/api/endpoints/aggregation/posts.ts +++ b/src/server/api/endpoints/aggregation/posts.ts @@ -1,13 +1,10 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import Note from '../../../../models/note'; /** * Aggregate notes */ -module.exports = params => new Promise(async (res, rej) => { +module.exports = (params: any) => new Promise(async (res, rej) => { // Get 'limit' parameter const [limit = 365, limitErr] = $.num.optional().range(1, 365).get(params.limit); if (limitErr) return rej('invalid limit param'); diff --git a/src/server/api/endpoints/aggregation/users.ts b/src/server/api/endpoints/aggregation/users.ts index b116c1454b..c084404d0a 100644 --- a/src/server/api/endpoints/aggregation/users.ts +++ b/src/server/api/endpoints/aggregation/users.ts @@ -1,13 +1,10 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import User from '../../../../models/user'; /** * Aggregate users */ -module.exports = params => new Promise(async (res, rej) => { +module.exports = (params: any) => new Promise(async (res, rej) => { // Get 'limit' parameter const [limit = 365, limitErr] = $.num.optional().range(1, 365).get(params.limit); if (limitErr) return rej('invalid limit param'); diff --git a/src/server/api/endpoints/aggregation/users/activity.ts b/src/server/api/endpoints/aggregation/users/activity.ts index 9109487ac6..d4c716d65b 100644 --- a/src/server/api/endpoints/aggregation/users/activity.ts +++ b/src/server/api/endpoints/aggregation/users/activity.ts @@ -1,6 +1,3 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import User from '../../../../../models/user'; import Note from '../../../../../models/note'; @@ -10,7 +7,7 @@ import Note from '../../../../../models/note'; /** * Aggregate activity of a user */ -module.exports = (params) => new Promise(async (res, rej) => { +module.exports = (params: any) => new Promise(async (res, rej) => { // Get 'limit' parameter const [limit = 365, limitErr] = $.num.optional().range(1, 365).get(params.limit); if (limitErr) return rej('invalid limit param'); @@ -73,13 +70,13 @@ module.exports = (params) => new Promise(async (res, rej) => { } } ]); - datas.forEach(data => { + datas.forEach((data: any) => { data.date = data._id; delete data._id; - data.notes = (data.data.filter(x => x.type == 'note')[0] || { count: 0 }).count; - data.renotes = (data.data.filter(x => x.type == 'renote')[0] || { count: 0 }).count; - data.replies = (data.data.filter(x => x.type == 'reply')[0] || { count: 0 }).count; + data.notes = (data.data.filter((x: any) => x.type == 'note')[0] || { count: 0 }).count; + data.renotes = (data.data.filter((x: any) => x.type == 'renote')[0] || { count: 0 }).count; + data.replies = (data.data.filter((x: any) => x.type == 'reply')[0] || { count: 0 }).count; delete data.data; }); @@ -89,7 +86,7 @@ module.exports = (params) => new Promise(async (res, rej) => { for (let i = 0; i < limit; i++) { const day = new Date(new Date().setDate(new Date().getDate() - i)); - const data = datas.filter(d => + const data = datas.filter((d: any) => d.date.year == day.getFullYear() && d.date.month == day.getMonth() + 1 && d.date.day == day.getDate() )[0]; diff --git a/src/server/api/endpoints/aggregation/users/followers.ts b/src/server/api/endpoints/aggregation/users/followers.ts index dfcaf8462f..847f376079 100644 --- a/src/server/api/endpoints/aggregation/users/followers.ts +++ b/src/server/api/endpoints/aggregation/users/followers.ts @@ -8,7 +8,7 @@ import FollowedLog from '../../../../../models/followed-log'; /** * Aggregate followers of a user */ -module.exports = (params) => new Promise(async (res, rej) => { +module.exports = (params: any) => new Promise(async (res, rej) => { // Get 'userId' parameter const [userId, userIdErr] = $.type(ID).get(params.userId); if (userIdErr) return rej('invalid userId param'); diff --git a/src/server/api/endpoints/aggregation/users/following.ts b/src/server/api/endpoints/aggregation/users/following.ts index 5f826fd71c..6c52752f98 100644 --- a/src/server/api/endpoints/aggregation/users/following.ts +++ b/src/server/api/endpoints/aggregation/users/following.ts @@ -8,7 +8,7 @@ import FollowingLog from '../../../../../models/following-log'; /** * Aggregate following of a user */ -module.exports = (params) => new Promise(async (res, rej) => { +module.exports = (params: any) => new Promise(async (res, rej) => { // Get 'userId' parameter const [userId, userIdErr] = $.type(ID).get(params.userId); if (userIdErr) return rej('invalid userId param'); diff --git a/src/server/api/endpoints/aggregation/users/post.ts b/src/server/api/endpoints/aggregation/users/post.ts index 11f9ef14cd..28ba1482bf 100644 --- a/src/server/api/endpoints/aggregation/users/post.ts +++ b/src/server/api/endpoints/aggregation/users/post.ts @@ -1,6 +1,3 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import User from '../../../../../models/user'; import Note from '../../../../../models/note'; @@ -8,7 +5,7 @@ import Note from '../../../../../models/note'; /** * Aggregate note of a user */ -module.exports = (params) => new Promise(async (res, rej) => { +module.exports = (params: any) => new Promise(async (res, rej) => { // Get 'userId' parameter const [userId, userIdErr] = $.type(ID).get(params.userId); if (userIdErr) return rej('invalid userId param'); @@ -67,13 +64,13 @@ module.exports = (params) => new Promise(async (res, rej) => { } } ]); - datas.forEach(data => { + datas.forEach((data: any) => { data.date = data._id; delete data._id; - data.notes = (data.data.filter(x => x.type == 'note')[0] || { count: 0 }).count; - data.renotes = (data.data.filter(x => x.type == 'renote')[0] || { count: 0 }).count; - data.replies = (data.data.filter(x => x.type == 'reply')[0] || { count: 0 }).count; + data.notes = (data.data.filter((x: any) => x.type == 'note')[0] || { count: 0 }).count; + data.renotes = (data.data.filter((x: any) => x.type == 'renote')[0] || { count: 0 }).count; + data.replies = (data.data.filter((x: any) => x.type == 'reply')[0] || { count: 0 }).count; delete data.data; }); @@ -83,7 +80,7 @@ module.exports = (params) => new Promise(async (res, rej) => { for (let i = 0; i < 30; i++) { const day = new Date(new Date().setDate(new Date().getDate() - i)); - const data = datas.filter(d => + const data = datas.filter((d: any) => d.date.year == day.getFullYear() && d.date.month == day.getMonth() + 1 && d.date.day == day.getDate() )[0]; diff --git a/src/server/api/endpoints/aggregation/users/reaction.ts b/src/server/api/endpoints/aggregation/users/reaction.ts index 2de2840258..adb5acfb4e 100644 --- a/src/server/api/endpoints/aggregation/users/reaction.ts +++ b/src/server/api/endpoints/aggregation/users/reaction.ts @@ -1,17 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import User from '../../../../../models/user'; import Reaction from '../../../../../models/note-reaction'; /** * Aggregate reaction of a user - * - * @param {any} params - * @return {Promise} */ -module.exports = (params) => new Promise(async (res, rej) => { +module.exports = (params: any) => new Promise(async (res, rej) => { // Get 'userId' parameter const [userId, userIdErr] = $.type(ID).get(params.userId); if (userIdErr) return rej('invalid userId param'); @@ -48,7 +42,7 @@ module.exports = (params) => new Promise(async (res, rej) => { }} ]); - datas.forEach(data => { + datas.forEach((data: any) => { data.date = data._id; delete data._id; }); @@ -58,7 +52,7 @@ module.exports = (params) => new Promise(async (res, rej) => { for (let i = 0; i < 30; i++) { const day = new Date(new Date().setDate(new Date().getDate() - i)); - const data = datas.filter(d => + const data = datas.filter((d: any) => d.date.year == day.getFullYear() && d.date.month == day.getMonth() + 1 && d.date.day == day.getDate() )[0]; diff --git a/src/server/api/endpoints/app/create.ts b/src/server/api/endpoints/app/create.ts index 553bd2381a..c7bc91a079 100644 --- a/src/server/api/endpoints/app/create.ts +++ b/src/server/api/endpoints/app/create.ts @@ -1,9 +1,7 @@ -/** - * Module dependencies - */ import rndstr from 'rndstr'; import $ from 'cafy'; import App, { isValidNameId, pack } from '../../../../models/app'; +import { ILocalUser } from '../../../../models/user'; /** * @swagger @@ -60,12 +58,8 @@ import App, { isValidNameId, pack } from '../../../../models/app'; /** * Create an app - * - * @param {any} params - * @param {any} user - * @return {Promise} */ -module.exports = async (params, user) => new Promise(async (res, rej) => { +module.exports = async (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'nameId' parameter const [nameId, nameIdErr] = $.str.pipe(isValidNameId).get(params.nameId); if (nameIdErr) return rej('invalid nameId param'); diff --git a/src/server/api/endpoints/app/name_id/available.ts b/src/server/api/endpoints/app/name_id/available.ts index 135bb7d2b4..58101a7e6a 100644 --- a/src/server/api/endpoints/app/name_id/available.ts +++ b/src/server/api/endpoints/app/name_id/available.ts @@ -40,7 +40,7 @@ import { isValidNameId } from '../../../../../models/app'; * @param {any} params * @return {Promise} */ -module.exports = async (params) => new Promise(async (res, rej) => { +module.exports = async (params: any) => new Promise(async (res, rej) => { // Get 'nameId' parameter const [nameId, nameIdErr] = $.str.pipe(isValidNameId).get(params.nameId); if (nameIdErr) return rej('invalid nameId param'); diff --git a/src/server/api/endpoints/app/show.ts b/src/server/api/endpoints/app/show.ts index 8d742ab182..2b98a3f142 100644 --- a/src/server/api/endpoints/app/show.ts +++ b/src/server/api/endpoints/app/show.ts @@ -1,8 +1,6 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; -import App, { pack } from '../../../../models/app'; +import App, { pack, IApp } from '../../../../models/app'; +import { ILocalUser } from '../../../../models/user'; /** * @swagger @@ -37,7 +35,7 @@ import App, { pack } from '../../../../models/app'; /** * Show an app */ -module.exports = (params, user, app) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser, app: IApp) => new Promise(async (res, rej) => { const isSecure = user != null && app == null; // Get 'appId' parameter diff --git a/src/server/api/endpoints/auth/accept.ts b/src/server/api/endpoints/auth/accept.ts index 695fbb0803..fc6cbc473d 100644 --- a/src/server/api/endpoints/auth/accept.ts +++ b/src/server/api/endpoints/auth/accept.ts @@ -1,12 +1,10 @@ -/** - * Module dependencies - */ import rndstr from 'rndstr'; const crypto = require('crypto'); import $ from 'cafy'; import App from '../../../../models/app'; import AuthSess from '../../../../models/auth-session'; import AccessToken from '../../../../models/access-token'; +import { ILocalUser } from '../../../../models/user'; /** * @swagger @@ -33,12 +31,8 @@ import AccessToken from '../../../../models/access-token'; /** * Accept - * - * @param {any} params - * @param {any} user - * @return {Promise} */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'token' parameter const [token, tokenErr] = $.str.get(params.token); if (tokenErr) return rej('invalid token param'); diff --git a/src/server/api/endpoints/auth/session/generate.ts b/src/server/api/endpoints/auth/session/generate.ts index d649a8d902..5a4d99ff3b 100644 --- a/src/server/api/endpoints/auth/session/generate.ts +++ b/src/server/api/endpoints/auth/session/generate.ts @@ -44,7 +44,7 @@ import config from '../../../../../config'; * @param {any} params * @return {Promise} */ -module.exports = (params) => new Promise(async (res, rej) => { +module.exports = (params: any) => new Promise(async (res, rej) => { // Get 'appSecret' parameter const [appSecret, appSecretErr] = $.str.get(params.appSecret); if (appSecretErr) return rej('invalid appSecret param'); diff --git a/src/server/api/endpoints/auth/session/show.ts b/src/server/api/endpoints/auth/session/show.ts index 434cc264a0..3d3b6bbf61 100644 --- a/src/server/api/endpoints/auth/session/show.ts +++ b/src/server/api/endpoints/auth/session/show.ts @@ -1,8 +1,6 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import AuthSess, { pack } from '../../../../../models/auth-session'; +import { ILocalUser } from '../../../../../models/user'; /** * @swagger @@ -46,12 +44,8 @@ import AuthSess, { pack } from '../../../../../models/auth-session'; /** * Show a session - * - * @param {any} params - * @param {any} user - * @return {Promise} */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'token' parameter const [token, tokenErr] = $.str.get(params.token); if (tokenErr) return rej('invalid token param'); diff --git a/src/server/api/endpoints/auth/session/userkey.ts b/src/server/api/endpoints/auth/session/userkey.ts index 3026b477f1..3ea48fbe34 100644 --- a/src/server/api/endpoints/auth/session/userkey.ts +++ b/src/server/api/endpoints/auth/session/userkey.ts @@ -49,7 +49,7 @@ import { pack } from '../../../../../models/user'; * @param {any} params * @return {Promise} */ -module.exports = (params) => new Promise(async (res, rej) => { +module.exports = (params: any) => new Promise(async (res, rej) => { // Get 'appSecret' parameter const [appSecret, appSecretErr] = $.str.get(params.appSecret); if (appSecretErr) return rej('invalid appSecret param'); diff --git a/src/server/api/endpoints/drive.ts b/src/server/api/endpoints/drive.ts index 26944dbc15..9caad273c8 100644 --- a/src/server/api/endpoints/drive.ts +++ b/src/server/api/endpoints/drive.ts @@ -1,9 +1,10 @@ import DriveFile from '../../../models/drive-file'; +import { ILocalUser } from '../../../models/user'; /** * Get drive information */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Calculate drive usage const usage = await DriveFile .aggregate([{ diff --git a/src/server/api/endpoints/drive/files.ts b/src/server/api/endpoints/drive/files.ts index 0f0e3d65a8..efce750747 100644 --- a/src/server/api/endpoints/drive/files.ts +++ b/src/server/api/endpoints/drive/files.ts @@ -1,13 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; import DriveFile, { pack } from '../../../../models/drive-file'; +import { ILocalUser } from '../../../../models/user'; /** * Get drive files */ -module.exports = async (params, user, app) => { +module.exports = async (params: any, user: ILocalUser) => { // Get 'limit' parameter const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); if (limitErr) throw 'invalid limit param'; diff --git a/src/server/api/endpoints/drive/files/create.ts b/src/server/api/endpoints/drive/files/create.ts index dd748d6bba..db2626af09 100644 --- a/src/server/api/endpoints/drive/files/create.ts +++ b/src/server/api/endpoints/drive/files/create.ts @@ -1,15 +1,13 @@ -/** - * Module dependencies - */ import * as fs from 'fs'; import $ from 'cafy'; import ID from '../../../../../cafy-id'; import { validateFileName, pack } from '../../../../../models/drive-file'; import create from '../../../../../services/drive/add-file'; +import { ILocalUser } from '../../../../../models/user'; /** * Create a file */ -module.exports = async (file, params, user): Promise => { +module.exports = async (file: any, params: any, user: ILocalUser): Promise => { if (file == null) { throw 'file is required'; } diff --git a/src/server/api/endpoints/drive/files/delete.ts b/src/server/api/endpoints/drive/files/delete.ts index a722b4e825..17eb0eb4b9 100644 --- a/src/server/api/endpoints/drive/files/delete.ts +++ b/src/server/api/endpoints/drive/files/delete.ts @@ -2,11 +2,12 @@ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import DriveFile from '../../../../../models/drive-file'; import del from '../../../../../services/drive/delete-file'; import { publishDriveStream } from '../../../../../publishers/stream'; +import { ILocalUser } from '../../../../../models/user'; /** * Delete a file */ -module.exports = async (params, user) => { +module.exports = async (params: any, user: ILocalUser) => { // Get 'fileId' parameter const [fileId, fileIdErr] = $.type(ID).get(params.fileId); if (fileIdErr) throw 'invalid fileId param'; diff --git a/src/server/api/endpoints/drive/files/find.ts b/src/server/api/endpoints/drive/files/find.ts index 98165990fe..75ab91f0a1 100644 --- a/src/server/api/endpoints/drive/files/find.ts +++ b/src/server/api/endpoints/drive/files/find.ts @@ -1,13 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import DriveFile, { pack } from '../../../../../models/drive-file'; +import { ILocalUser } from '../../../../../models/user'; /** * Find a file(s) */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'name' parameter const [name, nameErr] = $.str.get(params.name); if (nameErr) return rej('invalid name param'); diff --git a/src/server/api/endpoints/drive/files/show.ts b/src/server/api/endpoints/drive/files/show.ts index c7efda7ab0..e7dca486c5 100644 --- a/src/server/api/endpoints/drive/files/show.ts +++ b/src/server/api/endpoints/drive/files/show.ts @@ -1,13 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import DriveFile, { pack } from '../../../../../models/drive-file'; +import { ILocalUser } from '../../../../../models/user'; /** * Show a file */ -module.exports = async (params, user) => { +module.exports = async (params: any, user: ILocalUser) => { // Get 'fileId' parameter const [fileId, fileIdErr] = $.type(ID).get(params.fileId); if (fileIdErr) throw 'invalid fileId param'; diff --git a/src/server/api/endpoints/drive/files/update.ts b/src/server/api/endpoints/drive/files/update.ts index 12fa8e025d..825683b214 100644 --- a/src/server/api/endpoints/drive/files/update.ts +++ b/src/server/api/endpoints/drive/files/update.ts @@ -1,15 +1,13 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import DriveFolder from '../../../../../models/drive-folder'; import DriveFile, { validateFileName, pack } from '../../../../../models/drive-file'; import { publishDriveStream } from '../../../../../publishers/stream'; +import { ILocalUser } from '../../../../../models/user'; /** * Update a file */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'fileId' parameter const [fileId, fileIdErr] = $.type(ID).get(params.fileId); if (fileIdErr) return rej('invalid fileId param'); diff --git a/src/server/api/endpoints/drive/folders.ts b/src/server/api/endpoints/drive/folders.ts index bc6c50eb99..fac9780325 100644 --- a/src/server/api/endpoints/drive/folders.ts +++ b/src/server/api/endpoints/drive/folders.ts @@ -7,7 +7,7 @@ import DriveFolder, { pack } from '../../../../models/drive-folder'; /** * Get drive folders */ -module.exports = (params, user, app) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser, app: IApp) => new Promise(async (res, rej) => { // Get 'limit' parameter const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); if (limitErr) return rej('invalid limit param'); diff --git a/src/server/api/endpoints/drive/folders/create.ts b/src/server/api/endpoints/drive/folders/create.ts index 62e3b6f6e8..8f06b0f668 100644 --- a/src/server/api/endpoints/drive/folders/create.ts +++ b/src/server/api/endpoints/drive/folders/create.ts @@ -1,14 +1,12 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import DriveFolder, { isValidFolderName, pack } from '../../../../../models/drive-folder'; import { publishDriveStream } from '../../../../../publishers/stream'; +import { ILocalUser } from '../../../../../models/user'; /** * Create drive folder */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'name' parameter const [name = '無題のフォルダー', nameErr] = $.str.optional().pipe(isValidFolderName).get(params.name); if (nameErr) return rej('invalid name param'); diff --git a/src/server/api/endpoints/drive/folders/find.ts b/src/server/api/endpoints/drive/folders/find.ts index 9703d9e99d..b3238b5c32 100644 --- a/src/server/api/endpoints/drive/folders/find.ts +++ b/src/server/api/endpoints/drive/folders/find.ts @@ -1,13 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import DriveFolder, { pack } from '../../../../../models/drive-folder'; +import { ILocalUser } from '../../../../../models/user'; /** * Find a folder(s) */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'name' parameter const [name, nameErr] = $.str.get(params.name); if (nameErr) return rej('invalid name param'); diff --git a/src/server/api/endpoints/drive/folders/show.ts b/src/server/api/endpoints/drive/folders/show.ts index 44f1889001..c9b4930a76 100644 --- a/src/server/api/endpoints/drive/folders/show.ts +++ b/src/server/api/endpoints/drive/folders/show.ts @@ -1,13 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import DriveFolder, { pack } from '../../../../../models/drive-folder'; +import { ILocalUser } from '../../../../../models/user'; /** * Show a folder */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'folderId' parameter const [folderId, folderIdErr] = $.type(ID).get(params.folderId); if (folderIdErr) return rej('invalid folderId param'); diff --git a/src/server/api/endpoints/drive/folders/update.ts b/src/server/api/endpoints/drive/folders/update.ts index e24d8a14cd..f126c09f5b 100644 --- a/src/server/api/endpoints/drive/folders/update.ts +++ b/src/server/api/endpoints/drive/folders/update.ts @@ -1,14 +1,12 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import DriveFolder, { isValidFolderName, pack } from '../../../../../models/drive-folder'; import { publishDriveStream } from '../../../../../publishers/stream'; +import { ILocalUser } from '../../../../../models/user'; /** * Update a folder */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'folderId' parameter const [folderId, folderIdErr] = $.type(ID).get(params.folderId); if (folderIdErr) return rej('invalid folderId param'); @@ -48,7 +46,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { } // Check if the circular reference will occur - async function checkCircle(folderId) { + async function checkCircle(folderId: any): Promise { // Fetch folder const folder2 = await DriveFolder.findOne({ _id: folderId diff --git a/src/server/api/endpoints/drive/stream.ts b/src/server/api/endpoints/drive/stream.ts index 8cb3a99b42..515f74645a 100644 --- a/src/server/api/endpoints/drive/stream.ts +++ b/src/server/api/endpoints/drive/stream.ts @@ -1,13 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; import DriveFile, { pack } from '../../../../models/drive-file'; +import { ILocalUser } from '../../../../models/user'; /** * Get drive stream */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'limit' parameter const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); if (limitErr) return rej('invalid limit param'); diff --git a/src/server/api/endpoints/following/create.ts b/src/server/api/endpoints/following/create.ts index b9610658d1..3e45b8da53 100644 --- a/src/server/api/endpoints/following/create.ts +++ b/src/server/api/endpoints/following/create.ts @@ -1,15 +1,12 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; -import User, { pack } from '../../../../models/user'; +import User, { pack, ILocalUser } from '../../../../models/user'; import Following from '../../../../models/following'; import create from '../../../../services/following/create'; /** * Follow a user */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { const follower = user; // Get 'userId' parameter diff --git a/src/server/api/endpoints/following/delete.ts b/src/server/api/endpoints/following/delete.ts index 4fcdaf5a82..0af8813cf9 100644 --- a/src/server/api/endpoints/following/delete.ts +++ b/src/server/api/endpoints/following/delete.ts @@ -1,15 +1,12 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; -import User, { pack } from '../../../../models/user'; +import User, { pack, ILocalUser } from '../../../../models/user'; import Following from '../../../../models/following'; import deleteFollowing from '../../../../services/following/delete'; /** * Unfollow a user */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { const follower = user; // Get 'userId' parameter diff --git a/src/server/api/endpoints/following/requests/accept.ts b/src/server/api/endpoints/following/requests/accept.ts index 705d3b161a..a09e32e4d9 100644 --- a/src/server/api/endpoints/following/requests/accept.ts +++ b/src/server/api/endpoints/following/requests/accept.ts @@ -1,11 +1,11 @@ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import acceptFollowRequest from '../../../../../services/following/requests/accept'; -import User from '../../../../../models/user'; +import User, { ILocalUser } from '../../../../../models/user'; /** * Accept a follow request */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'userId' parameter const [followerId, followerIdErr] = $.type(ID).get(params.userId); if (followerIdErr) return rej('invalid userId param'); diff --git a/src/server/api/endpoints/following/requests/cancel.ts b/src/server/api/endpoints/following/requests/cancel.ts index 388a54890b..f2a40854c2 100644 --- a/src/server/api/endpoints/following/requests/cancel.ts +++ b/src/server/api/endpoints/following/requests/cancel.ts @@ -1,11 +1,11 @@ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import cancelFollowRequest from '../../../../../services/following/requests/cancel'; -import User, { pack } from '../../../../../models/user'; +import User, { pack, ILocalUser } from '../../../../../models/user'; /** * Cancel a follow request */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'userId' parameter const [followeeId, followeeIdErr] = $.type(ID).get(params.userId); if (followeeIdErr) return rej('invalid userId param'); diff --git a/src/server/api/endpoints/following/requests/list.ts b/src/server/api/endpoints/following/requests/list.ts index e8364335d1..287c5a8e46 100644 --- a/src/server/api/endpoints/following/requests/list.ts +++ b/src/server/api/endpoints/following/requests/list.ts @@ -1,10 +1,11 @@ //import $ from 'cafy'; import ID from '../../../../../cafy-id'; import FollowRequest, { pack } from '../../../../../models/follow-request'; +import { ILocalUser } from '../../../../../models/user'; /** * Get all pending received follow requests */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { const reqs = await FollowRequest.find({ followeeId: user._id }); diff --git a/src/server/api/endpoints/following/requests/reject.ts b/src/server/api/endpoints/following/requests/reject.ts index 1cfb562b55..69dddf1355 100644 --- a/src/server/api/endpoints/following/requests/reject.ts +++ b/src/server/api/endpoints/following/requests/reject.ts @@ -1,11 +1,11 @@ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import rejectFollowRequest from '../../../../../services/following/requests/reject'; -import User from '../../../../../models/user'; +import User, { ILocalUser } from '../../../../../models/user'; /** * Reject a follow request */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'userId' parameter const [followerId, followerIdErr] = $.type(ID).get(params.userId); if (followerIdErr) return rej('invalid userId param'); diff --git a/src/server/api/endpoints/following/stalk.ts b/src/server/api/endpoints/following/stalk.ts index f0bc8cbdfc..b9d19d57b4 100644 --- a/src/server/api/endpoints/following/stalk.ts +++ b/src/server/api/endpoints/following/stalk.ts @@ -1,10 +1,11 @@ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Following from '../../../../models/following'; +import { ILocalUser } from '../../../../models/user'; /** * Stalk a user */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { const follower = user; // Get 'userId' parameter diff --git a/src/server/api/endpoints/following/unstalk.ts b/src/server/api/endpoints/following/unstalk.ts index 0d0a018c34..255f22ca1f 100644 --- a/src/server/api/endpoints/following/unstalk.ts +++ b/src/server/api/endpoints/following/unstalk.ts @@ -1,10 +1,11 @@ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Following from '../../../../models/following'; +import { ILocalUser } from '../../../../models/user'; /** * Unstalk a user */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { const follower = user; // Get 'userId' parameter diff --git a/src/server/api/endpoints/hashtags/trend.ts b/src/server/api/endpoints/hashtags/trend.ts index 947dd6e448..17af9d6a9a 100644 --- a/src/server/api/endpoints/hashtags/trend.ts +++ b/src/server/api/endpoints/hashtags/trend.ts @@ -45,7 +45,10 @@ module.exports = () => new Promise(async (res, rej) => { return res([]); } - const tags = []; + const tags: Array<{ + name: string; + count: number; + }> = []; // カウント data.map(x => x._id).forEach(x => { diff --git a/src/server/api/endpoints/i.ts b/src/server/api/endpoints/i.ts index 379c3c4d88..5c769a02fd 100644 --- a/src/server/api/endpoints/i.ts +++ b/src/server/api/endpoints/i.ts @@ -1,12 +1,10 @@ -/** - * Module dependencies - */ -import User, { pack } from '../../../models/user'; +import User, { pack, ILocalUser } from '../../../models/user'; +import { IApp } from '../../../models/app'; /** * Show myself */ -module.exports = (params, user, app) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser, app: IApp) => new Promise(async (res, rej) => { const isSecure = user != null && app == null; // Serialize diff --git a/src/server/api/endpoints/i/2fa/done.ts b/src/server/api/endpoints/i/2fa/done.ts index 1a2706aa84..61f13c4c61 100644 --- a/src/server/api/endpoints/i/2fa/done.ts +++ b/src/server/api/endpoints/i/2fa/done.ts @@ -1,11 +1,8 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import * as speakeasy from 'speakeasy'; -import User from '../../../../../models/user'; +import User, { ILocalUser } from '../../../../../models/user'; -module.exports = async (params, user) => new Promise(async (res, rej) => { +module.exports = async (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'token' parameter const [token, tokenErr] = $.str.get(params.token); if (tokenErr) return rej('invalid token param'); diff --git a/src/server/api/endpoints/i/2fa/register.ts b/src/server/api/endpoints/i/2fa/register.ts index d314e1a280..d05892c84b 100644 --- a/src/server/api/endpoints/i/2fa/register.ts +++ b/src/server/api/endpoints/i/2fa/register.ts @@ -1,14 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import * as speakeasy from 'speakeasy'; import * as QRCode from 'qrcode'; -import User from '../../../../../models/user'; +import User, { ILocalUser } from '../../../../../models/user'; import config from '../../../../../config'; -module.exports = async (params, user) => new Promise(async (res, rej) => { +module.exports = async (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'password' parameter const [password, passwordErr] = $.str.get(params.password); if (passwordErr) return rej('invalid password param'); diff --git a/src/server/api/endpoints/i/2fa/unregister.ts b/src/server/api/endpoints/i/2fa/unregister.ts index 336a3564ab..fc197cb1e4 100644 --- a/src/server/api/endpoints/i/2fa/unregister.ts +++ b/src/server/api/endpoints/i/2fa/unregister.ts @@ -1,11 +1,8 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; -import User from '../../../../../models/user'; +import User, { ILocalUser } from '../../../../../models/user'; -module.exports = async (params, user) => new Promise(async (res, rej) => { +module.exports = async (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'password' parameter const [password, passwordErr] = $.str.get(params.password); if (passwordErr) return rej('invalid password param'); diff --git a/src/server/api/endpoints/i/authorized_apps.ts b/src/server/api/endpoints/i/authorized_apps.ts index d15bd67bf2..cfc93c1518 100644 --- a/src/server/api/endpoints/i/authorized_apps.ts +++ b/src/server/api/endpoints/i/authorized_apps.ts @@ -1,14 +1,12 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import AccessToken from '../../../../models/access-token'; import { pack } from '../../../../models/app'; +import { ILocalUser } from '../../../../models/user'; /** * Get authorized apps of my account */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'limit' parameter const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); if (limitErr) return rej('invalid limit param'); diff --git a/src/server/api/endpoints/i/change_password.ts b/src/server/api/endpoints/i/change_password.ts index a1a1a43406..9851fa895a 100644 --- a/src/server/api/endpoints/i/change_password.ts +++ b/src/server/api/endpoints/i/change_password.ts @@ -1,14 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; -import User from '../../../../models/user'; +import User, { ILocalUser } from '../../../../models/user'; /** * Change password */ -module.exports = async (params, user) => new Promise(async (res, rej) => { +module.exports = async (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'currentPasword' parameter const [currentPassword, currentPasswordErr] = $.str.get(params.currentPasword); if (currentPasswordErr) return rej('invalid currentPasword param'); diff --git a/src/server/api/endpoints/i/favorites.ts b/src/server/api/endpoints/i/favorites.ts index 23517baaff..dc343afaed 100644 --- a/src/server/api/endpoints/i/favorites.ts +++ b/src/server/api/endpoints/i/favorites.ts @@ -1,13 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Favorite, { pack } from '../../../../models/favorite'; +import { ILocalUser } from '../../../../models/user'; /** * Get favorited notes */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'limit' parameter const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); if (limitErr) return rej('invalid limit param'); diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index ba9c47508c..ce283fe48f 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -1,17 +1,15 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Notification from '../../../../models/notification'; import Mute from '../../../../models/mute'; import { pack } from '../../../../models/notification'; import { getFriendIds } from '../../common/get-friends'; import read from '../../common/read-notification'; +import { ILocalUser } from '../../../../models/user'; /** * Get notifications */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'following' parameter const [following = false, followingError] = $.bool.optional().get(params.following); diff --git a/src/server/api/endpoints/i/pin.ts b/src/server/api/endpoints/i/pin.ts index 423f0ac4ae..7f4a45e1f5 100644 --- a/src/server/api/endpoints/i/pin.ts +++ b/src/server/api/endpoints/i/pin.ts @@ -1,15 +1,12 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; -import User from '../../../../models/user'; +import User, { ILocalUser } from '../../../../models/user'; import Note from '../../../../models/note'; import { pack } from '../../../../models/user'; /** * Pin note */ -module.exports = async (params, user) => new Promise(async (res, rej) => { +module.exports = async (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'noteId' parameter const [noteId, noteIdErr] = $.type(ID).get(params.noteId); if (noteIdErr) return rej('invalid noteId param'); diff --git a/src/server/api/endpoints/i/regenerate_token.ts b/src/server/api/endpoints/i/regenerate_token.ts index 6e1e571297..3ffab5428e 100644 --- a/src/server/api/endpoints/i/regenerate_token.ts +++ b/src/server/api/endpoints/i/regenerate_token.ts @@ -1,16 +1,13 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; -import User from '../../../../models/user'; +import User, { ILocalUser } from '../../../../models/user'; import event from '../../../../publishers/stream'; import generateUserToken from '../../common/generate-native-user-token'; /** * Regenerate native token */ -module.exports = async (params, user) => new Promise(async (res, rej) => { +module.exports = async (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'password' parameter const [password, passwordErr] = $.str.get(params.password); if (passwordErr) return rej('invalid password param'); diff --git a/src/server/api/endpoints/i/signin_history.ts b/src/server/api/endpoints/i/signin_history.ts index 63a74b41b1..4ab9881f34 100644 --- a/src/server/api/endpoints/i/signin_history.ts +++ b/src/server/api/endpoints/i/signin_history.ts @@ -1,13 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Signin, { pack } from '../../../../models/signin'; +import { ILocalUser } from '../../../../models/user'; /** * Get signin history of my account */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'limit' parameter const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); if (limitErr) return rej('invalid limit param'); diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts index 1a1da997c9..57b050ebc4 100644 --- a/src/server/api/endpoints/i/update.ts +++ b/src/server/api/endpoints/i/update.ts @@ -1,16 +1,14 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; -import User, { isValidName, isValidDescription, isValidLocation, isValidBirthday, pack } from '../../../../models/user'; +import User, { isValidName, isValidDescription, isValidLocation, isValidBirthday, pack, ILocalUser } from '../../../../models/user'; import event from '../../../../publishers/stream'; import DriveFile from '../../../../models/drive-file'; import acceptAllFollowRequests from '../../../../services/following/requests/accept-all'; +import { IApp } from '../../../../models/app'; /** * Update myself */ -module.exports = async (params, user, app) => new Promise(async (res, rej) => { +module.exports = async (params: any, user: ILocalUser, app: IApp) => new Promise(async (res, rej) => { const isSecure = user != null && app == null; const updates = {} as any; diff --git a/src/server/api/endpoints/i/update_client_setting.ts b/src/server/api/endpoints/i/update_client_setting.ts index e91d7565fd..6d6e8ed24a 100644 --- a/src/server/api/endpoints/i/update_client_setting.ts +++ b/src/server/api/endpoints/i/update_client_setting.ts @@ -1,14 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; -import User from '../../../../models/user'; +import User, { ILocalUser } from '../../../../models/user'; import event from '../../../../publishers/stream'; /** * Update myself */ -module.exports = async (params, user) => new Promise(async (res, rej) => { +module.exports = async (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'name' parameter const [name, nameErr] = $.str.get(params.name); if (nameErr) return rej('invalid name param'); @@ -17,7 +14,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => { const [value, valueErr] = $.any.nullable().get(params.value); if (valueErr) return rej('invalid value param'); - const x = {}; + const x: any = {}; x[`clientSettings.${name}`] = value; await User.update(user._id, { diff --git a/src/server/api/endpoints/i/update_home.ts b/src/server/api/endpoints/i/update_home.ts index 48f6dbbb7a..511a647d88 100644 --- a/src/server/api/endpoints/i/update_home.ts +++ b/src/server/api/endpoints/i/update_home.ts @@ -1,8 +1,8 @@ import $ from 'cafy'; -import User from '../../../../models/user'; +import User, { ILocalUser } from '../../../../models/user'; import event from '../../../../publishers/stream'; -module.exports = async (params, user) => new Promise(async (res, rej) => { +module.exports = async (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'home' parameter const [home, homeErr] = $.arr( $.obj.strict() diff --git a/src/server/api/endpoints/i/update_mobile_home.ts b/src/server/api/endpoints/i/update_mobile_home.ts index d285a0a72d..b1f25624fd 100644 --- a/src/server/api/endpoints/i/update_mobile_home.ts +++ b/src/server/api/endpoints/i/update_mobile_home.ts @@ -1,8 +1,8 @@ import $ from 'cafy'; -import User from '../../../../models/user'; +import User, { ILocalUser } from '../../../../models/user'; import event from '../../../../publishers/stream'; -module.exports = async (params, user) => new Promise(async (res, rej) => { +module.exports = async (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'home' parameter const [home, homeErr] = $.arr( $.obj.strict() diff --git a/src/server/api/endpoints/i/update_widget.ts b/src/server/api/endpoints/i/update_widget.ts index b37761bde1..82bb04d1f4 100644 --- a/src/server/api/endpoints/i/update_widget.ts +++ b/src/server/api/endpoints/i/update_widget.ts @@ -1,8 +1,8 @@ import $ from 'cafy'; -import User from '../../../../models/user'; +import User, { ILocalUser } from '../../../../models/user'; import event from '../../../../publishers/stream'; -module.exports = async (params, user) => new Promise(async (res, rej) => { +module.exports = async (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'id' parameter const [id, idErr] = $.str.get(params.id); if (idErr) return rej('invalid id param'); @@ -18,7 +18,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => { //#region Desktop home if (widget == null && user.clientSettings.home) { const desktopHome = user.clientSettings.home; - widget = desktopHome.find(w => w.id == id); + widget = desktopHome.find((w: any) => w.id == id); if (widget) { widget.data = data; @@ -34,7 +34,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => { //#region Mobile home if (widget == null && user.clientSettings.mobileHome) { const mobileHome = user.clientSettings.mobileHome; - widget = mobileHome.find(w => w.id == id); + widget = mobileHome.find((w: any) => w.id == id); if (widget) { widget.data = data; @@ -50,8 +50,8 @@ module.exports = async (params, user) => new Promise(async (res, rej) => { //#region Deck if (widget == null && user.clientSettings.deck && user.clientSettings.deck.columns) { const deck = user.clientSettings.deck; - deck.columns.filter(c => c.type == 'widgets').forEach(c => { - c.widgets.forEach(w => { + deck.columns.filter((c: any) => c.type == 'widgets').forEach((c: any) => { + c.widgets.forEach((w: any) => { if (w.id == id) widget = w; }); }); diff --git a/src/server/api/endpoints/messaging/history.ts b/src/server/api/endpoints/messaging/history.ts index ec97642f17..713ba9dd7f 100644 --- a/src/server/api/endpoints/messaging/history.ts +++ b/src/server/api/endpoints/messaging/history.ts @@ -1,15 +1,13 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import History from '../../../../models/messaging-history'; import Mute from '../../../../models/mute'; import { pack } from '../../../../models/messaging-message'; +import { ILocalUser } from '../../../../models/user'; /** * Show messaging history */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'limit' parameter const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); if (limitErr) return rej('invalid limit param'); diff --git a/src/server/api/endpoints/messaging/messages.ts b/src/server/api/endpoints/messaging/messages.ts index 9c3a48334b..3eb20ec06b 100644 --- a/src/server/api/endpoints/messaging/messages.ts +++ b/src/server/api/endpoints/messaging/messages.ts @@ -1,13 +1,13 @@ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Message from '../../../../models/messaging-message'; -import User from '../../../../models/user'; +import User, { ILocalUser } from '../../../../models/user'; import { pack } from '../../../../models/messaging-message'; import read from '../../common/read-messaging-message'; /** * Get messages */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'userId' parameter const [recipientId, recipientIdErr] = $.type(ID).get(params.userId); if (recipientIdErr) return rej('invalid userId param'); diff --git a/src/server/api/endpoints/messaging/messages/create.ts b/src/server/api/endpoints/messaging/messages/create.ts index 41238de1e1..b3e4f6a8cd 100644 --- a/src/server/api/endpoints/messaging/messages/create.ts +++ b/src/server/api/endpoints/messaging/messages/create.ts @@ -1,11 +1,8 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import Message from '../../../../../models/messaging-message'; import { isValidText } from '../../../../../models/messaging-message'; import History from '../../../../../models/messaging-history'; -import User from '../../../../../models/user'; +import User, { ILocalUser } from '../../../../../models/user'; import Mute from '../../../../../models/mute'; import DriveFile from '../../../../../models/drive-file'; import { pack } from '../../../../../models/messaging-message'; @@ -17,7 +14,7 @@ import config from '../../../../../config'; /** * Create a message */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'userId' parameter const [recipientId, recipientIdErr] = $.type(ID).get(params.userId); if (recipientIdErr) return rej('invalid userId param'); diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts index 0e9ecf47df..ce460d0b8b 100644 --- a/src/server/api/endpoints/meta.ts +++ b/src/server/api/endpoints/meta.ts @@ -38,7 +38,7 @@ const client = require('../../../../built/client/meta.json'); /** * Show core info */ -module.exports = (params) => new Promise(async (res, rej) => { +module.exports = (params: any) => new Promise(async (res, rej) => { const meta: any = (await Meta.findOne()) || {}; res({ diff --git a/src/server/api/endpoints/mute/create.ts b/src/server/api/endpoints/mute/create.ts index 534020c671..415745e2c3 100644 --- a/src/server/api/endpoints/mute/create.ts +++ b/src/server/api/endpoints/mute/create.ts @@ -1,14 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; -import User from '../../../../models/user'; +import User, { ILocalUser } from '../../../../models/user'; import Mute from '../../../../models/mute'; /** * Mute a user */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { const muter = user; // Get 'userId' parameter diff --git a/src/server/api/endpoints/mute/delete.ts b/src/server/api/endpoints/mute/delete.ts index 949aff64ba..2d1d286585 100644 --- a/src/server/api/endpoints/mute/delete.ts +++ b/src/server/api/endpoints/mute/delete.ts @@ -1,14 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; -import User from '../../../../models/user'; +import User, { ILocalUser } from '../../../../models/user'; import Mute from '../../../../models/mute'; /** * Unmute a user */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { const muter = user; // Get 'userId' parameter diff --git a/src/server/api/endpoints/mute/list.ts b/src/server/api/endpoints/mute/list.ts index cf89f7e959..8b0171be33 100644 --- a/src/server/api/endpoints/mute/list.ts +++ b/src/server/api/endpoints/mute/list.ts @@ -1,15 +1,12 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Mute from '../../../../models/mute'; -import { pack } from '../../../../models/user'; +import { pack, ILocalUser } from '../../../../models/user'; import { getFriendIds } from '../../common/get-friends'; /** * Get muted users of a user */ -module.exports = (params, me) => new Promise(async (res, rej) => { +module.exports = (params: any, me: ILocalUser) => new Promise(async (res, rej) => { // Get 'iknow' parameter const [iknow = false, iknowErr] = $.bool.optional().get(params.iknow); if (iknowErr) return rej('invalid iknow param'); diff --git a/src/server/api/endpoints/my/apps.ts b/src/server/api/endpoints/my/apps.ts index 086e0b8965..7687afd0c7 100644 --- a/src/server/api/endpoints/my/apps.ts +++ b/src/server/api/endpoints/my/apps.ts @@ -1,13 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import App, { pack } from '../../../../models/app'; +import { ILocalUser } from '../../../../models/user'; /** * Get my apps */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'limit' parameter const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); if (limitErr) return rej('invalid limit param'); diff --git a/src/server/api/endpoints/notes.ts b/src/server/api/endpoints/notes.ts index e6fe80ac8a..5554e53aa4 100644 --- a/src/server/api/endpoints/notes.ts +++ b/src/server/api/endpoints/notes.ts @@ -7,7 +7,7 @@ import Note, { pack } from '../../../models/note'; /** * Get all notes */ -module.exports = (params) => new Promise(async (res, rej) => { +module.exports = (params: any) => new Promise(async (res, rej) => { // Get 'local' parameter const [local, localErr] = $.bool.optional().get(params.local); if (localErr) return rej('invalid local param'); diff --git a/src/server/api/endpoints/notes/conversation.ts b/src/server/api/endpoints/notes/conversation.ts index 02f7229ccf..b2bc6a2e72 100644 --- a/src/server/api/endpoints/notes/conversation.ts +++ b/src/server/api/endpoints/notes/conversation.ts @@ -1,13 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; -import Note, { pack } from '../../../../models/note'; +import Note, { pack, INote } from '../../../../models/note'; +import { ILocalUser } from '../../../../models/user'; /** * Show conversation of a note */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'noteId' parameter const [noteId, noteIdErr] = $.type(ID).get(params.noteId); if (noteIdErr) return rej('invalid noteId param'); @@ -29,10 +27,10 @@ module.exports = (params, user) => new Promise(async (res, rej) => { return rej('note not found'); } - const conversation = []; + const conversation: INote[] = []; let i = 0; - async function get(id) { + async function get(id: any) { i++; const p = await Note.findOne({ _id: id }); diff --git a/src/server/api/endpoints/notes/create.ts b/src/server/api/endpoints/notes/create.ts index 446764e1d6..64f3b5ce26 100644 --- a/src/server/api/endpoints/notes/create.ts +++ b/src/server/api/endpoints/notes/create.ts @@ -1,9 +1,6 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Note, { INote, isValidText, isValidCw, pack } from '../../../../models/note'; -import User, { ILocalUser } from '../../../../models/user'; +import User, { ILocalUser, IUser } from '../../../../models/user'; import DriveFile from '../../../../models/drive-file'; import create from '../../../../services/note/create'; import { IApp } from '../../../../models/app'; @@ -11,7 +8,7 @@ import { IApp } from '../../../../models/app'; /** * Create a note */ -module.exports = (params, user: ILocalUser, app: IApp) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser, app: IApp) => new Promise(async (res, rej) => { // Get 'visibility' parameter const [visibility = 'public', visibilityErr] = $.str.optional().or(['public', 'home', 'followers', 'specified', 'private']).get(params.visibility); if (visibilityErr) return rej('invalid visibility'); @@ -20,7 +17,7 @@ module.exports = (params, user: ILocalUser, app: IApp) => new Promise(async (res const [visibleUserIds, visibleUserIdsErr] = $.arr($.type(ID)).optional().unique().min(1).get(params.visibleUserIds); if (visibleUserIdsErr) return rej('invalid visibleUserIds'); - let visibleUsers = []; + let visibleUsers: IUser[] = []; if (visibleUserIds !== undefined) { visibleUsers = await Promise.all(visibleUserIds.map(id => User.findOne({ _id: id @@ -132,7 +129,7 @@ module.exports = (params, user: ILocalUser, app: IApp) => new Promise(async (res if (pollErr) return rej('invalid poll'); if (poll) { - (poll as any).choices = (poll as any).choices.map((choice, i) => ({ + (poll as any).choices = (poll as any).choices.map((choice: string, i: number) => ({ id: i, // IDを付与 text: choice.trim(), votes: 0 diff --git a/src/server/api/endpoints/notes/delete.ts b/src/server/api/endpoints/notes/delete.ts index 9bbb1541d6..70bcdbaab9 100644 --- a/src/server/api/endpoints/notes/delete.ts +++ b/src/server/api/endpoints/notes/delete.ts @@ -1,11 +1,12 @@ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Note from '../../../../models/note'; import deleteNote from '../../../../services/note/delete'; +import { ILocalUser } from '../../../../models/user'; /** * Delete a note */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'noteId' parameter const [noteId, noteIdErr] = $.type(ID).get(params.noteId); if (noteIdErr) return rej('invalid noteId param'); diff --git a/src/server/api/endpoints/notes/favorites/create.ts b/src/server/api/endpoints/notes/favorites/create.ts index 6832b52f75..23f7ac5f8d 100644 --- a/src/server/api/endpoints/notes/favorites/create.ts +++ b/src/server/api/endpoints/notes/favorites/create.ts @@ -1,14 +1,12 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import Favorite from '../../../../../models/favorite'; import Note from '../../../../../models/note'; +import { ILocalUser } from '../../../../../models/user'; /** * Favorite a note */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'noteId' parameter const [noteId, noteIdErr] = $.type(ID).get(params.noteId); if (noteIdErr) return rej('invalid noteId param'); diff --git a/src/server/api/endpoints/notes/favorites/delete.ts b/src/server/api/endpoints/notes/favorites/delete.ts index 07112dae15..7d2d2b4cb5 100644 --- a/src/server/api/endpoints/notes/favorites/delete.ts +++ b/src/server/api/endpoints/notes/favorites/delete.ts @@ -1,14 +1,12 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import Favorite from '../../../../../models/favorite'; import Note from '../../../../../models/note'; +import { ILocalUser } from '../../../../../models/user'; /** * Unfavorite a note */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'noteId' parameter const [noteId, noteIdErr] = $.type(ID).get(params.noteId); if (noteIdErr) return rej('invalid noteId param'); diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts index 2d4f2b6368..24ffdbcba7 100644 --- a/src/server/api/endpoints/notes/global-timeline.ts +++ b/src/server/api/endpoints/notes/global-timeline.ts @@ -1,15 +1,13 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Note from '../../../../models/note'; import Mute from '../../../../models/mute'; import { pack } from '../../../../models/note'; +import { ILocalUser } from '../../../../models/user'; /** * Get timeline of global */ -module.exports = async (params, user) => { +module.exports = async (params: any, user: ILocalUser) => { // Get 'limit' parameter const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); if (limitErr) throw 'invalid limit param'; diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts index 734cc9af0a..48490638d2 100644 --- a/src/server/api/endpoints/notes/local-timeline.ts +++ b/src/server/api/endpoints/notes/local-timeline.ts @@ -1,15 +1,13 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Note from '../../../../models/note'; import Mute from '../../../../models/mute'; import { pack } from '../../../../models/note'; +import { ILocalUser } from '../../../../models/user'; /** * Get timeline of local */ -module.exports = async (params, user) => { +module.exports = async (params: any, user: ILocalUser) => { // Get 'limit' parameter const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); if (limitErr) throw 'invalid limit param'; diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts index 163a6b4866..45511603af 100644 --- a/src/server/api/endpoints/notes/mentions.ts +++ b/src/server/api/endpoints/notes/mentions.ts @@ -1,19 +1,13 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Note from '../../../../models/note'; import { getFriendIds } from '../../common/get-friends'; import { pack } from '../../../../models/note'; +import { ILocalUser } from '../../../../models/user'; /** * Get mentions of myself - * - * @param {any} params - * @param {any} user - * @return {Promise} */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'following' parameter const [following = false, followingError] = $.bool.optional().get(params.following); diff --git a/src/server/api/endpoints/notes/polls/recommendation.ts b/src/server/api/endpoints/notes/polls/recommendation.ts index a272378d19..640140c3d1 100644 --- a/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/src/server/api/endpoints/notes/polls/recommendation.ts @@ -1,14 +1,12 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import Vote from '../../../../../models/poll-vote'; import Note, { pack } from '../../../../../models/note'; +import { ILocalUser } from '../../../../../models/user'; /** * Get recommended polls */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'limit' parameter const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); if (limitErr) return rej('invalid limit param'); diff --git a/src/server/api/endpoints/notes/polls/vote.ts b/src/server/api/endpoints/notes/polls/vote.ts index f8f4515308..72ac6bb202 100644 --- a/src/server/api/endpoints/notes/polls/vote.ts +++ b/src/server/api/endpoints/notes/polls/vote.ts @@ -1,6 +1,3 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import Vote from '../../../../../models/poll-vote'; import Note from '../../../../../models/note'; @@ -8,11 +5,12 @@ import Watching from '../../../../../models/note-watching'; import watch from '../../../../../services/note/watch'; import { publishNoteStream } from '../../../../../publishers/stream'; import notify from '../../../../../publishers/notify'; +import { ILocalUser } from '../../../../../models/user'; /** * Vote poll of a note */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'noteId' parameter const [noteId, noteIdErr] = $.type(ID).get(params.noteId); if (noteIdErr) return rej('invalid noteId param'); @@ -58,8 +56,8 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Send response res(); - const inc = {}; - inc[`poll.choices.${findWithAttr(note.poll.choices, 'id', choice)}.votes`] = 1; + const inc: any = {}; + inc[`poll.choices.${note.poll.choices.findIndex(c => c.id == choice)}.votes`] = 1; // Increment votes count await Note.update({ _id: note._id }, { @@ -100,12 +98,3 @@ module.exports = (params, user) => new Promise(async (res, rej) => { watch(user._id, note); } }); - -function findWithAttr(array, attr, value) { - for (let i = 0; i < array.length; i += 1) { - if (array[i][attr] === value) { - return i; - } - } - return -1; -} diff --git a/src/server/api/endpoints/notes/reactions.ts b/src/server/api/endpoints/notes/reactions.ts index 4ad952a7a1..d3b2d43432 100644 --- a/src/server/api/endpoints/notes/reactions.ts +++ b/src/server/api/endpoints/notes/reactions.ts @@ -1,18 +1,12 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Note from '../../../../models/note'; import Reaction, { pack } from '../../../../models/note-reaction'; +import { ILocalUser } from '../../../../models/user'; /** * Show reactions of a note - * - * @param {any} params - * @param {any} user - * @return {Promise} */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'noteId' parameter const [noteId, noteIdErr] = $.type(ID).get(params.noteId); if (noteIdErr) return rej('invalid noteId param'); diff --git a/src/server/api/endpoints/notes/reactions/create.ts b/src/server/api/endpoints/notes/reactions/create.ts index 21757cb427..33e457e308 100644 --- a/src/server/api/endpoints/notes/reactions/create.ts +++ b/src/server/api/endpoints/notes/reactions/create.ts @@ -1,15 +1,13 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import Note from '../../../../../models/note'; import create from '../../../../../services/note/reaction/create'; import { validateReaction } from '../../../../../models/note-reaction'; +import { ILocalUser } from '../../../../../models/user'; /** * React to a note */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'noteId' parameter const [noteId, noteIdErr] = $.type(ID).get(params.noteId); if (noteIdErr) return rej('invalid noteId param'); diff --git a/src/server/api/endpoints/notes/reactions/delete.ts b/src/server/api/endpoints/notes/reactions/delete.ts index afb8629112..1f2d662511 100644 --- a/src/server/api/endpoints/notes/reactions/delete.ts +++ b/src/server/api/endpoints/notes/reactions/delete.ts @@ -1,14 +1,12 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import Reaction from '../../../../../models/note-reaction'; import Note from '../../../../../models/note'; +import { ILocalUser } from '../../../../../models/user'; /** * Unreact to a note */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'noteId' parameter const [noteId, noteIdErr] = $.type(ID).get(params.noteId); if (noteIdErr) return rej('invalid noteId param'); @@ -45,7 +43,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Send response res(); - const dec = {}; + const dec: any = {}; dec[`reactionCounts.${exist.reaction}`] = -1; // Decrement reactions count diff --git a/src/server/api/endpoints/notes/replies.ts b/src/server/api/endpoints/notes/replies.ts index 608027f6b1..4aaf1d322b 100644 --- a/src/server/api/endpoints/notes/replies.ts +++ b/src/server/api/endpoints/notes/replies.ts @@ -1,10 +1,11 @@ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Note, { pack } from '../../../../models/note'; +import { ILocalUser } from '../../../../models/user'; /** * Get replies of a note */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'noteId' parameter const [noteId, noteIdErr] = $.type(ID).get(params.noteId); if (noteIdErr) return rej('invalid noteId param'); diff --git a/src/server/api/endpoints/notes/reposts.ts b/src/server/api/endpoints/notes/reposts.ts index 3098211b61..ea3f174e1a 100644 --- a/src/server/api/endpoints/notes/reposts.ts +++ b/src/server/api/endpoints/notes/reposts.ts @@ -1,17 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Note, { pack } from '../../../../models/note'; +import { ILocalUser } from '../../../../models/user'; /** * Show a renotes of a note - * - * @param {any} params - * @param {any} user - * @return {Promise} */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'noteId' parameter const [noteId, noteIdErr] = $.type(ID).get(params.noteId); if (noteIdErr) return rej('invalid noteId param'); diff --git a/src/server/api/endpoints/notes/search.ts b/src/server/api/endpoints/notes/search.ts deleted file mode 100644 index 9705dcfd6e..0000000000 --- a/src/server/api/endpoints/notes/search.ts +++ /dev/null @@ -1,364 +0,0 @@ -/** - * Module dependencies - */ -import $ from 'cafy'; import ID from '../../../../cafy-id'; -const escapeRegexp = require('escape-regexp'); -import Note from '../../../../models/note'; -import User from '../../../../models/user'; -import Mute from '../../../../models/mute'; -import { getFriendIds } from '../../common/get-friends'; -import { pack } from '../../../../models/note'; - -/** - * Search a note - * - * @param {any} params - * @param {any} me - * @return {Promise} - */ -module.exports = (params, me) => new Promise(async (res, rej) => { - // Get 'text' parameter - const [text, textError] = $.str.optional().get(params.text); - if (textError) return rej('invalid text param'); - - // Get 'includeUserIds' parameter - const [includeUserIds = [], includeUserIdsErr] = $.arr($.type(ID)).optional().get(params.includeUserIds); - if (includeUserIdsErr) return rej('invalid includeUserIds param'); - - // Get 'excludeUserIds' parameter - const [excludeUserIds = [], excludeUserIdsErr] = $.arr($.type(ID)).optional().get(params.excludeUserIds); - if (excludeUserIdsErr) return rej('invalid excludeUserIds param'); - - // Get 'includeUserUsernames' parameter - const [includeUserUsernames = [], includeUserUsernamesErr] = $.arr($.str).optional().get(params.includeUserUsernames); - if (includeUserUsernamesErr) return rej('invalid includeUserUsernames param'); - - // Get 'excludeUserUsernames' parameter - const [excludeUserUsernames = [], excludeUserUsernamesErr] = $.arr($.str).optional().get(params.excludeUserUsernames); - if (excludeUserUsernamesErr) return rej('invalid excludeUserUsernames param'); - - // Get 'following' parameter - const [following = null, followingErr] = $.bool.optional().nullable().get(params.following); - if (followingErr) return rej('invalid following param'); - - // Get 'mute' parameter - const [mute = 'mute_all', muteErr] = $.str.optional().get(params.mute); - if (muteErr) return rej('invalid mute param'); - - // Get 'reply' parameter - const [reply = null, replyErr] = $.bool.optional().nullable().get(params.reply); - if (replyErr) return rej('invalid reply param'); - - // Get 'renote' parameter - const [renote = null, renoteErr] = $.bool.optional().nullable().get(params.renote); - if (renoteErr) return rej('invalid renote param'); - - // Get 'media' parameter - const [media = null, mediaErr] = $.bool.optional().nullable().get(params.media); - if (mediaErr) return rej('invalid media param'); - - // Get 'poll' parameter - const [poll = null, pollErr] = $.bool.optional().nullable().get(params.poll); - if (pollErr) return rej('invalid poll param'); - - // Get 'sinceDate' parameter - const [sinceDate, sinceDateErr] = $.num.optional().get(params.sinceDate); - if (sinceDateErr) throw 'invalid sinceDate param'; - - // Get 'untilDate' parameter - const [untilDate, untilDateErr] = $.num.optional().get(params.untilDate); - if (untilDateErr) throw 'invalid untilDate param'; - - // Get 'offset' parameter - const [offset = 0, offsetErr] = $.num.optional().min(0).get(params.offset); - if (offsetErr) return rej('invalid offset param'); - - // Get 'limit' parameter - const [limit = 10, limitErr] = $.num.optional().range(1, 30).get(params.limit); - if (limitErr) return rej('invalid limit param'); - - let includeUsers = includeUserIds; - if (includeUserUsernames != null) { - const ids = (await Promise.all(includeUserUsernames.map(async (username) => { - const _user = await User.findOne({ - usernameLower: username.toLowerCase() - }); - return _user ? _user._id : null; - }))).filter(id => id != null); - includeUsers = includeUsers.concat(ids); - } - - let excludeUsers = excludeUserIds; - if (excludeUserUsernames != null) { - const ids = (await Promise.all(excludeUserUsernames.map(async (username) => { - const _user = await User.findOne({ - usernameLower: username.toLowerCase() - }); - return _user ? _user._id : null; - }))).filter(id => id != null); - excludeUsers = excludeUsers.concat(ids); - } - - search(res, rej, me, text, includeUsers, excludeUsers, following, - mute, reply, renote, media, poll, sinceDate, untilDate, offset, limit); -}); - -async function search( - res, rej, me, text, includeUserIds, excludeUserIds, following, - mute, reply, renote, media, poll, sinceDate, untilDate, offset, max) { - - let q: any = { - $and: [] - }; - - const push = x => q.$and.push(x); - - if (text) { - // 完全一致検索 - if (/"""(.+?)"""/.test(text)) { - const x = text.match(/"""(.+?)"""/)[1]; - push({ - text: x - }); - } else { - const tags = text.split(' ').filter(x => x[0] == '#'); - if (tags) { - push({ - $and: tags.map(x => ({ - tags: x - })) - }); - } - - push({ - $and: text.split(' ').map(x => ({ - // キーワードが-で始まる場合そのキーワードを除外する - text: x[0] == '-' ? { - $not: new RegExp(escapeRegexp(x.substr(1))) - } : new RegExp(escapeRegexp(x)) - })) - }); - } - } - - if (includeUserIds && includeUserIds.length != 0) { - push({ - userId: { - $in: includeUserIds - } - }); - } else if (excludeUserIds && excludeUserIds.length != 0) { - push({ - userId: { - $nin: excludeUserIds - } - }); - } - - if (following != null && me != null) { - const ids = await getFriendIds(me._id, false); - push({ - userId: following ? { - $in: ids - } : { - $nin: ids.concat(me._id) - } - }); - } - - if (me != null) { - const mutes = await Mute.find({ - muterId: me._id, - deletedAt: { $exists: false } - }); - const mutedUserIds = mutes.map(m => m.muteeId); - - switch (mute) { - case 'mute_all': - push({ - userId: { - $nin: mutedUserIds - }, - '_reply.userId': { - $nin: mutedUserIds - }, - '_renote.userId': { - $nin: mutedUserIds - } - }); - break; - case 'mute_related': - push({ - '_reply.userId': { - $nin: mutedUserIds - }, - '_renote.userId': { - $nin: mutedUserIds - } - }); - break; - case 'mute_direct': - push({ - userId: { - $nin: mutedUserIds - } - }); - break; - case 'direct_only': - push({ - userId: { - $in: mutedUserIds - } - }); - break; - case 'related_only': - push({ - $or: [{ - '_reply.userId': { - $in: mutedUserIds - } - }, { - '_renote.userId': { - $in: mutedUserIds - } - }] - }); - break; - case 'all_only': - push({ - $or: [{ - userId: { - $in: mutedUserIds - } - }, { - '_reply.userId': { - $in: mutedUserIds - } - }, { - '_renote.userId': { - $in: mutedUserIds - } - }] - }); - break; - } - } - - if (reply != null) { - if (reply) { - push({ - replyId: { - $exists: true, - $ne: null - } - }); - } else { - push({ - $or: [{ - replyId: { - $exists: false - } - }, { - replyId: null - }] - }); - } - } - - if (renote != null) { - if (renote) { - push({ - renoteId: { - $exists: true, - $ne: null - } - }); - } else { - push({ - $or: [{ - renoteId: { - $exists: false - } - }, { - renoteId: null - }] - }); - } - } - - if (media != null) { - if (media) { - push({ - mediaIds: { - $exists: true, - $ne: null - } - }); - } else { - push({ - $or: [{ - mediaIds: { - $exists: false - } - }, { - mediaIds: null - }] - }); - } - } - - if (poll != null) { - if (poll) { - push({ - poll: { - $exists: true, - $ne: null - } - }); - } else { - push({ - $or: [{ - poll: { - $exists: false - } - }, { - poll: null - }] - }); - } - } - - if (sinceDate) { - push({ - createdAt: { - $gt: new Date(sinceDate) - } - }); - } - - if (untilDate) { - push({ - createdAt: { - $lt: new Date(untilDate) - } - }); - } - - if (q.$and.length == 0) { - q = {}; - } - - // Search notes - const notes = await Note - .find(q, { - sort: { - _id: -1 - }, - limit: max, - skip: offset - }); - - // Serialize - res(await Promise.all(notes.map(async note => - await pack(note, me)))); -} diff --git a/src/server/api/endpoints/notes/search_by_tag.ts b/src/server/api/endpoints/notes/search_by_tag.ts index 1eb4cde49c..9be7cfffb6 100644 --- a/src/server/api/endpoints/notes/search_by_tag.ts +++ b/src/server/api/endpoints/notes/search_by_tag.ts @@ -1,6 +1,6 @@ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Note from '../../../../models/note'; -import User from '../../../../models/user'; +import User, { ILocalUser } from '../../../../models/user'; import Mute from '../../../../models/mute'; import { getFriendIds } from '../../common/get-friends'; import { pack } from '../../../../models/note'; @@ -8,7 +8,7 @@ import { pack } from '../../../../models/note'; /** * Search notes by tag */ -module.exports = (params, me) => new Promise(async (res, rej) => { +module.exports = (params: any, me: ILocalUser) => new Promise(async (res, rej) => { // Get 'tag' parameter const [tag, tagError] = $.str.get(params.tag); if (tagError) return rej('invalid tag param'); @@ -69,7 +69,6 @@ module.exports = (params, me) => new Promise(async (res, rej) => { const [limit = 10, limitErr] = $.num.optional().range(1, 30).get(params.limit); if (limitErr) return rej('invalid limit param'); - let includeUsers = includeUserIds; if (includeUserUsernames != null) { const ids = (await Promise.all(includeUserUsernames.map(async (username) => { const _user = await User.findOne({ @@ -77,10 +76,10 @@ module.exports = (params, me) => new Promise(async (res, rej) => { }); return _user ? _user._id : null; }))).filter(id => id != null); - includeUsers = includeUsers.concat(ids); + + ids.forEach(id => includeUserIds.push(id)); } - let excludeUsers = excludeUserIds; if (excludeUserUsernames != null) { const ids = (await Promise.all(excludeUserUsernames.map(async (username) => { const _user = await User.findOne({ @@ -88,16 +87,9 @@ module.exports = (params, me) => new Promise(async (res, rej) => { }); return _user ? _user._id : null; }))).filter(id => id != null); - excludeUsers = excludeUsers.concat(ids); - } - - search(res, rej, me, tag, includeUsers, excludeUsers, following, - mute, reply, renote, media, poll, sinceDate, untilDate, offset, limit); -}); -async function search( - res, rej, me, tag, includeUserIds, excludeUserIds, following, - mute, reply, renote, media, poll, sinceDate, untilDate, offset, max) { + ids.forEach(id => excludeUserIds.push(id)); + } let q: any = { $and: [{ @@ -105,7 +97,7 @@ async function search( }] }; - const push = x => q.$and.push(x); + const push = (x: any) => q.$and.push(x); if (includeUserIds && includeUserIds.length != 0) { push({ @@ -320,10 +312,10 @@ async function search( sort: { _id: -1 }, - limit: max, + limit: limit, skip: offset }); // Serialize res(await Promise.all(notes.map(note => pack(note, me)))); -} +}); diff --git a/src/server/api/endpoints/notes/show.ts b/src/server/api/endpoints/notes/show.ts index 78dc55a703..1ba7145996 100644 --- a/src/server/api/endpoints/notes/show.ts +++ b/src/server/api/endpoints/notes/show.ts @@ -1,17 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Note, { pack } from '../../../../models/note'; +import { ILocalUser } from '../../../../models/user'; /** * Show a note - * - * @param {any} params - * @param {any} user - * @return {Promise} */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'noteId' parameter const [noteId, noteIdErr] = $.type(ID).get(params.noteId); if (noteIdErr) return rej('invalid noteId param'); diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts index f1d741d5ee..18c0acd379 100644 --- a/src/server/api/endpoints/notes/timeline.ts +++ b/src/server/api/endpoints/notes/timeline.ts @@ -1,16 +1,14 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Note from '../../../../models/note'; import Mute from '../../../../models/mute'; import { getFriends } from '../../common/get-friends'; import { pack } from '../../../../models/note'; +import { ILocalUser } from '../../../../models/user'; /** * Get timeline of myself */ -module.exports = async (params, user, app) => { +module.exports = async (params: any, user: ILocalUser) => { // Get 'limit' parameter const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); if (limitErr) throw 'invalid limit param'; diff --git a/src/server/api/endpoints/notes/trend.ts b/src/server/api/endpoints/notes/trend.ts index 4735bec51e..9c0a1bb112 100644 --- a/src/server/api/endpoints/notes/trend.ts +++ b/src/server/api/endpoints/notes/trend.ts @@ -1,18 +1,12 @@ -/** - * Module dependencies - */ const ms = require('ms'); import $ from 'cafy'; import Note, { pack } from '../../../../models/note'; +import { ILocalUser } from '../../../../models/user'; /** * Get trend notes - * - * @param {any} params - * @param {any} user - * @return {Promise} */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'limit' parameter const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); if (limitErr) return rej('invalid limit param'); diff --git a/src/server/api/endpoints/notes/user-list-timeline.ts b/src/server/api/endpoints/notes/user-list-timeline.ts index a74a5141f9..8aa800b712 100644 --- a/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/src/server/api/endpoints/notes/user-list-timeline.ts @@ -1,16 +1,14 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Note from '../../../../models/note'; import Mute from '../../../../models/mute'; import { pack } from '../../../../models/note'; import UserList from '../../../../models/user-list'; +import { ILocalUser } from '../../../../models/user'; /** * Get timeline of a user list */ -module.exports = async (params, user, app) => { +module.exports = async (params: any, user: ILocalUser) => { // Get 'limit' parameter const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); if (limitErr) throw 'invalid limit param'; diff --git a/src/server/api/endpoints/notifications/mark_as_read_all.ts b/src/server/api/endpoints/notifications/mark_as_read_all.ts index 7a48ca3e62..faaaf65a2d 100644 --- a/src/server/api/endpoints/notifications/mark_as_read_all.ts +++ b/src/server/api/endpoints/notifications/mark_as_read_all.ts @@ -1,11 +1,11 @@ import Notification from '../../../../models/notification'; import event from '../../../../publishers/stream'; -import User from '../../../../models/user'; +import User, { ILocalUser } from '../../../../models/user'; /** * Mark as read all notifications */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Update documents await Notification.update({ notifieeId: user._id, diff --git a/src/server/api/endpoints/reversi/games.ts b/src/server/api/endpoints/reversi/games.ts index 9d879ecf23..1455f191f7 100644 --- a/src/server/api/endpoints/reversi/games.ts +++ b/src/server/api/endpoints/reversi/games.ts @@ -1,7 +1,8 @@ import $ from 'cafy'; import ID from '../../../../cafy-id'; import ReversiGame, { pack } from '../../../../models/reversi-game'; +import { ILocalUser } from '../../../../models/user'; -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'my' parameter const [my = false, myErr] = $.bool.optional().get(params.my); if (myErr) return rej('invalid my param'); diff --git a/src/server/api/endpoints/reversi/games/show.ts b/src/server/api/endpoints/reversi/games/show.ts index f32eb23513..d70ee547a2 100644 --- a/src/server/api/endpoints/reversi/games/show.ts +++ b/src/server/api/endpoints/reversi/games/show.ts @@ -1,8 +1,9 @@ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import ReversiGame, { pack } from '../../../../../models/reversi-game'; import Reversi from '../../../../../reversi/core'; +import { ILocalUser } from '../../../../../models/user'; -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'gameId' parameter const [gameId, gameIdErr] = $.type(ID).get(params.gameId); if (gameIdErr) return rej('invalid gameId param'); diff --git a/src/server/api/endpoints/reversi/invitations.ts b/src/server/api/endpoints/reversi/invitations.ts index fc487205a9..d7727071ae 100644 --- a/src/server/api/endpoints/reversi/invitations.ts +++ b/src/server/api/endpoints/reversi/invitations.ts @@ -1,6 +1,7 @@ import Matching, { pack as packMatching } from '../../../../models/reversi-matching'; +import { ILocalUser } from '../../../../models/user'; -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Find session const invitations = await Matching.find({ childId: user._id diff --git a/src/server/api/endpoints/reversi/match.ts b/src/server/api/endpoints/reversi/match.ts index 5a699ddbae..907df7cf43 100644 --- a/src/server/api/endpoints/reversi/match.ts +++ b/src/server/api/endpoints/reversi/match.ts @@ -1,11 +1,11 @@ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Matching, { pack as packMatching } from '../../../../models/reversi-matching'; import ReversiGame, { pack as packGame } from '../../../../models/reversi-game'; -import User from '../../../../models/user'; +import User, { ILocalUser } from '../../../../models/user'; import publishUserStream, { publishReversiStream } from '../../../../publishers/stream'; import { eighteight } from '../../../../reversi/maps'; -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'userId' parameter const [childId, childIdErr] = $.type(ID).get(params.userId); if (childIdErr) return rej('invalid userId param'); diff --git a/src/server/api/endpoints/reversi/match/cancel.ts b/src/server/api/endpoints/reversi/match/cancel.ts index bc8a4cd640..1c9c799dbe 100644 --- a/src/server/api/endpoints/reversi/match/cancel.ts +++ b/src/server/api/endpoints/reversi/match/cancel.ts @@ -1,6 +1,7 @@ import Matching from '../../../../../models/reversi-matching'; +import { ILocalUser } from '../../../../../models/user'; -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params: any, user: ILocalUser) => new Promise(async (res, rej) => { await Matching.remove({ parentId: user._id }); diff --git a/src/server/api/endpoints/sw/register.ts b/src/server/api/endpoints/sw/register.ts index b22a8b08ef..f04a77fa4d 100644 --- a/src/server/api/endpoints/sw/register.ts +++ b/src/server/api/endpoints/sw/register.ts @@ -1,13 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import Subscription from '../../../../models/sw-subscription'; +import { ILocalUser } from '../../../../models/user'; /** * subscribe service worker */ -module.exports = async (params, user, app) => new Promise(async (res, rej) => { +module.exports = async (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'endpoint' parameter const [endpoint, endpointErr] = $.str.get(params.endpoint); if (endpointErr) return rej('invalid endpoint param'); diff --git a/src/server/api/endpoints/username/available.ts b/src/server/api/endpoints/username/available.ts index b11bec4e58..aad3adc514 100644 --- a/src/server/api/endpoints/username/available.ts +++ b/src/server/api/endpoints/username/available.ts @@ -1,17 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import User from '../../../../models/user'; import { validateUsername } from '../../../../models/user'; /** * Check available username - * - * @param {any} params - * @return {Promise} */ -module.exports = async (params) => new Promise(async (res, rej) => { +module.exports = async (params: any) => new Promise(async (res, rej) => { // Get 'username' parameter const [username, usernameError] = $.str.pipe(validateUsername).get(params.username); if (usernameError) return rej('invalid username param'); diff --git a/src/server/api/endpoints/users.ts b/src/server/api/endpoints/users.ts index eb581cb7e6..b8df6f3ecf 100644 --- a/src/server/api/endpoints/users.ts +++ b/src/server/api/endpoints/users.ts @@ -1,13 +1,10 @@ -/** - * Module dependencies - */ import $ from 'cafy'; -import User, { pack } from '../../../models/user'; +import User, { pack, ILocalUser } from '../../../models/user'; /** * Lists all users */ -module.exports = (params, me) => new Promise(async (res, rej) => { +module.exports = (params: any, me: ILocalUser) => new Promise(async (res, rej) => { // Get 'limit' parameter const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); if (limitErr) return rej('invalid limit param'); diff --git a/src/server/api/endpoints/users/followers.ts b/src/server/api/endpoints/users/followers.ts index 810cd7341b..53133ee969 100644 --- a/src/server/api/endpoints/users/followers.ts +++ b/src/server/api/endpoints/users/followers.ts @@ -1,8 +1,5 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; -import User from '../../../../models/user'; +import User, { ILocalUser } from '../../../../models/user'; import Following from '../../../../models/following'; import { pack } from '../../../../models/user'; import { getFriendIds } from '../../common/get-friends'; @@ -10,7 +7,7 @@ import { getFriendIds } from '../../common/get-friends'; /** * Get followers of a user */ -module.exports = (params, me) => new Promise(async (res, rej) => { +module.exports = (params: any, me: ILocalUser) => new Promise(async (res, rej) => { // Get 'userId' parameter const [userId, userIdErr] = $.type(ID).get(params.userId); if (userIdErr) return rej('invalid userId param'); diff --git a/src/server/api/endpoints/users/following.ts b/src/server/api/endpoints/users/following.ts index 3373b9d632..2061200198 100644 --- a/src/server/api/endpoints/users/following.ts +++ b/src/server/api/endpoints/users/following.ts @@ -1,20 +1,13 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; -import User from '../../../../models/user'; +import User, { ILocalUser } from '../../../../models/user'; import Following from '../../../../models/following'; import { pack } from '../../../../models/user'; import { getFriendIds } from '../../common/get-friends'; /** * Get following users of a user - * - * @param {any} params - * @param {any} me - * @return {Promise} */ -module.exports = (params, me) => new Promise(async (res, rej) => { +module.exports = (params: any, me: ILocalUser) => new Promise(async (res, rej) => { // Get 'userId' parameter const [userId, userIdErr] = $.type(ID).get(params.userId); if (userIdErr) return rej('invalid userId param'); diff --git a/src/server/api/endpoints/users/get_frequently_replied_users.ts b/src/server/api/endpoints/users/get_frequently_replied_users.ts index 64d737a06b..ba8779d334 100644 --- a/src/server/api/endpoints/users/get_frequently_replied_users.ts +++ b/src/server/api/endpoints/users/get_frequently_replied_users.ts @@ -1,11 +1,8 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; import Note from '../../../../models/note'; -import User, { pack } from '../../../../models/user'; +import User, { pack, ILocalUser } from '../../../../models/user'; -module.exports = (params, me) => new Promise(async (res, rej) => { +module.exports = (params: any, me: ILocalUser) => new Promise(async (res, rej) => { // Get 'userId' parameter const [userId, userIdErr] = $.type(ID).get(params.userId); if (userIdErr) return rej('invalid userId param'); @@ -64,7 +61,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => { } }); - const repliedUsers = {}; + const repliedUsers: any = {}; // Extract replies from recent notes replyTargetNotes.forEach(note => { diff --git a/src/server/api/endpoints/users/lists/create.ts b/src/server/api/endpoints/users/lists/create.ts index 100a78b872..cdd1a0d813 100644 --- a/src/server/api/endpoints/users/lists/create.ts +++ b/src/server/api/endpoints/users/lists/create.ts @@ -1,13 +1,11 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import UserList, { pack } from '../../../../../models/user-list'; +import { ILocalUser } from '../../../../../models/user'; /** * Create a user list */ -module.exports = async (params, user) => new Promise(async (res, rej) => { +module.exports = async (params: any, user: ILocalUser) => new Promise(async (res, rej) => { // Get 'title' parameter const [title, titleErr] = $.str.range(1, 100).get(params.title); if (titleErr) return rej('invalid title param'); diff --git a/src/server/api/endpoints/users/lists/list.ts b/src/server/api/endpoints/users/lists/list.ts index d19339a1f5..bf8391d863 100644 --- a/src/server/api/endpoints/users/lists/list.ts +++ b/src/server/api/endpoints/users/lists/list.ts @@ -1,9 +1,10 @@ import UserList, { pack } from '../../../../../models/user-list'; +import { ILocalUser } from '../../../../../models/user'; /** * Add a user to a user list */ -module.exports = async (params, me) => new Promise(async (res, rej) => { +module.exports = async (params: any, me: ILocalUser) => new Promise(async (res, rej) => { // Fetch lists const userLists = await UserList.find({ userId: me._id, diff --git a/src/server/api/endpoints/users/lists/push.ts b/src/server/api/endpoints/users/lists/push.ts index da5a9a134c..d8a3b3c9aa 100644 --- a/src/server/api/endpoints/users/lists/push.ts +++ b/src/server/api/endpoints/users/lists/push.ts @@ -1,6 +1,6 @@ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import UserList from '../../../../../models/user-list'; -import User, { pack as packUser, isRemoteUser, getGhost } from '../../../../../models/user'; +import User, { pack as packUser, isRemoteUser, getGhost, ILocalUser } from '../../../../../models/user'; import { publishUserListStream } from '../../../../../publishers/stream'; import ap from '../../../../../remote/activitypub/renderer'; import renderFollow from '../../../../../remote/activitypub/renderer/follow'; @@ -9,7 +9,7 @@ import { deliver } from '../../../../../queue'; /** * Add a user to a user list */ -module.exports = async (params, me) => new Promise(async (res, rej) => { +module.exports = async (params: any, me: ILocalUser) => new Promise(async (res, rej) => { // Get 'listId' parameter const [listId, listIdErr] = $.type(ID).get(params.listId); if (listIdErr) return rej('invalid listId param'); diff --git a/src/server/api/endpoints/users/lists/show.ts b/src/server/api/endpoints/users/lists/show.ts index 16cb3382fd..e4ae239613 100644 --- a/src/server/api/endpoints/users/lists/show.ts +++ b/src/server/api/endpoints/users/lists/show.ts @@ -1,10 +1,11 @@ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import UserList, { pack } from '../../../../../models/user-list'; +import { ILocalUser } from '../../../../../models/user'; /** * Show a user list */ -module.exports = async (params, me) => new Promise(async (res, rej) => { +module.exports = async (params: any, me: ILocalUser) => new Promise(async (res, rej) => { // Get 'listId' parameter const [listId, listIdErr] = $.type(ID).get(params.listId); if (listIdErr) return rej('invalid listId param'); diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts index 061c363d0f..222a8d950a 100644 --- a/src/server/api/endpoints/users/notes.ts +++ b/src/server/api/endpoints/users/notes.ts @@ -1,15 +1,12 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; import getHostLower from '../../common/get-host-lower'; import Note, { pack } from '../../../../models/note'; -import User from '../../../../models/user'; +import User, { ILocalUser } from '../../../../models/user'; /** * Get notes of a user */ -module.exports = (params, me) => new Promise(async (res, rej) => { +module.exports = (params: any, me: ILocalUser) => new Promise(async (res, rej) => { // Get 'userId' parameter const [userId, userIdErr] = $.type(ID).optional().get(params.userId); if (userIdErr) return rej('invalid userId param'); diff --git a/src/server/api/endpoints/users/recommendation.ts b/src/server/api/endpoints/users/recommendation.ts index 23821a552f..1d0d889f11 100644 --- a/src/server/api/endpoints/users/recommendation.ts +++ b/src/server/api/endpoints/users/recommendation.ts @@ -1,20 +1,13 @@ -/** - * Module dependencies - */ const ms = require('ms'); import $ from 'cafy'; -import User, { pack } from '../../../../models/user'; +import User, { pack, ILocalUser } from '../../../../models/user'; import { getFriendIds } from '../../common/get-friends'; import Mute from '../../../../models/mute'; /** * Get recommended users - * - * @param {any} params - * @param {any} me - * @return {Promise} */ -module.exports = (params, me) => new Promise(async (res, rej) => { +module.exports = (params: any, me: ILocalUser) => new Promise(async (res, rej) => { // Get 'limit' parameter const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); if (limitErr) return rej('invalid limit param'); diff --git a/src/server/api/endpoints/users/search.ts b/src/server/api/endpoints/users/search.ts index 95b9e4f4bb..e29c8d32f1 100644 --- a/src/server/api/endpoints/users/search.ts +++ b/src/server/api/endpoints/users/search.ts @@ -1,40 +1,19 @@ -/** - * Module dependencies - */ -import * as mongo from 'mongodb'; import $ from 'cafy'; -import User, { pack } from '../../../../models/user'; -import config from '../../../../config'; +import User, { pack, ILocalUser } from '../../../../models/user'; const escapeRegexp = require('escape-regexp'); /** * Search a user - * - * @param {any} params - * @param {any} me - * @return {Promise} */ -module.exports = (params, me) => new Promise(async (res, rej) => { +module.exports = (params: any, me: ILocalUser) => new Promise(async (res, rej) => { // Get 'query' parameter const [query, queryError] = $.str.pipe(x => x != '').get(params.query); if (queryError) return rej('invalid query param'); - // Get 'offset' parameter - const [offset = 0, offsetErr] = $.num.optional().min(0).get(params.offset); - if (offsetErr) return rej('invalid offset param'); - // Get 'max' parameter const [max = 10, maxErr] = $.num.optional().range(1, 30).get(params.max); if (maxErr) return rej('invalid max param'); - // If Elasticsearch is available, search by $ - // If not, search by MongoDB - (config.elasticsearch.enable ? byElasticsearch : byNative) - (res, rej, me, query, offset, max); -}); - -// Search by MongoDB -async function byNative(res, rej, me, query, offset, max) { const escapedQuery = escapeRegexp(query); // Search users @@ -51,49 +30,5 @@ async function byNative(res, rej, me, query, offset, max) { }); // Serialize - res(await Promise.all(users.map(async user => - await pack(user, me, { detail: true })))); -} - -// Search by Elasticsearch -async function byElasticsearch(res, rej, me, query, offset, max) { - const es = require('../../db/elasticsearch'); - - es.search({ - index: 'misskey', - type: 'user', - body: { - size: max, - from: offset, - query: { - simple_query_string: { - fields: ['username', 'name', 'bio'], - query: query, - default_operator: 'and' - } - } - } - }, async (error, response) => { - if (error) { - console.error(error); - return res(500); - } - - if (response.hits.total === 0) { - return res([]); - } - - const hits = response.hits.hits.map(hit => new mongo.ObjectID(hit._id)); - - const users = await User - .find({ - _id: { - $in: hits - } - }); - - // Serialize - res(await Promise.all(users.map(async user => - await pack(user, me, { detail: true })))); - }); -} + res(await Promise.all(users.map(user => pack(user, me, { detail: true })))); +}); diff --git a/src/server/api/endpoints/users/search_by_username.ts b/src/server/api/endpoints/users/search_by_username.ts index 7c9fbba0fa..937f9af589 100644 --- a/src/server/api/endpoints/users/search_by_username.ts +++ b/src/server/api/endpoints/users/search_by_username.ts @@ -1,13 +1,10 @@ -/** - * Module dependencies - */ import $ from 'cafy'; -import User, { pack } from '../../../../models/user'; +import User, { pack, ILocalUser } from '../../../../models/user'; /** * Search a user by username */ -module.exports = (params, me) => new Promise(async (res, rej) => { +module.exports = (params: any, me: ILocalUser) => new Promise(async (res, rej) => { // Get 'query' parameter const [query, queryError] = $.str.get(params.query); if (queryError) return rej('invalid query param'); diff --git a/src/server/api/endpoints/users/show.ts b/src/server/api/endpoints/users/show.ts index b8c6ff25c4..bf7e2a2312 100644 --- a/src/server/api/endpoints/users/show.ts +++ b/src/server/api/endpoints/users/show.ts @@ -1,8 +1,5 @@ -/** - * Module dependencies - */ import $ from 'cafy'; import ID from '../../../../cafy-id'; -import User, { pack } from '../../../../models/user'; +import User, { pack, ILocalUser } from '../../../../models/user'; import resolveRemoteUser from '../../../../remote/resolve-user'; const cursorOption = { fields: { data: false } }; @@ -10,7 +7,7 @@ const cursorOption = { fields: { data: false } }; /** * Show user(s) */ -module.exports = (params, me) => new Promise(async (res, rej) => { +module.exports = (params: any, me: ILocalUser) => new Promise(async (res, rej) => { let user; // Get 'userId' parameter @@ -49,7 +46,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => { return rej('failed to resolve remote user'); } } else { - const q = userId !== undefined + const q: any = userId !== undefined ? { _id: userId } : { usernameLower: username.toLowerCase(), host: null }; diff --git a/src/server/api/private/signup.ts b/src/server/api/private/signup.ts index fee899b374..cb47d400b0 100644 --- a/src/server/api/private/signup.ts +++ b/src/server/api/private/signup.ts @@ -1,7 +1,7 @@ import * as Koa from 'koa'; import * as bcrypt from 'bcryptjs'; import { generate as generateKeypair } from '../../../crypto_key'; -import recaptcha = require('recaptcha-promise'); +const recaptcha = require('recaptcha-promise'); import User, { IUser, validateUsername, validatePassword, pack } from '../../../models/user'; import generateUserToken from '../common/generate-native-user-token'; import config from '../../../config'; diff --git a/src/server/api/service/github.ts b/src/server/api/service/github.ts index da33648a16..9024740a96 100644 --- a/src/server/api/service/github.ts +++ b/src/server/api/service/github.ts @@ -11,7 +11,7 @@ const handler = new EventEmitter(); let bot: IUser; -const post = async text => { +const post = async (text: string) => { if (bot == null) { const account = await User.findOne({ usernameLower: config.github_bot.username.toLowerCase() @@ -90,7 +90,7 @@ handler.on('push', event => { case 'refs/heads/master': const pusher = event.pusher; const compare = event.compare; - const commits = event.commits; + const commits: any[] = event.commits; post([ `Pushed by **${pusher.name}** with ?[${commits.length} commit${commits.length > 1 ? 's' : ''}](${compare}):`, commits.reverse().map(commit => `・[?[${commit.id.substr(0, 7)}](${commit.url})] ${commit.message.split('\n')[0]}`).join('\n'), diff --git a/src/server/api/stream/notes-stats.ts b/src/server/api/stream/notes-stats.ts index 739b325848..ab00620018 100644 --- a/src/server/api/stream/notes-stats.ts +++ b/src/server/api/stream/notes-stats.ts @@ -4,7 +4,7 @@ import Xev from 'xev'; const ev = new Xev(); export default function(request: websocket.request, connection: websocket.connection): void { - const onStats = stats => { + const onStats = (stats: any) => { connection.send(JSON.stringify({ type: 'stats', body: stats diff --git a/src/server/api/stream/requests.ts b/src/server/api/stream/requests.ts deleted file mode 100644 index d7bb5e6c5c..0000000000 --- a/src/server/api/stream/requests.ts +++ /dev/null @@ -1,19 +0,0 @@ -import * as websocket from 'websocket'; -import Xev from 'xev'; - -const ev = new Xev(); - -export default function(request: websocket.request, connection: websocket.connection): void { - const onRequest = request => { - connection.send(JSON.stringify({ - type: 'request', - body: request - })); - }; - - ev.addListener('request', onRequest); - - connection.on('close', () => { - ev.removeListener('request', onRequest); - }); -} diff --git a/src/server/api/stream/reversi-game.ts b/src/server/api/stream/reversi-game.ts index 63d9b220b0..ea8a9741d2 100644 --- a/src/server/api/stream/reversi-game.ts +++ b/src/server/api/stream/reversi-game.ts @@ -61,7 +61,7 @@ export default function(request: websocket.request, connection: websocket.connec } }); - async function updateSettings(settings) { + async function updateSettings(settings: any) { const game = await ReversiGame.findOne({ _id: gameId }); if (game.isStarted) return; @@ -78,7 +78,7 @@ export default function(request: websocket.request, connection: websocket.connec publishReversiGameStream(gameId, 'update-settings', settings); } - async function initForm(form) { + async function initForm(form: any) { const game = await ReversiGame.findOne({ _id: gameId }); if (game.isStarted) return; @@ -100,7 +100,7 @@ export default function(request: websocket.request, connection: websocket.connec }); } - async function updateForm(id, value) { + async function updateForm(id: string, value: any) { const game = await ReversiGame.findOne({ _id: gameId }); if (game.isStarted) return; @@ -108,7 +108,7 @@ export default function(request: websocket.request, connection: websocket.connec const form = game.user1Id.equals(user._id) ? game.form2 : game.form1; - const item = form.find(i => i.id == id); + const item = form.find((i: any) => i.id == id); if (item == null) return; @@ -131,7 +131,7 @@ export default function(request: websocket.request, connection: websocket.connec }); } - async function message(message) { + async function message(message: any) { message.id = Math.random(); publishReversiGameStream(gameId, 'message', { userId: user._id, @@ -246,7 +246,7 @@ export default function(request: websocket.request, connection: websocket.connec } // 石を打つ - async function set(pos) { + async function set(pos: number) { const game = await ReversiGame.findOne({ _id: gameId }); if (!game.isStarted) return; @@ -315,7 +315,7 @@ export default function(request: websocket.request, connection: websocket.connec } } - async function check(crc32) { + async function check(crc32: string) { const game = await ReversiGame.findOne({ _id: gameId }); if (!game.isStarted) return; diff --git a/src/server/api/stream/server-stats.ts b/src/server/api/stream/server-stats.ts index 342170a21e..2a058de6c3 100644 --- a/src/server/api/stream/server-stats.ts +++ b/src/server/api/stream/server-stats.ts @@ -4,7 +4,7 @@ import Xev from 'xev'; const ev = new Xev(); export default function(request: websocket.request, connection: websocket.connection): void { - const onStats = stats => { + const onStats = (stats: any) => { connection.send(JSON.stringify({ type: 'stats', body: stats diff --git a/src/server/api/streaming.ts b/src/server/api/streaming.ts index a3e4ed6f85..81adff0b58 100644 --- a/src/server/api/streaming.ts +++ b/src/server/api/streaming.ts @@ -14,7 +14,6 @@ import reversiGameStream from './stream/reversi-game'; import reversiStream from './stream/reversi'; import serverStatsStream from './stream/server-stats'; import notesStatsStream from './stream/notes-stats'; -import requestsStream from './stream/requests'; import { ParsedUrlQuery } from 'querystring'; import authenticate from './authenticate'; @@ -39,11 +38,6 @@ module.exports = (server: http.Server) => { return; } - if (request.resourceURL.pathname === '/requests') { - requestsStream(request, connection); - return; - } - // Connect to Redis const subscriber = redis.createClient( config.redis.port, config.redis.host); diff --git a/src/server/index.ts b/src/server/index.ts index fc3d252e10..f1fcf58c8d 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -62,7 +62,7 @@ app.use(mount(require('./web'))); function createServer() { if (config.https) { - const certs = {}; + const certs: any = {}; Object.keys(config.https).forEach(k => { certs[k] = fs.readFileSync(config.https[k]); }); diff --git a/webpack.config.ts b/webpack.config.ts index 480f5d4a7a..3c426ebb49 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -6,7 +6,7 @@ import * as fs from 'fs'; import * as webpack from 'webpack'; import chalk from 'chalk'; const { VueLoaderPlugin } = require('vue-loader'); -import jsonImporter from 'node-sass-json-importer'; +const jsonImporter = require('node-sass-json-importer'); const minifyHtml = require('html-minifier').minify; const WebpackOnBuildPlugin = require('on-build-webpack'); //const HardSourceWebpackPlugin = require('hard-source-webpack-plugin'); @@ -24,10 +24,17 @@ const meta = require('./package.json'); const version = meta.clientVersion; const codename = meta.codename; +declare var global: { + faReplacement: typeof faReplacement; + collapseSpacesReplacement: any; + base64replacement: any; + i18nReplacement: typeof i18nReplacement; +}; + //#region Replacer definitions global['faReplacement'] = faReplacement; -global['collapseSpacesReplacement'] = html => { +global['collapseSpacesReplacement'] = (html: string) => { return minifyHtml(html, { collapseWhitespace: true, collapseInlineTagWhitespace: true, @@ -35,7 +42,7 @@ global['collapseSpacesReplacement'] = html => { }).replace(/\t/g, ''); }; -global['base64replacement'] = (_, key) => { +global['base64replacement'] = (_: any, key: string) => { return fs.readFileSync(__dirname + '/src/client/' + key, 'base64'); }; @@ -89,10 +96,10 @@ const consts = { _WELCOME_BG_URL_: config.welcome_bg_url }; -const _consts = {}; +const _consts: { [ key: string ]: any } = {}; Object.keys(consts).forEach(key => { - _consts[key] = JSON.stringify(consts[key]); + _consts[key] = JSON.stringify((consts as any)[key]); }); //#endregion @@ -106,7 +113,7 @@ const plugins = [ new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(isProduction ? 'production' : 'development') }), - new WebpackOnBuildPlugin(stats => { + new WebpackOnBuildPlugin((stats: any) => { fs.writeFileSync('./built/client/meta.json', JSON.stringify({ version }), 'utf-8'); diff --git a/webpack/i18n.ts b/webpack/i18n.ts index e2cce060e8..f73af72584 100644 --- a/webpack/i18n.ts +++ b/webpack/i18n.ts @@ -4,7 +4,7 @@ export const pattern = /%i18n:([a-z0-9_\-@\.]+?)%/g; -export const replacement = (ctx, match, key) => { +export const replacement = (ctx: any, _: any, key: string) => { const client = '/src/client/app/'; let name = null; -- cgit v1.2.3-freya