diff --git a/src/server/api/endpoints/users/report-abuse.ts b/src/server/api/endpoints/users/report-abuse.ts
index b520b29e23656828cc375bd57abf1314acebded8..19beee433044966998e7c8f787000edbb3d3463f 100644
--- a/src/server/api/endpoints/users/report-abuse.ts
+++ b/src/server/api/endpoints/users/report-abuse.ts
@@ -2,6 +2,7 @@ import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
 import define from '../../define';
 import User from '../../../../models/user';
 import AbuseUserReport from '../../../../models/abuse-user-report';
+import { publishAdminStream } from '../../../../stream';
 
 export const meta = {
 	desc: {
@@ -47,12 +48,31 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
 		return rej('cannot report admin');
 	}
 
-	await AbuseUserReport.insert({
+	const report = await AbuseUserReport.insert({
 		createdAt: new Date(),
 		userId: user._id,
 		reporterId: me._id,
 		comment: ps.comment
 	});
 
+	// Publish event to moderators
+	setTimeout(async () => {
+		const moderators = await User.find({
+			$or: [{
+				isAdmin: true
+			}, {
+				isModerator: true
+			}]
+		});
+		for (const moderator of moderators) {
+			publishAdminStream(moderator._id, 'newAbuseUserReport', {
+				id: report._id,
+				userId: report.userId,
+				reporterId: report.reporterId,
+				comment: report.comment
+			});
+		}
+	}, 1);
+
 	res();
 }));
diff --git a/src/server/api/stream/channels/admin.ts b/src/server/api/stream/channels/admin.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6bcd1a7e0baf3db95d6361af28a51763c7a9fdce
--- /dev/null
+++ b/src/server/api/stream/channels/admin.ts
@@ -0,0 +1,16 @@
+import autobind from 'autobind-decorator';
+import Channel from '../channel';
+
+export default class extends Channel {
+	public readonly chName = 'admin';
+	public static shouldShare = true;
+	public static requireCredential = true;
+
+	@autobind
+	public async init(params: any) {
+		// Subscribe admin stream
+		this.subscriber.on(`adminStream:${this.user._id}`, data => {
+			this.send(data);
+		});
+	}
+}
diff --git a/src/server/api/stream/channels/index.ts b/src/server/api/stream/channels/index.ts
index 7248579abd33c0f3469e01cd3227519ae4b6f682..02f71b5851e07e45aacda79a38697a6fe8dda4ee 100644
--- a/src/server/api/stream/channels/index.ts
+++ b/src/server/api/stream/channels/index.ts
@@ -11,6 +11,7 @@ import messagingIndex from './messaging-index';
 import drive from './drive';
 import hashtag from './hashtag';
 import apLog from './ap-log';
+import admin from './admin';
 import gamesReversi from './games/reversi';
 import gamesReversiGame from './games/reversi-game';
 
@@ -28,6 +29,7 @@ export default {
 	drive,
 	hashtag,
 	apLog,
+	admin,
 	gamesReversi,
 	gamesReversiGame
 };
diff --git a/src/stream.ts b/src/stream.ts
index 596cb98e72c02505507330289525a0f4aa308fab..098d49ecd18c9131e8ab43494e1a7836a9dda84e 100644
--- a/src/stream.ts
+++ b/src/stream.ts
@@ -87,6 +87,10 @@ class Publisher {
 	public publishApLogStream = (log: any): void => {
 		this.publish('apLog', null, log);
 	}
+
+	public publishAdminStream = (userId: ID, type: string, value?: any): void => {
+		this.publish(`adminStream:${userId}`, type, typeof value === 'undefined' ? null : value);
+	}
 }
 
 const publisher = new Publisher();
@@ -107,3 +111,4 @@ export const publishHybridTimelineStream = publisher.publishHybridTimelineStream
 export const publishGlobalTimelineStream = publisher.publishGlobalTimelineStream;
 export const publishHashtagStream = publisher.publishHashtagStream;
 export const publishApLogStream = publisher.publishApLogStream;
+export const publishAdminStream = publisher.publishAdminStream;