Compare commits
11 Commits
595f14d98d
...
v0.0.25
Author | SHA1 | Date | |
---|---|---|---|
cc409dc3f7 | |||
af6091760c | |||
e1d93c003c | |||
ff9dd2dd03 | |||
7a306bb3d2 | |||
7ffc148358 | |||
50fef2edfa | |||
aa40084010 | |||
740d788c7c | |||
4c2fa2c1b3 | |||
4350c7b7a9 |
38
GNUmakefile
38
GNUmakefile
@ -3,21 +3,21 @@
|
|||||||
MAKEFLAGS += --warn-undefined-variables
|
MAKEFLAGS += --warn-undefined-variables
|
||||||
MAKEFLAGS += --no-builtin-rules
|
MAKEFLAGS += --no-builtin-rules
|
||||||
|
|
||||||
## [36;1m== Tilde Friends makefile build. ==[m
|
## == 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:
|
||||||
## [35mCC[m Compiler.
|
## CC := Compiler.
|
||||||
## [35mAS[m Assembler.
|
## AS := Assembler.
|
||||||
## [35mLD[m Linker.
|
## LD := Linker.
|
||||||
## [35mANDROID_SDK[m 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
|
||||||
|
@ -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
5
apps/storage.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"type": "tildefriends-app",
|
||||||
|
"emoji": "💾",
|
||||||
|
"previous": "&mvGTlWKFR5QM/3nb4fJ2WQq0n/gNKvBmhGDkAvb8ki8=.sha256"
|
||||||
|
}
|
127
apps/storage/app.js
Normal file
127
apps/storage/app.js
Normal 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
2
docs
Submodule docs updated: d14201ddcc...a40758cc4b
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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."
|
||||||
|
Reference in New Issue
Block a user