|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* \file
|
|
|
|
* Implement Community listings for the [Listing Provider API](https://codeberg.org/gravel/session-listing-providers).
|
|
|
|
*/
|
|
|
|
|
|
|
|
require_once "getenv.php";
|
|
|
|
require_once "utils/logging.php";
|
|
|
|
require_once "servers/servers-rooms.php";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents a Community listing in the Listing Provider API.
|
|
|
|
*/
|
|
|
|
class CommunityListing implements JsonSerializable {
|
|
|
|
/**
|
|
|
|
* @var string $id
|
|
|
|
* Unique listing identifier.
|
|
|
|
*/
|
|
|
|
public readonly string $id;
|
|
|
|
/**
|
|
|
|
* @var string $name
|
|
|
|
* Human-readable listing name.
|
|
|
|
*/
|
|
|
|
public readonly string $name;
|
|
|
|
/**
|
|
|
|
* @var string $rating
|
|
|
|
* One-word content rating for Communities listed.
|
|
|
|
*/
|
|
|
|
public readonly string $rating;
|
|
|
|
/**
|
|
|
|
* @var CommunityRoom[] $rooms
|
|
|
|
* Communities included in the listing.
|
|
|
|
*/
|
|
|
|
public readonly array $rooms;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new CommunityListing instance with the given parameters.
|
|
|
|
* @param string $id Unique listing identifier.
|
|
|
|
* @param string $name Human-readable listing name.
|
|
|
|
* @param string $rating One-word content rating for Communities listed.
|
|
|
|
* @param CommunityRoom[] $rooms Communities included in the listing.
|
|
|
|
*/
|
|
|
|
public function __construct(string $id, string $name, ?string $rating, array $rooms) {
|
|
|
|
$this->id = $id;
|
|
|
|
$this->name = $name;
|
|
|
|
$this->rating = $rating ?? "unknown";
|
|
|
|
$this->rooms = $rooms;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Produce associative listing data for JSON serialization.
|
|
|
|
*/
|
|
|
|
public function jsonSerialize(): mixed {
|
|
|
|
// TODO: Careful serialization
|
|
|
|
$details = get_object_vars($this);
|
|
|
|
$details['rooms'] = array_map(function(CommunityRoom $room){
|
|
|
|
return $room->to_listing_data();
|
|
|
|
}, $this->rooms);
|
|
|
|
return $details;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Produce associative data summarizing this listing.
|
|
|
|
*/
|
|
|
|
public function to_summary(): array {
|
|
|
|
return array(
|
|
|
|
'id' => $this->id,
|
|
|
|
'name' => $this->name,
|
|
|
|
'rating' => $this->rating,
|
|
|
|
'rooms' => count($this->rooms)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct Community listings from listing configuration and cached Communities.
|
|
|
|
* @return CommunityListing[]
|
|
|
|
* \todo Refactor
|
|
|
|
*/
|
|
|
|
function resolve_listings_config(): array {
|
|
|
|
global $LISTINGS_INI, $ROOMS_FILE;
|
|
|
|
|
|
|
|
$listings_raw = parse_ini_file($LISTINGS_INI, process_sections: true, scanner_mode: INI_SCANNER_RAW);
|
|
|
|
$servers_raw = file_get_contents($ROOMS_FILE);
|
|
|
|
$server_data = json_decode($servers_raw, true);
|
|
|
|
|
|
|
|
$servers = CommunityServer::from_details_array($server_data);
|
|
|
|
$rooms_all = CommunityServer::enumerate_rooms($servers);
|
|
|
|
|
|
|
|
$rooms_by_id = [];
|
|
|
|
foreach ($rooms_all as $room) {
|
|
|
|
$rooms_by_id[$room->get_room_identifier()] = $room;
|
|
|
|
}
|
|
|
|
|
|
|
|
$sogs_by_pubkey = [];
|
|
|
|
foreach ($servers as $server) {
|
|
|
|
$sogs_by_pubkey[$server->get_pubkey()] = $server;
|
|
|
|
}
|
|
|
|
|
|
|
|
$listings = [];
|
|
|
|
foreach ($listings_raw as $id => $listing_props) {
|
|
|
|
$rooms = [];
|
|
|
|
// TODO: Blocklist option
|
|
|
|
if (isset($listing_props['rooms'])) {
|
|
|
|
foreach ($listing_props['rooms'] as $room_id) {
|
|
|
|
if ($room_id == '*') {
|
|
|
|
$rooms = $rooms_all;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (isset($rooms_by_id[$room_id])) {
|
|
|
|
$rooms[] = $rooms_by_id[$room_id];
|
|
|
|
} else {
|
|
|
|
log_warning("Could not find room $room_id from listing $id.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (isset($listing_props['sogs'])) {
|
|
|
|
foreach ($listing_props['sogs'] as $public_key) {
|
|
|
|
if (isset($sogs_by_pubkey[$public_key])) {
|
|
|
|
/** @var CommunityServer $sogs */
|
|
|
|
$sogs = $sogs_by_pubkey[$public_key];
|
|
|
|
array_push($rooms, ...$sogs->rooms);
|
|
|
|
} else {
|
|
|
|
log_warning("Could not find sogs $public_key from listing $id.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$rooms = array_filter($rooms, function(CommunityRoom $room) {
|
|
|
|
return !$room->is_off_record();
|
|
|
|
});
|
|
|
|
|
|
|
|
$listings[] = new CommunityListing(
|
|
|
|
$id,
|
|
|
|
$listing_props['name'],
|
|
|
|
$listing_props['rating'],
|
|
|
|
$rooms
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $listings;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resolve and write configured Community listings to disk.
|
|
|
|
*/
|
|
|
|
function generate_listings() {
|
|
|
|
global $LISTING_PROVIDER_LISTING_SUMMARY, $LISTING_PROVIDER_LISTINGS;
|
|
|
|
log_info("Generating listings...");
|
|
|
|
|
|
|
|
$listings_resolved = resolve_listings_config();
|
|
|
|
$summaries = array_map(function(CommunityListing $listing) {
|
|
|
|
return $listing->to_summary();
|
|
|
|
}, $listings_resolved);
|
|
|
|
file_put_contents($LISTING_PROVIDER_LISTING_SUMMARY, json_encode($summaries));
|
|
|
|
foreach ($listings_resolved as $listing) {
|
|
|
|
$id = $listing->id;
|
|
|
|
file_put_contents(
|
|
|
|
"$LISTING_PROVIDER_LISTINGS/$id",
|
|
|
|
json_encode($listing)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
$listings_count = count($listings_resolved);
|
|
|
|
log_info("Generated $listings_count listings.");
|
|
|
|
}
|
|
|
|
|
|
|
|
file_exists($LISTING_PROVIDER_LISTINGS) or mkdir($LISTING_PROVIDER_LISTINGS, 0755, true);
|
|
|
|
|
|
|
|
$options = getopt("v", ["verbose"]);
|
|
|
|
if (isset($options["v"]) or isset($options["verbose"])) {
|
|
|
|
$LOGGING_VERBOSITY = LoggingVerbosity::Debug;
|
|
|
|
}
|
|
|
|
|
|
|
|
generate_listings();
|
|
|
|
?>
|