From 65ef7bc6c9a18e7421468d0853d0c67369c01f97 Mon Sep 17 00:00:00 2001 From: moderndevslulw Date: Sun, 6 Jul 2025 02:06:25 +0500 Subject: feat: channel pages --- lib/partials.php | 5 + lib/utils.php | 21 ++ public/channels/index.php | 372 +++++++++++++++++++++++++++++++++++ public/static/img/icons/door_in.png | Bin 0 -> 940 bytes public/static/img/icons/door_out.png | Bin 0 -> 932 bytes public/static/style.css | 143 +++++++++++++- 6 files changed, 539 insertions(+), 2 deletions(-) create mode 100644 lib/utils.php create mode 100644 public/channels/index.php create mode 100644 public/static/img/icons/door_in.png create mode 100644 public/static/img/icons/door_out.png diff --git a/lib/partials.php b/lib/partials.php index 0fec678..f957cb5 100644 --- a/lib/partials.php +++ b/lib/partials.php @@ -1,4 +1,6 @@ @@ -11,6 +13,9 @@ function html_navigation_bar()
home wiki + + channels +
1 ? "s" : ""); + } else if ($years == 0 && $days == 0 && $hours == 0) { + return "$minutes minute" . ($minutes > 1 ? "s" : ""); + } else if ($years == 0 && $days == 0) { + return "$hours hour" . ($hours > 1 ? "s" : ""); + } else if ($years == 0) { + return "$days day" . ($days > 1 ? "s" : ""); + } else { + return "$years year" . ($years > 1 ? "s" : ""); + } +} \ No newline at end of file diff --git a/public/channels/index.php b/public/channels/index.php new file mode 100644 index 0000000..b32b63b --- /dev/null +++ b/public/channels/index.php @@ -0,0 +1,372 @@ +prepare('SELECT c.*, cp.*, array_to_json(cp.features) as features + FROM channels c + LEFT JOIN channel_preferences cp ON cp.id = c.id + WHERE c.alias_id = ? + '); + $stmt->execute([$_GET['alias_id']]); + $channel = $stmt->fetch(PDO::FETCH_ASSOC) ?: null; + + if (!isset($channel)) { + http_response_code(404); + exit; + } + + $channel['features'] = json_decode($channel['features'] ?: '[]', true); + + // fetching custom commands + $stmt = $db->prepare('SELECT *, array_to_json(messages) as messages + FROM custom_commands + WHERE channel_id = ? + ORDER BY created_at DESC + '); + $stmt->execute([$channel['id']]); + $channel['commands'] = $stmt->fetchAll(PDO::FETCH_ASSOC); + foreach ($channel['commands'] as &$c) { + $c['messages'] = json_decode($c['messages'], true); + } + unset($c); + + // fetching timers + $stmt = $db->prepare('SELECT *, array_to_json(messages) as messages + FROM timers + WHERE channel_id = ? + '); + $stmt->execute([$channel['id']]); + $channel['timers'] = $stmt->fetchAll(PDO::FETCH_ASSOC); + foreach ($channel['timers'] as &$c) { + $c['messages'] = json_decode($c['messages'], true); + } + unset($c); + + // fetching events + $stmt = $db->prepare('SELECT e.*, array_to_json(e.flags) as flags, COUNT(es.id) as subscription_count + FROM events e + LEFT JOIN event_subscriptions es ON es.event_id = e.id + WHERE e.channel_id = ? + GROUP BY e.id + ORDER BY subscription_count DESC + '); + $stmt->execute([$channel['id']]); + $channel['events'] = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // fetching event names + $twitch_types = ['live', 'offline', 'title', 'category']; + $channel_ids = []; + foreach ($channel['events'] as &$e) { + $e['flags'] = json_decode($e['flags'], true); + if (in_array($e['event_type'], $twitch_types)) { + array_push($channel_ids, $e['target_alias_id']); + } + } + unset($e); + if (!empty($channel_ids)) { + $event_users = get_twitch_users(implode('&id=', $channel_ids)); + foreach ($event_users as $user) { + foreach ($channel['events'] as &$e) { + if ($e['target_alias_id'] == $user['id']) { + $e['name'] = $user['login']; + } + } + unset($e); + } + } + + // translating features + foreach ($channel['features'] as &$f) { + if ($f == "notify_7tv_updates") { + $f = "7TV Updates"; + } + } + unset($f); + + if ($response = get_twitch_users($channel['alias_id'])) { + $user = $response[0]; + $channel['pfp'] = $user['profile_image_url']; + $channel['description'] = $user['description'] ?: null; + } + + $has_content = !empty($channel['events']) || !empty($channel['commands']) || !empty($channel['timers']); +} else if (!SHOW_CHANNEL_LIST) { + http_response_code(403); + exit; +} else { + $stmt = $db->query('SELECT alias_id, alias_name FROM channels WHERE opt_outed_at IS NULL ORDER BY joined_at DESC'); + $stmt->execute(); + + $channels = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: []; +} + +// fetching user pfps +if (!empty($channels)) { + $channel_ids = []; + foreach ($channels as $c) { + array_push($channel_ids, $c['alias_id']); + } + + $response = get_twitch_users(implode('&id=', $channel_ids)); + + if (!empty($response)) { + foreach ($response as $c) { + $index = array_search($c['id'], array_column($channels, 'alias_id')); + $channels[$index]['pfp'] = $c['profile_image_url']; + } + } +} +?> + + + + + Channels - The Tinybot Project + + + + + + +
+ + + +
+ +
+ +
+ +
+

