Merge pull request #4 from padlocks/dev

rebrand
This commit is contained in:
bee! 2022-03-23 14:04:29 -07:00 committed by GitHub
commit 8548088e20
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 323 additions and 709 deletions

View file

@ -1,5 +1,6 @@
# SPPK # Compatiplural
url="https://devapi.apparyllis.com" url_override="https://devapi.apparyllis.com"
api_version="v1"
socket="wss://devapi.apparyllis.com/v1/socket" socket="wss://devapi.apparyllis.com/v1/socket"
pk_url="https://api.pluralkit.me/v2" pk_url="https://api.pluralkit.me/v2"
token="AAAAAAAAAAAAAAAAAAAA" token="AAAAAAAAAAAAAAAAAAAA"

View file

@ -1,5 +1,6 @@
# SPPK # Compatiplural
### SimplyPlural -> PluralKit Connectivity. ### SimplyPlural -> PluralKit Connectivity.
Compatiplural is a small application which enables the transfer of your SimplyPlural data to PluralKit.
### Fork this repository and simply deploy to Heroku! ### Fork this repository and simply deploy to Heroku!
This project already has a Procfile set up, so it's super easy to get started. Once you have forked / cloned this repo, you can connect it to Heroku and fill in your credentials as config variables. More info below. This project already has a Procfile set up, so it's super easy to get started. Once you have forked / cloned this repo, you can connect it to Heroku and fill in your credentials as config variables. More info below.
@ -8,7 +9,8 @@ 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 | https://devapi.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://devapi.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. |
| socket | wss://devapi.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://devapi.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. |

View file

@ -1,61 +0,0 @@
const config = require('./config.json')
const SAPI = require('./SimplyAPI.js')
const SimplyAPI = new SAPI(config)
let group = {
parent: "root",
color: "",
private: true,
preventTrusted: false,
name: "123",
desc: "test group",
emoji: "",
members: []
}
main = async () => {
getGroups()
findGroup("123")
createTestGroup(group)
deleteTestGroup("123")
}
getGroups = async () => {
SimplyAPI.getGroups()
.then((response) => {
console.log(response.data)
})
.catch(err => console.error(err))
}
findGroup = async (what) => {
SimplyAPI.findGroup(what, (group) => {
if (group) {
console.log(group)
}
})
}
createTestGroup = async (data) => {
SimplyAPI.createGroup(data)
.then((response) => {
console.log(response.data)
})
.catch(err => console.error(err))
}
deleteTestGroup = async (what) => {
await SimplyAPI.findGroup(what, async (group) => {
if (group) {
await SimplyAPI.deleteGroup(group.id)
.then(async (res) => {
if (res.status == 200) {
console.log(`group deleted: ${group.content.name}.`,)
}
})
.catch(err => console.error(err))
}
})
}
main()

View file

@ -1,58 +0,0 @@
const config = require('./config.json')
const SAPI = require('./lib/SimplyAPI.js')
const SimplyAPI = new SAPI(config)
let member = {
name: "Test",
desc: "a test member",
pronouns: "It/Its",
pkId: "",
color: "",
avatarUuid: "",
avatarUrl: "",
private: false,
preventTrusted: false,
preventFrontNotifs: false,
info: {
"Age": "19",
"Likes": "bread"
}
}
main = async () => {
findMember("Test")
createTestMember(member)
deleteTestMember("Test")
}
findMember = async (who) => {
SimplyAPI.findMemberCallback(who, (member) => {
if (member) {
console.log(member)
}
})
}
createTestMember = async (data) => {
SimplyAPI.createMember(data)
.then((response) => {
console.log(response.data)
})
.catch(err => console.error(err))
}
deleteTestMember = async (who) => {
await SimplyAPI.findMember(who, async (member) => {
if (member) {
await SimplyAPI.deleteMember(member.id)
.then((res) => {
if (res.status == 200) {
console.log(`member deleted: ${res.data.content.name}.`)
}
})
.catch(err => console.error(err))
}
})
}
main()

View file

