システムアカウントの発見

守矢諏訪子 <suwako@076.moe>、2026-11-11 15:15
コミット 90fc1533582f542e192f5fecf3a62253635ecdcc ・親 b928dd1 ・ファイルを見る

4個のファイル、+61 -5

@@ -142,6 +142,7 @@
142142 $url = (string)($_GET['actor'] ?? '');
143143 $local = Fedi::localUsername($url);
144144 if ($local !== null) $this->redirect("/microblog/user/{$local}");
145+ if ($url === Fedi::blogActorUrl()) $this->blogActor();
145146 if (!Remote::isSafeUrl($url) || Actor::isDomainBlocked($url)) $this->notFound();
146147 // 他のサーバーへの問い合わせはログインしたユーザーだけ(誰でも取得させられると、踏み台にされる)
147148 $doc = $this->me !== null ? Remote::actor($url, isset($_GET['refresh'])) : Remote::cachedActor($url);
@@ -171,6 +172,39 @@
171172 'items' => $this->present($items),
172173 'next' => null,
173174 ]);
175+ }
176+
177+ /**
178+ * このサイトのブログのアカウント(FEDIINFOのactor)のプロフィール
179+ * 記事はブログで読むので投稿は表示せず、マイクロブログからはフォロー出来ない(Social::follow)
180+ */
181+ private function blogActor(): never {
182+ try {
183+ $doc = Remote::actor(Fedi::blogActorUrl());
184+ } catch (\Exception $e) {
185+ $doc = null; // 公開鍵が読めない等
186+ }
187+ if (!$doc) $this->notFound();
188+ $this->render('microblog/profile', (string)($doc['name'] ?? $doc['preferredUsername'] ?? ''), [
189+ 'profile' => [
190+ 'url' => $doc['id'],
191+ 'name' => $name = (string)(($doc['name'] ?? '') ?: ($doc['preferredUsername'] ?? '')),
192+ 'color' => htmlspecialchars($name, ENT_QUOTES, 'UTF-8'),
193+ 'handle' => Remote::handle($doc),
194+ 'avatar' => self::safeUrl(is_string($doc['icon']['url'] ?? null) ? $doc['icon']['url'] : '') ?: '/static/user/noicon.png',
195+ 'bio' => Html::sanitize((string)($doc['summary'] ?? '')),
196+ 'local' => true,
197+ 'profilePage' => '',
198+ 'locked' => false,
199+ 'followers' => null,
200+ 'following' => null,
201+ ],
202+ 'relation' => ['self' => false, 'can' => false],
203+ 'blogActor' => true,
204+ 'items' => [],
205+ 'next' => null,
206+ ]);
207+ exit;
174208 }
175209
176210 //////// 投稿のページ
@@ -145,9 +145,7 @@
145145 */
146146 public static function webfinger(string $user, string $host): ?string {
147147 if (!preg_match('/^[A-Za-z0-9_.-]+$/', $user) || !preg_match('/^[A-Za-z0-9.-]+$/', $host)) return null;
148- if (strtolower($host) === strtolower(Fedi::host())) {
149- return Fedi::localActor($user) ? Fedi::actorUrl($user) : null;
150- }
148+ if (strtolower($host) === strtolower(Fedi::host())) return self::localHandle($user);
151149
152150 // 同じ人を何度も探さない様に、結果を1日キャッシュする
153151 // 見つからなかった場合は短くする(以前は1日で、相手のサーバーが一時的に応答しないと1日探せなかった)
@@ -221,8 +219,17 @@
221219 return $doc ? $doc['id'] : null;
222220 }
223221 if (preg_match('/^@?([A-Za-z0-9_.-]+)@([A-Za-z0-9.-]+\.[A-Za-z]{2,}(?::\d+)?)$/', $q, $m)) return self::webfinger($m[1], $m[2]);
224- if (preg_match('/^@([A-Za-z0-9]+)$/', $q, $m)) return Fedi::localActor($m[1]) ? Fedi::actorUrl($m[1]) : null;
222+ if (preg_match('/^@([A-Za-z0-9_.-]+)$/', $q, $m)) return self::localHandle($m[1]);
225223 return null;
224+ }
225+
226+ /**
227+ * このサイトのアカウント名からアクターのURL
228+ * ブログのアカウント(FEDIINFOのactor)も含める。同じ名前のユーザーより優先する(WebFingerと同じ)
229+ */
230+ private static function localHandle(string $user): ?string {
231+ if (ACTIVITYPUB_ENABLED && strcasecmp($user, (string)FEDIINFO['actor']) === 0) return Fedi::blogActorUrl();
232+ return Fedi::localActor($user) ? Fedi::actorUrl($user) : null;
226233 }
227234
228235 /**
@@ -277,6 +277,19 @@
277277 $test->assertNull(Fedi::localActor('dave'));
278278 });
279279
280+ $test->it('このサイトのアカウントを検索出来るはず(ブログのアカウントも)', function($test) use ($use, $alice): void {
281+ $use('A');
282+ // テストのサーバーはIPアドレスなので、「@user@host」はWebFingerで確認する
283+ $test->assertEquals($alice, Remote::webfinger('alice', '1.1.1.1'));
284+ $test->assertEquals($alice, Remote::resolve('@alice'));
285+ $blog = FEDIINFO['actor'];
286+ $test->assertEquals(Fedi::blogActorUrl(), Remote::webfinger($blog, '1.1.1.1'));
287+ $test->assertEquals(Fedi::blogActorUrl(), Remote::webfinger(strtoupper($blog), '1.1.1.1'));
288+ $test->assertEquals(Fedi::blogActorUrl(), Remote::resolve("@{$blog}"));
289+ $test->assertNull(Remote::webfinger('nobody', '1.1.1.1'));
290+ $test->assertNull(Remote::resolve('@dave'), 'BANされたユーザー');
291+ });
292+
280293 $test->it('リモートのユーザーをフォローして承認されるはず', function($test) use ($use, $pump, $alice, $bob): void {
281294 $use('A');
282295 $test->assertTrue(Social::follow('alice', $bob)->isSuccess);
@@ -76,7 +76,9 @@
7676 {@ endif @}
7777 </div>
7878 </section>
79-{@ if (empty($items)) @}
79+{@ if (!empty($blogActor)) @}
80+ <p class="paragraph">このサイトのブログのアカウントです。新しい記事を自動で投稿します。記事は<a href="/">ブログ</a>でお読み下さい(マイクロブログからはフォロー出来ません)。</p>
81+{@ elif (empty($items)) @}
8082 <p class="paragraph">表示出来る投稿はありません。</p>
8183 {@ endif @}
8284 {@ foreach ($items as $it) @}