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 += --no-builtin-rules
|
||||
|
||||
## [36;1m== Tilde Friends makefile build. ==[m
|
||||
## == Tilde Friends build. ==
|
||||
##
|
||||
## This is a list of all supported build targets.
|
||||
##
|
||||
## Note: Consider passing -j$(nproc) or adding it to your $MAKEFLAGS to build
|
||||
## in parallel (faster).
|
||||
## Consider passing -j$(nproc) or adding it to your $MAKEFLAGS to build in
|
||||
## parallel (faster).
|
||||
##
|
||||
## Useful variables to override:
|
||||
## [35mCC[m Compiler.
|
||||
## [35mAS[m Assembler.
|
||||
## [35mLD[m Linker.
|
||||
## [35mANDROID_SDK[m Path to the Android SDK.
|
||||
## CC := Compiler.
|
||||
## AS := Assembler.
|
||||
## LD := Linker.
|
||||
## ANDROID_SDK := Path to the Android SDK.
|
||||
|
||||
VERSION_CODE := 30
|
||||
VERSION_NUMBER := 0.0.25-wip
|
||||
VERSION_NUMBER := 0.0.25
|
||||
VERSION_NAME := This program kills fascists.
|
||||
|
||||
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:
|
||||
##
|
||||
help: ## Display this help message.
|
||||
@gawk -vG=$$(tput setaf 2) -vR=$$(tput sgr0) ' \
|
||||
match($$0, "^(([^#:]*[^ :]) *:)?([^#]*)##([^#].+|)$$",a) { \
|
||||
if (a[2] != "") { printf " make %s%-22s%s %s\n", G, a[2], R, a[4]; next }\
|
||||
if (a[3] == "") { print a[4]; next }\
|
||||
printf "\n%-36s %s\n","",a[4]\
|
||||
}' $(filter-out %.d,$(MAKEFILE_LIST))
|
||||
@awk \
|
||||
-F: \
|
||||
-vG=$$(tput setaf 2) \
|
||||
-vO=$$(tput setaf 3) \
|
||||
-vB=$$(tput setaf 4) \
|
||||
-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.
|
||||
.PHONY: 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>
|
||||
<span style="padding-right: 8px"
|
||||
><a tfarget="_top" href=${'#' + encodeURIComponent(self.message.id)}>%</a> ${new Date(
|
||||
self.message.timestamp
|
||||
).toLocaleString()}</span
|
||||
><a tfarget="_top" href=${'#' + encodeURIComponent(self.message.id)}
|
||||
>%</a
|
||||
>
|
||||
${new Date(self.message.timestamp).toLocaleString()}</span
|
||||
>
|
||||
${raw_button} ${self.format == 'raw' ? self.render_raw() : inner}
|
||||
${self.render_votes()}
|
||||
@ -449,7 +450,9 @@ ${JSON.stringify(mention, null, 2)}</pre
|
||||
class="w3-card-4 w3-theme-d4 w3-border-theme"
|
||||
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)
|
||||
<div>${this.render_votes()}</div>
|
||||
${(this.message.child_messages || []).map(
|
||||
@ -600,7 +603,11 @@ ${JSON.stringify(content, null, 2)}</pre
|
||||
${is_encrypted}
|
||||
<span style="flex: 1"></span>
|
||||
<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
|
||||
>
|
||||
<span>${raw_button}</span>
|
||||
@ -643,7 +650,11 @@ ${JSON.stringify(content, null, 2)}</pre
|
||||
${is_encrypted}
|
||||
<span style="flex: 1"></span>
|
||||
<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
|
||||
>
|
||||
<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>
|
||||
<span style="flex: 1"></span>
|
||||
<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
|
||||
>
|
||||
<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"
|
||||
package="com.unprompted.tildefriends"
|
||||
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.INTERNET"/>
|
||||
<application
|
||||
|
@ -874,9 +874,11 @@ static void _test_httpd(const tf_test_options_t* options)
|
||||
uv_sleep(1000);
|
||||
}
|
||||
|
||||
#if !defined(__HAIKU__)
|
||||
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");
|
||||
tf_ssb_destroy(ssb);
|
||||
#endif
|
||||
|
||||
_http_check_status_code("http://localhost:8080/404", 404);
|
||||
_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_body_contains("http://localhost:8080/&MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=.sha256/view", "Hello, world!");
|
||||
|
||||
#if !defined(__HAIKU__)
|
||||
char url[1024];
|
||||
snprintf(url, sizeof(url), "http://localhost:8080/%s/", app_id);
|
||||
_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);
|
||||
_http_check_body_contains(url, "Hello, world!");
|
||||
tf_free((void*)app_id);
|
||||
#endif
|
||||
|
||||
uv_process_kill(&process, SIGTERM);
|
||||
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."
|
||||
|
Reference in New Issue
Block a user