Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

⚡ Manage nostr-bot certify with lightning address via phoenixd getln… #1583

Open
wants to merge 41 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
4e7fd7c
handle zap request
ithiame Sep 10, 2024
beca9f5
handle zap request
ithiame Sep 10, 2024
71fc935
handle zap request
ithiame Sep 10, 2024
2b79d39
display zap on /admin/nostr
ithiame Oct 1, 2024
d12ad40
Merge branch 'main' of https://github.com/B2Bitcoin/B2BitcoinBootik i…
ithiame Oct 1, 2024
8ddb81e
Bolt 12 address phoenixD
ithiame Nov 4, 2024
2b57198
Bolt 12 address phoenixD
ithiame Nov 4, 2024
af88021
lightning payment qr code clickable
ithiame Nov 4, 2024
c77e0ab
⚡ Manage nostr-bot certify with lightning address via phoenixd getln…
ithiame Nov 5, 2024
418648c
test branch
ithiame Nov 6, 2024
6fd12ad
Merge branch 'lightning-qr-code-clickable' of https://github.com/B2Bi…
ithiame Nov 6, 2024
37ea850
update
ithiame Nov 6, 2024
406fa8b
update
ithiame Nov 6, 2024
8111c87
update
ithiame Nov 6, 2024
9138b5c
update
ithiame Nov 8, 2024
0d5eb4f
update
ithiame Nov 8, 2024
df15c01
update
ithiame Nov 8, 2024
fe0d8da
update
ithiame Nov 8, 2024
9d358dd
update
ithiame Nov 8, 2024
ae889fb
update
ithiame Nov 8, 2024
52e2e2e
update
ithiame Nov 8, 2024
d1ec38b
Merge branch 'nostr-zap' of https://github.com/B2Bitcoin/beBOP into t…
ithiame Nov 8, 2024
f6ca63c
update
ithiame Nov 8, 2024
aa5cea6
update
ithiame Nov 8, 2024
2f1ac66
update
ithiame Nov 8, 2024
5042347
update
ithiame Nov 8, 2024
8032344
update
ithiame Nov 8, 2024
4cba907
update
ithiame Nov 11, 2024
ddee47e
fix phoenixd bolt12 : save value in phoenixd runtimeConfig
ithiame Nov 18, 2024
e5a0e6b
fix suggested changes
ithiame Nov 18, 2024
0f0cd6c
fix suggested changes
ithiame Nov 18, 2024
1dc8c6b
fix suggested changes7
ithiame Nov 18, 2024
2a981e0
fix suggested changes7
ithiame Nov 18, 2024
a93522f
fix suggested changes7
ithiame Nov 19, 2024
cb78ca3
Merge branch 'main' into manage-nostr-bot-certify
Tirodem Nov 30, 2024
b865d4e
update
ithiame Dec 2, 2024
d54c514
update
ithiame Dec 2, 2024
892333e
Merge branch 'main' of https://github.com/B2Bitcoin/beBOP into test-b…
ithiame Dec 2, 2024
5281a9c
merge
ithiame Dec 2, 2024
07d1f7e
merge
ithiame Dec 2, 2024
b958236
fix conflict
ithiame Dec 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/lib/components/CmsDesign.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@
: ''}{token.height ? `height: ${token.height}px;` : ''}"
/>
<PictureComponent picture={pictureById[token.slug]} class="my-5 lg:hidden block" />
{:else if token.type === 'qrCode'}
{#if token.slug === 'Bolt12'}
<a href="lightning:{$page.data.bolt12Address}">
<img src="{$page.url.origin}/phoenixd/bolt12/qrcode" class="w-96 h-96" alt="QR code" />
</a>
{/if}
{:else if token.type === 'currencyCalculatorWidget'}
<CurrencyCalculator />
{:else if token.type === 'html'}
Expand Down Expand Up @@ -258,6 +264,16 @@
/>
{:else if token.type === 'pictureWidget'}
<PictureComponent picture={pictureById[token.slug]} class="my-5" />
{:else if token.type === 'qrCode'}
{#if token.slug === 'Bolt12'}
<a href="lightning:{$page.data.bolt12Address}">
<img
src="{$page.url.origin}/phoenixd/bolt12/qrcode"
class="w-96 h-96"
alt="QR code"
/>
</a>
{/if}
{:else if token.type === 'currencyCalculatorWidget'}
<CurrencyCalculator />
{:else if token.type === 'html'}
Expand Down
12 changes: 12 additions & 0 deletions src/lib/server/cms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ type TokenObject =
display: string | undefined;
raw: string;
}
| { type: 'qrCode'; slug: string; raw: string }
| { type: 'currencyCalculatorWidget'; slug: string; raw: string };

export async function cmsFromContent(
Expand All @@ -100,6 +101,7 @@ export async function cmsFromContent(
/\[TagProducts=(?<slug>[\p{L}\d_-]+)(?:[?\s]display=(?<display>[a-z0-9-]+))?\]/giu;
const GALLERY_WIDGET_REGEX =
/\[Gallery=(?<slug>[\p{L}\d_-]+)(?:[?\s]display=(?<display>[a-z0-9-]+))?\]/giu;
const QRCODE_REGEX = /\[QRCode=(?<slug>[\p{L}\d_-]+)\]/giu;
const CURRENCY_CALCULATOR_WIDGET_REGEX = /\[CurrencyCalculator=(?<slug>[a-z0-9-]+)\]/giu;

const productSlugs = new Set<string>();
Expand All @@ -112,6 +114,7 @@ export async function cmsFromContent(
const countdownFormSlugs = new Set<string>();
const tagProductsSlugs = new Set<string>();
const gallerySlugs = new Set<string>();
const qrCodeSlugs = new Set<string>();
const currencyCalculatorSlugs = new Set<string>();

const tokens: {
Expand Down Expand Up @@ -143,6 +146,7 @@ export async function cmsFromContent(
...matchAndSort(content, COUNTDOWN_WIDGET_REGEX, 'countdownWidget'),
...matchAndSort(content, TAG_PRODUCTS_REGEX, 'tagProducts'),
...matchAndSort(content, GALLERY_WIDGET_REGEX, 'galleryWidget'),
...matchAndSort(content, QRCODE_REGEX, 'qrCode'),
...matchAndSort(content, CURRENCY_CALCULATOR_WIDGET_REGEX, 'currencyCalculatorWidget')
].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
for (const match of matches) {
Expand Down Expand Up @@ -250,6 +254,14 @@ export async function cmsFromContent(
raw: match[0]
});
break;
case 'qrCode':
qrCodeSlugs.add(match.groups.slug);
token.push({
type: 'qrCode',
slug: match.groups.slug,
raw: match[0]
});
break;
case 'currencyCalculatorWidget':
currencyCalculatorSlugs.add(match.groups.slug);
token.push({
Expand Down
32 changes: 30 additions & 2 deletions src/lib/server/locks/nostr-notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ function initRelayPool() {
[
// Messages sent to us
{
kinds: [Kind.EncryptedDirectMessage, Kind.Text],
kinds: [Kind.EncryptedDirectMessage, Kind.Text, Kind.Zap],
'#p': [nostrPublicKeyHex]
}
],
Expand All @@ -109,7 +109,7 @@ function initRelayPool() {
if (!event.tags.some((tag) => tag[0] === 'p' && tag[1] === nostrPublicKeyHex)) {
return;
}
if (![Kind.EncryptedDirectMessage, Kind.Text].includes(event.kind)) {
if (![Kind.EncryptedDirectMessage, Kind.Text, Kind.Zap].includes(event.kind)) {
return;
}

Expand Down Expand Up @@ -222,6 +222,34 @@ async function handleNostrNotification(nostrNotification: NostRNotification): Pr
sig: ''
} satisfies Event;
}

if (nostrNotification.kind === Kind.Zap) {
const npub = nostrNotification.dest;

if (!npub) {
return;
}

const receiverPublicKeyHex = nostrToHex(npub);

return {
id: '',
content: await nip04.encrypt(nostrPrivateKeyHex, receiverPublicKeyHex, content),
created_at: getUnixTime(
max([
nostrNotification.minCreatedAt ?? nostrNotification.createdAt,
nostrNotification.createdAt
])
),
pubkey: nostrPublicKeyHex,
tags: [
['p', receiverPublicKeyHex],
['bootikVersion', String(NOSTR_PROTOCOL_VERSION)]
],
kind: Kind.Zap,
sig: ''
} satisfies Event;
}
})();

if (!event) {
Expand Down
4 changes: 2 additions & 2 deletions src/lib/server/orders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1073,8 +1073,8 @@ async function generatePaymentInfo(params: {
const invoice = await phoenixdCreateInvoice(satoshis, label, params.orderId);

return {
address: invoice.paymentAddress,
invoiceId: invoice.paymentHash,
address: invoice.payment_address,
invoiceId: invoice.r_hash,
processor: 'phoenixd'
};
} else {
Expand Down
38 changes: 35 additions & 3 deletions src/lib/server/phoenixd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,37 @@ export async function phoenixdBalance(): Promise<{ balanceSat: number; feeCredit
return await res.json();
}

export async function phoenixdGetBolt12(): Promise<string> {
const res = await fetch(`${runtimeConfig.phoenixd.url}/getoffer`, {
headers: {
Authorization: `Basic ${Buffer.from(`:${runtimeConfig.phoenixd.password}`).toString(
'base64'
)}`
}
});

if (!res.ok) {
throw error(500, `Error fetching Bolt12 offer: ${res.status} ${res.statusText}`);
}

return await res.text();
}

export async function phoenixdLnAddress(): Promise<string> {
const res = await fetch(`${runtimeConfig.phoenixd.url}/getlnaddress`, {
headers: {
Authorization: `Basic ${Buffer.from(`:${runtimeConfig.phoenixd.password}`).toString(
'base64'
)}`
}
});
if (!res.ok) {
throw error(500, `Could not get lnaddress: ${res.status} ${await res.text()}`);
}

return await res.text();
}

export async function phoenixdDetected(url?: string): Promise<boolean> {
return await Promise.race<boolean>([
fetch(`${url || runtimeConfig.phoenixd.url}/getinfo`).then(
Expand All @@ -48,7 +79,7 @@ export async function phoenixdCreateInvoice(
satoshis: number,
description: string,
externalId: string
): Promise<{ paymentHash: string; paymentAddress: string }> {
): Promise<{ payment_request: string; r_hash: string; payment_address: string }> {
const res = await fetch(`${runtimeConfig.phoenixd.url}/createinvoice`, {
method: 'POST',
headers: {
Expand Down Expand Up @@ -78,8 +109,9 @@ export async function phoenixdCreateInvoice(
.parse(await res.json());

return {
paymentHash: json.paymentHash,
paymentAddress: json.serialized
payment_request: json.serialized,
r_hash: json.paymentHash,
payment_address: json.serialized
};
}

Expand Down
4 changes: 3 additions & 1 deletion src/lib/server/runtime-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ const baseConfig = {
phoenixd: {
url: 'http://localhost:9740',
enabled: false,
password: ''
password: '',
lnAddress: '',
bolt12Address: ''
},
productActionSettings: {
eShop: {
Expand Down
12 changes: 12 additions & 0 deletions src/lib/utils/lightningPaymentQr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { Currency } from '$lib/types/Currency';
import { toBitcoins } from './toBitcoins';

export function lightningPaymentQrCodeString(
paymentAddress: string,
paymentAmount: number,
paymentCurrency: Currency
) {
return `lightning:${paymentAddress}?amount=${toBitcoins(paymentAmount, paymentCurrency)
.toLocaleString('en-US', { maximumFractionDigits: 8 })
.replaceAll(',', '')}`;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { setTimeout } from 'node:timers/promises';
import type { Event } from 'nostr-tools';
import { uniqBy } from '$lib/utils/uniqBy';
import { NOSTR_PROTOCOL_VERSION } from '$lib/server/locks/handle-messages';
import { isPhoenixdConfigured, phoenixdLnAddress } from '$lib/server/phoenixd';

export function load() {
return {
Expand Down Expand Up @@ -47,7 +48,9 @@ export const actions = {
: null;

const lndInfo = isLightningConfigured ? await lndGetInfo() : null;
const lnAddress = lndInfo?.uris?.[0];
const lnAddress =
lndInfo?.uris?.[0] ??
(isPhoenixdConfigured() ? await phoenixdLnAddress().catch(() => null) : null);

await collections.nostrNotifications.insertOne({
_id: new ObjectId(),
Expand Down
13 changes: 13 additions & 0 deletions src/routes/(app)/admin[[hash=admin_hash]]/nostr/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,19 @@
</label>
<button class="btn btn-black self-start" type="submit">Send</button>
</form>
<h2 class="text-2xl">Zaps</h2>

<ul>
{#each data.receivedMessages.filter((mes) => mes.kind === 9735) as message}
<li class="break-words">
{#if message.kind === 4}
<span title="Encrypted message">'⚡'</span>
{/if}
<time datetime={message.createdAt.toJSON()}>{message.createdAt.toLocaleString('en-UK')}</time>
| {JSON.stringify(message)}
</li>
{/each}
</ul>
<h2 class="text-2xl">Received messages</h2>

<ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import {
isPhoenixdConfigured,
phoenixdBalance,
phoenixdDetected,
phoenixdGetBolt12,
phoenixdInfo,
phoenixdLnAddress,
phoenixdPayInvoice,
phoenixdSendOnChain
} from '$lib/server/phoenixd.js';
Expand All @@ -29,17 +31,19 @@ export const load = async () => {
try {
const nodeInfo = await phoenixdInfo();
const balance = await phoenixdBalance();

const bolt12Address = await phoenixdGetBolt12();
return {
phoenixd: runtimeConfig.phoenixd,
nodeInfo,
balance
balance,
bolt12Address
};
} catch (err) {
return {
phoenixd: runtimeConfig.phoenixd,
nodeInfo: null,
balance: null
balance: null,
bolt12Address: null
};
}
};
Expand Down Expand Up @@ -76,7 +80,9 @@ export const actions = {
.parse(Object.fromEntries(await event.request.formData()));

runtimeConfig.phoenixd.password = parsed.password;
runtimeConfig.phoenixd.lnAddress = await phoenixdLnAddress();

runtimeConfig.phoenixd.bolt12Address = await phoenixdGetBolt12();
await collections.runtimeConfig.updateOne(
{ _id: 'phoenixd' },
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
let withdrawMode = 'bolt11' as 'bolt11' | 'bitcoin';

let defaultUrl = data.phoenixd.url || 'http://localhost:9740';
let showBolt12 = false;
</script>

<h1 class="text-3xl">PhoenixD</h1>
Expand Down Expand Up @@ -92,13 +93,20 @@
<div class="flex gap-2">
<button class="btn btn-black" type="submit">Save</button>
<button class="btn btn-red" type="submit" form="disableForm">Reset</button>
<button class="btn btn-gray" type="button" on:click={() => (showBolt12 = !showBolt12)}
>Get bolt12 address</button
>

{#if data.nodeInfo}
<button class="btn btn-blue ml-auto" type="button" on:click={() => showDialog()}
>Withdraw</button
>
{/if}
</div>
{#if showBolt12}
<p class="break-words">Bolt12 address: {data.bolt12Address}</p>
<p>To use it on page CMS, use this code : <code>[QRCode=Bolt12]</code></p>
{/if}
</form>
<form method="POST" action="?/disable" id="disableForm"></form>
<dialog bind:this={withdrawDialog} class="max-w-full w-[500px] rounded">
Expand Down
22 changes: 22 additions & 0 deletions src/routes/(app)/phoenixd/bolt12/qrcode/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { runtimeConfig } from '$lib/server/runtime-config';
import qrcode from 'qrcode';

export async function GET({}) {
try {
const bolt12Address = runtimeConfig.phoenixd.bolt12Address;

const svgQRCode = await qrcode.toString('lightning:' + bolt12Address, { type: 'svg' });

return new Response(svgQRCode, {
headers: { 'content-type': 'image/svg+xml' },
status: 200
});
} catch (error) {
console.error('Error on phoenixdGetBolt12:', error);

return new Response("Erreur lors de la génération de l'adresse Bolt12", {
headers: { 'content-type': 'text/plain' },
status: 500
});
}
}
3 changes: 2 additions & 1 deletion src/routes/+layout.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export async function load(event) {
viewportWidth,
contactModes: runtimeConfig.contactModes,
hideFromSearchEngines: runtimeConfig.hideFromSearchEngines,
ageRestriction: runtimeConfig.ageRestriction
ageRestriction: runtimeConfig.ageRestriction,
bolt12Address: runtimeConfig.phoenixd.bolt12Address
};
}
11 changes: 6 additions & 5 deletions src/routes/.well-known/lnurlp/[id]/+server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isLightningConfigured, lndGetInfo } from '$lib/server/lnd';

Check warning on line 1 in src/routes/.well-known/lnurlp/[id]/+server.ts

View workflow job for this annotation

GitHub Actions / lint

File ignored by default.
import { getPrivateS3DownloadLink } from '$lib/server/s3';
import { SATOSHIS_PER_BTC } from '$lib/types/Currency';
import { runtimeConfig } from '$lib/server/runtime-config';
Expand All @@ -18,14 +18,15 @@
};

export const GET = async ({ params, url }) => {
if (!isLightningConfigured) {
if (!isLightningConfigured && !runtimeConfig.phoenixd.lnAddress) {
throw error(400, 'Lighting is not configured');
}
if (isLightningConfigured) {
const info = await lndGetInfo();

const info = await lndGetInfo();

if (!info.uris.length) {
throw error(400, 'No public Lightning URI');
if (!info.uris.length) {
throw error(400, 'No public Lightning URI');
}
}

let picture: Buffer | null = null;
Expand Down
Loading
Loading