Compare commits

...

9 commits
v1.1.0 ... main

Author SHA1 Message Date
Bee
53c7367acf Merge branch 'main' of https://github.com/padlocks/Compatiplural 2025-01-02 12:26:14 -08:00
Bee
b33335d18a fix: crash when custom front is activated 2025-01-02 12:22:37 -08:00
bee!
a764d7b21c
Update dataManager.js 2024-06-19 22:01:14 -07:00
bee!
0f00694d2e
Update .env_example 2024-06-19 22:00:50 -07:00
bee!
2983fb83e3
Update README.md 2024-06-19 21:59:49 -07:00
bee!
5e0fe8aa0c
docker: add dockerfile, format log 2023-10-22 16:31:10 -07:00
bee!
1564a110e0
allow for publishing entire front instead
Signed-off-by: bee! <pascalyoung03@gmail.com>
2023-09-24 13:32:14 -07:00
bee!
032f4fa753
Merge pull request #8 from padlocks/dev
fix customStatus events, option to silence spam messages
2022-05-13 09:41:45 -07:00
bee!
9d54038ec7
fix customStatus events, option to silence spam messages 2022-05-13 09:39:48 -07:00
8 changed files with 134 additions and 29 deletions

2
.dockerignore Normal file
View file

@ -0,0 +1,2 @@
node_modules
npm-debug.log

View file

@ -1,10 +1,13 @@
# Compatiplural # Compatiplural
url_override="https://v2.apparyllis.com" url_override="https://api.apparyllis.com"
api_version="v1" api_version="v1"
socket="wss://v2.apparyllis.com/v1/socket" socket="wss://api.apparyllis.com/v1/socket"
pk_url="https://api.pluralkit.me/v2" pk_url="https://api.pluralkit.me/v2"
token="SIMPLYPLURAL_TOKEN" token="SIMPLYPLURAL_TOKEN"
userId="abcd1234" userId="abcd1234"
pk_token= "PLURALKIT_TOKEN" pk_token= "PLURALKIT_TOKEN"
heartbeat=4500000 heartbeat=4500000
max_workers=1 max_workers=1
silence_connections=true
full_swap=false
primary_tag="primary "

2
.gitignore vendored
View file

@ -5,3 +5,5 @@ config.json
package-lock.json package-lock.json
.env .env
.vercel .vercel
docker_build.bat
docker_run.bat

16
Dockerfile Normal file
View file

@ -0,0 +1,16 @@
FROM node:18
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
# If you are building your code for production
# RUN npm ci --omit=dev
# Bundle app source
COPY . .
#EXPOSE 8080
CMD [ "node", "index.js" ]

View file

@ -9,12 +9,14 @@ This project already has a Procfile set up, so it's super easy to get started. O
These can be set either in the .env file, in terminal, or in the config vars section of Heroku. These can be set either in the .env file, in terminal, or in the config vars section of Heroku.
| Setting | Default | Description | | Setting | Default | Description |
| ---------| ------- | ------------------ | | ---------| ------- | ------------------ |
| url_override | https://v2.apparyllis.com | The base URL for all SimplyPlural API requests. Unless you are running your own fork of Simply Plural, you shouldn't change this. | | url_override | https://api.apparyllis.com | The base URL for all SimplyPlural API requests. Unless you are running your own fork of Simply Plural, you shouldn't change this. |
| api_version | v1 | The target SimplyPlural API version. Unless you are running your own fork of Simply Plural, you shouldn't change this. | | api_version | v1 | The target SimplyPlural API version. Unless you are running your own fork of Simply Plural, you shouldn't change this. |
| socket | wss://v2.apparyllis.com/v1/socket | The socket URL for SimplyPlural. Unless you are running your own fork of Simply Plural, you shouldn't change this. | | socket | wss://api.apparyllis.com/v1/socket | The socket URL for SimplyPlural. Unless you are running your own fork of Simply Plural, you shouldn't change this. |
| pk_url | https://api.pluralkit.me/v2 | The base URL for all PluralKit API requests. Unless you are running your own fork of PluralKit, you shouldn't change this. | | pk_url | https://api.pluralkit.me/v2 | The base URL for all PluralKit API requests. Unless you are running your own fork of PluralKit, you shouldn't change this. |
| token | token_here | Your SimplyPlural account token. As of now, the only permission necessary is the Read permission. | | token | token_here | Your SimplyPlural account token. As of now, the only permission necessary is the Read permission. |
| userId | user_id | Your SimplyPlural account/system ID. You can find it in account info near the bottom. | | userId | user_id | Your SimplyPlural account/system ID. You can find it in account info near the bottom. |
| pk_token | pluralkit_token_here | Your PluralKit token. Get it by using `pk;token`. | | pk_token | pluralkit_token_here | Your PluralKit token. Get it by using `pk;token`. |
| heartbeat | 4500000 | The time in miliseconds before the websocket client reconnects to the websocket server. | | heartbeat | 4500000 | The time in miliseconds before the websocket client reconnects to the websocket server. |
| max_workers | 1 | Max number of workers for processing enqueued tasks. This probably shouldn't be changed. | | max_workers | 1 | Max number of workers for processing enqueued tasks. This probably shouldn't be changed. |
| silence_connections | true | Whether or not to silence the websocket connection and authentication messages. |
| full_swap | false | Determines whether to completely overwrite PluralKit's front with SimplyPlural's. No individual changes. |

View file

@ -7,7 +7,7 @@ function WebSocketClient(url) {
let connecting = false let connecting = false
let backoff = 250 let backoff = 250
const init = () => { const init = () => {
console.error(`::SimplyWS:: [${timestamp()}] connecting`) if (!process.env.silence_connections) console.error(`::SimplyWS:: [${timestamp()}] connecting`)
connecting = false connecting = false
if (client !== undefined) { if (client !== undefined) {
client.removeAllListeners() client.removeAllListeners()
@ -21,14 +21,14 @@ function WebSocketClient(url) {
timeout = setTimeout(() => client.terminate(), process.env.heartbeat || 350000) timeout = setTimeout(() => client.terminate(), process.env.heartbeat || 350000)
} }
client.on('ping', () => { client.on('ping', () => {
console.log(`::SimplyWS:: [${timestamp()}] pinged`) if (!process.env.silence_connections) console.log(`::SimplyWS:: [${timestamp()}] pinged`)
heartbeat() heartbeat()
}) })
client.on('open', (e) => { client.on('open', (e) => {
if (typeof this.onOpen === 'function') { if (typeof this.onOpen === 'function') {
this.onOpen() this.onOpen()
} else { } else {
console.log(`::SimplyWS:: [${timestamp()}] opened`) if (!process.env.silence_connections) console.log(`::SimplyWS:: [${timestamp()}] opened`)
console.log(e) console.log(e)
} }
heartbeat() heartbeat()
@ -37,7 +37,7 @@ function WebSocketClient(url) {
if (typeof this.onMessage === 'function') { if (typeof this.onMessage === 'function') {
this.onMessage(e) this.onMessage(e)
} else { } else {
console.log(`::SimplyWS:: [${timestamp()}] messaged`) if (!process.env.silence_connections) console.log(`::SimplyWS:: [${timestamp()}] messaged`)
} }
heartbeat() heartbeat()
}) })

View file

@ -50,9 +50,9 @@ async function findPrimary() {
return new Promise(async (resolve) => { return new Promise(async (resolve) => {
await Util.asyncForEach(fronters, async (fronter) => { await Util.asyncForEach(fronters, async (fronter) => {
if (fronter.content.customStatus) { if (fronter.content.customStatus) {
if (fronter.content.customStatus.toLowerCase().includes("primary")) { if (fronter.content.customStatus.toLowerCase().includes(Config.primary_tag)) {
let member = await system.getMemberById(fronter.content.member) let member = await system.getMemberById(fronter.content.member)
resolve(member.content.pkId) resolve({ name: member.content.name, pkId: member.content.pkId })
found = true found = true
} }
} }
@ -106,6 +106,82 @@ async function determineAction(eventData, frontData = []) {
return action return action
} }
async function swapFront() {
let system = new System(Config)
let front = await system.getFronters()
// start forming new front list
let newFront = []
let frontNames = []
for (member of front) {
let m = await system.getMemberById(member.content.member)
if (m.content && m.content.pkId) {
// fronting member pkID has been found
newFront.push(m.content.pkId)
frontNames.push(m.content.name)
}
else {
console.warn('::SimplyWS:: System member not found, this may be a custom front which is unsupported.')
}
}
// shift primary fronter to first in list
let primary = await findPrimary()
let primaryPK = primary.pkId
let primaryName = primary.name
if (primaryPK) {
if (newFront.indexOf(primaryPK) > 0) {
newFront.splice(newFront.indexOf(primaryPK), 1)
newFront.unshift(primaryPK)
}
if (frontNames.indexOf(primaryName) > 0) {
frontNames.splice(frontNames.indexOf(primaryName), 1)
frontNames.unshift(primaryName)
}
}
// post the new switch
let url = `${pkUrl}/systems/@me/switches`
await axios.post(url, JSON.stringify({ "members": newFront }), {
headers: pkHeader
})
.then(async (res) => {
// check if current front equals the new front
let front = await getPKFronters()
var equal = (front.length == newFront.length) && front.every(function(element, index) {
return element === newFront[index];
})
if (!equal) {
console.log('::SimplyWS:: Failed to swap front: ' + newFront)
await swapFront()
return
} else {
let formattedNames = frontNames.toString().replace(',',', ')
console.log(`::SimplyWS:: SimplyPlural -> PluralKit: ${formattedNames}`)
}
})
.catch(async err => {
let status = err.status || err.toJSON().status
if (status == 400) {
// if the fronter is already in the front, do nothing
return
}
else if (status == 404) {
return
}
else if (status == 429) {
// Too many requests
console.warn("::SimplyWS:: Too many requests, waiting to try again.")
setTimeout(async function () {
await swapFront()
}, 1000)
return
}
})
}
async function insertFront(member) { async function insertFront(member) {
// get current fronters and add new fronter // get current fronters and add new fronter
let fronters = await getPKFronters() let fronters = await getPKFronters()
@ -117,7 +193,7 @@ async function insertFront(member) {
} }
// find the "primary" fronter to move to the first element in the list // find the "primary" fronter to move to the first element in the list
let primary = await findPrimary() let primary = await findPrimary().pkId
if (primary) { if (primary) {
if (fronters.indexOf(primary) > 0) { if (fronters.indexOf(primary) > 0) {
fronters.splice(fronters.indexOf(primary), 1) fronters.splice(fronters.indexOf(primary), 1)
@ -178,11 +254,11 @@ async function removeFront(member) {
} }
// find the "primary" fronter to move to the first element in the list // find the "primary" fronter to move to the first element in the list
let p = await findPrimary() let primary = await findPrimary().pkId
if (p) { if (primary) {
if (fronters.indexOf(p) > 0) { if (fronters.indexOf(primary) > 0) {
fronters.splice(fronters.indexOf(p), 1) fronters.splice(fronters.indexOf(primary), 1)
fronters.unshift(p) fronters.unshift(primary)
} }
} }
@ -221,10 +297,9 @@ async function removeFront(member) {
async function updateCustomStatus(member) { async function updateCustomStatus(member) {
// find the "primary" fronter to move to the first element in the list // find the "primary" fronter to move to the first element in the list
let system = new System(Config)
let fronters = await getPKFronters() let fronters = await getPKFronters()
let primary = await findPrimary() let primary = await findPrimary().pkId
if (primary && fronters.length > 1) { if (primary && fronters.length > 1 && (member.content.pkId == primary)) {
if (fronters.indexOf(primary) >= 0) { if (fronters.indexOf(primary) >= 0) {
fronters.splice(fronters.indexOf(primary), 1) fronters.splice(fronters.indexOf(primary), 1)
fronters.unshift(primary) fronters.unshift(primary)
@ -234,9 +309,7 @@ async function updateCustomStatus(member) {
headers: pkHeader headers: pkHeader
}) })
.catch(async err => { .catch(async err => {
if (err.toJSON().status == 400) if (err.toJSON().status == 429)
unknownError400()
else if (err.toJSON().status == 429)
//Too many requests //Too many requests
console.warn("::SimplyWS:: Too many requests, waiting to try again.") console.warn("::SimplyWS:: Too many requests, waiting to try again.")
setTimeout(function () { setTimeout(function () {
@ -278,8 +351,9 @@ module.exports = {
getPKFronters, getPKFronters,
findPrimary, findPrimary,
determineAction, determineAction,
swapFront,
insertFront, insertFront,
removeFront, removeFront,
updateCustomStatus, updateCustomStatus,
calculateDiff calculateDiff
} }

View file

@ -3,7 +3,7 @@ dotenv.config()
const { Config, System } = require('simplyapi') const { Config, System } = require('simplyapi')
const { Util } = require('simplyapi') const { Util } = require('simplyapi')
const { initializeCache, determineAction, insertFront, removeFront, updateCustomStatus } = require('./dataManager') const { initializeCache, determineAction, swapFront, insertFront, removeFront, updateCustomStatus } = require('./dataManager')
const { const {
isMainThread, isMainThread,
@ -39,6 +39,7 @@ initiateWorkerPool = () => {
bc.onmessage = (event) => { bc.onmessage = (event) => {
//console.log('::SimplyWS:: received message from worker') //console.log('::SimplyWS:: received message from worker')
queue.push(event.data, (error, task) => { queue.push(event.data, (error, task) => {
// task completed
if (error.status) { if (error.status) {
console.log(`An error occurred while processing task ${error.message}`) console.log(`An error occurred while processing task ${error.message}`)
} }
@ -59,6 +60,7 @@ openWebSocket = () => {
wss.onError = (e) => { console.log('SimplyWS/onError :: %s', e) } wss.onError = (e) => { console.log('SimplyWS/onError :: %s', e) }
const bc = new BroadcastChannel('plural') const bc = new BroadcastChannel('plural')
let first_auth = true
wss.onMessage = (raw) => { wss.onMessage = (raw) => {
e = raw e = raw
let data = JSON.parse(e) let data = JSON.parse(e)
@ -66,15 +68,15 @@ openWebSocket = () => {
switch (data.msg) { switch (data.msg) {
case "Successfully authenticated": case "Successfully authenticated":
console.log('::SimplyWS:: authenticated') if (!process.env.silence_connections || first_auth) console.log('::SimplyWS:: authenticated')
first_auth = false
// cache current front // cache current front
initializeCache() initializeCache()
break break
case "Authentication violation: Token is missing or invalid. Goodbye :)": case "Authentication violation: Token is missing or invalid. Goodbye :)":
console.log('::SimplyWS:: invalid token, exiting..') console.error('::SimplyWS:: invalid token, exiting..')
process.exit(1) process.exit(1)
case "update": case "update":
initializeCache()
bc.postMessage({data: data}) bc.postMessage({data: data})
break break
default: default:
@ -93,8 +95,12 @@ update = async (data) => {
await Util.asyncForEach(data.results, async (o) => { await Util.asyncForEach(data.results, async (o) => {
let system = new System(Config) let system = new System(Config)
let member = await system.getMemberById(o.content.member) let member = await system.getMemberById(o.content.member)
let swap = Config.full_swap
// insert // insert
if (o.operationType == "insert") { if (swap) {
swapFront()
}
else if (o.operationType == "insert") {
insertFront(member) insertFront(member)
} }
else { else {