Compare commits
5 Commits
f4dbaecfd2
...
c776045198
Author | SHA1 | Date | |
---|---|---|---|
c776045198 | |||
f85ae21f9e | |||
4746c37d7a | |||
821b774695 | |||
7dff65812a |
@ -1,9 +1,9 @@
|
||||
debug =
|
||||
|
||||
[xmpp]
|
||||
domain =
|
||||
host =
|
||||
jid =
|
||||
password =
|
||||
password =
|
||||
resource =
|
||||
user =
|
||||
|
||||
[nostr]
|
||||
pkhex =
|
||||
|
367
index.js
367
index.js
@ -6,8 +6,8 @@ const os = require('os');
|
||||
const path = require('path');
|
||||
const wspf = require('websocket-polyfill');
|
||||
const WebSocket = require('ws');
|
||||
const xmpp = require('simple-xmpp');
|
||||
|
||||
const { client, xml } = require('@xmpp/client');
|
||||
|
||||
// nostr things
|
||||
let {
|
||||
@ -18,7 +18,6 @@ let {
|
||||
let letsCID;
|
||||
let signedEvent;
|
||||
|
||||
|
||||
// config setup for home folders
|
||||
// Determine the platform-specific config path
|
||||
function getConfigPath() {
|
||||
@ -40,184 +39,214 @@ const configPath = getConfigPath();
|
||||
if (fs.existsSync(configPath)) {
|
||||
console.log(`Found config at ${configPath}`);
|
||||
const config = ini.parse(fs.readFileSync(configPath, 'utf-8'));
|
||||
// get users nostr private hex
|
||||
let sk = config.nostr.pkhex;
|
||||
// calculate pubkey from private
|
||||
let pk = nostr.getPublicKey(sk);
|
||||
|
||||
// only allow users from config.ini -> [authorized_users] -> users
|
||||
const authorizedUsers = config.authorized_users.users
|
||||
? config.authorized_users.users.split(',').map(user => user.trim())
|
||||
: [];
|
||||
|
||||
// We've got a connection to XMPP!
|
||||
xmpp.on('online', function (data, to) {
|
||||
console.log('Connected with JID: ' + config.xmpp.jid);
|
||||
console.log('Pubkey: ' + pk);
|
||||
});
|
||||
|
||||
// We've got an XMPP message!
|
||||
xmpp.on('chat', function (from, message) {
|
||||
// setup debugging based on config.ini
|
||||
function debug() {
|
||||
if (config.debug === '1') {
|
||||
console.log('[DEBUG]');
|
||||
console.log(message);
|
||||
console.log(from);
|
||||
};
|
||||
}
|
||||
// deny users not in config.ini
|
||||
if (!authorizedUsers.includes(from)) {
|
||||
xmpp.send(from, 'Not authorized');
|
||||
debug();
|
||||
return; // Exit the function early
|
||||
}
|
||||
|
||||
// get the latest documentation for nostrsms
|
||||
if (message === '!help') {
|
||||
xmpp.send(from, 'https://wiki.vanderwarker.family/doku.php?id=code:nostrsms:commands');
|
||||
debug();
|
||||
|
||||
// get latest 10 posts from global
|
||||
} else if (message === "!g") {
|
||||
xmpp.send(from, `Here are the latest 10 posts from global on ${config.relays.read}`);
|
||||
async function getGlobalEvents() {
|
||||
// use read relays in config.ini
|
||||
const relay = nostr.relayInit(config.relays.read, WebSocket);
|
||||
// connect to read relay
|
||||
relay.on('connect', () => {
|
||||
console.log(`connected to ${relay.url}`);
|
||||
});
|
||||
relay.on('error', () => {
|
||||
console.log(`failed to connect to ${relay.url}`);
|
||||
});
|
||||
|
||||
await relay.connect();
|
||||
|
||||
// create subscription to get events
|
||||
let sub = relay.sub([{
|
||||
kinds: [1],
|
||||
limit: 10
|
||||
}, ])
|
||||
// we've go an event! time to parse!
|
||||
sub.on('event', event => {
|
||||
console.log('got event:', event);
|
||||
|
||||
// Check if the event is already an object
|
||||
const globalEvents = typeof event === 'string' ? JSON.parse(event) : event;
|
||||
// if globalEvents.* is set, set to const for later use
|
||||
if (globalEvents && globalEvents.content && globalEvents.pubkey && globalEvents.created_at) {
|
||||
const content = globalEvents.content;
|
||||
const author = globalEvents.pubkey;
|
||||
const createdAt = globalEvents.created_at;
|
||||
|
||||
// WIP move to debug
|
||||
console.log('Content:', content);
|
||||
console.log('Author:', author);
|
||||
console.log('Created At:', createdAt);
|
||||
|
||||
// send the latest posts to the user whom requested them
|
||||
xmpp.send(from, `\"${content}\", - ${author} @ ${createdAt}`);
|
||||
relay.close();
|
||||
} else {
|
||||
console.error('Invalid event structure or missing required properties.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// let's run the above code to get our events and send them on XMPP/SMS! :)
|
||||
getGlobalEvents().catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
// Well they didn't use a command, so let's get to the NOSTR STUFF!!1!
|
||||
} else {
|
||||
async function newPost() {
|
||||
// get relays from config.ini
|
||||
const relay = nostr.relayInit(config.relays.write, WebSocket);
|
||||
// look at that, already on the nostr
|
||||
relay.on('connect', () => {
|
||||
console.log(`connected to ${relay.url}`);
|
||||
});
|
||||
// dang...failed connection! Try again sooon, or try one of the thousands of other relays ;)
|
||||
relay.on('error', () => {
|
||||
console.log(`failed to connect to ${relay.url}`);
|
||||
});
|
||||
|
||||
// wait for the above to give us the go ahead
|
||||
await relay.connect();
|
||||
|
||||
// let's construct our "note"!
|
||||
let event = {
|
||||
// https://git.vanderwarker.family/nostr/nips/src/branch/master/01.md
|
||||
kind: 1,
|
||||
// get users public key hex
|
||||
pubkey: pk,
|
||||
// create this instant! (timestamp)
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
// add client tag for UIs to optionally display
|
||||
tags: [['client','NostrSMS','31990:9be1b8315248eeb20f9d9ab2717d1750e4f27489eab1fa531d679dadd34c2f8d:1726937516']],
|
||||
// set the nostr content to the incoming xmpp message
|
||||
content: message,
|
||||
};
|
||||
// sign nostr event
|
||||
const signedEvent = nostr.finishEvent(event, sk);
|
||||
// get nostr event it
|
||||
letsCID = signedEvent.id;
|
||||
showSID(letsCID, pk);
|
||||
// publish!
|
||||
await relay.publish(signedEvent);
|
||||
// done here. we can disconnect! (from nostr relay)
|
||||
relay.close();
|
||||
}
|
||||
// catch errors
|
||||
newPost().catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
// get's id, pubkey, kind, from signed event, and relay from config.ini and create
|
||||
// a nip-19 encoded entity to send back to the user
|
||||
// this can be pasted into most nostr clients for immediate fetching/viewing
|
||||
async function showSID(letsCID, pk) {
|
||||
console.log(letsCID);
|
||||
console.log(pk);
|
||||
const event = {
|
||||
id: letsCID,
|
||||
relays: [config.relays.write],
|
||||
author: pk,
|
||||
kind: 1,
|
||||
};
|
||||
const encodedNEvent = nostr.nip19.neventEncode(event);
|
||||
xmpp.send(from, "nostr:" + encodedNEvent);
|
||||
console.log(encodedNEvent)
|
||||
debug();
|
||||
}
|
||||
}
|
||||
});
|
||||
// xmpp seems to have an error, so we show it in the console
|
||||
xmpp.on('error', function (err) {
|
||||
console.error(err);
|
||||
});
|
||||
// set xmpp presence to nostrsms url (free advertisement! :P)
|
||||
xmpp.setPresence('chat', 'https://nostrsms.com');
|
||||
// connect to xmpp server using info from config.ini
|
||||
xmpp.connect({
|
||||
jid: config.xmpp.jid,
|
||||
// prepare xmpp client
|
||||
const xmpp = client({
|
||||
service: config.xmpp.host,
|
||||
domain: config.xmpp.domain,
|
||||
resource: config.xmpp.resource || 'bot',
|
||||
username: config.xmpp.user,
|
||||
password: config.xmpp.password,
|
||||
host: config.xmpp.host,
|
||||
port: 5222
|
||||
});
|
||||
// catch ctrl+c
|
||||
// TODO: add confirmation ??
|
||||
|
||||
// set up allow users list from config.ini
|
||||
const authorizedUsers = config.authorized_users.users
|
||||
? config.authorized_users.users.split(',').map(user => user.trim())
|
||||
: [];
|
||||
|
||||
// we've got a connection to XMPP!
|
||||
xmpp.on('online', async () => {
|
||||
console.log('Connected with JID: ' + config.xmpp.user);
|
||||
console.log('Pubkey: ' + pk);
|
||||
await xmpp.send(xml('presence', {}, xml('status', {}, 'https://nostrsms.com')));
|
||||
});
|
||||
|
||||
// Debug: Log all incoming stanzas
|
||||
xmpp.on('stanza', async (stanza) => {
|
||||
console.log('Received stanza:', stanza.toString());
|
||||
|
||||
// check for chat messages
|
||||
if (stanza.is('message') && stanza.attrs.type === 'chat') {
|
||||
// store from jid, and message contents
|
||||
const from = stanza.attrs.from;
|
||||
const body = stanza.getChildText('body');
|
||||
|
||||
// show them to the user in console
|
||||
console.log(`Message from ${from}: ${body}`);
|
||||
|
||||
// if there is no body, it's not a message.
|
||||
// it just qqs
|
||||
if (!body) return;
|
||||
|
||||
// deny messages if they aren't a valid user
|
||||
if (!authorizedUsers.includes(from)) {
|
||||
await xmpp.send(xml('message', { to: from, type: 'chat' }, xml('body', {}, 'Not authorized')));
|
||||
return;
|
||||
}
|
||||
// user needs help
|
||||
// let's send them a wiki link back. (This should be nostrfied....right?!)
|
||||
if (body === '!help') {
|
||||
await xmpp.send(xml('message', { to: from, type: 'chat' }, xml('body', {}, 'https://wiki.vanderwarker.family/doku.php?id=code:nostrsms:commands')));
|
||||
// fetch global posts
|
||||
} else if (body === '!g') {
|
||||
await xmpp.send(xml('message', { to: from, type: 'chat' }, xml('body', {}, `Here are the latest 10 posts from global on ${config.relays.read}`)));
|
||||
|
||||
async function getGlobalEvents() {
|
||||
// store read relays from config.ini
|
||||
const relay = nostr.relayInit(config.relays.read, WebSocket);
|
||||
// we've got a connection to the nostr!
|
||||
relay.on('connect', () => console.log(`connected to ${relay.url}`));
|
||||
// we've not got a connection to the nostr!
|
||||
relay.on('error', () => console.log(`failed to connect to ${relay.url}`));
|
||||
|
||||
// attempt connection to the nostr
|
||||
await relay.connect();
|
||||
|
||||
// create a subscription to get events
|
||||
let sub = relay.sub([{ kinds: [1], limit: 10 }]);
|
||||
// we've got an event!
|
||||
sub.on('event', event => {
|
||||
// check if the event recieved is valid json
|
||||
// if it is parse it
|
||||
const globalEvents = typeof event === 'string' ? JSON.parse(event) : event;
|
||||
// check for json data
|
||||
// continue if it exists (atomic)
|
||||
if (globalEvents && globalEvents.content && globalEvents.pubkey && globalEvents.created_at) {
|
||||
// store parsed data from event
|
||||
const content = globalEvents.content;
|
||||
const author = globalEvents.pubkey;
|
||||
const createdAt = globalEvents.created_at;
|
||||
// print out to console
|
||||
console.log('Content:', content);
|
||||
console.log('Author:', author);
|
||||
console.log('Created At:', createdAt);
|
||||
// send parsed event data to XMPP
|
||||
xmpp.send(xml('message', { to: from, type: 'chat' }, xml('body', {}, `"${content}", - ${author} @ ${createdAt} `)));
|
||||
// All set! Let's disconnect from the relay...or...
|
||||
// I am not sure if I will change this...
|
||||
relay.close();
|
||||
} else {
|
||||
// event recieved wasn't a valid nostr event
|
||||
console.error('Invalid event structure or missing required properties.');
|
||||
}
|
||||
});
|
||||
}
|
||||
// run and print errors to console
|
||||
getGlobalEvents().catch(console.error);
|
||||
} else {
|
||||
// user hasn't input a command, which defaults to a nostr post.
|
||||
async function newPost() {
|
||||
// store write relays from config.ini
|
||||
const relay = nostr.relayInit(config.relays.write, WebSocket);
|
||||
// we've got a connection to the nostr!
|
||||
relay.on('connect', () => console.log(`connected to ${relay.url}`));
|
||||
// we've not got a connection to the nostr!
|
||||
relay.on('error', () => console.log(`failed to connect to ${relay.url}`));
|
||||
|
||||
// attempt connection to the nostr
|
||||
await relay.connect();
|
||||
/*
|
||||
incredible, but this is a nostr event.
|
||||
truth be told it doesn't even need the tags.
|
||||
That's just referencing another type of
|
||||
event that advertises nostrsms and shows
|
||||
links to this source code. nostr is
|
||||
waaayyy more than a X (Twitter) replacement
|
||||
*/
|
||||
|
||||
let event = {
|
||||
// kind 1 is a basic text post
|
||||
kind: 1,
|
||||
// set users pubkey
|
||||
pubkey: pk,
|
||||
// this will be created using a Unix timestamp
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
// some clients will show via NostrSMS much like X has/had? idk haven't been on in years :P
|
||||
tags: [['client', 'NostrSMS', '31990:9be1b8315248eeb20f9d9ab2717d1750e4f27489eab1fa531d679dadd34c2f8d:1726937516']],
|
||||
// set event content to the message recieved from the user.
|
||||
content: body,
|
||||
};
|
||||
// sign the nostr event using privkey from config.ini
|
||||
const signedEvent = nostr.finishEvent(event, sk);
|
||||
// store event id
|
||||
letsCID = signedEvent.id;
|
||||
// sends id and pubkey to function below
|
||||
showSID(letsCID, pk);
|
||||
//
|
||||
await relay.publish(signedEvent);
|
||||
relay.close();
|
||||
}
|
||||
// run and print errors to console
|
||||
newPost().catch(console.error);
|
||||
|
||||
// hey look the function below!
|
||||
|
||||
// gets id and pk from above function ;)
|
||||
async function showSID(letsCID, pk) {
|
||||
// print id to console
|
||||
console.log(letsCID);
|
||||
// print pubkey to console
|
||||
console.log(pk);
|
||||
// takes the event id, your write relay(s), pubkey, and kind 1
|
||||
// mashing it together into a nevent
|
||||
// (makes it easier for other clients to find the event)
|
||||
const event = {
|
||||
id: letsCID,
|
||||
relays: [config.relays.write],
|
||||
author: pk,
|
||||
kind: 1,
|
||||
};
|
||||
|
||||
// encode event using nip-19 (nevent)
|
||||
const encodedNEvent = nostr.nip19.neventEncode(event);
|
||||
// send the nevent back to the user
|
||||
// the can copy and paste this into most nostr
|
||||
// clients and get the event
|
||||
await xmpp.send(xml('message', { to: from, type: 'chat' }, xml('body', {}, 'nostr:' + encodedNEvent)));
|
||||
// also print nevent to console
|
||||
console.log(encodedNEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// uh-oh, XMPP error
|
||||
xmpp.on('error', (err) => {
|
||||
// print error to console
|
||||
console.error('XMPP Error:', err);
|
||||
});
|
||||
// start XMPP connectioh
|
||||
async function startXMPP() {
|
||||
try {
|
||||
// wait for connection
|
||||
await xmpp.start();
|
||||
// print success message to console
|
||||
console.log('🚀 XMPP Started Successfully');
|
||||
} catch (err) {
|
||||
// print success message to console
|
||||
console.error('❌ Failed to Start XMPP:', err);
|
||||
// kill nostrsms process to let user try again
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// attempt to connect to XMPP
|
||||
startXMPP();
|
||||
|
||||
// user hit ctrl+c!
|
||||
process.on('SIGINT', function () {
|
||||
console.log("SigInt receieved. Shutdown inevitable!")
|
||||
console.log("SigInt received. Shutdown inevitable!");
|
||||
xmpp.stop();
|
||||
process.exit();
|
||||
});
|
||||
|
||||
// error out and show install instructions if config.ini isn't found.
|
||||
|
||||
// config.ini is invalid, or missing.
|
||||
// print wiki link to console for further instructions
|
||||
} else {
|
||||
console.log("=_-_+_-_=_-_+_-_=_-_+_-_=_-_+_-_=_-_+_-_=");
|
||||
console.error("Config file not found at ${configPath}");
|
||||
console.error(`Config file not found at ${configPath}`);
|
||||
console.log("=_-_+_-_=_-_+_-_=_-_+_-_=_-_+_-_=_-_+_-_=");
|
||||
console.log("");
|
||||
console.log("Please create a config.ini");
|
||||
|
4618
package-lock.json
generated
4618
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
@ -15,15 +15,16 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@types/ws": "8.5.5",
|
||||
"assert": "^2.1.0",
|
||||
"bech32": "2.0.0",
|
||||
"ini": "2.0.0",
|
||||
"nostr-tools": "1.14.2",
|
||||
"simple-xmpp": "1.3.0",
|
||||
"sqlite3": "5.1.6",
|
||||
"@xmpp/client": "^0.13.0",
|
||||
"bech32": "^2.0.0",
|
||||
"debug": "^4.3.4",
|
||||
"fs": "0.0.1-security",
|
||||
"ini": "^2.0.0",
|
||||
"nostr-tools": "^1.9.0",
|
||||
"os": "^0.1.2",
|
||||
"path": "^0.12.7",
|
||||
"websocket-polyfill": "0.0.3",
|
||||
"ws": "^8.14.0"
|
||||
"ws": "^8.13.0"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://git.vanderwarker.family/nostr/nostrsms/issues"
|
||||
|
Loading…
x
Reference in New Issue
Block a user