11 Commits

Author SHA1 Message Date
cc409dc3f7 build: Let's build 0.0.25.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 16m0s
2024-11-27 12:10:17 -05:00
af6091760c ssb+docs: prettier.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-11-27 12:07:00 -05:00
e1d93c003c docs: Update docs from wiki.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 16m5s
2024-11-27 10:13:16 -05:00
ff9dd2dd03 haiku: Disable a bit of a test that is giving me an SQLITE_PROTOCOL error only on Haiku.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-11-27 15:05:23 -05:00
7a306bb3d2 build: Fix a regex warning.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 16m11s
2024-11-27 14:36:50 -05:00
7ffc148358 build: I wanted to get the binary out of the makefile to appease F-Droid, and one thing lead to another.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-11-27 09:28:14 -05:00
50fef2edfa build: Fix on OpenBSD. TIL awk.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 15m11s
2024-11-27 09:06:02 -05:00
aa40084010 build: Redid this thing in sed to make it work on more platforms.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 15m16s
2024-11-26 22:55:01 -05:00
740d788c7c storage: Show accounts with the most follows, for help pruning accounts.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 16m5s
2024-11-26 16:25:15 -05:00
4c2fa2c1b3 storage: Show totals, too.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 16m26s
2024-11-26 16:05:28 -05:00
4350c7b7a9 storage: Add a little app to show something about feed sizes. 2024-11-26 15:59:02 -05:00
9 changed files with 188 additions and 27 deletions

View File

@ -3,21 +3,21 @@
MAKEFLAGS += --warn-undefined-variables MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules MAKEFLAGS += --no-builtin-rules
## == Tilde Friends makefile build. == ## == Tilde Friends build. ==
## ##
## This is a list of all supported build targets. ## This is a list of all supported build targets.
## ##
## Note: Consider passing -j$(nproc) or adding it to your $MAKEFLAGS to build ## Consider passing -j$(nproc) or adding it to your $MAKEFLAGS to build in
## in parallel (faster). ## parallel (faster).
## ##
## Useful variables to override: ## Useful variables to override:
## CC Compiler. ## CC := Compiler.
## AS Assembler. ## AS := Assembler.
## LD Linker. ## LD := Linker.
## ANDROID_SDK Path to the Android SDK. ## ANDROID_SDK := Path to the Android SDK.
VERSION_CODE := 30 VERSION_CODE := 30
VERSION_NUMBER := 0.0.25-wip VERSION_NUMBER := 0.0.25
VERSION_NAME := This program kills fascists. VERSION_NAME := This program kills fascists.
SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3470100.zip SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3470100.zip
@ -1245,12 +1245,22 @@ clean: ## Clean all generated files from the out/ directory.
## Documentation: ## Documentation:
## ##
help: ## Display this help message. help: ## Display this help message.
@gawk -vG=$$(tput setaf 2) -vR=$$(tput sgr0) ' \ @awk \
match($$0, "^(([^#:]*[^ :]) *:)?([^#]*)##([^#].+|)$$",a) { \ -F: \
if (a[2] != "") { printf " make %s%-22s%s %s\n", G, a[2], R, a[4]; next }\ -vG=$$(tput setaf 2) \
if (a[3] == "") { print a[4]; next }\ -vO=$$(tput setaf 3) \
printf "\n%-36s %s\n","",a[4]\ -vB=$$(tput setaf 4) \
}' $(filter-out %.d,$(MAKEFILE_LIST)) -vM=$$(tput setaf 5) \
-vC=$$(tput setaf 6) \
-vR=$$(tput sgr0) ' \
/^## ==.*==$$/ { sub(/^## ?/, ""); printf "%s%s%s\n", C, $$0, R } \
/^##.*:=.*/ { sub(/^## ?/, ""); sub(/:=/, ":"); printf " %s%-20s%s %s%s%s\n", M, $$1, R, O, $$2, R } \
/^##/ { sub(/^## ?/, ""); print $$0 } \
/^[[:alnum:]-]+:.*##/ { \
sub(/:.*##\s?/, ":"); \
printf " %s%-20s%s %s%s%s\n", G, $$1, R, O, $$2, R \
} \
' < $(filter-out %.d,$(MAKEFILE_LIST))
@echo "" # Blank line. @echo "" # Blank line.
.PHONY: help .PHONY: help
.DEFAULT_GOAL := help .DEFAULT_GOAL := help

