resolve #10
This commit is contained in:
parent
ccd496722a
commit
d16f22feb5
7 changed files with 111 additions and 51 deletions
14
migration/1609941393782-template.ts
Normal file
14
migration/1609941393782-template.ts
Normal 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"');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,13 +1,66 @@
|
|||
import { config } from '../config';
|
||||
import { User } from '../models/entities/user';
|
||||
import { Score } from '../types/Score';
|
||||
|
||||
export const format = (score: Score): string => `昨日のMisskeyの活動は
|
||||
export const defaultTemplate = `昨日のMisskeyの活動は
|
||||
|
||||
ノート: ${score.notesCount}(${score.notesDelta})
|
||||
フォロー : ${score.followingCount}(${score.followingDelta})
|
||||
フォロワー :${score.followersCount}(${score.followersDelta})
|
||||
ノート: {notesCount}({notesDelta})
|
||||
フォロー : {followingCount}({followingDelta})
|
||||
フォロワー :{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';
|
||||
};
|
||||
|
|
|
@ -72,4 +72,11 @@ export class User {
|
|||
default: false,
|
||||
})
|
||||
public remoteFollowersOnly: boolean;
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
length: 280,
|
||||
nullable: true,
|
||||
})
|
||||
public template: string | null;
|
||||
}
|
|
@ -13,6 +13,7 @@ import { AlertMode, alertModes } from '../types/AlertMode';
|
|||
import { Users } from '../models';
|
||||
import { send } from '../services/send';
|
||||
import { visibilities, Visibility } from '../types/Visibility';
|
||||
import { defaultTemplate, variables } from '../functions/format';
|
||||
|
||||
export const router = new Router<DefaultState, Context>();
|
||||
|
||||
|
@ -67,6 +68,8 @@ router.get('/', async ctx => {
|
|||
user,
|
||||
// To Activate Groundpolis Mode
|
||||
isGroundpolis: meta.version.includes('gp'),
|
||||
defaultTemplate,
|
||||
templateVariables: variables,
|
||||
usersCount: await getUserCount(),
|
||||
score: await getScores(user),
|
||||
from: ctx.query.from,
|
||||
|
@ -209,6 +212,8 @@ router.post('/update-settings', async ctx => {
|
|||
|
||||
const flag = ctx.request.body.flag;
|
||||
|
||||
const template = ctx.request.body.template?.trim();
|
||||
|
||||
const token = ctx.cookies.get('token');
|
||||
if (!token) {
|
||||
await die(ctx, 'ログインしていません');
|
||||
|
@ -226,6 +231,7 @@ router.post('/update-settings', async ctx => {
|
|||
alertMode: mode,
|
||||
localOnly: flag === 'localOnly',
|
||||
remoteFollowersOnly: flag === 'remoteFollowersOnly',
|
||||
template: template === defaultTemplate || !template ? null : template,
|
||||
visibility,
|
||||
});
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { getScores } from '../functions/get-scores';
|
|||
import { api } from './misskey';
|
||||
|
||||
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') {
|
||||
console.info(`send ${user.username}@${user.host}'s misshaialert as a note`);
|
||||
|
|
|
@ -70,77 +70,57 @@ block content
|
|||
| スコア通知方法に「Misskey に通知」を選んでいる場合、Groundpolis v3 および Misskey v12 の最新版以外では動作しません。めいすきーや古いバージョンをお使いの方は、「自動的にノートを投稿」をお使いください。
|
||||
form(method="post", action="/update-settings")
|
||||
p: label スコア通知方法:
|
||||
select#alertModeSelector(name="alertMode", tabindex=1)
|
||||
select#alertModeSelector(name="alertMode")
|
||||
each set in alertModes
|
||||
option(value=set[0], selected=(user.alertMode === set[0]))= set[1]
|
||||
#hideWhenAlertModeNotNote
|
||||
p: label 公開範囲:
|
||||
select(name="visibility", tabindex=2)
|
||||
select(name="visibility")
|
||||
each set in visibilities
|
||||
option(value=set[0], selected=(user.visibility === set[0]))= set[1]
|
||||
p
|
||||
| フラグ <br />
|
||||
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 />
|
||||
label
|
||||
input(type="radio", name="flag", value="localOnly", checked=user.localOnly, tabindex=4)
|
||||
input(type="radio", name="flag", value="localOnly", checked=user.localOnly)
|
||||
| ローカルのみ<br />
|
||||
if isGroundpolis
|
||||
label
|
||||
input(type="radio", name="flag", value="remoteFollowersOnly", checked=user.remoteFollowersOnly, tabindex=5)
|
||||
input(type="radio", name="flag", value="remoteFollowersOnly", checked=user.remoteFollowersOnly)
|
||||
| リモートフォロワーとローカル<br />
|
||||
#hideWhenAlertModeNothing
|
||||
div
|
||||
label 投稿テンプレート
|
||||
div: textarea(name="template", disabled, tabindex=6)
|
||||
details(tabindex=7)
|
||||
label 投稿テンプレート<br/>
|
||||
textarea(name="template")=user.template || defaultTemplate
|
||||
details()
|
||||
summary ヘルプ
|
||||
p
|
||||
code {notesCount}
|
||||
| などのテキストを挿入することで、投稿時に自動的に値が埋め込まれます。これを変数といいます。変数の表を次に示します。
|
||||
ul
|
||||
li 空欄にすると、デフォルト値にリセットされます。
|
||||
li ハッシュタグ #misshaialert は、テンプレートに関わらず自動付与されます。
|
||||
li
|
||||
code {notesCount}
|
||||
| といった形式のテキストは変数として扱われ、これを含めると投稿時に自動的に値が埋め込まれます。
|
||||
table
|
||||
thead: tr
|
||||
th 変数
|
||||
th 説明
|
||||
tbody
|
||||
tr
|
||||
td {notesCount}
|
||||
td ノート数
|
||||
tr
|
||||
td {followingCount}
|
||||
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) 保存
|
||||
each val, key in templateVariables
|
||||
tr
|
||||
td=key
|
||||
td=val.description
|
||||
button.primary(type="submit") 保存
|
||||
|
||||
|
||||
section.xd-card#settings
|
||||
.header
|
||||
h1.title 操作
|
||||
.body
|
||||
form.mb-2(action="/send", method="post", tabindex=9): 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="/optout", method="post", tabindex=11): button.danger#optout(style="display: inline-block") アカウント連携を解除する
|
||||
form.mb-2(action="/send", method="post"): button#send(style="display: inline-block") アラートをテスト送信
|
||||
form.mb-2(action="/logout", method="post"): button#logout(style="display: inline-block") ログアウト
|
||||
form.mb-2(action="/optout", method="post"): button.danger#optout(style="display: inline-block") アカウント連携を解除する
|
||||
|
||||
block script
|
||||
script.
|
||||
|
|
|
@ -171,10 +171,10 @@ textarea {
|
|||
border: none;
|
||||
outline: none;
|
||||
height: 8rem;
|
||||
line-height: 1;
|
||||
line-height: 1.2;
|
||||
color: var(--fg);
|
||||
&:focus {
|
||||
border: 1px solid var(--primary);
|
||||
color: var(--fg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue