diff --git a/package.json b/package.json index b921ed0..f6e81e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misshaialert", - "version": "1.5.0", + "version": "1.5.1", "description": "", "main": "built/app.js", "author": "Xeltica", diff --git a/src/const.ts b/src/const.ts index e9e2df7..7c84202 100644 --- a/src/const.ts +++ b/src/const.ts @@ -1,9 +1,7 @@ export default { - version: '1.5.0', + version: '1.5.1', changelog: [ - 'UIを全般的にスタイリッシュに刷新', - '自動投稿のテンプレートをカスタマイズできる機能を追加', - '自動投稿の公開範囲を設定できるように', - '新機能 みす廃ランキング および、みす廃レートを追加', + 'インスタンスの接続エラーにより後続処理が行えなくなる重大な不具合を修正', + '全員分の算出が終わるまで、ランキングを非表示に', ], }; \ No newline at end of file diff --git a/src/server/router.ts b/src/server/router.ts index 815ddb5..2c35b02 100644 --- a/src/server/router.ts +++ b/src/server/router.ts @@ -15,6 +15,7 @@ import { send } from '../services/send'; import { visibilities, Visibility } from '../types/Visibility'; import { defaultTemplate, variables } from '../functions/format'; import { getRanking } from '../functions/ranking'; +import { getState } from '../store'; export const router = new Router(); @@ -65,23 +66,28 @@ router.get('/', async ctx => { const usersCount = await getUserCount(); const ranking = await getRanking(10); + const commonLocals = { + usersCount, ranking, + state: getState(), + from: ctx.query.from, + }; + if (user && isAvailable) { const meta = await api<{ version: string }>(user?.host, 'meta', {}); await ctx.render('mypage', { - user, usersCount, ranking, + ...commonLocals, + user, // To Activate Groundpolis Mode isGroundpolis: meta.version.includes('gp'), defaultTemplate, templateVariables: variables, score: await getScores(user), - from: ctx.query.from, }); } else { // 非ログイン await ctx.render('welcome', { - usersCount, ranking, + ...commonLocals, welcomeMessage: welcomeMessage[Math.floor(Math.random() * welcomeMessage.length)], - from: ctx.query.from, }); } }); @@ -148,6 +154,7 @@ router.get('/teapot', async ctx => { router.get('/ranking', async ctx => { await ctx.render('ranking', { + state: getState(), ranking: await getRanking(null), }); }); diff --git a/src/services/worker.ts b/src/services/worker.ts index aa054fb..7d159fa 100644 --- a/src/services/worker.ts +++ b/src/services/worker.ts @@ -9,32 +9,44 @@ import { AlertMode } from '../types/AlertMode'; import { Users } from '../models'; import { send } from './send'; import { api } from './misskey'; +import * as Store from '../store'; export default (): void => { cron.schedule('0 0 0 * * *', async () => { + Store.dispatch({ + nowCalculating: true, + }); const users = await Users.find({ alertMode: Not('nothing'), }); for (const user of users) { - const miUser = await api(user.host, 'users/show', { username: user.username }, user.token); - try { + const miUser = await api(user.host, 'users/show', { username: user.username }, user.token); if (miUser.error) throw miUser.error; await updateRating(user, miUser); await send(user); - } catch (e) { - if (e.code === 'NO_SUCH_USER' || e.code === 'AUTHENTICATION_FAILED') { - // ユーザーが削除されている場合、レコードからも消してとりやめ - console.info(`${user.username}@${user.host} is deleted, so delete this user from the system`); - await deleteUser(user.username, user.host); - } else { - console.error(`${e.name}: ${e.message}`); - } - } finally { + + await updateScore(user, miUser); + if (user.alertMode === 'note') await delay(3000); - await updateScore(user, miUser); + } catch (e) { + if (e.code) { + if (e.code === 'NO_SUCH_USER' || e.code === 'AUTHENTICATION_FAILED') { + // ユーザーが削除されている場合、レコードからも消してとりやめ + console.info(`${user.username}@${user.host} is deleted, so delete this user from the system`); + await deleteUser(user.username, user.host); + } else { + console.error(`Misskey Error: ${JSON.stringify(e)}`); + } + } else { + // おそらく通信エラー + console.error(`Unknown error: ${e.name} ${e.message}`); + } } } + Store.dispatch({ + nowCalculating: false, + }); }); }; \ No newline at end of file diff --git a/src/store.ts b/src/store.ts new file mode 100644 index 0000000..0cd2829 --- /dev/null +++ b/src/store.ts @@ -0,0 +1,22 @@ +// Fluxもどき簡易Store +// getStateを介してステートを取得し、dispatchによって更新する +// stateを直接編集できないようになっている + +const defaultState: State = { + nowCalculating: false, +}; + +let _state: Readonly = defaultState; + +export type State = { + nowCalculating: boolean, +}; + +export const getState = () => Object.freeze({..._state}); + +export const dispatch = (mutation: Partial) => { + _state = { + ..._state, + ...mutation, + }; +}; \ No newline at end of file diff --git a/src/views/_components.pug b/src/views/_components.pug index 39fe70f..26d17cb 100644 --- a/src/views/_components.pug +++ b/src/views/_components.pug @@ -10,27 +10,31 @@ mixin ranking() p i.fas.fa-users strong 登録者数: !{usersCount}人 - +rankingTable() - p: a(href="/ranking") 全員分見る + if state && state.nowCalculating + p 現在計算中です。後ほどご確認ください + else + +rankingTable() + p: a(href="/ranking") 全員分見る mixin rankingTable() - table - thead: tr - th 順位 - th ユーザー - th レート - tbody - - - let rank = 1; - let lastRating = ''; - each rec in ranking - - const rating = rec.rating.toFixed(2); - tr - td=rank - td: +exta(href="https://" + rec.host + "/@" + rec.username) @!{rec.username}@!{rec.host} - td=rating + table + thead: tr + th 順位 + th ユーザー + th レート + tbody - - if (lastRating !== rating) { - rank++; - } - lastRating = rating + let rank = 1; + let lastRating = ''; + each rec in ranking + - const rating = rec.rating.toFixed(2); + tr + td=rank + td: +exta(href="https://" + rec.host + "/@" + rec.username) @!{rec.username}@!{rec.host} + td=rating + - + if (lastRating !== rating) { + rank++; + } + lastRating = rating + diff --git a/src/views/ranking.pug b/src/views/ranking.pug index 87105e6..c2170d6 100644 --- a/src/views/ranking.pug +++ b/src/views/ranking.pug @@ -9,4 +9,7 @@ block content summary これは何? p みす廃ランキングは、独自に算出された「みす廃レート」の高い順ランキングです。毎日みす廃あらーとが発行される度に更新されます。 p みす廃レートは、登録日からの経過日数およびノート数から算出されます。 - +rankingTable \ No newline at end of file + if state.nowCalculating + p 現在計算中です。後ほどご確認ください + else + +rankingTable \ No newline at end of file