113 lines
4.2 KiB
TypeScript
113 lines
4.2 KiB
TypeScript
import fs from 'fs/promises';
|
|
import path from 'path';
|
|
|
|
import notifier from 'node-notifier';
|
|
import ReconnectingWebSocket from 'reconnecting-websocket';
|
|
import { WebSocket } from 'ws';
|
|
|
|
const INSTANCE = process.env.FK_INSTANCE;
|
|
const TOKEN = process.env.FK_TOKEN;
|
|
|
|
const image_cache = path.resolve('image_cache');
|
|
await fs.mkdir(image_cache, { recursive: true });
|
|
|
|
const iconPath = path.join(image_cache, 'instance.jpg');
|
|
{
|
|
const resp = await fetch(`https://${INSTANCE}/api/meta`, { method: 'POST', headers: { authorization: `bearer ${TOKEN}` } });
|
|
const meta = await resp.json();
|
|
const iconResp = await fetch(meta.iconUrl);
|
|
const iconBuf = await iconResp.blob();
|
|
await fs.writeFile(iconPath, iconBuf.stream());
|
|
}
|
|
|
|
const socket = new ReconnectingWebSocket(`wss://${INSTANCE}/streaming?i=${TOKEN}`, undefined, { WebSocket });
|
|
socket.addEventListener('open', ({ type }) => console.log({ type }));
|
|
socket.addEventListener('close', ({ code, reason, wasClean, type }) => console.log({ code, reason, wasClean, type }));
|
|
socket.addEventListener('error', ({ error, message, type }) => console.log({ error, message, type }));
|
|
|
|
socket.addEventListener('open', (ev) => {
|
|
socket.send(JSON.stringify({ type: "connect", body: { channel: "main", id: "1" } }))
|
|
});
|
|
socket.addEventListener('message', ({ data }) => {
|
|
const payload = JSON.parse(data);
|
|
if (payload.type === 'channel' && payload.body.type === 'notification') {
|
|
const notification = payload.body.body;
|
|
const user = notification.user;
|
|
const note = notification.note;
|
|
const userName = user.host !== null ? `${user.username}@${user.host}` : user.username;
|
|
let title: string;
|
|
let body = 'note' in notification ? (note.cw != null ? note.cw : (note.text || '')) : undefined;
|
|
switch (notification.type) {
|
|
case 'pollEnded':
|
|
title = `${userName}'s poll has ended`;
|
|
break;
|
|
case 'follow':
|
|
title = `follow from ${userName}`;
|
|
break;
|
|
case 'followRequestAccepted':
|
|
title = `accept from ${userName}`;
|
|
break;
|
|
case 'mention':
|
|
title = `mention from ${userName}`;
|
|
break;
|
|
case 'pollVote':
|
|
title = `vote from ${userName}`;
|
|
break;
|
|
case 'quote':
|
|
title = `quote from ${userName}`;
|
|
break;
|
|
case 'reaction':
|
|
{
|
|
let reaction: string = notification.reaction;
|
|
reaction = reaction.split('@')[0];
|
|
if (reaction.startsWith(':'))
|
|
reaction = reaction.slice(1);
|
|
title = `${reaction} from ${userName}`;
|
|
}
|
|
break;
|
|
case 'receiveFollowRequest':
|
|
title = `follow req from ${userName}`;
|
|
break;
|
|
case 'renote':
|
|
title = `renote from ${userName}`;
|
|
break;
|
|
case 'reply':
|
|
title = `reply from ${userName}`;
|
|
break;
|
|
case 'groupInvited':
|
|
title = `group invite from ${userName}`;
|
|
break;
|
|
default:
|
|
console.log({ type: notification.type });
|
|
return;
|
|
}
|
|
|
|
const avatarUrl = new URL(user.avatarUrl);
|
|
const avatarServerPath = avatarUrl.pathname.split('/');
|
|
const avatarStem = avatarServerPath[avatarServerPath.length - 1];
|
|
const avatarFilename = `${avatarStem}.webp`;
|
|
const avatarPath = path.join(image_cache, avatarFilename);
|
|
(async () => {
|
|
try {
|
|
await fs.access(avatarPath);
|
|
} catch {
|
|
const resp = await fetch(avatarUrl);
|
|
if (!resp.ok) {
|
|
throw resp;
|
|
}
|
|
const buf = await resp.blob();
|
|
await fs.writeFile(avatarPath, buf.stream());
|
|
}
|
|
|
|
// console.log(user, userName, title, body);
|
|
notifier.notify({
|
|
title,
|
|
message: body,
|
|
icon: iconPath,
|
|
contentImage: avatarPath,
|
|
});
|
|
})()
|
|
}
|
|
});
|
|
|
|
await new Promise(() => { }); |