Compare commits

..

No commits in common. "main" and "v0.0.5" have entirely different histories.
main ... v0.0.5

10 changed files with 194 additions and 190 deletions

View File

@ -1,10 +1,10 @@
# About appticles
A bunch of microapps (aka appticles) created for [minimo.io](https://minimo.io).
A bunch of mini-apps or appticles created for [minimo.io](https://minimo.io).
These microapps are used for adding extra functionality to blog posts and articles. They are develop primarily using the Svelte Framework or Alpine.js.
These micro-apps were embedded on blog posts for adding extra functionality. They were developed primarily using the Svelte Framework or Alpine.js. Most of them just as proof-of-concepts and for learning purposes.
Minimo.io itself [was built using Eleventy](https://github.com/minimo-io/minimo-11ty).
The site [minimo.io](https://github.com/minimo-io/minimo-11ty) itself was built using [Eleventy](https://github.com/11ty/eleventy).
## Get in touch.

View File

@ -3,18 +3,22 @@
Find out who does and who doesn't follow you back on Nostr.
Let promote some reciprocity here! 😹
> Check out the [demo](https://minimo.io/app/nostr-followback/).
## ToDo
- Create groups to checks to fire simultaneously instad of one by one (to remember: fireing all the `fetchProfile` at once for a given npub resulted in a permanent halt of the process -mainly for big users). Maybe using https://lodash.com/docs `_.chunk(array, [size=1])`
- Actually load builds at minimo.io/app/ or something alike.
- Ask community for help about the bug (see below) so I can keep understanding the protocol and the library.
- Polish the proof-of-concept code, making it TS code and remove `// @ts-nocheck`!
- Create interfaces or new types instead of loose variables
- Save followbackers and not followbackers in lists to see details.
- Create an UI/UX that's worth looking at.
- Close connection with relays after all is checked
- Show the relay list to be used
- Upload a localStorage list of relays & older results for folloback
- Upload a localStorage list of relays
- ~~Actually load builds at minimo.io/app/ or something alike.~~
- ~~Configure Vite for the miniapp to be loaded in the article's url as base.~~
- ~~Remove SvelteKit (just Vite + Svelte).~~
## BUG
- **When user follows lots of people, progress freezes:**<br>
[NDK](https://github.com/nostr-dev-kit/ndk) keeps trying to re-connect to relays, and progress slows down or halts completly. Maybe slow requests down? Are they blocking the requests because they are too many too fast?

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,7 @@
{
"name": "nostr-followback",
"private": true,
"version": "0.0.1",
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",

View File

@ -1,55 +1,52 @@
<script lang="ts">
import NDK, { type NDKUserProfile } from "@nostr-dev-kit/ndk";
import type { Querying, FollowListed } from "./lib/types";
// @ts-nocheck
// Import the package
import NDK from "@nostr-dev-kit/ndk";
import { fetchUserProfile } from "./lib/fetchs";
import { relays } from "./lib/data/relays";
import Form from "./lib/components/form.svelte";
let npubToQuery = "npub1wujhdsytm3w6g0mpsqh8v7ezx83jcm64dlkwuqgm5v8lv0pds55ssudkw0";
let querying: Querying = "uninitiated";
let userProfile: NDKUserProfile | null;
// let npubToQuery = "npub1wujhdsytm3w6g0mpsqh8v7ezx83jcm64dlkwuqgm5v8lv0pds55ssudkw0";
let npubToQuery = "";
let querying = false;
let userName = "";
let userThumb = "";
let followsCount = 0;
let followBackCount = 0;
let notFollowBackCount = 0;
let unknownFollowBack = 0;
let totalCountOfContactsChecked = 0;
$: progress = ((totalCountOfContactsChecked / followsCount) * 100) | 0;
$: notFollowBackPercentage = ((notFollowersBack.length / originalFollow.length) * 100) | 0;
$: progress = ((totalCountOfContactsChecked / followsCount) * 100).toFixed() | 0;
let originalFollow: FollowListed[] = [];
let notFollowersBack: string[] = [];
let originalFollow = [];
let notFollowersBack = [];
async function checkFollowBacks() {
try {
const ndk = new NDK({
explicitRelayUrls: relays,
});
querying = "processing";
await ndk.connect(6000);
await ndk.connect();
// create user instance
const user = ndk.getUser({ npub: npubToQuery });
// query for user profile
userProfile = await user.fetchProfile();
const user = await fetchUserProfile(npubToQuery, ndk);
if (userProfile) {
userName = user.profile.name;
userThumb = user.profile.image;
if (userName) {
// console.log(user.profile);
const follows = await user.follows({ groupable: false }, false);
// query for a set of user followers
const follows = await user.follows();
followsCount = follows.size;
let lastFollowerBack;
let i = 0;
for await (const follower of follows) {
follows.forEach(async (follower) => {
//for (const follower of follows) {
// await new Promise((resolve) => setTimeout(resolve, 0));
const followerFollowList = await follower.follows();
try {
originalFollow.push({ npub: follower.npub, followsBack: "-" }); // add to follower list
originalFollow = originalFollow;
@ -65,7 +62,7 @@
}
}
originalFollow[i].followsBack = doesFollowBack ? "1" : "0";
originalFollow[i].followBack = doesFollowBack ? "1" : "0";
// decision making time
if (doesFollowBack) {
@ -84,31 +81,28 @@
totalCountOfContactsChecked++;
}
i++;
});
// follows.forEach(async (follower) => {
// });
}
} catch (error) {
console.log(follower);
console.error(`Error fetching npub:`, error);
console.error("Error fetching data:", error);
}
}
}
querying = "completed";
} catch (error) {
console.error(`Error fetching data (${npubToQuery}):`, error);
}
}
function ItemCount(i: number): string {
let ret = i.toString();
if (i < 10) ret = `0${i}`;
return ret;
}
//checkFollowBacks();
</script>
<Form
{progress}
{querying}
bind:npubToQuery
<form class="npub-form">
<input disabled={querying && progress < 100} type="text" placeholder="An npub to check" bind:value={npubToQuery} />
<input
type="button"
on:click={async () => {
querying = "uninitiated";
userProfile = null;
querying = true;
userName = "";
userThumb = "";
originalFollow = [];
notFollowersBack = [];
followBackCount = 0;
@ -117,37 +111,48 @@
totalCountOfContactsChecked = 0;
await checkFollowBacks();
}}
/>
disabled={!npubToQuery && progress < 100}
value="Analyze"
/>
</form>
{#if userProfile}
<!-- {#if !querying}
{npubToQuery}
{/if} -->
{#if userThumb}
<div class="user-box">
<img src={userProfile.image} width="50" style="border-radius:100%;" alt="user-thumb" />
<img src={userThumb} width="50" style="border-radius:100%;" alt="user-thumb" />
User:
<a href="https://primal.net/p/{npubToQuery}">{userProfile.displayName}</a>
<a href="https://primal.net/p/{npubToQuery}">{userName}</a>
|  Follows: {followsCount}
<br />
Unknown: {unknownFollowBack} - Follow_Back: {followBackCount} -
Unknown: {unknownFollowBack} | Follow_Back: {followBackCount} |
<strong>👉 Not_Follow_Back</strong>
:
<!-- <span title="Actually Counted">{notFollowBackCount}</span>
/ -->
<span title="Actualy counted">{notFollowersBack.length}</span>
<br />
{#if progress < 100}
<p>
Progress = <strong>{progress}%</strong>
Progress =
<strong>{progress}%</strong>
- {totalCountOfContactsChecked} of {followsCount}
<!-- - {totalCountOfContactsChecked} of {followBackCount +
notFollowBackCount +
unknownFollowBack} -->
</p>
{:else}
<p><strong>✅ Completed!</strong></p>
<p><strong>Completed!</strong></p>
{/if}
<hr />
<br />
<strong>Not followers</strong>
: {notFollowersBack.length} of
{originalFollow.length} ({notFollowBackPercentage}%)
Results ({originalFollow.length})
<ul>
{#each originalFollow as item, i}
<li>
#{ItemCount(i + 1)} - {@html item.followsBack == "0" ? "<span>🔴</span>" : "<span>🟢</span>"}
#{i + 1} - {@html item.followBack == "0" ? "<span>🔴</span>" : "<span>🟢</span>"}
<a target="_blank noreferrer noopener" href="https://primal.net/p/{item.npub}">
{item.npub}
</a>
@ -155,9 +160,30 @@
</li>
{/each}
</ul>
<!-- <br />
<strong>They don't follow you ({notFollowersBack.length}):</strong>
<br />
<ul>
{#each notFollowersBack as item, i (item)}
<li>
#{i + 1} - <a href="https://nostr.band/{item}" target="_blank noreferrer noopener">Nostr.Band</a>
/ <a href="https://primal.net/p/{item}" target="_blank noreferrer noopener">Primal</a>
:
{item}
</li>
{/each}
</ul> -->
</div>
{:else if querying == "uninitiated"}
{:else if !querying}
<!-- <p>Let's find out who does not follow you back in Nostr!</p> -->
{:else}
<div class="loader">Loading data...</div>
{/if}
<style>
.npub-form input[type="text"] {
padding: 5px;
width: 50%;
}
</style>

View File

@ -1,30 +0,0 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import type { Querying } from "../types";
const dispatch = createEventDispatcher();
export let npubToQuery = "";
export let progress = 0;
export let querying: Querying;
export let analyzeFn = function () {
dispatch("click");
};
</script>
<form class="npub-form">
<input
type="text"
placeholder="npub to check"
bind:value={npubToQuery}
disabled={querying == "processing" && progress < 100}
/>
<input type="button" on:click={analyzeFn} value="Analyze" />
</form>
<style>
.npub-form input[type="text"] {
padding: 5px;
width: 50%;
}
</style>

View File

@ -9,11 +9,11 @@ export const relays = [
"wss://nostr.mom",
"wss://nostrelay.yeghro.site",
"wss://relay.damus.io",
// "wss://relay.nostr.bg",
"wss://relay.nostr.bg",
"wss://relay.snort.social",
"wss://relay.primal.net",
"wss://nostr.bitcoiner.social",
"wss://nostr.mutinywallet.com",
"wss://relay.current.fyi",
// "wss://brb.io",
"wss://brb.io",
];

View File

@ -0,0 +1,9 @@
export async function fetchUserProfile(npub, ndk) {
const user = ndk.getUser({ npub });
await user.fetchProfile();
return user;
}
export async function fetchNotes(hexkey, ndk) {
const kind1filter = { kinds: [3], authors: [hexkey] };
return ndk.fetchEvent(kind1filter);
}

View File

@ -1,6 +0,0 @@
export type Querying = "completed" | "processing" | "uninitiated";
export type FollowListed = {
npub: string;
followsBack: string;
};