Skip to content

Commit d5ed05b

Browse files
committed
more changes
1 parent 1238937 commit d5ed05b

File tree

8 files changed

+397
-9
lines changed

8 files changed

+397
-9
lines changed

bun.lock

Lines changed: 150 additions & 1 deletion
Large diffs are not rendered by default.

new.dbml

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
-- the database schema for feedr 2.0, just here temporarily during development
2+
3+
Table discord {
4+
guild_id string [pk, not null, note: "Discord guild ID"]
5+
is_dm string [default: false, not null]
6+
allowed_public_sharing boolean [default: false, not null, note: "False by default due to privacy stuff"]
7+
}
8+
9+
Table guild_bluesky_subscriptions {
10+
id int [pk, not null, increment]
11+
guild_id string [not null, ref: > discord.guild_id]
12+
bluesky_user_id string [not null, ref: > bluesky.bluesky_user_id]
13+
notification_channel_id string [not null]
14+
notification_role_id string
15+
is_dm boolean [not null, default: false]
16+
check_for_replies boolean [not null, default: false]
17+
}
18+
19+
Table guild_youtube_subscriptions {
20+
id int [pk, not null, increment]
21+
guild_id string [not null, ref: > discord.guild_id]
22+
youtube_channel_id string [not null, ref: > youtube.youtube_channel_id]
23+
notification_channel_id string [not null]
24+
notification_role_id string
25+
is_dm boolean [not null, default: false]
26+
track_videos boolean [default: false, not null]
27+
track_shorts boolean [default: false, not null]
28+
track_streams boolean [default: false, not null]
29+
}
30+
31+
Table guild_twitch_subscriptions {
32+
id int [pk, not null, increment]
33+
guild_id string [not null, ref: > discord.guild_id]
34+
twitch_channel_id string [not null, ref: > twitch.twitch_channel_id]
35+
notification_channel_id string [not null]
36+
notification_role_id string
37+
is_dm boolean [not null, default: false]
38+
}
39+
40+
Table bluesky {
41+
bluesky_user_id string [pk, not null]
42+
latest_post_id string
43+
latest_reply_id string
44+
}
45+
46+
Table youtube {
47+
youtube_channel_id string [pk, not null]
48+
latest_video_id string
49+
latest_video_id_updated datetime
50+
latest_short_id string
51+
latest_short_id_updated datetime
52+
latest_stream_id string
53+
latest_stream_id_updated datetime
54+
youtube_channel_is_live boolean
55+
}
56+
57+
Table twitch {
58+
twitch_channel_id string [pk, not null]
59+
twitch_channel_is_live boolean [not null]
60+
}
61+
62+
Table bot_info {
63+
guilds_total int [not null, default: 0]
64+
channels_tracked int [not null, default: 0]
65+
total_members int [not null, default: 0]
66+
updated_at datetime [not null, default: `now()`]
67+
extended_info_updated_at datetime [not null, default: `now()`]
68+
}
69+
70+
Table bot_info_notifications {
71+
date date [not null]
72+
total_youtube int [not null, default: 0]
73+
total_twitch int [not null, default: 0]
74+
}
75+
76+
Table bot_info_top_channels {
77+
youtube_channel_id string [not null, pk, ref: > youtube.youtube_channel_id]
78+
guilds_tracking int [not null, default: 0]
79+
}
80+
81+
Table bot_info_top_guilds {
82+
guild_id int [pk, not null, ref: > discord.guild_id]
83+
members int [not null, default: 0]
84+
}
85+
86+
Table audit_logs {
87+
id int [pk, not null, increment]
88+
event_type string [not null, note: "e.g., subscribe_youtube, unsubscribe_twitch"]
89+
guild_id string [not null, ref: > discord.guild_id]
90+
related_id string [not null, note: "Related YouTube or Twitch channel ID"]
91+
note string
92+
occurred_at datetime [default: `now()`]
93+
}

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@
55
"version": "1.3.0",
66
"devDependencies": {
77
"@types/bun": "1.2.10",
8+
"@types/cors": "^2.8.17",
9+
"@types/express": "^5.0.1",
810
"@types/pg": "^8.11.14",
911
"@typescript-eslint/eslint-plugin": "8.11.0",
1012
"@typescript-eslint/parser": "8.11.0",
13+
"concurrently": "^9.1.2",
1114
"eslint": "^8.57.0",
1215
"eslint-config-prettier": "9.1.0",
1316
"eslint-plugin-import": "^2.26.0",
@@ -20,13 +23,16 @@
2023
},
2124
"scripts": {
2225
"dev": "bun --watch . --dev",
26+
"test:site": "bun --watch src/web/web.ts",
2327
"test:jetstream": "bun --watch src/utils/bluesky/jetstream.ts",
2428
"lint": "eslint . --ext .ts -c .eslintrc.json",
2529
"lint:fix": "eslint . --ext .ts -c .eslintrc.json --fix"
2630
},
2731
"dependencies": {
32+
"cors": "^2.8.5",
2833
"cron": "^3.2.1",
2934
"discord.js": "^14.19.1",
35+
"express": "^5.1.0",
3036
"pg": "^8.15.6"
3137
}
3238
}

