friendship ended with social-app. php is my new best friend
1<?php
2error_reporting(E_ALL);
3ini_set('display_errors', 'On');
4ini_set('log_errors_max_len', '0');
5
6require_once('vendor/autoload.php');
7require_once('config.php');
8require_once('lib/bskyToucher.php');
9
10use League\CommonMark\CommonMarkConverter;
11use React\Promise\Deferred;
12use React\EventLoop\Loop;
13use React\Promise\Promise;
14use Tracy\Debugger;
15use Tracy\OutputDebugger;
16use Matrix\Async;
17
18$bskyToucher = new BskyToucher();
19
20$favoriteFeeds = array_map(function ($feed) use ($bskyToucher) {
21 return $bskyToucher->getFeedInfo($feed);
22}, FAVORITE_FEEDS);
23
24function getPostOgImage(object $post): ?string {
25 if (!property_exists($post, 'embedType') || !$post->embedType) return null;
26
27 if ($post->embedType === 'app.bsky.embed.images') {
28 return $post->embeds[0]->imgUrl;
29 } else if ($post->embedType === 'app.bsky.embed.external' || $post->embedType === 'app.bsky.embed.video') {
30 return $post->embeds[0]->thumb;
31 } else if ($post->embedType === 'app.bsky.embed.record') {
32 return getPostOgImage($post->embeds[0]->post);
33 }
34 return null;
35}
36
37/*Debugger::enable();
38// This where errors and exceptions will be logged. Make sure this directory exists and is writable.
39Debugger::$logDirectory = __DIR__ . '/../log/';
40//Debugger::$strictMode = true; // display all errors
41// Debugger::$strictMode = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED; // all errors except deprecated notices
42if (Debugger::$showBar) {
43 // This is specific to the Tracy Extension for Flight if you've included that
44 // otherwise comment this out.
45 //new TracyExtensionLoader($app);
46}*/
47
48Flight::set('frontpageFeed', FRONTPAGE_FEED);
49Flight::set('slingshotInstance', SLINGSHOT_INSTANCE);
50Flight::set('constellationInstance', CONSTELLATION_INSTANCE);
51Flight::set('plcDirectory', PLC_DIRECTORY);
52Flight::set('defaultPds', DEFAULT_PDS);
53Flight::set('publicApi', PUBLIC_API);
54Flight::set('frontpageFeed', FRONTPAGE_FEED);
55Flight::set('defaultRelay', DEFAULT_RELAY);
56Flight::set('userAuth', null);
57Flight::set('flight.log_errors', false);
58Flight::set('flight.handle_errors', false);
59Flight::set('flight.content_length', false);
60
61Flight::set('standardParams', [
62 'siteTitle' => SITE_TITLE,
63 'themes' => THEMES,
64 'fonts' => FONTS,
65 'setTheme' => array_key_exists('sbs_theme', $_COOKIE) ? $_COOKIE['sbs_theme'] : DEFAULT_THEME,
66 'setFont' => array_key_exists('sbs_font', $_COOKIE) ? $_COOKIE['sbs_font'] : DEFAULT_FONT,
67 'userAuth' => Flight::get('userAuth'),
68 'favFeeds' => $favoriteFeeds,
69 'pages' => PAGES,
70 'links' => LINKS,
71 'ogdomain' => SITE_DOMAIN
72]);
73
74Flight::route('/', function () {
75 $bskyToucher = new BskyToucher();
76 $posts = $bskyToucher->getFeed(Flight::get('frontpageFeed'));
77 $feedInfo = $bskyToucher->getFeedInfo(Flight::get('frontpageFeed'));
78 $latte = new Latte\Engine;
79 $latte->render('./templates/home.latte', array_merge(Flight::get('standardParams'), [
80 'mainClass' => 'home feed',
81 'feedInfo' => $feedInfo,
82 'posts' => $posts->feed,
83 'cursor' => $posts->cursor,
84 'feedAtUri' => FRONTPAGE_FEED,
85 'ogtitle' => SITE_TITLE,
86 'ogdesc' => SITE_DESC,
87 'ogimage' => '',
88 'ogurl' => SITE_DOMAIN.'/'
89 ]));
90});
91
92Flight::route('/u/@handle:[a-z0-9\.]+/@rkey:[a-z0-9]+', function (string $handle, string $rkey): void {
93 $bskyToucher = new BskyToucher();
94 $post = $bskyToucher->getPost($handle, $rkey, Flight::get('userAuth') === null);
95 $atUri = 'at://'.$post->author->did.'/app.bsky.feed.post/'.$rkey;
96 $latte = new Latte\Engine;
97 $latte->render('./templates/single.latte', array_merge(Flight::get('standardParams'), [
98 'mainClass' => 'post',
99 'post' => $post,
100 'ogtitle' => SITE_TITLE." | ".$post->author->displayName." (@".$post->author->handle.")",
101 'ogdesc' => $post->content,
102 'ogimage' => getPostOgImage($post),
103 'ogurl' => SITE_DOMAIN.'/u/'.$handle.'/'.$rkey
104 ]));
105});
106
107Flight::route('/u/@handle:[a-z0-9\.]+(/@tab:[a-z]+)', function (string $handle, ?string $tab): void {
108 $bskyToucher = new BskyToucher();
109 $user = $bskyToucher->getUserInfo($handle, 'handle');
110 $posts = $bskyToucher->getUserPosts($user->did);
111 $latte = new Latte\Engine;
112 $latte->render('./templates/profile.latte', array_merge(Flight::get('standardParams'), [
113 'mainClass' => 'profile',
114 'handle' => $handle,
115 'posts' => $posts,
116 'user' => $user,
117 'ogtitle' => SITE_TITLE." | ".$user->displayName." (@".$user->handle.")",
118 'ogdesc' => $user->description,
119 'ogimage' => $user->avatar,
120 'ogurl' => SITE_DOMAIN.'/u/'.$user->handle.'/'
121 ]));
122});
123
124Flight::route('/f/@did:did:plc:[0-9a-z]+/@name:[a-z0-9\-\_]+', function (string $did, string $name): void {
125 $bskyToucher = new BskyToucher();
126 $feedUrl = "at://".$did."/app.bsky.feed.generator/".$name;
127 $feedInfo = $bskyToucher->getFeedInfo($feedUrl);
128 $creatorInfo = $bskyToucher->getUserInfo($feedInfo->creatorDid, 'did');
129 $posts = $bskyToucher->getFeed($feedUrl);
130 $latte = new Latte\Engine;
131 $latte->render('./templates/feed.latte', array_merge(Flight::get('standardParams'), [
132 'mainClass' => 'feed',
133 'posts' => $posts->feed,
134 'cursor' => $posts->cursor,
135 'feedName' => $feedInfo->title,
136 'feedAvatar' => $feedInfo->avatar,
137 'feedDescription' => $feedInfo->description,
138 'feedAtUri' => $feedUrl,
139 'feedAuthorName' => $creatorInfo->displayName,
140 'feedAuthorHandle' => $creatorInfo->handle,
141 'feedAuthorDid' => $creatorInfo->did,
142 'feedAuthorPds' => $creatorInfo->pds,
143 'ogtitle' => SITE_TITLE." | ".$feedInfo->title,
144 'ogdesc' => $feedInfo->description,
145 'ogimage' => $feedInfo->avatar,
146 'ogurl' => SITE_DOMAIN.'/f/'.$did.'/'.$name
147 ]));
148});
149
150Flight::route('/s', function (): void {
151 $latte = new Latte\Engine;
152 $latte->render('./templates/search.latte', array_merge(Flight::get('standardParams'), [
153 'mainClass' => 'search',
154 'params' => $_GET,
155 'ogtitle' => SITE_TITLE." | search".(array_key_exists('s', $_GET) ? ': '.$_GET['s'] : ''),
156 'ogdesc' => SITE_DESC,
157 'ogimage' => '',
158 'ogurl' => SITE_DOMAIN.'/u/'.$handle.'/'.$rkey
159 ]));
160});
161
162Flight::route('GET /login', function(): void {
163 $latte = new Latte\Engine;
164 $latte->render('./templates/login.latte', array_merge(Flight::get('standardParams'), [
165 'mainClass' => 'form',
166 'ogtitle' => SITE_TITLE." | login",
167 'ogdesc' => SITE_DESC,
168 'ogimage' => '',
169 'ogurl' => SITE_DOMAIN.'/login'
170 ]));
171});
172
173Flight::route('POST /login', function(): void {
174 // whatever stupid token exchange dance needs to happen to direct them to oauth
175});
176
177Flight::route('/createaccount', function(): void {
178 $latte = new Latte\Engine;
179 $latte->render('./templates/create.latte', array_merge(Flight::get('standardParams'), [
180 'mainClass' => 'form',
181 'ogtitle' => SITE_TITLE." | create account",
182 'ogdesc' => SITE_DESC,
183 'ogimage' => '',
184 'ogurl' => SITE_DOMAIN.'/createaccount'
185 ]));
186});
187
188Flight::route('/api/@endpoint', function (string $endpoint): void {
189 $bskyToucher = new BskyToucher();
190 $latte = new Latte\Engine;
191 if ($endpoint === 'userPosts') {
192 if (!array_key_exists('user', $_GET)) Flight::json(["error" => "missing user in query"]);
193 if (!preg_match('/^did:plc:([a-z0-9]+)$/', $_GET['user'], $_out)) Flight::json(["error" => "malformed user DID"]);
194 $user = $_GET['user'];
195 $auth = false; // TODO: set up so that it's like. actually dynamic later
196 $cursor = array_key_exists('cursor', $_GET) ? $_GET['cursor'] : 0;
197 $posts = $bskyToucher->getUserPosts($user, $cursor, $auth);
198 $latte->render('./templates/_partials/feedPosts.latte', array_merge(Flight::get('standardParams'), [
199 'posts' => $posts
200 ]));
201 return;
202 } else if ($endpoint === 'feedPosts') {
203 if (!array_key_exists('feed', $_GET)) Flight::json(["error" => "missing feed uri in query"]);
204 if (!preg_match('/^at:\/\/(did:plc:[a-z0-9]+)\/app.bsky.feed.generator\/([a-z0-9]+)$/', $_GET['feed'], $_out)) Flight::json(["error" => "malformed at-uri representing feed", "feed" => $_GET['feed']]);
205 $cursor = array_key_exists('cursor', $_GET) ? $_GET['cursor'] : 0;
206 $auth = false; // TODO: again. once i figure out auth
207 $posts = $bskyToucher->getFeed($_GET['feed'], $cursor, $auth);
208 if ($posts) {
209 $html = $latte->renderToString('./templates/_partials/feedPosts.latte', array_merge(Flight::get('standardParams'), [
210 'posts' => $posts->feed
211 ]));
212 Flight::json(['posts' => $html, 'cursor' => $posts->cursor]);
213 }
214 } else if ($endpoint === 'notifs') {
215
216 }
217});
218
219Flight::route('/@page', function (string $page): void {
220 $latte = new Latte\Engine;
221 $converter = new CommonMarkConverter();
222 $md = $converter->convert(file_get_contents('./pages/'.$page.'.md'));
223 $latte->render('./templates/page.latte', array_merge(Flight::get('standardParams'), [
224 'mainClass' => 'page',
225 'content' => $md,
226 'ogtitle' => SITE_TITLE." | ".$page,
227 'ogdesc' => SITE_DESC,
228 'ogimage' => '',
229 'ogurl' => SITE_DOMAIN.'/'.$page
230 ]));
231});
232
233Flight::start();
234
235?>