mirror of
https://git.joinsharkey.org/Sharkey/Sharkey.git
synced 2024-11-09 03:33:09 +02:00
フォロワー数、フォロー数もConditional roleで利用できるように
This commit is contained in:
parent
39c058a4bb
commit
4151087d3c
10 changed files with 79 additions and 9 deletions
|
@ -968,6 +968,10 @@ _role:
|
||||||
isRemote: "リモートユーザー"
|
isRemote: "リモートユーザー"
|
||||||
createdLessThan: "アカウント作成から~以内"
|
createdLessThan: "アカウント作成から~以内"
|
||||||
createdMoreThan: "アカウント作成から~経過"
|
createdMoreThan: "アカウント作成から~経過"
|
||||||
|
followersLessThanOrEq: "フォロワー数が~以下"
|
||||||
|
followersMoreThanOrEq: "フォロワー数が~以上"
|
||||||
|
followingLessThanOrEq: "フォロー数が~以下"
|
||||||
|
followingMoreThanOrEq: "フォロー数が~以上"
|
||||||
and: "~かつ~"
|
and: "~かつ~"
|
||||||
or: "~または~"
|
or: "~または~"
|
||||||
not: "~ではない"
|
not: "~ではない"
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { DI } from '@/di-symbols.js';
|
||||||
import type { MutingsRepository, BlockingsRepository, NotesRepository, AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepository, UserListJoiningsRepository } from '@/models/index.js';
|
import type { MutingsRepository, BlockingsRepository, NotesRepository, AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepository, UserListJoiningsRepository } from '@/models/index.js';
|
||||||
import { UtilityService } from '@/core/UtilityService.js';
|
import { UtilityService } from '@/core/UtilityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import { StreamMessages } from '@/server/api/stream/types.js';
|
||||||
import type { OnApplicationShutdown } from '@nestjs/common';
|
import type { OnApplicationShutdown } from '@nestjs/common';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -73,7 +74,7 @@ export class AntennaService implements OnApplicationShutdown {
|
||||||
const obj = JSON.parse(data);
|
const obj = JSON.parse(data);
|
||||||
|
|
||||||
if (obj.channel === 'internal') {
|
if (obj.channel === 'internal') {
|
||||||
const { type, body } = obj.message;
|
const { type, body } = obj.message as StreamMessages['internal']['payload'];
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'antennaCreated':
|
case 'antennaCreated':
|
||||||
this.antennas.push(body);
|
this.antennas.push(body);
|
||||||
|
|
|
@ -4,8 +4,9 @@ import Redis from 'ioredis';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { Meta } from '@/models/entities/Meta.js';
|
import { Meta } from '@/models/entities/Meta.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import type { OnApplicationShutdown } from '@nestjs/common';
|
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import { StreamMessages } from '@/server/api/stream/types.js';
|
||||||
|
import type { OnApplicationShutdown } from '@nestjs/common';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MetaService implements OnApplicationShutdown {
|
export class MetaService implements OnApplicationShutdown {
|
||||||
|
@ -40,7 +41,7 @@ export class MetaService implements OnApplicationShutdown {
|
||||||
const obj = JSON.parse(data);
|
const obj = JSON.parse(data);
|
||||||
|
|
||||||
if (obj.channel === 'internal') {
|
if (obj.channel === 'internal') {
|
||||||
const { type, body } = obj.message;
|
const { type, body } = obj.message as StreamMessages['internal']['payload'];
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'metaUpdated': {
|
case 'metaUpdated': {
|
||||||
this.cache = body;
|
this.cache = body;
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { MetaService } from '@/core/MetaService.js';
|
||||||
import { UserCacheService } from '@/core/UserCacheService.js';
|
import { UserCacheService } from '@/core/UserCacheService.js';
|
||||||
import { RoleCondFormulaValue } from '@/models/entities/Role.js';
|
import { RoleCondFormulaValue } from '@/models/entities/Role.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
|
import { StreamMessages } from '@/server/api/stream/types.js';
|
||||||
import type { OnApplicationShutdown } from '@nestjs/common';
|
import type { OnApplicationShutdown } from '@nestjs/common';
|
||||||
|
|
||||||
export type RoleOptions = {
|
export type RoleOptions = {
|
||||||
|
@ -69,7 +70,7 @@ export class RoleService implements OnApplicationShutdown {
|
||||||
const obj = JSON.parse(data);
|
const obj = JSON.parse(data);
|
||||||
|
|
||||||
if (obj.channel === 'internal') {
|
if (obj.channel === 'internal') {
|
||||||
const { type, body } = obj.message;
|
const { type, body } = obj.message as StreamMessages['internal']['payload'];
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'roleCreated': {
|
case 'roleCreated': {
|
||||||
const cached = this.rolesCache.get(null);
|
const cached = this.rolesCache.get(null);
|
||||||
|
@ -147,6 +148,18 @@ export class RoleService implements OnApplicationShutdown {
|
||||||
case 'createdMoreThan': {
|
case 'createdMoreThan': {
|
||||||
return user.createdAt.getTime() < (Date.now() - (value.sec * 1000));
|
return user.createdAt.getTime() < (Date.now() - (value.sec * 1000));
|
||||||
}
|
}
|
||||||
|
case 'followersLessThanOrEq': {
|
||||||
|
return user.followersCount <= value.value;
|
||||||
|
}
|
||||||
|
case 'followersMoreThanOrEq': {
|
||||||
|
return user.followersCount >= value.value;
|
||||||
|
}
|
||||||
|
case 'followingLessThanOrEq': {
|
||||||
|
return user.followingCount <= value.value;
|
||||||
|
}
|
||||||
|
case 'followingMoreThanOrEq': {
|
||||||
|
return user.followingCount >= value.value;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import type { CacheableLocalUser, CacheableUser, ILocalUser, User } from '@/mode
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import { StreamMessages } from '@/server/api/stream/types.js';
|
||||||
import type { OnApplicationShutdown } from '@nestjs/common';
|
import type { OnApplicationShutdown } from '@nestjs/common';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -39,7 +40,7 @@ export class UserCacheService implements OnApplicationShutdown {
|
||||||
const obj = JSON.parse(data);
|
const obj = JSON.parse(data);
|
||||||
|
|
||||||
if (obj.channel === 'internal') {
|
if (obj.channel === 'internal') {
|
||||||
const { type, body } = obj.message;
|
const { type, body } = obj.message as StreamMessages['internal']['payload'];
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'userChangeSuspendedState':
|
case 'userChangeSuspendedState':
|
||||||
case 'remoteUserUpdated': {
|
case 'remoteUserUpdated': {
|
||||||
|
@ -62,6 +63,13 @@ export class UserCacheService implements OnApplicationShutdown {
|
||||||
this.localUserByNativeTokenCache.set(body.newToken, user);
|
this.localUserByNativeTokenCache.set(body.newToken, user);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'follow': {
|
||||||
|
const follower = this.userByIdCache.get(body.followerId);
|
||||||
|
if (follower) follower.followingCount++;
|
||||||
|
const followee = this.userByIdCache.get(body.followeeId);
|
||||||
|
if (followee) followee.followersCount++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,7 @@ export class UserFollowingService {
|
||||||
private federatedInstanceService: FederatedInstanceService,
|
private federatedInstanceService: FederatedInstanceService,
|
||||||
private webhookService: WebhookService,
|
private webhookService: WebhookService,
|
||||||
private apRendererService: ApRendererService,
|
private apRendererService: ApRendererService,
|
||||||
|
private globalEventService: GlobalEventService,
|
||||||
private perUserFollowingChart: PerUserFollowingChart,
|
private perUserFollowingChart: PerUserFollowingChart,
|
||||||
private instanceChart: InstanceChart,
|
private instanceChart: InstanceChart,
|
||||||
) {
|
) {
|
||||||
|
@ -195,6 +196,8 @@ export class UserFollowingService {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alreadyFollowed) return;
|
if (alreadyFollowed) return;
|
||||||
|
|
||||||
|
this.globalEventService.publishInternalEvent('follow', { followerId: follower.id, followeeId: followee.id });
|
||||||
|
|
||||||
//#region Increment counts
|
//#region Increment counts
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
|
@ -314,6 +317,8 @@ export class UserFollowingService {
|
||||||
follower: {id: User['id']; host: User['host']; },
|
follower: {id: User['id']; host: User['host']; },
|
||||||
followee: { id: User['id']; host: User['host']; },
|
followee: { id: User['id']; host: User['host']; },
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
this.globalEventService.publishInternalEvent('unfollow', { followerId: follower.id, followeeId: followee.id });
|
||||||
|
|
||||||
//#region Decrement following / followers counts
|
//#region Decrement following / followers counts
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.usersRepository.decrement({ id: follower.id }, 'followingCount', 1),
|
this.usersRepository.decrement({ id: follower.id }, 'followingCount', 1),
|
||||||
|
|
|
@ -3,8 +3,9 @@ import Redis from 'ioredis';
|
||||||
import type { WebhooksRepository } from '@/models/index.js';
|
import type { WebhooksRepository } from '@/models/index.js';
|
||||||
import type { Webhook } from '@/models/entities/Webhook.js';
|
import type { Webhook } from '@/models/entities/Webhook.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { OnApplicationShutdown } from '@nestjs/common';
|
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import { StreamMessages } from '@/server/api/stream/types.js';
|
||||||
|
import type { OnApplicationShutdown } from '@nestjs/common';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WebhookService implements OnApplicationShutdown {
|
export class WebhookService implements OnApplicationShutdown {
|
||||||
|
@ -39,7 +40,7 @@ export class WebhookService implements OnApplicationShutdown {
|
||||||
const obj = JSON.parse(data);
|
const obj = JSON.parse(data);
|
||||||
|
|
||||||
if (obj.channel === 'internal') {
|
if (obj.channel === 'internal') {
|
||||||
const { type, body } = obj.message;
|
const { type, body } = obj.message as StreamMessages['internal']['payload'];
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'webhookCreated':
|
case 'webhookCreated':
|
||||||
if (body.active) {
|
if (body.active) {
|
||||||
|
|
|
@ -34,6 +34,26 @@ type CondFormulaValueCreatedMoreThan = {
|
||||||
sec: number;
|
sec: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type CondFormulaValueFollowersLessThanOrEq = {
|
||||||
|
type: 'followersLessThanOrEq';
|
||||||
|
value: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CondFormulaValueFollowersMoreThanOrEq = {
|
||||||
|
type: 'followersMoreThanOrEq';
|
||||||
|
value: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CondFormulaValueFollowingLessThanOrEq = {
|
||||||
|
type: 'followingLessThanOrEq';
|
||||||
|
value: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CondFormulaValueFollowingMoreThanOrEq = {
|
||||||
|
type: 'followingMoreThanOrEq';
|
||||||
|
value: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type RoleCondFormulaValue =
|
export type RoleCondFormulaValue =
|
||||||
CondFormulaValueAnd |
|
CondFormulaValueAnd |
|
||||||
CondFormulaValueOr |
|
CondFormulaValueOr |
|
||||||
|
@ -41,7 +61,11 @@ export type RoleCondFormulaValue =
|
||||||
CondFormulaValueIsLocal |
|
CondFormulaValueIsLocal |
|
||||||
CondFormulaValueIsRemote |
|
CondFormulaValueIsRemote |
|
||||||
CondFormulaValueCreatedLessThan |
|
CondFormulaValueCreatedLessThan |
|
||||||
CondFormulaValueCreatedMoreThan;
|
CondFormulaValueCreatedMoreThan |
|
||||||
|
CondFormulaValueFollowersLessThanOrEq |
|
||||||
|
CondFormulaValueFollowersMoreThanOrEq |
|
||||||
|
CondFormulaValueFollowingLessThanOrEq |
|
||||||
|
CondFormulaValueFollowingMoreThanOrEq;
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Role {
|
export class Role {
|
||||||
|
|
|
@ -14,7 +14,7 @@ import type { Page } from '@/models/entities/Page.js';
|
||||||
import type { Packed } from '@/misc/schema.js';
|
import type { Packed } from '@/misc/schema.js';
|
||||||
import type { Webhook } from '@/models/entities/Webhook.js';
|
import type { Webhook } from '@/models/entities/Webhook.js';
|
||||||
import type { Meta } from '@/models/entities/Meta.js';
|
import type { Meta } from '@/models/entities/Meta.js';
|
||||||
import { Role, RoleAssignment } from '@/models';
|
import { Following, Role, RoleAssignment } from '@/models';
|
||||||
import type Emitter from 'strict-event-emitter-types';
|
import type Emitter from 'strict-event-emitter-types';
|
||||||
import type { EventEmitter } from 'events';
|
import type { EventEmitter } from 'events';
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@ export interface InternalStreamTypes {
|
||||||
userChangeSuspendedState: Serialized<{ id: User['id']; isSuspended: User['isSuspended']; }>;
|
userChangeSuspendedState: Serialized<{ id: User['id']; isSuspended: User['isSuspended']; }>;
|
||||||
userTokenRegenerated: Serialized<{ id: User['id']; oldToken: User['token']; newToken: User['token']; }>;
|
userTokenRegenerated: Serialized<{ id: User['id']; oldToken: User['token']; newToken: User['token']; }>;
|
||||||
remoteUserUpdated: Serialized<{ id: User['id']; }>;
|
remoteUserUpdated: Serialized<{ id: User['id']; }>;
|
||||||
|
follow: Serialized<{ followerId: User['id']; followeeId: User['id']; }>;
|
||||||
|
unfollow: Serialized<{ followerId: User['id']; followeeId: User['id']; }>;
|
||||||
defaultRoleOverrideUpdated: Serialized<Role['options']>;
|
defaultRoleOverrideUpdated: Serialized<Role['options']>;
|
||||||
roleCreated: Serialized<Role>;
|
roleCreated: Serialized<Role>;
|
||||||
roleDeleted: Serialized<Role>;
|
roleDeleted: Serialized<Role>;
|
||||||
|
|
|
@ -6,6 +6,10 @@
|
||||||
<option value="isRemote">{{ i18n.ts._role._condition.isRemote }}</option>
|
<option value="isRemote">{{ i18n.ts._role._condition.isRemote }}</option>
|
||||||
<option value="createdLessThan">{{ i18n.ts._role._condition.createdLessThan }}</option>
|
<option value="createdLessThan">{{ i18n.ts._role._condition.createdLessThan }}</option>
|
||||||
<option value="createdMoreThan">{{ i18n.ts._role._condition.createdMoreThan }}</option>
|
<option value="createdMoreThan">{{ i18n.ts._role._condition.createdMoreThan }}</option>
|
||||||
|
<option value="followersLessThanOrEq">{{ i18n.ts._role._condition.followersLessThanOrEq }}</option>
|
||||||
|
<option value="followersMoreThanOrEq">{{ i18n.ts._role._condition.followersMoreThanOrEq }}</option>
|
||||||
|
<option value="followingLessThanOrEq">{{ i18n.ts._role._condition.followingLessThanOrEq }}</option>
|
||||||
|
<option value="followingMoreThanOrEq">{{ i18n.ts._role._condition.followingMoreThanOrEq }}</option>
|
||||||
<option value="and">{{ i18n.ts._role._condition.and }}</option>
|
<option value="and">{{ i18n.ts._role._condition.and }}</option>
|
||||||
<option value="or">{{ i18n.ts._role._condition.or }}</option>
|
<option value="or">{{ i18n.ts._role._condition.or }}</option>
|
||||||
<option value="not">{{ i18n.ts._role._condition.not }}</option>
|
<option value="not">{{ i18n.ts._role._condition.not }}</option>
|
||||||
|
@ -37,6 +41,9 @@
|
||||||
<MkInput v-else-if="type === 'createdLessThan' || type === 'createdMoreThan'" v-model="v.sec" type="number">
|
<MkInput v-else-if="type === 'createdLessThan' || type === 'createdMoreThan'" v-model="v.sec" type="number">
|
||||||
<template #suffix>sec</template>
|
<template #suffix>sec</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
|
|
||||||
|
<MkInput v-else-if="['followersLessThanOrEq', 'followersMoreThanOrEq', 'followingLessThanOrEq', 'followingMoreThanOrEq'].includes(type)" v-model="v.value" type="number">
|
||||||
|
</MkInput>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -85,6 +92,10 @@ const type = computed({
|
||||||
if (t === 'not') v.value.value = { id: uuid(), type: 'isRemote' };
|
if (t === 'not') v.value.value = { id: uuid(), type: 'isRemote' };
|
||||||
if (t === 'createdLessThan') v.value.sec = 86400;
|
if (t === 'createdLessThan') v.value.sec = 86400;
|
||||||
if (t === 'createdMoreThan') v.value.sec = 86400;
|
if (t === 'createdMoreThan') v.value.sec = 86400;
|
||||||
|
if (t === 'followersLessThanOrEq') v.value.value = 10;
|
||||||
|
if (t === 'followersMoreThanOrEq') v.value.value = 10;
|
||||||
|
if (t === 'followingLessThanOrEq') v.value.value = 10;
|
||||||
|
if (t === 'followingMoreThanOrEq') v.value.value = 10;
|
||||||
v.value.type = t;
|
v.value.type = t;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue