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