View File

@ -34,7 +34,7 @@ The `.tar.xz` source releases are all-inclusive.
## Building ## Building
Builds on Linux (x86_64 and aarch64), MacOS, OpenBSD, and Haiku. It's possible Builds on Linux (x86_64 and aarch64), MacOS, OpenBSD, and Haiku. It's possible
to build for Android, iOS, and Windows on Linux, if you have the right to build for Android, iOS, and Windows on Linux, if you have the right
dependencies in the right places. dependencies in the right places.
@ -47,7 +47,7 @@ On MacOS, Xcode's command-line tools are expected to be available.
### Build Commands ### Build Commands
Run `make` with no arguments to see available build targets and options. `make Run `make` with no arguments to see available build targets and options. `make
debug` is a good place to start. debug` is a good place to start.
To build in docker, `docker build .`. To build in docker, `docker build .`.
@ -58,7 +58,7 @@ standard.
## Running ## Running
By default, running the built `out/debug/tildefriends` executable will start a By default, running the built `out/debug/tildefriends` executable will start a
web server at <http://localhost:12345/>. It expects to be run with the web server at <http://localhost:12345/>. It expects to be run with the
repository root as the current working directory. `tildefriends -h` lists repository root as the current working directory. `tildefriends -h` lists
further options. further options.

View File

