Refactorng
This commit is contained in:
parent
865fd25af1
commit
aa4ef6745a
132 changed files with 180 additions and 212 deletions
4
src/misc/acct/parse.ts
Normal file
4
src/misc/acct/parse.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export default (acct: string) => {
|
||||
const splitted = acct.split('@', 2);
|
||||
return { username: splitted[0], host: splitted[1] || null };
|
||||
};
|
5
src/misc/acct/render.ts
Normal file
5
src/misc/acct/render.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { IUser } from '../../models/user';
|
||||
|
||||
export default (user: IUser) => {
|
||||
return user.host === null ? user.username : `${user.username}@${user.host}`;
|
||||
};
|
33
src/misc/cafy-id.ts
Normal file
33
src/misc/cafy-id.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import * as mongo from 'mongodb';
|
||||
import { Context } from 'cafy';
|
||||
|
||||
export const isAnId = (x: any) => mongo.ObjectID.isValid(x);
|
||||
export const isNotAnId = (x: any) => !isAnId(x);
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
export default class ID extends Context<mongo.ObjectID> {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.transform = v => {
|
||||
if (isAnId(v) && !mongo.ObjectID.prototype.isPrototypeOf(v)) {
|
||||
return new mongo.ObjectID(v);
|
||||
} else {
|
||||
return v;
|
||||
}
|
||||
};
|
||||
|
||||
this.push(v => {
|
||||
if (!mongo.ObjectID.prototype.isPrototypeOf(v) && isNotAnId(v)) {
|
||||
return new Error('must-be-an-id');
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public getType() {
|
||||
return super.getType('string');
|
||||
}
|
||||
}
|
35
src/misc/cli/indicator.ts
Normal file
35
src/misc/cli/indicator.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import * as readline from 'readline';
|
||||
|
||||
/**
|
||||
* Indicator
|
||||
*/
|
||||
export default class {
|
||||
private clock: NodeJS.Timer;
|
||||
|
||||
constructor(text: string) {
|
||||
let i = 0; // Dots counter
|
||||
|
||||
draw();
|
||||
this.clock = setInterval(draw, 300);
|
||||
|
||||
function draw(): void {
|
||||
cll();
|
||||
i = (i + 1) % 4;
|
||||
const dots = new Array(i + 1).join('.');
|
||||
process.stdout.write(text + dots); // Write text
|
||||
}
|
||||
}
|
||||
|
||||
public end(): void {
|
||||
clearInterval(this.clock);
|
||||
cll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear current line
|
||||
*/
|
||||
function cll(): void {
|
||||
readline.clearLine(process.stdout, 0); // Clear current text
|
||||
readline.cursorTo(process.stdout, 0, null); // Move cursor to the head of line
|
||||
}
|
85
src/misc/cli/progressbar.ts
Normal file
85
src/misc/cli/progressbar.ts
Normal file
|
@ -0,0 +1,85 @@
|
|||
import { EventEmitter } from 'events';
|
||||
import * as readline from 'readline';
|
||||
import chalk from 'chalk';
|
||||
|
||||
/**
|
||||
* Progress bar
|
||||
*/
|
||||
export default class extends EventEmitter {
|
||||
public max: number;
|
||||
public value: number;
|
||||
public text: string;
|
||||
private indicator: number;
|
||||
|
||||
constructor(max: number, text: string = null) {
|
||||
super();
|
||||
this.max = max;
|
||||
this.value = 0;
|
||||
this.text = text;
|
||||
this.indicator = 0;
|
||||
this.draw();
|
||||
|
||||
const iclock = setInterval(() => {
|
||||
this.indicator = (this.indicator + 1) % 4;
|
||||
this.draw();
|
||||
}, 200);
|
||||
|
||||
this.on('complete', () => {
|
||||
clearInterval(iclock);
|
||||
});
|
||||
}
|
||||
|
||||
public increment(): void {
|
||||
this.value++;
|
||||
this.draw();
|
||||
|
||||
// Check if it is fulfilled
|
||||
if (this.value === this.max) {
|
||||
this.indicator = null;
|
||||
|
||||
cll();
|
||||
process.stdout.write(`${this.render()} -> ${chalk.bold('Complete')}\n`);
|
||||
|
||||
this.emit('complete');
|
||||
}
|
||||
}
|
||||
|
||||
public draw(): void {
|
||||
const str = this.render();
|
||||
cll();
|
||||
process.stdout.write(str);
|
||||
}
|
||||
|
||||
private render(): string {
|
||||
const width = 30;
|
||||
const t = this.text ? `${this.text} ` : '';
|
||||
|
||||
const v = Math.floor((this.value / this.max) * width);
|
||||
const vs = new Array(v + 1).join('*');
|
||||
|
||||
const p = width - v;
|
||||
const ps = new Array(p + 1).join(' ');
|
||||
|
||||
const percentage = Math.floor((this.value / this.max) * 100);
|
||||
const percentages = chalk.gray(`(${percentage} %)`);
|
||||
|
||||
let i: string;
|
||||
switch (this.indicator) {
|
||||
case 0: i = '-'; break;
|
||||
case 1: i = '\\'; break;
|
||||
case 2: i = '|'; break;
|
||||
case 3: i = '/'; break;
|
||||
case null: i = '+'; break;
|
||||
}
|
||||
|
||||
return `${i} ${t}[${vs}${ps}] ${this.value} / ${this.max} ${percentages}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear current line
|
||||
*/
|
||||
function cll(): void {
|
||||
readline.clearLine(process.stdout, 0); // Clear current text
|
||||
readline.cursorTo(process.stdout, 0, null); // Move cursor to the head of line
|
||||
}
|
33
src/misc/dependencyInfo.ts
Normal file
33
src/misc/dependencyInfo.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import Logger from './logger';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
export default class {
|
||||
private logger: Logger;
|
||||
|
||||
constructor() {
|
||||
this.logger = new Logger('Deps');
|
||||
}
|
||||
|
||||
public showAll(): void {
|
||||
this.show('MongoDB', 'mongo --version', x => x.match(/^MongoDB shell version:? (.*)\r?\n/));
|
||||
this.show('Redis', 'redis-server --version', x => x.match(/v=([0-9\.]*)/));
|
||||
this.show('ImageMagick', 'magick -version', x => x.match(/^Version: ImageMagick (.+?)\r?\n/));
|
||||
}
|
||||
|
||||
public show(serviceName: string, command: string, transform: (x: string) => RegExpMatchArray): void {
|
||||
try {
|
||||
// ステータス0以外のときにexecSyncはstderrをコンソール上に出力してしまうので
|
||||
// プロセスからのstderrをすべて無視するように stdio オプションをセット
|
||||
const x = execSync(command, { stdio: ['pipe', 'pipe', 'ignore'] });
|
||||
const ver = transform(x.toString());
|
||||
if (ver != null) {
|
||||
this.logger.info(`${serviceName} ${ver[1]} found`);
|
||||
} else {
|
||||
this.logger.warn(`${serviceName} not found`);
|
||||
this.logger.warn(`Regexp used for version check of ${serviceName} is probably messed up`);
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.warn(`${serviceName} not found`);
|
||||
}
|
||||
}
|
||||
}
|
14
src/misc/environmentInfo.ts
Normal file
14
src/misc/environmentInfo.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import Logger from './logger';
|
||||
|
||||
export default class {
|
||||
public static show(): void {
|
||||
const env = process.env.NODE_ENV;
|
||||
const logger = new Logger('Env');
|
||||
logger.info(typeof env == 'undefined' ? 'NODE_ENV is not set' : `NODE_ENV: ${env}`);
|
||||
|
||||
if (env !== 'production') {
|
||||
logger.warn('The environment is not in production mode');
|
||||
logger.warn('Do not use for production purpose');
|
||||
}
|
||||
}
|
||||
}
|
50
src/misc/get-note-summary.ts
Normal file
50
src/misc/get-note-summary.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* 投稿を表す文字列を取得します。
|
||||
* @param {*} note (packされた)投稿
|
||||
*/
|
||||
const summarize = (note: any): string => {
|
||||
if (note.deletedAt) {
|
||||
return '(削除された投稿)';
|
||||
}
|
||||
|
||||
if (note.isHidden) {
|
||||
return '(非公開の投稿)';
|
||||
}
|
||||
|
||||
let summary = '';
|
||||
|
||||
// 本文
|
||||
summary += note.text ? note.text : '';
|
||||
|
||||
// メディアが添付されているとき
|
||||
if (note.media.length != 0) {
|
||||
summary += ` (${note.media.length}つのメディア)`;
|
||||
}
|
||||
|
||||
// 投票が添付されているとき
|
||||
if (note.poll) {
|
||||
summary += ' (投票)';
|
||||
}
|
||||
|
||||
// 返信のとき
|
||||
if (note.replyId) {
|
||||
if (note.reply) {
|
||||
summary += ` RE: ${summarize(note.reply)}`;
|
||||
} else {
|
||||
summary += ' RE: ...';
|
||||
}
|
||||
}
|
||||
|
||||
// Renoteのとき
|
||||
if (note.renoteId) {
|
||||
if (note.renote) {
|
||||
summary += ` RP: ${summarize(note.renote)}`;
|
||||
} else {
|
||||
summary += ' RP: ...';
|
||||
}
|
||||
}
|
||||
|
||||
return summary.trim();
|
||||
};
|
||||
|
||||
export default summarize;
|
28
src/misc/get-notification-summary.ts
Normal file
28
src/misc/get-notification-summary.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import getUserName from './get-user-name';
|
||||
import getNoteSummary from './get-note-summary';
|
||||
import getReactionEmoji from './get-reaction-emoji';
|
||||
|
||||
/**
|
||||
* 通知を表す文字列を取得します。
|
||||
* @param notification 通知
|
||||
*/
|
||||
export default function(notification: any): string {
|
||||
switch (notification.type) {
|
||||
case 'follow':
|
||||
return `${getUserName(notification.user)}にフォローされました`;
|
||||
case 'mention':
|
||||
return `言及されました:\n${getUserName(notification.user)}「${getNoteSummary(notification.note)}」`;
|
||||
case 'reply':
|
||||
return `返信されました:\n${getUserName(notification.user)}「${getNoteSummary(notification.note)}」`;
|
||||
case 'renote':
|
||||
return `Renoteされました:\n${getUserName(notification.user)}「${getNoteSummary(notification.note)}」`;
|
||||
case 'quote':
|
||||
return `引用されました:\n${getUserName(notification.user)}「${getNoteSummary(notification.note)}」`;
|
||||
case 'reaction':
|
||||
return `リアクションされました:\n${getUserName(notification.user)} <${getReactionEmoji(notification.reaction)}>「${getNoteSummary(notification.note)}」`;
|
||||
case 'poll_vote':
|
||||
return `投票されました:\n${getUserName(notification.user)}「${getNoteSummary(notification.note)}」`;
|
||||
default:
|
||||
return `<不明な通知タイプ: ${notification.type}>`;
|
||||
}
|
||||
}
|
14
src/misc/get-reaction-emoji.ts
Normal file
14
src/misc/get-reaction-emoji.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
export default function(reaction: string): string {
|
||||
switch (reaction) {
|
||||
case 'like': return '👍';
|
||||
case 'love': return '❤️';
|
||||
case 'laugh': return '😆';
|
||||
case 'hmm': return '🤔';
|
||||
case 'surprise': return '😮';
|
||||
case 'congrats': return '🎉';
|
||||
case 'angry': return '💢';
|
||||
case 'confused': return '😥';
|
||||
case 'pudding': return '🍮';
|
||||
default: return '';
|
||||
}
|
||||
}
|
5
src/misc/get-user-name.ts
Normal file
5
src/misc/get-user-name.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { IUser } from '../models/user';
|
||||
|
||||
export default function(user: IUser): string {
|
||||
return user.name || '名無し';
|
||||
}
|
18
src/misc/get-user-summary.ts
Normal file
18
src/misc/get-user-summary.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { IUser, isLocalUser } from '../models/user';
|
||||
import getAcct from '../misc/acct/render';
|
||||
import getUserName from './get-user-name';
|
||||
|
||||
/**
|
||||
* ユーザーを表す文字列を取得します。
|
||||
* @param user ユーザー
|
||||
*/
|
||||
export default function(user: IUser): string {
|
||||
let string = `${getUserName(user)} (@${getAcct(user)})\n` +
|
||||
`${user.notesCount}投稿、${user.followingCount}フォロー、${user.followersCount}フォロワー\n`;
|
||||
|
||||
if (isLocalUser(user)) {
|
||||
string += `場所: ${user.profile.location}、誕生日: ${user.profile.birthday}\n`;
|
||||
}
|
||||
|
||||
return string + `「${user.description}」`;
|
||||
}
|
53
src/misc/logger.ts
Normal file
53
src/misc/logger.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
import chalk, { Chalk } from 'chalk';
|
||||
|
||||
export type LogLevel = 'Error' | 'Warn' | 'Info';
|
||||
|
||||
function toLevelColor(level: LogLevel): Chalk {
|
||||
switch (level) {
|
||||
case 'Error': return chalk.red;
|
||||
case 'Warn': return chalk.yellow;
|
||||
case 'Info': return chalk.blue;
|
||||
}
|
||||
}
|
||||
|
||||
export default class Logger {
|
||||
private domain: string;
|
||||
|
||||
constructor(domain: string) {
|
||||
this.domain = domain;
|
||||
}
|
||||
|
||||
public static log(level: LogLevel, message: string): void {
|
||||
const color = toLevelColor(level);
|
||||
const time = (new Date()).toLocaleTimeString('ja-JP');
|
||||
console.log(`[${time} ${color.bold(level.toUpperCase())}]: ${message}`);
|
||||
}
|
||||
|
||||
public static error(message: string): void {
|
||||
Logger.log('Error', message);
|
||||
}
|
||||
|
||||
public static warn(message: string): void {
|
||||
Logger.log('Warn', message);
|
||||
}
|
||||
|
||||
public static info(message: string): void {
|
||||
Logger.log('Info', message);
|
||||
}
|
||||
|
||||
public log(level: LogLevel, message: string): void {
|
||||
Logger.log(level, `[${this.domain}] ${message}`);
|
||||
}
|
||||
|
||||
public error(message: string): void {
|
||||
this.log('Error', message);
|
||||
}
|
||||
|
||||
public warn(message: string): void {
|
||||
this.log('Warn', message);
|
||||
}
|
||||
|
||||
public info(message: string): void {
|
||||
this.log('Info', message);
|
||||
}
|
||||
}
|
16
src/misc/machineInfo.ts
Normal file
16
src/misc/machineInfo.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import * as os from 'os';
|
||||
import Logger from './logger';
|
||||
|
||||
export default class {
|
||||
public static show(): void {
|
||||
const totalmem = (os.totalmem() / 1024 / 1024 / 1024).toFixed(1);
|
||||
const freemem = (os.freemem() / 1024 / 1024 / 1024).toFixed(1);
|
||||
const logger = new Logger('Machine');
|
||||
logger.info(`Hostname: ${os.hostname()}`);
|
||||
logger.info(`Platform: ${process.platform}`);
|
||||
logger.info(`Architecture: ${process.arch}`);
|
||||
logger.info(`Node.js: ${process.version}`);
|
||||
logger.info(`CPU: ${os.cpus().length}core`);
|
||||
logger.info(`MEM: ${totalmem}GB (available: ${freemem}GB)`);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue