diff --git a/ui/noogle/package.json b/ui/noogle/package.json index e2adf43..5cd9a17 100644 --- a/ui/noogle/package.json +++ b/ui/noogle/package.json @@ -11,6 +11,7 @@ "type-check": "vue-tsc --build --force" }, "dependencies": { + "@getalby/sdk": "^3.4.0", "@rust-nostr/nostr-sdk": "^0.11.1", "@vuepic/vue-datepicker": "^7.4.1", "@vueuse/core": "^10.7.2", diff --git a/ui/noogle/public/Alby.jpg b/ui/noogle/public/Alby.jpg new file mode 100644 index 0000000..40a2a2d Binary files /dev/null and b/ui/noogle/public/Alby.jpg differ diff --git a/ui/noogle/public/Mutiny.png b/ui/noogle/public/Mutiny.png new file mode 100644 index 0000000..1d45cce Binary files /dev/null and b/ui/noogle/public/Mutiny.png differ diff --git a/ui/noogle/public/NWA.png b/ui/noogle/public/NWA.png new file mode 100644 index 0000000..b35e2d7 Binary files /dev/null and b/ui/noogle/public/NWA.png differ diff --git a/ui/noogle/public/NWC.png b/ui/noogle/public/NWC.png new file mode 100644 index 0000000..b35e2d7 Binary files /dev/null and b/ui/noogle/public/NWC.png differ diff --git a/ui/noogle/src/components/ImageGeneration.vue b/ui/noogle/src/components/ImageGeneration.vue index 0c8ff76..59b6c93 100644 --- a/ui/noogle/src/components/ImageGeneration.vue +++ b/ui/noogle/src/components/ImageGeneration.vue @@ -26,11 +26,14 @@ import { ref } from "vue"; import ModalComponent from "../components/Newnote.vue"; import VueDatePicker from "@vuepic/vue-datepicker"; import {timestamp} from "@vueuse/core"; -import {post_note, schedule, copyinvoice, copyurl, sleep, nextInput, get_user_infos, createBolt11Lud16, zaprequest} from "../components/helper/Helper.vue" +import {post_note, schedule, copyinvoice, copyurl, sleep, nextInput, get_user_infos} from "../components/helper/Helper.vue" +import {zap, createBolt11Lud16, zaprequest} from "../components/helper/Zap.vue" + import StringUtil from "@/components/helper/string.ts"; + let dvms =[] let hasmultipleinputs = false @@ -268,34 +271,13 @@ const urlinput = ref(""); - async function zap(invoice) { - let webln; - - //this.dvmpaymentaddr = `https://chart.googleapis.com/chart?cht=qr&chl=${invoice}&chs=250x250&chld=M|0`; - //this.dvminvoice = invoice - - - try { - webln = await requestProvider(); - } catch (err) { - await copyinvoice(invoice) +async function zap_local(invoice) { + let success = await zap(invoice) + if (success){ + dvms.find(i => i.bolt11 === invoice).status = "paid" + store.commit('set_imagedvm_results', dvms) } - - if (webln) { - try{ - let response = await webln.sendPayment(invoice) - dvms.find(i => i.bolt11 === invoice).status = "paid" - store.commit('set_imagedvm_results', dvms) - } - catch(err){ - console.log(err) - await copyinvoice(invoice) - } - - } - - - } +} defineProps({ @@ -417,7 +399,7 @@ const submitHandler = async () => { - + diff --git a/ui/noogle/src/components/Login.vue b/ui/noogle/src/components/Login.vue index 45a79af..dd8cf77 100644 --- a/ui/noogle/src/components/Login.vue +++ b/ui/noogle/src/components/Login.vue @@ -19,6 +19,102 @@

Sign out of your account

+ + + + +
@@ -92,10 +188,11 @@ import nip49, {decryptwrapper} from "./android-signer/helpers/nip49"; import { init as initNostrLogin } from "nostr-login" import { launch as launchNostrLoginDialog } from "nostr-login" import { logout as logoutNostrLogin } from "nostr-login" -import {parseandreplacenpubs} from "@/components/helper/Helper.vue" - +import {parseandreplacenpubs, hasActiveSubscription} from "@/components/helper/Helper.vue" +import {loadNWCObject} from "@/components/helper/Zap.vue" import {useDark, useToggle} from "@vueuse/core"; import {ref} from "vue"; +import {webln} from "@getalby/sdk"; const isDark = useDark(); //const toggleDark = useToggle(isDark); @@ -106,6 +203,10 @@ let nip89dvms = [] const nsec = ref(""); let logger = false + + + + export default { data() { @@ -115,6 +216,11 @@ export default { signer: "", supports_android_signer: false, ncryptsec: ref(""), + nwc: ref(""), + nwcmutiny: ref(""), + nwcalby: ref(""), + nwcconnector: ref("user"), + pw: ref("") @@ -136,7 +242,7 @@ export default { await this.sign_in_nip07() } else if (localStorage.getItem('nostr-key-method') === 'nsec') { - await this.sign_in_key(localStorage.getItem('nostr-key')) + await this.sign_in_key(localStorage.getItem('localNostrPrivkey')) } else if (localStorage.getItem('nostr-key-method') === 'nostr-login'){ console.log(localStorage.getItem('__nostrlogin_nip46')) @@ -156,6 +262,18 @@ export default { await this.sign_in_anon() } await this.getnip89s() + let nwc = loadNWCObject() + + + if(nwc.connectorType === "alby"){ + this.nwcalby = nwc.nwcUrl + } + else if(nwc.connectorType === "user"){ + this.nwc = nwc.nwcUrl + } + else if (nwc.connectorType === "mutiny"){ + this.nwcmutiny = nwc.nwcUrl + } @@ -214,7 +332,7 @@ export default { store.commit('set_pubkey', pubkey) store.commit('set_hasEventListener', false) localStorage.setItem('nostr-key-method', "nostr-login") - localStorage.setItem('nostr-key', '') + localStorage.setItem('nostr-key', pubkey.toHex()) console.log("Client Nip46 connected") await this.get_user_info(pubkey) this.reconcile_all_profiles(pubkey) @@ -328,9 +446,10 @@ export default { store.commit('set_signer', this.signer) store.commit('set_pubkey', pubkey) store.commit('set_hasEventListener', false) - console.log("LOGINANON") + console.log("LOGIN with Key") localStorage.setItem('nostr-key-method', "nsec") - localStorage.setItem('nostr-key', keys.secretKey.toBech32()) + localStorage.setItem('nostr-key', keys.publicKey.toHex()) + localStorage.setItem('localNostrPrivkey', keys.secretKey.toHex()) console.log("Client key connected") await this.get_user_info(pubkey) this.reconcile_all_profiles(pubkey) @@ -396,6 +515,9 @@ export default { store.commit('set_hasEventListener', false) localStorage.setItem('nostr-key-method', "nip07") localStorage.setItem('nostr-key', pubkey.toHex()) + + + console.log("Client Nip07 connected") await this.get_user_info(pubkey) @@ -562,19 +684,52 @@ export default { //console.log(dvmkinds) const filter = new Filter().kind(31990).customTag(SingleLetterTag.lowercase(Alphabet.K), dvmkinds) - //await client.reconcile(filter); - //const filterl = new Filter().kind(31990) - //let evts = await client.database.query([filterl]) let evts = await client.getEventsOf([filter], Duration.fromSecs(5)) + + let dvmkeys = [] + for (let evt of evts){ + for (const tag in evt.tags) { + if (evt.tags[tag].asVec()[1] >= 5000 && evt.tags[tag].asVec()[1] <= 5999 && deadnip89s.filter(i => i.id === evt.id.toHex()).length === 0) { + dvmkeys.push(evt.author) + } + } + + } + + const filtersubscription = new Filter().kind(37001).authors(dvmkeys) + let subscription_tiers = await client.getEventsOf([filtersubscription], Duration.fromSecs(5)) + //console.log(subscription_tiers) + for (const entry of evts){ for (const tag in entry.tags){ if (entry.tags[tag].asVec()[0] === "k") if(entry.tags[tag].asVec()[1] >= 5000 && entry.tags[tag].asVec()[1] <= 5999 && deadnip89s.filter(i => i.id === entry.id.toHex() ).length === 0) { // blocklist.indexOf(entry.id.toHex()) < 0){ + + try { let jsonentry = JSON.parse(entry.content) + + + let susbcrition_tier = subscription_tiers.find(x => x.author.toHex() === entry.author.toHex()) + + + let nip88 = { + title: "", + image: "", + amounts: [], + zaps: [], + perks: [], + description: "", + eventid: "", + event: "", + hasActiveSubscription: false, + subscribedUntil: 0, + subscriptionId: "", + } + if (jsonentry.picture){ jsonentry.image = jsonentry.picture } @@ -582,12 +737,79 @@ export default { if(!jsonentry.amount){ jsonentry.amount = "" } + if(jsonentry.amount === "subscription"){ + // if(susbcrition_tier) { + const filter = new Filter().kind(37001).author(entry.author) + let tiers = await client.getEventsOf([filter], Duration.fromSecs(5)) + + // todo handle multiple tiers + if (tiers.length > 0) { + let evt = tiers[0] + nip88.description = evt.content + nip88.eventid = evt.id.toHex() + nip88.event = evt.asJson() + + + for (let tag of evt.tags) { + if (tag.asVec()[0] === "amount"){ + let amount = { + amount: tag.asVec()[1], + unit: tag.asVec()[2], + cadence: tag.asVec()[3] + } + nip88.amounts.push(amount) + + } + else if(tag.asVec()[0] === "title"){ + nip88.title = tag.asVec()[1] + } + else if(tag.asVec()[0] === "image"){ + nip88.image = tag.asVec()[1] + } + else if(tag.asVec()[0] === "perk") { + nip88.perks.push(tag.asVec()[1]) + } + + else if(tag.asVec()[0] === "zap_local"){ + let zap = { + key: (tag.asVec()[1] !== "" ? tag.asVec()[1] : PublicKey.parse("npub1nxa4tywfz9nqp7z9zp7nr7d4nchhclsf58lcqt5y782rmf2hefjquaa6q8").toHex()) , + relay: tag.asVec()[2], + split: tag.asVec()[3] + } + + // TODO only use first tag for now, add others later. + // if(tag.asVec()[1] !== "" ){ + nip88.zaps.push(zap) + // } + + + } + + + + + } + let subscription_status = await hasActiveSubscription(localStorage.getItem("nostr-key"), evt.id.toHex(), evt.author.toHex(), nip88.amounts) + nip88.hasActiveSubscription = subscription_status.isActive + nip88.subscribedUntil = subscription_status.validuntil + nip88.subscriptionId = subscription_status.subscriptionId + + + jsonentry.nip88 = nip88 + console.log(jsonentry.nip88) + + } + } + jsonentry.id = entry.author.toHex() jsonentry.about = await parseandreplacenpubs(jsonentry.about) jsonentry.event = entry.asJson() jsonentry.kind = entry.tags[tag].asVec()[1] + + //jsonentry.nip90Params = JSON.parse(jsonentry.nip90Params) nip89dvms.push(jsonentry); + } catch (error){ //console.log(error) @@ -595,9 +817,10 @@ export default { } } - } - store.commit('set_nip89dvms', nip89dvms) + } + + store.commit('set_nip89dvms', nip89dvms) return nip89dvms @@ -664,6 +887,8 @@ export default { } }, + + async sign_out(){ this.current_user = "" if(localStorage.getItem('nostr-key-method') === "nostr-login"){ @@ -675,7 +900,40 @@ export default { //await this.state.client.shutdown(); await this.sign_in_anon() - } + }, + + async store_nwc(){ + let connector = "" + let nwc = "" + if (this.nwcalby.startsWith("nostr")){ + connector = "alby" + nwc = this.nwcalby + } + else if (this.nwcmutiny.startsWith("nostr") ){ + connector = "mutiny" + nwc = this.nwcmutiny + } + else{ + connector = "user" + nwc = this.nwc + } + localStorage.setItem("nwc", "{\"nwcUrl\":\"" + nwc + "\",\"connectorName\":\""+ connector +"\",\"connectorType\":\"" + connector +"\"}" ) + }, + + +async connect_alby_nwc(){ + +const alby = webln.NostrWebLNProvider.withNewSecret(); +let result = await alby.client.initNWC({ + name: `Noogle`, + }); + + if (result.payload.success){ + this.nwcalby = alby.client.getNostrWalletConnectUrl(true); + await this.store_nwc() + } +}, + }, }; @@ -713,6 +971,10 @@ export default { @apply bg-base-200 dark:bg-base-200 dark:text-white focus:ring-white border border-transparent px-3 py-1.5 text-sm leading-4 text-accent-content transition-colors duration-300 focus:ring-offset-2 focus:ring-offset-white dark:focus:ring-offset-gray-900; width: 220px; +} + +.nwc-Input { + @apply bg-base-200 dark:bg-base-200 dark:text-white focus:ring-white border border-white px-3 py-1.5 text-sm leading-4 text-accent-content transition-colors duration-300 focus:ring-offset-2 focus:ring-offset-white dark:focus:ring-offset-gray-900; diff --git a/ui/noogle/src/components/NoteTable.vue b/ui/noogle/src/components/NoteTable.vue index 3f6076e..9fa6a19 100644 --- a/ui/noogle/src/components/NoteTable.vue +++ b/ui/noogle/src/components/NoteTable.vue @@ -48,7 +48,7 @@ -
+
@@ -58,7 +58,7 @@

{{zapAmount/1000}}

-
+
@@ -91,6 +91,7 @@ import {copyinvoice, createBolt11Lud16, parseandreplacenpubs, zaprequest} from " import {requestProvider} from "webln"; import {Event, EventBuilder, EventId, PublicKey} from "@rust-nostr/nostr-sdk"; import amberSignerService from "@/components/android-signer/AndroidSigner"; +import zap, {zap_lud16} from "@/components/helper/Zap.vue"; const props = defineProps<{ @@ -145,56 +146,16 @@ async function react(eventid, authorid){ } -async function zap(lud16, eventid, authorid){ +async function zap_local(lud16, eventid, authorid){ - if(lud16 != Null && lud16 != ""){ - let invoice = await zaprequest(lud16, 21 , "with love from noogle.lol", eventid, authorid, store.state.relays) //Not working yet - // let invoice = await createBolt11Lud16(lud16, 21) - let webln; - try { - webln = await requestProvider(); - } catch (err) { - if (invoice === null){ - invoice = await createBolt11Lud16(lud16, 21) - } - - await copyinvoice(invoice) - - - } - if (webln) { - try{ - let response = await webln.sendPayment(invoice) - if(response.preimage != null && response.preimage != ""){ - - - let objects = (props.data.find(x=> x.id === eventid)) + let success = await zap_lud16(lud16, eventid, authorid) + if (success){ + let objects = props.data.find(x=> x.id === eventid) if (objects !== undefined){ - // console.log(objects.zapped) objects.zapped = true objects.zapAmount += 21000 - } + } - /* if (objects != undefined && objects.length > 0){ - console.log(objects[0]) - props.data.find(x=> x.id === eventid).zapped.push.apply(props.data.find(x=> x.id === eventid).zapped, true) - - - }*/ - - - // miniToastr.showMessage("Zapped " + author , "Success" , VueNotifications.types.warn) - console.log(response) - - } - - - } - catch(err){ - console.log(err) - await copyinvoice(invoice) - } - } } } diff --git a/ui/noogle/src/components/ProfileResultTable.vue b/ui/noogle/src/components/ProfileResultTable.vue index dee97ab..e45943c 100644 --- a/ui/noogle/src/components/ProfileResultTable.vue +++ b/ui/noogle/src/components/ProfileResultTable.vue @@ -5,15 +5,10 @@ :items="store.state.profile_results" >
- Avatar - - {{ author }} - - - -
+ {{ author }} +
diff --git a/ui/noogle/src/components/RecommendationGeneration.vue b/ui/noogle/src/components/RecommendationGeneration.vue index d2e31bb..01f9ef0 100644 --- a/ui/noogle/src/components/RecommendationGeneration.vue +++ b/ui/noogle/src/components/RecommendationGeneration.vue @@ -11,7 +11,7 @@ import { EventBuilder, Tag, EventId, - Nip19Event, Alphabet, Keys, nip04_decrypt, SecretKey, Duration + Nip19Event, Alphabet, Keys, nip04_decrypt, SecretKey, Duration, SingleLetterTag } from "@rust-nostr/nostr-sdk"; import store from '../store'; import miniToastr from "mini-toastr"; @@ -22,7 +22,9 @@ import {data} from "autoprefixer"; import {requestProvider} from "webln"; import Newnote from "@/components/Newnote.vue"; import SummarizationGeneration from "@/components/SummarizationGeneration.vue" -import {post_note, schedule, copyurl, copyinvoice, sleep, getEvents, get_user_infos, get_zaps, zaprequest, get_reactions, nextInput, createBolt11Lud16, getEventsOriginalOrder, parseandreplacenpubsName} from "../components/helper/Helper.vue" +import {post_note, schedule, copyurl, copyinvoice, sleep, getEvents, get_user_infos, get_zaps, get_reactions, nextInput, getEventsOriginalOrder, parseandreplacenpubsName} from "../components/helper/Helper.vue" +import {zap, createBolt11Lud16, zaprequest} from "../components/helper/Zap.vue" + import amberSignerService from "./android-signer/AndroidSigner"; import StringUtil from "@/components/helper/string.ts"; @@ -100,9 +102,6 @@ async function generate_feed(id) { } } - - - async function listen() { let client = store.state.client let pubkey = store.state.pubkey @@ -228,7 +227,7 @@ async function listen() { authors.push(evt.author) } catch(error){ - console.log(error) + //console.log(error) } } @@ -242,7 +241,11 @@ async function listen() { let ids = [] for (let evt of events){ - ids.push(evt.id) + try {ids.push(evt.id)} + catch(error){ + console.log(error) + } + } let zaps = await get_zaps(ids) let items = [] @@ -323,22 +326,18 @@ async function listen() { client.handleNotifications(handle); } - -const urlinput = ref(""); - - async function addAllContentDVMs() { - let relevent_dvms = [] + let relevant_dvms = [] for (const el of store.state.nip89dvms) { for (const tag of JSON.parse(el.event).tags) { if (tag[0] === "k" && tag[1] === "5300") { - relevent_dvms.push(PublicKey.parse(el.id)) + relevant_dvms.push(PublicKey.parse(el.id)) } } } let active_dvms = [] - for (let id of relevent_dvms) { + for (let id of relevant_dvms) { let jsonentry = { id: id.toHex(), last_active: 0 @@ -349,7 +348,7 @@ async function addAllContentDVMs() { console.log(active_dvms) - const filtera = new Filter().authors(relevent_dvms).kinds([6300, 7000]) + const filtera = new Filter().authors(relevant_dvms).kinds([6300, 7000]) let client = store.state.client let activities = await client.getEventsOf([filtera], Duration.fromSecs(1)) @@ -367,10 +366,10 @@ async function addAllContentDVMs() { // console.log(last_active) // If DVM hasn't been active for 3 weeks, don't consider it. - console.log(active_dvms) + //console.log(active_dvms) let final_dvms = [] for (let element of active_dvms) { - if (element.last_active > Timestamp.now().asSecs() - 60 * 60 * 24 * 7) { + if (element.last_active > Timestamp.now().asSecs() - 60 * 60 * 24 * 21) { final_dvms.push(store.state.nip89dvms.find(x => x.id === element.id)) } @@ -389,7 +388,8 @@ async function addAllContentDVMs() { amount: el.amount, bolt11: "", lud16: el.lud16, - subscription: "" + subscription: "", + nip88: el.nip88 } @@ -503,21 +503,75 @@ async function addDVM(event){ } -async function subscribe(lud16, days, amountperday, eventid, authorid) { - if (lud16 !== "") { - let profiles = await get_user_infos([PublicKey.parse(authorid)]) - if (profiles.length > 0) { - let current = profiles[0] - lud16 = current.profile.lud16 + + +async function subscribe(zaps, amount, cadence, activesubscriptioneventid, tierevent, tiereventid, dvmid) { + + // We only arrive here if no subscription exists, we might create a 7001 if it doesnt exist and we zap it + let client = store.state.client + + console.log(dvmid) + console.log(tiereventid) + console.log(JSON.stringify(tierevent)) + console.log(amount) + console.log(activesubscriptioneventid) + + + if (activesubscriptioneventid === ""){ + console.log("Creating 7001 event") + let tags = [ + Tag.parse([ "p", dvmid]), + Tag.parse([ "e" , tiereventid]), + Tag.parse([ "event", JSON.stringify(tierevent)]), + Tag.parse([ "amount", (amount).toString(), "msats", cadence]), + // Zap-splits todo order and splits + // Tag.parse([ "zap", authorid, "19" ]), // 95% + // Tag.parse([ "zap", "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52", "1" ]), // 5% to client developer where subscription was created + ] + + for(let zap of zaps){ + let zaptag = Tag.parse([ "zap", zap.key, zap.split]) + tags.push(zaptag) } + + console.log(tags) + let evt = new EventBuilder(7001, "Subscription from noogle.lol", tags) + let res = await client.sendEventBuilder(evt); + activesubscriptioneventid = res.toHex() + } - let invoice = await zaprequest(lud16, days * amountperday, "paid from noogle.lol", eventid, authorid, store.state.relays) - console.log(invoice) - await zap(invoice) + + let overallsplit = 0 + for (let zap of zaps){ + overallsplit += parseInt(zap.split) + } + for (let zap of zaps){ + let profiles = await get_user_infos([PublicKey.parse(zap.key)]) + if (profiles.length > 0) { + let current = profiles[0] + let lud16 = current.profile.lud16 + let splitted_amount = Math.floor((zap.split/overallsplit) * amount/1000) + console.log(splitted_amount) + console.log(overallsplit) + console.log(activesubscriptioneventid) + let invoice = await zaprequest(lud16, splitted_amount, "paid for " + cadence + " from noogle.lol", activesubscriptioneventid, dvmid, store.state.relays) + console.log(invoice) + await zapSubscription(invoice) + } + } + + + + dvms.find(x => x.nip88.eventid === tiereventid ).hasActiveSubscription = true + + + + // next, the dvm should listen to these 7001 events addressed to it and (or rather 9735 tagging the 7001 and the subscription should be considered valid for both) + } -async function zap(invoice) { +async function zapSubscription(invoice) { let webln; //this.dvmpaymentaddr = `https://chart.googleapis.com/chart?cht=qr&chl=${invoice}&chs=250x250&chld=M|0`; @@ -531,8 +585,9 @@ async function zap(invoice) { if (webln) { try{ let response = await webln.sendPayment(invoice) - dvms.find(i => i.bolt11 === invoice).status = "paid" - store.commit('set_recommendation_dvms', dvms) + + //dvms.find(i => i.bolt11 === invoice).status = "paid" + // store.commit('set_recommendation_dvms', dvms) } catch(err){ console.log(err) @@ -542,6 +597,17 @@ async function zap(invoice) { } +async function zap_local(invoice) { + + let success = await zap(invoice) + if (success){ + dvms.find(i => i.bolt11 === invoice).status = "paid" + store.commit('set_recommendation_dvms', dvms) + } + +} + + defineProps({ msg: { type: String, @@ -554,6 +620,7 @@ import ModalComponent from "../components/Newnote.vue"; import VueDatePicker from "@vuepic/vue-datepicker"; import {timestamp} from "@vueuse/core"; import NoteTable from "@/components/NoteTable.vue"; +import {nostrzapper_nwc} from "@rust-nostr/nostr-sdk/pkg/nostr_sdk_js_bg.wasm.js"; @@ -667,37 +734,22 @@ const submitHandler = async () => { - + - - -
+ +

Subscription required

-
-
-

Subscribe

-
-
-
-

Subscribe for a day

- - - -

Subscribe for a month

- - -
-
-
- -
- - + + @@ -723,13 +775,85 @@ const submitHandler = async () => {

Free

-

Flexible

-

Subscription

-
-
+

Flexible

+
+
+
+ +
+
+ + +
+
+ +
+ +

{{dvm.nip88.title}}

+ +

{{dvm.nip88.description}}

+
+
+

Subscribe and pay {{index.cadence}}

+ + +
+ +
+
+
+
+
+ + + +
+
+ +
+ +

{{dvm.nip88.title}}

+ +

{{dvm.nip88.description}}

+
+

Perks:

+
+

{{perk}}

+ + +
+
+

Subscription active until + {{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[0].split("-")[2].trim()}}.{{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[0].split("-")[1].trim()}}.{{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[0].split("-")[0].trim().slice(2)}} {{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[1].split("Z")[0].trim()}}

+ + + + +
+
+
+ +
+ + + + -
+
+
-->

-

+

{{dvm.amount/1000}}

@@ -787,6 +911,7 @@ const submitHandler = async () => { .sub-Button{ @apply btn hover:bg-nostr border-orange-500 text-base; + bottom: 0; } diff --git a/ui/noogle/src/components/RecommendationGeneration_old.vue b/ui/noogle/src/components/RecommendationGeneration_old.vue index 002a884..0f66e06 100644 --- a/ui/noogle/src/components/RecommendationGeneration_old.vue +++ b/ui/noogle/src/components/RecommendationGeneration_old.vue @@ -308,31 +308,19 @@ async function addDVM(event){ store.commit('set_recommendation_dvms', dvms) } -async function zap(invoice) { - let webln; - //this.dvmpaymentaddr = `https://chart.googleapis.com/chart?cht=qr&chl=${invoice}&chs=250x250&chld=M|0`; - //this.dvminvoice = invoice - try { - webln = await requestProvider(); - } catch (err) { - await copyinvoice(invoice) - } +async function zap_local(invoice) { - if (webln) { - try{ - let response = await webln.sendPayment(invoice) - dvms.find(i => i.bolt11 === invoice).status = "paid" + let success = await zap(invoice) + if (success){ + dvms.find(i => i.bolt11 === invoice).status = "paid" store.commit('set_recommendation_results', dvms) - } - catch(err){ - console.log(err) - await copyinvoice(invoice) - } } + } + defineProps({ msg: { type: String, @@ -345,6 +333,7 @@ import ModalComponent from "../components/Newnote.vue"; import VueDatePicker from "@vuepic/vue-datepicker"; import {timestamp} from "@vueuse/core"; import NoteTable from "@/components/NoteTable.vue"; +import zap from "@/components/helper/Zap.vue"; const isModalOpened = ref(false); const modalcontent = ref(""); @@ -456,7 +445,7 @@ const submitHandler = async () => { - +
diff --git a/ui/noogle/src/components/SummarizationGeneration.vue b/ui/noogle/src/components/SummarizationGeneration.vue index 7624427..3e19eef 100644 --- a/ui/noogle/src/components/SummarizationGeneration.vue +++ b/ui/noogle/src/components/SummarizationGeneration.vue @@ -28,6 +28,7 @@ import ModalComponent from "../components/Newnote.vue"; import VueDatePicker from "@vuepic/vue-datepicker"; import {timestamp} from "@vueuse/core"; import NoteTable from "@/components/NoteTable.vue"; +import {zap} from "@/components/helper/Zap.vue"; let dvms =[] async function summarizefeed(eventids) { @@ -245,36 +246,16 @@ async function listen() { } -async function zap(invoice) { - let webln; +async function zap_local(invoice) { - //this.dvmpaymentaddr = `https://chart.googleapis.com/chart?cht=qr&chl=${invoice}&chs=250x250&chld=M|0`; - //this.dvminvoice = invoice - - - try { - webln = await requestProvider(); - } catch (err) { - await copyinvoice(invoice) + let success = await zap(invoice) + if (success){ + dvms.find(i => i.bolt11 === invoice).status = "paid" + store.commit('set_summarization_dvms', dvms) } - if (webln) { - try{ - let response = await webln.sendPayment(invoice) - dvms.find(i => i.bolt11 === invoice).status = "paid" - store.commit('set_summarization_dvms', dvms) - } - catch(err){ - console.log(err) - await copyinvoice(invoice) - } - - } - - } - defineProps({ events: { type: Array, @@ -364,7 +345,7 @@ const submitHandler = async () => { - +
diff --git a/ui/noogle/src/components/helper/Helper.vue b/ui/noogle/src/components/helper/Helper.vue index ee38f00..c007c51 100644 --- a/ui/noogle/src/components/helper/Helper.vue +++ b/ui/noogle/src/components/helper/Helper.vue @@ -2,7 +2,19 @@ import {defineComponent} from 'vue' import store from "@/store"; import amberSignerService from "@/components/android-signer/AndroidSigner"; -import {Duration, Event, EventBuilder, EventId, Filter, Keys, PublicKey, Tag, Timestamp} from "@rust-nostr/nostr-sdk"; +import { + Alphabet, + Duration, + Event, + EventBuilder, + EventId, + Filter, + Keys, + PublicKey, + SingleLetterTag, + Tag, + Timestamp +} from "@rust-nostr/nostr-sdk"; import miniToastr from "mini-toastr/mini-toastr"; import VueNotifications from "vue-notifications"; import {bech32} from "bech32"; @@ -349,7 +361,7 @@ export async function parseandreplacenpubsName(note){ return finalnote.trimEnd() } -async function fetchAsync (url) { +export async function fetchAsync (url) { let response = await fetch(url); let data = await response.json(); return data; @@ -357,151 +369,202 @@ async function fetchAsync (url) { -export async function zaprequest(lud16, amount, content, zapped_event_id, zapped_user_id, relay_list){ - let url = "" - - console.log(lud16) - console.log(PublicKey.parse(zapped_user_id).toBech32()) - console.log(EventId.parse(zapped_event_id).toBech32()) - console.log(zapped_event_id) - - - zapped_user_id = PublicKey.parse(zapped_user_id).toHex() - zapped_event_id = EventId.parse(zapped_event_id).toHex() - - //overwrite for debug - //lud16 = "hype@bitcoinfixesthis.org" - //zapped_user_id = PublicKey.parse("npub1nxa4tywfz9nqp7z9zp7nr7d4nchhclsf58lcqt5y782rmf2hefjquaa6q8").toHex() - //zapped_event_id = EventId.parse("note1xsw95cp4ynelxdujd3xrh6kmre3y0lk699xn09z52mjenmktdllq9vtwyn").toHex() - - - - - - if (lud16 !== "" && lud16.toString().includes('@')){ - url = `https://${lud16.split('@')[1]}/.well-known/lnurlp/${lud16.split('@')[0]}`; - console.log(url) - } - else{ - return null - } - try{ - - let ob = await fetchAsync(url) - let callback = ob["callback"] - console.log(callback) - - - const urlBytes = new TextEncoder().encode(url); - const encoded_lnurl = bech32.encode('lnurl', bech32.toWords(urlBytes), 1023); - - - const amount_tag = ['amount', (amount * 1000).toString()]; - let relays = ['relays'] - relays.push.apply(relays, relay_list) - //let relays_tag = Tag.parse(relays); - - const lnurl_tag = ['lnurl', encoded_lnurl]; - - let tags = [] - let p_tag = ['p', zapped_user_id] - if (zapped_event_id !== null){ - let e_tag = ['e', zapped_event_id] - tags = [amount_tag, relays, p_tag, e_tag, lnurl_tag] - } - - else{ - tags = [amount_tag, relays, p_tag, lnurl_tag] - } - /*if (zaptype === "private") { - const key_str = keys.secret_key().to_hex() + zapped_event.id().to_hex() + zapped_event.created_at().as_secs().toString(); - const encryption_key = sha256(key_str).toString('hex'); - const zap_request = new EventBuilder(9733, content, [p_tag, e_tag]).to_event(keys).as_json(); - keys = Keys.parse(encryption_key); - const encrypted_content = enrypt_private_zap_message(zap_request, keys.secret_key(), zapped_event.author()); - const anon_tag = Tag.parse(['anon', encrypted_content]); - tags.push(anon_tag); - content = ""; - } */ - - - - let signer = store.state.signer - let zap_request = "" - - if (localStorage.getItem('nostr-key-method') === 'android-signer') { - let draft = { - content: content, - kind: 9734, - pubkey: store.state.pubkey.toHex(), - tags: tags, - createdAt: Date.now() - }; - - let res = await amberSignerService.signEvent(draft) - zap_request = JSON.stringify(res) - //await sleep(3000) - - } - else { - let tags_t = [] - for (let tag of tags){ - tags_t.push(Tag.parse(tag)) - } - let noteevent = new EventBuilder(9734, content, tags_t).toUnsignedEvent(store.state.pubkey) - let signedEvent = await signer.signEvent(noteevent) - zap_request = signedEvent.asJson() - } - - try{ - - const queryString = `amount=${(amount * 1000).toString()}&nostr=${encodeURIComponent(zap_request)}&lnurl=${encoded_lnurl}`; - - console.log(queryString) - let ob = await fetchAsync(`${callback}?${queryString}`) - return ob["pr"] - } - catch(e){ - console.log("HELLO" + e) - } - } - catch(error){ - console.log("ZAP REQUEST: " + error) +export async function hasActiveSubscription(pubkeystring, tiereventid, tierauthorid, amounts) { + console.log("Checking for subscription") + let client = store.state.client + let subscriptionstatus = { + isActive: false, + validuntil: 0, + subscriptionId: "", } + let event7001id = EventId + console.log(pubkeystring) + // look if user has 7001 event, and if 7001 has been canceled by 7002 + const filter = new Filter().kind(7001).author(PublicKey.parse(pubkeystring)).pubkey(PublicKey.parse(tierauthorid)).event(EventId.parse(tiereventid)) //only get latest with these conditions + let evts = await client.getEventsOf([filter], Duration.fromSecs(5)) + console.log(evts) + if (evts.length > 0) { + + //console.log("7001: " + evts[0].asJson()) + let checkispaid = [] + for (let tag of evts[0].tags){ + /*if (tag.asVec()[0] === "e"){ + console.log("sanity check") + const filtercheck = new Filter().kind(37001).id(EventId.parse(tag.asVec()[1])).limit(1) // get latest with these conditons # customTag(SingleLetterTag.lowercase(Alphabet.A), [eventid]) + let sanityevents = await client.getEventsOf([filtercheck], Duration.fromSecs(5)) + if (sanityevents.length === 0){ + subscriptionstatus.isActive = false + return subscriptionstatus + } + else{console.log(sanityevents[0].asJson()) + } + } */ + + if (tag.asVec()[0] === "zap"){ + checkispaid.push(tag) + } + } + + let splitids = [] + for (let tag of checkispaid){ + splitids.push(PublicKey.parse(tag.asVec()[1])) + console.log(tag.asVec()) + } + if (checkispaid.length === 0){ + subscriptionstatus.isActive = false + return subscriptionstatus + } + + for (let evt in evts){ + event7001id = evts[0].id + } + + event7001id = evts[0].id + + const filter = new Filter().kind(7002).pubkey(PublicKey.parse(tierauthorid)).event(event7001id).limit(1) // get latest with these conditons # customTag(SingleLetterTag.lowercase(Alphabet.A), [eventid]) + let cancelevts = await client.getEventsOf([filter], Duration.fromSecs(5)) + if (cancelevts.length > 0) { + if (cancelevts[0].createdAt.asSecs() > evts[0].createdAt.asSecs()) { + console.log("A subscription exists, but has been canceled") + subscriptionstatus.isActive = false + subscriptionstatus.subscriptionId = event7001id.toHex() + return subscriptionstatus + } + } + else { + console.log("A subscription exists, checking payment status") - return null + + + const zapfilter = new Filter().kind(9735).pubkeys(splitids).event(event7001id).limit(checkispaid.length) + let zapevents = await client.getEventsOf([zapfilter], Duration.fromSecs(5)) + if (zapevents.length > 0) { + console.log(zapevents) + let timeofourlastzap = 0 + + let overallamount = 0 + + let overall = 0 + for (let tag of checkispaid){ + let split = parseInt(tag.asVec()[2]) + overall += split + } + for(let zapevent of zapevents) { + + + if (zapevent.createdAt.asSecs() > timeofourlastzap) { + timeofourlastzap = zapevent.createdAt.asSecs() + } + + for (let tag of zapevent.tags) { + if (tag.asVec()[0] === "description") { + + let event9734 = Event.fromJson(tag.asVec()[1]) + + + for (let tag of event9734.tags) { + if (tag.asVec()[0] === "amount") { + let amount = parseInt(tag.asVec()[1]) + console.log("AMOUNT: " + amount) + overallamount = overallamount + amount + } + } + } + } + } + + + let entry + for (let index in amounts){ + if(parseInt(amounts[index].amount) === overallamount ){ + entry = amounts[index] + } + } + + if(!entry){ + console.log("Undefined amount") + subscriptionstatus.isActive = false + subscriptionstatus.subscriptionId = event7001id.toHex() + //subscriptionstatus.validuntil = zapevent.createdAt.asSecs() + 24*60*60 + return subscriptionstatus + } + else{ + console.log(entry.amount + " " + entry.cadence) + if (entry.cadence === "daily"){ + if (timeofourlastzap + 24*60*60 > Timestamp.now().asSecs()){ + console.log("A daily subscription exists, and is active") + subscriptionstatus.isActive = true + subscriptionstatus.subscriptionId = event7001id.toHex() + subscriptionstatus.validuntil = timeofourlastzap + 24*60*60 + return subscriptionstatus + + } + } + else if (entry.cadence === "monthly"){ + if (timeofourlastzap + 31*24*60*60 > Timestamp.now().asSecs()){ + console.log("A monthly subscription exists, and is active") + subscriptionstatus.isActive = true + subscriptionstatus.subscriptionId = event7001id.toHex() + subscriptionstatus.validuntil = timeofourlastzap + 31*24*60*60 + return subscriptionstatus + + } + } + else if (entry.cadence === "yearly"){ + if (timeofourlastzap + 366*24*60*60 > Timestamp.now().asSecs()){ + console.log("A yearly subscription exists, and is active") + subscriptionstatus.isActive = true + subscriptionstatus.subscriptionId = event7001id.toHex() + subscriptionstatus.validuntil = timeofourlastzap + 366*24*60*60 + return subscriptionstatus + + } + } + + + + + + /*else if (tag.asVec()[0] === "bolt11"){ + let lnurl = tag.asVec()[1] + console.log(lnurl) + } */ + + + + + + } + // todo check that subscription is within the range + + + + } else { + console.log("A subscription exists, but no payment has been made") + subscriptionstatus.isActive = false + subscriptionstatus.subscriptionId = event7001id.toHex() + return subscriptionstatus + } + + } + } -export async function createBolt11Lud16(lud16, amount) { - if (lud16 === null || lud16 === ""){ - return null; + + else { + console.log("No subscription exists") + subscriptionstatus.isActive = false + return subscriptionstatus } +} + + + + - let url; - if (lud16.includes('@')) { // LNaddress - const parts = lud16.split('@'); - url = `https://${parts[1]}/.well-known/lnurlp/${parts[0]}`; - } else { // No lud16 set or format invalid - return null; - } - try { - console.log(url); - const response = await fetch(url); - const ob = await response.json(); - const callback = ob.callback; - const amountInSats = parseInt(amount) * 1000; - const callbackResponse = await fetch(`${callback}?amount=${amountInSats}`); - const obCallback = await callbackResponse.json(); - return obCallback.pr; - } - catch (e) { - console.log(`LUD16: ${e}`); - return null; - } - } diff --git a/ui/noogle/src/components/helper/Zap.vue b/ui/noogle/src/components/helper/Zap.vue new file mode 100644 index 0000000..3abc452 --- /dev/null +++ b/ui/noogle/src/components/helper/Zap.vue @@ -0,0 +1,270 @@ + + + + + \ No newline at end of file