mirror of
https://git.joinsharkey.org/Sharkey/Sharkey.git
synced 2024-11-30 20:43:08 +02:00
Implement following stats
This commit is contained in:
parent
a62013f54d
commit
13f82856f9
4 changed files with 139 additions and 9 deletions
|
@ -7,6 +7,7 @@ import renderFollow from '../../remote/activitypub/renderer/follow';
|
||||||
import renderAccept from '../../remote/activitypub/renderer/accept';
|
import renderAccept from '../../remote/activitypub/renderer/accept';
|
||||||
import { deliver } from '../../queue';
|
import { deliver } from '../../queue';
|
||||||
import createFollowRequest from './requests/create';
|
import createFollowRequest from './requests/create';
|
||||||
|
import { followingStats } from '../stats';
|
||||||
|
|
||||||
export default async function(follower: IUser, followee: IUser, requestId?: string) {
|
export default async function(follower: IUser, followee: IUser, requestId?: string) {
|
||||||
// フォロー対象が鍵アカウントである or
|
// フォロー対象が鍵アカウントである or
|
||||||
|
@ -52,6 +53,8 @@ export default async function(follower: IUser, followee: IUser, requestId?: stri
|
||||||
});
|
});
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
followingStats.update(follower, followee, true);
|
||||||
|
|
||||||
// Publish follow event
|
// Publish follow event
|
||||||
if (isLocalUser(follower)) {
|
if (isLocalUser(follower)) {
|
||||||
packUser(followee, follower).then(packed => publishMainStream(follower._id, 'follow', packed));
|
packUser(followee, follower).then(packed => publishMainStream(follower._id, 'follow', packed));
|
||||||
|
|
|
@ -5,6 +5,7 @@ import pack from '../../remote/activitypub/renderer';
|
||||||
import renderFollow from '../../remote/activitypub/renderer/follow';
|
import renderFollow from '../../remote/activitypub/renderer/follow';
|
||||||
import renderUndo from '../../remote/activitypub/renderer/undo';
|
import renderUndo from '../../remote/activitypub/renderer/undo';
|
||||||
import { deliver } from '../../queue';
|
import { deliver } from '../../queue';
|
||||||
|
import { followingStats } from '../stats';
|
||||||
|
|
||||||
export default async function(follower: IUser, followee: IUser) {
|
export default async function(follower: IUser, followee: IUser) {
|
||||||
const following = await Following.findOne({
|
const following = await Following.findOne({
|
||||||
|
@ -37,6 +38,8 @@ export default async function(follower: IUser, followee: IUser) {
|
||||||
});
|
});
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
followingStats.update(follower, followee, false);
|
||||||
|
|
||||||
// Publish unfollow event
|
// Publish unfollow event
|
||||||
if (isLocalUser(follower)) {
|
if (isLocalUser(follower)) {
|
||||||
packUser(followee, follower).then(packed => publishMainStream(follower._id, 'unfollow', packed));
|
packUser(followee, follower).then(packed => publishMainStream(follower._id, 'unfollow', packed));
|
||||||
|
|
|
@ -6,6 +6,7 @@ import renderAccept from '../../../remote/activitypub/renderer/accept';
|
||||||
import { deliver } from '../../../queue';
|
import { deliver } from '../../../queue';
|
||||||
import Following from '../../../models/following';
|
import Following from '../../../models/following';
|
||||||
import { publishMainStream } from '../../../stream';
|
import { publishMainStream } from '../../../stream';
|
||||||
|
import { followingStats } from '../../stats';
|
||||||
|
|
||||||
export default async function(followee: IUser, follower: IUser) {
|
export default async function(followee: IUser, follower: IUser) {
|
||||||
await Following.insert({
|
await Following.insert({
|
||||||
|
@ -57,6 +58,8 @@ export default async function(followee: IUser, follower: IUser) {
|
||||||
});
|
});
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
followingStats.update(follower, followee, true);
|
||||||
|
|
||||||
await User.update({ _id: followee._id }, {
|
await User.update({ _id: followee._id }, {
|
||||||
$inc: {
|
$inc: {
|
||||||
pendingReceivedFollowRequestsCount: -1
|
pendingReceivedFollowRequestsCount: -1
|
||||||
|
|
|
@ -10,6 +10,7 @@ import Note, { INote } from '../models/note';
|
||||||
import User, { isLocalUser, IUser } from '../models/user';
|
import User, { isLocalUser, IUser } from '../models/user';
|
||||||
import DriveFile, { IDriveFile } from '../models/drive-file';
|
import DriveFile, { IDriveFile } from '../models/drive-file';
|
||||||
import { ICollection } from 'monk';
|
import { ICollection } from 'monk';
|
||||||
|
import Following from '../models/following';
|
||||||
|
|
||||||
type Obj = { [key: string]: any };
|
type Obj = { [key: string]: any };
|
||||||
|
|
||||||
|
@ -58,7 +59,7 @@ type Log<T extends Obj> = {
|
||||||
*/
|
*/
|
||||||
abstract class Stats<T> {
|
abstract class Stats<T> {
|
||||||
protected collection: ICollection<Log<T>>;
|
protected collection: ICollection<Log<T>>;
|
||||||
protected abstract async generateTemplate(init: boolean, latestLog?: T): Promise<T>;
|
protected abstract async getTemplate(init: boolean, latestLog?: T, group?: any): Promise<T>;
|
||||||
|
|
||||||
constructor(name: string) {
|
constructor(name: string) {
|
||||||
this.collection = db.get<Log<T>>(`stats.${name}`);
|
this.collection = db.get<Log<T>>(`stats.${name}`);
|
||||||
|
@ -127,7 +128,7 @@ abstract class Stats<T> {
|
||||||
|
|
||||||
if (latestLog) {
|
if (latestLog) {
|
||||||
// 現在の統計を初期挿入
|
// 現在の統計を初期挿入
|
||||||
const data = await this.generateTemplate(false, latestLog.data);
|
const data = await this.getTemplate(false, latestLog.data);
|
||||||
|
|
||||||
const log = await this.collection.insert({
|
const log = await this.collection.insert({
|
||||||
group: group,
|
group: group,
|
||||||
|
@ -142,7 +143,7 @@ abstract class Stats<T> {
|
||||||
// * Misskeyインスタンスを建てて初めてのチャート更新時など
|
// * Misskeyインスタンスを建てて初めてのチャート更新時など
|
||||||
|
|
||||||
// 空の統計を作成
|
// 空の統計を作成
|
||||||
const data = await this.generateTemplate(true);
|
const data = await this.getTemplate(true, null, group);
|
||||||
|
|
||||||
const log = await this.collection.insert({
|
const log = await this.collection.insert({
|
||||||
group: group,
|
group: group,
|
||||||
|
@ -237,7 +238,7 @@ abstract class Stats<T> {
|
||||||
promisedChart.unshift(Promise.resolve(log.data));
|
promisedChart.unshift(Promise.resolve(log.data));
|
||||||
} else { // 隙間埋め
|
} else { // 隙間埋め
|
||||||
const latest = logs.find(l => l.date.getTime() < current.getTime());
|
const latest = logs.find(l => l.date.getTime() < current.getTime());
|
||||||
promisedChart.unshift(this.generateTemplate(false, latest ? latest.data : null));
|
promisedChart.unshift(this.getTemplate(false, latest ? latest.data : null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,7 +316,7 @@ class UsersStats extends Stats<UsersLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
protected async generateTemplate(init: boolean, latestLog?: UsersLog): Promise<UsersLog> {
|
protected async getTemplate(init: boolean, latestLog?: UsersLog): Promise<UsersLog> {
|
||||||
const [localCount, remoteCount] = init ? await Promise.all([
|
const [localCount, remoteCount] = init ? await Promise.all([
|
||||||
User.count({ host: null }),
|
User.count({ host: null }),
|
||||||
User.count({ host: { $ne: null } })
|
User.count({ host: { $ne: null } })
|
||||||
|
@ -406,7 +407,7 @@ class NotesStats extends Stats<NotesLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
protected async generateTemplate(init: boolean, latestLog?: NotesLog): Promise<NotesLog> {
|
protected async getTemplate(init: boolean, latestLog?: NotesLog): Promise<NotesLog> {
|
||||||
const [localCount, remoteCount] = init ? await Promise.all([
|
const [localCount, remoteCount] = init ? await Promise.all([
|
||||||
Note.count({ '_user.host': null }),
|
Note.count({ '_user.host': null }),
|
||||||
Note.count({ '_user.host': { $ne: null } })
|
Note.count({ '_user.host': { $ne: null } })
|
||||||
|
@ -516,7 +517,7 @@ class DriveStats extends Stats<DriveLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
protected async generateTemplate(init: boolean, latestLog?: DriveLog): Promise<DriveLog> {
|
protected async getTemplate(init: boolean, latestLog?: DriveLog): Promise<DriveLog> {
|
||||||
const calcSize = (local: boolean) => DriveFile
|
const calcSize = (local: boolean) => DriveFile
|
||||||
.aggregate([{
|
.aggregate([{
|
||||||
$match: {
|
$match: {
|
||||||
|
@ -628,7 +629,7 @@ class NetworkStats extends Stats<NetworkLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
protected async generateTemplate(init: boolean, latestLog?: NetworkLog): Promise<NetworkLog> {
|
protected async getTemplate(init: boolean, latestLog?: NetworkLog): Promise<NetworkLog> {
|
||||||
return {
|
return {
|
||||||
incomingRequests: 0,
|
incomingRequests: 0,
|
||||||
outgoingRequests: 0,
|
outgoingRequests: 0,
|
||||||
|
@ -671,7 +672,7 @@ class HashtagStats extends Stats<HashtagLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
protected async generateTemplate(init: boolean, latestLog?: HashtagLog): Promise<HashtagLog> {
|
protected async getTemplate(init: boolean, latestLog?: HashtagLog): Promise<HashtagLog> {
|
||||||
return {
|
return {
|
||||||
count: 0
|
count: 0
|
||||||
};
|
};
|
||||||
|
@ -689,4 +690,124 @@ class HashtagStats extends Stats<HashtagLog> {
|
||||||
|
|
||||||
export const hashtagStats = new HashtagStats();
|
export const hashtagStats = new HashtagStats();
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
//#region Following stats
|
||||||
|
/**
|
||||||
|
* ユーザーごとのフォローに関する統計
|
||||||
|
*/
|
||||||
|
type FollowingLog = {
|
||||||
|
local: {
|
||||||
|
/**
|
||||||
|
* フォローしている
|
||||||
|
*/
|
||||||
|
followings: {
|
||||||
|
/**
|
||||||
|
* 合計
|
||||||
|
*/
|
||||||
|
total: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* フォローした数
|
||||||
|
*/
|
||||||
|
inc: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* フォロー解除した数
|
||||||
|
*/
|
||||||
|
dec: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* フォローされている
|
||||||
|
*/
|
||||||
|
followers: {
|
||||||
|
/**
|
||||||
|
* 合計
|
||||||
|
*/
|
||||||
|
total: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* フォローされた数
|
||||||
|
*/
|
||||||
|
inc: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* フォロー解除された数
|
||||||
|
*/
|
||||||
|
dec: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
remote: FollowingLog['local'];
|
||||||
|
};
|
||||||
|
|
||||||
|
class FollowingStats extends Stats<FollowingLog> {
|
||||||
|
constructor() {
|
||||||
|
super('following');
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
protected async getTemplate(init: boolean, latestLog?: FollowingLog, group?: any): Promise<FollowingLog> {
|
||||||
|
const [localFollowings, localFollowers, remoteFollowings, remoteFollowers] = init ? await Promise.all([
|
||||||
|
Following.count({ followerId: group, '_followee.host': null }),
|
||||||
|
Following.count({ followeeId: group, '_user.host': null }),
|
||||||
|
Following.count({ followerId: group, '_followee.host': { $ne: null } }),
|
||||||
|
Following.count({ followeeId: group, '_user.host': { $ne: null } })
|
||||||
|
]) : [
|
||||||
|
latestLog ? latestLog.local.followings.total : 0,
|
||||||
|
latestLog ? latestLog.local.followers.total : 0,
|
||||||
|
latestLog ? latestLog.remote.followings.total : 0,
|
||||||
|
latestLog ? latestLog.remote.followers.total : 0
|
||||||
|
];
|
||||||
|
|
||||||
|
return {
|
||||||
|
local: {
|
||||||
|
followings: {
|
||||||
|
total: localFollowings,
|
||||||
|
inc: 0,
|
||||||
|
dec: 0
|
||||||
|
},
|
||||||
|
followers: {
|
||||||
|
total: localFollowers,
|
||||||
|
inc: 0,
|
||||||
|
dec: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
remote: {
|
||||||
|
followings: {
|
||||||
|
total: remoteFollowings,
|
||||||
|
inc: 0,
|
||||||
|
dec: 0
|
||||||
|
},
|
||||||
|
followers: {
|
||||||
|
total: remoteFollowers,
|
||||||
|
inc: 0,
|
||||||
|
dec: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
public async update(follower: IUser, followee: IUser, isFollow: boolean) {
|
||||||
|
const update: Obj = {};
|
||||||
|
|
||||||
|
update.total = isFollow ? 1 : -1;
|
||||||
|
|
||||||
|
if (isFollow) {
|
||||||
|
update.inc = 1;
|
||||||
|
} else {
|
||||||
|
update.dec = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.inc({
|
||||||
|
[isLocalUser(follower) ? 'local' : 'remote']: { followings: update }
|
||||||
|
});
|
||||||
|
this.inc({
|
||||||
|
[isLocalUser(followee) ? 'local' : 'remote']: { followers: update }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const followingStats = new FollowingStats();
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
Loading…
Reference in a new issue