@ -1,26 +0,0 @@
const config = require('./config.json')
const SAPI = require('./SimplyAPI.js')
const SimplyAPI = new SAPI(config)
main = async () => {
getSystem()
getCurrentFronters()
}
getSystem = async () => {
SimplyAPI.getSystem()
.then((response) => {
console.log(response.data)
})
.catch(err => console.error(err))
}
getCurrentFronters = async () => {
SimplyAPI.getFronters()
.then((response) => {
console.log(response)
})
.catch(err => console.error(err))
}
main()

View file

@ -1,6 +0,0 @@
const SimplyAPI = require('./lib/SimplyAPI')
SimplyAPI.Validate = require('./lib/Validate')
SimplyAPI.Schemas = require('./lib/Schemas')
module.exports = SimplyAPI;

View file

@ -1,100 +0,0 @@
const memberSchema = {
type: "object",
properties: {
name: { type: "string" },
desc: { type: "string" },
pronouns: { type: "string" },
pkId: { type: "string" },
color: { type: "string" },
avatarUuid: { type: "string" },
avatarUrl: { type: "string" },
private: { type: "boolean" },
preventTrusted: { type: "boolean" },
preventFrontNotifs: { type: "boolean" },
info: {
type: "object",
properties: {
"*": { type: "string" }
}
}
},
nullable: false,
additionalProperties: false,
};
const groupSchema = {
type: "object",
properties: {
parent: { type: "string" },
color: { type: "string" },
private: { type: "boolean" },
preventTrusted: { type: "boolean" },
name: { type: "string" },
desc: { type: "string" },
emoji: { type: "string" },
members: { type: "array", items: { type: "string" } },
},
nullable: false,
additionalProperties: false,
dependencies: {
private: { required: ["preventTrusted"] },
preventTrusted: { required: ["private"] },
}
};
const customFrontSchema = {
type: "object",
properties: {
name: { type: "string" },
desc: { type: "string" },
avatarUrl: { type: "string" },
avatarUuid: { type: "string" },
color: { type: "string" },
preventTrusted: { type: "boolean" },
private: { type: "boolean" },
},
nullable: false,
additionalProperties: false,
}
const commentSchema = {
type: "object",
properties: {
time: { type: "number" },
text: { type: "string" },
documentId: { type: "string" },
collection: { type: "string" }
},
nullable: false,
additionalProperties: false,
required: ["time", "text", "documentId", "collection"]
}
const commentPatchSchema = {
type: "object",
properties: {
text: { type: "string" },
},
nullable: false,
additionalProperties: false,
required: ["text"]
}
const automatedTimerSchema = {
type: "object",
properties: {
name: { type: "string" },
message: { type: "string" },
action: { type: "number" },
delayInHours: { type: "number" },
type: { type: "number" },
},
nullable: false,
additionalProperties: false,
};
module.exports = {
memberSchema,
groupSchema
}

View file

