store vote to scylla
This commit is contained in:
parent
6d49a39273
commit
db573c342c
3 changed files with 110 additions and 25 deletions
|
@ -3,6 +3,7 @@ import type { Note } from "@/models/entities/note.js";
|
|||
import type { IRemoteUser, User } from "@/models/entities/user.js";
|
||||
import type { PollVote } from "@/models/entities/poll-vote.js";
|
||||
import type { Poll } from "@/models/entities/poll.js";
|
||||
import { ScyllaNote, ScyllaPollVote } from "@/db/scylla";
|
||||
|
||||
export default async function renderVote(
|
||||
user: { id: User["id"] },
|
||||
|
@ -27,3 +28,28 @@ export default async function renderVote(
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
export async function renderScyllaPollVote(
|
||||
user: { id: User["id"] },
|
||||
note: ScyllaNote,
|
||||
choice: number,
|
||||
pollOwner: IRemoteUser,
|
||||
): Promise<any> {
|
||||
if (!note.poll) throw new Error("note has no poll");
|
||||
|
||||
return {
|
||||
id: `${config.url}/users/${user.id}#votes/${note.id}/activity`,
|
||||
actor: `${config.url}/users/${user.id}`,
|
||||
type: "Create",
|
||||
to: [pollOwner.uri],
|
||||
published: new Date().toISOString(),
|
||||
object: {
|
||||
id: `${config.url}/users/${user.id}#votes/${note.id}`,
|
||||
type: "Note",
|
||||
attributedTo: `${config.url}/users/${user.id}`,
|
||||
to: [pollOwner.uri],
|
||||
inReplyTo: note.uri,
|
||||
name: note.poll.choices.get(choice),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@ import { publishNoteStream } from "@/services/stream.js";
|
|||
import { createNotification } from "@/services/create-notification.js";
|
||||
import { deliver } from "@/queue/index.js";
|
||||
import { renderActivity } from "@/remote/activitypub/renderer/index.js";
|
||||
import renderVote from "@/remote/activitypub/renderer/vote.js";
|
||||
import renderVote, {
|
||||
renderScyllaPollVote,
|
||||
} from "@/remote/activitypub/renderer/vote.js";
|
||||
import {
|
||||
PollVotes,
|
||||
NoteWatchings,
|
||||
|
@ -16,6 +18,9 @@ import { genId } from "@/misc/gen-id.js";
|
|||
import { getNote } from "../../../common/getters.js";
|
||||
import { ApiError } from "../../../error.js";
|
||||
import define from "../../../define.js";
|
||||
import createVote from "@/services/note/polls/vote.js";
|
||||
import { type ScyllaNote, scyllaClient } from "@/db/scylla.js";
|
||||
import { userByIdCache } from "@/services/user-cache.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["notes"],
|
||||
|
@ -74,8 +79,6 @@ export const paramDef = {
|
|||
} as const;
|
||||
|
||||
export default define(meta, paramDef, async (ps, user) => {
|
||||
const createdAt = new Date();
|
||||
|
||||
// Get votee
|
||||
const note = await getNote(ps.noteId, user).catch((err) => {
|
||||
if (err.id === "9725d0ce-ba28-4dde-95a7-2cbb2c15de24")
|
||||
|
@ -87,6 +90,57 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
throw new ApiError(meta.errors.noPoll);
|
||||
}
|
||||
|
||||
if (scyllaClient) {
|
||||
const scyllaNote = note as ScyllaNote;
|
||||
if (!scyllaNote.poll) {
|
||||
throw new ApiError(meta.errors.noPoll);
|
||||
}
|
||||
if (scyllaNote.poll.expiresAt && scyllaNote.poll.expiresAt < new Date()) {
|
||||
throw new ApiError(meta.errors.alreadyExpired);
|
||||
}
|
||||
await createVote(user, scyllaNote, ps.choice);
|
||||
|
||||
if (scyllaNote.userHost) {
|
||||
const pollOwner = (await userByIdCache.fetch(scyllaNote.userId, () =>
|
||||
Users.findOneByOrFail({
|
||||
id: scyllaNote.userId,
|
||||
}),
|
||||
)) as IRemoteUser;
|
||||
|
||||
deliver(
|
||||
user,
|
||||
renderActivity(
|
||||
await renderScyllaPollVote(user, scyllaNote, ps.choice, pollOwner),
|
||||
),
|
||||
pollOwner.inbox,
|
||||
);
|
||||
}
|
||||
|
||||
publishNoteStream(scyllaNote.id, "pollVoted", {
|
||||
choice: ps.choice,
|
||||
userId: user.id,
|
||||
});
|
||||
createNotification(scyllaNote.userId, "pollVote", {
|
||||
notifierId: user.id,
|
||||
noteId: scyllaNote.id,
|
||||
choice: ps.choice,
|
||||
});
|
||||
NoteWatchings.findBy({
|
||||
noteId: scyllaNote.id,
|
||||
userId: Not(user.id),
|
||||
}).then((watchers) => {
|
||||
for (const watcher of watchers) {
|
||||
createNotification(watcher.userId, "pollVote", {
|
||||
notifierId: user.id,
|
||||
noteId: scyllaNote.id,
|
||||
choice: ps.choice,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check blocking
|
||||
if (note.userId !== user.id) {
|
||||
const block = await Blockings.findOneBy({
|
||||
|
@ -100,6 +154,8 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
|
||||
const poll = await Polls.findOneByOrFail({ noteId: note.id });
|
||||
|
||||
const createdAt = new Date();
|
||||
|
||||
if (poll.expiresAt && poll.expiresAt < createdAt) {
|
||||
throw new ApiError(meta.errors.alreadyExpired);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ import { publishMainStream } from "@/services/stream.js";
|
|||
import { pushNotification } from "@/services/push-notification.js";
|
||||
import {
|
||||
Notifications,
|
||||
Mutings,
|
||||
NoteThreadMutings,
|
||||
UserProfiles,
|
||||
Users,
|
||||
|
@ -13,6 +12,8 @@ import type { User } from "@/models/entities/user.js";
|
|||
import type { Notification } from "@/models/entities/notification.js";
|
||||
import { sendEmailNotification } from "./send-email-notification.js";
|
||||
import { shouldSilenceInstance } from "@/misc/should-block-instance.js";
|
||||
import { UserMutingsCache } from "@/misc/cache.js";
|
||||
import { userByIdCache } from "./user-cache.js";
|
||||
|
||||
export async function createNotification(
|
||||
notifieeId: User["id"],
|
||||
|
@ -47,10 +48,12 @@ export async function createNotification(
|
|||
|
||||
const isMuted = profile?.mutingNotificationTypes.includes(type);
|
||||
|
||||
if (data.note != null) {
|
||||
const threadMute = await NoteThreadMutings.findOneBy({
|
||||
userId: notifieeId,
|
||||
threadId: data.note.threadId || data.note.id,
|
||||
if (data.note) {
|
||||
const threadMute = await NoteThreadMutings.exist({
|
||||
where: {
|
||||
userId: notifieeId,
|
||||
threadId: data.note.threadId || data.note.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (threadMute) {
|
||||
|
@ -64,7 +67,7 @@ export async function createNotification(
|
|||
createdAt: new Date(),
|
||||
notifieeId: notifieeId,
|
||||
type: type,
|
||||
// 相手がこの通知をミュートしているようなら、既読を予めつけておく
|
||||
// Make this notification read if muted
|
||||
isRead: isMuted,
|
||||
...data,
|
||||
} as Partial<Notification>).then((x) =>
|
||||
|
@ -76,38 +79,38 @@ export async function createNotification(
|
|||
// Publish notification event
|
||||
publishMainStream(notifieeId, "notification", packed);
|
||||
|
||||
// 2秒経っても(今回作成した)通知が既読にならなかったら「未読の通知がありますよ」イベントを発行する
|
||||
// Fire "new notification" event if not yet read after two seconds
|
||||
setTimeout(async () => {
|
||||
const fresh = await Notifications.findOneBy({ id: notification.id });
|
||||
if (fresh == null) return; // 既に削除されているかもしれない
|
||||
if (!fresh) return;
|
||||
// We execute this before, because the server side "read" check doesnt work well with push notifications, the app and service worker will decide themself
|
||||
// when it is best to show push notifications
|
||||
pushNotification(notifieeId, "notification", packed);
|
||||
if (fresh.isRead) return;
|
||||
|
||||
//#region ただしミュートしているユーザーからの通知なら無視
|
||||
const mutings = await Mutings.findBy({
|
||||
muterId: notifieeId,
|
||||
});
|
||||
if (
|
||||
data.notifierId &&
|
||||
mutings.map((m) => m.muteeId).includes(data.notifierId)
|
||||
) {
|
||||
return;
|
||||
// Ignore if the issuer is muted.
|
||||
if (data.notifierId) {
|
||||
const cache = await UserMutingsCache.init(notifieeId);
|
||||
if (await cache.isMuting(data.notifierId)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
publishMainStream(notifieeId, "unreadNotification", packed);
|
||||
|
||||
if (type === "follow")
|
||||
if (type === "follow" && data.notifierId)
|
||||
sendEmailNotification.follow(
|
||||
notifieeId,
|
||||
await Users.findOneByOrFail({ id: data.notifierId! }),
|
||||
await userByIdCache.fetch(data.notifierId, () =>
|
||||
Users.findOneByOrFail({ id: data.notifierId }),
|
||||
),
|
||||
);
|
||||
if (type === "receiveFollowRequest")
|
||||
if (type === "receiveFollowRequest" && data.notifierId)
|
||||
sendEmailNotification.receiveFollowRequest(
|
||||
notifieeId,
|
||||
await Users.findOneByOrFail({ id: data.notifierId! }),
|
||||
await userByIdCache.fetch(data.notifierId, () =>
|
||||
Users.findOneByOrFail({ id: data.notifierId }),
|
||||
),
|
||||
);
|
||||
}, 2000);
|
||||
|
||||
|
|
Loading…
Reference in a new issue