About

+ +

+ +
+

Language: +

+

Prefix:

+
+ +
+

Features:

+
+ +
+
+

Joined + ago +

+ +

Opted out!

+ +
+
+ + + +
+ +
+ + +
+

Events

+
+ + + + + + + + + + + + + + + + + +
NameTypeMessageFlagsSubscribers
+ + + + + + + + + +
+
+ + + + +
+

Custom commands

+
+ + + + + + + + + + + + + +
NameMessagesLast executed
+ +

+ +
+
+
+ + + + +
+

Timers

+
+ + + + + + + + + + + + + + + +
NameMessagesIntervalLast executed
+ +

+ +
ago
+
+ +
+
+ +

Nothing found...

+ + +
+

No one has joined yet... ;(

+ Be the first one! +
+ +
+

channels have already joined us

+ Check out the !join command to participate! +
+
+ + +
+ +
+

+
+ +
+ +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/static/img/icons/door_in.png b/public/static/img/icons/door_in.png new file mode 100644 index 0000000..d2ee9c6 Binary files /dev/null and b/public/static/img/icons/door_in.png differ diff --git a/public/static/img/icons/door_out.png b/public/static/img/icons/door_out.png new file mode 100644 index 0000000..466ea2c Binary files /dev/null and b/public/static/img/icons/door_out.png differ diff --git a/public/static/style.css b/public/static/style.css index 739e76c..2b6dcdd 100644 --- a/public/static/style.css +++ b/public/static/style.css @@ -2,8 +2,13 @@ --primary-color: #6ee7b7; --secondary-color: #59c59a; - --box-background: #9bbdad; - --box-border: #92b3a3; + --table-border: #d4d4d4; + + --box-background: #eef5f2; + --box-border: #77ac90; + --box-tab-background: #a2f3cb; + --box-tab-background-disabled: #8ad8b1; + --box-tab-foreground: #000000; --promo-button-background: linear-gradient(0deg, #ff9028 0% 50%, #ffb224 50% 100%); --promo-button-background-hover: #ff9028; @@ -12,6 +17,10 @@ --wiki-content-background: #fff; --wiki-sidebar-background: #fefefe; + + --card-background: #eee; + --card-border: #bdbdbd; + --card-background-hover: #e4e4e4; } * { @@ -94,6 +103,34 @@ button:hover { background: var(--promo-button-background-hover); } +table { + border-collapse: collapse; +} + +table tr { + border-bottom: 1px solid var(--table-border); +} + +table td, +table th { + border-right: 1px solid var(--table-border); +} + +table td:last-child, +table th:last-child { + border-right: none; +} + +table td, +table th { + padding: 4px; + text-align: left; +} + +table tr:last-child { + border-bottom: none; +} + .img-gradient { position: relative; display: inline-block; @@ -314,6 +351,87 @@ div:has(.wiki-content) { border-color: rgba(5, 150, 105, 255); } +/** +--- CHANNELS +*/ + +.card { + display: flex; + flex-direction: column; + padding: 16px; + gap: 16px; + background: var(--card-background); + border: 1px solid var(--card-border); + text-decoration: none; +} + +a.card:hover { + color: unset; + background: var(--card-background-hover); +} + +.card .icon { + border: 1px solid #000; +} + +.card .icon img { + width: 128px; + height: auto; +} + +.card p { + text-decoration: none; +} + +/** +--- BOX +*/ + +.box, +.box>.content { + padding: 4px; + background: var(--box-background); + border: 1px solid var(--box-border); +} + +.box:has(.tabs) { + background: none; + border: none; + padding: 0; +} + +.box>.tabs { + display: flex; + flex-direction: row; + align-items: end; + gap: 4px; +} + +.tab { + background: var(--box-tab-background); + color: var(--box-tab-foreground); + text-shadow: none; + border: 1px solid var(--box-border); + border-radius: 0; + padding: 2px 8px; +} + +.tabs>.tab { + background: var(--box-tab-background-disabled); + border-bottom: none; +} + +.tabs>.tab:disabled { + background: var(--box-tab-background); + padding: 4px 10px; + margin-bottom: 0; +} + +.box.red { + background: #ff6464; + border-color: maroon; +} + /** --- SHORTCUTS */ @@ -336,11 +454,20 @@ div:has(.wiki-content) { flex-direction: row; } +.grow { + flex-grow: 1; +} + .grid-2 { display: grid; grid-template-columns: auto auto; } +.grid-4 { + display: grid; + grid-template-columns: auto auto auto auto; +} + .align-center { align-items: center; } @@ -361,6 +488,10 @@ div:has(.wiki-content) { text-align: right; } +.text-center { + text-align: center; +} + .gap-64 { gap: 64px; } @@ -377,6 +508,10 @@ div:has(.wiki-content) { gap: 8px; } +.p-24 { + padding: 24px; +} + .p-16 { padding: 16px; } @@ -387,4 +522,8 @@ div:has(.wiki-content) { .border { border: 0.25px solid rgba(133, 133, 133, 0.25); +} + +.font-small { + font-size: 12px; } \ No newline at end of file -- cgit v1.2.3