feat: Webhook (#8457)
* feat: introduce webhook * wip * wip * wip * Update CHANGELOG.md
This commit is contained in:
		
							parent
							
								
									99e6ef5996
								
							
						
					
					
						commit
						8e5f2690f2
					
				
					 28 changed files with 815 additions and 10 deletions
				
			
		| 
						 | 
				
			
			@ -10,6 +10,8 @@ import { Blockings, Users, FollowRequests, Followings, UserListJoinings, UserLis
 | 
			
		|||
import { perUserFollowingChart } from '@/services/chart/index.js';
 | 
			
		||||
import { genId } from '@/misc/gen-id.js';
 | 
			
		||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
 | 
			
		||||
import { getActiveWebhooks } from '@/misc/webhook-cache.js';
 | 
			
		||||
import { webhookDeliver } from '@/queue/index.js';
 | 
			
		||||
 | 
			
		||||
export default async function(blocker: User, blockee: User) {
 | 
			
		||||
	await Promise.all([
 | 
			
		||||
| 
						 | 
				
			
			@ -57,9 +59,17 @@ async function cancelRequest(follower: User, followee: User) {
 | 
			
		|||
	if (Users.isLocalUser(follower)) {
 | 
			
		||||
		Users.pack(followee, follower, {
 | 
			
		||||
			detail: true,
 | 
			
		||||
		}).then(packed => {
 | 
			
		||||
		}).then(async packed => {
 | 
			
		||||
			publishUserEvent(follower.id, 'unfollow', packed);
 | 
			
		||||
			publishMainStream(follower.id, 'unfollow', packed);
 | 
			
		||||
 | 
			
		||||
			const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow'));
 | 
			
		||||
			for (const webhook of webhooks) {
 | 
			
		||||
				webhookDeliver(webhook, {
 | 
			
		||||
					type: 'unfollow',
 | 
			
		||||
					user: packed,
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -102,9 +112,17 @@ async function unFollow(follower: User, followee: User) {
 | 
			
		|||
	if (Users.isLocalUser(follower)) {
 | 
			
		||||
		Users.pack(followee, follower, {
 | 
			
		||||
			detail: true,
 | 
			
		||||
		}).then(packed => {
 | 
			
		||||
		}).then(async packed => {
 | 
			
		||||
			publishUserEvent(follower.id, 'unfollow', packed);
 | 
			
		||||
			publishMainStream(follower.id, 'unfollow', packed);
 | 
			
		||||
 | 
			
		||||
			const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow'));
 | 
			
		||||
			for (const webhook of webhooks) {
 | 
			
		||||
				webhookDeliver(webhook, {
 | 
			
		||||
					type: 'unfollow',
 | 
			
		||||
					user: packed,
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,8 @@ import { genId } from '@/misc/gen-id.js';
 | 
			
		|||
import { createNotification } from '../create-notification.js';
 | 
			
		||||
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
 | 
			
		||||
import { Packed } from '@/misc/schema.js';
 | 
			
		||||
import { getActiveWebhooks } from '@/misc/webhook-cache.js';
 | 
			
		||||
import { webhookDeliver } from '@/queue/index.js';
 | 
			
		||||
 | 
			
		||||
const logger = new Logger('following/create');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -89,15 +91,33 @@ export async function insertFollowingDoc(followee: { id: User['id']; host: User[
 | 
			
		|||
	if (Users.isLocalUser(follower)) {
 | 
			
		||||
		Users.pack(followee.id, follower, {
 | 
			
		||||
			detail: true,
 | 
			
		||||
		}).then(packed => {
 | 
			
		||||
		}).then(async packed => {
 | 
			
		||||
			publishUserEvent(follower.id, 'follow', packed as Packed<"UserDetailedNotMe">);
 | 
			
		||||
			publishMainStream(follower.id, 'follow', packed as Packed<"UserDetailedNotMe">);
 | 
			
		||||
 | 
			
		||||
			const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('follow'));
 | 
			
		||||
			for (const webhook of webhooks) {
 | 
			
		||||
				webhookDeliver(webhook, {
 | 
			
		||||
					type: 'follow',
 | 
			
		||||
					user: packed,
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Publish followed event
 | 
			
		||||
	if (Users.isLocalUser(followee)) {
 | 
			
		||||
		Users.pack(follower.id, followee).then(packed => publishMainStream(followee.id, 'followed', packed));
 | 
			
		||||
		Users.pack(follower.id, followee).then(async packed => {
 | 
			
		||||
			publishMainStream(followee.id, 'followed', packed)
 | 
			
		||||
 | 
			
		||||
			const webhooks = (await getActiveWebhooks()).filter(x => x.userId === followee.id && x.on.includes('followed'));
 | 
			
		||||
			for (const webhook of webhooks) {
 | 
			
		||||
				webhookDeliver(webhook, {
 | 
			
		||||
					type: 'followed',
 | 
			
		||||
					user: packed,
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		// 通知を作成
 | 
			
		||||
		createNotification(followee.id, 'follow', {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,12 +3,13 @@ import { renderActivity } from '@/remote/activitypub/renderer/index.js';
 | 
			
		|||
import renderFollow from '@/remote/activitypub/renderer/follow.js';
 | 
			
		||||
import renderUndo from '@/remote/activitypub/renderer/undo.js';
 | 
			
		||||
import renderReject from '@/remote/activitypub/renderer/reject.js';
 | 
			
		||||
import { deliver } from '@/queue/index.js';
 | 
			
		||||
import { deliver, webhookDeliver } from '@/queue/index.js';
 | 
			
		||||
import Logger from '../logger.js';
 | 
			
		||||
import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js';
 | 
			
		||||
import { User } from '@/models/entities/user.js';
 | 
			
		||||
import { Followings, Users, Instances } from '@/models/index.js';
 | 
			
		||||
import { instanceChart, perUserFollowingChart } from '@/services/chart/index.js';
 | 
			
		||||
import { getActiveWebhooks } from '@/misc/webhook-cache.js';
 | 
			
		||||
 | 
			
		||||
const logger = new Logger('following/delete');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -31,9 +32,17 @@ export default async function(follower: { id: User['id']; host: User['host']; ur
 | 
			
		|||
	if (!silent && Users.isLocalUser(follower)) {
 | 
			
		||||
		Users.pack(followee.id, follower, {
 | 
			
		||||
			detail: true,
 | 
			
		||||
		}).then(packed => {
 | 
			
		||||
		}).then(async packed => {
 | 
			
		||||
			publishUserEvent(follower.id, 'unfollow', packed);
 | 
			
		||||
			publishMainStream(follower.id, 'unfollow', packed);
 | 
			
		||||
 | 
			
		||||
			const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow'));
 | 
			
		||||
			for (const webhook of webhooks) {
 | 
			
		||||
				webhookDeliver(webhook, {
 | 
			
		||||
					type: 'unfollow',
 | 
			
		||||
					user: packed,
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,12 @@
 | 
			
		|||
import { renderActivity } from '@/remote/activitypub/renderer/index.js';
 | 
			
		||||
import renderFollow from '@/remote/activitypub/renderer/follow.js';
 | 
			
		||||
import renderReject from '@/remote/activitypub/renderer/reject.js';
 | 
			
		||||
import { deliver } from '@/queue/index.js';
 | 
			
		||||
import { deliver, webhookDeliver } from '@/queue/index.js';
 | 
			
		||||
import { publishMainStream, publishUserEvent } from '@/services/stream.js';
 | 
			
		||||
import { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js';
 | 
			
		||||
import { Users, FollowRequests, Followings } from '@/models/index.js';
 | 
			
		||||
import { decrementFollowing } from './delete.js';
 | 
			
		||||
import { getActiveWebhooks } from '@/misc/webhook-cache.js';
 | 
			
		||||
 | 
			
		||||
type Local = ILocalUser | {
 | 
			
		||||
	id: ILocalUser['id'];
 | 
			
		||||
| 
						 | 
				
			
			@ -111,4 +112,12 @@ async function publishUnfollow(followee: Both, follower: Local) {
 | 
			
		|||
 | 
			
		||||
	publishUserEvent(follower.id, 'unfollow', packedFollowee);
 | 
			
		||||
	publishMainStream(follower.id, 'unfollow', packedFollowee);
 | 
			
		||||
 | 
			
		||||
	const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow'));
 | 
			
		||||
	for (const webhook of webhooks) {
 | 
			
		||||
		webhookDeliver(webhook, {
 | 
			
		||||
			type: 'unfollow',
 | 
			
		||||
			user: packedFollowee,
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,9 +35,11 @@ import { Channel } from '@/models/entities/channel.js';
 | 
			
		|||
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
 | 
			
		||||
import { getAntennas } from '@/misc/antenna-cache.js';
 | 
			
		||||
import { endedPollNotificationQueue } from '@/queue/queues.js';
 | 
			
		||||
import { webhookDeliver } from '@/queue/index.js';
 | 
			
		||||
import { Cache } from '@/misc/cache.js';
 | 
			
		||||
import { UserProfile } from '@/models/entities/user-profile.js';
 | 
			
		||||
import { db } from '@/db/postgre.js';
 | 
			
		||||
import { getActiveWebhooks } from '@/misc/webhook-cache.js';
 | 
			
		||||
 | 
			
		||||
const mutedWordsCache = new Cache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -345,6 +347,16 @@ export default async (user: { id: User['id']; username: User['username']; host:
 | 
			
		|||
 | 
			
		||||
		publishNotesStream(noteObj);
 | 
			
		||||
 | 
			
		||||
		getActiveWebhooks().then(webhooks => {
 | 
			
		||||
			webhooks = webhooks.filter(x => x.userId === user.id && x.on.includes('note'));
 | 
			
		||||
			for (const webhook of webhooks) {
 | 
			
		||||
				webhookDeliver(webhook, {
 | 
			
		||||
					type: 'note',
 | 
			
		||||
					note: noteObj,
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		const nm = new NotificationManager(user, note);
 | 
			
		||||
		const nmRelatedPromises = [];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -365,6 +377,14 @@ export default async (user: { id: User['id']; username: User['username']; host:
 | 
			
		|||
				if (!threadMuted) {
 | 
			
		||||
					nm.push(data.reply.userId, 'reply');
 | 
			
		||||
					publishMainStream(data.reply.userId, 'reply', noteObj);
 | 
			
		||||
 | 
			
		||||
					const webhooks = (await getActiveWebhooks()).filter(x => x.userId === data.reply!.userId && x.on.includes('reply'));
 | 
			
		||||
					for (const webhook of webhooks) {
 | 
			
		||||
						webhookDeliver(webhook, {
 | 
			
		||||
							type: 'reply',
 | 
			
		||||
							note: noteObj,
 | 
			
		||||
						});
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -384,6 +404,14 @@ export default async (user: { id: User['id']; username: User['username']; host:
 | 
			
		|||
			// Publish event
 | 
			
		||||
			if ((user.id !== data.renote.userId) && data.renote.userHost === null) {
 | 
			
		||||
				publishMainStream(data.renote.userId, 'renote', noteObj);
 | 
			
		||||
 | 
			
		||||
				const webhooks = (await getActiveWebhooks()).filter(x => x.userId === data.renote!.userId && x.on.includes('renote'));
 | 
			
		||||
				for (const webhook of webhooks) {
 | 
			
		||||
					webhookDeliver(webhook, {
 | 
			
		||||
						type: 'renote',
 | 
			
		||||
						note: noteObj,
 | 
			
		||||
					});
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -620,6 +648,14 @@ async function createMentionedEvents(mentionedUsers: MinimumUser[], note: Note,
 | 
			
		|||
 | 
			
		||||
		publishMainStream(u.id, 'mention', detailPackedNote);
 | 
			
		||||
 | 
			
		||||
		const webhooks = (await getActiveWebhooks()).filter(x => x.userId === u.id && x.on.includes('mention'));
 | 
			
		||||
		for (const webhook of webhooks) {
 | 
			
		||||
			webhookDeliver(webhook, {
 | 
			
		||||
				type: 'mention',
 | 
			
		||||
				note: detailPackedNote,
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Create notification
 | 
			
		||||
		nm.push(u.id, 'mention');
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue