228 lines
7.6 KiB
JavaScript
228 lines
7.6 KiB
JavaScript
// initialize required packages
|
|
const fs = require('fs');
|
|
const ini = require('ini');
|
|
const nostr = require('nostr-tools');
|
|
const os = require('os');
|
|
const path = require('path');
|
|
const wspf = require('websocket-polyfill');
|
|
const WebSocket = require('ws');
|
|
const xmpp = require('simple-xmpp');
|
|
|
|
|
|
// nostr things
|
|
let {
|
|
bech32,
|
|
bech32m
|
|
} = require('bech32');
|
|
|
|
let letsCID;
|
|
let signedEvent;
|
|
|
|
|
|
// config setup for home folders
|
|
// Determine the platform-specific config path
|
|
function getConfigPath() {
|
|
const platform = os.platform();
|
|
if (platform === 'win32') {
|
|
// Windows
|
|
return path.join(process.env.APPDATA, 'nostrsms', 'config.ini');
|
|
} else if (platform === 'darwin') {
|
|
// macOS
|
|
return path.join(os.homedir(), 'Library', 'Application Support', 'nostrsms', 'config.ini');
|
|
} else {
|
|
// Linux and other Unix-like systems
|
|
return path.join(os.homedir(), '.config', 'nostrsms', 'config.ini');
|
|
}
|
|
}
|
|
|
|
// prepare config for parsing
|
|
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,
|
|
password: config.xmpp.password,
|
|
host: config.xmpp.host,
|
|
port: 5222
|
|
});
|
|
// catch ctrl+c
|
|
// TODO: add confirmation ??
|
|
process.on('SIGINT', function () {
|
|
console.log("SigInt receieved. Shutdown inevitable!")
|
|
process.exit();
|
|
});
|
|
|
|
// error out and show install instructions if config.ini isn't found.
|
|
} else {
|
|
console.log("=_-_+_-_=_-_+_-_=_-_+_-_=_-_+_-_=_-_+_-_=");
|
|
console.error("Config file not found at ${configPath}");
|
|
console.log("=_-_+_-_=_-_+_-_=_-_+_-_=_-_+_-_=_-_+_-_=");
|
|
console.log("");
|
|
console.log("Please create a config.ini");
|
|
console.log("See https://wiki.vanderwarker.family/doku.php?id=code:nostrsms:install");
|
|
console.log("and choose your preferred install method.");
|
|
process.exit(1);
|
|
}
|