0
0
Fork 0
This commit is contained in:
Xeltica 2021-01-06 23:50:42 +09:00
parent ccd496722a
commit d16f22feb5
7 changed files with 111 additions and 51 deletions

View file

@ -0,0 +1,14 @@
import {MigrationInterface, QueryRunner} from 'typeorm';
export class template1609941393782 implements MigrationInterface {
name = 'template1609941393782'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('ALTER TABLE "user" ADD "template" character varying(280)');
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('ALTER TABLE "user" DROP COLUMN "template"');
}
}

View file

@ -1,13 +1,66 @@
import { config } from '../config'; import { config } from '../config';
import { User } from '../models/entities/user';
import { Score } from '../types/Score'; import { Score } from '../types/Score';
export const format = (score: Score): string => `昨日のMisskeyの活動は export const defaultTemplate = `昨日のMisskeyの活動は
ノート: ${score.notesCount}(${score.notesDelta}) : {notesCount}({notesDelta})
フォロー : ${score.followingCount}(${score.followingDelta}) : {followingCount}({followingDelta})
フォロワー :${score.followersCount}(${score.followersDelta}) :{followersCount}({followersDelta})
${config.url} {url}`;
#misshaialert`; export type Variable = {
description?: string;
replace?: string | ((score: Score, user: User) => string);
};
export const variables: Record<string, Variable> = {
notesCount: {
description: 'ノート数',
replace: (score) => String(score.notesCount),
},
followingCount: {
description: 'フォロー数',
replace: (score) => String(score.followingCount),
},
followersCount: {
description: 'フォロワー数',
replace: (score) => String(score.followersCount),
},
notesDelta: {
description: '昨日とのノート数の差',
replace: (score) => String(score.notesDelta),
},
followingDelta: {
description: '昨日とのフォロー数の差',
replace: (score) => String(score.followingDelta),
},
followersDelta: {
description: '昨日とのフォロワー数の差',
replace: (score) => String(score.followersDelta),
},
url: {
description: 'みす廃アラートのURL',
replace: config.url,
},
username: {
description: 'ユーザー名',
replace: (_, user) => String(user.username),
},
host: {
description: '所属するインスタンスのホスト名',
replace: (_, user) => String(user.host),
},
};
const variableRegex = /\{([a-zA-Z0-9_]+?)\}/g;
export const format = (score: Score, user: User): string => {
const template = user.template || defaultTemplate;
return template.replace(variableRegex, (m, name) => {
const v = variables[name];
return !v || !v.replace ? m : typeof v.replace === 'function' ? v.replace(score, user) : v.replace;
}) + '\n\n#misshaialert';
};

View file

@ -72,4 +72,11 @@ export class User {
default: false, default: false,
}) })
public remoteFollowersOnly: boolean; public remoteFollowersOnly: boolean;
@Column({
type: 'varchar',
length: 280,
nullable: true,
})
public template: string | null;
} }

View file