@ -1,170 +0,0 @@
const { resolveRef } = require('ajv/dist/compile')
const axios = require('axios')
const schemas = require('./Schemas')
const validate = require('./Validate')
/**
* @param {Object} config
*/
class SimplyAPI {
constructor(config) {
this.url = config.url_override || 'https://devapi.apparyllis.com'
this.userId = config.userId
this.system = config.userId
this.token = config.token
this.header = {
'Content-Type': 'application/json',
'Authorization': this.token
}
}
getSystem = async () => {
let system = await axios.get(`${this.url}/v1/members/${this.system}`, {
headers: this.header
})
return system.data
//.then((response) => response)
//.catch(err => console.error(err.toJSON().message));
}
getGroups = async () => {
return axios.get(`${this.url}/v1/groups/${this.system}`, {
headers: this.header
})
.then((response) => response)
.catch(err => console.error(err.toJSON().message));
}
/**
* @param {string} group
* @param {function} callback
*/
findGroup = async (group, callback) => {
await this.getGroups()
.then((groups) => {
for (let i in groups.data) {
if (groups.data[i].content.name.includes(group)) {
callback(groups.data[i])
return
}
}
})
}
createGroup = async (group) => {
let valid = await validate.validateSchema(schemas.groupSchema, group)
if (valid) {
return axios.post(`${this.url}/v1/group/`, JSON.stringify(group), {
headers: this.header,
})
.then((response) => response)
.catch(err => console.error(err.toJSON().message));
} else {
let response = {}
response.data = {status: 'error', message: 'Invalid group schema'}
return response
}
}
deleteGroup = async (group) => {
return await axios.delete(`${this.url}/v1/group/${group}`, {
headers: this.header,
})
.then((response) => response)
.catch(err => console.error(err.toJSON().message));
}
/**
* @param {string} id
*/
findMemberById = async (id) => {
let found = false
let system = await this.getSystem()
return new Promise(async (resolve) => {
await asyncForEach(system, async (m) => {
if (m.id == id) {
found = true
resolve(m.content)
}
})
if (!found) resolve({ "name": "Unknown member" })
})
}
/**
* @param {string} member
*/
findMember = async (member) => {
let found = false
let system = await this.getSystem()
return new Promise(async (resolve) => {
await asyncForEach(system, async (m) => {
if (m.content.name.includes(member)) {
found = true
resolve(m)
}
})
if (!found) resolve({"name": "Unknown member"})
})
}
/**
* @param {string} member
* @param {function} callback
*/
findMemberCallback = async (member, callback) => {
await this.getSystem()
.then(async (system) => {
for (let i in system) {
if (system[i].content.name.includes(member)) {
await callback(system[i])
return
}
}
})
}
createMember = async (member) => {
let valid = await validate.validateSchema(schemas.memberSchema, member)
if (valid) {
return axios.post(`${this.url}/v1/member/`, JSON.stringify(member), {
headers: this.header,
})
.then((response) => response)
.catch(err => console.error(err.toJSON().message));
} else {
let response = {}
response.data = { status: 'error', message: 'Invalid group schema' }
return response
}
}
deleteMember = async (member) => {
return await axios.delete(`${this.url}/v1/member/${member}`, {
headers: this.header,
})
.then((response) => response)
.catch(err => console.error(err.toJSON().message));
}
getFronters = async () => {
return await axios.get(`${this.url}/v1/fronters/`, {
headers: this.header,
})
.then((response) => response.data)
.catch(err => console.error(err.toJSON().message));
}
}
asyncForEach = async (array, callback) => {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
module.exports = SimplyAPI

View file

@ -1,11 +0,0 @@
const Ajv = require('ajv')
const ajv = new Ajv()
class Validate {
static validateSchema = async (schema, body) => {
const validate = ajv.compile(schema)
return validate(body)
}
}
module.exports = Validate

View file

@ -1,20 +0,0 @@
{
"name": "simplyapi",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "padlocks",
"license": "ISC",
"optionalDependencies": {
"bufferutil": "^4.0.6",
"utf-8-validate": "^5.0.8"
},
"dependencies": {
"ajv": "^8.10.0",
"axios": "^0.26.0",
"ws": "^8.5.0"
}
}

272
dataManager.js Normal file
View file

@ -0,0 +1,272 @@
const axios = require('axios')
const { Config, System, Util } = require('SimplyAPI')
const pkUrl = Config.pk_url
const pkHeader = {
'Content-Type': 'application/json',
'Authorization': Config.pk_token
}
let cache = {}
async function initializeCache() {
let system = new System(Config)
cache.frontHistory = await system.getFronters()
}
function unknownError400() {
return
}
function unknownTarget(target) {
console.log('::SimplyWS:: Unknown update target: ' + target + '\n::SimplyWS:: Full message: ' + e)
}
function unrecognizedMessage(msg) {
console.log('::SimplyWS:: Unrecognized message: ' + msg + '\n::SimplyWS:: Full message: ' + e)
}
// async function asyncForEach(array, callback) {
// for (let index = 0; index < array.length; index++) {
// await callback(array[index], index, array)
// }
// }
async function getPKFronters() {
let members = []
let fronters = await axios.get(`${pkUrl}/systems/${Config.pk_system}/fronters`, {
headers: pkHeader
})
.catch((err) => {
if (err.toJSON().status == 429)
// Too many requests
setTimeout(async () => {
return await getPKFronters()
}, 1500)
})
if (fronters != undefined) {
fronters.data.members.forEach((key, value) => {
members.push(key.id)
})
}
return members
}
async function findPrimary() {
let found = false
let system = new System(Config)
let fronters = await system.getFronters()
return new Promise(async (resolve) => {
await Util.asyncForEach(fronters, async (fronter) => {
if (fronter.content.customStatus) {
if (fronter.content.customStatus.toLowerCase().includes("primary")) {
let member = await system.getMemberById(fronter.content.member)
resolve(member.content.pkId)
found = true
}
}
})
if (!found)
resolve(false)
})
}
async function determineAction(eventData, frontData = []) {
if (frontData.length == 0)
return 'remove'
let action = ''
// check for cache
if (!cache.frontHistory) {
let system = new System(Config)
let frontHistory = await system.getFronters()
cache.frontHistory = frontHistory
}
// get the difference between cached history and current front
let diff = await calculateDiff(cache.frontHistory, frontData)
// we handle one thing at a time, although this should be expanded since you can modify multiple custom statuses at once
if (diff.length >= 1) {
if (diff[0].content.customStatus || eventData.content.customStatus || diff[0].content.customStatus == "" || eventData.content.customStatus == "") {
// check if customStatus value is in cache
let foundInCache = Object.keys(cache.frontHistory).filter((key) => {
return cache.frontHistory[key] === diff[0].content.customStatus
})
// if value is unique, publish action
if (foundInCache.length == 0) {
action = 'customStatus'
}
}
else {
if (eventData.content.customStatus == '')
return 'customStatus'
console.error('::SimplyWS:: Unrecognized diff: ' + JSON.stringify(diff))
}
}
else {
// if there's an endTime, it was a removal event
if (eventData.content.endTime && !eventData.content.live) {
action = 'remove'
}
}
return action
}
async function insertFront(member) {
// get current fronters and add new fronter
let system = new System(Config)
let fronters = await getPKFronters()
fronters.push(member.content.pkId)
// find the "primary" fronter to move to the first element in the list
let primary = await findPrimary()
if (primary) {
if (fronters.indexOf(primary) > 0) {
fronters.splice(fronters.indexOf(primary), 1)
fronters.unshift(primary)
}
}
// cache front
cache.frontHistory = await system.getFronters()
// post the new switch
axios.post(`${pkUrl}/systems/${Config.pk_system}/switches`, JSON.stringify({ "members": fronters }), {
headers: pkHeader
})
.catch(err => {
if (err.toJSON().status == 400)
unknownError400()
else if (err.toJSON().status == 429)
// Too many requests
setTimeout(function () {
insertFront(member)
}, 1000)
return
})
let checkFront = await getPKFronters()
if (!checkFront.includes(member.content.pkId)) {
await insertFront(member)
return
} else {
console.log('::SimplyWS:: ' + member.content.name + ' was added to the front.')
}
}
async function removeFront(member) {
let system = new System(Config)
let fronters = await getPKFronters()
let index = fronters.indexOf(member.content.pkId)
fronters.splice(index, 1)
// find the "primary" fronter to move to the first element in the list
let p = await findPrimary()
if (p) {
if (fronters.indexOf(p) > 0) {
fronters.splice(fronters.indexOf(p), 1)
fronters.unshift(p)
}
}
// cache front
cache.frontHistory = await system.getFronters()
// post the new switch
axios.post(`${pkUrl}/systems/${Config.pk_system}/switches`, JSON.stringify({ "members": fronters }), {
headers: pkHeader
})
.catch(err => {
if (err.toJSON().status == 400)
unknownError400()
else if (err.toJSON().status == 429)
// Too many requests
setTimeout(function () {
removeFront(member)
}, 1000)
return
})
let checkFront = await getPKFronters()
if (checkFront.includes(member.content.pkId)) {
await removeFront(member)
return
} else {
console.log('::SimplyWS:: ' + member.content.name + ' was removed from the front.')
}
}
async function updateCustomStatus(member) {
// find the "primary" fronter to move to the first element in the list
let system = new System(Config)
let fronters = await getPKFronters()
let primary = await findPrimary()
if (primary && fronters.length > 1) {
if (fronters.indexOf(primary) >= 0) {
fronters.splice(fronters.indexOf(primary), 1)
fronters.unshift(primary)
// cache front
cache.frontHistory = await system.getFronters()
// post the new switch
axios.post(`${pkUrl}/systems/${Config.pk_system}/switches`, JSON.stringify({ "members": fronters }), {
headers: pkHeader
})
.catch(err => {
if (err.toJSON().status == 400)
unknownError400()
else if (err.toJSON().status == 429)
// Too many requests
setTimeout(function () {
updateCustomStatus(member)
}, 1000)
return
})
console.log('::SimplyWS:: ' + member.content.name + ' is now the primary fronter.')
}
}
else {
console.log('::SimplyWS:: ' + member.content.name + ' changed custom status.')
}
}
const { inspect } = require('util')
const transform = require('lodash.transform')
const isEqual = require('lodash.isequal')
const isArray = require('lodash.isarray')
const isObject = require('lodash.isobject')
const { PassThrough } = require('stream')
async function calculateDiff(origObj, newObj) {
return new Promise(function (resolve) {
changes = (newObj, origObj) => {
let arrayIndexCounter = 0
return transform(newObj, function (result, value, key) {
if (!isEqual(value, origObj[key])) {
let resultKey = isArray(origObj) ? arrayIndexCounter++ : key
result[resultKey] = (isObject(value) && isObject(origObj[key])) ? changes(value, origObj[key]) : value
}
})
}
resolve(changes(newObj, origObj))
})
}
module.exports = {
initializeCache,
unknownError400,
unknownTarget,
unrecognizedMessage,
getPKFronters,
findPrimary,
determineAction,
insertFront,
removeFront,
updateCustomStatus,
calculateDiff
}

289
index.js
View file

@ -1,291 +1,80 @@
const dotenv = require('dotenv') const dotenv = require('dotenv')
dotenv.config() dotenv.config()
const config = process.env
const axios = require('axios') const { Config, System } = require('SimplyAPI')
const SAPI = require('./SimplyAPI') const { Util } = require('SimplyAPI')
const SimplyAPI = new SAPI(config) const { initializeCache, determineAction, insertFront, removeFront, updateCustomStatus } = require('./dataManager')
const pkUrl = config.pk_url
const pkHeader = {
'Content-Type': 'application/json',
'Authorization': config.pk_token
}
let e let e
let cache = {}
main = async () => { main = async () => {
openWebSocket() openWebSocket()
} }
openWebSocket = async () => { openWebSocket = async () => {
const WebSocketClient = require('./WebsocketClient') const WebSocketClient = require('./WebsocketClient')
const wss = new WebSocketClient(config.socket); const wss = new WebSocketClient(Config.socket);
let initialPacket = { "op": "authenticate", "token": config.token } let initialPacket = { "op": "authenticate", "token": Config.token }
wss.onOpen = (_) => { wss.send(JSON.stringify(initialPacket)); } wss.onOpen = (_) => { wss.send(JSON.stringify(initialPacket)); }
wss.onClose = (e) => { console.log('SimplyWS/onClose :: %s', e); e = '' } wss.onClose = (e) => { console.log('SimplyWS/onClose :: %s', e); e = '' }
wss.onError = (e) => { console.log('SimplyWS/onError :: %s', e) } wss.onError = (e) => { console.log('SimplyWS/onError :: %s', e) }
wss.onMessage = async (raw) => { wss.onMessage = (raw) => {
e = raw e = raw
let data = JSON.parse(e) let data = JSON.parse(e)
if (Object.keys(data).length === 0) return if (Object.keys(data).length === 0) return
switch (data.msg) { switch (data.msg) {
case "Successfully authenticated": case "Successfully authenticated":
console.log('::SimplyWS:: authenticated') console.log('::SimplyWS:: authenticated')
// cache current front // cache current front
cache.frontHistory = await SimplyAPI.getFronters() 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.log('::SimplyWS:: invalid token, exiting..')
process.exit(1) process.exit(1)
case "update": case "update":
let response = await generateResponse(data.target, data); update(data)
if (response) console.log('::SimplyWS:: ' + response) break
break;
default: default:
unrecognizedMessage(data.msg) //unrecognizedMessage(data.msg)
break; break
} }
} }
} }
generateResponse = async (target, data) => { update = async (data) => {
let response = '' let target = data.target
switch (target) { switch (target) {
case 'frontHistory': case 'frontHistory':
//response += 'Front has changed!' //response += 'Front has changed!'
await asyncForEach(data.results, async (o) => { await Util.asyncForEach(data.results, async (o) => {
await SimplyAPI.findMemberById(o.content.member) let system = new System(Config)
.then(async (member) => { let member = await system.getMemberById(o.content.member)
if (o.operationType == "insert") { // insert
// get current fronters and add new fronter if (o.operationType == "insert") {
let fronters = await getPKFronters() insertFront(member)
fronters.push(member.pkId) }
else {
// get current fronters and patch the list
let frontData = await system.getFronters()
let action = await determineAction(o, frontData)
// if delete operation, remove the member from the list
switch (action) {
case "remove":
removeFront(member)
break
// find the "primary" fronter to move to the first element in the list case "customStatus":
let primary = findPrimary() updateCustomStatus(member)
if (primary) { break
if (fronters.indexOf(primary) > 0) { }
fronters.splice(fronters.indexOf(primary), 1) }
fronters.unshift(primary)
}
}
// cache front
cache.frontHistory = await SimplyAPI.getFronters()
// post the new switch
axios.post(`${pkUrl}/systems/${config.pk_system}/switches`, JSON.stringify({"members": fronters}), {
headers: pkHeader
})
.catch(err => {
if (err.toJSON().status == 400) unknownError400()
else console.error(err.message)
})
response += '' + member.name + ' was added to the front.'
return
}
else {
// get current fronters and patch the list
let fronters = await getPKFronters()
let frontData = await SimplyAPI.getFronters()
let action = await determineAction(o, frontData)
// if delete operation, remove the member from the list
switch (action) {
case "remove":
let index = fronters.indexOf(member.pkId)
fronters.splice(index, 1)
// find the "primary" fronter to move to the first element in the list
let p = findPrimary()
if (p) {
if (fronters.indexOf(p) > 0) {
fronters.splice(fronters.indexOf(p), 1)
fronters.unshift(p)
}
}
// cache front
cache.frontHistory = await SimplyAPI.getFronters()
// post the new switch
axios.post(`${pkUrl}/systems/${config.pk_system}/switches`, JSON.stringify({ "members": fronters }), {
headers: pkHeader
})
.catch(err => {
if (err.toJSON().status == 400) unknownError400()
else console.error(err.message)
})
response += '' + member.name + ' was removed from the front.'
break;
case "customStatus":
// find the "primary" fronter to move to the first element in the list
let primary = await findPrimary()
if (primary && fronters.length > 1) {
if (fronters.indexOf(primary) >= 0) {
fronters.splice(fronters.indexOf(primary), 1)
fronters.unshift(primary)
// cache front
cache.frontHistory = await SimplyAPI.getFronters()
// post the new switch
axios.post(`${pkUrl}/systems/${config.pk_system}/switches`, JSON.stringify({ "members": fronters }), {
headers: pkHeader
})
.catch(err => {
if (err.toJSON().status == 400) unknownError400()
else console.error(err.message)
})
response += '' + member.name + ' is now the primary fronter.'
}
}
else {
response += '' + member.name + ' changed custom status.'
}
break;
}
return
}
})
.catch(err => {
console.log('::SimplyWS:: Error finding member: ' + err)
})
}) })
break; break
default: default:
unknownTarget(data.target) //unknownTarget(data.target)
break; break
} }
return response
}
unknownError400 = () => {
return
}
unknownTarget = (target) => {
console.log('::SimplyWS:: Unknown update target: ' + target + '\n::SimplyWS:: Full message: ' + e)
}
unrecognizedMessage = (msg) => {
console.log('::SimplyWS:: Unrecognized message: ' + msg + '\n::SimplyWS:: Full message: ' + e)
}
findMember = (who) => {
return new Promise(function (resolve, reject) {
SimplyAPI.findMember(who, (member) => {
if (member) {
resolve(member)
} else {
reject({"name": "Unknown member"})
}
})
})
}
getPKFronters = async () => {
let members = []
let fronters = await axios.get(`${pkUrl}/systems/${config.pk_system}/fronters`, {
headers: pkHeader
})
.catch(err => console.error("An error occured while getting current fronters: " + err.message))
fronters.data.members.forEach((key, value) => {
members.push(key.id)
})
return members
}
findPrimary = async () => {
let found = false
let fronters = await SimplyAPI.getFronters()
return new Promise(async (resolve) => {
await asyncForEach(fronters, async (fronter) => {
if (fronter.content.customStatus) {
if (fronter.content.customStatus.toLowerCase().includes("primary")) {
let member = await SimplyAPI.findMemberById(fronter.content.member)
resolve(member.pkId)
found = true
}
}
})
if (!found) resolve(false)
})
}
determineAction = async (eventData, frontData = []) => {
if (frontData.length == 0) return 'remove'
let action = ''
// check for cache
if (!cache.frontHistory) {
let frontHistory = await SimplyAPI.getFronters()
cache.frontHistory = frontHistory
}
// get the difference between cached history and current front
let diff = await calculateDiff(cache.frontHistory, frontData)
// we handle one thing at a time, although this should be expanded since you can modify multiple custom statuses at once
if (diff.length >= 1) {
if (diff[0].content.customStatus || eventData.content.customStatus) {
// check if customStatus value is in cache
let foundInCache = Object.keys(cache.frontHistory).filter((key) => {
return cache.frontHistory[key] === diff[0].content.customStatus
})
// if value is unique, publish action
if (foundInCache.length == 0) {
action = 'customStatus'
}
}
else {
if (eventData.content.customStatus == '') return 'customStatus'
console.error('::SimplyWS:: Unrecognized diff: ' + JSON.stringify(diff))
}
}
else {
// if there's an endTime, it was a removal event
if (eventData.content.endTime && !eventData.content.live) {
action = 'remove'
}
}
return action
}
asyncForEach = async (array, callback) => {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
const { inspect } = require('util')
const transform = require('lodash.transform')
const isEqual = require('lodash.isequal')
const isArray = require('lodash.isarray')
const isObject = require('lodash.isobject')
const { PassThrough } = require('stream')
calculateDiff = async (origObj, newObj) => {
return new Promise(function (resolve) {
changes = (newObj, origObj) => {
let arrayIndexCounter = 0
return transform(newObj, function (result, value, key) {
if (!isEqual(value, origObj[key])) {
let resultKey = isArray(origObj) ? arrayIndexCounter++ : key
result[resultKey] = (isObject(value) && isObject(origObj[key])) ? changes(value, origObj[key]) : value
}
})
}
resolve(changes(newObj, origObj))
})
} }
main() main()

View file

@ -1,14 +1,15 @@
{ {
"name": "sppk", "name": "Compatiplural",
"version": "1.0.0", "version": "1.0.0",
"description": "A tool to automatically update PluralKit with SimplyPlural data.", "description": "SimplyPlural -> PluralKit Connectivity",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"start": "node .", "start": "node .",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"author": "padlocks", "author": "padlocks",
"license": "ISC", "license": "MIT",
"repository": "github:padlocks/Compatiplural",
"dependencies": { "dependencies": {
"ajv": "^8.10.0", "ajv": "^8.10.0",
"axios": "^0.26.0", "axios": "^0.26.0",
@ -17,6 +18,7 @@
"lodash.isequal": "^4.5.0", "lodash.isequal": "^4.5.0",
"lodash.isobject": "^3.0.2", "lodash.isobject": "^3.0.2",
"lodash.transform": "^4.6.0", "lodash.transform": "^4.6.0",
"simplyapi": "^0.1.1",
"ws": "^8.5.0" "ws": "^8.5.0"
}, },
"optionalDependencies": { "optionalDependencies": {