0
0
Fork 0

ディレクトリ再編

This commit is contained in:
xeltica 2021-09-04 11:00:38 +09:00
parent cb924ff92b
commit 85d471efbb
45 changed files with 204 additions and 41 deletions

View file

@ -0,0 +1,70 @@
import { config } from '../../config';
import { User } from '../models/entities/user';
import { Score } from '../../common/types/score';
export const defaultTemplate = `昨日のMisskeyの活動は
: {notesCount}({notesDelta})
: {followingCount}({followingDelta})
:{followersCount}({followersDelta})
{url}`;
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),
},
rating: {
description: 'みす廃レート',
replace: (_, user) => String(user.rating),
},
};
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

@ -0,0 +1,13 @@
import rndstr from 'rndstr';
import { UsedToken } from '../models/entities/used-token';
import { UsedTokens } from '../models';
export const genToken = async (): Promise<string> => {
let used: UsedToken | undefined = undefined;
let token: string;
do {
token = rndstr(32);
used = await UsedTokens.findOne({ token });
} while (used !== undefined);
return token;
};

View file

@ -0,0 +1,19 @@
import { User } from '../models/entities/user';
import { Score } from '../../common/types/score';
import { api } from '../services/misskey';
import { toSignedString } from './to-signed-string';
export const getScores = async (user: User): Promise<Score> => {
const miUser = await api<Record<string, number>>(user.host, 'users/show', { username: user.username }, user.token);
if (miUser.error) {
throw miUser.error;
}
return {
notesCount: miUser.notesCount,
followingCount: miUser.followingCount,
followersCount: miUser.followersCount,
notesDelta: toSignedString(miUser.notesCount - user.prevNotesCount),
followingDelta: toSignedString(miUser.followingCount - user.prevFollowingCount),
followersDelta: toSignedString(miUser.followersCount - user.prevFollowersCount),
};
};

View file

@ -0,0 +1,14 @@
import { Users } from '../models';
import { User } from '../models/entities/user';
export const getRanking = async (limit: number | null = 10): Promise<User[]> => {
const query = Users.createQueryBuilder('user')
.where('"user"."bannedFromRanking" IS NOT TRUE')
.orderBy('"user".rating', 'DESC');
if (limit !== null) {
query.limit(limit);
}
return await query.getMany();
};

View file

@ -0,0 +1 @@
export const toSignedString = (num: number): string => num < 0 ? num.toString() : '+' + num;

View file

@ -0,0 +1,13 @@
import dayjs from 'dayjs';
import { User } from '../models/entities/user';
import { updateUser } from './users';
import { MiUser } from './update-score';
export const updateRating = async (user: User, miUser: MiUser): Promise<void> => {
const elapsedDays = dayjs().diff(dayjs(miUser.createdAt), 'd') + 1;
await updateUser(user.username, user.host, {
prevRating: user.rating,
rating: miUser.notesCount / elapsedDays,
});
};

View file

@ -0,0 +1,17 @@
import { User } from '../models/entities/user';
import { updateUser } from './users';
export type MiUser = {
notesCount: number,
followingCount: number,
followersCount: number,
createdAt: string,
};
export const updateScore = async (user: User, miUser: MiUser): Promise<void> => {
await updateUser(user.username, user.host, {
prevNotesCount: miUser.notesCount ?? 0,
prevFollowingCount: miUser.followingCount ?? 0,
prevFollowersCount: miUser.followersCount ?? 0,
});
};

View file

@ -0,0 +1,44 @@
import { User } from '../models/entities/user';
import { Users } from '../models';
import { DeepPartial } from 'typeorm';
import { genToken } from './gen-token';
import pick from 'object.pick';
export const getUser = (username: string, host: string): Promise<User | undefined> => {
return Users.findOne({ username, host });
};
export const updateUsersMisshaiToken = async (user: User | User['id']): Promise<string> => {
const u = typeof user === 'number'
? user
: user.id;
const misshaiToken = await genToken();
Users.update(u, { misshaiToken });
return misshaiToken;
};
export const getUserByMisshaiToken = (token: string): Promise<User | undefined> => {
return Users.findOne({ misshaiToken: token });
};
export const upsertUser = async (username: string, host: string, token: string): Promise<void> => {
const u = await getUser(username, host);
if (u) {
await Users.update(u.id, { token });
} else {
await Users.insert({ username, host, token });
}
};
export const updateUser = async (username: string, host: string, record: DeepPartial<User>): Promise<void> => {
await Users.update({ username, host }, record);
};
export const deleteUser = async (username: string, host: string): Promise<void> => {
await Users.delete({ username, host });
};
export const getUserCount = (): Promise<number> => {
return Users.count();
};