You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
393 lines
10 KiB
PHP
393 lines
10 KiB
PHP
<?php
|
|
require_once 'utils/utils.php';
|
|
require_once 'servers/tags.php';
|
|
|
|
/**
|
|
* Provides tags for Session Communities.
|
|
*/
|
|
interface CommunitySourceWithTags {
|
|
/**
|
|
* Produce an array of string tags for all Communities found.
|
|
* @return string[][] Array associating room IDs with string tag arrays.
|
|
*/
|
|
public function get_tags(): array;
|
|
}
|
|
|
|
/**
|
|
* Parses Communities fetched from Session.directory.
|
|
*/
|
|
class SDIRCommunitySource implements CommunitySourceWithTags {
|
|
private function __construct(string $contents) {
|
|
$this->contents = $contents;
|
|
}
|
|
|
|
/**
|
|
* Create new instance of SDIRCommunitySource.
|
|
* Returns false if processing the source fails.
|
|
*
|
|
* @param string $contents Text from Session.directory to process.
|
|
*
|
|
* @return SDIRCommunitySource|false
|
|
*/
|
|
public static function from_contents(string $contents): SDIRCommunitySource | false {
|
|
$source = new SDIRCommunitySource($contents);
|
|
|
|
if (!$source->sdir_process_tags()) {
|
|
return false;
|
|
}
|
|
|
|
return $source;
|
|
}
|
|
|
|
private readonly string $contents;
|
|
|
|
/**
|
|
* @var string[][] $tags Array associating room IDs with string tag arrays.
|
|
*/
|
|
private array $tags;
|
|
|
|
private static function sdir_validate_entry(
|
|
array $room_entry,
|
|
bool &$missing_url,
|
|
bool &$missing_tags,
|
|
bool &$invalid_url
|
|
): bool {
|
|
if (!isset($room_entry['url']) || !is_string($room_entry['url'])) {
|
|
log_value($room_entry);
|
|
$missing_url = true;
|
|
return false;
|
|
}
|
|
|
|
if (!isset($room_entry['tags']) || !is_string($room_entry['tags'])) {
|
|
log_value($room_entry);
|
|
$missing_tags = true;
|
|
return false;
|
|
}
|
|
|
|
if (!filter_var($room_entry['url'], FILTER_VALIDATE_URL)) {
|
|
log_value($room_entry);
|
|
$invalid_url = true;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static function sdir_report_errors(bool $entry_missing_url, bool $entry_missing_tags, bool $entry_invalid_url) {
|
|
if ($entry_missing_url) {
|
|
log_error("One or more room entries from session.directory is missing the 'url' parameter.");
|
|
}
|
|
|
|
if ($entry_missing_tags) {
|
|
log_error("One or more room entries from session.directory is missing the 'tags' parameter.");
|
|
}
|
|
|
|
if ($entry_invalid_url) {
|
|
log_warning("One or more room entries from session.directory contain an invalid 'url' parameter.");
|
|
}
|
|
}
|
|
|
|
private function get_sdir_entries(): array|bool {
|
|
try {
|
|
return json_decode($this->contents, true, 512, JSON_THROW_ON_ERROR);
|
|
} catch (JsonException) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private function sdir_process_tags(): bool {
|
|
$entry_missing_url = false;
|
|
$entry_missing_tags = false;
|
|
$entry_invalid_url = false;
|
|
|
|
$rooms = SDIRCommunitySource::get_sdir_entries();
|
|
|
|
if (!$rooms) {
|
|
log_error("Could not parse entries from session.directory.");
|
|
return false;
|
|
}
|
|
|
|
foreach ($rooms as $room_entry) {
|
|
if (!SDIRCommunitySource::sdir_validate_entry(
|
|
$room_entry, $entry_missing_url, $entry_missing_tags, $entry_invalid_url
|
|
)) {
|
|
continue;
|
|
}
|
|
|
|
$url = $room_entry['url'];
|
|
$tag_string = $room_entry['tags'];
|
|
$nsfw = isset($room_entry['nsfw']) && $room_entry['nsfw'] == "1";
|
|
|
|
$room_id = url_get_room_id($url);
|
|
|
|
$this->tags[$room_id] = explode(',', $tag_string);
|
|
if ($nsfw) {
|
|
$this->tags[$room_id] = array_values(array_unique([
|
|
"nsfw", ...$this->tags[$room_id]
|
|
]));
|
|
}
|
|
}
|
|
|
|
SDIRCommunitySource::sdir_report_errors($entry_missing_url, $entry_missing_tags, $entry_invalid_url);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Produce an array of string tags for all Communities found.
|
|
* @return string[][] Array associating room IDs with string tag arrays.
|
|
*/
|
|
public function get_tags(): array {
|
|
return $this->tags;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parses Communities fetched from [Awesome-Session-Group-List](https://raw.githubusercontent.com/GNU-Linux-libre/Awesome-Session-Group-List/main/README.md).
|
|
*/
|
|
class ASGLCommunitySource implements CommunitySourceWithTags {
|
|
private function __construct(string $contents) {
|
|
$this->contents = $contents;
|
|
}
|
|
|
|
private readonly string $contents;
|
|
|
|
/**
|
|
* @var string[][] $tags;
|
|
*/
|
|
private array $tags = [];
|
|
|
|
/**
|
|
* Attempt to create an ASGLCommunitySource instance.
|
|
*
|
|
* Returns false if processing the source fails.
|
|
*
|
|
* @param string $contents Text from ASGL to process.
|
|
*
|
|
* @return ASGLCommunitySource|false
|
|
*/
|
|
public static function from_contents(string $contents): ASGLCommunitySource | false {
|
|
$source = new ASGLCommunitySource($contents);
|
|
|
|
if(!$source->asgl_process_tags()) {
|
|
return false;
|
|
}
|
|
|
|
return $source;
|
|
}
|
|
|
|
private function asgl_process_tags(): bool {
|
|
$lines = explode("\n", $this->contents);
|
|
// $last_headings = [];
|
|
$last_room_id = null;
|
|
$room_tags = [];
|
|
|
|
foreach ($lines as $line) {
|
|
ASGLCommunitySource::asgl_process_line($line, $last_room_id, $room_tags);
|
|
|
|
if ($last_room_id != null && count($room_tags) > 0) {
|
|
$this->tags[$last_room_id] = $room_tags;
|
|
$last_room_id = null;
|
|
$room_tags = [];
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static function asgl_process_line(
|
|
?string $line,
|
|
?string &$last_room_id,
|
|
array &$room_tags
|
|
) {
|
|
$line = trim($line);
|
|
|
|
if (strlen($line) == 0) {
|
|
$last_room_id = null;
|
|
return;
|
|
}
|
|
|
|
$urls = parse_join_links($line);
|
|
|
|
if (count($urls) == 1 && $urls[0] == $line) {
|
|
$last_room_id = url_get_room_id($urls[0]);
|
|
$room_tags = [];
|
|
return;
|
|
}
|
|
|
|
if (str_starts_with($line, "hashtag")) {
|
|
$room_tags = ASGLCommunitySource::read_asgl_tags($line);
|
|
}
|
|
}
|
|
|
|
private static function read_asgl_tags(string $line): array {
|
|
$matches = [];
|
|
preg_match_all('/`#([^`]+)`/', $line, $matches);
|
|
// Return first group matches.
|
|
return $matches[1];
|
|
}
|
|
|
|
/**
|
|
* Produce an array of string tags for all Communities found.
|
|
* @return string[][] Array associating room IDs with string tag arrays.
|
|
*/
|
|
public function get_tags(): array {
|
|
return $this->tags;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Finds Communities on the web.
|
|
*/
|
|
class CommunitySources {
|
|
private const SOURCES = array(
|
|
'ASGL' => 'https://raw.githubusercontent.com/GNU-Linux-libre/Awesome-Session-Group-List/main/README.md',
|
|
'LOKI' => 'https://lokilocker.com/Mods/Session-Groups/wiki/Session-Open-Groups',
|
|
'SDIR-JSON' => 'https://session.directory/scrape.php',
|
|
'SIMP' => 'https://simplifiedprivacy.com/techgroups',
|
|
'FARK' => 'https://freearkham.cc/',
|
|
|
|
// Unused
|
|
'SDIR' => 'https://session.directory/?all=groups',
|
|
'SDIR-PATTERN' => '/view_session_group_user_lokinet\.php\?id=\d+/',
|
|
'SDIR-BASE' => 'https://session.directory/',
|
|
);
|
|
|
|
private readonly string $contents_asgl;
|
|
private readonly string $contents_loki;
|
|
private readonly string $contents_sdir;
|
|
private readonly string $contents_fark;
|
|
private readonly string $contents_simp;
|
|
private readonly string $contents_aggregated;
|
|
|
|
/**
|
|
* Arraying associating room identifiers with arrays of raw tags.
|
|
* @var array<string,string[]> $room_tags
|
|
*/
|
|
private array $room_tags = [];
|
|
|
|
/**
|
|
* Creates a new CommunitySources instance with processed Community data from the Web.
|
|
*/
|
|
public function __construct() {
|
|
log_info("Requesting Awesome Session Group list...");
|
|
$this->contents_asgl = CommunitySources::fetch_source('ASGL');
|
|
|
|
log_info("Requesting Lokilocker Mods Open Group list...");
|
|
$this->contents_loki = CommunitySources::fetch_source('LOKI');
|
|
|
|
log_info("Requesting session.directory list...");
|
|
$this->contents_sdir = CommunitySources::fetch_source('SDIR-JSON');
|
|
|
|
log_info("Requesting FreeArkham.cc list...");
|
|
$this->contents_fark = CommunitySources::fetch_source('FARK');
|
|
|
|
log_info("Requesting SimplifiedPrivacy.com list...");
|
|
$this->contents_simp = CommunitySources::fetch_source('SIMP');
|
|
|
|
log_info("Parsing extra information...");
|
|
if (!$this->process_sources()) {
|
|
log_error("Could not parse extra information from one or more sources.");
|
|
}
|
|
|
|
log_info('Done fetching sources.');
|
|
|
|
$this->contents_aggregated =
|
|
$this->contents_asgl .
|
|
$this->contents_fark .
|
|
$this->contents_loki .
|
|
$this->contents_simp .
|
|
// Slashes are escaped when served, unescape them
|
|
str_replace("\\/", "/", $this->contents_sdir);
|
|
}
|
|
|
|
private static function source_cache_file(string $source_key) {
|
|
global $SOURCES_CACHE;
|
|
return "$SOURCES_CACHE/$source_key";
|
|
}
|
|
|
|
private static function fetch_source(string $source_key) {
|
|
$url = CommunitySources::SOURCES[$source_key];
|
|
|
|
$contents = file_get_contents($url);
|
|
log_debug($http_response_header[0]);
|
|
$cache_file = CommunitySources::source_cache_file($source_key);
|
|
|
|
if ($contents) {
|
|
file_put_contents($cache_file, $contents);
|
|
return $contents;
|
|
}
|
|
|
|
$contents = file_get_contents($cache_file);
|
|
if ($contents) {
|
|
log_warning("Could not fetch source from $url, using cache");
|
|
return $contents;
|
|
}
|
|
|
|
log_error("Could not fetch source from $url.");
|
|
return "";
|
|
}
|
|
|
|
/**
|
|
* @param string[][] $tags Array associating room IDs to tag arrays
|
|
*/
|
|
private function add_tags(array $tags) {
|
|
foreach ($tags as $room_id => $room_tags) {
|
|
if (!isset($this->room_tags[$room_id])) {
|
|
$this->room_tags[$room_id] = [];
|
|
}
|
|
|
|
$this->room_tags[$room_id] = [
|
|
...$this->room_tags[$room_id],
|
|
...$room_tags
|
|
];
|
|
}
|
|
}
|
|
|
|
private function process_sources(): bool {
|
|
$source_sdir = SDIRCommunitySource::from_contents($this->contents_sdir);
|
|
|
|
$source_asgl = ASGLCommunitySource::from_contents($this->contents_asgl);
|
|
|
|
$source_sdir && $this->add_tags($source_sdir->get_tags());
|
|
|
|
$source_asgl && $this->add_tags($source_asgl->get_tags());
|
|
|
|
if (!$source_sdir) {
|
|
return false;
|
|
}
|
|
|
|
if (!$source_asgl) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Return all known join links to Session Communities.
|
|
* @return string[] Join URLs.
|
|
*/
|
|
public function get_join_urls(): array {
|
|
return array_unique(
|
|
parse_join_links($this->contents_aggregated)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Return all known tags for the given room.
|
|
* @param string $room_id Room identifier.
|
|
* @return CommunityTag[] Array of string tags.
|
|
*/
|
|
public function get_room_tags($room_id): array {
|
|
if (!isset($this->room_tags[$room_id])) {
|
|
return [];
|
|
}
|
|
|
|
return $this->room_tags[$room_id];
|
|
}
|
|
}
|
|
|
|
file_exists($SOURCES_CACHE) or mkdir($SOURCES_CACHE, 0755, recursive: true);
|
|
?>
|