+function bluesky_fetch_notifications(int $uid, int $last_poll)
+{
+ $data = bluesky_xrpc_get($uid, 'app.bsky.notification.listNotifications');
+ if (empty($data->notifications)) {
+ return;
+ }
+ foreach ($data->notifications as $notification) {
+ $uri = bluesky_get_uri($notification);
+ if (Post::exists(['uri' => $uri, 'uid' => $uid]) || Post::exists(['extid' => $uri, 'uid' => $uid])) {
+ Logger::debug('Notification already processed', ['uid' => $uid, 'reason' => $notification->reason, 'uri' => $uri, 'indexedAt' => $notification->indexedAt]);
+ continue;
+ }
+ Logger::debug('Process notification', ['uid' => $uid, 'reason' => $notification->reason, 'uri' => $uri, 'indexedAt' => $notification->indexedAt]);
+ switch ($notification->reason) {
+ case 'like':
+ $item = bluesky_get_header($notification, $uri, $uid, $uid);
+ $item['gravity'] = Item::GRAVITY_ACTIVITY;
+ $item['body'] = $item['verb'] = Activity::LIKE;
+ $item['thr-parent'] = bluesky_get_uri($notification->record->subject);
+ $item['thr-parent'] = bluesky_fetch_missing_post($item['thr-parent'], $uid, $uid, $item['contact-id'], 0, $last_poll);
+ if (!empty($item['thr-parent'])) {
+ $data = Item::insert($item);
+ Logger::debug('Got like', ['uid' => $uid, 'result' => $data, 'uri' => $uri]);
+ } else {
+ Logger::info('Thread parent not found', ['uid' => $uid, 'parent' => $item['thr-parent'], 'uri' => $uri]);
+ }
+ break;
+
+ case 'repost':
+ $item = bluesky_get_header($notification, $uri, $uid, $uid);
+ $item['gravity'] = Item::GRAVITY_ACTIVITY;
+ $item['body'] = $item['verb'] = Activity::ANNOUNCE;
+ $item['thr-parent'] = bluesky_get_uri($notification->record->subject);
+ $item['thr-parent'] = bluesky_fetch_missing_post($item['thr-parent'], $uid, $uid, $item['contact-id'], 0, $last_poll);
+ if (!empty($item['thr-parent'])) {
+ $data = Item::insert($item);
+ Logger::debug('Got repost', ['uid' => $uid, 'result' => $data, 'uri' => $uri]);
+ } else {
+ Logger::info('Thread parent not found', ['uid' => $uid, 'parent' => $item['thr-parent'], 'uri' => $uri]);
+ }
+ break;
+
+ case 'follow':
+ $contact = bluesky_get_contact($notification->author, $uid, $uid);
+ Logger::debug('New follower', ['uid' => $uid, 'nick' => $contact['nick'], 'uri' => $uri]);
+ break;
+
+ case 'mention':
+ $data = bluesky_process_post($notification, $uid, Item::PR_PUSHED, 0, $last_poll);
+ Logger::debug('Got mention', ['uid' => $uid, 'result' => $data, 'uri' => $uri]);
+ break;
+
+ case 'reply':
+ $data = bluesky_process_post($notification, $uid, Item::PR_PUSHED, 0, $last_poll);
+ Logger::debug('Got reply', ['uid' => $uid, 'result' => $data, 'uri' => $uri]);
+ break;
+
+ case 'quote':
+ $data = bluesky_process_post($notification, $uid, Item::PR_PUSHED, 0, $last_poll);
+ Logger::debug('Got quote', ['uid' => $uid, 'result' => $data, 'uri' => $uri]);
+ break;
+
+ default:
+ Logger::notice('Unhandled reason', ['reason' => $notification->reason, 'uri' => $uri]);
+ break;
+ }
+ }
+}
+
+function bluesky_fetch_feed(int $uid, string $feed, int $last_poll)
+{
+ $data = bluesky_xrpc_get($uid, 'app.bsky.feed.getFeed', ['feed' => $feed]);
+ if (empty($data)) {
+ return;
+ }
+
+ if (empty($data->feed)) {
+ return;
+ }
+
+ $feeddata = bluesky_xrpc_get($uid, 'app.bsky.feed.getFeedGenerator', ['feed' => $feed]);
+ if (!empty($feeddata)) {
+ $feedurl = $feeddata->view->uri;
+ $feedname = $feeddata->view->displayName;
+ } else {
+ $feedurl = $feed;
+ $feedname = $feed;
+ }
+
+ foreach (array_reverse($data->feed) as $entry) {
+ $contact = bluesky_get_contact($entry->post->author, 0, $uid);
+ $languages = $entry->post->record->langs ?? [];
+
+ if (!Relay::isWantedLanguage($entry->post->record->text, 0, $contact['id'] ?? 0, $languages)) {
+ Logger::debug('Unwanted language detected', ['text' => $entry->post->record->text]);
+ continue;
+ }
+ $id = bluesky_process_post($entry->post, $uid, Item::PR_TAG, 0, $last_poll);
+ if (!empty($id)) {
+ $post = Post::selectFirst(['uri-id'], ['id' => $id]);
+ if (!empty($post['uri-id'])) {
+ $stored = Post\Category::storeFileByURIId($post['uri-id'], $uid, Post\Category::SUBCRIPTION, $feedname, $feedurl);
+ Logger::debug('Stored tag subscription for user', ['uri-id' => $post['uri-id'], 'uid' => $uid, 'name' => $feedname, 'url' => $feedurl, 'stored' => $stored]);
+ } else {
+ Logger::notice('Post not found', ['id' => $id, 'entry' => $entry]);
+ }
+ }
+ if (!empty($entry->reason)) {
+ bluesky_process_reason($entry->reason, bluesky_get_uri($entry->post), $uid);
+ }
+ }
+}
+
+function bluesky_process_post(stdClass $post, int $uid, int $post_reason, int $level, int $last_poll): int