@ -13,6 +13,7 @@ import { AlertMode, alertModes } from '../types/AlertMode';
import { Users } from '../models'; import { Users } from '../models';
import { send } from '../services/send'; import { send } from '../services/send';
import { visibilities, Visibility } from '../types/Visibility'; import { visibilities, Visibility } from '../types/Visibility';
import { defaultTemplate, variables } from '../functions/format';
export const router = new Router<DefaultState, Context>(); export const router = new Router<DefaultState, Context>();
@ -67,6 +68,8 @@ router.get('/', async ctx => {
user, user,
// To Activate Groundpolis Mode // To Activate Groundpolis Mode
isGroundpolis: meta.version.includes('gp'), isGroundpolis: meta.version.includes('gp'),
defaultTemplate,
templateVariables: variables,
usersCount: await getUserCount(), usersCount: await getUserCount(),
score: await getScores(user), score: await getScores(user),
from: ctx.query.from, from: ctx.query.from,
@ -209,6 +212,8 @@ router.post('/update-settings', async ctx => {
const flag = ctx.request.body.flag; const flag = ctx.request.body.flag;
const template = ctx.request.body.template?.trim();
const token = ctx.cookies.get('token'); const token = ctx.cookies.get('token');
if (!token) { if (!token) {
await die(ctx, 'ログインしていません'); await die(ctx, 'ログインしていません');
@ -226,6 +231,7 @@ router.post('/update-settings', async ctx => {
alertMode: mode, alertMode: mode,
localOnly: flag === 'localOnly', localOnly: flag === 'localOnly',
remoteFollowersOnly: flag === 'remoteFollowersOnly', remoteFollowersOnly: flag === 'remoteFollowersOnly',
template: template === defaultTemplate || !template ? null : template,
visibility, visibility,
}); });

View file

@ -4,7 +4,7 @@ import { getScores } from '../functions/get-scores';
import { api } from './misskey'; import { api } from './misskey';
export const send = async (user: User): Promise<void> => { export const send = async (user: User): Promise<void> => {
const text = format(await getScores(user)); const text = format(await getScores(user), user);
if (user.alertMode === 'note') { if (user.alertMode === 'note') {
console.info(`send ${user.username}@${user.host}'s misshaialert as a note`); console.info(`send ${user.username}@${user.host}'s misshaialert as a note`);

View file

@ -70,77 +70,57 @@ block content
| スコア通知方法に「Misskey に通知」を選んでいる場合、Groundpolis v3 および Misskey v12 の最新版以外では動作しません。めいすきーや古いバージョンをお使いの方は、「自動的にノートを投稿」をお使いください。 | スコア通知方法に「Misskey に通知」を選んでいる場合、Groundpolis v3 および Misskey v12 の最新版以外では動作しません。めいすきーや古いバージョンをお使いの方は、「自動的にノートを投稿」をお使いください。
form(method="post", action="/update-settings") form(method="post", action="/update-settings")
p: label スコア通知方法: p: label スコア通知方法:
select#alertModeSelector(name="alertMode", tabindex=1) select#alertModeSelector(name="alertMode")
each set in alertModes each set in alertModes
option(value=set[0], selected=(user.alertMode === set[0]))= set[1] option(value=set[0], selected=(user.alertMode === set[0]))= set[1]
#hideWhenAlertModeNotNote #hideWhenAlertModeNotNote
p: label 公開範囲: p: label 公開範囲:
select(name="visibility", tabindex=2) select(name="visibility")
each set in visibilities each set in visibilities
option(value=set[0], selected=(user.visibility === set[0]))= set[1] option(value=set[0], selected=(user.visibility === set[0]))= set[1]
p p
| フラグ <br /> | フラグ <br />
label label
input(type="radio", name="flag", value="none", checked=!user.localOnly && !user.remoteFollowersOnly, tabindex=3) input(type="radio", name="flag", value="none", checked=!user.localOnly && !user.remoteFollowersOnly)
| なし(標準)<br /> | なし(標準)<br />
label label
input(type="radio", name="flag", value="localOnly", checked=user.localOnly, tabindex=4) input(type="radio", name="flag", value="localOnly", checked=user.localOnly)
| ローカルのみ<br /> | ローカルのみ<br />
if isGroundpolis if isGroundpolis
label label
input(type="radio", name="flag", value="remoteFollowersOnly", checked=user.remoteFollowersOnly, tabindex=5) input(type="radio", name="flag", value="remoteFollowersOnly", checked=user.remoteFollowersOnly)
| リモートフォロワーとローカル<br /> | リモートフォロワーとローカル<br />
#hideWhenAlertModeNothing #hideWhenAlertModeNothing
div div
label 投稿テンプレート label 投稿テンプレート<br/>
div: textarea(name="template", disabled, tabindex=6) textarea(name="template")=user.template || defaultTemplate
details(tabindex=7) details()
summary ヘルプ summary ヘルプ
p ul
code {notesCount} li 空欄にすると、デフォルト値にリセットされます。
| などのテキストを挿入することで、投稿時に自動的に値が埋め込まれます。これを変数といいます。変数の表を次に示します。 li ハッシュタグ #misshaialert は、テンプレートに関わらず自動付与されます。
li
code {notesCount}
| といった形式のテキストは変数として扱われ、これを含めると投稿時に自動的に値が埋め込まれます。
table table
thead: tr thead: tr
th 変数 th 変数
th 説明 th 説明
tbody tbody
tr each val, key in templateVariables
td {notesCount} tr
td ノート数 td=key
tr td=val.description
td {followingCount} button.primary(type="submit") 保存
td フォロー数
tr
td {followersCount}
td フォロワー数
tr
td {notesDelta}
td 昨日とのノート数の差
tr
td {followingDelta}
td 昨日とのフォロー数の差
tr
td {followersDelta}
td 昨日とのフォロワー数の差
tr
td {url}
td みす廃アラートのURL
tr
td {ranking}
td みす廃ランキングの順位
tr
td {rating}
td みす廃レート
button.primary(type="submit", tabindex=8) 保存
section.xd-card#settings section.xd-card#settings
.header .header
h1.title 操作 h1.title 操作
.body .body
form.mb-2(action="/send", method="post", tabindex=9): button#send(style="display: inline-block") アラートをテスト送信 form.mb-2(action="/send", method="post"): button#send(style="display: inline-block") アラートをテスト送信
form.mb-2(action="/logout", method="post", tabindex=10): button#logout(style="display: inline-block") ログアウト form.mb-2(action="/logout", method="post"): button#logout(style="display: inline-block") ログアウト
form.mb-2(action="/optout", method="post", tabindex=11): button.danger#optout(style="display: inline-block") アカウント連携を解除する form.mb-2(action="/optout", method="post"): button.danger#optout(style="display: inline-block") アカウント連携を解除する
block script block script
script. script.

View file

@ -171,10 +171,10 @@ textarea {
border: none; border: none;
outline: none; outline: none;
height: 8rem; height: 8rem;
line-height: 1; line-height: 1.2;
color: var(--fg);
&:focus { &:focus {
border: 1px solid var(--primary); border: 1px solid var(--primary);
color: var(--fg);
} }
} }