From d3878aa55352a921db75aeb26d21591d939f6558 Mon Sep 17 00:00:00 2001
From: gravel
Date: Fri, 27 Jan 2023 15:11:51 +0100
Subject: [PATCH 01/25] Add table sort indicators
---
output/js/constants.js | 39 ++++++++++++---------
output/main.js | 29 +++++++++-------
output/styles2.css | 50 +++++++++++++++++++++++++--
sites/+components/tbl_communities.php | 5 +--
4 files changed, 91 insertions(+), 32 deletions(-)
diff --git a/output/js/constants.js b/output/js/constants.js
index 3002622..071f42d 100644
--- a/output/js/constants.js
+++ b/output/js/constants.js
@@ -33,7 +33,7 @@ export const ATTRIBUTES = {
ACTIVE: 'data-sort',
ASCENDING: 'data-sort-asc',
COLUMN: 'data-sorted-by',
- COLUMN_LITERAL: 'sorted-by'
+ // COLUMN_LITERAL: 'sorted-by'
}
};
@@ -41,7 +41,14 @@ export function columnAscendingByDefault(column) {
return column != COLUMN.USERS;
}
-export function columnIsSortable(column) { return column != COLUMN.QR_CODE; }
+export function columnIsSortable(column) {
+ return ![
+ COLUMN.QR_CODE,
+ COLUMN.PREVIEW,
+ // Join URL contents are not guaranteed to have visible text.
+ COLUMN.JOIN_URL
+ ].includes(column);
+}
export function columnNeedsCasefold(column) {
return [
@@ -64,22 +71,22 @@ export function columnIsNumeric(column) {
* @returns {HTMLElement}
*/
function createElement(tag, ...args) {
- const element = document.createElement(tag);
- if (args.length === 0) return element;
- const propsCandidate = args[0];
- if (typeof propsCandidate !== "string" && !(propsCandidate instanceof Element)) {
- // args[0] is not child element or text node
- // must be props object
- Object.assign(element, propsCandidate);
- args.shift();
- }
- element.append(...args);
- return element;
+ const element = document.createElement(tag);
+ if (args.length === 0) return element;
+ const propsCandidate = args[0];
+ if (typeof propsCandidate !== "string" && !(propsCandidate instanceof Element)) {
+ // args[0] is not child element or text node
+ // must be props object
+ Object.assign(element, propsCandidate);
+ args.shift();
+ }
+ element.append(...args);
+ return element;
}
export const element = new Proxy({}, {
- get(_, key) {
- return (...args) => createElement(key, ...args)
- }
+ get(_, key) {
+ return (...args) => createElement(key, ...args)
+ }
});
diff --git a/output/main.js b/output/main.js
index 0bc0bdb..2aba288 100644
--- a/output/main.js
+++ b/output/main.js
@@ -51,11 +51,11 @@ const filteredCommunities = {
// This can be achieved with `text-overflow: ellipsis` instead
// and generated entirely server-side.
const transformJoinURL = (join_link) => {
- return element.button({
- textContent: "Copy",
- className: "copy_button",
- onclick: () => copyToClipboard(join_link)
- });
+ return element.button({
+ textContent: "Copy",
+ className: "copy_button",
+ onclick: () => copyToClipboard(join_link)
+ });
}
function onLoad(timestamp) {
@@ -101,7 +101,7 @@ function hideBadCommunities() {
* Removes an element by its ID and returns the number of elements removed.
*/
function hideElementByID(id) {
- const element = document.getElementById(id);
+ const element = document.getElementById(id);
element?.remove();
return element ? 1 : 0;
}
@@ -245,17 +245,22 @@ function setSortState(table, { ascending, column }) {
}
table.setAttribute(ATTRIBUTES.SORTING.ASCENDING, ascending);
table.setAttribute(ATTRIBUTES.SORTING.COLUMN, column);
- // This can be used to style column headers in a consistent way, i.e.
- // #tbl_communities[data-sort-asc=true][sorted-by=name]::after #th_name, ...
- table.setAttribute(ATTRIBUTES.SORTING.COLUMN_LITERAL, COLUMN_LITERAL[column]);
+
+ // No way around this for brief CSS.
+ const headers = table.querySelectorAll("th");
+ headers.forEach((th, colno) => {
+ th.removeAttribute(ATTRIBUTES.SORTING.ACTIVE);
+ });
+ headers[column].setAttribute(ATTRIBUTES.SORTING.ACTIVE, true);
}
// This is best done in JS, as it would require
+
+
+
-
Copied URL to clipboard. Paste into Session app to join
From a1156ac04af155842ecf941682cce23898edbfb1 Mon Sep 17 00:00:00 2001
From: gravel
Date: Sat, 4 Mar 2023 15:17:45 +0100
Subject: [PATCH 04/25] Update README.md
---
README.md | 70 ++++++++++++++++++++++++++-----------------------------
1 file changed, 33 insertions(+), 37 deletions(-)
diff --git a/README.md b/README.md
index eebc2cf..0cab54f 100644
--- a/README.md
+++ b/README.md
@@ -2,17 +2,17 @@
## What does this site do?
-This script crawls known sources of published Session Communities,
-queries their servers for available information and
-displays this information as a static HTML page.
+This script crawls known sources of published Session Communities,
+queries their servers for available information and
+displays this information as a static HTML page.
The results of this can be viewed on https://sessioncommunities.online/.
## What is Session?
-Session is a private messaging app that protects your meta-data,
-encrypts your communications, and makes sure your messaging activities
-leave no digital trail behind.
+Session is a private messaging app that protects your meta-data,
+encrypts your communications, and makes sure your messaging activities
+leave no digital trail behind.
https://getsession.org/
@@ -22,58 +22,54 @@ https://getsession.org/
Currently this script crawls the following sites:
-- https://github.com/GNU-Linux-libre/Awesome-Session-Group-List
-- https://lokilocker.com/Mods/Session-Groups/wiki/Session-Open-Groups
-- https://session.directory/
+-
+-
+-
-Additionally, the following open community servers are polled:
+Additionally, a few other servers are hardcoded, see [querying logic](php/fetch-servers.php).
-- https://open.getsession.org
-- http://13.233.251.36:8081
+### How does this work?
-### Steps
+The [`update-listing.php`](php/update-listing.php) script invokes the following two PHP scripts: [`fetch-servers.php`](php/fetch-servers.php) to query available servers, and [`generate-html.php`](php/generate-html.php>) to generate the static HTML.
+
+The querying logic consists of these steps:
1. Fetching source HTML: `get_html_from_known_sources()`
1. Extracting Session invites from the HTML:
`extract_join_links_from_html()` and `get_servers_from_join_links()`
1. Making sure servers are online: `reduce_servers()`
-1. Querying the servers for all available rooms
-and normalize active user numbers: `query_servers_for_rooms()`
-1. De-duplicating servers based on public keys:
+1. Querying the servers for all available rooms
+and normalizing active user numbers: `query_servers_for_rooms()`
+1. De-duplicating servers based on public keys:
`get_pubkeys_of_servers()` and `reduce_addresses_of_pubkeys()`
1. Aggregating all server info & adding language data: `generate_info_arrays()`
-1. Generating static HTML content: `generateHTML()`
-
-### Legacy support
-Right now we fully support legacy SOGS servers,
-although this support is likely going to be dropped soon,
-since those servers can not even be joined anymore with current Session clients.
-Dropping legacy support will also increase maintainability.
+Static HTML is generated from the [`sites`](sites) directory to the [`output`](output) directory, which additionally contains static assets. All contents of `sites` are invoked to produce a HTML page unless they are prefixed with a `+` sign.
### Work around bad routing to Chinese servers
-Depending on your location, it is possible for you to get really bad routing to
-SOGS servers behind the GFW. In this case,
-the initial connection is still successful, but you'll never receive
-any actual content and the retrieval attempt will simply time out.
+Depending on your location, it is possible for you to get really bad routing to
+SOGS servers behind the [GFW](https://en.wikipedia.org/wiki/Great_Firewall). In this case,
+the initial connection is still successful, but you'll never receive
+any actual content and the retrieval attempt will simply time out.
This happens randomly. To make sure this won't affect the results, we simply
-check whether the server is online (the initial connection being successful),
-and then retry a lot of times with a short timeout
-until we eventually get the content.
+check whether the server is online (the initial connection being successful),
+and then retry a lot of times with a short timeout
+until we eventually get the content.
The details can be seen in `curl_get_contents()`.
### Official repositories
-- https://github.com/mdPlusPlus/sessioncommunities.online
-- https://lokilocker.com/SomeGuy/sessioncommunities.online
-If your favourite Session community is missing a language flag,
+- GitHub:
+- Lokinet Gitea:
+
+If your favourite Session community is missing a language flag,
you can issue a pull request here:
-- https://github.com/mdPlusPlus/sessioncommunities.online-languages/
+-
## Contact
-If you want to contact me, you can add me on Session via my
-[ONS](https://docs.oxen.io/using-the-oxen-blockchain/using-oxen-name-system):
-"someguy" (without the quotes)
+If you want to contact me, you can add me on Session via my
+[ONS](https://docs.oxen.io/using-the-oxen-blockchain/using-oxen-name-system):
+`someguy`.
From 3e1895c32d7947d65d5b67ec0747d7c330fe64d0 Mon Sep 17 00:00:00 2001
From: gravel
Date: Sat, 4 Mar 2023 15:18:00 +0100
Subject: [PATCH 05/25] Add CONTRIBUTING.md
---
CONTRIBUTING.md | 65 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
create mode 100644 CONTRIBUTING.md
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..1cb5dc1
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,65 @@
+# Contribution guidelines
+
+## Development environment
+
+### Prerequisites
+
+- PHP (version TBD)
+- `make`
+- `entr` to watch for file changes
+- `xdg-open` link handler to invoke browser
+- patience
+
+### Cloning or updating the repository
+
+Ensure the consistency of the `languages` submodule by using the following options:
+
+- `git clone --recurse-submodules `
+- `git pull --recurse-submodules`
+
+### Development
+
+Run at least once: `make fetch` to query servers. This can take around 5 minutes.
+
+Run when developing: `make dev` to watch for changes & serve HTML locally in browser.
+Does not respond to new files.
+
+See [`Makefile`](Makefile) for more details.
+
+### Running your own copy
+
+- point your webserver at the `output` folder
+- install systemd services from the `systemd` folder or an equivalent timer
+- `session_sudoers`: TBD
+
+## Code style guidelines
+
+### General
+
+**Indentation**: Tabs (4-wide)
+
+**Filename seperator**: Hyphen (`-`)
+
+### PHP
+
+**Identifier casing**: `snake_case` and `CONSTANT_CASE`
+
+**Comments and documentation**: TBD
+
+### HTML & CSS
+
+**Identifier casing**: `kebab-case`, occasional `snake_case`
+
+**Comments and documentation**: TBD
+
+### JavaScript
+
+**Identifier casing**: `camelCase` and `CONSTANT_CASE`, occasional `snake_case`
+
+**Comments and documentation**: [JSDoc](https://jsdoc.app/)
+
+## Contact
+
+- Web Development Session Community on [caliban.org](https://sog.caliban.org/)
+- Project lead, querying logic, deployment, community filtering: `someguy` on Session
+- Documentation, code quality, HTML generation, CSS, JS: `gravel` on Session
From 2750764c7cd6d068f27df05e8629de97e87a51f7 Mon Sep 17 00:00:00 2001
From: gravel
Date: Sat, 4 Mar 2023 15:21:01 +0100
Subject: [PATCH 06/25] Hotfix unknown identifier in truncate()
---
php/utils/server-utils.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/php/utils/server-utils.php b/php/utils/server-utils.php
index 72dd56a..5caa392 100644
--- a/php/utils/server-utils.php
+++ b/php/utils/server-utils.php
@@ -21,7 +21,7 @@
function truncate($url, $len) {
return (strlen($url) > $len + 3)
? substr($url, 0, $len).'...'
- : $string;
+ : $url;
}
/*
From 6c692fb4ee136b52a86e397d2737902550b38f02 Mon Sep 17 00:00:00 2001
From: mdPlusPlus
Date: Sat, 4 Mar 2023 15:46:31 +0100
Subject: [PATCH 07/25] Modify $known_servers
---
output/main.js | 1 -
php/fetch-servers.php | 4 ++--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/output/main.js b/output/main.js
index f6a341a..8865a5a 100644
--- a/output/main.js
+++ b/output/main.js
@@ -25,7 +25,6 @@ const filteredCommunities = {
tests: [
"2e9345+c7fb", // TestRoom
"762ba9+c7fb", // TesterRoom
- "appletonv2+4264", // -
"b4d829+c7fb", // Test
"e5853a+c7fb", // testtest
"fishing+8e2e", // Example group from PySOGS documentation
diff --git a/php/fetch-servers.php b/php/fetch-servers.php
index 7e79727..56d2693 100644
--- a/php/fetch-servers.php
+++ b/php/fetch-servers.php
@@ -30,11 +30,11 @@
// "http://13.233.251.36:8081", // found via shodan.io, but now offline
"http://164.92.176.135", // found via shodan.io
"http://176.119.147.102", // found via shodan.io
- "http://192.227.193.159", // found via shodan.io
+ // "http://192.227.193.159", // found via shodan.io, removed out of decency
"http://60a9fc9.online-server.cloud", // found via shodan.io
"http://93.95.230.10", // found via shodan.io
"http://94.176.239.60", // found via shodan.io
- "http://bitcoincash.tokyo", // found via shodan.io
+ // "http://bitcoincash.tokyo", // found via shodan.io, removed by request
"http://captain.geekgalaxy.com", // found via shodan.io
"http://session.hwreload.it", // found via shodan.io
"http://sogs.k9net.org", // found via shodan.io
From 36278181baec36fa771fc58621a8e9b09883a246 Mon Sep 17 00:00:00 2001
From: gravel
Date: Sat, 4 Mar 2023 21:49:18 +0100
Subject: [PATCH 08/25] Add basic 'Server' column
---
output/js/constants.js | 28 ++++++++++++++++-----------
output/main.js | 16 +++------------
output/styles2.css | 21 ++++++++++++++++++++
sites/+components/tbl_communities.php | 25 ++++++++++++++++++++++++
4 files changed, 66 insertions(+), 24 deletions(-)
diff --git a/output/js/constants.js b/output/js/constants.js
index 8fe3d67..6cf876f 100644
--- a/output/js/constants.js
+++ b/output/js/constants.js
@@ -14,7 +14,7 @@ export const dom = {
export const COLUMN = {
IDENTIFIER: 0, LANGUAGE: 1, NAME: 2,
DESCRIPTION: 3, USERS: 4, PREVIEW: 5,
- QR_CODE: 6, JOIN_URL: 7
+ QR_CODE: 6, SERVER_ICON: 7, JOIN_URL: 8
};
// Reverse enum.
@@ -43,18 +43,24 @@ export function columnAscendingByDefault(column) {
export function columnIsSortable(column) { return column != COLUMN.QR_CODE; }
-export function columnNeedsCasefold(column) {
- return [
- COLUMN.IDENTIFIER,
- COLUMN.NAME,
- COLUMN.DESCRIPTION
- ].includes(column);
+/**
+ * @type {Record any>}
+ */
+const TRANSFORMATION = {
+ numeric: (el) => parseInt(el.innerText),
+ casefold: (el) => el.innerText.toLowerCase().trim(),
+ tokenData: (el) => el.getAttribute("data-token")
}
-export function columnIsNumeric(column) {
- return [
- COLUMN.USERS
- ].includes(column);
+/**
+ * @type {Dictionary any>}
+ */
+export const COLUMN_TRANSFORMATION = {
+ [COLUMN.USERS]: TRANSFORMATION.numeric,
+ [COLUMN.IDENTIFIER]: TRANSFORMATION.casefold,
+ [COLUMN.NAME]: TRANSFORMATION.casefold,
+ [COLUMN.DESCRIPTION]: TRANSFORMATION.casefold,
+ [COLUMN.SERVER_ICON]: TRANSFORMATION.tokenData
}
/**
diff --git a/output/main.js b/output/main.js
index f6a341a..f41cacb 100644
--- a/output/main.js
+++ b/output/main.js
@@ -16,8 +16,7 @@
// Import magic numbers and data
import {
dom, COLUMN, COLUMN_LITERAL, COMPARISON, ATTRIBUTES,
- columnAscendingByDefault, columnIsSortable, columnNeedsCasefold,
- columnIsNumeric, element
+ columnAscendingByDefault, columnIsSortable, COLUMN_TRANSFORMATION, element
} from './js/constants.js';
// Hidden communities for transparency.
@@ -193,21 +192,12 @@ function makeRowComparer(column, ascending) {
}
// Callback to obtain sortable content from cell text.
- let contentToSortable = (text) => text.trim();
-
- if (columnNeedsCasefold(column)) {
- // Make certain columns sort regardless of casing.
- contentToSortable = (text) => text.toLowerCase().trim();
- }
- else if (columnIsNumeric(column)) {
- // Make certain columns sort on parsed numeric value instead of text.
- contentToSortable = (text) => parseInt(text);
- }
+ const columnToSortable = COLUMN_TRANSFORMATION[column] ?? ((el) => el.innerText.trim());
// Construct comparer using derived property to determine sort order.
const rowComparer = compareProp(
ascending ? compareAscending : compareDescending,
- row => contentToSortable(row.children[column].innerText)
+ row => columnToSortable(row.children[column])
);
return rowComparer;
diff --git a/output/styles2.css b/output/styles2.css
index 432dfee..f04fcb5 100644
--- a/output/styles2.css
+++ b/output/styles2.css
@@ -45,6 +45,27 @@ header {
.td_users { text-align: right; }
.td_preview { text-align: center; }
+.td_server_icon { text-align: center; }
+
+.td_server_icon-circle {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 2em;
+ height: 2em;
+ border-radius: 2em;
+ font-family: sans-serif;
+ margin: 0 auto;
+ color: white;
+ text-shadow: 0 0 0.5em #000;
+ box-shadow: 0 0 0.05em #777;
+}
+
+.td_server_icon-circle span {
+ position: relative;
+ top: 0.05em;
+}
+
.td_join_url {
font-family: monospace;
white-space: nowrap;
diff --git a/sites/+components/tbl_communities.php b/sites/+components/tbl_communities.php
index dbb5325..09d0f40 100644
--- a/sites/+components/tbl_communities.php
+++ b/sites/+components/tbl_communities.php
@@ -21,6 +21,7 @@
['id' => "users", 'name' => "Users"],
['id' => "preview", 'name' => "Preview"],
['id' => "qr", 'name' => "QR"],
+ ['id' => "server_icon", 'name' => "Server"],
['id' => "join_url", 'name' => "Join URL"],
];
?>
@@ -35,6 +36,22 @@
$room): ?>
+ join_link)[1];
+ $icon_hue = hexdec($token[2] . $token[2]);
+ $icon_color = "hsl($icon_hue, 80%, 50%)";
+
+ $hostname = explode("//", $room->join_link)[1];
+ $hostname = explode("/", $hostname)[0];
+ ?>
+
=$id?>
=$room->language?>
@@ -60,6 +77,14 @@
alt="Pictogram of a QR code"
>
+
diff --git a/sites/index.php b/sites/index.php
index 8d51d44..240e572 100644
--- a/sites/index.php
+++ b/sites/index.php
@@ -16,6 +16,11 @@
Self-updating list of active Session communities
+
@@ -65,8 +70,13 @@
only available with JS enabled.