@ -408,9 +408,10 @@ ${JSON.stringify(mention, null, 2)}</pre
> >
<tf-user id=${self.message.author} .users=${self.users}></tf-user> <tf-user id=${self.message.author} .users=${self.users}></tf-user>
<span style="padding-right: 8px" <span style="padding-right: 8px"
><a tfarget="_top" href=${'#' + encodeURIComponent(self.message.id)}>%</a> ${new Date( ><a tfarget="_top" href=${'#' + encodeURIComponent(self.message.id)}
self.message.timestamp >%</a
).toLocaleString()}</span >
${new Date(self.message.timestamp).toLocaleString()}</span
> >
${raw_button} ${self.format == 'raw' ? self.render_raw() : inner} ${raw_button} ${self.format == 'raw' ? self.render_raw() : inner}
${self.render_votes()} ${self.render_votes()}
@ -449,7 +450,9 @@ ${JSON.stringify(mention, null, 2)}</pre
class="w3-card-4 w3-theme-d4 w3-border-theme" class="w3-card-4 w3-theme-d4 w3-border-theme"
style="margin-top: 8px; padding: 16px; overflow-wrap: anywhere" style="margin-top: 8px; padding: 16px; overflow-wrap: anywhere"
> >
<a target="_top" href=${'#' + encodeURIComponent(this.message.id)}>${this.message.id}</a> <a target="_top" href=${'#' + encodeURIComponent(this.message.id)}
>${this.message.id}</a
>
(placeholder) (placeholder)
<div>${this.render_votes()}</div> <div>${this.render_votes()}</div>
${(this.message.child_messages || []).map( ${(this.message.child_messages || []).map(
@ -600,7 +603,11 @@ ${JSON.stringify(content, null, 2)}</pre
${is_encrypted} ${is_encrypted}
<span style="flex: 1"></span> <span style="flex: 1"></span>
<span style="padding-right: 8px" <span style="padding-right: 8px"
><a target="_top" href=${'#' + encodeURIComponent(self.message.id)}>%</a> ><a
target="_top"
href=${'#' + encodeURIComponent(self.message.id)}
>%</a
>
${new Date(this.message.timestamp).toLocaleString()}</span ${new Date(this.message.timestamp).toLocaleString()}</span
> >
<span>${raw_button}</span> <span>${raw_button}</span>
@ -643,7 +650,11 @@ ${JSON.stringify(content, null, 2)}</pre
${is_encrypted} ${is_encrypted}
<span style="flex: 1"></span> <span style="flex: 1"></span>
<span style="padding-right: 8px" <span style="padding-right: 8px"
><a target="_top" href=${'#' + encodeURIComponent(self.message.id)}>%</a> ><a
target="_top"
href=${'#' + encodeURIComponent(self.message.id)}
>%</a
>
${new Date(this.message.timestamp).toLocaleString()}</span ${new Date(this.message.timestamp).toLocaleString()}</span
> >
<span>${raw_button}</span> <span>${raw_button}</span>
@ -733,7 +744,11 @@ ${JSON.stringify(content, null, 2)}</pre
<tf-user id=${this.message.author} .users=${this.users}></tf-user> <tf-user id=${this.message.author} .users=${this.users}></tf-user>
<span style="flex: 1"></span> <span style="flex: 1"></span>
<span style="padding-right: 8px" <span style="padding-right: 8px"
><a target="_top" href=${'#' + encodeURIComponent(self.message.id)}>%</a> ><a
target="_top"
href=${'#' + encodeURIComponent(self.message.id)}
>%</a
>
${new Date(this.message.timestamp).toLocaleString()}</span ${new Date(this.message.timestamp).toLocaleString()}</span
> >
<span>${raw_button}</span> <span>${raw_button}</span>

5
apps/storage.json Normal file
View File

@ -0,0 +1,5 @@
{
"type": "tildefriends-app",
"emoji": "💾",
"previous": "&mvGTlWKFR5QM/3nb4fJ2WQq0n/gNKvBmhGDkAvb8ki8=.sha256"
}

127
apps/storage/app.js Normal file
View File

@ -0,0 +1,127 @@
async function query(sql, args) {
let rows = [];
await ssb.sqlAsync(sql, args ?? [], function (row) {
rows.push(row);
});
return rows;
}
async function get_biggest() {
return query(`
select author, sum(length(content)) as size from messages group by author order by size desc limit 10;
`);
}
async function get_total() {
return (
await query(`
select sum(length(content)) as size, count(distinct author) as count from messages;
`)
)[0];
}
async function get_names(identities) {
return query(
`
SELECT author, name FROM (
SELECT
messages.author,
RANK() OVER (PARTITION BY messages.author ORDER BY messages.sequence DESC) AS author_rank,
messages.content ->> 'name' AS name
FROM messages
JOIN json_each(?) AS identities ON identities.value = messages.author
WHERE
json_extract(messages.content, '$.type') = 'about' AND
content ->> 'about' = messages.author AND name IS NOT NULL)
WHERE author_rank = 1
`,
[JSON.stringify(identities)]
);
}
async function get_most_follows() {
return query(`
select author, count(*) as count
from messages
where content ->> 'type' = 'contact' and content ->> 'following' = true
group by author
order by count desc
limit 10;
`);
}
function nice_size(bytes) {
let value = bytes;
let index = 0;
let units = ['B', 'kB', 'MB', 'GB'];
while (value > 1024 && index < units.length - 1) {
value /= 1024;
index++;
}
return `${Math.round(value * 10) / 10} ${units[index]}`;
}
async function main() {
await app.setDocument(
'<p style="color: #fff">Finding the top 10 largest feeds...</p>'
);
let most_follows = await get_most_follows();
let total = await get_total();
let identities = await ssb.getAllIdentities();
let following1 = await ssb.following(identities, 1);
let following2 = await ssb.following(identities, 2);
let biggest = await get_biggest();
let names = await get_names(
[].concat(
biggest.map((x) => x.author),
most_follows.map((x) => x.author)
)
);
names = Object.fromEntries(names.map((x) => [x.author, x.name]));
for (let item of biggest) {
item.name = names[item.author];
item.following =
identities.indexOf(item.author) != -1
? 0
: following1[item.author] !== undefined
? 1
: following2[item.author] !== undefined
? 2
: undefined;
}
for (let item of most_follows) {
item.name = names[item.author];
}
let html = `<body style="color: #000; background-color: #ddd">\n
<h1>Storage Summary</h1>
<h2>Top 10 Accounts by Size</h2>
<ol>`;
for (let item of biggest) {
html += `<li>
<span style="color: #888">${nice_size(item.size)}</span>
<a target="_top" href="/~core/ssb/#${encodeURI(item.author)}">${item.name ?? item.author}</a>
</li>
\n`;
}
html += `
</ol>
<h2>Top 10 Accounts by Follows</h2>
<ol>`;
for (let item of most_follows) {
html += `<li>
<span style="color: #888">${item.count}</span>
${following2[item.author] ? '✅' : '🚫'}
<a target="_top" href="/~core/ssb/#${encodeURI(item.author)}">${item.name ?? item.author}</a>
</li>
\n`;
}
html += `
</ol>
<p>Total <span style="color: #888">${nice_size(total.size)}</span> in ${total.count} accounts.</p>
`;
await app.setDocument(html);
}
main().catch(function (e) {
print(e);
});

2
docs

Submodule docs updated: d14201ddcc...a40758cc4b

View File

@ -2,7 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unprompted.tildefriends" package="com.unprompted.tildefriends"
android:versionCode="30" android:versionCode="30"
android:versionName="0.0.25-wip"> android:versionName="0.0.25">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<application <application

View File

@ -874,9 +874,11 @@ static void _test_httpd(const tf_test_options_t* options)
uv_sleep(1000); uv_sleep(1000);
} }
#if !defined(__HAIKU__)
tf_ssb_t* ssb = tf_ssb_create(&loop, NULL, "file:out/test_db0.sqlite", NULL); tf_ssb_t* ssb = tf_ssb_create(&loop, NULL, "file:out/test_db0.sqlite", NULL);
const char* app_id = tf_ssb_db_get_property(ssb, "core", "path:test"); const char* app_id = tf_ssb_db_get_property(ssb, "core", "path:test");
tf_ssb_destroy(ssb); tf_ssb_destroy(ssb);
#endif
_http_check_status_code("http://localhost:8080/404", 404); _http_check_status_code("http://localhost:8080/404", 404);
_http_check_status_code("http://localhost:8080/", 303); _http_check_status_code("http://localhost:8080/", 303);
@ -889,6 +891,7 @@ static void _test_httpd(const tf_test_options_t* options)
_http_check_status_code("http://localhost:8080/~core/test/nonexistent.txt", 404); _http_check_status_code("http://localhost:8080/~core/test/nonexistent.txt", 404);
_http_check_body_contains("http://localhost:8080/&MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=.sha256/view", "Hello, world!"); _http_check_body_contains("http://localhost:8080/&MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=.sha256/view", "Hello, world!");
#if !defined(__HAIKU__)
char url[1024]; char url[1024];
snprintf(url, sizeof(url), "http://localhost:8080/%s/", app_id); snprintf(url, sizeof(url), "http://localhost:8080/%s/", app_id);
_http_check_body_contains(url, "<title>Tilde Friends</title>"); _http_check_body_contains(url, "<title>Tilde Friends</title>");
@ -897,6 +900,7 @@ static void _test_httpd(const tf_test_options_t* options)
snprintf(url, sizeof(url), "http://localhost:8080/%s/hello.txt", app_id); snprintf(url, sizeof(url), "http://localhost:8080/%s/hello.txt", app_id);
_http_check_body_contains(url, "Hello, world!"); _http_check_body_contains(url, "Hello, world!");
tf_free((void*)app_id); tf_free((void*)app_id);
#endif
uv_process_kill(&process, SIGTERM); uv_process_kill(&process, SIGTERM);
uv_close((uv_handle_t*)&process, NULL); uv_close((uv_handle_t*)&process, NULL);

View File

@ -1,2 +1,2 @@
#define VERSION_NUMBER "0.0.25-wip" #define VERSION_NUMBER "0.0.25"
#define VERSION_NAME "This program kills fascists." #define VERSION_NAME "This program kills fascists."