src/utils/database.ts

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { dbDiscordTable, dbYouTube } from "../types/database";
22

33
import { Pool } from "pg";
44

5-
import { dbCredentials } from "../config";
5+
import { dbCredentials, env } from "../config";
66

77
// import path from "path";
88
// import { Database } from "bun:sqlite";
@@ -30,7 +30,17 @@ export const pool: Pool = new Pool({
3030
export async function initTables(): Promise<boolean> {
3131
const createDiscordTable = `
3232
CREATE TABLE IF NOT EXISTS discord (
33-
guild_id TEXT PRIMARY KEY
33+
guild_id TEXT PRIMARY KEY,
34+
is_dm BOOLEAN NOT NULL DEFAULT FALSE,
35+
allowed_public_sharing BOOLEAN NOT NULL DEFAULT FALSE
36+
);
37+
`;
38+
39+
const createBlueskyTable = `
40+
CREATE TABLE IF NOT EXISTS bluesky (
41+
bluesky_user_id TEXT PRIMARY KEY,
42+
latest_post_id TEXT,
43+
latest_reply_id TEXT
3444
);
3545
`;
3646

@@ -54,14 +64,29 @@ export async function initTables(): Promise<boolean> {
5464
);
5565
`;
5666

67+
const createGuildBlueskySubscriptionsTable = `
68+
CREATE TABLE IF NOT EXISTS guild_bluesky_subscriptions (
69+
id SERIAL PRIMARY KEY,
70+
guild_id TEXT NOT NULL REFERENCES discord(guild_id),
71+
bluesky_user_id TEXT NOT NULL REFERENCES bluesky(bluesky_user_id),
72+
notification_channel_id TEXT NOT NULL,
73+
notification_role_id TEXT,
74+
is_dm BOOLEAN NOT NULL DEFAULT FALSE,
75+
check_for_replies BOOLEAN NOT NULL DEFAULT FALSE
76+
);
77+
`;
78+
5779
const createGuildYouTubeSubscriptionsTable = `
5880
CREATE TABLE IF NOT EXISTS guild_youtube_subscriptions (
5981
id SERIAL PRIMARY KEY,
6082
guild_id TEXT NOT NULL REFERENCES discord(guild_id),
6183
youtube_channel_id TEXT NOT NULL REFERENCES youtube(youtube_channel_id),
6284
notification_channel_id TEXT NOT NULL,
6385
notification_role_id TEXT,
64-
is_dm BOOLEAN DEFAULT FALSE
86+
is_dm BOOLEAN NOT NULL DEFAULT FALSE,
87+
track_videos BOOLEAN NOT NULL DEFAULT FALSE,
88+
track_shorts BOOLEAN NOT NULL DEFAULT FALSE,
89+
track_streams BOOLEAN NOT NULL DEFAULT FALSE
6590
);
6691
`;
6792

@@ -72,15 +97,39 @@ export async function initTables(): Promise<boolean> {
7297
twitch_channel_id TEXT NOT NULL REFERENCES twitch(twitch_channel_id),
7398
notification_channel_id TEXT NOT NULL,
7499
notification_role_id TEXT,
75-
is_dm BOOLEAN DEFAULT FALSE
100+
is_dm BOOLEAN NOT NULL DEFAULT FALSE
76101
);
77102
`;
78103

79104
const createBotInfoTable = `
80105
CREATE TABLE IF NOT EXISTS bot_info (
81-
guilds_total INTEGER NOT NULL,
82-
channels_tracked INTEGER NOT NULL,
83-
updated_at TIMESTAMP DEFAULT now()
106+
guilds_total INTEGER NOT NULL DEFAULT 0,
107+
channels_tracked INTEGER NOT NULL DEFAULT 0,
108+
total_members INTEGER NOT NULL DEFAULT 0,
109+
updated_at TIMESTAMP NOT NULL DEFAULT now(),
110+
extended_info_updated_at TIMESTAMP NOT NULL DEFAULT now()
111+
);
112+
`;
113+
114+
const createBotInfoNotificationsTable = `
115+
CREATE TABLE IF NOT EXISTS bot_info_notifications (
116+
date DATE NOT NULL,
117+
total_youtube INTEGER NOT NULL DEFAULT 0,
118+
total_twitch INTEGER NOT NULL DEFAULT 0
119+
);
120+
`;
121+
122+
const createBotInfoTopChannelsTable = `
123+
CREATE TABLE IF NOT EXISTS bot_info_top_channels (
124+
youtube_channel_id TEXT PRIMARY KEY REFERENCES youtube(youtube_channel_id),
125+
guilds_tracking INTEGER NOT NULL DEFAULT 0
126+
);
127+
`;
128+
129+
const createBotInfoTopGuildsTable = `
130+
CREATE TABLE IF NOT EXISTS bot_info_top_guilds (
131+
guild_id TEXT PRIMARY KEY REFERENCES discord(guild_id),
132+
members INTEGER NOT NULL DEFAULT 0
84133
);
85134
`;
86135

@@ -99,12 +148,18 @@ export async function initTables(): Promise<boolean> {
99148
await pool.query(createDiscordTable);
100149
console.log("Discord table created");
101150

151+
await pool.query(createBlueskyTable);
152+
console.log("Bluesky table created");
153+
102154
await pool.query(createYouTubeTable);
103155
console.log("YouTube table created");
104156

105157
await pool.query(createTwitchTable);
106158
console.log("Twitch table created");
107159

160+
await pool.query(createGuildBlueskySubscriptionsTable);
161+
console.log("Guild Bluesky Subscriptions table created");
162+
108163
await pool.query(createGuildYouTubeSubscriptionsTable);
109164
console.log("Guild YouTube Subscriptions table created");
110165

@@ -114,6 +169,15 @@ export async function initTables(): Promise<boolean> {
114169
await pool.query(createBotInfoTable);
115170
console.log("Bot Info table created");
116171

172+
await pool.query(createBotInfoNotificationsTable);
173+
console.log("Bot Info Notifications table created");
174+
175+
await pool.query(createBotInfoTopChannelsTable);
176+
console.log("Bot Info Top Channels table created");
177+
178+
await pool.query(createBotInfoTopGuildsTable);
179+
console.log("Bot Info Top Guilds table created");
180+
117181
await pool.query(createAuditLogsTable);
118182
console.log("Audit Logs table created");
119183

@@ -146,7 +210,7 @@ export async function checkIfChannelIsAlreadyTracked(channelId: string) {
146210
export async function addNewChannelToTrack(channelId: string) {
147211
console.log("Adding channel to track:", channelId);
148212
const res = await fetch(
149-
`https://youtube.googleapis.com/youtube/v3/playlists?part=snippet&id=${channelId.replace("UC", "UU")}&key=${process.env.YOUTUBE_API_KEY}`,
213+
`https://youtube.googleapis.com/youtube/v3/playlists?part=snippet&id=${channelId.replace("UC", "UU")}&key=${env.youtubeApiKey}`,
150214
);
151215

152216
if (!res.ok) {

src/web/public/index.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Document</title>
7+
<link rel="stylesheet" href="main.css">
8+
</head>
9+
<body>
10+
<div class="feedr">
11+
<h1>Feedr</h1>
12+
</div>
13+
<script src="main.js"></script>
14+
</body>
15+
</html>

src/web/public/main.css

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
body {
2+
margin: 0;
3+
background: #0a0a0a;
4+
font-family:
5+
system-ui,
6+
-apple-system,
7+
BlinkMacSystemFont,
8+
"Segoe UI",
9+
Roboto,
10+
Oxygen,
11+
Ubuntu,
12+
Cantarell,
13+
"Open Sans",
14+
"Helvetica Neue",
15+
sans-serif;
16+
}
17+
18+
.feedr {
19+
background: linear-gradient(135deg, #a970ff 25%, #f83d5c 75%);
20+
background-clip: text;
21+
-webkit-background-clip: text;
22+
-webkit-text-fill-color: transparent;
23+
color: transparent;
24+
margin: auto;
25+
text-align: center;
26+
width: fit-content;
27+
font-size: xx-large;
28+
}

src/web/public/main.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
async function fetchBotInfo() {
2+
const response = await fetch("/botinfo");
3+
4+
if (!response.ok) {
5+
throw new Error("Failed to fetch bot info");
6+
}
7+
8+
return response.json();
9+
}
10+
11+
fetchBotInfo();

src/web/web.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import path from "path";
2+
3+
import express from "express";
4+
import cors from "cors";
5+
6+
const app = express();
7+
const port = 3000;
8+
9+
app.use(cors());
10+
app.use(express.static(path.join(__dirname, "public")));
11+
12+
app.get("/", (req, res) => {
13+
res.sendFile(path.join(__dirname, "public", "index.html"));
14+
});
15+
16+
app.get("/botinfo", (req, res) => {
17+
res.status(500).json("Not implemented yet");
18+
});
19+
20+
app.listen(port, () => {
21+
console.log(`Feedr app listening on port http://localhost:${port}`);
22+
});

0 commit comments

Comments
 (0)